提高 SQLite 的每秒更新性能?
Improve UPDATE-per-second performance of SQLite?
我的问题直接来自 this 一个问题,尽管我只对 UPDATE 感兴趣,而且仅此而已。
我有一个用 C/C++
编写的应用程序,它大量使用 SQLite
,主要是 SELECT/UPDATE
,间隔非常频繁 (每 0.5 大约有 20 个查询到 1 秒)
我的数据库不大,暂时有2500条条记录,下面是table结构:
CREATE TABLE player (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(64) UNIQUE,
stats VARBINARY,
rules VARBINARY
);
到目前为止我没有使用 transactions
因为我正在改进代码并且想要稳定性而不是性能。
然后我通过仅执行 10 update
查询来测量我的数据库性能,以下(在不同值的循环中):
// 10 times execution of this
UPDATE player SET stats = ? WHERE (name = ?)
其中 stats
是恰好 150 个字符的 JSON,name
是 5-10 个字符。
没有事务,结果是 unacceptable:- 大约 1 整秒(每个 0.096)
使用事务,时间减少了 x7.5 倍:- 大约 0.11 - 0.16 秒(每次 0.013)
我尝试删除大部分数据库 and/or 重新排序/删除列以查看是否有任何改变,但没有。即使数据库仅包含 100 条记录(已测试),我也会得到上述数字。
然后我尝试使用 PRAGMA
选项:
PRAGMA synchronous = NORMAL
PRAGMA journal_mode = MEMORY
给了我更短的时间,但并非总是如此,更像是 大约 0.08 - 0.14 秒
PRAGMA synchronous = OFF
PRAGMA journal_mode = MEMORY
最后给了我非常短的时间 大约 0.002 - 0.003 秒 但我不想使用它,因为我的应用程序每秒都在保存数据库并且很可能损坏数据库 OS / 电源故障。
我的C SQLite
查询代码是:(comments/errorhandling/unrelated部分省略)
// start transaction
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
// query
sqlite3_stmt *statement = NULL;
int out = sqlite3_prepare_v2(query.c_str(), -1, &statement, NULL);
// bindings
for(size_t x = 0, sz = bindings.size(); x < sz; x++) {
out = sqlite3_bind_text(statement, x+1, bindings[x].text_value.c_str(), bindings[x].text_value.size(), SQLITE_TRANSIENT);
...
}
// execute
out = sqlite3_step(statement);
if (out != SQLITE_OK) {
// should finalize the query no mind the error
if (statement != NULL) {
sqlite3_finalize(statement);
}
}
// end the transaction
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
如您所见,这是一个非常典型的 TABLE
,记录数量很少,我只做了 10 次简单的 UPDATE
。我还能做些什么来减少我的 UPDATE
次吗?我正在使用最新的 SQLite 3.16.2
.
NOTE: The timings above are coming directly from a single END TRANSACTION
query. Queries are done into a simple transaction and i'm
using a prepared statement.
更新:
我执行了一些启用和禁用事务以及各种更新计数的测试。我使用以下设置执行了测试:
VACUUM;
PRAGMA synchronous = NORMAL; -- def: FULL
PRAGMA journal_mode = WAL; -- def: DELETE
PRAGMA page_size = 4096; -- def: 1024
结果如下:
无交易(10 次更新)
- 0.30800 秒(每次更新 0.0308)
- 0.30200 秒
- 0.36200 秒
- 0.28600 秒
无交易(100 次更新)
- 2.64400 秒(每次更新 0.02644)
- 2.61200 秒
- 2.76400 秒
- 2.68700 秒
无交易(1000 次更新)
- 28.02800 秒(每次更新 0.028)
- 27.73700 秒
- ..
有交易(10 次更新)
- 0.12800 秒(每次更新 0.0128)
- 0.08100 秒
- 0.16400 秒
- 0.10400 秒
有交易(100 次更新)
- 0.088 秒(每次更新 0.00088)
- 0.091 秒
- 0.052 秒
- 0.101 秒
有交易(1000 次更新)
- 0.08900 秒(每次更新 0.000089)
- 0.15000 秒
- 0.11000 秒
- 0.09100 秒
我的结论是 transactions
time cost per query
毫无意义。也许时代会随着大量更新而变得更大,但我对这些数字不感兴趣。 10 次和 1000 次更新在单个事务中 之间几乎没有时间成本差异。但是我想知道这是否是我机器上的硬件限制并且不能做太多事情。看来我不能低于 ~100
毫秒使用单个事务和范围 10-1000 更新,即使使用 WAL。
如果没有事务,固定时间成本约为 0.025
秒。
您可能仍然受到提交交易所需时间的限制。在您的第一个示例中,每个事务大约需要 0.10 才能完成,这非常接近插入 10 条记录的事务时间。如果在单个事务中批处理 100 或 1000 个更新,您会得到什么样的结果?
此外,SQLite 预计平均硬盘驱动器每秒处理大约 60 个事务,而您只能得到大约 10 个。您的磁盘性能可能是这里的问题吗?
对于如此少量的数据,数据库操作本身的时间是微不足道的;您要测量的是事务开销(强制写入磁盘所需的时间),这取决于 OS、文件系统和硬件。
如果您可以忍受它的限制(主要是没有网络),您可以通过启用 WAL mode 来使用异步写入。
尝试将索引添加到您的数据库:
CREATE INDEX IDXname ON player (name)
我的问题直接来自 this 一个问题,尽管我只对 UPDATE 感兴趣,而且仅此而已。
我有一个用 C/C++
编写的应用程序,它大量使用 SQLite
,主要是 SELECT/UPDATE
,间隔非常频繁 (每 0.5 大约有 20 个查询到 1 秒)
我的数据库不大,暂时有2500条条记录,下面是table结构:
CREATE TABLE player (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(64) UNIQUE,
stats VARBINARY,
rules VARBINARY
);
到目前为止我没有使用 transactions
因为我正在改进代码并且想要稳定性而不是性能。
然后我通过仅执行 10 update
查询来测量我的数据库性能,以下(在不同值的循环中):
// 10 times execution of this
UPDATE player SET stats = ? WHERE (name = ?)
其中 stats
是恰好 150 个字符的 JSON,name
是 5-10 个字符。
没有事务,结果是 unacceptable:- 大约 1 整秒(每个 0.096)
使用事务,时间减少了 x7.5 倍:- 大约 0.11 - 0.16 秒(每次 0.013)
我尝试删除大部分数据库 and/or 重新排序/删除列以查看是否有任何改变,但没有。即使数据库仅包含 100 条记录(已测试),我也会得到上述数字。
然后我尝试使用 PRAGMA
选项:
PRAGMA synchronous = NORMAL
PRAGMA journal_mode = MEMORY
给了我更短的时间,但并非总是如此,更像是 大约 0.08 - 0.14 秒
PRAGMA synchronous = OFF
PRAGMA journal_mode = MEMORY
最后给了我非常短的时间 大约 0.002 - 0.003 秒 但我不想使用它,因为我的应用程序每秒都在保存数据库并且很可能损坏数据库 OS / 电源故障。
我的C SQLite
查询代码是:(comments/errorhandling/unrelated部分省略)
// start transaction
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
// query
sqlite3_stmt *statement = NULL;
int out = sqlite3_prepare_v2(query.c_str(), -1, &statement, NULL);
// bindings
for(size_t x = 0, sz = bindings.size(); x < sz; x++) {
out = sqlite3_bind_text(statement, x+1, bindings[x].text_value.c_str(), bindings[x].text_value.size(), SQLITE_TRANSIENT);
...
}
// execute
out = sqlite3_step(statement);
if (out != SQLITE_OK) {
// should finalize the query no mind the error
if (statement != NULL) {
sqlite3_finalize(statement);
}
}
// end the transaction
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
如您所见,这是一个非常典型的 TABLE
,记录数量很少,我只做了 10 次简单的 UPDATE
。我还能做些什么来减少我的 UPDATE
次吗?我正在使用最新的 SQLite 3.16.2
.
NOTE: The timings above are coming directly from a single
END TRANSACTION
query. Queries are done into a simple transaction and i'm using a prepared statement.
更新:
我执行了一些启用和禁用事务以及各种更新计数的测试。我使用以下设置执行了测试:
VACUUM;
PRAGMA synchronous = NORMAL; -- def: FULL
PRAGMA journal_mode = WAL; -- def: DELETE
PRAGMA page_size = 4096; -- def: 1024
结果如下:
无交易(10 次更新)
- 0.30800 秒(每次更新 0.0308)
- 0.30200 秒
- 0.36200 秒
- 0.28600 秒
无交易(100 次更新)
- 2.64400 秒(每次更新 0.02644)
- 2.61200 秒
- 2.76400 秒
- 2.68700 秒
无交易(1000 次更新)
- 28.02800 秒(每次更新 0.028)
- 27.73700 秒
- ..
有交易(10 次更新)
- 0.12800 秒(每次更新 0.0128)
- 0.08100 秒
- 0.16400 秒
- 0.10400 秒
有交易(100 次更新)
- 0.088 秒(每次更新 0.00088)
- 0.091 秒
- 0.052 秒
- 0.101 秒
有交易(1000 次更新)
- 0.08900 秒(每次更新 0.000089)
- 0.15000 秒
- 0.11000 秒
- 0.09100 秒
我的结论是 transactions
time cost per query
毫无意义。也许时代会随着大量更新而变得更大,但我对这些数字不感兴趣。 10 次和 1000 次更新在单个事务中 之间几乎没有时间成本差异。但是我想知道这是否是我机器上的硬件限制并且不能做太多事情。看来我不能低于 ~100
毫秒使用单个事务和范围 10-1000 更新,即使使用 WAL。
如果没有事务,固定时间成本约为 0.025
秒。
您可能仍然受到提交交易所需时间的限制。在您的第一个示例中,每个事务大约需要 0.10 才能完成,这非常接近插入 10 条记录的事务时间。如果在单个事务中批处理 100 或 1000 个更新,您会得到什么样的结果?
此外,SQLite 预计平均硬盘驱动器每秒处理大约 60 个事务,而您只能得到大约 10 个。您的磁盘性能可能是这里的问题吗?
对于如此少量的数据,数据库操作本身的时间是微不足道的;您要测量的是事务开销(强制写入磁盘所需的时间),这取决于 OS、文件系统和硬件。
如果您可以忍受它的限制(主要是没有网络),您可以通过启用 WAL mode 来使用异步写入。
尝试将索引添加到您的数据库:
CREATE INDEX IDXname ON player (name)