前言
QueryId作为Druid区分查询的唯一标识,是我们定位查询问题有效的输入。用户查询有问题时,优先提供QueryId,如果在日志中捞不到的话,我们再根据dataSource名,interval以及查询装态来判断是哪条查询。
Druid Metrics
Druid 服务在运行时会将自身的一些metrics(指标信息)记录到日志文件中,也可以通过HTTP请求发送给其他的消息中间件,例如Apache Kafka。 默认情况下指标信息的记录是关闭的,需要在部署服务的时候手动开启。
在我们的集群中,Druid的指标信息是通过kafka实时落入Druid的一个dataSource中druid_metrics
。metrics记录的是全维度信息,通过service
维度来区分服务,通过metric
来取分指标,且指标对应的的指标值为value
。dataSource的schema可以入Druid的时候进行更改。不同服务对应的指标可以在官网查看[Druid Metrics](http://druid.io/docs/latest/operations/metrics.html。需要注意的是,有些指标是需要配置了相应的Monitor之后才可以记录的,具体信息可以在官网查看。
Superset
Superset 是一款由 Airbnb 开源的“现代化的企业级 BI(商业智能) Web 应用程序”,其通过创建和分享 dashboard,为数据分析提供了轻量级的数据查询和可视化方案。
Druid指标信息的可视化展示是通过Druid+Superset
来实现的。Druid提供数据查询来源,而Superset则将结果通过报表或者其他的方式展示出来。这样我们可以很轻松地查看和分析Druid的很多指标信息,包括:查询指标,集群状态,服务负载等等。
查询问题分析
现在我们认为用户的唯一输入是queryId,则可以将其作为过滤条件筛选出相应的指标。
确定查询语句
首先,最重要的是我们要获取到用户的查询语句,而查询是请求Broker的,所以据提的查询我们可以在Broker的日志中获取。但是,如果有多个Broker,我们如何确定是哪个Broker?一个一个查可以,也可以通过druid_metrics
查看。具体的查询语句如下:
{
"queryType":"topN",
"dataSource":"druid_metrics",
"dimension":"host",
"threshold":10,
"metric":"count",
"intervals":["2019-06-01T14:30:00+08:00/2019-06-06T14:40:00+08:00"],
"granularity":"all",
"filter":{
"type":"and",
"fields":[
{
"type":"selector",
"dimension":"id",
"value":"8c891ec8-fc76-4711-9048-abd0cc2a459c"
},
{
"type":"selector",
"dimension":"service",
"value":"druid/broker"
}
]
},
"aggregations":[
{
"type":"count",
"name":"count"
}]
}
查询结果如下:
[
{
"timestamp": "2019-06-04T07:00:00.015Z",
"result": [
{
"count": 9,
"host": "test-test.com:8082"
}
]
}
]
接着在服务器上对应的broker_request.log
日志中搜索即可,cat broker_request.log | grep ${queryId}
。
上述步骤可以确定查询语句。当然也可以在superset上完成,更加方便。
加上对应的过滤条件,直接查询即可。superset是一个很方便好用的可视化工具,对于一些指标信息的展示非常的直观。关于如何使用superset,这里不做赘述。
各个节点查询耗时
当然,有了查询语句我们不一定能够分析出问题所在。因为影响查询的因素有很多,查询语句只是一个方面。为了更加具体地分析问题,我们首先得清楚Druid查询的整条链路。
Druid中的数据分为两部分:实时数据和历史数据。历史数据在Druid的historical节点上查询,而实时数据会在middleManager的实时任务(peon进程)中查询。
说完了数据类别,再根据上图来分析Druid查询的链路。首先客户端的查询来到broker,broker首先会去ZK上获取查询的timeline,即对应的数据在什么位置,historical节点上还是middleManager(Peon进程)上。确定之后,broker会把查询成子查询,路由给对应的节点,对应节点处理查询之后,返回结果给broker,broker再把查询结果合并返回给客户端。
那么一次查询涉及到的Druid服务就有:Broker,Historical,middleManager上的peon进程。所以分析时,我们可以根据这入手。
我们先看看在各个节点上查询所耗费的时间和返回的数据量。具体的查询语句如下:
{
"queryType":"groupBy",
"dataSource":"druid_metrics",
"dimensions":["service","host"],
"intervals":["2019-06-01T14:30:00+08:00/2019-06-06T14:40:00+08:00"],
"granularity":"all",
"filter":{
"type":"and",
"fields":[
{
"type":"selector",
"dimension":"id",
"value":"8c891ec8-fc76-4711-9048-abd0cc2a459c"
},
{
"type":"in",
"dimension":"service",
"values":["druid/broker","druid/historical","druid/middlemanager"]
}
]
},
"aggregations":[
{
"type":"longMax",
"name":"cost_time",
"fieldName":"value_max"
}]
}
查询结果如下:
[
{
"version": "v1",
"timestamp": "2019-06-01T06:30:00.000Z",
"event": {
"host": "rantLing1-cool.com:8082",
"service": "druid/broker",
"cost_time": 4285
}
},
{
"version": "v1",
"timestamp": "2019-06-01T06:30:00.000Z",
"event": {
"host": "rantLing2-cool.com:8083",
"service": "druid/historical",
"cost_time": 1081
}
},
{
"version": "v1",
"timestamp": "2019-06-01T06:30:00.000Z",
"event": {
"host": "rantLing3-cool.com.com:8083",
"service": "druid/historical",
"cost_time": 1081
}
}
]
这里我们可以看到查询在每个节点的耗时,如果有耗时很长的情况,我们可以通过zabbix查看当时机器的负载情况,是否是外部因素导致的查询问题。当然还有可能是查询本身的数据量就很大,涉及的segments过多导致的查询问题。
小结:
查询出现了问题,例如超时,在集群正常服务的情况下。我确认问题的流程大致如下:
- 确认查询语句,返回的queryStatus中是否有明显的报错,例如返回数据量超过阈值等。如果没有继续排查;
- 确认查询涉及到的数据量是不是过大,设计的segment是不是过多。例如一个interval内有10T的数据,这样涉及的segments数量就有可能过多了,查询耗时可能就比较长。如果没有问题在继续下一步;
- 查看各个节点耗时,是broker端耗时较长还是其他节点。如果是节点耗时较长可以通过一些机器监控的工具查看当时的机器负载。如果是负载有问题,可能就要跟相关人员确认是什么原因导致了。当然负载过大可能也是因为较大的查询导致的。这个也是要另行分析的。
网友评论