redis的应用,缓存,分布式锁

1.应用

1.1可以用作缓存

作用:提交数据的查询效率,减少对数据库的访问频率

什么数据适合放入缓存

1.查询频率高,修改频率低

2.对安全系数比较低

如何实现

java 复制代码
@Service
public class DeptServer {
    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    public Dept selectById(Integer id){
        //查询是否在缓存
        ValueOperations forValue = redisTemplate.opsForValue();
        Object o = forValue.get("dept::" + id);
        if(o!=null){
            return (Dept) o;
        }
        Dept dept = deptMapper.selectById(id);
        //存储缓存
        forValue.set("dept::" + id,dept);
        return dept;
    }
}

1.1.2缓存注解

spring4.0以后,提供了缓存注解,可以使用注解完成缓存功能,无需自己写

1.在配置类中添加缓存配置

java 复制代码
 @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);
        //配置序列化(解决乱码的问题),过期时间600s
        RedisCacheConfiguration config=RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))//设置过期时间
                //设置key序列化方式
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                //设置value序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager=RedisCacheManager.builder(factory).cacheDefaults(config).build();
        return cacheManager;
     }

2.使用注解@Cacheable

java 复制代码
@Service
public class DeptServer {
    @Autowired
    private DeptMapper deptMapper;
    //@Cacheable针对查询的,缓存名为"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
    @Cacheable(value = "dept",key = "#id")
    public Dept selectById(Integer id){
        Dept dept = deptMapper.selectById(id);
        return dept;
    }
}

3.开启缓存注解

java 复制代码
package com.ghx.server;

import com.ghx.dao.DeptMapper;
import com.ghx.pojo.entity.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author :guo
 * @date :Created in 2025/2/18 14:22
 * @description:
 * @version:
 */
@Service
public class DeptServer {
    @Autowired
    private DeptMapper deptMapper;
    //@Cacheable针对查询的,缓存名为"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
    @Cacheable(value = "dept",key = "#id")
    public Dept selectById(Integer id){
        Dept dept = deptMapper.selectById(id);
        return dept;
    }
    //先执行方法体,再删除缓存
    @CacheEvict(value = "dept",key = "#id")
    public int delete(Integer id){
        return deptMapper.deleteById(id);
    }

    //先修改数据库,再修改缓存,把dept对象存入
    @CachePut(value = "dept",key = "#dept.id")
    public Dept update(Dept dept){
        deptMapper.updateById(dept);
        return dept;
    }

}
java 复制代码
package com.ghx.controller;

import com.ghx.pojo.entity.Dept;
import com.ghx.server.DeptServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author :guo
 * @date :Created in 2025/2/18 14:24
 * @description:
 * @version:
 */
@RestController
public class DeptController {
    @Autowired
    private DeptServer deptServer;

    @GetMapping("selectById/{id}")
    public Dept selectById(@PathVariable Integer id) {
        //@PathVariable获取请求参数的{}参数
        return deptServer.selectById(id);
    }

    @GetMapping("delete/{id}")
    public int delete(@PathVariable Integer id) {
        return deptServer.delete(id);
    }

    @PutMapping("update")
    public Dept update(@RequestBody Dept dept) {
        return deptServer.update(dept);
    }
}

1.1.3缓存注解

复制代码
@Cacheable针对查询的,缓存名如"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
复制代码
@CacheEvict先执行方法体,再删除缓存
复制代码
@CachePut先修改数据库,再修改缓存,把dept对象存入

1.2可以作为分布式锁

synchronized:

lock:接口

实现类:

复制代码
        ReentrantLock:重入锁
        ReentrantReadWriteLock:读写分离锁

加的为读锁则线程不堵塞

加的是写锁线程堵塞

上面的都是本地锁

本地锁

java 复制代码
 //如果在单线程下---该功能没有任何问题。
    //如果在多线程的情况---jmeter压测工具---发现出现线程安全问题了。==使用锁synchronized ()或lock锁。
    //发现使用锁之前没有问题了。但是如果该项目是一个集群。--发现在集群的情况下本地锁【只对当前工程有效】无效了。
    //解决方案就是集群工程共用一把锁就行。---可以使用redis来解决问题。
public String decrement(Integer productid) {
        //根据id查询商品的库存
        synchronized (this) {
            int num = stockDao.findById(productid);
            if (num > 0) {
                //修改库存
                stockDao.update(productid);
                System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
            } else {
                System.out.println("商品编号为:" + productid + "的商品库存不足。");
                return "商品编号为:" + productid + "的商品库存不足。";
            }
        }
    }

手动实现

java 复制代码
@Autowired
    private StringRedisTemplate redisTemplate;


    public String decrement(Integer productid) {
        ValueOperations<String,String> forValue = redisTemplate.opsForValue();
        //判断是否获取锁   如果程序死机了 ,无法执行完成
        //如果代码没有执行完成,会释放锁。      ------开启了一个守护线程,每隔10s检查当前线程是否还持有锁,如果当前线程还持有锁,就重新为当前线程分配30s
        //分配3次,33次后直接释放锁
        Boolean aBoolean = forValue.setIfAbsent("product::" + productid, "~~~~");
        if(aBoolean) {
            try {
                //根据id查询商品的库存
                int num = stockDao.findById(productid);
                if (num > 0) {
                    //修改库存
                    stockDao.update(productid);
                    System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                    return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
                } else {
                    System.out.println("商品编号为:" + productid + "的商品库存不足。");
                    return "商品编号为:" + productid + "的商品库存不足。";
                }
            } finally {
                redisTemplate.delete("product::" + productid);
            }

        }
        System.out.println("服务器正忙");
        return "服务器正忙";
    }

Redisson

每隔10s检查当前线程是否还持有锁,如果当前线程还持有锁,就重新为当前线程分配30s

依赖

复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.24.3</version>
</dependency>
java 复制代码
@Autowired
    private RedissonClient redissonClient;

    public String decrement(Integer productid) {
        RLock lock = redissonClient.getLock("product::" + productid);

        try {
            //必须为30s
            lock.lock(30, TimeUnit.SECONDS);
                //根据id查询商品的库存
                int num = stockDao.findById(productid);
                if (num > 0) {
                    //修改库存
                    stockDao.update(productid);
                    System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                    return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
                } else {
                    System.out.println("商品编号为:" + productid + "的商品库存不足。");
                    return "商品编号为:" + productid + "的商品库存不足。";
                }
        } finally {
             lock.unlock();
        }
相关推荐
喜欢便码9 分钟前
JS小练习0.1——弹出姓名
java·前端·javascript
王磊鑫1 小时前
重返JAVA之路-初识JAVA
java·开发语言
半兽先生2 小时前
WebRtc 视频流卡顿黑屏解决方案
java·前端·webrtc
南星沐3 小时前
Spring Boot 常用依赖介绍
java·前端·spring boot
代码不停3 小时前
Java中的异常
java·开发语言
Justice link4 小时前
部署redis cluster
数据库·redis·缓存
何似在人间5754 小时前
多级缓存模型设计
java·jvm·redis·缓存
多云的夏天4 小时前
ubuntu24.04-MyEclipse的项目导入到 IDEA中
java·intellij-idea·myeclipse
Fanxt_Ja4 小时前
【数据结构】红黑树超详解 ---一篇通关红黑树原理(含源码解析+动态构建红黑树)
java·数据结构·算法·红黑树
Aphelios3804 小时前
TaskFlow开发日记 #1 - 原生JS实现智能Todo组件
java·开发语言·前端·javascript·ecmascript·todo