暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Mysql事务相关

程序员升级之路 2021-10-24
347

最近重温了下Mysql相关原理的书,主要是事务的实现,这个对设计一个稳定的系统很有借鉴意义,发现事务的实现还是蛮复杂的,把中间看到的一些知识点和想法整理下。 


一、事务的核心属性

A:原子性

事务要么不执行,要么全部执行完,不会执行其中一部分;


B:一致性

主要是一些约束,如主键,外键,等


I:隔离性

指多线程之间的修改不会影响其它线程,如线程A改了数据没提交前B线程可以不看到,这个和事务隔离级别配置相关;


D:持久性

事务一旦提交了就不能丢失;


其中B主要是一些规则比较好理解,D也比较容易,事务提交的时候将数据全部刷新到存储介质上就行;A和I设计到多线程并发问题,还有异常处理问题,和D有些相关,如何保证提交的时候全部提交成功,而不会提交一部分。


二、Write-Ahead

数据库是IO型应用,对记录的修改最终是要写到磁盘上,一次事务可能修改多条记录,而这些记录会分布在磁盘的不同位置上,而磁盘是顺序写入性能高,下面是磁盘物理结构,具体组成就不介绍了;


如果大量随机磁道写入则会造成性能低下,所以有了Write-Ahead。


具体过程如下,数据库把对所有磁盘的修改以日志追加的方式写入,然后异步将这些内容刷新到真正存放数据的位置,这样就保证用户请求的时候性能是最高的;


举个例子,一个经典的例子是转账,需要将一个账上加钱,而另一个账上减钱,这个事务至少要执行2条Sql语句:

    update User set balance=balance-100 where id=100;
    update User set balance=balance+100 where id=200;
    复制

    假设第一条记录在0号盘面上,第二条记录在1号盘面上,这个时候数据库记录2条日志,大概内容如下:

    第1条日志:修改0号盘面X1磁道数据为Y1;

    第2条日志,修改1号盘面X2磁道数据库Y2;


    只要日志写入成功然后就可以返回给用户成功了,会有后台线程读取日志里的数据真正把0号盘面和1号盘面上的数据写回去。


    可以看到,事务提交分成2部分,写日志和写数据,写日志记录数据改了哪些地方,这个和硬件相关,这部分是顺序写的;而写数据的部分是离散的,但这部分是后台写,所以性能慢一点没关系,要保证数据的正确性。


    三、Redo Log

    上面说了事务提交前后要写2部分数据,一是日志,二是数据,其中日志在Mysql准确的说是InnoDB中就是以Redo Log来表示,这里讲几个细节:

    1、Redo Log以Redo Log Block来管理日志,每个Block是512字节,为什么是512,因为早期磁盘一个扇区就是512,这样可以保证写入的原子性,即不会512字节只会写一部分成功


    2、日志文件不能无限扩张

    日志过了一段时间就不需要了,这个时间是指数据的部分写入完成,因为这部分是异步写入的,如果中间当机则需要通过日志部分来恢复。


    3、LSN(Log Sequence Number)

    Redo Log日志编号,记录的是数据库从安装启动开始到当前写入的总的日志总量。这个用于恢复中,先不讲太细的东西。


    四、ARIES恢复算法

    这个是20世纪90年代IBM几位研究员提出的一个算法集,主要论文如下:

    AREIS: A Transaction Recovery Method Supporting Fine-Granularity Locking  and Parial Rollbacks Using WriteAhead Logging

    这个算法影响深远,基本上现在的关系型数据库都吸收了这个思想。


    先大概讲下基本原理,Mysql InnoDB中是以页为最小单位来管理磁盘的,一般为16KB,如果一个事务修改了某个页会将这个页标记为脏页,然后异步刷新到磁盘上。


    ARIES算法大概分3个阶段:

    1、分析阶段

    确定哪些数据页是脏页,为阶段2的Redo做准备。


    2、确定哪些事务未提交

    未提交的事务也写入Redo Log,如何判断哪些未提交呢,这里用到了Checkpoint机制,它是每隔一段时间将内存中的所有数据刷新到磁盘,注意是所有,对于数据库的场景来说,现在几百GB大小很常见了,这样做肯定不现实,一个是量太大,二是刷新过程中还要停止所有用户请求,像JAVA垃圾回收一样,要Stop The World。


    所以一般采用Fuzzy CheckPoint,具体是在内在中维护二张表:活跃事务表和脏页表。

    活跃事务表:维护一个关键变量LastLSN,即该事务产生的日志最后一条日志的LSN。

    脏页表是当前所有未刷新到磁盘上的页的集合,系统为每个页记录了RecoveryLSN,即导致该页面为脏页最早的LSN。


    Fuzzy CheckPoint就是对这2个关键表做一个Checkpoint,这样数据量就比较小了,形成一条日志写入Redo Log。


    有了这2张表就可以取出系统意外宕机的时候未提交的事务了,具体过程如下,从最近一次CheckPoint开始扫描Redo Log,遇到一个事务则加入到集合,如果遇到事务提交的日志则将事务从集合中删除掉。


    另外就是求宕机的时候未刷盘的脏页集合,从最后一个CheckPoint开始一直扫描,一直到Redo Log的结束,如果日志中记录的是新的页面就加入到脏页集合,当然这过程可能在误判,不过没关系,真正把Redo Log写入到数据存放真正位置是幂等的。在刷磁盘的时候,磁盘上每个页面会记录最后一次刷新的LSN,刷新过程中会判断两者的大小,如果页面的LSN比Redo Log的大则跳过这条日志。


    常用Maven插件

    《大型系统应用架构实战》——全球化技术读后感

    MyBatis源码分析四:动态Sql实现

    文章转载自程序员升级之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论