先自报家门:硅谷某独角兽资深架构师,曾面试过200+候选人,擅长算法和系统设计。
个人相关:3年前在公司做过一个短地址服务,目前在线上跑。
基于上述背景,所以我对这个问题很熟悉。同时它也是系统设计面试环节高频出现的题目,因为里面有很多可以考察的地方,能够相对综合的考察候选人的功力。
我先来讲讲这题正确的打开方式:
正确的原理就是通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。第一个使用这个服务的人得到的短地址是http://xx.xx/0 第二个是 http://xx.xx/1 第11个是 http://xx.xx/a 依次往后……相当于实现了一个62进制的自增字段即可。
这题在《系统架构设计 System Design - 第4章》:网站系统,API设计与短网址 里面有比较全面、深入地讲解。
下面我再展开几个子问题来详述:
1. 62进制如何用数据库或者KV存储来做?
其实我们并不需要在存储中用62进制,用10进制就好了。比如第10000个长地址,我们给它的短地址对应的编号是9999,我们通过存储自增拿到9999后,再做一个10进制到62进制的转换,转成62进制数即可。这个10~62进制转换,你完全都可以自己实现。
2. 如何保证同一个长地址,每次转出来都是一样的短地址?
上面的发号原理中,是不判断长地址是否已经转过的。也就是说用拿着百度首页地址来转,我给一个http://xx.xx/abc 过一段时间你再来转,我还会给你一个 http://xx.xx/xyz。
这看起来挺不好的,但是不好在哪里呢?不好在不是一一对应,而一长对多短。这与我们完美主义的基因不符合,那么除此以外还有什么不对的地方?
有人说它浪费空间,这是对的。同一个长地址,产生多条短地址记录,这明显是浪费空间的。那么我们如何避免空间浪费,有人非常迅速的回答我,建立一个长对短的KV存储即可。嗯,听起来有理,但……这个KV存储本身就是浪费大量空间。所以我们是在用空间换空间,而且貌似是在用大空间换小空间。
真的划算吗?这个问题要考虑一下。当然,也不是没有办法解决,我们做不到真正的一一对应,那么打个折扣是不是可以搞定?这个问题的答案太多种,各有各招,我这就不说了。
3. 如何保证发号器的大并发高可用?
上面设计看起来有一个单点,那就是发号器。如果做成分布式的,那么多节点要保持同步加1,多点同时写入,这个嘛,以CAP理论看,是不可能真正做到的。
其实这个问题的解决非常简单,我们可以退一步考虑,我们是否可以实现两个发号器,一个发单号,一个发双号,这样就变单点为多点了?依次类推,我们可以实现1000个逻辑发号器,分别发尾号为0到999的号。每发一个号,每个发号器加1000,而不是加1。这些发号器独立工作,互不干扰即可。
而且在实现上,也可以先是逻辑的,真的压力变大了,再拆分成独立的物理机器单元。1000个节点,估计对人类来说应该够用了。如果你真的还想更多,理论上也是可以的。
4. 具体存储如何选择?
这个问题就不展开说了,各有各道,主要考察一下对存储的理解。对缓存原理的理解,和对市面上DB、Cache系统可用性,并发能力,一致性等方面的理解。
5. 跳转用301还是302?
这也是一个有意思的话题。首先当然考察一个候选人对301和302的理解。浏览器缓存机制的理解。然后是考察他的业务经验。301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。同时对服务器压力也会有一定减少。
但是如果使用了301,我们就无法统计到短地址被点击的次数了。而这个点击次数是一个非常有意思的大数据分析数据源。能够分析出的东西非常非常多。所以选择302虽然会增加服务器压力,但是我想是一个更好的选择。
关于短URL的设计问题,就分享到这里。系统设计所覆盖的面非常广,能够考核的点也非常多,但实际面试时出的题几乎都很大而抽象,这让许多面试者对此都很发怵。
因为应届生通常缺乏工作经验,而大量初级的工程师每天都在重复CRUD,也没机会参与到大型项目的开发中去,所以面对网上大量理论型学习资料,啃起来费劲,又无法融汇到实操层面。
前面我说到,可以听一听《系统架构设计》,除了我上面讲过的网站系统(短URL设计),还有18大日常工作和面试高频出现的系统和知识点:
- 秒杀/订票系统
- IM系统
- 协同文档编辑系统
- 谷歌搜索系统(广告竞价)
- 用户登陆/注册系统
- 数据库
- 分布式计算 MapReduce
- 容器技术(K8S/Docker)
- MLE机器学习系统
……
第一个章节可以试听如何设计Twitter,也可以应用于设计微博、微信朋友圈等新鲜事系统。