UPDATE 是最常用也最危險的 DML 操作。本文系統梳理 UPDATE 基礎語法、百萬級數據更新效能陷阱、實測對比(UPDATE vs INSERT 新表)、鎖與索引影響分析,給出生產環境大批量更新的最佳實踐方案。
UPDATE table_name SET col1 = val1, col2 = val2, ... WHERE condition;
WHERE 將更新全表(極度危險)。UPDATE a JOIN b ON ... SET a.col = b.col WHERE ...SET col = NULL 合法,需欄位允許 NULL。tmp_open_ip:200 萬行,需更新 address。ip:44 萬行,s/e/a (起/止/地址)。UPDATE tmp_open_ip u INNER JOIN ip ON u.ip BETWEEN ip.s AND ip.e SET u.address = ip.a;
結果: 執行 5000+ 秒未完成,僅更新 11,373 行。原因: 對每行用戶日志掃描 IP 表範圍,觸發大量範圍掃描與行鎖,且無法利用索引高效連接。
CREATE TABLE tmp_open_address LIKE tmp_open_ip; -- 無索引更快 INSERT INTO tmp_open_address (email, address, ip) SELECT l.email, ip.a, l.ip FROM tmp_open_log l INNER JOIN ip ON l.ip BETWEEN ip.s AND ip.e;
結果: 16.9 秒完成 254 萬行寫入。優勢: 純順序寫入、無鎖升級、無回表、可並行、可分批提交。
CREATE TABLE new_table LIKE old_table; INSERT INTO new_table SELECT ..., updated_col FROM old_table JOIN ...; RENAME TABLE old_table TO old_table_bak, new_table TO old_table;
pt-online-schema-change 實現真正在線無鎖遷移。WHERE id BETWEEN ... AND ...,配合 LIMIT 避免長事務。WHERE pk IN (SELECT pk FROM ...) 而非範圍掃描。SET unique_checks=0; SET foreign_key_checks=0;(謹慎,確保數據完整性)。innodb_buffer_pool_size、innodb_log_file_size、innodb_flush_log_at_trx_commit=2(臨時犧牲持久性換效能)。-- 低效 UPDATE a SET a.col = (SELECT b.col FROM b WHERE b.id = a.id); -- 高效 UPDATE a JOIN b ON a.id = b.id SET a.col = b.col;
UPDATE 加排他鎖(X 鎖),持有至事務結束。REPEATABLE READ(預設)+ 範圍條件 → Next-Key Lock,易死鎖。SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED(需 binlog_format=ROW),減少間隙鎖。大批量更新首選「建新表 INSERT 替換」,順序寫入、無鎖、可驗證、可回滾。必須原地 UPDATE 時:分批、走索引、避開索引欄位、調整隔離級別與 InnoDB 參數。實戰中請務必先在測試環境壓測驗證,制定回滾預案,並在低峰期執行。