跳到主要内容

虚拟分片的测试流程

本章节讲解如何使用虚拟分片的分库分表,以及如何测试扩容新的分库分表

如果还不知道虚拟分片是什么,一定要去看前面虚拟分片的章节

一、前置工作

1.1 创建虚拟分片路由表和初始化数据

如果一开始就执行大麦pro项目,根目录/sql中的 damai_order_0.sqldamai_order_1.sql,就会直接有初始化配置的数据了

如果执行了扩容或者改变了路由表中的数据,可以单独执行以下sql进行重置配置

表关系

1.2 验证初始化:

我将初始配置(2库×4表,1024个虚拟分片)验证初始化.sql中的内容拿了出来,可以直接复制执行验证

-- 1. 验证总数(应该是1024条)
SELECT COUNT(*) FROM d_sharding_route_mapping;

-- 2. 验证每个物理分片的虚拟分片数量(每个应该是128条)
SELECT physical_database_suffix, physical_table_suffix, COUNT(*) AS cnt
FROM d_sharding_route_mapping
GROUP BY physical_database_suffix, physical_table_suffix
ORDER BY physical_database_suffix, physical_table_suffix;

-- 3. 验证虚拟分片ID的连续性(应该从0到1023)
SELECT MIN(logical_shard_id) AS min_id, MAX(logical_shard_id) AS max_id
FROM d_sharding_route_mapping;

-- 4. 查询特定虚拟分片ID(例如532)
SELECT * FROM d_sharding_route_mapping WHERE logical_shard_id = 532;
-- 应该返回:logical_shard_id=532, physical_database_suffix='1', physical_table_suffix=0

1.3 替换新的分库分表算法

订单服务中的 order 表使用了虚拟分片来实现新的分库分表算法,而 order_ticket_userd_order_ticket_user_record 还是使用原有的分库分表算法

因为新的分库分表算法需要使用虚拟分片路由表,所以把新的算法实现直接放到了订单服务中,并不影响原有的算法。

使用以下配置直接替换 shardingsphere-order-local.yaml 中的内容即可

注意: 如果自己数据库账号和密码不是root,需要进行修改

dataSources:
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/damai_order_0?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&autoReconnect=true
username: root
password: root
hikari:
# 连接池配置优化
minimum-idle: 15 # 最小空闲连接数(订单服务负载较高)
maximum-pool-size: 80 # 最大连接池大小(订单服务负载较高)
connection-timeout: 30000 # 连接超时时间(30秒)
idle-timeout: 600000 # 空闲连接超时时间(10分钟)
max-lifetime: 1800000 # 连接最大生命周期(30分钟)
leak-detection-threshold: 60000 # 连接泄漏检测阈值(60秒)
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/damai_order_1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&autoReconnect=true
username: root
password: root
hikari:
# 连接池配置优化
minimum-idle: 15 # 最小空闲连接数(订单服务负载较高)
maximum-pool-size: 80 # 最大连接池大小(订单服务负载较高)
connection-timeout: 30000 # 连接超时时间(30秒)
idle-timeout: 600000 # 空闲连接超时时间(10分钟)
max-lifetime: 1800000 # 连接最大生命周期(30分钟)
leak-detection-threshold: 60000 # 连接泄漏检测阈值(60秒)

rules:
- !SHARDING
tables:
d_order:
actualDataNodes: ds_${0..1}.d_order_${0..3}
databaseStrategy:
complex:
shardingColumns: order_number,user_id
# 新的分库算法
shardingAlgorithmName: databaseOrderVirtualShardingAlgorithm
tableStrategy:
complex:
shardingColumns: order_number,user_id
# 新的分表算法
shardingAlgorithmName: tableOrderVirtualShardingAlgorithm
d_order_ticket_user:
actualDataNodes: ds_${0..1}.d_order_ticket_user_${0..3}
databaseStrategy:
complex:
shardingColumns: order_number,user_id
shardingAlgorithmName: databaseOrderTicketUserComplexGeneArithmetic
tableStrategy:
complex:
shardingColumns: order_number,user_id
shardingAlgorithmName: tableOrderTicketUserComplexGeneArithmetic
d_order_ticket_user_record:
actualDataNodes: ds_${0..1}.d_order_ticket_user_record_${0..3}
databaseStrategy:
complex:
shardingColumns: order_number,user_id
shardingAlgorithmName: databaseOrderTicketUserRecordComplexGeneArithmetic
tableStrategy:
complex:
shardingColumns: order_number,user_id
shardingAlgorithmName: tableOrderTicketUserRecordComplexGeneArithmetic
d_order_program:
actualDataNodes: ds_${0..1}.d_order_program_${0..1}
databaseStrategy:
standard:
shardingColumn: program_id
shardingAlgorithmName: databaseOrderProgramModModel
tableStrategy:
standard:
shardingColumn: program_id
shardingAlgorithmName: tableOrderProgramModModel
bindingTables:
- d_order,d_order_ticket_user
broadcastTables:
- d_sharding_route_mapping
shardingAlgorithms:
# 新的分库算法
databaseOrderVirtualShardingAlgorithm:
type: CLASS_BASED
props:
sharding-count: 2
table-sharding-count: 4
strategy: complex
algorithmClassName: com.damai.shardingsphere.algorithm.DatabaseOrderVirtualShardingAlgorithm
# 新的分表算法
tableOrderVirtualShardingAlgorithm:
type: CLASS_BASED
props:
sharding-count: 4
strategy: complex
algorithmClassName: com.damai.shardingsphere.algorithm.TableOrderVirtualShardingAlgorithm
databaseOrderTicketUserComplexGeneArithmetic:
type: CLASS_BASED
props:
sharding-count: 2
table-sharding-count: 4
strategy: complex
algorithmClassName: com.damai.shardingsphere.DatabaseOrderComplexGeneArithmetic
tableOrderTicketUserComplexGeneArithmetic:
type: CLASS_BASED
props:
sharding-count: 4
strategy: complex
algorithmClassName: com.damai.shardingsphere.TableOrderComplexGeneArithmetic
databaseOrderTicketUserRecordComplexGeneArithmetic:
type: CLASS_BASED
props:
sharding-count: 2
table-sharding-count: 4
strategy: complex
algorithmClassName: com.damai.shardingsphere.DatabaseOrderComplexGeneArithmetic
tableOrderTicketUserRecordComplexGeneArithmetic:
type: CLASS_BASED
props:
sharding-count: 4
strategy: complex
algorithmClassName: com.damai.shardingsphere.TableOrderComplexGeneArithmetic
databaseOrderProgramModModel:
type: MOD
props:
sharding-count: 2
tableOrderProgramModModel:
type: MOD
props:
sharding-count: 2
props:
sql-show: false

二、生成测试数据,使用推荐用户ID验证迁移

2.1 执行脚本生成测试的订单数据

跳转到:虚拟分片扩容测试数据生成脚本 章节中,执行脚本来生成数据

2.2 找出测试用户ID(用于验证迁移)

测试用户ID:选择一个实际存在的用户ID

迁移路径damai_order_0.d_order_0damai_order_0.d_order_4

验证目标

  1. ✅ 迁移前能在d_order_0表查询到该用户的订单
  2. ✅ 迁移后能在d_order_4表查询到该用户的订单
  3. ✅ 迁移前后订单数量一致
  4. ✅ 通过ShardingSphere查询订单,路由正确

执行下面的sql,挑选其中一个用户ID,迁移前在d_order_0表,迁移后会被迁移到d_order_4

-- =====================================================
-- 查找实际存在的测试用户
-- =====================================================
-- 根据迁移逻辑,d_order_0 → d_order_4 的虚拟分片范围是 64-127
-- 我们需要找一个用户,其 logicalShardId 在 64-127 之间

-- 找出所有在d_order_0表中,且logicalShardId在64-127的用户
SELECT
user_id AS 用户ID,
COUNT(*) AS 订单数量,
calculate_logical_shard_id(user_id) AS 虚拟分片ID,
(user_id & 7) AS 基因位,
'damai_order_0.d_order_0 → damai_order_0.d_order_4' AS 迁移路径
FROM damai_order_0.d_order_0
GROUP BY user_id
HAVING calculate_logical_shard_id(user_id) >= 64
AND calculate_logical_shard_id(user_id) <= 127
ORDER BY calculate_logical_shard_id(user_id)
LIMIT 10;
表关系

2.3 迁移前,查看相关数据

2.3.1 查看该用户的所有订单详情(迁移前)

-- 设置你选择的测试用户ID
SET @test_user_id = <从上面查询结果中选择的用户ID>;

SELECT
id AS 订单ID,
order_number AS 订单号,
user_id AS 用户ID,
program_title AS 节目名称,
order_price AS 订单金额,
calculate_logical_shard_id(user_id) AS 用户虚拟分片ID,
calculate_logical_shard_id(order_number) AS 订单虚拟分片ID,
'damai_order_0.d_order_0' AS 当前表
FROM damai_order_0.d_order_0
WHERE user_id = @test_user_id
ORDER BY id;

记录下来

  • 订单数量:_____
  • 订单ID列表:_____
  • 订单号列表:_____
表关系

2.3.2 验证路由一致性(迁移前)

-- 验证该用户的所有订单是否都在d_order_0
-- 设置你选择的测试用户ID
SET @test_user_id = <从上面查询结果中选择的用户ID>;

SELECT
'迁移前检查' AS 检查项,
COUNT(*) AS 在d_order_0的订单数,
(SELECT COUNT(*) FROM damai_order_0.d_order_1 WHERE user_id = @test_user_id) AS 在d_order_1的订单数,
(SELECT COUNT(*) FROM damai_order_0.d_order_2 WHERE user_id = @test_user_id) AS 在d_order_2的订单数,
(SELECT COUNT(*) FROM damai_order_0.d_order_3 WHERE user_id = @test_user_id) AS 在d_order_3的订单数,
CASE
WHEN (SELECT COUNT(*) FROM damai_order_0.d_order_1 WHERE user_id = @test_user_id) = 0
AND (SELECT COUNT(*) FROM damai_order_0.d_order_2 WHERE user_id = @test_user_id) = 0
AND (SELECT COUNT(*) FROM damai_order_0.d_order_3 WHERE user_id = @test_user_id) = 0
THEN '✅ 所有订单都在d_order_0'
ELSE '❌ 订单分散在多张表'
END AS 验证结果
FROM damai_order_0.d_order_0
WHERE user_id = @test_user_id;

预期结果:所有订单都在d_order_0,其他表订单数为0

表关系

2.4 迁移前,应用程序查看相关数据

如果不想执行 2.3 迁移前,查看相关数据 章节的相关sql,也可以启动项目,直接调用使用程序来查看数据,这样还能直接验证程序是不是可以正常查询

  1. 先启动 order 服务、gateway 服务
  2. 调用接口(post):http://127.0.0.1:6085/damai/order/order/simple/list
  3. 接口请求头添加参数来跳过验证签名: no_verify=true
  4. 调用接口参数选择:

使用用户id查询:

{
"userId": "1000000000000000064"
}

使用订单编号查询:

{
"orderNumber": "29544954787266776"
}

查询获得结果后,可以先把结果记录下来,然后和后面的 4.4 迁移后,应用程序查看相关数据 章节中的查询结果进行对比,看看迁移前后数据查询是不是正确的。

三、执行扩容迁移

跳转到:虚拟分片扩容执行步骤 章节中,执行扩容迁移的操作

四、迁移后验证

4.1 确认订单已迁移到d_order_4

-- 设置你选择的测试用户ID
SET @test_user_id = <从上面查询结果中选择的用户ID>;

-- 方法1:直接查询d_order_4表
SELECT
user_id AS 用户ID,
COUNT(*) AS 订单数量,
calculate_logical_shard_id(user_id) AS 虚拟分片ID,
'damai_order_0.d_order_4' AS 当前位置
FROM damai_order_0.d_order_4
WHERE user_id = @test_user_id
GROUP BY user_id;

-- 方法2:确认d_order_0中已无该用户订单
SELECT
'damai_order_0.d_order_0' AS 原表,
COUNT(*) AS 剩余订单数,
CASE
WHEN COUNT(*) = 0 THEN '✅ 已清理'
ELSE '❌ 未清理干净'
END AS 清理状态
FROM damai_order_0.d_order_0
WHERE user_id = @test_user_id;

预期结果

  • d_order_4 中有该用户的订单
  • d_order_0 中该用户的订单数为0
表关系 表关系

4.2 查看该用户的所有订单详情(迁移后)

-- 设置你选择的测试用户ID
SET @test_user_id = <从上面查询结果中选择的用户ID>;

-- 查看迁移后的订单详情
SELECT
id AS 订单ID,
order_number AS 订单号,
user_id AS 用户ID,
program_title AS 节目名称,
order_price AS 订单金额,
calculate_logical_shard_id(user_id) AS 用户虚拟分片ID,
calculate_logical_shard_id(order_number) AS 订单虚拟分片ID,
'damai_order_0.d_order_4' AS 当前表
FROM damai_order_0.d_order_4
WHERE user_id = @test_user_id
ORDER BY id;

对比验证

  • ✅ 订单数量与迁移前一致
  • ✅ 订单ID列表与迁移前一致
  • ✅ 订单号列表与迁移前一致
  • ✅ 数据内容完全一致
表关系

4.3 验证路由映射更新

-- 检查路由映射表
SELECT
logical_shard_id AS 虚拟分片ID,
physical_database_suffix AS 物理库后缀,
physical_table_suffix AS 物理表后缀,
CONCAT('damai_order_', physical_database_suffix, '.d_order_', physical_table_suffix) AS 路由位置,
version AS 版本号
FROM d_sharding_route_mapping
WHERE logical_shard_id = 64;

预期结果

虚拟分片ID: 64
物理库后缀: 0
物理表后缀: 4
路由位置: damai_order_0.d_order_4
版本号: 2(或更高)
表关系

4.4 迁移后,应用程序查看相关数据

启动项目,直接调用使用程序来查看数据,这样还能直接验证程序是不是可以正常查询

  1. 先启动 order 服务、gateway 服务
  2. 调用接口(post):http://127.0.0.1:6085/damai/order/order/simple/list
  3. 接口请求头添加参数来跳过验证签名: no_verify=true
  4. 调用接口参数选择:

使用用户id查询:

{
"userId": "1000000000000000064"
}

使用订单编号查询:

{
"orderNumber": "29544954787266776"
}

可以和 2.4 迁移前,应用程序查看相关数据 章节中的数据对比,看看迁移前后数据查询是不是正确的。

4.5 推荐一键执行验证数据的SQL脚本

-- =====================================================
-- 测试用户ID迁移验证脚本
-- 使用方法:
-- 1. 先执行1.2节的查询,找出一个实际存在的用户ID
-- 2. 设置@test_user_id为你选择的用户ID
-- 3. 迁移前执行一次本脚本,记录结果
-- 4. 执行扩容迁移
-- 5. 迁移后再执行一次本脚本,对比结果
-- =====================================================

-- 设置测试用户ID(替换你的实际用户ID)
SET @test_user_id = <你选择的用户ID>;

-- 第一部分:基本信息
SELECT '========== 测试用户基本信息 ==========' AS '';
SELECT
@test_user_id AS 测试用户ID,
calculate_logical_shard_id(@test_user_id) AS 虚拟分片ID,
(@test_user_id & 7) AS 基因位,
'期望路由: 迁移前d_order_0, 迁移后d_order_4' AS 路由说明;

-- 第二部分:查询各表中的订单数量
SELECT '========== 各表订单分布 ==========' AS '';
SELECT 'd_order_0' AS 表名, COUNT(*) AS 订单数 FROM damai_order_0.d_order_0 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_1', COUNT(*) FROM damai_order_0.d_order_1 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_2', COUNT(*) FROM damai_order_0.d_order_2 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_3', COUNT(*) FROM damai_order_0.d_order_3 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_4', COUNT(*) FROM damai_order_0.d_order_4 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_5', COUNT(*) FROM damai_order_0.d_order_5 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_6', COUNT(*) FROM damai_order_0.d_order_6 WHERE user_id = @test_user_id
UNION ALL
SELECT 'd_order_7', COUNT(*) FROM damai_order_0.d_order_7 WHERE user_id = @test_user_id;

-- 第三部分:订单详情
SELECT '========== 订单详情 ==========' AS '';
SELECT
id,
order_number,
user_id,
program_title,
order_price,
calculate_logical_shard_id(order_number) AS 订单虚拟分片ID,
'from d_order_0' AS 来源表
FROM damai_order_0.d_order_0
WHERE user_id = @test_user_id
UNION ALL
SELECT
id,
order_number,
user_id,
program_title,
order_price,
calculate_logical_shard_id(order_number),
'from d_order_4'
FROM damai_order_0.d_order_4
WHERE user_id = @test_user_id
ORDER BY id;

-- 第四部分:路由映射检查
SELECT '========== 路由映射 ==========' AS '';
SELECT
logical_shard_id AS 虚拟分片ID,
CONCAT('damai_order_', physical_database_suffix, '.d_order_', physical_table_suffix) AS 路由表,
version AS 版本
FROM d_sharding_route_mapping
WHERE logical_shard_id = calculate_logical_shard_id(@test_user_id);

-- 第五部分:验证结果汇总
SELECT '========== 验证结果 ==========' AS '';
SELECT
'总订单数' AS 验证项,
(SELECT COUNT(*) FROM damai_order_0.d_order_0 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_1 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_2 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_3 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_4 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_5 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_6 WHERE user_id = @test_user_id) +
(SELECT COUNT(*) FROM damai_order_0.d_order_7 WHERE user_id = @test_user_id) AS 结果;

4.6 完整测试检查清单

检查项迁移前迁移后状态
用户存在✅ 在d_order_0✅ 在d_order_4⬜ 待验证
订单数量____ 个____ 个(应一致)⬜ 待验证
d_order_0中的订单____ 个0个⬜ 待验证
d_order_4中的订单0个____ 个⬜ 待验证
路由映射虚拟分片64→d_order_0虚拟分片64→d_order_4⬜ 待验证
ShardingSphere查询✅ 正常✅ 正常⬜ 待验证
数据完整性-✅ 无丢失⬜ 待验证

五、清理和重置

5.1 删除测试数据

-- ⚠️ 警告:此操作会删除所有数据,谨慎使用!

-- damai_order_0 库
TRUNCATE TABLE damai_order_0.d_order_0;
TRUNCATE TABLE damai_order_0.d_order_1;
TRUNCATE TABLE damai_order_0.d_order_2;
TRUNCATE TABLE damai_order_0.d_order_3;
TRUNCATE TABLE damai_order_0.d_order_4;
TRUNCATE TABLE damai_order_0.d_order_5;
TRUNCATE TABLE damai_order_0.d_order_6;
TRUNCATE TABLE damai_order_0.d_order_7;

-- damai_order_1 库
TRUNCATE TABLE damai_order_1.d_order_0;
TRUNCATE TABLE damai_order_1.d_order_1;
TRUNCATE TABLE damai_order_1.d_order_2;
TRUNCATE TABLE damai_order_1.d_order_3;
TRUNCATE TABLE damai_order_1.d_order_4;
TRUNCATE TABLE damai_order_1.d_order_5;
TRUNCATE TABLE damai_order_1.d_order_6;
TRUNCATE TABLE damai_order_1.d_order_7;

5.2 重置虚拟分片路由映射表的数据

将虚拟分片路由映射表恢复到初始的8个物理分片配置

表关系

5.3 再次生成测试数据:

如果还想再生成数据测试,可以再次执行存储过程来创建数据

注意:

此脚本在damai_order_0库或者damai_order_1库中任意 执行一次 就可以,执行后在damai_order_0库和damai_order_1库都会生成数据

生成数据的脚本:

CALL generate_test_data();