计数系统架构设计(转)

文章探讨了在大数据量、高并发场景下如何高效查询计数,并提出计数外置、使用缓存提高查询性能以及应对计数属性增加的策略。通过设置冗余计数表和利用MQ解耦更新,以及利用Redis缓存优化读写操作,解决了查询压力和一致性问题。同时,讨论了横表和竖表设计的选择以及MongoDB在处理频繁修改数据时的作用。
摘要由CSDN通过智能技术生成

本文主要节选和总结自沈剑大佬的 计数系统架构实践一次搞定 | 架构师之路和文章的评论,略有删改

一、问题描述

很多业务都有“计数”需求,以微博为例:

微博首页的个人中心部分,有三个重要的计数:关注了多少人的计数、粉丝的计数、发布博文的计数

微博首页的博文消息主体部分,有四个计数:转发计数、评论、点赞、浏览计数

本文主要讨论:数据量大、高并发情况下怎么高效查询?计数属性增加怎么快速扩展架构?

二、架构设计

不同的属性计数,分别用一张表来记录,比如博文转发表,新增一个转发则新增一条记录、博文评论表、博文点赞表新增一条评论或者点赞都在各自的表里新增一条记录,用户关注表、用户粉丝表、博文表、收藏表等,也是新增粉丝或者新增博文就在对应表里新增记录。

每个用户的三个计数值需要发起三次count查询,每篇博文的4个技术值,也需要4次count的查询。高并发下数据库压力较大。

缺点:每个用户需要3个count查询,首页十多篇博文,需要几十次count查询

2.1 计数外置

额外冗余设置 2 张计数表,分别用来记录用户维度和博文维度的计数,每个计数属性在计数表里是一列字段。比如上面的微博业务可以抽象出两个表(用户计数表和博文计数表),针对这两个维度来进行计数的存储:

t_user_count (uid, gz_count, fs_count, bw_count);

t_bw_count (bw_id, forword_count, comment_count, praise_count);

查询

每个用户的计数只需要查一次用户计数表即可,微博首页每条博文也仅需要查一次博文计数表即可,甚至可以用 in 语句把所有博文的计数放在同一条sql里执行,这样微博首页所有博文计数仅需一次查询即可。

更新

MQ 消息解耦,每当发生一个计数更新的事件,比如点赞事件、转发事件、都给 MQ 发送一条消息,由下游服务负责监听消息,给对应事件的表里增加条记录,更新总的计数表,如果有缓存,需要淘汰缓存。

2.2 加入缓存提高查询性能

像关注计数,粉丝计数,微博消息计数,变化的频率很低,查询的频率很高,这类读多些少的业务场景,非常适合使用缓存来进行查询优化,减少数据库的查询次数,降低数据库的压力。

缓存中的数据结构是hash,一个实体是一个hash,hash实体的 key 是{用户id拼接上:user} 或者{博文id拼接上:bowen},hash 实体每个元素的key是具体的属性,value是对应的计数值。

hset 123:user follower 10
hset 123:user fanse 20
hget 123:user // {"follower": 10, "fanse": 20}

一次 mget 网络操作即可拿到该用户或者该博文所有的属性计数值。

2.3 一致性不高的业务场景可以用缓存提高写性能

对于写多读少,一致性要求不高的计数,也可以先用缓存保存,然后定期刷到数据库中,以降低数据库的写压力。或者发生 50 个计数事件就同步一次 db。

写操作需要先 mget 查到原来的值,然后 mset 设置新值。

2.4 计数属性增加

在数据量很大,并发很高的情况下,如果想在冗余计数表中增加一个字段,比如用户计数表中增加一个收藏博文数,会很难。好的方式是以下量两种:

1、冗余的计数表设计时预留好字段

2、冗余计数表采用竖表设计而非横表设计,把列扩展变成了行扩展。即计数表的每个计数属性值不再存在同一条横向记录里,而是把每种属性的技术值单独存一条记录,这样每次增加属性,就只需要增加一条记录即可。

三、问题

问:计数外置,增加冗余计数表,怎么解决数据冗余必将引发的数据一致性问题

  • 对于一致性要求比较高的业务,要有定期 check 并 fix 的机制,例如关注计数,粉丝计数,博文总数计数等。check 机制包括定时任务扫库、日志扫增量、MQ 延时消息实现定时任务
  • 对于一致性要求比较低的业务,即使有数据不一致,业务可以接受,例如微博浏览数,微博转发数等

问:一致性要求高的场景能不能用 redis 来执行写操作

对于缓存维护计数定期更新到数据库的方式,数据库的数据存在一定的滞后性,如果缓存发生重启,从db恢复数据时可能会丢失部分数据。对于一致性要求比较高的业务,不太合适。

一般不建议把缓存当数据库来用,一是 redis 持久化不如 db 好,重启有可能丢数据;二是缓存持久化频繁,会降低性能。如果非要用 redis 来提高写性能,最好提高 redis 的持久化性能。

缓存计数自增的命令是:INCRBY,可以保证原子性。

问:能不能只用 redis,不用数据库

不太建议

问:如果把冗余计数表从横表换成竖表,那岂不是会导致记录数量扩大好几倍

是的,但是数据量可以很容易通过水平扩展的方式来解决。

问:博文的评论这些是不是用 mongo 来存比较好

可以使用mongodb来应对频繁修改表的需求

问:横表和竖表怎么选择

横表显然记录条数会比较少,查询相对较快,竖表记录条数会变多,查询相对较慢,如果表结构相对稳定就使用横表,如果表结构可能频繁变更,就用竖表,大部分根据经验来判断。比如配置性的数据用竖表就非常合适。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值