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
-
What is a transaction and what is it used for?
-
Several characteristics of transactions
-
Detailed explanation of common transaction operation instructions
-
Detailed explanation of transaction isolation level
-
Detailed explanation of dirty read, non-repeatable read, repeatable read and phantom read
-
Demonstrate the phenomena produced by various isolation levels
-
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:
-
The operation is successful: Account A decreases by 100; Account B increases by 100
-
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 autocommit
controlled 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 autocommit
whether 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)
autocommit
ON 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 autocommit
it 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
test1
shows 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. savepoin
This can be achieved using :
test1
Clear 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 savepoint
effect, 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.
savepoint
Need to berollback to sp1
used together to roll back operations betweensp1
save 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:
-
Read uncommitted: READ-UNCOMMITTED
-
Read submitted: READ-COMMITTED
-
Repeatable read: REPEATABLE-READ
-
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-READ
occur 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执行完毕。
可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。
关于隔离级别的选择
-
需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
-
隔离级别越高,并发性也低,比如最高级别
SERIALIZABLE
会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。 -
具体选择哪种需要结合具体的业务来选择。
-
读已提交(READ-COMMITTED)通常用的比较多。
总结
-
理解事务的4个特性:原子性、一致性、隔离性、持久性
-
掌握事务操作常见命令的介绍
-
set autocommit
可以设置是否开启自动提交事务 -
start transaction:开启事务
-
start transaction read only:开启只读事物
-
commit:提交事务
-
rollback:回滚事务
-
savepoint:设置保存点
-
rollback to 保存点:可以回滚到某个保存点
-
掌握4种隔离级别及了解其特点
-
了解脏读、不可重复读、幻读