添加专家系统字段默认值
ES 新增字段支持过滤
业务背景
当我们使用多维检索时,mysql显然不能满足我的使用场景,尤其是涉及到表与表之间的连接,数据量大的时候,mysql的查询性能就捉襟见肘了。
这时,ES的多维检索功能就派上用场了。我们可以将两个或两个以上的业务表做成一个更宽的索引,监控业务的binlog,并将数据保存到es中。
这样可以快速支持业务检索。
业务需求
通常会用到ES的动态模板,以后添加其他维度过滤会更方便。
大家都知道文档存储在ES的底层。当使用POST将字段添加到动态模板中时,先前的数据将不会具有与mysql相同的默认值。
如果产品端需要支持旧数据的过滤,那么我们就要参与刷ES索引。
分析
按照数据组织的方式,将数据重新插入到ES中肯定是不可行的。那么我们有没有一个可以类似于mysql的设置默认值的命令呢?
所以我翻了翻ES的官方文档,看到更新可以支持这个操作。下面是一个在es动态索引中添加类型的示例,以演示解决过程。若要实现,请在es中添加类型=0的原始文档,并支持索引。
现有文档数
GET index _ test/_ count Pitty
{
计数' : 2000,
_碎片' : {
共计: 12,
成功' : 12,
跳过' : 0,
失败' : 0
}
}
你可以看到另外2000份文件,
使用ES的term查询:
实际上,“术语”是一个桶聚合查询,可以理解为mysql的group by。
POST _ index _ test/_ search Pitty
{
尺寸' : 0,
aggs' : {
aggType' : {
条款' : {
字段“:”类型
}
}
}
}
查询结果:
{
拍了' : 2,
' time out _ : false,
_碎片' : {
共计: 12,
成功' : 12,
跳过' : 0,
失败' : 0
},
点击量' : {
总计' : {
值' : 2000,
关系' : 'gte '
},
max_score' : null,
点击量' : [ ]
},
聚合' : {
aggType' : {
doc _ count _ error _ upper _ bound ' : 0,
sum_other_doc_count' : 0,
桶' : [
{
密钥' : 1,
doc_count' : 5
},
{
key' : 2,
doc_count' : 4
},
{
key' : 3,
doc_count' : 4
}
]
}
}
}
可以看到,没有type=0的数据,只有新生成的type=1、2、3、2、3的数据,分别是5、4、4,共13条,数据总条数为2000条。
有1987件物品不见了。这些数据都是旧数据,不支持该字段的检索。
使用update更新
POST索引_test/_update/1
{
脚本' : {
lang': '无痛',
来源' : '
if (ctx。_source.type==null) { ctx。_source.type=0 }
'''
}
}
ES update支持脚本,这样我们在加载更新文档的时候,就可以根据其他一个或者几个字段来确认新添加的字段的值,我这里用的就是这个。
如果type为空,则将type指定为默认值0。
更新后使用术语查询结果。
` `` json
{
拍了' : 2,
time out _ : false,
_碎片' : {
共计: 12,
成功f
ul" : 12,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2000,
"relation" : "gte"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"aggType" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 0,
"doc_count" : 1
}
{
"key" : 1,
"doc_count" : 5
},
{
"key" : 2,
"doc_count" : 4
},
{
"key" : 3,
"doc_count" : 4
}
]
}
}
}
发现只增加了一条,重复执行更新命令也不会再增加了,通过分析update语句发现,其命令行update后的1指的是doc
Id。这种方式显然不能使用,我再猜想有没有类似mysql中根据条件update的语句呀查看官方文档后,返现ES支持
update_by_query的操作
使用update_by_query
使用update_by_query语句,在这里我删除了script中的条件判断,改成使用query
POST index_test/_update_by_query
{
"script": {
"lang": "painless",
"source": "ctx._source.type=0"
},
"query": {
"bool": {
"must_not": {
"exists": {
"field": "type"
}
}
}
}
}
其实使用scrpit的脚本判断要比query中使用must_not要慢。我理解使用script要access all 全表扫描。
如果使用了must_not 而且只有一个条件,我理解ES的执行引擎会使用倒排所以,查询出有的,然后取反,把不存在该字段的
doc ID返回。根据id去逐条更新,这样判断的次数从O(n)降到了理论的O(1)。
待结果返回后,重新使用term查询结果:
POST index_test/_searchpretty
{
"size" : 0,
"aggs" : {
"aggType" : {
"terms" : {
"field" : "type"
}
}
}
}
查询结果:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 12,
"successful" : 12,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2000,
"relation" : "gte"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"aggType" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 0,
"doc_count" : 1987
}
{
"key" : 1,
"doc_count" : 5
},
{
"key" : 2,
"doc_count" : 4
},
{
"key" : 3,
"doc_count" : 4
}
]
}
}
}
可以看到聚合结果中key=0的文档相较之前增加了好多,而且key = 0,1,2,3,4 的枚举加起来正好为2000.
证明更新成功了。
控制更新速度
在更新的过程中,如果要控制更新的速度,可以在更新的语句后添加参数,目前ES更新支持两个方式
按照索引分片更新
POST index_test/_update_by_queryrouting=1
其中routing为集群的第几个分片。
- 优点:单分片更新,如果分片被更新坏了,可以找运维删除分片,副本分片会主动替换主分片,并重新
分片副本分片,在这期间索引的状态可能是黄色。 - 缺点:更新不是原子的,而且需要清楚集群有多少个主分片才可以操作。
按照分页更新
POST index_test/_update_by_queryscroll_size=10000
其中scroll_size的最大值为集群配置的允许的最大值,可以通过_settings命令查询。
- 优点:可以控制集群中数据的更新速度,降低修复数据时,集群的负载。
- 缺点:需要判断使用合理的分页,一旦集群崩溃就会影响线上环境。
触类旁通
ES集群使用的SSD的硬盘,而且对内存要求较高,
当集群的存储超过一半时(超过了一半ES就无法再实现段合并了,高并发写入会产生较多分段Segment)
。一半情况下,业务的数据都是按照日期存储的,这时候我们可以把较早的数据备份到HDFS系统上,然后
在ES的集群上执行delete_by_query可以删除部分历史数据,这样可以使得ES集群一直处于比较好的性能区间。
I am chris, not arax!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/112083.html