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

如文章 "[Indexes load at startup](https://manticoresearch.com/blog/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](https://mnt.cr/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](./about-startup-mmap-mlock-and-force-preread/no_force_preread.png)  

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

还有哪些其他重要的因素？
1. 尝试调整操作系统参数，例如“swapinness”或如果条件允许，完全禁用交换功能。这有助于提高快速响应的概率（无需使用mlocking）。请注意，在现代Linux内核中，你拥有一个非常棒的功能，称为控制组（即cgroups）。你可以将你的守护进程放入专用的cgroup中，并为其调整任何系统设置（如上述的swapinness），而不会影响全局系统设置。
2. 现代SSD即使在随机访问时也非常快速，因此使用它们可能会消除仅“映射”（“mlocked”）和“缓存”数据之间的差异。
