Redis事务机制和缓存穿透、雪崩、击穿

2020-04-17  本文已影响0人  JBryan
1、常用命令

MULTIEXEC命令
以 MULTI 开始一个事务,然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set members 36
QUEUED
127.0.0.1:6379> get members
QUEUED
127.0.0.1:6379> exec
1) OK
2) "36"
127.0.0.1:6379>

DISCARD命令
DISCARD 命令用于取消一个事务, 它清空客户端的整个事务队列, 然后将客户端从事务状态调整回非事务状态, 最后返回字符串 OK 给客户端, 说明事务已被取消。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set username root
QUEUED
127.0.0.1:6379> get username
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get username
(nil)
127.0.0.1:6379>

WATCH命令
WATCH 命令用于在事务开始之前监视任意数量的键: 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。
在窗口一watch password,然后set password

127.0.0.1:6379> WATCH password
OK
127.0.0.1:6379> MULTI 
OK
127.0.0.1:6379> set password 123456
QUEUED
127.0.0.1:6379> get password
QUEUED

在窗口二修改password

127.0.0.1:6379> set password 456789
OK
127.0.0.1:6379>

此时回到窗口一,提交事务,执行失败

127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379>

2、Redis事务ACID

原子性(Atomicity)
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。如果一个事务队列中的所有命令都被成功地执行,那么称这个事务执行成功。

一致性(Consistency)
在正常状态下一个事务的所有命令是能按照原子性的原则执行的,但是执行的中途遇到错误,不会回滚,而是继续执行后续命令, 如下:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key1 value1
QUEUED
127.0.0.1:6379> sadd key1 value2
QUEUED
127.0.0.1:6379> set key2 value2
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get key2
"value2"
127.0.0.1:6379>

在set key1 value1之后,执行了一条错误的命令sadd key1 value2,第一条指令并不会回滚,而是继续执行下面一条set key2 value2指令。

隔离性(Isolation)
WATCH 命令用于在事务开始之前监视任意数量的键: 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。

持久性(Durability
因为事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,所以事务的持久性由 Redis 所使用的持久化模式决定。

3、缓存穿透、雪崩、击穿

3.1、缓存穿透

什么是缓存穿透?
缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候, 在缓存中找不到对应key的value,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次 无用的查询)。这样请求就绕过缓存直接查数据库。

有什么解决方案来防止缓存穿透?
采用布隆过滤器BloomFilter:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。
缓存空值:如果一个查询返回的数据为空(不管是数据不 存在,还是系统故障)我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。

3.2、缓存雪崩

什么是缓存雪崩?
如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。 由于原有缓存失效,新缓存未存储,期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU 和内存造成巨大压力,严重的会造成数据库宕机。

有什么解决方案来防止缓存雪崩?
1、加锁排队
互斥锁解决,Redis的SETNX去set一个mutex key, 当操作返回成功时,再去数据库读取数据并回设缓存; 否则,就重试整个get缓存的方法

2、数据预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key

3、双层缓存策略
C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。

4、设置不同的过期时间,让缓存失效的时间点尽量均匀

3.3、缓存击穿

什么是缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方案
1、设置热点数据永远不过期。
2、加互斥锁,保证同一时刻,只能有一个线程去访问数据库。类似线程安全的懒汉单例模式实现。

上一篇下一篇

猜你喜欢

热点阅读