转至:http://blog.sina.com.cn/s/blog_466c6640010002h4.html
Synchronizing with a master
当一个client探测到replication组内一个新的master后,在它能去处理新的数据库变化之前,这个client必须去同步这个新的master。同步是一个重量及操作,它能同时给
这个client和master增加负担。这儿有一些措施,一个应用程序可以用来减轻同步的负担。
这个client和master增加负担。这儿有一些措施,一个应用程序可以用来减轻同步的负担。
延迟client同步:
当组内有了一个新的master,不论是被应用程序指定的还是因为选举的结果,所有的clients必须去同步这个新的master。这会使新的master的资源过度损耗,因为很多clients可能都试图去和它通信和从它那儿取得记录。clients应用程序如果想延迟client的同步,应该用DB_REP_CONF_DELAYCLIENT标志去调用DB_ENV->rep_set_config 方法。这个配置使得client总从DB_ENV->rep_process_message方法返回DB_REP_NEWMASTER,但是这个client将不会继续去同步这个新的master。Client端应用程序选择延迟同步,用这种方法,那么将来再用DB_ENV->rep_sync方法去同步将是可靠的。
client-to-client的同步:
Clients可以接受和服务其他clients的请求。Clients请求记录调用这个Client应用程序的传输回调函数。可以由其他Clients满足的请求,传输函数的标志被设置成DB_REP_ANYWHERE。应用程序可以选择发送这些请求到任意client,或者忽略这个标志,而把请求发送到这个消息的环境id所指定的站点。
Client应用程序可以用不管什么算法,它们选择来负载均衡到其它clients的请求。
任何client接受到一个它不能满足的请求,都将回复给请求的client,告诉它,自己不能够提供请求的信息,而那个最初的请求者,将重新请求这个信息。另外,如果这个最初的请求没有到达发要发送给的那个目标client端,这个最初的发送请求的client也将重新请求这个信息。这这些任意一种情况下,这个重新的请求将把传输函数的标志设置成DB_REP_REREQUEST 。应用程序可能通过把这个请求进一步传递给naster来响应这个DB_REP_REREQUEST 标志(因为是被这个消息的环境id指定的),或继续把这个请求传送给另一个client。
任何client接受到一个它不能满足的请求,都将回复给请求的client,告诉它,自己不能够提供请求的信息,而那个最初的请求者,将重新请求这个信息。另外,如果这个最初的请求没有到达发要发送给的那个目标client端,这个最初的发送请求的client也将重新请求这个信息。这这些任意一种情况下,这个重新的请求将把传输函数的标志设置成DB_REP_REREQUEST 。应用程序可能通过把这个请求进一步传递给naster来响应这个DB_REP_REREQUEST 标志(因为是被这个消息的环境id指定的),或继续把这个请求传送给另一个client。
这延迟的同步和client-to-client的同步特性允许应用程序在replication组内做负载均衡。例如,考虑到一个组内有5个站点, A, B, C, D 和 E,站点E刚刚倒掉了,站点A被选举为master。站点C 和 D 被配置成延迟同步的,当B注意到站点A是一个新master,它立即进行同步。当B完成和master的同步的时候,站点C 和 D 上的应用程序调用 DB_ENV->rep_sync 方法,使它们也进行同步。站点C 和 D (和 E, 当它完成了重新引导) 可以发送他们的请求到站点 B, 而B可以负担因为同步而产生的工作和网络流量的冲击, 使的master,站点A,有空去处理正常的应用程序工作量和由于选举而暂停的写请求。
阻塞client的操作:
Clients在处理和master的同步的时候将阻塞bdb其它的操作。默认的,许多bdb方法将阻塞,直到client同步完成,然后,这些被阻塞的方法才继续调用。
那些不能等待的Client应用程序,将更愿意立即得到一个错误返回而不是阻塞,那么就应该用DB_REP_CONF_NOWAIT 标志调用DB_ENV->rep_set_config方法。如果这个clinet现在正在同步一个master,这个配置使得bdb方法调用立即返回一个DB_REP_LOCKOUT 错误,而不是阻塞。
Clients太落伍以至于不能同步:
Clients试图去和master同步的时候可能会发现,同步是不可能的了,因为client和master 已经失去联系很久了。默认地,client和master自动的检测这个状态和执行clinet内部初始化。因为内部初始化需要传输整个数据库到clinet,这可能会发费一个相对较长的时间,可能需要clinet应用程序的数据库句柄重新被打开。
那些不能等待的应用程序,更愿意推后内部初始化直到一个更加便利的时间,或者更希望做一个热备份而不是执行内部初始化,应该用REP_CONF_NOAUTOINIT 标志来调用DB_ENV->rep_set_config方法。这个配置使得bdb返回DB_REP_JOIN_FAILURE到应用程序而不是执行内部初始化。
Client应用程序如果选择了延迟同步,这样就有责任在将来的时间和master进行同步。这可以通过关闭DB_REP_CONF_NOAUTOINIT标志和调用DB_ENV->rep_sync 方法来完成,或通过执行一个热备份。
Initializing a new site
默认地,添加一个新站点到一个replication组,只需要这个client去加入。bdb将自动从master到client给它执行内部初始化,引导这个client和master同步。尽管如此,还依赖于网络和底层结构,在一些场合下使用“热备份”在组内去初始化一个client还是比较有利的。Clients不想自动执行内部初始化应该用DB_REP_CONF_NOAUTOINIT标志调用DB_ENV->rep_set_config 方法。这个配置使得bdb返回DB_REP_JOIN_FAILURE到应用程序的DB_ENV->rep_process_message方法,而不是执行内部初始化。
为了使用热备份去初始化一个到replication组的clinet,执行下面的步骤:
做个一master环境的档案备份,就像在“数据库和日志文件档案”(Database and log file archival)中描述的那样。这个备份可以是常规的备份或一个热备份。
拷贝这个档案备份到client的一个干净的环境目录中。在client的新环境里执行灾难恢复,就像在恢复程序中描述的那样。
重新配置和重新打开这个环境作为组内的一个client成员。如果拷贝这个备份到client,相对于用db_archive公用程序或用DB_ENV->log_archive方法再造日志文件的周期来说,发费较长的时间,它可能需要抑制日志再造(reclamation),直到这个新启动的client已经追上(caught up)和应用了所有在停工期间产生的log记录。
就像任何bdb应用程序一样,当应用程序启动的时候数据库环境必须在一个一致的(consistent)状态。这是可以通过在一个线程或一个进程启动时执行恢复来最简单的确定的。这是对clients和masters来说无害的,即使它们不是严格的必需的,也就是说即使他们实际上没什么需要恢复的。
Bulk transfer
组内的站点们可能通过用DB_REP_CONF_BULK标志调用DB_ENV->rep_set_config 方法,而把配置成使用批量传输的。当被配置成批量传输,站点们将在一个缓冲区内积聚记录,然后在一次单一的网络传输中把它们传送给另一个站点。配置批量传输对master来说当然很有意义。另外,使用client-to-client同步的应用程序可能会发现,配置成批量传输对clients站点们来说也是很有帮助的。
当一个master正在产生新的log记录时,或者,或请求任何master的信息时,如果批量传输被配置,记录将堆积在一个批量缓冲区中。当缓冲区被装满了或一个永久的记录(例如,一个事务提交或一个检查点记录)被这个client排队等待,批量缓冲区将发送给client。
当一个client在响应另一个client的请求的信息的时候,如果批量传输被配置,记录将在批量缓冲区中堆积。批量缓冲区将发送给client,当缓冲区被装满了或当这个client的请求已经被满足了,再没有特别类型的记录将需要这缓冲区去发送了。
批量缓冲区的大小,它自己已经内定了不能被配置。尽管如此,在一次传输数据的总的大小可以通过使用DB_ENV->set_rep_limit方法来限制。
Transactional guarantees
在总的数据库环境的事务保证的上下文中去考虑replication是和重要的。作为简要的回顾,在一个非复制(non-replicated )中事务保证,是基于写log文件记录到稳定的存储设备上,通常是一个磁盘驱动器。如果应用程序回系统出了故障,bdb的日志信息将在恢复时被回顾,而且数据库被更新,以使已提交的事物的改变部分出现,所有未提交的而改变的部分不出现。在这种情况下,没有信息将会丢失。
如果一个数据库环境不要求当事务提交的时候把log写到稳定存储设备上(使用DB_TXN_NOSYNC标志能以牺牲事务的经久性为代价来增加性能),bdb恢复将只能把这个存储系统恢复到最后一个提交了的被保存在磁盘上了的状态。在这种情况下,信息可能已经丢失(例如,一些已经由事物提交了的改变可能在恢复之后不会出现在数据库中)。
复制(Replicating)这个数据库环境通过添加一个新的组成到“稳定存储”上扩展了这个模式:这个clinet的replicated信息。如果一个数据库环境是replicated,在数据库或log丢失的情况下,这儿也不会有信息丢失,因为这个复制系统可以被配置成包含数据库和log记录直到故障点的全部的设置。一个数据库丢失了一个磁盘驱动器能使这个磁盘被复制,然后,它有能重新加入这个复制(replication)组。
由于这个新的稳定存储的组成部分,指定DB_TXN_NOSYNC到一个复制组将不再牺牲耐久性了,只要具备一个或多个clients有公认的master所发送消息的收条。因为网络连接通常比本地同步磁盘写的快,replication成为大幅度提高它们的性能和可靠性的一个方法。
应用程序发送函数的返回状态必须被应用程序设置成确保应用程序想要提供的事务保障。无论什么时候,发送函数返回失败,本地数据库环境的log将被刷新(flushed),以确保任危急到到数据库完整性的信息不被丢失。因为这个刷新是一个昂贵的开销,会影响数据库性能,如果可能的话,应用程序应该避免从发送函数那返回一个错误信息。
replication事务保障唯一感兴趣的消息类型是当应用程序的发送函数指定了DB_REP_PERMANENT 标志被调用。如果发送函数曾经返回失败这儿将是没有原因的,除非
DB_REP_PERMANENT
标志被指定--消息没有DB_REP_PERMANENT被指定,不会使数据库有明显的改变,而且发送函数可以成功返回到bdb,只要这个消息被发送到client(s),或仅仅被拷贝到本地应用程序内存中预备发送。
当一个client收到一个DB_REP_PERMANENT 消息,这个client将在返回前刷新它的log到稳定存储设备上(除非这个client环境曾经用DB_TXN_NOSYNC 选项配置过)。
如果这个client不能刷新一个全部的事务记录到磁盘,不管什么原因(例如,在被标志的消息前丢失了一个log记录),在client上调用DB_ENV->rep_process_message方法将返回DB_REP_NOTPERM 和在ret_lsnp参数中返回这个记录的LSN到应用程序。
这个应用程序的client或master消息处理的循环应该适当的带些动作以确保在这种情况下的正确的事务保障。当丢失的记录到达,而且允许随后处理这些以前存储的永久记录,在clinet上调用DB_ENV->rep_process_message 方法将返回DB_REP_ISPERM和返回永曾经被刷新到磁盘中的久记录的最大的LSN。Client 应用次序可以用这些LSN决定性的知道是否某些特定的LSN被永久存储了。
一个应用程序依赖于client的能力去成为master,而且保证没有数据曾经被丢失,将需要编写发送函数返回一个错误,无论什么时候它都不能保证将赢得下一次选举的站点会有这个记录。
应用程序不要求这个级别的事务保证将不需要让这发送函数返回失败(除非master的数据库环境用DB_TXN_NOSYNC配置过),因为任何危急到数据库完整性的信息在发送被调用前已经被刷新到本地log文件。
总的来说,发送函数要返回失败的唯一的原因是当master的数据库环境被配置成当事务提交的时候不去刷新log(也就是说,在master上DB_TXN_NOSYNC被配置了),
DB_REP_PERMANENT
标志被指定给消息,
而且发送函数不能决定哪些clients收到了当前的消息(和所有在当前消息之前的消息)。多少clients在发送函数成功返回之前需要接收这个消息那是应用程序的选择(可能不是过多的依赖于特定数目的cleints报告成功作为一个或多个物理上分布式的 clients)。
如果,尽管如此,master上的应用程序确实需要在磁盘上的耐久性,那么这个应用程序应该被配置成当事务提交的时候同步的刷新记录。如果一个client没被配置成同步刷新log,也就是说,一个client运行的时候DB_TXN_NOSYNC是被被配置的,当它变成master的时候,那么它将取决于应用程序去适当地重新配置那个client。也就是说,这个应用程序必须明确的调用DB_ENV->set_flags去重新配置这个新成为master的client,使异步log刷新无效。
当然,确保replicated master和client环境确实是互相独立的很重要。例如,如果master和clients在同一个能源供给上(on the same power supply),一个client承认收到一个消息对事情是没什么帮助的,因为能源供给的失败仍然潜在地会使信息丢失。
配置的你基于replication的应用程序,能事务和性能上找到最佳结合点,是很复杂的。简言之,这儿有一些应用程序可以设置的地方以调节:为master环境指定DB_TXN_NOSYNC;为client环境指定DB_TXN_NOSYNC;不同的站点一起参加选举的那个优先权;应用程序发送函数的行为。
首先,在一个replication client中当一个事物提交的时候,写和同步刷新log几乎没什么用处。如果这些系统共享一个资源或多个系统同时出故障也许写和同步刷新log还有点用。默认地,所有的bdb环境,不管是master还是client,当事务提交的时候或预先地,都同步地刷新log。
考虑两个有网络连接的的系统。一个作为master,一个作为只读的client。如果master倒掉了client取代之,在这次故障后,master又重新加入到replication组。master 和client都被配置成当事务提交的时候不同步刷新日志(也就是说DB_TXN_NOSYNC 在两个系统上都被配置)。这个应用程序的发送函数从不返回失败到bdb库,只个简单的传递消息到这个client(或许基于一个广播机制),而且总是返回成功。在client上,由client的DB_ENV->rep_process_message方法返回的DB_REP_NOTPERM也将被忽略。这个系统配置有优秀的性能,但在某些失败的模式下有可能丢失数据。
如果这个master和client都一下子到掉了,就有可能丢失已经提交了的事务,也就是说,事务的耐久性没有被维持。有责任去提高系统的能源供给的独立性和把他们放在物理上隔离的地方。
如果这两个系统间的连接出了故障(或者仅仅是一些消息丢失了),并且随后这个master倒掉了,也有可能失去已提交的事务。又是因为事务的耐久性没有被维护。下面的一些方法中可以提高可靠性:
使用可靠的网络协议(例如,tcp而不是udp)。
增加clients和网络路径的数目,以使消息不太可能被丢失。在这种情况下,确保 client确实收到了这消息以赢得任何随后的选举也是很重要的。 如果一个client没有受到这个消息而赢得了随后的选举,那么数据仍然可能被丢失。
进一步的,系统可能想保证消息交付给了client(s)(例如,去阻止网络连接简单的丢弃消息)。一些系统可能想确保clients从来不返回已过期的信息,也就是说,一旦事务提交后在master上返回成功,将没有client在响应只读请求时返回旧的信息。下面的改变可能会用来阐述这些观点:
编写应用程序的发送函数,使它们直到一个或多个client承认收到了这个消息时才返回到bdb。这个clients的数目的选择是由应用程序决定的:你将很可能会想网络隔离(确保每个物理站点的client收到这个消息)和地理上多样性(确保一个client在每条线路“each coast ”上都收到这个消息)。
编写client的消息处理循环,使直到DB_ENV->rep_process_message返回成功的时候才承认收到这条信息。当DB_ENV->rep_process_message 方法返回DB_REP_NOTPERM 时意味着这个消息不能被刷新到client的磁盘。如果这个client不向master承认收到这个消息直到随后调用的DB_ENV->rep_process_message方法返回DB_REP_ISPERM而且这个返回的LSN至少和这个消息的LSN一样大,然后这个master的发送函数将不会返回success到bdb库。这意味着,直到那些经由挑选的clients已经收到了这个消息而且认为完成了,mastet上正在提交事务的这个线程将才被允许继续下去。
作为选择,client的消息处理循环可以向master承认收到了这个消息,但是用一个错误代码指明那个应用程序的发送函数不要返回到bdb库,直到一个随后的来自同一个client的确认成功。
应用程序的发送回调函数被bdb调用包括一个记录的LSN被发送(如果对那个记录来说是适当的)。当DB_ENV->rep_process_message 返回指示一个永久的记录已经被写入,然后它也回返回这个被写入的永久记录的最大LSN 。
这儿最后的两关需要考虑。首先,当应用程序的发送函数被调用的后,事务是不可被中途中断的,因为这个 master可能已经把事物提交日志记录写入磁盘,所以中途中断将不再是一个可选择项。第二,一个相近的问题是,如果发送函数返回失败,尽管这个master将试图去刷新本地log,这个刷新操作将可能失败(例如,当本地磁盘是满的)。此外,事务不能被中途中断,因为一个或多个clients可能已经提交了这个事务即使发送函数返回失败。杰出的应用程序可能也忍受不了这些不太可能的失败方式。在那中情况下应用程序可能想:
1,配置这master总去同步本地提交的事务(关闭DB_TXN_NOSYNC 配置项)。这样将对性能有很大的影响,当然(使用replication的其中的一个原因是避免本地磁盘的写操作),在这种配置下,不管什么情况,写本地日志失败将使事务中途中断。
2,在任何情况下都不要从应用程序的发送函数中返回,除非挑选的那些client已经承认了收到那个消息。直到这个发送函数返回到这个bdb库,master上这个正在提交事务的线程都将等待,所以没有应用程序能表现出承认事务已经被提交了。
最后可供牵扯到这些类型的失败的应用程序选择的是使用分布式事务,保证完全的一致性实现全局的事务管理和垮过多bdb环境履行两阶段提交。更多的了解这个可以参见分“the Distributed Transactions”那一章。