本文作者 TomorrowWu,原创文章,转载注明出处,博客地址 https://segmentfault.com/u/to... 第一时间看后续精彩文章。觉得好的话,顺手分享到朋友圈吧,感谢支持。

最近在网上看到一个面试题:将长地址转成短地址,下面总结下看到的解决方案

错误回答1

实现一个算法,将场地址转短地址,实现长短一一对应,然后实现逆运算,将短地址换算回长地址

假设定义短地址是100位,通过排列组合,短地址数量=$62^{100}$
(62=10数字+26小写字母+26大写字母),世界上可能存在的长地址一定比这个还多,为所有可能存在的长链接实现一一对应,本身是不可能的,会出现碰撞,多个长链接对应一个短链接

错误回答2

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

没有改变本质(长链接数量远多于长链接),只要长链接够多,必然会碰撞

错误回答3

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

通过hash算出来之后,碰撞后生成多个xx1,xx2,xx3....xx20...的链接,这样查询数据库时进行like查询,判断应该在后面加12,22或23,不确定性很大

错误回答4

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

不是很科学

正确的原理

通过发号策略,每一个过来的长地址,发一个号牌即可,小型系统直接用MySQL的自增索引,大型应用,可以考虑分布式key-value系统做发号器,不停的自增即可

第一个使用这个服务,长地址对应的短地址是xx.xx/0,第二个是 xx.xx/2,第11个是xx.xx/a,相当于实现一个62进制的自增字段

子问题

62进制如何用数据库来做?

在数据库中并不需要存储62进制,存10进制就可以,比如拿到第10000个长地址,短地址对应的编号为9999,从数据库拿到之后,代码中做10进制转62进制的转换

如何保证同一个长地址,每次转出都是同样的短地址

上面发号策略中,是不判断长地址是否已转过,所以造成结果就是一长对多短,有人说浪费空间,建立一个长对短的map存储即可,但是用map存储本身就是浪费大量空间,甚至是用大空间换小空间,这就要考虑是否真有必要做一一对应,不能一对多;

最简单方案:建一个长对短的map,空间换空间,

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

长对短流程:

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

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

如何保证发号器的大并发高可用

上面设计的是单点的,如果做成分布式的,多节点生成短地址,要保证同步,复杂度变高了,相出一个比较简单的思路:可以实现两个发号器,一个发单号,一个发双号,依次类推,可以实现1000个发号器,分别发尾号为0到999的号,每发一个,发号器加1000,发号器独立工作,互不干扰.


tomorrowwu
444 声望27 粉丝

专注服务器开发,对区块链技术保持好奇心