Qouson's blog Qouson's blog
首页
  • Java 基础

    • 基础
    • String
  • Java 中级

    • 网络编程
  • Java 高级

    • JVM
    • 多线程
  • Spring
  • SpringMVC
  • SpringBoot
  • MySQL
  • Redis
  • MQ
  • ZooKeeper
  • git
  • linux
  • 设计模式
  • 数据结构与算法
  • 计算机基础
  • Java相关框架
  • 分布式
  • DDD领域驱动设计
  • 系统设计
  • 杂乱无章
Java知识图谱
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

qouson

Java界的小学生
首页
  • Java 基础

    • 基础
    • String
  • Java 中级

    • 网络编程
  • Java 高级

    • JVM
    • 多线程
  • Spring
  • SpringMVC
  • SpringBoot
  • MySQL
  • Redis
  • MQ
  • ZooKeeper
  • git
  • linux
  • 设计模式
  • 数据结构与算法
  • 计算机基础
  • Java相关框架
  • 分布式
  • DDD领域驱动设计
  • 系统设计
  • 杂乱无章
Java知识图谱
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • mysql

  • redis

    • Redis
    • 版本介绍
    • redis分布式锁
      • 面试题
      • 什么是分布式锁
      • 超卖案例
        • 场景:多个服务之间,同一时刻,同一个用户只能有一个请求,防止关键业务数据冲突和并发错误
        • 建module
        • 改pom
        • 改yaml/properties
        • RedisConfig配置类
        • JedisUtils
        • GoodsController
        • Redis数据
    • 缓存淘汰策略
    • Redis_xiaolingcode
  • mq

  • zookeeper

  • 中间件
  • redis
qouson
2024-05-23
目录

redis分布式锁

# Redis分布式锁

# 面试题

  • Redis除了做缓存,你还见过Redis的什么用法?
  • Redis做分布式锁要注意什么问题?
  • 如果Redis是单点部署,会带来什么问题?
  • 集群模式下,比如主从模式,会有什么问题?
  • 你的简历上有写redisson,介绍一下RedLock吧?
  • Redis分布式锁续命,看门狗知道吗?

# 什么是分布式锁

  • JVM层面的锁,是单机锁。
  • 分布式微服务架构,拆分各个微服务之间为了避免冲突和数据故障而加入的一种锁。
  • 分布式锁方案:zookeeper,mysql,redis[推荐] -redlock -- redisson lock/unlock

# 超卖案例

# 场景:多个服务之间,同一时刻,同一个用户只能有一个请求,防止关键业务数据冲突和并发错误

# 建module

  • boot-redis01
  • boot-redis02

# 改pom

  • 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:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jjc</groupId>
	<artifactId>boot-redis01</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>boot-redis01</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<!--web + actuator-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!--SpringBoot与redis整合-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
		</dependency>
		<!--jedis-->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>3.1.0</version>
		</dependency>
		<!--springboot-aop-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--redisson-->
		<dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson</artifactId>
			<version>3.15.1</version>
		</dependency>
		<!--一般通用-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

# 改yaml/properties

  • application.properties
# 第一个模块的配置,第二个修改端口号就好9999
server.port=8888  /
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
1
2
3
4
5
6
7
8
9
10

# RedisConfig配置类

  • RedisConfig.java
@Configuration
public class RedisConfig{

    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory){
        RedisTemplate<String,Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public Redisson redisson(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
        return (Redisson)Redisson.create(config);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# JedisUtils

  • JedisUtils.java
public class RedisUtils {
    private static JedisPool jedisPool;
    static{
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(20);
        jedisPoolConfig.setMaxIdle(10);
        jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379);
    }
    public static Jedis getJedis() throws Exception {
        if(null != jedisPool){
            return jedisPool.getResource();
        }
        throw new Exception("Jedispool is not ok");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# GoodsController

  • GoodsController.java
@RestController
public class GoodController{

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private Redisson redisson;

    @Value("${server.port}")
    private String serverPort;

    final Lock lock = new ReentrantLock();

    public static final String REDIS_LOCK = "REDIS_LOCK";

    @GetMapping("/buy_Goods")
    public String buy_Goods() throws Exception {
        //1.0
        //没有锁
        //2.0 加单机锁
        //synchronized lock
        //3.0 由于是分布式,单机锁,会超卖
        //stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK);
        //stringRedisTemplate.delete(REDIS_LOCK);
        //4.0
        //stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK);
        //finally{
        //    stringRedisTemplate.delete(REDIS_LOCK);
        //}
        //5.0 有服务器可能宕机,需要设置超时
        //stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK);stringRedisTemplate.expire(REDIS_LOCK,10,TimeUnit.SECONDS);stringRedisTemplate.delete(REDIS_LOCK);
        //6.0 加锁和设置超时不是原子操作,需要设置为原子操作
        //stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK,value,10,TimeUnit.SECONDS);stringRedisTemplate.delete(REDIS_LOCK);
        //7.0 可能会出现A删B的锁,需要判断一下
        //if(value.equalsIgnoreCase(stringRedisTemplate.opsForValue().get(REDIS_LOCK))){
        //    stringRedisTemplate.delete(REDIS_LOCK);
        //}
        //8.0 判断锁和删除操作不是原子操作,需要改为原子操作
        //8.1 使用redis事务
        //while(true){
        //    stringRedisTemplate.watch(REDIS_LOCK);
        //    if(value.equalsIgnoreCase(stringRedisTemplate.opsForValue().get(REDIS_LOCK))){
        //        stringRedisTemplate.setEnableTransactionSupport(true);
        //        stringRedisTemplate.multi();
        //        stringRedisTemplate.delete(REDIS_LOCK);
        //        List<Object> list = stringRedisTemplate.exec();
        //        if(null == list){
        //            continue;
        //        }
        //    }
        //    stringRedisTemplate.unwatch();
        //    break;
        //}
        //8.2 使用lua脚本
        //Jedis jedis = RedisUtils.getJedis();
        //String script = "if redis. call(' get', KEYS[1])==ARGV[1]" +
        //        "then" +
        //        "return redis. call(' del', KEYS[1])" +
        //        "else" +
        //       "return" +
        //        "end";
        //try{
        //    Object o = jedis.eval(script, Collections.singletonList(REDIS_LOCK),Collections.singletonList(value));
        //    if("1".equals(o.toString())){
        //        System.out.println("----del redis lock ok");
        //    }else{
        //        System.out.println("----del redis lock error");
        //    }
        //}finally {
        //    if(null != jedis){
        //        jedis.close();
        //    }
        //}
        //9.0 使用redisson进行缓存续命
        //RLock redissonLock = redisson.getLock(REDIS_LOCK);
        //redissonLock.lock();
        //if(redissonLock.isLocked()){
        //    if(redissonLock.isHeldByCurrentThread()){
        //        redissonLock.unlock();
        //    }
        //}
        String value = UUID.randomUUID().toString() + Thread.currentThread().getName();
        RLock redissonLock = redisson.getLock(REDIS_LOCK);
        redissonLock.lock();
        try {
            //查看库存数量
            String result = stringRedisTemplate.opsForValue().get("goods:001");
            int goodsNumber = result == null ? 0 : Integer.parseInt(result);
            //卖商品
            if(goodsNumber > 0){
                int realNumber = goodsNumber - 1;
                //成功买入,库存减少一件
                stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
                System.out.println("成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort);
                return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
            }else{
                System.out.println("商品卖完"+"服务端口:"+serverPort);
                return "商品卖完!"+"服务端口:"+serverPort;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(redissonLock.isLocked()){
                if(redissonLock.isHeldByCurrentThread()){
                    redissonLock.unlock();
                }
            }
        }
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

# Redis数据

# 放入001库存100个
set goods:001 100
1
2
编辑 (opens new window)
上次更新: 2024/05/24, 11:36:46
版本介绍
缓存淘汰策略

← 版本介绍 缓存淘汰策略→

最近更新
01
杂乱无章
12-25
02
基础-大彬
11-14
03
集合-大彬
11-14
更多文章>
Theme by Vdoing | Copyright © 2023-2025 qouson
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式