經過實測:1.09億的數(shù)據(jù)量進行中文檢索漓拾。ElasticSearch單機的檢索性能在0.005~5.6秒之間阁最,此檢索速度可滿足95%的業(yè)務場景(注意:每條ES文檔平均65個漢字,數(shù)據(jù)源取自幾千本小說骇两,大部分文檔在15~300個漢字之間速种,不然字數(shù)太多索引太大電腦存不下)。
前置文章
由于本文章的前置操作強依賴于另一篇文章低千,推薦閱讀:
萬字詳解PHP+Sphinx中文億級數(shù)據(jù)全文檢索實戰(zhàn)(實測億級數(shù)據(jù)0.1秒搜索耗時)
運行配置
和Sphinx環(huán)境保持一致配阵。
服務器配置:CentOS7.6 16核4G內存。固態(tài)硬盤示血。
ES配置:ElasticSearch 8.14.1單機棋傍,默認配置,使用IK分詞器的ik_max_word配置难审。不設置分片和副本數(shù)量瘫拣。
數(shù)據(jù)準備
和Sphinx用的數(shù)據(jù)源保持一致。
依舊是上次用的幾千本小說告喊,整合后的單個txt文件9.57個G拂铡,用\n間隔,作為一個ES文檔葱绒。
數(shù)據(jù)量為109 450 000條數(shù)據(jù)。
數(shù)據(jù)插入
- 創(chuàng)建索引與映射斗锭,并修改max_result_window參數(shù)
$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 //用于控制在搜索查詢中可以檢索到的最大文檔數(shù)地淀,有符號int類型,最大可設置2^31 - 1,大了會有性能問題
]
]
];
$response = $client->indices()->putSettings($params);
dd($response->asBool());
- 插入數(shù)據(jù)
//這段代碼只確贬牵可批量插入帮毁,忽略精準的數(shù)據(jù)處理高可用問題实苞。
$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新增數(shù)據(jù)建索引速度對比
應用 | 耗時 | 新增數(shù)據(jù)量 | 補充 |
---|---|---|---|
Sphinx | 50.5分鐘 | 109 450 000 | / |
ElasticSearch | 119分鐘 | 109 450 000 | (總時間 - PHP代碼執(zhí)行時間,總耗時190分鐘) |
實測ES與Sphinx查詢性能對比
某些項烈疚,ElasticSearch搜索出來的結果遠超MySQL和Sphinx查詢的結果黔牵,這是分詞匯總的緣故。
而Sphinx使用的是SPH_MATCH_PHRASE格式爷肝,所以數(shù)量不會有ES那么多猾浦,若用SPH_MATCH_ANY,可能有更多的檢索結果灯抛。
類型 | 搜索關鍵字 | Sphinx搜索耗時(秒) | ES搜索耗時(秒) | MySQL搜索耗時(秒) | Sphinx搜索數(shù)量 | ES搜索數(shù)量 | MySQL搜索數(shù)量 |
---|---|---|---|---|---|---|---|
數(shù)字 | 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并發(fā)性能對比
- 壓測方式 :ab -c 1 -n 10~1000 127.0.0.1/temp/es/test.php
- 中文定值關鍵字為華盛頓已脓,英文定值關鍵字為XYZ,30位隨機中文或英文字符乏奥,由代碼生成(用代碼生成數(shù)據(jù)源摆舟,是避免引入更好的數(shù)據(jù)源帶來了性能誤差)。
- 由于ES IK分詞器比Sphinx中文分詞器分詞粒度更細邓了,所以并發(fā)下30位隨機中文字符檢索性能極具下降恨诱。
生成任意正整數(shù)個中文字符
function generateRandomChinese($length) {
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= mb_convert_encoding('&#' . mt_rand(0x3e00, 0x9fa5) . ';', 'UTF-8', 'HTML-ENTITIES');
}
return $result;
}
生成任意正整數(shù)個英文字符
function generateRandomEnglish($length) {
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= chr(mt_rand(97, 122)); // 小寫字母ASCII碼范圍: 97~122;大寫字母:65~90
}
return $result;
}
類型 | 搜索次數(shù)(ab -n 參數(shù)值) | 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 | 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各項優(yōu)缺點直觀對比
項目 | ElasticSearch(相比于Sphinx) | Sphinx(相比于ElasticSearch) |
---|---|---|
創(chuàng)建索引性能 | 慢 | 快 |
查詢性能 | 相差無幾 | 相差無幾 |
并發(fā)性能 | 慢 | 快 |
中文分詞支持 | 需安裝IK分詞器 | 需安裝Mmseg分詞工具和Coreseek中文搜索引擎框架 |
實時搜索 | 友好 | 不友好 |
對增量數(shù)據(jù)(Insert) | 通過代碼層可直接同步ES | 需要運維層面的觸發(fā)而生成增量索引 |
與數(shù)據(jù)庫一致性同步問題(Update骗炉、Delete) | ES支持直接更新 | Sphinx不支持對索引更新照宝,需重建索引 |
客戶端語言支持 | Java、PHP句葵、JavaScript厕鹃、Perl、Ruby乍丈、Python剂碴、Golang、Eland轻专、.NET忆矛、Rust | Java、PHP、Python催训、Perl洽议、C |
開發(fā)語言 | Java | C++ |
支持跨平臺 | 是 | 是 |
架構 | C/S | C/S |
合作流程 | 內置數(shù)據(jù)庫,支持對自身數(shù)據(jù)進行復雜的增刪改查漫拭,但需要MySQL兜底 | 內置索引庫亚兄、幫MySQL找ID |
事務支持 | 不支持 | 不支持 |
系統(tǒng)內存占用 | 大 | 小 |
集群部署 | 支持 | 支持 |
集群協(xié)調模式 | 自動負載均衡 | 節(jié)點間協(xié)調 需要手動設置負載均衡和協(xié)調 |
數(shù)據(jù)分析 | 內建強大的聚合和分析功能 | 不支持復雜的數(shù)據(jù)分析 |
GUI | 需額外安裝組件,例如Kibana | 無官方可視化工具 |
生態(tài) | 繁榮 | 一般 |
上手難度 | 難 | 易 |
安全性 | 支持基于用戶的訪問控制采驻,集成X-Pack進行高級安全配置审胚。但內部的Log4j2組件存在高危漏洞 | 基本的權限管理,需依賴外部工具 |