文章目录
1.什么是事务?
一个事务就是一个完整的业务逻辑,是一个最小的工作单元,不可再分。
什么是一个完整的业务逻辑?
拿转账来说,从A账户向B账户转张10000
将A账户的钱减去10000
将B账户的钱加上10000
这就是一个完整的事务
以上操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。
和事务有关的是DML语句,其它语句和事务无关。
insert
delete
update
只有以上三个语句和事务有关。
因为只有以上三个语句是数据表中诗句的增删改,只要涉及到数据的增删改,就需要考虑到安全问题。
2.对事务的理解
假设所有的业务只需要一条DML语句就能完成,事务就没必要存在了。正是因为在处理某件业务时,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。
所到底,一个事务其实就是多条DML语句同时成功或同时失败(批量的DML语句同时成功或同时失败)。
3.如何实现事务
InnoDB存储引擎提供一组用来记录事务性活动的日志文件
事务开启
DML语句
DML语句
DML语句
DML语句
...
事务结束
在事务执行过程中,每一条DML语句的操作都会被记录到“记录事务性活动的日志文件”中,在事务执行过程中,我们可以提交事务,也可以回滚事务。
提交事务
清空实务性活动的文件ri'zh,将数据彻底持久化到数据库表中。 提交事务标志着事务的结束,并且是一种全部成功的结束。
回滚事务
将之前所有的DML操作全部撤销,并清空“记录事务性活动的日志文件”。回滚事务标志着事务的结束,并且是一种全部失败的结束。
3.1.如何提交和回滚事务
提交事务:commit ; 语句
回滚事务:rollback ; 语句
开启事务:start transaction
// 创建一个数据表
create table t_tran(
id int ,
name varchar(225),
sex char(1)
);
// 插入数据
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
// 查询此时表中的数据
select * from t_tran;
+------+------+------+
| id | name | sex |
+------+------+------+
| 1 | zs | 男 |
| 1 | zs | 男 |
| 1 | zs | 男 |
| 1 | zs | 男 |
+------+------+------+
// 回滚
rollback;
// 再次查询表中的数据
select * from t_tran;
+------+------+------+
| id | name | sex |
+------+------+------+
| 1 | zs | 男 |
| 1 | zs | 男 |
| 1 | zs | 男 |
| 1 | zs | 男 |
+------+------+------+
可以发现并没有回滚。这种自动提交实际上并不符合开发中的习惯,因为一个事务通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全,需要多条DML语句同时成功后在提交的。
想要取消掉MySQL中的自动提交,我们只需要开启事务即可start transaction;
3.1.1.回滚事务
// 删除t_tran表中的数据
delete from t_tran; // 此时表中没有数据
// 开启事务
start transaction;
// 执行DML语句
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
// 查询表中数据
select * from t_tran;
// 回滚事务
rollback;
// 查询表中数据
select * from t_tran;
可以看到,执行rollback后,表仍未空,说明回滚成功。表回到了它上一次提交时的状态。
3.1.2.提交事务
// 删除t_tran表中的数据
delete from t_tran; // 此时表中没有数据
// 开启事务
start transaction;
// 执行DML语句
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
insert into t_tran(id,name,sex) values(1,'zs','男');
// 查询表中数据
select * from t_tran;
// 提交事务
commit;
// 查询表中数据
select * from t_tran;
可以看到,执行了提交事务的语句后,我们再去回滚,也就只能将表的状态回滚到它上次提交事务之后
2.事务的特性
A:原子性(Atomicity)
。事务是数据库的逻辑工作单位,其包括的操作要么都做,要么都不做。
C:一致性(Consistency)
。事务的执行结果是使数据库从一个一致性状态变到另一个一致性状态。如果数据库系统运行中发生故障,有些操作尚未完成就被迫中断,但已完成的操作对数据库已做出修改,这时数据库处于一种不正确的状态,即不一致的状态。如A账户向B账户转10000块钱,可以定义一个事务,该事务中存在两个操作:从A账户中减去10000块钱;在B账户中加上10000块钱。这两个操作要么同时成功,要么同时失败,才能使数据库处于一致性的状态。可见原子性和一致性是密切相关的。
I:隔离性(Isolation)
。 事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。
D:持久性(Durability)
。持久性也称永久性,指一个事务一旦提交,它对数据库中数据的影响应该是永久性的
2.1.事务间的隔离级别
读未提交 | read uncommitted(最低的隔离级别) |
读已提交 | read committed |
可重复读 | repeatable read |
序列化/串行化 | serializable(最高的隔离级别) |
读未提交:事务T1可以读取到事务T2未提交的数据,这种隔离级别存在脏读(Dirty Read)现象。这种隔离级别一般都是理论上的,大多数隔离级别是从读已提交开始的。
读已提交:事务T1只能读取到事务T2提交之后的数据,解决了脏读问题,但是存在不可重复读的问题。
可重复读:事务T1开启之后,无论多久,每一次在事务T1中读取到的数据都是一致的,即使事务T2已经将数据修改并提交,事务T1读取到的数据仍没改变。
在可重复读隔离级别下,传是快照读,是不会看到别的事务对数据的修改的,只有幻读才能看到
序列化/串行化:事务T1执行时,事务T2不能执行。这种隔离级别最高,解决了上述所有问题。但是效率最低,不能并发,事务只能排队执行
脏读(dirty read)
脏读是指事务T1修改某一数据并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因被撤销,这时被T1修改过的数据恢复原值,T2读取到的数据就与数据库中的数据不一致,则T2读到的数据就是脏数据,即不正确的数据
不可重复读(non-repeatable read)
不可重复读是指事务T1读取表中数据后,事务T2对表中数据执行了更新操作,使得T1无法再现前一次读取结果。
不可重复读包括三种情况:
(1)事务T1读取某一数据后,事务T2对其进行修改,当事务T1再次读取该数据时,得到与前一次不同的值。
(2)事务T1按一定条件从数据库中读取了某些数据记录后,事务T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现某些记录不存在了
(3)事务T1按一定条件从数据库中读取某些数据后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。
后两种不可重复读有时也被称为幻读
现象
MySQL中默认的事务隔离级别是可重复读,Oracle中默认事务隔离级别是读已提交
2.2.验证隔离级别
MySQL中查看当前隔离级别
select @@transaction_isolation;
MySQL默认隔离级别repeatable-read
2.2.1.read uncommitted
设置全局的事务隔离级别为read uncommitted
set global transaction isolation level read uncommitted;
退出重新登录再次查询事务的隔离级别,修改成功
创建一个t_user表
create table t_user(
name varchar(255)
);
我们同时打开两个DOS窗口,同时开启事务对t_user表进行操作。暂且将左边的DOS窗口执行的事务叫作T1,右边的叫作T2
开启事务T1和事务T2,事务T2对数据库表执行insert操作,插入一条数据,但并未提交,此时使用事务T1读取表中内容是可以读取到的
事务T2因某些原因进行rollback操作,表中的数据又变为空了,但是事务T1读取到了数据,我们称这些数据是错误的,事务T1脏读。
2.2.2.read committed
设置全局的事务隔离级别为read committed
set global transaction isolation level read committed;
开启事务T1和T2,T1查询t_user表中数据,结果为空,T2向表中插入一条数据,但未提交事务。T1再次查询t_user表中数据,查询结果仍未空
T2提交事务,T1再次查询t_user表,得到一条数据
2.2.3.repeatable read
设置全局的事务隔离级别为repeatable read
set global transaction isolation level repeatable read;
T1查询表,得到一条数据,T2插入3条数据,T1再次查询,得到的仍未一条数据
T2提交事务,T1再次查询,仍为一条数据
2.2.4.serializable
设置全局的事务隔离级别为serializable
set global transaction isolation level serializable;
T1查询表,得到4条数据,T2向表中插入数据,但是insert语句并未执行,因为事务T1未提交
T1提交事务,T2再次插入数据,插入成功