连载中 5/20

分片键:一次选择,终身锁死

2026-07-20 · 1204 阅读 · 0 评论 · 0 赞

改不动的决策

分库分表的所有决策里,扩容可以重新设计、路由可以换中间件、表数可以从 64 加到 128,唯独分片键一旦选定,几乎是终身制。原因在于一条铁律:所有查询都必须能推导出数据在哪个分片。而查询是活着长出来的——今天按用户查订单,明天运营要按商家查,后天客服要按订单号查。分片键选了 user_id,所有不带 user_id 的查询,都会退化成扫全部分片的「全路由」,分片越多,扫得越惨。

所以选分片键的第一原则不是技术,是业务:把系统里频次最高、最重要的查询维度找出来,用数据说话——把慢查询日志和访问日志拉出来按 WHERE 条件聚类,哪个维度的查询占了八成,它就是候选。

经典难题:订单的三个主人

订单表是这个难题的教科书案例。一张订单至少有三个查询维度:买家(我的订单)、卖家(我的生意)、订单号(客服与支付回调)。三个维度互相独立,而分片键只能选一个——选 buyer_id,卖家的「店铺订单列表」就要扫全分片;选 seller_id,买家的「我的订单」同样遭殃。三种经典解法,按代价从小到大排:

解法一:冗余双写——订单数据写两份,一份按 buyer_id 分片、一份按 seller_id 分片,两个维度各自精准路由,代价是存储翻倍、写入要保证两份一致、任何字段变更都要双份同步。适合两个维度都高频的场景。解法二:异构索引——主数据按 buyer_id 分片,另建一套按 seller_id 组织的订单索引表(或直接喂给搜索引擎),索引只存定位信息,回主库取详情。ES 这类检索引擎天然适合干这个,代价是多维护一条数据同步链路。解法三:基因法——在生成订单号时,把 buyer_id 的哈希「基因」埋进去,订单号本身就能算出分片,按订单号查不再需要额外路由;再配合解法一或二覆盖卖家维度。基因法的订单号形如:

订单号 = 时间戳段 | buyer基因(hash(buyer_id) 低 10 位) | 序列段
路由:  slot = 订单号基因段 % 1024   // 与按 buyer_id 路由结果一致

选键的其余纪律

除了维度覆盖,分片键还有几条纪律。要分散:键的取值分布要均匀,用省份、状态这类低基数字段做分片键,等于给自己造热点;自增 ID 单调递增会让 Range 刀法出现写入热点,配 Hash 刀法则没问题。要常在:分片键必须在绝大多数查询和写入请求里天然可用——用户下单必带 user_id,天然贴合;如果分片键只在少数场景出现,数据写入就没了归属。要稳定:分片键的值不能变——数据一旦落片,改分片键等于删除加重新插入,业务上往往是灾难(换手机号都别想让用户数据搬家)。

把纪律合起来就是一句口诀:高频维度做键、基因法补洞、冗余或异构兜底。分片键定了,下一个问题是路由逻辑放在哪儿执行——写在代码里、坐在代理上,还是长在 SDK 里,下一篇比较三种落法。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#分片键#基因法#冗余双写#异构索引#路由

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞