如何设计一个 TinyURL 系统

基本需求

TinyURL 就是将一个长度很长的 URL 缩短,短 URL 更容易在短息或者基础通知中展示。

简单分析

涉及的接口有两个,申请短链的接口,短链转化的接口。

申请短链的接口,就是将长链映射到一个短链上,需要注意短链不能重复。

短链转化的接口就是将短链重新转化成长链,并进行重定向。

重定向类型

HTTP 的重定向状态码有 302 和 301 两种,301 是永久重定向,302 是临时重定向。

使用 301 的好处是大部分的浏览器会缓存该种重定向的映射关系,对于下次请求可以直接转发到目标 URL,相对于 302 来说对服务端压力更小,速度更快。

一般情况下都更倾向于使用 301 进行转发。

如果存在请求数的统计,需要切换到 302。

长短映射逻辑

选择 MySQL 作为具体的保存方式。

对短链 short_url 加唯一索引,确保短链的唯一性。

如果要求严格的一一对应的关系,不希望一个长链对应多个短链,那就对长短双链两个字段各自加唯一索引。

唯一索引会降低些许的写入性能,但是应该在接受的范围之内。


映射算法

映射最先想到的肯定是 Hash 算法。

最基础的方法就是将长链做 Hash,可以使用 MurmurHash 算法(Google 大佬开源的非加密型 Hash 算法),对于 MD5,SHA 等其他 Hash 算法有更强的随机性。

所有实现映射的流程如下:

  1. 使用 MurmurHash 算法对长链做摘要,得到之后的短链
  2. 将长链和短链写入 DB,如果遇到唯一键冲突一异常,则跳回第一步
  3. 写入成功,返回短链


对以上过程优化,就使用布隆过滤器,在入库前先判断一遍短链是否已经存在。

布隆过滤器存在误判的情况,但是只可能误判不存在,对于此处来说就是多执行几次 MurmurHash 的事情。

布隆过滤器是一种非常省内存的数据结构,长度为 10 亿的布隆过滤器,只需要 125 M 的内存空间。

除了 Hash 算法,另外就是采用发号器形式,从统一的发号服务中获取唯一序列。

业内开源的发号系统有以下几种:

  1. SnowFlake(Twitter 开源)
  2. Leaf(美团开源)
  3. TinyId(滴滴开源)

不好意思,以上的都没了解过,XD。

除了以上的方法还可以用 Redis 以及 MySQL 的主键自增,个人可能更喜欢使用 MySQL 的自增主键多一点。

如果感觉频繁写入性能太差,可能使用分表,每个表都可以分配一段主键 ID。

例如,我分16张表后,可以设置第一个表从1开始递增,第二个表从10000开始递增,如此实现多表的唯一。

或者新增一张号段表,使用 CAS 的更新方式去获取号段。

短链转长链请求

该请求逻辑非常简单,短链到长链就是一个简单的映射查询。

可以使用本地和 Redis 的多级缓存,提高接口的性能,获取长链的时间复杂度接近O(1)。

短链15分钟失效

类似超过30分钟不付款就取消订单的需求,但是这个失效不需要主动触发。

短链的失效可以在下次查询时通知,是否需要再次生成短链或者直接抛出异常根据需求定。

如果需要主动失效,可以使用以下几种方法:

  1. 数据库轮询,可以由 XXL-JOB 等定时器触发
  2. RabbitMQ 定时消息插件
  3. 本地定时,可以使用 JDK 的延时队列或者 Netty 的时间轮
  4. Redis + XXL-JOB 的定时,避免数据库的轮询将过期事件放入 ZSET,由 XXL-JOB 触发定时,查询当前时间之前的短链,然后执行过期。

results matching ""

    No results matching ""