【事务专题】日志相关问题(难点)

本问题对应的项目链接:高频题回答模版训练

事务的很多实现,都是因为有日志的支撑,比如binlog undo log, redo log 等,这部分其实也是难点。

1、Mysql 是怎么保证原子性的?

2、Mysql 怎么保证持久性的?

3、Mysql 怎么保证隔离性的?(锁 和 MVCC 机制)

4、介绍一下 binlog 和 redo log,他们两有啥区别?

5、两阶段提交了解吗?介绍一下,为啥需要两阶段提交呢?

6、幻读了解吗?介绍一下,innodb引擎是如何解决幻读问题等?

7、刚才我们说到了原子性,那宕机时还能保证原子性吗?undolog在宕机是怎么保证原子性的?

共以下 2 个回答

  • 終达·斯维达尼亚 永久会员 2024年2月19日 上午3:52

    1、Mysql 是怎么保证原子性的?

    mysql主要是通过undo log来保证原子性,即一片代码逻辑要么全部执行成功,要么全部进行回滚。当事务出现异常的时候,利用undolog中的信息进行回滚以保证原子性。

    2、Mysql 怎么保证持久性的?

    持久性是指,事务一旦提交,它对数据库的改变就应该是永久性的,接下来的其他操作或故障不能对其有影响。mysql在执行完sql语句后会将变更数据的记录写入redo log,同时在适当的时机将日志中记录的变更内容保存到磁盘中。即使系统宕机,mysql依然可以根据redo log 里记录的内容,将所有的数据恢复到最新状态。

    3、Mysql 怎么保证隔离性的?

    • 隔离性是指一个事务内部的操作以及操作的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能相互干扰
    • mysql中用锁机制来保证一个事务写操作对另一个事务写操作的隔离性,用 MVCC 机制来保证一个事务写操作对另一个事务读操作的隔离性。
    • MVCC 主要解决的是读写冲突的问题,它是由 undo log + read view 实现的。其中 undo log 可以为每条记录保存多个历史版本,MySQL 在执行快照读的时候,会根据事务的 read view 里的信息,顺着 undo log 的版本链找到满足可见性的记录

    4、介绍一下 binlog 和 redo log,他们两有啥区别?

    bin log和redo log是innodb引擎下的两大日志,因为这两大日志的存在可以让mysql做到临时崩溃后的数据恢复,下面是它们的区别

    • bin log是mysql原生的日志组件,而redo log是innodb引擎中引入的独有的日志组件
    • bin log记录的是sql语句的执行逻辑;而redo log记录的是数据库中数据的变化情况
    • bin log是增量记录的,在适当的时机可以重写日志以节省空间和加速恢复;而redo log分配有固定的内存空间,在适当的时机将日志数据写入磁盘并清除日志

    5、两阶段提交了解吗?介绍一下,为啥需要两阶段提交呢?

    在更新日志的时候,先写入redo log并更新为prepare状态,此时先不提交,接着写入bin log,再确认binlog写入无误后便提交redo log。两阶段提交可以保证数据的一致性,以免在服务器更新过程中宕机的时候出现两个日志的数据不一致。

    6、幻读了解吗?介绍一下,innodb引擎是如何解决幻读问题等?

    幻读是mysql在读某行时,由于其他事务的增删改操作导致读取的数据和预期的数据不一致,幻读存在于读未提交、读提交、可重复读的隔离级别中。幻读会带来两个问题,首先是会破坏给目标行加锁的语义,导致出现新的目标行被修改;其次是会导致数据和日志在逻辑上的不一致,即使把所有的记录都加上锁,还是可能会被其它事务插入新的记录。innodb处理幻读的手段是引入了间隙锁,这些就确保了无法再插入新的记录,同时已存在的数据也不能更新成间隙内的数据。

    7、刚才我们说到了原子性,那宕机时还能保证原子性吗?undolog在宕机是怎么保证原子性的?

    可以的,由于每个事务执行时,在提交事务前都会将对数据的修改记录到 undo log 中,现在假设 MySQL 正在执行某个事务时突然宕机,如果此时事务还没有提交,那么恢复时系统会通过 undo log 依次对数据进行相反操作,对这个未完成的事务进行回滚,确保事务的原子性;如果宕机前事务已经提交了,那么事务就无需回滚,一切正常。

    0 赞同 0 条回复
  • 秊 永久会员 2023年8月9日 下午10:57

    1、MySQL 是怎么保证原子性的?(undo log)

    原子性是说,一个事物要么全部执行成功,要么全部执行失败。MySQL 主要是利用 undo log,也就是回滚日志来实现原子性。

    平常我们在对数据进行增删改时,InnoDB 除了会记录 redo log,还会将更新的数据记录写进 undo log 中。当事务出现异常,执行失败的时候,就需要利用 undo log 中的信息将数据回滚到修改之前的版本。

    2、MySQL 怎么保证持久性的?(redo log)

    持久性是指,事务一旦提交,它对数据库的改变就应该是永久性的,接下来的其他操作或故障不能对其有影响。InnoDB 中主要是通过 redo log 来保证事务的持久性。其中还用到了 WAL 技术。

    WAL 的关键点在于 MySQL 的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。具体来说,当有一条记录需要更新时,InnoDB 引擎会先把记录写到 redo log 里,并更新内存,这个时候整个记录的更新就算完成了。后续,InnoDB 引擎会在适当的时候,由后台线程将缓存在 Buffer Pool 里的脏页刷新到磁盘里,这个时候往往也是系统空闲的时候。

    有了 redo log,当系统崩溃时,即使脏页的数据还没来得及持久化,但 redo log 已经持久化了,MySQL 就可以根据 redo log 里记录的内容,将所有的数据恢复到最新状态,整个过程也就是常说的 crash-safe 能力。

    3、MySQL 怎么保证隔离性的?(锁 和 MVCC 机制)

    隔离性是指一个事务内部的操作以及操作的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能相互干扰。隔离性可以防止多个事务并发执行时,可能存在交叉执行导致数据的不一致。MySQL 对隔离性的保证主要有两个方面,用锁机制来保证一个事务写操作对另一个事务写操作的隔离性,用 MVCC 机制来保证一个事务写操作对另一个事务读操作的隔离性。

    MySQL 中按照锁的粒度,可以分为全局锁,表锁和行锁。全局锁会使整个数据库处于只读状态,在做全库逻辑备份时经常用到。表级锁在操作数据时会锁定整张表,并发性能一般,而行锁可以做到只锁定需要操作的记录行,并发性能很好。但是由于加锁本身需要消耗资源,因此某些在锁定数据较多的情况下可以使用表锁来减少开销。

    MVCC 主要解决的是读写冲突的问题,它是由 undo log + read view 实现的。其中 undo log 可以为每条记录保存多个历史版本,MySQL 在执行快照读的时候,会根据事务的 read view 里的信息,顺着 undo log 的版本链找到满足可见性的记录。

    4、介绍一下 binlog 和 redo log,他们两有啥区别?

    redo log 是 InnoDB 独有的,记录的是某个数据页做了什么修改,每执行一个事务就会产生相应的 redo log。当事务提交时,只需将 redo log 持久化到磁盘即可,可以暂时不考虑将 Buffer Pool 里的脏页写回,而是在合适的时机交给后台线程去做。系统故障崩溃时,MySQL 也可以在重启之后利用 redo log 里的内容恢复数据。

    binlog 是 Server 层的日志,记录的是所有数据库表结构变更和表数据修改的日志。在 MySQL 完成一条更新操作后,Server 层都会生成一条 binlog,在事务提交时,会将该事务整个执行过程中产生的所有 binlog 统一写进 binlog 文件里,主要用于 “归档”。

    两者的区别主要有四点,首先是适用对象不同,binlog 是在 MySQL 的Server 层实现的,所有存储引擎都可以使用。而 redo log 是 InnoDB 引擎独有的;其次是文件格式不同,binlog 是逻辑日志,记录的是某个语句的原始逻辑,比如 “给 ID 为 1 的 A 字段加 1”。而 redo log 记录的是某个数据页上做的修改,比如 “某个表空间中的某个数据页的某个偏移量出做了什么更新”;还有是写入方式不同,binlog 是追加写,写满一个文件就在创建一个新文件继续写。而 redo log 是循环写,日志空间的大小是固定的,写满就需要先刷脏页,然后继续从头写。

    5、两阶段提交了解吗?介绍一下,为啥需要两阶段提交呢?

    两阶段提交在 MySQL 中主要用来确保 redo_log 和 bin_log 在逻辑上保持一致。

    具体的,在 MySQL 中执行一条更新语句时,在语句执行的最后需要分别写 redo log 和 binlog。MySQL 把这个过程分为了两个部分:首先先写 redo log,并把它标记为 prepare 状态,然后紧接着写 binlog,等到 binlog 写完之后再将 redo log 标记为 commmit 状态。这样的话,无论写日志的过程中 redo log 和 binlog 哪个环节出现问题,在崩溃恢复时都会保证两个日志系统的数据一致性。

    比如,假设写 redo log 处于 prepare 阶段之后,写 binlog 之前发生了崩溃,由于此时 binlog 还没有写,redo log 也没有提交,崩溃恢复时这个事务就会回滚。再假设 binlog 写完了,但 redo log 还没 commit 就崩溃了,这时在崩溃恢复时系统会判断 binlog 是否是完整的,如果完整,则提交事务,不完整则回滚事务。

    其实两阶段提交不仅存在于 MySQL 中,它还是分布式系统中用于保证事务一致性的协议。关键在于需要保证提交阶段所有参与者的操作要么都提交成功,要么都回滚。

    6、幻读了解吗?介绍一下,innodb引擎是如何解决幻读问题的?

    幻读是指一个事务在前后两次使用当前读查询同一个范围的时候,后一次的查询看到了前一次查询没有看到的新插入的行。需要注意的是,在 RR 隔离级别下的普通查询是快照读,利用 MVCC 确保了别的事务不会插入数据,所以幻读在 “当前读” 下才会出现。

    幻读会带来两个问题,首先是会破坏给目标行加锁的语义,导致出现新的目标行被修改;其次是会导致数据和日志在逻辑上的不一致,即使把所有的记录都加上锁,还是可能会被其它事务插入新的记录。

    InnoDB 为了解决幻读,引入了间隙锁,这样在使用 select … for update 语句时,会在对目标行加锁的时,不仅加了行锁,还给行两边的间隙也加上了间隙锁,这些就确保了无法再插入新的记录,同时已存在的数据也不能更新成间隙内的数据,也就不会出现幻读情况。

    不过也有一些特殊情况,比如事务 A 在 T1 时刻进行快照读,事务 B 在 T2 时刻插入记录,事务 A 在 T3 时刻进行当前读,此时会查出事务 B 新增的记录,也就出现了幻读。为了避免这类特殊场景下的幻读,最好是在事务开启之后,立马执行 select … for update 这类当前读语句,为所有记录加上 next-key lock,也就是行锁加间隙锁,彻底避免其他事务插入记录。

    7、刚才我们说到了原子性,那宕机时还能保证原子性吗?undo log 在宕机是怎么保证原子性的?

    可以的,由于每个事务执行时,在提交事务前都会将对数据的修改记录到 undo log 中,现在假设 MySQL 正在执行某个事务时突然宕机,如果此时事务还没有提交,那么恢复时系统会通过 undo log 依次对数据进行相反操作, 比如已经 insert 了一条记录,那么就 delete 这条记录等等,对这个未完成的事务进行回滚,确保事务的原子性;如果宕机前事务已经提交了,那么事务就无需回滚,一切正常。

    0 赞同 0 条回复

# 回答此问题

后才能回答