目录:
Redis从入门到精通(一):缓存
Redis从入门到精通(二):分布式锁以及缓存相关问题
Redis从入门到精通(三):Redis持久化算法以及内存淘汰策略
Redis从入门到精通(四):Redis常用数据结构以及指令
Redis从入门到精通(五):Redis6整合SpringBoot2.x+Mybatis+SpringCache
Redis从入门到精通(六):Redis高可用原理
Redis从入门到精通(七):跳跃表的简介与实现
Redis从入门到精通(八):Redis新特性
分布式架构
什么是分布式架构?
很简单,那就是服务不放在同一台主机上,或者不放在同一个CPU的核心上。这么做主要目的是发挥出CPU核心的性能,配合上集群,可以减少因故障导致的整个服务的瘫痪。但是,一旦做了分布式,就要面临几个问题:
- 同一个服务之间的数据怎么同步?
- 同一个数据在不同的服务之间怎么变化?
- 当一个服务挂掉,其他的服务怎么响应?
这里面是分布式架构的核心,当我们的Redis部署成分布式缓存的时候,同样要考虑这三点问题。这三个问题的背后其实就是著名的CAP理论。所谓CAP理论,就是:
- C(Consistency)一致性
- A(Availability)可用性
- P(Partition tolerance)分区容错性
这个问题的核心在于,CAP三个方面不能同时满足。很好理解,举个例子:淘宝的架构是分布式架构,那么假设在河北和广东分别有两个用户几乎同时购买同一件商品,同时这个商品只有最后一个库存,该怎么处理?
上面是一个很典型的生产者---消费者
模型。就是资源被两个消费者同时征用,如何处理?我们先看看操作系统是怎么处理的:谁要消费,就先在资源上上一个锁,其他进程无法再使用该资源,直到锁被释放。这在操作系统是很常见并且很稳定的解决方法,但是放在分布式系统中就成了棘手的问题。河北的用户买了该商品,同时将该商品锁定,理论上广东的用户无法再购买,因为商品已经被锁定了,但是呢,分布式架构数据同步(Consistency,数据一致性)需要时间,在上锁这个动作被另一台服务于广东人的服务器接收到前,广东人也完成了下单业务,问题就出现了,虽然广东人和河北人都买了该商品,但是该商品只有一个了,该给谁?
很明显,上述例子中,对于用户来说,可用性一直存在,但是数据一致性出现了问题。那么我们可不可以牺牲可用性保证数据一致性呢?当然可以,只需要将该商品服务存放于一台服务器,所有的消费都在这个服务器上就行。由于是一台服务器,用户下单后给商品上锁,同时另一个人不能在这期间购买,系统可用性下降,但是保证了数据一致性。
分区容错性我们暂时不考虑,那是集群的事,以后再说。
真实的项目不会和我说的一样简单,程序猿们往往在系统和用户之间加了中间件,比如消息队列来缓解CAP问题,但是也只是缓解,根源矛盾还是存在的。消息队列以后会出个专题专门学习一下。
分布式锁
什么是分布式锁
保证同一时间下,保证只有一个客户端对可共享资源进行某种操作。
我这里写的很含糊,某种操作,因为操作可以分成读、写、执行等等,上面的话如果分开讲,就会说到很复杂锁理论,以后也会出个文章说说我们见到的各种锁机制。这里我们不提那些。
还用上述的案例,下图中,用户1对服务器1上锁,用户2对服务器2上锁,结果依旧导致了数据不一致,因为每个服务器都是可以对数据库进行操作的,所以本地锁杜宇分布式架构来说显得很多余,于是便有了分布式锁。
分布式锁:
只有我们用一个统一的锁管理器,才能做到分布式锁的目标。那我们这个系列讲的Redis分布式锁,就是部署在这个分布式锁服务器上的。
分布式锁分类
- 基于数据库的MySQL锁,将MySQL部署在锁服务器,建立一个表,对表进行操作达到锁的目的
- 基于缓存的Redis锁,将Redis部署在锁服务器,利用缓存对锁ID进行处理达到锁目的
- 基于ZooKeeper的分布式锁,这个说起来太麻烦了,也不是系列文章重点,略过。
分布式锁满足条件
实现分布式锁需要满足的条件,这些条件缺一不可:
- 排他性:一个用户拿到锁,其他用户不能再对资源进行操作。
- 可重入性:同一个用户多种操作,不能被排斥。比如我买东西,先下单,后付款,两个操作,不能说下单加了锁,付款的时候就不能访问了。
- 容错性:如果一个用户加锁后,突然走了,可能网络故障等原因,但是呢锁一直没有释放,其他资源进不来,就成了死锁,所以我们要考虑这种情况,用户与所服务器无连接时,自动释放锁。
- 高性能、高可用
分布式锁的SpringBoot+Redis+lua脚本实现
本节内容在后面的学习中会提到,所以这里不讲,先把基础知识完善在说。
本节内容在Redis从入门到精通(五):Redis6整合SpringBoot2.x+Mybatis+SpringCache中有讲解。
缓存问题
我们在前一篇文章中说了热点key问题,在这里我们将缓存问题进一步细化。
缓存击穿
什么是缓存击穿呢,实际上缓存击穿是指,当某一主键key在某一时刻缓存过期,需要更新,但是同时有大量数据访问主键key代表的数据时,造成的缓存中无法击中key,需要从数据库中拉去数据,但是大量访问将数据库拖垮的问题。
解决方法:
- 设置key不过期
- 定期更新缓存,配合集群
- 设置互斥锁,某一个请求访问发现key过期,可以对该key加锁。只允许一个请求访问数据库,取出key,存入缓存(此时更新完毕),释放锁。其他的请求可以等待,也可以直接返回空数据。
缓存雪崩
什么是缓存雪崩,就是大量的缓存击穿同时出现,即大量热点key同时过期。
解决方法和缓存击穿一样:
- 设置热点缓存不过期,定时更新
- 设置随机过期时间
缓存穿透
注意,缓存穿透和缓存击穿不一样。缓存穿透是指访问大量不存在的数据,导致数据库被拖垮。这是黑客攻击的一种手段。比如在DDoS攻击中,假设某一场景,有大量数据登录QQ,账号密码都是随机的。这就可能出现非法qq账号,比如1,-625这种账号,明显不是qq号标准,这样就缓存中没有这些数据,这些数据就会被拿去到数据库查询。上万个连接同时到达,数据库就完蛋了。
解决方案:
- 前端设置规则校验
- 缓存存放临时key-null,设置其更新时间很短。被称为缓存空值。
我们的目的就是不让那么多数据访问到数据库从而数据库的拉闸,所以这种问题的核心思想就是“建墙阻挡”。