ElasticSearch第4篇(亿级中文数据量 ElasticSearch与Sphinx建索引速度、查询速度、并发性能、实测对比)

经过实测: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组件存在高危漏洞 基本的权限管理,需依赖外部工具

热门相关:富二代修仙日常   我的极品美女总裁   满级绿茶穿到八十年代重新做人   帝王娇宠:小萌妃,乖一点   爆萌宠妃:狼性邪帝,吃不够