1.计数器使用场景
微博的计数场景有:
微博的评论数、点赞数、转发数、浏览数、表态数等等(热点数据,经常查询)
用户的粉丝数、关注数、发布微博数、私信数等等
\
不好的设计:
把计数与微博数据存储在一起,这样每次更新计数的时候都需要锁住这一行记录,降低了写入的并发
点赞数不需要展示谁点赞,因此不用记录点赞行为(业务保证)
2.计数器的业务特点
数据量大
访问量大,对系统性能要求高
可用性和数据的准确性要求高
3.高并发计数系统如何设计
第一阶段:使用MySQL存储
将微博id,点赞,评论,转发计数下来
select repost_count, comment_count, praise_count, view_count from t_weibo_count where weibo_id = ?
\
第二阶段:MySQL的分库分表
单表的存储量级达到几千万的时候,性能上就会有损耗
因此采用分库分表的方式,分散数据量,提升读性能(写性能不变化在master上写,读会经过一次hash,查询复杂度logn,n越小查询越快)
\
怎么分库分表?
1.哈希算法对 weibo_id 计算哈希值,然后根据这个哈希值计算出需要存储到哪一个库哪一张表中
2.按照 weibo_id 生成的时间来做分库分表(ID 的生成最好带有业务意义的字段,比如生成 ID 的时间戳)
\
最近的数据是热点数据,因此按照时间分库分表
hash会使数据分布更均匀
\
第三阶段:使用Redis+MySQL
通过redis来加速读取性能,将redis也切分成多个分片
致命缺点:无法保证数据库和缓存数据一致性
第四阶段:完全使用Redis存储
将所有数据存储在redis中,异步加载到数据库中(兜底)
\
第五阶段:消息队列+Redis存储
使用消息队列削峰填谷
同时可以将多个写请求合并成一个写请求,提升写入性能
4.如何降级存储成本
1.可以优化原生redis数据库
- 一是原生的 Redis 在存储 Key 时是按照字符串类型来存储的,比如一个 8 字节的 Long 类型的数据,需要 8(sdshdr 数据结构长度)+ 19(8 字节数字的长度)+1(’\0’)=28 个字节,如果我们使用 Long 类型来存储就只需要 8 个字节,会节省 20 个字节的空间;
- 去除了原生 Redis 中多余的指针,如果要存储一个 KV 信息就只需要 8(weibo_id)+4(转发数)=12 个字节,相比之前有很大的改进
2.将先用weiboid的计数存储下一个hash下,减少了多个微博id计数
3.计数服务增加 SSD 磁盘,然后将时间上比较久远的数据 dump 到磁盘上,内存中只保留最近的数据
要读取冷数据的时候,使用单独的 I/O 线程异步地将冷数据从 SSD 磁盘中加载到一块儿单独的 Cold Cache 中\
\
5.总结
-
KISS原则(Keep It Simple and Stupid)
-
数据库 + 缓存的方案是计数系统的初级阶段,完全可以支撑中小访问量和存储量的存储服务\
-
使用 SSD+ 内存的方案可以最终解决存储计数数据的成本问题。这个方式适用于冷热数据明显的场景\
\