事务
MYSQL四大特性
ACID
- ATOMIC:原子性,由UNDO LOG日志保证的
- CONSISTENCE:一致性体现在 事务开始和结束 完整性得到保证,与现实的一致性,两个人银行转账的案例
ISOLATION:隔离性,由MVCC版本链和锁保证的
DURABILITY:持久性,由MYSQL的两段提交设计、REDO LOG日志和BIN LOG日志保证的
MYSQL事务和REDIS事务有什么区别?
- REDIS事务无法保证持久性,首先RDB快照写,是按照配置文件或者指定命令在特定时间才会写,AOF设置为ALWAYS,是先将数据放到AOF缓冲中,再写入,可能会出现丢死上一个事件循环中的数据的情况。
- REDIS无法保证原子性,无法回滚,主要命令有MULTI EXEC,开启事务能加快批量操作。
MYSQL事务隔离级别有哪些?分别解决什么问题?
- 事务级别对照表
| 事务级别 | 可能出现的并发问题 | 如何解决 |
|---|---|---|
| 读未提交 | 脏读、不可重复读、幻读 | |
| 读已提交 | 不可重复读、幻读 | MVCC版本链 |
| 可重复读 | 幻读 | MVCC+ReadVIEW+=临键锁 |
| 串行化 | 全是当前读,没有MVCC |
-
并发问题说明:
- 脏读:A事务读到B事务未提交的数据
- 不可重复读:强调读到了B事务修改的数据,A事务多次读取记录,在这个过程中B事务修改了数据并提交,导致A事务多次读的结果不一样
- 幻读:强调读到了B事务新增的数据,A事务多次读取记录,在这个过程中B事务新增了数据并提交,导致A事务读到的记录数不一样
什么是MVCC?
- MVCC是多版本并发控制,通过记录历史版本数据,解决读写并发冲突问题,避免加锁,提高MYSQL并发性能。
- 在行记录中,有事务ID和ROLL_PTR,MYSQL将历史数据存在UNDO LOG日志里面,ROLL_PTR指向UNDO_LOG日志,每次SELECT的时候会创建READVIEW快照,通过当前事务ID与历史数据比较,判断哪一条UNDO LOG可见。
MVCC中READVIEW可见性的规则?
- READVIEW主要有四个字段:创建这条记录的事务ID、所有活跃未提交的事务IDS、活跃未提交的最小事务ID、下一个事务ID
- 如果记录的事务id小于ids 就说明这条记录已提交
- 如果记录的事务id在ids中,就说明这条记录未提交
- 如果记录的事务id大于ids,就说明这事务还没开始
读已提交和可重复读在MVCC上有什么区别?
主要区别是生成READVIEW的时机不同
- 读已提交,每次执行SELECT 都会生成一次READVIEW数据,然后通过MVCC,只读取事务id小于活跃未提交事务ids中的记录
- 可重复读,只在事务开始之前生成一次READVIEW,在本次事务中一直用这个READVIEW
MYSQL的默认隔离级别是什么?怎么实现的?
默认隔离级别是可重复读,是通过MVCC(每个事务唯一的事务id,修改在undo-log链里面)+当前读上加快照读+行级锁:间隙锁
为什么很多互联网公司使用读已提交隔离级别?
因为MYSQL读已提交是通过MVCC版本链实现的,更新数据没有间隙锁,可以减少死锁的几率
MYSQL是如何解决不可重复读的问题的,又是怎么解决幻读的?
- MYSQL的读取有两种方式,快照读:就是直接的SELECT ... ,当前读:SELECT ... FOR UPDATE
- 快照读的情况下,MYSQL可以直接利用MVCC+READVIEW来实现,事务创建之前就做一次快照读,之后所有的操作都在快照的undo里面,保证可重复读
- 当前读的情况下,就没办法用MVCC+READVIEW了,所以这时候就要加间隙锁来解决,因为间隙锁和插入意向锁不兼容,所以也一部分解决了幻读的问题。
可重复读为什么没有完全解决幻读的问题?
-
可重复读为了当前读实现可重读 所以选择了间隙锁的方式,一定程度上解决了幻读,因为间隙锁只能阻塞其他事务,比如以下场景
- A事务先进行快照读,没有加锁
- B事务插入了一条id=5的数据
- A事务更新id=5的数据,A事务更新会执行当前读,所以能读到id=5,更新完了之后,id=5的事务id就是A
- A再进行快照读,因为快照读是通过版本链进行的,所以这时候读到了B事务的数据
-
总结:
- 两次都是快照读就不会幻读,但第一次快照读,中间发生一次当前读,那就会造成幻读。