关于面试:答面试官问如何设计短url服务

2次阅读

共计 3073 个字符,预计需要花费 8 分钟才能阅读完成。

什么是短 url

短 url,顾名思义,就是将长网址缩短到一个很短的网址,用户拜访这个短网址能够重定向到本来的长网址(还原)。这样能够达到易于记忆、转换的目标,还有暗藏链接参数,利于短信推广的作用,罕用于有字数限度的短信、微博、二维码等场景。

谬误答案

这个实现的思路真的是天花乱坠,此处总结几种谬误的实现形式来避坑。

1. 实现一个算法,能够间接把一百个字符左右的长网址缩短到 10 位以内,并能够原样还原,即可逆

不可能的。为所有可能存在的长链接实现一一对应, 自身是不可能的, 会呈现碰撞, 多个长链接对应一个短链接,所以不会可逆。

2. 实现算法将长地址转短地址, 不实现逆运算, 将短长对应关系存数据库中

不可能的。没有扭转实质(长链接数量远多于长链接), 只有长链接够多, 必然会碰撞

3. 用一个 hash 算法, 生成的短链接碰撞后, 在短链接前面加 1,2,3

物理逻辑上可行,生产利用不可行。效率会不可控的升高,通过算法算进去短 url 之后,hash 碰撞后生成多个 xx1,xx2,xx3….xx20… 的短 url,长短对应数量不可控,查找效率会升高。

4. 随机生成一个短地址, 去数据库查找是否用过, 用过就再随机, 直到随机到一个没用过的短地址, 存数据库

物理逻辑上可行,生产利用不可行。每次生成都有必要的全量查问操作,必定是不 OK 的。

5. 保护一个超级大的全量数据,事后生成超过理论生产应用的短 url,接口调用间接颁发,同步批改短 url 应用状态。

物理逻辑上可行,生产利用低可用。具体是在 redis 还是 db 里批量生成其实是截然不同的两种实现。

若是 redis, 那么外面要放入全量的短 url 么?否则怎么晓得这个短 url 到底是不是惟一的?如果全量,那对 redis 的可用性就要严格保障,为了高可用,就须要多节点同步维持全量数据,这个过程要做好不是那么的容易;若是 db, 那么就要有大量的并发锁定,意味着大量读写,这个对数据库也是个考验。

正确答案

建设一个发号器, 每次有一个新的长 URL 进来, 咱们就减少一, 并且将新的数值返回. 第一个来的 url 返回 ”www.x.cn/0″, 第二个返回 ”www.x.cn/1″.

细节问题

短 url 的还原跳转用 301 还是 302?

301 是永恒重定向,302 是长期重定向。

短地址一经生成就不会变动,所以用 301 是合乎 http 语义的。同时浏览器会对 301 申请保留一个比拟长期的缓存,这样就加重对服务器的压力;而且 301 对于网址的 SEO 有肯定的晋升。然而很多状况下咱们须要对接口点击或者用户行为进行一些业务监控解决的时候,301 显著就不适合了(浏览器间接依照缓存数据跳转了), 所以很多业务场景下还是采纳 302 比拟适合。

字符超长问题

即便到了 10 亿 (Billion) 转换而成的 62 进制也无非是 6 位字符,所以长度根本不在思考范畴内,这个范畴足够应用了。

对应关系如何存储?

这个对应数据必定是要落盘的, 不能每次零碎重启就从新排号, 所以能够采纳 mysql 等数据库来存储. 而且如果数据量小且 qps 低, 间接应用数据库的自增主键就能够实现.

如何保障长短链接一一对应?

依照下面的发号器策略, 是不能保障长短链接的一一对应的, 你间断用同一个 URL 申请两次, 后果值都是不一样的.

为了实现长短链接一一对应, 咱们须要付出很大的空间代价, 尤其是为了疾速响应, 咱们能够须要在内存中做一层缓存, 这样子太节约了.

然而能够实现一些变种的, 来实现局部的一一对应, 比方将最近 / 最热门的对应关系存储在 K - V 数据库中, 这样子能够节俭空间的同时, 放慢响应速度.

短 URL 的存储

咱们返回的短 URL 个别是将数字转换成 62 进制, 这样子能够更加无效的缩短 URL 长度, 那么 62 进制的数字对计算机来说只是字符串, 怎么存储呢? 间接存储字符串对等值查找好找, 对范畴查找等太不敌对了.

其实能够间接存储 10 进制的数字, 这样不仅占用空间少, 对查找的反对较好, 同时还能够更加不便的转换到更多 / 更少的进制来进一步缩短 URL.

短码平安问题

依照算法从 0 -61 都是 1 位字符,而后 2 位、3 位 … 这样的话很容易被人发现法则并进行攻打,当然进攻伎俩很多,申请签名之类的平安验证伎俩不在本文探讨范畴内。

首先计数器能够从一个比拟大的随机两头值开始,比方从 10000 开始计数,他的 62 进制是 2Bi 3 位的字符串;

而后采纳一些校验位算法(比方 Luhn 改良一下),计算出 1 位校验位拼接起来,4 位短码,这样能够排除肯定的平安危险;

再加点平安料的话,能够在 62 进制的转换过程中把排序好的 62 个字母数字随机打乱,比方 ABCD1234 打乱成1BC43A2D, 转换的 62 进制也就更难 hack 了;

最初如果仍不释怀,还能够在某些地位(比方 1,3,5)插入随机数,让人无奈看出法则来也能够达到良好的成果。

同一长网址短 url 是否应该雷同问题

发号策略中, 是不判断长地址是否已转过, 所以造成后果就是一长对多短, 有人说节约空间, 建设一个长对短的 map 存储即可, 然而用 map 存储自身就是节约大量空间, 甚至是用大空间换小空间, 这就要思考是否真有必要做一一对应, 不能一对多;

最简略计划: 建一个长对短的 map, 空间换空间,

更好的计划: 用 map 存储 ” 最近 ” 生成的长对短关系, 一小时过期机制实现 LRU 淘汰

长对短流程:

  1. 这个“最近”表中查看一下,看长地址有没有对应的短地址
  2. 有就间接返回,并且将这个 key-value 对的过期工夫重置为一小时
  3. 如果没有,就通过发号器生成一个短地址,并且将这个“最近”表中,过期工夫为 1 小时

一个地址被频繁应用 ,那么它会始终在这个 key-value 表中, 总能返回当初生成那个短地址,不会呈现反复的问题。如果它应用并不频繁,那么长对短的 key 会过期,LRU 机制主动就会淘汰掉它。

这样在空间和发号数量之间获得了一个均衡,此处也应该看具体的业务需要来,是否会存在一对多的状况。比方下单未领取,给用户发短信召回,短信内的短 url 外面存在用户昵称,订单号等个性化信息,即不须要减少这一逻辑环节了。

高并发

如果间接存储在 MySQL 中, 当并发申请增大, 对数据库的压力太大, 可能会造成瓶颈, 这时候是能够有一些优化的.

缓存

下面 保障长短链接一一对应 中也提到过缓存, 这里咱们是为了放慢程序处理速度.

能够将热门的长链接 (须要对长链接进来的次数进行计数), 最近的长链接(能够应用 redis 保留最近一个小时的) 等等进行一个缓存, 保留在内存中或者相似 redis 的内存数据库中, 如果申请的长 URL 命中了缓存, 那么间接获取对应的短 URL 进行返回, 不须要再进行生成操作.

批量发号

每一次发号都须要拜访一次 MySQL 来获取以后的最大号码, 并且在获取之后更新最大号码, 这个压力是比拟大的.

咱们能够每次从数据库获取 10000 个号码, 而后在内存中进行发放, 当残余的号码有余 1000 时, 从新向 MySQL 申请下 10000 个号码. 在上一批号码发放完了之后, 批量进行写入.

这样能够将对数据库继续的操作移到代码中进行, 并且异步进行获取和写入操作, 保障服务的继续高并发.

分布式

下面设计的零碎是有单点的, 那就是发号器是个单点, 容易挂掉.

能够采纳分布式服务, 分布式的话, 如果每一个发号器进行发号之后都须要同步给其余发号器, 那未必也太麻烦了.

换一种思路, 能够有两个发号器, 一个发单号, 一个发双号, 发号之后不再是递增 1, 而是递增 2.

类比可得, 咱们能够用 1000 个服务, 别离发放 0 -999 尾号的数字, 每次发号之后递增 1000. 这样做很简略, 服务相互之间根本都不必通信, 做好本人的事件就好了.

转自

https://www.kancloud.cn/marti…
欢送关注哦

正文完
 0