分布式文档存储
判定文档目标分片的方法
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。