数据库连接和事务以及线程之间的关系雩娄的木子

数据库中的事务就相当于是一个完整的业务逻辑,事务中的操作是最小工作单元,不可分割,要么同时成功,要么同时失败

事务只会增对于增删改操作,也就是所谓的DML语句:insert、update、delete;如果是查询的话,需要来使用事务。

只要是涉及到对数据的增删改操作,就要考虑着面临事务的问题。

如果全天下的业务逻辑中,只需要一个DML语句就能够操作完成业务的时候,就不需要涉及到事务机制。

但正是因为一个DML语句完成不了,,所以才需要事务机制,来保证多个DML语句在一个完整的业务逻辑中。

正是因为在做某个事情的时候,需要多个DML语句联合起来才能够完成,所以才能够体现出来事务的价值。

多个DML语句要么同时成功,要么同时失败,不允许成功一部分,也不允许只是失败一部分。

数据就是批量的DML语句,要么同时成功,要么同时失败;

InnoDB:提供了一组用来记录事务性活动的日志文件

事务开启了insertupdateupdateinsert事务结束了DML语句会记录到事务性活动日志中,在事务执行过程中,可以提交事务,也可以回滚事务。

提交事务:相当于是在事务性活动日志中记录一下,最终数据库接收到请求,说是要事务性活动日志保存到数据库表中,

将数据全部持久到数据库表中;并将事务性文件进行清空;提交是全部成功的结束。回滚事务:将之前在事务性活动日志中的记录全部清空,将之前的DML操作全部取消。结束是全部失败的结束

自动提交之后,无法回滚了,回滚点就在于上一次提交之后的地方

而本文作为《Spring设计思想-事务篇》的开篇,将深入数据库连接(java.sql.Connection对象)的特性、事务表示以及和Java线程之间的天然关系。懂得了底层的基本原理,在这些基础的概念之上再来理解Spring事务,就会容易很多。

在Java中,使用了java.sql.Connection实例来表示应用和数据库的一个连接,通信的方式目前基本上采用的是TCP/IP连接方式。通过对Connection进行一系列的事务控制。

可能有人有如下的想法:既然java.sql.Connection可以完成事务操作,那我在写代码的时候,直接创建一个然后使用不就行了?然而在事实上,我们并不能这么做,这是因为,java.sql.Connection和数据库之间有非常紧密的关系,其数据库的资源是很有限的。

应用程序和数据库之间建立Connection连接,而数据库机器会为之分配一定的线程资源来维护这种连接,连接数越多,消耗数据库的线程资源也就越多;

另外不同的connection实例之间,可能会操作相同的表数据,也就是高并发,为了支持数据库对ACID特性的支持,数据库又会牺牲更多的资源。

简单地来说,建立Connection连接,会消耗数据库系统的如下资源:

上述的几种资源会限制数据库的链接数和处理性能。

以MYSQL为例,可以通过如下语句查询数据库的最大支持情况:

--查看当前数据库最多支持多少数据库连接showvariableslike'%max_connections%';--设置当前运行时mysql的最大连接数,服务重启连接数将还原setGLOBALmax_connections=200;--修改my.ini或者my.cnf配置文件max_connections=200;数据库的连接数设置的越大越好吗?肯定不是的,连接数越大,对使用大量的线程维护,伴随着大量的线程上下文切换,并且与此同时,连接数越多,表数据锁使用的概率会更大,反而会导致整体数据库的性能下降。具体的设置范围,应当具体的业务背景来调优。

java.sql.Connection本身有如下两个比较关键的特性:

如下图所示:

有上图所示,对于java.sql.Connection对象的操作,一般会遵循序列化的事务操作模式,即:一个新事务的开启,必须在上一个事务完成之后(如果存在的话);

换成另外一种表述方式就是:对connection的操作必须是线性的。

Java中,当然一个线程可以在整个生命周期独占一个java.sql.Connection,使用该对象完成各种数据库操作。因为一个线程内的所有操作都是同步的和线性的。然而,在实际的项目中,并不会这样做,原因有两个:

结论:结合上述的两个症结,为了提高JDBC数据库连接的使用效率,目前普遍的解决方案是:当线程需要做数据库操作时,才会真正请求获取JDBC数据库连接,线程使用完了之后,立即释放,被释放的JDBC数据库连接等待下次分配使用基于这个结论,会衍生两个问题需要解决:

通过上述的图示中,可以看到,一个数据库连接对象,在线程进行事务操作时,线程在此期间内是独占数据库连接对象的,也就是说,在事务进行期间,有一个非常重要的特性,就是:数据库连接对象可以吸附在线程上,我把这种特性称之为事务对象的线程吸附性这种特性,正是由于这种特性,在Spring实现上,使用了基于线程的ThreadLocal来表示这种线程依附行为。

Java多线程访问同一个java.sql.Connection会导致事务错乱。例如:现有线程thread#1和线程thread#2,两个线程会有如下数据库操作:

*thread#1*:updatexxx;updateyyy;commit;

*thread#2*:deletezzz;insertttt;rollback;

语句执行的序列在connection对象上,可能表现成了:deletezzz;updatexxx;insertttt;rollback;updateyyy;commit;

有上图可以看到,Thread#1的请求updatexxx被thread#2回退掉,导致语句丢失,thread#1的事务不完整

解决上述事务不完整的问题,从本质上而言,就是多线程访互斥资源的方法。

多线程互斥访问资源的方式在Java中的实现方式有很多,如下使用有一个最简单的使用synchronized关键字来实现:

假设Thread#2先获取到了Connection锁,如下图所示:

存在的问题那上述的流程还有有点问题:假如thread#2在执行语句deletezzz,insertttt,rollback的过程中,在insertttt之前有一段业务代码抛出了异常,导致语句只执行到了deletezzz,这会导致在connection对象上有一个尚未提交的deletezzz请求;当thread#1拿到了connection对象的锁之后,接着执行updatexxx;updateyyy;commit;即:在两个线程执行完了之后,对connection的操作为deletezzz;updatexxx;updateyyy;commit;示例如下:

解决方案:确保每个线程在使用Connection对象时,最终要明确对Connection做commit或者rollback。调整后的伪代码如下所示:

java.sql.ConnectionsharedConnection=<创建流程>##thread#1的业务伪代码:synchronized(sharedConnection){try{`updatexxx`;`updateyyy`;`commit`;}catch(Exceptione){`rollback`;}}##thread#2的业务伪代码:synchronized(sharedConnection){try{`deletezzz`;`insertttt`;`rollback`;}catch(Exceptione){`rollback`;}}综上所述,解决多个线程访问同一个Connection对象时,必须遵循两个基本原则:

正常情况下,我们在写业务代码时,会有类似的流程:

结合上面的叙述,目前的做法,在完成事务后,并不会销毁java.sql.Connection实例,而是将其回收到连接池中。

一般连接池需要如下几个功能:

目前比较流行的几个连接池解决方案有:HikariCP,阿里的Druid,apache的DBCP等,具体的实现不是本文的重点,有兴趣的同学可以研究下。

最后小技巧PROPAGATION_NOT_SUPPORTED(仅仅为了让Spring能获取ThreadLocal的connection),

如果不使用事务,但是同一个方法多个对数据库操作,那么使用这个传播行为可以减少消耗数据库连接

一、事务操作是为了完成一个复杂的业务逻辑,完整的业务逻辑需要同时保证失败或者成功,这里是体现了事务的价值的地方。

二、数据库连接的特性:

三、从应用连接数据库,数据库需要维持一定的资源来保证外部应用能够连接,同时利用ACID特性保证多个连接操作同一个数据库;

四、和线程之间的关系。在一个线程的生命周期中,从一个数据库连接创建开始,到使用数据库连接开始事务,到事务提交,到关闭数据库连接期间,这可能只是在线程在使用到数据库的时候才会来使用到。但是使用数据库连接开启事务、提交事务这个过程对于整个线程的生命周期来说,这是一个相对短暂的过程。

而且,线程只会在使用到了数据库连接的时候才会来使用,使用完成就销毁,将数据库连接保存到线程上,也是一个不错的选择。

五、数据库连接上的事务应该是按照线性排列的,在一个数据库连接可以执行无数次SQL,按照顺序来进行执行。

THE END
1.python通过来体现语句之间的逻辑关系这样可以保证程序的鲁棒性,在遇到异常时进行相应的处理,从而保证程序的正常运行。 总结起来,Python通过灵活的条件语句、循环语句的运用,顺序结构的执行,函数的调用与返回,以及异常处理机制等手段来体现语句之间的逻辑关系。这使得Python成为了一款非常适合处理复杂问题的编程语言。https://www.python51.com/jc/123187.html
2.考研英语语句关系分类及区分方法考研英语语句之间关系主要是考查前后句子之间的逻辑关系。 句子与句子之间的关系可能是显性的,也可能是隐性的。显性的语句之间关系有明显的标志词出现,这会给考生理解文章的发展脉络带来很大方便。句际关系主要有以下几种: 一、顺接关系 后句是前句的延续或补充,标识词主要有then,after that,furthermore,also,when(https://yz.chsi.com.cn/kyzx/en/200907/20090706/27495222.html
3.科学网—MySql外键一对一,一对多,多对多表关系外键虽然能够帮你强制建立表关系 但是也会给表之间增加数据相关的约束;改也不好改,删也不好删。 删除的时候可以先删除把绑定关系表的数据,然后再删除被绑定关系表的数据。 现在绑定关系表是emp,被绑定关系表是dep;删除语句为: delete from emp where id = 4; https://wap.sciencenet.cn/blog-3445347-1282998.html
4.Python程序语法元素分析缩进可以嵌套使用什么意思缩进指每一行代码开始前的空白区域,用来表示代码之间的包含和层次关系 代码编写中,缩进可以用Tab键实现,也可以用多个空格**(一般是四个空格)**实现,但两者不能混用。建议使用四个空格的编写方式 除了单层缩进,一个程序的缩进还可以“嵌套”从而形成多层缩进,python语言对语句之间的层次关系没有限制,可以“无限制”嵌https://blog.csdn.net/qq_55016379/article/details/114675213
5.Java错题合集(3)48、一般情况下,选项是关系数据模型与对象模型之间匹配关系? 表对应类 记录对应对象 表的字段对应类的属性 49、J2EE中常用的名词解释正确的是? EJB容器:Enterprise java bean 容器 JMS:JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。 https://www.jianshu.com/p/29ec5bb08b71
6.胡敏教授解密考研阅读命题与对策者深层思路上的逻辑关系。从句子与句子,段落与段落之间的过渡可以读出作 者整个的思路及论证过程,才能做出正确的推理、判断或引申。平时训练时, 考生应多注意一些过渡词。 3.积极扩展词汇量,注意动词和名词,训练理解一些难句、复杂句。扩展词汇 量的目的是避免词汇的欠缺造成理解的失误。动词作为语句结构的枢纽,意义http://www.yuloo.com/kaoyan/news/2006-09-30/43221.shtml
7.华为HCIP华为数通工程师刷题日记1116(一个字惨)【答案解析】策略路由(PBR)的操作对象是数据包,所以if-match后面是可以跟ACL,但是不可以跟ip-prefix;机构组成类似于 route-policy:包含多个节点/条目,彼此之间是或的关系;每个节点/条目中,可以有1个条件(if-math),也可以多个条件(if-math),彼此之间与的关系;每个节点/条目中,可以有apply语句,就是修改数据包;如https://developer.aliyun.com/article/1436981
8.埋头刷了大半年Java面试题:如愿拿到众多大厂offer!分享还愿在工作中,SQL语句的优化和注意的事项 哪些库或者框架用到NIO Spring 都有哪几种注入方式,什么情况下用哪种,ioc实现原理 如何定位一个慢查询,一个服务有多条SQL你怎么快速定位 聚集索引和非聚集索引知道吗?什么情况用聚集索引什么情况用非聚集索引 Nosql引擎用的什么存储结构,关系型数据库和NoSQL各自的优劣点是什么https://maimai.cn/article/detail?fid=1735736133&efid=KbtUeCVp4epTWWihu6i76A