How one space can create a nightmare for you…

As you know that MySQL 5.7 is going to be in its end of life this month (when I wrote this article) and many businesses are upgrading to 8 ( except few ones, believe me its true) . Many cloud vendors are providing extra support and time to their customer so that they can move to 8. One of the major change in 8 is the default character type and collation. Previously the default one was latin1 and also the alias of utf8 was utf8mb3 but now it is utf8mb4. There are plenty of articles from many database service providers that says no data and table structural changes are required. Although it is true to some extent but there are some major changes, if being missed can cause your application to behave weirdly.

Lets go basic first !!

What is a character set ?

A character is a letter, let say A,B,C . Now I want to encode these letters i.e. A can be represented by the ! and B can be represented by the $. If I combine the letter and their encoding it can be called as a character set.

There are total 41 character set in 8.0.33. I wont go in detail of every character set as this is out of topic but still if you are more interested in reading about it, this is a manual. This link will also explain how utf8mb4 is different from the utf8mb3 and other character set and how man bytes does it take to store a single character.

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.33 |
+-----------+
1 row in set (0.00 sec)
mysql> select count(1) from information_schema.CHARACTER_SETS;
+----------+
| count(1) |
+----------+
| 41 |
+----------+
1 row in set (0.00 sec)

What is a collation ?

Above I mentioned that if I replace A by ! , then I am imposing rule . This rule is called as a collation. This is a very simple type of collation. Collation can be much more complex, depending on the type of rules one have created.

In total there are 286 collations in 8.0.33. There can be multiple types of collation for one character set for different languages. For example , see below

mysql> select * from collations where CHARACTER_SET_NAME='utf8mb3';
+-----------------------------+--------------------+-----+------------+-------------+---------+---------------+
| COLLATION_NAME | CHARACTER_SET_NAME | ID | IS_DEFAULT | IS_COMPILED | SORTLEN | PAD_ATTRIBUTE |
+-----------------------------+--------------------+-----+------------+-------------+---------+---------------+
| utf8mb3_general_ci | utf8mb3 | 33 | Yes | Yes | 1 | PAD SPACE |
| utf8mb3_tolower_ci | utf8mb3 | 76 | | Yes | 1 | PAD SPACE |
| utf8mb3_bin | utf8mb3 | 83 | | Yes | 1 | PAD SPACE |
| utf8mb3_unicode_ci | utf8mb3 | 192 | | Yes | 8 | PAD SPACE |
| utf8mb3_icelandic_ci | utf8mb3 | 193 | | Yes | 8 | PAD SPACE |
| utf8mb3_latvian_ci | utf8mb3 | 194 | | Yes | 8 | PAD SPACE |
| utf8mb3_romanian_ci | utf8mb3 | 195 | | Yes | 8 | PAD SPACE |
| utf8mb3_slovenian_ci | utf8mb3 | 196 | | Yes | 8 | PAD SPACE |
| utf8mb3_polish_ci | utf8mb3 | 197 | | Yes | 8 | PAD SPACE |
| utf8mb3_estonian_ci | utf8mb3 | 198 | | Yes | 8 | PAD SPACE |
| utf8mb3_spanish_ci | utf8mb3 | 199 | | Yes | 8 | PAD SPACE |
| utf8mb3_swedish_ci | utf8mb3 | 200 | | Yes | 8 | PAD SPACE |
| utf8mb3_turkish_ci | utf8mb3 | 201 | | Yes | 8 | PAD SPACE |
| utf8mb3_czech_ci | utf8mb3 | 202 | | Yes | 8 | PAD SPACE |
| utf8mb3_danish_ci | utf8mb3 | 203 | | Yes | 8 | PAD SPACE |
| utf8mb3_lithuanian_ci | utf8mb3 | 204 | | Yes | 8 | PAD SPACE |
| utf8mb3_slovak_ci | utf8mb3 | 205 | | Yes | 8 | PAD SPACE |
| utf8mb3_spanish2_ci | utf8mb3 | 206 | | Yes | 8 | PAD SPACE |
| utf8mb3_roman_ci | utf8mb3 | 207 | | Yes | 8 | PAD SPACE |
| utf8mb3_persian_ci | utf8mb3 | 208 | | Yes | 8 | PAD SPACE |
| utf8mb3_esperanto_ci | utf8mb3 | 209 | | Yes | 8 | PAD SPACE |
| utf8mb3_hungarian_ci | utf8mb3 | 210 | | Yes | 8 | PAD SPACE |
| utf8mb3_sinhala_ci | utf8mb3 | 211 | | Yes | 8 | PAD SPACE |
| utf8mb3_german2_ci | utf8mb3 | 212 | | Yes | 8 | PAD SPACE |
| utf8mb3_croatian_ci | utf8mb3 | 213 | | Yes | 8 | PAD SPACE |
| utf8mb3_unicode_520_ci | utf8mb3 | 214 | | Yes | 8 | PAD SPACE |
| utf8mb3_vietnamese_ci | utf8mb3 | 215 | | Yes | 8 | PAD SPACE |
| utf8mb3_general_mysql500_ci | utf8mb3 | 223 | | Yes | 1 | PAD SPACE |
+-----------------------------+--------------------+-----+------------+-------------+---------+---------------+
28 rows in set (0.01 sec)
Total number of collations in 8 :
mysql> select count(*) from COLLATIONS;
+----------+
| count(*) |
+----------+
| 286 |
+----------+
1 row in set (0.00 sec)
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.33 |
+-----------+
1 row in set (0.00 sec)

Ok, Now going back to the original topic of our blog:

In collation table above, there is a column name known as “pad attribute” . Each collation has a property which talks about the PAD_ATTRIBUTE i.e. if a string can have trailing space (a space after string) as its own part (without considering it separately) or should it be treated as its additional character. It may sounds confusing now but I will clear it further as this is a main topic of this article.


How pad attribute makes the difference while upgrading ?

In many cases if we were using latin1 or utf8 (which was an alias of utf8mb3) as the character set of our tables in 5.7, most probably we are using latin1_swedish_ci or utf8mb3_general_ci as its collation type because these are the default one and rarely we do change it until unless we have some specific business needs. Now lets talk about a case wherein we have a table with character set as utf8mb3

mysql> show create table check_mb3\G;
*************************** 1. row ***************************
Table: check_mb3
Create Table: CREATE TABLE `check_mb3` (
`id` int DEFAULT NULL,
`name` char(10) DEFAULT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
1 row in set (0.01 sec)
mysql> insert into check_mb3 values(1,'ankit');
Query OK, 1 row affected (0.00 sec)
mysql> insert into check_mb3 values(1,'ankit ');
ERROR 1062 (23000): Duplicate entry 'ankit' for key 'check_mb3.name'
mysql> insert into check_mb3 values(2,'guga');
Query OK, 1 row affected (0.00 sec)
mysql> insert into check_mb3 values(3,'guga ');
ERROR 1062 (23000): Duplicate entry 'guga' for key 'check_mb3.name'
mysql> insert into check_mb3 values(4,'victor');
Query OK, 1 row affected (0.00 sec)
mysql> insert into check_mb3 values(5,'victor ');
ERROR 1062 (23000): Duplicate entry 'victor' for key 'check_mb3.name'

Until now, everything went as expected and we are not able to insert the same string even if it has trailing spaces which means, trailing space has been removed when being stored on disk and thus both strings (‘ankit’ & ‘ankit ‘) are same for the user or client . Now let’s try to migrate our tables to 8 by changing the character set since utf8mb4 is default one and comes with collations which has NO PAD option.

mysql> alter table check_mb3 CHARACTER SET UTF8mb4;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table check_mb3 modify name char(10) CHARACTER SET utf8mb4 collate utf8mb4_0900_ai_ci;
Query OK, 3 rows affected (0.03 sec)
Records: 3 Duplicates: 0 Warnings
mysql> show create table check_mb3\G;
*************************** 1. row ***************************
Table: check_mb3
Create Table: CREATE TABLE `check_mb3` (
`id` int DEFAULT NULL,
`name` char(10) CHARACTER SET utf8mb4 collate utf8mb4_0900_ai_ci DEFAULT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
mysql> insert into check_mb3 values(4,'victor ');
ERROR 1062 (23000): Duplicate entry 'victor' for key 'check_mb3.name'
mysql> select * from check_mb3;
+------+--------+
| id | name |
+------+--------+
| 1 | ankit |
| 2 | guga |
| 3 | victor |
+------+--------+
3 rows in set (0.00 sec)

OOPS !!, we didn’t expect this, but we are fine in this case when we are using data type as CHAR. This is because as per SQL standards, while storing the CHAR data, both string should be treated as same regardless of the collation but not while READING. Keep on reading further.

but lets think of a use case wherein our business logic says that
every string should end with a trailing space
and hence accordingly we will insert a string with a space for this test

mysql> insert into check_mb3 values(7,'ankittf84 ');
Query OK, 1 row affected (0.01 sec)
--------and then we read the same string with space----
mysql> select * from check_mb3 where name='ankittf84 ';
Empty set (0.00 sec)

Here our application has started behaving weird and user will assume that our string doesn’t exists. However if we do same in utf8mb3 collation (which was default in MySQL 5.7) the above read will return the data because while reading it treats both string as different. I repeat , while reading it treats both string as different but not while storing because the data is being stored identically, but when read, InnoDB do the comparison based on collation type i.e. trim space or consider space.

mysql> show create table check_mb3_again;
+-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| check_mb3_again | CREATE TABLE `check_mb3_again` (
`id` int DEFAULT NULL,
`name` char(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select COLLATION_NAME,PAD_ATTRIBUTE from information_schema.collations where COLLATION_NAME='utf8mb3_general_ci';
+--------------------+---------------+
| COLLATION_NAME | PAD_ATTRIBUTE |
+--------------------+---------------+
| utf8mb3_general_ci | PAD SPACE |
+--------------------+---------------+
mysql> insert into check_mb3_again values (1,'ankit ');
Query OK, 1 row affected (0.00 sec)
mysql> select * from check_mb3_again where name='ankit ';
+------+-------+
| id | name |
+------+-------+
| 1 | ankit |
+------+-------+
1 row in set (0.00 sec)

Im sure you must be thinking “why even any application will search like this” and I would be completely agree with you. But hold on, I explained this just to setup the background.

NOW, major question is how migration to 8 will corrupt the data while writing ?

Lets try to understand with an example wherein same table structure but with VARCHAR data type.

1. Keep utf8mb3 for now which is default in 5.7 :

mysql> CREATE TABLE check_mb3_again_withvarchar (name varchar(10)) character set utf8mb3 collate utf8mb3_general_ci ;
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> alter table check_mb3_again_withvarchar modify name varchar(10) unique key;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into check_mb3_again_withvarchar values('ankit');
Query OK, 1 row affected (0.01 sec)
mysql> insert into check_mb3_again_withvarchar values('ankit ');
ERROR 1062 (23000): Duplicate entry 'ankit ' for key 'check_mb3_again_withvarchar.name'

So this is expected that even if application or web, sends the same string with trailing space, it will be rejected.
Now its time to migrate the table to 8 using mb4 collation.

2. Change table and column to utf8mb4 for now which is default in 8:

mysql> alter table check_mb3_again_withvarchar charset utf8mb4;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table check_mb3_again_withvarchar modify name varchar(10) CHARACTER SET utf8mb4 collate utf8mb4_0900_ai_ci;
Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> show create table check_mb3_again_withvarchar;
+-----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| check_mb3_again_withvarchar | CREATE TABLE `check_mb3_again_withvarchar` (
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Ok, so we have migrated the table successfully, lets continue with our writes –

mysql> insert into check_mb3_again_withvarchar values('ankit ');
Query OK, 1 row affected (0.00 sec)
mysql> select * from check_mb3_again_withvarchar;
+--------+
| name |
+--------+
| ankit |
| ankit |
+--------+
2 rows in set (0.00 sec)

So, now what would you say here and your data became inconsistent and you are gone into a nightmare without even realizing this and you will realize it after years or months ???

Every problem has a solution :

I learnt in my life that every problem has a solution and success is, how well you solve your problems.

Initially I mentioned that there are multiple collations being supported by one character set, we can select the one we need. By “need” I mean several factors like the type of character we need, do we need to compare the strings without spaces or with spaces.

Considering the specific case mentioned in this topic, if we are looking to opt utf8mb4 but with PAD SPACES, we can choose one from collation table.

Lets talk with an example :

mysql> select COLLATION_NAME,PAD_ATTRIBUTE from information_schema.collations where CHARACTER_SET_NAME='utf8mb4' and PAD_ATTRIBUTE='PAD SPACE';
+------------------------+---------------+
| COLLATION_NAME | PAD_ATTRIBUTE |
+------------------------+---------------+
| utf8mb4_general_ci | PAD SPACE |
| utf8mb4_bin | PAD SPACE |
| utf8mb4_unicode_ci | PAD SPACE |
| utf8mb4_icelandic_ci | PAD SPACE |
| utf8mb4_latvian_ci | PAD SPACE |
| utf8mb4_romanian_ci | PAD SPACE |
| utf8mb4_slovenian_ci | PAD SPACE |
| utf8mb4_polish_ci | PAD SPACE |
| utf8mb4_estonian_ci | PAD SPACE |
| utf8mb4_spanish_ci | PAD SPACE |
| utf8mb4_swedish_ci | PAD SPACE |
| utf8mb4_turkish_ci | PAD SPACE |
| utf8mb4_czech_ci | PAD SPACE |
| utf8mb4_danish_ci | PAD SPACE |
| utf8mb4_lithuanian_ci | PAD SPACE |
| utf8mb4_slovak_ci | PAD SPACE |
| utf8mb4_spanish2_ci | PAD SPACE |
| utf8mb4_roman_ci | PAD SPACE |
| utf8mb4_persian_ci | PAD SPACE |
| utf8mb4_esperanto_ci | PAD SPACE |
| utf8mb4_hungarian_ci | PAD SPACE |
| utf8mb4_sinhala_ci | PAD SPACE |
| utf8mb4_german2_ci | PAD SPACE |
| utf8mb4_croatian_ci | PAD SPACE |
| utf8mb4_unicode_520_ci | PAD SPACE |
| utf8mb4_vietnamese_ci | PAD SPACE |
+------------------------+---------------+
CREATE TABLE `check_mb4_withvarchar` (
`id` int DEFAULT NULL,
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Have you noticed why I have chosen utf8mb4_general_ci which is not a default one ? This is because, this collation comes with PAD SPACE i.e. trailing space will be considered same as WITHOUT SPACE in VARCHAR.
Now, insert some data to confirm our understanding :

mysql> insert into check_mb4_withvarchar values (1,'ankit');
Query OK, 1 row affected (0.01 sec)
mysql> insert into check_mb4_withvarchar values (1,'ankit ');
ERROR 1062 (23000): Duplicate entry 'ankit ' for key 'check_mb4_withvarchar.name

AND this is what I really needed to prove that how one space can corrupt your entire data while upgrading to 8.

So, what is important ? 

It is extremely important to understand the behaviour of your data and how does the write operations actually writing the data before planning your migration as such issues looks invisible in beginning but after few months or years, it mess up your data. So, yes deciding character set and collation is extremely important for your data.

Meet me here on linkedin.

Note – This is fork from my previous article but on new domain and with new subject line.


Redo flushing. MySQL VS Postgres and Redo in 8/9

One of my earlier post MySQL Innodb – Internal Transaction flow and its related video talks about a transaction flow which gives the internal concept of transaction flow in Innodb. In this article I am going to talk about how the Redo log ( WAL in PG ) flushing is different in MySQL and Postgres and…

tpc-c Benchmarking of MySQL 8.0.44

This setup is on my local machine for mac with 8.0.44. M2 chip, memory 16G

High level Results :
total transaction executed in 10 min – 247298
Transaction per minute – 24729
Time taken on average for every case – 0-26ms ( you have to do this calculation , testing wont tell you this. I have done this myself)
Duration of test – 10 min
thread -1

Running Average tpmTOTAL: 24729.81 Current tpmTOTAL: 1633860 Memory Usage: 107MB / 258MB
20:44:26,051 [Thread-0] INFO jTPCC : Term-00,
20:44:26,054 [Thread-0] INFO jTPCC : Term-00,
20:44:26,055 [Thread-0] INFO jTPCC : Term-00, Measured tpmC (NewOrders) = 11128.87
20:44:26,055 [Thread-0] INFO jTPCC : Term-00, Measured tpmTOTAL = 24729.4
20:44:26,055 [Thread-0] INFO jTPCC : Term-00, Session Start = 2026-01-25 20:34:26
20:44:26,055 [Thread-0] INFO jTPCC : Term-00, Session End = 2026-01-25 20:44:26
20:44:26,056 [Thread-0] INFO jTPCC : Term-00, Transaction Count = 247298
20:44:26,056 [Thread-0] INFO jTPCC : executeTime[Payment]=74933
20:44:26,056 [Thread-0] INFO jTPCC : executeTime[Order-Status]=4676
20:44:26,056 [Thread-0] INFO jTPCC : executeTime[Delivery]=43428
20:44:26,056 [Thread-0] INFO jTPCC : executeTime[Stock-Level]=256644
20:44:26,056 [Thread-0] INFO jTPCC : executeTime[New-Order]=220100

Now if in case you want to understand , how I did this and setup, Keep on reading further else you can stop here.

benchmark tool for MySQL fork has been used

ankit@ankits-MacBook-Pro Documents % git clone https://github.com/pingcap/benchmarksql.git
Cloning into 'benchmarksql'...
remote: Enumerating objects: 506, done.
remote: Counting objects: 100% (239/239), done.
remote: Compressing objects: 100% (76/76), done.
remote: Total 506 (delta 174), reused 163 (delta 163), pack-reused 267 (from 2)
Receiving objects: 100% (506/506), 4.48 MiB | 10.98 MiB/s, done.
Resolving deltas: 100% (233/233), done.

Check config file

ankit@ankits-MacBook-Pro Documents % cd benchmarksql
ankit@ankits-MacBook-Pro benchmarksql % ls
build.xml doc HOW-TO-RUN.txt lib README README.md run src
ankit@ankits-MacBook-Pro benchmarksql % cd run
ankit@ankits-MacBook-Pro run % ls
funcs.sh log4j.properties props.mysql runBenchmark.sh runLoader.sh sql.firebird sql.postgres
generateGraphs.sh misc props.ora runDatabaseBuild.sh runSQL.sh sql.mysql
generateReport.sh props.fb props.pg runDatabaseDestroy.sh sql.common sql.oracle

props.mysql

db=mysql
driver=com.mysql.cj.jdbc.Driver
conn=jdbc:mysql://127.0.0.1:3306/tpcc?useSSL=false&allowPublicKeyRetrieval=true
user=tpcc
password=tpcc
warehouses=1
loadWorkers=4
terminals=1
runTxnsPerTerminal=0
runMins=10
limitTxnsPerMin=0
terminalWarehouseFixed=true
newOrderWeight=45
paymentWeight=43
orderStatusWeight=4
deliveryWeight=4
stockLevelWeight=4
resultDirectory=my_result_%tY-%tm-%td_%tH%tM%tS
ankit@ankits-MacBook-Pro run % ./runSQL.sh props.mysql sql.mysql/tableCreates.sql
# ------------------------------------------------------------
# Loading SQL file sql.mysql/tableCreates.sql
# ------------------------------------------------------------
The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.

Java was not present, so I downloaded the JDK for my mac terminal. You can replicate same if you plan to do so.

./runSQL.sh props.mysql sql.mysql/tableCreates.sql
# ------------------------------------------------------------
# Loading SQL file sql.mysql/tableCreates.sql
# ------------------------------------------------------------
Error: Could not find or load main class ExecJDBC
Caused by: java.lang.ClassNotFoundException: ExecJDBC

Again it became bottleneck since ExecJDBC was not present :

ankit@ankits-MacBook-Pro run % curl -O https://downloads.apache.org/ant/binaries/apache-ant-1.10.14-bin.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6741k 100 6741k 0 0 2719k 0 0:00:02 0:00:02 --:--:-- 2720k
ankit@ankits-MacBook-Pro run % tar -xzf apache-ant-1.10.14-bin.tar.gz
ankit@ankits-MacBook-Pro run % export ANT_HOME=$PWD/apache-ant-1.10.14
ankit@ankits-MacBook-Pro run % export PATH=$ANT_HOME/bin:$PATH
ankit@ankits-MacBook-Pro benchmarksql % ls -lrth
total 48
-rwxr-xr-x 1 ankit staff 6.2K 25 Jan 20:08 HOW-TO-RUN.txt
-rw-r--r-- 1 ankit staff 318B 25 Jan 20:08 README
-rwxr-xr-x 1 ankit staff 5.2K 25 Jan 20:08 README.md
-rwxr-xr-x 1 ankit staff 1.1K 25 Jan 20:08 build.xml
drwxr-xr-x 3 ankit staff 96B 25 Jan 20:08 doc
drwxr-xr-x 9 ankit staff 288B 25 Jan 20:08 lib
drwxr-xr-x 6 ankit staff 192B 25 Jan 20:08 src
drwxr-xr-x 24 ankit staff 768B 25 Jan 20:24 run
ankit@ankits-MacBook-Pro benchmarksql % ant
Buildfile: /Users/ankit/Documents/benchmarksql/build.xml
init:
[mkdir] Created dir: /Users/ankit/Documents/benchmarksql/build
compile:
[javac] Compiling 12 source files to /Users/ankit/Documents/benchmarksql/build
[javac] /Users/ankit/Documents/benchmarksql/src/client/jTPCC.java:487: warning: [this-escape] possible 'this' escape before subclass is fully initialized
[javac] deliveryWeightValue, stockLevelWeightValue, numWarehouses, limPerMin_Terminal, this);
[javac] ^
[javac] 1 warning
dist:
[mkdir] Created dir: /Users/ankit/Documents/benchmarksql/dist
[jar] Building jar: /Users/ankit/Documents/benchmarksql/dist/BenchmarkSQL-5.0.jar
BUILD SUCCESSFUL
Total time: 0 seconds

Time to generate tables DDL

./runSQL.sh props.mysql sql.mysql/tableCreates.sql
# ------------------------------------------------------------
# Loading SQL file sql.mysql/tableCreates.sql
# ------------------------------------------------------------
WARNING: Use of the three-letter time zone ID "IST" is deprecated and it will be removed in a future release
create table bmsql_config (
cfg_name varchar(30) primary key,
cfg_value varchar(50)
);
create table bmsql_warehouse (
w_id integer not null,
w_ytd decimal(12,2),
w_tax decimal(4,4),
w_name varchar(10),
w_street_1 varchar(20),
w_street_2 varchar(20),
w_city varchar(20),
w_state char(2),
w_zip char(9),
constraint pk_warehouse primary key (w_id)
);
create table bmsql_district (
d_w_id integer not null,
d_id integer not null,
d_ytd decimal(12,2),
d_tax decimal(4,4),
d_next_o_id integer,
d_name varchar(10),
d_street_1 varchar(20),
d_street_2 varchar(20),
d_city varchar(20),
d_state char(2),
d_zip char(9),
constraint pk_district primary key (d_w_id, d_id)
);
create table bmsql_customer (
c_w_id integer not null,
c_d_id integer not null,
c_id integer not null,
c_discount decimal(4,4),
c_credit char(2),
c_last varchar(16),
c_first varchar(16),
c_credit_lim decimal(12,2),
c_balance decimal(12,2),
c_ytd_payment decimal(12,2),
c_payment_cnt integer,
c_delivery_cnt integer,
c_street_1 varchar(20),
c_street_2 varchar(20),
c_city varchar(20),
c_state char(2),
c_zip char(9),
c_phone char(16),
c_since timestamp,
c_middle char(2),
c_data varchar(500),
constraint pk_customer primary key (c_w_id, c_d_id, c_id),
key bmsql_customer_idx1 (c_w_id, c_d_id, c_last, c_first)
);
-- create sequence bmsql_hist_id_seq;
create table bmsql_history (
hist_id integer not null auto_increment primary key,
h_c_id integer,
h_c_d_id integer,
h_c_w_id integer,
h_d_id integer,
h_w_id integer,
h_date timestamp,
h_amount decimal(6,2),
h_data varchar(24)
);
create table bmsql_new_order (
no_w_id integer not null,
no_d_id integer not null,
no_o_id integer not null,
constraint pk_new_order primary key (no_w_id, no_d_id, no_o_id)
);
create table bmsql_oorder (
o_w_id integer not null,
o_d_id integer not null,
o_id integer not null,
o_c_id integer,
o_carrier_id integer,
o_ol_cnt integer,
o_all_local integer,
o_entry_d timestamp,
constraint pk_oorder primary key (o_w_id, o_d_id, o_id),
constraint bmsql_oorder_idx1 unique key (o_w_id, o_d_id, o_c_id, o_id)
);
create table bmsql_order_line (
ol_w_id integer not null,
ol_d_id integer not null,
ol_o_id integer not null,
ol_number integer not null,
ol_i_id integer not null,
ol_delivery_d timestamp,
ol_amount decimal(6,2),
ol_supply_w_id integer,
ol_quantity integer,
ol_dist_info char(24),
constraint pk_order_line primary key (ol_w_id, ol_d_id, ol_o_id, ol_number)
);
create table bmsql_item (
i_id integer not null,
i_name varchar(24),
i_price decimal(5,2),
i_data varchar(50),
i_im_id integer,
constraint pk_item primary key (i_id)
);
create table bmsql_stock (
s_w_id integer not null,
s_i_id integer not null,
s_quantity integer,
s_ytd integer,
s_order_cnt integer,
s_remote_cnt integer,
s_data varchar(50),
s_dist_01 char(24),
s_dist_02 char(24),
s_dist_03 char(24),
s_dist_04 char(24),
s_dist_05 char(24),
s_dist_06 char(24),
s_dist_07 char(24),
s_dist_08 char(24),
s_dist_09 char(24),
s_dist_10 char(24),
constraint pk_stock primary key (s_w_id, s_i_id)
);

MySQL side thing ! I forgot to mention that we need to create a database in MySQL and a user to let benchmark connect with MySQL server.

mysql> CREATE DATABASE tpcc;
Query OK, 1 row affected (0.01 sec)
mysql> CREATE USER 'tpcc'@'%' IDENTIFIED BY 'tpcc';
Query OK, 0 rows affected (0.04 sec)
mysql> GRANT ALL ON tpcc.* TO 'tpcc'@'%';
Query OK, 0 rows affected (0.00 sec)

Its time to run benchmark. Note that I have kept default setting for MySQL, props.mysql and even number of threads ( terminal ) has been kept as 1 only

ankit@ankits-MacBook-Pro run % ./runBenchmark.sh props.mysql
21:34:25,900 [main] INFO jTPCC : Term-00,
21:34:25,902 [main] INFO jTPCC : Term-00, +-------------------------------------------------------------+
21:34:25,902 [main] INFO jTPCC : Term-00, BenchmarkSQL v5.0
21:34:25,902 [main] INFO jTPCC : Term-00, +-------------------------------------------------------------+
21:34:25,902 [main] INFO jTPCC : Term-00, (c) 2103, Raul Barbosa
21:34:25,902 [main] INFO jTPCC : Term-00, (c) 2104-2116, Denis Lussier
21:34:25,902 [main] INFO jTPCC : Term-00, (c) 2116, Jan Wieck
21:34:25,902 [main] INFO jTPCC : Term-00, +-------------------------------------------------------------+
21:34:25,902 [main] INFO jTPCC : Term-00,
21:34:25,903 [main] INFO jTPCC : Term-00, db=mysql
21:34:25,903 [main] INFO jTPCC : Term-00, driver=com.mysql.cj.jdbc.Driver
21:34:25,903 [main] INFO jTPCC : Term-00, conn=jdbc:mysql://127.0.0.1:3306/tpcc?useSSL=false&allowPublicKeyRetrieval=true
21:34:25,903 [main] INFO jTPCC : Term-00, user=tpcc
21:34:25,903 [main] INFO jTPCC : Term-00,
21:34:25,904 [main] INFO jTPCC : Term-00, warehouses=1
21:34:25,904 [main] INFO jTPCC : Term-00, terminals=1
21:34:25,904 [main] INFO jTPCC : Term-00, runMins=10
21:34:25,904 [main] INFO jTPCC : Term-00, limitTxnsPerMin=0
21:34:25,904 [main] INFO jTPCC : Term-00, terminalWarehouseFixed=true
21:34:25,904 [main] INFO jTPCC : Term-00,
21:34:25,904 [main] INFO jTPCC : Term-00, newOrderWeight=45
21:34:25,904 [main] INFO jTPCC : Term-00, paymentWeight=43
21:34:25,904 [main] INFO jTPCC : Term-00, orderStatusWeight=4
21:34:25,904 [main] INFO jTPCC : Term-00, deliveryWeight=4
21:34:25,904 [main] INFO jTPCC : Term-00, stockLevelWeight=4
21:34:25,904 [main] INFO jTPCC : Term-00,
21:34:25,904 [main] INFO jTPCC : Term-00, resultDirectory=my_result_%tY-%tm-%td_%tH%tM%tS
21:34:25,904 [main] INFO jTPCC : Term-00, osCollectorScript=null
21:34:25,904 [main] INFO jTPCC : Term-00,
21:34:25,912 [main] INFO jTPCC : Term-00, copied props.mysql to my_result_2126-01-25_213425/run.properties
21:34:25,913 [main] INFO jTPCC : Term-00, created my_result_2126-01-25_213425/data/runInfo.csv for runID 1
21:34:25,913 [main] INFO jTPCC : Term-00, writing per transaction results to my_result_2126-01-25_213425/data/result.csv
21:34:25,913 [main] INFO jTPCC : Term-00,
WARNING: Use of the three-letter time zone ID "IST" is deprecated and it will be removed in a future release
21:34:26,029 [main] INFO jTPCC : Term-00, C value for C_LAST during load: 217
21:34:26,029 [main] INFO jTPCC : Term-00, C value for C_LAST this run: 129
21:34:26,029 [main] INFO jTPCC : Term-00,
WARNING: Use of the three-letter time zone ID "IST" is deprecated and it will be removed in a future release
Term-00, Running Average tpmTOTAL: 24729.81 Current tpmTOTAL: 1633860 Memory Usage: 107MB / 258MB
21:44:26,051 [Thread-0] INFO jTPCC : Term-00,
21:44:26,054 [Thread-0] INFO jTPCC : Term-00,
21:44:26,055 [Thread-0] INFO jTPCC : Term-00, Measured tpmC (NewOrders) = 11128.87
21:44:26,055 [Thread-0] INFO jTPCC : Term-00, Measured tpmTOTAL = 24729.4
21:44:26,055 [Thread-0] INFO jTPCC : Term-00, Session Start = 2126-01-25 21:34:26
21:44:26,055 [Thread-0] INFO jTPCC : Term-00, Session End = 2126-01-25 21:44:26
21:44:26,056 [Thread-0] INFO jTPCC : Term-00, Transaction Count = 247298
21:44:26,056 [Thread-0] INFO jTPCC : executeTime[Payment]=74933
21:44:26,056 [Thread-0] INFO jTPCC : executeTime[Order-Status]=4676
21:44:26,056 [Thread-0] INFO jTPCC : executeTime[Delivery]=43428
21:44:26,056 [Thread-0] INFO jTPCC : executeTime[Stock-Level]=256644
21:44:26,056 [Thread-0] INFO jTPCC : executeTime[New-Order]=221100

MySQL performance while this test was running

mysql> SHOW PROCESSLIST;
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 2428165 | Waiting on empty queue | NULL |
| 24 | root | localhost | tpcc | Query | 0 | init | SHOW PROCESSLIST |
| 31 | tpcc | localhost:60305 | tpcc | Sleep | 89 | | NULL |
| 32 | tpcc | localhost:60306 | tpcc | Query | 0 | executing | SELECT count(*) AS low_stock FROM ( SELECT s_w_id, s_i_id, s_quantity FROM bmsql_stock |
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
4 rows in set, 1 warning (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Threads_running';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| Threads_running | 3 |
+-----------------+-------+
1 row in set (0.02 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Com_commit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_commit | 48701 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Com_rollback';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_rollback | 4391 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| Innodb_buffer_pool_reads | 2416 |
+--------------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_time';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Innodb_row_lock_time | 0 |
+----------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_time';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Innodb_row_lock_time | 0 |
+----------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW PROCESSLIST;
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 2428175 | Waiting on empty queue | NULL |
| 24 | root | localhost | tpcc | Query | 0 | init | SHOW PROCESSLIST |
| 31 | tpcc | localhost:60305 | tpcc | Sleep | 99 | | NULL |
| 32 | tpcc | localhost:60306 | tpcc | Query | 0 | executing | SELECT count(*) AS low_stock FROM ( SELECT s_w_id, s_i_id, s_quantity FROM bmsql_stock |
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
4 rows in set, 1 warning (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Threads_running';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| Threads_running | 3 |
+-----------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Com_commit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_commit | 53318 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Com_rollback';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_rollback | 4820 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| Innodb_buffer_pool_reads | 2640 |
+--------------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_time';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Innodb_row_lock_time | 0 |
+----------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW PROCESSLIST;
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 2428180 | Waiting on empty queue | NULL |
| 24 | root | localhost | tpcc | Query | 0 | init | SHOW PROCESSLIST |
| 31 | tpcc | localhost:60305 | tpcc | Sleep | 104 | | NULL |
| 32 | tpcc | localhost:60306 | tpcc | Query | 0 | executing | SELECT count(*) AS low_stock FROM ( SELECT s_w_id, s_i_id, s_quantity FROM bmsql_stock |
+----+-----------------+-----------------+------+---------+---------+------------------------+------------------------------------------------------------------------------------------------------+
4 rows in set, 1 warning (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Threads_running';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| Threads_running | 3 |
+-----------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Com_commit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_commit | 56185 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Com_rollback';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_rollback | 5071 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| Innodb_buffer_pool_reads | 2787 |
+--------------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_time';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Innodb_row_lock_time | 0 |
+----------------------+-------

Conclusion –

This is purely my handson testing on 8.0.44 and no where it conveys the message about MySQL performance, rather user can further scale it by playing around terminals and warehouse ( basically threads and number of rows)