发布时间:2023-05-25 18:00
1. query then fetch(默认搜索方式)
搜索步骤如下:
优点:返回的数据量是准确的
缺点:性能一般,并且数据排名不准确
2. dfs query then fetch
比前面的方式多了一个DFS步骤。也就是查询之前,先对所有分片发送请求,把所有分片中的词频和文档频率等打分依据全部汇总到一块,再执行后面的操作。
详细步骤如下:
优点:返回的数据和数据排名都是准确的
缺点:性能较差
增删改流程
搜索流程
说到排序,我们必须要说Doc Values这个东西。那么Doc Values是什么呢?又有什么作用?
我们都知道ES之所以那么快速,归功于他的倒排索引的设计,然而他也不是万能的,倒排索引的检索性能是非常快的,但是在字段值排序时却不是理想的结构。
如上表可以看出,他只有词对应的doc,但是并不知道每一个doc中的内容,那么如果想要排序的话每一个doc都去获取一次文档内容岂不非常耗时?Doc Values的出现就是解决这个问题。
Doc Values是可以根据doc_values属性进行配置的,默认为true。当配置为false时,无法基于该字段排序、聚合、在脚本中访问字段值。
Doc Values是转置倒排索引和正排索引的关系来解决这个问题。倒排索引将词项映射到包含它们的文档,Doc Values将文档映射到它们包含的词项:
当数据被转置后,想要收集到每个文档行,获取所有的词项就比较简单了。所以搜索使用倒排索引查找文档,聚合操作和排序就要使用Doc Values里面的数据。
深入理解Doc Values
Doc Values是在索引时与倒排索引同时生成。也就是说Doc Values和倒排索引一样,基于Segement生成并且是不可变的。同时Doc Values和倒排索引一样序列化到磁盘,这样对性能和扩展性有很大帮助。
Doc Values通过序列化把数据结构持久化到磁盘,我们可以充分利用操作系统的内存,而不是JVM的Heap。当workingset远小于系统的可用内存,系统会自动将Doc Values保存在内存中,使得其读写十分高速;不过,当其远大于可用内存时,操作系统会自动把Doc Values写入磁盘。很显然,这样性能会比在内存中差很多,但是它的大小就不再局限于服务器的内存了。如果是使用JVM的Heap来实现是因为容易OutOfMemory导致程序崩溃了。
禁用Doc Values
Doc Values默认对所有字段启用,除了analyzed strings。也就是说所有的数字、地理坐标、日志、IP和不分析字符类型都会默认开启。
analyzed strings暂时不能使用Doc Values,因为分析后会生成大量的Token,这样非常影响性能。虽然Doc Values非常好用,但是如果你存储的数据确实不需要这个特性,就不如禁用他,这样不仅节省磁盘空间,也许会提升索引的速度。
要禁用Doc Values,在mapping设置即可。示例:
PUT my_index
{
"mappings": {
"properties": {
"session_id": {
"type": "keyword",
"doc_values": false
}
}
}
}
如下面这个例子,需要过滤date为2020-02-02的数据,去倒排索引中查找,发现2020-02-02对应的document list是doc2、doc3.
如上面的例子,根据document list,构建的bitset是[0,1,1],1代表匹配,0代表不匹配
另外多个过滤条件时,先过滤比较稀疏的条件,能先过滤掉尽可能多的数据。
caching bitset,跟踪query,在最近256个query中超过一定次数的过滤条件,缓存其bitset。对于小的segment(记录数小于1000或小于总大小3%),不缓存。
如果document有新增或修改,那么cached bitset会被自动更新
filter大部分情况下,在query之前执行,先尽量过滤尽可能多的数据
基于boost的权重控制
考虑如下场景:
我们搜索帖子,搜索标题包含java或spark或Hadoop或elasticsearch。但是需要优先输出包含java的,再输出spark的的,再输出Hadoop的,最后输出elasticsearch。
我们先看如果不考虑优先级时怎么搜索:
GET /article/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"title": {
"value": "java"
}
}
},
{
"term": {
"title": {
"value": "elasticsearch"
}
}
},
.....省略
]
}
}
}
搜索出来的结果跟我们想要的顺序不一致,那么我们下一步加权重。增加boost
GET /article/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"title": {
"value": "java",
"boost": 5
}
}
},
{
"term": {
"title": {
"value": "spark",
"boost": 4
}
}
}
]
}
}
}
基于dis_max的策略控制
dix_max想要解决的是:
如果我们想要某一个filed中匹配到尽可能多的关键词的被排在前面,而不是在多个filed中重复出现相同的词语的排在前面。
对于一个文档会将title匹配到的分数和content匹配到的分数相加。所以doc id为2的文档的分数比doc id为4的大。
dis_max查询:
GET /article/_search
{
"query": {
"dis_max": {
"queries": [
{"match": {"title": "java"}},
{"match":{"content":"java solution"}}
]
}
}
}
查询到的结果如下:
{
"took" : 6,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.4905943,
"hits" : [
{
"_index" : "article",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.4905943,
"_source" : {
"title" : "spark",
"content" : "spark is best big data solution based on scala,an programming language similar to java"
}
},
{
"_index" : "article",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.2039728,
"_source" : {
"title" : "java",
"content" : "i think java is the best programming language"
}
}
]
}
}
基于function_score自定义相关度分数
在用ES进行搜索时,搜索结果默认会以文档的相关度进行排序,而这个"文档的相关度",是可以通过function_score自定义的。
function_score提供了几种类型的得分函数: