简而言之,这篇文章想回答以下问题:如何(如果可能的话)我们如何配置SQLite数据库以确保任何INSERT命令在不到8毫秒内返回?
通过configure,我的意思是:编译选项,数据库编译指示选项和运行时选项。
为了给出一些背景知识,我们希望以120 fps应用相同的INSERT语句。 (1000 ms / 120fps≃8ms)
使用以下字符串创建数据库:
"CREATE TABLE IF NOT EXISTS MYTABLE ("
"int1 INTEGER PRIMARY KEY AUTOINCREMENT, "
"int2 INTEGER, "
"int3 INTEGER, "
"int4 INTEGER, "
"fileName TEXT);
和选项:
"PRAGMA SYNCHRONOUS=NORMAL;"
"PRAGMA JOURNAL_MODE=WAL;"
INSERT语句如下:
INSERT INTO MYTABLE VALUES (NULL, ?, ?, ?, ?)
最后? (对于fileName)是文件的名称,因此它是一个小字符串。因此每个INSERT都很小。
当然,我使用预编译语句来加速这个过程。
我有一个小程序,每8毫秒插入一次,并测量执行此插入所需的时间。更确切地说,程序进行一次插入,然后等待8 ms,然后进行另一次插入等...最后,推送了7200个插入,因此程序运行大约1分钟。
以下两个链接显示两个图表:
此图显示了插入所花费的毫秒数与以分钟为单位表示的时间的函数关系。正如您所看到的,大多数情况下,插入时间为0,但是有超过100毫秒的峰值。
此图像是相同数据的直方图表示。所有低于5毫秒的值都没有表示,但我可以告诉你,从7200个插入中,7161低于5毫秒(并且会在0处产生一个巨大的峰值,这会使图表的可读性降低)。
总计划时间是 真正的1m2.286s 用户0m1.228s sys 0m0.320s。
让我们说它1分4秒。别忘了我们花了7200倍8毫秒等待。所以7200插件需要4秒--->我们的速率为每秒1800个插入,因此每个插入的平均时间为0.55毫秒。 这真的很棒,除了在我的情况下,我希望所有的插入都低于8毫秒,图表显示情况显然不是这样。
那么这些高峰来自哪里?
当WAL文件达到给定大小(在我们的例子中为1MB)时,SQLite会生成一个检查点(WAL文件应用于真实数据库文件)。因为我们通过PRAGMA SYNCHRONOUS = NORMAL,所以此时,SQLite在硬盘上执行fsync。 我们假设这是这个fsync,使相应的插入真的很慢。 此长插入时间不依赖于WAL文件大小。我们使用链接到WAL文件的pragma WAL_AUTOCHECKPOINT(默认为1000),我们无法减小峰值的高度。
我们也试过PRAGMA SYNCHRONOUS = OFF。表现更好但仍然不够。 有关信息,我的计算机上的dirty_background_ratio(/ proc / sys / vm / dirty_background_ratio)设置为0,这意味着必须立即在硬盘驱动器上刷新缓存中的所有脏页。
有没有人有一个想法,如何#34;顺利"图表,意味着所有插入时间都不会超过8毫秒?
答案 0 :(得分:1)
默认情况下,SQLite中的所有内容都针对吞吐量而非延迟进行了优化。
WAL模式将大多数延迟移动到检查点,但如果您不想要那些大延迟,则必须使用更频繁的检查点,即在每次交易后执行检查点。
在这种情况下,WAL模式没有意义;最好试试journal_mode=persist
。
(这没有多大帮助,因为延迟主要来自同步,而不是来自数据量。)
如果WAL /日记操作太慢,即使synchronous=off
的速度不够快,那么您唯一的选择是禁用交易安全并尝试journal_mode=memory
甚至=off
。