200 萬行用戶日誌表 tmp_open_ip 需根據 44 萬行 IP 庫表 ip 更新地址欄位。直接 UPDATE ... JOIN 執行 1.5 小時未完成,預估需數天。
UPDATE tmp_open_ip AS u
INNER JOIN ip ON u.ip BETWEEN ip.s AND ip.e
SET u.address = ip.a;
瓶頸:
BETWEEN 無法有效用索引)CREATE TABLE tmp_open_address (
email VARCHAR(60) NOT NULL DEFAULT '',
address VARCHAR(50) NOT NULL DEFAULT '',
ip INT(10) UNSIGNED NOT NULL DEFAULT 0
) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- MyISAM 批次寫入更快,或 InnoDB 關閉雙重寫
INSERT INTO tmp_open_address (email, address, ip)
SELECT l.email, ip.a, l.ip
FROM tmp_open_log AS l
INNER JOIN ip ON l.ip BETWEEN ip.s AND ip.e;
結果: 254 萬行資料,**16.9 秒** 完成!較 UPDATE 提速 300 倍。
RENAME TABLE tmp_open_ip TO tmp_open_ip_old, tmp_open_address TO tmp_open_ip;
-- 或建立索引後再切換
ALTER TABLE tmp_open_address ADD KEY idx_email (email), ADD KEY idx_ip (ip);
RENAME TABLE ...
| 維度 | UPDATE | INSERT SELECT |
|---|---|---|
| 寫入模式 | 隨機更新現有頁面 | 順序追加新頁面 |
| 索引維護 | 即時維護所有二級索引 | 批次建立/延遲維護 |
| 鎖粒度 | 行鎖 + 間隙鎖 | 表級鎖(MyISAM)或意向鎖 |
| 日誌量 | Undo + Redo + Binlog(舊值+新值) | Redo + Binlog(僅新值) |
| 碎片產生 | 嚴重 | 無 |
WHERE id BETWEEN ? AND ? 循環 INSERT,避免長事務ALTER TABLE ... DISABLE KEYS(MyISAM)或 SET FOREIGN_KEY_CHECKS=0SET SESSION bulk_insert_buffer_size = 256M;大表大批次更新,**改寫為「建新表 INSERT SELECT → 切換表名」**,是關係型資料庫通用的高效能模式。配合分批、禁用索引、調整參數,可將小時級操作壓縮至秒級。