上厅房,下厨房,ElasticSearch有的忙

强烈建议先读一下本公众号《也浅谈下分布式存储要点》,对ES会有更好的认识。ES融合了倒排索引、行存、列存的诸多特色,已经再也不是一个简单的全文搜索引擎。程序员

ES是从厨房里走出来的,原型是Shay Banon给妻子作的食谱搜索引擎,一开始便充满了爱的味道。算法

儿女情长什么的,最影响程序员发挥了!json

最终,ElasticSearch斩断情愫,成为一个基于Lucene的分布式存储。安装后访问其页面,会显示:”You Know, for Search“,这句平凡的语句无心中透露的自信,深于一切怀疑和平淡。缓存

几点说明

几点说明
索引中的Type定义会引发诸多歧义,已经在6.0版本废弃
做为搜索时,是类实时性系统,写入到读取之间存在延迟,通常为1秒 ;但看成为存储时,是RT的
ES索引的分片数一旦肯定不可改变,既成事实的Mapping也是
你优化了写入,就可能干扰了查询和可靠性,大多时候不可兼得
你会常常碰到OOM,显然ES是内存大户,缺少这方面的保护
ES没有事务

基础概念

ES是典型的分布式系统。包含一到多个内存型节点。有分片,也有副本,可靠性高,自适应能力强。ES采用多机并行能力来进行扩展,是创建在一系列层级数据结构上的。这些抽象做为一个全局的路由表,存在于每一个运行的实例上,给了ES强大的功能和扩展能力。 bash

名词 解释
Cluster 集群,有着统一的名称。包含一个master节点,和落干其余类型节点。通常为对等节点
Node 节点,ES运行实例,指定集群中的任意一台便可加入
Index 索引,逻辑命名空间。类比DBMS中的
Shard 分片,每一个索引包含一个或多个分片,用来将数据分布在不一样实例上
Replica 每一个分片又分为主副本和从副本,用来保证HA和Failover。每个replica为具体lucene实例
Segment 段,倒排索引的子集,会不断的合并以减小资源占用
Document 文档,为具体存放的某条记录,好比某条订单信息
Feild 字段,文档中的某个字段,能够有不一样的类型

索引类型

倒排索引

不少同窗有一个误解,觉得ES是一个全文搜索引擎,那么就只有倒排索引这一种索引类型,那是错的。数据在写入es时,会产生多份数据用于不一样查询方式,使用的索引结构也不相同。数据结构

ES默认是对全部字段进行索引的(也就是倒排索引),若是不须要,能够在mapping中将index属性设置为no;若是字段须要精确查找,则设置为not_analyzedapp

为了增长倒排索引的Term查找速度,ES还专门作了Term index,它的本质是一棵Trie(前缀)树(使用FST技术压缩)。async

_all是一个特殊的字段,能够根据某个关键词,搜索整个文档内容(而不是某个字段),这个默认是关闭的。分布式

列式存储

按照以上的倒排索引结构,查找包含某个term的文档是很是迅捷的。若是要对这个字段进行排序的话,倒排索引就捉襟见肘了,须要使用其余的存储结构进行索引。性能

ES使用冗余的方式进行解决这个问题,它存储了另外一份数据,也就是Doc Values。能够说Doc Values是一个列式存储结构,适合排序、聚合操做等。放在内存中的fielddata功能和它相似,但没有内存容量的限制,大数据量优先使用。

到此为止,ES已经默认按照不一样的结构存储了两份数据了。但若是你不须要,仍是能够禁用的。一样是在mapping玩具中,给字段赋予属性"doc_values":false便可。

假如你用的是ELKB系列,倒排索引根本就没用到。

式存储

而做为行存_source字段,以json方式存储了原始文档。通常是不须要关闭的。但若是你的文档字段比较多,根据搜索后查出列表,再根据列表的数据到其余存储获取,那么就能够将_source关掉。相似的,设置"_source"{"enabled":false}便可。

若是只想要几个字段被存储,可使用include。

"_source":{
    "includes":["field1","field2"]
}
复制代码

ES有太多的这种细化的自定义,再也不详叙。

写入过程

找到分片

某个分片具体在哪一个节点上,由ES自行决定。每一个节点都缓存了这些路由信息,因此,你的请求发送到任何一个ES节点上,均可以执行。

ES选择的分片路由算法是Hash,这决定了它的分片数一旦肯定,不可更改。由于一旦变了,路由的数据就彻底不正确了。

若是Request中指定了路由条件,则直接使用Request中的Routing,不然使用Mapping中配置的,若是Mapping中无配置,则使用默认的_id字段值。

默认参与Hash计算的字段是 _id,使用ES自带的生成器能较好的平均数据,使用自定义的id可能会产生数据倾斜。

shard = hash(routing) % number_of_primary_shards
复制代码

剩下的,就是单机索引的事了。

单机Shard的写入过程

ES的写入性能能够很高(尤为是批量写入),取决于你的配置。一个文档要写入索引,直到读可见,要通过一系列的缓冲和合并。咱们拿ES官方博客的一张图来讲明。

ES的底层存储是Lucene,包含一系列的反向索引。这样的一批索引的信息就是上面提到的段(segment)。但记录不会直接写入段,而是先写入一个缓冲区。

当缓冲区满了,或者在缓冲区呆的够久,达到了刷新时间(划重点),会一次性将缓冲区的内容写进段中。这也是为何refresh_interval属性的配置会严重的影响性能。若是你不要很高的实时性,不妨将其配置的大一点。

缓冲区默认使用堆空间的10%,最小值为48mb(针对于分片的)。若是你的索引多且写入重,这部份内存的占用是可观的,能够适当加大。


问题是Segment是不可变的,删除、更新操做,并不能在原来的段上进行。ES对待全部的操做都是类似的,并不区别对待,删除和更新,有着和写入同样的mege过程。这会生成大量的段,每一个段会占用一个文件句柄,会浪费大量资源。ES有专门的进程负责段的自动合并,咱们不须要手动干涉。

段的合并会浪费大量的I/O和CPU资源,有tiered(默认)、log_byte_sizelog_doc三种合并策略,每种策略都有各自的配置参数。惋惜的是,索引一旦肯定,策略就不能更改了。调整这些参数,大多状况下效果显著。

经常使用的配置参数是执行归并的线程数,max_bytes_per_sec已经再也不使用了。

index.merge.scheduler.max_thread_count
复制代码

若是你的I/O太重,能够适量减小此值的大小。


即然是先写到缓冲区,就有丢的可能,好比忽然断电。为了解决此问题,ES在写Buffer的同时,也将数据写入一个叫作translog的文件。这个文件是顺序写,因此速度比较快。translog是故障恢复时,回放故障发生前夕数据的惟一途径。这些数据,没有机会可以写入到Lucene中。

将translog的写入周期改为async的,而不是基于请求的,会显著减小I/O占用。

操做系统在将磁盘写入文件时,也会有相应的buffer cache,这与其余DB如MySQL、PG的工做方式是同样的,使用fsync保证文件可以刷到磁盘上,很少描述。

节点类型

ES按照不一样的用途和场景,划分了不一样的节点类型。

Master Node 有资格被选择为主节点,而后控制整个集群

Data Node 该节点可以保存数据和执行操做

Tribe Node 部落节点,能够链接多个集群,对外提供统一的入口

Ingest Node 定义一个pipeline来处理数据,能够替代logstash中的某些功能

客户端节点 不保存数据也不协调集群,仅响应用户请求,将其发送到其余节点

End

ES经过冗余多份数据达到不一样的用途,开箱即用。开箱即用的意思也就是认识成本低,学习成本大,先把你吸引进门再说。

ES不是一匹好驯服的野马。一般,它并不像官方和其余PPT宣传的那样无所不能,你可能常常在OOM和分片移动中度日。

但愿在使用ES以前,可以了解它的一些底层设计结构。这样,在遇到一些瓶颈的限制之后,可以了解到它为何有这样或者那样的反应;另外在作一些解决方案的时候,可以多给本身一点底气。

不过话说回来,上得了厅房,下得了厨房的女人,你们仍是都喜欢~

相关文章
相关标签/搜索