本文作者 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淘汰
长对短流程:
- 这个“最近”表中查看一下,看长地址有没有对应的短地址
- 有就直接返回,并且将这个key-value对的过期时间重置为一小时
- 如果没有,就通过发号器生成一个短地址,并且将这个“最近”表中,过期时间为1小时
当一个地址被频繁使用,那么它会一直在这个key-value表中,总能返回当初生成那个短地址,不会出现重复的问题。如果它使用并不频繁,那么长对短的key会过期,LRU机制自动就会淘汰掉它。
如何保证发号器的大并发高可用
上面设计的是单点的,如果做成分布式的,多节点生成短地址,要保证同步,复杂度变高了,相出一个比较简单的思路:可以实现两个发号器,一个发单号,一个发双号,依次类推,可以实现1000个发号器,分别发尾号为0到999的号,每发一个,发号器加1000,发号器独立工作,互不干扰.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。