事务回滚日志

事务开始会在uodo log 生成对应日志,日志记录了修改前、后信息用于出现回滚现象使用,例如:事务执行一条 insert 在回滚日志会对应一条delete

从默认隔离级别(可重复读)回滚步骤

假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录:
回滚 - 图1
当前值是 4,但查询这条记录时,不同时刻启动的事务会有不同的 read-view。
如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。
注:当没有事务再需要用到这些回滚日志时,回滚日志会被删除

尽量避免长事物

  • MySQL 5.5 及以前,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有 200GB 的库。最终只好为了清理回滚段,重建整个库。
  • 除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。
  1. # 查询长事务
  2. select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

自动提交

MySQL 的事务启动方式有以下几种:

  1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
  2. set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。
  3. autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。