一、概念
- 集群(Cluster)一组拥有共同的 cluster name 的节点。
- 节点(Node) 集群中的一个 Elasticearch 实例。
- 索引(Index) 相当于关系数据库中的database概念,一个集群中可以包含多个索引。这个是个逻辑概念。
- 主分片(Primary shard) 索引的子集,索引可以切分成多个分片,分布到不同的集群节点上,一个分片是一个 Lucene 的实例,它本身就是一个完整的搜索引擎,文档不会跨分片存储。分片对应的是 Lucene 中的索引。
- 副本分片(Replica shard)每个主分片可以有一个或者多个副本。
- 类型(Type)相当于数据库中的table概念,mapping是针对 Type 的。同一个索引里可以包含多个 Type。
- Mapping 相当于数据库中的schema,用来约束字段的类型,不过 Elasticsearch 的 mapping 可以自动根据数据创建。
- 文档(Document) 相当于数据库中的row。
- 字段(Field)相当于数据库中的column。
- 分配(Allocation) 将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。
二、架构
Gateway:Gateway是Elasticsearch用来存储索引的文件系统,支持多种文件类型,LocalFileSystem是存储在本地的文件系统,SharedFileSystem是共享存储,也可以使用Hadoop的HDFS分布式存储,也可以存储在AmazonS3云服务上。
Lucene框架:Gateway的上层是一个分布式的Lucene框架,Elasticsearch的底层API是由Lucene提供的,每一个Elasticsearch节点上都有一个Lucene引擎的支持。
Elasticsearch的模块:Lucene之上是Elasticsearch的模块,包括索引模块、搜索模块、映射解析模块等。River相当于第三方插件,用来导入第三方数据源,在2.X之后己经不再使用。
Discovery:Elasticsearch模块之上是Discovery、Scripting和第三方插件。Discovery是Elasticsearch的节点发现模块,不同机器上的Elasticsearch节点要组成集群需要进行消息通信,集群内部需要选举master节点,这些工作都是由Discovery模块完成的。Scripting用来支持JavaScript、Python等多种语言,可以在查询语句中嵌入,使用Script语句性能稍低。Elasticsearch也支持多种第三方插件。
Elasticsearch的传输模块和JMX:再上层是Elasticsearch的传输模块和JMX。传输模块支持Thrift,Memcached、HTTP,默认使用HTTP传输。几t1X是Java的管理框架,用来管理Elasticsearch应用。
交互接口:最上层是Elasticsearch提供给用户的接口,可以通过阻RESTfulAPI和Elasticsearch集群进行交互。
三、副本与分片
分片(Shard)以及副本(Replica) 分布式存储系统为了解决单机容量以及容灾的问题,都需要有分片以及副本机制,同时分片和副本也提供负载均衡等支持。Elasticsearch 没有采用节点级别的主从复制,而是基于分片。ElasticSearch当前还未提供分片切分(shard-splitting)的机制,只能创建索引的时候静态设置,所以提前合理分配索引分片尤为重要,否则,当发现需要调整分片数量, 只能重新对数据进行创建索引。
提高分片数量有利于提高整体可用性,但过度分片会引起以下问题:
- 每个分片本质上就是一个Lucene索引, 因此会消耗相应的文件句柄, 内存和CPU资源
- 每个搜索请求会调度到索引的每个分片中. 如果分片分散在不同的节点倒是问题不太. 但当分片开始竞争相同的硬件资源时, 性能便会逐步下降
- ES使用词频统计来计算相关性. 当然这些统计也会分配到各个分片上. 如果在大量分片上只维护了很少的数据, 则将导致最终的文档相关性较差
分布式下的索引和搜索
- 创建索引:文档被索引到随机的主分片,然后转发到它的副本分片。
-
搜索请求:搜索在完整的分片集合上运行,无论他们是主分片还是副本分片,然后聚集结果返回。
四、服务发现以及选主 ZenDiscovery
- 节点启动后先ping(这里的ping是 Elasticsearch 的一个RPC命令。如果 discovery.zen.ping.unicast.hosts 有设置,则ping设置中的host,否则尝试ping localhost 的几个端口, Elasticsearch 支持同一个主机启动多个节点)
- Ping的response会包含该节点的基本信息以及该节点认为的master节点。
- 选举开始,先从各节点认为的master中选,规则很简单,按照id的字典序排序,取第一个。
- 如果各节点都没有认为的master,则从所有节点中选择,规则同上。这里有个限制条件就是 discovery.zen.minimum_master_nodes,如果节点数达不到最小值的限制,则循环上述过程,直到节点数足够可以开始选举。
- 最后选举结果是肯定能选举出一个master,如果只有一个local节点那就选出的是自己。
- 如果当前节点是master,则开始等待节点数达到 minimum_master_nodes,然后提供服务。
- 如果当前节点不是master,则尝试加入master。
ZenDiscovery支持任意数目的集群(1-N),所以不像 Zookeeper/Etcd 那样限制节点必须是奇数,也没有用投票的机制来选主,而是通过一个规则,只要所有的节点都遵循同样的规则,得到的信息都是对等的,选出来的主节点肯定是一致的。但分布式系统的问题就出在信息不对等的情况,这时候很容易出现脑裂(Split-Brain)的问题,大多数解决方案就是设置一个quorum值,要求可用节点必须大于quorum(一般是超过半数节点),才能对外提供服务。而 Elasticsearch 中,这个quorum的配置就是discovery.zen.minimum_master_nodes 。