萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 分表(Sharding)優化實戰:存儲過程自動化、性能對比、索引重構

分表動機

單表 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 s0.00 s∞ (索引覆蓋/全表掃描→單表掃描)
COUNT(*) WHERE user!='david'9.26 s0.00 s (差集查詢)∞
MIN/MAX(money) WHERE user='david'0.00 s0.00 s (Drop user_name 後)持平
SUM/AVG(money) WHERE user='david'2.15 s0.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 > ?";

跨分表查詢方案

遷移上線清單

  1. 雙寫期:寫入總表 + 分表,讀總表
  2. 驗證期:讀分表,對比總表數據一致性
  3. 切換期:流量切讀分表,停寫總表
  4. 清理期:歸檔/刪除總表數據

擴展:按時間分表(日志/流水)

-- 月度分表
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
方案實現優點缺點
應用層合並並行查各分表,內存合並排序無中間件、靈活跨分頁/排序複雜、內存壓力
中間件合並ProxySQL/ShardingSphere 自動改寫透明化、支持分頁排序引入組件、調試難
匯總表定時任務彙總到汇總表查詢極快數據有延遲、維護成本
全局二級索引表冗余 user_name+id 到單獨表定位分表快寫入雙寫、一致性風險