作者 | 李其霖
前言
目前,Elasticsearch最新的版本为8.13,而众安还在大量使用5.x版本。根据官方文档,ES8.x相对5.x查询和聚合分析的性能有了较大的提升,存储成本也可以降低约30%左右。除此之外,Elasticsearch8增加了向量KNN检索与RRF混排,Elasticsearch相关性引擎(ESRE), 快照(snapshot)检索,时序数据库(TSDB)等新特性。
另外,为降本增效,充分释放技术升级的红利,便利业务线快速升级。中间件搜索团队升级了二向箔(XSearch)搜索引擎服务平台,支持了ES8.x集群的接入和托管,以及低版本向ES8一键无感升级的功能。
一、背景介绍
目前,公司主要使用的Elasticsearch的版本为5.x,还有少量的2.x版本的ES,而Elasticsearch最新的版本为8.12, 公司使用的主流Elasticsearch版本落后了最新技术三代以上。随着公司业务增长,老版本Elasticsearch集群中数据不断膨胀,导致查询变慢,超时现象不断增多。由于老版本集群的性能瓶颈,只能通过不断扩容应对数据增长,导致集群成本不断上升。
另外,随着大语言模型的发展,对于向量存储和向量检索能力的需求也在不断增长。 企业内部的知识库内容需要经过向量化和检索召回后,才能通过提示词工程集成大语言模型的语义理解和推理能力。
而Elasticsearch8(以下简写为ES8)可以很好的满足公司业务对应检索性能,存储成本,向量存储和向量检索方面的需求。ES8相比老版本,显著地提升了检索的性能,降低了存储成本,并支持了向量存储,KNN检索和混合排序等新的功能特性。
二、性能提升
Elasticsearch最新的版本为8.12,相比ES5.x版本,性价比有了很大的提升。
搜索性能:提高30-50%的搜索性能。
聚合性能:平均提高60-90%的聚合性能,部分聚合查询性能提升2~10倍。
写入性能:提高20-30%的写入性能。
存储成本:降低约20%左右的存储成本。
| 2.1 Range查询性能提升
官方压测ES8的Range查询响应时间相比ES7提升20%。
ES8的Range query相比ES5的提升更明显。通过esrally压测工具的标准数据集压测结果显示,ES8相比ES5延迟降低约30%。
| 2.2 Wildcard查询性能提升
从ES7.9开始,加入了对 wildcard 类型的支持,旨在改善模糊匹配的查询效率和性能,特别是在处理大量文本数据时。这一新特性主要针对了之前版本中 wildcard 查询的性能问题,提供了更高效的方式来处理通配符和正则表达式的搜索需求。
我们来看下 wildcard 类型怎么使用:
首先定义一个 wildcard 类型的字段:
PUT my-index-000001
{
"mappings": {
"properties": {
"my_wildcard": {
"type": "wildcard"
}
}
}
}
使用 wildcard 查询如下所示:
GET my-index-000001/_search
{
"query": {
"wildcard": {
"my_wildcard": "*quite*lengthy"
}
}
}
查询效率
注:这里省却了索引详细信息,只需知道是同一个索引的比对测试。
综上所述,在模糊搜索字段字符串区分度很低的情况下 如:模糊查询单个数字,此时优化效率rt大概是之前的1/3左右,字段字符串区分度高的场景rt大概是之前的1/15左右,有明显效果。
| 2.3 聚合查询性能提升
ES8对于聚合查询性能有非常大的优化,很多metrics统计类型的聚合查询的相比ES5有10倍甚至百倍的提升。通过esrally压测工具的标准数据集压测结果显示,ES8相比ES5的聚合查询从原来的秒级响应提升到毫秒级,原来ES5需要用10多秒才能完成的聚合查询,ES8只需要10几毫秒就能完成。
三、成本降低
| 3.1 索引压缩
ES8使用了Lucene 9,Lucene9优化了压缩算法,降低了索引的大小,减少了磁盘IO,提升了索引和检索的性能:
● match_only_text 类型字段存储下降~15%
● text 类型字段存储下降
● 应用日志测试有3.4% 大小的下降
● geo_point 索引速度提升10%-15%
| 3.2 合成文档内容(Synthetic _source)
老版本的ES需要保存用户原始的文档内容,这占用了大量的磁盘资源,在访问大量数据时产生的IO会往往会导致响应变慢,甚至超时。ES8支持从索引的字段值中(doc_values)合成原始文档的数据,从而显著的减少存储资源的占用,大概可以减少30%左右的存储成本。由于减少了写入的数据量,也可以显著提升数据写入的吞吐率。
目前支持Synthetic _source的字段包括:
boolean, byte, double, float, geo_point, half_float, integer, ip, keyword, long, scaled_float, short, text
不过,由于需要通过计算合成原始文档内容,因此会增加一些cpu的消耗,轻微的影响到查询的性能。同时合成出来的文档的字段顺序会和写入时不一样。但这点限制相对于带来的成本的降低是可以忽略的。
| 3.3 降低节点堆内存消耗
ES8显著降低了索引元数据的内存消耗,相同内存可以容纳的分片数量是老版本ES的10倍。将可以显著降低索引数量大,查询量少的ES集群的成本,如日志类的ES集群。
四、功能特性
| 4.1 向量KNN检索
使用AIGC大语言模型回答本地知识库问题时,往往需要通过向量KNN检索召回相关的知识库内容。众安目前使用ES8作为向量检索引擎。ES8使用HNSW算法实现近似的向量K近邻(KNN)检索。HNSW是一种基于层次方法,它通过建立多层的图结构来实现对向量的快速检索。每层都根据欧几里得距离计算节点之间的距离,并选择合适的节点进行连接。HNSW的构建速度较慢,但其查询速度非常快,且能够处理高维数据。HNSW适用于高维数据和高数据量的情况。
如下图所示,每一层的入口节点连接了(红色虚线)下一层的节点集合,从L(=2)层开始寻找红点的K(=1)邻近节点,通过L2的邻近节点导航到L1层,找到L1的邻近节点,如此迭代,最终找到L0层的绿色节点为红色节点的最终邻近节点。
由于HNSW的分层实现了类似跳表索引的机制,可以大幅减少计算量,提升查询性能。
使用ES Query实现向量KNN检索的样例如下:
POST image-index/_search
{
"knn": {
"field": "image-vector",
"query_vector": [-5, 9, -12],
"k": 10,
"num_candidates": 100
}
}
除了应用于大语言模型的检索增强生成(RAG)应用的开发,其它类型的数据经过向量化后还可以应用于聚类分析和异常点分析。例如,金融信贷可以将用户的还款计划和实际还款数据向量化,分析有异常放款行为的用户。
| 4.2 Elastic Learned Sparse EncodeR
ELSER 是一种域外(out-of-domain)稀疏向量(Sparse Vector)预训练模型,这意味着它不需要对你自己的数据进行微调,可以开箱即用。相比TF-IDF或BM25等稀疏向量,ELSER提供了基于上下文含义和用户意图的搜索结果,而不是精确的关键字匹配。ELSER将索引和检索的文本扩展为词集合,这些词在不同的训练数据集中经常同时出现。这些词不一定是同义词,而是基于机器学习获得的关联词。不过ELSER目前还只支持英文,未来会推出支持中文的版本。
相比与大语言模型的密集向量(Dense Vector),ELSER稀疏向量检索速度更快,且在语义检索上也具备较好的性能。
使用ES Query实现ELSER稀疏向量检索的样例如下:
GET my_index/_search
{
"query": {
"text_expansion": {
"ml.tokens": {
"model_id": ".elser_model_1",
"model_text": "Sample"
}
}
}
}
ELSER稀疏向量模型可以与大语言向量Embedding模型结合,用户提升知识库内容召回的准确率。
| 4.3 机器学习模型和自然语言理解(NLP)
ES8可以内置一些机器学习或者自然语言理解的NLP模型,在索引和检索的时候使用指定的模型进行分析,并可以通过混合排序参与Ranking打分。例如下图所示,ES8可以使用文本和图片的向量模型,生成对应的词向量或者图片的特征向量。由于是本地部署的模型,具有较好的性能,适合一些数据量大,对向量化性能要求高的场景。
ES8还通过 HuggingFace 提供开箱即用型模型,提供一个可以快速启动的选项;此模型依靠的是一个不断壮大的领先语言模型社区。ES8可以从huggingface导入一些在ES集群中本地化部署的算法模型,然后在检索或者查询中可以指定模型进行计算。
下面是一个使用导入的向量化模型对查询文本进行KNN检索的查询样例:
POST /docs_index/_search
{
"knn": {
"field": "doc_part_vector",
"k": 5,
"num_candidates": 20,
"query_vector_builder": {
"text_embedding": {
"model_id": "<text-embedding-model-id>",
"model_text": "<query_string>"
}
}
}
}
ES8集成的模型目前可以支持以下 NLP 任务:
1. 模型填空 Fill-mask
这是一种语言模型的任务,模型需要根据给定的上下文和一个被遮挡的词,来预测这个遮挡的词是什么。例如,给定句子 "I want to buy a [MASK] of milk.",模型需要预测遮挡的词是什么,比如 "bottle"。这项任务通常涉及对上下文进行理解和生成可能的词。
2. 命名实体识别 (NER)
用于从文本中识别出具有特定含义的实体,例如人名、地名、组织名、日期、货币等。NER任务通常涉及识别和分类文本中的实体,并对它们进行标记。
3.文本分类 (二分类和多分类)
文本分类旨在将文本分为不同的类别或标签。二分类是将文本分为两个类别,如正面和负面;而多分类是将文本分为多个类别,如将新闻文章分类为政治、经济、体育等不同的类别。
4. 生成词向量
词向量是一种将词语表示为连续向量的技术,通过学习词语之间的语义和关联关系。生成词向量的任务通常涉及使用诸如Word2Vec、GloVe、FastText等模型,将词语映射到高维空间中的向量表示。
5. Zero-shot 分类
Zero-shot分类是指在没有事先见过的类别或标签的情况下对文本进行分类。通常使用零样本学习或迁移学习的技术来解决这个问题,这种任务要求模型在没有特定类别的训练数据的情况下进行分类。
| 4.4 混合排序
基于向量KNN的相似度排序在长文本和大数据量下准确率和性能不高,可以结合BM25的文本相关性排序与KNN的相似度排序来优化排序结果,同时可以通过关键词过滤来减少召回的文档数量,通过减少向量的计算量来提升检索性能。文本相关性排序和向量相似度排序的融合目前主要有两种:一种是线性融合,还有一种是倒数排序融合(RRF)。
1.线性融合
线性融合较为简单,将文本查询的score与KNN查询的相似度score通过boost加权后累加作为最终的排序score。下面是一个图片搜索的案例,通过图片的标题(title)和描述(description)的文本搜索flower,同时指定一个图片的向量,通过KNN相似检索图片。文本检索和向量KNN检索的权重分别为0.6和0.4。
POST image-index/_search
{
"query": {
"multi_match": {
"query": "flower",
"fields": ["title", "description"],
"boost": 0.6
}
},
"knn": {
"field": "image-vector",
"query_vector": [-5, 9, -12],
"k": 5,
"num_candidates": 100,
"boost": 0.4
}
}
2. 倒数排序融合(Reciprocal Rank Fusion)
线性融合需要根据文本相似度和向量KNN相似度的score分布,调节权重,有一定的调优成本。倒数排序融合(RRF)无需调优,不同的相关性指标也不必相互关联即可获得高质量的结果。该方法的优势在于不利用相关分数,而仅靠排名计算。
以下图为例,向量模型ELSER和文本相关性模型BM25分别对ABCDEFGH等8个文档进行排序分别得到每个文档的排名r(d), 再通过RRF算法融合计算出各个文档的RRF打分,从而得到最终的排序。
Elastic 8 支持具有多个密集向量查询和在倒排索引上运行的单个查询的 RRF。以下是RRF的查询示例:
GET example-index/_search
{
"query": {
"term": {
"text": "shoes"
}
},
"knn": {
"field": "vector",
"query_vector": [1.25, 2, 3.5],
"k": 50,
"num_candidates": 100
},
"rank": {
"rrf": {
"window_size": 50,
"rank_constant": 20
}
}
}
| 4.5 时序数据库(TSDS)
ES8中增加了分布式时序数据库TSDS(Time series data stream)的支持,TSDS的目标并不是替换传统的时序数据库,如influxdb,prometheus,opentsdb,而是为了满足已经使用ES的应用存储时序数据的需求,TSDS可以降低引入其它技术栈带来的开发和维护成本,同时能使用ES强大的查询和聚合功能。
如下图所示,TSDS的数据按照时间字段(@timestamp)进行分区,同时会记录每个分区的start_time和end_time, 并按一定数据间隔进行滚动。这样的好处是,在查询时只要读取时间间隔内的分区数据进行计算。与ES的普通索引相比,TSDS的存储经过特别的优化,存储成本降低约70%。
与传统的时序数据库类似,用户需要定义TSDS的维度字段(time_series_dimension)和指标字段(time_series_metric)。同时可以通过对维度字段做路由(routing_path)的方式让相同维度的数据落到相同的分片,提升查询和聚合的性能。下面是一个定义TSDS的索引模板:
{
"index_patterns": [
"metrics-weather_sensors-*"
],
"data_stream": {},
"template": {
"settings": {
"index.mode": "time_series",
"index.routing_path": [
"sensor_id",
"location"
]
},
"mappings": {
"properties": {
"sensor_id": {
"type": "keyword",
"time_series_dimension": true
},
"location": {
"type": "keyword",
"time_series_dimension": true
},
"temperature": {
"type": "half_float",
"time_series_metric": "gauge"
},
"humidity": {
"type": "half_float",
"time_series_metric": "gauge"
}
}
}
}
}
| 4.6 快照搜索(Searchable snapshots)
快照是用于备份和恢复数据的机制。它允许用户创建集群级别的快照,包括索引和对应的数据,然后在需要时将这些快照用于数据的恢复和迁移。快照搜索允许用户直接查询已经归档的索引数据,而不需要先从快照恢复到ES集群,然后再查询数据。考虑到很多归档的索引非常大,这个功能可以提升归档数据检索的效率。Elasticsearch从7.11版就开始支持快照搜索,在ES8中优化了快照的本地缓存功能,提升了快照搜索的查询性能。
| 4.7 数据探索和分析(OLAP)
1. ES|QL
ES8.11开始支持Elasticsearch 查询语言 (ES|QL) ,ESQL是一种支持迭代探索数据的查询语言。
ES|QL 查询由一系列由管道分隔的命令组成。每个查询都以源命令(FROM, ROW, SHOW)开始。源命令会生成一个表,通常包含来自 Elasticsearch 的数据。源命令后面可以跟一个或多个处理命令。处理命令通过添加、删除或更改行和列来更改输入表。你可以链接处理命令,并用竖线字符分隔:|。每个处理命令都作用于前一个命令的输出表。查询的结果是最终处理命令生成的表。
下面是一个ESQL的查询样例:
POST /_query
{
"query": """
FROM library
| EVAL year = DATE_TRUNC(1 YEARS, release_date)
| STATS MAX(page_count) BY year
| SORT year
| LIMIT 5
"""
}
2. 异步检索(Async Search)
当某些索引的数据量大或者查询比较复杂,导致查询的响应时间比较长时,可以试用异步检索,发起一个异步检索的任务,可以通过返回的任务的id监控异步检索任务的状态;并在需要的时候终止任务的执行。
例如,执行一个异步检索的查询。
POST /sales*/_async_search?size=0{
"sort": [
{ "date": { "order": "asc" } }
],
"aggs": {
"sale_date": {
"date_histogram": {
"field": "date",
"calendar_interval": "1d"
}
}
}
}
通过任务id获取异常检索的状态:
GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=
异步检索可以和ESQL结合用于海量数据的OLAP分析,或者与快照搜索结合用户归档数据的查询。异步检索适用于实时性要求不高的一些查询场景。
| 4.8 安全认证
ES8开源版开放了安全认证的特性,无需第三方插件即可使用Elastic原厂的安全认证功能。更好地保障Elasticsearch集群的数据安全性。
五、应用场景
当前已经在使用ES8的场景主要有AIGC和知识库服务,保险核心保单搜索和车险核心搜索。
| 5.1 AIGC和知识库服务
AIGC和知识库服务主要是使用了ES8的向量KNN检索,多路召回和混合排序的能力。
在ES8中,存储向量非常简单,只需要定义一个dense_vector类型字段,当然用户需要在中间件管理申请一个ES8版本的XSearch实例,然后,通过AIGC网关API写入向量数据,执行向量KNN检索。
| 5.2 保险保单核心搜索
保险保单核心承接了众安保险百亿级的核心保单数据,随着业务的增长,老的保单核心使用的ES5的集群已经接近性能瓶颈,升级到ES8可以明显提升性能和服务稳定性。
| 5.3 车险核心搜索
由于历史原因,车险核心搜索一直使用非常老的ES2.x版本,性能较差,而且使用直连的方式连ES,容易出错且不方便管理和升级。为方便管理和避免每次升级都需要修改代码,车险核心搜索接入二向箔(XSearch)搜索平台服务,并使用搜索平台的数据无感迁移服务升级到ES8。
六、未来规划
| 6.1 全司推广ES8
目前全司生产环境有三十多个ES集群,几百台ECS,存储了几百T的数据。很多ES集群使用了ES5版本,还有个别的ES2版本。全司全面推广使用ES8之后预计可以每年为公司节省30%左右的ECS,百万级的资源成本。并且可以给各业务线带来更快更好的数据检索体验。
| 6.2 搜索平台服务升级
ES8带来了一些比较好的功能特性和能力,例如时序数据库(TSDS),快照搜索(Searchable snapshots),数据探索和分析(OLAP),机器学习模型和自然语言理解的模型(NLP)以及安全认证。这些能力需要通过升级众安的搜索平台服务,向业务线输出,以方便业务线的接入和使用。
---------- END ----------
推荐阅读
| 众安开源AIGC工具代码助手DevPilot,让AI赋能每个开发者