MySQL(七)——undo日志

undo

事务id:只有在事务对表中的记录做改动时才会为这个事务分配一个唯一的事务id

  • INSERT:插入类型的undo日志主要记录主键信息,对应的删除该主键记录即可
  • DELETE:如果是删除记录会分为两阶段(根据事务的进度)
    • 事务未提交,语句已执行,会把记录的delete_mask改为1,中间状态,此时还在正常记录的链表中
    • 事务提交后,把delete_mask标记为1的记录从正常记录链表中移除,加入到已删除链表的头部
    • 而事务提交后,不需要用到undo日志,所以其实只要保存第一阶段的undo日志即可。
  • UPDATE:
    • 不更新主键
      • 就地更新:如果被更新的每个列,前后占用的存储空间一样大,可以直接在原纪录的基础上修改值
      • 如果空间不一样:先删旧记录,再插新记录:真正的删除记录,移到垃圾链表。如果新的记录占用的空间大小不超过旧的空间,则可以直接复用原来的空间,否则要在页面中新申请一段空间。如果页面没有新空间,需要进行页分裂。
    • 更新主键
      • 旧记录进行delete mark,因为如果这个事务没有提交,记录却删除了,则其他事务就无法根据主键找到该记录了。等到事务提交后再一到垃圾链表中,进行delete mark操作前会有一条undo日志
      • 重新插入新纪录。并产生一条undo日志

事务隔离级别

  • 脏写:一个事务修改了另一个未提交事务修改过的数据(这里的未提交指的是该事务进行修改的时候,读到的数据还是未提交时候的数据而不是已提交完后的数据)
  • 脏读:一个事务读到了另一个未提交事务修改过的数据,如果修改后回滚,读到了一个不存在的数据
  • 不可重复读:一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,简单来说就是一个事务内同一个查询可能返回不同的结果
  • 幻读:如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读。幻读与不可重复读的区别重点是幻读强调了读取到之前没有读取到的记录。

严重程度:脏写 > 脏读 > 不可重复读 > 幻读

隔离级别:

隔离级别 描述 脏读 不可重复读 幻读
READ UNCOMMITTED 读未提交 Possible Possible Possible
READ COMMITTED 读已提交 Not Possible Possible Possible
REPEATABLE READ 可重复读 Not Possible Not Possible Possible
SERIALIZABLE 可串行化 Not Possible Not Possible Not Possible

也就是说:

  • READ UNCOMMITTED隔离级别下,可能发生脏读不可重复读幻读问题。
  • READ COMMITTED隔离级别下,可能发生不可重复读幻读问题,但是不可以发生脏读问题。
  • REPEATABLE READ隔离级别下,可能发生幻读问题,但是不可以发生脏读不可重复读的问题。
  • SERIALIZABLE隔离级别下,各种问题都不可以发生。
  • 每种隔离级别都不允许脏写

​ MySQL默认是RR,但是互联网项目推荐RC。为什么默认是RR,因为在mysql5.0前,主从复制的binlog的格式为statement。顺序是先插后删,会造成先删后插的事务在master和slave中不一致,也就是主从不一致。所以需要靠RR来保持一致。

​ 在RR级别下,会存在间隙锁,出现死锁的概率高,且条件列未命中索引会锁表。而RC只锁行。

​ 在RC级别下的主从复制binlog要用row格式

MVCC(Multi-Version Concurrency Control)

版本链

  • 每次对记录进行改动都会记录一条undo日志,每条undo日志会有一个roll_pointer属性(insert的没有,因为没有更早版本),可以将这些undo日志都连起来,串成一个链表,如:
    • 记录的每次更新都会把旧值放到一条undo日志中,随着更新次数增多,会形成一个链表,最新的会在链头。且会记录事务id

ReadView

核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的。需要ReadView

  • m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。

  • min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。

  • max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。

    小贴士: 注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。

  • creator_trx_id:表示生成该ReadView的事务的事务id

    小贴士: 我们前边说过,只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为0。

有了ReadView,就可以根据以下步骤判断记录的某个版本是否可见:

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启(对RC来说且肯定还未提交,否则此时生成的ReadView的max_trx_id应该会大于当前访问版本,对RR来说,在他之后开启的事务是不允许被读到的),所以该版本不可以被当前事务访问。
  • 如果被访问版本的trx_id属性值在ReadViewmin_trx_idmax_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

如果某个版本的数据对当前事务不可见,就顺着版本链找到下一个版本的数据。

READ COMMITTED —— 每次读取数据前都生成一个ReadView

所以每一次查询之间如果有新的事务提交,那根据上面的查找流程,每次都会找到最新一次提交的事务的记录

REPEATABLE READ —— 在第一次读取数据时生成一个ReadView

所以只有第一次查询的时候生成ReadView,之后每次查询不管是否有新的事务提交,都会查到相同的结果(第一次查询时候的结果)。

具体流程参考:https://juejin.im/book/6844733769996304392/section/6844733770071801870

MVCC小结

​ 从上边的描述中我们可以看出来,所谓的MVCC(Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用READ COMMITTDREPEATABLE READ这两种隔离级别的事务在执行普通的SELECT操作时访问记录的版本链的过程,这样子可以使不同事务的读-写写-读操作并发执行,从而提升系统性能。READ COMMITTDREPEATABLE READ这两个隔离级别的一个很大不同就是:生成ReadView的时机不同,READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。

小贴士: 我们之前说执行DELETE语句或者更新主键的UPDATE语句并不会立即把对应的记录完全从页面中删除,而是执行一个所谓的delete mark操作,相当于只是对记录打上了一个删除标志位,这主要就是为MVCC服务的。不然如果更新主键的记录直接删了,就没办法出现在版本链中,就没办法通过MVCC来找到对应的记录。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354