合并-clickhouse

1,967 阅读4分钟

合并是什么

只有合并树系列的表才有合并这个功能。通常合并都是后台任务。合并的对象是一个表的某个分区之中的part们。合并的目的是为了将多个part合并成为一个part。 注意:一个表的跨分区的part是不能合并的。

合并的作用

1:降低磁盘的存储。当数据都存储在一个文件之中,会更好地发挥压缩算法的功能。 2:提高查询的速度。数据都存储在一个文件之中,索引文件更加少,加速查询。

合并占据的资源

合并会消耗 内存 cpu 和 磁盘。 因为合并会将原本的数据写入到一个新的文件之中,老的数据不会立即删除,老的数据会存在一定的时间,这个存在时间由具体的参数控制,所以会暂时导致磁盘数据量上涨。

合并的触发方式

一般来说,合并都是后台任务。对于后台合并而言,没有具体的参数暴露出来表示什么时候一定会进行合并。 可以执行 【optimize table 表名 final】 来手动触发合并,这个操作要慎用,很危险。

合并的实现原理

clickhouse 在后台维护了一个线程池,用于执行后台合并 和 mutation 操作。 合并的时候会选择所要进行合并的part们,然后每次从 每一个part 读取 指定的行数(由配置参数 merge_max_block_size 决定)到内存,在内存中合并,写入到磁盘,循环往复。

合并后的清除

因为合并是将原本不同目录的数据写入到一个新的目录之中,所以原本的数据会“变旧”,需要清除。 old_parts_lifetime 参数:unactive状态的part存活时间。如果unactive状态的part存活时间超过设置值,会在周期性清理之中被清除掉。 merge_tree_clear_old_parts_interval_seconds 与 merge_tree_clear_old_temporary_directories_interval_seconds 则是指定了清理的时间间隔。

合并的相关参数

  • background_pool_size :MergeTree 引擎类型的表,用于执行后台合并和mutations的线程数目。

  • number_of_free_entries_in_pool_to_execute_mutation:当合并线程池之中存在多少个空闲线程的情况下 可以执行 mutation

  • number_of_free_entries_in_pool_to_lower_max_size_of_merge: 当合并线程池之中存在多少个空闲线程的情况下会降低合并所选择的最大数据量。

  • background_merges_mutations_concurrency_ratio:一个比例,用于和 background_pool_size 参数相乘。 注意: number_of_free_entries_in_pool_to_execute_mutation 与 number_of_free_entries_in_pool_to_lower_max_size_of_merge 之中的 entries的总数 = background_pool_size * background_merges_mutations_concurrency_ratio

  • max_bytes_to_merge_at_max_space_in_pool:限制后台合并所能合并的最大数据量,可以防止大合并的出现。

  • max_parts_to_merge_at_once:一次最多可以选取多少part进行合并。

  • merge_tree_clear_old_parts_interval_seconds:每隔多少时间开始清除旧的part

  • merge_tree_clear_old_temporary_directories_interval_seconds: 每个多少时间开始清除旧的目录

  • merge_max_block_size: 合并的时候,从每个part读取的行数,用来进行合并。影响合并需要消耗的内存,默认的取值是 8192。

  • merge_selecting_sleep_ms:触发为合并选择符合条件的part的时间间隔

合并的监控

  • 当前集群执行过的合并的次数
  • 当前集群执行过的合并的数据量
  • 一段时间内集群的合并次数
  • 一段时间内 集群的合并失败次数
  • 一段时间内 集群合并的数据量(未压缩) *一段时间内 合并失败的数据量 (未压缩)
  • 查看一段时间内 合并失败的详细信息
  • 当前正在执行的合并任务列表
  • 一段时间内所有表的 平均合并耗时 最大合并耗时
  • 一段时间内 每个表的合并次数 合并数据量 合并失败次数 合并失败数据量 注意:集群的合并数据量 和 合并次数 有两种统计方式,一种是 system.events 另一种是 system.part_log。system.events之中的数据 更加实时,贴合服务器的cpu变化。system.part_log表之中的数据,会出现一些延时,因为合并完成之后,数据才会写入到system.part_log。

当前集群执行过的合并次数

select * from system.events where event = 'Merge'

注意:指标是累加值。可以用prometheus 来获得一段时间内的变化。

当前集群执行过的合并数据量(未压缩)

select * from system.events where event = 'MergedUncompressedBytes'

注意:指标是累加值。可以用prometheus 来获得一段时间内的变化。

一段时间内集群的合并次数

   select   count(*) as merge_cnt
    from    
      clusterAllReplicas('集群名','system.part_log')
    where     
     event_date >= toDate('起始日期') 
     and event_date <= toDate(’结束日期‘) 
     and event_time >= ’起始时间‘ 
     and event_time <= ’结束时间‘
     and event_type = 'MergeParts' 

注意:这次的统计 是从 system.part_log 表获得的,数据的波峰可能会比从system.events表之中获得的往后退一些时间,原因是 system.part_log 之中的数据,会等到合并已经完成才写入。

一段时间内 集群的合并失败次数

  select   
       count(*) as merge_failed_cnt
  from    
    clusterAllReplicas('集群名', 'system.part_log')
  where    
    event_date >= toDate('开始日期') 
    and event_date <= toDate('结束日期') 
    and event_time >= '开始时间'
    and event_time <= '结束时间'
    and event_type = 'MergeParts' 
    and error <> 0 

一段时间内 集群合并的数据量(未压缩)

   select   
       sum(read_bytes) as read_bytes
   from    clusterAllReplicas('集群名','system.part_log')
   where   
      event_date >= toDate('起始日期') 
      and event_date <= toDate('结束日期') 
      and event_time >= '开始时间'
      and event_time <= '结束时间'
      and event_type = 'MergeParts' 

一段时间内 合并失败的数据量 (未压缩)

   select  
          sum(read_bytes) as read_bytes 
   from   
       clusterAllReplicas('集群名','system.part_log')
   where    
      event_date >= toDate('起始日期') 
      and event_date <= toDate('结束日期') 
      and event_time >= '开始时间'
      and event_time <= ‘结束时间’
      and event_type = 'MergeParts' 
      and error <> 0 

查看一段时间内 合并失败的详细信息

select    
 event_date,   event_time,   event_type,   query_id,   duration_ms,   database,   table,   
 partition_id,   part_name,   error,   exception,   read_rows,   read_bytes
from    
  clusterAllReplicas('${cluster}','system.part_log')
where      
   event_date >= toDate('开始日期') 
   and event_date <= toDate('结束日期') 
   and event_time >= '开始时间' 
   and event_time <= '结束时间'
   and event_type = 'MergeParts' 
   and error <> 0
 order by event_time 
limit 100

当前正在执行的合并任务列表

select 
   * 
from   system.merges 
where   is_mutation <> 1 

一段时间内所有表的 平均合并耗时 最大合并耗时

  select 
     database, 
     table,    
    max(duration_ms)  as max_duration_ms, // 最大合并耗时
    avg(duration_ms) as avg_duration_ms  // 平均合并耗时
  from      
    clusterAllReplicas('集群名','system.part_log')  
  where   
    event_date >= toDate('开始日期') 
    and event_date <= toDate('结束日期')      
    and event_time >= '开始时间'
    and event_time <= '结束时间'
    and event_type = 'MergeParts' 
  group by  
    database,   table

一段时间内 每个表的合并次数 合并数据量 合并失败次数 合并失败数据量

 select 
     database,  
     table,  
     count(*) as merge_cnt, // 合并次数
     sum(if(error <> 0, 1, 0)) as merge_failed_cnt, // 合并失败次数
     sum(read_bytes) as merge_bytes, // 合并数据量 
     sum(if(error <> 0, read_bytes, 0)) as merge_failed_bytes // 合并失败数据量
 from   clusterAllReplicas('集群名','system.part_log')
   where   
    event_date >= toDate('开始日期') 
    and event_date <= toDate('结束日期')
    and event_time >= '开始时间'
    and event_time <= '结束时间'
    and event_type = 'MergeParts'
 group by    database,    table