作者:哇塞大嘴好帥(哇塞大嘴好帅)
这种把多条语句作为一个整体进行操作被称为数据库事务,要不这个整体都运行成功,要不都运行失败。
将所有SQL作为原子工作单元(事务是最小的执行单位)执行,要么全部执行,要么全部不起作用。
做个例子:比如A账户在银行给B账户转了500万,那么B账户就接收到了500万,不可能A账户把钱转出去了B账户没有收到。也不可能A账户没有转钱B账户收到了500万
当我们事务没有提交的时候,我们数据库发生了宕机或者断电, 当我们再次进入数据库的时候,数据是事务操作前的数据。
当我们事务提交之后,我们数据库发生了宕机或者断电,当我们再次进入数据库的时候,数据是事务操作后的数据。
总结:事务前事务后数据的完整性必须保持一致。
并发访问数据库的时候,一个用户的事务不能被其他事务所干扰,一个用户的事务不能被其他事务所干扰,各并发事务之间数据库是独立的
级别 | 描述 |
---|---|
Read Uncommited(读取未提交) | 最低的隔离级别,允许读取未提交的数据变更,可能会导致脏读、幻读、不可重复读。 |
Read Committed(读取已提交) | 允许读取并发事务已提交的数据,可以组织藏独,但是幻读或不可重复读仍有可能发生。 |
Repeatable Read(可重复读) | 对同一字段的多次读取结果都是一直,除非数据是被本身事务自己修改的,可以组织藏独和不可重复读,但是幻读依然可能出现。 |
Serializable(可串行化) | 最高的隔离界别,完全服役ACID的隔离级别,所有事务依次逐个执行,这样事务之间就完全不可能产生干扰,可以防止脏读,幻读,不可重复读。 |
A事务还没提交B事务就读取到了A事务未提交的结果。
在A本事务中,对自己微操作过的数据,进行了多次读取,结果发i西安了不一致或者记录不存在的情况(破坏了一致性,只有update和delete会发生)
对自己为操作过的数据,进行了多次读取,第一次读取的时候,记录不存在,第二次读取的时候记录出现了(破坏了一致性,只有insert会导致)
一个事务一旦提交,它对数据库中数据的改变就是永久性的,不会因为断电宕机导致事务不生效
首先将数据隔离级别设置为Read Uncommited
set SESSION transaction ISOLATION LEVEL READ uncommitted
start transaction
事务A查询此test1表
select *from test1
id | userid | mount |
---|---|---|
1 | 1 | 1111 |
2 | 2 | 2222 |
3 | 3 | 3333 |
事务B修改id为1的mount修改为8888再去查询
update test1 set mount = 888 where id =1;
select *from test1
id | userid | mount |
---|---|---|
1 | 1 | 8888 |
这时候事务A在去查询
select *from test1
id | userid | mount |
---|---|---|
1 | 1 | 8888 |
这时候时间B发生了某种原因进行了回滚,事务B修改8888变成最初的值1111,这时候事务A获取的数据就是脏数据
更改事务级别
set SESSION transaction ISOLATION LEVEL READ committed;
start transaction
这时候我们事件B修改值并且没有提交
update test1 set amount = 12 where id =1;
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 12 |
事件A查询
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 1111 |
如果我们进行了提交
update test1 set amount = 12 where id =1;
commit;
SELECT *from test1
这时候事件A再去查询
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 12 |
我们发现事件A没有了脏读,但是出现了一个问题,就是不可以重复读,当B事务没有提交的时候A事务查询的结果是1111,当B事务提交的时候A事务查询的结果是12.
更改事务
set session TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION
这时候我们事务B进行修改
UPDATE test1 set amount = 12 where id =1;
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 12 |
这时候A事务查询
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 1111 |
这时候A事务没有查询到脏读,而5000数据只有在他本身事件才是5000
这时候把事务B提交
commit;
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 12 |
这时候我们A事务再去查询
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 1111 |
我们发先仍然是1111,这就说明了多次查询结果他是一致性的,也就是可重复读的。但是他会存在幻读。
这时候我们在B事务插入一个数据并且提交事务查询
insert into test1 VALUES(4,4,4444);
commit;
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 1111 |
2 | 2 | 2222 |
3 | 3 | 3333 |
4 | 4 | 4444 |
这时候事务A再去查询
SELECT *from test1
id | userid | mount |
---|---|---|
1 | 1 | 1111 |
2 | 2 | 2222 |
3 | 3 | 3333 |
这是我们就在A事务插入数据
insert into test1 VALUES(4,4,4444);
会发现它报错了主键冲突因为我们id是主键,这时候就产生了幻读。设置Serializable就好了
解决一切
事务隔离级别 | 脏读 | 不可重复读 | 换读 |
---|---|---|---|
Read-uncommitted(读未提交) | 会 | 会 | 会 |
Read-committed(读取已提交) | 不会 | 会 | 会 |
Repeatable-read(可重复读) | 不会 | 不会 | 会 |
Serializable(串行化) | 不会 | 不会 | 不会 |
这四个级别只是一个标准们个个数据库厂商,并不是完全按照着这个规则来的