本文笔记摘自于《Mysql 技术内幕 InnoDB存储引擎》

Mysql InnoDB笔记(1)——体系架构Mysql InnoDB笔记(2)——表Mysql InnoDB笔记(3)——索引Mysql InnoDB笔记(4)——锁Mysql InnoDB笔记(5)——事务Mysql InnoDB笔记(6)——备份和性能调优


事务

事务特性

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

事务分类

  • 扁平事务(Flat Transactions)
    这种是我们最常用的事务类型,只有一个保存点。
  • 带有保存点的扁平事务(Flat Transactions with Savepoints)
    有多个保存点,可以设计保存点,并可回滚到某个保存点。
  • 链事务(Chained Transactions)
    提一个事务的同时开启下一个事务。
  • 嵌套事务(Nested Transactions)
  • 分布式事务(Distributed Transactions)

嵌套事务和分布式事务一般都不会直接用,都是在应用层做控制。

事务的实现

通过【锁】实现【隔离性】,【redo log】实现【持久性】,【undo log】实现【原子性、一致性】。

redo log是物理日志,恢复提交事务修改的页操作,undo log是逻辑日志,回滚行记录到某个版本。

redo log

在事务commit前,会把本次事务的操作记录到redo log buffer里,然后刷到磁盘的redo log file。在commit出异常的时候 ,可以从redo log恢复数据,保证数据的持久性。redo log基于页进行管理。

redo log是顺序写的,因此性能比较高。
innodb_flush_log_at_trx_commit可以设置从redo log buffer刷到redo log file的策略。默认是在事务提交时,强制调用fsync刷到磁盘。

注意一下redo log和bin log的区别

  • log block
    redo log以log block的形式存储在redo log buffer里,每个block大小为512字节。
  • log group
    redo log以block为单位写入redo log file,一个log group会包含多个redo log file,log group是一个逻辑上的概念。
  • redo log 格式
    不同的操作类型会有不同的格式
  • LSN(Log Sequence Number)
    在redo log file和redo log页都有记录LSN,用于判断该页是否要进行恢复操作。

undo log

undo log存放于undo段(undo segment),位于共享表空间内。

MVCC(多版本控制)也是通过undo segment来实现。
一致性非锁定读就是从undo segment读取快照。

undo log也会产生redo log

InnoDB有rollback segment,每个回滚段记录了1024个undo log segment,而undo页的申请在undo log segment中进行。

通过innodb_undo_logs设置rollback segment的数量,默认128

事务提交时,会把undo log放入purge的列表中,留给后续的purge操作,并且判断undo log所在的页是否可以重用,若可以则分配给下个事务使用。

事务提交后并不会马上删除undo log,这个事情交给purge线程来处理

  • undo log 格式
    有2种:insert undo log和update undo log

因insert操作对其他事务不可见,所以insert undo log在事务提交后,可以马上删除,不需要purge操作。

通过字典表INNODB_TRX_ROLLBACK_SEGMENTINNODB_TRX_UNDO可以查看undo相关信息

purge

purge用于完成最终的update和delete操作。对于按事务提交顺序挂在history list的undo log,purge会去列表尾端找到最先提交的事务,然后找到对应的undo page,判断相应的行的undo log有没有事务引用,如果没有则清除,清除后,此页可以被重用。

history list的长度会影响性能,innodb_max_purge_lag用于控制history list的长度,innodb_purge_batch_size用于控制每次操作清理的undo page数量。

group commit

将多个事务提交的fsync操作放在同一个fsync里完成,以提高磁盘效率。

事务控制语句

START TRANSACTION | BEGIN
COMMIT
ROLLBACK
SAVEPOINT identifier
RELEASE SAVEPOINT identifier
ROLLBACK TO [SAVEPOINT] identifier
SET TRANSACTION [READ UNCOMMITTED | READ COMMITTED | REPTATABLE READ | SERIALIZABLE]

了解一下隐式提交的SQL语句,DDL是其中一种,理解TRUNCATE TABLEDELETE的区别,那就是TRUNCATE TABLE无法回滚。

事务的隔离级别

  • READ UNCOMMITTED
    会产生脏读、不可重复读和幻读。
  • READ COMMITTED
    解决了脏读,但会出现不可重复读和幻读。
  • REPTATABLE READ
    INNODB默认的隔离级别。解决了脏读和不可重复读,但会出现幻读。

在RR级别下,其他数据库会产生幻读,但INNODB不会,因为INNODB使用了Next-Key Lock的算法,从而避免了幻读。

一个问题:在RR级别下使用快照读(MVCC)来支持可重复读,那为何A事务 update tb_test set a=2 where a=1;返回0呢?(A事务先开启,读取select a from tb_test where a=1;返回1;然后B事务开启,update tb_test set a=2 where a=1;提交)

答案是:要先理解快照读和当前读。当前读就是读最新提交的数据,那什么情况下会执行当前读呢?就是下面这些了,我理解是在当前事务执行。

select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…); 
update table set ? where ?; 
delete from table where ?;

另一个问题:假如使用Next-Key Lock,为何会产生幻读呢?在RR级别下,不都是快照读吗?怎么会有幻读呢,难道是其他事务执行insert和delete也触发了本事务的select当前读??

后面想了想,应该是使用select * from tb_test where a>1 for update; 或者update tb_test set a=1 where a>1; 这样的范围查询数据,才现在幻读吧,这样其实就是当前读了。

  • SERIALIZABLE
    解决所有问题,但是效率低。

丢失更新的问题好像跟INNODB没多大关系。。。

快照读的快照生成时间:不是在开启事务的时候,而是第一次执行select的时候。

分布式事务

只有在SERIALIZABLE隔离级别下才能使用,但一般我们也不用,通常是在应用层面解决。


上一篇:Mysql InnoDB笔记(4)——锁 下一篇:Mysql InnoDB笔记(6)——备份和性能调优