MySQL 笔记整理(3) --事务隔离,为什么你改了我还看不见?
笔记记录自林晓斌(丁奇)老师的《mysql实战45讲》
3) --事务隔离,为什么你改了我还看不见?
简单来说,事务就是要保证一组数据操作,要么全部成功,要么全部失败。在mysql中,事务支持是在引擎层实现的。但并不是所有的引擎都支持事务,这也是myisam被innodb取代的重要原因之一。
本篇内容均是在innodb下讨论。
提到事务,总免不了acid(atomicity,consistency,isolation,durability),(原子性,一致性,隔离性,持久性),本篇主要讨论的是隔离性。
隔离性在mysql中是分为不同的隔离级别的。包括 读未提交(read uncommitted),读提交(read commited),可重复读(repeatable read)(mysql默认隔离级别),串行化(serializable)。隔离性依次增强。
- 读未提交: 一个事务还没提交时,它所做的变更就能被别的事务看到。
- 读提交: 一个事务提交之后,它所做的变更才能被别的事务看到。
- 可重复读:一个事务的执行过程当中所能看到的数据,总是和这个事务启动时能看到的数据保持一致。当然在可重复读时,这个事务自身所做的变更对其他事务来说也是不可见的。
- 串行化: 写会加“写锁”,读会加“读锁”,出现读写冲突时,后访问的事务必须等前一个事务执行完成,才能继续执行。
(图片来源于 极客时间 林晓斌 《mysql 实战45讲》,如有版权问题请联系我删除)
以上图为例,在四种隔离级别下对应的情况分别为:
- 读未提交:事务a启动时查询结果为1, v1时值为2(读到了事务b未提交的数据),v2时值为2,v3时值为2
- 读提交:事务a启动时查询结果为1, v1时值为1(读不到事务b未提交的数据),v2时值为2(事务b已经提交,可以被事务a读到),v3时值为2
- 可重复读:事务a启动时查询结果为1, v1时值为1(读不到事务b未提交的数据),v2时值为1(事务a未提交,在事务a过程内保持与事务a启动时读到的数据一致),v3时值为2(事务a已经提交)
- 串行化:事务b在执行将1改成2时会被锁住,直到事务a提交后,事务b才可以继续执行。因此 v1,v2的值是1,v3的值是2.
在实现上,数据库里会创建一个视图,访问的时候以视图的逻辑结果为准。 在“可重复读”下,这个视图是在事务启动时建立的。在“读提交”下,这个视图是在每个sql语句开始执行的时候创建的。另:“读未提交”下,不创建视图,直接返回记录上的最新值。在“串行化”下使用加锁的方式来避免进行并行访问。
事务隔离级别的实现:
以可重复读为例,假设将一个值从1案顺序改成2,3,4.在回滚日志里会有如下的记录。
(图片来源于 极客时间 林晓斌 《mysql 实战45讲》,如有版权问题请联系我删除)
在mysql中,实际上每条记录在更新的时候都会同时记录一条回滚操作,通过这个回滚操作可以获得更新前的记录。当前值是4,但是在查询这条记录时,不同时刻启动的事务会有不同的read-view. 同一条记录在系统中存在多个版本,这就是数据库的多版本并发控制(mvcc),对于a来说,要想得到1,就必须将当前所有操作依次回滚。即使这个时候有一个新的事务将4改成其他值,这个新的事务也和之前是事务没有冲突。每个事务被隔离开来了。
当然,回滚操作的日志不会一直保留,直到系统中没有比这个回滚操作更早的read-view时就会删除回滚操作日志。因此当你使用长事务时,系统中可能会存在很古老的read-view视图,当然对应的回滚操作也不会删除,回滚日志就会变得很大。
上篇问题答案:
请问在什么场景下,一天一次备份会比一周一次备份更有优势?或者说,它影响了这个数据系统的哪些指标?
在一天一备份的情况下,最坏情况需要应用一天的binlog,一周一备份则会需要使用一周的binlog了。系统的对应指标就是 rto(恢复目标时间)。当然也不是说一天一备份就完全优于一周一备份,因为频繁的全量备份需要消耗更多存储空间,需要根据具体业务来评估。
问题:
如果你是数据库负责人,你有什么方案来避免长事务呢?