算法实战(五):如何用学过的数据结构和算法实现一个短网址系统?

算法实战(五):如何用学过的数据结构和算法实现一个短网址系统?

短网址服务是如果我们在微博里发布一条带网址的信息,微博会把里面的网址转化为一个更短的网址,只需要访问这个短网址,相当于访问原始的网址,尽管长度不一样,都可以跳转

原始网址:https://github.com/wangzheng0822/ratelimiter4j
短网址:http://t.cn/EtR9QEG

短网址服务整体介绍

短网址服务核心功能是把原始的长网址转化为短网址,还有当用户点击短网址的时候,短网址服务将浏览器重定向为原始网址

当用户点击短网址的时候,短网址服务将浏览器重定向为原始网址,如何实现?浏览器会先访问短网址服务,通过短网址获取到原始网址,再通过原始网址访问到页面

如何通过哈希算法生成短网址?

哈希算法可以将一个不管多长的字符串转化为一个长度固定的哈希值,可以利用哈希算法来生成短网址

在生成短网址的问题上,不需要关心反向解谜的难度,只需要关心哈希算法的计算速度和冲突概率

MurmurHash算法提供了两种长度的哈希值,一种是32bits,一种是128bits,为了最终生成的短网址尽可能短,选择32bits的哈希值,https://github.com/wangzheng0822/ratelimiter4j经过MurmurHash算法计算之后,生成的哈希值是181338494,加上短网址服务的域名,生成了最终的短网址http://t.cn/181338494(http://t.cn是短网址服务的域名)

1 如何让短网址更短?

需要改变一个哈希值的标识方法,就能轻松的把短网址变得更短,可以将10进制的哈希值转化为更高进制的哈希值,这样哈希值更短了。可以将10进制的哈希值转化为更高进制的哈希值,哈希值就变短了,16进制中,用A-E表示10-15,在网址URL中,合法字符有0-9,a-z,A-Z,62个字符,可以将10进制的哈希值转化为62进制,用62进制表示的短网址是http://t.cn/cgSqq

(181338494)10 = (cgSqq)62

181338494 / 62 余数=26(q)

2924814 /62 余数=26(q)

47174 /62 余数=54(S)

760 /62 余数=16(g)

12 /62 余数=12©

2 如何解决哈希冲突问题?

哈希算法无法避免的一个问题就是哈希冲突

保存短网址跟原始网址之间的对应关系,以便后续用户在访问短网址的时候可以根据对应关系查找到原始网址,存储这种对应关系方式有很多,比如自己设计存储系统或者利用现成的数据库,MySQL、Redis,假设短网址与原始网址之间的对应关系存储在MySQL数据库中

当有一个新的原始网址需要生成短网址的时候,利用MurmurHash算法生成短网址,然后拿这个新生成的短网址在MySQL数据库中查找,如果没有找到相同的短网址,表明这个新生成的短网址没有冲突,于是返回给用户,然后将这个短网址与原始网址之间的对应关系存储到MySQL数据库中。如果找到了相同的短网址,也不一定说明冲突了,从数据库中将短网址对应的原始网址也取出来,如果一样,就拿这个短网址直接用,如果不一样,就说明哈希算法发生了冲突,这个时候,可以给原始网址拼接一串特殊字符,比如"[DUPLICATED]",然后再重新计算哈希值,两次哈希计算都冲突的概率不高,假设出现了非常极端的情况,又发生冲突了,再换一个拼接字符串,比如"[OHMYGOD]",再计算哈希值,然后把计算得到的哈希值跟原始网址拼接了特殊字符串之后的文本一并存储在MySQL数据库中

当用户访问短网址的时候,短网址服务先通过短网址,在数据库中查找到对应的原始网址,如果原始网址有拼接特殊字段(通过字符串匹配算法找到),先将特殊字符去掉,再将不包含特殊字符的原始网址返回给浏览器

3 如何优化哈希算法生成短网址的性能?

为了判断生成的短网址是否冲突,需要拿生成的短网址在数据库中查找,非常慢

可以给短网址字段添加B+树索引,或者可以在短网址生成过程中,会执行两条SQL语句,第一个SQL语句是通过短网址查询短网址与原始网址的对应关系,第二个SQL语句是将新生成的短网址和原始网址之间的对应关系存储到数据库,一般情况下,数据和应用服务(只做计算不存储数据的业务逻辑部分)会部署在两个独立的服务器或虚拟服务器上,那两个SQL语句的执行需要两次网络通信,这种IO通信和SQL语句的执行才是整个短网址服务性能瓶颈所在,为了提高性能,必须减少SQL语句,给数据库中的短网址字段,添加一个唯一索引(不止是索引,还要求表中不能有重复的数据)当有新的原始网址需要生成短网址的时候,直接将生成的短网址与对应的原始网址尝试存储到数据库中,如果能将数据正常写入,说明这个新生成的短网址没有冲突,如果数据库反馈违反唯一性索引异常,重新执行“查询-写入过程”,SQL语句执行次数反增,但是大部分情况下,新生成的短网址和对应的原始网址在插入数据库中不会出现冲突,总的SQL语句执行次数大大减少

还可以借助布隆过滤器,将已经生成的短网址构建成布隆过滤器,当有新的短网址生成的时候,先拿这个新生成的短网址在布隆过滤器中查找,如果结果为不存在,说明不冲突,再执行写入短网址和对应原始网页的SQL语句即可

如何利用自增的ID生成器来生成短网址?

维护一个ID自增生成器,可以生成1,2,3……这样自增的整数ID,当短网址服务接受到一个原始网址转化成短网址的请求之后,先从ID生成器中取一个号码,然后将其转化成62进制表示法,拼接到短网址服务的域名(http://t.cn/)的后面,形成了最终的短网址,最后把生成的短网址和对应的原始网址存储到数据库中

1 相同的原始网址可能会对应不同的短网址

每新来一个原始网址就生成一个新的短网址,可能会有两个相同的原始网址生成不同短网址,如何处理?第一种是不做处理,第二种是借助哈希算法生成短网址,给一个原始网址生成短网址的时候,先拿原始网址在数据库中查找看是否已经存在,如果存在,取出对应的短网址直接返回用户,但是需要给数据库中的短网址和原始网址都加上索引,短网址加上索引为了提高用户查询短网址对应的原始网址的速度,原始网址加上索引为了加快通过原始网址查询短网址的速度

2 如何实现高性能的ID生成器?

实现ID生成器方法有很多,比如利用数据库自增字段,或者维护一个计数器,+1+1,但是一个计数器来应对频繁的短网址生成请求,必须要加锁,比较吃力,如何提高ID生成器的性能?可以给ID生成器装多个前置发号器,批量的给每个前置发号器发送ID号码,当我们接受到短网址生成请求的时候,选择一个前置发号器来取号码,通过多个前置发号器,提高了并发发号的能力

第二种是直接实现多个ID生成器同时服务,为了保证每个ID生成器生成的ID不重复,要求每个ID生成器按照一定的规则,来生成ID号码,比如,第一个ID生成器只能生成尾号为0 的,第二个只能生成尾号为1的……,通过多个ID生成器同时工作,提高效率

总结:

短网址服务的两种实现方法:

第一种是通过哈希算法生成短网址,采用MurmurHash算法,并将计算得到的10进制数转化成62进制表示法,第二种是通过ID生成器来生成短网址,维护一个ID自增的ID生成器,给每个原始网址分配一个ID号码,并转成62进制表示法,拼接短网址域名之后,形成短网址

发布了74 篇原创文章 · 获赞 9 · 访问量 9149

猜你喜欢

转载自blog.csdn.net/ywangjiyl/article/details/104902488