引言

当你刷抖音看到“https://v.douyin.com/xyz123”时,当你点开微博的“t.cn/abcd56”时——这些仅有6-8个字符的链接,就是现代互联网的“空间折叠术”:**短链**。

短链是什么?
  • 定义:通过算法将原始长链接(如商品页URL)压缩成极短字符串,实现“轻量化跳转”。

1
原链接 → https://www.某电商.com/product/123456789?source=weibo  短链   → t.cn/AbcD12  
  • 核心价值

    • 用户体验:避免长链接在社交平台“刷屏污染”
    • 流量追踪:统计点击量、用户来源等关键数据
    • 安全控制:隐藏真实URL参数,防止恶意篡改
为什么需要千亿级短链系统?

以抖音为例:

  • 日均生成量:10亿+条(电商链接、视频分享、广告追踪)
  • 峰值QPS:每秒超50万次生成请求(顶流明星发帖时)
  • 存储规模:千亿级数据需保存3-6个月

一、*场景痛点:为什么你的短链系统会崩?*

1. 经典错误方案复现

1
// 错误案例:直接Hash生成短码(面试中这样答直接挂!)  public String createShortUrl(String longUrl) {      String md5 = DigestUtils.md5Hex(longUrl);      return md5.substring(0, 6); // 6位短码,冲突率高达22%  }  

崩溃三宗罪

  1. 哈希冲突:6位短码仅能存储 568亿种组合,千亿级数据必然冲突
  2. 热Key问题:热门长链生成请求集中攻击单Redis节点
  3. 存储瓶颈:单表数据过亿后,MySQL查询延迟飙升

二、*工业级方案*

1. 分布式ID生成:短码不重复的核心

方案对比

方案 生成速度 冲突概率 适用场景
Snowflake 10万/秒 0 分布式环境
Redis原子INCR 5万/秒 0 小规模系统
UUID 1万/秒 0 无序列要求场景

优化版Snowflake代码

1
2
public class ShortCodeGenerator {      // 时间戳(41位) + 数据中心ID(5位) + 机器ID(5位) + 序列号(13位)      private static final int TOTAL_BITS = 64;      private static final int TIMESTAMP_BITS = 41;      private static final int SEQUENCE_BITS = 13;  
public String generate() { long timestamp = System.currentTimeMillis(); long sequence = (timestamp << (TOTAL_BITS - TIMESTAMP_BITS)) | (dataCenterId << (SEQUENCE_BITS + 5)) | (machineId << SEQUENCE_BITS) | sequenceCounter.getAndIncrement(); return Base62.encode(sequence); // Base62压缩为8位短码 } }

2. 分库分表:千亿级数据存储方案

分片策略

  • 一级分片:按短码首字符分库(Base62共62种字符 → 62个库)
  • 二级分片:按短码CRC32哈希值分表(单库分1024表)

图片

拓展知识:为什么在短链系统设计中选用 Base62 而非 Base64?

维度 Base62 Base64
字符集 0-9 + A-Z + a-z(62个) 0-9 + A-Z + a-z + + /(64个)
URL安全 ✅ 无特殊字符,直接使用 ❌ 需转义 +/
分片管理 ✅ 首字符直接映射62分片 ❌ 需处理特殊字符
适用场景 短链、API Key、邀请码等 二进制数据编码(如图片传输)

3. 缓存设计:抗住百万QPS的秘诀

三级缓存体系

  1. 本地缓存:Caffeine存储热点短链(TTL=1分钟,命中率30%)
  2. Redis集群:CRC16分片 + 数据压缩(节省40%内存)
  3. 布隆过滤器:拦截99.9%非法短码请求(误判率0.1%)

Redis Lua脚本(原子写入)

1
-- KEYS[1]=短码, ARGV[1]=长链接  if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then      redis.call('EXPIRE', KEYS[1], 604800) -- 7天过期      return 1  else      return 0  end  

三、*高可用设计:如何做到全年无宕机?*

1. 多机房双活架构

图片

  • 流量调度:基于RTT延迟自动选择最优机房
  • 数据同步:使用Binlog + Kafka实现跨机房同步

2. 熔断降级策略

  • 规则

    • Redis CPU >70% → 关闭非核心功能(如访问统计)
    • MySQL响应时间 >500ms → 返回静态缓存页
  • Sentinel配置示例

1
@SentinelResource(value = "shortUrlService",                    blockHandler = "handleBlock",                    fallback = "handleFallback")  public String getLongUrl(String shortCode) {      // 核心业务逻辑  }  

四、*实战避坑指南*

  1. 短码安全问题

    • 禁止连续字符:通过正则过滤 aaaaaa 类短码
    • 时效控制:设置7天过期时间,清理僵尸数据
  2. 数据迁移方案

    • 双写机制:新旧系统并行运行3天
    • 校验工具:对比新旧库差异率,确保<0.001%

** **

技术总结

  • 分治思想:千亿级问题拆解为ID生成、分片存储、缓存优化
  • 冗余设计:多机房、多副本保障高可用
  • 成本控制:冷热分离 + 数据压缩降低存储费用