当前位置: 首页 > news >正文

记一次clickhouse查询优化之惰性物化

记一次clickhouse查询优化之惰性物化

一、起因

测试clickhouse在10亿数据量下的查询性能,当limit<=10时,为21秒,当limit>10时,为63秒。下面是测试步骤。

  1. 建表语句
    create table test
    (_id  String,ct   UInt32,sip  IPv4,dip  IPv4,sp   UInt16,dp   UInt16
    )engine = MergeTree PARTITION BY toYYYYMMDD(toDateTime(ct, 'Asia/Shanghai'))ORDER BY ctSETTINGS index_granularity = 8192;
    
  2. 查询sql
    select _id,ct,sip,dip,sp,dp from test where sip = '192.168.0.1' order by ct desc limit 10 settings optimize_read_in_order=0
    
  3. 查询结果
    经过where过滤后,共20万条数据。

二、排查过程

  1. 查看执行计划
    explain actions=1 select _id,ct,sip,dip,sp,dp from test where sip = '192.168.0.1' order by ct desc limit 10 settings optimize_read_in_order=0
    
    limit 10查询计划如下
     	    ┌─explain────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐1. │ Expression (Project names)                                                                                                         │2. │ Actions: INPUT : 0 -> __table1._id String : 0                                                                                      │3. │          INPUT : 1 -> __table1.ct UInt32 : 1                                                                                       │4. │          INPUT : 2 -> __table1.sip IPv4 : 2                                                                                        │5. │          INPUT : 3 -> __table1.dip IPv4 : 3                                                                                        │6. │          INPUT : 4 -> __table1.sp UInt16 : 4                                                                                       │7. │          INPUT : 5 -> __table1.dp UInt16 : 5                                                                                       │8. │          ALIAS __table1._id :: 0 -> _id String : 6                                                                                 │9. │          ALIAS __table1.ct :: 1 -> ct UInt32 : 0                                                                                   │
    10. │          ALIAS __table1.sip :: 2 -> sip IPv4 : 1                                                                                   │
    11. │          ALIAS __table1.dip :: 3 -> dip IPv4 : 2                                                                                   │
    12. │          ALIAS __table1.sp :: 4 -> sp UInt16 : 3                                                                                   │
    13. │          ALIAS __table1.dp :: 5 -> dp UInt16 : 4                                                                                   │
    14. │ Positions: 6 0 1 2 3 4                                                                                                             │
    15. │   LazilyRead (Lazily Read)                                                                                                         │
    16. │   Lazily read columns: dip, sp, _id, dp                                                                                            │
    17. │     Limit (preliminary LIMIT (without OFFSET))                                                                                     │
    18. │     Limit 10                                                                                                                       │
    19. │     Offset 0                                                                                                                       │
    20. │       Sorting (Sorting for ORDER BY)                                                                                               │
    21. │       Sort description: __table1.ct DESC                                                                                           │
    22. │       Limit 10                                                                                                                     │
    23. │         Expression ((Before ORDER BY + Projection))                                                                                │
    24. │         Actions: INPUT :: 0 -> __table1._id String : 0                                                                             │
    25. │                  INPUT :: 1 -> __table1.ct UInt32 : 1                                                                              │
    26. │                  INPUT :: 2 -> __table1.sip IPv4 : 2                                                                               │
    27. │                  INPUT :: 3 -> __table1.dip IPv4 : 3                                                                               │
    28. │                  INPUT :: 4 -> __table1.sp UInt16 : 4                                                                              │
    29. │                  INPUT :: 5 -> __table1.dp UInt16 : 5                                                                              │
    30. │         Positions: 1 0 2 3 4 5                                                                                                     │
    31. │           Expression                                                                                                               │
    32. │           Actions: INPUT : 0 -> _id String : 0                                                                                     │
    33. │                    INPUT : 1 -> ct UInt32 : 1                                                                                      │
    34. │                    INPUT : 3 -> dip IPv4 : 2                                                                                       │
    35. │                    INPUT : 4 -> sp UInt16 : 3                                                                                      │
    36. │                    INPUT : 5 -> dp UInt16 : 4                                                                                      │
    37. │                    INPUT : 2 -> sip IPv4 : 5                                                                                       │
    38. │                    ALIAS _id :: 0 -> __table1._id String : 6                                                                       │
    39. │                    ALIAS ct :: 1 -> __table1.ct UInt32 : 0                                                                         │
    40. │                    ALIAS dip :: 2 -> __table1.dip IPv4 : 1                                                                         │
    41. │                    ALIAS sp :: 3 -> __table1.sp UInt16 : 2                                                                         │
    42. │                    ALIAS dp :: 4 -> __table1.dp UInt16 : 3                                                                         │
    43. │                    ALIAS sip :: 5 -> __table1.sip IPv4 : 4                                                                         │
    44. │           Positions: 6 0 4 1 2 3                                                                                                   │
    45. │             ReadFromMergeTree (default.test)                                                                                       │
    46. │             ReadType: Default                                                                                                      │
    47. │             Parts: 9                                                                                                               │
    48. │             Granules: 122500                                                                                                       │
    49. │             Prewhere info                                                                                                          │
    50. │             Need filter: 1                                                                                                         │
    51. │               Prewhere filter                                                                                                      │
    52. │               Prewhere filter column: equals(__table1.sip, '192.168.0.1'_String) (removed)                                         │
    53. │               Actions: INPUT : 0 -> sip IPv4 : 0                                                                                   │
    54. │                        COLUMN Const(String) -> '192.168.0.1'_String String : 1                                                     │
    55. │                        FUNCTION equals(sip : 0, '192.168.0.1'_String :: 1) -> equals(__table1.sip, '192.168.0.1'_String) UInt8 : 2 │
    56. │               Positions: 0 2                                                                                                       │└─explain────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    
    limit 100时查询计划如下
     	    ┌─explain──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐1. │ Expression (Project names)                                                                                                       │2. │ Actions: INPUT : 0 -> __table1._id String : 0                                                                                    │3. │          INPUT : 1 -> __table1.ct UInt32 : 1                                                                                     │4. │          INPUT : 2 -> __table1.sip IPv4 : 2                                                                                      │5. │          INPUT : 3 -> __table1.dip IPv4 : 3                                                                                      │6. │          INPUT : 4 -> __table1.sp UInt16 : 4                                                                                     │7. │          INPUT : 5 -> __table1.dp UInt16 : 5                                                                                     │8. │          ALIAS __table1._id :: 0 -> _id String : 6                                                                               │9. │          ALIAS __table1.ct :: 1 -> ct UInt32 : 0                                                                                 │
    10. │          ALIAS __table1.sip :: 2 -> sip IPv4 : 1                                                                                 │
    11. │          ALIAS __table1.dip :: 3 -> dip IPv4 : 2                                                                                 │
    12. │          ALIAS __table1.sp :: 4 -> sp UInt16 : 3                                                                                 │
    13. │          ALIAS __table1.dp :: 5 -> dp UInt16 : 4                                                                                 │
    14. │ Positions: 6 0 1 2 3 4                                                                                                           │
    15. │   Limit (preliminary LIMIT (without OFFSET))                                                                                     │
    16. │   Limit 100                                                                                                                      │
    17. │   Offset 0                                                                                                                       │
    18. │     Sorting (Sorting for ORDER BY)                                                                                               │
    19. │     Sort description: __table1.ct DESC                                                                                           │
    20. │     Limit 100                                                                                                                    │
    21. │       Expression ((Before ORDER BY + Projection))                                                                                │
    22. │       Actions: INPUT :: 0 -> __table1._id String : 0                                                                             │
    23. │                INPUT :: 1 -> __table1.ct UInt32 : 1                                                                              │
    24. │                INPUT :: 2 -> __table1.sip IPv4 : 2                                                                               │
    25. │                INPUT :: 3 -> __table1.dip IPv4 : 3                                                                               │
    26. │                INPUT :: 4 -> __table1.sp UInt16 : 4                                                                              │
    27. │                INPUT :: 5 -> __table1.dp UInt16 : 5                                                                              │
    28. │       Positions: 1 0 2 3 4 5                                                                                                     │
    29. │         Expression                                                                                                               │
    30. │         Actions: INPUT : 0 -> _id String : 0                                                                                     │
    31. │                  INPUT : 1 -> ct UInt32 : 1                                                                                      │
    32. │                  INPUT : 3 -> dip IPv4 : 2                                                                                       │
    33. │                  INPUT : 4 -> sp UInt16 : 3                                                                                      │
    34. │                  INPUT : 5 -> dp UInt16 : 4                                                                                      │
    35. │                  INPUT : 2 -> sip IPv4 : 5                                                                                       │
    36. │                  ALIAS _id :: 0 -> __table1._id String : 6                                                                       │
    37. │                  ALIAS ct :: 1 -> __table1.ct UInt32 : 0                                                                         │
    38. │                  ALIAS dip :: 2 -> __table1.dip IPv4 : 1                                                                         │
    39. │                  ALIAS sp :: 3 -> __table1.sp UInt16 : 2                                                                         │
    40. │                  ALIAS dp :: 4 -> __table1.dp UInt16 : 3                                                                         │
    41. │                  ALIAS sip :: 5 -> __table1.sip IPv4 : 4                                                                         │
    42. │         Positions: 6 0 4 1 2 3                                                                                                   │
    43. │           ReadFromMergeTree (default.test)                                                                                       │
    44. │           ReadType: Default                                                                                                      │
    45. │           Parts: 9                                                                                                               │
    46. │           Granules: 122500                                                                                                       │
    47. │           Prewhere info                                                                                                          │
    48. │           Need filter: 1                                                                                                         │
    49. │             Prewhere filter                                                                                                      │
    50. │             Prewhere filter column: equals(__table1.sip, '192.168.0.1'_String) (removed)                                         │
    51. │             Actions: INPUT : 0 -> sip IPv4 : 0                                                                                   │
    52. │                      COLUMN Const(String) -> '192.168.0.1'_String String : 1                                                     │
    53. │                      FUNCTION equals(sip : 0, '192.168.0.1'_String :: 1) -> equals(__table1.sip, '192.168.0.1'_String) UInt8 : 2 │
    54. │             Positions: 0 2                                                                                                       │└─explain──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    
    可以看到执行计划主要差异就是 limit 10 时多了一行两行 LazilyRead (Lazily Read)Lazily read columns: dip, sp, _id, dp,根据字面意思,可以猜测这个懒加载导致——当 limit 小于10时,会先加载条件中的sip和排序的ct,延迟加载其他列,而超过10时,会直接加载查询字段、条件、排序字段所有列,从而导致查询时间暴涨。
  2. 搜索原因
    通过搜索引擎检索,查询到下面这篇文章。
    https://clickhouse.com/blog/clickhouse-gets-lazier-and-faster-introducing-lazy-materialization
    clickhouse在25.4中通过引入惰性物化,实现了延迟加载查询的列,从而大大减少需要从磁盘读取的数据。
    下面是一段简单的介绍,详情可以查看原文。
    1. 当查询上文中的sql时,最简单粗暴的方式就是读取select、where、order by中的所有列的所有行,然后先根据 where 过滤,再根据 order by 排序,最后取前 limit 条。这样会导致从磁盘读取所有列的所有行,性能最差。
    2. 这里很自然能想到,为什么不先读取 where 中的列过滤,其他列仅读取过滤后的行呢?于是 clickhouse 会自动执行 prewhere 优化,即通过自动判断,将 where 中的一些条件挪到 prewhere 中(这部分本文不做介绍,prewhere 也可以手动指定,详情可以去官网找),先只读取 prewhere 中的列的所有行,执行过滤,再取 select、order by 中的列时,仅读取过滤后的行即可。但是,由于 clickhouse 是以 granule (默认8192行)为单位存储的,所以需要读取其他列中过滤后的行对应的 granule。上文示例查询到20万行,由于是随机数据均匀分布,这20万行基本对应了全部 granule,所以 prewhere 优化没有起到什么效果。
    3. 那么最后一点,能不能先执行 limit,其他列只取 limit 结果中的10条数据对应的行呢?当然可以,这就是惰性物化的作用。那么整体流程就是,先读取 prewhere 中的列的所有行,过滤后,再读取 order by 中的列,排序后,根据 limit 取10行,最后读取 select 中的列时,仅读取这10行对应的 granule 即可。通过设置query_plan_max_limit_for_lazy_materialization可以配置 limit 不高于多少时开启惰性物化,如set query_plan_max_limit_for_lazy_materialization = 100,设置为0代表不限制。

三、总结

clickhouse 25.4及以上版本,当需要取Top N时,一定要设置惰性物化 limit 阈值为 N 或以上,以大幅度减少需要读取的数据量,加速查询时间。

http://www.dtcms.com/a/351095.html

相关文章:

  • 手机移动代理IP:使用、配置、维护的10问10答
  • 通义灵码插件——AI 重构表单开发!半小时搭建可视化拖拽系统,效率碾压传统模式
  • 如何了解云手机的兼容性?
  • TikTok广告投放革命:指纹云手机如何实现智能群控与降本增效
  • 云手机和模拟器之间的区别
  • Windows下的异步IO通知模型
  • Tomcat下载历史版本
  • 深入浅出理解支持向量机(SVM):从原理到实践
  • 支持向量机(SVM)核心笔记
  • 人类记忆如何启发AI?LLM记忆机制综述解读
  • Vue中的props方式
  • SELinux存在于过去的Linux安全增强模块
  • 可解释的多尺度深度学习在胸腔积液细胞块与细胞学涂片恶性肿瘤检测及侵袭性子宫内膜癌识别中的应用|文献速递-深度学习人工智能医疗图像
  • 6年前抄写的某品牌集成灶-蒸汽炉
  • UCIE Specification详解(七)
  • Linux文件系统深入解析:从原理到实践
  • 校园跑腿小程序源码 | 跑腿便利店小程序(源码下载)
  • Nginx访问限制学习笔记
  • 智慧AI消防通道占用检测在危险区域的应用
  • 数据结构青铜到王者第五话---LinkedList与链表(2)
  • 懂支持向量机(SVM):从原理到实战拆解
  • 算法-每日一题(DAY15)用队列实现栈
  • SQLBot 智能问数、数据洞察逻辑拆解
  • 【GM3568JHF】FPGA+ARM异构开发板 应用编辑及源码下载
  • 零基础也能照做的WordPress网站安全漏洞修复 + 高级优化保姆级教程。
  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第七章知识点问答(22题)
  • Netty源码—性能优化和设计模式
  • HarmonyOS 中的 @Prop 装饰器:深入理解单向数据传递
  • 网站如何被搜索引擎收录(Google、Bing、百度等)
  • [特殊字符]Windows 资源监视器使用指南:查端口以后不用敲命令了