事务的隔离性是由锁实现的,原子性、一致性、持久性通过数据库的red log和undo log来完成。redo log称为重做日志,用来保证事务的原子性和持久性(恢复提交事务修改的页操作,通常是物理日志,记录的是页的物理修改操作)。 undo log用来保证事务的一致性(undo是逻辑日志,根据每行记录进行记录)。
一、 redo日志
分成两部分,①内存中的重做日志缓冲(redo log buffer),是容易丢失的;②重做日志文件(redo log file),是持久的。通过Force Log at Commit机制实现事务的持久性,即当事务提交时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT操作完成才算完成。 这里的日志是指重做日志,在InnoDB中,由redo log和undo log组成, redo log是用来保证事务的持久性,undo log 用来帮助事务回滚和MVCC的功能。 redo log 基本上都是顺序写的,在数据库运行是不需要对redo log文件进行读取操作。而 undo log是需要进行随机读写的。
1. 重做日志怎么保存到硬盘的
为了确保每次的日志都写入到硬盘,需要将重做日志缓冲写入日志文件后,InnoDB都需要调用一次fsync操作(相对于是能确定把数据写到硬盘操作),因为fsync操作是硬盘,所以硬盘的性能决定了事务提交的性能,也就是数据库的性能。
innodb_flush_log_at_trx_commit 控制重做日志刷新到磁盘的策略,默认值1(表示每次事务提交时候必须调用一次fsync操作); 0表示事务提交时不进行写入重做日志操作,这个操作由master thread完成, master thread会每1秒进行一次重做日志的fsync操作; 2表示事务提交时将重做日志写入重做日志文件,但知识写入文件系统中缓存中,不进行fsync操作。这种情况就是数据库发生宕机了,重启数据库会丢失从文件系统缓存刷新到重做日志文件的那部分事务。
2. 和二进制日志的区别(binlog)
①重做日志是在InnoDB存储引擎产生的,而二进制日志是在Mysql数据库的上层产生的,二进制日志是记录任何存储引擎的对于数据库的更改。
②两种日志的记录的内容形式不同。 二进制日志是一种逻辑日志,记录的是对应的sql日志。InnoDB的重做日志是物理格式的日志,记录的是对于每个页的修改。
③两种日志写入磁盘的时间点不同,二进制日志只在事务提交完成后进行一次写入。 而InnoDB存储引擎的重做日志在事务进行中不断被写入
3. 重做日志的存储
① 重做日志块(redo log block ) 重做日志都是以512字节进行存储的,重做日志缓存、日志文件都是以块(block)方式保存的,
② 充值日志块由日志块头(log block header) 占12字节、日志块尾巴(log block tailer) 占8字节、日志本身(492字节)组成。
下图重做日志块缓存的结构
③ 日志块头 log block header的组成
④log buffer根据一定的规则将内存中的log block刷新到磁盘
⑤ 重做日志的格式
4. LSN (log sequence number) 日志序列号
LSN表示事务写入重做日志的字节总量。 例如当前重做日志LSN为1000,有一个事务T1写入了100个字节的重做日志,那么LSN中就变成了1100。
通过命令 show engine innodb status 查看LSN的情况
Log sequence number表示当前的LSN
Log flushed up to 表示数学到重做日志文件的LSN
Log checkpoint at 表示刷新到磁盘的LSN
5. 恢复
二、undo日志
重做日志记录了事务的行为,可以很好的通过其对页进行“重做”操作。但是事务还需要进行回滚操作,这时候就需要undo。 如果用户执行的事务或语句又要某种原因失败了,或者用户使用了ROLLBACK语句请求回滚,就可以利用undo日志将数据回滚到修改之前的样子。
undo存放到数据库内部的一个特殊段(segment)中,这个段称为undo段(undo segment)。 undo段位于共享表空间内。
① undo是逻辑日志,是将数据库逻辑的恢复到原来的样子。而不是将数据库物理的恢复到执行语句或者事务之前的样子。所有的修改都是被逻辑修改了,数据库和页本身在回滚之后可能大不相同。 这是因为在多用户并发系统中,可能会有数千个并发事务,数据库的主要任务是协调对数据记录的并发访问。比如:一个事务在修改当前一个页中的某几个记录,同时还有别的事务在对同一个页中另外几条记录进行修改。因为,不能将一个页回滚到事务开始的样子。
② innoDB存储引擎中的MVCC(MVCC
,全称 Multi-Version Concurrency Control
,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。)的实现是通过undo来完成的。
③ undo log会产生redo log, 也就是undo log的产生会伴随redo log的产生,这是因为undo log也需要持久性的保护。
④ undo 的管理采用了段的方式,InnoDB有rollback segment,每个回滚段种记录了1024个undo log segment, 每个undo log segment段种进行undo页的申请。共享表空间偏移量为5的页(0,5)记录了所有rollback segment header所在的页,这个页的类型为FIL_PAGE_TYPE_SYS.
④ rollback segment都存储共享表空间中。 可通过参数对rollback segment做进一步的设置(innoDB版本需要>=5.6)
innodb_undo_directory 设置rollback segment文件所在的路径,该参数的默认值为“.”,表示当前InnoDB存储引擎的目录。
innodb_undo_logs 用来设置rollback segment的个数,默认值是128,在InnoDB1.2版本中,该参数用来替换之前版本的参数innodb_rollback_segments.
innodb_undo_tablespaces 设置构成rollback segment文件的数量,这样rollback segment可以较为平均的分布在多个文件中。
⑤ 当事务提交时,InnoDB会做以下两件事情:
a. 将undo log放入列表中,以供purge操作。
b. 判断undo log所在的页是否可以重用,若可以分配给下一个事务使用。
事务提交后并不能马上删除undo log及undo log所在的页。 这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本。故事务提交时将undo log放入一个链表中,是否可以最终删除undo log及undo log所在页由purge线程来判断。
可以通过show engine innodb status开查看链表中undo log的数量
History list length 代表了undo log的数量,这里为1327.
⑥ undo log格式
undo log分为:insert undo log 和 undate undo log
insert undo log是指在insert操作中产生的undo log. 因为insert 操作的记录,只对事务本身可见,对其他事务不可见(事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作。
update undo log 是对delete和update产生的undo log, 该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。
⑦ delete操作
delete操作并不是直接删除记录,而只是将记录标记已删除,也就是将记录的delete flag设置为1,而最终删除的是在purge操作中完成的。
⑧ update主键操作分两步, 首先将原主键记录标记为已删除,因为需要产生一个类型为TRX_UNDO_DEL_MARK_REC的undo log, 之后插入一条新的记录,因此需要产生一个类型TRX_UNDO_INSERT_REC的undo log。 undo_rec_no显示了产生的日志的步骤。
⑨ purge操作
purge用于最终完成delete和update操作。这样设计是因为InnoDB支持MVCC,所以记录不能在事务提交时立即进行处理。这时其他事物可能正在引用这行,故InnoDB需要保存记录之前的版本。而是否删除该条记录通过purge来判断。若该行记录已不被任何其他事务引用,那么就可以进行真正的delete操作。可见,purge操作是清理之前的delete和update操作,将上述操作"最终"完成,而实际执行的操作为delete操作,清理之前行记录的版本。
undo log是这样设计的:一个页允许多个事务的undo log存在。虽然不能代表事务在全局过程中提交的顺序,但是最后的事务产生的undo log总在最后。innodb 有一个history列表,它根据事务提交的顺序,将undo log进行链接。
InnoDB从history list中找undo log,然后再从undo page中找undo log的设计模式是为了避免大量的随机读取操作,从而提高purge的效率。
innodb_purge_batch_size用来设置每次purge操作需要清理的undo的数量。默认值20.设置的越大,每次回收的undo page越多,可提供的undo page越多,减少了磁盘存储空间与分配的开销。
该参数设置的太大,则每次需要purge处理更多的undo page,从而导致cpu和磁盘的IO过于集中对于undo log的处理,使性能下降。
innodb_max_purge_lag 用来控制history list的长度,若长度大于该参数时,其会“延缓”DML的操作,该参数默认值为0,表示不对history list做任何限制。当大于0时,就会延缓DML的操作。
InnoDB1.2引入了新的全局动态参数innodb_max_purge_lag_delay。用来控制delay最大的毫米数,当计算的delay值大于该参数时,将delay设置innodb_max_purge_lag_delay。 避免了purge操作缓慢导致其他sql线程出现无限制等待。
三、 group commit
若事务为非只读事务,则每次事务提交时需要进行一次fsync操作,以此保证日志都已经写入磁盘。当数据库发生宕机,可以通过重做日志进行恢复。为了提高磁盘fsync的效率,当前数据库都提供了group commit功能,即一次fsync可以刷新确保多个事务日志被写入文件。
对于InnoDB,事务提交时进行两个阶段的操作
a. 修改内存中事务对应的信息,并将日志写入重做日志缓冲。
b. 调用fsync将确保日志都从重做日志缓冲写入磁盘。
group commit将多个事务的重做日志通过一次fsync刷新到磁盘,减少了磁盘的压力,从而提高了数据库的整体性能。 InnoDB1.2版本之前,开启二进制日志后,group commit功能会失效。
Mysql5.6 BLGC(binary log group commit)
四、 对事务的统计
TPS(ftransaction per second) 每秒事务处理的能力
TPS = (com_commit+com_rollback) / time
Com_commit
:MySQL从上一次启动到当前所执行的提交语句总数量Com_rollback
:MySQL从上一次启动到当前所执行的回退语句总数量
查看com_commit SHOW GLOBAL STATUS LIKE 'com_commit';
查看com_rollback SHOW GLOBAL STATUS LIKE 'com_rollback';