sql - 确保 SQLite3 中唯一行的有效方法

标签 sql performance sqlite insert

我正在使用 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/

相关文章:

sql - 为什么当我使用临时表时,oracle 中的查询需要更长的时间?

php - SQL 更新查询无法正确处理 php 数据

mysql - 如何从sqlite +phonegap中的表中获取第一列名称

sqlite - 如何在sqlite 2数据库表中添加列

ios - [context assignObject : toPersistentStore ] do? 是什么

javascript - 我如何使 PHP/HTML 图像在单击时显示原始大小?

java - 将一年中的某一天转换为 Java 中的日期

c# - 衡量代码执行时间的最佳方法是什么?

python - 添加到 Pandas 的窗口

sql - 如何在重负载下强制停止长 postgres 查询?