到目前为止,资源共享一直是使用读写锁完成的。在高负载情况下,使用锁可能会在尝试更改索引时遇到问题。为了克服这些问题,我们不得不重新思考线程和索引之间的关系。
索引可大可小,并且在工作进程之间共享。在多核CPU上,您可以同时触发多个查询,这些查询将分布在不同的核心上,使用同一个索引。这很简单直接。然而,有时您需要更新索引。使用旧的fork工作进程进行无缝轮换效果相当不错:我们只需加载新的索引文件,让现有的工作进程继续使用旧的索引。在某一时刻,新的查询转向已经加载新索引的工作进程。因此,服务没有延迟:您只是无缝地从旧索引切换到新索引。使用旧版本的工作进程将完成(或崩溃),最终前一个索引被释放。
对于线程工作进程,使用了简单的RW锁 - ‘共享-排他锁’ 机制,其中多个共享工作进程(读取者)可以同时访问资源,但一个排他工作进程(写入者)可以修改它。因此,索引要么在查询之间"共享",要么被轮换"锁定"。它们在开始时加载,并在结束时释放。进行轮换时,一个线程加载新索引,但需要对活动索引描述符进行排他访问。然而,查询工作进程具有共享访问权限,并且没有人阻止它们获取新任务。这意味着在重负载下,由于最后一步 - 原子地将旧索引更改为新索引 - 您无法执行"无缝"轮换。
在2.7中引入的新模型中,索引现在在线程之间独立存在,类似于旧的"fork"情况,它们现在既不是"共享“也不是”锁定",而是不可变的。这使得运行更简单:工作进程不需要关心索引的"锁定“或”共享",它只是使用它。当您需要轮换(加载新索引)时,守护进程只是执行此操作,而不关心正在运行的查询(其线程也不关心"写入者")。最后,轮换只是将活动索引的指针切换到新加载的索引,然后所有操作都像使用fork一样:旧查询仍使用先前的索引,新查询指向刚加载的索引。唯一仍使用排他锁的情况是执行UPDATE操作。
新模型的另一个优势是,可以通过配置重新加载动态更改索引的类型。以前,通过在配置文件中更改并尝试重新加载(通过HUP信号)来更改索引类型并不总是可能的,例如将"普通"索引切换到"分布式"或从"分布式"切换到"模板"。现在,当您重新加载新配置时,其中旧索引的所有内容都是其名称 - 守护进程将首先解析新配置。然后,如果可以立即使用(这适用于模板和分布式索引)- 它将无缝交换它们。在新索引是"重量级“的情况下(即需要预缓存到RAM中,可能需要几分钟)- 轮换过程将被推迟,直到它们加载完成。**«延迟»**意味着调用者看到"确定"返回,"(全部轮换)"。但实际工作进程仍将所有新查询发送到旧索引,直到新索引最终加载并激活。