本文以一条insert语句为线索介绍 mini transaction

mini transation 主要用于innodb redo log 和 undo log写入,保证两种日志的ACID特性

mini-transaction遵循以下三个协议:

  1. The FIX Rules

  2. Write-Ahead Log

  3. Force-log-at-commit

The FIX Rules

修改一个页需要获得该页的x-latch

访问一个页是需要获得该页的s-latch或者x-latch

持有该页的latch直到修改或者访问该页的操作完成

Write-Ahead Log

每个页有一个LSN,每次页修改需要维护这个LSN,当一个页需要写入到持久化设备时,要求内存中小于该页LSN的日志先写入到持久化设备中

Force-log-at-commit

一个事务可以同时修改了多个页,Write-AheadLog单个数据页的一致性,无法保证事务的持久性

Force -log-at-commit要求当一个事务提交时,其产生所有的mini-transaction日志必须刷到持久设备中

这样即使在页数据刷盘的时候宕机,也可以通过日志进行redo恢复

代码简介

本文使用 MySQL 5.6.16 版本进行分析

mini transation 相关代码路径位于 storage/innobase/mtr/ 主要有 mtr0mtr.cc 和 mtr0log.cc 两个文件

另有部分代码在 storage/innobase/include/ 文件名以 mtr0 开头

mini transaction 的信息保存在结构体 mtr_t 中,结构体成员描述如下

一个 mini transaction 从 mtr_start(mtr)开始,到 mtr_commit(mtr)结束

下面一般省略 mtr_t 以外的参数

第一个 mtr 从 row_ins_clust_index_entry_low 开始

至此 insert 语句执行结束后

一条 insert 是一个单语句事务,事务提交时也会涉及 mini transaction

提交事务时,第一个 mtr 从 trx_prepare 开始

  1. trx_prepare
  2. trx_prepare_for_mysql
  3. innobase_xa_prepare
  4. ha_prepare_low
  5. MYSQL_BIN_LOG::prepare
  6. ha_commit_trans
  7. trans_commit_stmt
  8. mysql_execute_command
  9. mtr_memo_push(mtr_4) // undo page 加 RW_X_LATCH 入栈
  10. buf_page_get_gen
  11. trx_undo_page_get
  12. trx_undo_set_state_at_prepare
  13. trx_prepare
  14. mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, undo->state, MLOG_2BYTES, mtr_4) 写入TRX_UNDO_STATE
  15. trx_undo_set_state_at_prepare
  16. trx_prepare
  17. trx_undo_set_state_at_prepare
  18. trx_prepare
  19. trx_undo_set_state_at_prepare
  20. trx_prepare
  21. mtr_commit(mtr_4) // 提交 mtr_4
  22. trx_prepare
  23. mtr_start(mtr_5) // mtr_5 用于 commit transaction
  24. trx_commit
  25. trx_commit_for_mysql
  26. innobase_commit_low
  27. innobase_commit
  28. ha_commit_low
  29. MYSQL_BIN_LOG::process_commit_stage_queue
  30. MYSQL_BIN_LOG::ordered_commit
  31. MYSQL_BIN_LOG::commit
  32. ha_commit_trans
  33. trans_commit_stmt
  34. mysql_execute_command
  35. mtr_memo_push(mtr_5) // undo page 加 RW_X_LATCH 入栈
  36. buf_page_get_gen
  37. trx_write_serialisation_history
  38. trx_commit_low
  39. trx_commit
  40. trx_undo_set_state_at_finish(mtr_5) // set undo state, 这里是 TRX_UNDO_CACHED
  41. trx_write_serialisation_history
  42. trx_commit_low
  43. trx_commit
  44. mtr_memo_push(mtr_5) // 系统表空间 transaction system header page 加 RW_X_LATCH 入栈
  45. buf_page_get_gen
  46. trx_sysf_get
  47. trx_sys_update_mysql_binlog_offset
  48. trx_write_serialisation_history
  49. trx_commit_low
  50. trx_commit
  51. trx_sys_update_mysql_binlog_offset // 更新偏移量信息到系统表空间
  52. trx_write_serialisation_history
  53. trx_commit_low
  54. trx_commit
  55. mtr_commit(mtr_5) // 提交 mtr_5
  56. trx_commit

至此 insert 语句涉及的 mini transaction 全部结束

上面可以看到加锁、写日志到 mlog 等操作在 mini transaction 过程中进行

解锁、把日志刷盘等操作全部在 mtr_commit 中进行,和事务类似

mini transaction 没有回滚操作, 因为只有在 mtr_commit 才将修改落盘,如果宕机,内存丢失,无需回滚;如果落盘过程中宕机,崩溃恢复时可以看出落盘过程不完整,丢弃这部分修改

  1. mlog 中日志刷盘
  2. 释放 mtr 持有的锁,锁信息保存在 memo 中,以栈形式保存,后加的锁先释放
  3. 清理 mtr 申请的内存空间,memo 和 log
  4. mtr—>state 设置为 MTR_COMMITTED

上面的步骤 1. 中,日志刷盘策略和 innodb_flush_log_at_trx_commit 有关

  • 当设置该值为1时,每次事务提交都要做一次fsync,这是最安全的配置,即使宕机也不会丢失事务
  • 当设置为0时,事务提交不会触发redo写操作,而是留给后台线程每秒一次的刷盘操作,因此实例crash将最多丢失1秒钟内的事务