惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cisco Blogs
Schneier on Security
Schneier on Security
T
Tor Project blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threat Research - Cisco Blogs
C
CERT Recently Published Vulnerability Notes
Security Latest
Security Latest
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
Webroot Blog
Webroot Blog
Project Zero
Project Zero
Google Online Security Blog
Google Online Security Blog
The Last Watchdog
The Last Watchdog
Spread Privacy
Spread Privacy
Hacker News: Ask HN
Hacker News: Ask HN
PCI Perspectives
PCI Perspectives
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
W
WeLiveSecurity
Attack and Defense Labs
Attack and Defense Labs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
N
News | PayPal Newsroom
Help Net Security
Help Net Security
The Hacker News
The Hacker News
H
Heimdal Security Blog
O
OpenAI News
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
Cyberwarzone
Cyberwarzone
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 叶小钗
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
I
Intezer
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Security Affairs
P
Proofpoint News Feed
S
Secure Thoughts
腾讯CDC
Google DeepMind News
Google DeepMind News
量子位
罗磊的独立博客

ike‘s blog

mac软件推荐 用Siri来控制Windows电脑开机关机吧 2023sumppary 记录一下我的第一个网页游戏Mazeball 如何使用Clickhouse的索引 如何从zookeeper切换为clickhouse—keeper cloki分布式查询和clickhouse副本存储 使用ilogtail+cloki+clickhouse来做日志系统吧 clickhouse集群部署指南 快速安装部署clickhouse和cloki 快速部署thanos架构 如何使用go的jwt 如何修改git commit记录 业务容器化需要注意的一些地方 游戏企划 Prometheus Metrics精简优化2 来在线听音乐吧 Chatgpt的API入门 简单搭建国内也可以使用的chatgpt Prometheus Metrics精简优化 Kubernetes的探针机制 本地部署chatAI【chatglm】 AI绘画工具webui简单入门 之 高清化 写在地下城邂逅Ⅳ·灾厄篇·完结之后 如何部署gitalk作为评论系统 AI绘画工具webui简单入门 之 工具安装 grafanadb迁移到mysql kubectl常用命令 开始写博客啦 隐私政策URL Markdown Example Include Video in the Posts Markdown Extended Features Simple Guides for Fuwari Golang net/http & HTTP Serve 源码分析
clickhouse的一些优化建议和工具
ike · 2024-01-09 · via ike‘s blog

起因#

自从用cloki+clickhouse来替代日志系统后真是一波三折,问题不断Orz。 只能感叹不亏是小众的开源工具,还是一堆坑,下面记一下踩到的一些坑和对clickhouse的优化。

  • Q:主查询最后的查询会进行全表查询,但是left join的time_series_dist表巨大,有几百G,几十亿条数据,导致每次查询都得全部读取到内存里,一次查询占用80G内存,查询5分钟还查不出结果!
# 查询语句
WITH sel_a AS
    (
        SELECT
            samples.string AS string,
            samples.fingerprint AS fingerprint,
            samples.timestamp_ns AS timestamp_ns
        FROM cloki.samples_v3_dist AS samples
        WHERE ((samples.timestamp_ns >= 1704440225031000000) AND (samples.timestamp_ns <= 1704440525031000000)) AND (samples.fingerprint IN (
            SELECT sel_1.fingerprint
            FROM
            (
                SELECT fingerprint
                FROM cloki.time_series_gin
                WHERE (key = 'app') AND (val = 'myapp')
            ) AS sel_1
            ANY INNER JOIN
            (
                SELECT fingerprint
                FROM cloki.time_series_gin
                WHERE (key = 'cluster') AND (val = 'test-prod')
            ) AS sel_2 ON sel_1.fingerprint = sel_2.fingerprint
        ))
        ORDER BY timestamp_ns DESC
        LIMIT 100
    )
SELECT
    JSONExtractKeysAndValues(time_series.labels, 'String') AS labels,
    sel_a.*
FROM sel_a
ANY LEFT JOIN cloki.time_series_dist AS time_series ON sel_a.fingerprint = time_series.fingerprint
ORDER BY
    labels DESC,
    timestamp_ns DESC
  • A: 估计是开源cloki根据的bug,而且和orderby也有区别,调查了蛮多优化建议,其中比较有用的是
  1. 让表的orderby和查询的orderby一致,这样可以减少最后排序时间,所以我把ORDER BY fingerprint改成ORDER BY (fingerprint, labels)。
  2. left join后的表尽量要是小表,不过这里是固定了所以没法优化,所以只能减少表的大小,还好这个表只是存储标签,把TTL(生命周期)改成1天后就好多了。
  3. 网上还有说让经常查询的key作为主键分区,相当于做了索引,也可以加快查询速度和效率,所以我PARTITION BY date改成了PARTITION BY fingerprint,但其实是有问题的! 因为PARTITION BY是分区,如果是date的话,一天一个分区,这样查询如果是跨天查询就很快,如果改成fingerprint会变成每一个数据一个分区!CK集群直接报错Too many parts (100018) in all partitions in total,分区中存在过多的数据分片了,赶紧删了重新改回来了date,没有完全懂的东西还是不要乱动的好。当然可以把分区时间改成半天,也有一定的效果。
     CREATE TABLE cloki.time_series
     (
         `date` Date,
         `fingerprint` UInt64,
         `labels` String,
         `name` String,
       INDEX idx_fingerprint_labels (fingerprint) TYPE minmax GRANULARITY 8192
     )
     ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/time_series/{shard}', '{replica}', date)
     PARTITION BY date
     ORDER BY (fingerprint, labels)
     TTL date + toIntervalDay(1)
     SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600
    
  • R:查询问题解决后,基本上每次查询都可以在10s内完成,而且内存也不需要太大了,clickhouse里也有一些users.xml的default标签参数可以优化查询啥的,例如
    max_threads 这个值不要太高,一般是默认就好了,并不是说线程越高越好,越高内存占用也越大
    distributed_product_mode ReplicatedReplacingMergeTree+Distributed的分布式表一定要弄为global,如果只是MergeTree+Distributed可以local
    group_by_overflow_mode 默认是查询失败就throw丢弃,也可以改成查询超出内存后返回部分结果,一般不需要改
    max_memory_usage 单个查询最大多少内存,默认是10g,当然也可以改大,我是改成90%的内存大小
    max_bytes_before_external_group_by groupby超了多少内存后就写入到磁盘里,防止内存占用过多,虽然会影响效率,一般是改成max_memory_usage的一半
    max_bytes_before_external_sort orderby超了多少内存后就写入到磁盘里,防止内存占用过多,虽然会影响效率,一般是改成max_memory_usage的一半

  • Q:clickhouse其实更适合一次性插入几百万的数据,而不是短时间内大量一条一条的数据插入,就好像现在cloki每一条日志都是一个insert请求,导致大量的单条插入给到clickhouse,然后ck集群的同步会扛不住,会报错merge速度比不上insert速度,然后zookeeper或clickhouse-keeper的负载非常高,触发表readonly的保护机制,只要没merge完就不给写入,因为没有缓存的中间件,一堆日志写入失败丢失。

  • A:说白了就是clickhouse的使用方法不对,如果是数据量不大还好,官方说只要超过500k每秒的插入,基本上就会导致同步不过来表readonly无法写入。所以网上有一些解决方法:

  1. 用buffer表来做缓冲,Buffer(database, table, num_layers, min_time, max_time, min_rows, max_rows, min_bytes, max_bytes [,flush_time [,flush_rows [,flush_bytes]]]), 用下面的例子里的参数解释就是,如果满足最大max的100s或者100w行或者100m的数据其中一个条件,就触发缓存写入到目标表,又或者同时满足所有min的条件过了10s且1w行且10m的数据就会触发写入数据到目标表。不过这个方法官方也说了其实不怎么适用,因为本身clickhouse还是不适合用于短时间大量单条数据的插入,更适合单条插入大量数据的场景,官方建议是每秒不超过1条插入语句。
  CREATE TABLE cloki.samples_v3_buffer
(
    `fingerprint` UInt64,
    `timestamp_ns` Int64 CODEC(DoubleDelta),
    `value` Float64 CODEC(Gorilla),
    `string` String CODEC(ZSTD(5))
)
ENGINE = Buffer('cloki', 'samples_v3', 16, 10, 100, 10000, 1000000, 10000000, 100000000)
  1. buffer表不管用,那就只能想想其他办法,还好这个问题也有解决,clickhouse-bulk 是第三方的开源小工具,主要目的就是把小插入变成大插入,只需要设置好配置文件nohup ./clickhouse-bulk -config config.json >/dev/null 2>&1 &启动即可,所有的小插入会先缓存然后满足配置条件后写入到clickhouse集群里,而且写入失败会生成dump文件,重新尝试插入,相当于缓存了。
    {
    "listen": ":8124",   #启动的端口,clickhouse是8123 ,这个是8124,小心别漏了分号
    "flush_count": 1000000,  #缓存多少行后写入,我这边指定的是100w行写入一次
    "flush_interval": 5000,  #缓存多少秒后写入,我这边指定5秒后写入
    "clean_interval": 0,     #清理内部表的频率,例如插入到不同的临时表,或作为解决query_id等问题的方法,单位:毫秒
    "remove_query_id": true,  #有些驱动程序发送的query_id会阻止批量插入
    "dump_check_interval": 60, #尝试发送转储文件的间隔(秒);-1表示禁用,就是重新尝试插入dump文件的时间
    "debug": false,  # 记录传入的请求日志
    "log_queries": true, 
    "dump_dir": "dumps",   # 转储未发送的数据的目录(如果ClickHouse出错)
    "clickhouse": {
      "down_timeout": 60,  # 服务器宕机时等待的时间(秒)
      "connect_timeout": 10,  # 等待服务器连接的时间(秒)
      "tls_server_name": "", # 覆盖用于证书验证的TLS serverName(例如,如果在多个节点上共享相同的"cluster"证书)
      "insecure_tls_skip_verify": false, # 证书验证
      "servers": [
        "http://127.0.0.1:8123"
      ]
    },
    "use_tls": false,
    "tls_cert_file": "",
    "tls_key_file": ""
  }

最后#

到目前为止大部分问题解决了,虽然还有一些小问题,但日志系统也慢慢趋于稳定,clickhouse数据库号称比传统mysql数据库快一万倍,但坑还是蛮多的,目前国内用的其实不多,但以后估计会变成香饽饽吧。