索引表設計
在電商專案中,物理庫存系統是個極其重要的系統,訂單支付後,就會開始來佔用物理庫存。一般情況下,庫存系統都是要分庫的,因為主要的操作是寫操作,例如佔用/釋放/取消等寫操作。使用分庫可以降低資料庫寫的壓力。儘管寫操作為主,但是讀操作也是有的。
比如說,庫存佔用的時候,得先查詢是否有庫存,而這個查詢操作並不都會帶上分庫因子(用於路由到具體的某個資料庫),而是一些比較寬鬆的查詢條件,這些查詢條件對應的資料可能分佈在不同的資料庫上。這個時候為了查詢的方便,會構建一個索引表。這個索引表是存在另外的單獨的一個資料庫中,不會再分庫了的。
索引表的設計也分不同情況,大體可以分三種。
1、查詢欄位+資料庫主鍵
把查詢欄位放到索引表,還需要把對應的資料庫主鍵ID也放置進去。當查詢請求到來時,根據查詢條件找出對應的資料主鍵,再根據資料主鍵路由到對應的存有完整業務資料的資料分庫上。這種方案呢。索引表佔用空間小,可以支撐很久。但是要找出業務資料,還是需要路由到分庫上。另外,如果要把索引表的資料儲存到ES搜尋引擎上的話,這種方式就不行了。因為索引表中沒有外部系統要的業務資料。所以當時的庫存系統沒有使用這種索引表設計方案。
2、查詢欄位+資料庫主鍵+需要展示的業務欄位
這種方案呢。當請求到來的時候,直接查詢索引表即可。無需根據主鍵路由到分庫了。同時如果要結合ES的話。可以直接把索引表的資料弄到ES上即可。後續直接讓ES暴露查詢介面即可。目前我在唯品會參與的物理庫存專案中,是使用這種方式的。但是這個方案也有個缺點。就是索引表的體積比較大,後續資料量一大的話,也是個問題。能不能優化一下呢?
3、索引表拆分
上面說到的第二種方案,索引表的膨脹可能很快,可以考慮將索引表拆分。比如說:索引表只是儲存查詢條件和主鍵,而需要展示給外部系統的資料,另外儲存在單獨的表上。比如叫index_detail表。這個表擁有索引表的主鍵。這樣的話,當查詢請求到來的時候,先從索引表查詢到主鍵,再根據主鍵從index_detail表中查詢出資料。當然這樣做的話。ES的資料來源就變成多張表了,不過這是可以接受的。
如何把業務資料寫入到索引表
使用MQ
一般來說,構建索引資料是使用單獨一個應用來做的。比如叫data-index域。這個域會從訊息佇列中讀取訊息,用於構建索引資料。當業務資料發生變化後,生產者傳送MQ訊息到佇列上。
這裡的訊息設計也分兩種情況。一種是訊息只是帶有資料主鍵和操作型別(ADD/Update/DELETE),消費者拿到主鍵後再去DB獲取完整的資料並插入到索引表中。另一種方案呢,是訊息包含了大部分需要的欄位,消費者拿到訊息後直接把資料插入到索引表中。這兩種訊息設計,我在實際專案中都有用過。
直接操作DB
這種方案呢就比較粗暴,直接配置一個索引表庫的資料來源,當業務資料發生變化時,使用Mybatis或者JDBC把資料更新到索引表中。一般不建議這麼做,一來構建索引資料的邏輯跟資料的CRUD操作融合在一起了。二來,操作其他資料庫的資料,要麼通過介面的方式,要麼由單獨的域來做。建議還是使用MQ的方式來構建索引資料。
如何把索引表資料弄到ES上
監聽資料庫表的資料變化
像在唯品會這邊,自研了一個叫VDP的元件,使用storm job去監聽索引表資料的變化,一旦有變化,就把資料同步到佇列中,ES直接從佇列中獲取資料,並儲存到ES上。
這種方案的好處是,我們無需寫任何程式碼,資料自動可以同步到ES上。
使用MQ
如果公司內部沒有開發VDP這樣的元件,可以通過傳送MQ訊息的方式來將索引表的資料同步資料到ES上。
讓ES暴露CUD介面
另外一種方案是,讓ES暴露CUD介面,用於同步索引表資料。但是這樣就跟ES耦合在一塊了。不太推薦這麼做。
進一步的思考
1、ES不支援Group By這樣的操作,所以在構建索引表的時候,可以事先計算好Group By的一些統計資料,並儲存到索引表中;
2、一些後臺應用中,如果資料庫表的數量已經很大,好幾個億了,並且查詢的SQL還非常變態,用資料庫已經無法支援了,那麼可以使用ES,查詢速度快,也支援一些統計操作;
3、使用ES輸出資料時,也有個坑。經常會拿到髒資料的。例如當資料傳送變化後,構建索引資料並把索引資料同步到ES上是需要時間的,但是我們後臺通常有將資料下架的操作,下架的操作操作完後,再次點選查詢按鈕,可能還是看到髒資料,因為資料同步到ES上沒那麼快。現在我還沒想到很好的辦法來解決這個問題。歡迎網友提些建議。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!