背景
监控发现生产环境kafka的一个topic消费积压:
问题排查
我们是通过kafka引擎表将数据入到clickhouse ods层表中的。本身引擎表的性能是相当可以的,毕竟使用的批量入库,磁盘顺序写。首先怀疑是数据格式非法,导致一直消费不了。查看clickhouse的日志发现如下异常:
2022.02.10 16:43:50.607118 [ 22994 ] {} <Error> void DB::StorageKafka::threadFunc(size_t): Code: 349, e.displayText() = DB::Exception: Cannot convert NULL value to non-Nullable type: while converting source column request_ip to destination column request_ip: while pushing to view xxxx (f4222cc9-2374-4a3e-9937-1a5856204ae8), Stack trace (when copying this message, always include the lines below):
从异常来看,印证了自己的猜测。那么我们来看看如何解决吧?
解决
首先想到的是不是能将ods表中的request_ip字段修改为可空。clickhouse毕竟是olap数据库,不像mysql这样的,我们首先需要了解下clickhouse在这方面的限制及底层处理逻辑。查看官方文档说明如下:
改变列的类型是唯一的复杂型动作 - 它改变了数据文件的内容。对于大型表,执行起来要花费较长的时间。
该操作分为如下处理步骤:
1)为修改的数据准备新的临时文件
2)重命名原来的文件
3)将新的临时文件改名为原来的数据文件名
4)删除原来的文件
仅仅在第一步是耗费时间的。如果该阶段执行失败,那么数据没有变化。如果执行后续的步骤中失败了,数据可以手动恢复。例外的情形是,当原来的文件从文件系统中被删除了,但是新的数据没有写入到临时文件中并且丢失了。
列操作的 ALTER行为是可以被复制的。这些指令会保存在ZooKeeper中,这样每个副本节点都能执行它们。所有的 ALTER 将按相同的顺序执行。
The query waits for the appropriate actions to be completed on the other replicas.
然而,改变可复制表的列是可以被中断的,并且所有动作都以异步方式执行。
ALTER 操作限制
ALTER 操作允许在嵌套的数据结构中创建和删除单独的元素(列),但是不是整个嵌套结构。添加一个嵌套数据结构的列时,你可以用类似这样的名称 name.nested_name 及类型 Array(T) 来操作。嵌套数据结构等同于
列名前带有同样前缀的多个数组列。
不支持对primary key或者sampling key中的列(在 ENGINE 表达式中用到的列)进行删除操作。改变包含在primary key中的列的类型时,如果操作不会导致数据的变化(例如,往Enum中添加一个值,或者将DateTime 类型改成 UInt32),那么这种操作是可行的。
如果 ALTER 操作不足以完成你想要的表变动操作,你可以创建一张新的表,通过 INSERT SELECT将数据拷贝进去,然后通过 RENAME将新的表改成和原有表一样的名称,并删除原有的表。你可以使用 clickhouse-copier 代替 INSERT SELECT。
ALTER 操作会阻塞对表的所有读写操作。换句话说,当一个大的 SELECT 语句和 ALTER同时执行时,ALTER会等待,直到 SELECT 执行结束。与此同时,当 ALTER 运行时,新的 sql 语句将会等待。
对于不存储数据的表(例如 Merge 及 Distributed 表), ALTER 仅仅改变了自身的表结构,不会改变从属的表结构。例如,对 Distributed 表执行 ALTER 操作时,需要对其它包含该表的服务器执行该操作。
https://clickhouse.com/docs/zh/sql-reference/statements/alter/#alter_modify-column
我们这个字段是没有放在主键中的,否则还真是麻烦。修改涉及到ods层两张表,查看了数据量:第一张在20万左右,第二张表数据量已经接近40亿。针对这两张表,我们采用了两种不同方式来处理:
1、小数据量的表,我们直接通过ALTER语句进行类型修改。
2、大数据量的表,我们是直接修改的物化视图,对于空字段增加默认值,使用函数:ifNull(request_ip,'') request_ip。
这样处理后,效果立杆见影:
另外,补充下一个知识点:clickhouse本身是不支持频繁读写的,所以kafka引擎表也使用的批量的方式,来实现数据落盘的。这块你从kafka的topic消费组的监控上就可以看得很清楚:
比较有规律的抖动。批量入库的另外一个问题就是延迟了。
网友评论