事务就是要保证一组数据库操作,要么全部成功,要么全部失败。
在MySql中,事务支持是在引擎层实现的,但是只有InnoDB支持事务,MyISAM是不支持的。
事务具有原子性,一致性,隔离性,持久性,这里我们要谈的就是隔离性。
MySql标准的事务隔离级别包括:
读未提交(read uncomminted):一个事务还没有提交时,它做的变更就能被被的事务看到。
读提交(read commited):一个事务提交后,它做的变更才会被其他事务看到。
可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
串行化(serializable):对于同一行记录,“写”会加“写锁”,“读”会加“读锁”,出现读写锁冲突的时候,后访问的食物必须等待前一个事务执行完成,才能继续执行。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 是 | 是 | 是 |
不可重复读 | 否 | 是 | 是 |
可重复读 | 否 | 否 | 是 |
串行化 | 否 | 否 | 否 |
1.查看MySql默认的隔离级别,可见MySql默认可重复读。从上图我们可以看到,不管是什么隔离级别,都会存在一些问题,并且隔离的越严实,执行效率就会越低。比如:串行化,所以很多时候我们只是在效率和隔离之间找一个平衡点。
MySql5.7以前的版本:show variables like 'tx_isolation';
MySql5.7(包含)以后的版本:show variables like 'transaction_isolation';
mysql> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)
举例说明:
事务A | 事务B |
---|---|
1启动事务 | |
2.查询得到值V0 | 3启动事务 |
4.查询得到值V0 | |
5.将V0改为V0+1 | |
6.查询得到值V1 | |
7.提交事务B | |
8.查询得到值V2 | |
9.提交事务A | |
10.查询得到值V3 |
1.读未提交
- 将会话的隔离级别设置为读未提交,终端1和终端2一起设置
set session transaction isolation level read uncommitted;
- 按照上述表格中的顺序,依次执行,得到V0=1,V1=2,V2=2,V3=2
注意步骤6直接读到了步骤5中修改的值,此时事务B实际还未提交,这就是读未提交(read uncommited),事务级别读未提交存在脏读的问题,比如此用例中如果事务B做了回滚,那么事务A在第4步骤查询得到的值就是不正确的,这就是所谓的脏读
2.读提交
- 将会话的隔离级别设置为读提交,终端1和终端2一起设置
set session transaction isolation level read committed;
- 按照上述表格中的顺序,依次执行,得到V0=2,V1=2,V2=3,V3=3
注意步骤6没有读到步骤5中修改的值,步骤7,事务B提交之后,步骤8读到了步骤5修改的值,这就是读提交(read commited),事务级别读提交存在不可重复读的问题,比如此用例中,同一个事务,在不同的阶段读同一行数据读到了不同的值,这就是所谓的不可重复读,解决不可重复读的方法是加行锁,事务A在执行未提交的时候,事务B无法修改数据,即可避免
3.可重复读
- 将会话的隔离级别设置为可重复读,终端1和终端2一起设置
set session transaction isolation level repeatable read;
- 按照上述表格中的顺序,依次执行,得到V0=3,V1=3,V2=3,V3=4
注意步骤6没有读到步骤5中更改的值,步骤7事务B提交之后,步骤8依然没有读到步骤5中更改的值,直到事务A也提交之后才读到事务B更改的值,这就是可重复读(repeatable read)
- 事务级别可重复读存在幻读的问题
事务A | 事务B |
---|---|
1启动事务 | |
2查询V0 | |
3插入 | |
4查询V1 | |
5提交事务 | |
6查询V2 | |
7更新 | |
8查询V3 |
- 按照上述表格的顺序,依次执行,V0=V1=V2=5行,V3=6行,也就是在事务A中查到事务B插入的数据,这就是所谓的幻读,解决幻读的方法是加表锁,事务A在执行未提交的时候,事务B无法插入数据,即可避免
4.串行化
- 将会话的隔离级别设置为串行化,终端1和终端2一起设置
set session transaction isolation level serializable;
事务A | 事务B |
---|---|
1启动事务 | |
2启动事务 | |
3插入数据 | |
4查询数据 | |
5提交事务 | |
6查询数据 |
按照上述表格的顺序,依次执行,步骤3插入数据,但是未提交的时候,步骤4出现了获取锁超时的情况,这就是串行化(serializable
),同一行数据不允许并发执行,在写时,读必须等待。