Redis缓存——(分布式锁)

目录

  • 分布式缓存
  • 缓存击穿,穿透,雪崩
  • 分布式锁
    • Redisson实现分布式锁
      • Lock锁
      • 读写锁

分布式缓存

对于本地模式下的缓存,每次如果负载均衡请求的服务器不相同,那么会有很大的几率不通过缓存,而是直接通过DB进行数据交换。
Redis缓存——(分布式锁)_第1张图片
因此我们将缓存抽离,如下图所示,即操作同一个缓存,这就不会存在数据不一致的问题。
Redis缓存——(分布式锁)_第2张图片

缓存击穿,穿透,雪崩

缓存穿透:
指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是 数据库也无此记录,我们没有将这次查询的null写入缓存,这将导致这个不 存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

解决: null结果缓存,并加入短暂过期时间

缓存雪崩:
缓存雪崩是指在我们设置缓存时key采用了相同的过期时间, 导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时 压力过重雪崩。

解决: 原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这 样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

缓存穿透:
对于一些设置了过期时间的key,如果这些key可能会在某些 时间点被超高并发地访问,是一种非常“热点”的数据。
如果这个key在大量请求同时进来前正好失效,那么所有对 这个key的数据查询都落到db,我们称为缓存击穿。

解决: 加锁,大量并发只让一个去查,其他人等待,查到以后释放锁,其他 人获取到锁,先查缓存,就会有数据,不用去db

springboot整合redis进行缓存操作。

<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-redisartifactId>
		dependency>
  @Override
    public Book getById(Integer id) {

        String s = stringRedisTemplate.opsForValue().get(String.valueOf(id));
        if (!StringUtils.hasText(s)) {
            Book book = bookDao.selectById(id);
            //book不为null再执行下一步,为null则退出,解决缓存穿透(可使用布隆过滤器)
            String toJSONString = JSON.toJSONString(book);
            stringRedisTemplate.opsForValue().set(String.valueOf(id), toJSONString, 60, TimeUnit.SECONDS);//解决缓存雪崩
            return book;
        } else {
            Book book = JSON.parseObject(s, new TypeReference<Book>() {
            });
            return book;
        }
    }

分布式锁

推荐博客:
https://www.jianshu.com/p/2afeae8cda89?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation.

Redisson实现分布式锁

整合springboot


		<dependency>
			<groupId>org.redissongroupId>
			<artifactId>redissonartifactId>
			<version>3.16.8version>
		dependency>

单redis节点模式配置:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class MyRedissonConfig {
    @Bean(destroyMethod="shutdown")
    RedissonClient redisson() throws IOException {

        //创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");

        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

其他集群等配置可参考官网:
https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F.

Lock锁

官网介绍:
基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

@GetMapping("/findById/{id}")
    public Book findById(@PathVariable("id") Integer id) {

        /*redisson实现分布式锁*/

        Book book = null;
        //1获取一把锁,只要锁的名字一样就可以
        RLock lock = redissonClient.getLock("my-lock");
        //2、加锁
        lock.lock();//阻塞式等待:不断尝试获取锁(获取到锁后默认加锁30s)
        //锁是自动续期的,如果业务超长,运行期间自动给锁续上新的30s,不用担心业务时间长,锁自动过期被删掉
        //加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁(宕机),锁默认在30s以后自动删除

        //lock.lock(10, TimeUnit.SECONDS); 
        //此有参方法并不会自动续期!!!10自动解锁,自动解锁时间一定要大于业务的执行时间
        //1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
        //2、如果我们没有指定锁的超时时间,就使用30*1000[看门狗的默认时间]
        //只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10s秒自动续期,直至30s
        //internallockleasetime【看门狗时间】/3,10s
        try {
           book = bookService.findById(id);
           return book;
        }finally {
            lock.unlock();//解锁
        }
    }

读写锁

官网介绍:基于Redis的Redisson分布式可重入读写锁RReadWriteLock Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。

分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。
同样具有看门狗限定锁的时间。

保证一定读到最新数据,修改期间,写锁是一个排它锁(互斥锁),写锁没释放读就必须等待。读锁是一个共享锁。读读不互斥;读写、写写均互斥

@GetMapping("/getall/{page}/{size}")
    public Page<Book> getall(@PathVariable("page") Integer page, @PathVariable("size") Integer size) {

        //获取读写锁
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        //加读锁
        RLock rLock = readWriteLock.readLock();
        rLock.lock();
        Page<Book> bookPage = null;
        try {
            bookPage = bookService.getall(page, size);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();//释放锁
        }
        return bookPage;
    }

    @PutMapping("/update")
    public Integer update(@RequestBody Book book) {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        Integer integer=-1;

        //加写锁
        RLock rLock = readWriteLock.writeLock();
        rLock.lock();
        try {

            integer = bookService.updateById(book);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return integer;
    }

其他锁可参照官网。

推荐阅读:
链接: https://www.cnblogs.com/liuqingzheng/p/11080501.html

你可能感兴趣的:(Redis数据库,分布式锁,缓存,redis,分布式)