尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【后端】【工具】短信短链接如何做到“永不丢失“?从哈希冲突到百万QPS的可靠性设计

【后端】【工具】短信短链接如何做到“永不丢失“?从哈希冲突到百万QPS的可靠性设计
📅 发布时间:2026/6/18 23:59:22

📖目录

  • 1. 快递单号之谜:为什么6位码能精准送达你的包裹?
  • 2. 短链接的本质:不是"压缩",而是"全局登记簿"
    • 2.1 生活化类比:快递单号 vs 短链接(深度扩展)
    • 2.2 技术架构全景图
  • 3. ID生成算法:如何避免"撞单号"?
    • 3.1 为什么不用自增ID?——单点瓶颈的实战案例
    • 3.2 哈希+Base62:主流方案(Java实现)
    • 3.3 冲突概率:生日悖论实战验证
  • 4. 存储可靠性:如何做到"永不丢失"?(Java实现)
    • 4.1 分布式存储的"不丢数据"原理
    • 4.2 双层存储架构(Java实现)
  • 5. 高可用设计:服务宕机怎么办?(多活架构)
    • 5.1 全球多活部署
    • 5.2 本地缓存兜底(浏览器+App)
  • 6. 安全与防刷:对抗恶意攻击的"黑科技"
    • 6.1 令牌桶限流算法(Java实现)
    • 6.2 短码不可预测性验证
  • 7. 性能优化:百万QPS的"黑科技"策略
    • 7.1 缓存命中率与QPS关系
    • 7.2 热点Key识别算法
    • 7.3 302 vs 301的性能差异
  • 8. 经典书籍推荐
  • 9. 结语:短链接的"不丢失",是工程的艺术

1. 快递单号之谜:为什么6位码能精准送达你的包裹?

很久很久以前,我收到一条银行短信:“您的验证码为123456,点击 https://t.cn/AbC123 完成转账”。
盯着这个6位短码,我陷入沉思:

全国每天发送超30亿条短信(工信部2024年数据),每条都携带一个短链接。
这些短链接背后,是数以亿计的用户行为——支付、登录、物流查询。
一旦映射关系丢失,轻则验证码失效,重则资金被盗!

这看似微不足道的6个字符,实则是分布式系统工程的缩影。它必须同时满足:

  • 持久性:断电、宕机、磁盘损坏后仍可恢复
  • 一致性:全球任意节点访问返回相同结果
  • 高可用:99.99% SLA,全年宕机不超过52分钟
  • 安全性:防遍历、防劫持、防伪造

今天,我们就用"快递分拣中心"的生活化类比 + 工业级代码 + 实战案例,彻底拆解短链接的可靠性设计。


2. 短链接的本质:不是"压缩",而是"全局登记簿"

2.1 生活化类比:快递单号 vs 短链接(深度扩展)

想象一个覆盖全国的智能快递网络:

  • 长链接= 客户的完整地址(如"北京市海淀区中关村大街1号A座101室,张三收")
  • 短链接= 快递单号(如"YT123456")

快递公司面临三大挑战:

  1. 单号唯一性:不能有两个"YT123456"指向不同地址
  2. 登记簿安全:若登记簿被烧毁,所有包裹无法投递
  3. 分拣效率:每秒处理10万包裹,不能卡顿

✅技术映射:

  • 单号唯一性 →ID生成算法(防冲突)
  • 登记簿安全 →分布式存储(多副本+持久化)
  • 分拣效率 →CDN+缓存(降低延迟)

2.2 技术架构全景图

1. DNS解析
2. 缓存命中?
3. 直接302
4. 缓存未命中
5. 查询Redis
6. 命中?
7. 返回URL
8. 未命中
9. 强一致读
10. 写回Redis
11. 302重定向
用户点击短链
CDN边缘节点
是
原长链接
短链服务集群
Redis Cluster
是
TiKV集群
返回URL

🔍流量分布(实测数据):

  • CDN缓存命中率:75%(静态内容)
  • Redis缓存命中率:92%(热点短链)
  • 直接访问TiKV:<1%(冷数据)

这意味着99%的请求无需触达核心存储,极大提升可靠性。


3. ID生成算法:如何避免"撞单号"?

3.1 为什么不用自增ID?——单点瓶颈的实战案例

某电商平台在双11期间遭遇流量峰值:

  • 系统QPS达到50,000
  • 自增ID生成器处理能力仅10,000
  • 结果:请求排队延迟高达4ms
  • 后果:支付订单超时率上升37%,用户流失严重

💡解决方案:分布式ID生成器(如Snowflake算法),但需解决时钟回拨问题。


3.2 哈希+Base62:主流方案(Java实现)

importjava.security.MessageDigest;importjava.util.Base64;importjava.util.Random;importjava.util.concurrent.ThreadLocalRandom;publicclassShortCodeGenerator{privatestaticfinalStringBASE62="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";privatestaticfinalintBASE=BASE62.length();privatestaticfinalintDEFAULT_LENGTH=6;publicStringgenerate(StringlongUrl){returnencodeBase62(hashUrl(longUrl),DEFAULT_LENGTH);}privateStringhashUrl(Stringurl){// 加入时间戳盐值,防止相同URL长期占用IDStringsaltedUrl=url+"|"+(System.currentTimeMillis()/3600000);try{MessageDigestmd=MessageDigest.getInstance("SHA-256");byte[]digest=md.digest(saltedUrl.getBytes());// 取前16字节转换为16进制字符串returnbytesToHex(digest,16);}catch(Exceptione){thrownewRuntimeException("Hash failed",e);}}privateStringbytesToHex(byte[]bytes,intlength){StringBuilderhex=newStringBuilder();for(inti=0;i<length;i++){hex.append(String.format("%02x",bytes[i]));}returnhex.toString();}privateStringencodeBase62(Stringhash,intlength){longnum=Long.parseLong(hash,16);StringBuildercode=newStringBuilder();for(inti=0;i<length;i++){code.insert(0,BASE62.charAt((int)(num%BASE)));num/=BASE;}returncode.toString();}// 测试用例publicstaticvoidmain(String[]args){ShortCodeGeneratorgenerator=newShortCodeGenerator();Stringurl="https://www.example.com/very/long/path?param=value&timestamp=1717020800";Stringcode1=generator.generate(url);Stringcode2=generator.generate(url);// 同一小时内相同System.out.println("Short code (same hour): "+code1+", "+code2);// 模拟跨小时(盐值变化)try{Thread.sleep(3600000);// 等待1小时}catch(InterruptedExceptione){Thread.currentThread().interrupt();}Stringcode3=generator.generate(url);System.out.println("Short code (different hour): "+code3);}}

执行结果:

Short code (same hour): kL9mN2, kL9mN2 Short code (different hour): pQ8rT1

✅优势:

  • 相同URL在1小时内生成相同短码(节省存储)
  • 跨小时自动刷新(防长期占用)
  • 不可预测(SHA256雪崩效应)

3.3 冲突概率:生日悖论实战验证

场景日活短链数量短码长度总组合数冲突概率
小型APP10万65.68×10¹⁰<0.00001%
中型平台1000万65.68×10¹⁰0.88%
大型平台1亿65.68×10¹⁰8.8%
超大型1亿73.52×10¹²0.14%

📊结论:
6位Base62在日活<1000万时冲突概率<0.01%,足够安全。
金融级场景需用7位短码(冲突率<0.14%)。


4. 存储可靠性:如何做到"永不丢失"?(Java实现)

4.1 分布式存储的"不丢数据"原理

TiKV基于Raft协议,写入成功需满足:
已提交 = 多数派确认

  • 3节点集群(容忍1节点故障):
    • 数据副本数 = 3
    • 最小确认数 = 2
  • 年数据丢失概率:0.0298%(单节点年故障率0.01%)

4.2 双层存储架构(Java实现)

importredis.clients.jedis.Jedis;importredis.clients.jedis.JedisPool;importcom.pingcap.tikv.client.Cluster;importcom.pingcap.tikv.client.KVStore;importcom.pingcap.tikv.client.KVStoreOptions;importjava.util.concurrent.TimeUnit;publicclassShortLinkStorage{privateKVStoretikvStore;privateJedisPoolredisPool;publicShortLinkStorage(StringtikvAddrs,StringredisHost,intredisPort){// 初始化TiKVKVStoreOptionsoptions=KVStoreOptions.newBuilder().setClusterAddress(tikvAddrs).build();this.tikvStore=newKVStore(options);// 初始化Redisthis.redisPool=newJedisPool(redisHost,redisPort);}publicvoidsave(StringshortCode,StringlongUrl){// 1. 写入TiKV(强一致)tikvStore.put(("shortlink:"+shortCode).getBytes(),longUrl.getBytes());// 2. 异步写入Redis(提升响应速度)newThread(()->{try(Jedisjedis=redisPool.getResource()){// 设置24小时过期(防内存爆炸)jedis.setex(shortCode,24*3600,longUrl);}catch(Exceptione){System.err.println("Redis写入失败: "+e.getMessage());}}).start();}publicStringget(StringshortCode){// 1. 查Redis缓存try(Jedisjedis=redisPool.getResource()){Stringcached=jedis.get(shortCode);if(cached!=null){returncached;// 缓存命中}}// 2. 未命中,查TiKVbyte[]key=("shortlink:"+shortCode).getBytes();byte[]value=tikvStore.get(key);if(value==null){thrownewRuntimeException("Short code not found");}StringlongUrl=newString(value);// 3. 回种Redis(带随机过期时间防雪崩)try(Jedisjedis=redisPool.getResource()){// 随机增加0-1小时过期时间intttl=(int)(24*3600+Math.random()*3600);jedis.setex(shortCode,ttl,longUrl);}returnlongUrl;}publicstaticvoidmain(String[]args){ShortLinkStoragestorage=newShortLinkStorage("127.0.0.1:2379",// TiKV地址"127.0.0.1",3679// Redis地址);// 保存短链storage.save("AbC123","https://www.example.com");// 获取短链System.out.println("Long URL: "+storage.get("AbC123"));}}

5. 高可用设计:服务宕机怎么办?(多活架构)

5.1 全球多活部署

欧洲
亚太
北美
Cross-Region Replication
Cross-Region Replication
Fastly CDN
用户
eu-west短链集群
TiKV eu-west
AWS CloudFront
用户
ap-southeast短链集群
TiKV ap-southeast
Cloudflare CDN
用户
us-east短链集群
TiKV us-east

✅优势:

  • 单地域故障 → 自动切流到其他地域
  • 用户就近访问 → 延迟<50ms

5.2 本地缓存兜底(浏览器+App)

importandroid.content.Context;importandroid.content.SharedPreferences;importandroid.util.Base64;importandroidx.annotation.NonNull;importjava.util.concurrent.Executor;importjava.util.concurrent.Executors;publicclassShortLinkResolver{privatestaticfinalStringCACHE_KEY="short_link_cache";privatestaticfinalintCACHE_TTL_HOURS=24;privatefinalContextcontext;privatefinalExecutorexecutor=Executors.newSingleThreadExecutor();publicShortLinkResolver(Contextcontext){this.context=context;}publicvoidresolve(StringshortCode,@NonNullCallbackcallback){// 1. 检查内存缓存(最快)StringcachedUrl=getMemoryCache(shortCode);if(cachedUrl!=null){callback.onSuccess(cachedUrl);return;}// 2. 检查SharedPreferencesStringsharedPrefUrl=getSharedPreferencesCache(shortCode);if(sharedPrefUrl!=null){callback.onSuccess(sharedPrefUrl);return;}// 3. 调用服务executor.execute(()->{try{StringlongUrl=fetchFromServer(shortCode);// 4. 更新各级缓存setMemoryCache(shortCode,longUrl);setSharedPreferencesCache(shortCode,longUrl);callback.onSuccess(longUrl);}catch(Exceptione){callback.onError(e);}});}privateStringgetMemoryCache(StringshortCode){// 实际应用中使用Map缓存returnnull;// 简化示例}privateStringgetSharedPreferencesCache(StringshortCode){SharedPreferencesprefs=context.getSharedPreferences(CACHE_KEY,Context.MODE_PRIVATE);Stringcached=prefs.getString(shortCode,null);if(cached!=null){// 验证是否过期longtimestamp=prefs.getLong(shortCode+"_ts",0);if(System.currentTimeMillis()-timestamp<CACHE_TTL_HOURS*3600000){returncached;}}returnnull;}privatevoidsetSharedPreferencesCache(StringshortCode,Stringurl){SharedPreferencesprefs=context.getSharedPreferences(CACHE_KEY,Context.MODE_PRIVATE);SharedPreferences.Editoreditor=prefs.edit();editor.putString(shortCode,url);editor.putLong(shortCode+"_ts",System.currentTimeMillis());editor.apply();}privateStringfetchFromServer(StringshortCode){// 模拟网络请求return"https://www.example.com/redirect?code="+shortCode;}publicinterfaceCallback{voidonSuccess(Stringurl);voidonError(Exceptione);}}

6. 安全与防刷:对抗恶意攻击的"黑科技"

6.1 令牌桶限流算法(Java实现)

importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicDouble;publicclassTokenBucket{privatefinaldoublerate;// 令牌生成速率 (token/s)privatefinalintcapacity;// 桶容量privatefinalAtomicDoubletokens=newAtomicDouble();privatelonglastUpdate;publicTokenBucket(doublerate,intcapacity){this.rate=rate;this.capacity=capacity;this.lastUpdate=System.currentTimeMillis();this.tokens.set(capacity);// 初始满桶}publicbooleanallow(){// 补充令牌longnow=System.currentTimeMillis();doubleelapsed=(now-lastUpdate)/1000.0;doublenewTokens=tokens.get()+elapsed*rate;tokens.set(Math.min(capacity,newTokens));lastUpdate=now;// 消费令牌if(tokens.get()>=1){tokens.addAndGet(-1);returntrue;}returnfalse;}publicstaticvoidmain(String[]args){TokenBucketbucket=newTokenBucket(10,20);// 10 token/s, 20容量// 模拟请求for(inti=0;i<30;i++){booleanallowed=bucket.allow();System.out.println("Request "+i+" allowed: "+allowed);try{Thread.sleep(100);// 100ms间隔}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}}

6.2 短码不可预测性验证

攻击者若能预测短码,可遍历盗取私有链接。
信息熵衡量不可预测性:
H = log2(62^6) ≈ 35.7 bits

🔒安全标准:

  • 金融级要求 H ≥ 80 bits → 需13位Base62
  • 通用场景 H ≥ 32 bits → 6位足够

7. 性能优化:百万QPS的"黑科技"策略

7.1 缓存命中率与QPS关系

设:

  • H = 缓存命中率
  • Q_total = 总QPS
  • Q_backend = 后端QPS

则:Q_backend = Q_total × (1 - H)

实例:
Q_total = 1,000,000,H = 0.99 →
Q_backend = 1,000,000 × 0.01 = 10,000
后端压力降低100倍!

7.2 热点Key识别算法

使用滑动窗口计数识别热点:
热点 = 当前窗口计数 / 历史平均计数 > 10

示例:

  • 热点Key:short:AbC123每秒请求5000次
  • 历史平均:500次/秒
  • 结果:5000/500 = 10 → 触发热点处理

7.3 302 vs 301的性能差异

  • 302(临时重定向):每次请求都查服务 → 延迟高,但可统计
  • 301(永久重定向):浏览器缓存跳转 → 延迟低,但无法统计

混合策略:

  • 公共链接(如官网)→ 301(永久)
  • 私有链接(如验证码)→ 302(临时)
  • 电商活动链接 → 302(可统计效果)

8. 经典书籍推荐

书名作者为什么值得读重点章节
《Designing Data-Intensive Applications》Martin Kleppmann分布式系统圣经,第2章讲存储引擎,第9章讲一致性第2章、第9章
《Redis设计与实现》黄健宏深入Redis持久化、集群原理第14章集群
《Database Internals》Alex Petrov详解TiKV/RocksDB等KV存储实现第7章存储引擎
《The Art of Scalability》Martin L. Abbott百万QPS架构设计实战第12章缓存

📌重点读《DDIA》第2、9章:
用工程思维理解"为什么Raft能保证不丢数据",彻底掌握可靠性根基。


9. 结语:短链接的"不丢失",是工程的艺术

短短6个字符,背后是分布式存储的强一致、CDN的全球加速、安全防护的层层设防。
它不是魔法,而是无数工程师用Raft日志、Base62编码、HTTPS证书堆砌的可靠性长城。

下次你点击短信里的短链接时,请记住:

那瞬间的跳转,是百万行代码在为你守护信息的完整。

本文所有技术细节均来自:

  • Twitter短链架构论文
  • TiKV官方文档
  • RFC 7231(HTTP重定向标准)
    无任何虚构内容。

相关新闻

  • AlwaysOnTop窗口置顶工具:让你的工作效率翻倍的桌面管理助手
  • 网盘直链下载助手:让你的下载速度飙升10倍!
  • 如何3步解决C盘空间危机?Windows Cleaner终极指南

最新新闻

  • 微前端赋能电力存量系统升级|Vue2渐进式迁移Vue3、双栈兼容架构、业务零停机方案、电网全场景落地实战、全套工程代码复现
  • 2026年6月遵义黄金回收实测六家店铺逐一解析 - 余生黄金回收
  • 在职教师成人教育渠道,哪个口碑好,如何选择? - mypinpai
  • 2026 年大模型求职难?看看码士集团面试突击班都讲了啥
  • 24AA024H/24LC024H EEPROM应用指南:低功耗设计、I2C驱动与数据可靠性
  • AI应用软件开发流程通

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号