0%

Redis Note

Redis使用相关笔记

Redis Note

待完成

优点:内存数据库,读写速度快,支持setnx、lua脚本

使用

实现分布式锁

set key value nx

当有用户设置后,其他用户设置会返回为false

可以实现分布式锁

问题与解决

  • 释放锁(腾地方)

  • 一定要设置过期时间,服务器释放挂了,也会释放

  • 如果方法执行过长,锁提前过期?

    • 还是会存在多个方法执行的情况
    • 多个方法执行时,释放掉别人的锁 —— 判断是否是自己的锁

    解决方法:

    续期(看门狗机制)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int end = false;
    new Thread(() -> {
    if(!end){
    续期;
    }
    })
    -------------------
    public void controller(){
    int end = false;
    方法;
    end = true;
    }
  • 释放过程

    释放锁的时候,有可能会先判断是否是自己的锁,但如果这是锁过期了,还是会释放别人的锁

    1
    2
    3
    4
    5
    // 原子操作
    if(get lock == selfLock){
    当前位置 //set lock B
    del Lock
    }

    解决方式:

    Redis + Lua 脚本实现

  • Redis如果是集群(而不是只有一个Redis),如果分布式锁的数据不同步怎么办
    解决方法:

Redisson实现分布式锁

介绍

Java客户端,数据网格

实现了很多Java里支持的接口和数据结构

Redisson是一个Java中操作Redis的客户端,提供了大量的分布式数据集来简化对Redis的操作和使用,可以让开发者像使用本地集合一样使用Redis,完全感知不到Redis的存在。

引入方式

  1. SpringBoot引入,迭代过快,易出现版本问题,不推荐

  2. 建议直接引入Redisson,参考QuickStart

引入与示例

配置类

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
/**
* Redisson 配置
* @author yancy0109
*/
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {

private String host;
private String port;

@Bean
public RedissonClient redissonClient(){
// 1. Create config object
Config config = new Config();
String redisAddress = String.format("redis://%s:%s", host, port);
config.useSingleServer()
.setAddress(redisAddress)
.setDatabase(3);
// .setPassword();
// 2. Create Redisson instance

// Sync and Async API 支持 同步 or 异步
RedissonClient redisson = Redisson.create(config);

// Reactive API
// RedissonReactiveClient redissonReactive = redisson.reactive();

// RxJava3 API
// RedissonRxClient redissonRx = redisson.rxJava();
return redisson;
}
}

示例

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
//list
//存在本地JVM内存
List<String> list = new ArrayList<>();
list.add("yupi");
// list.get(0);
// list.remove(0);
System.out.println("list: " + list.get(0));
//存在redis内存
RList<String> rList = redissonClient.getList("test-list");
// rList.add("yupi");
// rList.get(0);
// rList.remove(0);
rList.remove(0);
System.out.println("rlist:" + rList.get(0));

//分布式锁操作
RLock lock = redissonClient.getLock("KeyName:lock");
try {
// 只有一个线程能取到锁
if (lock.tryLock(0,30000L, TimeUnit.MILLISECONDS)) {
//todo
}
} catch (InterruptedException e) {
log.error("doCacheError" + e.getMessage());
} finally {
// 只释放自己线程锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
定时任务 + 锁
  1. waitTime 设置为0 ,因为定时任务只需要执行一次,抢不到则放弃

  2. 注意释放锁需要放在Finally代码块

看门狗机制

redisson中提供的续期机制

开一个监听线程,如果方法还没执行完,就帮你充值Redis的过期时间

原理:

  1. 监听当前线程,默认过期时间为30秒,每10秒续期一次(补充至30秒)
  2. 如果线程挂掉(注意:DeBug模式也会被它当成服务器宕机),则不会续期

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void testWatch(){
RLock lock = redissonClient.getLock("yupao:precachejob:docache:lock");
try {
// 只有一个线程能取到锁
if (lock.tryLock(0,-1, TimeUnit.MILLISECONDS)) {
Thread.sleep(300000);
System.out.println("getLock: " + Thread.currentThread().getId());
}
} catch (InterruptedException e) {
System.out.println("doCacheError" + e.getMessage());
} finally {
// 只释放自己线程锁
if (lock.isHeldByCurrentThread()) {
System.out.println("unLock: " + Thread.currentThread().getId());
lock.unlock();
}
}
}

源码阅读