点赞系统项目总结
今天学习完了鱼皮哥新出的点赞系统项目, 整体的学习让我体验到了 MySQL Redis MQ 这些中间件是如何进行优化来提升系统的并发性能的, 接下来将从架构流程的演变来说说我个人的收获 项目链接 https://github.com/Whut2024/thumb-system
基础数据的保存
后端感觉就是 CRUD,但是如何提高 CRUD 的并发是一个复杂的过程
点赞系统最基础要做到可用性,最开始的数据存储直接使用的是常见数据库 MySQL,当用户进行点赞相关操作后请求发送到后台,后台进行修改信息的录入修改,这个阶段使用了数据库的事务保证了数据的一致性,但随着博客数量的增加和热点博客的出现
1 短时间频繁的要对一些热点进行点赞信息的修改,数据库 InnoDB 引擎下的行锁也有点绷不住,到达一定量的并发后就无法提升
2 同时对博客的查询量也会随着用户增长而水涨船高,数据库查询的并发度也会到达顶点
使用分布式缓存加速
优化用户查询博客
当用户查看一个博客时系统需要告诉用户他是否对该博客进行过点赞,这个数据开始是存储在数据库中的点赞信息表的,如果能只查询博客的内容信息,然后以较小的消耗去获取到用户对这一批博客哪些点过赞,性能可以大大优化,
速度比 MySQL 快的常见的就是本地内存缓存或者分布式缓存,考虑到以后的水平扩展,用户点赞后的信息会被暂时性的缓存在分布式缓存 redis 中,之后的查询就不会再去数据库中查询用户是否对莫个博客进行点赞,而是以批量分布式查询缓存的方式直接快速获取到点赞情况
如果现在有一个 blogIdList 要确认用户对其中的哪些点过赞,要么是客户端单独查询 blogIdList size 次,或者将 blogIdList 一次性给数据库用 where id in ( blogIdList ) 来过滤
可以发现会使用索引来查询,时间复杂度为 nlogm n 为 blogIdList size, m 为表的数据量
redis 基于哈希的批量查询的时间复杂度仅仅为 n,还是完全基于内存的,效率会高出很多
优化用户点赞博客
用户点赞后能不能在保证可用性的同时尽量减少对热点行的访问,用户对一个博客点赞后的点赞操作和博客被点赞数是需要被写入数据库和被更新,写数据库涉及到的锁冲突不大,更新点赞量这个操作的行锁冲突可能很大
这个点赞量是不需要保证实时的数据一致性的,很多情况下也是取大头的模糊显示,可以考虑暂时不使用 MySQL 持久化用户的点赞操作,只是将点赞操作缓存到 redis 中,采用定时任务批量的读取这些信息,刷新点赞数据进入数据库,相对于分散的高并发写数据库,批量异步写数据库的性能好的多,用户点赞后先保存 redis 就结束请求,响应的时间也大大缩短
优化点赞热点
某些热点博客会被频繁的查询的点赞,导致 redis 的大部分流量来源于这些博客,查询的信息也是重复不变的,这时考虑将某些热点直接的缓存到本地中,不再去查询 redis,减少大量重复 redis 访问流量
当前项目采用的是 heavy keeper 算法来确定热点数据,算法维护了多个 hash 桶数组,每个数组对应着一种 hash 函数,当一个 key 被访问时,不同 hash 函数得到的结果进行取模就的到了在桶数组中的索引下表,查看是否是同一个key,进行相关的权重累加或者减少
遍历完 hash桶数组后会得到最大的权重值,是否到达临界值,来决定是否缓存数据到本地
中间设置过定时任务来以一定频率衰减本地缓存 key 的权重,给后来的 key 提供取代机会
消息队列异步处理
当用户进行点赞操作后,相关信息会被缓存到 redis 中,但是定时任务可能存在如下问题
- 短时间点赞过多,redis 缓存的 set 中元素过多,可能会出现体积很大的 key
- 短时间点赞过多时直接查询 redis 会发生大 key 查询,阻塞 redis 单线程
- redis 缓存信息的处理负载不方便于均匀分配到不同的实例中,项目水平扩展性不强
- redis 缓存信息处理失败时不易于重试,重试可能导致之后的定时任务被阻塞,导致部分时间短的缓存一直无法持久化进入数据库中
使用消息队列后点赞操作会被作为消息发送到消息队列服务器,
消费者可以控制消费的速度,
消费者消费能力也更方便进行水平扩展,在 kafka parition 数充足的情况下可以直接向消费者组中添加消费者,
当消息处理出现异常时也可以进行重试,最后进入死信队列人工干预
点赞信息的处理在两种方式中都需要考虑幂等性
分布式数据库
当数据量增大时单机数据库的读写性能无法承受更大的并发,分裤分表的操作在应对复杂的逻辑查询时需要将相关数据查询到 proxy 层后再次进行聚合,逻辑复杂,分裤分表是写数据不是在同一实例数据库时还需要进行路由计算,事务也无法保证
当前项目采用分布式 TiDB 数据库存储,水平扩展方便,分布式事务有统一的消息中心进行管理
看看每一步的并发提升
配置如下
单独数据库的情况
缓存哪些用户点赞过
redis 作为数据库
点赞操作异步发送消息队列
redis 执行情况
Prometheus 情况

