微服务缓存你要这样玩

2,248 阅读5分钟

微服务缓存你要这样玩

微服务项目当中,缓存已经是必不可少的一个模块了,都2020年了不会微服务缓存怎么能行。今天我就给大家说说什么是微服务缓存,缓存应该怎么玩。

在开始之前,先简单整合一下redis,如果这块没有问题的大佬可以直接略过这部分内容。

1、springBoot整合redis

springboot已经帮我们把redis抽取成了一个startes ,也就是场景启动器。我们可以看一下官网:

我们需要在我们的pom文件当中引入redis:

之所以没有写版本号,是因为由父项目做了统一版本控制。

我们引入了redis之后就可以看

配置redis

我们需要关注的几个配置项:

  • host:redis所在服务器的IP地址
  • port:redis的端口号。一般为6379
  • password:redis的密码

redis如果没有设置密码,那就不需要写password这个配置项。

使用StringRedisTemplate来操作redis

首先需要通过@Autowired注入StringRedisTemplate

接下来编写测试类

至此,我们已经整合成功redis了

2、分布式系统下使用缓存的问题

  1. 缓存击穿

    缓存击穿是指对于一些设置了过期时间的key,假设这些key会在某个时间点被超高并发的访问。并且这个key在大量请求进来之前刚刚好失效,那么所有的请求就会全部转发到数据库,这种场景我们称之为缓存击穿。

    解决方案

    可以通过加锁解锁,大量并发先让一个请求去查,其他请求等待。其他人获取到锁,先去查缓存,命中缓存,就不会再去请求数据库了。关于加锁可能引发的一些问题,我们之后讨论。

  2. 缓存穿透

    缓存穿透指查询一个不存在的数据,由于缓存没有命中,将会去数据库查询,但是数据库也没有这条记录,我们也没有将这次查询到的null写入缓存,这将导致这个不存在数据每次都会去数据库查询,缓存就失去了意义。

    风险

    如果被人恶意利用,会使数据库瞬时压力过大,导致数据库崩溃。

    解决方案

    将查询到的null值写入缓存,并设置短暂的过期时间。

  3. 缓存雪崩

    缓存雪崩指的是在我们设置缓存key时采用了相同的过期时间,导致缓存在某一个时刻同时失效,所有请求同时转发到数据库,数据库瞬时压力过大,导致数据库崩溃、

    解决方案

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

3、加锁解决缓存击穿

  1. 使用同步代码块(synchronized)

    只要是同一把锁,就可以锁住需要获取这把锁的所有线程。结合springboot中的所有组件都是单例的。我们可以使用synchronized(this)

假设三个请求同时请求。请求没有命中缓存,A获取到了锁,B和C都处于一个阻塞等待状态,A去查数据库,查到数据后插入缓存中,请求返回,释放锁。B,C再次查询缓存,缓存命中,返回结果。

另外需要注意的是锁的时序问题未命中缓存,查数据库,插入缓存。以上三个操作一定是原子性的。不然就会引发锁时序问题,从而导致没有锁住。

  1. 分布式环境下如何加锁

本地锁只能锁住当前进程,所以我们需要分布式锁。

4、分布式锁

分布式锁原理

我们可以同时去抢同一把锁,抢到了,就执行相关业务逻辑,否则就必须等待,直到释放锁。

关于这把锁,我们可以使用redis,mysql等,可以去任何大家都能访问到的地方。

等待可以自旋的方式。

分布式锁的实现(基于redis)

先来看官网的命令介绍

  • EX seconds:设置键key的过期时间,单位时秒
  • PX milliserconds:设置键key的过期时间,单位时毫秒
  • NX:只有键key不存在的时候才会设置key的值
  • XX:只有键key存在的时候才会设置key的值

我们可以通过SET NX参数来实现分布式锁。

阶段一

很多时候有一些人会这样设计分布式锁,这样的分布式锁会带来一些问题:

  • setnx占好了锁,业务代码异常或者程序意外宕机了。没有执行删除锁的逻辑,造成了死锁

那出现这个问题的时候又该如何解决呢?

  • 设置锁的自动过期时间,即使没有删除锁,到期后也会自动删除

阶段二

这样的设计总没问题了吧?

其实不然:

  • 如果由于业务执行时间很长,锁过期了,我们直接删除,可能会把别人的锁删了。

解决:占锁的时候,指定为uuid,每个人匹配到自己的锁才删除。

此时我们已经可以保证加锁(占锁+过期时间)和删除锁(判断+删除)是原子操作,但是有一点我们仍然不能忽略,那就是锁的自动续期。

关于锁的自动续期我们可以使用一个线程每隔单位时间就去检查锁,如果还持有锁,那么久延长锁的时间。

当然有一个Redisson框架已经为我们封装好了这所有的操作,下次我们再来聊聊Redisson分布式锁。

感谢你的阅读,希望我的分享可以対你有所帮助。

我是武四三二一。