分布式系统原理(6)日志技术

版权声明:本文为博主原创文章,转载请附上原文链接。 https://blog.csdn.net/Raito__/article/details/83270987

日志技术

日志技术是宕机恢复的主要技术之一。日志技术最初使用在数据库系统中。严格来说日志技术不是一种分布式系统的技术,但在分布式系统的实践中,却广泛使用了日志技术做宕机恢复,甚至如 BigTable 等系统将日志保存到一个分布式系统中进一步增强了系统容错能力

本章首先简单介绍数据库系统中的日志技术,进而抽象简化问题模型,在简化模型的基础上介绍两种实用的日志技术 Redo Log 与 No Redo/No undo Log

数据库系统日志技术简述

在数据库系统中实现宕机恢复,其难点在于数据库操作需要满足 ACID,尤其在支持事务(transaction)的数据库系统中宕机往往发生在某些事务只执行了部分操作的时候。此时宕机恢复的主要目标就是数据库系统恢复到一个稳定可靠状态,消除未完成的事务对数据库状态的影响。数据库的日志主要分为 Undo Log、Redo Log、Redo/Undo Log 与 No Redo/No Undo Log。这四类日志的区别在更新日志文件和数据文件的时间点要求不同,从而造成性能和效率也不相同。本文不就数据库中的这四类日志技术做深入讨论,相关信息可以参考有关数据库系统方面的资料

Redo Log 与 Check point

问题模型

首先简化原数据库系统中的问题模型为一个较为简单的模型:假设需要设计一个高速的单机查询系统,将数据全部存放在内存中以实现高速的数据查询,每次更新操作更新一小部分数据(例如key-value 中的某一个 key)。现在问题为利用日志技术实现该内存查询系统的宕机恢复。与数据库的事务不同的是,这个问题模型中的每个成功的更新操作都会生效。这也等效为数据库的每个事务只有一个更新操作,且每次更新操作都可以也必须立即提交(Auto commit)

Redo Log

Redo Log 是一种非常简单实用的日志技术。在上节的问题模型中,只需按如下流程更新既可以实用 Redo Log
Redo Log 更新流程:

1.将更新操作的结果(例如 Set  K1=1,则记录 K1=1)以追加写(append)的方式写入磁盘的
日志文件
2.按更新操作修改内存中的数据
3.返回更新成功

上述更新流程中第 2 步没有考虑修改内存数据需要多线程互斥等问题,但对于说明 Redo Log的原理没有影响。从 Redo Log 的流程可以看出, Redo 写入日志的是更新操作完成后的结果(虽然本文不讨论 Undo Log,这点是与 Undo Log 的区别之一),且由于是顺序追加写日志文件,在磁盘等对顺序写有力的存储设备上效率较高用 Redo Log 进行宕机恢复非常简单,只需要“回放"日志即可
Redo Log 的宕机恢复:

1.从头读取日志文件中的每次更新操作的结果,用这些结果修改内存中的数据

从 Redo Log 的宕机恢复流程也可以看出,只有写入日志文件的更新结果才能在宕机后恢复。这也是为什么在 Redo Log 流程中需要先更新日志文件再更新内存中的数据的原因。假如先更新内存中的数据,那么用户立刻就能读到更新后的数据,一旦在完成内存修改与写入日志之间发生宕机,那么最后一次更新操作无法恢复,但之前用户可能已经读取到了更新后的数据,从而引起不一致的问题

Check point

宕机恢复流量的缺点是需要回放所有 redo 日志,效率较低,假如需要恢复的操作非常多,那么这个宕机恢复过程将非常漫长。解决这一问题的方法即引入 check point 技术。在简化的模型下, check point 技术的过程即将内存中的数据以某种易于重新加载的数据组织方式完整的 dump 到磁盘,从而
check point:

1.向日志文件中记录“Begin Check Point”
2.将内存中的数据以某种易于重新加载的数据组织方式 dump 到磁盘上
3.向日志文件中记录“End Check Point”

在 check point 流程中,数据可以继续按照上面的redo流程被更新, 这段过程中新更新的数据可以 dump到磁盘也可以不 dump 到磁盘,具体取决于实现。例如,check point 开始时 k1=v1,check point 过程中某次更新为 k1 = v2,那么 dump 到磁盘上的 k1 的值可以是 v1 也可以是 v2
基于 check point 的宕机恢复流程:

1.将 dump 到磁盘的数据加载到内存。
2.从后向前扫描日志文件,寻找最后一个“End Check Point”日志
3.从最后一个“End Check Point”日志向前找到最近的一个“Begin Check Point”日志,并回
放该日志之后的所有更新操作日志

上述 check point 的方式依赖 redo 日志中记录的都是更新后的数据结果这一特征,所以即使 dump的数据已经包含了某些操作的结果,重新回放这些操作的日志也不会造成数据错误。同一条日志可以重复回放的操作即所谓具有“幂等性”的操作。工程中,有些时候 Redo 日志无法具有幂等性,例如加法操作、append 操作等。此时,dump 的内存数据一定不能包括“begin check point”日志之后的操作。为此,有两种方法,其一是 check point 的过程中停更新服务,不再进行新的操作,另一种方法是,设计一种支持快照(snapshot)的内存数据结构,可以快速的将内存生成快照,然后写入check point 日志再 dump 快照数据。至于如何设计支持快照的内存数据结构,方式也很多,例如假设内存数据结构维护 key-value 值,那么可以使用哈希表的数据结构,当做快照时,新建一个哈希表接收新的更新,原哈希表用于 dump 数据,此时内存中存在两个哈希表,查询数据时查询两个哈希表并合并结果

No Undo/No Redo log

本节介绍另一种特殊的日志技术“No Undo/No Redo log”,这种技术也称之为“0/1 目录”(0/1 directory)
本节介绍这种技术并不再使用上节的问题场景,而假设另一种问题场景:若数据维护在磁盘中,
某批更新由若干个更新操作组成,这些更新操作需要原子生效,即要么同时生效,要么都不生效

0/1 目录技术中有两个目录结构,称为目录 0(Directory 0)和目录 1(Directory 1)。另有一个结构称为主记录(Master record)记录当前正在使用的目录称为活动目录。主记录中要么记录使用目录 0,要么记录使用目录 1。目录 0 或目录 1 中记录了各个数据的在日志文件中的位置

下图给出了一个 0/1 目录的例子。活动目录为目录 1,数据有 A、B、C 三项。查目录 1 可得A、B、C 三项的值分别为 2、5、2
0/1目录
0/1 目录的数据更新过程始终在非活动目录上进行,只是在数据生效前,将主记录中的 0、1 值反转,从而切换主记录
0/1 目录数据更新流程:

1.将活动目录完整拷贝到非活动目录
2.对于每个更新操作,新建一个日志项纪录操作后的值,并在非活动目录中将相应数据的位置
修改为新建的日志项的位置
3.原子性修改主记录:反转主记录中的值,使得非活动目录生效

0/1 目录的更新流程非常简单,通过 0、1 目录的主记录切换使得一批修改的生效是原子的。0/1 目录将批量事务操作的原子性通过目录手段归结到主记录的原子切换。由于多条记录的原子修改一般较难实现而单条记录的原子修改往往可以实现,从而降低了问题实现的难度。在工程中0/1 目录的思想运用非常广泛,其形式也不局限在上述流程中,可以是内存中的两个数据结构来回切换,也可以是磁盘上的两个文件目录来回生效切换

工程投影

日志技术的使用非常广泛,在 zookeeper 系统中,为了实现高效的数据访问,数据完全保存在内存中,但更新操作的日志不断持久化到磁盘,另一方面,为了实现较快速度的宕机恢复, zookeeper周期性的将内存数据以 checkpoint 的方式 dump 到磁盘

MySQL 的主从库设计也是基于日志。从库只需通过回放主库的日志,就可以实现与主库的同步。由于从库同步的速度与主库更新的速度没有强约束,这种方式只能实现最终一致性

Mola与 Armor系统支持多种不同的存储引擎,对于接受到的更新操作,这两个系统将操作日志(redo log)保存到磁盘,引擎可以通过回放日志实现副本数据的同步。在 mola中,由于不需要强一致性,日志与数据分离,且日志也保存多个副本,当日志副本更新满足 quorum 要求后就返回。引擎通过回放日志的方式实现数据更新,由于回放速度不一致,mola 提供最终一致性保证。同时,由于返回用户更新成功时只保证日志更新成功,此时读取引擎数据未必可以读到最新更新的数据。Armor*中更新了这一设计,日志与数据不分离,更新日志的同时也更新引擎数据,从而可以立刻读取到成功更新的数据

猜你喜欢

转载自blog.csdn.net/Raito__/article/details/83270987