一、总 结
- 事务是一个最小的不可再分的工作单元。 一个事务对应一套完整的业务逻辑。
- 事务管理机制的作用 —— 通过保证一系列数据操作过程的完整性,来保障数据的安全性。
- 使用事务的前提条件 —— 数据库管理系统必须使用支持事务的存储引擎。
- MySQL中默认采用InnoDB存储引擎,默认采用【自动提交模式】来管理事务。
- 事务机制的经典使用场景 —— 处理“银行账户之间的转账操作" 。
- 事务只和DML语句有关,只有DML语句的出现才存在事务管理。
- 开启事务管理的标志 —— DML语句的执行。
- 结束事务管理的标志 —— TCL语句的执行。
- 事务管理的四大特性 —— ACID : 【原子性、一致性、隔离性、持久性】
- 事务之间由低到高的四个隔离级别 —— 【读未提交、读已提交、可重复读、串行化读】
- 可以把一系列要执行的操作称为事务。而事务管理就是管理这些操作要么完全执行,要么完全不执行。反映在SQL中就是,事务只和DML语句有关。通常,一个完整的业务需要通过执行批量的DML语句之后,进行统一提交事务,才能达到效果。
二、解 析
(一)银行转账业务的分析
银行转账过程是一个完整的业务,最小的单元,不可再分,也就是说银行转账业务是一个完整的事务。
1) t_act 账户表中的数据如下:
2)需求:账户“1001”要 转账给 “1002”账户。
该一系列“转账”操作,应当会导致数据库表中的相关数据发生修改。假设转账金额是500元。 就应该执行下列两条DML语句:
update t_act set balance = 1000.0 where actno = ‘act-001’;
update t_act set balance = 500.0 where actno = ‘act-002’;
3)分析:
- 以上两条DML语句必须同时成功或者同时失败,因为它为最小业务单元,不可拆分;
- 当第一条DML语句执行成功之后,并不能立马将底层数据库中的第一个账户的数据修改,应该只是将操作记录了一下,这个记录是在内存中完成的。
- 当第二条DML语句执行成功之后,底层数据库文件中的数据需要完成同步。
- 若第二条DML语句执行失败,将清空所有的历史操作记录
4)技术(机制):
要完成以上功能,那必须借助mysql数据库的“事务“机制 transaction。
在mysql中并不是所有的数据存储引擎都支持事务管理的,只有 Innodb数据存储支持事务管理。且默认采用自动提交事务方式。
(即执行DML语句时,默认自动提交事务。)
在mysql中,,常用的3种存储引擎?
1)myISam (不支持事务)
2)InnoDB (MySql数据库默认使用是InnoDB存储引擎。该存储引擎支持事务。)
3)memory (不支持事务)
2. )一个完整的业务需要批量的DML语句(insert、update、delete)共同完成。
比如:银行账户转账 业务
从A账户向B账户转账10000 , 需要执行两条update语句:
update t_act set balance = balance - 10000 where actno = 'act-001';
update t_act set balance = balance + 10000 where actno = 'act-002';
以上两条DML语句必须同时成功,或者同时失败,不允许出现一条成功,一条失败。
要想保证以上的两条DML语句同时成功或者同时失败,那么就需要使用数据库的“事务机制”。
思考:假设所有的业务都能使用1条DML语句搞定,还需要事务机制吗?
该情况不需要使用到事务。
但实际开发中,通常一事务【业务】需要多条DML语句共同联合完成。
事务只和DML语句有关系;当执行DML语句时,其实就是开启了一个事务。
或者说 只有DML语句(insert/ delete/ update)中才存在事务。
因为它们这三个语句都是和数据库表当中的“数据”相关的。
以上所描述的批量DML语句共有多少DML语句,这个和业务逻辑有关系。
业务逻辑不同,则DML语句的个数不同。
4. )事务可以保证多个操作的原子性。即,事务的存在就是为了保证数据的完整性,安全性。*
事务可以保证多个操作的原子性:要么全部成功,要么全部失败。
对于数据库来说,事务保证批量的DML要么全部成功,要么全部失败
5. )【TCL】语句是和事务相关的语句 **
rollback 或commit 的执行,标志着事务的结束。
4.事务管理?
可以把一系列要执行的操作称为事务。
而事务管理就是管理这些操作要么完全执行,要么完全不执行。
经典例子:
A要给B转钱,首先A账户钱减少了,但由于数据库突然断电,导致无法给B账户加钱。
然后由于丢失数据,B不承认收到A的钱;
在这里事务就是确保加钱和减钱两个都完全执行或完全不执行,如果加钱失败,那么不会发生减钱。
0)-手动开启事务管理的命令
start transaction
这行命令作用是:
【start transaction命令的作用】:
在mysql开启了“自动提交”模式的状态下,关闭了事务的自动提交机制,对接下来要执行的DML语句进行手动开启事务管理。
手动开启事务管理之后,从此mysql不再将DML语句执行出来的结果立即更新到表中。而是会记录到事务的日志中。只有开发者手动地进行commit提交成功之后,mysql才会将DML执行结果同步到数据库的表中。
1)-事务开启的标志
任何一条DML语句执行,都标志事务的开启。
2)-事务结束的标志
commit 或 rollback的执行,都标志着事务的结束。
1)事务提交 操作
语法:commit ———— 事务成功的结束。
会将所有的DML语句操作记录 和 底层硬盘文件中数据进行一次同步。
2)事务回滚 操作
语法:rollback ———— 事务失败的结束。
会将所有的DML语句操作记录全部清空。
回滚会清掉开始事务管理之后写到事务日志中的内容,即恢复到开启事务管理之前。
注意:回退操作只是回退"写"的内容,对于普通的读表select语句不能回退。
3)-事务管理的意义
保证数据操作的完整性,安全性。
4)-注意事项
在事务进行过程中,未结束之前,DML语句是不会修改底层数据库文件中的数据。只是将历史操作在内存中记录一下。只有在事务结束,而且是成功结束(提交)的时候,才会修改底层硬盘文件中的数据。
只能回滚 insert、delete和update语句,不能回滚select语句(回滚select没有任何意义)。
对于create、drop、alter这些语句也无法回滚。
当 commit 或 rollback 语句执行后,事务会自动关闭(将来的更改会隐含提交)。
(二)事务管理的四大特性
1.原子性(Atomicity)
事务的整个操作是一个整体,不可以分割,要么全部成功,要么全部失败。
事务是最小的工作单元,不可再分。
只有两种结果:成功 或 失败
2.一致性(Consistency)
指的是事务操作的前后,数据表中的数据没有变化。
事务必须保证多条DML语句同时成功或者同时失败。
3.隔离性(Isolation)
事务之间的操作是相互隔离不受影响的。
事务A与事务B之间具有隔离。一个事务不会影响其他的事务运行。
4.持久性(Durability)
数据一旦提交,不可改变,永久的改变数据表数据
持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束。
只有事务成功提交的情况下,才有持久性。
在事务完成后,该事务对数据库所做的更改将持久化地保存在数据库中,事务才算成功的结束。且不会 被回滚。
(三)事务管理的提交模式
自动提交模式的状态用于决定新事务 如何启动 以及 何时启动。
自动提交模式的开启或关闭,可以通过服务器变量AUTOCOMMIT来控制。
1-启用 自动提交模式
如果自动提交模式被启用,则单条DML语句将缺省地开始一个新的事务。
如果该语句执行成功,事务将自动提交,并永久地保存该语句的执行结果。
如果语句执行失败,事务将自动回滚,并取消该语句的执行结果。
在自动提交模式下,仍可使用start transaction语句来显式地启动事务。这时,一个事务仍可包含多条语句,直到这些语句被统一提交或回滚。
2-禁用 自动提交模式
如果禁用自动提交,事务可以跨越多条语句。
在这种情况下,事务可以用COMMIT和ROLLBACK语句来显式地提交或回滚。
三、MySql中的事务管理
(一)MySQL中事务采用自动提交模式
默认情况下,mysql的事务管理采用的是自动提交模式:DML语句的只要执行一条DML,就开启了事务,并提交这次事务;于是执行结果会立马同步到到数据表中。
0.查询事务管理状态
show variables like 'autocommit';
查询结果 —— ON值,代表自动提交。
查询结果 —— OFF值,代表 不是自动提交。 需要手动执行 commit;
1.关闭自动提交事务
(1)使用命令
在自动提交模式开启的情况下,关闭事务的自动提交,手动开启事务。
step1:
start transaction; // 该SQL命令含义:关闭了事务的自动提交机制,手动开启事务管理。
step2:
执行若干条DML语句。
step3:
commit; // 进行事务的提交
start transaction命令的作用:
在mysql开启了“自动提交”模式的状态下,关闭了事务的自动提交机制,对接下来要执行的DML语句进行手动开启事务管理。
手动开启事务管理之后,从此mysql不再将DML语句执行出来的结果立即写到表中,而是会记录到事务的日志中。只有开发者手动地进行commit提交成功之后,mysql才会将DML执行结果同步到数据库的表中。
(2)修改变量值
MySql中事务的自动提交模式。该方式只对当前会话有效 (将off值改为on,则为"开启")
set autocommit=off;
或
set autocommit=0;
或
set session autocommit = off
关闭后自动提交事务之后,则需要commit来执行每一条语句,相当于手动开启了事务管理。
不过注意的是set autocommit针对的是会话变量,所以这个设置只在此次会话连接中生效。
2.开启自动提交事务
set autocommit=on;
或
set autocommit=1;
(二)事务间的隔离性
1.引发的相关问题
隔离性是事务管理的四大特性之一,事务之间存在隔离级别。
事务的隔离级别决定了事务之间可见的级别。
理论上隔离级别包括4个: 低 ——高
因事务之间的隔离性引发的相关问题
当多个客户端并发地访问同一个表时,可能出现下面的一致性问题:
(1)脏读取
一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交,这就出现了脏读取(Dirty Read)。
(2)不可重复读
在同一个事务中,同一个读操作对同一个数据的前后两次读取产生了不同的结果,这就是不可重复读(Non-repeatable Read)。
(3)幻像读
幻像读(Phantom Read)是指在同一个事务中以前没有的行,由于其他事务的提交而出现的新行数据。
2.事务的隔离级别
InnoDB 存储引擎支持这四种事务的隔离级别。用以控制事务所做的修改,并将修改通告至其它并发的事务:
1-read uncommitted 读未提交
2- read committed 读已提交
3- repeatable read 可重复读
4- serializable 串行化(序列化)
(1)read uncommitted
对方事务A还没有提交,我们当前事务B可以读取到对方未提交的数据。 这里读到的数据,叫“脏数据”或 “脏读 Dirty Read”
读未提交存在脏读(Dirty Read)现象:表示读到了脏的数据。
1) 事务A和和事务B,事务A未提交的数据,事务B可以读取。(允许一个事务可以看到其他事务未提交的修改。)
2) 这里读取到的数据可以叫做“脏数据”或“脏读 Dirty Read”
3) 读未提交隔离级别最低,这种级别一般叧在理论上存在,数据库默认隔离级别一般都高于该隔离级别;
(2)read committed
1) 事务A和事务B,事务A提交的数据,事务B才可读取到;(允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的。)
2) 该隔离级别高于“读未提交”级别,解决了: 脏读现象。
3) 换句话说:对方事务提交之后的数据,当前事务才可读取到。
4) 该隔离级别可以避免脏数据;
5) 该隔离级别能够导致“不可重复读取”
6) Oracle数据库管理系统默认隔离级别为“读已提交”
(Oracle数据库支持 READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。)
(3)repeatable read
1) 事务A和事务B,事务A提交之后的数据,事务B还是读取不到。事务B只能读取到,事务B开启事务时刻表中的数据。
2) 事务B是可重复读到数据的。(确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果,不管其他事务是否提交这些修改。 (银行总账))
3) 这种隔离级别高于“读已提交”。
4) 换句话说,对方提交之后的数据,还是读取不到。
5) 这种隔离级别可以避免“脏读和不可重复读”,达到“重复读取”;
6) MySQL数据库管理系统默认隔离级别为:可重复读
7) 这种隔离级别解决了:不可重复读问题,达到了“重复读取”。
该隔离级别存在的问题是:读取到的数据是幻象。
(4)serializable
序列化读/串行化读
1 ) 事务A和事务B,事务A在操作数据库表中数据的时候,事务B叧能排队等待;(将一个事务与其他事务完 全地隔离。 )
2) 这种事务隔离级别一般很少使用,吞吐量太低,用户体验不好;效率低。需要事务排队。
3) 事务A和事务B不再并发;避免了“幻想读”,每一次读取都是数据库表中真实的记录。
3.事务隔离级别的作用范围
- 会话级(session):只对当前会话有效
- 全局级(global) : 对所有会话有效
(1.1)查当前会话隔离级别
select @@tx_isolation;
或
select @@session.tx_isolation;
(1.2)查看全局隔离级别
select @@global.tx_isolation;
(2.1)设置会话隔离级别
set transaction isolation level 隔离级别类型 ;
或
set session transaction isolation level 隔离级别类型 ;
(2.2)设置全局隔离级别
set global transaction isolation level 隔离级别类型;
4-设置服务器缺省隔离级别
(1)静态设置:修改配置
关闭服务器,修改my.ini配置文件
在 my.ini 文件中的[mysqld]下面添加:
transaction-isolation = 隔离级别类型
(2)动态设置:使用命令
在运行的服务器中通过命令方式,动态设置
set [global/session] transaction isolation level 隔离级别类型;
四、JDBC中的事务
1-JDBC中的事务默认采用自动提交机制
即 ,只要执行任意一条DML语句,JDBC则会自动提交一次事务。
但是在实际的业务当中,通常都是使用N条DML语句共同联合才能完成的。必须保证他们这些DML语句在同一个事物中同时成功或者同时失败。
典型的案例:银行转账业务
2-代码演示 “银行转账”业务
分析:采用JDBC中默认的自动提交事务机制,会对该业务实现过程中产生的影响。以及给出解决方案。
代码编号:**《Jdbc_Transaction/演示银行转账业务》**
3-事务+【行级锁】的使用
1)悲观锁/行级锁
事务必须排队执行。被选中锁住的数据不允许被并发修改。
使用方法:select语句后面添加for update即可。如:
使用场景:
在实际开发中,在查询某张表数据时,为了保障数据的真实性,可以使用【行级锁】 (使用方法:在select语句之后添加for update)将需要的这些数据进行锁住。于是,别的线程开启的事务将无法对这些数据进行修改。
select ename,job,sal from emp where job = 'manager' for update;
这行语句的意思:工作岗位是manager的这些员工数据被锁定。(行级锁) 当前事务未结束时,这些数据被锁住了。其他的事务无法对这些被锁住的数据进行修改操作。
2)乐观锁
支持并发,事务也无需排队。但是会多一个版本号的概念。
多线程并发时,也可以对某条数据进行修改。
执行原理:
假设:开启事务1时,读取到数据的版本号是1.1。同时开启事务2,读取到数据的版本号也是1.1。
事务1先对数据进行了修改,修改之后发现版本号依旧是1.1,和它最初读取到的版本号一致(说明没有其他的事务对数据进行了修改)。于是提交事务修改数据。版本号将会变更为1.2。
事务2再进行修改数据,修改之后准备提交时,发现数据的版本号是1.2。和它最初读取到的版本号不一致(说明有其他的事务对数据进行了修改)。于是会执行回滚操作。
| ename | job | sal | version |
| ----- | ------- | ---- | ------- |
| james | manager | 8000 | 1.1 |
3)代码演示
结合Debug工具查看效果
涉及到两个程序
1-演示程序开启一个事务.java
2-该程序演示修改被锁定的记录.java
《Jdbc_Transaction/事务+行级锁的使用》
五、MyBatis中的事务
九.其他
锁机制
在事务操作一个表时,如果使用索引来取值,那么会锁定到对应行;
如果没有使用索引来取值,那么会锁定整个表。锁定之后其他连接无法操作指定行或表。
回滚点
作用:回滚点可以指定rollback回退的位置。
> 比如:现在打了100条命令,发现第81打错了,如果回滚到打了81命令之前一点而不是回滚到开启事务之前就可以节省下很多时间。
创建回滚点: savepoint 回滚点名;
回滚到回滚点: rollback to 回滚点名;
注意事项:回滚点在事务管理关闭(rollback或commit之后)之后失效,**不能在事务之外使用回滚点。
单机事务
分布式事务
即数据库不是一个。是数据库集群。分布方式的。 一个数据库不够
北京的数据库 转到 南京的数据库。
保证这两数据库的事务都成功,才能成功。