一文涵盖所有工作中遇到的redis操作,让你从此学会redis本文会从基础篇到进阶篇,逐步来讲解redis和springboot的整合,如何去操作,以及他的作用。让你学会使用redis,爱上使用redis。
介绍redis首先我们来介绍一下redis
NoSQL 数据库
redis是一个
key - value 存储系统(区别于 MySQL,他存储的是键值对)
之后我们来看
redis的数据结构String 字符串类型: name: "xiaou"
List 列表:names: ["xiaou", "dogxiaou", "xiaou"]
Set 集合:names: ["xiaou", "dogxiaou"](值不能重复)
Hash 哈希:nameAge: { "xiaou": 1, "dogxiaou": 2 }
Zset 集合:names: { xiaou - 9, dogxiaou - 12 }(适合做排行榜)
bloomfilter(布隆过滤器,主要从大量的数据中快速过滤值,比如邮件黑名单拦截)
geo(计算地理位置)
hyperloglog(pv / uv)
pub / sub(发布订阅,类似消息队列)
BitMap (1001010101010101010101010101)
如何与java整合?Spring Data Redis(推荐)Spring Data:通用的数据访问框架,定义了一组 增删改查 的接口
mysql、redis、jpa
spring-data-redis
Jedis独立于 Spring 操作 Redis 的 Java 客户端
要配合 Jedis Pool 使用
Lettuce高阶 的操作 Redis 的 Java 客户端
异步、连接池
Redisson分布式操作 Redis 的 Java 客户端,让你像在使用本地的集合一样操作 Redis(分布式 Redis 数据网格)
JetCache 对比
如果你用的是 Spring,并且没有过多的定制化要求,可以用 Spring Data Redis,最方便如果你用的不是 SPring,并且追求简单,并且没有过高的性能要求,可以用 Jedis + Jedis Pool如果你的项目不是 Spring,并且追求高性能、高定制化,可以用 Lettuce,支持异步、连接池如果你的项目是分布式的,需要用到一些分布式的特性(比如分布式锁、分布式集合),推荐用 redisson之后我们来介绍一下redis的基础操作
redis基础操作这里我们选用的是spring-boot-starter-data-redis
首先我们在本机上开启redis双击运行server这个就可以
image-20240402135038894看到这个就代表着redis已经成功运行了
image-20240402135102613之后来看maven的依赖
代码语言:javascript代码运行次数:0运行复制
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
之后我们对yml进行一个配置
代码语言:javascript代码运行次数:0运行复制server:
port: 8081
#spring:
# datasource:
# driver-class-name: com.mysql.jdbc.Driver
# url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&&allowPublicKeyRetrieval=true #url
# username: root
# password: 1234
spring:
redis:
database: 4
host: localhost
port: 6379之后我们要做的是对redis编写一个配置类
代码语言:javascript代码运行次数:0运行复制@Configuration
@EnableCaching // 启用缓存功能
public class RedisConfig {
// 定义 RedisTemplate Bean,用于操作 Redis 数据
@Bean
public RedisTemplate
RedisTemplate
redisTemplate.setConnectionFactory(factory);
// 设置 RedisTemplate 的键和值的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer()); // 设置键的序列化器为 StringRedisSerializer
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 设置值的序列化器为 GenericJackson2JsonRedisSerializer
// 设置 RedisTemplate 的哈希键和哈希值的序列化方式
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // 设置哈希键的序列化器为 StringRedisSerializer
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer
return redisTemplate;
}
// 定义 RedisCacheManager Bean,用于管理缓存
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
// 创建 RedisCacheWriter 实例,用于向 Redis 写入缓存,非锁定方式
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
// 创建 RedisCacheConfiguration 实例,配置缓存的默认行为
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())); // 设置缓存值的序列化方式
// 返回 RedisCacheManager 实例,用给定的 RedisCacheWriter 和 RedisCacheConfiguration 初始化
return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
}
之后我们就可以使用redisTemplate进行一个操作
使用RedisTemplate进行常见的Redis操作,如存储、检索和删除数据
首先要做的第一步就是引入RedisTemplate
这里推荐一个redis的可视化工具 quickredis
QuickOfficial - QuickRedis (quick123.net)
代码语言:javascript代码运行次数:0运行复制@Autowired
private RedisTemplate redisTemplate;String首先来看第一个
代码语言:javascript代码运行次数:0运行复制// 测试设置字符串类型数据到 Redis
@Test
public void testString() {
String key = "user:token:0001";
redisTemplate.opsForValue().set(key, UUID.randomUUID().toString(), 30, TimeUnit.MINUTES);
System.out.println(redisTemplate.opsForValue().get(key));
}这个就是像redis添加了一个key
image-20240402141034447之后来看这个
代码语言:javascript代码运行次数:0运行复制// 测试字符串类型数据自增操作
@Test
public void testString2() {
String key = "article:A00001:viewsCount";
redisTemplate.opsForValue().increment(key);
System.out.println(redisTemplate.opsForValue().get(key));
}increment实现了对这个key的一个计数功能。比如我们第一次执行,就是1 第二次就是2 以此类推
第三个是测试设置Map类型到redis中
代码语言:javascript代码运行次数:0运行复制// 测试设置 Map 类型数据到 Redis
@Test
public void testString3() {
Map
user.put("id", "0001");
user.put("name", "张三疯");
user.put("age", 28);
user.put("birthday", new Date(2005 - 1900, 10, 03));
String key = "user:0001";
redisTemplate.opsForValue().set(key, user);
System.out.println(redisTemplate.opsForValue().get(key));
}image-20240402141418044Hash代码语言:javascript代码运行次数:0运行复制 // 测试设置哈希类型数据到 Redis
@Test
public void testHash() {
String key = "user:0001:cart";
Map
shoppingCart.put("cartId", "123456789");
shoppingCart.put("userId", "987654321");
List
Map.of("itemId", "1", "itemName", "手机", "price", 999.99, "quantity", 1),
Map.of("itemId", "2", "itemName", "笔记本电脑", "price", 1499.99, "quantity",
2),
Map.of("itemId", "3", "itemName", "耳机", "price", 49.99, "quantity", 3)
);
shoppingCart.put("items", items);
shoppingCart.put("totalAmount", 3149.92);
shoppingCart.put("creationTime", "2046-03-07T10:00:00");
shoppingCart.put("lastUpdateTime", "2046-03-07T12:30:00");
shoppingCart.put("status", "未结账");
redisTemplate.opsForHash().putAll(key, shoppingCart);
System.out.println(redisTemplate.opsForHash().get(key, "items"));
}image-20240402141734052set代码语言:javascript代码运行次数:0运行复制// 测试设置集合类型数据到 Redis
@Test
public void testSet() {
String key = "author:0001:fans";
redisTemplate.opsForSet().add(key, "张三", "李四", "王五");
System.out.println("粉丝量:" + redisTemplate.opsForSet().size(key));
}image-20240402141809859zset代码语言:javascript代码运行次数:0运行复制// 测试设置有序集合类型数据到 Redis
@Test
public void testZset() {
String key = "user:0001:friends";
redisTemplate.opsForZSet().add(key, "张三", System.currentTimeMillis());
redisTemplate.opsForZSet().add(key,"李四", System.currentTimeMillis());
redisTemplate.opsForZSet().add(key,"王五", System.currentTimeMillis());
Set set = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
System.out.println(set);
}image-20240402141927751list代码语言:javascript代码运行次数:0运行复制@Test
public void testList() {
String key = "order:queue";
Map
order1.put("orderId", "1001");
order1.put("userId", "2001");
order1.put("status", "已完成");
order1.put("amount", 500.75);
order1.put("creationTime", "2024-03-07T09:30:00");
order1.put("lastUpdateTime", "2024-03-07T10:45:00");
order1.put("paymentMethod", "在线支付");
order1.put("shippingMethod", "自提");
order1.put("remarks", "尽快处理");
Map
order2.put("orderId", "1002");
order2.put("userId", "2002");
order2.put("status", "待处理");
order2.put("amount", 280.99);
order2.put("creationTime", "2024-03-07T11:00:00");
order2.put("lastUpdateTime", "2024-03-07T11:00:00");
order2.put("paymentMethod", "货到付款");
order2.put("shippingMethod", "快递配送");
order2.put("remarks", "注意保鲜");
// A程序接收订单请求并将其加入队列
redisTemplate.opsForList().leftPush(key,order1);
redisTemplate.opsForList().leftPush(key,order2);
// B程序从订单队列中获取订单数据并处理
System.out.println("处理订单:" + redisTemplate.opsForList().rightPop(key));
}image-20240402142133783这是一些简单的操作,除了这些,我们来介绍一些别的东西
redis进阶操作 @RedisHash注解用于将Java对象映射到Redis的Hash数据结构中,使得对象的存储和检索变得更加简单
首先我们创建和实体类一样的数据库表
这个是集体类
代码语言:javascript代码运行次数:0运行复制
@Data
@RedisHash
public class User {
@Id
private Integer id;
private String name;
private Integer age;
private String phone;
}2.创建接口,注意需要继承CrudRepository,这样改接口就具备对应实体的redis中的增删改查操作
代码语言:javascript代码运行次数:0运行复制
public interface UserRedisMapper extends CrudRepository
}之后进行一个测试
代码语言:javascript代码运行次数:0运行复制// 测试 Redis 与 Spring Data Redis 集成的操作
@Test
public void testRedisHash(){
User user = new User();
user.setId(100);
user.setName("张三疯");
user.setAge(18);
user.setPhone("19988889999");
// 保存
userRedisMapper.save(user);
// 读取
User redisUser = userRedisMapper.findById(100).get();
System.out.println("redisUser:" + redisUser);
// 更新
user.setPhone("18899998888");
userRedisMapper.save(user);
// 删除
//userRedisMapper.deleteById(100);
// 判断存在
boolean exists = userRedisMapper.existsById(100);
System.out.println("exists: " + exists);
}这样就可以用redis来对数据库进行一个操作。
redis工作场景分析session存储信息用来做数据共享分布式
这个需要引入
代码语言:javascript代码运行次数:0运行复制
之后设置一下配置
代码语言:javascript代码运行次数:0运行复制# session 失效时间
session:
timeout: 86400
store-type: redis之后我们例如在用户登陆的时候
代码语言:javascript代码运行次数:0运行复制/**
* 用户登录
*/
@Override
public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
//登陆逻辑....
//获得了登陆的用户信息
//模拟
User safetyUser=null;
//记录session
HttpSession session = request.getSession();
session.setAttribute(USER_LOGIN_STATE, safetyUser);
return safetyUser;
}可以设置这个session
这个可以用来做,例如获得当前用户
代码语言:javascript代码运行次数:0运行复制/**
* 获取当前用户
*
*/
@GetMapping("/current")
public BaseResponse
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
return ResultUtils.success(userObj);
}等等操作,你可以理解为,她在redis里面存放着一个用户信息。你需要的时候可以随时的去取出来这个信息
分布式锁这个推荐用的是redisson
Redisson 是一个 java 操作 Redis 的客户端,提供了大量的分布式数据集来简化对 Redis 的操作和使用,可以让开发者像使用本地集合一样使用 Redis,完全感知不到 Redis 的存在。
可以看下面的俩个事例
代码语言:javascript代码运行次数:0运行复制// list,数据存在本地 JVM 内存中
List
list.add("yupi");
System.out.println("list:" + list.get(0));
list.remove(0);
// 数据存在 redis 的内存中
RList
rList.add("xiaou");
System.out.println("rlist:" + rList.get(0));
rList.remove(0);代码语言:javascript代码运行次数:0运行复制void testWatchDog() {
RLock lock = redissonClient.getLock("test:precachejob:docache:lock");
try {
// 只有一个线程能获取到锁
if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
// todo 实际要执行的方法
doSomeThings();
System.out.println("getLock: " + Thread.currentThread().getId());
}
} catch (InterruptedException e) {
System.out.println(e.getMessage());
} finally {
// 只能释放自己的锁
if (lock.isHeldByCurrentThread()) {
System.out.println("unLock: " + Thread.currentThread().getId());
lock.unlock();
}
}
}限流这个也推荐使用redisson
代码语言:javascript代码运行次数:0运行复制public void doRateLimit(String key) {
RRateLimiter rRateLimiter = redissonClient.getRateLimiter(key);
//rate是每个时间单位允许访问几次 rateInterval就是时间单位
rRateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);
//每当一个操作来了之后,去请求一个令牌
boolean canOp = rRateLimiter.tryAcquire(1);
if (!canOp) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "当前访问过于频繁");
}
}抽奖代码语言:javascript代码运行次数:0运行复制/**
* 抽奖
* @return
*/
@GetMapping("/lucky-draw")
public String luckyDraw() {
try {
// 获取奖池和参与者列表
RList
RList
// 进行抽奖
String winner = drawWinner(prizePool, participants);
// 返回抽奖结果
return " The winner is: " + winner;
} finally {
// 关闭 Redisson 客户端连接
redissonClient.shutdown();
}
}总结当然除了这些,还有很多使用redis的场景,欢迎各位来补充。
文中所有的源码在如下仓库xiaou61/xiaou-easy-code: 前后端通用解决方案 springboot vue react 原生js (github.com)
文章也会在这里同步发送Xiaou-EasyCode-Docs (xiaou61.top)
这个项目是我自己开发的一个前后端通用解决方案的一个项目,致力于各种工作中常用的代码的demo展示。目前github已有400+star 如果你有兴趣,可以去里面提出你的issue或者是pr 共同来完善这个项目。