什么是数据倾斜
-
-
- 前言
- 正文
- 跨境支付平台数据倾斜
-
-
- [1. 路由算法改造](#1. 路由算法改造)
- [2. 历史数据迁移方案](#2. 历史数据迁移方案)
- [3. 业务影响与收益](#3. 业务影响与收益)
-
- 热Key场景:618大促直播秒杀
-
前言
最近几年ai的冲击已经让我有点对于书面的东西,不在那么的想写了,我真切的感觉ai如果可以有人看着,基本很多事情都可以解决,很荣幸,今年2026年6月9日能写一点东西跟大家分享,其实这种东西已经从书籍,变成了ai的对话了,他们会以更精准的能力方便大家理解,但是个人心得往往却不能处理的那么拟人化。我决定还是写一点东西吧。
其实在写这个文章的时候,很多人基本没有接触过数据倾斜,只有大数据量化的东西才会出现,有时候面试官再问这个问题的时候,实际是考察有没有大型项目,或者沉逾项目的开发经验,想听听你对数据倾斜的看法。
我只是结合自己的实践来说明我对这方面的看法
正文
什么是数据倾斜呢,首先要知道数据倾斜是指在分布式计算或数据库环境中,数据分布不均匀的现象。单体数据库中,也就是有些表的数据特别大,有些数据节点特别小,在理想的分布式系统中,数据和计算负载应该均匀分布在所有节点上。然而,由于各种原因,某些节点可能承载比其他节点更多的数据或计算负载,这就是数据倾斜,
跨境支付平台数据倾斜
-
系统 :跨境支付平台,订单表按 10亿+ 数据量设计,按
merchant_id(商户ID)分128张表 -
故障现象:
- 夜间批处理任务频繁超时
- 某分表体积达 320GB (是平均值的 85倍)
- 该分表所在磁盘IO持续100%
根因排查
sql
-- 数据分布分析SQL
SELECT table_name,
FORMAT_BYTES(data_length) AS size,
table_rows
FROM information_schema.tables
WHERE table_schema = 'payment_db'
ORDER BY data_length DESC LIMIT 5;
结果发现:
| 分表名称 | 数据大小 | 行数 |
|---|---|---|
| payment_093 | 320 GB | 2.1亿 |
| payment_017 | 3.8 GB | 250万 |
| payment_102 | 4.1 GB | 270万 |
- 症结点:商户
M123456(某国际电商平台)占单表 98.7% 数据 - 业务逻辑:该商户每秒处理 3400+ 笔小额支付订单
解决方案落地(双重分片策略)
1. 路由算法改造
java
public String determineTableShard(String merchantId, String orderTime) {
// Step 1: 识别商户类型
MerchantType type = merchantService.getType(merchantId);
// Step 2: 普通商户直接取模
if (type == NORMAL) {
return "payment_" + (merchantId.hashCode() % 128);
}
// Step 3: 大商户附加时间分片(按小时)
if (type == LARGE_MERCHANT) {
String hourSlot = new SimpleDateFormat("HH").format(orderTime);
int baseShard = merchantId.hashCode() % 32; // 一级分片
return String.format("payment_large_%s_%02d", baseShard, Integer.parseInt(hourSlot));
}
}
路由效果:
原分表:payment_093 (320GB)
新分表:
payment_large_21_00 (13GB)
payment_large_21_01 (11GB)
...
payment_large_21_23 (14GB)
2. 历史数据迁移方案
bash
# 使用DataX分批迁移(避免锁表)
$ datax.py -j "-Xms8g -Xmx8g" job_migration.json
# 迁移作业核心配置
{
"content": [{
"reader": {
"name": "mysqlreader",
"parameter": { "querySql": "SELECT * FROM payment_093" }
},
"writer": {
"name": "mysqlwriter",
"parameter": {
"writeMode": "insert",
"preSql": ["TRUNCATE TABLE ${targetTable}"],
"postSql": ["ANALYZE TABLE ${targetTable}"]
}
}
}]
}
3. 业务影响与收益
| 指标 | 改造前 | 改造后 | 提升幅度 |
|---|---|---|---|
| 最大单表体积 | 320 GB | 14 GB | 95.6%↓ |
| 批量处理耗时 | 4.5小时 | 23分钟 | 80%↓ |
| 磁盘IO峰值 | 100% | 35% | 65%↓ |
热Key场景:618大促直播秒杀
灾难现场复盘
- 事件:某品牌手机限量1000台秒杀,售价1元
- 流量 :瞬间 120万QPS 请求商品缓存Key
product:998 - 结果 :
- Redis主节点CPU 100%
- 网络带宽打满(2Gbps→6Gbps突增)
- 连接池耗尽引发雪崩
多维解决方案组合拳
1. 热Key探测防御链
#mermaid-svg-HdZLklcFVmfUBiMo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HdZLklcFVmfUBiMo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HdZLklcFVmfUBiMo .error-icon{fill:#552222;}#mermaid-svg-HdZLklcFVmfUBiMo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HdZLklcFVmfUBiMo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HdZLklcFVmfUBiMo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HdZLklcFVmfUBiMo .marker.cross{stroke:#333333;}#mermaid-svg-HdZLklcFVmfUBiMo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HdZLklcFVmfUBiMo p{margin:0;}#mermaid-svg-HdZLklcFVmfUBiMo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HdZLklcFVmfUBiMo .cluster-label text{fill:#333;}#mermaid-svg-HdZLklcFVmfUBiMo .cluster-label span{color:#333;}#mermaid-svg-HdZLklcFVmfUBiMo .cluster-label span p{background-color:transparent;}#mermaid-svg-HdZLklcFVmfUBiMo .label text,#mermaid-svg-HdZLklcFVmfUBiMo span{fill:#333;color:#333;}#mermaid-svg-HdZLklcFVmfUBiMo .node rect,#mermaid-svg-HdZLklcFVmfUBiMo .node circle,#mermaid-svg-HdZLklcFVmfUBiMo .node ellipse,#mermaid-svg-HdZLklcFVmfUBiMo .node polygon,#mermaid-svg-HdZLklcFVmfUBiMo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HdZLklcFVmfUBiMo .rough-node .label text,#mermaid-svg-HdZLklcFVmfUBiMo .node .label text,#mermaid-svg-HdZLklcFVmfUBiMo .image-shape .label,#mermaid-svg-HdZLklcFVmfUBiMo .icon-shape .label{text-anchor:middle;}#mermaid-svg-HdZLklcFVmfUBiMo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HdZLklcFVmfUBiMo .rough-node .label,#mermaid-svg-HdZLklcFVmfUBiMo .node .label,#mermaid-svg-HdZLklcFVmfUBiMo .image-shape .label,#mermaid-svg-HdZLklcFVmfUBiMo .icon-shape .label{text-align:center;}#mermaid-svg-HdZLklcFVmfUBiMo .node.clickable{cursor:pointer;}#mermaid-svg-HdZLklcFVmfUBiMo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HdZLklcFVmfUBiMo .arrowheadPath{fill:#333333;}#mermaid-svg-HdZLklcFVmfUBiMo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HdZLklcFVmfUBiMo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HdZLklcFVmfUBiMo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HdZLklcFVmfUBiMo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HdZLklcFVmfUBiMo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HdZLklcFVmfUBiMo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HdZLklcFVmfUBiMo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HdZLklcFVmfUBiMo .cluster text{fill:#333;}#mermaid-svg-HdZLklcFVmfUBiMo .cluster span{color:#333;}#mermaid-svg-HdZLklcFVmfUBiMo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HdZLklcFVmfUBiMo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HdZLklcFVmfUBiMo rect.text{fill:none;stroke-width:0;}#mermaid-svg-HdZLklcFVmfUBiMo .icon-shape,#mermaid-svg-HdZLklcFVmfUBiMo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HdZLklcFVmfUBiMo .icon-shape p,#mermaid-svg-HdZLklcFVmfUBiMo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HdZLklcFVmfUBiMo .icon-shape .label rect,#mermaid-svg-HdZLklcFVmfUBiMo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HdZLklcFVmfUBiMo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HdZLklcFVmfUBiMo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HdZLklcFVmfUBiMo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上报访问计数
超过阈值
客户端SDK
Kafka集群
Flink实时计算
热Key管理中心
推送防御策略
边缘节点
服务节点
2. 热Key拆分子Key策略
java
// 客户端访问逻辑
public String getProductInfo(String productId, String userId) {
// 计算用户所在分片 (0-999)
int shardIndex = userId.hashCode() % 1000;
// 构建子Key
String shardKey = "product_v2:" + productId + "_" + String.format("%04d", shardIndex / 100);
return redisClient.get(shardKey);
}
存储层操作:
python
# 自动拆分热Key
def hotkey_rewrite(key):
if detect_hotkey(key):
for i in range(10): # 拆10个子Key
shard_key = f"{key}_shard_{i}"
redis.cluster.set(shard_key, get_original_data(key))
return True
return False
3. 三级缓存架构
#mermaid-svg-tYyTi548kn4zdCPX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tYyTi548kn4zdCPX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tYyTi548kn4zdCPX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tYyTi548kn4zdCPX .error-icon{fill:#552222;}#mermaid-svg-tYyTi548kn4zdCPX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tYyTi548kn4zdCPX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tYyTi548kn4zdCPX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tYyTi548kn4zdCPX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tYyTi548kn4zdCPX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tYyTi548kn4zdCPX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tYyTi548kn4zdCPX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tYyTi548kn4zdCPX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tYyTi548kn4zdCPX .marker.cross{stroke:#333333;}#mermaid-svg-tYyTi548kn4zdCPX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tYyTi548kn4zdCPX p{margin:0;}#mermaid-svg-tYyTi548kn4zdCPX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tYyTi548kn4zdCPX .cluster-label text{fill:#333;}#mermaid-svg-tYyTi548kn4zdCPX .cluster-label span{color:#333;}#mermaid-svg-tYyTi548kn4zdCPX .cluster-label span p{background-color:transparent;}#mermaid-svg-tYyTi548kn4zdCPX .label text,#mermaid-svg-tYyTi548kn4zdCPX span{fill:#333;color:#333;}#mermaid-svg-tYyTi548kn4zdCPX .node rect,#mermaid-svg-tYyTi548kn4zdCPX .node circle,#mermaid-svg-tYyTi548kn4zdCPX .node ellipse,#mermaid-svg-tYyTi548kn4zdCPX .node polygon,#mermaid-svg-tYyTi548kn4zdCPX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tYyTi548kn4zdCPX .rough-node .label text,#mermaid-svg-tYyTi548kn4zdCPX .node .label text,#mermaid-svg-tYyTi548kn4zdCPX .image-shape .label,#mermaid-svg-tYyTi548kn4zdCPX .icon-shape .label{text-anchor:middle;}#mermaid-svg-tYyTi548kn4zdCPX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tYyTi548kn4zdCPX .rough-node .label,#mermaid-svg-tYyTi548kn4zdCPX .node .label,#mermaid-svg-tYyTi548kn4zdCPX .image-shape .label,#mermaid-svg-tYyTi548kn4zdCPX .icon-shape .label{text-align:center;}#mermaid-svg-tYyTi548kn4zdCPX .node.clickable{cursor:pointer;}#mermaid-svg-tYyTi548kn4zdCPX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tYyTi548kn4zdCPX .arrowheadPath{fill:#333333;}#mermaid-svg-tYyTi548kn4zdCPX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tYyTi548kn4zdCPX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tYyTi548kn4zdCPX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tYyTi548kn4zdCPX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tYyTi548kn4zdCPX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tYyTi548kn4zdCPX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tYyTi548kn4zdCPX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tYyTi548kn4zdCPX .cluster text{fill:#333;}#mermaid-svg-tYyTi548kn4zdCPX .cluster span{color:#333;}#mermaid-svg-tYyTi548kn4zdCPX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tYyTi548kn4zdCPX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tYyTi548kn4zdCPX rect.text{fill:none;stroke-width:0;}#mermaid-svg-tYyTi548kn4zdCPX .icon-shape,#mermaid-svg-tYyTi548kn4zdCPX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tYyTi548kn4zdCPX .icon-shape p,#mermaid-svg-tYyTi548kn4zdCPX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tYyTi548kn4zdCPX .icon-shape .label rect,#mermaid-svg-tYyTi548kn4zdCPX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tYyTi548kn4zdCPX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tYyTi548kn4zdCPX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tYyTi548kn4zdCPX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 带参数请求
是
否
查询Redis集群
是
否
客户端
CDN边缘节点
本地缓存存在?
返回数据
Nginx+OpenResty
是否热Key?
返回本地Lua缓存
回源数据库
防御效果验证
-
压测数据 :
bash# 模拟120万QPS wrk -t 128 -c 5000 -d 300s --latency -s script.lua http://seckill.com # 优化后结果 Requests/sec: 1,203,456 99% Latency: 32.54ms -
各层承载分布 :
缓存层级 请求量(QPS) 流量占比 客户端缓存 683,201 56.8% CDN边缘节点 398,765 33.1% Redis集群 121,490 10.1%