短链接实现
前几天面试的时候被面试官问到了这个问题,当时的回答不是很好,经过资料查阅与思考后把想法记录下来,便于后面复习查看。
介绍
短链接项目可以将一个很长的网址(如带有许多参数的商品链接)转换成一个非常短的、易于分享的网址,当用户访问这个短链接时,系统会自动、快速地将用户浏览器跳转到原始的长网址页面。此外,还需要记录链接的点击次数、地理位置、来源、时间等信息。要求进行一个高并发(读/写)、高可用、高性能的实现。
参考答案
架构
- CDN:将热门的短链接-长链接映射关系缓存到 CDN 节点。用户访问时,如果 CDN 有缓存,会直接在最近的节点完成 302/301 跳转,不会到达源服务器,极大提升了性能和并发能力。
- 负载均衡:负责将请求流量均匀分发到后端的多个应用服务器。
- 应用服务器集群:运行短链接核心逻辑的无状态服务。
- 缓存层:如 Redis 集群,用于缓存短链接和长链接的映射关系,抵挡数据库的压力。
- 数据层:如 MySQL,负责持久化存储数据。
- 发号器:用于生成全局唯一短码的服务。
发号器选型
雪花算法
雪花算法是一种分布式 ID 生成算法。它生成一个 64-bit 的 long 类型 ID,包含时间戳、数据中心 ID、机器 ID 和序列号。具有全局唯一、趋势递增(适合索引)、去中心化的优点。
号段模式
建立一个独立的发号器服务。应用服务器每次不是获取一个 ID,而是从发号器获取一个 ID 区间,然后在本地内存中逐一使用这个区间的 ID,用完后再去申请下一个号段。
为什么不使用数据库自增 ID
严重依赖单个数据库的写入性能,是系统的性能瓶颈,且无法水平扩展。
为什么不使用 UUID
生成的字符串太长,不适用于短链接,且无序,作为数据库主键时,会导致数据库索引(B+树)频繁地页分裂,在高并发写入时严重影响数据库性能。
跳转服务
数据读取路径: 遵循 Cache-Aside 模式,读取路径为:CDN -> 缓存 (Redis) -> 数据库 (DB)。
- 对于访问频率极高,CDN 可以直接缓存 HTTP 301/302 的跳转响应。
- 如果 CDN 未命中,请求会到达应用服务器。服务器首先查询缓存(如 Redis 集群),Redis 的 QPS 可达 10万/秒,能轻松应对绝大部分请求。
- 只有当缓存也未命中时,才会查询数据库。查询到之后,会将结果写回缓存中,方便下次快速访问。
缓存优化:
- 缓存穿透:对于恶意请求的、不存在的短链接使用布隆过滤器,避免无效的数据库查询。
- 缓存雪崩:对于大量缓存在同一时间失效,可以通过设置随机的过期时间来避免。
数据库选型
存储“短链接-长链接”映射关系
这种数据读取非常频繁,写入并发也很高(题目要求),可以采用关系型数据库(如 MySQL,使用数据库集群),使用分库分表的策略来实现高并发写入的要求,或使用消息队列(如 Kafka)进行流量削峰。对于分库分表,选择短链接进行 hash 计算然后取模,决定数据存储到哪一个库、哪一张表中。
存储统计分析信息
这种数据是典型的“写多读少”场景,且数据几乎不变,一旦写入,几乎不会再更新。可以采用 LSM-Tree 架构的 NoSQL 分布式数据库(如 HBase、TiDB),其具有极高的写入吞吐量,擅长处理高并发写入。如果需要实时进行分析,可以选择存储到搜索引擎(如 Elasticsearch 集群)中,数据写入后很快就可以被查询到。
应用服务
应用服务最好是无状态服务,不存储任何状态信息。这样,任何一台服务器都可以处理任何请求,便于负载均衡和弹性伸缩。可以使用水平扩容,将应用服务部署在多台机器上组成集群,通过负载均衡器分发流量。为了应对数据中心级别的故障,可以将服务部署在不同地理位置的多个机房,达到高可用的需求。

浙公网安备 33010602011771号