面试题:让你设计一个短链系统,怎么设计?

设计一个短链系统(Short URL System)是一个经典的面试题,涉及高并发、高性能、分布式系统设计等多个方面。以下是设计短链系统的详细思路和实现方案。


1. 需求分析

核心功能

  1. 生成短链:将长 URL 转换为短 URL。
  2. 访问短链:通过短 URL 重定向到原始长 URL。
  3. 高并发支持:支持大量用户同时生成和访问短链。
  4. 数据持久化:存储短链和长 URL 的映射关系。

非功能需求

  1. 高性能:短链访问需要低延迟。
  2. 高可用:系统需要 7×24 小时可用。
  3. 可扩展性:支持水平扩展以应对流量增长。
  4. 安全性:防止恶意攻击(如短链滥用)。

2. 系统设计

系统架构

  1. 客户端:用户通过浏览器或 App 访问短链系统。
  2. API 网关:负责请求路由、负载均衡和限流。
  3. 短链生成服务:负责生成短链并存储映射关系。
  4. 短链重定向服务:负责根据短链查找原始 URL 并重定向。
  5. 存储层:存储短链和长 URL 的映射关系。
  6. 缓存层:缓存热门短链,减少数据库访问。

3. 详细设计

短链生成算法

  1. 哈希算法
    • 使用哈希函数(如 MD5、SHA-256)对长 URL 进行哈希。
    • 取哈希值的前几位作为短链。
    • 优点:简单易实现。
    • 缺点:可能存在哈希冲突。
  2. 自增 ID + 进制转换
    • 使用分布式 ID 生成器(如 Snowflake)生成唯一 ID。
    • 将 ID 转换为 62 进制(a-z, A-Z, 0-9)作为短链。
    • 优点:无冲突,短链长度可控。
    • 缺点:需要分布式 ID 生成器。

存储设计

  1. 数据库表结构
    • 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
      );
  2. 缓存设计
    • 使用 Redis 缓存热门短链。
    • Key: short_code,Value: long_url
    • 设置缓存过期时间,避免缓存雪崩。

高并发支持

  1. 分布式 ID 生成器
    • 使用 Snowflake 算法生成唯一 ID。
    • 确保 ID 在分布式环境下唯一。
  2. 缓存预热
    • 提前将热门短链加载到缓存中。
    • 减少数据库访问压力。
  3. 限流与降级
    • 使用 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
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容