單表 t_group 1038 萬筆,按 user_name 分 8 組,單表查詢 user_name <> 'david' 耗時 9 秒,不可接受。
t_group_david, t_group_leo, t_group_livia, ... (共 8 表)
DELIMITER $$
CREATE PROCEDURE sp_split_table()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE v_user_name VARCHAR(20);
DECLARE v_table_name VARCHAR(64);
DECLARE cur1 CURSOR FOR SELECT user_name FROM t_group GROUP BY user_name;
DECLARE CONTINUE HANDLER FOR 1329 SET done = 1;
OPEN cur1;
WHILE done <> 1 DO
FETCH cur1 INTO v_user_name;
IF NOT done THEN
SET v_table_name = CONCAT('t_group_', v_user_name);
SET @stmt = CONCAT('CREATE TABLE ', v_table_name, ' LIKE t_group');
PREPARE s1 FROM @stmt; EXECUTE s1; DEALLOCATE PREPARE s1;
SET @stmt = CONCAT('INSERT INTO ', v_table_name, ' SELECT * FROM t_group WHERE user_name = ''', v_user_name, '''');
PREPARE s1 FROM @stmt; EXECUTE s1; DEALLOCATE PREPARE s1;
END IF;
END WHILE;
CLOSE cur1;
SET @stmt = NULL;
END$$
DELIMITER ;
| 查詢 | 原大表 | 分表後 | 備註 |
|---|---|---|---|
COUNT(*) WHERE user_name='david' | 1.71s | 0.00s | 覆蓋索引秒出 |
COUNT(*) WHERE user_name<>'david' | 9.26s | 0.00s | 改用 總數 - david數 |
SUM/AVG money WHERE user_name='david' | 2.15s | 0.94s | 分表後仍需掃描,但數據量少 8 倍 |
ALTER TABLE t_group_david DROP COLUMN user_name;
效果:
MIN/MAX 從 1.5s 降為 0.00s(索引生效)SUM/AVG 從 1.68s 降為 0.94s-- 原:跨 user_name 聚合
SELECT user_name, COUNT(*) FROM t_group GROUP BY user_name;
-- 新:UNION ALL 分表結果
SELECT 'david' AS user_name, COUNT(*) FROM t_group_david
UNION ALL SELECT 'leo', COUNT(*) FROM t_group_leo
...;
-- 原:排除某 user
SELECT COUNT(*) FROM t_group WHERE user_name <> 'david';
-- 新:總數 - 該分表數
SELECT (SELECT COUNT(*) FROM t_group) - (SELECT COUNT(*) FROM t_group_david) AS total;
物理分表將大表拆小,配合刪除冗餘欄位、查詢改寫(排除查詢用總數相減),將 9 秒查詢壓至毫秒級。核心是「以空間換時間、以冗餘換查詢簡單度」。