# Threads in Manticore Search

本文将讨论 Manticore Search 当前实现的工作者以及如何调整工作者的参数。



在 Manticore Search 中，目前有两种多进程模式，由 [workers](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#workers) 指令控制。当前默认的 MPM 是 ***thread_pool***，另一种是 ***threads***。

### 线程


在多进程模式 '**[threads](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#workers)**' 中，**每个**传入的网络 ***连接*** 都会创建一个新的专用线程。线程在客户端断开连接之前保持活动状态——即只要连接存在。在此期间，线程将执行来自连接的查询。

默认情况下，引擎将生成无限数量的线程——如果操作系统允许的话。虽然这看起来不错，但实际上允许创建无限线程会带来一些成本。首先，创建/销毁线程有 CPU 成本。虽然这与 fork 相比成本较低，但仍然存在。

每秒生成数百或数千个线程可能会影响系统的性能，具体取决于可用的 CPU 核心数。线程数量远大于可用核心数会导致这些线程争夺资源——首先是 CPU 核心，其次是存储。

例如，如果我们有一个 16 核系统，但允许生成 160 个搜索线程，这意味着每个 CPU 核心有 10 个线程。这些线程不会同时运行，但必须排队等待 CPU 核心分配周期。在存储方面，我们可能有 160 个潜在的请求者从存储中读取数据。如果存储无法在没有延迟（延迟）的情况下持续服务这些线程，延迟可能会影响 CPU 核心的分配，因为一个线程可能获得在 CPU 核心上执行其计算的绿灯，但必须等待数据。
**[max_children](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#max-children)** 设置允许限制一次可以激活的线程数量。当达到此限制时，引擎将开始以 **'maxed out'** 错误拒绝传入的连接。在设置 max_children 时，需要记住当客户端关闭连接时线程会被终止。如果连接处于 ***空闲*** 状态，线程将保持活动状态并被 max_children 计数。max_children 可以增加到一个取决于服务器处理活动查询能力的限制。这包括处理能力（CPU）和 IO 操作（存储）。常见的错误是将 max_children 设置为非常高的值。在系统能力范围内，存在一个临界点，当生成过多线程时只会导致查询变慢。如果在优化索引和查询方面没有更多可做的，应考虑使用新的搜索服务器来分担负载。

 
  
  
### thread_pool


在上一节中，我们讨论了线程的开销以及连接即使未使用也会保持线程活跃。

管理线程的另一种策略是不为每个连接创建线程，而是使用一个 **线程池** 来处理工作。在这种情况下，工作线程不与传入的连接相关联，因为连接由另一类线程处理。当守护进程启动时，它会创建一定数量的线程。传入的网络连接由所谓的网络线程处理，它们的任务是从连接接收查询并将它们路由到线程池中的线程。

如果线程池太忙（所有工作者都在处理查询），新查询将被发送到 **待处理队列**。如果待处理队列被填满，服务器将 '**max out**'。默认情况下没有设置限制，但排队时间过长对获取长时间查询没有帮助。为队列设置一个限制是个好主意，以表明服务器接收到的查询多于其处理能力。这可以通过 **[queue_max_length](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#queue-max-length)** 指令设置。与线程模型中的 max_children 相比，其中在一定数量的打开连接后开始达到上限，在 thread_pool 的情况下，守护进程在活动查询多于 max_children（线程池中的线程数）+ queue_max_length 时开始 'max out'。

在 [thread_pool](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#workers) 情况下，[max_children](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#max-children) 指令定义了线程池中创建的线程数量，这些线程在守护进程启动时创建。默认情况下，使用 **1.5x** CPU 核心数的值。将 max_children 设置为高值不会增加服务器的性能或容量。因为这些线程仅处理任务，它们需要尽快访问 CPU。

设置高值只会导致与上一节相同的问题，即 CPU 争用和性能下降，还会导致守护进程启动变慢。与线程模式相比，其中 max_children 可以设置为 CPU 核心数的数倍，甚至数百的值是有意义的，在 thread_pool 的情况下，将值设置为 2-3 倍的 CPU 核心数几乎没有意义。

默认情况下，为了处理网络连接，会使用一个专用线程。该线程的工作通常很简单，因为它仅作为线程池的代理，通常可以承受大量流量。可以通过 **[net\_workers](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#net-workers)** 指令增加网络线程的数量。对于每秒查询率极高的设置，增加网络线程可以带来性能提升。

网络线程还有一些精细调整的设置。网络线程可能并不总是处于忙碌状态，因为有时（以亚秒级为单位）它可能不会接收到查询。线程会进入睡眠状态并失去CPU优先级。唤醒或重新获得CPU时间的花费很小，但对于高性能系统来说，每一毫秒都很重要。

为了解决这个问题，可以通过 **[net\_wait\_tm](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#net-wait-tm)** 指令激活忙等待循环。如果 net\_wait\_tm 值为正数，线程将每隔 10\***net\_wait\_tm**（其中 net\_wait\_tm 值以毫秒为单位）“打一次”CPU。如果 net\_wait\_tm 为零，忙等待循环将持续运行——需要注意的是，即使网络线程没有接收太多流量，这也会产生额外的CPU使用量。要禁用忙等待循环，应使用 -1 的负值。默认情况下，net\_wait\_tm 的值为 1，表示每 10 毫秒进行一次忙等待循环。

网络线程也可以被限制。**[net\_throttle\_accept](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#net-throttle-accept-net-throttle-action)** 选项限制了网络线程一次接受的客户端数量，而 **[net\_throttle\_action](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#net-throttle-accept-net-throttle-action)** 明确指定了每次迭代处理的请求数量。默认情况下，不强制任何限制。在网络负载较高的场景中，限制网络线程是有意义的。

一个容易被误解的方面是 max\_children 与 [dist\_threads](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#dist-threads) 之间的关系。dist\_threads 负责某些操作的多线程处理，例如本地 [distributed](https://docs.manticoresearch.com/latest/html/searching/distributed_searching.html#distributed-searching) 索引、使用 load\_files 的 [snippets](https://docs.manticoresearch.com/latest/html/api_reference/additional_functionality.html#build-excerpts) 或 [CALL PQ](https://docs.manticoresearch.com/latest/html/sphinxql_reference/call_pq_syntax.html) 命令。然而，这些是子进程线程，它们不计入 [max\_children](https://docs.manticoresearch.com/latest/html/conf_options_reference/searchd_program_configuration_options.html#max-children) 指令，该指令仅计算主查询线程。
