设计 Bit.ly 是一个类似的问题,区别是 pastebin 需要存储的是 paste 的内容,而不是原始的未短化的 url.
怎么处理这个问题?
收集这个问题的需求和范畴。问相关问题来明确用例和约束,讨论一些假设。
我们把问题的范畴定在如下用例:
超出范畴的用例:
约束和假设
状态假设
计算使用
向面试官说明你是否应该粗略计算一下使用情况
简单的转换指南:
2.5 百万 req/s
1 req/s = 2.5 百万 req/m
40 req/s = 1 亿 req/m
400 req/s = 10 亿 req/m
概述一个包括所有重要的组件的高层次设计
深入每一个核心组件的细节
用例:用户输入一段文本,然后得到一个随机生成的链接
我们可以用一个 关系型数据库 作为一个大的哈希表,用来把生成的 url 映射到一个包含 paste 文件的文件服务器和路径上。
为了避免托管一个文件服务器,我们可以用一个拖管的对象存储,比如 Amazon 的 S3 或者 NoSQL文档类型存储。
作为一个大的哈希表的关系型数据库的替代方案,我们可以用 NoSQL键值存储。我们需要讨论选择 SQL 和 NoSQL 之间的权衡。
向面试官阐明你需要写多少代码
paste 表可以有如下结构:
shortlink char(7) NOT NULL
expiration_length_in_minutes int NOT NULL
created_at datetime NOT NULL
paste_path varchar(255) NOT NULL
PRIMARY KEY(shortlink)
我们将在 shortlink 字段和 created_at 字段上创建一个数据库索引,用来提高查询的速度(避免因为扫描全表导致的长时间查询)并将数据保存到内存中,从内存里面顺序读取 1MB 的数据需要大概 250 微秒,而从 SSD 上读取则需要花费 4 倍的时间,从磁盘上则需要花费 80 倍的时间。
为了生成唯一的 url, 我们可以:
def base_encode(num, base=62):
digits = []
while num > 0
remainder = modulo(num, base)
digits.push(remainder)
num = divide(num, base)
digits = digits.reverse
取输出的前7个字符,结果会有63*7个可能的值,应该足以满足在3年内处理3.6亿个短视频的约束:
url = base_encode(md5(ip_address+timestamp))[:URL_LENGTH]
我们将会用一个公开的 REST 风格接口:
curl -X POST --data '{"expiration_length_in_minutes":"60",\"paste_contents":"Hello World"}' https://pastebin.com/api/v1/paste
于内部通信,我们可以用 RPC。
REST API:
curl https://pastebin.com/api/v1/paste?shortlink=foobar
Response:
{
"paste_contents": "Hello World",
"created_at": "YYYY-MM-DD HH:MM:SS",
"expiration_length_in_minutes": "60"
}
因为实时分析不是必须的,所以我们可以简单的 MapReduce Web Server 的日志,用来生成点击次数。
class HitCounts(MRJob):
def extract_url(self, line):
"""Extract the generated url from the log line."""
...
def extract_year_month(self, line):
"""Return the year and month portions of the timestamp."""
...
def mapper(self, _, line):
"""Parse each log line, extract and transform relevant lines.
Emit key value pairs of the form:
(2016-01, url0), 1
(2016-01, url0), 1
(2016-01, url1), 1
"""
url = self.extract_url(line)
period = self.extract_year_month(line)
yield (period, url), 1
def reducer(self, key, values):
"""Sum values for each key.
(2016-01, url0), 2
(2016-01, url1), 1
"""
yield key, sum(values)
为了删除过期的 pastes,我们可以直接搜索 SQL 数据库 中所有的过期时间比当前时间更早的记录,
所有过期的记录将从这张表里面删除(或者将其标记为过期)。