短链存储设计技术应运而生于互联网高速发展的背景之下。随着在线业务的爆炸性增长,短链接服务成为了缓解URL长度限制、提升用户体验的重要手段。然而,随之而来的是海量的短链数据存储需求以及对高性能访问的要求。在这样的背景下,如何高效地存储和管理大规模短链接,同时确保高速访问和成本控制,是短链设计的重要一环。
下图为一个短链从创建到访问的一个流程,不过本文不会介绍一个短链平台如何从0到1的一个完整搭建,重点放在短链生成的选择以及冲突问题解决,也就是下图的【生成key映射源链】部分。
码生成
短链生成的常见做法是就是使用 62 进制(0-9a-zA-Z)编码,将长 URL 映射为较短的字符串,数据库存储短链与长链的映射关系,短链做唯一索引控制。 数据库记录对应上面的记录最主要的就是
短链code: iAmCode
源链接:https://user.37.com.cn/role-trade/pages/user/evaluate?user_identityxxxxx...
方案对比:
- 哈希方案
使用哈希算法将链接计算得到一个随机字符串。
如果比较长,可以取前面的一部分作为短链,也可以用两个不同的算法各自取一段合并。这种方式的优点是生成的短链是随机的,不容易被猜测,并且可以做幂等,但是缺点也是比较明显,会生成重复的短链,所以要做冲突检测。出现重复的情况下,需要把数据取出来做对比,如果是相同的数据,可以直接返回,如果不同,还需要重新生成(额外加字段变长),极端情况下会做多次检查,直到没有重复的短链。
- 随机生成方案
随机生成字符串
这种方式的优点是生成的短链是随机的,不容易被猜测,缺点依然会生成重复的短链,所以要做冲突检测。出现重复的情况下,只要重复N次,直到没有重复的短链。相比哈希方案,这种方式的优点是不需要做数据对比。
- 自增方案
自增方案是每次生成串的时候,取一个自增的数字,然后转换为62进制
这种方式的优点是不会生成重复的短链,缺点是短链是递增的,不安全,容易被猜测。当然也可以略施技巧做下混淆,稍微提高门槛,但是不会完全解决问题。但是呢,复杂性相对前两种会高,需要有额外的机制保障拿到一个自增ID,比如分布式ID生成器,或者数据库自增ID,或者Redis自增ID等等。不管哪种都需要依赖外部服务,这样会增加系统的复杂度。
综上三个方案做个简单对比:
性能 | 幂等 | 预生成 | 不可预测 | 复杂性 | 生成Key冲突 | 风险 | |
---|---|---|---|---|---|---|---|
随机生成 | 👍👍 | 不支持 | 支持 | 是 | 简单随机生成+冲突(重新获取随机数) 👍👍👍 | 基数越大,冲突越大 1kw,冲突15,概率0.000015% 1e,冲突1385,概率0.001385%2e,冲突5753,概率0.0028765% | 短链回收后,被重复利用后再打开 |
哈希生成 | 👍 | 支持 | 不支持 | 是 | 简单-哈希截取+冲突(幂等检查、 补位重哈希处理) 👍👍 | 接近同上 | 短链回收后,被重复利用后再打开位数不能变 |
自增生成 | 👍👍👍 | 不支持 | 支持 | 混淆,不能完全无法预测 | 简单-自增ID(发号器) 👍 | 几乎无 | 溢出,可能性极低生成流量大,可能堵塞 |
我们方案选择的基本原则就是要简单,越是底层的服务,看重的无非就是稳定、高性能。只有架构/流程足够简单,稳定基本就很容易做到,而我们选择了随机生成方案。
从方案复杂性考虑:
对比哈希方案,随机生成方案的优点是不需要做数据对比,并且业务对于幂等并没有要求,同样一个长链接,生成的短链允许是不同的;
对比自增方案,随机生成方案只要本地服务即能处理,不需要依赖外部服务。同时从表格可以看出当数据量很大情况,冲突的概率其实很小;而且比较灵活,比如可以根据业务需求自定义短链内容。
高性能方面,其实就是短链生成的速率和查询速度。
生成的速率上:
随机生成和自增方案都可以通过空间换时间的方式,提前生成好一串码,比如随机生成方案,则提前在内存里生成一批不存在的码,不过分配后还要再检查一遍冲突情况。 而自增方案只需要提前分配好ID段,程序自动递增1就行了(参考发号器方案),当然重启就会拿新段,会造成一定浪费。从性能上看自增方案则会略胜一筹,因为自增的方式不需要处理冲突的情况,随机生成需要检查处理冲突的情况,而且数据基数越大,冲突的情况也会越明显。而哈希方案则无法做到,因为它依赖于源内容才能知道生成什么码,无法预分配。
解决冲突:
那么既然我们选择了随机生成方案,并且短链作为一个基础服务,必然要减少冲突的发生保证服务的响应速度。那减少冲突的情况,就是尽量让冲突的概率维持在一个稳定的范围内。既然冲突概率变大,就是数据基数大,而我们又不能让业务不生成短链,短链一定是会持续创建的。但我们可以从业务的角度来让基数变小,短链其实也是有一定时效性,一般只在活动期间有效,或者像电商那样商品上架期间才有用,即使没有规定时间访问,但是基本没人会将链接存在过个几个月几年再打开看,真到那时候业务也变化很大不能用了。所以在短链创建的时候会要求要设定有效期,这样当过了有效期,再访问就直接显示404提示不存在。然后有额外的脚本将数据库里过期的数据定期回收归档,这样子基本能保证数据不会无限制的增长下去。
至于查询速度上,就和方案无关了,很大程度上就取决于数据库的查询速度。这个和我们主题不太相关,我们不做多讲,我们使用的是mysql+redis的方案,也是业务成熟的使用方式。