事务
基础概念:ACID原则
A:原子性:构成事务的所有操作,要么全部执行,要么全部不执行
C:一致性:在事务执行前后,数据库的一致性约束没有被破坏
I: 隔离性:数据库的事务一般都是并发的,隔离性是指并发的两个事务的执行互不干扰,一个事务不能看到另一个事务的中间执行状态;通过配置事务的隔离级别可以避免脏读、重复读等问题
D: 持久性:事务完成后,会持久化到数据库,并不会进行回滚
本地事务和分布式事务
- 本地事务:同一数据库和服务器称为本地事务
- 分布式事务:分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上,且属于不同的应用,分布式事务需要保证这些操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
- 举例:
分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操 作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分事务、创建订单减库存事务,银行转账事务等都是分布式事务。
目前分布式事务中处理比较好的就是seata
分布式事务理论
CAP定律
这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
- 一致性:在分布式系统中,同一时刻的数据是否相同
- 可用性: 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
- 分区容错性:在网络故障的情况下,在一致性和可用性之间做出选择
CAP是无法同时存在的
- 当库存服务减库存以后,那么需要将数据同步到其他的服务上,这是为了保证数据一致性C,但是网络是不可靠的,所以我们系统就需要保证分区容错性P,也就是我们必须容忍网络所带来的的一些问题,此时如果我们想保证C那么就需要舍弃A,也就是说我们在保证C的情况下,就必须舍弃A,也就是CP无法保证高可用。
- 如果为了保证A,高可用的情况下,也就是必须在限定时间内给出响应,同样由于网络不可靠P,订单服务就有可能无法拿到新的数据,但是也要给用户作出响应,那么也就无法保证C一致性。所以AP是无法保证强一致性的。
- 如果我们想保证CA,也就是高可用和一致性,也就是必须保证网络良好才能实现,那么也就是说我们需要将库存、订单、用户放到一起,但是这种情况也就丧失了P这个保证,这个时候系统也就不是分布式系统了。
- 总结:在分布式系统中,p是必然的存在的,所以我们只能在C和A之间进行取舍,在这种条件下就诞生了BASE理论
BASE理论
Base理论是Basically Avaiable(基本可用),Soft state(软状态),eventually consistent(最终一致性)三个短语的缩写
Base理论的核心思想:即使无法做到强一致性,但是每个应用都可以根据自身业务特点,采用适当的方式来是系统达到最终一致性
- 基本可用:基本可用是指分布式系统出现不可预知故障时,允许损失部分可用性 注意:这不是等价与不可用比如:
(1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
(2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
- 软状态:是指允许系统的数据出现中间状态,并认为该中间状态的存在不会影响整体可用性,即允许系统不同节点的数据副本之间进行数据同步的过程存在延迟
- 最终一致性:最终一致性表示的是所有的数据副本,在经过一段时间后,最终达到一致的状态,因此最终一致性是需要保证系统最终的数据一致,而不需要实时保证数据的强一致性
**基本可用:**保证核心服务是可以使用的,至于其他的服务可以适当的降低响应时间,甚至是服务降级
**软状态:**存在中间状态,不影响整体系统使用,数据同步存在延时
**最终一致性:**再过了流量高峰期以后,经过一段时间的同步,保持各服务数据的一致
Seata-Server安装
分布式事务解决方案
2PC即两阶段提交协议,是将整个事务流程分为两个阶段,P是指准备阶段,C是指提交阶段。
- 准备阶段(Prepare phase)
- 提交阶段(commit phase)
举例:比如说相亲对象两个人去吃饭,店老板要求,先付钱在吃饭,这是男女双方提出了AA,也就是说只有男女双方都付钱,才能落座吃饭,但是只要两个人中有一个不统一付款就不能落座吃饭。
- 准备阶段:老板要求男方付款,男方付款。老板要求女方付款,女方付款
- 提交阶段:老板出餐,两人纷纷落座
其实此例子就形成了一个事务,如果男女双方有一个人拒绝付款,那么老板就不会出餐,并且会把已收取的钱原路退回。
真个事务过程是由事务管理器和参与者组成的,店老板就是事务管管理器,男女双发就是参与者,事务管理器决策整个分布式事务在计算机中关系数据库支持的两阶段提交协议:
- 准备阶段(Prepare phase):事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。
- (Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数据文件)
- 提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的资源。
具体步骤图例:
成功:
失败:
Seata简介
官网: https://seata.io/zh-cn/docs/overview/what-is-seata.html
概念:Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
在我们的微服务系统中,对应业务被对应的拆分成独立模块,在官方提供的架构图中,我们可以看出当前是三个服务:
- 仓储服务:对给定的商品进行增删操作记录数量
- 订单服务:根据采购者的需求创建订单
- 账户服务:从用户账户中扣除余额、积分等
在这套架构中,用户下单购买商品的业务,就需要三个服务来完成,每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题就没办法保证,Seata就是来进行解决这种问题的解决方案。
Seata术语
官网地址:https://seata.io/zh-cn/docs/overview/terminology.html
要了解Seata,首先我们要了解一下Seata的几个关键的概念:
-
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
-
TM (Transaction Manager) - 事务管理器(发起者,同时也是RM的一种)
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
-
RM (Resource Manager) - 资源管理器(每个参与事务的微服务)
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata Server环境搭建
2. 解压,配置配置文件
2.1 配置registry.conf
修改registry.conf中的配置中心和注册中心为nacos
修改file.conf中的db
双击启动seata.bat
查看nacos中是否有seata-sever服务
Seata Server(TC)环境搭建详解
Server端存储模式(store.mode)支持三种:
- file:单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高(默认)
- DB:高可用模式,全局事务会话信息通过DB共享,相对性能差一些
- redis:Seata-Server1.3及以上版本支持,性能较高,存在事务信息丢失风险,需要配合实际场景使用。
具体操作
- 修改Seata-Server模式为DB高可用模式
找到以下对应的db配置,要修改其中的jdbc连接,以及要注意其中涉及到了三个表,分别是global_table,branch_table,lock_table分别是全局事务会话表,分支事务会话表,锁数据表;
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `distributed_lock`
(
`lock_key` CHAR(20) NOT NULL,
`lock_value` VARCHAR(20) NOT NULL,
`expire` BIGINT,
primary key (`lock_key`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
- 重启Seata即可生效。