被最广泛使用的异步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所分担的载荷就越多,这种方案就越有效。
这种考虑是从两个事实出发的。
(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空间。
联系客服