如何用学过的数据结构和算法实现一个短网址系统
短网址:长网址->短网址,点击短网址->原始网址
类似中介
如何实现?
第一种:哈希算法:无论多长字符串都能转为固定哈希值
用哪个哈希算法?我们只关心计算速度和冲突概率->MurmurHash算法-》2种长度哈希值:32bits,128bits,选择32bits,计算得到哈希值,拼上网址服务域名-》t.cn/15213453
还是长,怎么更短?修改哈希值表示方法:10进制转为62进制:t.cn/cgSqq,为什么62进制?0-9,a-z,A-Z,62个字符
有哈希冲突问题,怎么解决?冲突了2个原始网址->一个短网址
保存短网址跟原始网址对应关系
-
一个短网址出现,在MySQL中找,没有就不冲突,建立对应关系,存储到MySQL;
-
找到了不一定冲突,如果MySQL原始网址跟我们正在处理原始网址一样,说明有人请求过这个原始网址的短地址了,我们直接使用
-
如果MySQL原始网址跟我们正在处理原始网址不同,冲突了
-
冲突了怎么办?
-
给原始网址拼接一串特殊字符,比如"[DUPLICATED]",然后重新计算哈希值
-
这时候冲突概率很低,如果真出现了怎么办?
- 换一串特殊子串,再计算哈希值,把哈希值和原始网址拼接存储在MySQL
-
-
如果原始网址有拼接特殊字符,先将特殊字符去掉,然后返回给浏览器
-
-
如何优化哈希算法生成短网址性能?
-
MySQL数据很多,查找就慢了,怎么解决?
-
给短网址添加B+树索引
-
还能再提高吗?
-
分析:跟数据库两次交道:短网址->原始网址对应关系,新生的短网址和原始网址存储到数据库
-
数据库和应用服务部署在两个独立的服务器或虚拟服务器,2条SQL语句执行就需要2次网络通信,这种IO通信耗时以及SQL语句执行,才是整个短网址服务性能瓶颈所在,所以减少SQL语句才是问题所在,那怎么办?
-
给数据库中短网址添加唯一索引,有新的原始网址需要生成短网址时,不先拿生成的短网址在数据库判重,直接将生成的短网址和对应原始网址存储到数据库,如果能正常写入,没有违反唯一索引,没有冲突
-
如果违反了,重新执行"查询,写入"过程,SQL反而增加了,但是大部分情况不会冲突-》1条SQL
-
布隆过滤器优化SQL次数
- 我们把已经生成的短网址,构建成布隆过滤器。我们知道,布隆过滤器是比较节省内存的一 种存储结构,长度是 10 亿的布隆过滤器,也只需要 125MB 左右的内存空间。 当有新的短网址生成的时候,我们先拿这个新生成的短网址,在布隆过滤器中查找。如果查 找的结果是不存在,那就说明这个新生成的短网址并没有冲突。这个时候,我们只需要再执 行写入短网址和对应原始网页的 SQL 语句就可以了。通过先查询布隆过滤器,总的 SQL 语 句的执行次数减少了。
-
-
-
-
第二种:ID生成器
我们可以维护一个 ID 自增生成器。它可以生成 1、2、3…这样自增的整数 ID。当短网址服务接收到一个原始网址转化成短网址的请求之后,它先从 ID 生成器中取一个号码,然后将其转化成 62 进制表示法,拼接到短网址服务的域名(比如t.cn/)后面,就形成了最终的短网址。最后,我们还是会把生成的短网址和对应的原始网址存储到数据库中。
-
相同的原始网址可能会对应不同的短网址
- 第一种处理思路是不做处理。听起来有点无厘头,我稍微解释下你就明白了。实际上,相同的原始网址对应不同的短网址,这个用户是可以接受的。在大部分短网址的应用场景里,用户只关心短网址能否正确地跳转到原始网址。至于短网址长什么样子,他其实根本就不关心。所以,即便是同一个原始网址,两次生成的短网址不一样,也并不会影响到用户的使 用。
- 第二种处理思路是借助哈希算法生成短网址的处理思想,当要给一个原始网址生成短网址的时候,我们要先拿原始网址在数据库中查找,看数据库中是否已经存在相同的原始网址了。如果数据库中存在,那我们就取出对应的短网址,直接返回给用户。 不过,这种处理思路有个问题,我们需要给数据库中的短网址和原始网址这两个字段,都添加索引。短网址上加索引是为了提高用户查询短网址对应的原始网页的速度,原始网址上加索引是为了加快刚刚讲的通过原始网址查询短网址的速度。这种解决思路虽然能满足“相同原始网址对应相同短网址”这样一个需求,但是是有代价的:一方面两个索引会占用更多的存储空间,另一方面索引还会导致插入、删除等操作性能的下降。
-
如何实现高性能的 ID 生成器? 实现 ID 生成器的方法有很多,比如利用数据库自增字段。当然我们也可以自己维护一个计数器,不停地加一加一。但是,一个计数器来应对频繁的短网址生成请求,显然是有点吃力的(因为计数器必须保证生成的 ID 不重复,笼统概念上讲,就是需要加锁)。如何提高 ID生成器的性能呢?关于这个问题,实际上,有很多解决思路。我这里给出两种思路。
- 我们可以给 ID 生成器装多个前置发号器。我们批量地给每个前置发号器发送 ID 号码。当我们接受到短网址生成请求的时候,就选择一个前置发号器来取号码。这样通过多个前置发号器,明显提高了并发发号的能力。 (请人)
1. 第二种思路跟第一种差不多。不过,我们不再使用一个 ID 生成器和多个前置发号器这样的架构,而是,直接实现多个 ID 生成器同时服务。为了保证每个 ID 生成器生成的 ID 不重复。我们要求每个 ID 生成器按照一定的规则,来生成 ID 号码。比如,第一个 ID 生成器只能生成尾号为 0 的,第二个只能生成尾号为 1 的,以此类推。这样通过多个 ID 生成器同时工作,也提高了 ID 生成的效率。
思考
- 如果我们还要额外支持用户自定义短网址功能(http//t.cn/{用户自定部分}),我们又该如何改造刚刚的算法呢?
-
将用户自定义后的短网址和原网址映射关系存入数据库
-
成功,短网址生成成功
-
失败,有冲突,进行判重
- 如果数据库短网址对应原网址相同,则短网址有效,直接使用
- 如果不同,发生冲突,更改自定义网址
-
- 我们在讲通过 ID 生成器生成短网址这种实现思路的时候,讲到相同的原始网址可能会对应不同的短网址。针对这个问题,其中一个解决思路就是,不做处理。但是,如果每个请求都生成一个短网址,并且存储在数据库中,那这样会不会撑爆数据库呢?我们又该如何解决呢?
- 设置原网址唯一约束,新生成短网址插入失败,查询存在的原网址对应的短网址返回