虚拟分片的测试流程
本章节讲解如何使用虚拟分片的分库分表,以及如何测试扩容新的分库分表
如果还不知道虚拟分片是什么,一定要去看前面虚拟分片的章节
一、前置工作
1.1 创建虚拟分片路由表和初始化数据
如果一开始就执行大麦pro项目,根目录/sql中的 damai_order_0.sql 和 damai_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_user 和 d_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_0 → damai_order_0.d_order_4
验证目标:
- ✅ 迁移前能在
d_order_0表查询到该用户的订单 - ✅ 迁移后能在
d_order_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,也可以启动项目,直接调用使用程序来查看数据,这样还能直接验证程序是不是可以正常查询
- 先启动 order 服务、gateway 服务
- 调用接口(post):http://127.0.0.1:6085/damai/order/order/simple/list
- 接口请求头添加参数来跳过验证签名: no_verify=true
- 调用接口参数选择:
使用用户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 迁移后,应用程序查看相关数据
启动项目,直接调用使用程序来查看数据,这样还能直接验证程序是不是可以正常查询
- 先启动 order 服务、gateway 服务
- 调用接口(post):http://127.0.0.1:6085/damai/order/order/simple/list
- 接口请求头添加参数来跳过验证签名: no_verify=true
- 调用接口参数选择:
使用用户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();