INSERT 是 MySQL 最基礎也最關鍵的 DML 操作。從單行插入到百萬級批量導入,從主鍵衝突處理到衍生列計算,本文系統梳理完整語法變體、核心子句行為、性能優化策略與實戰避坑指南,助您寫出**快、正、省**的寫入代碼。
-- 單行
INSERT INTO worker (name, email) VALUES ('tom', 'tom@yahoo.com');
-- 批量多行(單條 SQL,性能提升數量級)
INSERT INTO worker (name, email) VALUES
('tom', 'tom@yahoo.com'),
('paul', 'paul@yahoo.com'),
('john', 'john@yahoo.com');
-- 部分列(其餘列用默認值/自增)
INSERT INTO worker (name) VALUES ('tom'), ('paul');
INSERT INTO worker SET name = 'tom', email = 'tom@yahoo.com';
-- 未指定列自動填充默認值
-- 指定列映射
INSERT INTO tbl1 (col1, col2) SELECT col3, col4 FROM tbl2;
-- 全列匹配(結構必須完全一致)
INSERT INTO tbl1 SELECT col3, col4 FROM tbl2;
限制:SELECT 不能含 ORDER BY;目標表不能出現在 FROM 子句中(避免死循環)。
ON DUPLICATE KEY UPDATE:寫入去重「瑞士軍刀」當插入導致 **主鍵** 或 **唯一索引** 衝突時,轉為 UPDATE 現有行。
-- 基礎:衝突時更新指定列
INSERT INTO t (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=3;
-- 引用待插入值(VALUES() 函數)
INSERT INTO t (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=VALUES(c);
-- 跨列運算:衝突時 c = a + b(待插入值)
INSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
VALUES(col) 只在 ON DUPLICATE KEY UPDATE 中有效,引用**本次插入擬寫入的值**。INSERT ... SELECT ... ON DUPLICATE KEY UPDATE:批量去重同步
INSERT INTO tbl1 (a,b,c)
SELECT col1, col2, col3 FROM tbl2
ON DUPLICATE KEY UPDATE c=VALUES(c);
| 數據類型 | 隱式默認值 |
|---|---|
| 數值型 | 0 |
| 字符串 | '' (空串) |
| 日期時間 | '0000-00-00 00:00:00' (零值) |
建議:啟用 STRICT_TRANS_TABLES 避免靜默寫入異常數據。
INSERT DELAYED:異步寫入(MyISAM/MEMORY/ARCHIVE 專用)
INSERT DELAYED INTO worker (name) VALUES ('tom'), ('paul');
| 特性 | 說明 |
|---|---|
| 立即返回 | 客戶端不等待寫入完成,獲取確認即返回 |
| 隊列合併 | 多客戶端插入合併為塊寫入,減少 I/O |
| 限制 | 不支持 SELECT/ON DUPLICATE KEY UPDATE;LAST_INSERT_ID() 不可用;從庫忽略 |
| 適用引擎 | MyISAM, MEMORY, ARCHIVE(InnoDB 不支持) |
現狀:MySQL 8.0 已移除,建議改用應用層異步隊列(Kafka/RabbitMQ)+ 批量寫入。
LOW_PRIORITY / HIGH_PRIORITY / IGNORE 修飾符LOW_PRIORITY:等待無讀鎖才寫入(MyISAM 表級鎖場景)。HIGH_PRIORITY:覆蓋 LOW_PRIORITY,搶佔寫入。IGNORE:忽略主鍵/唯一鍵衝突、數據截斷等錯誤,轉為警告繼續執行。
-- 應用層組裝:每批 1000~5000 行,單條 SQL 長度 < max_allowed_packet
INSERT INTO t (a,b) VALUES (?,?),(?,?)...; -- PreparedStatement 批量綁定
SET autocommit=0;
-- 批量循環
INSERT ...; -- 累積 1000 行
COMMIT; -- 適度提交,避免超大事務鎖持有過久
SET autocommit=1;
ALTER TABLE t DISABLE KEYS → 導入 → ENABLE KEYS。DROP INDEX / SET foreign_key_checks=0,導入後重建。| 現象 | 原因 | 修正 |
|---|---|---|
| Packet too large | 單條 SQL 超過 max_allowed_packet | 增大參數 / 減小批次行數 |
| Duplicate entry | 主鍵/唯一鍵衝突 | 用 ON DUPLICATE KEY UPDATE 或 IGNORE |
| Auto_increment 值不連續 | 事務回滾/失敗/DELAYED 導致預分配 ID 浪費 | 正常現象,業務勿依賴連續性 |
| INSERT ... SELECT 死鎖 | 目標表與源表加鎖順序不一致 | 拆分為應用層讀寫 / 調整隔離級別 |
| LAST_INSERT_ID() 返回 0 | 使用了 INSERT DELAYED / 多行插入只返回第一行 ID | 改用普通 INSERT / 查詢 information_schema |
DELIMITER //
CREATE PROCEDURE batch_insert_worker(IN p_count INT)
BEGIN
DECLARE v_sql TEXT DEFAULT 'INSERT INTO worker (name,email) VALUES ';
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
WHILE i < p_count DO
SET v_sql = CONCAT(v_sql, '(', QUOTE(CONCAT('user_', i)), ',', QUOTE(CONCAT('user_', i, '@example.com')), '),');
SET i = i + 1;
IF i % 1000 = 0 THEN
SET v_sql = CONCAT(SUBSTRING(v_sql, 1, CHAR_LENGTH(v_sql)-1), ';');
PREPARE stmt FROM v_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
SET v_sql = 'INSERT INTO worker (name,email) VALUES ';
END IF;
END WHILE;
IF i % 1000 != 0 THEN
SET v_sql = CONCAT(SUBSTRING(v_sql, 1, CHAR_LENGTH(v_sql)-1), ';');
PREPARE stmt FROM v_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
END IF;
SET autocommit = 1;
END//
DELIMITER ;
INSERT 看似簡單,實則蘊含「批量vs單行」「同步vs異步」「去重vs報錯」「有序vs無序」多維度權衡。掌握 ON DUPLICATE KEY UPDATE 幂等寫入模式、批量綁定最佳實踐、主鍵設計對寫入性能的決定性影響,配合事務邊界與索引策略的動態調整,才能在高併發寫入洪流中,構建**高吞吐、低延遲、強一致**的數據寫入管道。願本文成為您寫入優化路上的「武林秘籍」。