前言:redis 作为spring boot 的中间件,缓存数据库,被广泛使用,所以非常值得深入研究
使用场景
- 分布式锁
- 分布式系统多节点系统中 登录的token的存储和访问
- 分布式系统缓存 将数据先缓存的redis ,防止高并发系统,一次性访问大量访问造成系统奔溃
- 分布式系统中 如果id 使用 int 类型,需使用redis 创建一个常数然后自增获取
- 简单的消息发布和订阅
数据类型及其增删改查
String 类型
Hash 类型
List 类型
sort List 带排序得List
Set 类型 去重类型
数据查询
那些简单得查询比较多直接看教程吧
https://www.runoob.com/redis/redis-hyperloglog.html
对应得提供一个 spring redis 整合的 工具类
package com.ruoyi.common.core.redis;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
* spring redis 工具类
*
* @author ruoyi
**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
@Autowired
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
{
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout)
{
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit)
{
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key)
{
return redisTemplate.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection)
{
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList)
{
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key)
{
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
{
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key)
{
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
{
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey)
{
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
/**
* 删除Hash中的数据
*
* @param key
* @param mapkey
*/
public void delCacheMapValue(final String key, final String hkey)
{
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
{
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern)
{
return redisTemplate.keys(pattern);
}
}
数据排序
场景实现
分布式锁
分布式锁实现有很多种方式,比较全面成熟的实现方式 Redlock + Redisson
Redisson中的分布式锁具有内置的死锁检测和解除机制。Redisson使用了RedLock的基本思想,但在其实现中添加了死锁监测和解除的功能。当Redisson检测到某个客户端的锁处于死锁状态时,它会尝试解除死锁并释放相应的锁。Redisson的死锁解除机制依赖于Redis的WATCH命令和Lua脚本,用于检测死锁并执行解锁操作。
RLock redissonLock = redisson.getLock(lockKey);
try {
//加锁
redissonLock.lock();
}catch (Exception e){
}finally {
redissonLock.unLock();
}
锁超时:Redlock和Redisson
如果程序还没有执行完,但是锁超时了,可以使用Redlock和Redisson两个常用的分布式锁的实现库,实现锁续命。
Redlock:Redlock是一个由Redis的创始人提出的算法,用于实现分布式锁。在Redlock中,锁的超时处理主要通过续约机制实现。
获取锁时,会设置一个锁的超时时间(例如30秒)。
在持有锁的客户端执行业务逻辑期间,定期(例如每10秒)续约锁的超时时间,延长锁的有效期。
Redisson:Redisson是一个基于Redis的Java分布式应用开发框架,提供了丰富的分布式功能,包括分布式锁。在Redisson中,锁的超时处理主要通过看门狗(Watchdog)机制实现。
获取锁时,会设置一个锁的超时时间(例如30秒)。
同时,启动一个看门狗线程,定期检查锁的剩余过期时间。
如果看门狗线程检测到锁的剩余过期时间低于某个阈值(例如10秒),会自动延长锁的超时时间,防止锁在业务逻辑执行期间过期。
登录session
使用 setnx key value expire time 对token 进行处理
具体在项目中的使用流程
登录 添加 token
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
redisCache.setCacheObject(loginBody.getUsername(),token,1000, TimeUnit.SECONDS);
return ajax;
}
刷新 token
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
在 filter 中 校验 接口的时候调用刷新token 的方法
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
{
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
}
缓存存储
缓存雪崩和缓存穿透
一般高并发项目会将项目一部分数据缓存在redis中,redis作为内存数据库,访问速度极快可以承受高并发,如果这些请求直接落到数据库,一次性几千上万的连接过来可能会导致数据库宕机
redis id自增
创建一个表 对应id 值缓存这个 需要的时候 加1 就可以了
部署备份
备份方式
参考:https://www.cnblogs.com/itdragon/p/7906481.html
使用rdb 和aof 的方式对 redis 进行备份