批量插入比单条 INSERT
快得多,但要注意事务边界
单条
INSERT INTO t VALUES (1),(2)和 1000 条
INSERT语句在高并发下性能差距可达 10 倍以上。关键不是“能不能用批量”,而是“在哪切分事务”。
MySQL 的
autocommit=1默认开启,每条
INSERT都触发一次刷盘(
innodb_flush_log_at_trx_commit=1时),这是最大瓶颈。 用
INSERT INTO t VALUES (1),(2),(3),...,(1000)替代循环执行 1000 次单值
INSERT显式开启事务:
BEGIN; INSERT ... ; INSERT ... ; COMMIT;,把 100–1000 行包在一个事务里(太大易锁表、回滚开销高) 临时调大
innodb_log_file_size和
innodb_buffer_pool_size,避免频繁 checkpoint 写入前禁用唯一索引和外键检查(仅限导入场景):
SET unique_checks=0; SET foreign_key_checks=0;,完成后恢复
LOAD DATA INFILE
是最快写入方式,但权限和路径限制多
比等量
INSERT快 5–20 倍,本质是绕过 SQL 解析层,直接走存储引擎接口。但它要求数据文件在 MySQL 服务端本地(或启用了
local_infile=ON并配合客户端
--local-infile),且用户需有
FILE权限。 格式必须严格:字段分隔符(
FIELDS TERMINATED BY ',')、行结束符(
LINES TERMINATED BY '\n')、空值表示(
NULL或
\N)都要匹配 避免使用
REPLACE或
IGNORE,它们会触发行锁或唯一键扫描;如需去重,先写临时表再
INSERT ... SELECT ... ON DUPLICATE KEY UPDATE大文件分片导入更稳:
split -l 100000 data.csv chunk_,再逐个
LOAD DATA INFILE '/path/chunk_aa'导入后立刻执行
ANALYZE TABLE t,更新统计信息,避免后续查询执行计划劣化
高并发写入时,自增主键冲突和热点页争用要主动规避
InnoDB 的聚簇索引让主键成为写入热点。若所有连接都往同一数据页末尾插入(比如
AUTO_INCREMENT主键),会产生
LOCK_MODE_X行锁竞争,甚至
adaptive hash index锁等待。 用
innodb_autoinc_lock_mode=2(交错模式),允许并发获取自增值(需 binlog_format=ROW) 避免
REPLACE INTO或
INSERT ... ON DUPLICATE KEY UPDATE在高频写入场景滥用——它们先查后写,容易锁住唯一索引项 考虑用
UUID_SHORT()或雪花 ID(应用层生成)替代
AUTO_INCREMENT,但注意 UUID 无序性会导致二级索引碎片,需定期
OPTIMIZE TABLE写多读少场景可加
innodb_change_buffering=inserts(默认已开),延迟合并非唯一二级索引变更,降低随机 IO
连接池配置不当会让优化全失效
哪怕 SQL 和参数全调优了,如果应用层用短连接(每次请求新建/销毁连接),或连接池最大数设成 1000,照样卡在
Creating sort index或
Waiting for table metadata lock。 连接池
maxActive/
maxPoolSize不应盲目设高;建议按实际并发请求数 × 1.5 设(如 QPS 200,平均响应 100ms,则活跃连接约 20) 务必启用
wait_timeout和
interactive_timeout(建议 300 秒),防止空闲连接长期占着不放 用
SHOW PROCESSLIST查看是否有大量
Sleep状态连接;用
performance_schema.events_statements_summary_by_digest找出慢写入的 SQL 模板 写入类请求尽量走专用连接池(和读库分离),避免慢写拖垮读响应
真正卡住高并发写的,往往不是磁盘或 CPU,而是锁等待、日志刷盘节奏、或者连接没被复用。调参前先看
SHOW ENGINE INNODB STATUS\G里的
SEMAPHORES和
TRANSACTIONS部分,比盲目改
innodb_buffer_pool_size有用得多。
