MySQL默认开启了AUTOCOMMIT,关闭自动提交。
1 | mysql> set autocommit = 0; |
并发一致性问题
- 丢失修改
- 脏读(dirty read)
- 不可重复读(unrepeatable read)
- 幻影读(phantom read)
事务隔离级别
MySQL有四种事务隔离级别
- 未提交读(READ-UNCOMMITTED):
- 提交读(READ-COMMITTED):
- 可重复读(REPEATABLE-READ):
- 可串行化(SERIALIZABLE):
脏读 | 不可重复 | 幻影读 | |
---|---|---|---|
未提交读 | × | × | × |
提交读 | ✔ | × | × |
可重复读 | ✔ | ✔ | × |
可串行化 | ✔ | ✔ | ✔ |
MySQL默认的隔离级别为可重复读(REPEATABLE READ)。
1 | mysql> select @@global.transaction_isolation, @@transaction_isolation; |
修改事务的隔离级别:
1 | set [session | global] transaction isolation level 隔离级别; |
实战:并发一致性问题
数据准备
首先,先建立一个数据库;
1 | create database `test`; |
建立表
1 | create table `account` ( |
插入数据
1 | insert into `account` (name, balance) values ('user1', 120); |
关闭自动提交
打开两个命令行界面,关闭自动提交
测试脏读
设置会话的事务隔离级别为未提交读(read uncommitted)。
1 | mysql> select @@transaction_isolation; |
1.事务T1执行如下操作
1 | mysql> start transaction; |
2.事务T2执行如下操作
1 | mysql> start transaction; |
此时,余额为140元。
3.事务T1回滚
1 | mysql> rollback; |
4.事务T2再次读取该条数据。
1 | mysql> select * from account where id = 1; |
此时,余额为120元,发生了脏读。
测试不可重复读
将两个命令行界面的事务隔离级别设置为提交读(read committed)。
1 | mysql> set session transaction isolation level read committed; |
1.事务T2读取id为1的数据:余额为120元。
1 | mysql> start transaction; |
2.事务T1更改id为1的余额:更改完成后,余额为140元。
1 | mysql> start transaction; |
3.事务T2再次读取数据:余额为140元,出现了不可重复读。
1 | mysql> select * from account where id = 1; |
测试幻影读
将两个命令行界面的事务隔离级别设置为可重复读(repeatable read)。
1 | mysql> set session transaction isolation level repeatable read; |
1.事务T1读取余额在100-150之间的所有数据
1 | mysql> start transaction; |
2.事务T2在表中插入一条数据,余额为150元。
1 | mysql> start transaction; |
3.事务T1先执行一条更新语句,然后再次读取100-150之间的所有数据。
1 | mysql> update account set name = concat(name, '1'); |
再次读取时,出现了4条数据,发生了幻影读。
注意:事务T1的更新操作必须改变事务T2插入的数据,否则,不会触发幻影读。