【发布时间】:2024-01-22 10:39:02
【问题描述】:
我在我的一个项目中使用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 代码或以其他方式导致项目变得不可维护。
【问题讨论】:
标签: sql performance sqlite insert