表 t_group 1038 萬行,按 user_name(8 個用戶)水平拆分為 t_group_{user} 8 張子表。單列 user_name 索引基數極低(Cardinality=8),屬「爛索引」。
CREATE PROCEDURE sp_split_table()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE v_user VARCHAR(20);
DECLARE v_tbl VARCHAR(64);
DECLARE cur CURSOR FOR SELECT user_name FROM t_group GROUP BY user_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
WHILE done <> 1 DO
FETCH cur INTO v_user;
IF NOT done THEN
SET v_tbl = CONCAT('t_group_', v_user);
SET @sql = CONCAT('CREATE TABLE ', v_tbl, ' LIKE t_group');
PREPARE s1 FROM @sql; EXECUTE s1; DEALLOCATE PREPARE s1;
SET @sql = CONCAT('INSERT INTO ', v_tbl, ' SELECT * FROM t_group WHERE user_name = "', v_user, '"');
PREPARE s1 FROM @sql; EXECUTE s1; DEALLOCATE PREPARE s1;
END IF;
END WHILE;
CLOSE cur;
SET @sql = NULL;
END;
| 查詢類型 | 大表 (t_group) | 子表 (t_group_david) | 子表去除 user_name 後 |
|---|---|---|---|
COUNT(*) WHERE user_name='david' | 1.71 s | 0.00 s | 0.00 s |
COUNT(*) WHERE user_name<>'david' | 9.26 s | N/A | 0.00 s (總數-子表數) |
MIN/MAX(money) WHERE user_name='david' | 0.00 s (Index Scan) | 1.50 s (Full Scan) | 0.00 s |
SUM/AVG(money) WHERE user_name='david' | 2.15 s | 1.68 s | 0.94 s |
user_name<>'david' 改為 總數 - 子表數,從 9 秒降為毫秒級。user_name 在子表中恆定,刪除後 MIN/MAX 從全表掃描變為索引掃描,SUM/AVG 掃描行數減少,性能顯著提升。(user_name, money) 支持 GROUP BY 鬆散索引掃描;子表無需 user_name 索引。user_name 導向對應子表。UNION ALL 子表或應用層合併。money 篩選)需評估是否引入全局索引表或 ES。