- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
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代表不匹配
另外多个过滤条件时,先过滤比较稀疏的条件,能先过滤掉尽可能多的数据。
基于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提供了几种类型的得分函数:
random_score
随机打分,也就是每次查询出来的排序都不一样。
举一个例子:
GET /article/_search
{
"query": {
"function_score": {
"query": {"match_all": {}},
"random_score": {}
}
}
}
field_value_factor
该函数可以根据文档中的字段来计算分数。
示例:
GET /item/_search
{
"query": {
"function_score": {
"field_value_factor": {
"field": "price",
"factor": 1.2,
"modifier": "none"
}
}
}
}
属性 | 说明 |
---|---|
field | 要从文档中提取的字段 |
factor | 字段值乘以的值,默认为1 |
modifier | 应用于字段值的修复符 |
modifier的取值有如下多种:
Modifier | 说明 |
---|---|
none | 不要对字段值应用任何乘数 |
log | 取字段值的常用对数。因为此函数将返回负值并在0到1之间的值上使用时导致错误,所以建议改用log1p |
log1p | 将字段值上加1并取对数 |
log2p | 将字段值上加2并取对数 |
ln | 取字段值的自然对数。因为此函数将返回负值并在0到1之间的值上使用时引起错误,所以建议改用 ln1p |
ln1p | 将1加到字段值上并取自然对数 |
ln2p | 将2加到字段值上并取自然对数 |
square | 对字段值求平方 |
sqrt | 取字段值的平方根 |
reciprocal | 交换字段值,与1 / x相同,其中x是字段的值 |
field_value_score函数产生的分数必须为非负数,否则将引发错误。如果在0到1之间的值上使用log和ln修饰符将产生负值。请确保使用范围过滤器限制该字段的值以避免这种情况,或者使用log1p和ln1p
在ES中我们一般采用的分页方式是from+size的形式,当数据量比较大时,Es会对分页作出限制,因为此时性能消耗很大。
举个例子:一个索引分10个shards,然后一个搜索请求,from=990,size=10。
此时es会从每个shards上去查询1000条数据,尽管每条数据只有_doc_id和_score,但是经不住它量大啊。如果from是10000呢?就更加耗费资源了。
解决方案
1. 利用scroll遍历
scroll分为初始化和遍历两步。
步骤1:
POST /item/_search?scroll=1m&size=2
{
"query": { "match_all": {}}
}
步骤2:
GET /_search/scroll
{
"scroll": "1m",
"scroll_id" : "步骤1中查询出的值"
}
2. search after方式
在ES 5.x后提供的一种,根据上一页的最后一条数据来确定下一页的位置的方式。如果分页请求的过程中,有数据的增删改,也会实时的反映到游标上。这种方式依赖上一页的数据,所以不能跳页。
步骤1:
GET /item/_search
{
"query": {
"match_all": {}
},
"size": 2
,"sort": [
{
"_id": {
"order": "desc"
}
}
]
}
查询结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 6,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "item",
"_type" : "_doc",
"_id" : "uL6choEB9TD2fYkcrziw",
"_score" : null,
"_source" : {
"title" : "小米8手机",
"images" : "http://image.lagou.com/12479122.jpg",
"price" : 2688,
"createTime" : "2022-02-02 12:02:02"
},
"sort" : [
"uL6choEB9TD2fYkcrziw"
]
},
{
"_index" : "item",
"_type" : "_doc",
"_id" : "tr6YgYEB9TD2fYkcFzjY",
"_score" : null,
"_source" : {
"title" : "小米手机",
"images" : "http://image.lagou.com/12479122.jpg",
"price" : 2688,
"createTime" : "2022-02-01 12:02:02"
},
"sort" : [
"tr6YgYEB9TD2fYkcFzjY"
]
}
]
}
}
步骤2:
GET /item/_search
{
"query": {
"match_all": {}
},
"size": 2,
"search_after":["tr6YgYEB9TD2fYkcFzjY"]
,"sort": [
{
"_id": {
"order": "desc"
}
}
]
}
总结对比:
分页方式 | 性能 | 优点 | 缺点 | 场景 |
---|---|---|---|---|
from + size | 低 | 灵活性好,实现简单 | 深度分页问题 | 数据量比较小,能容忍深度分页问题 |
scroll | 中 | 解决了深度分页问题 | 无法反映数据的实时性(快照版本)维护成本高,需要维护一个scroll_id | 海量数据的导出需要查询海量结果集的数据 |
search_after | 高 | 性能最好 | 不存在深度分页问题能够反映数据的实时变更实现连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果 | 海量数据的分页 |
书山有路勤为径,学海无涯苦作舟
我正在使用python 2.7 当我尝试在其上运行epsilon操作时出现此错误, 这是我的代码 import cv2 import numpy as np img = cv2.imread('img
1 很多程序员对互联网行业中广泛讨论的“35岁危机”表示不满,似乎所有的程序员都有着35岁的职业保质期。然而,随着AI技术的兴起,这场翻天覆地的技术革命正以更加残酷且直接的方式渗透到各行各业。程序员
我有一个包含多个子模块的项目,我想列出每个子模块的相对深度 该项目: main_project submodule1 submodule1\submodule1_1 submo
我有一张彩色图像及其深度图,它们都是由 Kinect 捕获的。我想将它投影到另一个位置(以查看它在另一个视角下的样子)。由于我没有 Kinect 的内在参数(相机参数);我该如何实现? P.S:我正在
给出了这三个网址: 1) https://example.com 2) https://example.com/app 3) https://example.com/app?param=hello 假
这个着色器(最后的代码)使用 raymarching 来渲染程序几何: 但是,在图像(上图)中,背景中的立方体应该部分遮挡粉红色实体;不是因为这个: struct fragmentOutput {
我希望能够在 ThreeJS 中创建一个房间。这是我到目前为止所拥有的: http://jsfiddle.net/7oyq4yqz/ var camera, scene, renderer, geom
我正在尝试通过编写小程序来学习 Haskell...所以我目前正在为简单表达式编写一个词法分析器/解析器。 (是的,我可以使用 Alex/Happy...但我想先学习核心语言)。 我的解析器本质上是一
我想使用像 [parse_ini_file][1] 这样的东西。 例如,我有一个 boot.ini 文件,我将加载该文件以进行进一步的处理: ;database connection sett
我正在使用 Mockito 来测试我的类(class)。我正在尝试使用深度 stub ,因为我没有办法在 Mockito 中的另一个模拟对象中注入(inject) Mock。 class MyServ
我试图在调整设备屏幕大小时重新排列布局,所以我这样做: if(screenOrientation == SCREEN_ORIENTATION_LANDSCAPE) { document
我正在 Ubuntu 上编写一个简单的 OpenGL 程序,它使用顶点数组绘制两个正方形(一个在另一个前面)。由于某种原因,GL_DEPTH_TEST 似乎不起作用。后面的物体出现在前面的物体前面
static FAST_FUNC int fileAction(const char *pathname, struct stat *sb UNUSED_PARAM, void *mo
我有这样的层次结构: namespace MyService{ class IBase { public: virtual ~IBase(){} protected: IPointer
我正在制作一个图片库,需要一些循环类别方面的帮助。下一个深度是图库配置文件中的已知设置,因此这不是关于无限深度循环的问题,而是循环已知深度并输出所有结果的最有效方法。 本质上,我想创建一个 包含系统中
如何以编程方式在树状结构上获取 n 深度迭代器?在根目录中我有 List 每个节点有 Map> n+1 深度。 我已修复 1 个深度: // DEPTH 1 nodeData.forEach(base
我正在构建一个包含大量自定义元素的 Polymer 单页界面。 现在我希望我的元素具有某种主样式,我可以在 index.html 或我的主要内容元素中定义它。可以这样想: index.html
我正在尝试每 25 秒连接到配对的蓝牙设备,通过 AlarmManager 安排,它会触发 WakefulBroadcastReceiver 以启动服务以进行连接。设备进入休眠状态后,前几个小时一切正
假设有一个有默认值的函数: int foo(int x=42); 如果这被其他人这样调用: int bar(int x=42) { return foo(x); } int moo(int x=42)
是否可以使用 Javascript 获取 url 深度(级别)? 如果我有这个网址:www.website.com/site/product/category/item -> depth=4www.w
我是一名优秀的程序员,十分优秀!