提高SQLite每秒插入性能的方法

提高SQLite每秒插入性能的方法

技术背景

在使用SQLite数据库时,插入操作的性能是一个关键问题,特别是在处理大量数据时。提高每秒插入性能可以显著提升应用程序的响应速度和效率。

实现步骤

1. 将插入/更新操作放在事务中

将多个插入或更新操作放在一个事务中可以减少数据库的提交次数,从而提高性能。

2. 选择合适的日志模式

对于较旧版本的SQLite,可以考虑使用不那么严格的日志模式(pragma journal_mode),如NORMALOFF。但在新版本中,OFF/MEMORY设置在应用程序崩溃时可能不安全。

3. 调整页面大小

使用PRAGMA page_size调整页面大小,较大的页面大小可以提高读写速度,但会占用更多内存。

4. 先插入数据再创建索引

如果有索引,先完成所有插入操作,再创建索引,这样比先创建索引再插入数据要快得多。

5. 处理并发访问

SQLite在写入时会锁定整个数据库,虽然可以有多个读取器,但写入会被锁定。较新版本的SQLite通过添加WAL(Write-Ahead Logging)有所改进。

6. 节省空间

较小的数据库运行速度更快,例如,对于键值对,尽量将键设置为INTEGER PRIMARY KEY

7. 使用共享页面缓存

如果使用多线程,可以尝试使用共享页面缓存,允许线程之间共享加载的页面,避免昂贵的I/O调用。

8. 批量插入

INSERT/UPDATE语句分块执行,例如每10,000条记录一个块。

9. 多线程读取

如果只关心读取性能,可以从多个线程的多个连接中读取数据,但可能会读取到陈旧的数据。

10. 调整缓存大小

提高缓存大小(PRAGMA cache_size)可以提高事务的性能。

11. 使用SQLITE_STATIC

对于插入操作,使用SQLITE_STATIC代替SQLITE_TRANSIENT,可以节省内存分配、复制和释放操作。

12. 避免sqlite3_clear_bindings

在测试代码中,每次设置绑定就足够了,无需额外调用sqlite3_clear_bindings

13. 使用多个事务

当处理包含坏记录或重复记录的文件时,可以使用多个事务,每10,000条记录一个事务,遇到错误时进行回滚。

14. 调整硬盘设置

禁用硬盘的压缩和索引选项可以提高I/O速率,从而提高插入性能。

15. 使用PRAGMA journal_mode = WAL

在某些情况下,使用PRAGMA journal_mode = WAL可以将插入速度提高一倍。

16. 使用WITHOUT ROWID

对于需要将数据导入索引的情况,使用WITHOUT ROWID可以将表和索引合并为一个B-Tree。

17. 使用ContentProvider插入批量数据

在Android开发中,可以使用ContentProvider插入批量数据,提高插入性能。

核心代码

使用事务插入数据

1
2
3
4
5
BEGIN TRANSACTION;
INSERT INTO table_name (column1, column2) VALUES (value1, value2);
INSERT INTO table_name (column1, column2) VALUES (value3, value4);
-- 更多插入语句
COMMIT;

设置日志模式

1
PRAGMA journal_mode = NORMAL;

调整页面大小

1
PRAGMA page_size = 4096;

Android中使用ContentProvider插入批量数据

1
2
3
4
5
6
7
8
9
10
11
12
private SQLiteDatabase database;
database = dbHelper.getWritableDatabase();

public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
database.beginTransaction();
for (ContentValues value : values) {
db.insert("TABLE_NAME", null, value);
}
database.setTransactionSuccessful();
database.endTransaction();
return values.length;
}

最佳实践

  • 在处理大量数据插入时,优先使用事务。
  • 根据数据库的使用场景和硬件环境,选择合适的日志模式、页面大小和缓存大小。
  • 对于多线程应用,合理使用共享页面缓存和多线程读取。
  • 在插入数据前,先分析数据,处理可能出现的错误记录,避免回滚。

常见问题

1. 事务中出现错误怎么办?

可以使用多个事务,每一定数量的记录一个事务,遇到错误时进行回滚,然后从成功的记录继续插入。

2. 硬盘设置对插入性能有影响吗?

是的,硬盘的压缩和索引选项会影响I/O速率,禁用这些选项可以提高插入性能。

3. 使用WITHOUT ROWID有什么限制?

WITHOUT ROWID表不能有AUTOINCREMENT列,并且在某些情况下可能会影响查询性能。


提高SQLite每秒插入性能的方法
https://119291.xyz/posts/2025-05-09.improve-insert-per-second-performance-of-sqlite/
作者
ww
发布于
2025年5月9日
许可协议