3.2.13 分发索引

为了实现分布式的搜索,需要把索引从一台服务器备份到另外一台服务器。最简单的方法是:在生成索引的服务器上用tar命令压缩索引目录,然后在另外一台搜索服务器上执行wget获得压缩成一个文件的索引。在Windows下,可以使用7-zip软件中的7z压缩格式备份正在写入的索引。

当增加、修改索引时,Lucene索引文件一般不会发生太大的变化。优化索引时,索引文件才会有大的变化。所以,实际中可以通过只传送修改的文件(也就是同步索引文件目录)的方式实现索引分发。rsync(http://samba.anu.edu.au/rsync/)是一个开源的工具,提供了快速的增量文件传输功能。使用rsync可以保持主服务器中的索引目录能够定期同步到从服务器的索引目录中。rsync支持通过SSH进行网络加密传输,也可以利用SSH客户端密钥建立服务器之间的信任关系。当在两台服务器之间保持大型、复杂目录结构同步的时候,使用rsync比tar或wget等方式都要快,而且可以做到精确同步。使用rsync的分布式垂直搜索架构如图3-8所示。

图3-8 分布式垂直搜索架构

rsync命令的基本形式如下:

        rsync [OPTION]... 来源地址路径 目的地地址路径

rsync可以通过两种不同的方式连接一个远程系统:使用一个远程shell程序(例如ssh或rsh)作为中转,或者通过TCP直接连接一个rsync守护进程。当地址路径信息包含“::”分隔符时启动服务器传输模式;当地址路径包含单个冒号“:”分隔符时启动远程shell传输模式。

首先可以在主服务器和从服务器上都安装最新的rsync软件。rsync的编译安装非常简单,只需要以下简单的几步:

        # ./configure
        # make
        # make install

然后用一个命令就可以一次性同步索引目录:

        #rsync -ave ssh master:/home/index/ /home/index/

这个命令把远端master机器上的/home/index/目录中的内容同步到本地的/home/index/目录下。

在创建索引的机器上,通过在rsync命令中声明--daemon参数可以运行rsync守护进程。在索引机器上建立配置文件rsyncd.conf和密码文件rsyncd.secrets。前台搜索机器通过crontab定时运行rsync获取最新的索引文件。

配置rsync守护进程的方法是修改/etc/rsyncd.conf并把rsync守护进程设置成自启动方式。Linux中的大部分网络服务都是由inetd启动的,修改/etc/xinetd.d/rsync文件把其中的disable = yes改成disable = no。在/etc/目录下建立配置文件rsyncd.conf。

        #cat /etc/rsyncd.conf


        use chroot = yes # 使用chroot
        max connections = 4 # 最大连接数为4
        pid file = /var/run/rsyncd.pid
        lock file = /var/run/rsync.lock
        log file = /var/log/rsyncd.log # 日志记录文件


        [index] # 这里是认证的模块名,在client端需要指定
        path = /home/index # 需要做镜像的目录
        auth users = index # 认证的用户名
        uid = index
        gid = index
        secrets file = /etc/rsyncd.secrets  # 认证文件名
        read only = yes # 只读

在/etc/目录下建立配置文件rsyncd.secrets。rsyncd.secrets是rsyncd的密码文件,保存有登录Linux系统的用户名和密码。

        #cat /etc/rsyncd.secrets


        index:abcde # 用户名index密码abcde

然后配置客户端。为了避免以交互的方式输入密码,大多使用密码文件。也可以将两台服务器生成密钥互相设为信任认证,这样做的麻烦是程序不通用,而且每两台服务器都需要生成证书。假设密码文件是rsyncd.secrets。

        #cat /home/index/rsyncd.secrets
        abcde

需要把密码文件改为只有所属人有权限。

        #chmod 600

把文件同步命令写到一个可执行的脚本:

        #cat index_rsyncd.sh
        #! /bin/bash
        rsync -tvzrp --progress --password-file=/home/index/rsyncd.secrets --delete
        --exclude /home/index/logs index@master::index  /home/index/

通过crontab设定,让这个脚本每10分钟运行一次。

        #echo "0,10,20,30,40,50  index_rsyncd.sh">>/etc/crontab

为了实现搜索从服务器和索引主服务器的索引同步,在索引主服务器上,为索引做阶段性的检查点。每分钟的时候,关闭IndexWriter,并且从Java中执行cp -lr index index.DATE命令,这里DATE是指当前时间。这样通过构建硬连接树,而不是制作完全的备份,就有效地制作了一个索引的备份。如果Lucene重写了任何文件(例如segments文件),将会创建新的inode,而原来的备份不变。

在每个搜索从服务器上,定期检查新的检查点。当发现一个新的index.DATE时,使用cp -lr index index.DATE准备一份备份,然后使用rsync-W -delete master:index.DATE index.DATE得到增量的索引改变,最后使用原子性的符号连接操作安装更新的索引 (ln -fsn index.DATE index)。

在从服务器上,当索引版本改变的时候将重新打开“index”。最好是在一个独立的线程中定时检查索引版本。当索引版本改变时,打开新的索引版本,执行一些热门查询操作,预加载Lucene缓存。然后在一个同步块中,替换在线服务的Searcher变量。

在主索引服务器的一个crontab中,定时移走最旧的检查点索引。

这样可以实现每分钟的同步,将主索引服务器上的mergeFactor设为2,以最小化产品中的segments数量。主服务器有一个热备份。

当增加文档到已经存在的索引库时,会生成新的.cfs文件。这个文件需要全部传输(而不是差量传输),因为文件名改了。所以,为了使增量更新更有效率,尽量不要让索引使用复合文件格式。