經過實測:1.09億的數據量進行中文檢索。ElasticSearch單機的檢索性能在0.005~5.6秒之間,此檢索速度可滿足95%的業務場景(註意:每條ES文檔平均65個漢字,數據源取自幾千本小說,大部分文檔在15~300個漢字之間,不然字數太多索引太大電腦存不下)。 前置文章 由於本文章的前置操 ...
經過實測: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組件存在高危漏洞 | 基本的許可權管理,需依賴外部工具 |