Berkeley DB -- DB Replication (HA)上部
Introduction
bdb包括对构建基于复制(replication)的高可用性应用程序的支持。bdb replication组由一些独立配置的数据库环境组成。
组里只有一个master数据库环境和一个或多个client环境。Master环境支持读和写,client环境支持只读。如果master环境倒掉了,应用程序将可能提升一个client为新的master。数据库环境可能在单独的计算机上,在单独的硬件分区上(partitions)一个不统一的内存访问系统上,或在一个单独的server的一个磁盘上。唯一的约束就是,replication组的所有的参与者必须在一个字节序(endianness)相同的机器上(都是大数再前或都是小数在前的操作系统)。我们期望这个约束在以后的版本中会去掉。因为总是用bdb环境,任何数量的并发进程或线程可能访问一个数据库环境。在master环境中,多个线程可能读写这个环境。在client环境中,多个线程可能要读这个环境。
应用程序可能被编写成在master和clients间提供不同程度的稳固性。系统能同步的运行以便复制品(replicas)能保证是最新的,对应于所有已提交的事务。但是这样做可能回招致性能上的很大的下降。高性能解决方案有考虑全局的稳固性,允许clients的数据过时一个应用程序可控制的一段时间。
尽管bdb包括必要的构建高可用性数据库环境的底层基础,应用程序仍然必须提供一些鉴定的(critical)组成部分:
应用程序有责任提供通信下部构造。应用程序可能用任何适当的通信协议。例如RPC, TCP/IP, UDP, VI或底板(backplane)消息传递。
应用程序有责任命名。bdb涉及到一个replication组成员的时候是靠一个应用程序提供的id,应用程序必须映射那个id到一个特殊的数据库环境中或通信通道中。
应用程序有责任监视master和clients的状态,和识别任何不可用的(unavailable)数据库环境。应用程序必须提供所有的需要的安全策略。
例如,应用程序可能选择去加密数据,用一个安全的套接层,或什么也不做。
最后,bdb replication实现还有一个附加的特性去增强可靠性。bdb中的replication实现成执行数据库更新用一个不同的编码路径而不是用标
准的。这意味着,有bug的软件的操作可能会毁坏replication master,但不会把clients也毁坏。
Replication和相近的方法的描述:
DB_ENV->rep_elect 举行一个replication竞选
DB_ENV->rep_process_message 处理一个replication消息
DB_ENV->rep_stat Replication统计
DB_ENV->rep_sync Replication同步
Replication 配置:
DB_ENV->rep_set_config 配置replication系统
DB_ENV->rep_start 为replication配置一个环境
DB_ENV->set_rep_limit 限制在响应但个消息时的数据发送
DB_ENV->set_rep_transport 配置replication传输
Replication environment IDs
每个在replication组中的数据库环境必须有一个独一无二的标识符,为它自己和其它replication组中的成员都分配一个不同标识符。这些标识符不必要是全局的,也就是说,每个数据库环境可以分配本地化的标识符给replication组的成员。就是在每数据库环境中都能区分出其他成员就行了,当然全局统一给指定标识符也不为错,只是非必要的。
应用程序有责任去标志每个进来的传递给DB_ENV->rep_process_message的有适当标识符的replication消息。随后,bdb将用这些相同的标识符去标志发送函数发出去的消息。
负标识符被bdb保留使用,不应该被应用程序指定给那些环境。有两个保留的标识符准备给应用程序使用的是:
DB_EID_BROADCAST:指定一个消息应该被广播给所有replication组中的成员。
DB_EID_INVALID:是一个无效的环境id,可能被用于初始化一些环境id变量,那些变量随后被检查合法性。
Replication environment priorities
每个replication组中的数据库环境变量必须有一个优先权,它指定了在replication组中不同环境间的一个相对的顺序。这个顺序在币桓鰉aster倒掉,在决定选举哪个环境作为新master的时候的一个重要因素。优先权必须是一个非负的整数,但不必要replication组中是独一无二的。优先权为0意味着这个系统永远不能成为一个master,是被忽略的。数越大表示优先权越高,例如,如果一个replication组由3个数据库环境组成,两个由OC3 连接,第三个由T1连接,那么第三个数据库环境就应该被指定一个低点的优先权。
决定哪个client将当选为master时,先看谁有最多的近期的log记录。log记录一样多时,将参考优先权。如果log和优先权一样多时候,将随即
选择一个。
Building replicated applications
最简单的方法去构建一个replicatedbdb应用程序,是首先构建(和调试)这个应用程序的一个事务版本。然后,添加一个薄的replication层到这个应用程序。所有高可用性应用程序用下面的附加的四个bdb方法:DB_ENV->rep_elect, DB_ENV->rep_process_message, DB_ENV->rep_start 和 DB_ENV->set_rep_transport 还有可能用这个配置方法DB_ENV->set_rep_limit。
DB_ENV->set_rep_transport:配置replication系统的通信底层构造。
DB_ENV->rep_start:配置,或重新配置一个已经存在的数据库环境成为一个master或client.
DB_ENV->rep_process_message:处理从replication组中别的环境进来的一个消息。对clients来说,它负责接受log记录和根据master过来的消息更新本地数据库。对master和clients来说,都有责任处理一些行政的功能(例如,处理消息丢失的协议),和允许新的clients加入到活动的replication组。这个方法只应该在环境已经通过DB_ENV->rep_start被配置成一个master或client后被调用。
DB_ENV->rep_elect:使replication组选举一个新的master,它在clients与master失去联系的时候和应用程序想在剩余的站点中选择一个新master的时候被调用。
DB_ENV->set_rep_limit:在响应一个单独的调用DB_ENV->rep_process_message时,将要发送的数据量上强加一个上限。当一个客户执行恢复的时候,也就是说,当一个replica站点是同去同步master的时候,clients可能找master要大量的log记录,那样它将使它们之间在未来一段时间内忙于循环发送大量数据,那么这时候可能就想在失去控制和接受其他正常消息前,用DB_ENV->set_rep_limit限制master将发送的数据量。
为了把replication添加到应用程序,应用程序初始化必须改变,还有应用程序的通信底层构造必须编写。应用程序初始化改变相对简单,通信底层构造编码可能会很复杂。
由于实现的的原因,所有replicated的数据库必须放在环境指定的数据目录中。如果你的数据库放在默认的环境home目录中,他们必须就放在本目录中,而不是子目录中。必须注意那些使用相对路径和在打开环境后更改工作目录的应用程序。在这中应用程序中,replication初始化代码将找不到数据库。而那些改变工作目录的可能需要使用绝对路径。
在应用程序初始化期间,应用程序执行三个附加的任务:第一,当打开它的数据库环境的时候,它必须指定DB_INIT_REP标志。第二,它必须提供关于它的通信底层构造bdb信息。第三,它必须启动bdb replication系统。通常,一个replicated应用程序将做正常的bdb恢复和配置,非常像其他的事务应用程序。然而,一旦数据库环境被打开,它将调用DB_ENV->set_rep_transport方法去配置bdb的replication,
and 然后将调用DB_ENV->rep_start方法去加入或创建replication组。
当在应用程序启动时调用DB_ENV->rep_start的时候,应用程序有两个选择:通过配置为组内指定一个master,或者,可选择,配置所有组内成员为clients然后调用一个选举方法,在它们之间选择一个master。每种方法都只正确的,完全取决于你的应用程序。DB_ENV->rep_start调用的结果,往往是发现了一个master,或者,声明本地环境作为master。如果在一段可以接受的时间内还没找出一个作为master,应用程序应该调用DB_ENV->rep_elect搞一次选举。
考虑到多进程或多环境句柄修改在replicated环境中的数据库的情况。所有的修改必须在master环境中完成。第一个进程加入或创建master环境必须调用DB_ENV->set_rep_transport方法和DB_ENV->rep_start方法,随后的一些replication进程必须至少DB_ENV->set_rep_transport方法。这些进程有可能调用DB_ENV->rep_start方法(只要他们使用相同的master或者client参数)。如果多个进程正在修改master环境,这儿必须有一个统一标准的通信底层构造,以便到达clients的消息有一个单一的master ID。附加地,应用程序必须被构造成以便于所有进来的消息能被一个单一的DB_ENV句柄处理。
请注意,不是所有的运行在replicated环境中的进程都需要调用DB_ENV->set_rep_transport或DB_ENV->rep_start。
在master环境中运行的只读进程在任何情况下都不必要配置成replication。那些运行在clients环境中的进程在定义时就是只读的,所以也不必要配置成replication的(尽管,clients在一些情况下有变成master的可能,通常最简单的是,在进程启动的时候配置成replication的,而不是当一个clinets变成master的那个时候试图去配置)。很显然,在每个client上至少一个控制线程必须被配置成replication的,因为消息继续在master和client间被传递。
由于实现的原因,所有的进入的replication消息必须用同一个DB_ENV句柄处理。它不要求一个单一的控制线程处理所有的消息,只要求所有的控制线程用同一个句柄处理消息。
没有附加的要求去调用一个方法关闭一个由多方参加的replication组中数据库环境。应用程序应该像平时一样通过调用DB_ENV->close关闭环境。
Building the communications infrastructure
应用程序具备replication支持,典型的被写成一个或多个控制线程循环在一个或多个通信通道上,接受和发送消息。这些线程为本地数据库环境从远程环境接受消息,和为远程环境从本地环境接受消息。远程环境消息通过DB_ENV->rep_process_message方法从远程环境被传递到本地数据库环境。本地环境的消息通过指定给DB_ENV->set_rep_transport方法的回调函数,被发送出去。
那些进程通过调用DB_ENV->set_rep_transport方法建立通信通道。而不管它是运行在client还server环境上。这个方法指定发送函数,一个bdb用来发送消息给组内的其它数据库环境的回调函数。这个函数携带一个环境id和两个不透明的数据对象。
发送函数有责任根据id传送两个数据对象里的信息到特定的数据库环境,而后,[那边的]接受应用程序调用DB_ENV->rep_process_message方法去处理这个消息。
传输机制的细节完全留给了应用程序;唯一的要求是,每个控制器(进程或线程)的数据缓冲区和大小,和发送站点上传送给发送函数的那些DBT数据结构,通过调用加以适当的参数调用DB_ENV->rep_process_message被如实的拷贝和交付到接受站点。被广播的消息(无论是被广播媒体广播还是当直接通过设置DB_ENV->set_rep_transport方法的参数DB_EID_BROADCAST),不应该被消息发送器处理。在所有的情况下,应用程序的传输媒体或软件必须确保,当打算把一个消息发送给不同的数据库环境的时候从不调用DB_ENV->rep_process_message,或者,从同一个环境发送的广播消息,在这个环境上DB_ENV->rep_process_message 将被调用。DB_ENV->rep_process_message方法是免线程的(free-threaded),它可以安全地同时地交付任意数量的消,这些消息可以是来自这个bdb环境中任意进程或线程的。
这儿有一些DB_ENV->rep_process_message方法返回的信息(返回值):
DB_REP_DUPMASTER:意味着组内的另一个数据库环境也相信它(另一个环境)自己将成为一个master。这个应用程序应该完成所有活动的事务,关闭所有打开的数据库句柄,用DB_ENV->rep_start方法重新配置它自己为一个client,然后通过调用DB_ENV->rep_elect号召一次选举。
DB_REP_HOLDELECTION:意味着组内的另一个环境已经号召了一个选举。这一应用程序应该调用DB_ENV->rep_elect参与选举。
DB_REP_IGNORE:意味着着个消息不能被处理。它一般暗示这个消息跟现在的replication状态不相关,就像一个迟到的过期的老消息。
DB_REP_ISPERM:意味着一个持久的消息,也许是一个以前返回的消息要作为一个记录被写入的。ret_lsnp包含这个永久记录的写入最大LSN。
DB_REP_NEWMASTER:意味着一个新的master已经被选举出来了。这个调用也返回master对应的本地id。如果这个master id改变了,这个应用程序可能需要重新配置自己(例如,更新数据时,直接询问新的master而不是旧的)。如果新master就是本地环境自己,那么这个应用程序必须调用DB_ENV->rep_start方法,重新配置自己作为一个replication组的master来支持bdb库。
DB_REP_NEWSITE:意味着收到了组内的一个未知成员的消息。应用程序应该重新配置它自己以便它能发送消息到那个站点。
DB_REP_NOTPERM:一个标志着DB_REP_PERMANENT的消息被成功处理,但是没有被写入磁盘。这通常是暗示一个或多个消息本应该在这个消息之前到达,但没有到。这个操作将被写入磁盘当丢失的消息到达的时候。参数ret_lsnp将包含这条记录的LSN 。这个应用程序应该施行所有认为必要的措施来保持它的可恢复性特征。
DB_REP_STARTUPDONE:意味着client已经完成了他的启动同步活动,现在正在处理来自master的活的日志消息。活的日志消息是指:master正在发送的,将要发送出来的消息,因为反对重新发送那些将由client请求的日志消息。
Connecting to a new site
为了添加一个新站点到replication组,所有需要做的就是,这个client成员的加入。bdb将从master到client自动地执行一个内部初始化,然后,将为刚加入的成员执行恢复,以使它的数据能和master同步。
无论何时,当DB_ENV->rep_process_message返回DB_REP_NEWSITE的时候,连接新的站点到组内将会发生。这个应用程序应该分配给这个新站点一个本地环境id号,将来所有的来自这个站点的传递给DB_ENV->rep_process_message消息都应该包含那个环境id号。当然,在DB_ENV->rep_process_message返回之前应用程序就能知道一个新站点也是可能的(例如,应用次序使用面向连接的协议很有可能立即探测到一个新站点,然而,应用程序使用广播协议就不可能了)。
无论怎样,只要在应用程序中支持动态添加数据库环境到replication组,环境添加到一个已经存在的组可能需要提供联系信息(例如,在一个应用程序中使用TCP/IP套接字,一个域名或IP地址都将是一些需要提供的合理的值)。这些可以用DB_ENV->rep_start方法的cdata参数来完成。cdata所引用的信息被预先包装在这个新环境发送的初始的联系信息中,和通过使用DB_ENV->rep_process_message返回的rec 参数,被提供给组内已存在的成员。如果没有附加的信息被提供给bdb以转交给组内已存在的成员,那么在DB_ENV->rep_process_message返回DB_REP_NEWSITE之后,传递给DB_ENV->rep_process_message方法的rec参数的数据域将为NULL。
Elections
应用程序有责任发起选举。举行一次选举将没有任何风险,因为bdb选举步骤保证这儿没有一个以上的master数据库环境。
无论何时,当Clients与master环境失去联系的时候,当他们看到DB_ENV->rep_process_message方法返回DB_REP_HOLDELECTION 的时候,或当不管由于什么原因,使他们不知道谁是master的时候,他们都应该发起一次选举 。应用程序不必要在一开始启动的时候就立即举行选举,因为任何已经存在的master将会在调用DB_ENV->rep_start后被发现。如果在一小段等待时间后后没发现master,那么应用程序就应该发起一次选举。
为了使一个client去赢得选举,组内选择必须没有master,而且这个client必须有最多的最近的 log记录,有相同数量记录的,优先权高者获
胜。应用程序指定最小量的组内成员必须参与将要要公布胜出者的选举。我们推荐至少((N/2) + 1)个成员参加。如果少于或等于半数参加,将给予一个警告。
如果一个应用程序的策略是这样的:哪一个站点将赢得选举可以在数据库环境信息的条目里参数化的(也就是说,站点的数目,可用log记录,和相对优先权都是那些参数),那么bdb就可以透明的处理所有的选举。尽管如此,这儿还存在这种情况,就是应用程序完全知道而且需要去干涉选举结果。例如,应用程序可能被选定去处理master的挑选工作,明确的指明master和clients站点。应用程序在这些情况下,可能永远不需要发起一次选举。作为选择,应用程序可能会选择使用DB_ENV->rep_elect的参数,去强制正确的选举结果。也就是说,如果一个应用有3个站点,A, B, 和 C,当C倒掉后A必须成为胜出者,应用程序可以通过在选举后指定适当的优先权保证选举的结果:
on A: priority 100, nsites 2
on B: priority 0, nsites 2
用DB_ENV->rep_start方法配置多于一个以上的master是很危险的,应用程序应该小心的不这样做。应用程序应该只配置他们自己作为master,如果他们是仅有的可能成为master的站点,或者,如果他们赢得了选举。一个应用程序只能知道它赢得了选举,如果DB_ENV->rep_elect方法返回成功信息,和本地环境的id号成为新的master环境id,或者,如果DB_ENV->rep_process_message返回DB_REP_NEWMASTER和本地环境的id号成为新的master环境id。
为了添加一个数据库环境到组内,意图成为一个master,首先作为一个client添加它。因为它可能数据过时,还需要考虑现在的master,允许它从现在的master那里更新自己的数据。然后,关闭现在的master。推测,这刚添加的client将要赢得随后的选举。如果这个client没有赢得选举,很有可能是没有给它足够的时间从当前的master那去更新它自己的数据。
如果一个client不能找到一个master或赢得一场选举,那意味着,网络被隔离,这儿没有足够的环境一起参与这次选举使得其中一个参与者能够胜出。在着种情况下,应用程序应该重复地调用B_ENV->rep_start和DB_ENV->rep_elect,交互地尝试去发现一个已存在的master,和举行一次选举宣布一个新的master。在一个令人绝望的环境中,一个应用程序可能通过调用DB_ENV->rep_start简单的宣布自己成为master,或通过减少所需的参与者数目去赢得一个选举,直到这个选举胜出。
下面这些解决方案都是不推荐使用的:在网络被隔离的情况下,下面任何一个选择都可能导致组内有两个master,环境中的数据库可能会不可挽救的产生分歧,因为它们被那些masters以不同的方法修改了。在双系统replication组的情况下,应用程序可能想要求访问一个远程网络站点,或一些其它外部的加赛(tie-breaker)以允许系统宣布自己为master。
如果好多系统同时倒掉,一个非首选的数据库环境赢得选举也是可能的。因为一个选举的胜出者很快就会被宣布,如果有足够多的环境参与这场选举的话 。在一个速度慢,但是连接良好的机器上的环境,可能会和一个速度快但是连接很糟糕的环境失去联系。在好多环境同时倒掉的情况下(例如,一批replicated机器在同一个机房),应用程序一开始应该引导这些数据库环境作为clients上线(那样将允许它们立即去处理读请求),然后等足够的时间后,等稍微慢些的机器都都追赶上之后就举行一场选举。如果,不管什么原因,一个非首选的数据库环境成为了master,在一个replicated环境中更换master是可能的。例如,首选的master倒掉了,组内一个client成员成为了新master。为了使首选的master再恢复到它master的地位,跟着下面的做就可以:
一,首选的master应该重新启动,重新作为一个client加入到组内。
二,一旦首选的master追赶上了组内的进度(数据同步的进度),当前的master应该完成所有当前的活动的事务,然后重新用DB_ENV->rep_start方法配置自己成为一个client上线。
三,然后,当前的master,或那个首选的master应该用DB_ENV->rep_elect方法发起一次选举。