⚠️ 此页面为自动翻译,翻译可能不完美。

About startup, mmap, mlock and --force-preread

如文章 " Indexes load at startup " 中所述,现在所有索引(属性和词表文件)不会被物理加载到 RAM 中,而是通过内存映射的方式。这使得在启动时能够更快地加载它们,但也带来了一些副作用,我想要解释一下。

首先,由于我们进行了映射,索引可能不会永久锁定在物理 RAM 中,因此您不需要拥有足够的 RAM 来容纳所有索引。拥有合理的 RAM 量在许多情况下已经可以为您提供快速的搜索查询,因为当索引被缓存时,它们的工作速度显著提高。

第二个后果是,实际上内存映射只占用了进程的“虚拟地址空间”区域。只要在任何现代系统中地址有 64 位,我们可以说可以加载和提供几乎任何大小的索引,尽管实际的空闲 RAM 可能有限。但请注意,这仅与索引数据有关。Manticore 搜索守护进程还需要物理 RAM 来进行常规工作,例如内部哈希表、缓冲区、数组等。

如果您查看进程的内存统计信息,您会看到 RSS(或 RES)列中的某个数字,这实际上是占用的 RAM(主要是堆),但不是加载的索引(除非您设置 mlock=1)。它们主要反映在“VSZ”列中。此外,如果您加载一个巨大的索引(大约整个 RAM 空间),然后执行“free”命令,您会发现它实际上并未反映在“已使用”空间中,而是主要反映在“buff/cache”中,因此也反映在“可用”空间中。

默认情况下,加载的索引不会被锁定在内存中,而是被缓存。如果操作系统需要为其他进程分配更多 RAM,它会牺牲缓存的数据来完成。因此,“加载”索引并不能保证它实际上在 RAM 中并能以可预测的速度响应。

这在实践中意味着什么?

  1. 首先,默认情况下没有保证。“通过内存映射在启动时加载索引”并随后逐页访问该映射,只会加载您访问的页面。操作系统不保证在访问一页后,下一次“加载”会持久地将之前一页锁定在 RAM 中。是的,它可能会——如果您有 128GB 的空闲 RAM,而加载的索引只有 30GB。但如果您有一个 120GB 的索引,只有 16GB 的 RAM,“加载”将以相同的方式成功,但由于索引无法完全适应 RAM,它不会被完全缓存,响应时间会增加。
  2. 第二,没有保证加载的索引会永久保持相同的响应时间。想象一下,再次加载一个 30GB 的索引到一个有 50GB 空闲 RAM 的系统上,一切似乎都运行得很快。但随后您还加载了一个占用大量 RAM 的进程,它占用了 40GB。这意味着,您的 30GB 索引中只有约 10GB 仍然被缓存,其余部分的访问现在需要从磁盘读取。

因此,无论是惰性预读,还是甚至 --force-preread 选项,都不能保证整个索引被缓存并能永久、可预测地快速响应。没有保证,只有概率。您拥有的 RAM 越多,整个索引被缓存并能以最大速度响应的概率就越高。所有这些 mmap “操作”只是关于概率的。

但我需要的是保证,而不是概率!这可能吗?

是的!唯一(也是唯一)确保整个索引被锁定在 RAM 中的方法是使用 mlock 选项。 它应该在索引配置中设置(而不是在命令行选项中)。它需要您拥有执行此操作的权限(有关详细信息,请参阅系统 'man mlock')。它是如何工作的?守护进程将内存映射索引文件,然后对它们调用 'mlock'。此时操作系统将识别它是否有足够的 RAM 来加载所有所需的映射,如果有的话,它将立即执行加载。这可能是一个相对较长的操作(只需考虑您的存储速度并估算加载所需数据量所需的时间)。

因此,我们可以实现目标——拥有一个完全锁定在 RAM 中的索引,它能以可预测的速度响应。这是好的。

但还需要提到一些与 mlock 相关的内容。

  1. 首先,如前所述,您需要有权限运行它。这在一定程度上来自于它的工作方式,可能会影响整个系统。不过在大多数情况下,这并不是大问题,除非您使用的是权限非常有限的共享托管。
  2. 其次,映射的缓存(mlocking)是一个阻塞过程,我们无法管理。内部我们只是调用 mlock(),它在内部做一些魔法操作,并在所有操作完成后几秒钟或几分钟后返回。无法中断,无法限制 I/O,只能等待。因此,mlocking 过程可能会影响机器上的其他 I/O 操作。
  3. 当系统为 mlocking 寻找 RAM 时,很可能会调用 OOM-killer 来为您释放 RAM,这可能会杀死另一个进程。请注意!
  4. 即使您使用 mlock,您可能仍然需要在许多情况下使用 --force-preread。这里的困境是:
    • 不使用 --force-preread 时,searchd 会更快地开始处理连接,但索引在后台完全预读之前会较冷。这可能对传入的查询不利。
    • 使用 --force-preread 时,您需要等待(可能几分钟),但之后您将能够提供非常出色的性能。

使用 mlock 但不使用 --force-preread 时,它可能看起来像这样:

no_force_preread

而在相同硬件上使用 --force-preread 时,相同的索引会是这样:

还有哪些其他重要因素需要考虑?

  1. 尝试调整操作系统参数,例如 'swapinness' 或者如果条件允许,完全禁用交换功能。这有助于提高快速响应的概率(无需使用 mlocking)。请注意,在现代 Linux 内核中,您有这样一个非常棒的功能,称为控制组(aka cgroups)。您可以将您的守护进程放入专用的 cgroup 中,并为其调整任何系统设置(如上述的 swapinness),而不会影响全局系统设置。
  2. 现代 SSD 即使在随机访问时也非常快速,因此使用它们可能会消除仅 'mapped'('mlocked')数据和 'cached' 数据之间的差异。

安装Manticore Search

安装Manticore Search