【发布时间】:2014-04-02 13:26:25
【问题描述】:
简而言之,这篇文章想回答以下问题:我们如何(如果可能)配置 SQLite 数据库以绝对确保任何 INSERT 命令将在 8 毫秒内返回?
通过配置,我的意思是:编译选项、数据库编译选项和运行时选项。
为了提供一些背景知识,我们希望以 120 fps 的速度应用相同的 INSERT 语句。 (1000 毫秒/120 fps ≃ 8 毫秒)
使用以下字符串创建数据库:
"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, ?, ?, ?, ?)
最后一个? (for fileName) 是文件的名称,所以它是一个小字符串。因此,每个 INSERT 都很小。
当然,我使用预编译语句来加速这个过程。
我有一个小程序,每 8 毫秒插入一次,并测量执行此插入所需的时间。更准确地说,程序进行一次插入,然后等待 8 毫秒,然后进行另一次插入,等等……最后,推送了 7200 次插入,因此程序运行了大约 1 分钟。
这里有两个显示两个图表的链接:
此图显示了插入所花费的毫秒数与以分钟表示的时间的函数关系。如您所见,大多数情况下,插入时间为 0,但也有超过 100 毫秒的尖峰。
此图像是相同数据的直方图表示。所有低于 5 毫秒的值都没有表示出来,但我可以告诉你,从 7200 次插入来看,7161 低于 5 毫秒(并且会在 0 处产生一个巨大的峰值,这会使图表的可读性降低)。
总节目时间为 实际 1m2.286s 用户 0m1.228s 系统 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,这意味着缓存中的所有脏页必须立即刷新到硬盘上。
有没有人知道如何“平滑”图表,这意味着所有插入时间都不会超过 8 毫秒?
【问题讨论】:
-
为什么需要这样的时机?从在 RS485 通信需要 40 毫秒时间片的系统上工作的角度来看,我认为您不会在非实时操作系统上达到目标。您使用的是什么操作系统?
-
我一般不喜欢多线程,但这似乎是你的 INSERT 语句的执行应该在一个单独的线程中的情况,两个线程安全地共享一个 std::deque 记录插入。
-
@Steger:这正是我们在上述项目中所做的,并赋予了我们快速线程实时优先级,但我们仍然无法始终达到所需的时序。不过,我们到达了“足够好”的地方。
-
您可以尝试其中一些stackoverflow.com/questions/1711631/… 优化。
标签: c++ sqlite optimization real-time