大家好,我是波特~

日常开发里,短链绝对是高频场景:短信营销、公众号分享、二维码跳转、数据统计…… 都离不开它。今天咱们不玩虚的,直接从 0 到 1用 Spring Boot 撸一个可上线的短链系统,包含数据库设计、核心算法、完整代码、性能优化,复制粘贴就能跑!

一、为什么要做短链系统?

先搞清楚需求,再写代码才不慌。

短链系统核心价值:

  • 缩短长度:超长链接变精简,适配短信、微博、朋友圈字符限制

  • 数据统计:按渠道生成短链,统计点击量、来源、地域、设备

  • 安全可控:隐藏原始 URL,支持过期、禁用、访问限制

  • 体验更好:二维码更简洁,用户更愿意点击

一句话:长链接负责业务,短链接负责传播

二、系统整体设计

一个健壮的短链系统,必须包含这 5 块:

核心模块

  • 短链生成:长 URL → 唯一短码(核心)

  • 存储映射:短码 ↔ 长链接持久化

  • 跳转服务:访问短码 302 重定向到原链接

  • 过期 / 禁用:支持临时短链、违规下架

  • 访问统计:PV、UV、来源、时间分布

关键技术选型

  • 开发框架:Spring Boot

  • 数据库:MySQL(持久化)

  • 缓存:Redis(加速跳转、防击穿)

  • 生成算法:雪花 ID + Base62

  • 安全:URL 校验、黑名单、限流

三、数据库设计

先明确表结构设计思路:核心是存储“短码-长链接”的映射关系,同时记录短链的创建时间、过期时间、点击量、状态等信息,方便后续管控和统计;短码作为唯一索引,提升查询速度(跳转时需要根据短码快速查询长链接)。

短链主表:short_link

CREATE TABLE`short_link` (
`id`bigintNOT NULLw COMMENT'主键',
`short_code`varchar(11) NOTNULLCOMMENT'短链码',
`original_url`varchar(500) NOTNULLCOMMENT'原始长链接',
`expire_time` datetime DEFAULTNULLCOMMENT'过期时间',
`create_time` datetime DEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',
`click_count`intDEFAULT'0'COMMENT'点击次数',
`status`tinyintDEFAULT'1'COMMENT'1-正常 0-禁用',
  PRIMARY KEY (`id`),
UNIQUEKEY`uk_short_code` (`short_code`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COMMENT='短链映射表';
  • short_code:唯一索引,跳转查询必加速

  • original_url:支持 500 长度,覆盖绝大多数链接

  • 增加状态、点击量、过期时间,直接满足生产需求

四、短链生成算法:Base62

短链生成的核心的是“将长链接转换成唯一的短码”,目前主流有两种方案,咱们先对比分析,再选择最适合实战的方案:

1、哈希算法:对长链接进行哈希运算,得到固定长度的哈希值,再截取前几位作为短码。优点是实现简单,无需依赖数据库;缺点是存在哈希冲突(不同长链接可能生成相同短码),需要额外处理冲突(比如重试、拼接随机数),适合对唯一性要求不高的场景。

2、雪花 ID + Base62:无冲突、长度短、性能高。使用雪花 ID 后,短链(short_code)最大长度为 11 位。本文示例直接用方案 2

Base62 是什么?

Base62 使用 [0-9][a-z][A-Z] 共 62 个字符编码,可生成短而唯一的字符串

Base62工具类

public class Base62Util {
    privatestaticfinal String CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    privatestaticfinalint SCALE = 62;

    // 数字 → Base62字符串
    public static String encode(long num) {
        StringBuilder sb = new StringBuilder();
        while (num > 0) {
            int idx = (int) (num % SCALE);
            sb.append(CHARS.charAt(idx));
            num = num / SCALE;
        }
        return sb.reverse().toString();
    }
}

五、Spring Boot 完整代码实现

1、pom.xml文件中的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

application.yml配置文件:

spring:
  datasource:
    url:jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false
    username:root
    password:pottercoding
jpa:
    hibernate:
      ddl-auto:update
    show-sql:true

2、实体类

@Data
@Entity
@Table(name = "short_link")
publicclass ShortLink {
    @Id
    private Long id;

    @Column(unique = true, nullable = false, length = 10)
    private String shortCode;

    @Column(nullable = false, length = 500)
    private String originalUrl;

    private LocalDateTime expireTime;

    private LocalDateTime createTime = LocalDateTime.now();

    private Integer clickCount = 0;

    private Integer status = 1;
}

3、DAO 层

public interface ShortLinkRepository extends JpaRepository<ShortLink, Long> {
    // 根据短码查询
    ShortLink findByShortCode(String shortCode);
}

4、Service 层(核心逻辑)

@Service
@RequiredArgsConstructor
publicclass ShortLinkServiceImpl implements ShortLinkService {

    privatefinal ShortLinkRepository repository;

    /**
     * 生成短链
     */
    @Transactional
    public String createShortLink(String originalUrl) {
        // URL合法性校验
        if (!UrlCheckUtil.isValidUrl(originalUrl)) {
            thrownew RuntimeException("URL格式不合法");
        }

        ShortLink link = new ShortLink();
        long id = IdUtil.getSnowflakeNextId();
        link.setId(id);
        link.setOriginalUrl(originalUrl);

        // ID → Base62短码
        String shortCode = Base62Util.encode(id);
        link.setShortCode(shortCode);
        repository.save(link);

        // 返回完整短链
        return"http://localhost:8080/" + shortCode;
    }

    /**
     * 根据短码获取长链接
     */
    public String getOriginalUrl(String shortCode) {
        ShortLink link = repository.findByShortCode(shortCode);
        if (link == null) {
            thrownew RuntimeException("短链不存在");
        }
        if (link.getStatus() == 0) {
            thrownew RuntimeException("短链已禁用");
        }
        if (link.getExpireTime() != null && LocalDateTime.now().isAfter(link.getExpireTime())) {
            thrownew RuntimeException("短链已过期");
        }

        // 点击量+1(异步更优)
        link.setClickCount(link.getClickCount() + 1);
        repository.save(link);

        return link.getOriginalUrl();
    }
}

5、Controller 层

@RestController
@RequiredArgsConstructor
publicclass ShortLinkController {
    privatefinal ShortLinkService shortLinkService;

    /**
     * 生成短链接口
     */
    @PostMapping("/api/shorten")
    public String shorten(@RequestParam String url) {
        return shortLinkService.createShortLink(url);
    }

    /**
     * 短链跳转
     */
    @GetMapping("/{shortCode}")
    public void redirect(@PathVariable String shortCode, HttpServletResponse response) throws IOException {
        try {
            String originalUrl = shortLinkService.getOriginalUrl(shortCode);
            response.sendRedirect(originalUrl);
        } catch (Exception e) {
            response.getWriter().write(e.getMessage());
        }
    }
}

6、URL 校验工具

public class UrlCheckUtil {
    private static final String REGEX = "^(https?|ftp)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";

    public static boolean isValidUrl(String url) {
        return Pattern.matches(REGEX, url);
    }
}

六、启动测试

启动项目后,调用生成接口:http://localhost:8080/api/shorten?url=https://www.pottercoding.cn,访问返回短链:http://localhost:8080/2r7p8jXzVPa,访问该短链可直接调整到我的个人网站,完美!

图片

图片

七、生产级优化

1、Redis 缓存加速:跳转是超高并发读,必须加 Redis:

  • 短码 → 长链接缓存

  • 过期时间和短链保持一致

2、布隆过滤器防缓存穿透:防止恶意请求不存在的短码打崩数据库。

3、点击量异步更新:用 @Async 或消息队列更新点击量,不影响跳转性能。

4、分布式部署:用雪花 ID替代自增 ID,支持多实例部署。

5、安全加固

  • 黑名单过滤恶意 URL

  • 接口限流防刷

  • 短码随机打乱,避免遍历

代码地址,可直接下载运行:https://pan.baidu.com/s/19lhO7x2NCphUvkcd4swj6w?pwd=j6ux 提取码: j6ux

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐