持续创作,加速成长!这是我参加「日新计划 10 月更文挑战」的第3天,点击检查活动概况

1. 事务背景

得物上一代日志渠道的存储首要依赖于 ES。跟着公司事务的高速发展,日志场景逐渐发生了一些新需求,首要表现在:运用数量逐渐增多,研制需求打印更多的日志定位事务问题,安全合规需求保存更长时刻的日志。跟着 Clickhouse 的运用广泛,咱们了解到职业部分知名公司现已将日志渠道逐渐由 ES 搬迁至Clickhouse,以此来获取更好的写入功能与高紧缩比。因此咱们与日志渠道研制团队开端进行日志渠道新存储的选型评估,本文会介绍咱们怎么经过 Clickhouse 的冷热别离存储替代 ES 的实施计划。

2. 前置介绍

2.1 ClickHouse 简介

ClickHouse 是一个用于联机分析( OLAP )的列式数据库管理体系( DBMS )。列式数据库更适合于 OLAP场景(关于大多数查询而言,处理速度至少进步了 100 倍),下面经过图片更有利于直观了解:

  • 行式

    ClickHouse 冷热分离存储在得物的实践
    图片来历:ClickHouse.com/docs/assets…

  • 列式

    ClickHouse 冷热分离存储在得物的实践
    图片来历:ClickHouse.com/docs/assets…

一起 ClickHouse 依据列存,还供给了很高的紧缩比。紧缩算法 lz4 能够到 1:4 ,zstd 能够到 1:10 。本文首要是介绍咱们在 ClickHouse 上面的实践,关于 ClickHouse 自身的特性及相关介绍就不在这儿赘述了。

2.2 ClickHouse 存储战略

ClickHouse 创立表时是支撑表级数据 TTL 战略的,TTL 战略能够支撑数据过期主动兼并(Compaction)后删去,当然也支撑主动兼并后移动到其他Disk或Volumn。日志渠道的多级存储便是利用了存储战略,但由于踩了TTL的一个坑,咱们终究抛弃表级TTL设置,改成搬家表part的使命调度完结TTL战略,后面会说到。

  • 装备存储战略
<path>/data1/ClickHouse/data/</path>  --为了便于查找,咱们主张在默许的存储途径下方添加存储战略
    <storage_configuration>
            <disks>
                <hot>
                    <path>/data1/ClickHouse/hot/</path>  --这儿留意,运用存储战略后,主张任何数据途径之间不要有子集联系
                </hot>
                <cold>
                    <path>/data2/ClickHouse/cold/</path>
                </cold>
            </disks>
            <policies>
                <ttl>
                    <volumes>
                        <hot>
                           <disk>hot</disk>
                        </hot>
                        <cold>
                           <disk>cold</disk>
                        </cold>
                    </volumes>
                </ttl>
            </policies>
    </storage_configuration>
    • < path > 为ClickHouse默许的存储途径,找到该标签后在下方添加存储战略标签<storage_configuration>。
    • <storage_configuration>:固定标签,界说存储战略。
    • < dicks > 固定标签,下面会界说磁盘称号,以及磁盘绝对途径。
    • < hot >、< cold >:自界说标签,用来符号该途径,可按照此称号界说便于区别。
    • < policies >:固定标签,界说具体存储战略称号。
    • < ttl >:自界说标签,界说具体存储战略的称号,用于表级TTL,可按照此称号界说便于区别。
    • < volumes >:固定标签,界说卷组。
    • < hot >、< cold >:卷组称号,每个卷组下能够包括一个或多个disk标签,disk标签取值为< disks >标签下界说的磁盘称号。
  • 创立表

示例:

CREATE TABLE db_rdsauditlog_local ON CLUSTER auditlog
(
   `check_rows` Int64,
   `client_ip` String,
   `db` String,
   `fail` Int64,
   `instance_id` String,
   `latency` Int64,
   `origin_time` DateTime('Asia/Shanghai'),
   `return_rows` Int64,
   `sql` String,
   `thread_id` Int64,
   `update_rows` Int64,
   `user` String,
   `tables` Array(String),
   `sqlhash` String,
   `sqlfingerprint` String,
   `sqltype` String,
   INDEX instance_sqltype (instance_id, sqltype) TYPE set(100) GRANULARITY 5,
   INDEX origintime_sqlhash (instance_id,sqlhash) TYPE set(100) GRANULARITY 5,
   INDEX origintime_instance_clientip(instance_id, client_ip) TYPE set(100) GRANULARITY 5
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(origin_time)
ORDER BY origin_time
TTL origin_time + INTERVAL 6 hour TO DISK 'cold'
SETTINGS storage_policy = 'ttl',index_granularity = 8192;
Create table db_rdsauditlog on cluster auditlog as db_rdsauditlog_local ENGINE=Distributed(auditlog, default, db_rdsauditlog_local, rand());

创立表级 TTL ,要想运用分级冷热存储,必需求指定存储战略,才能运用到 Disk ,为什么要指定存储战略,我的了解是TTL能够指定到 Disk 和 Volumn 级,可是 Volumn 等级只能界说在存储战略里。

具体阅览:ClickHouse.com/docs/en/eng…

2.3 分区战略

PARTITION BY– 分区键,表级可选的参数。在大多数状况下,其实不需求分区键,一起即使运用了分区键,不太主张运用比月更细粒度的分区键,分区不会加速查询(与 ORDER BY 表达式相反)。你永久不应该运用太细化的分区。不要按客户端标识符或称号对数据进行分区,而是将客户端标识符或称号作为 ORDER BY 表达式中的榜首列(官方文档这句话仍是要好好紧记,一般咱们只主张时刻字段做分区)。

以上是官方文档的原话,可是实践中咱们需求依据具体状况是能够创立乃至到小时等级粒度的分区,仍是那句话,看需求,用不好会有副作用,后面会说到。

3. 事务需求

事务背景中除了说到需求很高的写入才能,很高的紧缩比,实践需求调研中,根底架构同学还提出各个事务域都有自己的日志时刻查询规模需求(ES作为存储时,研制乃至需求能够供给天级的日志保存时刻),比方7天,30天,90天,乃至更长等不同规模,由于冷数据查询频次较低,能够运用更低价的存储介质,别的有一些数据保存一段时刻之后不会被查但需求满意合规要求,再长的日志就能够删去。根底架构希望DBA能够帮忙在ClickHouse的存储上对数据保存时刻供给一些主张,尽最大或许下降存储本钱。

总结一下便是如下需求:

  • 怎么能尽或许满意各个事务域的天级保存战略?
  • 怎么将数据能够依据日期存放在不同的存储介质中?
  • 多级存储战略选用什么样的存储介质能够尽最大或许下降存储本钱?

依据这些需求,DBA对这些需求供给了如下计划:

  • 天级保存战略,咱们运用了表分区战略,并规避了一些坑。
  • 多级存储首要运用了三级存储战略,即Hot+Cold+Arch(Archive后续均已 Arch 替代)。
  • Hot盘选用ESSD PL1盘挂载宿主机,磁盘称号为hot;Cold选用ESSD PL0盘挂载宿主机,磁盘称号为cold;Arch咱们终究挑选直接挂载OSS文件体系(由于中心咱们还调研过JuiceFS,但终究仍是抛弃了),磁盘称号为arch。

下面逐个讲解。

4. 计划规划

如事务需求,咱们需求处理三个问题:

4.1 怎么尽或许满意各个事务域的天级保存战略?

  • 计划1: 分区战略PARTITION BY (application,environment,toYYYYMMDD(log_time))

最初咱们希望运用分区战略,用运用称号+环境+按天分区的组合分区战略来满意要求,即PARTITION BY (application,environment,toYYYYMMDD(log_time))。这样每个运用每个环境有独立的分区,事务研制还能够很灵敏的随意修正天级的日志保存时刻,这样每个分区能够依据保存战略独立的移动到不同的磁盘。理想很丰满,现实很骨感。尽管这个计划能够很好的满意需求,可是真正写入的时候,咱们遇到了问题,由于线上运用在较多,且还在呈上升趋势,每个运用还或许有多个环境,即使每个运用每天只写入到一个天分区,实践测验过程中,咱们发现写入功能严重不足,写入很慢,一起碰到多个参数值不行报错的问题。

  • max_partitions_per_insert_block,默许值是100。

意思是一次刺进的数据块涉及到的分区数量。由于日志是消费的kafka一个某个topic,一个topic或许有几百个运用,每个运用还有多个环境,即使写入天级分区,分区是物理上隔离成不同的目录,一次写入也会被ClickHouse拆分红几千个不同partition中的part,一次insert涉及到的partition数爆炸,提示刺进数据的part数量超越该参数值(too many partitions for single insert blocks),远超参数设置,100底子不行,调整到1w仍是报错。写入量不是很大的状况下,是能够恰当调整该参数,但测验环境日志发生的实在太碎,上调该参数很快就有too many parts的报错,too many parts的呈现便是ClickHouse兼并跟不上写入,直接拒绝写入数据,这便是下面的参数max_parts_in_total值不行。

  • max_parts_in_total,默许值10w。

测验环境中由于有一些运用数据较少,导致攒批数据比较难,加上若代码在批次写入数据的地方处理不好,很简单呈现频繁写入较少数据行的part,加上运用数,环境较多,导致写入的数据较碎,一个表内active的part数据非常简单超越10w(能够检查system.parts表中状态为active的数据个数),多次报错too many parts in total的过错。这儿能够恰当进步background_pool_size的值提高兼并速度,可是关于很多较碎的part也是杯水车薪,不能处理底子问题。终究咱们抛弃计划1,挑选计划2。

  • 计划2: 分区战略PARTITION BY (toDate(log_time), log_save_time, oss_save_time)

计划1的问题最首要仍是分区字段设置的问题,咱们决定在满意写入才能的前提下供给必定才能的日志保存时刻。终究将分区字段去掉运用称号application字段(为了确保查询速度,咱们将application字段放入到order by的榜首个字段。

由于三级保存战略,最开端想到用多个表即咱们界说日志保存时刻规模的固定选项(7d,30d,90d,180d),日志保存时刻需求研制做一些妥协,不能随意修正保存时刻,依据这几个选项创立对应的表,这样能发挥写入的最佳功能。可是会有一个问题,便是当研制提工单修正运用的保存时刻后,时刻调整会导致日志落入不同的表,这样代码中查询语句不变的状况下会呈现变更保存时刻后查不到历史数据的问题,尽管能够代码做必定的路由规矩,但很繁琐,不行友好,抛弃该计划。

怎么处理?经过咱们讨论,咱们想到一个方法,表中添加两个字段log_save_time,oss_save_time,这两个字段是int类型,将分区字段调整为PARTITION BY (toDate(log_time), log_save_time, oss_save_time)。log_save_time为每条日志在hot盘中保存的时刻,超出会被使命移动到cold盘;oss_save_time为每条日志在cold盘中保存的时刻,超出会被移动到arch盘(这儿的移动使命会在下面介绍到)。使命每天会查询system.parts表,检查分区字段中三个字段比照,即toDate(log_time)和当时日期比较,差值大于分区中log_save_time的值,则移动该分区到cold盘,差值大于oss_save_time则移动到arch盘。这样假如运用日志保存战略的元数据信息修正,新发生的日志数据这两个字段也会写入新的保存战略值,新数据会落到不同的分区中。那么怎么完结?进入问题2。

4.2 怎么满意依据日期存放在不同的存储介质中?

处理了榜首个问题,那么日志怎么依据分区设定做过期移动?

  • 计划1: 运用表级TTL设置

最开端咱们想到在表的创立语句中运用表级TTL设置:

PARTITION BY (toDate(log_time), log_save_time, oss_save_time)
ORDER BY (application, environment, log_time, ip, file_offset)
TTL origin_time + INTERVAL 24 hour TO DISK 'cold' --类似这样,
SETTINGS allow_nullable_key = 1, storage_policy = 'ttl', index_granularity = 8192

长处是利用ClickHouse自身的才能做数据的搬家,完结过期数据搬迁到冷存储介质。
但会有一个问题,便是会导致前面说到的会依据多个可选时刻规模创立多个对应的表。

别的还有一个坑便是,表级TTL一旦要修正TTL的保存时刻,ClickHouse会reload表的一切part目录,导致IO Util打满,集群无法呼应,这个很坑,现在还没有好的方法处理,所以抛弃该计划。

  • 计划2: 开发调度使命,手动移动分区数据

结合问题1,终究咱们挑选这样创立表结构(只作为 Demo 参考,并非事务真实状况),如下:

CREATE TABLE dw_log.tb_logs_local
(
    `application` String,
    `environment` String,
    `ip` String,
    `filename` String,
    `keys` Array(Nullable(String)),
    `values_string` Array(Nullable(String)),
    `values_number` Array(Nullable(Float64)),
    `file_offset` Nullable(UInt32),
    `message` String CODEC(ZSTD(1)),
    `log_type` String,
    `log_time` DateTime64(3),
    `log_level` String,
    `trace_id` String,
    `pid` Int64,
    `endpoint` String,
    `log_save_time` Int32,
    `oss_save_time` Int32,
    `meta_size` Int64,
    INDEX meta_size meta_size TYPE SET(100) GRANULARITY 2,
    INDEX application application TYPE SET(100) GRANULARITY 2,
    INDEX environment environment TYPE SET(100) GRANULARITY 2,
    INDEX ip ip TYPE SET(100) GRANULARITY 2,
    INDEX idx_message message TYPE tokenbf_v1(512, 2, 0) GRANULARITY 2,
    INDEX trace_id trace_id TYPE SET(100) GRANULARITY 2,
    INDEX log_level log_level TYPE SET(10) GRANULARITY 2,
    INDEX pid pid TYPE SET(100) GRANULARITY 2,
    INDEX idx_endpoint endpoint TYPE tokenbf_v1(512, 2, 0) GRANULARITY 2,
    INDEX logtime log_time TYPE minmax GRANULARITY 2
)
ENGINE = MergeTree
PARTITION BY (toDate(log_time), log_save_time, oss_save_time)
ORDER BY (application, environment, log_time, ip, file_offset)
SETTINGS allow_nullable_key = 1, storage_policy = 'ttl', index_granularity = 8192

日志渠道会起一个调度使命,一起维护运用和(log_save_time,oss_save_time)的对应联系。每天依据该表的信息做运用对应日志分区的搬家动作。

alter table dw_log.tb_logs_local on cluster default MOVE PARTITION XXX to disk 'cold'

但这儿也有一个小问题便是当研制需求修正日志保存时刻时,比方保存时刻调大,则新的数据会落到新的分区里,这样之前的分区会由于匹配规矩原因被提早删去,比方 7 天调整到 30 天,那么由于之前保存时刻在分区中仍是7这个值,到了第7天,之前的分区现已满意删去战略会被删去。尽管改成 30 天保存战略,依然会呈现有7天日志查不到的状况,当然时刻往后推移7天后仍是能查到完好的 30 天日志。反之保存时刻调小,比方从 30 调整到 7 ,会呈现最长有 23 天的日志被持续保存,空间没有及时开释。时刻推移 23 天后空间开释。这些问题不要紧,能够容忍,比照多个表的计划,该计划利大于弊,终究挑选该计划。

4.3 挑选何种介质满意归档数据的海量存储?

处理了过期战略,表结构的规划后,前面说到的arch磁盘来存储根本不查的数据,运用低存储本钱介质来下降本钱,咱们首要想到的便是能不能运用OSS?答案是能够,一起咱们检查过费用,平等容量的 OSS 本钱仅是 ESSD PL0 的三分之一,这无疑能够大幅下降存储费用。但怎么用最优,需求调研+测验。

  • 计划1: ClickHouse + JuiceFS + OSS

JuiceFS首要功能便是将 S3 转成文件体系挂载运用,且在多家知名互联网公司都有上线案例( 某海外电商渠道的公开技术分享文章就说到了依据 JuiceFS 完结的 ClickHouse 冷热别离存储),因此咱们依据这些信息开端调研这个计划的可行性。

JuiceFS 介绍和架构图如下:

JuiceFS 是一款面向云原生规划的高功能共享文件体系,在 Apache 2.0 开源协议下发布。供给齐备的 POSIX 兼容性,可将简直一切目标存储接入本地作为海量本地磁盘运用,亦可一起在跨渠道、跨地区的不同主机上挂载读写。

JuiceFS 选用「数据」与「元数据」别离存储的架构,然后完结文件体系的分布式规划。运用 JuiceFS 存储数据,数据自身会被耐久化在目标存储(例如,Amazon S3),相对应的元数据能够按需耐久化在 Redis、MySQL、TiKV、SQLite 等多种数据库中。

JuiceFS 供给了丰富的 API,适用于各种形式数据的管理、分析、归档、备份,能够在不修正代码的前提下无缝对接大数据、机器学习、人工智能等运用渠道,为其供给海量、弹性、低价的高功能存储。

ClickHouse 冷热分离存储在得物的实践
图片来历:www.juicefs.com/docs/zh/ass…

当时考虑 JuiceFS 的别的一个原因是它的读缓存机制,当拜访 JuiceFS 中的文件时,会有多级缓存给经常拜访的数据供给更好的功能,读恳求会依次尝试内核分页缓存、JuiceFS 进程的预读缓冲区、本地磁盘缓存,当缓存中没找到对应数据时才会从目标存储读取,并且会异步写入各级缓存确保下一次拜访的功能。

ClickHouse 冷热分离存储在得物的实践
图片来历:www.juicefs.com/docs/zh/ass…

可见 JuiceFS 支撑将目标存储挂载到 ECS 上,经过文件体系做本地盘拜访,一起支撑读缓存来加速文件读取,咱们也做了测验,确实读写功能还不错。

本地读写测验:

ClickHouse 冷热分离存储在得物的实践

将 JuiceFS 的文件体系挂载到 ClickHouse 存储战略下,进行 SSBM 测验成果如下:

QPS(ESSD PL0) QPS(JuiceFS)
Q1.1 30.919 28.642
Q1.2 210.061 191.958
Q1.3 395.616 350.915
Q2.1 5.344 5.053
Q2.2 5.786 6.029
Q2.3 6.173 6.446
Q3.1 3.297 2.618
Q3.2 4.484 4.657
Q3.3 5.427 5.748
Q3.4 243.571 216.825
Q4.1 2.978 3.011
Q4.2 9.057 8.845
Q4.3 14.022 14.155

从 ClickHouse 角度看,查询功能和PL0相当,乃至略好一些。( PL0 相比OSS本钱要高不少)

可是,终究咱们没有运用 JuiceFS 原因如下:

  • 整个JuiceFS架构引入第三方存储介质用来保存文件的元数据。
  • 元数据存储运用Redis不能确保元数据的一致性,若 Redis 发生切换,元数据丢掉则 ClickHouse 存在丢掉数据的危险;
  • 元数据存储运用 MySQL、 QPS 和 RT 均不能满意 ClickHouse 高速文件拜访对元数据的拜访频次;
  • 元数据存储运用 TiKV,会添加运维复杂性。

依据以上问题,咱们为了防止引入其他技术栈带来额外的运维本钱,当然这儿并不是说 JuiceFS 有什么弊端,而是在咱们这个场景下需求的是尽或许少引入其他组件。刚好咱们有现成的目标存储服务。于是持续测验了原生挂载 OSS 的计划(也便是下面要讲到的计划)。这次调研也让咱们深化了解了JuiceFS的架构和优势,为后续在其他场景的运用奠定了根底。

  • 计划2: ClickHouse + OSS(咱们终究挑选的计划)

ClickHouse 原生的 MergeTree 自身就支撑直接挂载 S3 作为数据盘运用,咱们依据这个特性做了测验,成果契合预期。尽管官方的 demo 是 S3,由于 OSS 也是支撑 S3 协议,所以也是同样能够运用的。需求留意的是 endpoint 这儿有必要 http 最初,不然无法正常挂载。

存储战略装备如下:

<storage_configuration>
            <disks>
                <hot>
                    <path>/data1/ClickHouse/hot/data/</path>
                    <move_factor>0.1</move_factor>
                </hot>
                <cold>
                    <path>/data2/ClickHouse/cold/data/</path>
                </cold>
                <arch>
                   <type>s3</type>
                   <endpoint>http://log-sh.oss-cn-xx-internal.xxx.com/xxxxxxxxxx/</endpoint>
                   <access_key_id>xxxxxxxx</access_key_id>
                   <secret_access_key>xxxxxx</secret_access_key>
                   <metadata_path>/data1/ClickHouse/disks/s3/</metadata_path>
                   <cache_enabled>true</cache_enabled>
                   <data_cache_enabled>true</data_cache_enabled>
                   <cache_path>/data1/ClickHouse/disks/s3/cache/</cache_path>
                </arch>
            </disks>
            <policies>
                <ttl>
                    <volumes>
                        <hot>
                           <disk>hot</disk>
                        </hot>
                        <cold>
                           <disk>cold</disk>
                        </cold>
                        <arch>
                           <disk>arch</disk>
                        </arch>
                    </volumes>
                </ttl>
            </policies>
    </storage_configuration>

测验状况:

a. 写测验,move数据从hot盘到OSS。

ClickHouse 冷热分离存储在得物的实践

OSS 写入能够到7Gb/s。

b. 重启集群也能很快启动。

ClickHouse 冷热分离存储在得物的实践

该计划架构简单可靠,便于运维,终究咱们挑选了直接挂载 OSS 。挑选 OSS 后,关于归档的数据量,费用能够比 ESSD PL0 还能节省 66%。而且咱们给每个集群单独请求一个 OSS Bucket,下降本钱的一起还能满意满意的写功能。日志渠道现在现已陆续有日志满意归档战略开端搬迁,相信会节省很大的一笔费用。

5. 存储架构

依据上面讲的计划,咱们的日志渠道存储架构如下:

ClickHouse 冷热分离存储在得物的实践

该架构充分利用了云上的根底设施带来的优势,比方:

  1. 依据 ESSD 云盘完结了单副本的存储架构(云盘现已完结了多副本存储),若 ClickHouse 节点毛病会主动搬迁到新的 ClickHouse 节点,ESSD 磁盘会一起搬迁。能够完结热(内存 copy)搬迁或许自行触发搬迁;
  2. 依据 ESSD 的本地扩展才能,能够无损完结磁盘空间的垂直扩容,服务不受影响;比方前期存储空间占用不多,能够先给预算的 50%,跟着数据量逐渐上升再扩容;
  3. 依据 ECS 的扩缩容才能,完结快速的节点才能扩容或降配减缩本钱。由于 ClickHouse 自身不具备主动化 reblance 的才能,咱们能够先给较多低配节点,后期跟着事务量上升进行 ECS 的垂直升配即可;
  4. 依据低本钱目标存储 OSS,存储不拜访或许拜访量很少的归档数据。假如自己经过 HDD 建立的话,是需求一次性投入。云上的 OSS 能够按量收费,相比照较划算。

6. 总结

这篇文章首要讲解了前一段时刻 DBA 团队在日志渠道的事务改造中参加的一部分事项,如表字段索引规划主张、过期战略的计划制定、SQL 编写和优化主张、供给本钱下降计划等输出。终究经过与日志渠道研制同学的尽力,咱们将日志渠道存储由 ES 搬迁到了 ClickHouse ,不光获得了高功能的写入才能,一起也额外节省存储本钱 50% 以上。

引证:

  • ClickHouse.com/docs/en/eng…

  • www.juicefs.com/docs/zh/com…

  • www.juicefs.com/docs/zh/com…

文/Chen

重视得物技术,每周一三五晚18:30更新技术干货
要是觉得文章对你有协助的话,欢迎谈论转发点赞~