当前位置: 首页 > news >正文

Mysql-经典实战案例(3): pt-archiver 实现 MySQL 千万级大表分库分表(上)

零基础实战:使用 pt-archiver 实现 MySQL 千万级大表的水平分表(Hash分片)


本文适合人群:MySQL新手、想低成本实践数据库分表的开发者
环境要求:MySQL 5.7+、Linux系统(建议CentOS/Ubuntu)
你将学会

  • 📦 1分钟生成百万测试数据
  • 🔧 安全拆解大表为5个哈希分片表
  • 🛠 零停机分表迁移实操技巧

一、为什么需要分表?

假设您有一张 订单表 orders 数据达到 1000万行,面临以下问题:
1️⃣ 查询变慢SELECT * FROM orders WHERE user_id='xxx' 需要扫描全表
2️⃣ 维护困难:索引膨胀、备份耗时
3️⃣ 容灾风险:单表损坏可能导致业务瘫痪

解决方案
👉 Hash分表:将数据按 user_id 的哈希值分散到 orders_0~orders_4 五个分片中,查询永远只需扫一张小表!


二、5分钟快速搭建测试环境

环境准备清单

  1. 安装 MySQL 5.7(已安装可跳过)

    # Ubuntusudo apt install mysql-server-5.7# CentOSsudo yum install mysql-community-server
    
  2. 安装 Percona Toolkit

    # 所有Linux版本通用wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.debsudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.debsudo apt-get updatesudo apt-get install percona-toolkit
    
  3. 创建测试数据库

    CREATE DATABASE test_sharding;USE test_sharding;
    
  4. 开启LOCAL INFILE参数

    允许客户端从本地文件系统中读取数据文件,并将其内容加载到远程 MySQL 服务器的表中,使用pt-archiver分片导入的前提参数。

    local_infile = on
    

三、1分钟生成百万测试数据

复制下列代码到 MySQL客户端,快速生成100万测试订单:

-- 创建原始订单表
CREATE TABLE orders (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id VARCHAR(20) NOT NULL,  -- 分片键(关键字段)
  amount DECIMAL(10,2) NOT NULL,
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- 快速填充100万数据(耗时约30秒)
INSERT INTO orders (user_id, amount)
SELECT 
  CONCAT('user_', FLOOR(RAND() * 1000000)),
  ROUND(RAND() * 100, 2)
FROM 
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) a,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) b,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) c,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) d,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) e,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) f,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) g,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) h,
  (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) i;

执行成功后验证

SELECT COUNT(*) FROM orders; 

四、哈希分表实战:拆解大表为5个分片

1. 创建分片表

-- 创建5个分片表(结构与原表一致)
CREATE TABLE orders_0 LIKE orders;
CREATE TABLE orders_1 LIKE orders;
CREATE TABLE orders_2 LIKE orders;
CREATE TABLE orders_3 LIKE orders;
CREATE TABLE orders_4 LIKE orders;

2. 关键分片逻辑

通过 哈希函数 CRC32 将数据均匀分散到分片中:

CRC32(user_id) % 5 = 0 → 存入 orders_0  
CRC32(user_id) % 5 = 1 → 存入 orders_1  
... 以此类推

3. 使用 pt-archiver 迁移数据

将原表数据按 Hash 规则迁移到分片表(不锁表、不影响线上业务):

迁移到 orders_0 分片
/opt/percona-toolkit-3.6.0/bin/pt-archiver --source h=127.0.0.1,P=3306,u=root,p=密码,D=test_sharding,t=orders --dest h=127.0.0.1,P=3306,u=root,p=Qy@950422,D=test_sharding,t=orders_0 --where "CRC32(user_id) %5=0" --limit 500 --commit-each --bulk-insert --no-delete --no-check-charset

迁移其他分片

修改 --where 条件和目标表名,依次执行:

# orders_1(余数1)
pt-archiver ... --where "CRC32(user_id) %5=1" --dest t=orders_1

# orders_2(余数2)
pt-archiver ... --where "CRC32(user_id) %5=2" --dest t=orders_2
...(省略orders_3/4)


五、分片结果验证

1. 核对各分片数据量

SELECT 
  table_name AS '分片表',
  FORMAT(table_rows,0) AS '数据量'
FROM information_schema.tables 
WHERE table_schema = 'test_sharding' 
  AND table_name LIKE 'orders_%';

期望结果(误差应小于2%):

+-----------+-----------+
| 分片表    | 数据量    |
+-----------+-----------+
| orders_0  | 390,780   |
| orders_1  | 390,112   |
| orders_2  | 389,778   |
| orders_3  | 388,776   |
| orders_4  | 389,778   |
+-----------+-----------+

2. 数据完整性检验

-- 所有分片总和 = 原表总数
SELECT 
  (SELECT COUNT(*) FROM orders_0) + 
  (SELECT COUNT(*) FROM orders_1) +
  (SELECT COUNT(*) FROM orders_2) +
  (SELECT COUNT(*) FROM orders_3) +
  (SELECT COUNT(*) FROM orders_4) AS total_count;

-- 应与原表数据量一致
SELECT COUNT(*) FROM orders;

注意!pt-archiverBug不会迁移max(id)那条数据,需要手动完成最后一行数据的迁移

定位数据差异

SELECT * 
FROM orders 
WHERE NOT EXISTS (
  SELECT 1 FROM orders_0 WHERE orders_0.id = orders.id UNION ALL
  SELECT 1 FROM orders_1 WHERE orders_1.id = orders.id UNION ALL
  SELECT 1 FROM orders_2 WHERE orders_2.id = orders.id UNION ALL
  SELECT 1 FROM orders_3 WHERE orders_3.id = orders.id UNION ALL
  SELECT 1 FROM orders_4 WHERE orders_4.id = orders.id 
) 
LIMIT 1;

+---------+-----------+--------+---------------------+
| id      | user_id   | amount | create_time         |
+---------+-----------+--------+---------------------+
| 1953125 | user_9823 |  39.33 | 2025-03-14 17:03:24 |
+---------+-----------+--------+---------------------+

使用insert 插入最后一条数据到orders_4表

INSERT INTO orders_4 (id, user_id, amount, create_time)
SELECT id, user_id, amount, create_time 
FROM orders 
WHERE id=1953125;


六、常见问题排雷指南

1. 迁移性能慢(每秒不到1000条)

  • 优化技巧

    -- 增加批量大小(视内存调整)
    --limit 1000 \
    --bulk-insert \
    

2. 主键冲突错误

  • 解决方案

    • 迁移前清空分片表:TRUNCATE orders_0
    • 或使用 --replace 参数覆盖已有数据

3. 数据分布不均匀

  • 原因排查

    • 检查分片键是否有倾斜(如某user_id占比过高)
    • 改用 一致性哈希算法(如 SHA1(user_id)

七、分表后如何查询数据?

通过业务代码计算目标分片,直连对应表:

# Python示例:按user_id定位分片
user_id = "user_12345"
shard_no = crc32(user_id.encode()) % 5  # 计算分片编号
table_name = f"orders_{shard_no}"
query = f"SELECT * FROM {table_name} WHERE user_id = %s"

总结

通过本方案,我们实现了:

  • 🚀 100万数据安全拆分:无锁迁移、分钟级完成
  • 📊 分布式查询提速:查询性能提升5~10倍
  • 🔒 数据零丢失保证:每一步操作均可回滚验证

下一篇预告
1、如何完成垂直分表?将单表中高频访问的"热数据"与低频使用的"冷数据"分离存储!
2、如何可以将分片逻辑封装到中间件(如 MyCAT),彻底告别单表性能瓶颈!

相关文章:

  • 设计模式之外观模式:原理、实现与应用
  • 设备物联网无线交互控制,ESP32无线联动方案,产品智能化响应
  • OpenCV计算摄影学(23)艺术化风格化处理函数stylization()
  • 【Android Studio】解决遇到的一些问题
  • Vue3项目中可以尝试封装那些组件
  • SpringSecurity——如何实现验证码登录页面
  • 内存回收异常导致OOM的问题
  • Android中的Wifi框架系列
  • JS中的变量提升
  • OpenCV 图像双线性插值
  • 地球46亿年历史的“微观褶皱”
  • 六十天前端强化训练之第二十二天之React 框架 15天深度学习总结(大师版)
  • 节点编辑器STNodeEditor快速入门,流程图编程
  • 【USTC 计算机网络】第二章:应用层 - 应用层原理
  • postgresql 高版本pgsql备份在低版本pgsql中恢复失败,报错:“unsupported version”
  • 直线模组定位精度差的原因
  • Flume详解——介绍、部署与使用
  • 最长最短单词(信息学奥赛一本通-1143)
  • 深圳南柯电子|医疗设备EMC检测测试整改:保障患者安全的第一步
  • centos 安装pip时报错 Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64
  • 两部门发布山洪灾害气象预警:北京西部、河北西部等局地山洪可能性较大
  • 苹果Safari浏览器上的搜索量首次下降
  • 吴清:推动公募基金高质量发展的行动方案今天将会发布
  • 刘诚宇、杨皓宇进球背后,是申花本土球员带着外援踢的无奈
  • 48岁黄世芳履新中国驻毛里求斯大使,曾在广西工作多年
  • 联合国秘书长古特雷斯呼吁印巴保持最大克制