跳到主要内容

虚拟分片查询一致性分析

如果小伙伴对于 为什么通过 订单编号(OrderNumber)以及 用户id(userId)在虚拟分片路由表查询后,能够定位到相同的物理库和物理表 还有疑惑的话,可以详细查看此篇章节的讲解

一、观察到的现象

订单编号查询:

shardingKey = 1978005150261182474
→ 虚拟分片266 → 库0.表2

用户ID查询:

shardingKey = 1136344580112367618
→ 虚拟分片258 → 库0.表2

现象:

虚拟分片ID不同(266 vs 258),但最终路由到相同位置(库0.表2)!

二、真实数据计算验证

订单号:1978005150261182474

步骤1:提取基因位(后3位)

1978005150261182474 & 0b111 = 2 (二进制:0b10)

步骤2:解析基因位

  • 表索引(后2位):2
  • 库索引(第3位):0

步骤3:计算物理分片索引

physicalShardIndex = 0 × 4 + 2 = 2

步骤4:计算虚拟偏移

1978005150261182474 % 128 = 10

步骤5:计算虚拟分片ID

logicalShardId = 2 × 128 + 10 = 266

用户ID:1136344580112367618

步骤1:提取基因位(后3位)

1136344580112367618 & 0b111 = 2 (二进制:0b10)

步骤2:解析基因位

  • 表索引(后2位):2
  • 库索引(第3位):0

步骤3:计算物理分片索引

physicalShardIndex = 0 × 4 + 2 = 2

步骤4:计算虚拟偏移

1136344580112367618 % 128 = 2

步骤5:计算虚拟分片ID

logicalShardId = 2 × 128 + 2 = 258

关键对比

维度订单号用户ID是否相同
基因位(后3位)22✅ 相同
物理分片索引22✅ 相同
虚拟偏移102❌ 不同
虚拟分片ID266258❌ 不同
虚拟分片范围256-383256-383✅ 相同
最终路由库0.表2库0.表2✅ 相同

三、核心机制解析

3.1 为什么 physicalShardIndex 相同?

关键:订单号生成时嵌入了用户ID的基因位!

回顾 SnowflakeIdGenerator.getOrderNumber() 方法:

public synchronized long getOrderNumber(long userId, 
long tableCount,
long databaseCount) {
//pro项目中有详细的代码和注释
}

结果:

userId的后3位 = 订单号的后3位

验证:
userId: 1136344580112367618 & 0b111 = 2
orderNumber: 1978005150261182474 & 0b111 = 2
✅ 相同!

3.2 为什么虚拟分片ID不同但路由相同?

物理分片2对应的虚拟分片范围:256 - 383

路由表映射:

┌─────────────────┬──────────────────┬──────────────────┐
│ 虚拟分片ID │ 物理库表 │ 说明 │
├─────────────────┼──────────────────┼──────────────────┤
│ 256 │ db_0.table_2 │ │
│ 257 │ db_0.table_2 │ │
│ 258 │ db_0.table_2 │ ← 用户ID路由到这里│
│ ... │ db_0.table_2 │ │
│ 266 │ db_0.table_2 │ ← 订单号路由到这里│
│ ... │ db_0.table_2 │ │
│ 383 │ db_0.table_2 │ │
└─────────────────┴──────────────────┴──────────────────┘

关键:虚拟分片 256-383 的所有ID都映射到同一个物理表!

三层保障机制

第1层保障:基因法
├─ 订单号生成时嵌入userId的基因位(后3位)
└─ 结果:订单号和userId的基因位相同 ✅

第2层保障:物理分片索引
├─ 基因位相同 → physicalShardIndex相同
└─ 结果:都路由到物理分片2 ✅

第3层保障:虚拟分片范围
├─ physicalShardIndex相同 → 虚拟分片在同一个128范围内
├─ 物理分片2 → 虚拟分片256-383
└─ 结果:266和258都在256-383范围内 ✅

最终保障:路由表映射
├─ 虚拟分片256-383全部映射到同一个物理表
└─ 结果:都路由到 damai_order_0.d_order_2 ✅

3.3 为什么必须整体迁移128个虚拟分片?

❌ 错误示例(只迁移部分虚拟分片)

虚拟分片258 → 迁移到 db_0.table_6
虚拟分片266 → 保留在 db_0.table_2

结果:
用户ID查询 → 路由到 table_6 ✗
订单号查询 → 路由到 table_2 ✗
查询不一致!数据找不到!

✅ 正确示例(整体迁移128个虚拟分片)

虚拟分片256-383 → 全部迁移到 db_0.table_6

结果:
用户ID查询 → 路由到 table_6 ✓
订单号查询 → 路由到 table_6 ✓
查询一致!数据都在table_6!

3.4 🔑 核心问题:为什么 266 和 258 都在 [256, 383] 范围内?

💡 最核心的本质(一句话说清楚)

关键就是这一行代码

int virtualOffset = (int)(Math.abs(shardingKey) % virtualShardsPerPhysical);
// ↑
// 对128取模

付费内容提示

该文档的全部内容仅对「码力全开」项目实战&技术讲解 知识星球用户开放

加入星球,一次获得完整项目资料、全栈技术知识库和长期答疑服务。

100万+字全栈技术知识库深入讲解技术核心、数据库、中间件和分布式等内容
8套热门的实战项目持续更新的企业级项目覆盖高并发、微服务、数据中台 和 AI Agent 等方向
AI 技术知识大模型面试详解覆盖 AI 模型原理、Agent、RAG、MCP、Skills、Harness 等核心知识点
文档 + 视频两种讲解形式既能系统阅读,也能跟随视频理解核心业务

完整项目实战资料

每套项目均包含从 0 到 1 讲解文档核心业务讲解视频

从基础项目到复杂业务场景,项目资料会持续更新。

8 套项目
  • 01Nexus Agent AI 智能体
  • 02Nexus Agent Pro 完全版
  • 03黑马点评Plus
  • 04大麦
  • 05大麦Pro
  • 06大麦AI
  • 07流量切换
  • 08数据中台

加入后还能获得

进入星球后,即可享受上述所有服务,保证不会再有其他隐藏费用。

从学习、面试到项目启动,都可以继续获得支持。

  • 1 对 1 解答项目和技术问题都可以提问
  • 针对性补充没有讲清楚的内容会继续补充
  • 面试与简历指导梳理回答技巧和项目亮点
  • 中间件云环境项目依赖可以直接接入使用
  • 面试后复盘被问住的问题可以继续交流
  • 远程问题解决项目启动问题可协助排查
知识星球二维码

扫码进入知识星球

  1. 打开微信,扫描左侧二维码,加入「码力全开」项目实战&技术讲解 知识星球
  2. 查看星球使用指导,获取完整项目讲解资料索引
解锁全部付费内容
🎁优惠