QQ看点内容中心存储系统介绍

383 阅读6分钟

 存储系统对一个产品的数据安全、服务可扩展性有着重要影响,今天结合信息流内容的特点,介绍信息流产品内容处理系统的存储选择以及对应的考量。下图是内容处理系统在整个信息流产品中的位置:

内容信息包括2部分:

  • 静态字段:包括标题、正文、封面url、发布时间、作者、正文图片url等,自媒体通过文章创作平台发布后,就不会发生变化。
  • 动态字段:针对内容处理的结果字段,例如,内容的分类、tag字段,内容是否低俗、优质的过滤字段,内容是否和历史内容重复、重复内容的rowkey字段。

 为了方便工程和机器学习算法处理,文章的静态字段和动态字段最好是保存在一张宽表里面,从多张表获取信息会增加处理的复杂度。

存储系统的考量维度

 内容推荐有时效性限制,一般来说,图文是3天,图集1个月,视频3个月。对于超过3天的图文,即使处理了也进入不了推荐池,所以能根据时间区间检索文章是一个很高频、很急迫的场景。

 下面是业界常用的一些数据库,从几个方面进行了比较,我们最终选定HBase作为信息流内容的存储。HBase既可为在线服务提供实时KV读写(若数据都在内存,4核8GB内存云服务器,可提供>= 1万的QPS),也可为离线任务提供批量Scan条件查询。

HBase相关介绍

 HBase底层文件和Leveldb、Rocksdb一样,都是参照Google的BigTable数据存储系统来设计的。

  • 写操作: 先往WAL(Write Head Log,HLog)中追加一条记录(使得宕机后可以恢复缓存中的数据,顺序写日志文件,操作系统的文件系统控制落盘时机),然后往内存MemStore写数据后就返回,速度非常快,待MemStore满了之后再落盘到StoreFile(HFile)。
  • 读操作: 涉及Merge操作,速度比较慢。比如,先前针对一个key,先设置一个0,然后加5、再减2,在HStore里面有三条记录,从近到远分别是“减2”、“加5”、“设置0”,其中“设置0“是一个”终态“,HBase只有读到了”终态“才会停止往前读,Merge这三个操作,最终读出来的值是3,并将”终态3“设置到HStore里面,下次读取的时候不用再Merge了,直接读取到值3。

HBase分布式扩展

 从单机到分布式扩展,无外乎通过以下两个手段:

  • 分片,解决单机无法存放和高效处理的限制。
  • 多副本,解决单机故障、数据丢失的风险。

 HBase也是通过分片(HRegionServer)、多副本(HDFS)来扩展Leveldb、Rocksdb,从而实现单机到分布式的跳跃:

  • HRegionServer负责本Region的数据读写,通过Zookeeper和上层HMaster建立联系。HMaster负责所有HRegionServer的容灾和负载均衡工作,当某台HRegionServer宕机后,HMaster可以通过Zookeeper得到通知,将上面的HLog拷贝到其他机器、并回放来实现数据迁移,不过,迁移期间对应数据无法访问。当某台机器负责过高,也通过类似的方式实现数据迁移。HMaster一般3台机器,通过Zookeeper来选择其中一台作为Leader,从而实现高可靠性。HBase的元数据也是保存在Zookeeper的。
  • 客户端通过HBase的API(Java API)访问数据,首先是和Zookeeper通信建立连接,获得数据所属的HRegionServer信息,然后直接和其建立连接进行数据的读写工作。从这里可以看出,HMaster不负责对外提供数据读写工作。
  • HBase底层使用HDFS来实现数据的多副本存储。HDFS分NameNode(SecondNameNode)和DataNode,NameNode起管理角色,包括数据块映射、副本策略、命名空间,其宕机后由SecondNameNode负责接管。DataNode存放数据并负责实际的数据读写。同一份数据,先由NameNode确定好存放在哪几个DataNode,然后先转发给就近的DataNode进行写入,再由DataNode分发给其他副本DataNode,只要有一份写入成功,则返回成功,再通过异步机制保证剩余副本成功写入。(若某个DataNode宕机无法写入,则先由NameNode重新选择分配一个新的DataNode,然后将存量和新增数据迁移过去。)

HBase Rowkey的设计

 HBase只支持一级索引,所有需要索引的信息都需要反映到Rowkey里面去。目前我们采用的Rowkey设计如下:

 Rowkey已经作为全部门统一的文章ID,极大的方便了业务处理,包括前端用户行为数据、后端B侧帐号文章数据、多App表现数据等,通过Rowkey关联起来,提升用户行为分析和相关数据挖掘效率。

HBase Scan和Get、Set

 Scan操作对HBase的RegionServer资源消耗较大,实际中尽量少用,原先我们服务中多处使用了Scan操作,对HBase压力较大,同时加大了单Key的读写延时。后来内容处理系统架构改为Workflow Engine后(为行文方便,以下简称WE),只有WE的Spout模块存在Scan操作,其他所有模块都不允许有Scan代码存在(再后面已经不让有任何HBase读写操作了)。

 内容处理涉及的环节和模块较多,工作流的中间状态建议缓存到Redis集群,待一个工作流完成后再统一Merge到HBase中。先前各个模块自由落盘(到HBase),70%的HBase资源消耗在内容处理环节本身。就像一个发电站,若发出来的电绝大部分都用在维持发电站自身的运作上,显然是不合理的。内容处理系统调整为WE架构后,消除了对HBase的浪费使用。

HBase前面再挡一层

 随着外部对HBase的访问增多(包括CMS,推荐系统重建内存数据等),单靠HBase自身的缓存已不能应付,我们在HBase前面加了一层Redis集群作为Cache,由于涉及到读写一致性问题,复杂性增加很多,后面单独开篇讲解。

HBase的Replication

 内容处理会涉及到离线分析任务,需要批量提取文章原始信息和处理结果信息,它们对及时性要求不高,能够容忍一定的延迟。我们通过Replication机制,将文章数据从主机同步到备机,离线分析任务只访问备机,避免干扰主机的线上实时服务。

总结

 存储的地位举足轻重,选择合适的存储对业务发展至关重要。同样的,也不要忽视字段的设计和管理,HBase可以方便灵活的增加列,几乎零成本,正因为如此,需要有统一的地方进行字段名称的分配,否则会增加额外的映射和沟通成本。

文章首发于微信公众号【jameswhale的技术人生】