到此 灵熙云工作室 - 实践出真理 查看全文内容
像CSDN的点赞功能只记录了数量,微信朋友圈的点赞功能有显示点赞人头像(获取userId查询用户信息封装返回即可)
点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,甚至宕机,所以用缓存处理再合适不过。本文以文章点赞为例来展开叙述
数据格式选择
Redis有5种数据结构分别为:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)
。
由于需要记录文章和点赞人,还有点赞状态(点赞、取消),分析下 Redis 数据格式中Hash
最合适。
因为Hash
里的数据都是存在一个Key
中,通过Key
很方便的把所有的点赞数据都取出。Key
里面的数据还可以存成键值对
的形式,方便存入点赞人、被点赞人和点赞状态
。
文章 id
为articleId
,点赞人的 id
为userId
,点赞状态
为1(点赞)和0(取消点赞)
。文章 id
和点赞人 id
作为HashKey
,两个 id 中间用::
隔开,点赞状态
作为HashValue
。
整合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没有序列化
,在可视化工具中看到的数据为乱码,获取数据时也可能为空
,模糊查询(下文有叙述)功能也使用不了
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持久化数据
。
当然也可加上限时内限制每个用户点赞次数
的逻辑,防止恶意刷接口
,逻辑简单,在此就不累述啦
到此 灵熙云工作室 - 实践出真理 查看全文内容