当前位置: 首页 > news >正文

什么是读、写扩散?

什么是读、写扩散?

读写扩散的概念

inbox: 收件箱,你收到的消息,即你所关注的人发布的消息。
outbox: 发件箱,你发布的消息。

写扩散(Push)

该方式为每个用户维护一个订阅列表,记录该用户订阅的消息索引(一般为消息ID、类型、发表时间等一些元数据)。每当用户发布消息时,都会去更新其follower的订阅列表。
优点:读很轻。初始化时仅需要读取自己的inbox即可。
缺点:写很重。每发布一个消息,会导致大量的写操作。
注:一般来说,用户发布消息,并不会更新所有followers的订阅列表,仅更新在线followers即可。

读扩散(Pull)

该方式为每个用户维护一个发送列表,记录该用户所有发表过的消息索引。
优点:写很轻,节省空间。用户每发布一条消息,仅需更新自己的outbox。
缺点:读操作很重,计算量大。假设你收听了1k用户,则初始化时,需要从1k个用户的outbox拉取消息,然后计算获得最新的n条消息。

混合模式(Push+Pull)

该方式既为读写扩散的结合,根据用户followers的数量来决定是读扩散还是写扩散。例如followers大于1k的,则使用读扩散,否则使用写扩散。

实际场景

从目前现在网上的一些资料来看,Twitter是写扩散,腾讯微薄是读扩散,新浪微薄则是二者结合。

好,下面我们就结合群聊和朋友圈的场景来看一下读写扩散是怎么使用的。

这里要知道微信每个人的好友有上限,群聊的人数也有上限,所以这就限制了微信的设计复杂度没有新浪微博那样复杂,因为群聊是个耗时的操作,所以可以采用异步队列写扩散的方式来实现消息的发送。

当A用户在群里发送了一条消息(在outbox)以后,会在群中其他所有人的inbox中添加一条这条消息的索引,即使你并没有在线,当离线用户上线的时候,只需要检查自己的inbox列表就能收到“离线消息”。

换句话说就是:消息扩散写到每个人的消息存储(消息收件箱)后,接收者到后台同步数据时,只需要检查自己收件箱即可,同步逻辑跟单聊消息是一致的,这样可以统一数据同步流程,实现起来也会很轻量。

至于为什么不采用读扩散,一是因为一个群聊的人数相对较少,利用写扩散更加合适,二是如果采用读扩散,微信群聊每秒钟都可能有消息产生,那么用户会执行大量的读操作,这个过程并不是完全可靠的,很可能发生数据丢失。

朋友圈例子

下面再看一个微信朋友圈的例子

比如有两个用户小王和Mary。小王和Mary各自有各自的相册,可能在同一台服务器上,也可能在不同的服务器上。现在小王上传了一张图片到自己的朋友圈。上传图片不经过微信后台服务器,而是直接上传到最近的腾讯CDN节点,所以非常快。图片上传到该CDN后,小王的微信客户端会通知微信的朋友圈CDN:这里有一个新的发布(比如叫K2),这个发布的图片URL是什么,谁能看到这些图片,等等此类的元数据,来把这个发布写到发布的表里。

在发布的表写完之后,会把这个K2的发布索引到小王的相册表里。所以相册表其实是很小的,里面只有索引指针。相册表写好了之后,会触发一个批处理的动作。这个动作就是去跟小王的每个好友说,小王有一个新的发布,请把这个发布插入到每个好友的时间线里面去。

然后比如说现在Mary上朋友圈了,而Mary是小王的一个好友。Mary拉自己的时间线的时候,时间线会告诉到有一个新的发布K2,然后Mary的微信客户端就会去根据K2的元数据去获取图片在CDN上的URL,把图片拉到本地。

在这个过程中,发布是很重的,因为一方面要写一个自己的数据副本,然后还要把这个副本的指针插到所有好友的时间线里面去。如果一个用户有几百个好友的话,这个过程会比较慢一些。这是一个单数据副本写扩散的过程。但是相对应的,读取就很简单了,每一个用户只需要读取自己的时间线表,就这一个动作就行,而不需要去遍历所有好友的相册表。

为什么选择这样一个写扩散的模型?因为读是有很多失败的。一个用户如果要去读两百个好友的相册表,极端情况下可能要去两百个服务器上去问,这个失败的可能性是很大的。但是写失败了就没关系,因为写是可以等待的,写失败了就重新去拷贝,直到插入成功为止。所以这样一个模型可以很大的减少服务的开销。

相关文章:

  • 一文搞定权限设计模型(RBAC,ABAC)超详细图文解析
  • 一文搞定权限管理!授权、鉴权超详细解析
  • Go 中的 JSON如何序列化和反序列化?来看看go的包怎么实现!
  • Go中如何比较两个json?深度优先搜索解决,超详细代码!
  • Go语言实现枚举方法,const和iota结合轻松实现
  • Go msgp序列化使用详解!比Json更快!面试时吊打面试官!
  • 缓存击穿了怎么办?使用singleflight轻松解决!
  • Go中优雅的获取Map元素的多种方法
  • Go中的nil是是什么?和java的null有区别吗?
  • 大厂面试必会语言:GO语言入门,看这一篇就够了
  • 无需安装!Windows11网页版来了!一键带你体验win11!
  • Go语言里如何采用面向对象编程?Go中一样能够面向对象!
  • Go 面试系列:如何比较GO中的结构体?
  • Go面试系列:Goroutine为什么设计为没有ID?
  • MySQL常见面试题:什么是主从延时?如何降低主从延时?
  • ES6指北【2】—— 箭头函数
  • JS 中的深拷贝与浅拷贝
  • android图片蒙层
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • C语言笔记(第一章:C语言编程)
  • happypack两次报错的问题
  • JavaScript类型识别
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Object.assign方法不能实现深复制
  • Windows Containers 大冒险: 容器网络
  • zookeeper系列(七)实战分布式命名服务
  • Zsh 开发指南(第十四篇 文件读写)
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 记录一下第一次使用npm
  • 扑朔迷离的属性和特性【彻底弄清】
  • 强力优化Rancher k8s中国区的使用体验
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • $NOIp2018$劝退记
  • (八)Flask之app.route装饰器函数的参数
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十)T检验-第一部分
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (一) springboot详细介绍
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (转载)Google Chrome调试JS
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .NET Core 2.1路线图
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET 发展历程
  • .NET 服务 ServiceController
  • .net6 webapi log4net完整配置使用流程
  • .net实现客户区延伸至至非客户区
  • .Net中wcf服务生成及调用
  • @Autowired自动装配