这是我参与「第三届青训营 -后端场」笔记创作活动的的第6篇笔记。
分布式锁
先介绍一下锁的分类:悲观锁、乐观锁
悲观锁:悲观主义者,做事之前先加锁,先取锁再访问。具有强烈的独占和排他特性(读写都排斥的锁&读写锁)。
乐观锁:CAS(MVCC用版本号解决ABA问题),乐观主义者,先做事,再检查,不行就重试。乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户
乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
分布式锁:分布式环境中,不同进程间的协同并发资源要使用的锁。一般的实现方式是基于Redis、Zookeeper或者数据库。
缓存一致性
缓存一致性指缓存中的数据与数据库中的数据是一致的。
一、缓存主要用于读取数据、数据库用于更新数据
数据读取的时候:先查缓存,缓存查不到数据库,然后把查到的结果放到缓存中。(要及时更新缓存)
数据更新的时候:
-
不管什么顺序都有一定的风险:
- 先删除缓存,再更新数据库:删除缓存后有人访问数据库,则缓存里最终是未更新前的记录
- 先更新数据库再更新缓存:若多数人更改,应该先做修改的先刷新进缓存,后做修改的后刷新进缓存,但是有可能前者延迟才做,导致缓存值错误。
-
先更新数据库,再删除缓存(这种情况风险最小);
- 问题一:在缓存中把热点数据删除了。
- 问题二:如果缓存删除失败,则错误数据永久地存在于缓存当中。
-
缓存设置过期时间,保证最终一致性;
二、缓存主要用于读取和存储,数据库用于最终存储(数据库用作备份)
数据读取的时候:先查缓存,一般所有需要读取的数据会预加载在缓存中。
数据更新的时候:先更新缓存,再异步(可以批量)更新数据库。(因为直接更新数据库性能不好)(例如用AOF做持久化)
基于分布式缓存redis的实践
1.分布式锁
SET lock_key unique_value NX PX 10000
第一次SET成功的,拿到锁;拿到的锁,有过期时间;拿到锁后,不需要锁时,删除锁。
为什么加锁操作要设置过期时间?==》避免死锁
加了过期时间就没问题了吗?==》Redis集群中会出现两个进程都拿到锁的情况:master拿锁之后还没同步到slave上就挂了,结果slave升级为master,另一个进程又从新的master上又拿锁,结果又拿到了。==》解决方案:红锁:一个锁打散,一个进程要拿好几次,拿到超过半数才算拿到了。
2.扣减库存
针对减库存的高并发场景,更好的方法是Redis的INCR(或者DECR)(不用管原来是什么值,不用先读后写,改完返回目前值,并且这个操作是原子的,Redis自己已经处理好并发同步问题了。)避免了先读后写的尴尬。
CAP理论
CAP理论指的是一个分布式系统最多只能同时满足一致性(Consistency,正确性,能够读到最近的写,比如修改数据后可以立刻读到;不能一半用户读到了新数据,另一半读到了老数据,要所有用户读到相同的数据)、可用性(Avaliablity,要保证所有的请求都有响应,不管响应中的值是否正确,只要能读到值就是可用的)、分区容错性(Partition tolerance,在分布式环境中,不同机器时间要相互通信,在对外提供服务时,如果互相之间的通信(网络)出现问题了,那首先还是要保证对外的服务是正常的)
事实上,我们只能同时满足CAP中的两项:
- CA:相当于不是一个分布式数据库了,相当于满足ACID的单机数据库,而不再是分布式的。
- PC:主从复制时不能提供服务,死抓同步,没法及时响应
- PA:复制之间也可以提供服务,只是可能不同的从状态不同
BASE理论:
基本可用(Basically Available)软状态(Soft State)最终一致性(Eventually Consistent),主要是牺牲了强一致性。 只要几个从主机最终都和主机一样是对的就行,不需要在过程中时时刻刻都是正确的,抛弃了强一致性。
分布式事务
分布式事务指事务的操作(很多台数据库和很多台服务器)位于不同的节点上,需要保证事务的ACID特性,要么都成功,要么都失败。
例如,在下单场景下,订单生成、扣减库存、扣减金额可能不在同一个节点上,就会涉及分布式事务。
基于DB:两阶段协议、三阶段协议
两阶段协议:对锁机制,保证事务可串行性的最常用协议是两阶段封锁协议。该协议要求每个事务分两个阶段提出加锁和解锁申请:
(1)增长阶段。事务可以获得锁,但不能释放锁。
(2)缩减阶段。事务可以释放锁,但不能获得锁。
一开始,事务处于增长阶段,事务根据需要获得锁。一旦该事务释放了锁,它就进入缩减阶段,不能再发出加锁请求。
两阶段封锁协议实现了事务集的串行化调度,但同时,一个事务的失败可能会引起一连串事务的回滚。为避免这种情况的发生,我们需要进一步加强对两阶段封锁协议的控制,这就是:严格两阶段封锁协议和强两阶段封锁协议。
严格两阶段封锁协议除了要求封锁是两阶段之外,还要求事务持有的所有排它锁必须在事务提交之后方可释放。这个要求保证未提交事务所写的任何数据,在该事务提交之前均以排它锁封锁,防止其他事务读取这些数据。
强两阶段封锁协议,要求事务提交之前不得释放任何锁。使用锁机制的数据库系统,要么使用严格两阶段封锁协议,要么使用强两阶段封锁协议。
两阶段封锁协议并不保证不会发生死锁,数据库系统必须采取其他的措施,预防和解决死锁问题。
基于系统:TCC(try,confirm,cancel)TCC
基于本地消息表、消息队列