Clickhouse中的多个小插入

15

我在ClickHouse中有一个事件表(MergeTree),想同时运行很多小插入操作。但是服务器会过载并变得无响应。此外,一些插入操作会丢失。在ClickHouse的错误日志中有很多记录:

01:43:01.668 [ 16 ] <Error> events (Merger): Part 201 61109_20161109_240760_266738_51 intersects previous part

有没有一种方法可以优化这些查询?我知道我可以使用批量插入来处理某些类型的事件。基本上,运行一个包含多个记录的插入,clickhouse 可以很好地处理。但是,其中一些事件,如点击或打开,无法以这种方式处理。

另一个问题是:为什么 clickhouse 决定存在相似的记录,而实际上它们并不存在?在插入时存在类似的记录,其具有与索引中相同的字段,但其他字段不同。

我也会时不时收到以下错误:

Caused by: ru.yandex.clickhouse.except.ClickHouseUnknownException: ClickHouse exception, message: Connect to localhost:8123 [ip6-localhost/0:0:0:0:0:0:0:1] timed out, host: localhost, port: 8123; Connect to ip6-localhost:8123 [ip6-localhost/0:0:0:0:0:0:0:1] timed out
    ... 36 more

大多数情况下,在构建项目时运行针对ClickHouse数据库的测试。

5个回答

21

ClickHouse有一种特殊类型的表,称为Buffer表。它存储在内存中,并允许进行许多小插入而不会出现问题。我们每秒接近200个不同的插入-它可以正常工作。

缓冲表:

CREATE TABLE logs.log_buffer (rid String, created DateTime, some String, d Date MATERIALIZED toDate(created))
ENGINE = Buffer('logs', 'log_main', 16, 5, 30, 1000, 10000, 1000000, 10000000);
主表:
CREATE TABLE logs.log_main (rid String, created DateTime, some String, d Date) 
ENGINE = MergeTree(d, sipHash128(rid), (created, sipHash128(rid)), 8192);

手册中的详细信息:https://clickhouse.yandex/docs/en/operations/table_engines/buffer/


在处理缓冲表时,如果您想在主表中添加新字段,则不能即时完成。您需要停止所有操作,并在主表和缓冲表中插入新字段,然后再次运行所有操作。 - Alexander Suvorov

11

我们有基于开关的事件生成。将它们批量插入CH,意味着我们需要为它们构建一个暂存区,可能是队列或其他临时位置。这是我们必须要做的吗?还是有另一种推荐的单行插入方式?第二个跟进问题:文档中指出:“为了提高性能,您可以并行执行多个INSERT查询...”。这是否意味着我们可以运行并行进程/线程,每个进程都在并行插入,但每个进程需要每秒执行一批? - ipolevoy
是的,您需要在某个队列或服务内部缓冲区中累积事件,并进行批量插入。 - uYSIZfoz
1
您可以并行执行多个INSERT操作;建议在所有线程中每秒执行一个批处理。 - uYSIZfoz
这不是一个错误,而是数据库架构的预期症状。 - Andy Gee

9
我曾经遇到类似的问题,虽然没有那么严重——每秒约进行20次插入操作会导致服务器负载、内存消耗和CPU使用率升高。我创建了一个缓冲表(Buffer table)来将插入操作在内存中进行缓冲,然后定期将它们刷新到“真正”的磁盘表中。就像魔术一样,所有情况都变得平稳了:负载、内存和CPU使用率回到了正常水平。好处是您可以对缓冲表运行查询,并从内存和磁盘中获取匹配行——因此客户端不受缓冲的影响。请参阅https://clickhouse.tech/docs/en/engines/table-engines/special/buffer/

你是否使用缓冲区进行单行插入?文档建议即使对于多个单行插入也不要使用缓冲区:请注意,即使对于缓冲表的多个单行插入,逐行插入数据也没有意义。我正在尝试找到一种方法来插入多个单行,而我不想自己构建队列系统。 - Gokhan Sari
@GokhanSari - “这没有意义”的说法是主观的。也许对于你的用例,你会发现它确实是有意义的。在尝试构建更复杂的机制之前,请先试用它!如果您不打算每秒插入成百上千个单行,则缓冲表可能完全可以胜任。 - zooglash

1

1
clickhouse的MergeEngines设计并不适合同时进行小写操作。据我所知,MergeTree会根据分区将写入表中的数据“parts”合并,然后重新组织这些“parts”,以获得更好的聚合读取效果。如果经常进行小写操作,则可能会遇到另一个名为“Merge”的异常。请注意保留html标记。
Error: 500: Code: 252, e.displayText() = DB::Exception: Too many parts (300). Merges are processing significantly slow

当你试图理解为什么会抛出上述异常时,这个想法会更加清晰。CH需要合并数据,并且存在可以存在的部分数量的上限!每个批量写入都被添加为一个新部分,然后最终与分区表合并。
SELECT
    table, count() as cnt
FROM system.parts 
WHERE database = 'dbname' GROUP BY `table` order by cnt desc

上述查询可以帮助您监视部件,观察在编写时部件如何增加并最终合并。
我对此的最佳建议是将数据集缓冲,并定期将其刷新到数据库中,但这意味着没有实时分析。
使用缓冲区很好,但请考虑以下几点:
- 如果服务器异常重启,则缓冲区中的数据将丢失。 - 对于缓冲区表,FINAL和SAMPLE无法正常工作。这些条件传递到目标表,但不用于处理缓冲区中的数据。 - 向缓冲区添加数据时,其中一个缓冲区会被锁定。(因此无法读取) - 如果将目标表复制,则在写入缓冲区表时会丢失一些预期的复制表特性。(无去重)
请仔细阅读,这是一个特殊的引擎:https://clickhouse.tech/docs/en/engines/table-engines/special/buffer/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接