[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:

  1. 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 containing 0-9, a-z, A-Z.
  2. Each shortened URL must be unique; that is, no two different URLs can be shortened to the same URL.
  3. 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:

    1. How many unique identifiers possible? Will you run out of unique URLs?
    2. Should the identifier be increment or not? Which is easier to design? Pros and cons?
    3. Mapping an identifier to an URL and its reversal - Does this problem ring a bell to you?
    4. How do you store the URLs? Does a simple flat file database work?
    5. What is the bottleneck of the system? Is it read-heavy or write-heavy?
    6. Estimate the maximum number of URLs a single machine can store.
    7. Estimate the maximum number of queries per second (QPS) for decoding a shortened URL in a single machine.
    8. 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.
    9. How could you handle redundancy? i,e, if a server is down, how could you ensure the service is still operational?
    10. Keep URLs forever or prune, pros/cons? How we do pruning? (Contributed by @alex_svetkin)
    11. What API would you provide to a third-party developer? (Contributed by @alex_svetkin)
    12. 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 = 1;
        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 = 0;
        for (int i = 0; i < s.size(); ++i) {
            n = n * 62 + convert(s[i]);
        }
        return n;
    }

    int convert(char c) {
        if (c >= '0' && c <= '9') {
            return c - '0';
        } else if (c >= 'a' && c <= 'z') {
            return c - 'a' + 10;
        } else if (c >= 'A' && c <= 'Z') {
            return c - 'A' + 36;
        }
        return -1;
    }

    string base10ToBase62(int n) {
        string str = "";
        while (n != 0) {
            str.insert(str.begin(), elements[n % 62]);
            n /= 62;
        }
        while (str.size() != 6) {
            str.insert(str.begin(), '0');
        }
        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

本文转自博客园Grandyang的博客,原文链接:[LeetCode] Design TinyURL 设计精简URL地址

,如需转载请自行联系原博主。

时间: 2024-10-29 17:43:10

[LeetCode] Design TinyURL 设计精简URL地址的相关文章

以不同的观点分析在URL地址设计中常遇到的2个问题

我们知道影响站点的收录排名因素有很多,诸如内容的质量.网站的速度,站点的结构.URL地址的结构等等.很多细节都会对站点日后的优化产生影响,因此我们在站点设计的时候总是会从多方面进行思考.遇到这些问题的时候,我们往往会从各种渠道获得相关的解决方法.但是这些前辈所给我们的建议是否就一定是适合我们的站点呢?笔者认为不然,有的问题还是要有自己的想法,切勿过度的盲从.下面笔者将从不同的观点分析我们在站点url结构设计中容易遇到的2个问题. 疑问一:我们的URL地址是否需要统一? 这个问题我想大多数的SEO

花时间设计URL地址的结构 不要听天由命

URL的设计是一个很复杂的问题,我不能说有什么"正确"的解决方案--其挺类似于其他方面的设计的,有好的URL设计,有糟糕的URL设计,在这两者之间的情况也个个不同--它是主观的.不过这并不意味着不存在用于创建出非常好的URL的最佳做法.我希望我这些年来学到的一些URL设计的最佳做法能够给你留下深刻的印象,并且我会解释为什么我认为使用新的HTML5 javascript的history API来工作是一件很令人兴奋的事情. 是花一些时间来设计你的URL地址的结构.不要把它留给你的框架来决

[LeetCode] Design Search Autocomplete System 设计搜索自动补全系统

Design a search autocomplete system for a search engine. Users may input a sentence (at least one word and end with a special character '#'). For each character they type except '#', you need to return the top 3historical hot sentences that have pref

[LeetCode] Design Excel Sum Formula 设计Excel表格求和公式

Your task is to design the basic function of Excel and implement the function of sum formula. Specifically, you need to implement the following functions: Excel(int H, char W): This is the constructor. The inputs represents the height and width of th

[LeetCode] Design Log Storage System 设计日志存储系统

You are given several logs that each log contains a unique id and timestamp. Timestamp is a string that has the following format: Year:Month:Day:Hour:Minute:Second, for example, 2017:01:01:23:59:59. All domains are zero-padded decimal numbers. Design

URL地址中所暴露出来的网站特点

我喜欢浏览网页时多个心眼.这个心眼需要观察许多元素,每个元素都能提供信息.孤立的一个元素不重要,但是多个元素组成的信息,往往能获得有价值的判断.那么从这些元素说起: 永远不 忽略URL URL是一个重要的信息,职业敏感让分析师绝对不会忽略URL地址的特点.URL反映出网站的蛛丝马迹,直接以例子说话: $URL地址所在的域名 很多人在网上被骗,很大原因就是从来不关注URL所在的域名. 比如:http://www.taobao.ipx32.com/about.html,看似弱智的骗术却让很多 网友

谷歌推出URL地址缩写服务“Goo.gl”

12月15日消息,据国外媒体报道,谷歌本周一进入了一个新的领域,推出一个新的URL地址缩写的服务.这项服务的名称是"Goo.gl". 与目前引人瞩目的TinyURL和Bit.ly等缩写的网络地址不同,Goo.gl不是用户可以通过一个单独的网站进行访问的通用链接的缩写地址.相反,它是建在谷歌产品中的,是从谷歌的浏览器工具条和谷歌的Feedburner RSS服务开始的.这两项服务现在都可以创建连接到内容来源的缩写的Goo.gl地址,同时使用比较少的字符.这对于像在Twitter那样的地方

ios-使用变量语法打开URL地址

问题描述 使用变量语法打开URL地址 有一个变量agencyWebsite和一个标签,应该在点击下面方法的时候打开一个网站. - (void)website1LblTapped { NSURL *url = [NSURL URLWithString:self.agencyWebsite]; [[UIApplication sharedApplication] openURL:url]; } 在编译器的警报: Incompatible pointer types sending UILabel*

urlwatch 1.13发布 监控指定URL地址脚本

urlwatch 1.13这个版本增加了允许urls.txt文件,每一个网站的URL包含POST数据,网页使用HTTP POST请求的支持.从这个版本开始,urlwatch支持Python 3.x及早期的Python 2.x的版本,对于早期的3.2以下版本的Python,你必须安装PyPI"future",因为urlwatch现在需要获取该模块同时访问Web页面带宽利用率将更高. urlwatch 是一个用来监控指定的URL地址的Python脚本,一旦指定的 URL 内容有变化时候将通