当前位置: 首页 > news >正文

设计一款高效的短链服务系统

目录

1、短链系统(Short URL System)

1.1、介绍

1.2、设计背景

1.3、缺陷

1.4、使用场景

2、系统设计

2.1、短码如何生成?且保证全局唯一且不重复?

2.2、如何存储?数据库设计

2.3、高并发下如何扛住流量

2.4、短码会不会被穷举?如何防刷?

2.5、如何统计

2.6、短链过期怎么实现

2.7、 CDN/网关怎么配合

3、工程实践

3.1、系统目标

3.2、数据库设计

3.3、技术选型

3.4、短码生成策略

3.5、核心代码实现

4、性能与安全优化建议


前言

        在互联网信息爆炸的时代,URL 作为资源定位的核心载体,常常因携带大量参数而变得冗长复杂。这不仅影响用户体验——尤其在移动端、社交媒体和二维码场景下难以传播与识别,也给营销追踪、安全管控和数据分析带来挑战。

如下所示:

        短链系统(Short URL System)应运而生。它通过将原始长链接映射为简短、易记、美观的短码(如https://s.example.com/Abc123),在不改变业务逻辑的前提下,显著提升了链接的可分享性、可管理性与安全性。

        从微博、微信到电商平台的促销活动,短链已成为现代 Web 应用不可或缺的基础设施之一。

        本项目旨在设计并实现一个高性能、高可用、可扩展的短链服务。系统基于 Spring Boot 构建,结合分布式 ID 生成、Redis 缓存加速、异步统计与安全防护机制,兼顾开发效率与生产级稳定性。无论是用于内部运营工具,还是支撑亿级流量的公开服务,该系统均能提供可靠支撑。


1、短链系统(Short URL System)

1.1、介绍

短链不是为“技术炫技”而生的,而是为了解决 特定业务场景下的现实问题

1.2、设计背景

场景 1:URL 太长,用户没法用

例子:微信/QQ 群里发一个带参数的链接

https://example.com/activity?user_id=12345&campaign=summer2024&source=wechat

&token=abc...xyz(长达 200+ 字符)

问题

1、微信会自动换行,点开变成两截 → 打不开;

2、用户复制容易漏掉后半段;

3、二维码太密,扫不出来

解决方案

→ 生成 https://s.xxx.com/Abc123;

→ 链接短、美观、二维码清晰、不怕截断;

场景 2:需要追踪流量来源(营销分析)

  • 运营要推广一个活动,通过:

    • 短信发给 A 用户群 → 短链s.xxx.com/sms

    • 公众号推送给 B 用户群 → 短链 s.xxx.com/wx

    • 抖音评论区放链接 → 短链s.xxx.com/dy

  • 后台统计:

    • sms 链接点击了 1 万次,转化率 5%

    • dy 链接点击了 50 万次,但转化率只有 0.1%

  • 价值:知道哪个渠道效果好,优化投放策略

📌 本质:把“不可追踪的流量”变成“可分析的数据”

场景 3:安全或风控需求(隐藏真实地址)

  • 秒杀、抢购、限量发售时,不想让机器人提前知道真实接口

  • 对外只公布一个短链,真实 URL 动态绑定

  • 甚至可以做到:同一个短链,不同用户点开跳不同页面(防刷)

📌 本质:增加攻击者成本,不是为了性能

1.3、缺陷

如下所示:

1.4、使用场景

如下所示:

举个例子:

  • 小公司做促销 → 用微信自带的“公众号文章链接”,或者用 新浪短网址
  • 大厂(如淘宝、拼多多)→ 自研短链平台,每天处理几十亿次跳转

2、系统设计

2.1、短码如何生成?且保证全局唯一且不重复?

采用 分布式唯一 ID + Base62 编码 的方式生成短码。

常见分布式id方案对比如下:

具体来说:

1、使用 Snowflake 算法(或公司内部的分布式 ID 服务,如美团 Leaf)生成全局唯一的 long 类型 ID;

2、将该 ID 通过 Base62 编码(字符集为0-9a-zA-Z)转换为 6~8 位的字符串,作为短码;

3、在数据库中,short_code 字段设置为 唯一索引,防止极端情况下的冲突(虽然 Snowflake 冲突概率极低);

4、如果发生冲突(比如时钟回拨导致 ID 重复),就重试一次生成新 ID。

好处:

  • 短码无序,不易被猜测,安全性高;
  • 不依赖数据库自增 ID,支持水平扩展;
  • 6 位 Base62 可支持约 568 亿条链接(62⁶),完全满足业务需求。

2.2、如何存储?数据库设计

数据库表设计:

CREATE TABLE short_url (id BIGINT PRIMARY KEY,          -- 分布式IDlong_url TEXT NOT NULL,         -- 原始长链接short_code VARCHAR(10) UNIQUE,  -- 短码,唯一索引created_at DATETIME,expired_at DATETIME,            -- 可选:过期时间visit_count INT DEFAULT 0       -- 可选:访问次数
);
CREATE INDEX idx_short_code ON short_url(short_code);

存储策略上:

读多写少:99% 是跳转请求(读),只有创建时是写;

用 Redis 缓存 short_codelong_url 的映射,设置合理 TTL(或永不过期);

跳转时优先查 Redis,未命中再查 DB,并回填缓存;

对于超大 long_url,可考虑分表或使用对象存储(如 OSS)+ 引用 ID,但一般 TEXT 足够。


2.3、高并发下如何扛住流量

面对高并发跳转请求,会从三层优化:

1. 缓存层

所有热点短链映射都缓存在 Redis,QPS 可轻松支撑 10w+。Redis 集群部署,避免单点。

2. 服务层

跳转接口是无状态的,用 Spring Boot + Nginx 做负载均衡,K8s 自动扩缩容。

3. 边缘优化(高阶)

        对于超大流量场景(如微博),可将热点映射推送到 CDN 边缘节点,用户请求直接在 CDN 层 302 跳转,完全绕过后端服务。同时,对 /create 接口做 限流(如 Sentinel 或 Guava RateLimiter),防止恶意刷短链。

2.4、短码会不会被穷举?如何防刷?

        理论上,短码空间有限(如 6 位 Base62 ≈ 568 亿),但暴力穷举成本极高,实际风险较低。更关键的是:短链本身不应承载敏感信息。

防护策略包括:

  • 短码长度 ≥ 6 位,避免空间过小;
  • 不使用连续 ID(如自增 ID),防止被顺序猜测;
  • 敏感链接额外加 token 或权限校验(例如:/Abc123?token=xxx),仅靠短码无法访问;
  • 监控异常行为:同一 IP 短时间内大量 404 请求,触发风控拦截;
  • 短链设置有效期,过期自动失效。

总之,安全不能只依赖“隐蔽性”,而要靠“权限控制 + 监控”。

2.5、如何统计

统计是有价值的(用于运营分析、渠道效果评估),但不能影响跳转性能

做法:

  • 跳转接口 异步记录点击事件
  • 将点击日志发送到 消息队列(如 Kafka 或 RocketMQ)
  • 由独立的消费服务批量处理,更新 DB 中的 visit_count,或写入数据仓库供 BI 分析;
  • 跳转延迟几乎不受影响(< 10ms);
  • 即使 MQ 暂时不可用,也可降级(如本地缓冲 + 定时上报);
  • 支持后续扩展:用户地域、设备、来源等维度分析。

2.6、短链过期怎么实现

过期机制我会结合 缓存 TTL + DB 状态检查 实现:

  • 创建短链时,如果设置了有效期(如 7 天),则:
    • 在 Redis 中设置 key 的 TTL(如 expire  short:Abc123 604800);
    • 同时在 DB 的 expire_at 字段记录过期时间;
  • 用户访问时:
    • 先查 Redis,若存在则直接跳转;
    • 若 Redis 未命中,再查 DB,并校验当前时间是否 < expire_at;
    • 如果已过期,返回 404,并清理 DB(或由定时任务归档);

这种方式兼顾性能与准确性,避免定时任务扫全表的低效操作。

2.7、 CDN/网关怎么配合

对于超大规模场景(如 Twitter、微博),完全可以不用后端参与跳转

具体做法:

  • 将热点短链的映射关系(如 JSON 文件或配置表)定期推送到 CDN 边缘节点
  • 用户请求 https://s.xxx.com/Abc123 时,CDN 直接返回 HTTP 302 跳转响应,Location 为原始长链接;
  • 整个过程 0 次后端调用,延迟极低,成本也低;

当然,这要求:

  • 热点集中(符合 20% 链接占 80% 流量的规律);
  • 有完善的配置推送和版本管理机制;
  • 冷门链接 fallback 到后端服务。

这是一种典型的 边缘计算 + 静态化 优化思路。


3、工程实践

3.1、系统目标

  • 用户输入长链接(如 https://example.com/very/long/path?query=123
  • 系统生成唯一短码(如 abc123
  • 访问 http://yourdomain/abc123 自动跳转到原长链接
  • 支持统计(可选)、有效期、去重等

3.2、数据库设计

CREATE TABLE short_url (id BIGINT AUTO_INCREMENT PRIMARY KEY,long_url VARCHAR(2048) NOT NULL,          -- 原始长链接short_code VARCHAR(10) NOT NULL UNIQUE,   -- 短码(如 abc123)created_at DATETIME DEFAULT CURRENT_TIMESTAMP,expired_at DATETIME NULL,                 -- 过期时间(可选)visit_count INT DEFAULT 0                 -- 访问次数(可选)
);-- 建议加索引
CREATE INDEX idx_short_code ON short_url(short_code);
CREATE INDEX idx_long_url ON short_url(long_url(255)); -- MySQL 对长字段需指定前缀

🔒 注意:long_url 可能超过 255 字符,所以用 VARCHAR(2048) 或 TEXT

3.3、技术选型

3.4、短码生成策略

方案1:自增 ID + Base62 编码(推荐)

  • 利用数据库自增 ID 的唯一性
  • 将 ID 转为 62 进制(0-9, a-z, A-Z),缩短长度
public class Base62Util {private static final String CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final int BASE = CHARS.length();public static String encode(long num) {if (num == 0) return String.valueOf(CHARS.charAt(0));StringBuilder sb = new StringBuilder();while (num > 0) {sb.append(CHARS.charAt((int)(num % BASE)));num /= BASE;}return sb.reverse().toString();}
}

示例:ID=123456 → 短码="21Ic"

3.5、核心代码实现

1. Entity

@Entity
@Table(name = "short_url")
public class ShortUrl {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, length = 2048)private String longUrl;@Column(nullable = false, unique = true, length = 10)private String shortCode;private LocalDateTime createdAt = LocalDateTime.now();private LocalDateTime expiredAt;private Integer visitCount = 0;// getters & setters
}

2. Repository

public interface ShortUrlRepository extends JpaRepository<ShortUrl, Long> {Optional<ShortUrl> findByShortCode(String shortCode);Optional<ShortUrl> findByLongUrl(String longUrl); // 用于去重
}

3. Service

@Service
@Transactional
public class ShortUrlService {@Autowiredprivate ShortUrlRepository repository;// 可选:集成 Redis 缓存// @Autowired private RedisTemplate<String, String> redisTemplate;public String createShortUrl(String longUrl) {// 1. 去重:如果已存在,直接返回var existing = repository.findByLongUrl(longUrl);if (existing.isPresent()) {return existing.get().getShortCode();}// 2. 保存到 DB(利用自增 ID)ShortUrl entity = new ShortUrl();entity.setLongUrl(longUrl);ShortUrl saved = repository.save(entity); // 此时 ID 已生成// 3. 生成短码String shortCode = Base62Util.encode(saved.getId());saved.setShortCode(shortCode);repository.save(saved); // 再次保存短码return shortCode;}public String getLongUrl(String shortCode) {// 可加入 Redis 缓存逻辑return repository.findByShortCode(shortCode).filter(url -> url.getExpiredAt() == null || url.getExpiredAt().isAfter(LocalDateTime.now())).map(ShortUrl::getLongUrl).orElse(null);}public void incrementVisitCount(String shortCode) {repository.findByShortCode(shortCode).ifPresent(url -> {url.setVisitCount(url.getVisitCount() + 1);repository.save(url);});}
}

4. Controller

@RestController
public class ShortUrlController {@Autowiredprivate ShortUrlService shortUrlService;// 创建短链@PostMapping("/shorten")public ResponseEntity<Map<String, String>> shorten(@RequestBody Map<String, String> request) {String longUrl = request.get("url");if (longUrl == null || !longUrl.startsWith("http")) {return ResponseEntity.badRequest().build();}String shortCode = shortUrlService.createShortUrl(longUrl);String shortUrl = "http://localhost:8080/" + shortCode; // 实际应使用域名return ResponseEntity.ok(Map.of("shortUrl", shortUrl, "code", shortCode));}// 跳转@GetMapping("/{code}")public void redirect(@PathVariable String code, HttpServletResponse response) throws IOException {String longUrl = shortUrlService.getLongUrl(code);if (longUrl == null) {response.sendError(HttpServletResponse.SC_NOT_FOUND, "短链不存在或已过期");return;}// 可选:异步增加访问次数(避免阻塞跳转)shortUrlService.incrementVisitCount(code);response.sendRedirect(longUrl); // 默认 302 临时重定向// 若需 301:response.setStatus(301); response.setHeader("Location", longUrl);}
}

部署示例

  • 域名:s.example.com
  • 用户访问:https://s.example.com/abc123→ 跳转到原始链接;
  • 创建接口:https://api.example.com/shorten

4、性能与安全优化建议

优化点说明
Redis 缓存缓存shortCode ----> longUrl 映射,减少 DB 查询
异步统计使用 @Aysnc 异步更新访问次数.
限流对  /shorten 接口做 IP 限流(防止刷短链)
URL 校验验证 longUrl 是否合法、是否黑名单域名
HTTPS生产环境务必使用 HTTPS
短码长度控制Base62 下,6位可支持 568亿条(62^6 ≈ 5.68e10)
分布式 ID若不用 DB 自增,可用 Snowflake(需处理时钟回拨)

总结:

        短链系统不是技术亮点,而是一个“业务工具”。它解决的不是“代码怎么写更快”,而是“用户怎么用更方便”、“运营怎么分析更清楚”、“安全怎么防更有效”。


参考文章:

1、https://blog.csdn.net/wy990880/article/details/146535053?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9F%AD%E9%93%BE%E7%B3%BB%E7%BB%9F&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-146535053.142^v102^pc_search_result_base2&spm=1018.2226.3001.4187https://blog.csdn.net/wy990880/article/details/146535053?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9F%AD%E9%93%BE%E7%B3%BB%E7%BB%9F&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-146535053.142^v102^pc_search_result_base2&spm=1018.2226.3001.4187

http://www.dtcms.com/a/606721.html

相关文章:

  • 算法33.0
  • 建造个网站花多少钱今天福州头条新闻
  • 优秀简历制作网站wordpress主题更换字体教程 hu
  • 如何在沟通不畅导致误解后进行修复
  • c语言编译器哪个好用 | 探讨选择合适C语言编译器的标准与建议
  • 广州十度网络网站开发最好潍坊网站seo
  • 38.附近商户实现
  • 做网站 帮别人卖服务器成都网站设计
  • 园林景观网站源码做网站前应该先出图
  • Zookeeper 基础入门与应用场景解析
  • 雅虎做网站推广网站设计网页设计
  • cms建设网站官方网站的优势
  • c语言printf输出控制符
  • 数据库三大范式详解
  • 做精酿啤酒购买的网站竞价广告
  • JAVA国际版二手车交易二手车市场系统源码支持Android+IOS+H5+APP
  • 做电脑网站手机能显示不出来seo网络推广企业
  • 织梦网站首页打开慢怎么注册一个域名
  • 关于图的算法题总结
  • HarmonyOS:动画衔接
  • 百度收录不了网站建设网站注意实现
  • 方寸之间藏智慧:家用电器的进化与生活革新
  • 智能手机市场再次洗牌,远控何以成为数码生活新“连接器”?
  • 网站建设宣传软文范例新洲网站建设
  • 建旅游网站费用明细网站建设公司 优势
  • CSS笔记
  • 个人网站怎么建立要多少钱宣传册
  • Plaxis岩土工程全模块Python自动化建模与案例,涵盖塑性、渗流、固结、动力、稳定安全及热力TM等核心问题
  • 双人对话生成模型 MOSS 上线,支持零样本语音克隆
  • SMOTE详解