SQLDelight批量插入1万行数据耗时久或死锁如何优化?
问题诱因
- 核心不是死锁,是单条循环插入的性能开销过大:你当前的写法即使包裹在事务中,每次循环调用单条
save语句,都会重复生成PreparedStatement、绑定参数的开销,1万次累加后耗时过长,很容易触发超时、ANR类异常。 - 如果两个表的插入逻辑拆分在不同事务、且不同线程并发执行时,才可能出现事务锁竞争导致的死锁,你当前贴出的单表事务代码不存在该风险。
- 若表上存在多个索引,每次执行
INSERT OR REPLACE都需要同步更新所有索引,1万次操作的索引更新开销会被进一步放大。
优化方案
1. 改用SQLDelight批量插入语法
重写.sq文件中的插入语句,使用SQLDelight内置的<values>标签支持批量参数:
saveBatch: INSERT OR REPLACE INTO TableA VALUES ? <values> ,? </values>;
Kotlin调用时直接传入整个数据集,无需循环执行单条插入:
val tableAQueries = database.tableAQueries withContext(Dispatchers.IO) { tableAQueries.transaction { // 直接传入整个列表,仅执行一次SQL tableAQueries.saveBatch(rows) } }
该优化可以把一万次SQL执行降为1次,性能提升至少10倍以上。
2. 超大批次拆分
如果单批次数据量超过5000行,建议拆分多批执行,比如每2000行为一批插入,避免单条SQL语句过长导致的数据库解析开销。
3. 事务与数据库配置优化
- 把两个表的插入逻辑合并到同一个事务中,避免多次开启、提交事务的开销,同时彻底避免跨事务的锁竞争死锁风险。
- 若数据为可复现的缓存类数据,批量插入前临时执行
PRAGMA synchronous = OFF关闭磁盘同步校验,插入完成后恢复原有配置,可大幅降低IO耗时。 - 调用事务时添加
exclusive = true参数,开启排他事务,避免其他读操作抢占锁资源:tableAQueries.transaction(exclusive = true) { // 插入逻辑 }
4. 索引优化
插入前临时删除表上非必要的索引,插入完成后再重建索引,比每次插入同步更新索引的效率高30%以上。
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

