我们一直在一家托管公司的虚拟机上运行服务器,并且刚刚注册了一个专用主机(AMD Opteron 3250、4 核、8GB RAM、2 x 1TB 软件 RAID、ext3)。
在运行性能测试时,我们注意到一些 SQLite 事务(插入、删除和/或更新的组合)比在我的 2010 MacBook Pro 上花费的时间要长 10 到 15 倍。
经过大量的谷歌搜索和阅读后,我们了解了安装选项,它们是:
data=ordered,barrier=1
我们做了一些实验,并获得了最佳表现
data=writeback,barrier=0
我已经阅读过这些内容,并且了解他们所做工作的基本原理,但我不太清楚我们这样运行是否是个好主意?
问题
对于托管服务来说,上述配置是否合理?
如果发生断电或系统崩溃,那么数据可能会丢失,文件可能会损坏。如果我们每 15 分钟拍摄一次数据库快照,这可能会缓解这种情况,但拍摄快照时数据库可能不会同步。我们应该如何(可以吗?)确保此类快照的完整性?
我们还应该考虑其他选择吗?
谢谢
答案1
第一条建议
如果您不能丢失任何数据(我的意思是一旦用户输入了新数据,如果在接下来的几秒钟内不能丢失)并且因为您没有像UPS这样的东西,那么我就不会删除写屏障,也不会切换到写回。
消除写障碍
如果删除写屏障,那么在发生崩溃或断电的情况下,文件系统将需要执行 fsck 来修复磁盘结构(请注意,即使屏障处于开启状态,大多数日志文件系统仍会执行 fsck,尽管日志的重放应该已经足够)。删除写屏障时,建议尽可能删除任何磁盘缓存(在硬件上),这有助于最大限度地降低风险。不过,您应该对这种更改的影响进行基准测试。您可以尝试此命令(如果您的硬件支持它)hdparm -W0 /dev/<your HDD>
。
请注意,ext3 对元数据更改使用了 2 个屏障,而 ext4 在使用 mount 选项时仅使用一个屏障journal_async_commit
。
虽然T'so 解释道为什么在 ext3 早期会出现少量数据损坏的情况(默认情况下屏障是关闭的直到内核 3.1),除非发生日志回绕(日志是循环日志),否则日志的放置方式是,数据以安全的顺序 - 先写日志,后写数据 - 即使硬盘支持重新排序写入。
基本上,当日志回绕时,系统崩溃或断电是不幸的。但是,您需要保留。此外,data=ordered
尝试使用 进行基准测试。data=ordered,barrier=0
如果您可以承受几秒钟的数据丢失,您可以同时激活这两个选项data=writeback,barrier=0
,然后尝试使用该commit=<nrsec>
参数。查看此参数的手册这里。基本上,您给出一个秒数,这是 ext3 文件系统同步其数据和元数据的时间段。
您还可以尝试摆弄和基准测试一些有关脏页(需要写入磁盘的页)的内核可调参数,有一个好文章这里解释了有关这些可调参数的所有内容以及如何使用它们。
障碍总结
您应该对更多可调参数的组合进行基准测试:
data=writeback,barrier=0
与...结合使用hdparm -W0 /dev/<your HDD>
- 使用
data=ordered,barrier=0
data=writeback,barrier=0
与其他挂载选项一起使用commit=<nrsec>
并尝试不同的 nrsec 值- 使用选项 3. 并尝试在内核级别进一步调整脏页。
- 使用安全的
data=ordered,barrier=1
,但尝试其他可调参数:尤其是文件系统电梯(CFQ、Deadline 或 Noop)及其各自的可调参数。
考虑迁移到 ext4 并对其进行基准测试
如上所述,ext4 写入所需的屏障比 ext3 要少。此外,ext4 支持扩展,这对于大文件来说可能会带来更好的性能。因此,这是一个值得探索的解决方案,尤其是因为无需重新安装即可轻松从 ext3 迁移到 ext4:官方文档;我在一个系统上做过这个,但是使用这个Debian 指南。自内核 2.6.32 以来,Ext4 确实非常稳定,因此在生产中使用是安全的。
最后考虑
这个答案远非完整,但它为您提供了足够的材料来开始调查。这在很大程度上取决于需求(在用户或系统级别),因此很难给出一个直接的答案,对此深表歉意。
答案2
警告:下文可能存在不准确之处。我边写边学了很多这方面的东西,所以请谨慎对待。这篇文章很长,但你可以只阅读我们正在使用的参数,然后跳到最后的结论部分。
在许多层面上,你可能会担心 SQLite 的写入性能:
我们查看了以粗体突出显示的参数。具体参数包括
- 磁盘写入缓存。现代磁盘具有 RAM 缓存,用于优化旋转磁盘的磁盘写入。启用此功能后,数据可以无序写入,因此如果发生崩溃,您最终可能会得到一个部分写入的文件。使用 hdparm -W /dev/... 检查设置,并使用 hdparm -W1 /dev/... 进行设置(打开它,使用 -W0 将其关闭)。
- barrier=(0|1)。网上有很多评论说“如果你以 barrier=0 运行,那么就不要启用磁盘写入缓存”。你可以在以下网址找到关于 barrier 的讨论http://lwn.net/Articles/283161/
- 数据=(日志 | 有序 | 回写)。查看http://www.linuxtopia.org/HowToGuides/ext3JournalingFilesystem.html了解这些选项的描述。
- commit=N。告诉 ext3 每 N 秒(默认 5)同步所有数据和元数据。
- SQLite pragmasynchronous=ON | OFF。当设置为 ON 时,SQLite 将确保事务在继续之前“写入磁盘”。关闭此功能实际上会使其他设置变得无关紧要。
- SQLite pragma cache_size。控制 SQLite 将为其内存缓存使用多少内存。我尝试了两种大小:一种是整个数据库可以放入缓存中,另一种是缓存是最大数据库大小的一半。
阅读有关 ext3 选项的更多信息ext3 文档。
我对这些参数的多种组合进行了性能测试。ID 是场景编号,如下所述。
我首先在我的计算机上使用默认配置作为方案 1 运行。我认为方案 2 是最“安全”的,然后在适当/提示的情况下尝试了各种组合。使用我最终使用的地图可能最容易理解:
我编写了一个测试脚本,它运行了大量事务,包括插入、更新和删除,所有事务都针对仅包含 INTEGER、仅包含 TEXT(包含 id 列)或混合的表。我针对上述每种配置多次运行了此脚本:
底部的两个场景是 #6 和 #17,它们有“pragmasynchronous=off”,因此它们速度最快也就不足为奇了。接下来的三个集群是 #7、#11 和 #19。这三个在上面的“配置图”上以蓝色突出显示。基本上配置是磁盘写入缓存打开、barrier=0,数据设置为除“journal”之外的其他值。在 5 秒(#7)和 60 秒(#11)之间更改提交似乎没有什么区别。在这些测试中,数据=有序和数据=写回之间似乎没有太大区别,这让我很惊讶。
这混合更新测试是中间的峰值。在这个测试中,有一组场景明显更慢。这些都是数据=日志. 否则,其他场景之间就没什么区别了。
我进行了另一项时间测试,该测试对不同类型的组合进行了更异构的插入、更新和删除混合。这些测试花费的时间更长,这就是为什么我没有将其包含在上面的图中:
在这里你可以看到写回配置(#19)比有序配置(#7 和 #11)慢一点。我预计写回会稍微快一点,但也许这取决于你的写入模式,或者也许我还没有读够关于 ext3 的内容 :-)
各种场景在某种程度上代表了我们的应用程序执行的操作。在挑选出一些场景后,我们使用一些自动化测试套件运行了计时测试。结果与上述结果一致。
结论
- 这犯罪参数似乎没有什么区别,所以我们将其保留为 5s。
- 我们将启用磁盘写入缓存,屏障=0, 和数据=有序。我在网上看到一些人认为这是一个糟糕的设置,其他人似乎认为这应该是很多情况下的默认设置。我想最重要的是你要做出明智的决定,知道自己在做什么权衡。
- 我们不会在 SQLite 中使用同步指令。
- 设置 SQLite缓存大小pragma 使得 DB 能够放入内存中,从而提高某些操作的性能,正如我们预期的那样。
- 上述配置意味着我们承担的风险略大。我们将使用SQLite 备份 API尽量减少部分写入时磁盘故障的风险:每 N 分钟拍摄一次快照,并保留最后的 M。我在运行性能测试时测试了这个 API,它给了我们信心这样做。
- 如果我们仍然想要更多,我们可以考虑对内核进行一些改动,但是即使不这么做,我们也已经改进了足够多的东西。
感谢@Huygens 提供的各种提示和指点。