cloudnativecube / octopus

14 stars 2 forks source link

Clickhouse计算与存储分离调研 #48

Open godliness opened 3 years ago

godliness commented 3 years ago

根据clickhouse社区的roadmap介绍: https://github.com/ClickHouse/ClickHouse/issues/17623 目前已经将计算存储分离方案纳入计划当中,目前已经完成的相关PR有:

https://github.com/ClickHouse/ClickHouse/pull/9646 mergetree引擎支持S3 https://github.com/ClickHouse/ClickHouse/pull/16240 replicatedmergetree 引擎支持S3

以下是对目前实现的进度做一个分析介绍:

image

已经完成:

insert: 用户直接将数据插入到某一个replica后,其他replica会通过zookeeper进行感知,将元数据从该replica fetch过来加载到内存和写入本地以保持同步(同理,新加入replica也会如此而进行同步),元数据文件存储在本地磁盘,但是文件里的内容其实是s3的文件名,真实的数据统统存储在s3上,所有replica共享这些数据,当有多个replica同时操作共享文件时会对文件进行加锁,从而保证数据的一致性。

merge: 多个replica可以让其中一个replica做merge操作,其他replica等他其merge操作完成后,向其索要merge后的元数据文件保存到本地,从而减少性能损耗。

select: 经过验证目前是可以支持分布式表查询多个shards(均共用底层s3存储),从而支持并发查询,查询请求可以落到多个replicas(水平扩缩),底层共用s3存储。

delete: 在进行删除操作时,clickhouse内部会有share data lock机制,该机制确保所有的replica的元数据全都删除后才会真正的去删除s3对象存储上的数据。

godliness commented 3 years ago

目前社区有个人(虎哥 ck contributor top50)借鉴了clickhouse并基于rust语言打算实现一版云版的clickhouse(非常值得借鉴): https://github.com/BohuTANG/ClickHouse/commit/f67d98ef408fda1a359e4fb17848619ef1f6e59b#commitcomment-48631223 https://github.com/datafuselabs/datafuse https://datafuse.dev/

目前该开源项目看起来才刚刚开始

godliness commented 3 years ago

https://github.com/ClickHouse/ClickHouse/pull/22012 支持s3Cluster表引擎,可以实现s3分布式存储多文件并发获取的查询

Cas-pian commented 3 years ago

https://cloud.tencent.com/developer/article/1759109 腾讯云CK计算存储分离的经验

mdianjun commented 3 years ago

AWS SDK for C++:https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/examples-s3-objects.html

godliness commented 3 years ago

@mdianjun @Cas-pian @glove747 @mxzlxy @AnberLu @FishermanZzhang

目前遗留问题:

  1. replica pod漂移到其他节点后表信息怎么重建
  2. 跨shard fetch partition操作时写数据是否导致数据不同步
  3. 计算存储分离扩shard 的rebalance操作怎么设计 在k8s上
  4. s3存储性能问题 都存一个bucket的瓶颈以及s3扩容是否影响写
  5. Debug模式的程序会导致程序Crash https://github.com/cloudnativecube/octopus/issues/82 https://github.com/ClickHouse/ClickHouse/issues/23480
godliness commented 3 years ago
  1. Debug模式的程序会导致程序Crash #82 ClickHouse/ClickHouse#23480

官方的计算存储分离 s3 feature 没有在debug模式测试过,所以会有很多问题,若想继续debug新功能去避免一些可疑的问题,请使用 RelWithDebInfo 模式

godliness commented 3 years ago
  1. 跨shard fetch partition操作时写数据是否导致数据不同步

https://clickhouse.tech/docs/en/sql-reference/statements/alter/partition/#alter_fetch-partition 通过该原语跨shard fetch partition数据时,若被fetch的shard上有新数据写入该partition, 那么无论我们是否使用s3计算存储分离都会造成数据不一致的情况。

假如 shard1 fetach shard2

shard1                      shard2

fetchPartition          processQuery

fetchPartition会根据zookeeper中存储的信息找到对应shard下所有的replicas, 然后从中选择一个最优的replica(logPointer最多然后queue最小的)通过这样来确保fetch的数据是最新版,然后再从zookeeper中找到对应replica下partition下的所有parts,然后分别多次(max_fetch_partition_retries_count)调用fetchPart函数来进行拉取。

processQuery会首先走一些并行度的限制,若通过,则本地先findPart看看是否存在查找part会根据几种part的状态进行查找PreCommitted, Committed, Outdated,然后就根据是否开启s3_zero_copy来进行发送数据,若开启s3_zero_copy发送的仅仅是一些metafiles空壳映射文件,若未开启则发送的是真正的数据文件。

整个过程没有对被fetch的分区或表有相关锁操作,所以fetch仅仅是当时所拥有的parts,若有新parts写入是无法同步的;另外,当被fetch的partition中的parts进行了merge,那么就会导致被merge的parts fetch不到,因为没有对那些parts加锁无法阻止其merge, 但极端情况会出现一部分没有merge和同样数据已经merge的parts都被fetch到detach目录中,这样再attach的时候数据并不会重复,因为attach操作判断已经merge的part包含了一部分没有merge的part, 剩下一部分没有merge的part会变成inactive前缀依然留在detached目录中。

所以如果上k8s做计算存储分离的话,除了可以水平扩展一个shard的replica以外,考虑自动扩shard我们就要考虑re-balance,如何做好数据的reblance是个挑战点,之前我们有调研社区的move part to shard原语,这个方法是有锁的粒度缩小到part,我们可以考虑一下: https://github.com/cloudnativecube/octopus/issues/21

godliness commented 3 years ago

mark: https://altinity.com/blog/clickhouse-and-s3-compatible-object-storage

大概看了下该篇文章,主要是介绍了以及几个点:

S3 Table Function

我们可以使用该table function对S3上的数据进行导入与导出,导出的话我们可以利用参数set max_threads=32, max_insert_threads=32 提高下载并行度,但是导入的话目前s3 table function 仅能一个文件一个文件的导入,但这里后期需要feature request 按分区并行导入,需要做一些优化。

另外,input_format_parallel_parsing的开启可能会导致过去的消耗内存,建议关闭,这个我觉得可能并行度越高数据转成内部数据格式需要创建一些内部数据结构chunk之类的,列数越多行数越多,肯定是消耗内存的,具体要看机器配置约定是否关闭。

Query Performance

后面笔者对local存储与本地存储做了一些性能测试,大概的测试结果是,小数据集简单查询差异比较明显,本地磁盘会明显优于s3存储,但s3存储第二次查询时速度会有一定的提升,原因是s3存储会将一些s3上的元数据文件缓存到本地,那么第二次查询时便直接拉取 .bin 文件就可以了;本地存储利用的是linux的pagecache做缓存,但s3存储由于是网络传输,无法利用pagecache,所以使用该方式进行缓存。

大数据集复杂查询的场景下,本地存储与s3的性能差异有所缩减,笔者这里说虽然依然比不上本地存储的速度,但是对于交互式查询来说是可以接受的。

Issues and limitations

s3存储目前不支持副本,不过这个文章可能比较老旧,目前replicatedmergetree引擎已经支持支持S3,具体实现细节请参考 https://github.com/cloudnativecube/octopus/issues/48#issue-839267435

插入与merge性能低下,笔者这里说对于插入性能并行上传目前已经实现了,merge性能可以通过分层存储策略让本地磁盘做merge,s3上禁用merge,从而保证历史数据的安全

目前s3上存储的数据文件是没有层级的,那么也就是说你无法知道哪个文件数据哪个表,那么当需要扩容节点恢复元数据时,也根本没有办法从s3上同步元数据

还有一个缺点就是目前连接s3的安全问题,需要显示指定密钥信息,需要改成token方式鉴权


以上是对于该文章的总结, 目前s3存储并不是真正的计算与存储分离,无法弹性扩缩容,虽然单个shard的多个replica是可以的,但这仅仅提高的是多个sql并发查询,没有提高单个sql的并行计算能力,那么就目前的话,多个shard的水平扩缩容有需要考虑rebalance的问题,比较麻烦,因为s3存储的元数据的hard link是存在clickhouse本地的,每个shard看到的元数据是自己的,没有真正共享全局元数据,我觉得若要真正实现计算存储分离和弹性水平扩缩容,就需要让ck实现dag+mpp,根据sql的任务来计划启动多少个pod, 每个pod知道全局的元数据,完成任务后再聚合结果进行返回。

godliness commented 3 years ago

每个part文件中的多个列文件会按照index_granularity的大小切割后形成多个MarkRange,每次s3 client会根据range的范围计算offset字节偏移量来读取文件(按照range读取),而且目前看来每读取一次就会创建一个httpconnection, 一个比较复杂的查询会创建几千个httpconnections,随着index_granularity的调大,同样的查询httpconnections明显减少,查询时间也会缩短。

https://github.com/ClickHouse/ClickHouse/issues/15172

s3查询优化点:

  1. 目前每个s3文件是按照mark range线程同步的方式读取的,可以改成并行同时读取多个ranges
  2. 底层访问s3的接口应该可配置(结合minio s3的一些request参数配置?),以满足更高的性能需求
  3. ck访问s3底层使用http连接池,复用httpconnection连接
  4. 开启自适应索引粒度,控制每次请求读取文件的大小
godliness commented 3 years ago

https://github.com/rockset/rocksdb-cloud rocksdb云化案例

https://github.com/albogdano/lucene-s3directory lucene云化案例

@mdianjun

LvLiangLiang-dev commented 2 years ago

https://github.com/ClickHouse/ClickHouse/pull/29279 目前实现了单shard多replica并发读,每个replica读一部分数据,单shard 多replica多写功能也是存在的, 但是单机读写多shard没有实现,你有好的方案吗

yej2021 commented 1 year ago

还有一个缺点就是目前连接s3的安全问题,需要显示指定密钥信息,需要改成token方式鉴权

@godliness 你好,token方式已经支持了吗?