我们有一台总 RAM 为 64GB 的服务器,应用程序通常最多使用 30GB 的可用 RAM。其中一个应用程序处理大量平面文件,我们遇到了吞吐量问题,即等待磁盘 I/O。在探索可能的解决方案时,出现了 RAM 磁盘的想法。RAM 磁盘的问题是固有的波动性。
我找到了关于 RAM 磁盘、RAID 1 配置和逻辑镜像卷以对物理磁盘进行分组的单独文档,但我似乎找不到任何文档表明这些磁盘复制解决方案是否可以与 RAM 磁盘一起使用。更重要的是,由于想法是让 RAM 磁盘可用于读/写,并让物理磁盘“遮蔽”RAM 磁盘,赶上写入,我们希望 RAM 磁盘成为所有磁盘的“主要”磁盘读/写。
需要注意的是,我们希望避免仅通过 RAM 缓存操作系统的文件,但如果我们可以获得与独立 RAM 磁盘相同的性能,那是可行的。我们最初避免了这种情况,因为通常某些文件不会长时间访问,但仍需要按需读取/写入速度。
你可以
vmtouch
用来解决你的问题。这是一个实用程序,它允许您将某些文件甚至整个目录及其下的所有内容固定在页面缓存中,这样它们就不会被驱逐,即使它们长时间未被访问(这是您不简单地访问它们的最初原因依赖于页面缓存)。这最多需要与 RAM 磁盘相同的内存量,或者实际上更少。您仍将使用页面缓存,但它会产生与对所有内容使用 RAM 磁盘相似的性能(实际上性能更优,因为不涉及 MD 驱动程序)。这可以一起破解,但这是一个坏主意,并且可能在可靠性和可维护性方面存在多个问题。
我认为 RAMdisk 和物理磁盘的 RAID1 会限制物理磁盘的性能,因为 RAID1 功能的一部分是确保两个副本同步。
对于读取,可能会有一些好处,因为 MD 驱动程序可以在不同设备之间分配读取。
创建它的可能步骤:
losetup
从文件中创建块设备。mdadm
用新创建的块设备和对应的硬盘分区创建阵列。我自己还没有尝试过,所以这只是理论上的例子。
首先,RAM 磁盘在 Linux 上几乎从来都不是正确的答案。因为它是一个块设备,你最终的任何读取都必须经过块层、文件系统和常规 VFS 层,数据除了存储在 RAM 磁盘中之外,最终还会缓存在 RAM 中。这种数据重复以及涉及的额外层数是 tmpfs 在 Linux 上存在的原因,而不是涉及块层,tmpfs 文件系统只是将数据直接存储在页面缓存中,跳过所有额外的复杂性。它还会根据存储在其中的数据量自动调整大小(而不是必须预先定义大小),它甚至可以利用交换空间。如果您认为您需要一个 ramdisk,那么 99% 的时间您真的应该使用 tmpfs。
现在,就实际解决方案而言......
如果您的所有数据实际上都适合 RAM,那么最好将其全部固定在 RAM 中,方法是使用vmtouch 之类的工具,或者让应用程序映射所有文件,然后在所有映射区域上调用 mlock。
如果您的数据不能全部放入 RAM,您有两个现实的选择:
如果您需要持久性,RAMDISK 不是正确的解决方案。
我强烈建议投资一对快速(阅读:企业级,具有断电保护)NVMe 磁盘以放入经典 RAID1(镜像)阵列。
我已经使用 AWS 临时磁盘完成了类似的操作,这些磁盘速度非常快但无法在电源关闭/打开周期中存活下来。
我们有一个“种子磁盘”,它是 GP2(现在是 GP3)的普通廉价 EBS 卷,它位于具有快速临时磁盘的 RAID1 中
我为 rc.local 创建了一个 bash 脚本,以通过
nvme list
命令输出确定是否存在临时磁盘,并在适当的时候将其加入 raid。在您的情况下,启动时必须创建 ramdisk,将其加入现有的降级阵列。
最后两个是每个900G的临时磁盘。
好消息是,写入 mdX 设备将通过有序的重启和断电持续存在。意外的硬断电可能会导致写入丢失。
所以这是一个糟糕的备份替代品——您仍然应该使用适合您的任何方法进行备份。
如果你有这么多空闲 ram(可以容纳这些文件中的大部分及其元数据),那么它们大部分都驻留在 RAM 缓存中,而你的限制因素不是读取,而是写入它们。
如果是这种情况,在 RAM 中强制镜像此卷不会给您带来任何性能。
在另一个 i/o 活动不断将文件踢出 RAM 的可能情况下,为类似磁盘的解决方案锁定这么多 RAM 可能会影响这些另一个 i/o 进程。
内存缓存、Redis
您基本上描述了 Memcached,以及一些 Redis。两者都擅长缓存,Redis对持久化的支持更好。
请注意,如果这些平面文件总大小小于 30GB(在您的计算机中),您只能获得“全部”性能,否则应采用某种驱逐机制。即便如此,如果此应用程序非常频繁地使用某些文件,Redis/Memcached 解决方案将提高性能。
这些产品得到供应商的大力支持,因此您可以使用外部托管的 Memcached/Redis 服务器将您的机器与缓存的细节完全隔离开来。
问题是关于 RAM 磁盘的速度和持久性。只要允许异步写入(保持磁盘“赶上写入”),这是可能的。
只要应用程序不使用
sync
orfsync
,它就会运行得更快,并且更容易使用常规缓存进行配置,而无需使用 RAM 磁盘和镜像卷配置。为了保持应用程序运行,即使它写入大量数据使大量内存变脏,在这个问题的情况下需要允许 32 GB 的脏内存。这使内核刷新器线程中的所有磁盘写入远离应用程序进程,并由
sysctl vm.dirty_bytes=$((32*1024*1024*1024)) # 32 GB
(默认情况下
sysctl vm.dirty_ratio=20
允许不超过 20% 的“可用”内存变脏,如果达到此限制,应用程序将受到限制,这发生在 32 GB 内存变脏之前很久。)由于该应用程序“处理大量平面文件”,我怀疑它具有线性读取行为,因此显式预取数据不会有帮助。但如果它具有随机读取行为,则应在启动应用程序之前预热缓存。