这样保证了同一个用户创建的所有订单都落到了同一个分片上,order_id的最后4个bit都相同,于是:
- 通过user_id %16 能够定位到分片
- 通过order_id % 16也能定位到分片
@Testpublic 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_ORDERWHERE order_id = '1595662702879973377-20160169';由于字段 order_id 的设计中直接包含了分片键信息,所以我们可以直接通过分片键部分直接定位到分片上 。同样地,在插入时,由于可以知道插入时 user_id 对应的值,所以只要在业务层做一次字符的拼接,然后再插入数据库就行了 。
这样的实现方式较冗余表和索引表的设计来说,效率更高,查询时可以直接定位到数据对应的分片信息,只需 1 次查询就能获取想要的结果 。
这样实现的缺点是,主键值会变大一些,存储也会相应变大 。但是只要主键值是有序的,插入的性能就不会变差 。而通过在主键值中保存分片信息,却可以大大提升后续的查询效率,这样空间换时间的设计,总体上看是非常值得的 。
实际上淘宝的订单号也是这样构建的

文章插图
上图是我的淘宝订单信息,可以看到,订单号的最后 6 位都是 607041,所以可以大概率推测出:
- 淘宝订单表的分片键是用户 ID;
- 淘宝订单表,订单表的主键包含用户 ID,也就是分片信息 。这样通过订单号进行查询,可以获得分片信息,从而查询 1 个分片就能得到最终的结果 。
当然有些业务场景确实没办法避免,对于非sharding key的查询可以参考上面三种方案实现,不过实际上只能算两种 。
推荐阅读
- 钱币|铜钱没有年号却成了争抢的品种,“花钱”的收藏潜力如何?
- 艺考音乐(艺考生需要收藏的100条乐理小常识)
- |咸丰重宝在清代钱币收藏里面的地位如何
- 第四套人民币|第四套人民币902和第五套人民币995,谁的收藏价值高呢?
- |华为、腾讯、阿里标杆企业盘点实践,收藏!
- 翡翠|佳士得香港2022秋拍,黄花梨家具收藏热度再创新高!
- 橙红年代:聂万峰说谎,刘子光招供,聊聊马思纯的哭戏还有……
- 给想赚钱的女生8条建议,趁现在提升自己的能力
- 银行|明年比今年更“难熬”?普通老百姓将面临5个难题,建议早做准备
- 六年级语文小升初分类专题―句子,题型多样,值得收藏练一练
