提交订单业务分析

思路:去验令牌,创建订单,验价格,远程锁库存,远程扣减积分等整个过程是事务操作。(事务是无法控制远程业务的,需要每个都加事务)

注意: 1,《提交订单》按钮页面,既订单结算页,此时设置防重令牌,避免多次提交,每次刷新该页面令牌会变。 2,提交订单首先利用redis的String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";脚本 【令牌的对比和删除必须保证原子性】验证令牌,原子验证令牌和删除令牌 3,保存订单之后,要进行锁库存,避免用户付款的时候出现库存不足的情况;而且库存要是失败了,还要撤销保存订单的逻辑。

问题1:如果库存锁定成功了,但是由于网络超时,导致返回一个超时异常,让其他程序误以为出现异常了会触发之前的步骤“创建订单”订单回滚,库存扣减。 问题2: 位于锁库存之后的业务“远程扣减积分服务”若异常,上面的库存将不会回滚。简单理解就是,远程服务执行完成,下面的其他方法出现问题,导致:已执行的远程请求肯定不能回滚,因为不在一个连接里面,事务没法控制。

查看《本地事务与分布式事务》文档 解决方式1:seatea   @GlobalTransactional  //高并发 的时候又会出问题,效率极低

 Seata控制分布式事务  *  1)、每一个微服务先必须创建 undo_log;  *  2)、安装事务协调器;seata-server: https://github.com/seata/seata/releases  *  3)、整合  *      1、导入依赖 spring-cloud-starter-alibaba-seata  seata-all-0.7.1  *      2、解压并启动seata-server;  *          registry.conf: 注册中心配置; 修改registry type=nacos  *          file.conf:  *      3、所有想要用到分布式事务的微服务使用seata DataSourceProxy代理自己的数据源  *      4、每个微服务,都必须导入  *              registry.conf  *              file.conf  vgroup_mapping.{application.name}-fescar-service-group = "default"  *      5、启动测试分布式事务  *      6、给分布式大事务的入口标注@GlobalTransactional  *      7、每一个远程的小事务用 @Transactional

库存解锁的场景 1)、下订单成功,订单过期没有支付被系统自动取消、被用户手动取消。都要解锁库存 2)、下订单成功,库存锁定成功,接下来的业务调用失败,导致订单回滚。seata效率太慢,不考虑. 之前锁定的库存就要自动解锁。

解决:RabbitMQ  先了解一下基本知识点,具体代码以后放。

一、本地事务

1 、事务的基本性质

数据库事务的几个特性:原子性 (Atomicity ) 、一致性 ( Consistency ) 、隔离性或独立性 ( Isolation)

和持久性 (Durabilily) ,简称就是 ACID ;

 

原子性:一系列的操作整体不可拆分,要么同时成功,要么同时失败

 

一致性:数据在事务的前后,业务整体一致。

 

转账。 A:1000 ; B:1000 ; 转 200

事务成功 ; A : 800

B : 1200

 

隔离性:事务之间互相隔离。

 

持久性:一旦事务成功,数据一定会落盘在数据库。

在以往的单体应用中,我们多个业务操作使用同一条连接操作不同的数据表,一旦有异常,

我们可以很容易的整体回滚;

提交esn 提交订单_隔离级别

Business :我们具体的业务代码

Storage :库存业务代码;扣库存

Order :订单业务代码;保存订单

Account :账号业务代码;减账户余额

比如买东西业务,扣库存,下订单,账户扣款,是一个整体;必须同时成功或者失败

一个事务开始,代表以下的所有操作都在同一个连接里面;

2 、事务的隔离级别

READ UNCOMMITTED (读未提交)

该隔离级别的事务会读到其它未提交事务的数据,此现象也称之为脏读。  READ COMMITTED (读提交)

一个事务可以读取另一个已提交的事务,多次读取会造成不一样的结果,此现象称为不可重

复读问题, Oracle 和 SQL Server 的默认隔离级别。

  REPEATABLE READ (可重复读)

该隔离级别是 MySQL 默认的隔离级别,在同一个事务里, select 的结果是事务开始时时间

点的状态,因此,同样的

select 操作读到的结果会是一致的,但是,会有幻读现象。 MySQL

InnoDB 引擎可以通过

next-key locks 机制(参考下文 " 行锁的算法 " 一节)来避免幻读。

 

SERIALIZABLE (序列化)

在该隔离级别下事务都是串行顺序执行的, MySQL 数据库的 InnoDB 引擎会给读操作隐式

加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题。

3 、事务的传播行为

1 、 PROPAGATION_REQUIRED : 如果当前没有事务,就创建一个新事务,如果当前存在事务,

就加入该事务,该设置是最常用的设置。

2 、 PROPAGATION_SUPPORTS : 支持当前事务,如果当前存在事务,就加入该事务,如果当

前不存在事务,就以非事务执行。

3 、 PROPAGATION_MANDATORY : 支持当前事务,如果当前存在事务,就加入该事务,如果

当前不存在事务,就抛出异常。

4 、 PROPAGATION_REQUIRES_NEW : 创建新事务,无论当前存不存在事务,都创建新事务。

5 、 PROPAGATION_NOT_SUPPORTED : 以非事务方式执行操作,如果当前存在事务,就把当

前事务挂起。

6 、 PROPAGATION_NEVER : 以非事务方式执行,如果当前存在事务,则抛出异常。

7 、 PROPAGATION_NESTED : 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,

则执行与 PROPAGATION_REQUIRED 类似的操作。