C#使用SQLite事务批量插入数据时报database is locked错误如何解决
错误说明
你遇到的是SQLite常见的写锁冲突错误:
Unhandled exception. code = Busy (5), message = System.Data.SQLite.SQLiteException (0x800007AF): database is locked
database is locked
核心原因
- 参数重复添加导致执行异常:你在
foreach循环中反复调用AddWithValue给同一个命令对象追加参数,而非覆盖原有参数值,导致命令逻辑异常,事务持有写锁的时间被大幅拉长,甚至出现执行异常导致锁未正常释放。 - 默认锁等待时间过短:SQLite默认的锁超时时间很短,只要短时间内获取不到写锁就会直接抛出Busy错误。
- 大事务持有锁时间过长:如果
records数据量很大,单事务执行时间过长,会导致其他请求等待超时触发锁错误。
修复方案
- 修正参数添加逻辑,避免循环内重复追加参数
提前在循环外定义好命令参数,循环内仅修改参数值即可,优化后的代码如下:
public void InsertRecords(List<string> records, Device device) { using (var conn = new SQLiteConnection(connStr)) { conn.Open(); // 设置30秒锁超时,大幅降低锁冲突报错概率 conn.BusyTimeout = 30000; using (var transaction = conn.BeginTransaction()) { // 初始化插入records的命令,提前绑定参数 var insRecStmt = new SQLiteCommand( "INSERT INTO records(content) VALUES(@content);", conn, transaction ); insRecStmt.Parameters.Add("@content", System.Data.DbType.String); // 初始化插入foo的命令,提前绑定参数 var fooStmt = new SQLiteCommand( "INSERT INTO foo(rec_id, bar) VALUES (@rec_id, @bar);", conn, transaction ); fooStmt.Parameters.Add("@bar", System.Data.DbType.Int32).Value = device.Id; fooStmt.Parameters.Add("@rec_id", System.Data.DbType.Int64); foreach (var record in records) { // 仅修改参数值,不要重复Add insRecStmt.Parameters["@content"].Value = record; insRecStmt.ExecuteNonQuery(); var rowID = conn.LastInsertRowId; fooStmt.Parameters["@rec_id"].Value = rowID; fooStmt.ExecuteNonQuery(); } transaction.Commit(); } } }
- 调整连接字符串配置
可以在连接字符串中开启共享缓存,降低同进程多连接的锁冲突概率,参考配置:Data Source=你的数据库文件路径;Version=3;Cache=Shared; - 大数量场景拆分事务
如果单次插入的records数量超过1万条,建议拆分事务,每1000~5000条提交一次,避免单事务持有锁时间过长。
内容的提问来源于stack exchange,提问作者N N
相关产品推荐
相关产品推荐

