-
2020-09-23 19:52:19
关系型数据库保证数据的完整性和一致性是最佳的。
完整性
- 实体完整性:每个实体独一无二(主键/唯一索引)
- 参照完整性 (引用完整性): 子表中不能有父表中没有的数据(外键)
- 域完整性:数据都是有效数据(数据类型和长度,非空约束,默认值约束,检查约束)
-- 检查学生年龄是否为1到200的数字 stuage in check (stuage between 1 and 200) alter table tb_student add constraint;
一致性
数据的一致性用事务来实现。
事务:一系列对数据不可分割的操作(要么全做,要么全不做)事务ACID特性:
atomicity 原子性 不可分割 consistency 一致性 前后数据状态保持一致 isolation 隔离性 多个并发事务不知道彼此的中间状态 duration 持久性 事务完成后,对数据的修改要持久化 并发事务访问数据可能产生的五大类问题:
- 第一类丢失更新
- 第二类丢失更新
- 脏读(读脏数据)
- 不可重复读
- 幻读
解决:
-
单版本并发控制-加锁-悲观锁-性能太差
-
多版本并发控制(MVCC)- 乐观锁 - 不会经常遇到数据冲突 一条数据多个版本 -性能更好-需要冗余字段支持
直接操作锁非常麻烦,设置事务隔离级别,数据库自动加合适的锁
事务隔离级别:
- 读未提交
- 读提交 --避免读到脏数据 oracle默认
- 可重复读–避免幻读 mysql默认
- 串行化–最高事务隔离级别,性能最差,无并发 ,悲观锁。
更多内容:参考这篇
更多相关内容 -
分布式数据一致性(数据多份副本一致性)
2019-05-22 15:19:34前言 分布式数据库的数据一致性管理是其最重要的内核技术之一,也是保证分布式数据库满足数据库最基本的ACID特性中的 “一致性”...1.1 数据一致性是什么 大部份使用传统关系型数据库的DBA在看到“数据一致性”时,...前言
分布式数据库的数据一致性管理是其最重要的内核技术之一,也是保证分布式数据库满足数据库最基本的ACID特性中的 “一致性”(Consistency)的保障。在分布式技术发展下,数据一致性的解决方法和技术也在不断的演进,本文就以分布式数据库作为案例,介绍分布式数据库数据一致性的原理以及实际实现。
1、数据一致性
1.1 数据一致性是什么
大部份使用传统关系型数据库的DBA在看到“数据一致性”时,第一反应可能都是数据在跨表事务中的数据一致性场景。但是本文介绍的“数据一致性”,指的是**“数据在多份副本中存储时,如何保障数据的一致性”**场景。
由于在大数据领域,数据的安全不再由硬件来保证,而是通过软件手段,通过同时将数据写入到多个副本中,来确保数据的安全。数据库在同时向多个副本写入记录时,如何确保每个副本数据一致,称为“数据一致性”。
1.2 关系型数据库如何保障数据一致性
传统的关系型数据库对于运行环境–硬件要求都比较高,例如Oracle会建议用户使用小型机+共享存储作为数据库的运行环境,DB2 DPF也同样建议用户采用更好的服务器+高端存储来搭建数据库的运行环境。所以在数据存储安全的技术要求下,传统关系型数据库更多是依赖硬件的技术来保障数据的安全性。
因为关系型数据库的数据安全是基于硬件来保障,并且数据也不会通过同时存储多份来保障数据的安全,所以关系型数据库的用户默认认为数据存储是一致的。1.3 分布式存储如何保障数据一致性
本文在讨论分布式存储时,主要指的是大数据产品中的分布式文件系统和分布式数据库,例如:SequoiaDB和HDFS。
用户在搞明白分布式存储的数据一致性原理时,必须要先明白为什么他们就需要数据一致性,和分布式存储的数据存储与关系型数据库的数据存储又有什么区别。
大数据技术的诞生,确确实实让系统的性能有新的突破,并且支持硬件以水平扩展的方式来获得线性增长的性能和存储。这些都是过去传统关系型数据库所无法提供的。另外,大数据技术也抛弃了运行环境必须足够好的硬性要求,而是允许用户通过批量廉价X86服务器+本地磁盘的方式搭建规模集群,从而获得比过去依赖硬件垂直扩展所提供的更强的计算能力和更多的存储空间。
大数据技术的核心思想就是分布式,将一个大的工作任务分解成多个小任务,然后通过分布式并发操作的方式将其完成,从而提高整个系统的计算效率或者是存储能力。而在分布式环境下,由于硬件的要求降低,必然需要大数据产品提供另外一个重要的功能–数据安全。
大数据产品在解决数据安全的方式上,都比较接近,简单来说,就是让一份数据通过异步或者同步的方式保存在多台机器上,从而保障数据的安全。分布式存储在解决数据安全的技术难点后,又引入了一个新的技术问题,就是如何保障多个副本中的数据一致性。目前SequoiaDB是使用Raft算法来保证数据在多个副本中一致性。
2、Raft算法
2.1 Raft算法背景
在分布式环境下,最著名的一致性算法应该是Paxos算法,但是由于它实在过于晦涩难懂,并且实现起来极度困难,所以在2013年,Diego Ongaro、John Ousterhout两个人以易懂(Understandability)为目标设计了一套一致性算法Raft。Raft算法最大的特点在于简单易懂,并且实现起来简单
2.2 Raft算法概述
与Paxos不同,Raft强调的是易懂,Raft和Paxos一样只要保证n/2+1节点正常就能够提供服务。
众所周知当问题较为复杂时可以把问题分解为几个小问题来处理,Raft也使用了分而治之的思想。Raft算法重点解决三个子问题:选举(Leader election)、日志复制(Log replication)、安全性(Safety)。
Raft算法强化了Leader节点的功能,Follower节点的数据只能够从Leader中获取,所以Follower节点的实现就变得简单,只要负责和Leader保持通信,并且接受Leader推送的数据即可。
2.3 Raft算法原理
2.3.1 节点角色
Raft算法中,对节点的状态分为3种角色,分别是Leader(领导者)、Follower(追随者)和Candidate(候选者)。
Leader,负责处理来自客户端的请求,负责将日志同步到Follower中,并且保证与Follower之间的heartBeat联系;
Follower,当集群刚刚启动时,所有节点均为Follower状态,它的工作主要为响应Leader的日志同步请求,响应Candidate的请求,以及把请求到Follower的事务请求转发给Leader;
Candidate,选举Leader时负责投票,选举出来Leader后,节点将从Candidate状态变为Leader状态。
2.3.2 Terms
在分布式环境下,“时间同步”一直都是老大难的技术难题。Raft为了解决这个问题,将时间划分为一个一个的Term(可以理解为“逻辑时间”)来处理在不同时间段里的数据一致性。
Terms有以下原则
- 每个Term中,至多存在一个Leader
- 某些Term中,有可能存在由于选举失败,没有Leader的情况
- 每个节点自己维护本地的currentTerm
- 每个Term都是一个连续递增的编号
- 如果Follower的Term编号比别的Follower Term编号小时,该Follower Term编号将更新Term编号,以保持与其他Follower Term编号一致
2.3.3 选举
Raft的选举由定时器触发,每个节点的触发时间都不相同。
所有的节点在开始时状态都为Follower,当定时器触发选举后Term编号递增,该节点的状态由Follower转为Candidate,并且向其他节点发起RequestVote RPC请求,这时选举有3种情况可能发生:
发起RequestVote的节点收到n/2+1(过半数)个节点的投票,该节点将从Candidate状态变为Leader状态,开始向其他节点发送HeartBeat以保持Leader的正常状态
如果收到投票请求后,该节点发现发起投票的节点Term大于自己,则该节点状态从Candidate转为Follower,否则保持Candidate状态,并且拒绝该投票请求
选举期间发生了超时,则Term编号递增,重新发起选举
2.3.4 日志复制
日志复制主要的作用就是用来保证节点的数据一致性与高可用性。
当Leader被选举出来后,所有的事务操作都必须要经过Leader处理。这些事务操作成功后,将会被按顺序写入到LOG中,每个LOG都包含一个index编号。
Leader在LOG发生变化后,通过HeartBeat将新的LOG同步到Follower上,Follower在接收到LOG后,再向Leader发送ACK信息,当Leader接到大多数(2/n+1)Follower的ACK信息后,将该LOG设置为已提交,并且Leader将LOG追加到本地磁盘中。
同时Leader将在下一个HeartBeat中,通知所有的Follower将该LOG存储在各自的本地磁盘中。
2.3.5 安全性
安全性是用于确保每个节点都是按照相同的日志序列进行执行的安全机制。
如果当某个Follower在同步Leader的日志时失败,但是未来该Follower又可能被选举为Leader时,就有可能导致前一个Leader已经commit的日志发生覆盖,这样就导致了节点执行不同序列的日志。
Raft的安全性就是用于保证选举出来的Leader一定包含先前已经commit LOG 的机制,主要遵循的原则如下:
每个Term 只能选举一个Leader;
Leader的日志完整性,则当Candidate重新选举Leader时,新的Leader必须要包含先前已经commit的LOG;
Candidate在选举新的Leader时,使用Term来保证LOG的完整性;
3、分布式数据库数据一致性技术实现
以国产原厂的分布式数据库SequoiaDB为例,SequoiaDB在多副本的部署中,采用Raft算法保证数据在多副本环境中保持一致。
SequoiaDB集群中,总共包含3中角色节点,分别是协调节点、编目节点和数据节点。由于协调节点本身不存任何数据,所以只有编目节点和数据节点存在事务操作,换言之,编目分区组和数据分区组的副本同步采用Raft算法保证数据一致性。
3.1 编目节点和数据节点的事务日志介绍
编目节点和数据节点由于都是需要存储数据的,并且在集群部署中该,为了确保数据的安全,都是建议采用分布式的方式进行部署,所以在数据同步中,需要采用Raft算法的基本原理进行数据同步。
编目节点和数据节点在存储数据时,共包含两大部分,一个真实的数据文件,另一个是事务日志文件。
SequoiaDB的节点事务日志,默认情况下由20个64MB(总大小为1.25GB)的文件构成。节点的事务日志主要包含一个index编号和数据操作内容,index编号保持永远递增状态。另外,SequoiaDB节点的事务日志不会永久保存,而是当所有的事务日志写满后,再重新从第一个文件开始进行覆盖写入。
3.2 编目分区组的数据一致性
由于编目分区组是保存SequoiaDB集群的元信息,数据同步要求高,所以编目分区组的数据一致性要求为强一致性,即每次向编目分区组执行事务操作时,必须要确保所有的编目节点操作成功,才计算该操作执行成功,否则该事务操作将在整个编目分区组中回退事务日志,以保证分区组内的数据一致性。
另外,编目分区组还有一个比较重要的特性,即编目分区组必须要存在主节点才能够正常工作,如果老的主节点宕机了,编目分区组暂时没有主节点,则该编目分区组不能够对外提供任何事务操作和数据查询操作。
3.3 数据分区组的数据一致性
数据分区组的数据一致性默认情况下为最终一致性性,即只要求主节点执行事务操作成功即视为操作成功,主节点将在未来异步同步ReplicaLOG到从节点上。
3.4 主从节点的事务日志同步
SequoiaDB的主从节点是通过事务日志同步来保证数据一致性的,并且主从节点的事务日志同步是单线程完成。
如果当主节点和从节点的LSN差距为一条记录,则主节点会主动将最新的事务日志推送给从节点。
如果主节点和从节点的LSN差距超过一条记录,则从节点会主动向主节点请求同步事务日志,主节点收到同步请求后,会将从节点的LSN号到主节点最新的LSN号对应的事务日志打包一次性发送给从节点。
3.5 从节点日志重放
当从节点获取到主节点推送过来的事务日志后,就会自动解析事务日志和重放。从节点在重放事务日志时,默认情况下会以10并发来重放事务日志。
从节点在执行并发重放日志时有条件限制,即在集合的唯一索引个数<=1的情况下,INSERT、DELETE、UPDATE、LOB WRITE、LOBUPDATE、LOB REMOVE操作可以支持并发重放事务日志。从节点在做并发重放时,是通过记录的OID进行打散并发执行,这样就可以保证对相同记录的操作不会由于并发重放导致数据不一致。
但是用户需要注意,从节点在重放事务日志时, DROP CL操作不能够支持并发重放。
4、SequoiaDB数据一致性应用
目前SequoiaDB数据分区组的数据一致性是基于集合级别进行配置的。用户在使用SequoiaDB过程中,可以随时调整数据一致性的强度。
4.1 创建集合时指定
在一个多副本的SequoiaDB集群中,集合默认的数据一致性行级别为“最终一致性”。用户可以在创建集合时显式指定该集合的“数据一致性强度”,例如可以在SequoiaDB Shell中执行以下命令
db.CSNAME.createCL(“CLNAME”,{ReplSize:3})
ReplSize参数填写范围
4.2 修改已经存在的集合
如果集合在创建时没有设置“数据一致性”ReplSize参数,用户也可以对已经存在的集合进行修改,在SequoiaDB Shell修改命令如下
db.CSNAME.CLNAME.alter({ReplSize:3})
ReplSize的取值范围和创建集合时一致。
4.3 如何查看集合的ReplSize参数
如果用户希望检查当前集合的RepliSize参数值,可以通过数据库快照进行查看,在SequoiaDB Shell查看命令如下
db.snapshot(SDB_SNAP_CATALOG,{}, {“Name”:null, “IsMainCL”:null,“MainCLName”:null, “ReplSize”:null})
打印信息如下{
“MainCLName”:“test.main2”,
“Name”: “foo.bar2”,
“IsMainCL”: null,
“ReplSize”: null
}
{
“IsMainCL”: true,
“Name”: “test.main2”,
“MainCLName”: null,
“ReplSize”: null
}
{
“Name”: “foo.tt”,
“ReplSize”: 3,
“IsMainCL”: null,
“MainCLName”: null
}
5、总结
分布式的数据库,通过Raft算法来确保在分布式情况上数据的一致性,并且编目分区组和数据分区组对数据一致性要求又有所不同,编目分区组始终要求的是数据在多副本请情况下数据强一致性,而数据分区组则可以由用户在创建集合时来执行数据一致性的强度,强度越高,数据安全性越好,但是执行的效率就会相对较差,反之依然。
目前SequoiaDB在数据一致性场景上,用户的调整空间较大,可以根据不同的业务要求来调整数据一致性的强度,以满足业务或追求性能最优,或者数据最安全的技术要求。
-
如何保证分布式系统数据一致性
2018-12-24 10:26:05面试的时候,有面试官问到:选取你比较熟悉的项目,谈谈如何在做容灾负载的时候数据一致性问题,具体点比如你里面的派单,如何保证一个司机不在同一时间内接到两个订单,然后保证实时性? 一般的解决方案是在派单...面试的时候,有面试官问到:选取你比较熟悉的项目,谈谈如何在做容灾负载的时候数据一致性问题,具体点比如你里面的派单,如何保证一个司机不在同一时间内接到两个订单,然后保证实时性?
一般的解决方案是在派单前,会做一次查询判断,单系统部署的时候,判断查询加同步操作确实能解决,多实例负载的时候,事务控制就很难办到了,这个时候我想到了redis 缓存,进入的时候会预先缓存司机订单,订单成功了修改缓存状态,失败了清理。然后又会引发新问题,实例宕机了,司机就一直无法派单了,这个时候需要定时任务去清理,这个时候系统需要通过心跳来判断应用是否正常运行(当然可以借用zookeeper),还需要判断缓存中的数据从哪个实例中插入的,清理的时候需要单台清理,这个时候可能又要单实例应用需要动态生成唯一标识或使用ip来做到唯一性。整个过程非常复杂,单纯面试的时间,其实很难有完整的逻辑思路,因为这已经到具体实现了,几分钟的时间,可能会随着面试官的思路,进入到了很深的坑里。
然后面试官说出了自己的方案,使用mysql的for update锁记录,其实很想反驳,但还是忍住了,for update的时候,宕机了或者应用异常没释放,怎么解决,手动去数据库清理锁吗?说到这里,其实面试官其实自己也不知道怎么解决,如果角色互换,其实他也会被你问的方寸大乱。CAP原则下,强数据一致性必然会导致应用的可用性降低,实时性降低,一般情况使用同步队列处理就好,反正和12306似的,排队半天告诉你没票了。
面试下来后,我路上思考了一番,其实可以使用预订单的方式,派单的时候原先的逻辑不变,只是插入临时订单,事务结束后,再来判断司机是否已有订单即可,然后根据判断是否更新临时订单为正常订单还是实时返回客户失败提示。
后续搜索了关于分布式数据一致性的解决方案,大多没有非常好的方案,看了一篇博文,总结了一下:
问题的起源
在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性?
具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要满足要么同时成功;要么同时失败。A、B、C 可能是多个不同部门开发、部署在不同服务器上的远程服务。
在分布式系统来说,如果不想牺牲一致性,CAP 理论告诉我们只能放弃可用性,这显然不能接受。为了便于讨论问题,先简单介绍下数据一致性的基础理论。
强一致
当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可用性。
弱一致性
系统并不保证续进程或者线程的访问都会返回最新的更新过的值。系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体的承诺多久之后可以读到。
最终一致性
弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统。
在工程实践上,为了保障系统的可用性,互联网系统大多将强一致性需求转换成最终一致性的需求,并通过系统执行幂等性的保证,保证数据的最终一致性。但在电商等场景中,对于数据一致性的解决方法和常见的互联网系统(如 MySQL 主从同步)又有一定区别,群友的讨论分成以下 6 种解决方案。
1. 规避分布式事务——业务整合
业务整合方案主要采用将接口整合到本地执行的方法。拿问题场景来说,则可以将服务 A、B、C 整合为一个服务 D 给业务,这个服务 D 再通过转换为本地事务的方式,比如服务 D 包含本地服务和服务 E,而服务 E 是本地服务 A ~ C 的整合。
优点:解决(规避)了分布式事务。
缺点:显而易见,把本来规划拆分好的业务,又耦合到了一起,业务职责不清晰,不利于维护。
由于这个方法存在明显缺点,通常不建议使用。
2. 经典方案 - eBay 模式
此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。
消息日志方案的核心是保证服务接口的幂等性。
考虑到网络通讯失败、数据丢包等原因,如果接口不能保证幂等性,数据的唯一性将很难保证。
eBay 方式的主要思路如下。
Base:一种 Acid 的替代方案
此方案是 eBay 的架构师 Dan Pritchett 在 2008 年发表给 ACM 的文章,是一篇解释 BASE 原则,或者说最终一致性的经典文章。文中讨论了 BASE 与 ACID 原则在保证数据一致性的基本差异。
如果 ACID 为分区的数据库提供一致性的选择,那么如何实现可用性呢?答案是
BASE (basically available, soft state, eventually consistent)
BASE 的可用性是通过支持局部故障而不是系统全局故障来实现的。下面是一个简单的例子:如果将用户分区在 5 个数据库服务器上,BASE 设计鼓励类似的处理方式,一个用户数据库的故障只影响这台特定主机那 20% 的用户。这里不涉及任何魔法,不过它确实可以带来更高的可感知的系统可用性。
文章中描述了一个最常见的场景,如果产生了一笔交易,需要在交易表增加记录,同时还要修改用户表的金额。这两个表属于不同的远程服务,所以就涉及到分布式事务一致性的问题。
文中提出了一个经典的解决方法,将主要修改操作以及更新用户表的消息放在一个本地事务来完成。同时为了避免重复消费用户表消息带来的问题,达到多次重试的幂等性,增加一个更新记录表 updates_applied 来记录已经处理过的消息。
系统的执行伪代码如下
(点击可全屏缩放图片)
基于以上方法,在第一阶段,通过本地的数据库的事务保障,增加了 transaction 表及消息队列 。
在第二阶段,分别读出消息队列(但不删除),通过判断更新记录表 updates_applied 来检测相关记录是否被执行,未被执行的记录会修改 user 表,然后增加一条操作记录到 updates_applied,事务执行成功之后再删除队列。
通过以上方法,达到了分布式系统的最终一致性。进一步了解 eBay 的方案可以参考文末链接。
3. 去哪儿网分布式事务方案
随着业务规模不断地扩大,电商网站一般都要面临拆分之路。就是将原来一个单体应用拆分成多个不同职责的子系统。比如以前可能将面向用户、客户和运营的功能都放在一个系统里,现在拆分为订单中心、代理商管理、运营系统、报价中心、库存管理等多个子系统。
拆分首先要面临的是什么呢?
最开始的单体应用所有功能都在一起,存储也在一起。比如运营要取消某个订单,那直接去更新订单表状态,然后更新库存表就 ok 了。因为是单体应用,库在一起,这些都可以在一个事务里,由关系数据库来保证一致性。
但拆分之后就不同了,不同的子系统都有自己的存储。比如订单中心就只管理自己的订单库,而库存管理也有自己的库。那么运营系统取消订单的时候就是通过接口调用等方式来调用订单中心和库存管理的服务了,而不是直接去操作库。这就涉及一个『分布式事务』的问题。
分布式事务有两种解决方式
1. 优先使用异步消息。
上文已经说过,使用异步消息 Consumer 端需要实现幂等。
幂等有两种方式,一种方式是业务逻辑保证幂等。比如接到支付成功的消息订单状态变成支付完成,如果当前状态是支付完成,则再收到一个支付成功的消息则说明消息重复了,直接作为消息成功处理。
另外一种方式如果业务逻辑无法保证幂等,则要增加一个去重表或者类似的实现。对于 producer 端在业务数据库的同实例上放一个消息库,发消息和业务操作在同一个本地事务里。发消息的时候消息并不立即发出,而是向消息库插入一条消息记录,然后在事务提交的时候再异步将消息发出,发送消息如果成功则将消息库里的消息删除,如果遇到消息队列服务异常或网络问题,消息没有成功发出那么消息就留在这里了,会有另外一个服务不断地将这些消息扫出重新发送。
2. 有的业务不适合异步消息的方式,事务的各个参与方都需要同步的得到结果。这种情况的实现方式其实和上面类似,每个参与方的本地业务库的同实例上面放一个事务记录库。
比如 A 同步调用 B,C。A 本地事务成功的时候更新本地事务记录状态,B 和 C 同样。如果有一次 A 调用 B 失败了,这个失败可能是 B 真的失败了,也可能是调用超时,实际 B 成功。则由一个中心服务对比三方的事务记录表,做一个最终决定。假设现在三方的事务记录是 A 成功,B 失败,C 成功。那么最终决定有两种方式,根据具体场景:
-
重试 B,直到 B 成功,事务记录表里记录了各项调用参数等信息;
-
执行 A 和 B 的补偿操作(一种可行的补偿方式是回滚)。
对 b 场景做一个特殊说明:比如 B 是扣库存服务,在第一次调用的时候因为某种原因失败了,但是重试的时候库存已经变为 0,无法重试成功,这个时候只有回滚 A 和 C 了。
那么可能有人觉得在业务库的同实例里放消息库或事务记录库,会对业务侵入,业务还要关心这个库,是否一个合理的设计?
实际上可以依靠运维的手段来简化开发的侵入,我们的方法是让 DBA 在公司所有 MySQL 实例上预初始化这个库,通过框架层(消息的客户端或事务 RPC 框架)透明的在背后操作这个库,业务开发人员只需要关心自己的业务逻辑,不需要直接访问这个库。
总结起来,其实两种方式的根本原理是类似的,也就是将分布式事务转换为多个本地事务,然后依靠重试等方式达到最终一致性。
4. 蘑菇街交易创建过程中的分布式一致性方案
交易创建的一般性流程
我们把交易创建流程抽象出一系列可扩展的功能点,每个功能点都可以有多个实现(具体的实现之间有组合/互斥关系)。把各个功能点按照一定流程串起来,就完成了交易创建的过程。
面临的问题
每个功能点的实现都可能会依赖外部服务。那么如何保证各个服务之间的数据是一致的呢?比如锁定优惠券服务调用超时了,不能确定到底有没有锁券成功,该如何处理?再比如锁券成功了,但是扣减库存失败了,该如何处理?
方案选型
服务依赖过多,会带来管理复杂性增加和稳定性风险增大的问题。试想如果我们强依赖 10 个服务,9 个都执行成功了,最后一个执行失败了,那么是不是前面 9 个都要回滚掉?这个成本还是非常高的。
所以在拆分大的流程为多个小的本地事务的前提下,对于非实时、非强一致性的关联业务写入,在本地事务执行成功后,我们选择发消息通知、关联事务异步化执行的方案。
消息通知往往不能保证 100% 成功;且消息通知后,接收方业务是否能执行成功还是未知数。前者问题可以通过重试解决;后者可以选用事务消息来保证。
但是事务消息框架本身会给业务代码带来侵入性和复杂性,所以我们选择基于 DB 事件变化通知到 MQ 的方式做系统间解耦,通过订阅方消费 MQ 消息时的 ACK 机制,保证消息一定消费成功,达到最终一致性。由于消息可能会被重发,消息订阅方业务逻辑处理要做好幂等保证。
所以目前只剩下需要实时同步做、有强一致性要求的业务场景了。在交易创建过程中,锁券和扣减库存是这样的两个典型场景。
要保证多个系统间数据一致,乍一看,必须要引入分布式事务框架才能解决。但引入非常重的类似二阶段提交分布式事务框架会带来复杂性的急剧上升;在电商领域,绝对的强一致是过于理想化的,我们可以选择准实时的最终一致性。
我们在交易创建流程中,首先创建一个不可见订单,然后在同步调用锁券和扣减库存时,针对调用异常(失败或者超时),发出废单消息到MQ。如果消息发送失败,本地会做时间阶梯式的异步重试;优惠券系统和库存系统收到消息后,会进行判断是否需要做业务回滚,这样就准实时地保证了多个本地事务的最终一致性。
5. 支付宝及蚂蚁金融云的分布式服务 DTS 方案
业界常用的还有支付宝的一种 xts 方案,由支付宝在 2PC 的基础上改进而来。主要思路如下,大部分信息引用自官方网站。
分布式事务服务简介
分布式事务服务 (Distributed Transaction Service, DTS) 是一个分布式事务框架,用来保障在大规模分布式环境下事务的最终一致性。DTS 从架构上分为 xts-client 和 xts-server 两部分,前者是一个嵌入客户端应用的 JAR 包,主要负责事务数据的写入和处理;后者是一个独立的系统,主要负责异常事务的恢复。
核心特性
传统关系型数据库的事务模型必须遵守 ACID 原则。在单数据库模式下,ACID 模型能有效保障数据的完整性,但是在大规模分布式环境下,一个业务往往会跨越多个数据库,如何保证这多个数据库之间的数据一致性,需要其他行之有效的策略。在 JavaEE 规范中使用 2PC (2 Phase Commit, 两阶段提交) 来处理跨 DB 环境下的事务问题,但是 2PC 是反可伸缩模式,也就是说,在事务处理过程中,参与者需要一直持有资源直到整个分布式事务结束。这样,当业务规模达到千万级以上时,2PC 的局限性就越来越明显,系统可伸缩性会变得很差。基于此,我们采用 BASE 的思想实现了一套类似 2PC 的分布式事务方案,这就是 DTS。DTS在充分保障分布式环境下高可用性、高可靠性的同时兼顾数据一致性的要求,其最大的特点是保证数据最终一致 (Eventually consistent)。
简单的说,DTS 框架有如下特性:
-
最终一致:事务处理过程中,会有短暂不一致的情况,但通过恢复系统,可以让事务的数据达到最终一致的目标。
-
协议简单:DTS 定义了类似 2PC 的标准两阶段接口,业务系统只需要实现对应的接口就可以使用 DTS 的事务功能。
-
与 RPC 服务协议无关:在 SOA 架构下,一个或多个 DB 操作往往被包装成一个一个的 Service,Service 与 Service 之间通过 RPC 协议通信。DTS 框架构建在 SOA 架构上,与底层协议无关。
-
与底层事务实现无关: DTS 是一个抽象的基于 Service 层的概念,与底层事务实现无关,也就是说在 DTS 的范围内,无论是关系型数据库 MySQL,Oracle,还是 KV 存储 MemCache,或者列存数据库 HBase,只要将对其的操作包装成 DTS 的参与者,就可以接入到 DTS 事务范围内。
以下是分布式事务框架的流程图
实现-
一个完整的业务活动由一个主业务服务与若干从业务服务组成。
-
主业务服务负责发起并完成整个业务活动。
-
从业务服务提供 TCC 型业务操作。
-
业务活动管理器控制业务活动的一致性,它登记业务活动中的操作,并在活动提交时确认所有的两阶段事务的 confirm 操作,在业务活动取消时调用所有两阶段事务的 cancel 操作。”
与 2PC 协议比较
-
没有单独的 Prepare 阶段,降低协议成本
-
系统故障容忍度高,恢复简单
6. 农信网数据一致性方案
1. 电商业务
公司的支付部门,通过接入其它第三方支付系统来提供支付服务给业务部门,支付服务是一个基于 Dubbo 的 RPC 服务。
对于业务部门来说,电商部门的订单支付,需要调用
-
支付平台的支付接口来处理订单;
-
同时需要调用积分中心的接口,按照业务规则,给用户增加积分。
从业务规则上需要同时保证业务数据的实时性和一致性,也就是支付成功必须加积分。
我们采用的方式是同步调用,首先处理本地事务业务。考虑到积分业务比较单一且业务影响低于支付,由积分平台提供增加与回撤接口。
具体的流程是先调用积分平台增加用户积分,再调用支付平台进行支付处理,如果处理失败,catch 方法调用积分平台的回撤方法,将本次处理的积分订单回撤。
(点击图片可以全屏缩放)
2. 用户信息变更
公司的用户信息,统一由用户中心维护,而用户信息的变更需要同步给各业务子系统,业务子系统再根据变更内容,处理各自业务。用户中心作为 MQ 的 producer,添加通知给 MQ。APP Server 订阅该消息,同步本地数据信息,再处理相关业务比如 APP 退出下线等。
我们采用异步消息通知机制,目前主要使用 ActiveMQ,基于 Virtual Topic 的订阅方式,保证单个业务集群订阅的单次消费。
总结
分布式服务对衍生的配套系统要求比较多,特别是我们基于消息、日志的最终一致性方案,需要考虑消息的积压、消费情况、监控、报警等。
参考资料
-
Base: An Acid Alternative (eBay 方案)
In partitioned databases, trading some consistency for availability can lead to dramatic improvements in scalability.
英文版 : http://queue.acm.org/detail.cfm?id=1394128
中文版: http://article.yeeyan.org/view/167444/125572
来自:http://weibo.com/ttarticle/p/show?id=2309403965965003062676
-
-
数据一致性解决方案
2022-03-12 21:21:17数据一致性解决方案 CAP理论 C:一致性、A:可用性、P:分区容错性 CAP只能满足两个 CA:两阶段提交的严格选举协议 CP弱A:RAFT协议等多数派选举协议 AP:GOSSIP等冲突解决协议 数据一致性 时间一致性:所有相关数据...数据一致性解决方案
CAP理论
C:一致性、A:可用性、P:分区容错性
CAP只能满足两个
CA:两阶段提交的严格选举协议
CP弱A:RAFT协议等多数派选举协议
AP:GOSSIP等冲突解决协议数据一致性
时间一致性:所有相关数据副本任意时刻数据一致
事务一致性:事务之行前后数据一致
应用一致性:分布式事务一致BASE理论
基本可用:允许偶尔失败
软状态:数据状态要求任意时刻一致
最终一致性:弱一致性数据一致性解决方案
多副本数据一致性:
一般用于容灾和高可用:副本之间通过同步复制或者异步复制的方式达到数据一致。
分布式事务数据一致性:
本地事务是指数据库的单机事务处理,有点是严格的ACID特性,高效、可靠、状态可以只在资源管理器中维护、应用编程模型简单。缺点:不具备分布式事务处理能力,隔离的最小单位受限于资源管理器。
分布式事务:事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点上。强一致性解决方案:
一次请求对多个数据源的数据进行完整性以及一致性的操作,满足事务的特性,要么全部成功,要么全部失败,保证原子性以及可见性。
强一致性通过锁定资源的方式,但是会牺牲性能,使用之前需要谨慎评估。XA分布式事务
XA协议:
是X/OPEN组织提出的分布式事务规范,该规范主要定义了全局事务管理器(TM)和本地资源管理器(RM)之间的接口,本地资源管理器往往由数据库实现。
主流的数据库产品都提供了XA接口,XA接口是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁。
之所以需要XA,是因为在分布式系统中从理论上讲,两台机器是无法达到一致性状态的,因此引入了一个单节点进行协调。
有全局事务管理器管理的机器可以跨多个资源和进程,负责各个本地资源的提交和回滚,全局事务管理器一般使用XA二阶段提交协议与数据库进行交互。二阶段提交(2PC)
准备阶段:
事务协调者向所有事务参与者发送事务内容,询问是否可以提交事务,并等待参与者回复。
提交阶段:
如果协调者收到了参与者的失败信息或者超时信息,直接给所有参与者发送回滚信息进行事务回滚;否则发送提交信息。
2PC方案实现起来比较简单,但是实际项目中使用比较少
缺陷:- 性能问题:所有参与者在事务提交阶段处于同步阻塞状态,容易造成性能问题;
- 可靠性问题:如果协调者存在单点故障问题,一旦协调者出现故障,参与者一直处于锁定状态;
- 数据一致性问题:第二阶段如果发生局部网络问题,一部分收到提交消息,一部分没有收到,会导致数据不一致。
三阶段提交(3PC)
三阶段提交是在二阶段提交的基础上的改进版本,主要是加入了超时机制,同时在协调者和参与者中都引用了超时机制。
三阶段是将二阶段里面的准备阶段拆分为两个阶段,插入了一个预提交阶段,这样原先二阶段准备之后,参与者发生崩溃或者错误所引起的问题。
第一阶段:
事务协调者向所有事务参与者发送事务内容,询问是否可以提交事务(canCommit),并等待参与者回复。参与者收到请求之后,检查是否可以提交,如果认为可以执行事务操作,返回Yes,并进入预备状态(参与者不执行事务操作),否则返回No。
第二阶段:
协调者根据第一阶段中参与者的相应情况来决定是否可以进行基于事务的预提交操作:
任何一个参与者反馈abord,即中断事务,协调者向所有参与者发送abord,abord或者超时都会中断事务。
第三阶段:
第二阶段中所有参与者均返回yes,会向所有参与者发送doCommit请求,参与者收到doCommit请求之后会正式执行事务提交,并释放整个事务期间的占用资源,各参与者向协调者返回完成的消息,协调者收到所有参与者的反馈的ack完成的消息后,完成事务的提交。
注意:
在进入第三阶段之后,无论是协调者出现问题,还是协调者与参与者的网络出现问题,都会导致参与者无法收到协调者的请求,此时参与者都会在等待超时之后继续执行事务提交(因为异常的情况毕竟是少数)弱一致性解决方案
弱一致性:复制是异步的,最终一致性是弱一致性的特例,保证用户最终能够读取到某操作对系统特定的数据更新。
弱一致性为了提高系统的吞吐量,允许一定程度上的数据脏读。最终一致性解决方案
强一致性的技术实现成本很高,运行性能低,很难满足真实业务场景下的高并发需求,在实际的生产环境中,通常采用最终一致性的解决方案。
最终一致性:不追求任意时刻系统都能满足数据完整且一致的要求,通过一段业务上可接受时间之后,系统能达到数据完整且一致的目标。消息队列
建立两个消息topic,一个用来处理正常的业务提交,一个用来处理异常冲正消息。请求服务向正常的topic提交业务请求,如果全部正常,则数据一致;如果出现任何异常,向异常的topic提交业务请求,负责回滚。
事务消息方案
核心: 消息队列必须支持半事务消息(如RocketMQ),半事务消息提供类似于X/OPEN XA二阶段提交的分布式事务功能,这样能够确保请求服务执行本地事务和发送消息在一个全局事务中,只有本地事务成功执行之后,消息才会被投递。消息一旦被投递,默认设计业务执行必须成功(通过重试机制)。如果因为某些异常导致业务最终执行失败,系统无法自愈。只能通过告警的方式等待人工干预。
数据订阅方案
核心: 监听数据库的更新日志,并转换成消息流供消费端进行订阅处理。业务订阅是异步的,会存在一定的消息延迟。
TCC事务补偿
TCC是服务化的二阶段编程模型,尝试(try)、确认(confirm)、取消(cancel)均由业务编码实现。
try阶段: 尝试执行业务,完成所有业务的检查,实现一致性;预留必须的业务资源,实现准隔离型。
confirm阶段: 真正执行业务,不做任何检查,仅适用于try阶段预留的业务资源,同时要满足幂等性。
cancel阶段: 取消执行业务,释放try阶段预留的业务资源,操作要满足幂等性。相比于二阶段提交:
TCC位于业务层服务,二阶段提交位于资源层
**性能提升: **业务来实现控制资源锁的粒度大小,不会锁定整个资源
数据最终一致性: 基于confirm和concel的幂等性,保证最终事务完成确认或者取消。
可靠性: 解决了XA协议的协调者单点故障的问题,由主业务方发起并控制整个业务活动,业务管理器也变成多点,引入集群。
缺点:
都要有具体业务实现,业务耦合度较高,提高开发成本。saga事务模式
核心思想: 将长事务拆成多个本地短事务,由saga事务协调器进行协调,如果正常结束,则正常完成;如果某个步骤失败,根据相反顺序依次调用补偿操作,达到事务的最终一致性。
基本协议:- saga事务由许多幂等的有序的小事务组成;
- 每个小事务都有对应的补偿,用于撤销执行后的结果;
和TCC相比,没有预留动作,直接提交。
有些场景不适合补偿,比如发送短信,发了之后如果撤销,就得再给用户发送说明撤销,体验较差。
-
HDFS数据一致性
2018-09-11 10:22:452.NameNode如何保证元数据的一致性 3.校验和 4.为实现高可用,HDFS采用的诸多策略 4.1 冗余副本 4.2 机架感知 4.3 心跳机制 4.4 安全模式 4.5 校验和 4.6 回收站 4.7 元数据保护 4.8 快照机制 ... -
浅析数据一致性
2016-02-19 15:27:38什么是数据一致性? 在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。 实践中,导致数据不一致的情况... -
数据一致性问题、redis如何与数据库保持一致性问题,完整版详解
2020-07-29 21:11:03数据一致性问题 写作背景:看了些视频,网上也看了很多篇文章,感觉写得都很片面,不是很全,所以我整体总结了一些,我不喜欢重复造轮子,对于网上很多篇复制机类型的文章(很多文章所有字体相同)且很多不正确严重... -
数据库一致性、完整性
2016-11-20 13:09:44来源: http://www.zybang.com/question/e23cf724ee5a0d00d2cba4f9ccb2d23b.html ... ...数据完整性(data integrity)是 指数据的精确性(accuracy) 和可靠性(reliability)。它是应防 -
数据库中的数据完整性(实体完整性、域完整性、引用完整性)
2019-05-06 17:56:51确保数据的完整性 = 在创建表时给表中添加约束 完整性的分类: 实体完整性: 域完整性: 引用完整性: 4.1 实体完整性 实体:即表中的一行(一条记录)代表一个实体(entity) 实体完整性的作用:标识... -
Elasticsearch分片数量选择及如何保证数据一致性
2019-09-01 15:46:11Elasticsearch集群中保持数据一致性的过程中,涉及到内部之间的通讯,先简单介绍一下es中各模块的作用和功能 1)cluster cluster模块是主节点执行集群管理和封装的实现,管理集群状态,维护集群层面的配置信息。... -
SQL Server 数据库之数据完整性
2019-12-01 14:48:50目录 ~ 简述 ~ 实体完整性 · 主键约束 · 唯一性约束 ... 数据完整性是指数据库中数据在逻辑上的一致性、准确性、有效性和相容性。它包括实体完整性、参照完整性和用户自定义完整性。而在 SQL S... -
异步并发保持数据一致性
2015-12-30 03:27:13问题背景:项目中不断切换界面的过程中网络异步请求【耗时操作】,但是数据并未加载完成,此时界面中触碰事件时候,直接崩掉【频率低但存在】。 解决思路:监听异步操作完成后才让界面上的按钮可以响应单击【触碰】... -
三种妙法搞定冗余表数据一致性
2016-05-30 22:36:52首先聊聊为什么会有冗余表的需求。互联网很多业务场景的数据量很大,此时数据库架构要进行水平切分,水平切分会有一个patition key,通过patition key的查询能够直接定位到库,但是非patition key上的查询可能就需要... -
分布式数据一致性思考-B端系统一致性
2020-07-18 22:17:06目录背景什么是一致性?B端业务场景重试幂等并发小结总结 背景 已经好久没写博客了,看了下最近的一篇已经是去年的了,由于工作一直忙,没有抽时间来写(其实就是懒)。加上也没有觉得非常有收获的事情,所以就干脆... -
维护数据完整性
2012-08-22 20:14:38维护数据完整性 一、概述 数据的完整性,是指存储在数据库中的数据的正确性和可靠性,它是衡量数据中数据质量的一种标准。数据完整性可以分为四类。 1、实体完整性,实体完整性的目的是确保数据库中所有... -
数据库的完整性
2022-03-18 21:36:15一、什么是数据库完整性 数据库的完整性是指数据库中数据的正确性和相容性。 数据库完整性由各种完整性约束来保证。而完整性约束既可以通过DBMS来保障,也可以通过应用程序来实现。如果是基于DBMS,完整性约束将作为... -
保证分布式数据一致性的6种方案
2018-07-30 17:17:07在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性? 具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要满足要么同时成功;要么同时失败。A、B、C 可能是多... -
Cache 一致性
2019-06-16 16:13:16Write invalidate提供了实现Cache一致性的简单思想,处理器上会有一套完整的协议,来保证Cache一致性。比较经典的Cache一致性协议当属MESI协议,奔腾处理器有使用它,很多其他的处理器都是使用它的变种。 单核处理器... -
redis主从保证数据一致性
2020-03-11 11:00:04redis主从保证数据一致性 前言 在redis中为了保证redis的高可用,一般会搭建一种集群模式就是主从模式。 主从模式可以保证redis的高可用,那么redis是怎么保证主从服务器的数据一致性的,接下来我们浅谈下redis主... -
数据完整性
2019-04-14 19:37:26关于SQL server的数据完整性的简要内容 -
烦人的数据不一致问题到底怎么解决?——通过“共识”达成数据一致性
2018-08-24 09:35:04是前一篇《不知道是不是最通俗易懂的《数据一致性》剖析了》的后续内容。 前一篇可能讲的过于通俗,逼格不高,不太受大家待见。。本篇会继续坚持尽量讲的通俗易懂,坚信让更多的人看懂才有更大的价值。不过相对来... -
数据库有哪几种完整性
2021-01-25 16:07:09数据库有哪几种完整性发布时间:2020-06-01 22:20:13来源:亿速云阅读:262作者:鸽子什么是数据库完整性数据库完整性可确保输入至数据库中的数据,是准确、有效及一致的。数据库中任何数据改动,都必须乎合所有完整... -
分布式系统的一致性问题(汇总)
2019-09-02 15:32:19保证分布式系统数据一致性的6种方案 问题的起源 在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性? 具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要... -
【转】如何维护数据库的完整性与一致性?
2012-07-22 22:08:00数据完整性(Data Integrity)是指数据的精确性(Accuracy) 和可靠性...数据完整性分为四类:实体完整性(Entity Integrity)、域完整性(Domain Integrity)、参照完整性(Referential Integrity)、用户定... -
微服务架构下的数据一致性保证
2018-09-30 17:30:08设计到系统,其中绕不开的就是数据一致性,从本地事务,到后来的分布式事务,都能够有效的保证数据一致性。但是在微服务架构中,这两种方式都不是最好的选择。 1. 使用本地事务和分布式事务保证一致性 在传统的... -
微服务架构下的数据一致性:概念及相关模式
2016-07-09 17:23:29设计到系统,其中绕不开的就是数据一致性,从本地事务,到后来的分布式事务,都能够有效的保证数据一致性。但是在微服务架构中,这两种方式都不是最好的选择。1. 使用本地事务和分布式事务保证一致性在传统的单击... -
数据库系统原理——数据库的安全性与完整性(一)
2020-07-03 17:48:592、 掌握实体完整性、参照完整性和用户自定义完整性的定义和维护方法; 3、 掌握数据库触发器的设计和使用方法。 二、 实验内容 3.1数据库安全性实验 设有一个企业,包括采购、销售和客户管理等三个部门,采购部门... -
面试问题(如何保证分布式数据最终一致性)
2018-03-01 10:51:42保证分布式系统数据一致性的6种方案编者按:本文由「高可用架构后花园」群讨论整理而成。有人的地方,就有江湖有江湖的地方,就有纷争问题的起源在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用... -
如何检查数据库中数据的一致性?
2017-11-12 14:55:00数据库一致性检查(dbcc)提供了一些命令用于检查数据库的逻辑和物理一致性。Dbcc主要有两个功能: 使用checkstorage 或 checktable 及 checkdb 在页一级和行一级检查页链及数据指针。 使用checkstorage, checkalloc,...