Spring Boot 操作 Redis 的各种实现

news2024/12/23 0:51:41

一、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.2、Redisson

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

1.3、 Lettuce

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

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

二、RedisTemplate

2.1、使用配置

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

 

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

application-dev.yml

 

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

2.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<CustomersEntity> 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;
    }

2.3、扩展

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


3.3.2、stringRedisTemplate API(部分展示)
  • opsForHash --> hash操作

  • opsForList --> list操作

  • opsForSet --> set操作

  • opsForValue --> string操作

  • opsForZSet --> Zset操作

3.3.3 StringRedisTemplate默认序列化机制
 

public class StringRedisTemplate extends RedisTemplate<String, String> {

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

三、RedissonClient 操作示例

3.1 基本配置

3.1.1、Maven pom 引入
 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.2</version>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>LATEST</version>
</dependency>

3.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: false

threads: 0
nettyThreads: 0
codec:
  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
}

3.1.3、读取配置

新建读取配置类

 

@Configuration
public 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

3.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<String> keyObj = redissonClient.getBucket(key);
        keyObj.set(key + "1-v1");
        return key;
    }

    @GetMapping("/get/{key}")
    public String g1(@PathVariable String key) {
        // 设置字符串
        RBucket<String> 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<String, Ur> ss = redissonClient.getMap("UR");
        ss.put(ur.getId().toString(), ur);
        return ur.toString();
    }

    @GetMapping("/hget/{id}")
    public String h2(@PathVariable String id) {
        // hash 查询
        RMap<String, Ur> ss = redissonClient.getMap("UR");
        Ur ur = ss.get(id);
        return ur.toString();
    }

    // 查询所有的 keys
    @GetMapping("/all")
    public String all(){
        RKeys keys = redissonClient.getKeys();
        Iterable<String> keys1 = keys.getKeys();
        keys1.forEach(System.out::println);
        return keys.toString();
    }

    // ================== ==============读写锁测试 =============================

    @GetMapping("/rw/set/{key}")
    public void rw_set(){
//        RedissonLock.
        RBucket<String> 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<String> 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 丰富的配置机制选择,这里是详细的配置说明

https://github.com/redisson/redisson/wiki/2.-Configuration

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

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

https://github.com/redisson/redisson/wiki/11.-Redis-commands-mapping

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

  • Lock

  • Fair Lock

  • MultiLock

  • RedLock

  • ReadWriteLock

  • Semaphore

  • PermitExpirableSemaphore

  • CountDownLatch

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

4.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<String> 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;
    }

}

4.2 使用示例

 

@Transactional
@Service
public 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<CustomersEntity> 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<CustomersEntity> 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<CustomersEntity> 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<CustomersEntity> 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<CustomersEntity> 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<CustomersEntity> cacheList() {
        List<CustomersEntity> all = customerRepo.findAll();
        return all;
    }

    // todo 先查询缓存,再校验是否一致,然后更新操作,比较实用,要清楚缓存的数据格式(明确业务和缓存模型数据)
    @Override
    @CachePut(value = "cache:all",unless = "null == #result",key = "#root.methodName")
    public List<CustomersEntity> cacheList2() {
        List<CustomersEntity> all = customerRepo.findAll();
        return all;
    }

}

4.3 扩展

基于spring缓存实现

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/723593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

卡尔曼滤波实例分析(二)

1 问题 假设一物体以一初速度 v 0 v_0 v0​位于一高度为 y 0 y_0 y0​处正处于匀速下降状态&#xff0c;此时该物体启动制动装置&#xff0c;以一个加速度为 a a a的作用力反向运动 &#xff08;1&#xff09;建模 速度&#xff1a; V V 0 − a ∗ t V V_0 - a*t VV0​−a∗…

API6中JS UI实现富文本组件居右显示

【关键字】 RichText、富文本组件、API6、JS UI、居右显示 【关键代码如下】 index.hml <div class"container"><text>文本行高</text><text>文本行高</text><text>文本行高</text><text>文本行高</text>&…

SciencePub学术 | 计算机与生物信息类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 计算机与生物信息类重点SCIE&EI征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 计算机与生物信息类重点SCIE&EI 【期刊简介】IF&#xff1a;7.5-8.0&#xff0c;JCR1区&#xff0c;中科院1区TOP&…

vscode设置自己用的注释格式

ctrlshiftP 打开设置 输入snippets&#xff0c;选择配置用户代码片段[Snippets: Configure User Snippets]输入JavaScript&#xff0c;选择JavaScript.json 把这段代码替换进去 "Print to jsNoteTitle": {"prefix": "jsNoteTitle","body&q…

阿里云国际站:阿里云还值得我们期待吗?

阿里云还值得我们期待吗&#xff1f;   "阿里云&#xff0c;还可以继续期待吗&#xff1f;"这是一个近期在各大行业论坛和科技洪流之中频繁引发热议的问题。然而&#xff0c;深究其实质&#xff0c;答案不言自明——无须怀疑&#xff0c;阿里云绝对值得我们赋予期待…

MTK日志路径——aosp\device\mediatek\common\mtklog

这里写目录标题 device\mediatek\common\mtklog1.mtklog-config-basic-eng.prop2.mtklog-config-basic-user.prop3.mtklog-config-bsp-eng.prop4.mtklog-config-bsp-user.prop device\mediatek\common\mtklog 在安卓源码中&#xff0c;device\mediatek\common\mtklog目录包含了…

解决node+mysql不能保存emoji表情包的问题

效果图 1.前端 2.数据库 实现 Emoji表情是4个字节&#xff0c;而mysql的utf8编码最多3个字节&#xff0c;所以数据插不进去&#xff0c;而utfmb64是支持四个字节的。所以需要将mysql编码从utf8转换成utf8mb4&#xff0c;从【数据库、表、相应字段】都需要修改为utf8mb4&…

Flask+Vue+小程序电商生态系统

完整资料进入【数字空间】查看——baidu搜索"writebug" 本项目为FlaskVue小程序全栈开源电商生态系统。本项目使用的技术有&#xff1a;前端&#xff1a;VueIviewWxxcx&#xff1b;后端&#xff1a;FlaskMysql&#xff1b;部署&#xff1a;NginxGunicorn。该项目包含…

C++—异常与类型转换、大小端存储、不使用额外空间的情况下交换两个数

异常 常见的异常包括&#xff1a;数组下标越界&#xff0c;除法计算的时候除数为0&#xff0c;动态分配空间时空间不足。 try&#xff0c;throw&#xff0c;catch #include <iostream> using namespace std; int main() {double m 1, n 0;try {cout << "b…

其实,大多数的项目经理都是在假装努力罢了

早上好&#xff0c;我是老原。 有很多项目经理平时忙的团团转&#xff0c;看起来努力的不行&#xff0c;但实际上进度缓慢、结果不佳&#xff0c;更别说个人成长了&#xff0c;问就是工作都这么忙了&#xff0c;哪有时间。 说来也能理解&#xff0c;毕竟现在不是007就是996&a…

spring整合RabbitMQ

先导入依赖POM.xml 之前导的不全一直报错后面导入的可能有多的 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sc…

Python3,66行代码,搞了个音乐下载器,从此听歌再也不需要花费银子了,真香!

66行代码敲出音乐下载器 1、引言2、代码实战2.1 思路2.2 安装2.3 示例 3、 总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;最近比较流行的那首歌&#xff0c; 咋又说费了。 小鱼&#xff1a;那你就冲个VIP呗。 小屌丝&#xff1a;开玩笑&#xff0c; 我的钱又不是大风刮过来…

第2集丨JavaScript 中原型链(prototype chain)与继承

目录 一、一些基础概念1.1 ECMAScript 标准1.2 prototype和 __proto__1.3 constructor属性1.4 函数名 二、原型链的维护2.1 内部原型链和构造器原型链2.2 从实例回溯原型链2.3 修正原型指向 三、基于原型链的继承3.1 继承属性3.2 继承“方法” 四、构造函数4.1 案例一个简单的实…

SAP S4 Hana 财务凭证存储表变化

一、统一日记账的表 1. ACDOCA里有的&#xff0c;BSEG里不一定有 以下的一些凭证行项目&#xff0c;都是只在ACDOCA表里面存在&#xff0c;而在BSEG表里不存在的&#xff08;你可以通过BKPF表的BSTAT字段的凭证状态U来识别&#xff09;&#xff1a; 资产折旧过账 特定账套&am…

VMware Workstation虚拟机如何连接usb网卡

小伙伴不知道怎么将网卡链接到VMware虚拟机系统里面&#xff0c;因此今天我们就做一个简单的教程&#xff0c;教大家如何连接虚拟机使用&#xff1a; 1.插上usb网卡&#xff0c;然后如下图所示操作&#xff1a; 2.点击连接网卡到虚拟机之后&#xff0c;查询一下网卡是否识别到…

【JAVA程序员学C++】第二节、引用与指针,类型转换,结构体

一、指针与引用 1.1 指针 先说指针&#xff0c;由于java有jvm&#xff0c;所以对于java程序员&#xff0c;对于内存这一块关注就毕竟少了。但是C不同&#xff0c;C里面所有的堆内存&#xff0c;都需要程序员自己把控&#xff0c;把控不好&#xff0c;泄露了也是常有的事情。 …

Leetcode刷题笔记--Hot11-20

1--有效的括号&#xff08;20&#xff09; 主要思路&#xff1a; 利用栈&#xff0c;遍历字符串&#xff0c;遇到左括号则入栈&#xff0c;遇到右括号则出栈&#xff0c;并判断出栈元素是否与右括号匹配&#xff1b; 当字符串有效时&#xff0c;栈为空&#xff08;所有左括号都…

python语法 数据结构-字典和集合

文章目录 1. 字典1. 1. 字典特征1. 2. 创建字典1. 3.字典常用方法1.3.1 get()1.3.2 clear()1.3.3 copy()1.3.4 copy()1.3.4 update(key value)1.3.5 keys()、 values()和items() 1. 4. 获取字典值1. 4.1 通过 Key1. 4.2 通过迭代 1. 5. 列表与运算符 2. 集合2. 1. 元组特征2. …

NSS [HNCTF 2022 WEEK2]easy_include

NSS [HNCTF 2022 WEEK2]easy_include 过滤好多&#xff0c;试试日志包含。 服务器是nginx ?file/var/log/nginx/access.log 修改UA头为<?php system(‘ls’); ?> 修改UA头为<?php system(‘ls /’); ?> 修改UA头为<?php system(tac /ffflllaaaggg); ?&g

word2vec工具实战(使用gensim)

最开始需要新建一个conda环境 conda create -n word2vec python3.8 conda activate word2vec然后安装一下所需要的库 pip install numpy pip install scipy pip install gensim pip install jieba首先下载一下数据集zhwiki-20230701-pages-articles.xml.bz2&#xff0c;为了方…