mysql事务

194 阅读4分钟

前段时间在写一个商城项目的卡券兑换功能时,发生了一个小小的bug。卡券兑换的逻辑应该是先验证,验证通过后卡券兑换,然后把卡券设置成已使用。因为项目才刚上线,并且开发工期紧,所以代码就直接这么上线了。然后因为其他的原因,导致RDS高并发处理变慢,然后用户多次点击后导致卡券充值多次的情况。后来,我们项目组开发组长说加个事务来预防这种问题。之前也了解过事务,但是了解的一知半解,因为之前公司的项目也没有用到这些,这次正式碰到了,就去网上了解了一下这方面的相关知识。

下面主要介绍一下mysql事务的的隔离级别以及会造成的各种影响

mysql中,innodb所提供的事务符合ACID的要求,而事务通过事务日志中的redo log和undo log满足了原子性、一致性、持久性,事务还会通过锁机制满足隔离性,在innodb存储引擎中,有不同的隔离级别,它们有着不同的隔离性。

隔离级别:可重复读 下面我们用实战来演示一下 我们建一张user表,里面又account,password字段。

我们现在这里面插入两条数据,如图。下面我们打开两个命令行界面,来演示两个事务之间的相互影响。

我们在两个命令行中开启两个事务,如图。 接下来们在两个事务中分别查询出数据:

此时两个表的数据是一样的。这个时候我们再第一个命令行中更新这个表的数据

这个时候第一个命令行界面中再运行发现数据已变更。这个时候我们再另外一个界面中查询这个数据,看id=4的这条数据是否发生变更。

很明显数据并没有变更。这个时候我们把第一个事务提交后,再来看看第二个事务查询出来的结果。

数据也没有发生变化!

下面介绍一下 这种隔离级别下出现的一种幻读的情况,接下来我们继续开启两个命令行界面,开启两个事务。

我们在第一个事务中插入一条数据

这个时候在第二个事务中是看不到的。 我们提交第一个事务,这个时候在第二个事务中看到的数据还是原来的两条

接下来我们在第二个事务中更新数据

这个时候再去查找的时候多了一条数据,这就是在“可重读”情况下出现的幻读现象。

隔离级别:串行化 我们再开两个命令行界面,把隔离界别改成串行化,表还是用之前的那张表

我们在第一个命令行界面中执行select语句,在第二个命令行界面中执行insert语句

这个时候我们发现第二个命令行界面中的inset语句被阻塞了,我们等一会在第一个命令行界面中执行commit操作

insert语句超时了,再一次证明了这个insert语句被阻塞了,假如我们早一点提交第一个命令行的事务,那么第二个界面的insert语句会被执行。 同样反过来我们在第一个界面先执行insert语句,那么第二个界面的select语句也会被阻塞,知道第一个命令行界面事务被提交。

当在事务2中执行查询语句时,查询被阻塞,此时事务1被提交,当事务1被提交后的一瞬间,事务2中的语句已经查询出结果,从返回结果可以看出,这个查询语句被阻塞了31秒左右的时间,当事务1中的写锁释放时,事务2才读出了数据。从上述实验上来看,当事务处于串行化隔离级别时,是不可能出现幻读的情况的,因为如果另一个事务中对表添加了写锁,那么在当前事务中是无法读到数据的,必须等到另一个事务提交,另一个事务释放了对表的写锁,当前事务才能进行申请读锁,使用串行化的隔离级别不会出现幻读的情况,但是,聪明如你一定发现了,当事务的隔离级别设置为串行化时,数据库失去了并发的能力,所以,我们很少将隔离级别设置为串行化,因为这种隔离性过于严格了。

还有两种隔离是“读未提交”和“读已提交”,这两个隔离比较简单。顾名思义,读未提交就是在一个事务中操作了数据,事务不用提交就在另一个事务中读到;“读已提交”在一个事务中操作了数据,提交后就能在另一个事务中读到。