扁平事务:最简单的事务,所有操作处于同一层次。
带有保存点的扁平事务:增加了保存点,用于通知系统记住事务当前状态,在发生错误回滚时可以回到保存点。(事务回滚到保存点后状态还是活跃的,此时想要完全回滚还需rollback work)。
链事务:系统故障时,带有保存点的扁平事务的所有保存点会消失,再恢复事务需要从头执行,因此引入链事务,即:提交事务操作和开始下一个事务合并为一个原子性操作,下一个事务可以看到上一个事务的结果,好像在同一个事务中进行。链事务只能在当前事务内回滚。
嵌套事务:由一个顶层事务控制着各个层次的子事务,子事务可以是嵌套、也可以是扁平事务。子事务需要父事务提交后才可提交,子事务不具有持久性。
分布式事务:在分布式环境下运行的扁平事务。需要根据所在位置访问不同网络节点。在使用分布式事务时,innodb事务隔离级别必须设为串行化。
innodb原生不支持嵌套事务,但可以通过带有保存点的事务来模拟串行嵌套事务。
事务的隔离性通过锁实现,原子性、持久性通过redo log(重做日志)实现、一致性通过undo log(回滚日志)完成。
redo恢复提交事务修改的页操作,通常是物理日志,基本是顺序写的,undo回滚记录到特定版本,是逻辑日志,需要随机读写。
undo是逻辑日志,若此事务执行过程中当前页可能已被其他事务修改(在生产环境中非常常见),回滚即撤销本次事务操作,不影响其他事务执行结果,所以不能将本页数据恢复到执行前。用来帮助事务回滚及对MVCC的支持(快照读)。
redo 存放在重做日志文件中,但是undo存放在数据库内部undo段中,它位于表共享空间内,undo并非用于将数据库恢复到原来的样子,而只是回滚某个事务的操作,在此事务执行过程中其他事务对数据库的修改它是不能恢复的。
undo log会产生redo log,因为undo log也需要持久性保护。
mysql还有一种二进制日志binlog,用来进行point-in-time的恢复和主从复制环境的建立。
redo log与binlog区别:
redo log在innodb存储引擎层产生,是物理格式日志,记录的是对于每个页的修改,在事务进行中不断被写入。(页(Page):是 Innodb 存储引擎用于管理数据的最小磁盘单位,常见的页类型有数据页、Undo 页、系统页、事务数据页等)
redo log是WAL(Write-Ahead Logging 预写日志系统)的典型应用,MySQL在有事务提交对数据进行更改时,只会在内存中修改对应的数据页和记录redo log日志,完成后即表示事务提交成功,至于磁盘数据文件的更新则由后台线程异步处理,此外还能提升语句的执行性能(写redo log是顺序写,相比于更新数据文件的随机写,日志的写入开销更小,能显著提升语句的执行性能,提高并发量)
redo log没有打开O_DIRECT选项(linux文件系统写入选项,开启时数据可以跳过缓存系统直接写入磁盘),所以redo log buffer(重做日志缓冲)只是先刷入redo log file(重做日志文件)的缓存中,此时刷入的数据并没有落到磁盘上,之后为了确保redo log写入磁盘,就通过fsync操作将数据写入磁盘。
fsync的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能,也就是数据库的性能。
redo log写入磁盘有三种策略:
0:事务提交时redo log buffer不写入redo log,而是由主线程每1秒执行fsync写入一次redo log(也需要先刷入文件系统缓存,再写入磁盘)
1(默认):事务提交时必须进行一次fsync操作(每次commit先写入日志文件,再写入磁盘)
2:事务提交时将redo log 从redo log buffer写入重做日志文件的缓存中,不进行写入磁盘操作(在此设置下,mysql宕机而操作系统不宕机时,事务不会丢失;但操作系统宕机会导致未从文件系统缓存刷新到重做日志文件那部分事务会丢失)
数据表明在50万数据插入时,innodb_flash_log_at_trx_commit=0时用时约13.9s,为1时约1分53s,为2时约23.37s,可见为0和2时大大节省了事务提交的性能,但是丧失了其ACID特性,因此,应该将上述50万数据插入后进行一次性的commit,而不是每插入一条就commit一次。
redo log是固定大小的,所以只能循环写,当日志写满了,就需要对旧的记录进行擦除,但在擦除之前,需要确保这些要被擦除记录对应在内存中的数据页都已经刷到磁盘中了。在redo log满了到擦除旧记录腾出新空间这段期间,是不能再接收新的更新请求,所以有可能会导致MySQL卡顿。设置太大恢复时间可能很长,太小导致切换redo log文件增多,导致性能抖动。
redo log在事务执行过程中多次写入,其记录的是物理操作日志,因此每个事务对应多条日志,且是并发写入的。
重做日志块:重做日志以512字节为单位进行存储,以块的形式保存,如果一个页中重做日志数量大于512字节,需要多个重做日志块,redo log buffer就是由redo log block组成的。
由于重做日志块的大小和磁盘扇区一样都是512字节,所以其写入可以保证原子性,不必进行二次写(innodb的特性之一)。
binlog在mysql数据库上层产生,mysql中任何引擎对数据库的更改都会产生binlog,是一种逻辑日志,只在事务完成提交后写入一次。
mysql 的bin log有三种模式:
row:行模式,记录被修改的行,即使只更新一个字段,也会记录整个行。
详细记录每行数据的变更细节,不记录sql语句,binlog日志会很大,产生大量磁盘IO
statement:语句模式,记录每条会修改数据的sql语句。
减少了日志量,减少了IO,但在主从环境下,语句在不同主机的执行可能会有不同的结果,比如一些函数。
mixed:混合模式,一般语句使用statement,一些函数使用row,mysql会根据每条具体的sql语句选择。
查看bin log是否开启:show variables like 'log_bin';
查看bin log模式:show variales like 'binlog_format';
查看bin log日志列表:show binary logs;
查看某个bin log 日志内容:show binlog events in 'binlog.000002';
mysql5.6之前,在开启bin log后,innodb的group commit功能会失效,为了保证存储引擎中的事务和二进制日志的一致性,二者之间使用了两阶段事务,但仍有较大缺陷,5.7后引入了三阶段提交。
mysql的事务是默认提交的,但可以更改为手动。
redo log | undo log | bin log | |
产生 |
innodb引擎在事务执行过程中产生 每个事务可产生多条 undo本身页会产生redo log |
innodb在事务执行过程中产生 每个事务可产生多条 |
mysql数据库在事务提交时产生 每个事务产生1条 |
内容 | 物理日志,记录对页的更新操作 | 逻辑日志,记录一条数据在当前操作前的状态 | 逻辑日志,记录更新语句sql或被修改的行 |
用途 | 前滚,宕机或介质故障后恢复数据 |
回滚操作 快照读,支持MVCC |
恢复数据 主从架构 |
写入 |
循环写,固定大小,写满后擦除 文件大小可设置 |
用户可以设置存储在独立表空间或在共享表空间文件中,也需要回收 文件大小可设置
| 追加写,不会覆盖 |
位置 | 磁盘单独的日志文件 | 存放在表空间,undo段 | 磁盘单独的日志文件 |