萬字詳解PHP+Sphinx中文億級資料全文檢索實戰(實測億級資料0.1秒搜尋耗時)

小松聊PHP进阶發表於2024-03-29

Sphinx查詢效能非常厲害,億級資料下輸入關鍵字,大部分能在0.01~0.1秒,少部分再5秒之內查出資料。

Sphinx

  • 官方文件:http://sphinxsearch.com/docs/sphinx3.html
  • 極簡概括:
    由C++編寫的高效能全文搜尋引擎的開源元件,C/S架構,跨平臺(支援Linux、Windows、MacOS),支援分散式部署,並可直接適配MySQL。
  • 解決問題:
    因為MySQL的 like %keyword% 不走索引,且全文索引不支援中文,所以需要藉助其它元件。適用於不經常更新的資料的全文搜尋。
  • 同類產品:
    ElasticSearch、Solr、Lucene、Algolia、XunSearch。
  • 使用思路:
    傳送給Sphinx關鍵字,然後Sphinx返回id給PHP,PHP再呼叫MySQL根據id查詢。也就是幫著MySQL找id的,MySQL走主鍵索引,查詢效能很高。
  • API支援:
    Sphinx附帶了三種不同的API,SphinxAPI、SphinxSE和SphinxQL。SphinxAPI是一個可用於Java、PHP、Python、Perl、C和其他語言的原生庫。SphinxSE是MySQL的一個可插拔儲存引擎,支援將大量結果集直接傳送到MySQL伺服器進行後期處理。SphinxQL允許應用程式使用標準MySQL客戶端庫和查詢語法查詢Sphinx。
  • SQL擴充套件
    Sphinx不僅限於關鍵字搜尋。在全文搜尋結果集之上,您可以計算任意算術表示式、新增WHERE條件、排序依據、分組依據、使用最小值/最大值/AVG值/總和、聚合等。本質上,支援成熟的SQL SELECT。

建表

CREATE TABLE `articles` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(16000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=320974 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

億級大資料來源準備

  1. 決策分析:
    億級的資料量,還都得是中文,如果是52個大小寫字母和10個數字可以隨機,中文隨機效果差,最好是有能閱讀通暢的資料來源,而不是隨機漢字資料來源。
  2. 嘗試尋找:
    嘗試看了txt版的《三國演義》,發現行數太少,所以轉變思維,更大的資料量,只能是長篇小說,翻看了最長的小說,也才22萬行,1行小說對應1條MySQL資料,資料量還是太少,雖然迴圈小說內容插入也行,但還是差點意思。
  3. 資源整合:
    程式設計師天天玩的就是資料,這點小事難不倒我,去網盤找txt小說資源合集https://www.aliyundrive.com/s/LBBg4ZvWip2/folder/63a138e95845afd3baa947db96342937033c254f
    找到了如下35000本txt的小說。
  4. 資料合併:
    下載了幾千個txt小說,需要用命令把這些合併成一個檔案,使用cat * > all.txt方便PHP程式逐行讀取。整合後的單個txt檔案9.57個G。
  5. 資料入庫:
    大資料檔案一次載入會把記憶體撐爆,所以需要使用yield 生成(迭代)器去逐行讀取檔案。
    set_time_limit(0);
    $file = 'C:/Users/Administrator/Desktop/all.txt';
    /**
     * @function 逐行讀取大檔案
     * @param    $file_name string 檔名
     * @return   Generator|object
     */
    function readLargeFile($file_name) {
        $file = fopen($file_name, 'rb');
        if (! $file) {
            return false;
        }

        while (! feof($file)) {
            $line = fgets($file);
            if ($line !== false) {
                yield $line;
            }
        }

        fclose($file);
    }

// 使用生成器逐行讀取大檔案
    $file_resource = readLargeFile($file);
    if(! $file_resource) {
        echo '檔案讀取錯誤';
        die;
    }

    $db = \Illuminate\Support\Facades\DB::table('articles');
    $arr = [];
    foreach ($file_resource as $line) {
        //獲取單行編碼
        $from_charset = mb_detect_encoding($line, 'UTF-8, GBK, GB2312, BIG5, CP936, ASCII');
        //轉碼操作,因為有些是ANSI,GBK的編碼,最好轉換成UTF-8
        $utf8_str     = @iconv($from_charset, 'UTF-8', $line);
        //遇見空行,直接過濾
        if(in_array($utf8_str, ["\n", "\r", "\n\r", "\r\n"])) {
            continue;
        }
        
        $arr[] = ['content' => $utf8_str];
        if(count($arr) >= 10000) {
            $db->insert($arr);
            $arr = [];
        }
    }
  1. 效能調優:
    MyISAM引擎:
    insert 發現1秒才1400行的插入速度,不行得調整。
    改為批次插入,一次性插入10000行資料,並修改/etc/my.cnf 裡面的max_allowed_packet,將1M改為512M
    最佳化後,每秒2萬的插入速度。

    如果是InnoDB引擎
    可以調整redo log 刷盤策略,set global innodb_flush_log_at_trx_commit=0
    並新增緩衝池innodb_buffer_pool_size大小為系統總記憶體的70%
    把max_allowed_packet,將1M改為512M,就行了。

  2. 數量檢查:
    兩萬年後,用SELECT count(*) FROM articles一看,資料量109 450 000,搞定。

  3. 效能測試:
    SELECT count(*) FROM articles where content like '%晴空萬里%',找出來了1931個,花了242秒。

編譯安裝並配置Sphinx Server

虛擬機器裡的CentOS,記得要足夠的空間,用來儲存資料
一個億的資料佔了系統很大空間,系統儲存空間所剩無幾了,/usr/local地方沒地方存,所以把Sphinx放到了/home下,因為/dev/mapper/centos-home掛載到了/home下。

可以事先安裝mmsge
wget https://files.cnblogs.com/files/JesseLucky/coreseek-4.1-beta.tar.gz
tar zxf coreseek-4.1-beta.tar.gz
cd /home/coreseek-4.1-beta/mmseg-3.2.14
./bootstrap
./configure --prefix=/home/mmseg
make && make install

然後安裝Sphinx
cd /home/coreseek-4.1-beta/csft-4.1
./buildconf.sh

發現報錯,不著急
automake: warnings are treated as errors
/usr/share/automake-1.13/am/library.am: warning: 'libstemmer.a': linking libraries using a non-POSIX
/usr/share/automake-1.13/am/library.am: archiver requires 'AM_PROG_AR' in 'configure.ac'
libstemmer_c/Makefile.am:2:   while processing library 'libstemmer.a'
/usr/share/automake-1.13/am/library.am: warning: 'libsphinx.a': linking libraries using a non-POSIX
/usr/share/automake-1.13/am/library.am: archiver requires 'AM_PROG_AR' in 'configure.ac'
src/Makefile.am:14:   while processing library 'libsphinx.a'

vim ./configure.ac +62
再AC_PROG_RANLIB的下方新增AM_PROG_AR

再次執行./buildconf.sh
發現有如下報錯:
configure.ac:231: the top level
configure.ac:62: error: required file 'config/ar-lib' not found
configure.ac:62:   'automake --add-missing' can install 'ar-lib'

vim buildconf.sh +5
把--foreign改成--add-missing

再次執行./buildconf.sh
直到configure檔案出現。

./configure --prefix=/home/coreseek --with-mysql=/usr/local/mysql --with-mmseg=/home/mmseg --with-mmseg-includes=/home/mmseg/include/mmseg/ --with-mmseg-libs=/home/mmseg/lib/

make && make install
若發現報錯:有ExprEval字樣
vim /home/coreseek-4.1-beta/csft-4.1/src/sphinxexpr.cpp
將所有的
T val = ExprEval ( this->m_pArg, tMatch );
替換為
T val = this->ExprEval ( this->m_pArg, tMatch ); 

再次執行make,發現還報錯
/home/coreseek-4.1-beta/csft-4.1/src/sphinx.cpp:22292:對‘libiconv_open’未定義的引用
/home/coreseek-4.1-beta/csft-4.1/src/sphinx.cpp:22310:對‘libiconv’未定義的引用
/home/coreseek-4.1-beta/csft-4.1/src/sphinx.cpp:22316:對‘libiconv_close’未定義的引用

vim /home/coreseek-4.1-beta/csft-4.1/src/Makefile +249
把
LIBS = -ldl -lm -lz -lexpat  -L/usr/local/lib -lrt  -lpthread
改成  
LIBS = -ldl -lm -lz -lexpat -liconv -L/usr/local/lib -lrt  -lpthread

make && make install
成功安裝

裝好之後配置它,漢字是提示,記得執行環境要刪掉

cd /home/coreseek/etc
記得這個名字一定得是csft.conf,換成其它的也行,但是到後期增量索引合併時會報錯
cp sphinx-min.conf.dist csft.conf
配置內容就是有漢字的地方
vim csft.conf
source articles 起個名,叫articles
{
        type                    = mysql
        sql_host                = 資料庫IP
        sql_user                = 資料庫使用者名稱
        sql_pass                = 資料庫密碼
        sql_db                  = 資料庫
        sql_port                = 3306  埠
        sql_query_pre           = select names utf8mb4 加上這行
        sql_query               = select id,content from articles Sphinx建索引的資料來源

        sql_attr_uint           = group_id   這行用不上可以去掉
        sql_attr_timestamp      = date_added 這行用不上可以去掉

        sql_query_info_pre      = select names utf8mb4 加上這行
        sql_query_info          = select id,content from articles where id=$id 用Sphinx做什麼SQL的查詢邏輯
        #sql_query_post          = update sphinx_index_record set max_id = (select max(id) from articles) where table_name = 'articles'; 這個是增量索引要用的東西,這裡暫時用不上,後文會講。
}


index articles 起個名,叫articles
{
        source                  = articles                         名字與source一致
        path                    = /home/coreseek/var/data/articles 索引路徑
        docinfo                 = extern                           這行用不上可以去掉
        charset_dictpath        = /home/mmseg/etc                  新增這行,表示分詞讀取詞典檔案的位置
        charset_type            = zh_cn.utf-8                      設定字符集編碼
}

儲存並退出,注意資料來源要與索引是一對一的。

然後留意兩個檔案
/home/coreseek/bin/indexer 是用來建立索引的
/home/coreseek/bin/searchd 是啟動服務端程序的。

開啟服務

開啟服務
/home/coreseek/bin/searchd -c /home/coreseek/etc/csft.conf
> ps aux | grep search
root      83205  0.0  0.0  47732  1048 ?        S    02:01   0:00 /home/coreseek/bin/searchd -c /home/coreseek/etc/csft.conf
root      83206  0.5  0.0 116968  3728 ?        Sl   02:01   0:00 /home/coreseek/bin/searchd -c /home/coreseek/etc/csft.conf

9312埠
> netstat -nlp | grep 9312
tcp        0      0 0.0.0.0:9312            0.0.0.0:*               LISTEN      83206/searchd

配置檔案引數說明

https://sphinxsearch.com/docs/current.html#conf-mem-limit
indexer段-----------------------------
mem_limit這是建立索引時所需記憶體,預設32M,可以適當調大,格式如下
mem_limit = 256M
mem_limit = 262144K # same, but in KB
mem_limit = 268435456 # same, but in bytes



https://sphinxsearch.com/docs/current.html#conf-query-log
在searchd段-----------------------------
listen 9312
表示sphinx預設埠
listen=9306
mysql41,表示Sphinx伺服器將監聽在 9306 埠,並使用 MySQL 4.1 協議與客戶端進行通訊。

可以註釋掉以下兩行,使其不記錄日誌
#log                    = /home/coreseek/var/log/searchd.log
#query_log              = /home/coreseek/var/log/query.log

read_timeout
網路客戶端請求讀取超時,以秒為單位。可選,預設為5秒。Searchd將強制關閉未能在此超時時間內傳送查詢的客戶端連線。
max_children
併發搜尋數量,設定為0,表示不限制。
max_matches
引數用於設定每次搜尋操作返回的最大匹配項數

seamless_rotate
在 Sphinx 伺服器配置檔案中,seamless_rotate 引數用於控制索引旋轉操作的行為,確保索引更新過程中的查詢不會被中斷。“旋轉”(rotate)是指在更新索引資料時,將舊的索引檔案替換為新生成的索引檔案的過程。
當設定為 1 或 true(預設值),seamless_rotate 使得 Sphinx 能夠在後臺載入新的索引資料,同時繼續使用舊的索引資料處理查詢請求,直到新的索引完全載入並準備好被使用。這個過程完成後,新的索引將無縫地接管,替換舊的索引,而不會干擾或中斷正在進行的搜尋操作。
如果將 seamless_rotate 設定為 0 或 false,則在索引旋轉時,Sphinx 會暫停處理新的查詢請求,直到新的索引檔案完全載入並準備就緒。這可能會導致短暫的服務中斷,因此,除非有特殊需求,一般建議保持 seamless_rotate 的預設設定(即啟用無縫旋轉),以確保索引更新過程中搜尋服務的連續性和穩定性。

preopen_indexes
是否在啟動時強制預開啟所有索引。可選,預設為1

unlink_old
用於控制在索引旋轉時是否刪除舊的索引檔案。當設定為 1 或 true 時(預設值),舊的索引檔案將被刪除,保持預設值就好。

客戶端配置

PHP SphinxClient手冊:http://docs.php.net/manual/tw/class.sphinxclient.php

cp /home/coreseek-4.1-beta/testpack/api/*.php /test
vim /test/sphinxapi.php
把sphinxapi裡面的SphinxClient()方法改為__construct(),防止稍高版本的PHP,產生類名和方法名一致的通知。

可以先測試,只要返回陣列,就Server和Client都能正常工作。
vim /test/test_coreseek.php
把裡面的網路搜尋改成晴空萬里,把SPH_MATCH_ANY改成SPH_MATCH_PHRASE。
sphinx檔案有個limit選項,這個預設是20,所以最多顯示出20個,可以去修改它。
/usr/local/php5.6/bin/php /test/test_coreseek.php

這是二次改過的test_coreseek.php,對於新手有很友好的提示。

require ( "./sphinxapi.php" );
$search = new SphinxClient ();
//連線Sphinx伺服器
$search->SetServer ( '127.0.0.1', 9312);
//設定超時秒數
$search->SetConnectTimeout ( 3 );
//查詢出來的資料庫ID存放位置
$search->SetArrayResult ( true );
//配置搜尋模式
$search->SetMatchMode (SPH_MATCH_PHRASE);
//分頁,引數1偏移量,從0開始,引數2限制條目
$search->SetLimits(0, 200);
//執行查詢,引數1關鍵字
$res = $search->Query ("黑色衣服", "索引名稱");

if($res === false) {
    echo '查詢失敗';
    return;
}


//獲取主鍵
$primary_keys = [];
if(! empty($res['matches'])) {
    foreach($res['matches'] as $v) {
        $primary_keys[] = $v['id'];
    }
}

//獲取總數
$all_count = $res['total_found'];


//獲取耗時
$time = $res['time'];


$ids = implode(',', $primary_keys);
echo <<<RESULT
主鍵id: $ids
查詢總數:$all_count
耗時: $time
RESULT;

匹配模式

  • 官方文件:https://sphinxsearch.com/docs/current.html#matching-modes
  • 呼叫方法:(new SphinxClient())->SetMatchMode (SPH_MATCH_ALL);
  • 分詞舉例:假設關鍵字“白色衣服”,詞語被分成了“白色”和“衣服”。
匹配模式 一句話概括 會被匹配 不會被匹配
SPH_MATCH_ALL 同時包含這些分詞時會被匹配 白色的繩子晾著他的衣服 他的衣服
SPH_MATCH_ANY 匹配任意一個分詞就行 白色的紙 白雲
SPH_MATCH_PHRASE 相當於like '%關鍵詞%' 白色衣服 白色的衣服
SPH_MATCH_EXTENDED 支援Sphinx的表示式 下方有詳解 白色的衣服
SPH_MATCH_EXTENDED2 SPH_MATCH_EXTENDED的別名 下方有詳解 白色的衣服
SPH_MATCH_BOOLEAN 支援布林方式搜尋 下方有詳解 白色的衣服
SPH_MATCH_FULLSCAN 強制使用全掃描模式 下方有詳解 白色的衣服

詳解:SPH_MATCH_FULLSCAN
SPH_MATCH_FULLSCAN,強制使用全掃描模式。注意,任何查詢項都將被忽略,這樣過濾器、過濾器範圍和分組仍然會被應用,但不會進行文字匹配。
當滿足以下條件時,將自動啟用SPH_MATCH_FULLSCAN模式來代替指定的匹配模式:
查詢字串為空(即:它的長度是0)。
Docinfo儲存設定為extern。
在完全掃描模式下,所有索引的文件都被認為是匹配的。這樣的查詢仍然會應用過濾器、排序和分組,但不會執行任何全文搜尋。這對於統一全文和非全文搜尋程式碼或解除安裝SQL伺服器很有用(在某些情況下,Sphinx掃描比類似的MySQL查詢執行得更好)。

詳解:SPH_MATCH_BOOLEAN
https://sphinxsearch.com/docs/current.html#boolean-syntax

  • kw1|kw2|kw3:或的關係,滿足任意1項即可。
  • kw1 -kw2,或者kw1 !kw2:表示!=kw2

詳解:SPH_MATCH_EXTENDED
https://sphinxsearch.com/docs/current.html#extended-syntax

  • kw1|kw2|kw3:或的關係,滿足任意1項即可,
  • kw1 -kw2,或者kw1 !kw2:表示!=kw2。
  • @mysql欄位名 欄位全部內容:匹配某個欄位的內容。
  • @* 欄位全部內容:匹配所有欄位的內容。
  • ^kw:表示以關鍵字開頭。
  • kw$:表示以什麼關鍵字結尾。

實測索引建立效能

  • 建立索引 /home/coreseek/bin/indexer -c /home/coreseek/etc/csft.conf articles,這裡的article就是source段的名稱。
  • 伺服器配置:CentOS7.6 16核4G記憶體 固態硬碟。
  • Sphinx索引建立速度:36262.76/sec,每條語句大小約10~150個漢字。
  • Sphinx索引檔案建立速度:4441931 bytes/sec。
  • 一共耗時:1億條資料,建立索引花了50.5分鐘,索引檔案約10個G。
  • 原文如下:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

 using config file '/home/coreseek/etc/csft.conf'...
indexing index 'articles'...
WARNING: Attribute count is 0: switching to none docinfo
collected 109450000 docs, 13406.8 MB
WARNING: sort_hits: merge_block_size=28 kb too low, increasing mem_limit may improve performance
sorted 3133.8 Mhits, 100.0% done
total 109450000 docs, 13406849231 bytes
total 3018.247 sec, 4441931 bytes/sec, 36262.76 docs/sec
total 322950 reads, 202.304 sec, 27.9 kb/call avg, 0.6 msec/call avg
total 20920 writes, 25.975 sec, 997.1 kb/call avg, 1.2 msec/call avg

實測查詢效能

  • 開啟searchd服務/home/coreseek/bin/searchd -c /home/coreseek/etc/csft.conf

  • 伺服器配置:CentOS7.6 16核4G記憶體

  • 實測億級資料下搜尋晴空萬里只花費0.011秒,上文提到MySQL則需要242秒,效能差了22000倍。或許是受或分詞器影響,Sphinx查詢出來1898條,MySQL查詢資料為1931條

  • 多次測試:在109450000條資料中,從不同角度測試Sphinx SPH_MATCH_PHRASE匹配模式,相當於mysql where field like '%關鍵字%':

型別 搜尋關鍵字 Sphinx搜尋耗時(秒) MySQL搜尋耗時(秒) Sphinx搜尋數量 MySQL搜尋數量
數字 123 0.005 305.142 3121 8143
中文單字 0.013 223.184 67802 103272
英文單字母 A 0.031 339.576 136428 1017983
單中文標點 4.471 125.106 67088012 67096182
單英文標點 . 0 251.171 0 6697242
可列印特殊字元 0 355.469 0 0
中文詞語(易分詞) 黑色衣服 0.066 346.442 1039 1062
中文詞語(不易分詞) 夏威夷 0.011 127.054 3636 3664
中文詞語(熱門) 你好 0.022 126.979 102826 137717
中文詞語(冷門) 旖旎 0.010 345.493 4452 4528
英文單詞 good 0.010 137.562 553 1036
中文短語 他不禁一臉茫然 1.742 218.272 0 0
英文短語 I am very happy 0.015 355.235 1 0
長文字 陳大人不急著回答,他先從櫃檯下面又抽出了一份文案,翻了好一陣之後才回答道:“瞧,果然如此,如今廣州這邊官職該放得都放出去了,只剩下消防營山字營的一個哨官之職。不出所料的話,督撫大人準會委你這個職務。 0.131 129.204 1 1

實測Sphinx併發效能

壓測方式 :ab -c 1 -n 10~1000 192.168.3.180/test_coreseek.php

中文定值關鍵字為華盛頓,英文定值關鍵字為XYZ,30位隨機中文或英文字元,由程式碼生成。

生成任意正整數箇中文字元
function generateRandomChinese($length) {
    $result = '';
    for ($i = 0; $i < $length; $i++) {
        $result .= mb_convert_encoding('&#' . mt_rand(0x3e00, 0x9fa5) . ';', 'UTF-8', 'HTML-ENTITIES');
    }
    return $result;
}

生成任意正整數個英文字元
function generateRandomEnglish($length) {
    $result = '';
    for ($i = 0; $i < $length; $i++) {
        $result .= chr(mt_rand(97, 122)); // 小寫字母ASCII碼範圍: 97~122;大寫字母:65~90
    }
    return $result;
}
型別 請求量 (搜尋次數) 耗時(秒)
固定中文多次搜尋 10 0.256
固定中文多次搜尋 100 1.435
固定中文多次搜尋 1000 11.604
隨機30位中文字元多次搜尋 10 0.517
隨機30位中文字元多次搜尋 100 2.305
隨機30位中文字元多次搜尋 1000 17.197
固定英文多次搜尋 10 0.327
固定英文多次搜尋 100 0.747
固定英文多次搜尋 1000 8.510
隨機30位英文字元多次搜尋 10 0.077
隨機30位英文字元多次搜尋 100 0.766
隨機30位英文字元多次搜尋 1000 9.428

建立增量索引前的資料查詢問題

對於update很多資料,sphinx不會自動更新索引,所以可以選擇在公司業務空閒時間重建索引。
對於insert,可以使用增量索引,建立一個表用於儲存已經新增sphinx索引記錄的最大值。

CREATE TABLE `sphinx_index_record` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Sphinx索引建立進度表id',
  `table_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '表名',
  `max_id` int unsigned NOT NULL COMMENT '最大id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

並insert一條資料:
INSERT INTO `test`.`sphinx_index_record` (`id`, `table_name`, `max_id`) VALUES (1, 'articles', 109450000);
  • 對於Sphinx:當需要新增增量索引時,讀取這個表中的資料,獲取到這個值,使其建立索引的起始點,為max_id欄位的值。
  • 對於max_id欄位的業務作用:可以封裝一個方法,可以讓這個值載入Redis,redis有值就讀取,無值就查詢然後載入Redis。
  • 對於業務程式碼:例如100萬行資料建立了Sphinx索引,然後又新增了1000條,這1000條資料沒有sphinx索引,可以在業務程式碼中使用mysql union或者其它方式,讓這1000條資料,做查詢。
    虛擬碼如下:
$max_id = 從快取中獲取獲取的索引位置,假設是100萬;
$kw = 搜尋關鍵字;

$id_s = sphinx($kw);

sql1是根據sphinx的
$sql1 = select * from table where id in $id_s;
sql2是對未新增sphinx索引的剩餘表資料的操作
$sql2 = select * from table where id > $max_id like "%$kw%";

使用PHP的方式,或者mysql union的方式都行,讓兩個資料集合並,查詢出的結果,透過介面返回。
這樣兼顧新增的1000條資料也能被查詢的到。

建立增量索引

這塊需要承接上文。

第一步:自動維護索引最大值,這一步可以在首次建立索引時,就可以完成。
vim /home/coreseek/etc/csft.conf
在source段最後,新增一行程式碼,這是讓sphinx建立增量索引後,自動維護sphinx_index_record表資料。
source articles {
...
...
sql_query_post = update sphinx_index_record set max_id = (select max(id) from articles) where table_name = 'articles';
}


第二步:新增增量索引配置
把source段複製出來,然後貼上到下方,注意括號範圍,不要巢狀,例如:
source articles_add
{
        type                    = mysql
        sql_host                = 資料庫IP
        sql_user                = 資料庫使用者名稱
        sql_pass                = 資料庫密碼
        sql_db                  = 資料庫
        sql_port                = 3306  埠
        sql_query_pre           = select names utf8mb4 加上這行
        -------------------------------修改這裡start-------------------------------------------
        sql_query               = select id,content from articles where id > (select max_id from sphinx_index_record where table_name = 'articles') Sphinx建立增量索引的資料來源
        -------------------------------修改這裡end-------------------------------------------

        sql_attr_uint           = group_id   這行用不上可以去掉
        sql_attr_timestamp      = date_added 這行用不上可以去掉

        sql_query_info_pre      = select names utf8mb4 加上這行
        sql_query_info          = select id,content from articles where id=$id 用Sphinx做什麼SQL的查詢邏輯
}

這裡記得index段也新增一個配置。永遠記住,source段與index段一對一的。

index articles_add 這裡需要改
{
        source                  = articles_add 這裡需要改
        path                    = /home/coreseek/var/data/articles_add 這裡需要改
        #docinfo                = extern
        charset_dictpath        = /home/mmseg/etc
        charset_type            = zh_cn.utf-8
}

執行建立索引功能。
/home/coreseek/bin/indexer -c /home/coreseek/etc/csft.conf articles_add
合併索引:語法indexer --merge 主索引名 增量索引名
/home/coreseek/bin/indexer --merge articles articles_add
如果不關閉searchd,可以新增 --rotate引數強制合併索引。

需要留意一下:如果主索引10個G,增量索引0.1G,則需要20.2G的臨時空間去進行和合並。

多配置

這個也好辦,直接在csft.conf配置檔案內source段和index段複製貼上,根據上文的兩段文章,該建立索引的建立索引,該重啟的重啟。
不需要引入多個檔案,就和MySQL一樣,只需要一個/etc/my.cnf就行了,相加配置,接著往下續就行了。

新建立索引後不會生效,需要關閉searchd程序後重新啟動。

整合到框架思路

方案1:
使用composer安裝新的包,PHP8.0及以上不會報錯。
記得要搜尋sphinx client或sphinxapi,不要搜素sphinx,這會把SphinxQL的解決方案也給搜出來。
用這個包就行,composer require nilportugues/sphinx-search
用法與自帶的完全一致,而且遇到PHP8不報錯。

$sphinx = new \NilPortugues\Sphinx\SphinxClient();
$sphinx->setServer('192.168.3.180',9312);
$sphinx->SetConnectTimeout (3);
$sphinx->SetArrayResult (true);
$sphinx->SetMatchMode (SPH_MATCH_PHRASE);
$sphinx->SetLimits(0, 200);
$res = $sphinx->query ("黑色衣服", "articles");
print_r($res);

方案2:
使用原生自帶的包,PHP8.0及以上會報錯。
sphinxapi.php放置到app/Libs/Others目錄下,並新增自己的名稱空間App\Libs\Others\SphinxApi。
app/Libs/helper.php存放了自定義封裝的方法,並使用composer dump-autoload配置,跟隨框架自動載入。

封裝成一個方法,方便呼叫,並放入helper.php下
function sphinx() {
    $sphinx = new App\Libs\Others\SphinxApi();
    $sphinx->SetServer (config('services.sphinx.ip'), config('services.sphinx.port'));
    $sphinx->SetConnectTimeout (config('services.sphinx.timeout');
    $sphinx->SetArrayResult (true);
    return $sphinx;
}

//控制器隨處呼叫
$sphinx = sphinx();
$sphinx->SetMatchMode (SPH_MATCH_PHRASE);
$sphinx->SetLimits(0, 200);
$res = $sphinx->Query ("黑色衣服", "索引名稱");
...

Sphinx的不足之處

  • 查詢結果遺漏:受分詞器影響,實測查詢結果比實際的儲存要少一些,這會造成資訊的損失。
  • 客戶端程式碼陳舊:自帶的的PHP Sphinx Api的程式碼,使用PHP8.0及以上會報錯。
  • 不會自動新增增量索引:MySQL新增的資料,Sphinx並不會自動建立索引。
  • 不會自動改變索引:MySQL update的資料,Sphinx不會自動改變索引。
  • 不支援Canal監聽bin log實時同步Sphinx索引。

鳴謝

感謝3位博主在C++編譯安裝報錯時提供的解決方案:
夜裡小郎君的博文:https://blog.csdn.net/b876143268/article/details/53771310
mingaixin的博文:https://www.cnblogs.com/mingaixin/p/5013356.html
晚晚的博文:https://www.cnblogs.com/caochengli/articles/3028630.html

相關文章