索引表設(shè)計(jì)
在電商項(xiàng)目中轨淌,物理庫存系統(tǒng)是個(gè)極其重要的系統(tǒng)迂烁,訂單支付后,就會(huì)開始來占用物理庫存递鹉。一般情況下盟步,庫存系統(tǒng)都是要分庫的,因?yàn)橹饕牟僮魇菍懖僮黪锝幔缯加?釋放/取消等寫操作却盘。使用分庫可以降低數(shù)據(jù)庫寫的壓力。盡管寫操作為主媳拴,但是讀操作也是有的黄橘。比如說,庫存占用的時(shí)候屈溉,得先查詢是否有庫存塞关,而這個(gè)查詢操作并不都會(huì)帶上分庫因子(用于路由到具體的某個(gè)數(shù)據(jù)庫),而是一些比較寬松的查詢條件子巾,這些查詢條件對應(yīng)的數(shù)據(jù)可能分布在不同的數(shù)據(jù)庫上帆赢。這個(gè)時(shí)候?yàn)榱瞬樵兊姆奖阈⊙梗瑫?huì)構(gòu)建一個(gè)索引表。這個(gè)索引表是存在另外的單獨(dú)的一個(gè)數(shù)據(jù)庫中椰于,不會(huì)再分庫了的怠益。
索引表的設(shè)計(jì)也分不同情況,大體可以分三種瘾婿。
1蜻牢、查詢字段+數(shù)據(jù)庫主鍵
把查詢字段放到索引表,還需要把對應(yīng)的數(shù)據(jù)庫主鍵ID也放置進(jìn)去偏陪。當(dāng)查詢請求到來時(shí)孩饼,根據(jù)查詢條件找出對應(yīng)的數(shù)據(jù)主鍵,再根據(jù)數(shù)據(jù)主鍵路由到對應(yīng)的存有完整業(yè)務(wù)數(shù)據(jù)的數(shù)據(jù)分庫上竹挡。這種方案呢。索引表占用空間小立膛,可以支撐很久揪罕。但是要找出業(yè)務(wù)數(shù)據(jù),還是需要路由到分庫上宝泵。另外好啰,如果要把索引表的數(shù)據(jù)存儲(chǔ)到ES搜索引擎上的話,這種方式就不行了儿奶。因?yàn)樗饕碇袥]有外部系統(tǒng)要的業(yè)務(wù)數(shù)據(jù)框往。所以當(dāng)時(shí)的庫存系統(tǒng)沒有使用這種索引表設(shè)計(jì)方案。
2闯捎、查詢字段+數(shù)據(jù)庫主鍵+需要展示的業(yè)務(wù)字段
這種方案呢椰弊。當(dāng)請求到來的時(shí)候,直接查詢索引表即可瓤鼻。無需根據(jù)主鍵路由到分庫了秉版。同時(shí)如果要結(jié)合ES的話〔绲唬可以直接把索引表的數(shù)據(jù)弄到ES上即可清焕。后續(xù)直接讓ES暴露查詢接口即可。目前我在唯品會(huì)參與的物理庫存項(xiàng)目中祭犯,是使用這種方式的秸妥。但是這個(gè)方案也有個(gè)缺點(diǎn)。就是索引表的體積比較大沃粗,后續(xù)數(shù)據(jù)量一大的話粥惧,也是個(gè)問題。能不能優(yōu)化一下呢陪每?
3影晓、索引表拆分
上面說到的第二種方案镰吵,索引表的膨脹可能很快,可以考慮將索引表拆分挂签。比如說:索引表只是保存查詢條件和主鍵疤祭,而需要展示給外部系統(tǒng)的數(shù)據(jù),另外存儲(chǔ)在單獨(dú)的表上饵婆。比如叫index_detail表勺馆。這個(gè)表擁有索引表的主鍵。這樣的話侨核,當(dāng)查詢請求到來的時(shí)候草穆,先從索引表查詢到主鍵,再根據(jù)主鍵從index_detail表中查詢出數(shù)據(jù)搓译。當(dāng)然這樣做的話悲柱。ES的數(shù)據(jù)來源就變成多張表了,不過這是可以接受的些己。
如何把業(yè)務(wù)數(shù)據(jù)寫入到索引表
使用MQ
一般來說豌鸡,構(gòu)建索引數(shù)據(jù)是使用單獨(dú)一個(gè)應(yīng)用來做的。比如叫data-index域段标。這個(gè)域會(huì)從消息隊(duì)列中讀取消息涯冠,用于構(gòu)建索引數(shù)據(jù)。當(dāng)業(yè)務(wù)數(shù)據(jù)發(fā)生變化后逼庞,生產(chǎn)者發(fā)送MQ消息到隊(duì)列上蛇更。
這里的消息設(shè)計(jì)也分兩種情況。一種是消息只是帶有數(shù)據(jù)主鍵和操作類型(ADD/Update/DELETE),消費(fèi)者拿到主鍵后再去DB獲取完整的數(shù)據(jù)并插入到索引表中赛糟。另一種方案呢派任,是消息包含了大部分需要的字段,消費(fèi)者拿到消息后直接把數(shù)據(jù)插入到索引表中璧南。這兩種消息設(shè)計(jì)吨瞎,我在實(shí)際項(xiàng)目中都有用過。
直接操作DB
這種方案呢就比較粗暴穆咐,直接配置一個(gè)索引表庫的數(shù)據(jù)源颤诀,當(dāng)業(yè)務(wù)數(shù)據(jù)發(fā)生變化時(shí),使用Mybatis或者JDBC把數(shù)據(jù)更新到索引表中对湃。一般不建議這么做崖叫,一來構(gòu)建索引數(shù)據(jù)的邏輯跟數(shù)據(jù)的CRUD操作融合在一起了。二來拍柒,操作其他數(shù)據(jù)庫的數(shù)據(jù)心傀,要么通過接口的方式,要么由單獨(dú)的域來做拆讯。建議還是使用MQ的方式來構(gòu)建索引數(shù)據(jù)脂男。
如何把索引表數(shù)據(jù)弄到ES上
監(jiān)聽數(shù)據(jù)庫表的數(shù)據(jù)變化
像在唯品會(huì)這邊养叛,自研了一個(gè)叫VDP的組件,使用storm job去監(jiān)聽索引表數(shù)據(jù)的變化宰翅,一旦有變化弃甥,就把數(shù)據(jù)同步到隊(duì)列中,ES直接從隊(duì)列中獲取數(shù)據(jù)汁讼,并存儲(chǔ)到ES上淆攻。
這種方案的好處是,我們無需寫任何代碼嘿架,數(shù)據(jù)自動(dòng)可以同步到ES上瓶珊。
使用MQ
如果公司內(nèi)部沒有開發(fā)VDP這樣的組件,可以通過發(fā)送MQ消息的方式來將索引表的數(shù)據(jù)同步數(shù)據(jù)到ES上耸彪。
讓ES暴露CUD接口
另外一種方案是伞芹,讓ES暴露CUD接口,用于同步索引表數(shù)據(jù)蝉娜。但是這樣就跟ES耦合在一塊了丑瞧。不太推薦這么做。
進(jìn)一步的思考
1蜀肘、ES不支持Group By這樣的操作,所以在構(gòu)建索引表的時(shí)候稽屏,可以事先計(jì)算好Group By的一些統(tǒng)計(jì)數(shù)據(jù)扮宠,并存儲(chǔ)到索引表中;
2狐榔、一些后臺(tái)應(yīng)用中坛增,如果數(shù)據(jù)庫表的數(shù)量已經(jīng)很大,好幾個(gè)億了薄腻,并且查詢的SQL還非常變態(tài)收捣,用數(shù)據(jù)庫已經(jīng)無法支持了,那么可以使用ES庵楷,查詢速度快罢艾,也支持一些統(tǒng)計(jì)操作;
3尽纽、使用ES輸出數(shù)據(jù)時(shí)咐蚯,也有個(gè)坑。經(jīng)常會(huì)拿到臟數(shù)據(jù)的弄贿。例如當(dāng)數(shù)據(jù)發(fā)送變化后春锋,構(gòu)建索引數(shù)據(jù)并把索引數(shù)據(jù)同步到ES上是需要時(shí)間的,但是我們后臺(tái)通常有將數(shù)據(jù)下架的操作差凹,下架的操作操作完后期奔,再次點(diǎn)擊查詢按鈕侧馅,可能還是看到臟數(shù)據(jù),因?yàn)閿?shù)據(jù)同步到ES上沒那么快∧琶龋現(xiàn)在我還沒想到很好的辦法來解決這個(gè)問題馁痴。歡迎網(wǎng)友提些建議。