RSS

Monthly Archives: September 2011

异步Database Replication的更新延迟问题

被最广泛使用的异步Database Replication有可能因更新延迟(lag)而出现问题:当查询请求被slave处理时,如果slave因更新延迟而包含尚未更新的陈腐数据,会导致查询结果不准确。

例如OLTP(On-Line Transaction Processing,联机事物处理)型的查询请求要求极高的实时性,如在博客中发表完一篇文章就应立即看到它出现在文章列表中,slave上的replication通常无法及时做完,这时如果查询请求由slave来处理,返回的就是错误的陈腐数据(stale data)。Replication的延迟虽然通常很小,但毕竟不是实时同步,在slave上查不到最新数据的情况会存在的,何况在master高负载时向slave更新的延迟会更大。

当某slave的Replication延迟过大时,应将此slave移出服务后强制同步数据。不过在延迟存在但还不算过大时,如何在保证查询准确性的前提下合适地使用slave来分担负载呢?下面讨论下Peter Zaitsev在他的文章中提到了三个方法,至于它们之间的混合使用就不在讨论之内了。

根据查询类型决定如何分派查询请求

根据查询对数据的实时性要求,将查询分为time critical和non time critical两种,前者受Replication延迟的影响很大,如前文提到的OLTP型的查询;后者对Replication的延迟不敏感,对最新数据没有迫切的要求,如多为报表使用的OLAP(On-Line Analytical Processing,联机分析处理)型查询。

在分派查询请求时,识别该查询请求的实时性要求,将non time critical的查询交给slave处理,而将time critical的查询交master处理。可见,只读性质的数据分析式查询越多,slave所分担的载荷就越多,这种方案就越有效。

根据session为访问者指派数据库副本

这种考虑是从两个事实出发的。

(1)刚刚做过更新操作的访问者需要实时获得更新后的数据

做完更新操作的访问者应在一定时间段内只从master读取数据,以保证他能读到可能由他自己写入的最新数据。这个时间段应长于所有slave上Replication的最长时间,以保证他写的数据已经replicate给了slave。

(2)刚刚查询过数据的访问者应当在再次做相同查询时看到基本相同的数据

如果一个访问者的两次只读查询请求是由两个slave分别处理的,那么第一次读到的数据有可能在第二次读时找不到,因为不同slave的Replication延迟是不同的,存在被查询的数据在一个slave中被更新了但尚未更新到另一个slave中的情况。所以,给访问者session,让他在这一段时间内持续访问同一个slave。

这种方法说起来容易做起来难。而且,在session的时间段内访问者无法摆脱所指派的slave,失去了在请求之间切换到其它slave上获取较新数据的机会。

根据访问对象指派数据库副本

根据所查询数据的最后更改时间(last updated time)决定将查询请求发给master或slave。数据的最后更改时间离当前时间越近,它出现在slave上的几率就越小,这种请求只能由master出马;反之,则可使用slave处理请求。

一个例子:博客上的文章有最后更新时间,查询某篇文章时,先查该文章的新旧,如果文章很新那向master请求数据,如果文章已经不那么新了,那就由slave处理查询请求。

这意味着数据查询请求被执行之前需要额外查询一次其最后更新时间?没错。幸运的是,我们可以使用memcached之类的对象缓存系统来代替直接向master做这个查询。

这中方法的思想很美,不过适用范围较窄。对于可追踪最后更新时间的对象,如博客文章、评论等,单个查询其数据时可采用此方法;如果查询这样的对象集合,那就要参考实时性要求去考虑了。在复杂性方面,缓存的维护以及与Replication时间之间的权衡为该方法增加了难度,任何没有更新到所有slave上的数据对象都需要维护在cache中,而已经更新至所有slave上的数据对象理论上都需要从cache中移除,以清理cache空间。

References

  • Peter Zaitsev的文章,http://www.mysqlperformanceblog.com/2007/02/14/getting-use-of-slave-in-mysql-replication/
 
Comments Off on 异步Database Replication的更新延迟问题

Posted by in Uncategorized

 

Database Replication的数据同步方式

为保证数据在各数据库副本上的一致性,Replication需要频繁同步数据,下文谈谈三种同步方式,以MySQL上的master-slave模型为例。

同步方式(Synchronous)

如果master接收更新请求后,在自身执行的同时将信息传播给所有slave,并要等收到所有slave获得更新信息的反馈之后才返回,那此同步过程会大大拖累性能。这个道理很容易理解,而MySQL Cluster号称拥有Synchronous Replication的特性,我确实还没搞清它怎样能做到。

异步方式(Asynchronous)

典型MySQL上的Replication是异步的,master处理完更新请求后返回,不等待slave获取到更新信息。借用51CTO.com的这张图来介绍下异步Replication的过程吧。

  1. master上有更新到来,先将更新存入binary-log中;
  2. master上的sql进程执行Database更新,同时master将有新更新的消息散播出去,让各slave随时来请求,这个散播互动的过程通常是基于publish-subscription的;
  3. 某slave向master发来IO请求,指定要binary-log上某偏移量之后的跟更新信息;
  4. master接受IO请求,并返回所请求的更新信息,以及binray-log上当前的偏移量;
  5. slave收到IO反馈,将收到的更新信息追加到relay-log末尾,并将binray-log偏移量记录到master-info文件中,然后向master返回Acknowledge消息;
  6. slave的sql进程监测到relay-log中有新内容后,会立即在database上执行更新。

根据binary-log的信息存储形式,Replication又可区分为如下三种级别,相应都在MySQL上有配置。

  • Row level – Binary Log 中会记录成每一行数据被修改的形式,然后在 Slave 端再对相同的数据进行修改,不用在乎执行的sql语句的上下文相关的信息。显然此方法log中数据量巨大。
  • Statement level – 每一条会修改数据的 Query 都会记录到 Masterçš„ Binary Log 中。log中不存在大量数据了,但它不得不记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在slave端杯执行的时候能够得到和在master端执行时候相同的结果。这种结果常因为query语句无法完美Replicate而出现数据不一致。
  • Mixed level – 它是上两种方法的折中,会根据执行的每一条具体的 Query 语句来区分对待记录的日志形式,也就是在Statementå’ŒRow之间选择一种。

半同步方式(Semi-synchronous)

又是一个典型的折中。因为上面提到的异步方式下,master不管slave数据更新的进度,若slave出现明显的落后(Lag),数据不一致的问题和单点故障的风险都会被放大;而完全同步的方式对性能的损害又过大。

借用orczhou.com的图片如下来解释半同步方式。

在半同步的方式下,master等待至少一个slave收到更新,即等待其Acknowledge消息,以此保证至少有一个slave在master突然宕机的情况下能够不丢失数据,并迅速代替master机器。不过如果等待时间超过限制,半同步模式会自动转为异步模式,不再等待slave。

Reference

  • 半同步方式的更多解释,http://dev.mysql.com/doc/refman/5.5/en/replication-semisync.html
  • master-slave在MySQL上的配置,http://members.cox.net/midian/howto/mysqlReplication.htm
 
Comments Off on Database Replication的数据同步方式

Posted by in Uncategorized

 

Database Replication与应用模型介绍

Database Replication是一个在冗余数据库之间分享数据,以维护多个数据一致的数据库副本(Replica)的方法,目的是提高可靠性、容错性和可访问性。其目的和方式,直白地说,是以冗余数据的方式提高容错能力,以实时维护数据库副本的方式保证宕机时热切换,同时分散数据查询的载荷以提高性能,而所有的一切对外部用户是透明的。当然,不同的应用背景对数据库的期望是不同的,可能偏重于上述的某些个能力,并不是兼有。

例如分支团队希望在远程得到一部分数据库数据,而不是实时访问主数据库,那么可通过Replication为其建一个主数据库中某些表的副本,这个副本中的数据可隔一定时间同步一次。

Replication不是数据库备份技术,数据库备份的要旨在于将某一个时间节点上的数据库状态备份起来,供未来的数据恢复或参考用;而Replication的数据库副本对所做的更新都是无条件执行的,至于执行之后的数据状态是好是坏,它们不关心。不过Replication有助于数据库备份,后文介绍master-slave模式时会提及。

从数据一致性的维护上讲,应用Replication要首先安排所有数据库副本的责任角色,谁能接收更改请求并分享出来,谁仅读取其他副本的数据更新。称前者为master,称后者为slave,通常有master-slave和multi-master两种应用模型,下面首先介绍这两种模型。

master-slave

一个server上的DB做master,其他所有server上的DB副本都作为slave。任何时候都由master接收更新请求并首先执行,然后将更新的信息传递给slave,slave上再应用更新。slave仅用来接收查询请求,当master宕机时会按照切换策略将一个slave升级为master,而原master在修复之后则作为slave重新加入。

借用来自MySQL Reference Manual的下图说明master-slave模型的Replication的应用场景。

图中的应用场景是一个大量数据库查询的web应用,使用master-slave的首要目的是分担载荷。

  1. 用户的request先由Load Balancer分派到某一较轻负载的web应用实例上;
  2. 根据request中对数据的不同操作类型,web应用实例将数据实时性要求较宽松的只读请求发给slave处理,而将数据实时性要求高的读操作或写操作发给master处理;
  3. 写操作的信息在master数据库上执行的同时,被Replicate给各个slave。

另外,上文提到Replication并不属于数据库备份技术,但数据库备份操作确实可从中受益。master-slave模型中,可在某一slave更新数据后将它从slave阵列中切出,暂时不再接受任何request或replication,这时可对此slave数据库进行备份。备份过程可专心进行,相比直接对服务中的数据库做备份,难度降低许多。备份完毕的这台server可主动请求master的数据,更新完毕后可重新作为slave加入阵列继续提供服务。

multi-master

“能够接收更新请求并首先执行”是具有master角色的标准,该应用模型中所有的server上的DB副本都是master,可接收更新请求或数据查询请求,因为宕机时的切换成本比master-slave要小。这种应用模型的在副本数据更新的信息来源上也分两种方式。

1. Active Replication

任何更新请求都分别发给各个副本执行,这种主动行为的好处在于简化了副本间的数据同步,却让负载同时发生,失去了副本间并发响应查询请求的性能。

2. Passive Replication

任何更新请求只发给某一个副本,它接收到之后会立即执行,然后将自己的状态传播给其他副本,其他副本在此过程中只被动等待。这种副本间传递信息的方式比master-slave更要复杂,因为这张网上的任一节点都能当做其他节点的信息来源,而且两条对临界数据的修改可能随时出现,所以需要分布式并发控制。常见的方法有两个,一个基于锁机制,将修改所涉及到的资源的加锁,以同步不同master上收到的更改的执行顺序;另一个基于时间戳和事务回滚,各副本先应用事物所描述的更新,在其他副本的信息到来后发现冲突时,再依时间戳为据选择是否回滚前面的更新。

Reference

  • Replication的解释,http://en.wikipedia.org/wiki/Database_replication
  • 在负载均衡上的应用,http://dev.mysql.com/doc/refman/5.6/en/replication-solutions-scaleout.html
  • 在宕机时切换上的应用,http://dev.mysql.com/doc/refman/5.6/en/replication-solutions-switch.html
 
Comments Off on Database Replication与应用模型介绍

Posted by in Uncategorized

 

中秋月圆

在把家庭生活看得越来越淡、对待邻里关系若有实无的如今,中秋节不算个什么重要时节了,甚至过年也变得只是走走过场而已。我本打算埋头宅在家啃书,独自抵御阴雨的寒冷,却意外地度过了温暖的三天假期。

妻只休第一天,让她好好睡一个奢侈的懒觉吧~ 她却仍然被无情的电话吵醒,无奈地赶去单位处理事情。中午接到她,说想吃肉丸呼啦汤,立刻满足。饭后决定带她散散心,哪怕雨大天阴,驱车奔浐灞而去。停车,沿浐河边信步而行,雨声沙沙,河床树顶的白鹭却不因阴雨而烦闷,或嬉戏、或悠悠然~ 忽然发现垃圾箱边一只小笨狗注视着我们,在雨中颤巍巍的,饿么?不能让这小家伙即离了家人又没有饭吃,中秋的月饼送上。不够吗?这就买肉去!借这石板搭一个雨棚吧,虽然不忍离去。后赶往高新,接老妈去奶奶家,团圆的晚饭其乐融融。愿长辈健康、大家开心~

次日一早醒来,窝在沙发里拽本书看。眼光扫扫家里,未扔的垃圾袋、半箱腐坏的水果、洗衣机上一堆的衣物、斑点的地板、冰箱里的剩食、歪斜的沙发衬垫,不能再忍了。平时早出晚归,我俩人确实不够勤快,现今是收拾的时候了,等妻下班了就是一惊喜。心中既怀此念,动手时更加有劲,挨个清除,逐一料理,整顿完毕已是困意连连……被电话叫醒时,我才记起和三位发小儿早有约。晚餐时对炖羊肉一番大快朵颐,把酒谈论最近的境遇。饭后怀念起从前一起游戏的日子,遂往网吧去,早已不熟悉的网吧中,《生死四人组》的世界里我们团结配合、相互帮扶,真是只有最知心的朋友们才能达到的境地啊,当游戏完毕,我们更加惺惺相惜~ 朋友们,加油!在自己的人生长跑里,可以吊吊车尾、可以呼呼喘气,但一定要努力下去,大步流星的终究还会是我们!

最后一天假期,天似乎晴了,被窝里坐着看书挺惬意。老妈的电话里问我有什么安排,我听得出来,其实是想让我开车载她们出去散散心,老爸也终于休假了,这半天的假期让我为他们再拆分吧。于是立即答应,来吧,把小舅也叫上一起,我带你们去呼吸雨后的新鲜空气。车过灞河桥,这宽阔的水面、暗流里翻出的浊浪,却是平日里瞧不见的光景,多亏这一阵雨。沿河东行驶,一片环绿的湖水吸引了我们,以及先到的若干钓鱼爱好者。湖水宁静,丝毫不似河水的昏黄;草地、小丘天然生成,全是清洗过的碧绿。长辈们哄然称好,这确是个十个农家鱼塘也不换的宝地。雨后的阳光倾泻着,我们尽兴之后也不得不返回了。送回父母,我还要赶去我另一对父母那里,中秋佳节,他们也想女儿、女婿。说说时事,聊聊家常,互相问各种“缺不缺”、“好不好”,话里绝不是寒暄,心里却有各种温馨~ 末了出门,回我们的小家去。

车在立交上,看云里透出的淡淡圆月,心中温暖畅快,无须言语~

 

Tags: ,

Mock,有人爱它我偏恨

一次又一次地在项目中用Mock写测试,每次都是无奈和不情愿。

对话一

Me: 为什么要用Mock呢?
Dev: 因为客户要求只有Model中的代码才能touch数据库。
Me: 构造数据做其他层次的测试而不是mock,这不可以吗?
Dev: …测试数据构造很麻烦。我们用Mock分离其依赖层次的代码,这样就可以专心写当前方法的测试了。
Me: 依赖的低层次方法全都Mock了理想返回值,接下来直接断言结果就好了?高层次的测试过了但低层的反而没过!
Dev: ……Mock不是保证了各层依赖的方法真的被调用了嘛。
Me: 产品代码里我能看见谁被调用,可代码集成后结果到底对不对呢?集成测试在哪呢,怎么知道产品代码放在一起真能work?
Dev: ………Cucumber测试……
Me: Holy crap!

你应懂得:

  • Mock应该作为分离模块和模块之间的手段,以降低测试的难度,例如Mock数据查询,返回自己构造的测试数据而不是访问模块之外的资源。
  • 模块内的层次之间的关联在测试中未必非要由Mock打断,在一定数据的基础之上,为一个原子功能做跨越一二个类的单元测试,更能从集成的角度证明代码的正确性。
  • 比证明“某些依赖确实被调用到”更有意义的是证明“对给定的输入数据能得出期待的结果”。不要把基于结果断言的测试变成用Mock描述实现过程。
  • 如果已经知道是delegate方法到其他方法上,那就省了Mock的测试吧,就像拿着标准答案答考题,过家家一样毫无意义,除非你不幸要完成变态且愚昧的“100%测试覆盖率”。
  • 如果对“某层次代码能否间接访问到数据库”没有苛刻要求,那么有必要的话,可为每个开发机器配置独立数据库,并区分开test数据库和development数据库,让test数据库只跑测试。这样在写访问数据相关的测试时,就可大胆预置测试数据,之后清除也方便,让测试能够“所见即所得”从而更加真实。

对话二

Me: Mock的代码这么多,还对应着产品代码里的实现细节,这不容易写更不容易看懂啊!
Dev: 这也不算多,构造测试数据才麻烦呢。被测方法里调的其他对象方法你不得不Mock啊,不然会报错。
Me: 但是我要用测试来驱动开发啊,这中间的细节我需要到真正写出产品代码时才能确定,这样的测试也太难写了吧?
Dev: …是有点麻烦,不然你先写好断言部分,产品代码需要时再补上Mock吧。
Me: 这是先开发后补测试啊!
Dev: ……一点一点写的…最终反正是有效的测试啦。
Me: 如果被Mock的方法被更改了实现呢,你能及时发现吗?测试全过了可应用启动却废了,这事发生过吧?
Dev: ………改代码时细心一点,多搜索下检查检查,启动应用看一看……
Me: Holy crap!

你应懂得:

  • Mock在测试中必然会构建假设,通常如“某类的某方法在某条件下调用时应返回某结果”。应该将其控制到所测功能的数据源头处,而不是任其泛滥成为实现过程在测试中的描述。
  • Mock的假设在描述时必然会牵扯到所测方法的实现细节,这在一定程度上为先写测试后写代码增加了难度。
  • 过多地使用Mock会降低测试代码的可读性,更重要的是其可维护性。错误的假设不易分辨,因为真实改变时假设还保持不变,无法通过测试的失败而被发现,而代码检查、集成测试以及人工测试成为了代价昂贵的补偿方法。
 
Comments Off on Mock,有人爱它我偏恨

Posted by in Uncategorized