文章转载自公众号 松花皮蛋的黑板报 , 作者 梁松华
一、ES是什么
Elasticsearch是一个基于Apache Lucene的开源搜索引擎,通过简易的API来隐藏Lucene的复杂性。ES的基本结构包括,Cluster集群(由n个节点组成),Node实例节点,Index索引(一系列documents的集合),Shard分片(索引的数据是分配到各个分片的),Replica备份节点(相应有Primay Shard主分片)
ES架构
ES和RDMS的区别
二、ES不是什么
1、ES不是数据库,不是可靠的数据存储系统。 [Indexing] A network partition can cause in flight documents to be lost 2、ES不是实时系统,数据写入成功只是translog成功,类似mysql的binlog,同理删除数据也不是实时的。其实ES内部有一个后台线程,定时将内存的数据写入到存储引擎中。当然可以写入数据后refresh,但是会重新打开所有索引文件,需要解压和刷缓存等等,性能影响极大 3、ES不是一个强一致性的系统。也就是说同样的query多次查询的数据可能会不一致。由于shard的主分片和副本是由独立的节点去刷新的,刷新的频率并不同步,这样同样的query发送到不同的分片(主片和从片)上看到的数据是不同的,导致的结果是查询到的数据也不完全同步。简单说,ES是一个最终一致性系统
三、Mapping映射
mapping相当于数据库的表结构,决定了ES在建立倒排索引、进行检索时对文档采取的相关策略,如数字类型、日期类型、文件类型等。在写数据前ES不强制要求创建mapping,因为ES有动态识别和创建的机制,但是非常不建议使用ES的动态识别和创建的机制,因为很多情况下这并非你所需要。推荐的做法是在写数据之前仔细的创建mapping mapping不能更新,存在的字段不能被更新和删除,不存在的字段可以添加 mapping冲突。同一索引不同类型同名字段的映射会冲突 https://www.elastic.co/blog/great-mapping-refactoring#conflicting-mappings
四、Template模板
模板是描述表结构(mapping)和表设置(setting)的数据结构,在ES中创建一个索引模板,其本质就是包含mapping及分片信息,模板中有个属性 template 表示该模板用来匹配什么样的索引。比如"template:timingsoa-*“,创建的所有以timingsoa开头的索引,将会应用这个模板,一般用来匹配按周期创建的索引,例如 timingsoa-20190417
代码语言:javascript复制{
"template": "timingsoa-*",
"settings": {
"index": {
"refresh_interval": "1s",
"number_of_shards": "4",
"number_of_replicas": "1"
}
},
"mappings": {
"timingsoa": {
"properties": {
"time": {
"format": "yyyy-MM-dd HH:mm:ss",
"type": "date"
},
"className": {
"type": "keyword"
},
"methodName": {
"type": "keyword"
},
"invokeNo": {
"type": "keyword"
},
"threadId": {
"type": "keyword"
},
"invokeIp": {
"type": "keyword"
},
"message": {
"analyzer": "ik_smart",
"type": "text"
},
"lineNumber": {
"type": "integer"
},
"invokeSerialNum": {
"type": "integer"
}
}
}
}
}
索引存在Linux服务器的文件系统上,背后是文件系统,不是类似HDFS的文件系统
五、Routing路由
rouing就是hash key,这个key决定写入和查询的分片id
六、Alia别名
代码语言:javascript复制PUT timingsoa
{
"mappings": {
"timingsoa": {
"properties": {
"time": {
"format": "yyyy-MM-dd HH:mm:ss",
"type": "date"
},
"className": {
"type": "keyword"
},
"methodName": {
"type": "keyword"
},
"invokeNo": {
"type": "keyword"
},
"threadId": {
"type": "keyword"
},
"invokeIp": {
"type": "keyword"
},
"message": {
"analyzer": "ik_smart",
"type": "text"
},
"lineNumber": {
"type": "integer"
},
"invokeSerialNum": {
"type": "integer"
}
}
},
"aliases": {
"admin_w": {},
"admin_r": {}
}
}
为什么需要别名https://www.elastic.co/guide/en/elasticsearch/guide/current/index-aliases.html
七、ES集群
1、节点发现
多播方式,也是ES的默认使用方式,但是无法跨网络组建集群。另外一种是单播方式,可以跨网络组建集群
2、主节点选举
所有配置有master:true的节点,根据节点id进行排序,然后取出第一个作为主节点
3、存活检测
有两种错误检测方式,一种是master节点ping集群中所有其他的节点来验证他们是否存活,另一种是每个节点ping master节点来验证它是否存活
4、容灾
ES中的index,首先会进行分片,每一个分片数据一般都会有自己的副本数据,ES分配分片的策略会保证同一个分片数据和自己的副本不会分配到同一个节点上 当集群中的某一节点宕机后,ES的master在ping该节点时通过一定的策略会发现该节点不存活;此时,ES开启恢复过程,恢复的策略如下: 恢复的目标是保证集群中分片的副本数不变
4.1 恢复的目标是保证集群中分片的副本数不变 4.2 如果宕机的节点上承载某分片的主分片,那么此时(恢复过程)会将该分片分配在其他节点上的某一副本提升为主分片(记住:同一分片和其副本总是不在同一节点上,保证有对应的副本可供提升的) 4.3 根据4.1保证副本数不变,如果宕机的节点承载某分片的副本,那么ES会在其他非宕机节点上用主分片复制一个副本 4.4 整个过程不影响集群的读写功能;但是由于多了复制分片和迁移分片的过程,集群的读写性能受影响
5、扩容和缩容
整个过程不影响集群的读写功能,但是由于多了复制分片和迁移分片的过程,集群的读写性能受影响
八、ES写入文档过程
图片来自https://zhuanlan.zhihu.com/p/34669354
另外ES没有原地update的能力,所有的update都是标记删除老文档。并发update同一条document,ES内部采用了乐观并发的处理,并发修改的操作直到最后要提交是才加锁检查版本号,如果发现修改之前获取的版本号已经改变(即已经被人修改),那么会抛出这个异常,然后由用户决定如何处理该异常
九、ES查询文档过程
图片来自 https://zhuanlan.zhihu.com/p/34674517
当然你可以指定perference,指定在主分片查询或者先主后副等等
十、ES索引创建过程
十一、ES新节点加入过程
十二、ES规范
12.1、Mapping设计规范
1、禁用mapping的dynamic 当 indices的mapping已经确定时(即不会改变时),强烈建议将 dynamic设置为false,这样可以避免非法数据被ES索引 2、避免不必要的tokenizer ES在进行analyzer时,会经过以下三个步骤:character -> tokenizer -> token filer,当text不需要进行 tokenizer时,需要设置index:not_analyzed(5.X版本设置为type:keyword) 3、nested结构 结构化的JSON文档会平整成索引内的简单键值对,会造成交叉对象匹配,数据间的关联性就会丢失。而nested将list里的每个doc单独变成子文档进行存储,避免了这个问题 https://www.elastic.co/guide/en/elasticsearch/guide/master/nested-objects.html 4、列式存储doc_values(大部分type默认为true) ES提供了doc_value属性,如果对某字段设置了doc_value,那么该字段被用来排序或者聚合的情况下,并不会加载到内存而是仍然从硬盘读取,防止OOM。实际上Lucene在构建索引时,会额外建立一个有序的基于document=>field value的映射列表 5、TTL的使用经验 ES2.X对TTL已经是deprecated, 在最新版本5.X里已经是remove了
12.2、索引设计规范
1、设置合理的number_of_shards number_of_shards决定indices在ES集群中,如何均衡的分布在各个 data node,而使用ES进行搜索时,ES会并行的查询分布在各个data node的shard(而都在同一个节点的 shards,只能进行串行的操作),最后将各个data node返回的数据进行聚合,并返回给客户端 2、合理设置 routing key 可以通过合理设置routing key,可以避免在查询时查询多个shards。因为相同的routing key都在同一个shard 3、定期创建索引 有些业务需要定期创建索引,比如日志等 4、索引数据删除 建议基于索引删除数据,新版本已不推荐使用type,应考虑多建索引 5、Type 在很多数据能分为独立index的情况下,不要放到一个index里用type去分。但是type能减少index的数量,而且在父子文档和文档映射相似的情下,使用type更佳,因为搜索一个index下的多个type,和只搜索一个 type相比没有额外的开销,需要合并结果的分片数量是一样的 https://www.elastic.co/blog/index-vs-type
12.3、查询规范
1、search type不要用dfs_query_then_fetch,用query_then_fetch足够 这里再提下前面的ES默认查询流程,也就是scatter多分片-gather-sort,而dfs_query_then_fetch多了初始化发散(initial scatter),进行真正的查询之前,先把各个分片的词频率和文档频率收集一下,然后进行词搜索的时候,各分片依据全局的词频率和文档频率进行搜索和排名,查询效率慢但是精度高 2、尽可能的用filter,它快 3、使用scroll/scan,相当于数据库的游标方式代替深分页(size=10&from=10000) 4、不要使用post filter,post filter的内部实现相当于数据库的扫表,因此非常非常慢 5、非常不推荐依赖delete-by-query这个插件做删除操作 性能很差,对集群也有不良影响,推荐按日期去建索引,速度快、性能高
十三、其他
1、倒排索引 倒排索引一般表示为一个关键词,然后是它的频度,位置等,类似图书的目录 2、TF-IDF 词频(TF)表示一个给定词语t在给定文档d中出现的概率,概率越高重要程度越高 文档频率(DF)表示文档集中包含给定词语t的所有文档数目,显然TF越高同时DF越小区分度就越高。反之,就类似停止词,"你,是,的"等等