目录
- 前言
- 一、读未提交(Read Uncommitted)
- 二、读已提交(Read Committed)
- 三、可重复读(Repeatable Read)
- 四、串行化(Serializable)
前言
在MySQL中,事务的隔离级别决定了一个事务可能受其他并发事务影响的程度。可重复读作为默认的隔离级别,确保了在同一个事务中,多次读取同一行数据的结果都是一致的。
-- 查看当前会话的隔离级别:
-- 对于MySQL 8.0及以上版本,使用命令
SELECT @@transaction_isolation;。
-- 对于MySQL 5.0至7.9版本,使用命令
SELECT @@tx_isolation;。
-- 查看全局的隔离级别:
-- 对于MySQL 8.0及以上版本,使用命令
SELECT @@global.transaction_isolation;。
-- 对于MySQL 5.0至7.9版本,使用命令
SELECT @@global.tx_isolation;。
要设置MySQL的隔离级别,可以使用以下命令:
-- 设置会话级别的隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局级别的隔离级别:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
MySQL的隔离级别包括以下几种:
一、读未提交(Read Uncommitted)
这个级别下,事务可以读取其他事务未提交的数据。
事务一测试sql:
-- 设置隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 查看隔离级别
SELECT @@tx_isolation;
-- 启动事务一
START TRANSACTION;
SELECT name as 更改前姓名, age as 更改前年龄 FROM student WHERE id = 1;
UPDATE student SET age = age + 1 WHERE id = 1;
COMMIT;
事务二测试sql:
-- 设置隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 查看隔离级别
SELECT @@tx_isolation;
-- 启动事务二
START TRANSACTION;
SELECT name as 姓名, age as 年龄 FROM student WHERE id = 1;
COMMIT;
可以看到事务二中可以读取事务一未提交的修改,读未提交的隔离级别可能会导致脏读、不可重复读和幻读。
二、读已提交(Read Committed)
事务一测试sql:
-- 设置隔离级别为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL Read Committed;
-- 查看隔离级别
SELECT @@tx_isolation;
-- 启动事务一
START TRANSACTION;
SELECT name as 更改前姓名, age as 更改前年龄 FROM student WHERE id = 1;
UPDATE student SET age = age + 1 WHERE id = 1;
COMMIT;
事务二测试sql:
-- 设置隔离级别为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL Read Committed;
-- 查看隔离级别
SELECT @@tx_isolation;
-- 启动事务二
START TRANSACTION;
SELECT name as 姓名, age as 年龄 FROM student WHERE id = 1;
-- 模拟此处执行其他业务后再次查询,睡眠3s
DO SLEEP(3);
SELECT name as 姓名, age as 年龄 FROM student WHERE id = 1;
COMMIT;
在这个级别下,事务只能读取到已经提交的数据。
读已提交此隔离级别,可以解决了读未提交的脏读,但是在同一事务中会出现同一sql多个结果的情况,会出现不可重复读和幻读。
三、可重复读(Repeatable Read)
这是MySQL默认的隔离级别,可以防止脏读和不可重复读,但在某些情况下仍可能发生幻读
事务一sql:
-- 设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL Repeatable Read;
-- 查看隔离级别
SELECT @@tx_isolation;
-- 启动事务一
START TRANSACTION;
SELECT name as 更改前姓名, age as 更改前年龄 FROM student WHERE id = 1;
UPDATE student SET age = age + 1 WHERE id = 1;
COMMIT;
事务二sql:
-- 设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL Repeatable Read;
-- 查看隔离级别
SELECT @@tx_isolation;
-- 启动事务二
START TRANSACTION;
SELECT name as 姓名, age as 年龄 FROM student WHERE id = 1;
-- 模拟此处执行其他业务后再次查询,睡眠3s
DO SLEEP(3);
SELECT name as 姓名, age as 年龄 FROM student WHERE id = 1;
COMMIT;
但会导致幻读。如表数据为
幻读(很魔幻),就是事务 A 根据条件查询得到了 3 条数据,但此时事务 B 删除或者增加了 1 条,但可重复读的隔离级别导致了事务 A 再次进行查询的时候真实的数据集已经发生了变化,但是A却查询不出来这种变化,因此产生了幻读。
就会导致B事务增加了id为4的数据,但A事务读取数据时id只到3,插入id为4的记录就会报错。
四、串行化(Serializable)
这是最高的隔离级别。
事务一测试sql:
-- 设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL Serializable;
-- 启动事务一
START TRANSACTION;
INSERT INTO student (name, age) VALUES ('John', 20);
INSERT INTO student (name, age) VALUES ('John1', 21);
SELECT * FROM student WHERE id BETWEEN 1 and 100;
COMMIT;
事务二测试sql:
-- 设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL Serializable;
-- 启动事务二
START TRANSACTION;
SELECT * FROM student WHERE age BETWEEN 1 and 100;
-- 模拟此处执行其他业务后再次查询,睡眠3s
DO SLEEP(5);
SELECT * FROM student WHERE age BETWEEN 1 and 100;
COMMIT;
通过完全串行化执行事务来避免脏读、不可重复读和幻读,但这可能会显著降低系统的并发性能。
实际生产不会把隔离级别设置为可串行化,默认可重复读隔离级别在MySQL中可以提供一定程度的保护,以防止数据的不一致性,但它并不能完全防止幻读现象的发生。在设计数据库应用时,需要根据具体的业务需求和数据一致性要求来选择合适的隔离级别和锁策略。