Delphi(FireDAC)操作SQLite:提交事务后数据未实时写入如何解决?
看起来你遇到的是SQLite WAL模式下的典型行为——哪怕手动提交了事务,WAL日志里的变更也不会立刻合并到主数据库文件,除非触发了检查点(Checkpoint)。默认情况下,SQLite只会在日志文件达到一定大小(默认1000页,约4MB)或者数据库连接关闭时自动执行检查点,这就是为什么你只有关闭应用才看到数据写入主库的原因。
下面给你几个具体的解决办法,按优先级排序:
1. 手动触发WAL检查点(最直接有效)
每次事务提交后,直接执行SQLite的检查点命令,强制把WAL日志中的变更合并到主数据库文件。修改你的代码,在FDConnection1.Commit之后添加这行:
FDConnection1.ExecSQL('PRAGMA wal_checkpoint(FULL)');
FULL参数会确保所有未写入的日志都被合并,并且等待操作完成后再返回。如果追求更快的速度,也可以用PASSIVE(后台异步执行),但FULL更适合你的批量更新场景,能保证数据立即写入主库。
2. 调整自动检查点阈值
如果不想每次手动执行检查点,可以修改SQLite的自动检查点触发条件。在连接初始化时执行:
FDConnection1.ExecSQL('PRAGMA wal_autocheckpoint=100'); // 设置为100页,约400KB时触发检查点
这个值可以根据你的批量更新大小调整:数值越小,检查点触发越频繁,主库更新越及时,但可能会带来轻微的性能开销。
3. 优化FireDAC连接参数
检查你的FDConnection配置,确保以下几点:
- 确认已开启WAL模式:连接参数里添加
PRAGMA journal_mode=WAL(你提到已经设置,但可以再确认下) - 调整同步模式:WAL模式下默认
synchronous=NORMAL,如果需要更强的持久化保障,可以设置为FULL:
注意这个参数会增加IO开销,但能确保日志写入磁盘后才返回Commit结果。FDConnection1.Params.Add('PRAGMA synchronous=FULL');
4. 排除FireDAC缓存干扰
虽然你用了手动事务,但也要确认FireDAC的UpdateOptions.AutoCommit是否为False(手动管理事务时必须设为False);另外检查FetchOptions.Mode是否为fmAll,避免查询缓存导致你看不到最新数据。
最后,结合你的代码,修改后的事务部分大概是这样:
try Query.Connection := FDConnection1; FDConnection1.Open; FDConnection1.StartTransaction; Query.SQL.Text := 'select 1 from t_Manufacturers where m_Name = ' + QuotedStr(ManString); Query.Open; if Query.RecordCount = 0 then begin { 未找到,执行插入 } Query.SQL.Text := 'insert into t_Manufacturers (m_Name, m_ManUID) values (:Name, null)'; Query.ParamByName('Name').AsString := ManString; Query.ExecSQL; { 保存m_ManUID用于日志记录 } Query.SQL.Text := 'select m_ManUID from t_Manufacturers where m_Name = ' + QuotedStr(ManString); Query.Open; end; Result := Query.FieldByName('m_ManUID').AsInteger; FDConnection1.Commit; // 手动触发检查点,强制写入主库 FDConnection1.ExecSQL('PRAGMA wal_checkpoint(FULL)'); except on E : EDatabaseError do begin MessageDlg('Database error adding manufacturer: ' + E.Message, mtError, [mbOk], 0); FDConnection1.Rollback; end; end;
这样每次批量更新提交后,都会立刻把WAL日志合并到主数据库文件,不用等到应用关闭了。
内容的提问来源于stack exchange,提问作者Steve_B

