通用点赞设计思路

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 点赞作为一个高频率的操作,如果每次操作都读写数据库会增加数据库的压力,所以采用缓存+定时任务来实现。点赞数据是在redis中缓存半小时,同时定时任务是每隔5分钟执行一次,做持久化存储,这里的缓存时间和任务执行时间可根据项目情况而定。

点赞作为一个高频率的操作,如果每次操作都读写数据库会增加数据库的压力,所以采用缓存+定时任务来实现。点赞数据是在redis中缓存半小时,同时定时任务是每隔5分钟执行一次,做持久化存储,这里的缓存时间和任务执行时间可根据项目情况而定。

优点

1.降低对数据库的影响
2.提高点赞的效率

缺点

1.如果任务挂了,会丢失点赞数据
2.持久化存储不是实时的

时序图

在这里插入图片描述

数据库设计

create table user_like(
id bigint(20) unsigned not null auto_increment comment 'id',
user_id bigint(20) not null default 0 comment '用户id',
liked_id varchar(21) not null default '' comment '被点赞的id',
liked_status int(11) not null default 0 comment '点赞状态,0未点赞,1已点赞',
liked_type int(11) not null default 0 comment '点赞的类型',
liked_time timestamp not null default '0000-00-00 00:00:00.000000' comment '点赞时间',
 is_delete tinyint not null default '0' comment '是否逻辑删除',
 create_time timestamp not null default CURRENT_TIMESTAMP comment '创建时间',
 update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP comment '更新时间',
 primary key (id),
 unique uniq_user_id_liked_id_type(user_id,liked_id,liked_type),
 key idx_liked_id (liked_id),
 key idx_create_time (create_time),
 key idx_update_time (update_time)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='用户点赞表';

create table user_like_stat(
id bigint(20) unsigned not null auto_increment comment 'id',
liked_id varchar(21) not null default '' comment '被点赞id',
liked_count int(11) not null default 0 comment '点赞总数量',
 is_delete tinyint not null default '0' comment '是否逻辑删除',
 create_time timestamp not null default CURRENT_TIMESTAMP comment '创建时间',
 update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP comment '更新时间',
 primary key (id),
 unique uniq_info_num(liked_id),
 key idx_create_time (create_time),
 key idx_update_time (update_time)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='点赞统计表';

实现步骤

1.设计缓存数据格式

整个点赞模块主要采用缓存来完成,所以要选择合适数据结构,我选择hash数据结构来实现,应为它可以添加、获取、移除单个键值对,并且可以获取所有键值对。主要缓存两种数据,一种是用户的点赞状态,一种是被点赞id的点赞数量。这两种数据分别用两个key存储,这两个key中都是存储的多个键值对。键值对格式如下:

用户的点赞状态key-value------>{"被点赞的id::用户id" :"点赞状态::点赞时间::点赞类型"}

被点赞id的点赞数量key-value------>{"被点赞id" : "点赞数量"}

2.大key拆分

点赞的数据量比较大的情况下,上面的设计会造成单个key存储的value很大,由于redis是单线程运行,如果一次操作的value很大,会对整个redis的响应时间有影响,所以我们这里在将上面的两个key做拆分。固定key的数量,每次存取时都先在本地计算出落在了哪个key上,这个操作就类似于redis分区、分片。有利于降低单次操作的压力,将压力平分到多个key上。

//点赞状态key拆分
newHashKey  =  hashKey +"_"+ (userId% 5);   
hset (newHashKey, field, value) ;  
hget(newHashKey, field)


//点赞数量key拆分
newHashKey  =  hashKey +"_"+ Math.abs((hash*(被点赞id)) % 5);   
hset (newHashKey, field, value) ;  
hget(newHashKey, field)

3.代码实现

以下值截取了部分代码,提供思路。

1.点赞状态枚举
在这里插入图片描述
2.点赞类型枚举
在这里插入图片描述
3.用户点赞类
在这里插入图片描述
4.点赞接口实现
这里使用策略设计模式来实现,方便以后的扩展,对这个设计模式不了解的请点击

https://juejin.im/post/6844903703653138445

这里进行策略的选择在这里插入图片描述在这里插入图片描述
5.逻辑
取消点赞和这个接口相同,只需要替换下点赞状态和redis增量
在这里插入图片描述
6.定时任务
定时任务采用Azkaban任务调度系统,每个5分种运行一次任务,把点赞数据从redis缓存中取出做持久化到mysql。

4.改进点

现在的读取都是用的一个key,接下来可以优化为把key做读写分离。写入和读取分别用不同的key,这样做可以减少资源的浪费,要不每次跑定时任务都会把已经持久化并且缓存未失效的数据拿出来做一遍查询。

以上就是点赞的一个实现思路,大家有什么更好的方法或者改进的点,欢迎提出来。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5月前
|
算法 Java 编译器
C++ | 深入浅出类的封装思想【图文案例,通俗易懂】-1
C++ | 深入浅出类的封装思想【图文案例,通俗易懂】
127 3
|
5月前
|
算法 Java 编译器
C++ | 深入浅出类的封装思想【图文案例,通俗易懂】-3
C++ | 深入浅出类的封装思想【图文案例,通俗易懂】
40 0
|
8月前
|
JavaScript 前端开发
JavaScriptDOM编程(基础&进阶)2
JavaScriptDOM编程(基础&进阶)2
31 0
|
8月前
|
XML 移动开发 JavaScript
JavaScriptDOM编程(基础&进阶)1
JavaScriptDOM编程(基础&进阶)1
33 0
|
8月前
|
JavaScript 前端开发
JavaScriptDOM编程(基础&进阶)3
JavaScriptDOM编程(基础&进阶)3
35 0
|
5月前
|
存储 编译器 程序员
C++ | 深入浅出类的封装思想【图文案例,通俗易懂】-2
C++ | 深入浅出类的封装思想【图文案例,通俗易懂】
63 0
|
6天前
|
缓存 监控 前端开发
前端性能优化:从基础到进阶
前端性能优化:从基础到进阶
22 0
|
10月前
|
前端开发
项目实战典型案例22——原型图的面向对象思路
项目实战典型案例22——原型图的面向对象思路
52 1
|
10月前
|
数据库 Nacos
【项目实战典型案例】08.用户成为设计者的正例
【项目实战典型案例】08.用户成为设计者的正例
|
10月前
|
设计模式 安全 Java
基于设计模式改造短信网关服务实战篇(设计思想、方案呈现、源码)
基于设计模式改造短信网关服务实战篇(设计思想、方案呈现、源码)
224 0