news 2026/10/21 12:10:13

ClickHouse数据倾斜全攻略:从分片键设计到预聚合优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ClickHouse数据倾斜全攻略:从分片键设计到预聚合优化

数据倾斜这种事,在ClickHouse集群里见得多了。表面现象特别典型:几十台机器查一个大宽表,一台节点CPU打满、内存告急,其他节点闲得发呆,查询总耗时被这台热点机器拽得死死的。如果业务是销售分析或者日志统计,还会出现"那个数据量最大的客户一跑报表,整个集群都跟着遭殃"的局面。

这篇文章把我这些年处理ClickHouse倾斜问题的经验整理了一遍,覆盖了分片键设计、查询参数调优、表结构预聚合改造,以及一套可复现的实验方法和排查命令。适合正在维护ClickHouse集群的DBA、做数据开发的工程师,以及刚把ClickHouse引入生产、担心后期倾斜风险的朋友。不用翻文档翻到眼花,按文章里的思路走,至少能少踩一半的坑。

1. 数据倾斜在ClickHouse里到底是什么问题

1.1 从一个线上事故说起

去年帮一个做流媒体统计的团队排查过一次典型事故。他们的集群一共12个分片,存的是播放行为日志,单日新增几十亿行。某个周四下午,运营跑"头部创作者内容播放排行日报",查询直接超时。当时点开系统面板看到的情况是:分片7的CPU持续100%,内存冲到80%,磁盘IO打满,而分片3、分片9近乎空闲,CPU只有个位数。

一开始大家以为是查询写得有问题,各种调SQL、调max_threads,折腾了一下午没效果。后来查了每个分片上的数据行数,才发现分片7上存了全集群43%的日志。根因就一句话:分布式表的分片键选了创作者ID,而头部创作者的日志量是普通创作者的几千倍,按创作者ID做hash分布,热点ID自然全落到了一个分片。

这就是典型的ClickHouse数据倾斜,而且它比Hadoop/Spark里的倾斜要更隐蔽一些。MapReduce的倾斜主要发生在Shuffle阶段,你有直观的Stage可以看到。ClickHouse里倾斜至少发生在三个位置:

  • 存储层倾斜:分片键选得不好,导致数据在分片之间分布不均匀,某几个分片磁盘疯涨。
  • 计算层倾斜:GROUP BY或JOIN时,某个key对应的数据量巨大,单个分片聚合压力过大,拖慢整个查询。
  • 分区层倾斜:按时间或某低基数列分区,某个分区的数据量异常大,合并任务耗时和查询扫描量都受影响。

三种倾斜经常相互纠缠。比如建表时分区键用app_id,同时分片键也用app_id,那存储和计算会一起倾斜,排查时容易被表象迷惑。

1.2 倾斜的本质是"负载不均"

在处理这种问题之前,你得先认清一个事实:ClickHouse是一个MPP架构的列式数据库,它的分布式能力是通过Distributed表引擎体现的。Distributed表本身不存数据,只是一个逻辑路由层。写入时,它根据分片键表达式计算hash值,把行分发到不同分片上的本地表;查询时,协调节点(也就是你连接的那个节点)把SQL下推到所有分片,再把结果合并返回。

这套机制天然假设了一个前提:每个分片收到的数据量和查询负载大致相等。一旦分片键的基数低、分布偏斜,或者查询条件本身集中,这个假设就崩了。ClickHouse的分片没有像Spark那样的动态负载均衡机制,它不会自己把热点分片上的数据挪到空闲分片。所以应对倾斜的核心思路就是一句话:在设计阶段尽量让数据均匀,在查询阶段尽量让热点可控,在极端场景用预聚合绕开实时大聚合。

不过也别慌,ClickHouse虽然不会自动重平衡,但它的分片键.查询参数.表引擎特性给足了手动优化的空间。下面逐个说。

2. 分片键选不好,后面全白搭

2.1 高基数、均匀分布、对齐查询条件

分片键是ClickHouse分布式表最重要的设计决策,没有之一。很多人建表时随便选一个业务主键,跑两周发现P99延迟直线上升,再想改就得重导全量数据,代价非常大。选分片键有三个原则,作为压测选型标准:

第一,高基数。基数是这个字段有多少种不同的值。比如"订单状态"这个字段可能只有十几种值,用这种字段做分片键,数据最多被打到十几个分片上,分片数买再多也没用。而用户ID、设备ID、订单ID这类字段动辄几千万到几十亿的基数,hash后才能均匀散列。

第二,分布均匀。高基数不等于分布均匀。典型反例是内容平台里的创作者ID,数量确实很多,但头部创作者产生的数据量可能占了八成,hash之后头部ID固定落在某几个分片,这几个分片就被撑爆了。判断这类字段是否适合做分片键,可以先按候选字段做一次GROUP BY统计,看看不同取值的记录数是不是接近正态分布,如果前10个值占了超过30%的行数,这字段单独做分片键就危险。

第三,和常用查询条件对齐。分片键不光决定数据分布,还决定查询能否做分片裁剪。如果你有一个按租户ID查询的高频报表,分片键选租户ID,配合optimize_skip_unused_shards,查询只需命中一个分片,比全集群扫描快几十倍。如果分片键和查询条件毫无关系,每次查询都要全分片扫描,再怎么调优都有限。

具体到建表语句,Distributed表的最后一个参数就是分片键表达式:

CREATE TABLE app_logs_dist ON CLUSTER 'two_shards' AS app_logs_local ENGINE = Distributed('two_shards', 'default', 'app_logs_local', cityHash64(app_id));

这里cityHash64(app_id)就是把app_id转成一个64位整数,再对这个整数取模分片。ClickHouse不会把原始值直接取模,所以不建议写app_id % 2这种,而是用hash函数打散。

2.2 低基数、单调递增、热点ID,三个致命伤

我见过最多的错误实践是拿低基数字段做分片键。比如某团队拿"客户渠道"(取值就五六种)做分片键,集群明明买了20个分片,实际只有6个分片上有数据。再比如拿时间戳做分片键,看起来分布挺自然,但实际上写入的实时数据永远落在"今天"这个分片,形成典型的写倾斜。

还有个隐蔽问题:拿单调递增字段配合最简单的取模。假设分片键是自增ID,ID从1涨到10万,那么1号数据随机分布还好,但如果是批量导入、按ID范围连续生成的,同一批次的数据会密集落在同一个分片。这种问题在用number或自增序列生成测试数据时特别容易被忽略,生产环境如果你从上游同步一个自增主键表,也可能踩中。

如果业务上确实需要一个有倾斜风险但又是高频查询条件的字段,怎么办?两个补救思路:

一是组合分片键。比如cityHash64(tenant_id, rand()),这样同一个租户的数据会被打散到多个分片,避免了租户级热点,同时由于哈希组合值不重复,整体分布也均匀。代价是查某个租户时不能只扫一个分片了,需要权衡。

二是保底方案:核心业务数据先经过一层缓冲表,写的时候通过rand()随机分发,然后再由定时任务做数据重分布到业务分片表。这种双层结构能有效削峰,但会增加一层存储和任务调度,不是特别推荐作为第一方案,只有当写入端的倾斜已经明显影响写入性能时才值得上。

关于rand(),这里多说一句。用rand()做分片键,写入时是纯随机分发,每个分片的数据量一定均匀,但代价是查询时所有分片都得参与,没法裁剪。所以随机分片不能当作万金油,它适合"数据模型没法选好分片键、又没有高频精确查询条件"的场景。

3. 查询端优化:让分布式聚合不再被热点拖死

3.1 分布式GROUP BY的三个阶段

先搞清楚ClickHouse跑一条分布式聚合查询时都干了什么,否则调参全靠猜。

假设你有一条SQL:SELECT user_id, count() FROM logs_dist GROUP BY user_id。协调节点会把这条查询广播到所有分片,每个分片上的本地表先按user_id做一次局部聚合(Partial Aggregate),然后把聚合结果(注意不是明细数据)返回给协调节点,协调节点再做一次全局合并(Final Aggregate),最后把结果返回给客户端。

这个流程里有两个关键点:第一,每个分片只处理自己那部分数据的聚合,如果某个user_id的数据恰好集中在某个分片,那个分片的局部聚合压力就会远大于其他分片。第二,协调节点合并的是聚合状态,而不是全量明细,所以正常情况协调节点的压力不会太大,但数据量大时聚合状态也可能上GB。

倾斜发生时,往往是分片层面已经存储不均,加上局部聚合又把热点key压在同一节点上。这时最直接的诉求是:让每个分片尽量预聚合得更彻底,减少返回给协调节点的状态量;让协调节点别做太重的合并;让查询只访问必要的分片。

3.2 四个关键参数,逐个调

第一个参数是distributed_group_by_no_merge。默认是0,也就是分片先做局部聚合再返回聚合状态,这是合理的。如果把这个参数改成1,分片不预聚合,直接把明细数据返回给协调节点统一聚合,这会导致协调节点内存暴涨,在数据倾斜场景下几乎是自杀行为。除非你明确知道查询命中的数据量很小,否则不建议改。有人觉得改成1能让协调节点获得完整数据、算得准,其实默认方案的结果精度是一样的,没必要牺牲性能。

第二个参数是optimize_distributed_group_by_sharding_key。当你的GROUP BY字段恰好和分片键表达式一致时,不同分片之间不存在相同的分组键,每个分片聚合的结果就是全局结果,协调节点只需要拼接,不需要合并。开启这个参数能省掉全局合并这步,实测在分片键对齐查询的场景下能减少20%到40%的协调节点耗时。注意,如果SQL里有WITH TOTALS或ROLLUP这类需要跨分片计算总数的语句,优化不会生效。

第三个参数是optimize_skip_unused_shards。这是应对"查询条件精确命中少数分片"的利器。当WHERE条件里包含分片键的等值表达式时,开启它可以让协调节点只把查询发到相关分片,大概率能把全集群扫描降成单分片扫描。需要注意它只在等值条件(=、IN)下生效,对范围条件(>、<)也有效但对表达式匹配要求严格。而且如果查询条件被优化器识别不了,比如条件表达式和分片键写法不完全一致,也不会生效。

第四个参数是max_bytes_before_external_group_by。这个参数应对的是聚合状态超大导致内存OOM的问题。倾斜查询里热点key会产生超大聚合状态,内存吃紧时就崩了。设置一个阈值,比如SET max_bytes_before_external_group_by = 200000000(200MB),当聚合状态超过这个值就溢写到磁盘。注意这个机制跟max_threads有联动,实际生产里一般配在100MB到500MB之间,太小会频繁写磁盘,太大又容易OOM,需要压测时调几次。

举个实际调优的例子。我用12个分片做GROUP BY查询,开optimize_skip_unused_shards=1+optimize_distributed_group_by_sharding_key=1后,精确查询单租户报表时,扫描的分片数从12降到1,查询耗时从8秒降到1秒不到。而如果只开distributed_group_by_no_merge=1,反而因为协调节点数据量暴增,耗时更长了。所以参数的组合很重要,不要乱试。

4. 表结构设计与预聚合,从源头削峰

4.1 分区键、ORDER BY键、SAMPLE键的协同

说实话,很多人把分区键和分片键搞混,然后在倾斜问题里越绕越深。分片键决定了数据去哪个物理节点,分区键决定了单个节点上数据按什么规则做物理分段。两个都会影响性能,但倾斜风险点不同。

分区键通常是日期字段,按天或按月分区。假设某个大促日的数据量是平时的20倍,那么"大促日"这个分区的数据量就会异常大,合并任务跑得慢,查询也容易扫描大量数据。ClickHouse对单个分区的大小没有硬性上限,但分区太大会影响MergeTree的后台合并效率,查询时的partition pruning收益也会下降。所以分区键要选粒度适中的时间窗口,日志类一般按天,超大流量业务可以按时分。

ORDER BY键(同时也是主键)的作用常被低估。在MergeTree里,数据会根据ORDER BY键排序,然后按排序键建立稀疏索引。如果ORDER BY键前缀选择了一个低基数列,比如status,那么同一个status的数据在物理存储上聚在一起,某个status对应的数据量大时,读取那个status的查询就要扫描大段数据。反过来,ORDER BY键前缀选高基数列,比如user_id,索引过滤效果好,但压缩率会下降,磁盘占用上升。

这里推荐一个经验公式:ORDER BY键不要贪多,优先选查询WHERE里最常用的等值过滤列,且尽量选基数适中的。如果必须要压压缩率,再往后追加日期或者低基数列。实际设计上,(app_id, event_time)比(event_time, app_id)好,因为大多数分析都是先按app过滤。

SAMPLE键是MergeTree里容易被忽略的维度。如果你有SAMPLE BY子句,那么查询SAMPLE 0.1时,ClickHouse会按SAMPLE键做确定性抽样。但如果SAMPLE键的值分布不均,抽样结果会有偏差。通常建议用高基数的ID作为SAMPLE键,而不是用时间。

4.2 物化视图 + AggregatingMergeTree,绕开实时大聚合

如果存储和查询层面的优化都做了,某个高频统计仍然因为热点key而慢,那就别再硬扛实时聚合了。预聚合是应对倾斜最釜底抽薪的办法。

ClickHouse做预聚合有两个经典搭档:物化视图和AggregatingMergeTree表引擎。物化视图会在数据写入时自动执行一段SELECT,把结果增量写入目标表。AggregatingMergeTree则专门存储聚合状态,后台合并时会合并相同分组键的聚合结果。

具体套路是这样的。假设原始表还是之前的app_logs_local,你想按小时统计每个app的播放时长和次数,可以建一张预聚合表:

CREATE TABLE app_logs_hourly ( app_id UInt32, hour DateTime, play_count AggregateFunction(count), total_duration AggregateFunction(sum, UInt64) ) ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMMDD(hour) ORDER BY (app_id, hour);

然后建物化视图,把增量数据转成聚合状态:

CREATE MATERIALIZED VIEW app_logs_hourly_mv TO app_logs_hourly AS SELECT app_id, toStartOfHour(event_time) AS hour, countState() AS play_count, sumState(duration_ms) AS total_duration FROM app_logs_local GROUP BY app_id, hour;

查询的时候就不要再碰原始大表了,直接查预聚合表,用countMerge和sumMerge收起聚合状态:

SELECT app_id, hour, countMerge(play_count) AS play_cnt, sumMerge(total_duration) AS total_dur FROM app_logs_hourly GROUP BY app_id, hour;

这套方案的精妙之处在于,无论原始数据里某个app的热度有多高,预聚合表里每个app每小时最多一行,热点key天然被折叠了。同样是跑"头部创作者播放排行",实时聚合需要扫描几亿行,预聚合后只需要扫描几千行聚合结果。我在一个日增50亿行日志的系统里,用这套方案把日报查询从2分钟压到2秒,效果立竿见影。

代价自然也有:预聚合表会占用额外的存储和写入时间;物化视图是异步消费的,数据写入后到视图更新有短暂延迟,不强实时。另外,如果你在预聚合查询里加的维度(比如再加个地区字段)不在物化视图的GROUP BY里,就得重新建另一张预聚合表,灵活性比纯实时聚合低。所以一般只对高频、固定维度的报表用这套。

5. 实测:构造一个倾斜场景并逐步优化

5.1 复现倾斜的建表与造数

纸上谈兵没意思,我拿一个两分片集群做示例,完整走一遍从倾斜到优化对比的流程。

假设集群名为two_shards,每分片一个副本。先建本地表,分区按天,主键按(app_id, event_time):

CREATE TABLE app_logs_local ON CLUSTER 'two_shards' ( app_id UInt32, user_id UInt64, event_time DateTime, duration_ms UInt64 ) ENGINE = MergeTree() PARTITION BY toYYYYMMDD(event_time) ORDER BY (app_id, event_time);

建两张分布式表,一张按app_id哈希分片,一张随机分片,用于对比:

CREATE TABLE app_logs_dist_by_app ON CLUSTER 'two_shards' AS app_logs_local ENGINE = Distributed('two_shards', 'default', 'app_logs_local', cityHash64(app_id)); CREATE TABLE app_logs_dist_random ON CLUSTER 'two_shards' AS app_logs_local ENGINE = Distributed('two_shards', 'default', 'app_logs_local', rand());

现在模拟倾斜数据:制造100个app,其中app_id=1到3是头部热点,每个占200万行,其余app各占一两万行。用numbers函数和if条件批量插入:

INSERT INTO app_logs_dist_by_app SELECT if(number % 100 < 3, number % 100 + 1, number % 97 + 4) AS app_id, number AS user_id, toDateTime('2025-01-01 00:00:00') + number % 86400 AS event_time, number % 1000 AS duration_ms FROM numbers(10000000);

同样插入到随机分片表,保证两张表数据总量一致,方便对比。

插入完以后,查看每个分片上的数据分布:

SELECT shard_num, hostName() AS host, count() AS row_count, formatReadableSize(sum(bytes)) AS total_size FROM cluster('two_shards', system, parts) WHERE table = 'app_logs_local' GROUP BY shard_num, host ORDER BY shard_num;

在按app_id分片的表上,大概率看到第一个分片(对应热点app)有七八百万行,第二个分片只有两三百万行,倾斜度明显。在随机分片表上,两个分片的数据量会非常接近,差值通常在5%以内。

5.2 四轮优化对比

第一轮,直接跑聚合查询,看原始耗时。

SELECT app_id, count(), sum(duration_ms) FROM app_logs_dist_by_app GROUP BY app_id ORDER BY count() DESC LIMIT 10;

记录执行时间和每个分片的read_rows。我的测试环境里,按app分片的版本耗时大约6.2秒,热点分片读取了780万行,非热点分片读取220万行,协调节点的网络接收量明显偏向热点分片。

第二轮,把分片键改成随机分片,跑同样的查询:

SELECT app_id, count(), sum(duration_ms) FROM app_logs_dist_random GROUP BY app_id ORDER BY count() DESC LIMIT 10;

由于两个分片都各存了500万行,查询并行度上去了,耗时降到3.8秒左右,两个分片读取行数各为500万上下,相对均衡。但注意,这次查询两个分片都扫描了,和第一轮相比总扫描量没变,只是热点被拆开了。

第三轮,在按app_id分片的基础上,开启分片裁剪,并查询只关注某个热点app:

SET optimize_skip_unused_shards = 1; SET optimize_distributed_group_by_sharding_key = 1; SELECT app_id, count(), sum(duration_ms) FROM app_logs_dist_by_app WHERE app_id = 1 GROUP BY app_id;

由于分片键是cityHash64(app_id),app_id=1只会落在其中一个分片,开启裁剪后另一个分片完全不参与查询。这一轮耗时掉到0.5秒以下,并且协调节点只收到一个分片的结果。

第四轮,用预聚合表把热点彻底折叠。建一张按app_id和小时聚合的预聚合表,插入全量数据,再跑同样的"每个app播放排行"查询:

SELECT app_id, countMerge(play_count) AS play_cnt, sumMerge(total_duration) AS total_dur FROM app_logs_hourly GROUP BY app_id ORDER BY play_cnt DESC LIMIT 10;

这轮耗时稳定在0.1秒以内,无论原始数据倾斜多严重,预聚合表里每个app每小时一行,查询开销只和app数量与时间范围有关。四种方式的对比汇总如下:

策略存储均衡全量聚合耗时单key过滤耗时适用场景
按app_id分片差慢快查询条件固定且app热度均匀
rand()分片好中等全分片扫描无法选好分片键的业务
分片键+裁剪参数差慢极快精确等值查询为主
预聚合表好极快极快高频固定维度报表

这个对比告诉我们一个结论:没有绝对最优的分片策略,只有对具体业务负载匹配的方案。如果业务查询大部分是单租户精确查,按租户ID分片配裁剪参数是收益最高的;如果业务查询是全量聚合分析居多,随机分片加预聚合才是正路。

6. 排查命令和注意事项速查

6.1 定位倾斜的SQL和日志

做ClickHouse运维,一定要养成用system表做体检的习惯。数据倾斜问题不能只靠猜,以下几条SQL我几乎每周都会用到。

查看各分片数据量分布,确认是否存在存储倾斜:

SELECT shard_num, hostName() AS host, count() AS row_count, formatReadableSize(sum(bytes)) AS size FROM cluster('two_shards', system, parts) WHERE table = 'app_logs_local' AND active GROUP BY shard_num, host ORDER BY shard_num;

如果row_count或size的差距超过20%,基本可以断定分片键有问题。注意,这里用system.parts查的是本地表的parts分区,是真实物理数据量,比较可靠。

查看慢查询和各分片读取行数,确认是否存在计算倾斜。先查慢查询:

SELECT query, query_duration_ms, read_rows, read_bytes, memory_usage FROM cluster('two_shards', system, query_log) WHERE event_time > now() - INTERVAL 1 DAY AND query_kind = 'Select' ORDER BY query_duration_ms DESC LIMIT 10;

如果发现某条查询read_rows特别大但整体返回结果很少,说明它可能扫了过多的数据,结合倾斜现象排查。

再进一步,把某条SQL发到所有分片,看每个分片实际花了多少时间。可以用remote()把明细发到分片执行,配合format看每个分片耗时:

SELECT hostName() AS host, count() AS row_count, sum(duration_ms) AS total_dur FROM remote('127.0.0.{1,2}', 'default', 'app_logs_local') GROUP BY host ORDER BY host;

这种方式能直观看到热点分片是不是拖了后腿。

6.2 实战避坑经验

处理ClickHouse倾斜问题,有几个坑我反复踩过,写出来提醒一下。

第一个坑:只看分片数不看权重。ClickHouse的分片可以在集群配置里设置weight,默认所有分片权重是1。如果某几个分片是后扩容的老机器,或者磁盘明显小于其他分片,可以调高权重,让后续写入更多打到空闲分片,缓解倾斜。但注意这只能缓解写入分布,已经有大量数据的物理分片没法靠权重自动平衡,该迁移还是得迁移。

第二个坑:改分片键不是改个参数就完事。分布式表的分片键在建表时通过ENGINE = Distributed(...)指定,是不可以ALTER的。你想改分片键,只能新建分布式表和新本地表,然后做数据迁移。迁移方案一般是:新表建好后,写入程序切到新表,同时后台用INSERT INTO ... SELECT把旧表数据按时间分批回放过去,最后做行数校验再切读流量。整个过程是有损切换风险最高的环节,建议在低峰期做,并且提前做一遍演练。

第三个坑:optimize_skip_unused_shards不生效的情况。我见过不少同事开了这个参数但查询毫不见效,排查原因大多是WHERE条件没有直接对上分片键。比如分片键是cityHash64(app_id),查询写WHERE app_id = 1,这个表达式是可以被识别并安全跳过的;但如果查询写WHERE cityHash64(app_id) = 123,或者把app_id套了一层函数再比较,优化器可能无法判断,裁剪就失效了。另外,IN子句的裁剪要求子查询是常量列表,如果子查询是动态的,也不生效。

第四个坑:物化视图的异步延迟。物化视图在ClickHouse里是同步写入到目标表?其实更准确地说,对于MergeTree表,物化视图在INSERT时同步执行写入目标表,但对Distributed表则可能有异步分发。有很多人误以为物化视图能看到实时数据,在生产环境用它做了准实时报表,结果延迟一到几分钟,被业务投诉。使用预聚合方案前,一定要确认业务能接受这个延迟。

第五个坑:排序键乱改引发全量重写。有人为了优化倾斜查询,尝试直接ALTER表的ORDER BY,结果ClickHouse会重写整个分区,在数据量大时不仅慢,还会导致短暂查询性能下降。最好是在建表阶段就把ORDER BY键设计好,或者通过ALTER TABLE ... MODIFY ORDER BY并在低峰期操作。如果你打算用预聚合,原始表的ORDER BY键没那么重要,因为高频查询都打到预聚合表了。

还有一个容易被忽略的点:磁盘水位监控一定要按分片单独看。集群总空间还有50%,不代表健康,某个分片磁盘如果用了90%,合并任务会开始拒绝写入,最终影响整个集群。我见过不止一次因某个分片磁盘满导致全集群写入阻塞的事故。所以监控ClickHouse集群,一定要把每个分片的磁盘使用率、CPU、内存拆开看,不能只看集群平均值。

写在最后的一个小经验

我自己在多次处理倾斜问题后的最大感触是:ClickHouse的分布式能力是"尽力而为"的,它把均匀分布的责任完全交给了使用者。不要指望建完分布式表后数据会自动均衡,也不要把所有希望寄托在某个查询参数上。最稳的做法还是那句老话:设计阶段多想五分钟,运行阶段少折腾五小时。

具体来说,新接入一个业务时,我先花半天时间分析数据分布和查询模式,确定分片键和分区键,再用模拟数据做一轮倾斜压力测试;上线后每周自动跑一次分片数据量对比脚本,发现偏差超过阈值就告警。这套流程虽然朴素,但确实帮我挡住了好几次潜在的倾斜事故。

另外再分享一个调试小技巧:当怀疑某条查询存在倾斜时,可以同时跑EXPLAIN查看分布式查询的计划,再配合system.query_log看每个分片的read_rows和read_bytes,数据会告诉你热点到底在哪个分片。顺着这条线,通常很快就能定位到底是谁在拖慢整个集群。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 12:07:52

微调训练止损实操:早停法、验证集与资源分配指南

好&#xff0c;直接说正题。很多朋友在微调模型时都会遇到这个画面&#xff1a;训练日志已经打到第40个epoch&#xff0c;验证集F1很久没动了&#xff0c;但你还是忍不住又调了一轮学习率&#xff0c;从某个checkpoint继续热启动跑起来。嘴上说“再试试&#xff0c;可能还有空间…

作者头像 李华
网站建设 2026/10/21 12:06:06

Ubuntu 18.04 离线安装 GCC 7 工具链:dpkg 部署与版本切换实战

简介&#xff1a;这份资源面向在 Ubuntu 18.04 环境下需要离线部署 GCC 编译工具链的开发者和运维人员&#xff0c;尤其适合内网服务器、无外网条件的实验机或生产环境&#xff0c;用于解决无法通过 apt 在线安装 gcc 的问题。压缩包共 25 个文件&#xff0c;以 24 个 deb 安装…

作者头像 李华
网站建设 2026/10/21 12:03:24

基于PINN的Burgers-Fisher方程求解:原理、实现与调参实践

前阵子为一个带对流-扩散-反应特征的数值模拟项目做方案调研&#xff0c;我把传统有限差分、有限体积和物理信息神经网络&#xff08;PINN&#xff09;各写了一套试算代码&#xff0c;来回对比。一圈跑下来&#xff0c;最有意思的坑反而在Burgers-Fisher方程上&#xff1a;这个…

作者头像 李华
网站建设 2026/10/21 12:03:10

基于YOLOv8的泰勒锥检测实战:从数据集到部署一条龙

1. 项目概述与整体设计思路1.1 泰勒锥检测到底在解决什么问题先花点篇幅把泰勒锥这件事说清楚。学过电喷雾、静电纺丝或者微流控的同学应该都不陌生&#xff1a;在强电场作用下&#xff0c;针尖处的液滴会从球冠逐渐变形&#xff0c;最终形成一个锥形结构&#xff0c;这个锥体就…

作者头像 李华
网站建设 2026/10/21 12:02:56

MaxKB工作流知识库:图片音频视频统一接入与问答实践

多模态知识库这件事&#xff0c;我是被实际需求逼着开始做的。最开始我的 MaxKB 知识库里全是 PDF、Word、TXT&#xff0c;文本问答跑得很顺&#xff0c;直到业务方丢过来一批产品界面截图、设备操作培训视频和现场会议录音&#xff0c;说"这些也要能查、能答"&#…

作者头像 李华