什么是数据倾斜

什么是数据倾斜

前言

最近几年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%

相关推荐
李白的天不白1 小时前
一个服务器可以搭建多个网站
java·tomcat
●VON1 小时前
AtomGit Flutter鸿蒙客户端:共享组件
java·flutter·华为·harmonyos·鸿蒙
程序猿乐锅1 小时前
【JAVASE | 第十七篇】Java 网络通信
java·开发语言
执于代码2 小时前
Java交互打印的问题
java
我命由我123452 小时前
Windows 操作系统 - Windows 查看防火墙是否开启、Windows 查看防火墙放行端口
java·运维·开发语言·windows·java-ee·操作系统·运维开发
fly spider2 小时前
Spring 原理总览:从启动到请求执行
java·数据库·spring
大大杰哥2 小时前
SSeEmitter的基本使用和介绍
java·sse·通信
闪电悠米2 小时前
黑马点评-Redis 消息队列-02_list_pubsub_limits
java·数据库·ide·redis·缓存·list·intellij-idea
海梨花2 小时前
字节面试高频算法题
java·算法·面试·职场和发展