设计一个短链系统(Short URL System)是一个经典的面试题,涉及高并发、高性能、分布式系统设计等多个方面。以下是设计短链系统的详细思路和实现方案。
1. 需求分析
核心功能
- 生成短链:将长 URL 转换为短 URL。
- 访问短链:通过短 URL 重定向到原始长 URL。
- 高并发支持:支持大量用户同时生成和访问短链。
- 数据持久化:存储短链和长 URL 的映射关系。
非功能需求
- 高性能:短链访问需要低延迟。
- 高可用:系统需要 7×24 小时可用。
- 可扩展性:支持水平扩展以应对流量增长。
- 安全性:防止恶意攻击(如短链滥用)。
2. 系统设计
系统架构
- 客户端:用户通过浏览器或 App 访问短链系统。
- API 网关:负责请求路由、负载均衡和限流。
- 短链生成服务:负责生成短链并存储映射关系。
- 短链重定向服务:负责根据短链查找原始 URL 并重定向。
- 存储层:存储短链和长 URL 的映射关系。
- 缓存层:缓存热门短链,减少数据库访问。
3. 详细设计
短链生成算法
- 哈希算法:
- 使用哈希函数(如 MD5、SHA-256)对长 URL 进行哈希。
- 取哈希值的前几位作为短链。
- 优点:简单易实现。
- 缺点:可能存在哈希冲突。
- 自增 ID + 进制转换:
- 使用分布式 ID 生成器(如 Snowflake)生成唯一 ID。
- 将 ID 转换为 62 进制(a-z, A-Z, 0-9)作为短链。
- 优点:无冲突,短链长度可控。
- 缺点:需要分布式 ID 生成器。
存储设计
- 数据库表结构:
short_code
:短链的唯一标识。long_url
:原始长 URL。expires_at
:短链过期时间(可选)。CREATE TABLE short_urls ( id BIGINT PRIMARY KEY AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL UNIQUE, long_url TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP );
- 缓存设计:
- 使用 Redis 缓存热门短链。
- Key:
short_code
,Value:long_url
。 - 设置缓存过期时间,避免缓存雪崩。
高并发支持
- 分布式 ID 生成器:
- 使用 Snowflake 算法生成唯一 ID。
- 确保 ID 在分布式环境下唯一。
- 缓存预热:
- 提前将热门短链加载到缓存中。
- 减少数据库访问压力。
- 限流与降级:
- 使用 API 网关对请求进行限流。
- 在系统压力过大时,降级部分功能(如短链生成)。
4. 关键问题与解决方案
1. 短链冲突
- 解决方案:
- 使用唯一 ID 生成器(如 Snowflake)避免冲突。
- 如果使用哈希算法,可以在冲突时追加随机字符。
2. 短链过期
- 解决方案:
- 在数据库中存储短链的过期时间。
- 定期清理过期短链。
3. 缓存穿透
- 解决方案:
- 对不存在的短链,缓存空值(如
NULL
)。 - 使用布隆过滤器(Bloom Filter)快速判断短链是否存在。
- 对不存在的短链,缓存空值(如
4. 安全性
- 解决方案:
- 对短链进行校验,防止恶意短链。
- 限制单个用户生成短链的频率。
5. 示例实现
短链生成服务(Java)
import java.util.Base64;
import java.security.MessageDigest;
public class ShortUrlGenerator {
private static final String BASE62 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 生成短链
public static String generateShortUrl(String longUrl) {
try {
// 使用 MD5 哈希
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hash = md.digest(longUrl.getBytes());
// 取前 6 位作为短链
String shortCode = Base64.getUrlEncoder().encodeToString(hash).substring(0, 6);
return shortCode;
} catch (Exception e) {
throw new RuntimeException("生成短链失败", e);
}
}
}
短链重定向服务(Java + Spring Boot)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;
@RestController
public class ShortUrlController {
@Autowired
private ShortUrlService shortUrlService;
// 访问短链
@GetMapping("/{shortCode}")
public RedirectView redirect(@PathVariable String shortCode) {
String longUrl = shortUrlService.getLongUrl(shortCode);
if (longUrl == null) {
throw new RuntimeException("短链不存在");
}
return new RedirectView(longUrl);
}
}
短链服务(Java + Spring Boot)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.data.redis.core.StringRedisTemplate;
@Service
public class ShortUrlService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private ShortUrlRepository shortUrlRepository;
// 获取长 URL
public String getLongUrl(String shortCode) {
// 从缓存中查找
String longUrl = redisTemplate.opsForValue().get(shortCode);
if (longUrl != null) {
return longUrl;
}
// 从数据库中查找
longUrl = shortUrlRepository.findByShortCode(shortCode);
if (longUrl != null) {
// 存入缓存
redisTemplate.opsForValue().set(shortCode, longUrl);
}
return longUrl;
}
}
6. 总结
- 短链生成:使用哈希算法或自增 ID + 进制转换。
- 存储设计:数据库 + 缓存(如 Redis)。
- 高并发支持:分布式 ID 生成器 + 缓存预热 + 限流。
- 安全性:防止缓存穿透和恶意短链。
通过以上设计,可以实现一个高性能、高可用的短链系统。
THE END
暂无评论内容