我正在使用 SQLite3在我的一个项目中,我需要确保插入到表中的行相对于它们的某些列的组合是唯一的。在大多数情况下,插入的行在这方面会有所不同,但在匹配的情况下,新行必须更新/替换现有行。
显而易见的解决方案是使用复合主键,并使用冲突子句来处理冲突。因此:
CREATE TABLE Event (Id INTEGER, Fld0 TEXT, Fld1 INTEGER, Fld2 TEXT, Fld3 TEXT, Fld4 TEXT, Fld5 TEXT, Fld6 TEXT);
变成了这个:
CREATE TABLE Event (Id INTEGER, Fld0 TEXT, Fld1 INTEGER, Fld2 TEXT, Fld3 TEXT, Fld4 TEXT, Fld5 TEXT, Fld6 TEXT, PRIMARY KEY (Fld0, Fld2, Fld3) ON CONFLICT REPLACE);
这确实按照我的需要强制实现了唯一性约束。不幸的是,此更改还会导致性能损失,这超出了我的预期。我做了
使用 sqlite3
命令行实用程序进行了一些测试,以确保我的其余代码没有错误。测试涉及输入 100,000 行,或者在单个
事务或 100 个事务,每个事务 1,000 行。我得到了以下结果:
| 1 * 100,000 | 10 * 10,000 | 100 * 1,000 |
|---------------|---------------|---------------|
| Time | CPU | Time | CPU | Time | CPU |
| (sec) | (%) | (sec) | (%) | (sec) | (%) |
--------------------------------|-------|-------|-------|-------|-------|-------|
No primary key | 2.33 | 80 | 3.73 | 50 | 15.1 | 15 |
--------------------------------|-------|-------|-------|-------|-------|-------|
Primary key: Fld3 | 5.19 | 84 | 23.6 | 21 | 226.2 | 3 |
--------------------------------|-------|-------|-------|-------|-------|-------|
Primary key: Fld2, Fld3 | 5.11 | 88 | 24.6 | 22 | 258.8 | 3 |
--------------------------------|-------|-------|-------|-------|-------|-------|
Primary key: Fld0, Fld2, Fld3 | 5.38 | 87 | 23.8 | 23 | 232.3 | 3 |
我的应用程序目前执行最多 1,000 行的事务,我对性能下降 15 倍感到惊讶。我预计吞吐量最多会下降 3 倍,而 CPU 使用率会上升,如 100k 事务案例所示。我想维护主键约束所涉及的索引需要大量同步数据库操作,因此在这种情况下我的硬盘驱动器成为瓶颈。
使用 WAL mode确实有一些效果——性能提高了大约 15%。不幸的是,这本身是不够的。 PRAGMA synchronous = NORMAL
似乎没有任何效果。
我可能可以通过增加事务大小来恢复一些性能,但我宁愿不这样做,因为内存使用量增加以及对响应性和响应性的担忧 可靠性。
每行中的文本字段的长度可变,平均约为 250 字节。查询性能无关紧要,但插入性能非常重要。我的应用程序代码是用 C 编写的,并且(应该)至少可以移植到 Linux 和 Windows。
有没有办法在不增加事务大小的情况下提高插入性能? SQLite 中的某些设置(除了永久强制数据库进入异步操作之外的任何设置)或以编程方式在我的应用程序代码中设置?例如,有没有一种方法可以在不使用索引的情况下确保行的唯一性?
赏金:
通过使用我自己的答案中描述的散列/索引方法,我设法将性能下降的程度降低到我的应用程序可能可以接受的程度。 然而,似乎随着表中行数的增加,索引的存在使得插入越来越慢。
我对任何可以提高此特定用例性能的技术或微调设置感兴趣,只要它不涉及破解 SQLite3 代码或以其他方式导致项目变得无法维护。
最佳答案
我已经使用 sqlite 在运行时插入了数百万行,这就是我用来提高性能的方法:
- 使用尽可能少的事务。
- 使用参数化命令 插入数据(准备 命令一次,只需改变 循环中的参数值)
- 设置 PRAGMA synchronous关闭(不确定 它如何与 WAL 一起工作)
- 增加数据库的页面大小。
- 增加缓存大小。这是一个重要的设置,因为它会导致 sqlite 实际将数据写入磁盘的次数减少,并会在内存中运行更多操作,从而使整个过程更快。
- 如果您需要索引,请在插入行后通过运行必要的 sqlite 命令添加它。在这种情况下,您需要自己确保唯一性,就像您目前正在做的那样。
如果您尝试这些,请发布您的测试结果。相信大家会很感兴趣。
关于sql - 确保 SQLite3 中唯一行的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5181927/