寫在前面
在當今互聯網行業,尤其是現在分布式、微服務開發環境下,為了提高搜索效率,以及搜索的精准度,會大量使用Redis、Memcached等NoSQL數據庫,也會使用大量的Solr、Elasticsearch等全文檢索服務。那么,這個時候,就會有一個問題需要我們來思考和解決:那就是數據同步的問題!如何將實時變化的數據庫中的數據同步到Redis/Memcached或者Solr/Elasticsearch中呢?
互聯網背景下的數據同步需求
在當今互聯網行業,尤其是現在分布式、微服務開發環境下,為了提高搜索效率,以及搜索的精准度,會大量使用Redis、Memcached等NoSQL數據庫,也會使用大量的Solr、Elasticsearch等全文檢索服務。那么,這個時候,就會有一個問題需要我們來思考和解決:那就是數據同步的問題!如何將實時變化的數據庫中的數據同步到Redis/Memcached或者Solr/Elasticsearch中呢?
例如,我們在分布式環境下向數據庫中不斷的寫入數據,而我們讀數據可能需要從Redis、Memcached或者Elasticsearch、Solr等服務中讀取。那么,數據庫與各個服務中數據的實時同步問題,成為了我們亟待解決的問題。
試想,由於業務需要,我們引入了Redis、Memcached或者Elasticsearch、Solr等服務。使得我們的應用程序可能會從不同的服務中讀取數據,如下圖所示。
本質上講,無論我們引入了何種服務或者中間件,數據最終都是從我們的MySQL數據庫中讀取出來的。那么,問題來了,如何將MySQL中的數據實時同步到其他的服務或者中間件呢?
注意:為了更好的說明問題,后面的內容以MySQL數據庫中的數據同步到Solr索引庫為例進行說明。
數據同步解決方案
1.在業務代碼中同步
在增加、修改、刪除之后,執行操作Solr索引庫的邏輯代碼。例如下面的代碼片段。
public ResponseResult updateStatus(Long[] ids, String status){
try{
goodsService.updateStatus(ids, status);
if("status_success".equals(status)){
List<TbItem> itemList = goodsService.getItemList(ids, status);
itemSearchService.importList(itemList);
return new ResponseResult(true, "修改狀態成功")
}
}catch(Exception e){
return new ResponseResult(false, "修改狀態失敗");
}
}
優點:
操作簡便。
缺點:
業務耦合度高。
執行效率變低。
2.定時任務同步
在數據庫中執行完增加、修改、刪除操作后,通過定時任務定時的將數據庫的數據同步到Solr索引庫中。
定時任務技術有:SpringTask,Quartz。
哈哈,還有我開源的mykit-delay框架,開源地址為:https://github.com/sunshinelyz/mykit-delay。
這里執行定時任務時,需要注意的一個技巧是:第一次執行定時任務時,從MySQL數據庫中以時間字段進行倒序排列查詢相應的數據,並記錄當前查詢數據的時間字段的最大值,以后每次執行定時任務查詢數據的時候,只要按時間字段倒序查詢數據表中的時間字段大於上次記錄的時間值的數據,並且記錄本次任務查詢出的時間字段的最大值即可,從而不需要再次查詢數據表中的所有數據。
注意:這里所說的時間字段指的是標識數據更新的時間字段,也就是說,使用定時任務同步數據時,為了避免每次執行任務都會進行全表掃描,最好是在數據表中增加一個更新記錄的時間字段。
優點:
同步Solr索引庫的操作與業務代碼完全解耦。
缺點:
數據的實時性並不高。
3.通過MQ實現同步
在數據庫中執行完增加、修改、刪除操作后,向MQ中發送一條消息,此時,同步程序作為MQ中的消費者,從消息隊列中獲取消息,然后執行同步Solr索引庫的邏輯。
我們可以使用下圖來簡單的標識通過MQ實現數據同步的過程。
我們可以使用如下代碼實現這個過程。
public ResponseResult updateStatus(Long[] ids, String status){
try{
goodsService.updateStatus(ids, status);
if("status_success".equals(status)){
List<TbItem> itemList = goodsService.getItemList(ids, status);
final String jsonString = JSON.toJSONString(itemList);
jmsTemplate.send(queueSolr, new MessageCreator(){
@Override
public Message createMessage(Session session) throws JMSException{
return session.createTextMessage(jsonString);
}
});
}
return new ResponseResult(true, "修改狀態成功");
}catch(Exception e){
return new ResponseResult(false, "修改狀態失敗");
}
}
優點:
業務代碼解耦,並且能夠做到准實時。
缺點:
需要在業務代碼中加入發送消息到MQ的代碼,數據調用接口耦合。
4.通過Canal實現實時同步
Canal是阿里巴巴開源的一款數據庫日志增量解析組件,通過Canal來解析數據庫的日志信息,來檢測數據庫中表結構和數據的變化,從而更新Solr索引庫。
使用Canal可以做到業務代碼完全解耦,API完全解耦,可以做到准實時。
Canal開源地址:https://github.com/alibaba/canal。
重磅福利
關注「 冰河技術 」微信公眾號,后台回復 “設計模式” 關鍵字領取《深入淺出Java 23種設計模式》PDF文檔。回復“Java8”關鍵字領取《Java8新特性教程》PDF文檔。回復“限流”關鍵字獲取《億級流量下的分布式限流解決方案》PDF文檔,三本PDF均是由冰河原創並整理的超硬核教程,面試必備!!
好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!
寫在最后
如果你覺得冰河寫的還不錯,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習高並發、分布式、微服務、大數據、互聯網和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章干貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨干!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術干貨,讓你對如何提升技術能力不再迷茫!