Elasticsearch原理解析与性能调优
基本概念
定义
- 一个分布式的实时文档存储,每个字段 可以被索引与搜索
- 一个分布式实时分析搜索引擎
- 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据
用途
- 全文检索
- 结构化搜索
- 分析
VS传统数据库
- 传统数据库
- 提供精确匹配
- ES
- 提供精确匹配
- 全文检索
- 处理同义词
- 给文档相关性评分
- 生成分析与聚合数据
- 实时
专有名词
索引(名词)
类似于数据库
索引(动词)
类似于insert。例如索引一个文档到一个索引
倒排索引
默认每个属性都会有一个倒排索引,可以设置属性不被索引,它只能被覆盖,不能被修改
类型
类似表,同一索引的不同类型,可以拥有不同的字段,但应该拥有大部分相似的字段。它可以包含大小写,不能包含句号,不能以下划线开头,长度限制为256.
Id
文档的id,可以在生成文档时指定或自动生成,自动生成的ID,在大部分情况下多个节点的时候唯一。如果在创建文档时,ID冲突,服务器会返回409
文档
类似于记录,文档只能被替换,而不能被修改,文档的字段类型需要一致,否则无法进行精确匹配
精确值字段
对于数字,日期,布尔和一个not_analyzed字段,进行查询,会适用精确匹配
全文搜索字段
否则,进行相关性搜索
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
例如
- megacorp是索引
- employee是类型
- 1是文档id
- json内容是文档
交互
RESTful API
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
VERB |
适当的 HTTP 方法 或 谓词 : GET 、 POST 、 PUT 、 HEAD 或者 DELETE 。 |
---|---|
PROTOCOL |
http 或者 https (如果你在 Elasticsearch 前面有一个 https 代理) |
HOST |
Elasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点。 |
PORT |
运行 Elasticsearch HTTP 服务的端口号,默认是 9200 。 |
PATH |
API 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如:_cluster/stats 和 _nodes/stats/jvm 。 |
QUERY_STRING |
任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读) |
BODY |
一个 JSON 格式的请求体 (如果请求需要的话) |
例子
request:
curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}
'
response:
{
"count" : 0,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
}
}
检索文档功能
获取一个文档
GET /megacorp/employee/1
简单查询
GET /megacorp/employee/_search #查询前十条记录
GET /megacorp/employee/_search?q=last_name:Smith #查询smith的前十条记录
表达式查询
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
GET /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
全文检索
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
短语查询
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
分析(类似于聚合group by)
request:
GET /megacorp/employee/_search
{
"aggs" : {
"all_interests" : {
"terms" : { "field" : "interests" },
"aggs" : {
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}
}
response:
...
"all_interests": {
"buckets": [
{
"key": "music",
"doc_count": 2,
"avg_age": {
"value": 28.5
}
},
{
"key": "forestry",
"doc_count": 1,
"avg_age": {
"value": 35
}
},
{
"key": "sports",
"doc_count": 1,
"avg_age": {
"value": 25
}
}
]
}
修改文档
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
旧文档不会马上删掉
新文档会被索引
文档的version会加一
删除文档
DELETE /megacorp/employee/123
文档的version依然会加一
分布式特性
ES自动执行的分布式动作
- 分配文档到不同的容器 或 分片 中,文档可以储存在一个或多个节点中
- 按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡
- 复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失
- 将集群中任一节点的请求路由到存有相关数据的节点
- 集群扩容时无缝整合新节点,重新分配分片以便从离群节点恢复
水平扩展VS垂直扩展
ES对水平扩展是友好的,通过购置更多的机器,可以更好的使用ES的分布式功能
集群
集群拥有一个或多个节点,当有节点加入或者退出集群时,集群会重新平均分配所有数据的分布
主节点功能
增加/删除索引
增加/删除节点
不涉及文档的变更和搜索,因此单一的主节点不会成为集群的性能瓶颈
索引分片的元数据在每个ES节点都有存储,每个节点在接到请求后,都知道到哪台ES node找到数据,通过转发请求到ES node所在的机器
一个分片的最大文档数:(2^31-128)
一个索引的主分片数在建立时被确定,且无法修改:因为文档的存储是用shard = hash(routing) % number_of_primary_shards来确定文档的位置的。routing默认是id,也可以自定义
分片的副本数可以随时修改
建立索引
PUT /blogs
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
故障转移
增加一台机器到集群
删除节点
分布式写入冲突
对于多个client的写入ES,有可能造成写入冲突,导致数据的丢失
在一些场景下,数据丢失是可以接受的
但是在某些场景下,是不允许的。
悲观控制并发
传统数据库的控制方式。通过对记录加锁,来实现并发的串行执行
乐观并发控制
ES采用乐观控制
所谓乐观控制,就是服务器假设大部分情况下,是不会发生冲突的,如果发生冲突,则拒绝修改,客户端可以需要通过重新获取并重试进行处理。
过程如下图
分布式文档存储
确定文档位于那个shard
shard = hash(routing) % number_of_primary_shards
API支持带routing参数,来自定义路由,来确保相关文档路由到同一个分片
以ID新建,写入和删除文档
一致性保证
- none: 主分片活跃,允许写入
- all: 在所有分片活跃,允许写入
- quorum: 半数以上节点活跃,允许写入
如果暂时没有足够的分片活跃,ES会等待,默认等待1分钟,可以通过参数timeout改变这个值,如果超时,则失败返回
新索引默认有
1
个副本分片,这意味着为满足规定数量
应该 需要两个活动的分片副本。 但是,这些默认的设置会阻止我们在单一节点上做任何事情。为了避免这个问题,要求只有当number_of_replicas
大于1的时候,规定数量才会执行。
以ID检索文档
与上图类似
以ID更新文档
与上图类似,但在更新完文档后,会重建索引
在局部更新文档的时候,主分片会以整份文档来同步给副本,来保证数据的完整性
通过条件获取多个文档
搜索
返回特殊字段
GET /_search
{
"hits" : {
"total" : 14,
"hits" : [
{
"_index": "us",
"_type": "tweet",
"_id": "7",
"_score": 1,
"_source": {
"date": "2014-09-17",
"name": "John Smith",
"tweet": "The Query DSL is really powerful and flexible",
"user_id": 2
}
},
... 9 RESULTS REMOVED ...
],
"max_score" : 1
},
"took" : 4,
"_shards" : {
"failed" : 0,
"successful" : 10,
"total" : 10
},
"timed_out" : false
}
took
执行的毫秒数
_shards
查询分片的状态,例如有几个分片是失败的,几个是成功的
timeout
可以通过在查询设定超时,如果查询超过时间,则只返回已经成功获得的数据,剩余的数据将丢弃
_index
数据来源的Lucene索引,文档的每一个字段,都拥有一个不同的Lucene索引
在查询中可以指定Lucene的索引,默认是不指定的,所以会查询该文档的所有索引,并汇总结果。如果指定,则会限定仅在指定的Lucene索引中查询数据
分页
GET /_search?size=5&from=5
此方式只适用于浅分页,如果查询过深,会导致严重的性能问题。
因为例如查询size为5,from=10000。那么ES会从各分片中都查询10005条记录,如果有100个shard,那么就会有100*10005条记录,ES再对这100*10005排序,并仅返回5条记录
深分页
使用游标scroll
它在ES中建立了一个有有效期的快照,提供给scroll进行数据的深度查询
倒排索引
对一下文档进行倒排:
- The quick brown fox jumped over the lazy dog
- Quick brown foxes leap over lazy dogs in summer
得到:
Term Doc_1 Doc_2
-------------------------
Quick | | X
The | X |
brown | X | X
dog | X |
dogs | | X
fox | X |
foxes | | X
in | | X
jumped | X |
lazy | X | X
leap | | X
over | X | X
quick | X |
summer | | X
the | X |
------------------------
倒排面临的挑战
- Quick跟quick,用户有可能认为它们是相同的,也有可能认为是不同的
- dog和dogs非常接近,在相关性搜索时,它们应该都被搜索到
- jump和leap是同义词,在相关性搜索时,它们应该都被搜索到
理解相关性
相关性的分数是一个模糊的概念。没有精确值,没有唯一正确的答案。是一种根据各种规则对文档进行的一种量化的估计。
它评分的准则如下:
检索词频率
检索词在该字段出现的频率越高,分数越高
反向文档频率
检索词在索引出现的频率越高,分数越低
字段长度准则
字段的长度越长,分数越低
相关性破坏
在使用全文检索某个关键字的时候,会出现,相关度低的文档的得分高于相关度高的文档的得分。
例如检索词milk。索引内有两个主分片,milk在P1出现了5次,在P2出现了6次。由于P1和P2的词分布不一样。
P1的词量比P2的词量高,那么milk算在P1出现占比小,导致在P1得相关性得分高,而在P2,占比da,导致在P2的相关性得分低。
原因
是因为局部数据分布不均匀导致的
解决方法
- 插入更多的文档
- 使用?search_type=dfs_query_then_fetch进行全局评分。但会有严重的性能问题。不推荐使用。
查询过滤bitset
每次使用检索词查询,都会为检索词建立一个bitset,bitset包含了匹配的文档的序号。在热搜索的检索词,ES会对这些bitset有针对的进行缓存,而不用在再次查询的时候,重新查找倒排索引。
对于多个查询可以有下图
当倒排索引重建的时候,bitset在缓存会自动失效
缓存的策略
- 最近256次被使用的bitset,会被缓存
- 段内记录小于1w的,不会被缓存
索引管理
创建索引
可以显式创建,也可以隐式创建。
在大集群下,索引的创建,涉及元数据的同步,有可能导致集群负载的大量增加。此时需要禁用索引的隐式创建
action.auto_create_index: false
删除索引
删除索引,会涉及大量数据的删除,如果用户意外地试图通过一条命令,把所有索引删掉,这可能导致可怕的后果
通过禁用此操作,可以设置如下
action.destructive_requires_name: true
分析器
每个索引都可以设置自己的分析器,分析器的用途主要是在全文索引上面,通过对不同的语言,使用不同的分词,不同的词转换来构造倒排索引和计算相关性。
分片
倒排索引的不变性
好处
- 一旦被读入系统缓存,就会一直留在那里,直到LRU算法把不常用的倒排索引剔除。这对ES的读取性能提供了非常大的提升
不好
- 新的文档加入,不能增量更新,只能重建索引并替换
如何保证新数据能实时能查询到
用更多的索引。
对于新的文档,不马上重建索引,而是通过新增额外的索引。在查询数据时,通过轮询所有的索引,并合并结果返回。
ES并不是严格意义上的实时,准确来说是准实时,由于data从插入到建立倒排索引这段时间,新数据是不能访问的
聚合
像数据库的group by。只是语法不一样。功能相通
应用层性能调优
调大 refresh interval
默认刷新时间是1s,每次刷新都会有一次磁盘写入,并创建一个新的段。通过设置更大的刷新时间,可以让磁盘写入的次数更低,写入的段更大。减少段合并的次数。
禁止OS把ES置换出去
OS的内核会在内存紧张的时候,把进程置换到外村。而对于性能跟内存强相关的ES来说,置换到外存是致命的。通过设置进程在内核的参数,禁止置换,可以避免OS的这种动作
预留大量的文件系统缓存给ES
由于ES大部分数据的不变性,使得ES的大部分磁盘操作,都可以通过文件系统的缓存来加快速度。一旦ES的倒排索引和数据缓存到系统,如果没有其他进程的干扰,而且是比较频繁访问的数据,则会一直驻留在系统缓存,使得ES的大部分操作都是走内存的。一般来说,分配一半的内存给文件系统,是合适的。
使用自动生成ID
如果指定ID,ES会在集群内检查是否ID已经存在,这对大集群来说,是昂贵的。如果ID是自动生成的,ES会跳过检查,直接插入文档
更好的硬件
- 更大的内存
- SSD
- 本地磁盘
不要使用join关联查询
ES不适合做关联查询,会导致严重的性能问题。
如果业务一定要join,可以把关联的数据都写到一个索引内,或者通过应用程序来做关联的动作。
强制merge只读索引
merge成一个单一的段,会得到更好的性能
增加副本
有更多的机器,通过提高副本数,可以提高读效率
不要返回大数据
ES不适合这场景
避免稀疏
不要把不相关的信息存入同一个索引
数据预热
对于热点数据,可以通过一个客户端请求ES,让数据先占据filesystem cache。
冷热数据分离
冷热数据部署在不同的机器,可以让热数据在缓存内不会被冷数据冲走
内核层性能调优
限流
如果ES出现高负载的请求,ES的协调节点会累积大量的请求在内存在等待处理,随着请求数的增加,协调节点的内存占用会越来越大,最后导致OOM。
通过限流,可以有效缓解。
大查询
如果客户端发来了一个复杂的查询,使得需要返回的数据异常的大,这也会导致OOM问题。
通过修改内核,让如果请求的内存占用超过系统可以承受的范围,则截断来解决
FST过大引发OOM
FST是对倒排索引在内存的索引,它通过前缀状态机的方法,快速的定位检索词在倒排索引的磁盘位置,达到减少磁盘访问次数而加快检索速度的目的。
但由于FST是常驻内存的,如果倒排索引达到一定规模时,FST必然会引起OOM问题。而且FST是存放在JVM堆内内存的。堆内内存的上限时32G。
而10 TB的数据就需要10G到15G的内存来存放FST。
- 通过把FST的存储放到堆外内存
- 通过LRU算法来管理FST,对不常用的FST置换出内存
- 修改ES访问FST的逻辑,使得ES可以从堆内直接访问堆外的FST
- 在堆内增加FST的cache,加快命中速度
Ref:
- https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
- https://zhuanlan.zhihu.com/p/99718374
- https://blog.csdn.net/wangnan9279/article/details/79287907/
Elasticsearch原理解析与性能调优的更多相关文章
- ElasticSearch中的JVM性能调优
ElasticSearch中的JVM性能调优 前一段时间被人问了个问题:在使用ES的过程中有没有做过什么JVM调优措施? 在我搭建ES集群过程中,参照important-settings官方文档来的, ...
- Elasticsearch索引和查询性能调优的21条建议
Elasticsearch部署建议 1. 选择合理的硬件配置:尽可能使用 SSD Elasticsearch 最大的瓶颈往往是磁盘读写性能,尤其是随机读取性能.使用SSD(PCI-E接口SSD卡/SA ...
- Dubbo性能调优参数及原理
本文是针对 Dubbo 协议调用的调优指导,详细说明常用调优参数的作用域及源码. Dubbo调用模型 常用性能调优参数 参数名 作用范围 默认值 说明 备注 threads provider 200 ...
- elasticsearch性能调优
转载 http://www.cnblogs.com/hseagle/p/6015245.html 该es调优版本可能有低,但是思想主体不变,不合适的参数可以自己找最新的版本相应的替代,或者增删 ela ...
- Kafka跨集群迁移方案MirrorMaker原理、使用以及性能调优实践
序言Kakfa MirrorMaker是Kafka 官方提供的跨数据中心的流数据同步方案.其实现原理,其实就是通过从Source Cluster消费消息然后将消息生产到Target Cluster,即 ...
- [Spark性能调优] 第一章:性能调优的本质、Spark资源使用原理和调优要点分析
本課主題 大数据性能调优的本质 Spark 性能调优要点分析 Spark 资源使用原理流程 Spark 资源调优最佳实战 Spark 更高性能的算子 引言 我们谈大数据性能调优,到底在谈什么,它的本质 ...
- ElasticSearch 2 (12) - Shard数调优(ElasticSearch性能)
ElasticSearch 2 (12) - Shard数调优(ElasticSearch性能) 摘要 当创建一个索引的时候,我们经常会面对一个问题:要为索引分配多少个shard?多少个replica ...
- spark 性能调优(一) 性能调优的本质、spark资源使用原理、调优要点分析
转载:http://www.cnblogs.com/jcchoiling/p/6440709.html 一.大数据性能调优的本质 编程的时候发现一个惊人的规律,软件是不存在的!所有编程高手级别的人无论 ...
- 性能调优的本质、Spark资源使用原理和调优要点分析
本课主题 大数据性能调优的本质 Spark 性能调优要点分析 Spark 资源使用原理流程 Spark 资源调优最佳实战 Spark 更高性能的算子 引言 我们谈大数据性能调优,到底在谈什么,它的本质 ...
随机推荐
- 021 01 Android 零基础入门 01 Java基础语法 03 Java运算符 01 赋值运算符
021 01 Android 零基础入门 01 Java基础语法 03 Java运算符 01 赋值运算符 本文知识点:Java中的赋值运算符 赋值运算符 赋值运算符从右往左运算 赋值运算符左边不能是常 ...
- Linux网卡命名规则
网卡命名 一.为什么需要这个 服务器通常有多块网卡,有板载集成的,同时也有插在PCIe插槽的.Linux系统的命名原来是eth0,eth1这样的形式,但是这个编号往往不一定准确对应网卡接口的 ...
- Cesium.Viewer
<!DOCTYPE html><html lang="en"><head> <meta charset="utf-8" ...
- java高级&资深&专家面试题-行走江湖必备-持续更新ing
行走江湖必备一份面试题,这里给大家整理了一套.0面试官最喜欢问的问题或者出场率较高的面试题,助校招或者社招路上的你一臂之力! 首先我们需要明白一个事实,招聘的一个很关键的因素是在给自己找未来的同事,同 ...
- PHP代码审计03之实例化任意对象漏洞
前言 根据红日安全写的文章,学习PHP代码审计的第三节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完相关知识点,会用一道CTF题目来加深巩固.之前分别学习讲解了in_arr ...
- python 递归求和
例子: 1 mylist = [1,2,3,4,5] 2 def func(var): 3 if var == []: 4 print('列表为空') 5 return 0 6 return var[ ...
- pytest文档47-allure报告添加用例失败截图
前言 使用 selenium 做 web 自动化的时候,很多小伙伴希望用例失败的时候能截图,把异常截图展示到allure报告里面. pytest 有个很好的钩子函数 pytest_runtest_ma ...
- Java9系列第6篇-Stream流API的增强
我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...
- 【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
问题描述 在使用Azure Redis时,遇见Read Timed out异常, Redis的客户端使用的时jedis.问题发生时,执行redis部分指令出错,大部分get指令,set指令能正常执行. ...
- Django折腾日记(django2.0)
新建项目 django-admin startproject mysite 运行 python manage.py runserver 创建一个应用 python manage.py startapp ...