1、直接写入数据库:
优点:这种方法实现简单,只需完成数据库的增删改查就行;
缺点:数据库读写压力大,如果遇到热门文章在短时间内被大量点赞的情况,直接操作数据库会给数据库带来巨大压力,影响效率。
2、使用 redis 缓存:
优点:性能高,读写速度快,缓解数据库读写的压力;
缺点:开发复杂,不能保证数据安全性即 redis 挂掉的时候会丢失数据, 同时不及时同步 redis 中的数据, 可能会在 redis 内存置换的时候被淘汰掉。不过对于点赞数据我们不需要那么精确,丢失一点数据问题不大。
接下来就从以下三个方面对点赞功能做详细的介绍
•redis 缓存设计
•数据库设计
•开启定时任务持久化存储到数据库
1、redis 缓存设计及实现我们已经在前一篇文章中介绍了如何整合redis,在这里就不再重复说明了。我们了解到,在进行点赞操作时,需要记录以下几种数据:用户被其他用户点赞的详细记录和点赞操作的记录。为了方便查询和存取,我使用了 hash 结构进行存储,其存储结构如下:
(1)某用户被其他用户点赞的详细记录: map_user_liked 为键值, 被点赞用户id::点赞用户id 为 filed, 1或者0 为 value
(2)某用户被点赞的数量统计: map_user_liked_count 为键值, 被点赞用户id 为 filed, count 为 value
部分代码如下/*** 将用户被其他用户点赞的数据存到redis*/@overridepublic void saveliked2redis(string likeduserid, string likedpostid) { string key = rediskeyutils.getlikedkey(likeduserid, likedpostid); redistemplate.opsforhash().put(rediskeyutils.map_key_user_liked,key, likedstatusenum.like.getcode());}//取消点赞@overridepublic void unlikefromredis(string likeduserid, string likedpostid) { string key = rediskeyutils.getlikedkey(likeduserid, likedpostid); redistemplate.opsforhash().put(rediskeyutils.map_key_user_liked,key,likedstatusenum.unlike.getcode());}/*** 将被点赞用户的数量+1*/@overridepublic void incrementlikedcount(string likeduserid) { redistemplate.opsforhash().increment(rediskeyutils.map_key_user_liked_count,likeduserid,1);}//-1@overridepublic void decrementlikedcount(string likeduserid) { redistemplate.opsforhash().increment(rediskeyutils.map_key_user_liked_count, likeduserid, -1);}/*** 获取redis中的用户点赞详情记录*/@overridepublic list<userlikedetail> getlikeddatafromredis() { cursor<map.entry<object,object>> scan = redistemplate.opsforhash().scan(rediskeyutils.map_key_user_liked, scanoptions.none); list<userlikedetail> list = new arraylist<>(); while (scan.hasnext()){ map.entry<object, object> entry = scan.next(); string key = (string) entry.getkey(); string[] split = key.split("::"); string likeduserid = split[0]; string likedpostid = split[1]; integer value = (integer) entry.getvalue(); //组装成 userlike 对象 userlikedetail userlikedetail = new userlikedetail(likeduserid, likedpostid, value); list.add(userlikedetail); //存到 list 后从 redis 中删除 redistemplate.opsforhash().delete(rediskeyutils.map_key_user_liked, key); } return list;}/*** 获取redis中的用户被点赞数量*/@overridepublic list<userlikcountdto> getlikedcountfromredis() { cursor<map.entry<object,object>> cursor = redistemplate.opsforhash().scan(rediskeyutils.map_key_user_liked_count, scanoptions.none); list<userlikcountdto> list = new arraylist<>(); while(cursor.hasnext()){ map.entry<object, object> map = cursor.next(); string key = (string) map.getkey(); integer value = (integer) map.getvalue(); userlikcountdto userlikcountdto = new userlikcountdto(key,value); list.add(userlikcountdto); //存到 list 后从 redis 中删除 redistemplate.opsforhash().delete(rediskeyutils.map_key_user_liked_count,key); } return list;}
redis 存储结构如图
2、数据库设计这里我们可以和直接将点赞数据存到数据库一样,设计两张表:
(1)用户被其他用户点赞的详细记录:user_like_detail
drop table if exists `user_like_detail`;create table `user_like_detail` ( `id` int(11) not null auto_increment, `liked_user_id` varchar(32) character set utf8 collate utf8_general_ci not null comment '被点赞的用户id', `liked_post_id` varchar(32) character set utf8 collate utf8_general_ci not null comment '点赞的用户id', `status` tinyint(1) null default 1 comment '点赞状态,0取消,1点赞', `create_time` timestamp(0) not null default current_timestamp(0) comment '创建时间', `update_time` timestamp(0) not null default current_timestamp(0) on update current_timestamp(0) comment '修改时间', primary key (`id`) using btree, index `liked_user_id`(`liked_user_id`) using btree, index `liked_post_id`(`liked_post_id`) using btree) engine = innodb auto_increment = 7 character set = utf8 collate = utf8_general_ci comment = '用户点赞表' row_format = dynamic;set foreign_key_checks = 1;
(2)用户被点赞的数量统计:user_like_count
drop table if exists `user_like_count`;create table `user_like_count` ( `id` int(11) not null auto_increment, `like_num` int(11) null default 0, primary key (`id`) using btree) engine = innodb auto_increment = 7 character set = utf8 collate = utf8_general_ci row_format = dynamic;set foreign_key_checks = 1;
3、开启定时任务持久化存储到数据库我们使用 quartz 来实现定时任务,将 redis 中的数据存储到数据库中,为了演示效果,我们可以设置一分钟或者两分钟存储一遍数据,这个视具体业务而定。在同步数据的过程中,我们首先要将 redis 中的数据在数据库中进行查重,舍弃重复数据,这样我们的数据才会更加准确。
部分代码如下//同步redis的用户点赞数据到数据库@override@transactionalpublic void translikedfromredis2db() { list<userlikedetail> list = redisservice.getlikeddatafromredis(); list.stream().foreach(item->{ //查重 userlikedetail userlikedetail = userlikedetailmapper.selectone(new lambdaquerywrapper<userlikedetail>() .eq(userlikedetail::getlikeduserid, item.getlikeduserid()) .eq(userlikedetail::getlikedpostid, item.getlikedpostid())); if (userlikedetail == null){ userlikedetail = new userlikedetail(); beanutils.copyproperties(item, userlikedetail); //没有记录,直接存入 userlikedetail.setcreatetime(localdatetime.now()); userlikedetailmapper.insert(userlikedetail); }else{ //有记录,需要更新 userlikedetail.setstatus(item.getstatus()); userlikedetail.setupdatetime(localdatetime.now()); userlikedetailmapper.updatebyid(item); } });}@override@transactionalpublic void translikedcountfromredis2db() { list<userlikcountdto> list = redisservice.getlikedcountfromredis(); list.stream().foreach(item->{ userlikecount user = userlikecountmapper.selectbyid(item.getkey()); //点赞数量属于无关紧要的操作,出错无需抛异常 if (user != null){ integer likenum = user.getlikenum() + item.getvalue(); user.setlikenum(likenum); //更新点赞数量 userlikecountmapper.updatebyid(user); } });}
以上就是怎么利用redis实现点赞功能的详细内容。
