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

thk*_*ala 26 sql sqlite performance insert

我在我的一个项目中使用SQLite3,我需要确保插入到表中的行对于它们的一些列的组合是唯一的.在大多数情况下,插入的行在这方面会有所不同,但如果匹配,新行必须更新/替换现有行.

显而易见的解决方案是使用复合主键,并使用conflict子句来处理冲突.在此之前:

CREATE TABLE Event (Id INTEGER, Fld0 TEXT, Fld1 INTEGER, Fld2 TEXT, Fld3 TEXT, Fld4 TEXT, Fld5 TEXT, Fld6 TEXT);
Run Code Online (Sandbox Code Playgroud)

成了这个:

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);
Run Code Online (Sandbox Code Playgroud)

这确实强制执行我需要它的唯一性约束.不幸的是,这种变化也会导致性能损失超出我的预期.我使用sqlite3命令行实用程序进行了一些测试,以确保我的其余代码中没有错误.测试涉及在单个事务中或在每个1,000行的100个事务中输入100,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     |
Run Code Online (Sandbox Code Playgroud)

我的应用程序目前执行最多1,000行的交易,我对性能下降15倍感到惊讶.我预计吞吐量下降最多3倍,CPU使用率也会上升,如100k交易案例所示.我想维护主键约束所涉及的索引需要大量的同步数据库操作,因此在这种情况下我的硬盘成为瓶颈.

使用WAL模式确实会产生一些影响 - 性能提升约15%.不幸的是,这本身还不够.PRAGMA synchronous = NORMAL似乎没有任何影响.

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

每行中的文本字段具有平均约250个字节的可变长度.查询性能无关紧要,但插入性能非常重要.我的应用程序代码在C中,并且(应该是)可移植到至少Linux和Windows.

有没有办法在不增加事务大小的情况下提高插入性能?SQLite中的一些设置(除了永久强制DB进入异步操作之外的任何东西,或者是在我的应用程序代码中以编程方式)?例如,有没有办法确保行唯一性而不使用索引?

BOUNTY:

通过使用我自己的答案中描述的散列/索引方法,我设法将性能降低到一定程度,以至于我的应用程序可能接受它.但是,似乎随着表中行数的增加,索引的存在使得插入越来越慢.

我感兴趣的是任何技术或微调设置都会提高这个特定用例的性能,只要它不涉及破解SQLite3代码或导致项目变得无法维护.

Gio*_*rgi 15

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

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

如果您尝试这些,请发布您的测试结果.我相信每个人都会感兴趣.


Mik*_*ll' 8

ON CONFLICT REPLACE子句将使SQLite删除现有行,然后插入新行.这意味着SQLite可能会花费一些时间

  • 删除现有行
  • 更新索引
  • 插入新行
  • 更新索引

这是我对它的看法,基于SQLite文档和阅读其他数据库管理系统.我没看过源代码.

SQLite有两种表达唯一性约束的方法:PRIMARY KEYUNIQUE.但是,它们都创建了一个索引.

现在真的很重要的东西...

你做了测试真是太好了.大多数开发人员不这样做.但我认为你的测试结果非常误导.

在您的情况下,将行插入到没有主键的表中的速度并不重要.没有主键的表不满足您对数据完整性的基本要求.这意味着您不能依靠您的数据库来为您提供正确的答案.

如果它没有给出正确的答案,我可以真正,非常快.

要获得有意义的时间插入没有键的表,您需要

  • 插入新数据之前运行代码以确保不违反未声明的主键约束,并确保使用正确的值(而不是插入)更新现有行,或者
  • 插入到该表运行代码以清除(Fld0,Fld2,Fld3)上的重复项,并协调冲突

当然,这些过程所需的时间也必须考虑在内.

FWIW,我通过在1000个语句的事务中将100K SQL插入语句运行到您的模式中进行了测试,并且只花了30秒.1000个插入语句的单个事务,似乎是您在生产中所期望的,花费了149毫秒.

也许你可以通过插入一个无键的临时表来加快速度,然后从中更新键控表.