常用ClickHouse问题诊断查询

常用ClickHouse问题诊断查询

Clickhouse是一个性能强大的OLAP数据库,在实际使用中会遇到各种各样的问题,同时也有很多可以调优的地方。本文阐述如何对ClickHouse做问题诊断和性能分析。

相关的系统表

序号表名含义说明
1system.asynchronous_insert_log异步插入数据日志2023年版本新加的
2system.asynchronous_metrics系统当前性能度量指标异步更新
3system.asynchronous_metric_log系统历史性能度量指标异步更新
4system.crash_log系统崩溃日志
5system.filesystem_cache_log基于文件的缓存的日志
6system.metric_log系统历史性能度量指标与system.asynchronous_metrics内容不一样
7system.part_log系统的数据part的日志,记录数据part的创建、合并、下载、移除、更新、移动很有用
8system.processes当前正在运行的查询很常用
9system.query_log系统的查询日志很有用
10system.query_views_log各种视图的查询日志通过实验发现并不会记录任何日志,即使打开log_query_views。
11system.query_thread_log系统查询的线程方面日志记录 thread_name, thread_id, master_thread_id 信息
12system.opentelemetry_span_logOpenTelemetry的span日志见下方关于OpenTelemetry 的说明
13system.processors_profile_log查询执行过程中的详细i性能信息
14system.session_log客户端登录/登出日志可以用于安全防护
15system.settings系统配置的设置信息
16system.text_log系统的文本日志,与日志文件的内容相同
17system.trace_log系统运行跟踪日志,包含callstack。很有用
18system.transactions_info_log事务工作日志,包括开始和结束时间、状态
19system.zookeeper_log对ZooKeeper的操作日志

OpenTelemetry 是一个开放标准,用于生成、收集和处理分布式系统的跟踪、日志和度量数据。它提供了一组 API 和 SDK,可以集成到应用程序和基础设施中,以收集和传递数据,并将其汇聚到一个集中式的存储库中,以支持分析、监视和故障排除。

性能度量

性能度量的数据来源于system.asynchronous_metricssystem.asynchronous_metric_logsystem.metric_log

实时性能度量

展示当前所有的性能度量指标

select*,formatReadableSize(value),value
fromsystem.asynchronous_metrics
order bymetric;

度量指标非常丰富,有300多个。以下是一些指标的说明:

度量指标含义
CompiledExpressionCacheCount已编译并保存在缓存中的表达式的数量
jemalloc.*jemalloc相关指标,需要深入理解jemalloc内存分配
MarkCacheBytes / MarkCacheFiles.mrk 文件的缓存
MemoryCodeClickHouse代码所占内存大小
MemoryDataAndStack数据和堆栈所占内存大小(包括虚拟内存)
MemoryResident占用真实内存的大小
MemoryShared共享内存大小
MemoryVirtual虚拟内存大小
NumberOfDatabases数据库的数量
NumberOfTables表的数量
ReplicasMaxAbsoluteDelay集群副本最大的绝对延迟时间(单位:秒)
ReplicasMaxRelativeDelay集群副本最大的相对延迟时间(单位:秒)
ReplicasMaxInsertsInQueue单个Replicated表的要从其他副本抓取的part的最大数量
ReplicasSumInsertsInQueue所有的Replicated表的从其他副本抓取的part的总计数量
ReplicasMaxMergesInQueue单个Replicated表的merge队列里的merge任务的最大数量
ReplicasSumMergesInQueue所有的Replicated表的merge队列里的merge任务的总计数量
ReplicasMaxQueueSize单个Replicated表的任务队列的任务的最大数量
ReplicasSumQueueSize全部Replicated表的任务队列的任务的最大数量
UncompressedCacheBytes/UncompressedCacheCells解压缩后的数据的缓存所占内存空间
UptimeClickHouse的在线累计时间(单位:秒)

重点指标 - 内存相关

select*,formatReadableSize(value)
fromsystem.asynchronous_metrics
wheremetric ilike '%memory%' or metric ilike '%cach%'
order bymetric;

重点指标 - CPU相关

select*
fromsystem.asynchronous_metrics
wheremetric ilike '%cpu%'
order bymetric;

CPU的每个核都有对应的CPU指标,目前每个CPU核有大约10个指标,以CPUx为后缀。

度量指标(去掉后缀CPUx)解释
OSGuestNiceTime度量虚拟机中的进程的“好”时间(nice time)的指标。Nice time表示进程优先级,范围为-20到+19,默认值是0。nice 值越小,表示该进程的优先级越高,即更容易获得 CPU 时间片。
OSGuestTime度量虚拟机中的进程的时间的指标,表示虚拟机中运行的进程在使用 CPU 时的总时间,单位为纳秒,可以用来监测虚拟机中进程的 CPU 利用率和主机系统的负载情况。
OSIOWaitTime进程等待 I/O 操作的时间的指标,可以用来监测进程对 I/O 设备的使用负载情况。一般这个指标如果高,说明瓶颈在I/O上,对强调吞吐量的系统是不利的。
OSIdleTimeCPU空闲时间的指标,单位为纳秒。
OSIrqTime度量进程在硬件中断(如 I/O 设备完成操作、时钟中断等)服务例程中消耗 CPU 时间的指标。这个指标主要表示I/O中断对系统的影响,单位为纳秒。这个指标过高表示I/O中断处理时间是瓶颈,但优化的方法可能不在软件层面。
OSNiceTime度量主机中进程的“好”时间(nice time)的指标。Nice time表示进程优先级,范围为-20到+19,默认值是0。nice 值越小,表示该进程的优先级越高,即更容易获得 CPU 时间片。
OSSoftIrqTime度量软中断服务例程消耗 CPU 时间的指标,单位为纳秒。
OSStealTime度量虚拟化环境中虚拟机被宿主机“偷走” CPU 时间的指标,表示虚拟机中运行的进程因宿主机“偷走” CPU 时间而被迫等待的时间,单位为纳秒。该指标过高表示宿主机的负载过高,资源争夺激烈。
OSSystemTimeCPU系统态时间的指标,表示进程在内核态中使用 CPU 的总时间,单位为纳秒。
OSUserTimeCPU用户态时间的指标,表示进程在用户态中使用 CPU 的总时间,单位为纳秒。
OSCPUVirtualTimeMicroseconds虚拟CPU上的运行时间,单位微秒。

参考:Metrics List

历史性能指标

把 实时性能度量中的表system.asynchronous_metrics替换成system.asynchronous_metric_logsystem.asynchronous_metric_log多了event_dateevent_time两列。

例如,看历史内存相关的度量,用如下查询:

select *
from system.asynchronous_metric_log
wheremetric ilike '%memory%'
order byevent_time
desc;

下面介绍一些常用查询。

内存占用峰值变化趋势(由近及远)

with interval 5 minute as time_frame_size -- 时间间隔,当前是5分钟
, 100 as bar_width -- 条状图的宽度,当前是100
, (select max(value) from system.asynchronous_metric_log where metric = 'OSMemoryTotal') as max_mem
, now() - interval 24 hour as time_start
, now() as time_end
select toStartOfInterval(event_time, time_frame_size) as timeframe,max(value) as `used_memory`,formatReadableSize(`used_memory`) as `used_memory_readable`,formatReadableSize(max_mem) as `max_memory`,bar(used_memory / max_mem, 0, 1, bar_width)
from system.asynchronous_metric_log
where metric = 'MemoryResident' and event_time >= time_start and event_time <= time_end
group by timeframe
order by timeframe desc;

CPU使用峰值变化趋势

with interval 5 minute as time_frame_size -- 时间间隔,当前是5分钟
, 20 as bar_width -- 条状图的宽度,当前是30
, now() - interval 24 hour as time_start
, now() as time_end
select toStartOfInterval(event_time, time_frame_size) as timeframe,maxIf(value, metric = 'OSUserTimeNormalized') as cpu_usr,maxIf(value, metric = 'OSSystemTimeNormalized') as cpu_sys,bar(cpu_usr, 0, 1, bar_width) as barCPU_usr,bar(cpu_sys, 0, 1, bar_width) as barCPU_sys
from system.asynchronous_metric_log
where metric in ['OSUserTimeNormalized', 'OSSystemTimeNormalized'] and event_time >= time_start and event_time <= time_end
group by timeframe
order by timeframe desc;

CPU与内存峰值变化趋势

with interval 5 minute as time_frame_size -- 时间间隔,当前是5分钟
, 25 as bar_width -- 条状图的宽度,当前是100
, (select max(value) from system.asynchronous_metric_log where metric = 'OSMemoryTotal') as max_mem
, now() - interval 24 hour as time_start
, now() as time_end
select toStartOfInterval(event_time, time_frame_size) as timeframe,maxIf(value, metric = 'MemoryResident') as `used_memory`,formatReadableSize(`used_memory`) as `used_memory_readable`,formatReadableSize(max_mem) as `max_memory`,maxIf(value, metric = 'OSUserTimeNormalized') as cpu_usr,maxIf(value, metric = 'OSSystemTimeNormalized') as cpu_sys,    cpu_usr + cpu_sys as cpu,bar(used_memory / max_mem, 0, 1, bar_width) as barMem,bar(cpu_usr + cpu_sys, 0, 1, bar_width) as barCPU_usr
from system.asynchronous_metric_log
where metric in ['OSUserTimeNormalized', 'OSSystemTimeNormalized', 'MemoryResident']and event_time >= time_startand event_time <= time_end
group by timeframe
order by timeframe desc;

各种类型查询内存使用统计

用以下查询获得某个时间段的各种类型查询的内存使用统计结果。

WITH now() - INTERVAL 24 HOUR AS min_time,  -- you can adjust thatnow() AS max_time,   -- you can adjust thatINTERVAL 1 HOUR as time_frame_size
SELECT timeframe,formatReadableSize(max(mem_overall)) as peak_ram,formatReadableSize(maxIf(mem_by_type, event_type='Insert'))     as inserts_ram,formatReadableSize(maxIf(mem_by_type, event_type='Select'))     as selects_ram,formatReadableSize(maxIf(mem_by_type, event_type='MergeParts')) as merge_ram,formatReadableSize(maxIf(mem_by_type, event_type='MutatePart')) as mutate_ram,formatReadableSize(maxIf(mem_by_type, event_type='Alter'))      as alter_ram,formatReadableSize(maxIf(mem_by_type, event_type='Create'))     as create_ram,formatReadableSize(maxIf(mem_by_type, event_type not IN ('Insert', 'Select', 'MergeParts','MutatePart', 'Alter', 'Create') )) as other_types_ram,groupUniqArrayIf(event_type, event_type not IN ('Insert', 'Select', 'MergeParts','MutatePart', 'Alter', 'Create') ) as other_types
FROM (SELECT toStartOfInterval(event_timestamp, time_frame_size) as timeframe,toDateTime( toUInt32(ts) ) as event_timestamp,t as event_type,SUM(mem) OVER (PARTITION BY t ORDER BY ts) as mem_by_type,SUM(mem) OVER (ORDER BY ts) as mem_overallFROM (WITH arrayJoin([(toFloat64(event_time_microseconds) - (duration_ms / 1000), toInt64(peak_memory_usage)), (toFloat64(event_time_microseconds), -peak_memory_usage)]) AS dataSELECTCAST(event_type,'LowCardinality(String)') as t,data.1 as ts,data.2 as memFROM system.part_logWHERE event_time BETWEEN min_time AND max_time AND peak_memory_usage != 0UNION ALL WITH arrayJoin([(toFloat64(query_start_time_microseconds), toInt64(memory_usage)), (toFloat64(event_time_microseconds), -memory_usage)]) AS dataSELECT query_kind,data.1 as ts,data.2 as memFROM system.query_logWHERE event_time BETWEEN min_time AND max_time AND memory_usage != 0
))
group by timeframe
order by timeframe desc;

MergeTree相关的性能指标

Merge的性能指标

SELECTtable,round((elapsed * (1 / progress)) - elapsed, 2) AS estimate,elapsed,progress,is_mutation,formatReadableSize(total_size_bytes_compressed) AS size,formatReadableSize(memory_usage) AS mem
FROM system.merges
ORDER BY elapsed DESC

Mutations的性能指标

SELECTdatabase,table,substr(command, 1, 30) AS command,sum(parts_to_do) AS parts_to_do,anyIf(latest_fail_reason, latest_fail_reason != '')
FROM system.mutations
WHERE NOT is_done
GROUP BYdatabase,table,command

故障处理

在遇到当前系统发生故障的情况下,按照下面介绍的方法一一排除。

当前运行的查询

通过了解当前运行的查询,可以了解目前是否有查询长期运行。

select query_id, * from system.processes;

或者更聚焦一些,

SELECTinitial_query_id,elapsed,formatReadableSize(memory_usage),formatReadableSize(peak_memory_usage),query
FROM system.processes
ORDER BY peak_memory_usage DESC
LIMIT 10;

强制终止查询

对于长期执行而不结束的查询,需要及时终止以释放系统资源给其他查询使用,这样避免整个系统被卡死。

kill query where query_id = '<查询ID>'; -- 替换<查询ID>为真正要终止的查询ID

查询错误分析

首先根据查询的错误提示找出错误关键字,替换keyword,用以下查询得到要找的出错的查询。

with '%<包含关键字>%' as keyword  -- 把<包含关键字>替换为实际关键字
select event_time_microseconds, query_id, query, `exception`, exception_code, stack_trace  from system.query_log where exception ilike keyword order by event_time_microseconds desc;

通过stack_trace可以大致知道抛异常的地方。

系统崩溃分析

查看所有崩溃日志

select * from system.crash_log order by event_time desc;

按照构建版本统计崩溃次数

select min(event_time) as begin_time, max(event_time) as end_time, build_id, count() from system.crash_log group by build_id order by end_time desc;

调优分析

调优分析一般分为两步:1. 找到有问题的一个或者多个查询;2. 仔细审视有问题的查询的执行细节以及相关的性能指标细节。

已加载的表所占用内存分析

如果发现ClickHouse启动后在任何操作都没有的情况下就占用了大量的内存,很可能是加载的表占用过多内存。特别是JoinMemorySet 引擎的表,这些会把数据全部加载到内存里。我们需要找出这样的加载就占内存的表。

with (select formatReadableSize(sum(total_bytes)) from system.tables where engine in ('Memory','Set','Join')) as all_bytes
selectdatabase as `数据库名`,name as `表名`,formatReadableSize(total_bytes) as `占用内存`,all_bytes as `总共占用`
from system.tables
where engine in ('Memory','Set','Join')
order by total_bytes desc;

同时MergeTree引擎家族也会预加载一些数据在内存中,也需要找出来。

SELECTcount() as `parts数量`,sumIf(data_uncompressed_bytes, part_type = 'InMemory') as `内存中的数据片大小`,formatReadableSize(sum(primary_key_bytes_in_memory)) as `主键占用内存`,formatReadableSize(sum(primary_key_bytes_in_memory_allocated)) as `为主键分配的内存大小`
FROM system.parts;

用shell脚本实时刷新

echo "         Merges      Processes       PrimaryK       TempTabs          Dicts"; \
for i in `seq 1 600`; do clickhouse-client --empty_result_for_aggregation_by_empty_set=0  -q "select \
(select leftPad(formatReadableSize(sum(memory_usage)),15, ' ') from system.merges)||
(select leftPad(formatReadableSize(sum(memory_usage)),15, ' ') from system.processes)||
(select leftPad(formatReadableSize(sum(primary_key_bytes_in_memory_allocated)),15, ' ') from system.parts)|| \
(select leftPad(formatReadableSize(sum(total_bytes)),15, ' ') from system.tables \WHERE engine IN ('Memory','Set','Join'))||
(select leftPad(formatReadableSize(sum(bytes_allocated)),15, ' ') FROM system.dictionaries)
"; sleep 3;  done 

需优化的查询的定位

在一个时间段内发生的众多查询中找出最需要优化的查询。

某个时间段的所有查询并按执行时长排序

得到某个时间段里的所有查询并按执行时长从大到小排序。这样可以快速找到最耗时的查询。

with now() - interval 24 hour as time_start  -- 开始时间, now() as time_end   -- 结束时间
selectevent_time_microseconds,initial_query_id,query,query_start_time,query_duration_ms,memory_usage,formatReadableSize (memory_usage),`databases`,`tables`,stack_trace,*
fromsystem.query_log
whereevent_time_microseconds >= time_start and event_time_microseconds <= time_endandtype in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']
order byquery_duration_ms desc;

某个时间段的所有查询并按内存消耗大小排序

得到某个时间段里的所有查询并按内存消耗从大到小排序。这样可以快速找到最消耗内存的查询。

with now() - interval 24 hour as time_start  -- 开始时间, now() as time_end   -- 结束时间
selectevent_time_microseconds,initial_query_id,query,query_start_time,query_duration_ms,memory_usage,formatReadableSize (memory_usage),`databases`,`tables`,stack_trace,*
fromsystem.query_log
whereevent_time_microseconds >= time_start and event_time_microseconds <= time_endandtype in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']
order bymemory_usage desc;

某个时间段最耗CPU的SQL语句

with now() - interval 24 hour as time_start -- 开始时间, now() as time_end -- 结束时间
select any(query), sum(`ProfileEvents.Values`[indexOf(`ProfileEvents.Names`, 'UserTimeMicroseconds')]) as `总消耗CPU(ms)` 
from system.query_log 
where type = 'QueryFinish'and event_time_microseconds >= time_start and event_time_microseconds <= time_end
group by normalizedQueryHash(query)
order by `总消耗CPU(ms)` desc
limit 100

某个时间段最耗内存的SQL语句

with now() - interval 24 hour as time_start -- 开始时间, now() as time_end -- 结束时间
select any(query), count() as `出现次数`,avg(memory_usage) as avg_memory_usage,formatReadableSize(avg(memory_usage)) as `平均内存使用`
from system.query_log 
where type = 'QueryFinish'and event_time_microseconds >= time_start and event_time_microseconds <= time_end
group by normalizedQueryHash(query)
order by avg_memory_usage desc
limit 100;

未完成的查询

with now() - interval 24 hour as time_start  -- 开始时间, now() as time_end   -- 结束时间
selectquery_id as `查询ID`,min(event_time) as `查询时间`,any(query),groupArray(type) as `事件类型`
from system.query_log
where event_time_microseconds >= time_start and event_time_microseconds <= time_end
group by query_id
HAVING countIf(type = 'QueryFinish') = 0
order by `查询时间` desc;

重点关注的高消耗查询

with now() - interval 24 hour as time_start -- 开始时间, now() as time_end -- 结束时间
select query_id,query,query_duration_ms as `查询时长ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'RealTimeMicroseconds')] / 1000 as `实际CPU时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'UserTimeMicroseconds')] / 1000 as `用户态CPU时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'SystemTimeMicroseconds')] / 1000 as `系统态CPU时间ms`,formatReadableSize(memory_usage) as `总内存消耗`,read_rows as ReadRows,formatReadableSize(read_bytes) as `总计读取字节`,written_rows as WrittenTows,formatReadableSize(written_bytes) as `总计写入字节`,result_rows as ResultRows,formatReadableSize(result_bytes) as `总计输出结果字节`
from system.query_log
where (event_time_microseconds >= time_startand event_time_microseconds <= time_end)and type in ('QueryFinish', 'ExceptionWhileProcessing')
order by query_duration_ms desc
limit 100;

调查某个查询执行期间的情况

要调查分析某个具体查询的执行性能,首先得到这个查询的开始和结束时间,然后替换历史性能指标等查询中的 time_starttime_end,得到该期间的有用的度量指标。

获取查询的时间范围

with '<查询ID>' as q_id -- 替换<查询ID>为真实查询ID,例如 D0V_Yabdl9bP9zixMQIxFA==, (selectany(query_start_time_microseconds), any(query_start_time_microseconds + interval query_duration_ms ms), any(query_duration_ms)fromsystem.query_logwhere query_id = q_id and type in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']) as time_span, time_span.1 as time_start, time_span.2 as time_end, time_span.3 as duration
select time_start, time_end;

用查询时间范围查询历史度量指标

为了方便分析,通常会将查询时间范围前后扩大一些,例如前后增加15秒,这样容易看到变化趋势。用以下with短语替换 time_starttime_end 参数。

with '<查询ID>' as q_id -- 替换<查询ID>为真实查询ID,例如 D0V_Yabdl9bP9zixMQIxFA==, interval 15 second as margin, (selectany(query_start_time_microseconds), any(query_start_time_microseconds + interval query_duration_ms ms), any(query_duration_ms)fromsystem.query_logwhere query_id = q_id and type in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']) as time_span, time_span.1 - margin as time_start, time_span.2 + margin as time_end
select time_start, time_end;

例如,以下展示查询D0V_Yabdl9bP9zixMQIxFA==执行期间内存的变化。

with interval 5 second as time_frame_size -- 时间间隔
, 100 as bar_width -- 条状图的宽度,当前是100
, (select max(value)from system.asynchronous_metric_logwhere metric = 'OSMemoryTotal'
) as max_mem
, 'D0V_Yabdl9bP9zixMQIxFA==' as q_id -- 替换<查询ID>为真实查询ID,例如 D0V_Yabdl9bP9zixMQIxFA==
, interval 15 second as margin
, (select any(query_start_time_microseconds),any(query_start_time_microseconds + interval query_duration_ms ms),any(query_duration_ms)from system.query_logwhere query_id = q_idand type in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']
) as time_span
, time_span.1 - margin as time_start  -- 查询范围的开始时间前一点
, time_span.2 + margin as time_end  -- 查询范围的结束时间后一点
select toStartOfInterval(event_time, time_frame_size) as timeframe,max(value) as `used_memory`,formatReadableSize(`used_memory`) as `used_memory_readable`,formatReadableSize(max_mem) as `max_memory`,bar(used_memory / max_mem, 0, 1, bar_width) -- 设置搁置
from system.asynchronous_metric_log
where metric = 'MemoryResident'and event_time >= time_startand event_time <= time_end
group by timeframe
order by timeframe desc;

某查询运行时内存与CPU利用率

按照之前的方法,替换time_starttime_end

with interval 5 minute as time_frame_size -- 时间间隔,当前是5分钟
, 25 as bar_width -- 条状图的宽度,当前是100
, (select max(value) from system.asynchronous_metric_log where metric = 'OSMemoryTotal') as max_mem
, 'D0V_Yabdl9bP9zixMQIxFA==' as q_id -- 替换<查询ID>为真实查询ID,例如 D0V_Yabdl9bP9zixMQIxFA==
, interval 15 second as margin
, (select any(query_start_time_microseconds),any(query_start_time_microseconds + interval query_duration_ms ms),any(query_duration_ms)from system.query_logwhere query_id = q_idand type in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']
) as time_span
, time_span.1 - margin as time_start  -- 查询范围的开始时间前一点
, time_span.2 + margin as time_end  -- 查询范围的结束时间后一点
select toStartOfInterval(event_time, time_frame_size) as timeframe,maxIf(value, metric = 'MemoryResident') as `used_memory`,formatReadableSize(`used_memory`) as `used_memory_readable`,formatReadableSize(max_mem) as `max_memory`,maxIf(value, metric = 'OSUserTimeNormalized') as cpu_usr,maxIf(value, metric = 'OSSystemTimeNormalized') as cpu_sys,    cpu_usr + cpu_sys as cpu,bar(used_memory / max_mem, 0, 1, bar_width) as barMem,bar(cpu_usr + cpu_sys, 0, 1, bar_width) as barCPU_usr
from system.asynchronous_metric_log
where metric in ['OSUserTimeNormalized', 'OSSystemTimeNormalized', 'MemoryResident']and event_time >= time_startand event_time <= time_end
group by timeframe
order by timeframe desc;

查询执行期间同时执行的其他查询

某些情况下一个查询运行过慢的原因并不完全是自身原因,而是因为同时有其他查询在并行执行,这就需要研究查询执行期间的其他查询的运行情况。

用以下查询列出查询期间所有的同时发生的查询(包括本查询在内)。

with 'D0V_Yabdl9bP9zixMQIxFA==' as q_id -- 替换<查询ID>为真实查询ID,例如 D0V_Yabdl9bP9zixMQIxFA==, interval 15 second as margin, (select any(query_start_time_microseconds),any(query_start_time_microseconds + interval query_duration_ms ms),any(query_duration_ms)from system.query_logwhere query_id = q_id and type in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']) as time_span, time_span.1 - margin as time_start  -- 查询范围的开始时间前一点, time_span.2 + margin as time_end  -- 查询范围的结束时间后一点
select query_id,query,event_time_microseconds,query_start_time_microseconds,(query_start_time_microseconds + interval query_duration_ms ms) as query_end_time_microseconds,query_duration_ms,formatReadableSize (memory_usage),type,databases
fromsystem.query_log
where(query_end_time_microseconds >= time_start and query_end_time_microseconds <= time_endorquery_start_time_microseconds >= time_start and query_start_time_microseconds <= time_end)and type in ['QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing']
order by event_time_microseconds;

查询执行细节分析

当知道某个查询有问题之后,就可以更细节地分析某个查询的具体的问题(例如速度慢或者内存占用过多)的原因。

很多执行细节信息隐藏在trace_log日志中,在trace_log日志打开的时,CH在代码执行过程中每隔一段时间会记录当前的代码执行快照,即当时的代码执行callstack、线程号、申请内存大小(如果是关于内存的日志)等信息。这些信息对分析死锁、性能瓶颈、内存浪费、内存频繁申请等问题很有帮助。

查看代码执行过程快照

下面查询可以获得某个查询的执行过程中的一系列调用堆栈快照。如果某个callstack频繁出现,就大概率意味着程序执行卡在了这个地方,这个地方可能就是性能瓶颈,或者是出现了死锁。

with '<查询ID>' as qid  -- 替换<查询ID>为真实值,例如:Ep983OmKg4Ya9Zvyu9OB9A==
selectevent_time_microseconds,trace_type,thread_id,arrayMap(addr -> demangle(addressToSymbol(addr)), trace) as callstack
fromsystem.trace_log
wheretrace_type in ['CPU']and query_id = qid
order by event_time_microseconds desc
settings allow_introspection_functions = 1;

查看代码执行过程的内存申请释放

下面查询可以获得某个查询的执行过程中的一系列申请内存的情况的快照。每次内存的分配都有记录。通过以下查询可以看到内存是怎么一步步被消耗的。

with '<查询ID>' as qid  -- 替换<查询ID>为真实值,例如:Ep983OmKg4Ya9Zvyu9OB9A==
selectevent_time_microseconds,size,formatReadableSize(size) as `本次分配内存`,sum(size) over w as size_running,formatReadableSize(size_running) as `累计分配内存`,thread_id ,arrayMap(addr -> demangle(addressToSymbol(addr)), trace) as callstack
fromsystem.trace_log
wheretrace_type in ['Memory']and query_id = qid
window w as (rows between unbounded preceding and current row)
order by event_time_microseconds desc
settings allow_introspection_functions = 1;

火焰图分析

<略>

IO分析

要找出磁盘I/O为瓶颈的查询,需要获得一段时间内的所有查询在I/O方面的负载情况,分析其中I/O重的查询。

一段时间内每个SQL的I/O以及CPU详细情况

with now() - interval 24 hour as time_start -- 开始时间, now() as time_end -- 结束时间
select query_id,query,query_duration_ms as `查询时长ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'RealTimeMicroseconds')] / 1000 as `实际CPU时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'UserTimeMicroseconds')] / 1000 as `用户态CPU时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'SystemTimeMicroseconds')] / 1000 as `系统态CPU时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'DiskReadElapsedMicroseconds')] / 1000 as `磁盘读取时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'DiskWriteElapsedMicroseconds')] / 1000 as `磁盘写入时间ms`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'IOBufferAllocs')] as `IO缓冲区分配次数`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'IOBufferAllocBytes')] as `IOBufferAllocBytes`,formatReadableSize(IOBufferAllocBytes) as `IO缓冲区分配大小`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'FileOpen')] as `文件打开次数`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'ReadBufferFromFileDescriptorReadBytes')] as ReadBufferFromFileDescriptorReadBytes,formatReadableSize(ReadBufferFromFileDescriptorReadBytes) as `文件读取字节数`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'WriteBufferFromFileDescriptorWriteBytes')] as WriteBufferFromFileDescriptorWriteBytes,formatReadableSize(WriteBufferFromFileDescriptorWriteBytes) as `文件写入字节数`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'WriteBufferFromFileDescriptorWrite')] as `文件写入次数`,ProfileEvents.Values [indexOf(ProfileEvents.Names, 'ZooKeeperTransactions')] as `ZooKeeper事务数`,ProfileEvents,read_rows as ReadRows,formatReadableSize(read_bytes) as `总计读取字节`,written_rows as WrittenTows,formatReadableSize(written_bytes) as `总计写入字节`,result_rows as ResultRows,formatReadableSize(result_bytes) as `总计输出结果字节`
from system.query_log
where (event_time_microseconds >= time_startand event_time_microseconds <= time_end)and type in ('QueryFinish', 'ExceptionWhileProcessing')
order by query_duration_ms desc
limit 100;

日志分析

按照关键字过滤日志,使用如下命令:

grep <关键字> /var/log/clickhouse-server.log

例如,

grep MemoryTracker /var/log/clickhouse-server.log

对于旧的已经打包成.gz的日志文件,用zgrep命令去查看,不需要专门解压缩了。命令如下:

zgrep <关键字> /var/log/clickhouse-server.log.*.gz

例如,

zgrep MemoryTracker /var/log/clickhouse-server.log.*.gz

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/74925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 2851. String Transformation

Leetcode 2851. String Transformation 0. 吐槽1. 算法思路 1. 整体思路2. 字符串匹配优化 2. 代码实现 题目链接&#xff1a;2851. String Transformation 0. 吐槽 这道题多少有点坑爹&#xff0c;题目本身挺有意思的&#xff0c;是一道数组题目&#xff0c;其实用数学方法…

Java开发面试--nacos专区

1、 Nacos是什么&#xff1f; 请简要介绍Nacos是什么以及它的主要功能和用途。 答&#xff1a; 简介&#xff1a; Nacos是一个开源的、高性能、动态服务发现、配置和服务管理平台&#xff0c;通常用于微服务架构中。Nacos的名称来源于"Naming"&#xff08;服务发现…

【2023年11月第四版教材】第12章《质量管理》(第一部分)

第12章《质量管理》&#xff08;第一部分&#xff09; 1 章节说明2 管理基础3 管理过程3.1 管理ITTO汇总★★★ 1 章节说明 【本章分值预测】大部分内容不变&#xff0c;细节有一些变化&#xff0c;预计选择题考3-4分&#xff0c;案例和论文都有可能考&#xff1b;是需要重点学…

【Vue2.0源码学习】生命周期篇-初始化阶段(initState)

文章目录 1. 前言2. initState函数分析3. 初始化props3.1 规范化数据3.2 initProps函数分析3.3 validateProp函数分析3.4 getPropDefaultValue函数分析3.5 assertProp函数分析 4. 初始化methods5. 初始化data6. 初始化computed6.1 回顾用法6.2 initComputed函数分析6.3 defineC…

Unity记录一些glsl和hlsl的着色器Shader逆向代码

以下内容一般基于 GLSL 300 之后 以下某些代码行&#xff0c;是“伪代码“&#xff0c;绝大部分是renderDoc 逆向产生标准代码 本人OpenlGL零基础&#xff0c;也不打算重头学 目录 Clip&#xff08;&#xff09; 剔除函数 discard; FS最终颜色输出 out 和最终颜色相加方程…

Hadoop的HDFS的集群安装部署

注意&#xff1a;主机名不要有/_等特殊的字符&#xff0c;不然后面会出问题。有问题可以看看第5点&#xff08;问题&#xff09;。 1、下载 1.1、去官网&#xff0c;点下载 下载地址&#xff1a;https://hadoop.apache.org/ 1.2、选择下载的版本 1.2.1、最新版 1.2.2、其…

docker报错解决方法

ERROR: readlink /var/lib/docker/overlay2/l: invalid argument 注意&#xff1a;会清空已有安装 sudo service docker stop sudo rm -rf /var/lib/docker sudo service docker start

酷开系统游戏空间,开启大屏娱乐新玩法

在这个充满科技感和无限创意的时代&#xff0c;游戏已经成为我们生活的一部分。而随时着科技的不断发展&#xff0c;以及游戏爱好者的游戏需求在不断提高&#xff0c;促使游戏体验也向更加丰富多彩的方向发展。显然&#xff0c;酷开科技早已经认识到游戏发展的新蓝图&#xff0…

一百七十二、Flume——Flume采集Kafka数据写入HDFS中(亲测有效、附截图)

一、目的 作为日志采集工具Flume&#xff0c;它在项目中最常见的就是采集Kafka中的数据然后写入HDFS或者HBase中&#xff0c;这里就是用flume采集Kafka的数据导入HDFS中 二、各工具版本 &#xff08;一&#xff09;Kafka kafka_2.13-3.0.0.tgz &#xff08;二&#xff09;…

javaee springMVC model的使用

项目结构图 pom依赖 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org…

腾讯云CVM标准型S4服务器性能测评_CPU_网络收发包PPS详解

腾讯云服务器CVM标准型S4性能测评&#xff0c;包括S4云服务器CPU型号、处理器主频、网络收发包PPS、队列数、出入内网带宽能力性能参数说明&#xff0c;标准型 S4 实例是次新一代的标准型实例&#xff0c;CPU采用2.4GHz主频的Intel Xeon Skylake 6148处理器&#xff0c;腾讯云百…

Visual studio解决‘scanf: This function or variable may be unsafe. 问题

使用C语言的scanf函数在Visual Studio软件上运行会报如下错误&#xff1a; scanf: This function or variable may be unsafe. Consider using scanf s instead. To disable deprecation, use. CRT SECURE NO WARNINGS. See online help for details. 这个函数或变量可能是不安…

LeetCode 92. Reverse Linked List II【链表,头插法】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

React 防抖与节流用法

在React中&#xff0c;防抖和节流是优化性能和提升用户体验的常用技术。下面是它们的用法&#xff1a; 防抖&#xff08;Debounce&#xff09;&#xff1a;防抖是指在某个事件触发后&#xff0c;等待一段时间后执行回调函数。如果在等待时间内再次触发该事件&#xff0c;将重新…

upload-labs1-21关文件上传通关手册

upload-labs文件上传漏洞靶场 目录 upload-labs文件上传漏洞靶场第一关pass-01&#xff1a;第二关Pass-02第三关pass-03&#xff1a;第四关pass-04&#xff1a;第五关pass-05&#xff1a;第六关pass-06&#xff1a;第七关Pass-07第八关Pass-08第九关Pass-09第十关Pass-10第十一…

苹果手机远程控制安卓手机,为什么不能发起控制?

这位用户想要用iOS设备远程控制安卓设备&#xff0c;在被控端安装好AirDroid之后&#xff0c;就在控制端的苹果手机上也安装了AirDroid&#xff0c;然而打开控制端的软件&#xff0c;却没有在手机界面上看到【远程控制】按钮&#xff0c;于是提出了以上疑问。 解答 想要让iOS设…

自动驾驶:轨迹预测综述

自动驾驶&#xff1a;轨迹预测综述 轨迹预测的定义轨迹预测的分类基于物理的方法&#xff08;Physics-based&#xff09;基于机器学习的方法&#xff08;Classic Machine Learning-based&#xff09;基于深度学习的方法&#xff08;Deep Learning-based&#xff09;基于强化学习…

用postman 推送消息到GCP的pubsub

创建1个Topic 和 2个 subscription 我们可以用terraform 去创建1个topic 和 2个subscriptions # topic resource "google_pubsub_topic" "topic_a" {name "TopicA"project var.project_id }# subscriptions resource "google_pubsub_s…

【AIGC专题】Stable Diffusion 从入门到企业级实战0402

一、概述 本章是《Stable Diffusion 从入门到企业级实战》系列的第四部分能力进阶篇《Stable Diffusion ControlNet v1.1 图像精准控制》第02节&#xff0c; 利用Stable Diffusion ControlNet Openpose模型精准控制图像生成。上一节&#xff0c;我们介绍了《Stable Diffusion C…

numpy详解

Numpy 简介 Numpy&#xff08;Numerical Python&#xff09;是一个在Python领域做数值计算非常重要的库&#xff0c; Pandas、Matplotlib、Statmodels、Scikit-learn和其它一些科学计算库都依赖Numpy 也就是说有时候你可能没有直接import numpy as np&#xff0c;但是却在背后…