<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
我們知道在分庫分表中對於toC業務來說,需要選擇使用者屬性如 user_id 作為分片鍵,不推薦使用order_id這樣的作為分片鍵。
那問題來了,對於訂單表來說,選擇了user_id作為分片鍵以後如何檢視訂單詳情呢?比如下面這樣一條SQL:
SELECT * FROM T_ORDER WHERE order_id = 801462878019256325
由於查詢條件中的order_id不是分片鍵,所以需要查詢所有分片才能得到最終的結果。如果下面有 1000 個分片,那麼就需要執行 1000 次這樣的 SQL,這時效能就比較差了。
可以通過ShardingSphere-JDBC生成的SQL得知,根據order_id查詢會對所有分片進行查詢然後通過UNION ALL
進行合併。
但是,我們知道 order_id 是主鍵,應該只有一條返回記錄,也就是說,order_id 只存在於一個分片中。這時,可以有以下三種設計:
當然,這三種設計的本質都是通過冗餘實現空間換時間的效果,否則就需要掃描所有的分片,當分片資料非常多,效率就會變得極差。
下面我們逐一分析。
這種做法很容易理解,同一份訂單資料在插入時儲存兩份,根據user_id 和 order_id分別做兩個分庫分表的實現。
通過對錶進行冗餘,對於 order_id 的查詢,只需要在 order_id = 801462878019256325
的分片中直接查詢就行,效率最高。但是這個方案設計的缺點又很明顯:冗餘資料量太大。
索引表法是對第一種冗餘法的改進,由於第一種方案冗餘的資料量太大,所以索引表方案中只建立一個包含user_id和order_id的索引表,在插入訂單時再插入一條資料到索引表中。
表結構如下
CREATE TABLE idx_orderid_userid ( order_id bigint user_id bigint, PRIMARY KEY (order_id) )
在實現時可以將idx_orderid_userid表通過Redis快取來代替,如果此表資料量很大也可以將其分庫分表,但是它的分片鍵是 order_id。
如果這時再根據欄位 order_id 進行查詢,可以進行類似二級索引的回表實現:先通過查詢索引表得到記錄 order_id = 801462878019256325
對應的分片鍵 user_id 的值,接著再根據 user_id 進行查詢,最終定位到想要的資料,如:
原始SQL:
SELECT * FROM T_ORDER WHERE order_id = 801462878019256325
拆分後的SQL:
# step 1 SELECT user_id FROM idx_orderid_userid WHERE order_id = 801462890610556951 # step 2 SELECT * FROM T_ORDER WHERE user_id = ? AND order_id = 801462890610556951
這個例子是將一條 SQL 語句拆分成 2 條 SQL 語句,但是拆分後的 2 條 SQL 都可以通過分片鍵進行查詢,這樣能保證只需要在單個分片中完成查詢操作。不論有多少個分片,也只需要查詢 2個分片的資訊,這樣 SQL 的查詢效能可以得到極大的提升。
通過索引表的方式,雖然儲存上較冗餘全表容量小了很多,但是要根據另一個分片鍵進行資料的儲存,還是顯得不夠優雅。
因此,最優的設計,不是建立一個索引表,而是將分片鍵的資訊儲存在想要查詢的列中,這樣通過查詢的列就能直接知道所在的分片資訊,這種方法也叫叫做基因法。
基因法的原理出自一個理論:對一個數取餘2的n次方,那麼餘數就是這個數的二進位制的最後n位數。
假如我們現在根據user_id進行分片,採用user_id % 16的方式來進行資料庫路由,這裡的user_id%16,其本質是user_id的最後4個bit位 log(16,2) = 4 決定這行資料落在哪個分片上,這4個bit就是分片基因。
如上圖所示,user_id=20160169的使用者建立了一個訂單(20160169的二進位制表示為:1001100111001111010101001)
這樣保證了同一個使用者建立的所有訂單都落到了同一個分片上,order_id的最後4個bit都相同,於是:
不好理解的話,可以看下面這段程式碼:
@Test public void modIdTest(){ long userID = 20160169L; //分片數量 int shardNum = 16; String gen = getGen(userID, shardNum); log.info("userID:{}的基因為:{}",userID,gen); long snowId = IdWorker.getId(Order.class); log.info("雪花演演算法生成的訂單ID為{}",snowId); Long orderId = buildGenId(snowId,gen); log.info("基因轉換後的訂單ID為{}",orderId); Assert.assertEquals(orderId % shardNum , userID % shardNum); }
執行結果如下:
原始訂單ID為1595662702879973377
,通過基因轉換後ID變成了1595662702879973385
,對於使用者id 和 新生成的訂單id對其取模結果一樣。
上面那種做法是基因替換,替換掉訂單id的分片基因。下面這種做法就更顯直接。
將訂單表 orders 的主鍵設計為一個字串,這個字串中最後一部分包含分片鍵的資訊,如:
order_id = string(order_id + user_id)
那麼這時如果根據 order_id 進行查詢:
SELECT * FROM T_ORDER WHERE order_id = '1595662702879973377-20160169';
由於欄位 order_id 的設計中直接包含了分片鍵資訊,所以我們可以直接通過分片鍵部分直接定位到分片上。
同樣地,在插入時,由於可以知道插入時 user_id 對應的值,所以只要在業務層做一次字元的拼接,然後再插入資料庫就行了。
這樣的實現方式較冗餘表和索引表的設計來說,效率更高,查詢時可以直接定位到資料對應的分片資訊,只需 1 次查詢就能獲取想要的結果。
這樣實現的缺點是,主鍵值會變大一些,儲存也會相應變大。但是隻要主鍵值是有序的,插入的效能就不會變差。而通過在主鍵值中儲存分片資訊,卻可以大大提升後續的查詢效率,這樣空間換時間的設計,總體上看是非常值得的。
實際上淘寶的訂單號也是這樣構建的
上圖是我的淘寶訂單資訊,可以看到,訂單號的最後 6 位都是 607041,所以可以大概率推測出:
分庫分表後需要遵循一個基本原則:所有的查詢儘量帶上sharding key,有時候業務需要根據技術限制進行妥協,那種既要...又要...就是在耍流氓。
當然有些業務場景確實沒辦法避免,對於非sharding key的查詢可以參考上面三種方案實現,不過實際上只能算兩種。
曾經在面試時我還被問到過這個問題~
今天的文章是屬於理論知識,Talk is cheap,Show me the code!
接下來兩篇文章我將結合ShardingSphere-JDBC實現上述兩種方案,更多關於分庫分表非分片鍵查詢的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45