高并发系统设计 -- 粉丝关注列表如何设计

news2024/11/15 10:19:29

粉丝关注列表如何设计和落地

业务场景

在这里插入图片描述

上图我们简称relation页。relation页展示用户的关系相关信息,包含两个子页面:

  1. follower页,展示关注该用户的所有用户信息。
  2. attention页,展示该用户关注的所有用户信息

主要操作

用户可以为自己增加,删除attention,即关注某个其他用户或者对其他用户取消关注。可以删除follower,即取消其他某个用户对自己的关注。

业务特点

  1. 海量的用户数据。亿级的用户数量,每个用户千级的帖子数量,平均千级的follower/attention数量。
  2. 高访问量。每秒十万量级的平均页面访问,每秒万量级的帖子发布。
  3. 用户分布的非均匀。部分用户的帖子数量/follower数量,相关页面访问数量会超出其他用户一到几个数量级
  4. 时间分布的非均匀分布,某个用户可能突然在某个时间成为热点用户,其follower可能徒增数个量级

一个典型社交类系统的典型特性归结为三个关键词:大数据量,高访问量,非均匀性

relation存储层设计

最简单的就是基于DB存储,只需要两张表即可。

table_relation表:id主键,关注者id,被关注者id

table_user_info表:id主键,用户信息(头像,名称,注册时间,大v认证,手机号等信息)

所以我们这里只需要查询两张表就可以查询出关注,粉丝和用户信息:

select count(1) from table_relation where 关注者id = 'xx';
select count(1) from table_relation where 被关注者id = 'xx';
select * from table_user_info where id = 'xx';

随着用户数量的增多,table_relation和table_user_info表的行数增多。千万,亿级用户,每个用户相关关系百级,那么就需要水平切分。

对于某一个用户的信息查询,首先根据userId计算出它的数据在哪一个分片,再在对应的分片info表里查询到相关数据。userId分片的映射关系有多种方式,例如:hash取模,userId字段的某几个特殊位,一致性hash等等。

这里有一个问题。table_relation根据follower进行拆分,查询某个用户关注的人很容易,因为相同的followerId的数据一定分布在相同的分片上(我关注了谁)。但是一旦需要查询谁关注了某个用户(谁关注了我),这样查询需要路由到所有分片上进行,因为相同的attentionId的数据分散在不同的分片上,查询效率低。由于查询follower和attention的访问量是相似的,所以无论根据followerId还是attentionId进行拆分,总会有一般的场景查询效率低下。

所以针对上述问题,进行垂直拆分,分为follower表和attention表,分表记录某个用户的关注者和被关注者,接下来在对follower表和attention表分别基于userId进行水平拆分。

follower表:主键id,userId,followerId

attention表:主键id,userId,attentionId

这个时候实现查询:

select count(1) from table_follower_xx where userId = 'xx';
select count(1) from table_attention_xx where userId = 'xx';
// 查询用户
select * from table_user_info where userId in (xx);

上述三条语句,前两条可以落在相同的分片上,DB操作次数为两次,但最后一条仍需要查询多次DB。

同时,进行垂直拆分的时候,如果我们要写DB,就提升了一倍。而且上述操作仍然需要count查询,即使在userId上建立了索引仍然会有问题存在:

  1. 对于某些用户,他们被很多人关注(比如明星几千万的关注),他们在follower表进行count查询的时候,需要在userId上扫描的行数仍然很多,我们称这些用户为热点用户。每一次展示热点用户的关注者数量的操作都是低效的。另一方面,热点用户由于被很多用户关注,他的timeline页面会被高频的访问,使得原本低效的展示操作总是被高频的访问,性能风险进一步扩大。
  2. 当某一个用户的follower比较多的时候,通常会在relation页面里无法一页展示完,因此要进行分页处理,每一页显示固定数量的用户,然而DB实现分页的时候,扫描效率随着offset增加而增加,使得这些热点用户的relation页展示到最后几页的时候变得低效。
  3. 用户详细信息的展示,每次展示relation页面的时候,需要对每个follower或者attention分别查询info表,使得info的查询服务能力无法随着info分片线性增加。被attention限制住了。

引入缓存

针对上面的问题,我们这里就需要引入缓存来解决。

同时,在DB层面,我们可以在userInfo表的信息中,将每个用户的关注者和被关注者的数量存入DB,这样一来,对于timeline页和feed页的relation摘要展示仅仅通过查询userInfo表即可完成。

同时可以在缓存中也添加上关注者和被关注者的数量。一些大V账号,我们也可以进行服务器端的本地缓存进行二级缓存

引入缓存之后的业务操作实现方式也相应做了调整:

  1. 某用户timeline/feed页面的relation摘要信息展示:展示方式首先分局用户作为key查询缓存,未命中的时候,再查询DB。
  2. 某用户relation页面详细信息展示,分为两个子页面:follower列表展示和attention展示:
    • 同样先查询follower和attention的缓存,对于频繁被查询的热点用户,它的数据一定在缓存中,由此将DB数据量最多,访问频度最高的用户档在缓存外。
    • 对于每个用户的info信息,热点用户由于被更多的用户关注,他更有可能在详情页面被查询到,所以这类用户总在本地缓存中能够被查询到,本地缓存设置一个不长的过期时间。

热点用户需要考虑的实时性问题

对于热点用户的follower详情页,由于热点用户过长的缓存list,它们每次被查询都有极高的网络传输,同时因为热点,查询频率也更高,加重了网络负担。info查询中follower和attention的数量随时变化着,为了使得查询的数值实时,系统需要在尽量间隔短的时间重新进行count,对于热点用户,如果期望实现秒级数据延迟,那么意味着每秒需要对百万甚至千万级别的数据进行count。如何解决这些动态变化着的数据的大访问量,实时性成为挑战。

新增的用户是放在最前面的,往往我们最前面的N页查询是比较频繁的,占总查询的百分之99.

我们就可以去单独分出来一个list页,在redis中,我们把新增的关注用户放在list页,我们可以直接去查询这块新增数据。

剩下后面的数据的话,我们可以去查询redis list查询,也可以去查询db。

db层面做sql优化,时间会短一点,同时我们可以查询后面用户的数据。我们也可以存在list中,时间设置的稍微短一些,这样方便。

当需要查看某个用户的relation详情页时,涉及对follower和attention列表的分页查询。通常单个用户的关注的人数量有限,绝大用户在1000以内,且每次查询对第一页查询的频率远高于后面的页,那么无论直接将列表存入DB或者是缓存中,都能做到较高的吞吐量。但是对于热点用户的follower,情况就比较复杂一点:follower的数量是不可控的,即使是小概率的翻页操作,对于follower很多热点用户,仍然是高访问量的操作;且每次翻页的扫描成本很高。单个分布式缓存的value列表无法承载过长的follower列表。

针对热点用户的follower列表查询问题,采用基于增量化的实现辅助解决。首先,同一个follower列表的前N页(假设5页)的访问频率占用到总访问量的绝大部分。而前N页的follower个数是常数个;其次follower列表的展示以follow时间进行排序,最近加入的follower通常排在最前面,即增量化模块的最新数据最有可能放在首N页。作为增量化的消费者每次拉取的最近N页条变更事件直接存入热点用户的follower缓存中。

最后最重点的一个问题,如果大V上了一个综艺或者拍了一个电视剧,短时间内突然爆火。导致粉丝关注越来越多从几百万或者一千万直接干到四五千万了,这时候要怎么办呢?如果把他的关注列表和粉丝列表都放在和普通用户的关注列表和粉丝列表一个分片下,是会影响正常用户的性能的,所以我们要怎么做呢?

这里我们如果碰到这种大V了,我们可以进行数据迁移,把大V单独放在一个放到一个分片上,这样就不会影响正常用户的操作了。

由此我们可以看到,微博的大v认证是从产品的角度解决是技术的问题,是一举两得的解决方案,微信红包的抢拆分离也是同样的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/174009.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数论之欧拉筛法(含朴素筛选、埃式筛选详细代码)

文章目录前言朴素筛法(纯暴力,O(n^2^))埃式筛法(找出合数来确认质数, O(n*log(logn)))欧拉筛法(线性筛选,O(n))参考文章前言 在学习Acwing c蓝桥杯辅导课第八讲数论-1295. X的因子链…

Linux常用命令——tcpdump命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) tcpdump 一款sniffer工具,是Linux上的抓包工具,嗅探器。 补充说明 tcpdump命令是一款抓包,嗅探器工具,它可以打印所有经过网络接口的数据包的头信息,…

【MySQL】CentOS7 卸载以及安装 MySQL 详细流程

一、卸载 MySQL 查看 MySQL 安装版本 mysqladmin --version通过 rpm 查找 MySQL rpm -qa|grep -i mysql查看 MySQL 运行状态 systemctl status mysqld.service关闭 MySQL 服务 systemctl stop mysqld.service通过 yum remove 删除 MySQL 安装包 把上面所有的安装包挨个删除…

用友U8和旺店通·企业奇门单据接口对接

对接系统旺店通企业奇门旺店通是北京掌上先机网络科技有限公司旗下品牌,国内的零售云服务提供商,基于云计算SaaS服务模式,以体系化解决方案,助力零售企业数字化智能化管理升级。为零售电商企业的订单管理及仓储管理提供解决方案&a…

Java寒假作业——编程题

二、编程题(ACM模式)1-2题1 (2022蚂蚁金服)一个字母可以拆分成两个字母表顺序的前一个字母,例如,b可以拆分成aa,c可以拆分成bb。打印出最短的可以拆分成 K 个 a 的字符串,字母顺序无…

Spring的三种装配机制(XML、JavaConfig、自动装配)

Spring中bean有三种装配机制一、在xml中显示装配二、 在java中显式装配&#xff0c;都需要在Config配置类重写三、自动装配1. xml方式自动装配2. 使用注解装配等于依赖关系注入&#xff0c;即一个Bean作为属性&#xff0c;装配到另一个Bean。 一、在xml中显示装配 <!--1. …

GCC是什么

GCC是什么 说到 GCC&#xff0c;就不得不提 GNU&#xff0c;“GNU”是“GNUs Not Unix!”&#xff08;GNU并非Unix&#xff01;&#xff09;的首字母递归缩写&#xff0c;中文名“革奴计划”。GNU 计划的最终目标是打造出一套完全自由&#xff08;即自由使用、自由更改、自由发…

Java基础之《netty(28)—TCP粘包拆包原理》

一、基本介绍 1、TCP是面向连接的&#xff0c;面向流的&#xff0c;提供高可靠性服务。收发两端&#xff08;客户端和服务器端&#xff09;都要有一一成对的socket&#xff0c;因此&#xff0c;发送端为了将多个发给接收端的包&#xff0c;更有效的发给对方&#xff0c;使用了…

C++语法复习笔记-第6章 c++指针

文章目录1. 计算机内存1. 储存层次2. 内存单元与地址3. 指针定义2. 左值与右值1. 数组与指针1. 概念3. C中的原始指针1. 数组指针与指针数组2. const pointer 与 pointer to const3. 指向指针的指针4.关于野指针4.1 指向指针的指针4.2 NULL指针4.3 野指针5. 指针的基本运算5.1 …

MySQL 批量插入

文章目录MySQL批量插入10w条数据创建表创建函数创建存储过程调用存储过程MySQL批量插入10w条数据 创建表 创建emp&#xff08;部门&#xff09;表 创建dept&#xff08;员工&#xff09;表 创建函数 创建rand_num函数&#xff0c;随机生成部门编号&#xff0c;保证部门编…

Spark RDD算子

文章目录Spark RDD算子一、RDD 转换算子1、Value 类型(1) mapSpark RDD算子 RDD 方法也叫做RDD算子&#xff0c;主要分为两类&#xff0c;第一类是用来做转换的&#xff0c;例如flatMap()&#xff0c;Map()方法&#xff0c;第二类是行动的&#xff0c;例如&#xff1a;collenc…

Spring Security in Action 第八章 配置授权:api授权

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;以实战为线索&#xff0c;逐步深入SpringSecurity相关知识相关知识&#xff0c;打造完整的SpringSecurity学习步骤&#xff0c;提升工程化编码能力和思维能力&#xff0c;写出高质量代码。希望大家都能够从中有所收获&#…

并查集(Java实现)

基本实现 任务&#xff1a; 维护多个不相交的集合&#xff0c;支持两种操作&#xff1a;合并两个集合&#xff0c;查询一个元素所在的集合。 说明&#xff1a; 维护一个森林&#xff0c;每一棵树都代表一个集合&#xff0c;树根元素为这个集合的代表元。利用数组father[]查询记…

[标准库]STM32F103R8T6 串口的收发

前言 这篇记录一下怎么调用标准库的函数来初始化一个串口&#xff0c;并调库实现发数据和收数据&#xff0c;以及串口收中断的使用。 越往深处学习越感觉其实32就是一个功能更加齐全和强大的MCU&#xff0c;其实跟51没有什么本质上的区别。很多设置的地方都是同质化的。比如需…

JVM知识点整理(整理中)

JVM知识点整理1、JVM与java体系结构1.1、java的体系结构1.2、JVM1.2.1、从跨平台的语言到跨语言的平台1.2.2、常用的JVM实现1.2.3、JVM的位置1.2.4、JDK、JER、JDK1.2.5、JVM的整体结构1.2.6、java代码的执行流程1.2.7、JVM的代码模型1.2.8、JVM的生命周期2、类加载子系统2.1、…

ARM NandFlash 介绍

一、NandFlash 的接口 1、Nand 的型号与命名 (1) Nand 的型号命名都有含义&#xff0c;就拿 K9F2G08 来示例分析一下&#xff1a;K9F 表示是三星公司的 NandFlash 系列。2G 表示 Nand 的大小是 2Gbit&#xff08;256MB&#xff09;。08 表示 Nand 是 8 位的&#xff08; 8 位…

员工入职管理系统|员工管理系统|基于SpringBoot+Vue的企业新员工入职系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

SICTF2023 WP

前言 新年前的最后一场比赛&#xff0c;感谢shenghuo2师傅提供的misc和密码的wp&#xff0c;把misc和密码ak了&#xff0c;太强了 web 兔年大吉 源码 <?php highlight_file(__FILE__); error_reporting(0);class Happy{private $cmd;private $content;public function _…

Registration Center

CAP●一致性(Consistency)&#xff1a;所有节点在同一时间具有相同的数据&#xff1b;●可用性(Availability) &#xff1a;保证每个请求不管成功或者失败都有响应&#xff1b;某个系统的某个节点挂了&#xff0c;但是并不影响系统的接受或者发出请求。●分隔容忍(Partition to…

python循环语句

Python循环语句 文章目录Python循环语句一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.While循环结构2.While无限循环3.For循环语法4.break语句和continue语句一、实验目的 掌握循环结构的语法 二、实验原理 Python中的循环语句有 for 和 while。 Python…