为什么朋友圈保存的视频不能发到私信

差不多十年前,随着功能机的淘汰和智能机的普及,互联网开始进入移动互联网时代,最具代表性的产品就是微博、微信,以及后来的今日头条、快手等。这些移动化联网时代的新产品在过去几年间借着智能手机的风高速成长。

这些产品都是Feed流类型产品,由于Feed流一般是按照时间“从上往下流动”,非常适合在移动设备端浏览,最终这一类应用就脱颖而出,迅速抢占了上一代产品的市场空间。

Feed流是Feed + 流,Feed的本意是饲料,Feed流的本意就是有人一直在往一个地方投递新鲜的饲料,如果需要饲料,只需要盯着投递点就可以了,这样就能源源不断获取到新鲜的饲料。 在信息学里面,Feed其实是一个信息单元,比如一条朋友圈状态、一条微博、一条咨询或一条短视频等,所以Feed流就是不停更新的信息单元,只要关注某些发布者就能获取到源源不断的新鲜信息,我们的用户也就可以在移动设备上逐条去浏览这些信息单元。

当前最流行的Feed流产品有微博、微信朋友圈、头条的资讯推荐、快手抖音的视频推荐等,还有一些变种,比如私信、通知等,这些系统都是Feed流系统,接下来我们会介绍如何设计一个Feed流系统架构。

Feed流本质上是一个数据流,是将 “N个发布者的信息单元” 通过 “关注关系” 传送给 “M个接收者”。

Feed流系统是一个数据流系统,所以我们核心要看数据。从数据层面看,数据分为三类,分别是:

  • 发布者的数据:发布者产生数据,然后数据需要按照发布者组织,需要根据发布者查到所有数据,比如微博的个人页面、朋友圈的个人相册等。
  • 关注关系:系统中个体间的关系,微博中是关注,是单向流,朋友圈是好友,是双向流。不管是单向还是双向,当发布者发布一条信息时,该条信息的流动永远是单向的。
  • 接收者的数据:从不同发布者那里获取到的数据,然后通过某种顺序(一般为时间)组织在一起,比如微博的首页、朋友圈首页等。这些数据具有时间热度属性,越新的数据越有价值,越新的数据就要排在最前面。

针对这三类数据,我们可以有如下定义:

  • 存储库:存储发布者的数据,永久保存。
  • 关注表:用户关系表,永久保存。
  • 同步库:存储接收者的时间热度数据,只需要保留最近一段时间的数据即可。

设计Feed流系统时最核心的是确定清楚产品层面的定义,需要考虑的因素包括:

  • 产品用户规模:用户规模在十万、千万、十亿级时,设计难度和侧重点会不同。
  • 关注关系(单向、双写):如果是双向,那么就不会有大V,否则会有大V存在。
    上述是选择数据存储系统最核心的几个考虑点,除此之外,还有一些需要考虑的:
  • 如何实现Meta和Feed内容搜索?

    • 虽然Feed流系统本身可以不需要搜索,但是一个Feed流产品必须要有搜索,否则信息发现难度会加大,用户留存率会大幅下降。
  • Feed流的顺序是时间还是其他分数,比如个人的喜好程度?

    • 双向关系时由于关系很紧密,一定是按时间排序,就算一个关系很紧密的人发了一条空消息或者低价值消息,那我们也会需要关注了解的。
    • 单向关系时,那么可能就会存在大V,大V的粉丝数量理论极限就是整个系统的用户数,有一些产品会让所有用户都默认关注产品负责人,这种产品中,该负责人就是最大的大V,粉丝数就是用户规模。
      接下来,我们看看整个Feed流系统如何设计。

上一节,我们提前思考了Feed流系统的几个关键点,接下来,在这一节,我们自顶向下来设计一个Feed流系统。

第一步,我们首先需要定义产品,我们要做的产品是哪一种类型,常见的类型有:

接着,再详细看一下这几类产品的异同:

上述对比中,只对比各类产品最核心、或者最根本特点,其他次要的不考虑。比如微博中互相关注后就是双向关注了,但是这个不是微博的立命之本,只是补充,无法撼动根本。

从上面表格可以看出来,主要分为两种区分:

  • 关注关系是单向还是双向:

    • 如果是单向,那么可能就会存在大V效应,同时时效性可以低一些,比如到分钟级别;
    • 如果是双向,那就是好友,好友的数量有限,那么就不会有大V,因为每个人的精力有限,他不可能主动加几千万的好友,这时候因为关系更精密,时效性要求会更高,需要都秒级别。
    • 用户对feed流最容易接受的就是时间,目前大部分都是时间。
    • 但是有一些场景,是从全网数据里面根据用户的喜好给用户推荐和用户喜好度最匹配的内容,这个时候就需要用推荐了,这种情况一般也会省略掉关注了,相对于关注了全网所有用户,比如抖音、头条等。
      确定了产品类型后,还需要继续确定的是系统设计目标:需要支持的最大用户数是多少?十万、百万、千万还是亿?

用户数很少的时候,就比较简单,这里我们主要考虑 亿级用户 的情况,因为如果系统能支持亿级,那么其他量级也能支持。为了支持亿级规模的用户,主要子系统选型时需要考虑水平扩展能力以及一些子系统的可用性和可靠性了,因为系统大了后,任何一个子系统的不稳定都很容易波及整个系统。

我们先来看看最重要的存储,不管是哪种同步模式,在存储上都是一样的,我们定义用户消息的存储为存储库。存储库主要满足三个需求:

  • 可靠存储用户发送的消息,不能丢失。否则就找不到自己曾经发布到朋友圈状态了。
  • 读取某个人发布过的所有消息,比如个人主页等。

所以,存储库最重要的特征就是两点:

  • 由于数据要永久保存,数据会一直增长,所以要易于水平扩展。

综上,可以选为存储库的系统大概有两类:

关系型数据库(分库分表)
  • 对于可靠性,分布式NoSQL的可靠性要高于关系型数据库,这个可能有违很多人的认知。主要是关系型数据库发展很长时间了,且很成熟了,数据放在上面大家放心,而分布式NoSQL数据库发展晚,使用的并不多,不太信任。但是,分布式NoSQL需要存储的数据量更多,对数据可靠性的要求也加严格,所以一般都是存储三份,可靠性会更高。目前在一些云厂商中的关系型数据库因为采用了和分布式NoSQL类似的方式,所以可靠性也得到了大幅提高。
  • 水平扩展能力:对于分布式NoSQL数据库,数据天然是分布在多台机器上,当一台机器上的数据量增大后,可以通过自动分裂两部分,然后将其中一半的数据迁移到另一台机器上去,这样就做到了线性扩展。而关系型数据库需要在扩容时再次分库分表。
  • 如果是自建系统,且不具备分布式NoSQL数据库运维能力,且数据规模不大,那么可以使用MySQL,这样可以撑一段时间。
  • 如果数据规模很大,那么也要用分布式NoSQL,否则就是走上一条不归路。

如果使用Tablestore,那么存储库表设计结构如下:

到此,我们确定了存储库的选型,那么系统架构的轮廓有了:

系统规模和产品类型,以及存储系统确定后,我们可以确定同步方式,常见的方式有三种:

  • 推模式(也叫写扩散):和名字一样,就是一种推的方式,发送者发送了一个消息后,立即将这个消息推送给接收者,但是接收者此时不一定在线,那么就需要有一个地方存储这个数据,这个存储的地方我们称为:同步库。推模式也叫写扩散的原因是,一个消息需要发送个多个粉丝,那么这条消息就会复制多份,写放大,所以也叫写扩散。这种模式下,对同步库的要求就是写入能力极强和稳定。读取的时候因为消息已经发到接收者的收件箱了,只需要读一次自己的收件箱即可,读请求的量极小,所以对读的QPS需求不大。归纳下,推模式中对同步库的要求只有一个:写入能力强。
  • 拉模式(也叫读扩散):这种是一种拉的方式,发送者发送了一条消息后,这条消息不会立即推送给粉丝,而是写入自己的发件箱,当粉丝上线后再去自己关注者的发件箱里面去读取,一条消息的写入只有一次,但是读取最多会和粉丝数一样,读会放大,所以也叫读扩散。拉模式的读写比例刚好和写扩散相反,那么对系统的要求是:读取能力强。另外这里还有一个误区,很多人在最开始设计feed流系统时,首先想到的是拉模式,因为这种和用户的使用体感是一样的,但是在系统设计上这种方式有不少痛点,最大的是每个粉丝需要记录自己上次读到了关注者的哪条消息,如果有1000个关注者,那么这个人需要记录1000个位置信息,这个量和关注量成正比的,远比用户数要大的多,这里要特别注意,虽然在产品前期数据量少的时候这种方式可以应付,但是量大了后就会事倍功半,得不偿失,切记切记。
  • 推拉结合模式:推模式在单向关系中,因为存在大V,那么一条消息可能会扩散几百万次,但是这些用户中可能有一半多是僵尸,永远不会上线,那么就存在资源浪费。而拉模式下,在系统架构上会很复杂,同时需要记录的位置信息是天量,不好解决,尤其是用户量多了后会成为第一个故障点。基于此,所以有了推拉结合模式,大部分用户的消息都是写扩散,只有大V是读扩散,这样既控制了资源浪费,又减少了系统设计复杂度。但是整体设计复杂度还是要比推模式复杂。
Redis、memcache等缓存系统或搜索系统(推荐排序场景)

介绍完同步模式中所有场景和模式后,我们归纳下:

  • 如果产品中是双向关系,那么就采用推模式。
  • 如果产品中是单向关系,且用户数少于1000万,那么也采用推模式,足够了。
  • 如果产品是单向关系,单用户数大于1000万,那么采用推拉结合模式,这时候可以从推模式演进过来,不需要额外重新推翻重做。
  • 如果是一个初创企业,先用推模式,快速把系统设计出来,然后让产品去验证、迭代,等客户数大幅上涨到1000万后,再考虑升级为推拉集合模式。
  • 如果是按推荐排序,那么是另外的考虑了,架构会完全不一样,这个后面专门文章介绍。

如果选择了Tablestore,那么同步库表设计结构如下:

其他内容,同步库中不需要包括消息内容。

确定了同步库的架构如下:

前面介绍了同步和存储后,整个Feed流系统的基础功能完成了,但是对于一个完整Feed流产品而言,还缺元数据部分,接下来,我们看元数据如何处理:

Feed流系统中的元数据主要包括:

4.1 用户详情和列表

主要是用户的详情,包括用户的各种自定义属性和系统附加的属性,这部分的要求只需要根据用户ID查询到就可以了。

可以采用的分布式NoSQL系统或者关系型数据库都可以。

如果使用NoSQL数据库Tablestore,那么用户详情表设计结构如下:

主键列,用于唯一确定一个用户 用户昵称,用户自定义属性 用户性别,用户自定义属性 其他属性,包括用户自定义属性列和系统附加属性列。Tablestore是FreeSchema类型的,可以随时在任何一行增加新列而不影响原有数据。

4.2 关注或好友关系

这部分是存储关系,查询的时候需要支持查询关注列表或者粉丝列表,或者直接好友列表,这里就需要根据多个属性列查询需要索引能力,这里,存储系统也可以采用两类,关系型、分布式NoSQL数据库。

  • 如果已经有了关系型数据库了,且数据量较少,则选择关系型数据库,比如MySQL等。
  • 如果数据量比较大,这个时候就有两种选择:

      1. 需要分布式事务,可以采用支持分布式事务的系统,比如分布式关系型数据库。
      1. 使用具有索引的系统,比如云上的Tablestore,更简单,吞吐更高,扩容能力也一并解决了。

如果使用Tablestore,那么关注关系表设计结构如下:

  • 如果需要查询某个人的粉丝列表:使用TermQuery查询固定user_id,且按照timestamp排序。
  • 当前数据写入Table后,需要5~10秒钟延迟后会在多元索引中查询到,未来会优化到2秒以内。

除了使用多元索引外,还可以使用GlobalIndex。

思考一个问题,发送者将消息发送后,接收者如何知道自己有新消息来了?客户端周期性去刷新?如果是这样子,那么系统的读请求压力会随着客户端增长而增长,这时候就会有一个风险,比如平时的设备在线率是20%~30%,突然某天平台爆发了一个热点消息,大量休眠设备登陆,这个时候就会出现“查询风暴”,一下子就把系统打垮了,所有的用户都不能用了。

解决这个问题的一个思路是,在服务端维护一个推送session池,这个里面记录哪些用户在线,然后当用户A发送了一条消息给用户B后,服务端在写入存储库和同步库后,再通知一下session池中的用户B的session,告诉他:你有新消息了。然后session-B再去读消息,然后有消息后将消息推送给客户端。或者有消息后给客户端推送一下有消息了,客户端再去拉。

这个session池使用在同步中,但是本质还是一个元数据,一般只需要存在于内存中即可,但是考虑到failover情况,那就需要持久化,这部分数据由于只需要指定单Key查询,用分布式NoSQL或关系型数据库都可以,一般复用当前的系统即可。

设备ID,同一个用户可能会有多个设备,不同设备的读取位置可能不一致,所以这里需要一个设备ID。如果不需要支持多终端,则这一列可以省略。 该接收者已经推送给客户端的最新的顺序ID

除了私信类型外,其他的feed流类型中,都有评论功能,评论的属性和存储库差不多,但是多了一层关系:被评论的消息,所以只要将评论按照被被评论消息分组组织即可,然后查询时也是一个范围查询就行。这种查询方式很简单,用不到关系型数据库中复杂的事务、join等功能,很适合用分布式NoSQL数据库来存储。

所以,一般的选择方式就是:

  • 如果系统中已经有了分布式NoSQL数据库,比如Tablestore、Bigtable等,那么直接用这些即可。
  • 如果没有上述系统,那么如果有MySQL等关系型数据库,那就选关系型数据库即可。
  • 如果选择了Tablestore,那么“评论表”设计结构如下:
微博ID或朋友圈ID等消息的ID

如果需要搜索评论内容,那么对这张表建立多元索引即可。

最近几年,“赞”或“like”功能很流行,赞功能的实现和评论类似,只是比评论少了一个内容,所以选择方式和评论一样。

如果选择了Tablestore,那么“赞表”设计结构同评论表,这里就不再赘述了。

系统架构中加了元数据系统后的架构如下:

到此,我们已经介绍完了Feed流系统的主题架构,Feed流系统算是完成了。但是Feed流产品上还未结束,对于所有的feed流产品都需要有搜索能力,比如下面场景:

这些内容搜索只需要字符匹配到即可,不需要非常复杂的相关性算法,所以只需要有能支持分词的检索功能即可,所以一般有两种做法:

使用搜索引擎,将存储库的内容和用户信息表内容推送给搜索系统,搜索的时候直接访问搜索系统。
使用具备全文检索能力的数据库,比如最新版的MySQL、MongoDB或者Tablestore。

所以,选择的原则如下:

  • 如果存储库使用了MySQL或者Tablestore,那么直接选择这两个系统就可以了。
  • 如果整个系统都没使用MySQL、Tablestore,且已经使用了搜索系统,那么可以直接复用搜索系统,其他场景都不应该再额外加一个搜索系统进来,徒添复杂度。

如果使用Tablestore,那么只需要在相应表上建立多元索引即可:

  • 如果需要对用户名支持搜索,那么需要对user_table建立多元索引,其中的nick_name需要是Text类型,且单字分词。
  • 如果需要对Feed流内容支持搜索,那么需要对存储库表:store_table建立多元索引,这样就能直接对Feed流内容进行各种复杂查询了,包括多条件筛选、全文检索等。

系统架构中加了搜索功能后的架构如下:

目前的Feed流系统中的排序方式有两种,一种是时间,一种是分数。

我们常用的微博、朋友圈、私信这些都是时间线类型的,因为这些产品定义中,需要我们主动关注某些人后才会看到这些人发表的内容,这个时候,最重要的是实时性,而不是发布质量,就算关注人发布了一条垃圾信息,我们也会被动看到。这种类型的产品适用于按照时间线排序。这一篇我们介绍的架构都是基于时间类型的。

另外一种是不需要关注任何人,我们能看到的都是系统希望我们看到的,系统在后台会分析我们的每个人的爱好,然后给每个人推送差异化的、各自喜欢的内容,这一种的架构和基于时间的完全不一样,我们在后续的推荐类型中专门介绍。

在Feed流应用中有一个问题,就是如果用户删除了之前发表的内容,系统该如何处理?因为系统里面有写扩散,那么删除的时候是不是也要写扩散一遍?这样的话,删除就不及时了,很难应对法律法规要求的快速删除。

针对这个问题,我们在之前设计的时候,同步表中只有消息ID,没有消息内容,在用户读取的时候需要到存储库中去读消息内容,那么我们可以直接删除存储库中的这一条消息,这样用户读取的时候使用消息ID是读不到数据的,也就相当于删除的内容,而且删除速度会很快。除了直接删除外,另外一种办法是逻辑删除,对于删除的feed内容,只做标记,当查询到带有标记的数据时就认为删除了。

更新和删除Feed处理逻辑一样,如果使用了支持多版本的存储系统,比如Tablestore,那么也可以支持编辑版本,和现在的微博一样。

上面介绍了不同子功能的特点和系统要求,能满足需求的系统主要有两类,一类是阿里云的Tablestore单系统,一类是开源组件组成的组合系统。

  • 开源组件组成的组合系统:包括MySQL、Redis、HBase等,这些系统单个都不能解决Feed流系统中遇到的问题,需要组合在一起,各司其职才能完成一个Feed流系统,适用于热衷开源系统,人多且喜欢运维操作的团队。
  • Tablestore单系统:只使用Tablestore单个系统就能解决上述的所有问题,这时候肯定有人要问?你是不是在吹牛? 这里不是吹牛,Tablestore在三年前就已经开始重视Feed流类型业务,之前也发表过多篇文章介绍,功能上也在专门为Feed流系统特别定制设计,所以到今天,只使用Tablestore一款产品,是可以满足上述需求的。选择Tablestore做Feed流系统的用户具有以下一些特征:

    • 产品设计目标规模大,千万级或亿级。
    • 不喜欢运维,喜欢专注于开发。
    • 高效率团队,希望尽快将产品实现落地。
    • 希望一劳永逸,未来系统随着用户规模增长可以自动扩容。
    • 希望能按量付费,用户少的时候费用低,等用户增长起来后费用在跟随用户数增长。
      如果具有上述四个特征的任何一个,那么都是适合于用Tablestore。

上面我们介绍了Feed流系统的设计理论,具体到不同的类型中,会有不同的侧重点,下面会逐一介绍。

朋友圈是一种典型的Feed流系统,关系是双写关系,关系有上限,排序按照时间,如果有个人持续产生垃圾内容,那就只能屏蔽掉TA,这一种类型就是典型的写扩散模型。

我们接下来会在文章《朋友圈类系统架构设计》中详细介绍朋友圈类型Feed流系统的设计。

微博也是一种非常典型的Feed流系统,但不同于朋友圈,关系是单向的,那么也就会产生大V,这个时候就需要读写扩散模式,用读扩散解决大V问题。同时,微博也是主动关注类型的产品,所以排序也只能是时间,如果按照推荐排序,那么效果就会比较差。

接下里会在文章《微博类系统架构设计》中详细介绍微博类型Feed流系统的设计。

头条是最近几年快速崛起的一款应用,在原有微博的Feed流系统上产生了进化,用户不需要主动关注其他人,只要初始浏览一些内容后,系统就会自动判断出你的喜好,然后后面再根据你的喜好给你推荐你可能会喜好的内容,训练时间长了后,推送的内容都会是你最喜欢看的。

后面,我们会在文章《头条类系统架构设计》中详细介绍头条类型Feed流系统的设计。

私信也算是一种简单的Feed流系统,或者也可以认为是一种变相的IM,都是单对单的,没有群。我们后面也会有一篇文章《私信类系统架构设计》中做详细介绍。

上面我们介绍了Feed流系统的整体框架,主要是产品定义、同步、存储、元数据、评论、赞、排序和搜索等内容,由于篇幅有限,每一章节都介绍的比较简单。读者如果对某一部分看完后仍然有疑问,可以继续再文后提问,我会继续去完善这篇文章,希望未来读者看完这篇文章后,就可以轻轻松松设计出一个亿级规模的Feed流系统。

另外,我们也欢迎有兴趣的读者一起来完成这个系列,帮忙实现朋友圈、微博、头条或者私信类型的文章,有任何问题都欢迎来讨论。

在互联网领域,尤其现在的移动互联网时代,Feed流产品是非常常见的,比如我们每天都会用到的朋友圈,微博,就是一种非常典型的Feed流产品,还有图片分享网站Pinterest,花瓣网等又是另一种形式的Feed流产品。除此之外,很多App的都会有一个模块,要么叫动态,要么叫消息广场,这些也是Feed流产品,可以说,Feed流产品是遍布天下所有的App中。

我们在讲如何设计Feed流系统之前,先来看一下Feed流中的一些概念:

  • Feed:Feed流中的每一条状态或者消息都是Feed,比如朋友圈中的一个状态就是一个Feed,微博中的一条微博就是一个Feed。
  • Feed流:持续更新并呈现给用户内容的信息流。每个人的朋友圈,微博关注页等等都是一个Feed流。
  • Timeline:Timeline其实是一种Feed流的类型,微博,朋友圈都是Timeline类型的Feed流,但是由于Timeline类型出现最早,使用最广泛,最为人熟知,有时候也用Timeline来表示Feed流。
  • 关注页Timeline:展示其他人Feed消息的页面,比如朋友圈,微博的首页等。
  • 个人页Timeline:展示自己发送过的Feed消息的页面,比如微信中的相册,微博的个人页等。

Feed流系统有一些非常典型的特点,比如:

  • 多账号内容流:Feed流系统中肯定会存在成千上万的账号,账号之间可以关注,取关,加好友和拉黑等操作。只要满足这一条,那么就可以当做Feed流系统来设计。
  • 非稳定的账号关系:由于存在关注,取关等操作,所以系统中的用户之间的关系就会一直在变化,是一种非稳定的状态。
  • 读写比例100:1:读写严重不平衡,读多写少,一般读写比例在10:1,甚至100:1以上。
  • 消息必达性要求高:比如发送了一条朋友圈后,结果部分朋友看到了,部分朋友没看到,如果偏偏女朋友没看到,那么可能会产生很严重的感情矛盾,后果很严重。

上面的就是Feed流产品的一些特点,下面我们来看一下Feed流系统的分类。

Feed流的分类有很多种,但最常见的分类有两种:

  • Timeline:按发布的时间顺序排序,先发布的先看到,后发布的排列在最顶端,类似于微信朋友圈,微博等。这也是一种最常见的形式。产品如果选择Timeline类型,那么就是认为Feed流中的Feed不多,但是每个Feed都很重要,都需要用户看到
  • Rank:按某个非时间的因子排序,一般是按照用户的喜好度排序,用户最喜欢的排在最前面,次喜欢的排在后面。这种一般假定用户可能看到的Feed非常多,而用户花费在这里的时间有限,那么就为用户选择出用户最想看的Top N结果,场景的应用场景有图片分享、新闻推荐类、商品推荐等。

上面两种是最典型,也是最常见的分类方式,另外的话,也有其他的分类标准,在其他的分类标准中的话,会多出两种类型:

  • Aggregate:聚合类型,比如好几个朋友都看了同一场电影,这个就可以聚合为一条Feed:A,B,C看了电影《你的名字》,这种聚合功能比较适合在客户端做。一般的Aggregate类型是Timeline类型 + 客户端聚合。
  • Notice:通知类型,这种其实已经是功能类型了,通知类型一般用于APP中的各种通知,私信等常见。这种也是Timeline类型,或者是Aggregate类型。

上面介绍了Feed流系统的概念,特征以及分类,接下来开始进入关键部分:如何实现一个千万级Feed流系统。由于系统中的所有用户不可能全部在线,且不可能同时刷新和发布Feed,那么一个能支撑千万量级Feed流的系统,其实在产品上可以支撑上亿的用户。

如果要设计一个Feed流系统,最关键的两个核心,一个是存储,一个是推送。

我们先来看存储,Feed流系统中需要存储的内容分为两部分,一个是账号关系(比如关注列表),一种是Feed消息内容。不管是存储哪一种,都有几个问题需要考虑:

  • 如何能支持100TB,甚至PB级数据量?
  • 数据量大了后成本就很关键,成本如何能更便宜?
  • 如何保证账号关系和Feed不丢失?

我们后面再解答这三个问题,先继续看推送

推送系统需要的功能有两个,一个是发布Feed,一个是读取Feed流。对于提送系统,仍然有一些问题需要在选型之前考虑:

  • 如何才能提供千万的TPS和QPS?
  • 如何保证读写延迟在10ms,甚至2ms以下?
  • 如何保证Feed的必达性?

再解答这些问题之前,我们先来大概了解下阿里云的表格存储TableStore。

表格存储(TableStore)是阿里云自主研发的专业级分布式NoSQL数据库,是基于共享存储的高性能、低成本、易扩展、全托管的半结构化数据存储平台,
支撑互联网和物联网数据的高效计算与分析。

目前不管是阿里巴巴集团内部,还是外部公有云用户,都有成千上万的系统在使用。覆盖了重吞吐的离线应用,以及重稳定性,性能敏感的在线应用。目前使用的系统中,有些系统每秒写入行数超过3500万行每秒流量超过5GB单表总行数超过10万亿行单表数据量超过10PB

表格存储的具体的特性可以看下面这张图片。

这里就不详细介绍表格存储(TableStore)的功能和特性了,有兴趣的话可以到官网页面和云栖博客了解,地址如下:

我们接下来解决之前提出来的问题。
Feed流系统中需要存储的系统有两类,一类是账号关系(比如关注列表),一类是Feed消息。

我们先来看账号关系(比如关注列表)的存储,对于账号关系,它有一些特点:

  • 是一系列的变长链表长度可达亿级别
  • 这样就会导致数据量比较大,但是关系极其简单
  • 还有一点是性能敏感,直接影响关注,取关的响应速度。

最适合存账号关系(关注列表)的系统应该是分布式NoSQL数据库,原因是数据量极大,关系简单不需要复杂的join,性能要求高。
对内设计实现简单,对外用户体验好。

除了上面这些特点外,还有一个特点:

  • 有序性:有序性并不要求具有排序功能,只需要能按照主键排序就行,只要能按照主键排序,那么关注列表和粉丝列表的顺序就是固定的,可预期的。

使用开源HBase存储账号关系

能满足有序性的分布式NoSQL数据库中,开源HBase就是一个,所以很多企业会选择开源HBase来存储账号关系,或者是关注列表。

这样虽然满足了上述四个特征,可以把系统搭建起来,但是会有一些麻烦的问题:

  • 需要自己运维,调查问题,Fix bug,会带来较大的复杂度和成本开支。
  • GC会导致比较大的毛刺,影响用户体验,

除此之外,阿里云的表格存储也属于有序性的分布式NoSQL数据库,之前有不少很有名的系统选择使用表格存储,在下面一些地方给系统带来了收益:

  • 单表支持10万亿行+,10PB+的数据量,再快的数据增长速度都不用担心。
  • 数据按主键列排序,保证有序性和可预期性。
  • 单key读写延迟在毫秒级别,保证关注,取关的响应时间。
  • 全托管的分布式NoSQL数据库服务,无需任何运维
  • 全部采用C++ 实现,彻底无GC问题,也就不会由于GC而导致较大的毛刺。

使用表格存储(TableStore)来存储账号关系会是一个比较好的选择。

接下来看一下Feed消息的存储。

Feed消息有一个最大的特点:

  • 数据量大,而且在Feed流系统里面很多时候都会选择写扩散(推模式)模式,这时候数据量会再膨胀几个数量级,所以这里的数据量很容易达到100TB,甚至PB级别。

除此之外,还有一些其他特点:

  • 数据不能丢失,可靠性要求高
  • 自增主键功能,保证个人发的Feed的消息ID在个人发件箱中都是严格递增的,这样读取时只需要一个范围读取即可。由于个人发布的Feed并发度很低,这里用时间戳也能满足基本需求,但是当应用层队列堵塞,网络延迟变大或时间回退时,用时间戳还是无法保证严格递增。这里最好是有自增功能。

根据上述这些特征,最佳的系统应该是具有主键自增功能的分布式NoSQL数据库,但是在开源系统里面没有,所以常用的做法有两种:

  • 关系型数据库 + 分库分表
  • 关系型数据库 + 分布式NoSQL数据库:其中 关系型数据库提供主键自增功能。

使用关系型数据库存储Feed消息

目前业界有很多用户选择了关系系数据库+ 分库分表,包括了一些非常著名的Feed流产品,虽然这个架构可以运行起来,但是存在一些问题。

  • 分库分表带来了运维复杂性
  • 分库分表带来了逻辑层和数据层的极大耦合性
  • 关系型数据库,比如开源MySQL数据库的主键自增功能性能差。不管是用MyISAM,还是InnoDB引擎,要保证自增ID严格递增,必须使用表锁,这个粒度非常大,会严重限制并发度,影响性能。
  • 有些用户觉得关系型数据库的可靠性高一些,但是关系型数据库的可靠性一般也就最多6个9,这个可靠性和分布式数据库完全不在一个层级,要低4到5个级别。

基于上述原因,一些技术公司开始考虑使用表格存储(TableStore),表格存储是一个具有自增主键功能的分布式NoSQL数据库,这样就只需要使用一种系统,除此之外还有以下的考虑:

  • 单表可达10PB,10万亿行。
  • 天然分布式数据库,无需分库分表
  • 两种实例类型:高性能实例采用全SSD存储媒介,提供极佳的读写性能。混合存储实例采用SSD+SATA存储媒介,提供极低的存储成本。
  • 主键自增功能性能极佳,其他所有系统在做自增功能的时候都需要加锁,但是表格存储的主键自增功能在写入自增列行的时候,完全不需要锁,既不需要表锁,也不需要行锁。

从上面看,使用TableStore的话,不管是在功能,性能,扩展性还是成本方面都要更加适合一些。

看完推送系统选择后,我们再来看看推送方案的选择。

我们先来回顾下之前说的Feed流系统最大的特点:

  • 读写严重不平衡,读多写少,一般读写比例都在10;1,甚至100:1之上。

除此之外,还有一个方面会被推送方案影响:

  • 发布, 刷新Feed时的延时本质上由推送方案决定,其他的任何操作都只能是优化,质量量变,无法质变。

在推送方案里面的,有两种方案,分别是:

  • 拉方案:也称为读扩散
  • 推方案:也成为写扩散

对于拉方案和推方案,他们在很多方面完全相反,在看对比之前有一点要强调下:

  • 对Feed流产品的用户而言,刷新Feed流(读取)时候的延迟敏感度要远远大于发布(写入)的时候。
粉丝的关注页(收件箱)
所有关注者的个人页Timeline
放大读:读写比例到1万:1 放大写减少读:读写比例到50:50

在上面的对比中可以明显看出来,推模式要远远比拉模式更好一些,但是也有一个副作用:

针对这个缺点,可以从两个方面考虑:

  • 目前的存储价格很低很低了,就以表格存储为例,容量型实例存储10TB的数据量,在现在(2017年10月)每年费用是1万六千元,以后价格会随着硬件技术升级,软件性能优化等继续降低。还有数据量越大价格越便宜。
  • 想省点钱,那继续可以优化:

    • 对大V采用拉模式,普通用户使用推模式,这种模式有个缺点,后面会有分析。
    • 对活跃粉丝采用推模式,非活跃粉丝采用拉模式(这种方式可以较好的避免大流量对平台的冲击)

通过上述两个方案的对比后,总结下各个方案的适用场景:

    • 很多Feed流产品的第一版会采用这种方案,但很快就会抛弃。
    • 另外,拉模式 + 图计算 就会是另一番天地,但是这个时候重心就是图计算了。
    • Feed流系统中最常用、有效的模式;
    • 用户关系数比较均匀,或者有上限,比如朋友圈;
    • 偏推荐类,同一个Feed对不同用户价值不同,需要为不同用户计算分数,比如pinterest。
    • 大部分用户的账号关系都是几百个,但是有个别用户是1000万以上,比如微博。

上面了解了推送方案,接下来看下推送系统选择

如果要实现一个千万量级的Feed流产品,那么推送系统需要具备一些特点:

  • 具备千万TPS/QPS的能力。
  • 读写链路延迟敏感,读写直接会影响用户发布,刷新Feed流时的延迟,尤其是极其敏感的刷新时的延迟。
  • Feed消息的必达性要求很高。
  • 主键自增功能,仍然是保证用户收件箱中的Feed ID是严格递增的,保证可以通过Scan(上次读取的最大ID --->MAX)读取到最新未读消息。
  • 最好能为用户存储Timeline中所有的Feed。

从上述特点来看,需要的推送系统最好是一个性能极佳,又可靠的有自增功能的NoSQL系统,所以,业内一般如果选择开源系统的话,会在选择了关系型数据库作为存储系统的基础上,选择开源Redis,这样就能覆盖上述的几个特征,也能保证Feed流系统正常运行起来,但是也会带来一些其他问题:

  • 纯内存系统,内存价格极高,整体成本就比较高了。
  • 属于单机系统,为了支持千万TPS和保证消息必达性,需要使用cluster和replica模式,结果就是不仅带来了运维的复杂性,而且带来了成本的机器增加,成本再次上升。
  • 成本上升了以后,就有架构师开始考虑是否可以节省一些成本,要节省成本只能是减少开源Redis里面存储的数据量,一般有两种做法,这两种做法都能减少存入Redis中的数据量:

    • 只在开源Redis中存储Feed ID,不存储Feed内容。整体数据量会大量减少,但是在读取的时候需要先读Feed ID,然后在到存储系统里面去读取Feed内容,网络开销增长了一倍,而且是串行的,对用户的刷新延迟有较大影响。
    • 只对普通用户或者活跃用户使用推模式,对大V和非活跃用户直接使用拉模式。

上述两个方案虽然可以节省成本,但是是以牺牲用户体验为代价的,最终需要在成本和用户体验之间权衡。

除了使用开源系统外,还可以使用阿里云的表格存储(TableStore),有不少用户选择TableStore作为推送系统的原因无非下面几点:

  • 天然分布式,单表可支持千万级TPS/QPS。
  • LSM存储引擎极大优化写,高性能实例极大优化读
  • 写入成功即保证落盘成功,数据可靠性提供10个9的SLA保障。
  • 磁盘性数据库,费用比内存性的要低几个量级。
  • 单表可存储十万亿行以上的数据,价格又低,轻松保存用户Feed流中的所有Feed数据。

上面说了使用开源Redis和阿里云TableStore的异同,如果使用开源可以用Redis,如果选择阿里云自研NoSQL数据库,可以使用TableStore。

下面我们来看一下使用TableStore的架构图,这里为了通用性,选用推拉结合的方式,推模式更加简单。

我们先来看中间黑色框中的部分,这部分是使用TableStore的数据,从左往右分别是:

  • 个人页Timeline:这个是每个用户的发件箱,也就是自己的个人页页面。
  • 关注页Timeline:这个是每个用户的收件箱,也就是自己的关注页页面,内容都是自己关注人发布的消息。
  • 关注列表:保存账号关系,比如朋友圈中的好友关系;微博中的关注列表等。
  • 虚拟关注列表:这个主要用来个性化和广告。

当你发布一条Feed消息的时候,流程是这样的:

  1. Feed消息先进入一个队列服务。
  2. 先从关注列表中读取到自己的粉丝列表,以及判断自己是否是大V。
  3. 将自己的Feed消息写入个人页Timeline(发件箱)。如果是大V,写入流程到此就结束了。
  4. 如果是普通用户,还需要将自己的Feed消息写给自己的粉丝,如果有100个粉丝,那么就要写给100个用户,包括Feed内容和Feed ID。
  5. 第三步和第四步可以合并在一起,使用BatchWriteRow接口一次性将多行数据写入TableStore。
  6. 发布Feed的流程到此结束。

当刷新自己的Feed流的时候,流程是这样的:

  1. 先去读取自己关注的大V列表
  2. 去读取自己的收件箱,只需要一个GetRange读取一个范围即可,范围起始位置是上次读取到的最新Feed的ID,结束位置可以使当前时间,也可以是MAX,建议是MAX值。由于之前使用了主键自增功能,所以这里可以使用GetRange读取。
  3. 如果有关注的大V,则再次并发读取每一个大V的发件箱,如果关注了10个大V,那么则需要10次访问。
  4. 合并2和3步的结果,然后按时间排序,返回给用户。

至此,使用推拉结合方式的发布,读取Feed流的流程都结束了。

如果只是用推模式了,则会更加简单:

    1. 不用区分是否大V,所有用户的流程都一样,都是三步。
    1. 不需要第一步,也不需要第三步,只需要第二步即可,将之前的2 + N(N是关注的大V个数) 次网络开销减少为 1 次网络开销。读取延时大幅降级。

个性化和定向广告是两种很强烈的产品需求。个性化可以服务好用户,增大产品竞争力和用户粘性,而定向广告可以为产品增加盈利渠道,而且还可以不招来用户反感,那么这两种方式如何实现呢? 在Feeds流里面这两种功能的实现方式差不多,我们以定向广告为例来说明:

  1. 通过用户特征分析对用户分类,比如其中有一类是新生类:今年刚上大学的新生。(具体的用户特征分析可以依靠TableStore + MaxCompute,这里就不说了)。
  2. 创建一个广告账号:新生广告
  3. 让这些具有新生特征的用户虚拟关注新生广告账号。用户看不到这一层关注关系。
  4. 从七月份开始就可以通过新生广告账号发送广告了。
  5. 最终,每个用户可能会有多个特征,那么就可能虚拟关注多个广告账号。

上面是定向广告的一种比较简单的实现方式,其他方式就不再赘述了。

上面我们详细说了使用TableStore作为存储和推送系统的架构,接下来我们看看新架构能给我们带来多大收益。

  • 只使用1种系统,架构、实现简单。不再需要访问多个系统,架构,开发,测试,运维都能节省大力人力时间。
  • TableStore 主键自增列功能性能极优。由于架构的不同,不仅不需要表锁,行锁也不需要,所以性能要远远好于关系型数据库。
  • 可以保存所有的Feed。一是系统可以支持存储所有Feed,二是价格便宜,存的起。
  • 无须将Feed ID和内容分开存储。价格便宜,也就不需要再分开存储ID和内容了。
  • 全托管服务,无运维操作,更无需分库分表。
  • 磁盘型(SSD、Hybrid)数据库,成本低。
  • 可靠性10个9,数据更可靠,更不易丢失。
  • 大V和普通用户的切分阈值更高,读取大V的次数更少,整体延时更低。

如果使用大V/普通用户的切分方式,大V使用拉模式,普通用户使用推模式,那么这种架构就会存在一种很大的风险。
比如某个大V突然发了一个很有话题性的Feed,那么就有可能导致整个Feed产品中的所有用户都没法读取新内容了,原因是这样的:

  • 大V发送Feed消息。
  • 大V的活跃粉丝(用户群A)开始通过拉模式(架构图中读取的步骤3,简称读3)读取大V的新Feed。
  • Feed内容太有话题性了,快速传播。
  • 未登录的大V粉丝(用户群B)开始登陆产品,登陆进去后自动刷新,再次通过读3步骤读取大V的Feed内容。
  • 非粉丝(用户群C)去大V的个人页Timeline里面去围观,再次需要读取大V个人的Timeline,同读3.

结果就是,平时正常流量只有用户群A,结果现在却是用户群A + 用户群B+ 用户群C,流量增加了好几倍,甚至几十倍,导致读3路径的服务模块被打到server busy或者机器资源被打满,导致读取大V的读3路径无法返回请求,如果Feed产品中的用户都有关注大V,那么基本上所有用户都会卡死在读取大V的读3路径上,然后就没法刷新了。

所以这里设计的时候就需要重点关心下面两点:

  • 单个模块的不可用,不应该阻止整个关键的读Feed流路径,如果大V的无法读取,但是普通用户的要能返回,等服务恢复后,再补齐大V的内容即可。
  • 当模块无法承受这么大流量的时候,模块不应该完全不可服务,而应该能继续提供最大的服务能力,超过的拒绝掉。
  • 不使用大V/普通用户的优化方式,使用活跃用户/非活跃用户的优化方式。这样的话,就能把用户群A和部分用户群B分流到其他更分散的多个路径上去。而且,就算读3路径不可用,仍然对活跃用户无任何影响。
  • 完全使用推模式就可以彻底解决这个问题,但是会带来存储量增大,大V微博发送总时间增大,从发给第一个粉丝到发给最后一个粉丝可能要几分钟时间(一亿粉丝,100万行每秒,需要100秒),还要为最大并发预留好资源,如果使用表格存储,因为是云服务,则不需要考虑预留最大额度资源的问题。

接下来我们来实现一个消息广场的功能。很多App中都有动态或消息广场的功能,在消息广场中一般有两个Tab,一个是关注人,一个是广场,我们这里重点来看关注人。

  • 用户可以查看自己发布的消息列表
  • 用户可以查看自己关注的人的消息
  • 使用TableStore作为存储和推送系统
  • 采用Timeline的显示方式,希望用户可以认真看每条Feed

接着,我们看看角色和每个角色需要的功能:

Feed消息中至少需要包括下面内容:

  • 类型:verb,比如图片,视频,文本

  • get_range接口调用关注列表,返回粉丝列表。
  • batch_write_row接口将feed内容和ID批量写入个人页表(发件箱)和所有粉丝的关注页表(收件箱),如果量太大,可以多次写入。或者调用异步batch_write_row接口,目前C++ SDK和JAVA SDK提供异步接口。
    • put_row接口直接写入一行数据(关注人,粉丝)到关注列表和粉丝列表(粉丝,关注人)即可。
    • 从客户端获取上次读取到的最新消息的ID:last_id
    • 使用get_range接口读取最新的消息,起始位置是last_id,结束位置是MAX。
    • 如果是读取个人页的内容,访问个人页表即可。如果是读取关注页的内容,访问关注页表即可。

上面展示了如何使用表格存储TableStore的API来实现。这个虽然只用到几个接口,但是仍然需要学习表格存储的API和特性,还是有点费时间。

为了更加易用性,我们接下来会提供Feeds流完整解决方案,提供一个LIB,接口直接是add_activity(),follow()和get_activity()类似的接口,使用上会更加简单和快捷。

前面讲述的都是Timeline类型的Feed流类型,但是还有一种Feed流类型比较常见,那就是新闻推荐,图片分享网站常用的Rank类型。
我们再来回顾下Rank类型擅长的领域:

  • 潜在Feed内容非常多,用户无法全部看完,也不需要全部看完,那么需要为用户选出她最想看的内容,典型的就是图片分享网站,新闻推荐网站等。

我们先来看一种架构图:

  • 这种Rank方式比较轻量级,适用于推拉结合的场景。
  • 读流程里面会先读取所有的Feed内容,这个和Timeline也一样,Timeline里面的话,这里会直接返回给用户,但是Rank类型需要在一个排序模块里面,按照某个属性值排序,然后将所有结果存入一个timeline cache中,并返回分数最高的N个结果,下次读取的时候再返回[N+1, 2N]的结果。
  • 这种比较重量级,适用于纯推模式。
  • 每个用户有两个收件箱:

    • 一个是关注页Timeline,保存原始的Feed内容,用户无法直接查看这个收件箱。
    • 一个是rank timeline,保存为用户精选的Feed内容,用户直接查看这个收件箱。
  • 写流程结束后还有一个数据处理的流程。个性化排序系统从原始Feed收件箱中获取到新的Feed 内容,按照用户的特征,Feed的特征计算出一个分数,每个Feed在不同用户的Timeline中可能分数不一样的,计算完成后再排序然后写入最终的rank timeline。
  • 这种方式可以真正为每个用户做到“千人千面”。

上述两种方式是实现Rank的比较简单,常用的方式。

从上面的内容来看,表格存储(TableStore)在存储方面可以支持10PB级,推送方面可以支撑每秒千万的TPS/QPS,在Feed流系统中可以发挥很大的价值。

目前,已经有不少著名公司在使用表格存储(TableStore)来构建他们自己的Feed流系统,最终为系统,产品,公司都带来了不少收益。

移动互联网时代,微信和微博已经成为这个时代的两大支柱类社交应用。
这两类应用,其中一个是IM产品,一个是Feed流产品,微信的朋友圈也属于Feed流。
如果再细心去发现,会发现基本所有移动App都有Feed流的功能:消息广场、个人关注、通知、新闻聚合和图片分享等等。各种各样的Feed流产品占据了我们生活的方方面面。

IM和Feed流功能已经基本成为所有App标配,如何开发一个IM或者Feed流功能是很多架构师、工程师要面临的问题。虽然是一个常见功能,但仍然是一个巨大的挑战,要考虑的因素非常多,比如:

  1. 存储系统里面如何选型才能支持大量数据存储,而且价格便宜。
  2. 同步系统使用何种架构、推送模型和系统才能保证高并发的同时延迟稳定?

为了解决上述问题,我们之前推出了三篇文章来阐述:

  • :介绍了IM系统的传统架构模型和现代架构模型选型,以及抽象出了一个Timeline模型。
  • :介绍了使用Table Store主键列自增功能后,对传统IM架构带来的巨大冲击,在稳定性,性能,成本,架构复杂度等多个方面都产生了巨大收益。

  • :介绍了如何设计一款支持千万级并发的Feed流系统,详细分析了存储系统、同步系统选型、推拉模式(读扩散、写扩散)的选择及改进等。

上述三篇文章推出后,用户反响很好,在各个平台的传播很广,为很多用户提供了设计一款IM和Feed流产品的架构思路,但是从这里到完全实现一个可靠的IM、Feed流系统平台还有很长的路,比如:

  1. 如何才能更加简单的弄懂消息存储和同步模型?
  2. 如何将架构模型映射到存储系统和推送系统中?
  3. 如何才能保证对存储系统和推送系统的使用是最佳方式?

针对上述三个问题,在中引入了一个逻辑模型概念:Timeline模型。

在《现代IM系统中消息推送和存储架构的实现》中基于IM系统提出了Timeline模型,进一步会发现Timeline模型适用场景可以更广泛:

  • 生产者产生的一条消息可能会被一个或多个消费者消费。
  • 消费者需要聚合来自多个生产者的消息在一个页面展现。

IM和Feed流产品完全匹配上述四个特征,所以Timeline模型可以完全适用于IM和Feed流场景中。

下面我们来看看如何在各个场景中使用Timeline:

    • 会话Timeline(存储历史消息)
    • 用户A的收件箱(A的同步库Timeline)
    • 用户B的收件箱(B的同步库Timeline)
    • 会话Timeline(存储历史消息)
    • 用户A的收件箱(A的同步库Timeline)
  • 用户N的收件箱(N的同步库Timeline)

每个用户只有一个同步库Timeline,就算用户A在10个群里面,那么这个10个群的同步消息都是发送给用户A的这一个同步库Timeline中。

  • 朋友A的收件箱(A的同步库Timeline)
  • 朋友N的收件箱(N的同步库Timeline)

如果是微博,粉丝可能会达到上亿级别,这时候会比朋友圈稍微复杂些:

  • 粉丝A的收件箱(A的同步库Timeline)
  • 粉丝M的收件箱(M的同步库Timeline)
    剩余N - M的粉丝直接读大V自己的存储库Timeline内容即可,那么怎么设置M和N - M,可以参考这篇文章。

从上面分析可以看出来,不管是IM,还是Feed流产品都可以将底层的存储、同步逻辑抽象成一个对多个Timeline进行读写的模型。

有了Timeline概念模型后,从IM/Feed流应用映射到Timeline就比较容易了,但是从Timeline映射到存储、同步系统仍然很复杂,主要体现在:

  • 如何实现Timeline概念模型?
  • 如何将Timeline模型转换成对存储系统、同步系统的读写接口?
  • 如何设计存储系统、同步系统的表结构?
  • 如何选择存储系统、同步系统的读写方式?
  • 如何评估存储系统、同步系统的最大承载能力?
  • 如何实现才能保证性能最佳?
  • 如何才能吸取大型同类型系统架构设计的经验教训、
  • 如何才能避免一些实现、使用上的隐患?

这些问题涉及的内容光,细节多,深度大,坑较多等,整体上很繁杂,这一部分在耗费了大量人力之后,结果可能并不理想。

针对上述问题,只要存储系统和推送系统确定后,剩余的工作都是类似的,可以完全将经验封装起来成为一个LIB,将表结构设计,读写方式,隐患等等都解决好,然后供后来者使用,后来者可以不用再关心Timeline到底层存储系统之间的事情了。

目前已经开源在了GitHub上:。

Timeline层,提供最终的读写接口,用户操作的也是Timeline的接口。

有了Timeline LIB之后,如果要实现一个IM或者Feed流,只需要创建两种类型Timeline(存储类,同步类),然后调用Timeline的读写接口即可。

Timeline的接口主要分为三类:

    • scan:同步范围读取

 

  • get接口一般用于读取单条消息,在IM和Feed流中可使用的场景非常少,甚至可以不使用。
  • scan是读取消息或Feed流消息的主要途径,通过scan可以读取到已经产生,但还未消费的消息。

使用之前,需要先实现一个满足自己业务特点的Message类,此Message类能表示业务中的一条完整消息。
需要实现IMessage的下列接口:

  • 消息ID的作用主要是用于消息去重,最大使用场景是IM中的多人群。对于Timeline模型,消息ID只需要在一段时间内(比如1小时或1天内)当前会话和收件人的消息中唯一即可。比如在IM中,只需要在某个会话或者群里面唯一即可,这时候其实更好的方式是由客户端生成这个消息ID。最简单的方式是客户端循环使用0~10000之间的值和用户ID拼接后作为消息ID即可满足要求。
    • 在get和scan接口中设置消息ID。
    • 使用默认构造函数,生成一个同类型实例。用于在Store中自动生成同类型实例对象。
    • 需要将消息内容完整序列号为字节数组。这个接口会被store接口在写入的时候调用。
    • 反序列化接口,这个接口会被store在读取的时候调用。

在一个IM或Feed流产品中,一般会有两个子系统,一个是存储系统,一个是同步系统。

需要为这两个系统各自生成一个Store对象。

  • 存储系统Store:存储数据时间长,数据量大,成本很重要。如果不使用读写扩散相结合的方式,那么存储系统的store可以使用成本更低的混合存储(SSD + SATA)。
  • 同步系统Store:存储受时间段,但是延时敏感,可以使用高性能存储(SSD),读取性能更稳定。
    如果首次使用,需要调用store的create接口创建相应存储表。

当在IM或Feed流产品中读取最新消息时,就是对相应同步库Timeline的范围读取(scan)。

当在IM或Feed流产品中读取历史消息时,就是对相应存储库Timeline的范围读取(scan)。

如果是推拉结合的微博模式,则读取最新消息时,就是对相应存储库Timeline和同步库Timeline的同时范围读取(scan)。

  • TET_RETRY:网络不稳定或者底层存储系统在负载均衡,可以继续做重试。
  • TET_INVALID_USE:使用方式不对,建议直接报错进程推出。

这一节会演示下如何使用Timeline LIB实现IM的群组功能。

构造两个store,一个用来存储,一个用例同步。

user_A发一条群消息:“有人吗”。

user_C读取自己最新的同步消息

上面的示例演示了如何用Timeline LIB实现IM中的群组功能。其他的朋友圈,微博等也类似,这里就不赘述了。

我们目前在上实现两个场景的实例:

也欢迎大家共享其他场景的实现代码。

我们使用阿里云ECS做了性能测试,效果较理想。
不同接口的写入性能上,批量batch接口最快,其次是异步writeAsync,最后是同步write接口。

如果使用一台8核的ECS,只需要3秒钟就可以完成100万条消息的写入。

由于DistributeTimelineStore使用了Table Store作为存储和同步系统,Table Store是阿里云的一款服务化NoSQL服务,支持的TPS在理论上无上限,实际中仅受限于集群大小,所以整个Timeline LIB的写入能力和压力器的CPU成正比。

下面的图展示了不同机型上完成1000万条消息写入的延迟:

在一台8核ECS上只需要27秒就可以完成1000万写入,由于写入能力和CPU成线线性关系,如果用两台16核的,则只需要7秒就可以完成1000万消息的写入。

我们再来看一下scan读取的性能,读取20条1KB长度消息,LIB端延迟一直稳定在3.4ms,Table Store服务端延迟稳定在2ms。

这个量级和能力,可以撑得住目前所有的IM和Feed流产品的压力。

Timeline LIB的想法来源于Table Store的真实场景需求,并且为了用户可以更加简单的使用,增加了主键列自增功能。

如果用户希望使用其他系统,比如MySQL作为存储系统,可以实现IStore接口构造自己的Store类。我们也欢迎大家提供自己的各种Store层实现,最终希望为社交场景的架构师和开发者提供一套完整的易用性开发框架。

      • 目前仅支持一种store:DistributeTimelineStore,虽然可以同时支持存储和同步,但是功能还是比较简单,接下来会支持更多的Store,比如全球多向同步的GlobalTimelineStore等。
      • 目前Timeline接口比较偏向系统层,后续会提供更偏向业务层的接口,比如适用于IM的接口,适用于Feed流的接口等。

有个写公众号的朋友H,某天在群里发问:“刚写的这篇文章朋友圈里没见谁转发啊,居然阅读量破万,涨粉上千,好奇怪啊!”问话里带着点疑惑、纳闷儿,又带着点踌躇满志、暗自得意。

诚然,公众号的文章,最初是得靠微信朋友的转发,才会有一定的阅读量。

可是,在一个公众号持续做了几年,持续提供同一类型或风格的内容后,就会有相对固定的读者群,此时的增粉,除了勤快更新高质量的内容外,大概就要靠偶尔的转型或抓取与自己公号平时关注点差异大一些的热点了。

在我看来,H有这个疑问,大概是忽略了下面这几点:

1.朋友圈的“好友”分很多种

有的就是单纯交流感情的朋友,有的是工作上的领导同事,有的是合作伙伴,有的纯粹是消费形成的关系……当然,如果换个方式来分,也可以分为让我们感到温暖的、让我们见世面的、让我们认知升级的、可以切磋技能的、可以培养爱好的等等。

就像我们不能指望跟仅供感情交流的朋友借钱一样,我们也不能指望领导同事转发自己的文章。这就已经排除掉了朋友圈转发的第一个类别。

2.朋友圈里肯定有只看视频从来不阅读文章的

毕竟当下最火热的新鲜关注点,是图片和视频,很多人逐渐形成了只读图、看视频的习惯,传统的文字阅读受到了很大的冲击,说“式微”也不算过分。

或者也可以说,是否会逐字逐句阅读,几乎成了人与人之间层次的分水岭,所以只看视频和只读文章的人群之间是有很大的区分度的。

在这样的情况下,对于单纯提供文字内容的公众号文章而言,可以说是不符合市场风口的,方枘圆凿,结果可想而知。或者说提供这样的产品,就是在区分受众了。这也是朋友圈里没人转发的另一个可能的原因。

3.阅读微信公号文章的朋友也有口味的不同

各花入各眼。有人喜欢牛奶,有人喜欢豆浆,有人喜欢咖啡,有人喜欢果汁,有人喜欢白开水。就读文章的人来说,有的喜欢轻松愉悦的,有的喜欢严肃思考的,有的喜欢娱乐八卦的,有的喜欢奋发励志的。

甲之蜜糖,乙之砒霜。一些人非常感兴趣、视为珍宝的内容,可能另一些人连看都不会看一眼。口味不同,审美不同,对文章的看法自然不同。

4.文章也像人一样同气相求

一篇文章遇到我们,在我们笔下成文,成文之后,就像出生到这个世上的孩子,ta只是经由我们出现,但出现之后就并不属于我们了,ta的未来、际遇等会有自己的轨迹,也不受我们的主观干扰。并且,ta的好坏优劣也自有评说,与我们的初衷可能大相径庭。

文章写出之后,遇到的会是ta该遇到的人,转发的则会是与之三观相合的人。与某类文章契合的人,朋友圈里可能会有,但陌生人中会比朋友圈要多得多,分享一般就在这样的分散的各个点之间线性传播中进行着。

5.人潜意识里会有“远交近攻”的后台程序

如果说“远交近攻的后台程序”不接地气的话,我们可以用“文人相轻”来代替,或许就好理解得多。

人们似乎往往对自己不认识的人突然声名鹊起很容易接受,但如果身边某个默默无闻甚至自己瞧不上眼的人有一天一下子中了千万彩票,或是因某个契机脱颖而出,把自己衬得灰头土脸的,就很少能有人泰然处之,一般是没法继续做朋友的。

写文章水平相近的人很难互相欣赏,比如张爱玲和苏青之间。其它方面或者行业,也大都如此吧。

欣赏,常常是要拉开距离,或者年龄,或者层次,或者水平,才可能出现。否则,很难会产生。

6.文章题目、封皮等对点击率和转发量都很关键

有的题目,一眼就能勾起人的阅读欲,但有些题目,一看就让人觉得“不正经”,如果转发的话,容易产生道德压力或会带来不必要的麻烦,很多人害怕“人设”崩塌,点开读了就到此为止了。封皮那关键的几句话有时也会决定看到的人是否愿意点开或转发。

总之,吸引眼球、直击痛点但又拿捏好分寸、写得酣畅淋漓的文章,才会让人一气呵成地点开、阅读、分享。

7.有种阅读叫“私密阅读”

不知大家是否有这样的体验:限于前述种种原因和某类顾虑,有些特别好的文章,哪怕是自己读来击节赞叹的那种,囿于一些限制,或者念及避免风险,特别想“奇文共赏”,就悄悄地私信发给自己特别信任或者合拍的人。

如果恰好对方也对此文感兴趣,就会热烈探讨一番,或默契赞赏,或各抒己见,瞬间就能抛却尘世疲乏,如二人寻一清静幽雅处席地而坐,清茶一杯,悠然自得,“目送归鸿,手挥五弦,俯仰自得,游心太玄”。

对,我称这种阅读为“私密阅读”,很有阅读快感的那一类,“私密阅读”肯定是不会分享到朋友圈的。

8.针对同频共振的陌生人写作,才是自媒体写手主攻方向

像谈恋爱一样,适合某个人的人是某一类人,数量是相当可观的,爱读某一类文章的人,也是很多很多的。

就我自己的写作传播体会而言,微信好友中常常转发我文章到朋友圈的,绝对是真爱:要么是特别爱我这个人,要么是非常爱我的文。无论哪一种,都值得我格外珍惜。

持续写文很久、连续公开发文有一阵子的我,在写文发文以及跟读者互动的过程中逐渐发现,单靠朋友圈熟人传播毕竟受众有限——或许微信号上有几千“好友”,但真正读自己文章的人可能连1%都没有——想让自己悉心写出的文章有更多的人看到,最终还是要结合自身特点,预设合适的读者群体,然后靠文字功底、对生活的思考、对热点的把握等,持续提供某一类精神食粮,或者餐后甜点。

如果这些暂时还没考虑清楚,那就像我一样,先写着吧,写着写着,有些问题的答案或许就自己跳出来了。

今天接着复盘一个朋友圈文案,这个案例也是我学习过来的,有句话不是说吗?

学到的就要教人,赚到的就要给人;

同时也是真心觉得这个剧情式的卖货文案不错,于是就借花献佛,复盘一下这个朋友圈文案,因为不管是做微商、做电商也好,想在朋友圈卖货,做好微信朋友圈营销,都要学会一件事,就是软文植入广告,达到卖货赚钱的目的。

但是道理都知道,就是不会用,没有方向,那么这套经典的6+1剧情式带货朋友圈文案,一定是你菜,别错过。

首先6+1=7,也就是说想要卖爆货,至少要有这7个过程,寻寻渐进,不能着急,很多人容易犯的误点就是,一上来就是硬广,如果你朋友圈的人对你还不熟悉,你也没有去培养信任感,就想着发几条广告就能把产品卖出去,简直就像是天方夜谭,基本没可能。

先分享这个社群发售的案例,具体看文案话术,来拆解一下,找到它发圈背后的思路;

文案:XX群,5.18号要开群了,此刻的你肯定希望微信给你一个特别提醒的功能,前2个群没有来的及上车的,这次抓紧了。

此群和前2个群一样,群+知识星球+直播间,点赞等我特别提醒噢!

配图:另配了3张图片,一个是群公告的图片,还有2张是前面2个群的群二维码图片。

拆解:案例背景分析完之后呢,我们现在来拆解了,从这条朋友圈里你能获得什么信息呢?你发的朋友圈和他的朋友圈有什么不同的地方呢,可以对比一下;

第一:这个开群的时间已经提前公布了,是5月18号;

第二:群的内容形式也告诉大家了,群+星球+直播间,又配合了3张图片,

第三:具体的行动指令有了,就是想进群的要先点赞,等特别提醒,这样可以第一时间收到开群的信息。

OK,那总结一下,这条朋友圈的特点,就是这3个:时间+内容的期待+行动指令

所以,第一条发圈的要点是:

微商社群转型新思维――朋友圈文案

文案:针对本人朋友圈发个福利,3群这个月5月18号开群,送三张门票,中的微信私聊我,没中的可以点赞等通知噢~

配图:一个可以抽奖的图片和第一条朋友圈的截图

拆解:第二条朋友圈看完,你同样能收到什么信息呢?

第一:这是一个福利朋友圈,也就是说有机会中奖的可以免费进这个付费的社群,具体方式就是扫图一的抽奖码

第二:强调开群时间,5月18号;

第三:就算你没中奖,也可以点赞等通知,第一时间知道开群的通知。

那么总结第二条朋友圈的特点,就是时间加内容的期待,免费福利,还有行动指令,就是扫码加点赞。

所以,第二条朋友圈的要点是:

行动指令:扫码抽奖+点赞

微商社群转型新思维――朋友圈文案

文案:一个活动8万5付费用户,公众号涨粉8万,路径体验极好,星球复盘见!

图片:一张对话截图,一张收款8万5的截图,2张公众号涨粉的截图,一张活动表,一张星球二维码图片,总共6张。

拆解:同样的,这条朋友圈又能得出什么信息呢?

第一:很明显这是一条,只有结果,没有答案的朋友圈。

第二:也就是说他有这样一个案例,一场活动带来了8万5的付费用户,公众号涨粉8万,而且强调路径体验很好,再接着一句,复盘文星球见。

第三:如果说,你在看到,8万5,8万粉,很好,这样的内容描述,是不是会很好奇,他是怎么做到的,那么你想知道吗,很想知道吗,想知道就扫码来星球吧,所以第6张放的就是星球的入口,扫码就可以进了。

那再回到第一条朋友圈,是不是更加的期待了,群+直播间+星群,说明星球也是很有货的,是有价值的,也就是说买了这个社群,可以享受到这么多超值的服务,那是不是有人开始要按耐不住了呢,同时更加的期待这个社群。

那么总结一下,这条朋友圈的特点,也就是它只有结果,但是没有答案,如果你想知道我是怎么做到的,可以直接扫码去购买这个星球,看看星球里面的复盘内容,整个节奏同样是提高用户对内容的期待,并且发出的行动指令,

第三条朋友圈发圈的要点:

行动指令:扫码付费进入星球

微商社群转型新思维――朋友圈文案

文案:注意!第1群今晚开小灶,平台流量Or社交流量,准备好笔记本。

图片:一张流量笔记表,一张流量图,一张2个手机图

拆解:这条朋友圈文案很简短,但是信息量很大,你从这条朋友圈里得到了什么信息?

第一:开小灶,平台流量Or社交流量,准备好笔记本,这是一条很有价值的干货分享

第二:如果你不在这个群里,你会损失很大

第三:如果你想听这个内容,很感兴趣,是不是就很想进群听呢,那么你就会评论,你能不能进群呢?

同样总结一下,这条朋友圈的特点,就是告诉大家一个讯息,我的社群很有价值,我不但有直播间,还有星球,而且还会再群里开小灶,这就是在提高用户的期待值。

第四条朋友圈发圈要点:

微商社群转型新思维――朋友圈文案

文案:技不如人,甘拜下风。

图片:一张对话截图,2张新增了31万用户的前后对照图片。

拆解:同样,这也是一条只有结果,没有答案的朋友圈。结果用图片的形式表现出来了。

说的是社群的群友按着实操方法,做到了新增31万用户,但是怎么做到的,在朋友圈里我不会告诉你,如果说你在这方面是想要去了解具体的原因,那你就会很好奇,这样朋友圈也就是暗示你说这个群里,优秀的人很多,并且大家都在实际操作;

如果说你进入到这个群里了,你还有机会和这样优秀的人一起去交流沟通

那总结一下这条朋友圈的特点,其实也是做了用结果证明,用户对结果的期待,就是告诉大家,如果说你加入我的社群,你可以接触到什么样的人,你可能会做成什么样的事情。

第五条朋友圈发圈要点:

微商社群转型新思维――朋友圈文案

文案:【开群通知】3群已开群,同1、2群一样,微信群+知识星球+直播间,一票通。

目前刚开群,已有100人预定,空位400,扫码图6,付款截图发我,晚上统一拉群。

图片:一张群二维码图片,一张知识星球成员图片,一张直播间课程图片,两张群聊开小灶,还有星球的复盘图片,最后一张付款图片。

拆解:这条朋友圈是告诉大家哪些信息呢,

第一:我的社群已经到时间开群了

第二:买一张票就可以获得群+直播间+知识星球,1个价格,3重价值,感觉特别超值。

第三:群里还会有额外的小灶,还会有非常牛逼的复盘。

第四:表示社群的火爆,也就是说这个群只有500个名额,虽然说今天才开始,但是之前就已经有100个名额被占了。所以现在只剩400个,你需要赶紧去行动,

第五:群的付款码,你可以直接扫码支付,再截图给群主入群,你也可以参加学习,买到这个社群了。

所以第六条朋友圈其实就是一个产品转化的一个官宣,正式到了成交环节。

但是在这个成交推广之前,已经做了足够的预热,并且前面报名的情况很火爆;其他有意向加入的小伙伴已经付款了。如果说你要付款的话,就是直接扫码截图去私信,就OK。

那总结一下这条朋友圈特点,是做了用户对内容的期待,还有非常直接的行动指令

那么到了第六条,是有一个点需要注意的,也就是前面5条有点赞评论的朋友圈,一定要再去回复一下大家,通知大家已经开群了,具体看后面新的朋友圈,这样有兴趣的人就会去翻看你的朋友圈,会让更多的人知道了解你的社群,因为不是每一个人都会刚好看到你的这个信息的,所以要去再次提醒。

第六条朋友圈发圈要点:

行动指令:扫码付款后私信

微商社群转型新思维――朋友圈文案

文案:每一哪个群主会推荐别人的社群,他推荐了我,还打了这么多字,+表情符号。

图片:群聊的对话截图,评论区截图,知识星球的截图,三张图片。

拆解:同样的这条朋友圈你又看到了什么信息呢?

在前面的6条朋友圈说的只是群里很好,案例很丰富,牛逼的人很多,但是现在这条朋友圈是通过第三方的角度告诉你,这个群真的很好,并且这个人这个群里的群友愿意在自己的群再去推荐别人的社群,

那么,同样总结一下,这条朋友圈的特点是,再次强调我的社群很好,并且是第三方证言,更加有说服力。如果说你的社群不是真的很好的话,别人是不会做这样的推荐的,所以这里是通过其他人的评论告诉你,我的社群真的很不错。

第七条朋友圈的发圈要点:

微商社群转型新思维――朋友圈文案

那么以上总共7条朋友圈,也就是关于6+1剧情式朋友圈的经典案例就已经分析完了,不知道看完这几条朋友圈有什么感受呢?

每条朋友圈我都做了一个发圈要点,如果你注意,就知道,有2个关键词。

一个是用户期待,一个是行动指令

第一点:这个用户期待怎么理解呢?

就是说你发布的朋友圈内容不能乱发,你要时刻传递一种信息,让用户很期待,从不了解,到了解,到有兴趣,到很想买,你是要一步步的设计和影响看你朋友圈的人,而不只是自嗨。这个关于发圈的4大误区,上一篇文有讲到。朋友圈复盘:了解朋友圈用户心理,发圈的这4大误区你中了几条?

你要让用户对你产品的内容,对产品可以购买的时间,对产品能够带来的实际效果产生期待,你要去带着用户跟着你去走,然后去让用户对你的产品充满着想要购买的欲望。

第二点:行动指令是指什么?

也就是说你要让用户做出具体的动作,和你去产生链接,这个动作可以是评论,可以点赞,也可以扫码。因为我们经常是说刷朋友圈,也就是说别人的内容都是一扫而过的。

如果你能在吸引到别人注意力的情况之下,还能让别人付出相对应的时间成本,或者说非常简单的体力成本,那是不是说他对你朋友圈的内容就会比对其他人朋友圈产生更加深刻的印象。

所以,你是要持续的取发出行动指令,你发这条朋友圈的目的是什么,是想让对方点赞,扫码,还是付款,你都要直接的把这个指令说出来,同时行动的路径一定要非常的短,能动一个手指做到的,决不动两个手指,能一个字说完的绝不说两个字。

而且,如果你注意到了文案的话术,是可以看到,这些文案,都说的很精简,表达的意思很明确,而且都是大白话文,没有出现和产品一点相关的专业词和说明,不知不觉中,就像看电视剧一样就从头到尾的把你的产品植入了用户的大脑,先放一点回看,在转折处结束,再放一点下一集的预告,这样的剧情式朋友圈是很吸引人的;

那么通过拆解,我们要学的不能只是他的话术和图片,文案格式,而是发圈背后的思路,为什么要这样发?这样发的好处是什么?我能从中学到什么?

那么关于6+1剧情式的朋友圈发圈要点最后总结一下,说的简单一点,就是第一不断提高用户的期待,第二,持续的下达简单的行动指令。

微商社群转型新思维――朋友圈文案

可以看到,虽然说这两个点,很简单,但是他们的带货能力是超强的,那剧情式的发圈原理又是什么呢?

它的发圈原理就是,我们说要营销的话,最讲究的是一个影响力,而这个发圈原理就是利用了营销的七次法则,也就是说,你持续影响一个人7次,就能达到一个比较好的效果。

那么你在朋友圈营销的时候呢,你需要给能够影响别人的时间,首先就是要让人先知道你有这个产品,先了解,到认可,到相信,最后愿意买的一个过程,所以在朋友圈营销不能着急。

可以换位思考一下,如果你在线下的门店买东西,你看到一个新的牌子,一个新的产品,你也不会马上就买对吗,是不大可能第一次就直接消费的;

你经常路过这里,然后时不时的会看到她们产品的活动,或者客户很多,回头客也很多,店员又很专业,而你又正好需要买,这中间肯定是有一段时间的,先让产品走进用户的眼睛、然后植入大脑,最后走进心里,才有买的可能;

微商社群转型新思维――朋友圈文案

那么,在朋友圈营销更是这样,都是隔着屏幕没有见过面的陌生人,你想通过一次发圈就达到销售卖货的目的,想让别人直接付款,这是很难的。

这跟你产品本身多好,没有多大的关系,重要的是要通过影响力的传播,传递你产品真的很好的信息,如果说你影响的次数不够,是很难在用户心中留下一个比较深刻的印象的。

OK,写到这里也差不多了,不知道这个剧情式的带货文案,看的过不过瘾?

其实,对这个6+1的朋友圈文案,还是有点要求的,因为不是每一个都有这么好的案例,和基础,去做沉淀和积累,再来一个爆发的;

那么我们一般人,也就是刚入行做微商,或者做朋友圈营销的朋友,会觉得这个6+1太长了,学不来,那有没有简单一点的文案模板呢?或者是提供一些发圈的素材,因为总是不知道要发什么,写什么?

这些都是在发朋友圈会遇到的问题,OK,我们后面的文,再接着分享。今天先写到这里,我到小帅,一个默默做内容的输出者。

我要回帖

更多关于 微博私信聊天记录找回 的文章

 

随机推荐