《深入分布式缓存:从原理到实践》学习笔记(4)

第十二章 社交场景架构进化:从数据库到缓存

12.1 社交业务示例

  • 业务的点:海量数据、高访问量、用户分布的非均匀、时间分布的分均匀、用户+时间的非均匀分布

12.2 关系(relation)的存储

  • 问题引入1

    • 对于热点用户的count查询是低效的;timeline页面展示是低效的却被高频访问

    • follower较多时需要分页,然后DB在实现分页时效率随着offset增加而降低。

    • 用户信息的展示,需要通过follower或者followee分别查询inof表,使得info的查询服务能力无法随着info分片线性增加

  • 解决问题1,引入缓存

    • 将关注与被关注数加入userInfo表

    • 与关系相关的两张表的value变成列表

    • 由于info和count展示场景不同,key频度也不同,所有分为两个表

    • userinfo访问频度极高,所以放在本地缓存中

  • 问题引入2

    • 热点用户的follower详情页查询有极高的网络传输量,且频繁。

    • info查询的multi-key问题没有完成解决,本地缓存的userinfo不能解决本质问题,大部分用户是非热点用户

    • info查询中followee和follower的数量统计期望实现秒级别的数据延迟

  • 解决问题2

    • 将关注、取关的增量操作,放到增量列表中;

    • 独立计数服务,将统计的数据量持久化到缓存中,则count就变成了O(1)的get操作

    • 计数服务从增量列表中获取数据,进行写操作

    • 在做热点用户的follower/followee的列表查询时,针对前N页的查询(99%的情况),直接从增量数据中获取。

12.3 帖子(Post的存储)

  • 基于DB的方案

    • 查询优化:将userId+postTime组成的二级缓存,组合成postId的形式

    • 吞吐量优化:水平拆分,应对单击DB数据量和吞吐量上限问题

  • DB存储方案的问题

    • 读写分离后的延迟问题

    • 冷热数据的分离问题

  • 引入服务端缓存

    1. key-value的选型:由于社交网络中一个用户一周发的帖子总长度元低于redis中value的上限,则

      1. key:userId+时间戳(精确到星期)

      2. value:redis的hash类型,field为postId,value为帖子内容

      3. expire设置为1星期

  • 引入本地缓存,解决热点数据访问造成的单点问题

    • 缓存顶层key为用户id,value为近期发布的帖子,以FIFO的原则;key的逐出规则基于LRU

    • 对于分散在不同服务器上的本地缓存,固定的间隔周期(1秒)会访问一次服务器。

12.4 时间线(timeline)的存储

  • 基于DB的方案

    • push模式:用户每次新增一条帖子都将其push到该用户的follower所在的分片上,post表和timeline表根据用户id进行水平拆分

    • pull模式:不新增timeline表,写操作没有额外开销,但是读操作需要对所有followee的帖子进行时间扫描

    • push/pull结合:

      • pull模式存在timeline查询压力大的问题;push模式存在热点用户帖子发布后的复制份数不可控问题。

      • 如下图,无论一个用户有多少follower都只复制100份,控制了副本数;每个数据库按照followee分片,且不同的follower从不同的数据库的分片中查询,降低DB查询压力。

  • 增量查询引入服务端缓存

    • 每个列表中的元素为用户ID,即A、B、C等

    • 以B+数的方式存储

    • 间隔0.5秒将有发帖的用户id写入该缓存列表

    • 每10秒产生一个新列表

 

第十三章 缓存在社交网络Feed系统中的架构实践

  • Feed:类型微博的信息条目在技术上也称之为status或feed,核心技术包括:

    • Feed的聚合和分发

    • Feed信息的组装与展现

    • 用户管理管理,即关注/粉丝关系管理

  • 推拉混合模式

    • 用户A发帖被写入到消息队列 - > 从消息队列读取帖子到用户A的Outbox

      - > 基于性能考虑,部分个性化数据push到目标用户Inbox

    • 用户访问时,系统将用户自己的inbox和TA所有关注人的outbox进行pull并实时聚合

  • Feed系统架构

13.2 Feed缓存模型

  • 类似微博访问首页的一个请求,可能需要到资源层获取几十数百甚至上千个的资源数据,并聚合组装出最新若干条微博给用户,所以一个好的缓存架构占有重要的位置

13.3 Feed缓存架构的设计

  • 大量热数据集中访问时,会导致缓存服务节点过载,单节点不能承载热数据的访问量(如明星发表微波所在的节点)。

    • 引入L1-Main-HA的三层架构模式:

      • L1层容量较小,冷数据会较快的被移除。且L1有多组,大量的热数据访问会平均分散到多个L1

      • Main为主要的缓存层,处理大部分的缓存数据请求

      • HA层保证Main节点不可用或宕机时,能够继续提供服务。一般相邻IDC(数据中心)的Main层互做对方的HA层,以降低机器成本

  • 集合类数据的缓存设计

    • Redis提供了丰富的value类型和api,用于服务端的计算。如微博中的关注列表、双向关注、共同关注等,需要分页获取、计算关系等操作。

    • 活跃用户的复杂关系计算性业务(如我关注的人里谁也关注了TA)进行预先计算,并将中间结果存放到memcache中。

    • memcached和Redis的混用:

      • 对于大集合数据的全量读取,由于Redis为单进程/线程模型,响应结果的拼装和发送是一个重量级操作,若要保证峰值的可用性只能增加slave数量,成本开销较大

      • 将Memcached作为Redis的前一层,抗读。其多线程实现以及纯粹的二进制kv高校读取,可以用较少的Memcached内存获得较大的性能提升。

  • 针对无法直接缓存的业务数据类型(如“是否赞”、计算类业务,短短几天的存储成本都是巨大的),需要定制化的开发缓存组件。

13.4 Feed缓存的扩展

  • 计数服务的扩展:

    • 一条微博至少3个计数查询,单条feed的各种计数存储在一起(1000亿 -> 330亿)

    • 近一半微博没有转、评论、赞,计数为0的微博不存储(330亿 -> 160亿)

    • 存储结构优化,三个计数和key=3*4+8=20字节,则总共只需要 16G*20=320G,1主3从的模式,则为320G*4=1.25T

    • 总体特性:

      • 内存优化:预先分配Table数组存储技术,并采用double hash解决冲突

      • Schema支持多列:一个feed id 对应多个计数可作为一条计数记录

      • 冷热数据分离:热放内存和冷放磁盘

      • LRU缓存:被频繁访问的冷数据放到LRU缓存

      • 异步IO线程访问冷数据:冷数据的访问不影响整体性能

  • 存在性判断扩展

    • 采用基于Bloomfilter的方式,将一个元素通过k个相互独立的hash函数映射到bit数组的不同位置,当判断一个元素是否存在时,只要经过几个hash函数得到的bit位上的值都为1,则得到true。

 

你可能感兴趣的:(《深入分布式缓存:从原理到实践》学习笔记(4))