当前位置:首页 > 问答 > 正文

用Redis搞微博那事儿,实时监控数据流咋整才顺手

用Redis来处理微博这种海量实时数据流,核心思路就是把它当成一个高速运转的“数据枢纽”和“临时工作台”,避免所有请求都直接去压垮后面更复杂、更慢的数据库,想做得顺手,关键得在几个环节上用对劲儿。

得把数据流的“入口”弄得又快又稳,用户一发微博,一点赞,一转发,这个动作产生的事件就像一滴水,瞬间涌进来,这时候,绝对不能让它直接去写MySQL这种关系型数据库,因为磁盘IO操作再优化也是有延迟的,微博团队的做法(参考“TimYang”等技术博客的分享)是,先用一个非常轻量级的消息队列把事件接住,Redis的List结构或者更高效的Streams结构(Redis 5.0之后)就是干这个的绝佳选择,生产者(比如发布微博的后端服务)只管把事件信息,用户A点赞了微博B”,简化成一个JSON字符串,然后快速地“LPUSH”或者“XADD”进Redis的某个特定队列里,这个操作是内存级别的,速度极快,能瞬间响应前端请求,用户感觉上就是“秒赞”,这就完成了第一步:削峰填谷,把瞬间的高并发请求先化解成平稳的数据流。

就得有另一组“工人”(消费者服务)不停地从这个队列里“RPOP”或者用阻塞的方式读取新消息,这些工人服务拿到消息后,才开始干重活,一条新微博发布的事件被消费了,工人要做几件事:一,把微博正文等关键数据异步写入MySQL做持久化;二,也是更体现实时性的一步,更新各种计数器和粉丝的时间线。

计数器更新是Redis的拿手好戏,微博的转发数、评论数、点赞数,这些数字每秒钟都可能变化成千上万次,用Redis的Hash结构就特别顺手,可以为每条微博创建一个Hash,里面存着 like_count、comment_count、repost_count 这几个字段,每次有点赞事件过来,工人服务就对这个微博ID对应的Hash里的 like_count 执行HINCRBY命令,让它加1,这个操作也是内存计算,速度微秒级,能完美支撑实时变化,这就是Redis作为高速计数板的角色。

再说说最难搞的“时间线”(Timeline),你关注了很多人,怎么一刷新就能看到他们最新发布的微博?如果每次都去数据库里查你所有关注的人,然后按时间排序,那数据库直接就跪了,经典的“推模式”(Fan-out)就派上用场了,当某个大V发布一条新微博时,工人服务除了存数据库,还会做一件事:立刻查出这个大V的所有粉丝ID,然后为每一个粉丝的“个人时间线”(通常用一个Redis的Sorted Set实现)里,插入这条新微博的ID,并以发布时间戳作为分数(Score),Sorted Set能自动按分数(时间戳)排序,这样每个粉丝的时间线里天然就是按时间倒序排好的微博ID列表,当用户刷新首页时,后端服务只需要根据用户ID,到他对应的Sorted Set里,用ZREVRANGE命令快速取出最新几十条微博ID,然后再根据这些ID去Redis缓存或者数据库里批量取出微博内容(这又用到了Redis的Hash或String结构做缓存),拼装好返回给前端,这个过程非常快,因为主要操作都是在内存里完成的,对于粉丝量巨大的大V,这种“推”的模式在发微博时会有“写风暴”问题,所以业界通常会采用“推拉结合”的混合模式,但核心思想依然离不开Redis的Sorted Set来维护有序时间线。

实时监控和排行榜也离不开Redis,比如要做一个“一小时热门话题榜”,可以用Redis的Sorted Set,以话题名为成员,一小时内该话题的讨论量(或搜索量)作为分数,每次有相关事件发生,就ZINCRBY一下对应话题的分数,要查榜单时,直接ZREVRANGE命令取前N名,又快又准,整个数据流从产生到被消费、计数、分发、排序,Redis在其中扮演了消息队列、高速缓存、计数器和排序引擎多个角色,通过一系列巧妙的数据结构组合,让实时监控和数据流转变得非常“顺手”。

用Redis搞微博那事儿,实时监控数据流咋整才顺手