Redis高效点赞与取消功能

到此 灵熙云工作室 - 实践出真理 查看全文内容

![在这里插入图片描述](https://img-blog.csdnimg.cn/cf7b84bbc66c40859df6b475c722407e.png

像CSDN的点赞功能只记录了数量,微信朋友圈的点赞功能有显示点赞人头像(获取userId查询用户信息封装返回即可)

点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,甚至宕机,所以用缓存处理再合适不过。本文以文章点赞为例来展开叙述

数据格式选择

Redis有5种数据结构分别为:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)

由于需要记录文章和点赞人,还有点赞状态(点赞、取消),分析下 Redis 数据格式中Hash最合适。

因为Hash里的数据都是存在一个Key中,通过Key很方便的把所有的点赞数据都取出。Key里面的数据还可以存成键值对的形式,方便存入点赞人、被点赞人和点赞状态

文章 idarticleId点赞人的 id userId点赞状态1(点赞)和0(取消点赞)文章 id 点赞人 id 作为HashKey,两个 id 中间用::隔开,点赞状态作为HashValue

2.png

整合SpringBoot

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

基础配置

spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0

配置类

package cn.goitman.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @author Nicky
 * @version 1.0
 * @className RedisConfig
 * @blog goitman.cn | blog.csdn.net/minkeyto
 * @description Redis配置类
 * @date 2022/5/16 14:40
 */
@Configuration
public class RedisConfig {

    /**
    * 凡事使用到template的redis操作都必须走@Transanctional注解式事务,要不然会导致连接一直占用,不关闭
    */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate template = new RedisTemplate();
        // 改变redisTemplate的序列化方式,key为字符串格式,value为json格式
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // HashKey 和 HashValue 为json格式
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        // 开启事务支持
        template.setEnableTransactionSupport(true);
        return template;
    }

    /**
    * 配置事务管理器
    */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) throws SQLException {
        return new DataSourceTransactionManager(dataSource);
    }
}

如果redisTemplate没有序列化,在可视化工具中看到的数据为乱码,获取数据时也可能为空,模糊查询(下文有叙述)功能也使用不了

1.png

Redis接口

package cn.goitman.service.impl;

import cn.goitman.pojo.Article;
import cn.goitman.pojo.Likes;
import cn.goitman.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Nicky
 * @version 1.0
 * @className RedisServiceImpl
 * @blog goitman.cn | blog.csdn.net/minkeyto
 * @description Redis接口实现类
 * @date 2022/5/13 16:43
 */
@Service
@Transactional
public class RedisServiceImpl implements RedisService {

    // 文章点赞 KEY
    public static final String KEY_ARTICLE_LIKE = "ARTICLE_LIKE";

    // 文章点赞数量 KEY
    public static final String KEY_ARTICLE_LIKE_COUNT = "ARTICLE_LIKE_COUNT";

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 保存点赞和文章点赞量
     */
    @Override
    public void saveLike(String articleId, String userId) {
        String field = getLikeKey(articleId, userId);
        redisTemplate.opsForHash().put(KEY_ARTICLE_LIKE, field, 1);
        redisTemplate.opsForHash().increment(KEY_ARTICLE_LIKE_COUNT, articleId, 1);
    }

    /**
     * 取消点赞和文章点赞量
     */
    @Override
    public void unLike(String articleId, String userId) {
        String field = getLikeKey(articleId, userId);
        redisTemplate.opsForHash().put(KEY_ARTICLE_LIKE, field, 0);
        redisTemplate.opsForHash().increment(KEY_ARTICLE_LIKE_COUNT, articleId, -1);
    }

    /**
     * 删除点赞数据
     */
    @Override
    public void deleteLike(List<Likes> list) {
        for (Likes like : list) {
            String field = getLikeKey(like.getArticleId(), like.getUserId());
            redisTemplate.opsForHash().delete(KEY_ARTICLE_LIKE, field);
        }
    }

    /**
     * 删除文章点赞量数据
     */
    @Override
    public void deleteLikeCount(String articleId) {
        redisTemplate.opsForHash().delete(KEY_ARTICLE_LIKE_COUNT, articleId);
    }

    /**
     * 获取全部点赞数据
     */
    @Override
    public List<Likes> getAllLikeData() {
        List<Likes> list = new ArrayList<>();
        Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(KEY_ARTICLE_LIKE, ScanOptions.NONE);
        while (cursor.hasNext()) {
            Map.Entry<Object, Object> entry = cursor.next();
            String keys = entry.getKey().toString();

            String[] keyArr = keys.split("::");
            Likes like = new Likes(keyArr[0], keyArr[1], (Integer) entry.getValue());

            list.add(like);
        }
        return list;
    }

    /**
     * 获取文章点赞量数据
     */
    @Override
    public List<Article> getArticleLikeCount() {
        List<Article> list = new ArrayList<>();
        Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(KEY_ARTICLE_LIKE_COUNT, ScanOptions.NONE);
        while (cursor.hasNext()) {
            Map.Entry<Object, Object> entry = cursor.next();
            String articleId = entry.getKey().toString();
            Article article = new Article(articleId, (Integer) entry.getValue());
            list.add(article);
        }
        return list;
    }

    /**
     * 拼接文章ID和点赞人ID作为key
     */
    private String getLikeKey(String articleId, String userId) {
        return new StringBuilder().append(articleId).append("::").append(userId).toString();
    }
}

搞掂,就是这么简单高效,在Redis内,存在相同数据只会修改value,并且Redis默认RDB持久化数据

当然也可加上限时内限制每个用户点赞次数的逻辑,防止恶意刷接口,逻辑简单,在此就不累述啦

到此 灵熙云工作室 - 实践出真理 查看全文内容

  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

靈熙雲

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值