在宏观上将分布式一致性分为了:数据一致性和事务的一致性。
数据一致性: 描述的是在单用户,单操作,多数据副本的情况下保持一致性。
- 我们为了提高数据的可靠性,一般会存在多个数据副本,如何保证多个数据副本的一致性,就带来了一致性问题。
- 在数据一致性的情况下,分为了状态一致性和操作的一致性。
状态一致性:在状态的视角下,主要分为强一致性和最终一致性。
- 强一致性:主要的代表就是mysql主从全同步复制。
- 只有当主从完全同步后才能对客户端,反馈提交成功,这样可以保证数据的强一致性,但存在很多缺点。
- 性能差:只有完成全同步才能对外提供服务,延长了响应时间,也增加的服务器负担。
- 可用性问题:过于追求可用性,会带来一个可用性会将低的问题,比如,每台服务器的非故障率是95%,主从同步后就是95%*95%*95%,可靠西行反而降低了。
- 最终一致性:我们在主副本修改完成后就,反馈成功,其他副本的数据同步可以异步更新,就是经过一段时间后,最后所有副本也可以达到数据一致。
最终一致性:在操作的角度上我们朱主要分为了五种一致性实现。
- 写好读一致性:保证写操作用户修改后再次读取到的数据是最新的数据。 (异常情况:当我们用户在主副本修改了数据A,再次读取时可能读取到副本B的数据,读到就数据)
- 单调读一致性:保证我们读取到最新数据后,不能读取到就数据(情况就是:用户A没有修改数据,只是读操作,读取到最新数据后,再次读取有可能读取到其他副本的旧数据) 解决办法:使用hash映射,避免多个副本间切换。
- 前缀一致性:我们要保证数据的前后因果顺序。(先修改数据A,再修改数据B,当我们先读取到数据B,再读取到数据A时,存在一致性问题)
- 线性一致性:数据建立在事件发生的先后顺序上,我们将所有的操作规定在同一条时间线上,将操作原子化,利用全局时钟来时间。(在某些场景下,可能找不到绝对的全局时间,所以存在局限性。)
- 因果一致性:将部分事件集中起来使用逻辑时钟来实现局部的顺序执行。(虽然他相较于线性一致性,较弱,但是在并发场景下具有更好的性能。)
事务一致性:事务的ACID特性。
- 一致性:操作的执行要和我们的预期结果保持一致。(比较针对的是并发相关的问题。)
- 持久性:核心的思想就是应对系统故障。
- 存储硬件无损下进行可恢复操作:我们依靠的是预写日志(write ahead log)保证第一时间存储数据。
- 存储硬件损坏下进行可恢复操作:
- 单体数据库下的同步和半同步的方式,具有一定的容错能力。
- 事先将日志存在到共享系统中,从共享系统进行恢复,通过冗余的方式进行。
- 使用paxos/raft进行日志同步,核心还是增加冗余备份,从本分中恢复。
- 原子性:在分布式中实现原子性比较复杂(留个伏笔)
- 隔离性:存在多个隔离级别,分别是读已提交,可重复读,快照隔离,串行化;引入了几个异常问题,分别是不可重复读,幻读,写倾斜的问题。
- 写倾斜问题:当我们要修改数据A,但是数据A依赖于数据B,我们修改时只对数据A进行了上锁,数据B没有上锁,数据B的修改可能会造成数据A的改变,所以出现了写倾斜的问题。 解决办法:1. 使用两阶段锁协议来严格保证串行化。
- 快照隔离:快照隔离级别是基于MVCC来实现的,在快照读隔离级别当中写操作和写操作是不会阻塞的,写操作是在mvcc的基础上,读取以前的历史数据来进行的修改操作,一旦读取到其他事务还没有提交到的数据就会回滚。
分布式数据库的强一致性
分布式数据库中最高的保证就是严格的串行化(srtict serializable),缺点需要付出很大的代价。
我们一般会放低标准,主要数据一致性达到了因果一致性;事务一致性达到了,读已提交的都可以视为“强一致性。”