事务其实不难理解。
我这里做个不恰当的比喻。
张山要完成 “喝水‘’ 这一件事。我们初步设定需要3步来完成:
-
张山找水瓢。
-
张山用水瓢舀了一瓢水。
-
张山把水送到嘴里。
如果张山没有找到水瓢,那么这件事完不成。如果在第二步的时候不小心把水洒了,也完不成这件事。只有这3步都顺利完成的情况下,才能说张山喝水这件事完成了 。
回归正题:
什么是事务呢?
事务就是应用程序中一系列的严密操作,所有操作必须成功完成。否则每个操作中所作的更改都将被撤销,,也就是要么全部成功,要么全部失败回滚。也就是所说的原子性。
要想深入了解事务,那么我们必须知道事务的特性:
(ACID)缩写:
-
原子性(Atomicity):要么同时成功要么全部失败回滚。
-
一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态,比如:转账业务,张山给李四转100元,转账前后张山和李四的总钱数必须一致。
-
隔离性(Isolation):并发执行的事务之间不能相互干扰。
-
持久性(Durability):当一个事务一旦提交,对数据库的数据改变是永久性的。
MYSQL中sql标准定义了四种隔离级别:
作用是用来规避下面事件的发生:
脏读,不可重复读和幻读。 -
读取未提交的内容(Read Uncommitted)
-
读取已提交的内容(Read Committed)
-
可重复读(Repeatable Read)
-
串行化(Serializable)
在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:
接下来我们说说四种隔离级别:
Read Uncommitted :
一个事务读取了另外一个事务还没有提交的内容。
比如:张山给李四转账100,张山和李四没人账户上都是1000元。
当张山给李四转账100,但是还没有提交这个事务。此时李四去查看自己的账户确实多了100元。这时李四读取到了张山没有提交的内容。与此同时,张山做了个回滚操作。那么李四过段时间再去查看账户时,问题出现了,张山明明转了100给我,但是为啥突然没有了呢。这就是脏读问题。
接下来实际数据库演示一下:
这里我有一张account表:
张山和李四账户金额都是1000元。接下来我开俩个窗口分别模拟张山和李四对数据库表的操作:
mysql中默认的隔离级别是Repeatable Read(可重复读)
前提是都将数据库的隔离级别调整到Read Uncommitted(读未提交的)
接下来开启事务,跟新数据(张山给李四转100元),但不提交
左边模拟张山给李四转账100,右边模拟李四查询账户金额确实多了100.
接下来张山要做一个回滚操作。
这时,李四再去查询自己的账户,哎呀,咋回事呢,明明已经转过来了,这就是所谓的读未提交的内容,导致的问题就是脏读的发生。
Read committed:
读已提交的内容:
例如:张山的妻子是李四,张山去住酒店,在刷卡的同时,李四正好从张山卡中转出了700元,但是还没提交事务,poss机此时读取卡中确实是1000元,能够支付房费,正当此时,李四将事务提交了,poss机准备输入密码刷卡时,在读取卡中已经只有300元了。这就是不可重复读问题。事务A在俩次查询的过程中查询的数据不一致。因为俩次查询的过程中事务B更新了数据。
1.設置隔离级别为读已提交的内容:
A事务(张山)开启事务:
start transaction;
B事务(李四)跟新数据(从卡里转出700元),但没有提交事务:
update account set money=‘300’ where username=‘zhangshan’;
A事务(张山)此时去查询账户金额,确实是1000元没变:
B(李四)事务进行事务的提交:
commit;
A事务(张山)又去查询账户发现金额变成了300元。
这就是一个读已提交的内容。会有不可重复读,幻读问题发生。
Repeatable read:
可重复读级别:mysql默认的数据库隔离级别。可以防止脏读,不可重复读问题。但是可能会出现幻读现象。
在一个事务操作的期间,不受另外一个事务的影响。不与其他事务串行化。
怎么理解呢?其实就是一个A在开启事务同时,不管B事务做了什么操作,在A事务没有提交结束的时候,都不会影响到A事务的动作。接下来我们实际演示一遍:
1.设置隔离级别:
set session transaction isolation level repeatable read;
张山开启事务,此时数据还是初始1000元:
start transaction;
李四修改了数据,但是未提交:即转走了张山700元:
update account set money='300' where username='zhangshan';
张山去查询自己的账户,还是1000元没有变化:
李四提交了事务:
commit;
张山再去查询自己的账户:
发现还是1000元。这个就避免了不可重复读问题。
这是李四去插入一条数据,并提交:
此时张山去查询时:情况为:
再次读取数据,发现数据依然未发生变化,虽然可以重复读了,但是却发现读的不是最新数据,这就是所谓的“幻读”。
A:张山提交本次事务,再次读取数据,发现读取正常了。
最后这个例子不易演示,因为现在mysql多版本控制。让repeatalbe read 也不会出现幻读。可以放心使用,因为它加入了读写锁进行控制。
Serializable:
1.设置隔离界别:
2.A开启事务,并且查询数据,没有提交:
此时,B开启事务,进行插入操作:
开始会进行等待,然后超时 ,Lock wait timeout 锁等待超时。
A提交事务:
commit;
B事务 插入成功:
serializable完全锁定字段,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止。是完整的隔离级别,会锁定对应的数据表格,因而会有效率的问题。
自此数据库的事务应该大家很清楚了把。