MySQL InnoDB transaction isolation level

1. Database transactions and their isolation

In a database management system, a transaction is a single unit of logic or work, sometimes made up of multiple operations. Any logical calculation done in a consistent mode in a database is known as a transaction. One example is a transfer from one bank account to another: the complete transaction requires subtracting the amount to be transferred from one account and adding that same amount to the other.

A database transaction, by definition, must be atomic (it must either complete in its entirety or have no effect whatsoever), consistent (it must conform to existing constraints in the database), isolated (it must not affect other transactions) and durable (it must get written to persistent storage).[1] Database practitioners often refer to these properties of database transactions using the acronym ACID.

The above is Wikipedia’s description of database transactions, which can be briefly described as a logical unit composed of multiple operations, with atomicity (all operations succeed or all failures), consistency (consistency constraints), and isolation (interactions Isolation) and persistence (success/failure both need to be persistently written).

This article only discusses isolation.

Complete isolation between transactions is the ideal state of database products. It can only be achieved when transactions are executed serially. However, most system reads and writes comply with the 28 principle, and 20% of write requests and 80% of read requests. All requests are executed serially, and the performance of the database is very poor. In order to take into account performance and isolation, 4 isolation levels are derived, Read Uncommited, Read Commited ), Repeatable Read, Serializable, four levels of isolation increase, and concurrency decrease, mainstream database products can use different isolation levels for different business scenarios, SQL server defaults to RC , MySQL innoDB uses RR by default.

In the following, I will briefly describe the isolation level and the underlying implementation principles of MySQL (innoDB engine) as an example.

2. Preparation

1. Database

MySQL 5.7+

2. Common commands

SELECT @@global.tx_isolation;-- 查看全局隔离级别
SELECT @@tx_isolation;-- 查看当前会话数据库隔离级别

SET global transaction isolation level read committed; -- 设置全局隔离级别
SET session transaction isolation level read committed; -- 设置当前会话隔离级别

BEGIN;-- 开启事务
COMMIT;-- 提交事务
ROLLBACK;-- 回滚事务

SET AUTOCOMMIT=0;//关闭事务自动提交
SET AUTOCOMMIT=1;//开启事务自动提交

3. Initialize the data

DROP TABLE IF EXISTS `t`;
CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `value` varchar(10) NOT NULL COMMENT 'value',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. Read uncommitted

Set the global isolation level

SET global transaction isolation level read uncommitted;
SESSION1 SESSION2
BEGIN;  
SELECT * FROM t;  
 

BEGIN;

  INSERT INTO t(id, value) VALUES(1,'a');
SELECT * FROM t;  
  ROLLBACK;
SELECT * FROM t;  
COMMIT;  

According to the operation process in the above table, you can see that the uncommitted data in other transactions can be read in SESSION1. This phenomenon is called dirty read. The official interpretation of MySQL is as follows: 

An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed. It is only possible with the isolation level known as read uncommitted.

 4. Read submitted

SET global transaction isolation level read committed;
SESSION1 SESSION2
BEGIN;  
SELECT * FROM t;  
 

BEGIN;

  INSERT INTO t(id, value) VALUES(1,'a');
SELECT * FROM t;  
  COMMIT;
SELECT * FROM t;  
COMMIT;  

 According to the operation process in the above table, you can see that before the transaction in SESSION2 is committed, the data read in SESSION1 is always consistent, but in the transaction of SESSION1, the data read twice is inconsistent. This phenomenon is called non-repeatable reading. The official interpretation of MySQL is as follows:

The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (changed by another transaction committing in the meantime).

5. Repeatable reading 

SET global transaction isolation level repeatable read;
SESSION1 SESSION2
BEGIN;  
SELECT * FROM t;  
 

BEGIN;

  INSERT INTO t(id, value) VALUES(1,'a');
SELECT * FROM t;  
  COMMIT;
SELECT * FROM t;  
COMMIT;  

According to the operations in the above table, the data read by the transaction in SESSION1 at any time is consistent, avoiding the previous non-repeatable reading problem, and the two transactions seem to be in peace, which seems to meet our expectations for transaction characteristics (ACID), but is that really the case?

SESSION1 SESSION2
BEGIN;  
SELECT * FROM t;  
 

BEGIN;

  INSERT INTO t(id, value) VALUES(1,'a');
SELECT * FROM t;  
  COMMIT;
INSERT INTO t(id, value) VALUES(1,'a');  
SELECT * FROM t;  
COMMIT;  

We added a write operation after the second query, and the following error will be reported (adjust the order of the insert statement of SESSION1 and the commit statement of SESSION2, you can see the different effects, which will be explained in the subsequent article on locks)

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY' 

There is no data when querying, and the primary key duplicate error is reported when writing. This kind of phenomenon is called phantom read. The official interpretation of MySQL is

A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.

 大意为在一个事务的两次查询之间有另外一个事务提交了新增或修改(包括删除)某一行的操作,这一行匹配当前事务的where条件,就会发生幻读。幻读在select操作下是感知不到的,受益于MySQL的mvcc机制,但严格来说幻读也发生了,什么都没读到,但是在修改的时候发生错误。

6.可串行化

SET global transaction isolation level serializable;
SESSION1 SESSION2
BEGIN;  
SELECT * FROM t;  
 

BEGIN;

  INSERT INTO t(id, value) VALUES(1,'a');
SELECT * FROM t;  
  COMMIT;
SELECT * FROM t;  
COMMIT;  

根据上表中的操作,不同的事务在同时开启的情况下,对一张表的操作读/写并发时会根据先后顺序串行化执行,InnoDB的实现是给所有的select查询添加FOR SHARE MODE,是增加了读意向锁,可以提高读的并发性,这种优化仅在autocommit=0时有效,官方文档:

This level is like REPEATABLE READ, but InnoDB implicitly converts all plain SELECT statements to SELECT ... FOR SHARE if autocommit is disabled. If autocommit is enabled, the SELECT is its own transaction.

7.总结

 MySQL InnoDB存储引擎提供了四种隔离级别:读未提交(RU) 读已提交(RC) 可重复读(RR) 可串行化(S) 解决的问题以及存在的问题如下表。

  脏读 不可重复读 幻读
读未提交 ✔️ ✔️ ✔️
读已提交 × ✔️ ✔️
可重复读 × × ✔️
可串行化 × × ×

系统可以根据自身对并发性能与数据一致性的容忍程度选择相应的隔离级别,然后通过一定的手段(如加锁)来弥补一致性上的缺失。

 

Guess you like

Origin blog.csdn.net/zibaihe007/article/details/111143310