system desing 系统设计(十): twitter或weibo搜索功能设计
1、所有的IT系统,到最后都只剩2个功能:read data和write data!一般情况下,read data远比write data频率更高,毕竟write data的最终目的还是为了read data的嘛!为了加快read data的速度,一般可以有这么几个改经:
- cache:从硬件上将,memory的速度比disk快,所以一些hot data可以放redis做缓存!如果memory都存不下了,可以用SSD盘存warm data。如果SSD也存不下(大概率是存不下的,memory和SSD的价格比普通的机械disk贵多了),最终的cold data存普通的disk!
上面的方式本质上是硬件的分级存储,通过不同速度的硬件存储不同热度的数据来加速read data!除了硬件加速,软件的数据结构和算法也能加速read data(其实就是fast index and search啦),诸如:
- B+树,mysql用的就是这个,比较多的就是InnoDB了,因为支持transaction,data存放leaf node,取data的时候少一次地址的跳转
- skip List:redis sorted set 用的就是这个!
- Inverted index:Elastic search用的就是这个!
总的来说,单机加速数据的read有下面这些办法:
2、树形结构容易理解,现在来看看invert index的思路!最能说明invert index的图如下了:
其实也很容易理解:把tweet分词后,每个词出现的id都祥加记录,然后同一个词的所有id集合起来(mapReduce最适合干这类活了)。如果要查找词在那条tweet出现过,直接找到词的倒排表就行了,所有搜索引擎的查询用的都是inverted index!
(1)现在的第一个问题:词典和倒排表很明显是KV形式的数据,都是怎么存放的了?回想一下我们之前介绍的Big Table:这种KV形式的数据最终都是以文本形式存放在磁盘的file文件的!只不过为了加快读取的速度,刚开始的KV数据会放在内存,达到256M后统一flush到磁盘,然后在memory记录disk中file的地址!Elastic search采用的方式和Big Table比不能说十分相似吧,只能说是一模一样!只不过把内存块或disk的file文件改了个名字,叫:segment!其他的完全一样了!(可以参考文章末尾Elastic官方的介绍)这个就是来自官网的图示了:
(2)既然存储方式和Big Table一摸一样,那index updata的方式是不是和Big Table也一样了?那是必然的啊,都采用append的方式!原因也很简单:
- 如果直接修改原来的record,搜先要找到以前的record,光是查找就要花时间!
- 就算找到了,能直接修改么?万一要倒排表要增加,后面存储的数据岂不是都要往后挪动?这个也耗时啊!
所以还是有index更新,新的index也只能先在内存append,然后找机会flush到disk!查找的时候,需要遍历词典的每一个record,找到所有和key匹配的倒排表,然后把表项都aggregate聚合起来,就能得到whole invert index了!思路是不是也简单了?
(3)有些index更新很快,如果都是append,会导致segment越来越多。每次查找可以的时候就会遍历大量的segment,效率受影响,所以个Big Table类似,无数个小的segment也需要找合适的机会合并成少数大的segment!
了解完Elastic Search的原理后,再理解检索就容易了:直接根据用户query的key在memory和disk查找词典和倒排表就行了!如果是一些热点事件,比如某些famouse start、大V又被爆出离婚啊、出轨啊之类的,这种“坏事传千里”的事情肯定会在短时间内出现大量的tweet,直接在memory和磁盘末尾的segment查找key,就能找到最新、最新的热点事件了!
3、有了这些铺垫,现在来看看twitter是怎么做搜索的!申明,这些资料都来自文章末尾的参考5,貌似是twitter内部大牛分享的!
(1)twitter存放数据:就是按照memory 2%的hot data、ssd 16%的warm data、剩余普通硬盘存放的!
(2)twitter invert index的建立,就是借助lucene的segment搞的!这里就能看出lucene的优势之一:能直接从mysql取数据生成invert index,非常方便!
后来又增加了blender,如是如下:
blender的功能:
- 对用户通过key找到的tweet做进一步处理,比如根据tweet本身的热度(评论、点赞、转发、阅读)+ 是否是朋友关注 + 人气都等指标综合排序。这里也可以推断出earlyBird应该只负责建invert index,其他啥事都不干了;至于怎么在set排序,可以用redis的sorted set功能,底层是基于skip list的!
- 处理个跟踪用户的请求和展示,让这部分功能彻底和invert index decoupling!
(3)对于短时间内发生的热点事件,需要及时建索引,才能让用户查到!建invert index前面介绍过了,但是怎么及时同步lastest tweet到Elastic search了?用kafka呗!
kafka对接mysql的slave,通过binlog方式把增量tweet读取出来,然后直接对接Elastic search建invert index,这样就能及时对lastest tweet建索引啦,用户也能很快的搜到新tweet啦! by the way:Elastic search还能直接对接HDFS读取里面的数据建invert index,是不是很方便了?不过由于HDFS的数据都是离线批量写入的,所以对HDFS建索引也是离线批量建的,流程如下:
(4)tweet table和user table的设计:
我个人更倾向于用hive,原因很简单:
- hive扩展性比mysql好,单集群PB级的数据都是小case
- hive查询慢,可以和Elastic search配合,加快查询速度
4、在实际的业务中,很多地方都要用到分布式自增ID,比如前面提到的short url的生成,tweet自研了snow flake算法,能分布式生成全局的自增ID,如下:
一共64bit=8byte,每个bit都有特定用户,如上所示:最多支持在1024个node上每ms生成409.6w个不会碰撞collision的自增ID【严格讲:是碰撞的概率很小了】!比timestamp+random的方式强多了吧!本质上:还是需要位数够多,才能保证不重复!
5、总结一下各类存储的作用:
参考:
1、https://z.itpub.net/article/detail/0AA856CBD67F89CC0DF45928079122F9 实时es索引
2、https://juejin.cn/post/7103571482306936862 准实时索引底层实现原理
3、https://juejin.cn/post/6995136398365818888 实时增量重建索引
4、https://www.elastic.co/guide/cn/elasticsearch/guide/current/dynamic-indices.html#deletes-and-updates es官方:动态更新索引
5、https://www.6aiq.com/article/1544116316790 实时检索 6700 亿条推文,细谈 Twitter 搜索引擎的演进历程
6、https://www.cnblogs.com/jajian/p/11223992.html 全文搜索引擎Elasic search
7、https://zhuanlan.zhihu.com/p/85837641 分布式ID神器之雪花算法