持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第28天,点击查看活动详情
一致性设计
6.1 基础理论
6.1.1 单机事务
事务的四大特性:
- 原子性
指通过事务保证所有操作是不可分割的,要么全成功、要么全失败
- 一致性
指通过事务保证数据从一种状态变化到另一种状态。至少事务结束前,所有数据处于有效状态。一致性的核心是事务处理的中见状态不可见。
- 隔离性
是指事务内的操作不受其他操作的影响,多个事务同时处理同一个数据的时候,多个事务是互不影响的。
- 持久性
指事务被提交后,应该持久化,永久保存下来。
6.1.2 CAP定理
- 一致性
- 可用性
- 分区容错性
6.1.3 BASE理论
- BA:Basically Available 基本可用
- S:Soft state,软状态
- E:Eventually consistent,最终一致
BASE理论的核心思想是:如果做强一致性无法做到,或者要付出很大的代价,那么可以根据自身业务特点,采用适当的方式使系统达到最终一致性,只要最终对用户没有影响或者影响是可接受的即可。
6.2 如何实现强一致性
两阶段提交 三阶段提交
6.3 如何实现最终一致性
- 重试机制
- 本地记录日志
- 可靠事件模式
- TCC事务模型
TCC是下面三个单词的简写:Try、Confirm、Cancel
TCC的优点是:是在业务层处理,平衡数据库压力。比2PC性能好很多,没有真正在数据库加锁。
缺点是:增加业务复杂度,需要提供Try、Confirm、Cancel接口。需要提供幂等性接口
支付宝目前的XTS框架就是采用的TCC模式。
6.4 分布式锁
6.4.1 分布式锁的实现方式
-
基于数据库实现悲观锁和乐观锁
-
基于Zookeeper实现分布式锁
- 客户端连接zookeeper,并在/lock(自己定)目录下创建一个临时有序节点。第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推;
- 查询/lock下的所有子节点列表,判断自己创建的子节点是否为序号最小的,如果是就代表拿到了锁,否则监听刚好在自己之前一位的子节点的删除事件,获得变更通知之后重复此步骤。
- 拿到锁后执行业务代码
- 完成业务流程后,删除对应的子节点释放锁
- 基于Redis实现分布式锁
可以根据SETNX实现。但是存在很多问题。
问题1:超时问题?
如果线程A拿到锁,执行的比较慢,那么你超时时间就需要设置的比较长,那么就会阻塞很久。
问题2:如何释放锁?
能直接删除吗?不能。如果线程A拿到锁,业务执行完了,去查询看锁超时没有,查询到没有超时,这时他准备去删除锁。刚要删还没删的时候这个锁超时了,线程B进来拿到锁,那么线程A的删除操作就把B的锁删了。
解决方案是,SETNX的时候,value值可以在客户端生成一个随机值,删除的时候判断是自己生成的再删。
问题3:单点问题如何解决?
redis是单点的,如果宕机了,那么整个系统就会崩溃。如果是主从结构,那么master宕机了,存储的key还没同步到slave,此时slave升级为新的master,客户端2从新的master上就能拿到同一个资源的锁。这样客户端1和客户端2都拿到锁,就不安全了。
解决方案:RedLock算法。简单说就是N个(通常是5)独立的redis节点同时执行SETNX,如果大多数成功了,就拿到了锁。这样就允许少数节点不可用。
6.5 如何保证幂等
- 利用redis
- 利用数据库的唯一约束