1. 刨析分布式查询及相关性算分
1.1 分布式搜索的运行机制
- ElasticSearch的搜索,会分为两阶段进行
- 第一阶段 Query
- 第二阶段 Fetch
- Query Then Fetch
1.2 Query阶段
- 用户发出搜索请求到ES节点。节点收到请求后,会已Coordinating节点的身份,在6个主副中随机选择3个分片,发送查询请求
- 被选中的分片执行查询,进行排序。然后,每个分片都会返回FROM + Size个排序后的文档ID和排序值给Coordinating节点
1.3 Fetch阶段
- Coordinating节点会将Query阶段,从每个分片获取的排序后的文档ID列表,重新进行排序。选取From到From+Size个文档的ID
- 以multi get请求的方式,到响应的分片上获取详细的文档数据
1.4 Query Then Fetch潜在的问题
- 性能问题
- 每个分片上需要差的文档个数=from+size
- 最终协调节点需要处理: number_of_shard*(from+size)
- 深度分页
- 相关性算分
- 每个分片都基于自己分片上的数据进行相关性算分。这回导致打分偏离的情况,特别是数据量很少时。相关性算分在分片之间相互独立。当文档总数很少的情况下,如果主分片大于1,主分片数越多,相关性算分会越不准。
解决算分不准的方法
- 数据量不大的时候,可以将主分片数设置为1
- 当数据量足够大时候,只要保证文档均匀分散在各个分片上,结果一般就不会出现偏差
- 使用DFS Query Then Fetch
- 搜索的URL中指定参数_search?search_type=dfs_query_then_fetch
- 会到每个分片把各分片的词频和文档频率进行搜集,然后完整的进行一次相关性算分,耗费更加多的CPU和内存,执行性能低下,一般不建议使用
2. 排序与Doc Values&FieldData
2.1 代码实操
# 按日期排序,可以发现_score为null
POST /kibana_sample_data_ecommerce/_search
{
"size": 5,
"query": {
"match_all": {
}
},
"sort": [
{"order_date": {"order": "desc"}}
]
}
# 多字段排序
POST /kibana_sample_data_ecommerce/_search
{
"size": 5,
"query": {
"match_all": {
}
},
"sort": [
{"order_date": {"order": "desc"}},
{"_doc":{"order": "asc"}},
{"_score":{ "order": "desc"}}
]
}
# 对 text 字段进行排序。默认会报错,需打开fielddata
POST /kibana_sample_data_ecommerce/_search
{
"size": 5,
"query": {
"match_all": {
}
},
"sort": [
{"customer_full_name": {"order": "desc"}}
]
}
# 打开 text的 fielddata,然后再次执行上述查询
PUT kibana_sample_data_ecommerce/_mapping
{
"properties": {
"customer_full_name" : {
"type" : "text",
"fielddata": true,
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
# 关闭 keyword的 doc values
PUT test_keyword
PUT test_keyword/_mapping
{
"properties": {
"user_name":{
"type": "keyword",
"doc_values":false
}
}
}
PUT temp_users
PUT temp_users/_mapping
{
"properties": {
"name":{"type": "text","fielddata": true},
"desc":{"type": "text","fielddata": true}
}
}
POST temp_users/_doc
{"name":"Jack","desc":"Jack is a good boy!","age":10}
# 打开fielddata 后,查看 docvalue_fields数据
POST temp_users/_search
{
"docvalue_fields": [
"name","desc"
]
}
# 查看整型字段的docvalues
POST temp_users/_search
{
"docvalue_fields": [
"age"
]
}
2.2 排序的过程
- 排序是针对字段原始内容进行。倒排索引无法发挥作用
- 需要用到正排索引。通过文档ID和字段快速得到字段原始内容
- ElasticSearch两种实现方法
- Fielddata
- Doc Values(列式存储,对Text类型无效)
2.3 Doc Values与Field Data对比
Doc Values | Field Data | |
---|---|---|
何时创建 | 索引时,和倒排索引一起创建 | 搜索时动态创建 |
创建位置 | 磁盘文件 | JVM Heap |
优点 | 避免大量内存占用 | 索引速度快,不占用额外磁盘空间 |
缺点 | 降低索引速度,占用额外磁盘空间 | 文档过多时,动态创建开销大,占用过多JVM Heap |
2.3 关闭Doc Values
- 默认启用,可以通过mapping设置关闭
- 增加索引的速度 / 减少磁盘空间
- 如果重新打开,需要重建索引
- 当明确不需要做排序或聚合分析时可以关闭