≫ 扩展

SphinxSE

SphinxSE 是一个 MySQL 存储引擎,可以通过 MySQL/MariaDB 服务器的可插拔架构进行编译。

尽管名字叫 SphinxSE,但它实际上并不存储任何数据。相反,它作为一个内置客户端,使 MySQL 服务器能够与 searchd 通信,执行搜索查询并检索搜索结果。所有的索引和搜索都在 MySQL 之外进行。

一些常见的 SphinxSE 应用包括:

  • 简化将 MySQL 全文搜索(FTS)应用移植到 Manticore 的过程;
  • 使 Manticore 能够与尚无原生 API 的编程语言一起使用;
  • 当需要在 MySQL 端进行额外的 Manticore 结果集处理时提供优化(例如,与原始文档表的 JOIN 或额外的 MySQL 端过滤)。

安装 SphinxSE

您需要获取 MySQL 源代码,准备好后重新编译 MySQL 二进制文件。MySQL 源代码(mysql-5.x.yy.tar.gz)可以从 http://dev.mysql.com 网站获取。

编译带有 SphinxSE 的 MySQL 5.0.x

  1. sphinx.5.0.yy.diff 补丁文件复制到 MySQL 源代码目录并运行
    $ patch -p1 < sphinx.5.0.yy.diff

    如果没有针对您需要构建的具体版本的 .diff 文件,请尝试应用版本号最接近的 .diff。补丁必须能够无拒绝地应用。

  2. 在 MySQL 源代码目录中,运行
    $ sh BUILD/autorun.sh
  3. 在 MySQL 源代码目录中,创建 sql/sphinx 目录,并将 Manticore 源代码中的 mysqlse 目录下的所有文件复制到该目录。例如:
    $ cp -R /root/builds/sphinx-0.9.7/mysqlse /root/builds/mysql-5.0.24/sql/sphinx
  4. 配置 MySQL 并启用新引擎:
    $ ./configure --with-sphinx-storage-engine
  5. 构建并安装 MySQL:
    $ make
    $ make install

编译带有 SphinxSE 的 MySQL 5.1.x

  1. 在 MySQL 源代码目录中,创建 storage/sphinx 目录,并将 Manticore 源代码中的 mysqlse 目录下的所有文件复制到该新位置。例如:
    $ cp -R /root/builds/sphinx-0.9.7/mysqlse /root/builds/mysql-5.1.14/storage/sphinx
  2. 在 MySQL 源代码目录中,运行:
    $ sh BUILD/autorun.sh
  3. 配置 MySQL 并启用 Manticore 引擎:
    $ ./configure --with-plugins=sphinx
  4. 构建并安装 MySQL:
    $ make
    $ make install

检查 SphinxSE 安装

要验证 SphinxSE 是否已成功编译到 MySQL 中,启动新构建的服务器,运行 MySQL 客户端,并执行 SHOW ENGINES 查询。您应该看到所有可用引擎的列表。Manticore 应该存在,且“Support”列应显示“YES”:

‹›
  • sql
sql
📋
mysql> show engines;
‹›
Response
+------------+----------+-------------------------------------------------------------+
| Engine     | Support  | Comment                                                     |
+------------+----------+-------------------------------------------------------------+
| MyISAM     | DEFAULT  | Default engine as of MySQL 3.23 with great performance      |
  ...
| SPHINX     | YES      | Manticore storage engine                                       |
  ...
+------------+----------+-------------------------------------------------------------+
13 rows in set (0.00 sec)

使用 SphinxSE

要使用 SphinxSE 进行搜索,您需要创建一个特殊的 ENGINE=SPHINX “搜索表”,然后使用带有全文查询的 SELECT 语句,将查询放在查询列的 WHERE 子句中。

以下是示例创建语句和搜索查询:

CREATE TABLE t1
(
    id          INTEGER UNSIGNED NOT NULL,
    weight      INTEGER NOT NULL,
    query       VARCHAR(3072) NOT NULL,
    group_id    INTEGER,
    INDEX(query)
) ENGINE=SPHINX CONNECTION="sphinx://localhost:9312/test";
SELECT * FROM t1 WHERE query='test it;mode=any';

在搜索表中,前三列 必须 具有以下类型:第 1 列(文档 ID)为 INTEGER UNSIGNEDBIGINT,第 2 列(匹配权重)为 INTEGERBIGINT,第 3 列(您的查询)为 VARCHARTEXT。此映射是固定的;您不能省略这三列中的任何一列,不能更改它们的位置或类型。此外,查询列必须被索引,而其他列应保持未索引。列名被忽略,因此您可以使用任意名称。

附加列必须是 INTEGERTIMESTAMPBIGINTVARCHARFLOAT。它们将按名称绑定到 Manticore 结果集中提供的属性,因此它们的名称必须与 sphinx.conf 中指定的属性名称匹配。如果 Manticore 搜索结果中没有匹配的属性名称,该列将包含 NULL 值。

特殊的“虚拟”属性名称也可以绑定到 SphinxSE 列。为此,请使用 _sph_ 替代 @。例如,要获取 @groupby@count@distinct 虚拟属性的值,分别使用 _sph_groupby_sph_count_sph_distinct 列名。

CONNECTION 字符串参数用于指定 Manticore 主机、端口和表。如果在 CREATE TABLE 中未指定连接字符串,则假定表名为 *(即搜索所有表)且为 localhost:9312。连接字符串语法如下:

CONNECTION="sphinx://HOST:PORT/TABLENAME"

您可以稍后更改默认连接字符串:

mysql> ALTER TABLE t1 CONNECTION="sphinx://NEWHOST:NEWPORT/NEWTABLENAME";

您也可以在每个查询中覆盖这些参数。

如示例所示,查询文本和搜索选项都应放在搜索查询列(即第 3 列)的 WHERE 子句中。选项用分号分隔,名称和值用等号分隔。可以指定任意数量的选项。可用选项包括:

  • query - 查询文本;
  • mode - 匹配模式。必须是 "all"、"any"、"phrase"、"boolean" 或 "extended" 之一。默认是 "all";
  • sort - 匹配排序模式。必须是 "relevance"、"attr_desc"、"attr_asc"、"time_segments" 或 "extended" 之一。除 "relevance" 外,其他模式后面还需要冒号后的属性名(或 "extended" 的排序子句):
    ... WHERE query='test;sort=attr_asc:group_id';
    ... WHERE query='test;sort=extended:@weight desc, group_id asc';
  • offset - 结果集偏移量;默认是 0;
  • limit - 从结果集中检索的匹配数;默认是 20;
  • index - 要搜索的表名:
    ... WHERE query='test;index=test1;';
    ... WHERE query='test;index=test1,test2,test3;';
  • minid, maxid - 匹配的最小和最大文档 ID;
  • weights - 赋予 Manticore 全文字段的权重的逗号分隔列表:
    ... WHERE query='test;weights=1,2,3;';
  • filter, !filter - 逗号分隔的属性名和要匹配的值集合:
    # only include groups 1, 5 and 19
    ... WHERE query='test;filter=group_id,1,5,19;';
    # exclude groups 3 and 11
    ... WHERE query='test;!filter=group_id,3,11;';
  • range, !range - 逗号分隔的(整数或 bigint)Manticore 属性名,以及要匹配的最小和最大值:
    # include groups from 3 to 7, inclusive
    ... WHERE query='test;range=group_id,3,7;';
    # exclude groups from 5 to 25
    ... WHERE query='test;!range=group_id,5,25;';
  • floatrange, !floatrange - 逗号分隔的(浮点数)Manticore 属性名,以及要匹配的最小和最大值:
    # filter by a float size
    ... WHERE query='test;floatrange=size,2,3;';
    # pick all results within 1000 meter from geoanchor
    ... WHERE query='test;floatrange=@geodist,0,1000;';
  • maxmatches - 每查询最大匹配数值,如 max_matches 搜索选项
    ... WHERE query='test;maxmatches=2000;';
  • cutoff - 最大允许匹配数,如 cutoff 搜索选项
    ... WHERE query='test;cutoff=10000;';
  • maxquerytime - 最大允许的查询时间(以毫秒为单位),如同 max_query_time 搜索选项
    ... WHERE query='test;maxquerytime=1000;';
  • groupby - 分组函数和属性。请阅读 此处 了解分组搜索结果:
    ... WHERE query='test;groupby=day:published_ts;';
    ... WHERE query='test;groupby=attr:group_id;';
  • groupsort - 分组排序子句:
    ... WHERE query='test;groupsort=@count desc;';
  • distinct - 在进行分组时计算 COUNT(DISTINCT) 的属性:
    ... WHERE query='test;groupby=attr:country_id;distinct=site_id';
  • indexweights - 用逗号分隔的表名和权重列表,用于在多个表中搜索时使用:
    ... WHERE query='test;indexweights=tbl_exact,2,tbl_stemmed,1;';
  • fieldweights - 用逗号分隔的每字段权重列表,可被排序器使用:
    ... WHERE query='test;fieldweights=title,10,abstract,3,content,1;';
  • comment - 用于在查询日志中标记此查询的字符串,如同 comment 搜索选项
    ... WHERE query='test;comment=marker001;';
  • select - 用于计算的表达式字符串:
    ... WHERE query='test;select=2*a+3*** as myexpr;';
  • host, port - 远程 searchd 主机名和 TCP 端口,分别为:
    ... WHERE query='test;host=sphinx-test.loc;port=7312;';
  • ranker - 用于“扩展”匹配模式的排名函数,如同 ranker。已知值包括 "proximity_bm25"、"bm25"、"none"、"wordcount"、"proximity"、"matchany"、"fieldmask"、"sph04"、"expr:EXPRESSION" 语法以支持基于表达式的排名器(其中 EXPRESSION 应替换为您的具体排名公式),以及 "export:EXPRESSION":
    ... WHERE query='test;mode=extended;ranker=bm25;';
    ... WHERE query='test;mode=extended;ranker=expr:sum(lcs);';

    “export”排名器的功能类似于 ranker=expr,但它保留每个文档的因子值,而 ranker=expr 在计算最终的 WEIGHT() 值后会丢弃它们。请注意,ranker=export 旨在偶尔使用,例如训练机器学习(ML)函数或手动定义您自己的排名函数,不应在实际生产中使用。使用此排名器时,您可能希望检查 RANKFACTORS() 函数的输出,该函数生成包含每个文档所有字段级因子的字符串。

‹›
  • sql
sql
📋
SELECT *, WEIGHT(), RANKFACTORS()
    FROM myindex
    WHERE MATCH('dog')
    OPTION ranker=export('100*bm25');
‹›
Response
*************************** 1\. row ***************************
           id: 555617
    published: 1110067331
   channel_id: 1059819
        title: 7
      content: 428
     weight(): 69900
rankfactors(): bm25=699, bm25a=0.666478, field_mask=2,
doc_word_count=1, field1=(lcs=1, hit_count=4, word_count=1,
tf_idf=1.038127, min_idf=0.259532, max_idf=0.259532, sum_idf=0.259532,
min_hit_pos=120, min_best_span_pos=120, exact_hit=0,
max_window_hits=1), word1=(tf=4, idf=0.259532)
*************************** 2\. row ***************************
           id: 555313
    published: 1108438365
   channel_id: 1058561
        title: 8
      content: 249
     weight(): 68500
rankfactors(): bm25=685, bm25a=0.675213, field_mask=3,
doc_word_count=1, field0=(lcs=1, hit_count=1, word_count=1,
tf_idf=0.259532, min_idf=0.259532, max_idf=0.259532, sum_idf=0.259532,
min_hit_pos=8, min_best_span_pos=8, exact_hit=0, max_window_hits=1),
field1=(lcs=1, hit_count=2, word_count=1, tf_idf=0.519063,
min_idf=0.259532, max_idf=0.259532, sum_idf=0.259532, min_hit_pos=36,
min_best_span_pos=36, exact_hit=0, max_window_hits=1), word1=(tf=3,
idf=0.259532)
  • geoanchor - 地理距离锚点。更多关于地理搜索的信息请参见 本节。接受4个参数,分别是纬度和经度属性名,以及锚点坐标:
    ... WHERE query='test;geoanchor=latattr,lonattr,0.123,0.456';

一个非常重要的注意事项是,让 Manticore 处理排序、过滤和结果集切片要高效得多,而不是增加最大匹配数并在 MySQL 端使用 WHEREORDER BYLIMIT 子句。这有两个原因。首先,Manticore 采用多种优化,执行这些任务比 MySQL 更好。其次,searchd 需要打包、传输和 SphinxSE 需要解包的数据量更少。

使用 SphinxSE 时关于存储字段的重要说明

从版本 5.0.0 起,Manticore 默认存储所有字段。当 Manticore 与 MySQL 或 MariaDB 通过 SphinxSE 一起使用时,通常不需要存储所有字段,因为原始数据已经存储在 MySQL/MariaDB 中。在这种设置中,建议通过设置显式禁用相关 Manticore 表的存储字段:

stored_fields =

请参阅设置参考:stored_fields

如果保持默认(存储所有字段),然后通过 SphinxSE 一次选择大量文档,可能会超过引擎的内部限制,并收到类似以下错误:

"bad searchd response length"

设置 stored_fields = 可以避免将大量存储的负载发送回 MySQL/MariaDB,并防止在典型的 SphinxSE 集成中出现此错误。

SHOW ENGINE SPHINX STATUS

您可以使用 SHOW ENGINE SPHINX STATUS 语句获取与查询结果相关的更多信息:

‹›
  • sql
sql
📋
mysql> SHOW ENGINE SPHINX STATUS;
‹›
Response
+--------+-------+-------------------------------------------------+
| Type   | Name  | Status                                          |
+--------+-------+-------------------------------------------------+
| SPHINX | stats | total: 25, total found: 25, time: 126, words: 2 |
| SPHINX | words | sphinx:591:1256 soft:11076:15945                |
+--------+-------+-------------------------------------------------+
2 rows in set (0.00 sec)

您也可以通过状态变量访问此信息。请注意,使用此方法不需要超级用户权限。

‹›
  • sql
sql
📋
mysql> SHOW STATUS LIKE 'sphinx_%';
‹›
Response
+--------------------+----------------------------------+
| Variable_name      | Value                            |
+--------------------+----------------------------------+
| sphinx_total       | 25                               |
| sphinx_total_found | 25                               |
| sphinx_time        | 126                              |
| sphinx_word_count  | 2                                |
| sphinx_words       | sphinx:591:1256 soft:11076:15945 |
+--------------------+----------------------------------+
5 rows in set (0.00 sec)

SphinxSE 搜索表可以与使用其他引擎的表进行连接。以下是使用 example.sql 中的 "documents" 表的示例:

‹›
  • sql
sql
📋
mysql> SELECT content, date_added FROM test.documents docs
-> JOIN t1 ON (docs.id=t1.id)
-> WHERE query="one document;mode=any";
mysql> SHOW ENGINE SPHINX STATUS;
‹›
Response
+-------------------------------------+---------------------+
| content                             | docdate             |
+-------------------------------------+---------------------+
| this is my test document number two | 2006-06-17 14:04:28 |
| this is my test document number one | 2006-06-17 14:04:28 |
+-------------------------------------+---------------------+
2 rows in set (0.00 sec)
+--------+-------+---------------------------------------------+
| Type   | Name  | Status                                      |
+--------+-------+---------------------------------------------+
| SPHINX | stats | total: 2, total found: 2, time: 0, words: 2 |
| SPHINX | words | one:1:2 document:2:2                        |
+--------+-------+---------------------------------------------+
2 rows in set (0.00 sec)

通过 MySQL 构建摘要

SphinxSE 还提供了一个 UDF 函数,允许您使用 MySQL 创建摘要。此功能类似于 HIGHLIGHT(),但可以通过 MySQL+SphinxSE 访问。

提供 UDF 的二进制文件名为 sphinx.so,应自动构建并安装在与 SphinxSE 一起的适当位置。如果由于某种原因未自动安装,请在构建目录中找到 sphinx.so 并复制到您的 MySQL 实例的插件目录。完成后,使用以下语句注册 UDF:

CREATE FUNCTION sphinx_snippets RETURNS STRING SONAME 'sphinx.so';

函数名必须是 sphinx_snippets;不能使用任意名称。函数参数如下:

原型: function sphinx_snippets ( document, table, words [, options] );

document 和 words 参数可以是字符串或表列。选项必须像这样指定:'value' AS option_name。有关支持的选项列表,请参阅 高亮部分。唯一的 UDF 特定附加选项称为 sphinx,允许您指定 searchd 的位置(主机和端口)。

使用示例:

SELECT sphinx_snippets('hello world doc', 'main', 'world',
    'sphinx://192.168.1.1/' AS sphinx, true AS exact_phrase,
    '[**]' AS before_match, '[/**]' AS after_match)
FROM documents;
SELECT title, sphinx_snippets(text, 'table', 'mysql php') AS text
    FROM sphinx, documents
    WHERE query='mysql php' AND sphinx.id=documents.id;
Last modified: October 31, 2025