终极篇-mysql事务隔离知识

1 基础知识

1.1 事务ACID

1.1.1 原子性

整个事务要么全部成功,要么全部失败。

银行转账的梗,张三转账给李四1000元:

  • 李四账户+1000;
  • 张三账户-1000;
    上面事务的两个动作必须全部成功,全部失败。

扩展阅读:
对InnoDB来说,只要client收到server发送过来的commit成功报文,那么这个事务一定是成功的。如果收到的是rollback的成功报文,那么整个事务的所有操作一定都要被回滚掉,就好像什么都没执行过一样。另外,如果连接中途断开或者server crash事务也要保证会滚掉。InnoDB通过undolog保证rollback的时候能找到之前的数据。

1.1.2 一致性

指的是在任何时刻,包括数据库正常提供服务的时候,数据库从异常中恢复过来的时候,数据都是一致的,保证不会读到中间状态的数据。一致性和原子性往往形影不离。

比如上面的例子:

  • 无论转账多少次,张三和李四账户余额的总和不会发生变化。
  • 结果必须是使数据库从一个一致性状态变到另一个一致性状态。

扩展阅读:
在InnoDB中,主要通过crash recovery和double write buffer的机制保证数据的一致性。

1.1.3 隔离性

指的是多个事务可以同时对数据进行修改,但是相互不影响。

要点:

  • 不能产生脏数据

扩展阅读:
InnoDB中,依据不同的业务场景,有四种隔离级别可以选择。默认是RR隔离级别,因为相比于RC,InnoDB的RR性能更加好。

1.1.4 持久化

指的是事务commit的数据在任何情况下都不能丢。

  • 这里应该还包括rollback的数据也不能丢失

扩展阅读:
在内部实现中,InnoDB通过redolog保证已经commit的数据一定不会丢失。

2 隔离性

2.1 事务引发的问题

2.1.1 脏读

读取到其他事务尚未提交的信息。

比如上面转账事务还没提交,这时,李四去查询自己的账号,发现多出了1000元。
如果,转账事务回滚,那么李四再次查询发现钱又没了。

2.1.2 不可重复度

一个事务内两次读取,内容不一样,本质是读读的问题。

2.1.3 幻读

一个事务内,先读出某条记录,发现不存在,然后做写的操作,到提交时,却报主键冲突。这是一个读写的问题。

典型例子:账号(唯一索引)注册
A用户用账号:zhangjianhua,进行注册。
B用户也用账号:zhangjianhua,进行注册。
俩人同时提交,服务端代码的逻辑,一般是,先查询这个账号存不存在,不存在就新增:

1.   begin transaction;
2.   select count(1) from user where account='zhangjianhua';
3.   if count(1) = 0 {
       insert into user(account) values('zhangjianhua');}
4.   commit;

假如A用户和B用户的事务都同时执行完第2步的查询,发现没这个人,A先第3步插入,紧接着A事务提交。B事务继续执行插入操作,提交。这时B报错。
那B就觉得很奇怪,明明查询zhangjianhua是不存在的啊,为什么会保存不了。(还有一种情况:A事务插入后,没提交,B事务执行插入,这时B事务会挂起,直到A事务提交完毕,才报错)

上面的问题:实际就是不可重复读。实际开发中也不需要规避这种问题。下面我会说明。

2.2 事务隔离级别

mysql默认有四种隔离级别:

  • READ-UNCOMMITTED
  • READ-COMMITTED
  • REPEATABLE-READ
  • SERIALIZABLE

扩展阅读:

mysql隔离级别

MySQL Glossary

2.2.1 READ-UNCOMMITTED

未提交读:读取其它事务尚未提交的数据。显然这种隔离级别会导致脏读。

2.2.2 READ-COMMITTED

提交读,也就是大家常说的RC读(oracle隔离级别之一):读取其他事务提交后的数据。这种隔离级别可以避免脏读,但是会发生不可重复度。
第一个事务第一次读取后,其他事务提交了第一个事务范围内的数据。第一个事务第二次读取发现数据不一致。

2.3.3 REPEATABLE-READ

重复读,即RR读:可同时规避脏读,重复读。但是不能规避幻读。

2.3.4 SERIALIZABLE

可串行化(oracle隔离级别之二): 可防止脏读、不可重复度、幻读。代价很高。

你可能感兴趣的:(终极篇-mysql事务隔离知识)