分表動機
單表 1000 萬+ 行,按 user_name 查詢耗時 2-10 秒,需物理隔離熱數據,降低單表掃描量。
自動化分表存儲過程
DELIMITER $$
DROP PROCEDURE IF EXISTS `sp_split_table_by_user`$$
CREATE PROCEDURE `sp_split_table_by_user`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE v_user_name VARCHAR(20) DEFAULT '';
DECLARE v_table_name VARCHAR(64) DEFAULT '';
-- 獲取所有用戶名
DECLARE cur CURSOR FOR
SELECT DISTINCT `user_name` FROM `t_group`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_user_name;
IF done THEN LEAVE read_loop; END IF;
SET v_table_name = CONCAT('t_group_', v_user_name);
-- 1. 創建分表(複製結構含索引)
SET @stmt = CONCAT('CREATE TABLE `', v_table_name, '` LIKE `t_group`');
PREPARE s1 FROM @stmt; EXECUTE s1; DEALLOCATE PREPARE s1;
-- 2. 導入數據
SET @stmt = CONCAT('INSERT INTO `', v_table_name, '` SELECT * FROM `t_group` WHERE `user_name` = ', QUOTE(v_user_name));
PREPARE s1 FROM @stmt; EXECUTE s1; DEALLOCATE PREPARE s1;
-- 3. 刪除分區鍵列(節省空間、提升聚合速度)
SET @stmt = CONCAT('ALTER TABLE `', v_table_name, '` DROP COLUMN `user_name`');
PREPARE s1 FROM @stmt; EXECUTE s1; DEALLOCATE PREPARE s1;
END LOOP;
CLOSE cur;
END$$
DELIMITER ;
-- 執行
CALL sp_split_table_by_user();
性能對比實測(1000 萬行,8 用戶)
| 查詢 | 原大表 | 分表 | 提升 |
| COUNT(*) WHERE user='david' | 1.71 s | 0.00 s | ∞ (索引覆蓋/全表掃描→單表掃描) |
| COUNT(*) WHERE user!='david' | 9.26 s | 0.00 s (差集查詢) | ∞ |
| MIN/MAX(money) WHERE user='david' | 0.00 s | 0.00 s (Drop user_name 後) | 持平 |
| SUM/AVG(money) WHERE user='david' | 2.15 s | 0.94 s (Drop user_name 後) | 2.3x |
關鍵優化點:刪除分區鍵列
ALTER TABLE `t_group_david` DROP COLUMN `user_name`;
- 列值全相同,存儲純浪費
- 移除後聚合查詢(MIN/MAX/SUM/AVG)由全表掃描變為索引全掃描/覆蓋索引,速度質變
- 節省 20 字節/行 × 130 萬行 ≈ 26MB 空間
應用層路由策略
中間件路由(推薦)
// ShardingSphere / Mycat / ProxySQL 配置片段
rules:
- table: t_group
shardingColumn: user_name
algorithm: HINT -- 應用顯式指定
# 或 RANGE/MOD 等算法
應用層動態表名
function getTableName(string $userName): string {
return 't_group_' . $userName;
}
$sql = "SELECT * FROM `" . getTableName($userName) . "` WHERE id > ?";
跨分表查詢方案
| 方案 | 實現 | 優點 | 缺點 |
| 應用層合並 | 並行查各分表,內存合並排序 | 無中間件、靈活 | 跨分頁/排序複雜、內存壓力 |
| 中間件合並 | ProxySQL/ShardingSphere 自動改寫 | 透明化、支持分頁排序 | 引入組件、調試難 |
| 匯總表 | 定時任務彙總到汇總表 | 查詢極快 | 數據有延遲、維護成本 |
| 全局二級索引表 | 冗余 user_name+id 到單獨表 | 定位分表快 | 寫入雙寫、一致性風險 |
遷移上線清單
- 雙寫期:寫入總表 + 分表,讀總表
- 驗證期:讀分表,對比總表數據一致性
- 切換期:流量切讀分表,停寫總表
- 清理期:歸檔/刪除總表數據
擴展:按時間分表(日志/流水)
-- 月度分表
CREATE TABLE `logs_202401` LIKE `logs`;
CREATE TABLE `logs_202402` LIKE `logs`;
-- 交換分區(MySQL 8.0+ Instant DDL 不支持分區交換,需 ALTER TABLE ... REORGANIZE PARTITION)
-- 或使用 pt-osc / gh-ost 在線變更
分表是「以空間換時間、以複雜度換性能」的架構決策。務必先嘗試:索引優化、分區表、讀寫分離、緩存,確無法滿足時再引入分表。
copyright ©
萬盛學電腦網 all rights reserved