> 文档中心 > Spring Boot + Redis 实现各种操作,写得太好了吧

Spring Boot + Redis 实现各种操作,写得太好了吧

点击关注公众号:互联网架构师,后台回复 2T获取2TB学习资源!

 

上一篇:Alibaba开源内网高并发编程手册.pdf

一、Jedis,Redisson,Lettuce 三者的区别

共同点:都提供了基于 Redis 操作的 Java API,只是封装程度,具体实现稍有不同。

不同点:

  • 1.1、Jedis

是 Redis 的 Java 实现的客户端。支持基本的数据类型如:String、Hash、List、Set、Sorted Set。

特点:使用阻塞的 I/O,方法调用同步,程序流需要等到 socket 处理完 I/O 才能执行,不支持异步操作。Jedis 客户端实例不是线程安全的,需要通过连接池来使用 Jedis。

  • 1.1、Redisson

优点点:分布式锁,分布式集合,可通过 Redis 支持延迟队列。

  • 1.3、 Lettuce

用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

基于 Netty 框架的事件驱动的通信层,其方法调用是异步的。Lettuce 的 API 是线程安全的,所以可以操作单个 Lettuce 连接来完成各种操作。

二、Jedis

三、RedisTemplate

3.1、使用配置

maven 配置引入,(要加上版本号,我这里是因为 Parent 已声明)

 org.springframework.boot spring-boot-starter-data-redis    

application-dev.yml

spring:  redis:    host: 192.168.1.140    port: 6379    password:    database: 15 # 指定redis的分库(共16个0到15)

3.2、使用示例

@Resource private StringRedisTemplate stringRedisTemplate;     @Override    public CustomersEntity findById(Integer id) { // 需要缓存 // 所有涉及的缓存都需要删除,或者更新 try {     String toString = stringRedisTemplate.opsForHash().get(REDIS_CUSTOMERS_ONE, id + "").toString();     if (toString != null) {  return JSONUtil.toBean(toString, CustomersEntity.class);     } } catch (Exception e) {     e.printStackTrace(); } // 缓存为空的时候,先查,然后缓存redis Optional byId = customerRepo.findById(id); if (byId.isPresent()) {     CustomersEntity customersEntity = byId.get();     try {  stringRedisTemplate.opsForHash().put(REDIS_CUSTOMERS_ONE, id + "", JSONUtil.toJsonStr(customersEntity));     } catch (Exception e) {  e.printStackTrace();     }     return customersEntity; } return null;    }

3.3、扩展

3.3.1、spring-boot-starter-data-redis 的依赖包

8373b23f4150a99b14f9bf0ebbc380f3.png

3.3.2、stringRedisTemplate API(部分展示)

opsForHash --> hash 操作
opsForList --> list 操作
opsForSet --> set 操作
opsForValue --> string 操作
opsForZSet --> Zset 操作
7f43b40ae0f3df3c40ca4e788b08bf5c.png

3.3.3 StringRedisTemplate 默认序列化机制

public class StringRedisTemplate extends RedisTemplate {/** * Constructs a new StringRedisTemplate instance. {@link #setConnectionFactory(RedisConnectionFactory)} * and {@link #afterPropertiesSet()} still need to be called. */public StringRedisTemplate() {RedisSerializer stringSerializer = new StringRedisSerializer();setKeySerializer(stringSerializer);setValueSerializer(stringSerializer);setHashKeySerializer(stringSerializer);setHashValueSerializer(stringSerializer);}}

四、RedissonClient 操作示例

4.1 基本配置

4.1.1、Maven pom 引入

     org.springframework.boot     spring-boot-starter-data-redis       org.redisson     redisson     3.8.2     true       org.redisson     redisson-spring-boot-starter     LATEST 

4.1.2、添加配置文件 Yaml 或者 json 格式

redisson-config.yml

# Redisson 配置singleServerConfig:  address: "redis://192.168.1.140:6379"  password: null  clientName: null  database: 15 #选择使用哪个数据库0~15  idleConnectionTimeout: 10000  pingTimeout: 1000  connectTimeout: 10000  timeout: 3000  retryAttempts: 3  retryInterval: 1500  reconnectionTimeout: 3000  failedAttempts: 3  subscriptionsPerConnection: 5  subscriptionConnectionMinimumIdleSize: 1  subscriptionConnectionPoolSize: 50  connectionMinimumIdleSize: 32  connectionPoolSize: 64  dnsMonitoringInterval: 5000  #dnsMonitoring: falsethreads: 0nettyThreads: 0codec:  class: "org.redisson.codec.JsonJacksonCodec"transportMode: "NIO"

或者,配置 redisson-config.json

{  "singleServerConfig": {    "idleConnectionTimeout": 10000,    "pingTimeout": 1000,    "connectTimeout": 10000,    "timeout": 3000,    "retryAttempts": 3,    "retryInterval": 1500,    "reconnectionTimeout": 3000,    "failedAttempts": 3,    "password": null,    "subscriptionsPerConnection": 5,    "clientName": null,    "address": "redis://192.168.1.140:6379",    "subscriptionConnectionMinimumIdleSize": 1,    "subscriptionConnectionPoolSize": 50,    "connectionMinimumIdleSize": 10,    "connectionPoolSize": 64,    "database": 0,    "dnsMonitoring": false,    "dnsMonitoringInterval": 5000  },  "threads": 0,  "nettyThreads": 0,  "codec": null,  "useLinuxNativeEpoll": false}

4.1.3、读取配置

新建读取配置类

@Configurationpublic class RedissonConfig {    @Bean    public RedissonClient redisson() throws IOException { // 两种读取方式,Config.fromYAML 和 Config.fromJSON// Config config = Config.fromJSON(RedissonConfig.class.getClassLoader().getResource("redisson-config.json")); Config config = Config.fromYAML(RedissonConfig.class.getClassLoader().getResource("redisson-config.yml")); return Redisson.create(config);    }}

或者,在 application.yml 中配置如下

spring:  redis:    redisson:      config: classpath:redisson-config.yaml

4.2 使用示例

@RestController@RequestMapping("/")public class TeController {    @Autowired    private RedissonClient redissonClient;    static long i = 20;    static long sum = 300;//    ========================== String =======================    @GetMapping("/set/{key}")    public String s1(@PathVariable String key) { // 设置字符串 RBucket keyObj = redissonClient.getBucket(key); keyObj.set(key + "1-v1"); return key;    }    @GetMapping("/get/{key}")    public String g1(@PathVariable String key) { // 设置字符串 RBucket keyObj = redissonClient.getBucket(key); String s = keyObj.get(); return s;    }    //    ========================== hash =======================-=    @GetMapping("/hset/{key}")    public String h1(@PathVariable String key) { Ur ur = new Ur(); ur.setId(MathUtil.randomLong(1,20)); ur.setName(key);      // 存放 Hash RMap ss = redissonClient.getMap("UR"); ss.put(ur.getId().toString(), ur); return ur.toString();    }    @GetMapping("/hget/{id}")    public String h2(@PathVariable String id) { // hash 查询 RMap ss = redissonClient.getMap("UR"); Ur ur = ss.get(id); return ur.toString();    }    // 查询所有的 keys    @GetMapping("/all")    public String all(){ RKeys keys = redissonClient.getKeys(); Iterable keys1 = keys.getKeys(); keys1.forEach(System.out::println); return keys.toString();    }    // ================== ==============读写锁测试 =============================    @GetMapping("/rw/set/{key}")    public void rw_set(){// RedissonLock. RBucket ls_count = redissonClient.getBucket("LS_COUNT"); ls_count.set("300",360000000l, TimeUnit.SECONDS);    }    // 减法运算    @GetMapping("/jf")    public void jf(){ String key = "S_COUNT";// RAtomicLong atomicLong = redissonClient.getAtomicLong(key);// atomicLong.set(sum);// long l = atomicLong.decrementAndGet();// System.out.println(l); RAtomicLong atomicLong = redissonClient.getAtomicLong(key); if (!atomicLong.isExists()) {     atomicLong.set(300l); } while (i == 0) {     if (atomicLong.get() > 0) {  long l = atomicLong.getAndDecrement();   try {Thread.sleep(1000l);   } catch (InterruptedException e) {e.printStackTrace();   }  i --;  System.out.println(Thread.currentThread().getName() + "->" + i + "->" + l);     } }    }    @GetMapping("/rw/get")    public String rw_get(){ String key = "S_COUNT"; Runnable r = new Runnable() {     @Override     public void run() {  RAtomicLong atomicLong = redissonClient.getAtomicLong(key);  if (!atomicLong.isExists()) {      atomicLong.set(300l);  }  if (atomicLong.get() > 0) {      long l = atomicLong.getAndDecrement();      i --;      System.out.println(Thread.currentThread().getName() + "->" + i + "->" + l);  }     } }; while (i != 0) {     new Thread(r).start();//     new Thread(r).run();//     new Thread(r).run();//     new Thread(r).run();//     new Thread(r).run(); } RBucket bucket = redissonClient.getBucket(key); String s = bucket.get(); System.out.println("================线程已结束================================" + s); return s;    }}

4.3 扩展

4.3.1 丰富的 jar 支持,尤其是对 Netty NIO 框架

4.3.2 丰富的配置机制选择,这里是详细的配置说明

关于序列化机制中,就有很多

dea92190bc0b2cffca6c34b9b9c00764.png
62505939d701b81e01d367ddfd4836d5.png

4.3.3 API 支持(部分展示),具体的 Redis --> RedissonClient , 可查看这里

d51fd6cb8980ec927f8442d6867de04d.png

4.3.4 轻便的丰富的锁机制的实现

4.3.4.1 Lock

4.3.4.2 Fair Lock

4.3.4.3 MultiLock

4.3.4.4 RedLock

4.3.4.5 ReadWriteLock

4.3.4.6 Semaphore

4.3.4.7 PermitExpirableSemaphore

4.3.4.8 CountDownLatch

五、基于注解实现的 Redis 缓存

5.1 Maven 和 YML 配置

参考 RedisTemplate 配置

另外,还需要额外的配置类

// todo 定义序列化,解决乱码问题@EnableCaching@Configuration@ConfigurationProperties(prefix = "spring.cache.redis")public class RedisCacheConfig {    private Duration timeToLive = Duration.ZERO;    public void setTimeToLive(Duration timeToLive) { this.timeToLive = timeToLive;    }    @Bean    public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); // 解决查询缓存转换异常的问题 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解决乱码的问题) RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()  .entryTtl(timeToLive)  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))  .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory)  .cacheDefaults(config)  .build(); return cacheManager;    }}

5.2 使用示例

@Transactional@Servicepublic class ReImpl implements RedisService {    @Resource    private CustomerRepo customerRepo;    @Resource    private StringRedisTemplate stringRedisTemplate;    public static final String REDIS_CUSTOMERS_ONE = "Customers";    public static final String REDIS_CUSTOMERS_ALL = "allList";    // =====================================================================使用Spring cahce 注解方式实现缓存    // ==================================单个操作    @Override    @Cacheable(value = "cache:customer", unless = "null == #result",key = "#id")    public CustomersEntity cacheOne(Integer id) { final Optional byId = customerRepo.findById(id); return byId.isPresent() ? byId.get() : null;    }    @Override    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#id")    public CustomersEntity cacheOne2(Integer id) { final Optional byId = customerRepo.findById(id); return byId.isPresent() ? byId.get() : null;    }     // todo 自定义redis缓存的key,    @Override    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#root.methodName + '.' + #id")    public CustomersEntity cacheOne3(Integer id) { final Optional byId = customerRepo.findById(id); return byId.isPresent() ? byId.get() : null;    }    // todo 这里缓存到redis,还有响应页面是String(加了很多转义符\,),不是Json格式    @Override    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#root.methodName + '.' + #id")    public String cacheOne4(Integer id) { final Optional byId = customerRepo.findById(id); return byId.map(JSONUtil::toJsonStr).orElse(null);    }     // todo 缓存json,不乱码已处理好,调整序列化和反序列化    @Override    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#root.methodName + '.' + #id")    public CustomersEntity cacheOne5(Integer id) { Optional byId = customerRepo.findById(id); return byId.filter(obj -> !StrUtil.isBlankIfStr(obj)).orElse(null);    }    // ==================================删除缓存    @Override    @CacheEvict(value = "cache:customer", key = "'cacheOne5' + '.' + #id")    public Object del(Integer id) { // 删除缓存后的逻辑 return null;    }    @Override    @CacheEvict(value = "cache:customer",allEntries = true)    public void del() {    }    @CacheEvict(value = "cache:all",allEntries = true)    public void delall() {    }    // ==================List操作    @Override    @Cacheable(value = "cache:all")    public List cacheList() { List all = customerRepo.findAll(); return all;    }    // todo 先查询缓存,再校验是否一致,然后更新操作,比较实用,要清楚缓存的数据格式(明确业务和缓存模型数据)    @Override    @CachePut(value = "cache:all",unless = "null == #result",key = "#root.methodName")    public List cacheList2() { List all = customerRepo.findAll(); return all;    }}

5.3 扩展

基于 spring 缓存实现
86ab93d5014c7efe76191358e908cb54.png

https://blog.csdn.net/qq_42105629/article/details/102589319

-End-

最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。

f6001c9d9559fa0c25e91151b560acfe.png

正文结束

推荐阅读 ↓↓↓

1.心态崩了!税前2万4,到手1万4,年终奖扣税方式1月1日起施行~

2.深圳一普通中学老师工资单曝光,秒杀程序员,网友:敢问是哪个学校毕业的?

3.从零开始搭建创业公司后台技术栈

4.程序员一般可以从什么平台接私活?

5.清华大学:2021 元宇宙研究报告!

6.为什么国内 996 干不过国外的 955呢?

7.这封“领导痛批95后下属”的邮件,句句扎心!

8.15张图看懂瞎忙和高效的区别!

7a8d2dbae3a49ced369c622735e7e0aa.gif

艺术字体转换器