批量写入怎么提速
写入吞吐低通常不是磁盘慢,而是把本可以合并的动作拆成了很多次:一行一次插入、一行一次提交、一行一次网络往返。做法很直接——把多行合成一条语句,把多条语句合成一次网络往返,把多次提交合成一个事务,把每批的行数控制在合理区间。批量大小不是越大越好:事务太大会延长锁持有时间、放大失败重试的代价。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上,应用侧与驱动侧配合 |
|---|---|
| 语句级合并 | 一条插入语句写多组值,一次解析 |
| 事务级合并 | 按批提交,减少提交开销与刷盘次数 |
| 驱动改写 | 连接参数开启后把多条插入合并发送 |
| 批量大小 | 常见区间为几百到几千行,按单批耗时调整 |
典型使用场景
- 日志与埋点入库:高频小批量写入,需要合并往返
- 数据迁移:从外部文件批量导入,需权衡批大小与事务长度
- 接口批量落库:一次请求带多条记录,用多值插入
- 定时任务写入:按批提交并记录进度,便于中断续跑
- 排查写入瓶颈:区分是网络往返、提交开销还是索引维护导致的慢
合并写入示例
合并写入示例
-- 单语句多行插入:一次解析、一次网络往返
INSERT INTO events (uid, kind, created_at) VALUES
(1, 'click', NOW()),
(1, 'view', NOW()),
(1, 'order', NOW());
-- 大批量按批提交(结构示意)
-- START TRANSACTION;
-- 每 1000 行执行一次插入
-- COMMIT; ← 提交后开始下一批
-- 驱动层合并:开启连接参数后,批处理语句会被合并发送
-- MySQL 连接参数:rewriteBatchedStatements=true
-- PostgreSQL 连接参数:reWriteBatchedInserts=true
-- 导入超大文件时优先用数据库自带的批量导入通道
-- MySQL:LOAD DATA INFILE
-- PostgreSQL:COPY优化点与代价
| 优化点 | 收益 | 代价 |
|---|---|---|
| 语句级合并 | 解析与往返次数下降 | 单条语句过长时解析内存上升 |
| 事务级合并 | 提交与刷盘次数下降 | 事务变长,锁持有时间增加 |
| 驱动改写 | 自动合并批处理语句 | 需确认驱动版本与参数行为 |
| 专用导入通道 | 大文件导入速度最高 | 数据格式要求严格,需预处理 |
| 关闭不必要的索引 | 写入期间维护成本下降 | 导入后需重建索引,期间查询变慢 |
批太大与批太小各有代价:太大时单批失败要整体重做,长事务还会推高复制的延迟;太小时往返开销占比高。用单批耗时作为调整依据,先测后调。
导入期间临时去掉次要索引、导入后重建,常比边写边维护索引快得多。重建索引本身也占资源,安排在业务低峰执行。