如何设计一个像百度短网址同样的服务,一个生成短网址、将短网址定向到原始URL的服务。redis
首先,须要一个一对一的映射表,去获取短URL,而后根据他恢复出完整的URL。这将会涉及到将这些数据保存到数据库。数据库
咱们要考虑下面这些问题:服务器
咱们假设咱们设计的系统能够提供超过10000亿的URL,若是咱们以从62个字符[a-z,A-Z,0-9]
中选取n
个做为短网址,咱们能够存储62^n
个网址。因此,在知足条件的状况下,咱们选择最小的n
。对于咱们的需求来讲,咱们取n=7
,这样能够提供短网址的个数是62^7 ~= 35000亿
。分布式
一切从简,咱们假定短网址相似http://dwz.com/<alias_hash>
,其中alias_hash
是一个计算过得字符串。函数
最开始呢,咱们把全部的映射存到一个单机数据库,直接生成一个随机的长度为7的字符串alias_hash
做为映射中的键ID
。性能
因此,咱们只须要存储<ID, URL>
。当使用者输入完整网址http://www.google.com
,系统生成一个像abcd123
同样的长度为7的随机字符串做为ID
,存储到数据库中就是<abcd123, http://www.google.com>
。google
使用的时候,访问http://dwz.com/abcd123
,系统搜索IDabcd123
,找到后重定向到http://www.google.com
。url
咱们不能保证经过长URL生成alias_hash
的惟一性。生成alias_hash
的时候可能会发生冲突(2个长URL映射到同一个短URL),但咱们须要每一个长URL生成的短URL都是惟一的,这样咱们才能根据短URL定位到惟一的长URL,可是计算alias_hash
的函数是单向函数。设计
这里是否是单向函数其实不重要,咱们不须要根据短网址再次作计算,只须要搜索,不知道做者为何这么写。
一个很简单但依然高效的方案,创建这样的数据表:code
Table Tiny_Url( ID : int PRIMARY_KEY AUTO_INC, Original_url : varchar, Short_url : varchar )
其中自增主键ID
用来作这样的转换:(ID, 10) <==> (short_url, BASE)
。使用者随时插入一个长URL,会返回最新的插入ID
,把他转化为短URL标识,保存这个短URL标识,并返回给使用者。
ID
和短URL的互相转换)string idToShortURL(long int n) { // Map to store 62 possible characters char map[] = "abcdefghijklmnopqrstuvwxyzABCDEF" "GHIJKLMNOPQRSTUVWXYZ0123456789"; string shorturl; // Convert given integer id to a base 62 number while (n) { shorturl.push_back(map[n%62]); n = n/62; } // Reverse shortURL to complete base conversion reverse(shorturl.begin(), shorturl.end()); return shorturl; } // Function to get integer ID back from a short url long int shortURLtoID(string shortURL) { long int id = 0; // initialize result // A simple base conversion logic for (int i=0; i < shortURL.length(); i++) { if ('a' <= shortURL[i] && shortURL[i] <= 'z') id = id*62 + shortURL[i] - 'a'; if ('A' <= shortURL[i] && shortURL[i] <= 'Z') id = id*62 + shortURL[i] - 'A' + 26; if ('0' <= shortURL[i] && shortURL[i] <= '9') id = id*62 + shortURL[i] - '0' + 52; } return id; }
若是咱们的服务要处理大量数据,分布式存储能够提升咱们的吞吐量。思路也很简单,计算出原始URL的hash值,而后去对应的及其存储,而后就和单机状况同样了。一般使用一致性hash路由到集群中对应的节点。
下面的例子是伪代码:
hash_val
。hash_val
定位一致性hash环上的机器。idToShortURL()
获取短URLhash_val
和短URL做为咱们最终短URL(长度=8),返回给使用者。hash_val
。hash_val
定位到机器。
这里可能有问题,坐着的意思多是最多62台机器,应该是第一个字符是标识机器的,值多是
[a-z,A-Z,0-9]
中的一个。
看了评论,这里有个问题,若是某台机器挂了,摘掉以后,须要将该机器上的数据复制到一致性hash环上的下一台机器的时候,会发生冲突,因此仍是使用redis之类的生成全局惟一的
ID
,直接落到环上对应的机器,这样设计更好。
这里我想深刻讨论的是使用GUID
(全局惟一标识)做为ID
,与增量ID
相比有哪些优缺点?
若是你深刻insert/query
处理语句的话,你会发现使用随机字符串做为ID
可能会牺牲一小部分性能。特别是当已经有无数的记录的时候,插入是很昂贵的操做,数据库须要寻找合适的页存储插入的ID
。可是,使用增量的ID
,插入会简单不少,直接找到最后的页就好了。