Elasticsearch是一款基于Apache Lucene構(gòu)建的開源全文檢索引擎,它能夠輕松地進(jìn)行大規(guī)模的橫向擴(kuò)展,以支撐PB級的結(jié)構(gòu)化和非結(jié)構(gòu)化海量數(shù)據(jù)的處理。而關(guān)系型數(shù)據(jù)庫比較擅長對數(shù)據(jù)的管理,但對全文檢索功能的支持相對不足,所以有時候一些實際項目需要將關(guān)系型數(shù)據(jù)庫中的數(shù)據(jù)同步到Elasticsearch中,以提供更加強(qiáng)大的全文檢索功能。另外,一些基于關(guān)系型數(shù)據(jù)庫的歷史遺留系統(tǒng)的存在,當(dāng)遇到全文檢索的新需求時,就更加需要將數(shù)據(jù)同步到Elasticsearch中。近日,在線銀行支付平臺GoCardless的軟件工程師Chris Sinjakli發(fā)表了一篇題為《將數(shù)據(jù)從PostgreSQL同步到Elasticsearch的經(jīng)驗教訓(xùn)》的博文。在文章中,他結(jié)合自己的實際經(jīng)歷(GoCardless使用Elasticsearch增強(qiáng)搜索功能)總結(jié)了將數(shù)據(jù)從關(guān)系型數(shù)據(jù)庫PostgreSQL同步到Elasticsearch的經(jīng)驗教訓(xùn)。
Chris首先指出當(dāng)需要把數(shù)據(jù)同時存儲到PostgreSQL和Elasticsearch兩個地方時,開發(fā)者需要深入考慮的一些問題,如當(dāng)Elasticsearch處理有很大延遲時將會發(fā)生什么未知事情、如果更新時出現(xiàn)異常將會發(fā)生什么情況、怎么知道Elasticsearch正確處理了每次更新等。接下來Chris引出要解決以上問題必須做到異步的更新、達(dá)到最終一致性、進(jìn)行索引重建。
關(guān)于如何做到異步更新,Chris指出GoCardless開發(fā)團(tuán)隊構(gòu)造了一個隊列用于數(shù)據(jù)的異步同步,且通過線程池來協(xié)助處理。這樣既可以單獨(dú)更新,也可以批量更新,并使用基于JSON格式的數(shù)據(jù)和利用Elasticsearch的API保證了響應(yīng)時間和可預(yù)知性。
關(guān)于如何確保一致性,Chris指出Elasticsearch的更新API不具有線程安全性,尤其在高并發(fā)更新時。如果只是調(diào)用該更新API來索引更新數(shù)據(jù)的話,就有可能引起并發(fā)問題。不過,Elasticsearch提供了一個具有樂觀鎖的索引版本系統(tǒng),通過該系統(tǒng)就可以做到安全的更新。但是當(dāng)在更新索引的同時,用戶還是有可能搜索出臟數(shù)據(jù)。慶幸的是,Elasticsearch還提供了另一種處理索引版本的方案,該方案是由發(fā)起請求的外部程序來設(shè)置版本類型并提供版本號,這樣使得Elasticsearch總是保持同步的文檔具有最高版本號。GoCardless開發(fā)團(tuán)隊考慮到PostgreSQL的事務(wù)ID(64位整數(shù))在保證事務(wù)情況下能夠?qū)崿F(xiàn)自增,所以GoCardless開發(fā)團(tuán)隊就使用PostgreSQL的事務(wù)ID作為版本號。這樣就可以實現(xiàn)每次同步到Elasticsearch的數(shù)據(jù)都是最合適的(盡管不是最新的),但最后仍會達(dá)到數(shù)據(jù)的一致性。
關(guān)于如何重建索引,Chris指出以上的異步方式仍然存在丟失更新的可能,如網(wǎng)絡(luò)分區(qū)下引起的問題。為了處理以上問題,GoCardless開發(fā)團(tuán)隊采取周期地將最近寫入到PostgreSQL的記錄進(jìn)行一次批量同步并使用Elasticsearch的Bulk API重新批量索引所同步數(shù)據(jù)的方案。該方案以較小的重復(fù)記錄為代價徹底解決了更新丟失的問題,并且只需使用與原來同樣的代碼和在無需停止服務(wù)器的情況下即可實現(xiàn)索引重建。Chris還特別指出,如果想在無需停止服務(wù)器的情況實現(xiàn)重建索引,這就需要從一開始就正確地使用Elasticsearch的索引別名。
最后,Chris指出如果要構(gòu)建更加良好的搜索體驗,還有很多工作需要做,尤其是不同的應(yīng)用程序有著不同的約束條件,所以他建議開發(fā)者在開始編寫產(chǎn)品代碼前就要深入思考相關(guān)問題及處理方案。