mysql数据一致性和副本复制-part2

来源:互联网 发布:诺基亚淘宝官方旗舰店 编辑:程序博客网 时间:2024/06/06 00:52

    (5) InnoDB通过在UNDO日志中进行查找,最终可以找到每一行记录的创建版本和删除版本(过期版本),从而找到每一行记录的生命周期,作为simple select建立快照的依据。MySQL 5.5.32版本- Read-view数据结构(具体代码可参见 storage/innobase/include/read0read.h、storage/innobase/read/read0read.c::read_view_open_now() 、 storage/innobase/include/read0read.ic ::read_view_sees_trx_id() )。
这里写图片描述
    InnoDB可见性判断:innoDB中每个事务在开始执行的时候,会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view,即对当前事务不可见的事务的集合),它列举了当前事务不该看到的那些活动事务,但排除了当前事务自身也排除了在内存中已经提交的事务。关于如何判断当前系统活跃事务列表中的事务是否应该进入read view(即对当前事务不可见),可以参考read view的创建代码storage/innobase/read/read0read.c的函数read_view_open_now,关于判断行记录是否可见的代码位于storage/innobase/include/read0read.ic的函数read_view_sees_trx_id。在read_vied_sees_trx_id函数里主要进行以下3种比较(非真实代码,仅描述比较算法):
    a. low_limit_id是“高水位”,即当时活跃事务中的最大事务的db_tx_id;如果读到row的db_tx_id >= low_limit_id,说明读到的这些row在low_limit_id之后(即当前事务创建之后)才完成提交,因而对当前事务是不可见的。
if (trx_id >= view->low_limit_id) {
return(FALSE);
}
    b. up_limit_id是“低水位”,即当时活跃事务中最小事务的db_tx_id;如果读到row的db_tx_id < up_limit_id,说明读到的这些row在low_limit_id之前(即当前事务创建之前)都已经提交,因而对当前事务是可见的。
if (trx_id < view->up_limit_id) {
return(TRUE);
}
    c. 当读到row的db_tx_id 落入区间[up_limit_id, low_limit_id)时,可以使用二分查找法在当前事务建立的read view中进行查找,如果在read view中找到db_tx_id,则说明事务db_tx_id当时是活跃的,因而是不可见的(有些事务虽然落入区间[up_limit_id, low_limit_id),但是由于执行时间短,已经完成提交了,不处于活动状态,因而是可见的)。
    当查找到的行记录是不可见的,需要从该行的DB_ROLL_PTR指针所指向的回滚段(UNDO日志)中取出最近的删除版本号,然后再使用上述3种比较算法判断这个版本的可见性,依此类推,此过程可能伴随递归。对于二级索引的可见性判断则需要通过聚簇索引来完成。
小节:显然,单一的更新操作(update、insert、delete)本身就是一个事务过程,在InnoDB中,查询也是一个事务,只读事务。当读写事务并发访问同一行数据时,能读到什么样的内容则依赖事务的隔离级别。在MVCC机制下,一个事务总是能看到自身开始时数据的状态,而不会受到其它事务的影响。但从MVCC的工作机制中也可以发现:快照读的结果不能作为用户执行update、delete、insert等变更操作时的判断依据,同时快照读还可能导致事务看到实际上已经根本不存在的数据或表状态等。由于MVCC仅针对普通的select,对于update、delete以及加锁的select操作MVCC并不生效–这些操作总是针对当前最新的数据。因此,如果在事务A的执行过程中,另一个事务B修改了一些行,则在事务A中用相同的where条件进行匹配时,select和update/delete很可能会找到不同的行,作为数据库的使用者需要清晰的认识到这一点。
总结:读事务一般由SELECT语句触发,在InnoDB中保证其非阻塞,但带FOR UPDATE的SELECT事务除外,带FOR UPDATE的SELECT事务会对行加排它锁,等待更新事务完成后读取其最新内容,就整个InnoDB的设计目标来说,就是提供高效的、非阻塞的查询操作。
补充
<1>在InnoDB聚簇索引中每行记录都实现了三个隐藏字段:6字节的事务ID(DB_TRX_ID)、7字节的回滚指针(DB_ROLL_PTR)、隐藏的ROW_ID。6字节事务ID用来标识该行所述的事务,7字节的回滚指针指向行的前一个过期版本,从而可以形成一个过期版本的链供查找历史版本或回滚。可见每一个record(row)都有一个行版本号和最近的一个过期版本号(或不存在)。
<2>对于被标记为删除的行,InnoDB有专门的线程(purge线程)负责进行物理删除,当行可以被物理删除时,必须满足:当前不存在版本号小于该删除行版本号的事务,这样可以确保不会有事务再引用到该行。
<3>每开始一个新的事务,系统版本号都会递增,事务开始时刻的系统版本号作为事务的版本号,用来和查询到的每行记录的版本号做比较。
1.4 Redo-log与Undo-log
    为了满足事务的持久性,防止buffer pool数据丢失,InnoDB引入了redo log。为了满足事务的原子性,InnoDB引入了undo log。
(1)Redo–log:redo log记录数据库中每个数据页(Page)的变更情况,当mysql执行数据恢复时,重新执行redo log即可。引入buffer pool会导致更新的数据不会实时持久化到磁盘,当系统崩溃时,虽然buffer pool中的数据丢失,数据没有持久化,但是系统可以根据Redo Log的内容,将所有数据恢复到最新的状态。redo log在磁盘上作为一个独立的文件存在。默认情况下会有两个文件,名称分别为 ib_logfile0和ib_logfile1。记录的内容格式是:< space id, page no, operation code, data>,为了满足幂等规则,InnoDB中每个数据页上都记录一个LSN。每次更新数据页时,将LSN修改为当前操作的redo log的LSN。在恢复时,如果数据页的LSN大于等于当前redo log的LSN,则跳过此日志。
(2)Undo-log:为了满足事务的原子性,在操作任何数据之前,首先将数据备份到undo log,然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用undo log中的备份将数据恢复到事务开始之前的状态。与redo log不同的是,磁盘上不存在单独的undo log文件,它存放在数据库内部的一个特殊段(segment)中,这称为undo段(undo segment),undo段位于共享表空间内。
(3)Undo-log持久化:undo log和 redo log本身是分开的。InnoDB的undo log是记录在数据文件(ibd)中的,而且innodb将undo log的内容看作是数据,因此对undo log本身的操作(如向undo log中插入一条undo记录等),都会记录redo log。undo log可以不必立即持久化到磁盘上,即便丢失了,也可以通过redo log将其恢复。
(4)Redo-log持久化:随着时间的积累,redo log会变的很大。如果每次都从第一条记录开始恢复,恢复的过程就会很慢,无法被容忍。为了减少恢复的时间,就引入了checkpoint机制。假设在某个时间点,所有的脏页都被刷新到了磁盘上,这个时间点之前的所有redo log就不需要重做了。系统记录下这个时间点时redo log的结尾位置作为checkpoint。在进行恢复时,从这个checkpoint的位置开始即可,checkpoint点之前的日志也就不再需要了,可以被删除掉。
    由于InnoDB以page为最小处理单元,而操作系统磁盘操作原子单位往往与其大小不符(常见的InnoDB page为16K,操作系统4K),此时会存在partial page write problem,发生部分写时,由于磁盘上page物理数据受到了污染,InnoDB无法确认该page的正确性(checksum校验),此时没法通过磁盘数据+redo-log进行推导,因此无法恢复。如果可以保证InnoDB page的写入是原子的就不会存在改问题(比如有些RAID可以保证)。
    InnoDB采用DoubleWriteBuffer来解决该问题,DoubleWriteBuffer是开辟在InnoDB tablespace文件上的一块由许多连续page组成的空间,在数据刷新磁盘过程中,先将物理数据连续写到DoubleWriteBuff后,再将物理数据离散写到数据实际位置。为了提高I/O性能,redo-log、undo-log等在内存也均有buff,数据写入磁盘的顺序是:redo-log(内存buff) 写入磁盘,数据(内存buff)写入doubleWriteBuff,数据(内存buff)写入实际磁盘位置。显然,写入磁盘前内存中的redo-log、undo-log中包含的事务数量阈值决定了完成故障恢复后的数据和真实数据之间的新旧差异程度。

故障发生时间点 恢复策略 redo-log写入磁盘过程中 redo-log写入磁盘未成功代表事务未成功 redo-log成功后,数据写入doubleWriteBuff前 buffpool数据丢失,但可通过磁盘数据+redo-log推导得出 数据写入doubleWriteBuff过程中 磁盘数据正确,可以通过磁盘数据+redo-log推导得出 数据写入实际磁盘位置过程中 磁盘数据污染,但doubleWriteBuff数据正确,直接用其覆盖即可
0 0
原创粉丝点击