在本文中,我们将讨论索引如何在启动时加载,以及这对传入查询和搜索实例管理的影响。
在较早的Sphinx版本中,如果设置了preopen选项,索引会被预读并加载到内存中。在此期间,守护进程将拒绝接受任何传入连接。对于小索引来说,这并不是大问题,因为它们加载得很快。但对于需要加载数十甚至数百GB的巨大索引来说,这确实是个问题,因为加载完成可能需要几分钟甚至更长时间。
从Sphinx 2.3版本开始,这一情况发生了变化,因为索引文件现在是通过内存映射(mmap)的方式加载,而不是简单地读取并加载到内存中。使用mmap时,守护进程的一个影子线程(而非主线程)会先为每个页面读取一个字节,然后继续读取其余数据,逐步将所有内容加载到RAM中,同时允许访问数据。简而言之,这是对索引文件的延迟加载。
通过这种方式,守护进程可以在启动后立即接受连接。如果某个查询需要访问尚未加载到内存中的随机数据,它将从磁盘读取。这也意味着查询的执行速度可能比平时慢(具体取决于存储性能),但至少守护进程可以返回结果。更进一步,一旦某个索引的文件加载完成,守护进程就可以为其提供“全速”响应,即使其他索引仍在加载中。
虽然这对大多数用户来说是个好消息,但对某些用户而言,“延迟”加载可能并非最佳选择。例如,使用简单策略(随机或轮询)的负载均衡器,这些策略不测量响应时间,会在守护进程启动后立即将重启的实例加入集群。由于查询需要直接从磁盘读取数据,实例的响应时间将比平时更高,甚至高很多,这并非理想情况。相反,如果可能的话,最好在守护进程完成索引加载后,再将实例添加到集群中。
为了解决这种情况,自2.3版本起, searchd 提供了一个选项,但遗憾的是直到最近才被记录:'--force-preread'。使用此选项时,守护进程仍会通过mmap加载索引文件,但预读操作会成为阻塞操作(就像旧版本一样),searchd将不会响应,直到所有内容都加载到内存中。