1 基础知识
1.1 事务ACID
1.1.1 原子性
整个事务要么全部成功,要么全部失败。</br>
银行转账的梗,张三转账给李四1000元:
- 李四账户+1000;
- 张三账户-1000;
上面事务的两个动作必须全部成功,全部失败。
</br>
扩展阅读:
对InnoDB来说,只要client收到server发送过来的commit成功报文,那么这个事务一定是成功的。如果收到的是rollback的成功报文,那么整个事务的所有操作一定都要被回滚掉,就好像什么都没执行过一样。另外,如果连接中途断开或者server crash事务也要保证会滚掉。InnoDB通过undolog保证rollback的时候能找到之前的数据。
1.1.2 一致性
指的是在任何时刻,包括数据库正常提供服务的时候,数据库从异常中恢复过来的时候,数据都是一致的,保证不会读到中间状态的数据。一致性和原子性往往形影不离。</br>
比如上面的例子:
- 无论转账多少次,张三和李四账户余额的总和不会发生变化。
- 结果必须是使数据库从一个一致性状态变到另一个一致性状态。
扩展阅读:
在InnoDB中,主要通过crash recovery和double write buffer的机制保证数据的一致性。
1.1.3 隔离性
指的是多个事务可以同时对数据进行修改,但是相互不影响。</br>
要点:
- 不能产生脏数据
扩展阅读:
InnoDB中,依据不同的业务场景,有四种隔离级别可以选择。默认是RR隔离级别,因为相比于RC,InnoDB的RR性能更加好。
1.1.4 持久化
指的是事务commit的数据在任何情况下都不能丢。</br>
- 这里应该还包括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隔离级别之二): 可防止脏读、不可重复度、幻读。代价很高。
网友评论