前面研究过ES的get api的整体思路,作为编写ES插件时的借鉴。当时的重点在与理解整体流程,主要是shardOperation()的方法内部的调用逻辑,就弱化了shards()方法。实际上shards()方法在理解ES的结构层面,作用更大一些。我们还是从get api入手来理解shards()

先回顾一下get api的使用流程:

 添加文档到ES:curl -XPUT 'http://localhost:9200/test1/type1/1' -d '{"name":"hello"}'根据文档ID读取数据:curl -XGET 'http://localhost:9200/test1/type1/1' 

使用很简单。但是如果考虑到分布式,背后的逻辑就不简单了。 假如ES集群有3个节点,数据所在的索引也有3个分片,每个分片一个副本。即index的设置如下:

{"test1" : {"settings" : {"index" : {"number_of_replicas" : "1","number_of_shards" : "3"}}}
}

那么id为1的doc该分发到那个分片呢? 这个问题需要一篇详细的博文解答,这里我们先简单给一个结论:

默认情况下,ES会按照文档id计算一个hash值, 采用的是Murmur3HashFunction,然后根据这个id跟分片数取模。实现代码是MathUtils.mod(hash, indexMetaData.getNumberOfShards()); 最后的结果作为文档所在的分片id,所以ES的分片标号是从0开始的。

不知存,焉知取。

再整理一下取数据的核心流程:

s1: 根据文档id定位到数据所在分片。由于可以设为多个副本,所以一个分片会映射到多个节点。s2: 根据分片节点的映射信息,选择一个节点,去获取数据。 这里重点关注的是节点的选择方式,简而言之,我们需要负载均衡,不然设置副本就没有意义了。

上面两步都关联着一个核心的数据结构ClusterState, 我们可以使用_cluster/state?pretty来查看这个数据结构:

# http://localhost:9200/_cluster/state?pretty{"cluster_name" : "elasticsearch","version" : 4,"state_uuid" : "b6B739p5SbanNLyKxTMHfQ","master_node" : "KnEE25tzRjaXblFJq5jqRA","blocks" : { },"nodes" : {"KnEE25tzRjaXblFJq5jqRA" : {"name" : "Mysterio","transport_address" : "127.0.0.1:9300","attributes" : { }}},"metadata" : {"cluster_uuid" : "ZIl7g86YRiGv8Dqz4DCoAQ","templates" : { },"indices" : {"test1" : {"state" : "open","settings" : {"index" : {"creation_date" : "1553995485603","uuid" : "U7v5t_T7RG6rNU3JlGCCBQ","number_of_replicas" : "1","number_of_shards" : "1","version" : {"created" : "2040599"}}},"mappings" : { },"aliases" : [ ]}}},"routing_table" : {"indices" : {"test1" : {"shards" : {"0" : [ {"state" : "STARTED","primary" : true,"node" : "KnEE25tzRjaXblFJq5jqRA","relocating_node" : null,"shard" : 0,"index" : "test1","version" : 2,"allocation_id" : {"id" : "lcSHbfWDRyOKOhXAf3HXLA"}}, {"state" : "UNASSIGNED","primary" : false,"node" : null,"relocating_node" : null,"shard" : 0,"index" : "test1","version" : 2,"unassigned_info" : {"reason" : "INDEX_CREATED","at" : "2019-03-31T01:24:45.845Z"}} ]}}}},"routing_nodes" : {"unassigned" : [ {"state" : "UNASSIGNED","primary" : false,"node" : null,"relocating_node" : null,"shard" : 0,"index" : "test1","version" : 2,"unassigned_info" : {"reason" : "INDEX_CREATED","at" : "2019-03-31T01:24:45.845Z"}} ],"nodes" : {"KnEE25tzRjaXblFJq5jqRA" : [ {"state" : "STARTED","primary" : true,"node" : "KnEE25tzRjaXblFJq5jqRA","relocating_node" : null,"shard" : 0,"index" : "test1","version" : 2,"allocation_id" : {"id" : "lcSHbfWDRyOKOhXAf3HXLA"}} ]}}
}

整个结构比较复杂,我们慢慢拆解, 一步步逐个击破。 拆解的思路还是从使用场景入手。

  1. IndexMetaData的学习
    metaData的格式如下:

    "metadata" : {
    "cluster_uuid" : "ZIl7g86YRiGv8Dqz4DCoAQ",
    "templates" : { },
    "indices" : {"test1" : {"state" : "open","settings" : {"index" : {"creation_date" : "1553995485603","uuid" : "U7v5t_T7RG6rNU3JlGCCBQ","number_of_replicas" : "1","number_of_shards" : "1","version" : {"created" : "2040599"}}},"mappings" : { },"aliases" : [ ]}
    }
    }

即metadata中存储了集群中每个索引的分片和副本数量, 索引的状态, 索引的mapping, 索引的别名等。这种结构,能提供出来的功能就是根据索引名称获取索引元数据, 代码如下:

# OperationRouting.generateShardId()IndexMetaData indexMetaData = clusterState.metaData().index(index);if (indexMetaData == null) {throw new IndexNotFoundException(index);}final Version createdVersion = indexMetaData.getCreationVersion();final HashFunction hashFunction = indexMetaData.getRoutingHashFunction();final boolean useType = indexMetaData.getRoutingUseType();

这里我们关注点就是clusterState.metaData().index(index)这句代码,它实现了根据索引名称获取索引元数据的功能。 通过元数据中的分片数结合文档id,我们就能定位出文档所在的分片。 这个功能在Delete, Index, Get 三类API中都是必须的。 这里我们也能理解为什么ES的索引分片数量不能修改: 如果修改了,那么hash函数就没法正确定位数据所在分片。

  1. IndexRoutingTable的学习
"routing_table" : {"indices" : {"test1" : {"shards" : {"0" : [ {"state" : "STARTED","primary" : true,"node" : "KnEE25tzRjaXblFJq5jqRA","relocating_node" : null,"shard" : 0,"index" : "test1","version" : 2,"allocation_id" : {"id" : "lcSHbfWDRyOKOhXAf3HXLA"}}, {"state" : "UNASSIGNED","primary" : false,"node" : null,"relocating_node" : null,"shard" : 0,"index" : "test1","version" : 2,"unassigned_info" : {"reason" : "INDEX_CREATED","at" : "2019-03-31T01:24:45.845Z"}} ]}}}}

routing_table存储着每个索引的分片信息,通过这个结构,我们能清晰地了解如下的信息:

1. 索引分片在各个节点的分布
2. 索引分片是否为主分片

假如一个分片有2个副本,且都分配在不同的节点上,那么get api一共有三个数据节点可供选择, 选择哪一个呢?这里暂时不考虑带preference参数。
为了使每个节点都能公平被选择到,达到负载均衡的目的,这里用到了随机数。参考RotateShuffer

/*** Basic {@link ShardShuffler} implementation that uses an {@link AtomicInteger} to generate seeds and uses a rotation to permute shards.*/
public class RotationShardShuffler extends ShardShuffler {private final AtomicInteger seed;public RotationShardShuffler(int seed) {this.seed = new AtomicInteger(seed);}@Overridepublic int nextSeed() {return seed.getAndIncrement();}@Overridepublic List<ShardRouting> shuffle(List<ShardRouting> shards, int seed) {return CollectionUtils.rotate(shards, seed);}}

也就是说使用ThreadLocalRandom.current().nextInt()生成随机数作为种子, 然后取的时候依次旋转。
Collections.rotate()的效果可以用如下的代码演示:

    public static void main(String[] args) {List<String> list = Lists.newArrayList("a","b","c");int a = ThreadLocalRandom.current().nextInt();List<String> l2 = CollectionUtils.rotate(list, a );List<String> l3 = CollectionUtils.rotate(list, a+1);System.out.println(l2);System.out.println(l3);}-----
[b, c, a]
[c, a, b]

比如请求A得到的节点列表是[b,c,a], 那么请求B得到的节点列表是[c,a,b]。这样就达到了负载均衡的目的。

  1. DiscoveryNodes的学习。
    由于routing_table中存储的是节点的id, 那么将请求发送到目标节点时,还需要知道节点的ip及端口等配置信息。 这些信息存储在nodes中。
  "nodes" : {"KnEE25tzRjaXblFJq5jqRA" : {"name" : "Mysterio","transport_address" : "127.0.0.1:9300","attributes" : { }}}

通过这个nodes获取到节点信息后,就可以发送请求了,ES所有内部节点的通信都是基于transportService.sendRequest()

总结一下,本文基于get api 梳理了一下ES的ClusterState中的几个核心结构: metadata,nodesrouting_table。 还有一个routing_nodes这里没有用到。后面梳理清楚使用场景后再记录。

ES学习笔记之-ClusterState的学习相关推荐

  1. Hadoop学习笔记—18.Sqoop框架学习

    Hadoop学习笔记-18.Sqoop框架学习 一.Sqoop基础:连接关系型数据库与Hadoop的桥梁 1.1 Sqoop的基本概念 Hadoop正成为企业用于大数据分析的最热门选择,但想将你的数据 ...

  2. JavaScript学习笔记(十)——学习心得与经验小结

    JavaScript学习笔记(十)--学习心得与经验小结 目前我先列好提纲,利用每晚和周末的闲暇时间,将逐步写完 ^_^ 转载于:https://www.cnblogs.com/mixer/archi ...

  3. oracle 百万记录 cache,学习笔记:通过案例深入学习In-Memory Database Cache 总结配置过程...

    天萃荷净 详细记录关于In-Memory Database Cache配置方法与使用案例 一.Oracle数据库创建相关用户和权限 1.创建timesten用户 store information a ...

  4. Hadoop学习笔记—15.HBase框架学习(基础知识篇)

    Hadoop学习笔记-15.HBase框架学习(基础知识篇) HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问.HBase的目标是存储并处理大型的数据.HBase ...

  5. Hadoop学习笔记—16.Pig框架学习

    Hadoop学习笔记-16.Pig框架学习 一.关于Pig:别以为猪不能干活 1.1 Pig的简介 Pig是一个基于Hadoop的大规模数据分析平台,它提供的SQL-LIKE语言叫Pig Latin, ...

  6. 学习笔记之——基于深度学习的图像超分辨率重建

    最近开展图像超分辨率( Image Super Resolution)方面的研究,做了一些列的调研,并结合本人的理解总结成本博文~(本博文仅用于本人的学习笔记,不做商业用途) 本博文涉及的paper已 ...

  7. CS230(DeepLearning)Leture2的学习笔记(2)之深度学习

    CS230_Leture2的学习笔记2之深度学习 我们先来回顾下第二章的学习目标:        而对于第二章Week1的学习目标已经完成,具体学习内容参考博主另一篇博客,站内搜索 CS230(Dee ...

  8. 梅科尔工作室-Django学习笔记(二次学习)(3)

    本次学习的是如何用django实现前端鸿蒙OS登录 下面介绍一下本次的操作过程. 1.首先创立MySQL数据并将数据库导入到django当中 其中:导入数据库的操作: 2.配置urls 对应新建的pe ...

  9. 【学习笔记】Pytorch深度学习—Batch Normalization

    [学习笔记]Pytorch深度学习-Batch Normalization Batch Normalization概念 `Batch Normalization ` `Batch Normalizat ...

最新文章

  1. QT 实用代码片段 (持续更新)
  2. eclipse中运行tomcat提示端口被占的4种解决方案
  3. git钩子放服务器_linux服务器布置git 并带钩子
  4. 使用 nc (Netcat) 建立傳送資料的 socket server
  5. python将非0数视为false_python 面试题
  6. Android udp json+数组 ---gt;bytes发送数据
  7. 2021高通人工智能应用创新大赛踩坑指南
  8. 持续集成部署Jenkins工作笔记0020---20.在GitHub上创建WebHook
  9. apex英雄机器人探路者怎么玩_《王者荣耀》新英雄澜怎么玩 《王者荣耀》新英雄澜连招推荐...
  10. Handler 消息传递机制
  11. qt和arcgis for qt在地图上做测距(画线和显示距离,单位km)
  12. HTML5小游戏--数字炸弹
  13. 第七十三回 玄德进位汉中王  云长攻拔襄阳郡
  14. 2017年看的tracking论文
  15. SSD_Resnet 飞机与油桶数据集实战
  16. 使用原生JS+Canvas实现五子棋游戏
  17. 巴黎出差及场测攻略V1.3
  18. outer和left outer join有什么区别??
  19. 关于 Facebook的 SWE_NG_GCR-SEAsia
  20. c 语言 清华大学郑莉,c 语言程序设计郑莉清华大学答案

热门文章

  1. 深度linux安装好上不了网,Deepin Linux 无法上网
  2. mysql联合索引like_MySQL全文索引、联合索引、like查询、json查询速度大比拼
  3. java 搜索引擎 关键词高亮_和我一起打造个简单搜索之SpringDataElasticSearch关键词高亮(示例代码)...
  4. C++编译运行过程分析
  5. 洛谷 P2725 邮票题解
  6. 弗尤博客(十一)之搜索博文
  7. vue 中 created 和 mounted 钩子生命周期 问题
  8. linux运维、架构之路-HAProxy反向代理
  9. [Android-Appium]: 安卓自动化测试: 环境准备
  10. Selenium2+python自动化5-操作浏览器基本方法