經過實測: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元件存在高危漏洞 | 基本的許可權管理,需依賴外部工具 |