You need to enable JavaScript to run this app.
导航
ByteHouse 容量规划最佳实践
最近更新时间:2025.11.07 14:04:41首次发布时间:2025.11.07 14:04:41
复制全文
我的收藏
有用
有用
无用
无用

CPU 水位

水位建议

  • 正常:集群整体 CPU 使用率小于 80%。
  • 观察:间歇性超过 90%,但查询写入延迟无明显变化。
  • 危险:集群 CPU 使用持续超过 90%,查询写入延迟增加。

排查思路

建议优先使用 ByteHouse 企业版的 SQL 诊断功能定位到 CPU 占用高的 SQL,SQL 诊断功能详情请参见SQL 诊断。然后根据以下建议尝试优化。
该思路同样适用于内存、IO 水位高的场景。当业务流量上涨时可优先通过垂直扩容缓解资源压力,垂直扩容操作详情请参见垂直变配

SELECT 查询优化

  • 减少扫描数据量WHERE 子句最佳实践
    • 分区键过滤:永远在 WHERE 中包含分区键的过滤条件。
    • 谓词下推:确保过滤条件能被下推到存储层。避免在 WHERE 子句的列上使用非单调函数,这可能导致索引和分区裁剪失效。

推荐在值上进行计算

WHERE event_time >= '2025-08-01 00:00:00' AND event_time < '2025-09-01 00:00:00';

反模式在列上使用函数

WHERE toYYYYMM(event_time) > 202508;

使用值过滤条件,在分区过滤基础上引擎可以进一步通过 Part 分区键上的 minmax 信息,裁剪掉不必要的 Part。

  • 优化 JOIN 操作
    • JOIN 顺序:将大表放在 JOIN 子句的左侧,小表放在右侧,通常引擎会自动 reorder。
    • JOIN 前预过滤:先用 CTE 或子查询对 JOIN 的表进行过滤和聚合,减少 JOIN 的数据量。

表结构设计优化

  • 合理选择分区键Partition Key
    分区键是 ByteHouse 中最重要、最有效的剪枝工具。通过在 WHERE 子句中指定分区键的过滤条件,ByteHouse 可以仅扫描相关的分区,跳过大量无关数据。
    选择原则

    1. 必须是查询过滤的常用字段:选择那些在绝大多数查询中都会用来过滤数据的列,最典型的就是日期或时间字段(如 dt, event_date)。
    2. 基数适中Cardinality):分区的数量不宜过多或过少。分区过多(如按秒分区)会增加元数据管理的开销;分区过少(如按年分区)则失去了分区裁剪的意义。通常建议按月、周或者按天分区,总分区数建议在 10 万以下。
    3. 分区粒度均匀:确保每个分区的数据量大致均衡,避免数据倾斜。
      示例
    -- 创建一个按天分区的事件表 
    CREATE TABLE events (
        event_id String,
        user_id UInt64,
        event_time DateTime,
        -- ... 其他列
    ) ENGINE = CNCHMergeTree
    PARTITION BY toYYYYMMDD(event_time) -- 使用日期作为分区键
    ORDER BY (user_id, event_time);
    
  • 排序键Order Key与数据分布
    排序键决定了在每个分区内部,数据是如何物理排序存储的。这种有序性带来了两大好处:

    • 加速过滤:对于排序列的范围查询,ByteHouse 可以利用其有序性快速定位数据块,类似索引效果。
    • 提升压缩率:相同的值聚集在一起,可以获得更高的压缩比。
      选择原则
    1. 高频过滤列在前:将最常用于 WHERE 条件的列放在排序键的最前面。
    2. 低基数列在前:将基数较低的列(如 countrygender)放在基数较高的列(如 user_id)前面,可以提升压缩效果。
  • 数据类型选择
    选择最紧凑、最合适的数据类型,不仅可以节省存储空间,还能提升计算效率。

    • 数值类型:用 UInt 系列代替 Int(如果无负数),并选择能容纳数据范围的最小类型,如用 UInt8UInt16 代替 UInt64
    • 字符串类型:对于基数有限的字符串,使用 LowCardinality(String)Enum 类型,可以将其转换为字典编码存储,极大减少存储并加速过滤和聚合。
    • 避免 ****NULL:尽量为列定义 NOT NULL 属性,Nullable 列会带来额外的存储和计算开销。

使用跳数索引(Data Skipping Index)

跳数索引是 ByteHouse 支持的一种查询加速功能。在处理大量数据时,查询性能可能会因为需要进行全列扫描来应用 WHERE 子句而下降。跳数索引通过让查询跳过那些确认不包含匹配值的数据块来解决这个问题。

创建跳数索引

创建索引时涉及四个主要参数:

  1. 索引名称Index Name):作为创建、删除或物化索引文件的标识符。
  2. 索引表达式Index Expression):用于计算和确定索引中存储值范围的公式,可包含列、基本运算符和函数。
  3. 类型Type):定义了用于跳过读取和评估每个索引块的计算方式。
  4. 粒度Granularity):定义每个索引块包含的颗粒数量。例如,若主表索引粒度为 8192 行,跳数索引粒度为 4,则每个索引块将包含 32,768 行(8192 x 4)。

语法示例

/*
索引名称: key_i_idx
索引表达式: key_i
类型: minmax
粒度: 1
*/
INDEX key_i_idx key_i TYPE minmax GRANULARITY 1

支持的数据类型

  1. 最大最小值minmax
    • 描述:这是一种高效的索引方法,它为每个数据块保留索引表达式的最小值和最大值。对于松散排序的列最有效,通常是查询执行中最具成本效益的索引类型。
    • 限制:仅适用于标量或元组表达式,不能与生成数组或映射数据类型的表达式一起使用。
  2. 数据集Set
    • 描述:这种轻量级索引包含一个参数 max_size,用于设置每个块的值集大小。当 max_size 为 0 时,值集大小不受限制。它适用于每组颗粒中值聚集在一起的列。
    • 成本:索引的成本和性能取决于块内的基数。如果唯一值过多,评估成本会变高;如果值集由于超过 max_size 而为空,则索引不会被应用。
  3. 布隆过滤器类型Bloom filter types
    布隆过滤器是一种紧凑的数据结构,用于判断集合成员是否存在,可能存在极小的误报率。在跳数索引中,误报仅会导致读取一些不必要的块。
    • tokenbf_v1:专为增强的布隆过滤器功能定制,适用于 StringFixedString 类型。它将输入表达式按非字母数字字符分割成字符序列。此索引可用于 LIKEEQUALSin 等操作,适合在非结构化日志中搜索少量特定值。其参数包括:
      • 过滤器大小(字节)
      • 哈希函数数量
      • 哈希函数种子
    • ngrambf_v1:功能与 tokenbf_v1 类似,但增加了一个额外参数,即要索引的 ngram 的大小。它对于没有单词间断的语言(如中文)的文本搜索很有用。
    • bloom_filter([false_positive]):通用布隆过滤器,可通过 false_positive 参数(范围 0-1,默认 0.025)指定假阳性概率。支持多种数据类型,包括 Int*, UInt*, Float*, String, Array 等。

内存水位

水位建议

  • 正常:常驻内存小于 60%,且没有查询因内存报错。
  • 观察:内存使用持续在 80%。
  • 危险:内存超过 80% ,查询频繁报错 Memory limit exceeded,进程开始因内存溢出(Out of Memory,OOM)被 kill。

排查思路

常驻内存低,查询报错内存超限

  1. 可参考 CPU 部分的排查思路,优先通过 SQL 诊断定位内存使用高的 SQL 类型,并尝试进行优化,SQL 诊断功能详情请参见SQL 诊断
  2. 可通过垂直扩容,将集群计算资源切换为 1:8 的规格,增加集群内存规格,操作详情请参见垂直变配

常驻内存高

建议开启高级诊断功能后,联系 ByteHouse 团队处理,高级诊断操作详情请参见高级诊断

磁盘容量

当集群开启冷存后,强烈建议不设置本地存储上限,并为每张表设置冷存移动的 TTL,避免因设置不当导致新写入的数据直接进入冷存储,进而影响整表的查询的性能。为表设置冷存移动 TTL 操作请参见通过 SQL 语句建表

水位建议

  • 正常:70%~85%,若已开启冷存,则同时应保证磁盘使用低于冷存策略阈值。默认冷存策略阈值为 100%。
  • 观察:高于 85% 或高于冷存策略阈值。
  • 危险:高于 90%。

排查思路

执行以下命令,定位占用空间多的表,并根据查询结果判断是调整 TTL 或扩容磁盘。TTL 用法详情请参见TTL,扩容磁盘操作详情请参见垂直变配

SELECT database, table, formatReadableSize(sum(bytes_on_disk)) AS disk_bytes FROM system.parts WHERE active and disk_name='default' GROUP BY database, table ORDER BY sum(bytes_on_disk);

特殊情况
若集群存储空间小(小于 500G)且表数量多(高于 2000 张),则可能存在表元信息占据过多磁盘空间的场景。此时,需要通过 XML 修改集群参数,找到 merge_tree 相关的部分,添加以下参数并重启集群,以释放多余的磁盘空间。修改集群参数操作详情请参见配置集群参数

<merge_tree>
    <manifest_store_keep_log_file_num>1</manifest_store_keep_log_file_num>
<merge_tree>

IO 水位

水位建议

  • 正常:低于 60%。
  • 观察:间歇性高于 80%。
  • 危险:持续 30 分钟高于 80% 且查询写入变慢。

排查思路

  • 可参照 CPU 部分的排查思路,优先通过 SQL 诊断功能定位 IO 使用高的 SQL,并进行相应的优化。使用 SQL 诊断功能时,请特别注意是否有 SQL 进行了预期外的数据扫描。SQL 诊断功能详情请参见SQL 诊断
  • 若查询使用不高,则确认集群是否开启了冷存,若已开启,且集群存储水位高于冷存策略规则,则会频繁触发数据从热存移动到冷存进而导致 IO 升高,建议调整 TTL或扩容磁盘, 确保存储水位低于冷存规则策略。TTL 用法详情请参见TTL,扩容磁盘操作详情请参见垂直变配
  • 购买额外带宽提升集群整体 IO 性能,购买额外带宽请参见增加额外带宽