微服务之一致性设计

85 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 分布式锁的实现方式
  1. 基于数据库实现悲观锁和乐观锁

  2. 基于Zookeeper实现分布式锁

  • 客户端连接zookeeper,并在/lock(自己定)目录下创建一个临时有序节点。第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推;
  • 查询/lock下的所有子节点列表,判断自己创建的子节点是否为序号最小的,如果是就代表拿到了锁,否则监听刚好在自己之前一位的子节点的删除事件,获得变更通知之后重复此步骤。
  • 拿到锁后执行业务代码
  • 完成业务流程后,删除对应的子节点释放锁
  1. 基于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 如何保证幂等

  1. 利用redis
  2. 利用数据库的唯一约束