被最廣泛使用的異步Database Replication有可能因更新延遲(lag)而出現(xiàn)問題:當查詢請求被slave處理時,如果slave因更新延遲而包含尚未更新的陳腐數(shù)據(jù),會導致查詢結(jié)果不準確。
例如OLTP(On-Line Transaction Processing,聯(lián)機事物處理)型的查詢請求要求極高的實時性,如在博客中發(fā)表完一篇文章就應立即看到它出現(xiàn)在文章列表中,slave上的replication通常無法及時做完,這時如果查詢請求由slave來處理,返回的就是錯誤的陳腐數(shù)據(jù)(stale data)。Replication的延遲雖然通常很小,但畢竟不是實時同步,在slave上查不到最新數(shù)據(jù)的情況會存在的,何況在master高負載時向slave更新的延遲會更大。
當某slave的Replication延遲過大時,應將此slave移出服務后強制同步數(shù)據(jù)。不過在延遲存在但還不算過大時,如何在保證查詢準確性的前提下合適地使用slave來分擔負載呢?下面討論下Peter Zaitsev在他的文章中提到了三個方法,至于它們之間的混合使用就不在討論之內(nèi)了。
根據(jù)查詢對數(shù)據(jù)的實時性要求,將查詢分為time critical和non time critical兩種,前者受Replication延遲的影響很大,如前文提到的OLTP型的查詢;后者對Replication的延遲不敏感,對最新數(shù)據(jù)沒有迫切的要求,如多為報表使用的OLAP(On-Line Analytical Processing,聯(lián)機分析處理)型查詢。
在分派查詢請求時,識別該查詢請求的實時性要求,將non time critical的查詢交給slave處理,而將time critical的查詢交master處理??梢姡蛔x性質(zhì)的數(shù)據(jù)分析式查詢越多,slave所分擔的載荷就越多,這種方案就越有效。
這種考慮是從兩個事實出發(fā)的。
(1)剛剛做過更新操作的訪問者需要實時獲得更新后的數(shù)據(jù)
做完更新操作的訪問者應在一定時間段內(nèi)只從master讀取數(shù)據(jù),以保證他能讀到可能由他自己寫入的最新數(shù)據(jù)。這個時間段應長于所有slave上Replication的最長時間,以保證他寫的數(shù)據(jù)已經(jīng)replicate給了slave。
(2)剛剛查詢過數(shù)據(jù)的訪問者應當在再次做相同查詢時看到基本相同的數(shù)據(jù)
如果一個訪問者的兩次只讀查詢請求是由兩個slave分別處理的,那么第一次讀到的數(shù)據(jù)有可能在第二次讀時找不到,因為不同slave的Replication延遲是不同的,存在被查詢的數(shù)據(jù)在一個slave中被更新了但尚未更新到另一個slave中的情況。所以,給訪問者session,讓他在這一段時間內(nèi)持續(xù)訪問同一個slave。
這種方法說起來容易做起來難。而且,在session的時間段內(nèi)訪問者無法擺脫所指派的slave,失去了在請求之間切換到其它slave上獲取較新數(shù)據(jù)的機會。
根據(jù)所查詢數(shù)據(jù)的最后更改時間(last updated time)決定將查詢請求發(fā)給master或slave。數(shù)據(jù)的最后更改時間離當前時間越近,它出現(xiàn)在slave上的幾率就越小,這種請求只能由master出馬;反之,則可使用slave處理請求。
一個例子:博客上的文章有最后更新時間,查詢某篇文章時,先查該文章的新舊,如果文章很新那向master請求數(shù)據(jù),如果文章已經(jīng)不那么新了,那就由slave處理查詢請求。
這意味著數(shù)據(jù)查詢請求被執(zhí)行之前需要額外查詢一次其最后更新時間?沒錯。幸運的是,我們可以使用memcached之類的對象緩存系統(tǒng)來代替直接向master做這個查詢。
這中方法的思想很美,不過適用范圍較窄。對于可追蹤最后更新時間的對象,如博客文章、評論等,單個查詢其數(shù)據(jù)時可采用此方法;如果查詢這樣的對象集合,那就要參考實時性要求去考慮了。在復雜性方面,緩存的維護以及與Replication時間之間的權(quán)衡為該方法增加了難度,任何沒有更新到所有slave上的數(shù)據(jù)對象都需要維護在cache中,而已經(jīng)更新至所有slave上的數(shù)據(jù)對象理論上都需要從cache中移除,以清理cache空間。