最近使用腾讯云Elasticsearch Service的用户提出,对线上的ES集群进行查询,响应越来越慢,希望能帮忙优化一下。
查询越来越慢的语句如下:
{
"_source": false,
"size": 0,
"aggs": {
"traceId": {
"aggs": {
"timestamp_millis": {
"min": {
"field": "timestamp_millis"
}
}
},
"terms": {
"field": "traceId",
"order": {
"timestamp_millis": "desc"
},
"size": 10
}
}
},
"query": {
"bool": {
"filter": {
"bool": {
"must": [
{
"range": {
"timestamp_millis": {
"from": 1556431798000,
"include_lower": true,
"include_upper": true,
"to": 1556435398000
}
}
},
{
"term": {
"user": "1275813850"
}
}
]
}
}
}
}
}
从查询语句上看到用户使用了聚合查询(aggregation query), 第一反应就是聚合查询影响了查询速度。但是又发现,用户的索引是按天创建的,查询昨天的数据量较大的索引(300GB)响应并不慢,可以达到ms级别,但是查询当天的正在写入数据的索引就很慢,并且响应时间随着写入数据的增加而增加。
初步分析查询性能瓶颈就在于聚合查询,但是又不清楚为什么查询旧的索引会比较快,而查询正在写入的索引会越来越慢。所以趁机找了些资料了解了下聚合查询的实现,最终了解到:
经过最终讨论,决定从业务角度对查询性能进行优化,既然对持续写入的索引构建Global Cardinals会越来越慢,那就降低索引的粒度,使得持续写入的索引数据量降低,同时增加了能够使用Global Cardinals缓存的索引数据量。
详细的优化方案如下:
根据优化方案,需要实现的内容包括:
其中,第一步需要在client端进行,写入数据时根据当前时间指定索引名称,如当前时间是
"2019-05-07 03:50:06", 则写入的索引名称为2019-05-07-03;第二步和第三步都是定时任务,实战时尝试使用SCF(腾讯云Serverless云函数)进行简单的配置即可。
在腾讯云SCF控制台中,选择"新建",进入云函数创建页面:
配置函数名称,选择名为"ES写入函数"的模板,该模板自带elasticsearch模块,可以使用es的api操作集群。
创建完成后,需要在"函数配置"TAB页对函数的网络进行配置,选择和Elasticsearch集群同vpc下的网络:
接下来,就可以配置函数代码和触发方式,并进行测试。
###1. 定期reindex
定期reindex的函数代码如下:
# -*- coding: utf8 -*-
from datetime import datetime
from elasticsearch import Elasticsearch
import random
import time
# ES集群地址
ESServer = Elasticsearch("10.0.128.35:9200")
def reindex_hourly_2_daily():
# 索引前缀,到月份
index_prefix = "test-index-"+time.strftime( "%Y-%m" ,time.localtime(time.time())) +"-"
# 当前天
current_day = time.localtime(time.time()).tm_mday
# 当前小时,因为SCF是UTC时间,所以加8个小时,如果不在SCF里运行,则不用加8个小时,也不用进行时区转换
current_hour = time.localtime(time.time()).tm_hour + 8
# 时区转换
if current_hour >=24:
current_hour= current_hour-24
current_day = current_day +1
# 前一个小时
last_hour = current_hour -1
# 前一天
last_day = current_day-1
# 前一个小时的索引
last_hour_index=''
# 按天建的索引
daily_index=''
# 如果是0点,则把前一天23点的索引迁移到前一天按天建的索引
if current_hour ==0:
last_hour=23
last_day = current_day-1
# 构造出如2019-05-05格式的索引,日期中的天数小于10则补0
if last_day<10:
daily_index = index_prefix + "0"+ str(last_day)
else:
daily_index = index_prefix + str(last_day)
# 构造出如2019-05-05-01格式的索引,日期中的小时数小于10则补0
if last_hour<10:
last_hour_index = daily_index+ "-0"+ str(last_hour)
else:
last_hour_index = daily_index+ "-"+str(last_hour)
else:
# 构造出如2019-05-05格式的索引
if current_day<10:
daily_index = index_prefix + "0"+ str(current_day)
else:
daily_index = index_prefix + str(current_day)
if last_hour<10:
last_hour_index = daily_index+ "-0"+ str(last_hour)
else:
last_hour_index = daily_index+ "-"+ str(last_hour)
# 自动创建按天建的索引
ESServer.indices.create(daily_index, ignore=400)
body= {}
source ={
'index':last_hour_index
}
dest = {
'index':daily_index
}
body={
'source':source,
'dest':dest
}
# 执行reindex,source和index相同的情况下,重复执行多次也不会造成数据重复
rsp = ESServer.reindex(body=body,wait_for_completion=False)
# 执行reindex返回taskId, 可以通过轮询taskId判断操作是否完成
print rsp
def main_handler(event,context):
reindex_hourly_2_daily()
函数代码说明:
配置定期reindex函数的触发方式为每小时的第1分钟执行:
根据需要,可以选择在每天凌晨0点到5点这个时间段,业务请求量不大时,删除前一天按小时建的索引,避免过多的重复数据,以及避免分片数量膨胀。
函数代码如下:
# -*- coding: utf8 -*-
from datetime import datetime
from elasticsearch import Elasticsearch
import random
import time
ESServer = Elasticsearch("10.0.128.35:9200")
def delete_old_index():
# 索引前缀,到月份
index_prefix = "test-index-"+time.strftime( "%Y-%m" ,time.localtime(time.time())) +"-"
# 当前天
current_day = time.localtime(time.time()).tm_mday
# 当前小时,因为SCF是UTC时间,所以加8个小时,如果不在SCF里运行,则不用加8个小时,也不用进行时区转换
current_hour = time.localtime(time.time()).tm_hour + 8
# 前一天
last_day = current_day-1
if current_hour >=24:
last_day = current_day
# 需要删除的索引,以通配符表示,如2019-05-05-*,表示删除前一天所有的按小时建的索引
will_delete_index_prefix=''
if last_day<10:
will_delete_index_prefix = index_prefix + "0"+ str(last_day) +"-"
else:
will_delete_index_prefix = index_prefix + str(last_day)+"-"
for i in range(24):
hour = ""
if i<10:
hour = "0"+str(i)
else:
hour = str(i)
ESServer.indices.delete(will_delete_index_prefix+hour, ignore=[400, 404])
def main_handler(event,context):
delete_old_index()
函数说明:
配置定期删除索引函数的触发方式为每天的2点执行(SCF使用的是UTC时间,所以cron表达式中需要加8个小时):
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。