当我们在谷歌上搜索或者在亚马逊上购物时,只要在搜索框中打字,网页上就会展示一个或者更多的与搜索词匹配的结果。这个功能叫作自动补全(Autocomplete)、提前输入(Typeahead)、边输边搜(Search-as-you-type)或者增量搜索(Incremental Search)。图-1展示了一个谷歌搜索的示例,在搜索框中输入“dinner”后,谷歌显示了一系列自动补全结果。搜索自动补全是很多产品的一个重要功能。这引出了我们的面试问题:设计一个搜索自动补全系统,也即设计一个能展示“Top k查询词”或者“k个最常被搜索的查询词”的系统。
图-1
1.确定设计的边界
在系统设计面试中,第一步是问足够多的问题来厘清需求。以下是候选人与面试官的对话示例:候选人:系统是只支持匹配查询词的开头部分,还是也支持从其中间开始迚行匹配?
面试官:只支持从头开始匹配。
候选人:系统应该返回多少条自动补全建议?
面试官:5条。
候选人:系统如何确定要返回哪5条建议?
面试官:根据流行度来决定,也即基于历史的查询频率决定。
候选人:系统支持拼写检查吗?
面试官:不,系统不支持拼写检查或者自动纠正。
候选人:查询词都是英文的吗?
面试官:是的。如果最后时间允许,我们也可以讨论一下多语言支持。
候选人:系统允许输入大写字母和特殊字符吗?
面试官:不,我们假设所有的查询词都是小写字母字符。
候选人:有多少用户?
面试官:1000万DAU。
汇总需求:
•快速响应:当用户输入一个查询词时,系统必须快速显示自动补全建议。Facebook工程博客上一篇关于自动补全系统的文章“The Life of a Typeahead Query”显示,系统需要在100毫秒内返回结果,否则会导致用户界面卡顿。
•相关性:自动补全建议应该是与搜索词相关的。
•有序:系统返回的结果必须是根据流行度或者其他排名模型排序的。
•可扩展性:系统必须能应对高访问量。•高可用性:当系统的一部分下线、运行缓慢或者遭遇突发网络故障时,系统应该是可用和可访问的。
1.1 封底估算
•每天有1000万活跃用户(DAU)。•假设平均每人每天会进行10次搜索。
•假设我们使用ASCII字符编码,那么1个字符占用1字节。假设一个查询包含4个单词,每个单词平均有5个字符,那么每个查询就有4×5=20字节。
•每当在搜索框中输入一个字符,客户端就会向后端发送一个请求以获取自动补全建议。平均来说,每个查询会发送20个请求。举个例子,当你输入完“dinner”这个查询词时,下面的6个请求会被发送到后端。
•每秒处理约24,000个查询(QPS)。
10,000,000×10个查询/天×20字符/查询÷24÷3600≈24,000
•峰值QPS为QPS×2,约为48,000。
•假设每天的查询中有20%是新的,这意味着每天有0.4GB新数据被添加到存储中。10,000,000×10个查询/天×20字节/查询×20%=0.4GB
2 高级设计
总体来看,该系统可以分成如下两部分。•数据收集服务:它实时收集用户输入的查询并进行聚合。对于大型数据集,实时处理是不现实的,尽管如此,这是一个好的起点。我们会在13.3节中探索更实际的解决方案。
•查询服务:根据查询的内容或者前缀,返回前5个被频繁搜索的词。