数据库锁(Redisson分布式锁介绍和使用)

数据库锁(Redisson分布式锁介绍和使用)
Redisson分布式锁介绍和使用

一、概述

官方网站 & 高性能版

官方描述:Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。

它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。
Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。

二、配置

Redisson程序化的配置方法是通过构建Config对象实例来实现的,具体配置可参加API。下列基础的连接配置:

  • 单机模式
// 默认连接地址 11:6379 无密码    RedissonClient redisson = Redisson.create();    // 指定    Config config = new Config();    config.useSingleServer().setAddress("redis\://localhost:6379");    RedissonClient redisson = Redisson.create(config);
  • 集群模拟
Config config = new Config();    config.useClusterServers()    .setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒    //可以用"rediss\://"来启用SSL连接    .addNodeAddress("redis\://11:7000", "redis\://11:7001")    .addNodeAddress("redis\://11:7002");    RedissonClient redisson = Redisson.create(config);
  • 哨兵模式
Config config = new Config();    config.useSentinelServers()    .setMasterName("mymaster")    //可以用"rediss\://"来启用SSL连接    .addSentinelAddress("11:26389", "11:26379")    .addSentinelAddress("11:26319");    RedissonClient redisson = Redisson.create(config);
  • 主从模式
Config config = new Config();    config.useMasterSlaveServers()    //可以用"rediss\://"来启用SSL连接    .setMasterAddress("redis\://11:6379")    .addSlaveAddress("redis\://11:6389", "redis\://11:6332", "redis\://11:6419")    .addSlaveAddress("redis\://11:6399");    RedissonClient redisson = Redisson.create(config);

三、分布式对象

  • 特殊数据结构:BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque
//BigeSet示例    RBitSet set = redisson.getBitSet("simpleBitset");    set.set(0, true);    set.set(1812, false);    set.clear(0);    set.addAsync("e");    set.xor("anotherBitset");
  • 特殊组件:Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter
// 步隆过滤器示例    RBloomFilter\ bloomFilter = redisson.getBloomFilter("sample");    // 初始化布隆过滤器,预计统计元素数量为55000000,期望误差率为03    bloomFilter.tryInit(55000000L, 03);    bloomFilter.add(new SomeObject("field1Value", "field2Value"));    bloomFilter.add(new SomeObject("field5Value", "field8Value"));    bloomFilter.contains(new SomeObject("field1Value", "field8Value"));
  • 其他:Remote service, Spring cache, Executor service, Live Object service, Scheduler service

三、分布式锁

1、锁的类别

  • 可重入锁(Reentrant Lock)

基于Redis的Redisson分布式可重入锁RLock,实现了

java.util.concurrent.locks.Lock

接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
RLock对象完全符合Java的Lock规范。也就是说只有拥有锁的进程才能解锁,其他进程解锁则会抛出

IllegalMonitorStateException

错误。

数据库锁(Redisson分布式锁介绍和使用)

// 防止IllegalMonitorStateException异常    if (lock.isLocked() && lock.isHeldByCurrentThread()) {        lock.unlock();    }

联锁(MultiLock)

基于Redis的Redisson分布式联锁RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。
用于加锁不同ID集合时,只要执行集合与当前加锁集合有交集,就会阻塞等待。

RLock lock1 = redissonInstancegetLock("lock1");    RLock lock2 = redissonInstancegetLock("lock2");    RLock lock3 = redissonInstancegetLock("lock3");    RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);    // 同时加锁:lock1 lock2 lock3    // 所有的锁都上锁成功才算成功。    lock.lock();    ...    lock.unlock();

红锁(RedLock)

  • 读写锁(ReadWriteLock)

写锁是一个排他锁(互斥锁,独享锁);读锁是 一个共享锁。

  • 信号量(Semaphore)
  • 闭锁(CountDownLatch)

基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与

java.util.concurrent.CountDownLatch

相似的接口和用法。

2、分布式锁Java应用示例

  • 1)、引用maven依赖
    org.redisson    redisson    1
  • 2)、配置RedissonClient

MyRedissonConfig给容器配置一个RedissonClient实例即可,所有对redisson的使用都是通过RedissonClient对象。

@Configurationpublic class MyRedissonConfig {    @Bean(destroyMethod = "shutdown")    public RedissonClient redisson() throws IOException {        //1、创建配置        Config config = new Config();        //Redis url should start with redis:// or rediss:// (for SSL connection)        //可以用"rediss://"来启用SSL连接        config.useSingleServer()                .setAddress("redis://localhost:6379")                .setPassword("123456")                .setDatabase(0);        //2、根据Config创建出RedissonClient实例        return Redisson.create(config);    }}
  • 3)、基础示例
@ResponseBody    @GetMapping("/hello")    public String hello() {    //1、获取一把锁,只要锁的名字一样,就是同一把锁    RLock lock = redisson.getLock("redisson\:my‐lock");    //2、加锁    //        lock.lock();    //阻塞式等待。默认加锁30s时间    //1)、锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s。不用担心业务时间长,锁自动过期删掉    //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s 以后自动删除        lock.lock(10, TimeUnit.SECONDS);        //10s钟自动解锁,自动解锁时间一定要大于业务执行时间        //问题:lock.lock(10, TimeUnit.SECONDS); 在锁时间到了以后,不会自动续期        //1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就 我们指定的时间        //2、如果我们未指定超时间件,就使用 30 * 1000【LockWatchdogTimeout看门狗的 默认时间】        // 只要占锁成功,就会启动一个定时任务。【重新给锁设置过期时间,新的过期时间就 是看门狗的默认时间】,每隔10s都会自动再次续期,续成满时间        // internalLockLeaseTime / 3 【看门狗时间 / 3,10s】 26        //最佳实战        //1)、lock.lock(10, TimeUnit.SECONDS); 省掉了整个续期操作。手动解锁        try {            System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());            Thread.sleep(60000);        } catch (Exception e) {            e.printStackTrace();        } finally {            //3、解锁 假设解锁代码没有运行,redisson会不会出现问题            System.out.println("释放锁..." + Thread.currentThread().getId());            lock.unlock();    //            if (lock.isLocked() && lock.isHeldByCurrentThread()) {    //                lock.unlock();    //            }    }    return "hello";    }
  • 4)、读锁示例
@ResponseBody    @GetMapping("/read")    public String readValue() {    RReadWriteLock lock = redisson.getReadWriteLock("rw‐lock");    String s = "";    //加读锁    RLock rLock = lock.readLock();    rLock.lock();    System.out.println("读锁加锁成功..." + Thread.currentThread().getId());    try {    Thread.sleep(30000);    s = redisson.getBucket("writeValue").toString();    } catch (Exception e) {    e.printStackTrace();        } finally {            rLock.unlock();            System.out.println("读锁释放..." + Thread.currentThread().getId());        }        return s;    }

5)、写锁示例

保证一定能读到最新数据,修改期间,写锁是一个排他锁(互斥锁,独享锁)。读锁是 一个共享锁
写锁没释放,读就必须等待
读 + 读,相当于无锁,并发读,只会在redis中记录好,所有当前的读锁,他们都会时加锁成功
写 + 读,等待写锁释放
写 + 写,阻塞方式是同
读 + 写,有读锁,写也需要等待
只要有写的存在,都必须等待

@ResponseBody@GetMapping("/write")public String writeValue() {RReadWriteLock lock = redisson.getReadWriteLock("rw‐lock");String s = "";RLock rLock = lock.writeLock();//1、改数据加写锁,读数据加读锁rLock.lock();System.out.println("写锁加锁成功..." + Thread.currentThread().getId());try {s = UUID.randomUUID().toString();Thread.sleep(30000);} catch (InterruptedException e) {e.printStackTrace();} finally {rLock.unlock();System.out.println("写锁释放..." + Thread.currentThread().getId());}return s;}

四、Redisson、Jedis、Lettuce优缺点对比

Redisson

  • 优点:
    实现了分布式特性和可扩展的 Java 数据结构,适合分布式开发;
    API线程安全;
    基于Netty框架的事件驱动的通信,可异步调用。
  • 缺点:
    API更抽象,学习使用成本高。

Jedis

  • 优点:
    提供了比较全面的Redis操作特性的API
    API基本与Redis的指令一一对应,使用简单易理解。
  • 缺点:
    同步阻塞IO;
    不支持异步;
    线程不安全。

Lettuce

  • 优点:
    线程安全;
    基于Netty 框架的事件驱动的通信,可异步调用;
    适用于分布式缓存。
  • 缺点:
    API更抽象,学习使用成本高。

其中Jedis是用的最普遍的,Spring的RedisTemplate用的就是Jedis,适用很多单体应用或者伪分布式应用等;而Redisson更多应用于分布式场景,更强大,更可靠,作为分布式锁的选择,和zookeeper有着一样的地位。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

相关阅读