跨境站数据库分表优化海量产品:跨境站产品数据量太大?手把手教
为什么跨境站的产品表会越查越慢
当你运营一个拥有百万甚至千万级SKU的跨境站时,产品表(比如 products)随着订单、库存、价格历史数据的积累,单表行数轻松突破千万。
这时即使加了索引,SELECT 和 INSERT 也会明显变慢——原因很简单:B+树索引层数增加,磁盘IO开销翻倍。分表 就是把一张大表拆成多张小表,让每次查询只扫描一部分数据,从而大幅降低响应时间。
本文以 MySQL 5.7+ 为例,演示水平分表的完整过程,保证你可以对照执行。
分表前的准备工作
1. 判断是否需要分表
- 查看当前产品表行数:
SELECT COUNT(*) FROM products; - 如果超过 500 万行且
SELECT查询耗时超过 500ms,就值得考虑分表。 - 记录当前每秒写入量,方便分表后对比。
2. 选择合适的分表键
分表键决定了数据如何分布。
跨境站常用分表键:
- product_id(按ID哈希均分)
- category_id(按品类归堆)
- region(按站点/国家分)
推荐使用 product_id 哈希分表,因为数据分布最均衡,且后续扩容简单。
3. 准备分表数量
一般幂次方,例如 16、64、128 张子表。
数量越多,单表行数越少,但跨表查询成本会上升。
经验值:单张子表行数控制在 200 万以内。
假设当前 2000 万行,分 16 张表,每张约 125 万,合理。
核心操作:水平分表脚本与执行
以下操作在 your_db 数据库上执行,请提前备份。
第一步:创建分表结构
-- 假设原表 products 结构如下:
-- id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), sku VARCHAR(100), price DECIMAL(10,2), created_at DATETIME
-- 创建 16 张子表,以 t_0 ~ t_15 命名
DELIMITER $$
CREATE PROCEDURE create_shard_tables()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < 16 DO
SET @sql = CONCAT('CREATE TABLE IF NOT EXISTS products_', i,
' LIKE products');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
CALL create_shard_tables();
第二步:迁移数据
因为分表键为 id,取模路由到对应子表:
INSERT INTO products_0 SELECT * FROM products WHERE id % 16 = 0;
INSERT INTO products_1 SELECT * FROM products WHERE id % 16 = 1;
-- 重复执行至 products_15
生产环境可用 mysqldump 分片导出或使用 pt-archiver 工具,但这里为了新手理解,展示最直接的 SQL。注意:迁移期间原表仍在写入,建议在维护窗口执行。
第三步:创建路由逻辑
在应用中修改数据库查询代码。
例如 PHP 伪代码:
function getProductById($id) {
$shardKey = $id % 16;
$tableName = 'products_' . $shardKey;
$sql = "SELECT * FROM {$tableName} WHERE id = ?";
// 执行查询
}
如果框架不支持动态表名,可使用 ThinkPHP 的模型表名设置或 Laravel 的 setTable() 方法。
常见问题与避坑指南
1. 跨表查询怎么办?
- 场景:按品类或价格范围筛选。
- 解决方法:在分表后新增 汇总表 或使用 Elasticsearch 做全文搜索。简单方案:业务上限制跨表查询为后台专用,前台只允许按 ID 或 SKU 精准查询。
2. 分表后自增ID冲突
- 每个子表单独
AUTO_INCREMENT会重复。建议:改为使用雪花算法或UUID做主键,保证全局唯一。迁移时可先将原表 ID 保留,新数据用雪花ID。
3. 迁移过程中写丢失
- 风险点:把数据从原表搬到子表时,新写入可能被漏掉。
- 规避:先停写(锁表)迁移,或使用双写策略(同时写入原表和子表一段时间)再切换。锁表脚本:
LOCK TABLES products WRITE;
-- 执行迁移
UNLOCK TABLES;
效果验证方法
1. 对比查询时间
-- 原表查询耗时
SELECT * FROM products WHERE id = 123456;
-- 分表后查询
SELECT * FROM products_0 WHERE id = 123456;
记录 Query_time,通常分表后耗时下降 80% 以上。
2. 检查单表行数
SELECT COUNT(*) FROM products_0; -- 期望在 200 万以内
3. 监控慢查询日志
开启慢查询日志,观察分表后慢查询条目是否减少。
总结
跨境站数据库分表优化海量产品,核心就是用空间换时间。
只要选对分表键、控制子表行数、做好路由逻辑,索引依然有效,查询速度能恢复到百万级数据时的水平。
初次分表建议在业务低峰期操作,提前备份,并保留原表作为回退方案。
如果你在操作中遇到报错,比如 Table 'products_0' doesn't exist,请先确认子表是否创建成功,再检查哈希路由是否正确。