mysql的事务隔离级别用于控制多个事务并发执行时,一个事务对另一个事务的数据可见性,避免并发操作引发的数据不一致问题。mysql默认支持读未提交、读已提交、可重复读、串行化四种隔离级别,其中可重复读是innodb引擎的默认隔离级别。

mysql支持的四种事务隔离级别
mysql的四种事务隔离级别按照隔离强度从低到高排列,对应的并发性能和数据一致性保障也有所不同:
- 读未提交(READ UNCOMMITTED):一个事务可以读取另一个未提交事务修改的数据,会出现脏读、不可重复读、幻读问题,实际生产中很少使用。
- 读已提交(READ COMMITTED):一个事务只能读取已经提交的事务修改的数据,解决了脏读问题,但会出现不可重复读和幻读。
- 可重复读(REPEATABLE READ):同一个事务中多次读取同一范围的数据,结果保持一致,解决了脏读和不可重复读,innodb引擎通过间隙锁解决了大部分幻读场景。
- 串行化(SERIALIZABLE):事务串行执行,完全隔离,能解决所有并发问题,但并发性能最低,适合对数据一致性要求极高的场景。
查看当前事务隔离级别
在调整隔离级别之前,可以先查看当前mysql实例、当前会话的事务隔离级别,查看方式如下:
查看全局隔离级别
全局隔离级别对所有新建立的会话生效,已存在的会话不受影响,查看语句如下:
-- 查看全局事务隔离级别,两种方式等效 SELECT @@GLOBAL.transaction_isolation; SHOW GLOBAL VARIABLES LIKE 'transaction_isolation';
查看会话隔离级别
会话隔离级别只对当前连接生效,查看语句如下:
-- 查看当前会话事务隔离级别,两种方式等效 SELECT @@SESSION.transaction_isolation; SHOW SESSION VARIABLES LIKE 'transaction_isolation';
控制事务隔离级别的三种方式
mysql支持三种粒度控制事务隔离级别,分别是全局配置、会话级配置、单事务级配置,作用范围和生效时机各不相同。
全局配置(永久生效)
如果要让事务隔离级别对所有新连接永久生效,需要修改mysql的配置文件,步骤如下:
- 打开mysql的配置文件,linux系统一般是
/etc/my.cnf,windows系统一般是my.ini。 - 在
[mysqld]配置段下添加如下配置:
[mysqld] # 设置全局默认事务隔离级别为可重复读,可选值:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE transaction_isolation=REPEATABLE-READ
修改完成后重启mysql服务,新建立的连接都会使用配置的隔离级别。
会话级配置(当前连接生效)
如果只需要调整当前会话的隔离级别,不需要修改配置文件,执行如下SQL即可,设置后当前连接的所有后续事务都会使用该隔离级别:
-- 设置当前会话的事务隔离级别为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 也可以用简写形式 SET SESSION TRANSACTION_ISOLATION = 'READ-COMMITTED';
单事务级配置(单个事务生效)
如果只需要为某一个事务单独设置隔离级别,可以在开启事务时指定,该设置只对当前这一个事务生效,不影响其他事务:
-- 开启一个隔离级别为串行化的事务 START TRANSACTION WITH CONSISTENT SNAPSHOT ISOLATION LEVEL SERIALIZABLE; -- 执行事务内的SQL操作 SELECT * FROM user_table WHERE id = 1; UPDATE user_table SET name = 'test' WHERE id = 1; -- 提交事务 COMMIT;
不同隔离级别的效果演示
下面通过一个简单的示例演示读已提交和可重复读两种常用隔离级别的区别,首先创建测试表并插入初始数据:
-- 创建测试表
CREATE TABLE test_isolation (
id INT PRIMARY KEY AUTO_INCREMENT,
num INT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入初始数据
INSERT INTO test_isolation (num) VALUES (100);
读已提交级别演示
打开两个mysql连接,都设置为读已提交隔离级别:
-- 两个连接都执行,设置会话隔离级别为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
连接1执行事务,不提交:
-- 连接1执行 START TRANSACTION; UPDATE test_isolation SET num = 200 WHERE id = 1; -- 此时暂不提交事务
连接2查询数据,结果为100,因为连接1的事务未提交:
-- 连接2执行 START TRANSACTION; SELECT num FROM test_isolation WHERE id = 1; -- 结果为100 COMMIT;
连接1提交事务:
-- 连接1执行 COMMIT;
连接2再次查询,结果为200,因为连接1的事务已经提交,读已提交级别可以读取到已提交的数据:
-- 连接2执行 START TRANSACTION; SELECT num FROM test_isolation WHERE id = 1; -- 结果为200 COMMIT;
可重复读级别演示
同样打开两个连接,设置为可重复读隔离级别:
-- 两个连接都执行,设置会话隔离级别为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
连接1执行事务,不提交:
-- 连接1执行 START TRANSACTION; UPDATE test_isolation SET num = 300 WHERE id = 1; -- 暂不提交
连接2开启事务并查询,结果为200(上一个示例提交后的结果):
-- 连接2执行 START TRANSACTION; SELECT num FROM test_isolation WHERE id = 1; -- 结果为200
连接1提交事务:
-- 连接1执行 COMMIT;
连接2在同一个事务中再次查询,结果仍然为200,符合可重复读的特性,同一个事务内多次查询结果一致:
-- 连接2执行(不提交当前事务) SELECT num FROM test_isolation WHERE id = 1; -- 结果还是200 COMMIT;
连接2提交事务后,再次开启新事务查询,结果才会变为300:
-- 连接2执行 START TRANSACTION; SELECT num FROM test_isolation WHERE id = 1; -- 结果为300 COMMIT;
隔离级别选择建议
实际业务中可以根据场景选择合适的隔离级别:
- 如果是普通业务场景,没有特殊的并发要求,使用默认的可重复读即可,innodb引擎已经做了很多优化,能兼顾性能和数据一致性。
- 如果业务需要读取其他事务已提交的最新数据,比如报表统计类场景,可以选择读已提交。
- 如果对数据一致性要求极高,比如金融核心交易场景,可以选择串行化,但要接受较低的并发性能。
- 尽量避免使用读未提交级别,容易出现脏读问题导致数据异常。