ElasticSearch第4篇(億級中文資料量 ElasticSearch與Sphinx建索引速度、查詢速度、併發效能、實測對比)

小松聊PHP进阶發表於2024-07-28

經過實測:1.09億的資料量進行中文檢索。ElasticSearch單機的檢索效能在0.005~5.6秒之間,此檢索速度可滿足95%的業務場景(注意:每條ES文件平均65個漢字,資料來源取自幾千本小說,大部分文件在15~300個漢字之間,不然字數太多索引太大電腦存不下)。

前置文章

由於本文章的前置操作強依賴於另一篇文章,推薦閱讀:
萬字詳解PHP+Sphinx中文億級資料全文檢索實戰(實測億級資料0.1秒搜尋耗時)

執行配置

和Sphinx環境保持一致。
伺服器配置:CentOS7.6 16核4G記憶體。固態硬碟。
ES配置:ElasticSearch 8.14.1單機,預設配置,使用IK分詞器的ik_max_word配置。不設定分片和副本數量。

資料準備

和Sphinx用的資料來源保持一致。
依舊是上次用的幾千本小說,整合後的單個txt檔案9.57個G,用\n間隔,作為一個ES文件。
資料量為109 450 000條資料。

資料插入

  • 建立索引與對映,並修改max_result_window引數
$params = [
    'index' => 'performance_test',
    'body'  => [
        'settings' => [
            'analysis' => [
                'analyzer' => [
                    'ik_analyzer' => [
                        'type'      => 'ik_max_word',
                    ],
                ],
            ],
        ],
        'mappings' => [
            'properties' => [
                'id' => [
                    'type'     => 'integer',
                ],
                'content' => [
                    'type'     => 'text',
                    'analyzer' => 'ik_analyzer',
                ],
            ],
        ],
    ],
];

$response = $client->indices()->create($params);
dd($response->asBool());


$params = [
    'index' => 'performance_test',
    'body'  => [
        'index' => [
            'max_result_window' => 2147483647 //用於控制在搜尋查詢中可以檢索到的最大文件數,有符號int型別,最大可設定2^31 - 1,大了會有效能問題
        ]
    ]
];

$response = $client->indices()->putSettings($params);
dd($response->asBool());
  • 插入資料
//這段程式碼只確保可批次插入,忽略精準的資料處理高可用問題。
$start = microtime(true);
ini_set('memory_limit', '4096M');
set_time_limit(0);

include __DIR__ . './vendor/autoload.php';

$client = \Elasticsearch\ClientBuilder::create()->setHosts(['192.168.0.183:9200'])
    ->setBasicAuthentication('elastic', '123456')->build();


/**
 * @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('E:/其它/一億行漢字文字.txt');
foreach ($file_resource as $loop => $line) {
    $loop ++;
    $from_charset = mb_detect_encoding($line, 'UTF-8, GBK, GB2312, BIG5, CP936, ASCII');
    $utf8_str     = @iconv($from_charset, 'UTF-8', $line);
    if(in_array($utf8_str, ["\n", "\r", "\n\r", "\r\n"])) {
        continue;
    }


    $params['body'][] = ['index' => ['_index' => 'performance_test', '_id' => $loop]];
    $params['body'][] = ['id' => $loop, 'content' => $utf8_str];


    if(count($params['body']) >= 100000) {
        $client->bulk($params); //忽略批次插入的錯誤
        $params = [];
    }
}

echo '插入耗時:' . bcsub(microtime(true), $start, 3) . '秒';

實測ES與Sphinx新增資料建索引速度對比

應用 耗時 新增資料量 補充
Sphinx 50.5分鐘 109 450 000 /
ElasticSearch 119分鐘 109 450 000 (總時間 - PHP程式碼執行時間,總耗時190分鐘)

實測ES與Sphinx查詢效能對比

某些項,ElasticSearch搜尋出來的結果遠超MySQL和Sphinx查詢的結果,這是分詞彙總的緣故。
而Sphinx使用的是SPH_MATCH_PHRASE格式,所以數量不會有ES那麼多,若用SPH_MATCH_ANY,可能有更多的檢索結果。

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

實測ES與Sphinx併發效能對比

  • 壓測方式 :ab -c 1 -n 10~1000 127.0.0.1/temp/es/test.php
  • 中文定值關鍵字為華盛頓,英文定值關鍵字為XYZ,30位隨機中文或英文字元,由程式碼生成(用程式碼生成資料來源,是避免引入更好的資料來源帶來了效能誤差)。
  • 由於ES IK分詞器比Sphinx中文分詞器分詞粒度更細,所以併發下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;
}
型別 搜尋次數(ab -n 引數值) Sphinx耗時(秒) ES耗時(秒)
固定中文多次搜尋 10 0.256 0.623
固定中文多次搜尋 100 1.435 1.915
固定中文多次搜尋 1000 11.604 18.821
隨機30位中文字元多次搜尋 10 0.517 4.257
隨機30位中文字元多次搜尋 100 2.305 52.505
隨機30位中文字元多次搜尋 1000 17.197 超時
固定英文多次搜尋 10 0.327 0.584
固定英文多次搜尋 100 0.747 5.085
固定英文多次搜尋 1000 8.510 50.423
隨機30位英文字元多次搜尋 10 0.077 0.0623
隨機30位英文字元多次搜尋 100 0.766 4.810
隨機30位英文字元多次搜尋 1000 9.428 50.698

ES與Sphinx各項優缺點直觀對比

專案 ElasticSearch(相比於Sphinx) Sphinx(相比於ElasticSearch)
建立索引效能
查詢效能 相差無幾 相差無幾
併發效能
中文分詞支援 需安裝IK分詞器 需安裝Mmseg分詞工具和Coreseek中文搜尋引擎框架
實時搜尋 友好 不友好
對增量資料(Insert) 透過程式碼層可直接同步ES 需要運維層面的觸發而生成增量索引
與資料庫一致性同步問題(Update、Delete) ES支援直接更新 Sphinx不支援對索引更新,需重建索引
客戶端語言支援 Java、PHP、JavaScript、Perl、Ruby、Python、Golang、Eland、.NET、Rust Java、PHP、Python、Perl、C
開發語言 Java C++
支援跨平臺
架構 C/S C/S
合作流程 內建資料庫,支援對自身資料進行復雜的增刪改查,但需要MySQL兜底 內建索引庫、幫MySQL找ID
事務支援 不支援 不支援
系統記憶體佔用
叢集部署 支援 支援
叢集協調模式 自動負載均衡 節點間協調 需要手動設定負載均衡和協調
資料分析 內建強大的聚合和分析功能 不支援複雜的資料分析
GUI 需額外安裝元件,例如Kibana 無官方視覺化工具
生態 繁榮 一般
上手難度
安全性 支援基於使用者的訪問控制,整合X-Pack進行高階安全配置。但內部的Log4j2元件存在高危漏洞 基本的許可權管理,需依賴外部工具

相關文章