分布式文档存储

判定文档目标分片的方法

shard = hash(routing) % number_of_primary_shards
routing:默认是文档id,支持自定义。
通过哈希算法计算routing的到一个数字,数字与主分片总数取余,结果就是目标分片号。
所以主分片数量是不可变更的

写操作时文档的传递

  • 客户端向ES集群发起请求,假设由A节点的Y分片接收。
  • Y分片通过文档id计算所属分片为X。
  • Y节点通过分片清单得知X主分片所在节点。
  • Y节点将请求转发到B节点的X分片。
  • X分片执行请求成功后,开始并行向所有副本分片转发请求。
  • 各副本分片接收到请求后开始处理,处理后返回结果。
  • 当所有副本分支均成功后,X分片向Y分片返回成功。
  • Y分片向客户端返回成功。
  • 当客户端接收到成功返回后,既表数据已安全的保存到主分片以及副本分片中。

一致性参数consistency

当ES执行写操作时,主分片要求必须有 规定数量 的副本分片处于活跃可用状态,才会执行写操作。

  • one 主分片OK即可。
  • all 主分片与所有副本分片均OK才可。
  • quorum(默认) int( (主节点数 + 设置的副本数) / 2 ) + 1
  • 当设置的副本数>1时才生效。

等待参数timeout

当副本分片不满足 规定数量 时,ES会等待默认1分钟。使用timeout设置超时时间。100 表示100ms 10s 表示10秒
当检索操作超时时,不是直接返回错误。而是将已经查询到的结果返回

读操作时文档的传递

  • 客户端向ES集群发起请求,假设由Y节点接收。
  • Y节点通过id计算所属分片为X。
  • Y节点通过分片清单得知X主分片、副本分片各自所在节点。
  • Y节点采用轮训机制将请求转发给B节点。此处轮训机制实现了负载均衡。
  • B节点处理请求,响应结果给Y节点。
  • Y节点返回结果给客户端。

读写的冲突

当A客户端新建文档处于执行中时,B客户端发起获取文档请求。此时文档在各分片之间处于不一致状态。存在以下几种情况:

  • 主分片未处理结束时:返回404
  • 主分片处理完,副本分片未处理结束时:
  • 如果轮训到主分片则返回文档
  • 如果轮训到副本分片则返回404
  • 主分片处理完,副本分片处理完时:均可返回文档

局部更新操作时文档的传递

  • 客户端发送局部更新请求到ES集群,假设由Y节点接收。
  • Y节点通过文档id计算所属主分片为X。
  • Y节点通过分片清单得知X主分片存在于A节点。
  • Y节点转发请求到A节点上的X主分片。
  • X主分片提取JSON,更改JSON,替换文档。
  • 如果更新失败,会重复retry_on_conflict次。
  • X主分片并发向副本分片转发请求(动作变为完整更新)。
  • X主分片等待所有副本分片返回成功后,向Y节点返回成功。
  • Y节点返回结果给客户端。

主副分片之间为什么由局部更新改为完整更新

当N个客户端同时对同一文档发起局部更新请求时。
主分片多进程处理局部更新请求,依靠version机制保证文档的完整性。
主分片多进程向副本分片并发传递文档时,因并发、网络等原因,无法保证转发请求顺序到达副本分片。
如果采用局部更新的方式,请求顺序颠倒到达,因version机制导致延迟请求处理失败。提前到达的局部更新仅包含自身变更内容,这种情况将造成文档内容丢失。
如果采用完整更新文档的方式,请求顺序颠倒到达,因version机制导致延迟请求处理失败。提前到达的完整更新包含全部变更内容,这种情况不会造成文档内容丢失。

即便丢失了过程更新,但是最后version版本各分片是一致的,成功的跳过了检查机制

疑问待查证

ES集群对首次接收到的请求,是如何分配节点的。
各节点之间如何做到共享节点清单的。
主节点处理失败,直接向客户端返回失败。
副本分片处理失败,主分片和其他副本分片是否进行回滚操作?

有趣的格式(大概意思)

为什么批量操作要用/N结尾?而不是发送一个完整JSON?
如果采用JSON格式,处理过程如下

  • 接收JSON字符串
  • JSON字符串toJsonObject
  • send(JsonObject.get(x).toString) 分发至各个分片。

第一步 字符串占X空间
第二步 JsonObject占Y(Y>=X)空间
第三步 拆分总和占Z(Z=Y)空间
总结:共占用 X+Y+Z空间

如果采用/N结尾,处理过程如下
接收一段内容,转发一段内容
总结:共占用 X空间

搜索——最基本的工具

搜索功能包括

  • 像关系型数据库一样进行结构化查询。
  • 全文检索找出所有匹配关键字的文档并按照_相关性(relevance)_ 排序后返回结果。
  • 以上二者兼而有之

映射

描述数据在每个字段内如何存储

分析

全文是如何处理使之可以被搜索的

领域特定查询语言

Elasticsearch 中强大灵活的查询语言

空索引

  • hits
  • total 匹配文档总数
  • hits 匹配文档前十
  • _id
  • _type
  • _index
  • _source
  • took 总耗时
  • shards 参与处理的分片数量
  • failed 失败数
  • successful 成功数
  • total 总数
  • timeout 是否超时,如果超时表示结果不完整
    实例
GET /_search
{
   "hits" : {
      "total" :       14,
      "hits" : [
        {
          "_index":   "us",
          "_type":    "tweet",
          "_id":      "7",
          "_score":   1,
          "_source": {
             "date":    "2014-09-17",
             "name":    "John Smith",
             "tweet":   "The Query DSL is really powerful and flexible",
             "user_id": 2
          }
       },
        ... 9 RESULTS REMOVED ...
      ],
      "max_score" :   1
   },
   "took" :           4,
   "_shards" : {
      "failed" :      0,
      "successful" :  10,
      "total" :       10
   },
   "timed_out" :      false
}

多索引,多类型

对于多索引,多类型的请求,处理方法相当于批量操作。

/_search
    在所有的索引中搜索所有的类型 
/gb/_search
    在 gb 索引中搜索所有的类型 
/gb,us/_search
    在 gb 和 us 索引中搜索所有的文档 
/g*,u*/_search
    在任何以 g 或者 u 开头的索引中搜索所有的类型 
/gb/user/_search
    在 gb 索引中搜索 user 类型 
/gb,us/user,tweet/_search
    在 gb 和 us 索引中搜索 user 和 tweet 类型 
/_all/user,tweet/_search
    在所有的索引中搜索 user 和 tweet 类型

排序

默认使用_source做降序排序,可以指定排序列。
排序在分片中进行,各分片结果返回后再由主分片二次排序。

分页

  • size 显示数量,默认10
  • from 显示跳过的的数量,默认是0
    分页在副本分片中进行,然后在主分片中二次分页。

分页的消耗(大概意思)

假设进行size=10,form=0的分页检索操作:

  • 各副本分片查询取结果集中10条记录
  • 主分支接收各副本分支返回结果。10*副本数量。
  • 主分支排序50,取top10

假设进行size=10,form=100的分页检索操作:

  • 各副本分片查询取结果集中110条记录
  • 主分支接收各副本分片返回结果。110*副本数量。
  • 主分支排序550,取top100~top110

轻量搜索

不建议用

文章中部分内容来源于:
Elasticsearch: The Definitive Guide by Clinton Gormley and Zachary Tong (O’Reilly). Copyright 2015 Elasticsearch BV, 978-1-449-35854-9。