Fun with Mysql Series - Part 14: Detailed explanation of transactions

Environment: mysql5.7.25, demonstrated in cmd command.

Database transactions are often used during the development process, so this chapter is very important.

Contents of this article

  1. What is a transaction and what is it used for?

  2. Several characteristics of transactions

  3. Detailed explanation of common transaction operation instructions

  4. Detailed explanation of transaction isolation level

  5. Detailed explanation of dirty read, non-repeatable read, repeatable read and phantom read

  6. Demonstrate the phenomena produced by various isolation levels

  7. Regarding the choice of isolation level

What is a transaction?

A transaction in a database refers to the execution of a batch of operations on the database. These operations will eventually either all succeed or fail, and there will be no partial success.

for example

For example, if user A transfers 100 to user B, the process is as follows:

1.从A账户扣100
2.给B账户加100

If supported by transactions, there are only two results in the end:

  1. The operation is successful: Account A decreases by 100; Account B increases by 100

  2. Operation failed: neither account A nor B has changed.

If there is no transaction support, an error may occur: Account A is reduced by 100, and the system hangs up. As a result, account B does not add 100, and account A loses 100 out of thin air.

Several characteristics of transactions (ACID)

Atomicity

The entire process of a transaction is like an atomic operation. In the end, either all succeed or all fail. This atomicity is seen from the final result. From the final result, this process is indivisible.

Consistency

Before the transaction starts, during execution, and after the execution, at these time points, when multiple people observe the data of the transaction operation, the data they see is consistent. For example, during the transaction operation, connection A sees 100, then When B also looked at it at this time, it was also 100. It would not be said that the data AB saw was different. The data they saw at a certain point in time was consistent.

Isolation

The execution of a transaction cannot be interfered with by other transactions. That is, the operations and data used within a transaction are isolated from other concurrent transactions, and transactions executed concurrently cannot interfere with each other.

Durability

Once a transaction is committed, its changes to the data in the database should be permanent. When the transaction is committed, the data will be persisted to the hard disk, and the modification will be permanent.

Transaction operations in Mysql

Transactions in MySQL are implicit transactions by default. When insert, update, and delete operations are performed, the database automatically starts the transaction, commits or rolls back the transaction.

Whether to enable implicit transactions is autocommitcontrolled by variables.

So transactions are divided into implicit transactions and explicit transactions .

implicit transaction

Transactions are automatically opened, submitted or rolled back, such as insert, update, delete statements. The opening, submission or rollback of transactions are automatically controlled internally by MySQL.

Check autocommitwhether the variable has automatic submission turned on

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)

autocommitON means automatic submission is turned on.

explicit transaction

Transactions need to be started, committed or rolled back manually, which is controlled by the developer.

2 ways to manually control transactions:

Way 1:

grammar:

//设置不自动提交事务
set autocommit=0;
//执行事务操作
commit|rollback;

Example 1: Submit transaction operation, as follows:

mysql> create table test1 (a int);
Query OK, 0 rows affected (0.01 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values(1);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

Example 2: Rollback transaction operation, as follows:

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values(2);
Query OK, 1 row affected (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

You can see that the above data has been rolled back.

We restore autocommitit back:

mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)

Way 2:

grammar:

start transaction;//开启事务
//执行事务操作
commit|rollback;

Example 1: Submit transaction operation, as follows:

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values (2);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test1 values (3);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)

Two pieces of data were successfully inserted above.

Example 2: Rollback transaction operation, as follows:

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test1;
Query OK, 3 rows affected (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)

The data we deleted in the above transaction test1shows that 3 rows were deleted, and the transaction was finally rolled back.

savepoint keyword

We performed a large number of operations during the transaction. Maybe we just want to roll back some data. How to do this?

We can divide a large number of operations into several parts and then specify a certain part to be rolled back. savepoinThis can be achieved using :

test1Clear table data first :

mysql> delete from test1;
Query OK, 3 rows affected (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

Demonstration savepointeffect, watch carefully:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)

mysql> savepoint part1;//设置一个保存点
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values (2);
Query OK, 1 row affected (0.00 sec)

mysql> rollback to part1;//将savepint = part1的语句到当前语句之间所有的操作回滚
Query OK, 0 rows affected (0.00 sec)

mysql> commit;//提交事务
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

As can be seen from the above, 2 insertion operations were performed, and only 1 piece of data was inserted in the end.

savepointNeed to be rollback to sp1used together to roll back operations between sp1save points.rollback to

read-only transaction

It means that some read-only operations, such as queries, are performed in the transaction, but insert, update, and delete operations will not be performed. There may be some performance optimizations for read-only transactions within the database.

Usage is as follows:

start transaction read only;

Example:

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction read only;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    1 |
+------+
2 rows in set (0.00 sec)

mysql> delete from test1;
ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    1 |
+------+
2 rows in set (0.00 sec)

Executing delete in a read-only transaction will report an error.

Some issues in the transaction

These issues are primarily based on the visibility of data across multiple transactions.

dirty read

During execution, one transaction reads data that other transactions have not yet committed.
This is relatively easy to understand.

read committed

Literally we can understand that during a transaction operation, data that has been submitted by other transactions can be read.

Each read operation in a transaction reads the latest data submitted by other transactions in the database (equivalent to the current read)

repeatable read

No matter how many times a read operation is performed in a transaction operation, the read result will be the same.

Phantom reading

Dirty read, non-repeatable read, repeatable read, phantom read, the most difficult to understand is phantom read

Take mysql as an example:

Phantom reads will only occur in repeatable read mode and will not occur in other isolation levels.

Examples of phantom reading phenomena:

In repeatable read mode, for example, if there is a user table with mobile phone number as the primary key, there are two things that perform the following operations:

The operation of transaction A is as follows:
1. Open the transaction
2. Query the record with the number X, which does not exist
3. Insert the data with the
number It is found that it still does not exist (because it is a repeatable read, the read record X still does not exist)

Transaction B operation: A record of

The above operation is like an hallucination for A. It is clear that the query X (the second and fourth steps in A) does not exist, but it cannot be inserted successfully.

Phantom reading can be understood this way: the subsequent operation in the transaction (inserting number X) requires support from the above read operation (querying the record of number Like an illusion.

If you still can't understand it, continue reading below for a detailed demonstration later.

transaction isolation level

When multiple transactions are in progress at the same time, how to ensure the correctness of the data in the current transaction? For example, when two things A and B are in progress at the same time, can A see the data submitted by B or the data unsubmitted by B? This requires It depends on the isolation level of the transaction to ensure that the effects produced by different isolation levels are different.

The transaction isolation level mainly solves the above problems of data visibility and data correctness between multiple transactions.

There are 4 types of isolation levels:

  1. Read uncommitted: READ-UNCOMMITTED

  2. Read submitted: READ-COMMITTED

  3. Repeatable read: REPEATABLE-READ

  4. SERIAL: SERIALIZABLE

The isolation levels in the above four are getting stronger and stronger, which will lead to the concurrency of the database getting lower and lower.

Check the isolation level

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)

Isolation level settings

In 2 steps, modify the file and restart mysql, as follows:

Modify the my.init file in mysql, and set the isolation level to: READ-UNCOMMITTED, as follows:

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=READ-UNCOMMITTED

Open the cmd window as an administrator and restart mysql, as follows:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

Problems that occur in various isolation levels

isolation level dirty read non-repeatable read Phantom reading
READ-UNCOMMITTED have have none
READ-COMMITTED none have none
REPEATABLE-READ none none have
SERIALIZABLE none none none

There are some differences between the table and the Internet, mainly in the area of ​​phantom reading. Phantom reading will only appear in the repeatable reading level and does not exist in other levels.

Let's demonstrate the visibility issues in various isolation levels by opening two windows, called windows A and B, and logging in to mysql in both windows.

READ-UNCOMMITTED: read uncommitted

Set the isolation level to READ-UNCOMMITTED:

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=READ-UNCOMMITTED

restart mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

Check the isolation level:

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)

First clear the test1 table data:

delete from test1;
select * from test1;

Perform the following operations in 2 windows in chronological order:

time window A Window B
T1 start transaction;
T2 select * from test1;
T3 start transaction;
T4 insert into test1 values (1);
T5 select * from test1;
T6 select * from test1;
T7 commit;
Q8 commit;

The A window is as follows:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

B window is as follows:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

take a look:

T2-A: No data, T6-A: There is data. B has not submitted at time T6. At this time, A has seen the data inserted by B, indicating that a dirty read has occurred .

T2-A: No data, T6-A: There is data. The query results are different, indicating that repeatable reading is not possible .

Conclusion: When reading is uncommitted, you can read uncommitted data from other transactions. The results of multiple reads are different, and dirty reads and non-repeatable reads occur.

READ-COMMITTED: read committed

Set the isolation level toREAD-COMMITTED

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=READ-COMMITTED

restart mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

Check the isolation level:

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)

First clear the test1 table data:

delete from test1;
select * from test1;

Perform the following operations in 2 windows in chronological order:

time window A Window B
T1 start transaction;
T2 select * from test1;
T3 start transaction;
T4 insert into test1 values (1);
T5 select * from test1;
T6 select * from test1;
T7 commit;
Q8 select * from test1;
T9 commit;

The A window is as follows:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

B window is as follows:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

take a look:

T5-B: There is data, T6-A window: No data, A cannot see B's data, indicating that there is no dirty read .

T6-A window: no data, T8-A: saw the data inserted by B, and B has submitted it at this time. A saw the data submitted by B, which means that the submitted data can be read .

T2-A, T6-A: No data, T8-A: There is data. The results of multiple readings are different, indicating that repeatable reading is not possible .

Conclusion: In the case of Read Committed, the data that has not been submitted by other transactions cannot be read, but the data that has been submitted by other transactions can be read. The results of multiple reads are different. Dirty reads do not occur, but Read Committed, Cannot be read repeatedly.

REPEATABLE-READ: repeatable read

Set the isolation level toREPEATABLE-READ

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=REPEATABLE-READ

restart mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

Check the isolation level:

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)

First clear the test1 table data:

delete from test1;
select * from test1;

Perform the following operations in 2 windows in chronological order:

time window A Window B
T1 start transaction;
T2 select * from test1;
T3 start transaction;
T4 insert into test1 values (1);
T5 select * from test1;
T6 select * from test1;
T7 commit;
Q8 select * from test1;
T9 commit;
T10 select * from test1;

The A window is as follows:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> select * from test1;
Empty set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    1 |
+------+
2 rows in set (0.00 sec)

B window is as follows:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    1 |
+------+
2 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

take a look:

T2-A and T6-A windows: no data, T5-B: data. A cannot see B's data, indicating that there is no dirty read .

T8-A: No data. B has submitted it at this time. A cannot see the data submitted by B. The results of three reads in A are the same, indicating that there is no data, indicating that the read can be repeated .

Conclusion: In the case of repeatable reading, no dirty reading occurs, no data submitted by other transactions is read, and the results of multiple reads are consistent, so the read can be repeated.

Phantom reading demonstration

Phantom reads will only REPEATABLE-READoccur at the (repeatable read) level, and you need to change the isolation level to repeatable read first.

Set the isolation level toREPEATABLE-READ

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=REPEATABLE-READ

restart mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

Check the isolation level:

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)

Prepare data:

mysql> create table t_user(id int primary key,name varchar(16) unique key);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t_user values (1,'路人甲Java'),(2,'路人甲Java');
ERROR 1062 (23000): Duplicate entry '路人甲Java' for key 'name'

mysql> select * from t_user;
Empty set (0.00 sec)

Above we created the t_user table, and added a unique constraint to name, which means that name cannot be repeated, otherwise an error will be reported.

Perform the following operations in 2 windows in chronological order:

time window A Window B
T1 start transaction;
T2 start transaction;
T3 -- Insert 路人甲Java
into t_user values ​​(1,'passer A Java');
T4 select * from t_user;
T5 -- 查看路人甲Java是否存在
select * from t_user where name='路人甲Java';
T6 commit;
T7 -- 插入路人甲Java
insert into t_user values (2,'路人甲Java');
T8 -- 查看路人甲Java是否存在
select * from t_user where name='路人甲Java';
T9 commit;

A窗口如下:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t_user where name='路人甲Java';
Empty set (0.00 sec)

mysql> insert into t_user values (2,'路人甲Java');
ERROR 1062 (23000): Duplicate entry '路人甲Java' for key 'name'
mysql> select * from t_user where name='路人甲Java';
Empty set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

B窗口如下:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t_user values (1,'路人甲Java');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t_user;
+----+---------------+
| id | name          |
+----+---------------+
|  1 | 路人甲Java    |
+----+---------------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

看一下:

A想插入数据路人甲Java,插入之前先查询了一下(T5时刻)该用户是否存在,发现不存在,然后在T7时刻执行插入,报错了,报数据已经存在了,因为T6时刻B已经插入了路人甲Java

然后A有点郁闷,刚才查的时候不存在的,然后A不相信自己的眼睛,又去查一次(T8时刻),发现路人甲Java还是不存在的。

此时A心里想:数据明明不存在啊,为什么无法插入呢?这不是懵逼了么,A觉得如同发生了幻觉一样。

SERIALIZABLE:串行

SERIALIZABLE会让并发的事务串行执行。

看效果:

将隔离级别置为SERIALIZABLE

# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=SERIALIZABLE

重启mysql:

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

查看隔离级别:

mysql> show variables like 'transaction_isolation';
+-----------------------+--------------+
| Variable_name         | Value        |
+-----------------------+--------------+
| transaction_isolation | SERIALIZABLE |
+-----------------------+--------------+
1 row in set, 1 warning (0.00 sec)

先清空test1表数据:

delete from test1;
select * from test1;

按时间顺序在2个窗口中执行下面操作:

时间 窗口A 窗口B
T1 start transaction;
T2 select * from test1;
T3 start transaction;
T4 insert into test1 values (1);
T5 select * from test1;
T6 commit;
T7 commit;

按时间顺序运行上面的命令,会发现T4-B这样会被阻塞,直到T6-A执行完毕。

可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。

关于隔离级别的选择

  1. 需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余

  2. 隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。

  3. 具体选择哪种需要结合具体的业务来选择。

  4. 读已提交(READ-COMMITTED)通常用的比较多。

总结

  1. 理解事务的4个特性:原子性、一致性、隔离性、持久性

  2. 掌握事务操作常见命令的介绍

  3. set autocommit可以设置是否开启自动提交事务

  4. start transaction:开启事务

  5. start transaction read only:开启只读事物

  6. commit:提交事务

  7. rollback:回滚事务

  8. savepoint:设置保存点

  9. rollback to 保存点:可以回滚到某个保存点

  10. 掌握4种隔离级别及了解其特点

  11. 了解脏读、不可重复读、幻读

Guess you like

Origin blog.csdn.net/weixin_46228112/article/details/132696530