[LeetCode] Design TinyURL 设计精简URL地址
Note: For the coding companion problem, please see: Encode and Decode TinyURL.
How would you design a URL shortening service that is similar to TinyURL?
Background:
TinyURL is a URL shortening service where you enter a URL such as https://leetcode.com/problems/design-tinyurl and it returns a short URL such as http://tinyurl.com/4e9iAk.
Requirements:
- For instance, "http://tinyurl.com/4e9iAk" is the tiny url for the page
"https://leetcode.com/problems/design-tinyurl". The identifier (the highlighted part) can be any string with 6 alphanumeric characters containing0-9,a-z,A-Z. - Each shortened URL must be unique; that is, no two different URLs can be shortened to the same URL.
Note about Questions:
Below are just a small subset of questions to get you started. In real world, there could be many follow ups and questions possible and the discussion is open-ended (No one true or correct way to solve a problem). If you have more ideas or questions, please ask in Discuss and we may compile it here!
Questions:
- How many unique identifiers possible? Will you run out of unique URLs?
- Should the identifier be increment or not? Which is easier to design? Pros and cons?
- Mapping an identifier to an URL and its reversal - Does this problem ring a bell to you?
- How do you store the URLs? Does a simple flat file database work?
- What is the bottleneck of the system? Is it read-heavy or write-heavy?
- Estimate the maximum number of URLs a single machine can store.
- Estimate the maximum number of queries per second (QPS) for decoding a shortened URL in a single machine.
- How would you scale the service? For example, a viral link which is shared in social media could result in a peak QPS at a moment's notice.
- How could you handle redundancy? i,e, if a server is down, how could you ensure the service is still operational?
- Keep URLs forever or prune, pros/cons? How we do pruning? (Contributed by @alex_svetkin)
- What API would you provide to a third-party developer? (Contributed by @alex_svetkin)
- If you can enable caching, what would you cache and what's the expiry time? (Contributed by @Humandroid)
这道系统设计的题跟之前的算法还是不一样的,代码只是其中的一部分,估计大部分还是要跟面试官侃大山,博主也不太熟悉这类题目,还是照着ztlevi大神的帖子来写吧。
S: Scenario 场景
长URL和短URL的相互转换
N: Need 需求
- QPS (Queires Per Second) 每秒查询数
- 日活用户:100M
- 每日人均使用量:(写)long2short 0.1,(读) short2long 1
- 每日请求量:写 10M,读 100M
- QPS:一天共有86400秒,约100K。写 100, 读 1K
- 峰值QPS:写 200, 读 2K
(千级的量可以用一个单SSD的MySQL机器来处理)
- Storage 存储
- 每天10M个新映射(长URL到短URL)
- 一个映射大约占100B的大小
- 每天1GB,1TB大约能扛三年
对于这种系统来说,存储不是问题。只有像Netflix那样的系统可能会有存储问题。通过SN分析,我们对系统有了一个大框架印象,这个系统可以使用单SSD机器来实现。
A: API 接口
只有一种类型的服务:URLService
- Core (Business Logic) Layer
- Class: URLService
- Interface:
- URLService.encode(string long_url)
- URLService.decode(stirng short_url)
- Web Layer
- REST API:
- GET: /{short_url}, return a http redirect response (301)
- POST: goo.gl method - google shorten URL
Request Body: {url=longUrl} e.g. {"longUrl": "http://www.google.com/"}
Return OK(200), short_url is included in the data
K: Data Access 数据访问
Step 1: Pick a storage structure 选择一个存储结构
- SQL VS NoSQL?
- 需要支持事务Transactions吗?NoSQL不支持事务Transactions。
- 需要Rich SQL Query吗? NoSQL不支持SQL那么多的Query。
- 需要高效开发吗?大多数的网络框架对SQL的支持性非常好,意味着系统不需要太多的代码。
- 需要AUTO_INCREMENT ID吗? NoSQL不支持这个,仅有一个全局卫衣的Object_id。
- 需要高QPS吗?NoSQL有高性能。比如Memcached的QPS可达到百万级,MondoDB可达万级,MySQL只有千级。
- 系统的可伸缩性Scalability有多高?SQL需要开发者写代码去伸缩Scale,而NoSQL自带该功能(Sharding,replica)。
- Answer 回答:
- 不需要 -> NoSQL
- 不需要 -> NoSQL
- 无所谓,因为只有很少的代码 -> NoSQL
- 算法需要AUTO_INCREMENT ID -> SQL
- 写 200,读 2K,不高 -> SQL
- 不高 -> SQL
- System Alogrithm 系统算法
- Hash 函数
long_url => md5/sha1
- md5将一个字符串转为128位,通常用16个字节的十六进制来表示:
http://site.douban.com/chuan -> c93a360dc7f3eb093ab6e304db516653
- sha1将字符串转为160位,通常用20个字节的十六进制来表示:
http://site.douban.com/chuan -> dff85871a72c73c3eae09e39ffe97aea63047094
这两个算法使得哈希值是随机分布的,但是冲突Conflicts无法避免。任何哈希算法都无法避免冲突问题。
- 优点:简单。我们用转换字符串的前6个字符
- 缺点:冲突
解决方法 1. 使用(long_url + timestamp)作为哈希函数的关键字Key。2. 当冲突时,重新生成哈希值(生成的值不同因为时间戳改变了)。
总之,当urls的个数超过十亿个,可能会有大量的冲突使得系统不高效。
- base62
将short_url用62 base标记。6位可以表示62^6 57 billion。
每个short_url表示一个十进制数,可以当作SQL数据库中的AUTO_INCREMENT ID。
class URLService {
public:
URLService() {
COUNTER = ;
elements = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
string longToShort(string url) {
string short_url = base10ToBase62(COUNTER);
long2short[url] = COUNTER;
short2long[COUNTER] = url;
++COUNTER;
return "http://tiny.url/" + short_url;
}
string shortToLong(string url) {
string prefix = "http://tiny.url/";
url = url.substr(prefix.size());
int n = base62ToBase10(url);
return short2long[n];
}
int base62ToBase10(string s) {
int n = ;
for (int i = ; i < s.size(); ++i) {
n = n * + convert(s[i]);
}
return n;
}
int convert(char c) {
if (c >= '' && c <= '') {
return c - '';
} else if (c >= 'a' && c <= 'z') {
return c - 'a' + ;
} else if (c >= 'A' && c <= 'Z') {
return c - 'A' + ;
}
return -;
}
string base10ToBase62(int n) {
string str = "";
while (n != ) {
str.insert(str.begin(), elements[n % ]);
n /= ;
}
while (str.size() != ) {
str.insert(str.begin(), '');
}
return str;
}
private:
unordered_map<string, int> long2short;
unordered_map<int, string> short2long;
int COUNTER;
string elements;
};
Step 2: Database Schema 数据库概要
一个表(id, long_url)。id是主键,通过long_url排序。基本的系统架构为:
Browser <-> Web <-> Core <-> DB
O: Optimize 优化
如何提高响应速度?
- 在网络服务器和数据库之间提高响应速度
使用Memcached来提高响应速度。当获得long_url时,先在缓存中搜索。我们可以把90%的读请求放在缓存当中。
- 在网络服务器和用户浏览器之间提高响应速度
不同的地区使用不同的网络服务器和缓存服务器。所有的地区共享一个数据库用来匹配用户到最近的网络服务器(通过DNS),当他们不在缓存中的时候。
如果我们需要多于一台的MySQL机器?
- 问题:
- 缓存用完了
- 越来越多的请求
- 越来越多的缓存丢失
- 解决方案:
- 垂直切分 Vertical Sharding
- 水平切分 Horizontal Sharding
最好的方式是水平切分。当前的表结构是(id, long_url),哪列可以当作切分关键字。
一个简单的方法是id模块切分。
现在有另一个问题:如何能使多个机器共享一个全局的AUTO_INCREMENT ID?
两种方法:1. 多使用一个机器去维护id。2. 使用zookeeper。都很操蛋。
所以,我们不适用AUTO_INCREMENT ID
好处是将切分关键字当作short_url的第一个字节。
另一种方法是用统一的哈希将循环断成62份。有多少份并没有啥关系,因为可能并没有62台机器(可能有360或其他的)。每台机器都是为循环的一部分的服务负责。
write long_url -> hash(long_url)%62 -> put long_url to the specific machine according to hash value -> generate short_url on this machine -> return short_url
short_url request -> get the sharding key (first byte of the short_url) -> search in the corresponding machine based on sharding key -> return long_url
每当我们增加一台新机器,将最多使用的机器的一半范围放到心的机器中。
更多优化
将中文服务器放在中国,美国的服务器放在美国。使用地理信息当作切分关键字,例如,0是中国的网站,1是美国的网站。
参考资料:
https://discuss.leetcode.com/topic/95853/a-complete-solution-for-tinyurl-leetcode-system-design
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Design TinyURL 设计精简URL地址的更多相关文章
- [LeetCode] Encode and Decode TinyURL 编码和解码精简URL地址
Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ...
- [LeetCode] 534. Design TinyURL 设计短网址
Note: For the coding companion problem, please see: Encode and Decode TinyURL. How would you design ...
- 535 Encode and Decode TinyURL 编码和解码精简URL地址
详见:https://leetcode.com/problems/encode-and-decode-tinyurl/description/ C++: class Solution { public ...
- LeetCode Design TinyURL
原题链接在这里:https://leetcode.com/problems/design-tinyurl/description/ 题目: How would you design a URL sho ...
- [LeetCode] Design HashMap 设计HashMap
Design a HashMap without using any built-in hash table libraries. To be specific, your design should ...
- [LeetCode] Design Twitter 设计推特
Design a simplified version of Twitter where users can post tweets, follow/unfollow another user and ...
- [LeetCode] Design HashSet 设计HashSet
Design a HashSet without using any built-in hash table libraries. To be specific, your design should ...
- [LeetCode] Design Tic-Tac-Toe 设计井字棋游戏
Design a Tic-tac-toe game that is played between two players on a n x n grid. You may assume the fol ...
- LeetCode 622:设计循环队列 Design Circular Queue
LeetCode 622:设计循环队列 Design Circular Queue 首先来看看队列这种数据结构: 队列:先入先出的数据结构 在 FIFO 数据结构中,将首先处理添加到队列中的第一个元素 ...
随机推荐
- C# MVC NPOI导出
前台: <form id="fmexp" method="post" target="_blank"> </form> ...
- js如何获取隐藏的元素的高度
首先,正常情况下,确保div是有高度的. <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
- (译文)学习ES6非常棒的特性——Async / Await函数
try/catch 在使用Async/Await前,我们可能这样写: const main = (paramsA, paramsB, paramsC, done) => { funcA(para ...
- JavaScript简史
JavaScript诞生于1995年. 当时的web正在日益兴起,人们对客户端语言的需求也越来越强烈.当时走在技术革新最前沿的Netscape公司决定开发一种客户端语言,用来处理简单的输入验证. 当时 ...
- Beta冲刺第五天
一.昨天的困难 没有困难. 二.今天进度 1.林洋洋:日程刷新重构. 2.黄腾达:创建协作日程当选择只触发一次时自动填充1,并禁用input. 3.张合胜:修复列表显示日程重复单位的格式化. 三.明日 ...
- 1013团队alpha冲刺日志集合帖
alpha冲刺day1 alpha冲刺day2 alpha冲刺day3 alpha冲刺day4 alpha冲刺day5 alpha冲刺day6 alpha冲刺day7 alpha冲刺day8 alph ...
- Python 双向链表
操作 is_empty() 链表是否为空 length() 链表长度 travel() 遍历链表 add(item) 链表头部添加 append(item) 链表尾部添加 insert(pos, it ...
- 201621123031 《Java程序设计》第3周学习总结
Week03-面向对象入门 1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识点组织起来.请使用工具画出本周学习到的知识点及知识点之间的联系 ...
- 在Eclipse中调用Algs4库
首先下载Eclipse,我选择的是Eclipse IDE for Java Developers64位版本,下载下来之后解压缩到喜欢的位置然后双击Eclipse.exe启动 然后开始新建项目,File ...
- 洛谷 U10783 名字被和谐了
https://www.luogu.org/problem/show?pid=U10783 题目背景 众所周知,我们称g是a的约数,当且仅当g是正数且a mod g = 0. 众所周知,若g既是a的约 ...