首页 > 其他分享 >分布式事务-本地事务

分布式事务-本地事务

时间:2023-06-06 21:34:14浏览次数:32  
标签:事务 提交 Transactional 回滚 程序员 本地 void 分布式

image.png

一、本地事务

1、事务的基本性质

数据库事务的几个特性:原子性(Atomicity)、一致性(Consistency)、隔离性或独立性(isolation)、持久性(Durability),简称就是 ACID。

  • 原子性:一系列的操作整体不可拆分,要么同时成功,要么同时失败。
  • 一致性:数据在事务的前后,业务整体一致。 转账:A:1000; B:1000; 转 200 事务成功; A:800; B:1200
  • 隔离性:事务之间互相隔离
  • 持久性:一旦事务成功,数据一定会落盘在数据库。 在以往的单体应用中,我们多个业务操作使用同一条连接操作不同的数据表,一旦有异常,我们可以很容易的整体回滚。 image.png

Business:我们具体的业务代码 Storage:库存业务代码,扣减库存 Order:订单业务代码,保存订单 Account:账号业务代码,减账户余额 比如买东西业务,扣库存,下订单,账户扣款,是一个整体,必须同时成功或失败。 本地事务的适用非常简单,在方法上加上@Transactional 注解就可以了。

2、事务的隔离级别

数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。

Read uncommitted#

读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。 事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。

那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。

Read committed 读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他买单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…

分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。 那怎么解决可能的不可重复读问题?Repeatable read !

Repeatable read# 重复读,就是在开始读取数据(事务开启)时,不再允许修改操作

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他买单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。

分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。

什么时候会出现幻读? 事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

那怎么解决幻读问题?Serializable!

Serializable 序列化# Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。 在代码中我们可以指定事务的隔离级别:

@Transactional(isolation=Isolation.READ_COMMITED)

3、事务的传播行为

1、支持当前事务: TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。

2、不支持当前事务: TransactionDefinition.PROPAGATION_REQUIRED_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。 TransactionDefinition.PROPAGETION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。 TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

3、其他 TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

@Transactional  // a事务的所有设置就传播到了和他共用一个事务的方法。
public void a() {

// b,c是否和a共用一个事务?
b();   // b需要一个事务,这时b和a共用一个事务,即在a事务里边
c();   // c是一个新事务

int i = 10 / 0;   // 哪个会回滚呢?
}

@Transactional(propagation=Propagation.REQUIRED)
public void b() {
}

@Transactional(propagation=Propagation.REQUIRED_NEW)
public void c() {
}

如果上边的代码 int i = 10 / 0;执行,哪个会回滚呢? 由于b和a是在同一个事务里边,所以,两个都会回滚,而c是一个新事物,所以,不会回滚。

我们还可以给事务添加超时时间 @Transactional(timeout =20),如果共用事务,则子事务的设置就不起作用了,会用父类的事务。

@Transactional(timeout =20)   // a事务的所有设置就传播到了和他共用一个事务的方法。
public void a() {

// b,c是否和a共用一个事务?
b();   // b需要一个事务,这时b和a共用一个事务,即在a事务里边
c();   // c是一个新事务

int i = 10 / 0;   // 哪个会回滚呢?
}

// b这里设置的超时时间不起作用,因为它用的是a的事务,所以,超时时间和a的一样
@Transactional(propagation=Propagation.REQUIRED,  timeout =2 )
public void b() {
}

@Transactional(propagation=Propagation.REQUIRED_NEW, ,  timeout =30)
public void c() {
}

4、SpringBoot事务关键点

  • 事务的自动配置 TransactionAutoConfiguration

事务的坑 在同一个类里面,编写两个方法,内部调用的时候,会导致事务设置失效。原因是没有用到代理对象的缘故。

@Transactional(timeout =20)   // a事务的所有设置就传播到了和他共用一个事务的方法。
public void a() {

// b,c做任何设置都没用,都是和a共用一个事务,事务最大的一个特性就是代理
// b,c是否和a共用一个事务?
b();   // b需要一个事务,这时b和a共用一个事务,即在a事务里边
c();   // c是一个新事务

int i = 10 / 0;   // 哪个会回滚呢?
}

// b这里设置的超时时间不起作用,因为它用的是a的事务,所以,超时时间和a的一样
@Transactional(propagation=Propagation.REQUIRED,  timeout =2 )
public void b() {
}

@Transactional(propagation=Propagation.REQUIRED_NEW, ,  timeout =30)
public void c() {
}

事务使用代理对象来控制的,上边的b,c和a是相同的对象,同一个对象内事务方法互调默认失败,原因是绕过了代理对象,

解决: 1)、导入spring-boot-starter-aop,引入了 aspectj 2)、@EnableTransactionManagement(proxyTargetClass=true) 3)、@EnableAspectJAutoProxy(exposeProxy=true);开启 aspectj 动态代理功能。以后所有的动态代理都是 aspectj 创建(即使没有接口也可以创建动态代理),对外暴露代理对象。 4)、本类互调用调用对象(OrderServiceImpl obj = AopContext.currentProxy()) 如果有本类互调的可以使用上边的解决方案。

5、本地事务问题#

在提交订单方法上,我们加了本地事务 @Transactional,当订单创建失败时,会自动回滚相关的数据,但是该方法包含了远程调用锁库存等服务,如何回滚远程的锁库存数据是一个问题;还有一个问题,当远程锁库存成功,但是由于网络等问题,响应超时了,这时还以为锁库存失败了,订单就会自动回滚,这就会出现一个很严重的问题,订单回滚了,而锁定的库存没有回滚。

标签:事务,提交,Transactional,回滚,程序员,本地,void,分布式
From: https://blog.51cto.com/u_15993308/6427792

相关文章

  • MPP大规模并行计算数据库与分布式数据库的区别
    最近调研分布式TP数据库。结合公司使用的MPP数据库,一度感觉两者很像,随着分布式的深入研究,结合行内MPP数据库使用过正中遇到的问题,简单的总结一下分布式数据库与MPP数据库的区别。分布式数据库系统与并行数据库系统MPPDB有许多相似点,如都有用网络连接各个数据处理结点的特点。网络中......
  • 分布式OLTP数据库选型---汇报
    汇报思路 开场:各位好,分布式数据库经近个月的集中调研,目前有了阶段性的进展,现就调研的结果对各位领导做一个详细的汇报。 本次汇报共16页PPT。汇报思路为:漏斗形,有宽到窄的一个思路体系。 汇报时间我会控制在30分钟内,先总体做串讲,完了针对有疑问的地方我们进行详细的分析讨论。......
  • 分布式缓存
    常见缓存中间件:MemcachedmongoDBRedisMemcached:简单key-value数据结构,不支持持久化(可重启缓存功能并不算),不支持集群(客户端自己控制),性能强。mongoDB:数据结构非常全面的文档型数据库,支持持久化,支持集群,性能中等。Redis:技能五种基本数据类型和扩展类型,支持持久化,支持集群,性能强......
  • Spring框架中事务控制的运行原理
    PhotobyTomaszFilipekfromPexels:https://www.pexels.com/photo/nature-photography-of-flower-field-1646178/SpringTransaction基本介绍我们在日常开发中经常使用Spring框架来实现事务管理。事务管理是指在执行一系列操作时,保证这些操作要么全部成功,要么全部失败,不......
  • Taurus.mvc .Net Core 微服务开源框架发布V3.1.7:让分布式应用更高效。
    前言:自首个带微服务版本的框架发布:Taurus.MVCV3.0.3微服务开源框架发布:让.NET架构在大并发的演进过程更简单已经过去快1年了,在这近一年的时间里,版本经历了N个版本的迭代。如今,是时候写文章介绍一下了:以下介绍中,仅以.NetCore6为示例代码。框架支持在.NetFramework2.0+......
  • 9.4. 分布式与微服务架构
    在本章节中,我们将介绍分布式系统和微服务架构的基本概念。分布式系统解决了单体应用面临的可扩展性、高可用性等问题,而微服务架构进一步提升了系统的可维护性和灵活性。9.4.1.分布式系统基本概念分布式系统是由多个独立的计算节点组成的系统,这些节点通过网络进行通信和协作。......
  • chatglm+langchain本地cpu实战.
    #Setupenvirnment#大概使用59个G内存.condacreate-nlangchainpython=3.8.1-ycondaactivatelangchain#拉取仓库gitclonehttps://github.com/imClumsyPanda/langchain-ChatGLM.git#安装依赖cdlangchain-ChatGLMpython3-mpipinstall-rrequirements.txtpy......
  • spingboot maven 使用简化配置 将本地包加入classpath
    不是使用dependencyManagement<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version><relativePath......
  • 应用问题解决-分布式锁(LUA保证删除原子性)
    问题:删除操作缺乏原子性场景1、index1获得锁、执行具体操作、比较lock的uuid值确实和自己生成的uuid是否相等,相等则删除锁。uuid=v1set(lock,uuid)uuid.equals(get("lock"))2、但是index1执行删除前,lock刚好过期时间已经到了,被redis自动释放3、此时index2获取锁,执行具体......
  • 分布式搜索elasticsearch集群监控工具bigdesk
    bigdesk是elasticsearch的一个集群监控工具,可以通过它来查看es集群的各种状态,如:cpu、内存使用情况,索引数据、搜索情况,http连接数等。项目git地址: https://github.com/lukas-vlcek/bigdesk。和head一样,它也是个独立的网页程序,使用方式和head一样。插件安装运行......