小编典典

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

sql

我在一个项目中使用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使用率也会增加,如10万笔交易中所示。我猜想维护主键约束所涉及的索引需要大量的同步数据库操作,因此在这种情况下使我的硬盘成为瓶颈。

使用WAL模式确实会产生一些效果-
性能提高约15%。不幸的是,仅凭这一点还不够。PRAGMA synchronous = NORMAL似乎没有任何作用。

可能 可以通过增加事务大小来恢复某些性能,但是由于内存使用量增加以及对响应性和可靠性的担忧,我宁愿不这样做。

每行中的文本字段的平均可变长度约为250个字节。查询性能没有太大关系,但是插入性能非常重要。我的应用程序代码使用C语言,并且(至少可以移植到)Linux和Windows中。

有没有办法在不增加事务大小的情况下提高插入性能?是SQLite中的某些设置(除了永久性地迫使数据库进行异步操作外,什么都可以)还是在我的应用程序代码中以编程方式进行设置?例如,有没有一种方法可以在不使用索引的情况下确保行的唯一性?

赏金:

通过使用我自己的答案中描述的哈希/索引方法,我设法在某种程度上将性能下降降低到了我的应用程序可以接受的程度。但是,随着表中行数的增加,索引的出现似乎使插入变得越来越慢。

我对在此特定用例中可以提高性能的任何技术或微调设置感兴趣,只要它不涉及破解SQLite3代码或以其他方式导致项目无法维护即可。


阅读 331

收藏
2021-04-15

共1个答案

小编典典

我已经使用sqlite在运行时插入数百万行,这就是我用来提高性能的方法:

  • 使用尽可能少的交易。
  • 使用参数化的命令来插入数据(准备命令一次,只需在循环中更改参数值)
  • PRAGMA同步设置为 OFF(不确定如何与WAL配合使用)
  • 增加数据库的页面大小。
  • 增加缓存大小。 这是一个重要的设置,因为它将导致sqlite将数据实际写入磁盘的次数减少,并且将在内存中运行更多的操作,从而使整个过程更快。
  • 如果您需要索引,请通过运行必要的sqlite命令在插入行后将其添加。在这种情况下,您需要像现在一样确保自己的唯一性。

如果尝试这些,请发布测试结果。我相信这对每个人都会很有趣。

2021-04-15