最近比较忙,今天抽空完善了一下。

What/Sphinx是什么?
定义:Sphinx是一个全文检索引擎。

Why/为什么使用Sphinx?
遇到一个类似这样的需求:用户可以通过文章标题和文章搜索到一片文章的内容,而文章的标题和文章的内容分别保存在不同的库,而且是跨机房的这种类似的例子。
sphinx 官网下载地址(这里根据自己的情况选择相应的版本下载即可)
这里我下载的是sphinx-3.1.1-release-win64.zip,将下载的文件解压,解压后将文件夹重命名为sphinx(方便后续操作,目录结构如下图所示) sphinx 目录结构
1.png

如果没有data和log目录自己创建一下即可。
将上一步得到的sphinx文件夹剪切或复制到某个磁盘下,这里我放到E:\PRO\2\下

将E:\PRO\2\sphinx\etc\sphinx-min.conf.dist复制到E:\PRO\2\sphinx\bin\下,并重命名为sphinx.conf;

修改E:\PRO\2\sphinx\bin\sphinx.conf文件的数据库配置,这里根据自己的情况进行配置即可(如下图) bin 目录

2.png

Minimal Sphinx configuration sample (clean, simple, functional)

主索引数据原定义

source article_main
{
    type            = mysql                         #数据库类型
    sql_host        = localhost                     #数据库地址
    sql_user        = root                          #用户名
    sql_pass        = 123456                        #密码
    sql_db          = test                          #数据库名

    sql_port        = 3306                          #端口
    sql_query_pre   = SET NAMES utf8                #编码
    sql_query_pre   = REPLACE INTO sph_counter SELECT 1, MAX(id) FROM sphinx_article      #获取数据源表最大的主键id 插入到sph_counter表做标记

    #使用多次查询,那么这个多次查询就需要有个范围和步长,sql_query_range和sql_range_step就是做这个使用的。
    sql_query_range =
    sql_range_step  = 10000
    sql_query       = SELECT * FROM sphinx_article WHERE id <= (SELECT max_doc_id FROM sph_counter WHERE counter_id = 1)
    #sql_attr_uint      = cat_id
    #sql_attr_uint      = member_id
    sql_attr_timestamp  = created                                    #从SQL读取到的值必须为整数,作为时间属性
    #sql_query_info_pre  = SET NAMES utf8                             #命令行查询时,设置正确的字符集
    #sql_query_post      = SELECT * FROM sphinx_article WHERE id=$id  #命令行查询时,从数据库读取原始数据信息
}

# 增量索引数据原定义
source article_delta :article_main
{
    sql_query_pre = SET NAMES utf8
    sql_query_range =
    sql_range_step  = 10000
    sql_query       = select * from sphinx_article where id > (select max_doc_id from sph_counter where counter_id = 1)

    sql_attr_timestamp  = created
    sql_query_post  = UPDATE sph_counter  SET max_doc_id=(SELECT MAX(id) FROM sphinx_article) where counter_id=1

    #sql_query_info_pre      = SET NAMES utf8
    #sql_query = SELECT * FROM article_info WHERE id=$id
}

# 主索引index定义
index article_main
{
    source          = article_main                    #对应的source名称
    path            = E:/PRO/2/sphinx/data/article_main
    mlock           = 0
    morphology        = none
    min_word_len        = 1
    html_strip                = 0
    ngram_len     = 1
    ngram_chars = U+4E00..U+9FBB, U+3400..U+4DB5, U+20000..U+2A6D6, U+FA0E, U+FA0F, U+FA11, U+FA13, U+FA14, U+FA1F, U+FA21, U+FA23, U+FA24, U+FA27, U+FA28, U+FA29, U+3105..U+312C, U+31A0..U+31B7, U+3041, U+3043, U+3045, U+3047, U+3049, U+304B, U+304D, U+304F, U+3051, U+3053, U+3055, U+3057, U+3059, U+305B, U+305D, U+305F, U+3061, U+3063, U+3066, U+3068, U+306A..U+306F, U+3072, U+3075, U+3078, U+307B, U+307E..U+3083, U+3085, U+3087, U+3089..U+308E, U+3090..U+3093, U+30A1, U+30A3, U+30A5, U+30A7, U+30A9, U+30AD, U+30AF, U+30B3, U+30B5, U+30BB, U+30BD, U+30BF, U+30C1, U+30C3, U+30C4, U+30C6, U+30CA, U+30CB, U+30CD, U+30CE, U+30DE, U+30DF, U+30E1, U+30E2, U+30E3, U+30E5, U+30E7, U+30EE, U+30F0..U+30F3, U+30F5, U+30F6, U+31F0, U+31F1, U+31F2, U+31F3, U+31F4, U+31F5, U+31F6, U+31F7, U+31F8, U+31F9, U+31FA, U+31FB, U+31FC, U+31FD, U+31FE, U+31FF, U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9, U+A000..U+A48C, U+A492..U+A4C6
}

# 增量索引index定义
index article_delta:article_main
{
    source          = article_delta
    path            = E:/PRO/2/sphinx/data/article_delta
    mlock           = 0
    morphology        = none
    min_word_len        = 1
    html_strip                = 0
    ngram_len     = 1
    ngram_chars = U+4E00..U+9FBB, U+3400..U+4DB5, U+20000..U+2A6D6, U+FA0E, U+FA0F, U+FA11, U+FA13, U+FA14, U+FA1F, U+FA21, U+FA23, U+FA24, U+FA27, U+FA28, U+FA29, U+3105..U+312C, U+31A0..U+31B7, U+3041, U+3043, U+3045, U+3047, U+3049, U+304B, U+304D, U+304F, U+3051, U+3053, U+3055, U+3057, U+3059, U+305B, U+305D, U+305F, U+3061, U+3063, U+3066, U+3068, U+306A..U+306F, U+3072, U+3075, U+3078, U+307B, U+307E..U+3083, U+3085, U+3087, U+3089..U+308E, U+3090..U+3093, U+30A1, U+30A3, U+30A5, U+30A7, U+30A9, U+30AD, U+30AF, U+30B3, U+30B5, U+30BB, U+30BD, U+30BF, U+30C1, U+30C3, U+30C4, U+30C6, U+30CA, U+30CB, U+30CD, U+30CE, U+30DE, U+30DF, U+30E1, U+30E2, U+30E3, U+30E5, U+30E7, U+30EE, U+30F0..U+30F3, U+30F5, U+30F6, U+31F0, U+31F1, U+31F2, U+31F3, U+31F4, U+31F5, U+31F6, U+31F7, U+31F8, U+31F9, U+31FA, U+31FB, U+31FC, U+31FD, U+31FE, U+31FF, U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9, U+A000..U+A48C, U+A492..U+A4C6
}

indexer
{
    mem_limit       = 128M
}

searchd
{
    listen          = 9312
    listen          = 9306:mysql41
    log         = E:/PRO/2/sphinx/log/searchd.log
    query_log       = E:/PRO/2/sphinx/log/query.log
    read_timeout        = 5
    max_children        = 30
    pid_file        = E:/PRO/2/sphinx/log/searchd.pid
    seamless_rotate     = 1
    preopen_indexes     = 1
    unlink_old      = 1
    workers         = threads # for RT to work
    binlog_path     = E:/PRO/2/sphinx/log
}

然后建立数据主表,并插入一些数据作为搜索
CREATE TABLE sphinx_article (
id int(11) NOT NULL AUTO_INCREMENT,
title varchar(255) CHARACTER SET utf8 DEFAULT '',
cat_id tinyint(1) unsigned zerofill NOT NULL DEFAULT '0',
member_id int(11) unsigned NOT NULL,
content longtext CHARACTER SET utf8 NOT NULL,
created int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=139 DEFAULT CHARSET=latin1;
插入的数据如图: 主表数据如图
3.png
建立存储主表的最大id表,用于添加数据时sphinx的更新索引文件
CREATE TABLE sph_counter (
counter_id int(11) NOT NULL COMMENT '标识不同的数据表',
max_doc_id int(11) NOT NULL COMMENT '每个索引表的最大ID,会实时更新',
PRIMARY KEY (counter_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
接下来我们打开cmd窗口,进入目录E:PRO2sphinxbin 建立主索引,执行

E:PRO2sphinxbinindexer.exe -c E:PRO2sphinxbinsphinx.conf article_main
建立增量索引

E:PRO2sphinxbinindexer.exe -c E:PRO2sphinxbinsphinx.conf article_delta
如果运行上述命令行有错误,则根据错误提示去改动配置文件即可。

这时候你可以去看一下E:PRO2sphinxbindata目录里面已经生成了索引文件(如下图所示,索引文件的名字对应你sphinx.conf中主索引index定义path的article_main)
索引文件生成
4.png
现在启动表示sphinx服务
E:/PRO/2/sphinx/bin/searchd.exe --config E:/PRO/2/sphinx/bin/sphinx.conf
启动成功如图所示(有错误就根据错误提示去更改配置文件): sphinx 启动成功如图
5.png
下面我们去完善搜索界面,前端代码

test.php

<?php
 header('Content-type:text/html;charset=utf-8');
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>sphinx test</title>
</head>
<body>
<form name="form" method="get" action="index.php">
    <label>
        <input style="width: 400px;" type="text" name="keyword">
    </label>
    <label>
        <input type="submit" name="Submit" value="sphinx搜索">
    </label>
</form>
</body>
</html>

前端界面如下:
6.png

index.php文件

<?php
/**
 * Created by ZhengNiu.
 * User: 77103
 * Date: 2019/7/4
 * Time: 16:32
 */
header("Content-type:text/html;charset=utf-8;");
if (empty($_GET)) {
    header("Location:test.php");
}

$keyword = $_GET['keyword'];
if (trim($keyword) == '') {
    exit('请输入关键词');
}

require_once './sphinx/api/sphinxapi.php';

$cl = new SphinxClient();

$cl->SetServer('localhost',9312);

$res = $cl->Query($keyword, 'article_main');
$data = [];
if (isset($res['matches']) && !empty($res['matches'])) {
    $ids = implode(',', array_keys($res['matches']));
    $m = new Mysqli('localhost','root','123456','test');
    if ($m->connect_errno) {
        exit($m->connect_error);
    }
    $dt = $m->query("SELECT id,title,cat_id,member_id,content,created FROM sphinx_article WHERE id IN" .'('. $ids .')');
    while ($row = $dt->fetch_assoc()) {
        $data[] = $row;
    }
}
echo "<pre>";
print_r($data);

搜索结果:
7.png

还有一个问题未解决,如果我更新了或者添加了数据库记录,我每次都要运行一下重新生成索引文件???这样是不是太不灵活了。我们可以写一个sphinx.bat脚本,加入到windows 的计划任务中,这样就可以了。

添加数据库内容时更新索引文件原理:

1.新建一张表,记录一下上一次已经创建好索引的最后一条记录的ID
2.当索引时,然后从数据库中取出所有ID大于上面那个sphinx中的那个ID的数据, 这些就是新的数据,然后创建一个小的索引文件
3.把上边我们创建的增量索引文件合并到主索引文件上去
4.把最后一条记录的ID更新到第一步创建的表中
sphinx.bat 脚本内容

E:PRO2sphinxbinindexer.exe -c E:PRO2sphinxbinsphinx.conf article_main

E:PRO2sphinxbinindexer.exe -c E:PRO2sphinxbinsphinx.conf --merge article_main article_delta --rotate
加入到windows 任务计划启动即可(需要优化bat脚本隐藏执行)
加入计划任务的步骤:
计划任务
8.png

创建任务
9.png

为任务填写一个名称 任务名称

13.png

触发任务的条件

10.png

选择你要执行的bat脚本

16.png

完成查看列表
12.png

现在我们向数据库添加一些数据,再添加之前,我们先去看一下数据库的数据
sphinx_article 表 最大的id是138
sph_counter 表 记录的max_doc_id也是138
如图: sphinx_article
17.png

sph_counter表

18.png

下面我们运行插入一些数据

$m = new Mysqli('localhost','root','123456','test');
for ($i = 138; $i <= 140; $i++) {
    $add = [
        'id' => $i,
        'title' => "我爱吃肉肉$i",
        'cat_id' => rand(1, 99),
        'member_id' => rand(1, 999),
        'content' => uniqid(),
        'created' => time()
    ];
    $key = implode(",", array_keys($add));
    $val = "'" . implode("','", array_values($add)) . "'";
    $sql = "INSERT INTO `sphinx_article` ({$key}) VALUES ({$val})";
    $m->query($sql);
}
$m->close();die;

这时候查看
sphinx_article 表 最大的id是140
sph_counter 表 记录的max_doc_id是138
我们的任务任务是每分钟一次。一分钟后我们去检索一下数据,看看可以查到我们新插入的两条数据不。

19.png

结果: 完成

21.png

到这里也就结束了。
源码地址: 链接:https://pan.baidu.com/s/1ai5Xlze98FX3RQefbcLspg 提取码:s6zy 复制这段内容后打开百度网盘手机App,操作更方便哦

关注友儿不迷路

Last modification:May 21st, 2020 at 01:42 pm
如果觉得我的文章对你有用,请随意赞赏