分片和副本
- shard代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。
- index包含多个shard,将数据拆成多台节点进行存放,解决单机容量问题,在ES分片技术中分为主分片(primary shard)和副分片(replica shard)
- 每个shard都是一个最小的工作单元,承载部分数据,每个shard都是一个Lucene实例,有完整的建立索引和处理请求的能力
- 增加或减少节点时,shard会自动在nodes中负载均衡
- primary shard和replica shard,每个document肯定只存在于某一个primary shard及其对应的replica shard中,不可能存在多个primary shard
- replica shard是primary shard的副本,负责容错,以承担读请求负载
- primary shard的数据量在创键索引的时候就固定了,replica shard的数量可以随时修改
- primary shard的默认数量是5,replica shard默认数量是1,表示5个主分片,每个主分片对应一个副本
- primary shard不能和自己的replica shard 放在同一个节点上(否在节点宕机,primary shard和副本都丢失,起不到容错作用),但是可以和其他的primary shard的replica shard放在同一个节点上
- ES容错机制,3台ES服务
- 如图:假如索引的primary shard为2,replica shard为2
- 该模式只允许一个节点宕机,才能保证数据的完整性
- 如果宕掉两台就会丢失数据,健康值就red了
- 索引的primary shard为3,replica shard为2:如图,这样的才能提高容错机制,该模式最高允许两台机器宕机而保证数据的完整性,同一主分片的副本不能在同一台节点上
- 当mastar和其中的一个slave宕机了,另一个slave还保证着一个完整索引的数据,这个slave会将R2-3,R1-2提升为主分片负责处理请求
- 结论:容错性最高的分片数量为节点的平方
主分片数量不能修改
- 如果主分片数量一旦确定就不能修改,副分片可以修改
- 原因:
- 文档路由的分片上,一个索引由多个分片构成,当添加(删除,修改)一个文档是,es就需要决定这个文档存储在哪个分片上,这个过程就称为数据路由(routing)
- 路由算法shard=hash(routing)%number_of_primary_shards
- 示例:一个索引,3个主分片
- (1)每次增删改查时,都有一个routing值,默认是文档的_id的值
- (2)对这个routing值使用哈希函数进行计算
- (3)计算出的这个值在和主分片求余
- 余数肯定在[0,number_of_primary_shards)之间文档就在对应的shard上,routing值默认是文档的_id的值,也可以手动指定一个值,手动指定对于负载均衡以提高批量的性能都有帮助
- primary shard一旦确定就不能修改了,假如主分片数量一改他就傻了,不知道数据在哪了。。。。。。,这里没有用到副分片数量,所以副分片的数量可以修改
文档查询原理
- 第一步:查询请求发给任意一个节点,该节点就成了coordinating node,该节点使用路由算法算出文档所在的primary shard
- 第二步:协调节点把请求转发给primary shard 也可以转发给replica shard(是用轮询调度算法(Round-Robin Scheduling,把请求平均分配至primary shard 和replica shard))
- 第三步:处理请求的节点把结果返回给协调节点,协调节点在返回给应用程序
- 特殊情况:请求的文档还在建立索引的过程中,primary shard上存在,但replica shard不存在,但是请求转发到了replica shard上,这是就会提示找不到文档
- 如图:
文档增删改原理
- 增删改原理同样也是发送给集群中个任意一个节点,通过数据路由算法,该文档应该存放在那个primary shard,和查询类似但是副本节点不接收增删改请求,所有请求都发往主分片,之后再把数据同步到主分片所对应的副本分片上。集群中的master节点负责管理集群范畴的变更,例如创建或删除索引,添加节点到集群或从集群删除节点。master节点无需参与文档层面的变更和搜索,这意味着仅有一个master节点并不会因流量增长而成为瓶颈。任意一个节点都可以成为 master 节点。
分页查询中deep paging问题
- GET /index/type/_search{"from":0,"size":2,"query":{"terms":{"terms":{"xingqu":"喝酒"}}}}
- GET /_search?from=0&size=3
- deep paging 查询的很深,比如一个索引有是哪个primary shard ,分别存储了6000条数据,我们要得到第100页的数据(每页10条),类似这种情况就叫deep paging
- 如何得到第100页的10条数据
- 在每个sharding 中所有990到999 这10 条数据然后用这30条数据进行排序,排序之后取10条数据就是要搜索的数,这种做法是错误的,因为3个shard中的数据的相关度分数(_score)不一样,可能这某一个shard中第一条数据的相关度分数比另一个shard中第1000条都要高,所以在每个shard中搜索990到999这条10条数据然后排序的做法是不对的
- 正确的做法是每个shard把0到999条数据全部搜索出来(按排序顺序),然后全部返回给协调节点,由协调节点按相关度分数排序后,取出第100页的10条数据,然后返回给客户端。
- deep paging性能问题
- (1)耗费网络带宽,因为搜索过深的话,各分片要把数据传递给协调节点,这个过程是有大量的数据传递的,消耗网络
- (2)消耗内存,各shard要把数据传递给协调节点,这个传递回来的数据,是被协调节点报保存在内存中的,这样会消耗大量的内存
- (3)消耗cpu 协调节点要把传回来的数据进行排序,这个排序过程很消耗cpu
- 鉴于deep paging的性能问题,所以应尽量减少使用。