计数服务怎么设计?
1.计数的特点?
● 请求量特别大:无论是作品维度的,点赞数,评论数,收藏数,还是用户维度的关注数,粉丝数都有较大的访问量,所以要尽量支持高并发的读取
● 写请求量大:点赞,点踩,评论,收藏,用户参与的成本很低所有会有较高的并发量
● 非产品的绝对强依赖:与用户钱包账户余额不同,在极端情况下允许部分数据丢失
● 对数据的精确性要求与数值的增加成反比:999->1000,10000->20000,不在显示个位数了
2.关系型数据库困境?
Mysql
存储引擎
InnoDB
ACID,一致性,
count(1) 扫描,太笨重
MyISAM
总行数保留到磁盘
不可取,不仅不支持事务,而且最小的锁粒度是表锁
3.是否使用关系型数据库?
通常来说,
关系型数据库无法面对写压力,可以使用异步的情况进行更新,先更新缓存,再使用消息队列异步通知关系型数据库进行异步的数据更新;
如果无法应对读的压力呢,可以使用缓存对外提供服务;
但是呢?计数的读写量大几乎每时每刻都在与缓存系统直接交互,这样看来在维护关系型数据库有点多次一举,在我看来直接将Redis当做数据的存储系统也是不错的方案,天然满足了高并发的能力要求,而且减少了非必要的数据存储;
4.内存使用优化
一开始是count_{content_id}_comment -> hash存储方式
hash在key:value较少的时候节约内存,在filed小于512个时,且大小不超过64个字节时,Redis底层会使用压缩列表的编码格式来实现hash对象
hash对象的filed不会超过512个,计数的value也大多是int64类型,所以使用这种结构刚刚好;
5.冷热数据分离
为了避免资源和用户活跃度此消彼长的状态呢,可以使用LRU,LFU算法将最近少用的,最近低频的数据进行迁移,
如何区分冷热数据呢?不同的业务场景区分是比较大的,用户维度的计数,可以认为有较多粉丝,较为活跃的用户是热数据,对于作品维度呢,可以认为一年内的作品都是热数据,发布时间较久的数据为冷数据,
6.如何应对过热数据?
在某热点事件发生的时候,计数系统会特别繁忙,可能会导致请求大量请求同时打到同一个Redis节点上,为了避免出现意外呢,异步写,写聚合:
先把请求放入消息队列中直接成功返回,接下来创建消费者,读取队列中的一定数量的请求,然后呢,将这些请求中指向同一个Redis key 的请求聚合成一个Redis的命令,使用lua脚本,将多个请求聚合为一个请求写入
7.不同场景不同选型
如果是和金额相关的场景呢,就可以踏踏实实的使用关系型数据库,对精确性不那么严格的场景就可以使用基于内存来做: