使用MySql也有一段时间了,但是很多MySql相关或者说是数据库相关的知识还是一知半解,最近在学hibernate这个框架时碰到挺多和数据库相关的知识盲区,所以下面根据自己对MySql系统相关知识消化的结果进行简单总结,这是总结关于事务隔离级别的博客 。
一、事务的性质:原子性,隔离性和一致性概念。
1、原子性:数据库的操作是以一组mysql指令进行的,数据库要有这样一种保证:每次事务操作的多条sql语句要么全部执行成功,要不全部执行失败,不可以某些sql语句执行成功而另外一些执行成功。原子性是针对程序的事务而言的,它体现了一个事务,就是数据库的最小组成部分。
2、隔离性:在并发操作中,数据应该是被隔离开来的,也就是说,保证在某种并发操作中数据只能有一条线程访问,和线程当中的同步类似,不同的隔离级别会对应不同的线程并发操作,这个是本篇博客的重点,后面具体总结。
3、一致性:数据库的所有数据均是有意义的状态,也就是说数据应该前后一致。这里可以用转账例子理解:A转账100块给B,数据的一致性要求A-100而B增加100,两者同时发生,数据才是有意义的,也就是数据库才能保证一致性。总的来说,无论是原子性还是隔离性,其实都是为了保证一致性。
二、隔离级别(mysql)
MySql主要有四个隔离级别:读未提交,读已提交,重复读以及串行化,四个级别依次从低到高。
1、读未提交。先上个简单的丑图:
读未提交的意思就是:读操作可以读未提交事务的sql语句对数据库的操作。这种隔离机制无法保证数据的原子性,读了没有提交成功的数据便是脏读。读未提交本身就是不能保证原子性,所以一般不会用。
2、读已提交。先看丑图
读已提交的意思是,数据读取必须要在处理该数据的事务被回滚或者提交完成后才进行,这样可以保证同一数据的修改和读取不同时进行,从而保证数据的一致性。当然,它解决了脏读的问题,但是却没有解决另外一个问题,即不可重复读的带来的问题,先看下面丑图:
不可重复读使得在线程一在两个sql指令(假设他们是同一个指令)读取相同的数据前后读取出现了不一样的结果,因为在sql指令一和二之间的时间间隔里面另外一条线程对数据进行更新操作,当然,这也可以理解为数据及时更新所造成的。但是,读者可能会疑惑,这样到底会造成什么后果?举个例子,小明去商场买东西,在结账之前他首先查询了下自己的余额够不够用,发现余额是充足的,然后愉快地去结账了,但是在这个短短时间内,他老婆利用了他的银行卡进行了转账操作,小明买单时发现扣款失败,余额不足,这便会给小明带来困惑:老子明明卡里面的钱还足够的,为毛扣费不成功,当然,数据前后读取不一致还会导致很多其他问题,具体不细说。
归根到底,不可重复读的问题是由于现场一在进行读操作时没有锁定数据,造成了前后读取数据的不一致。
3、可重复读,解决上面不可重复读问题。其实他就是在不可重复读的基础上,当数据在前后两个读取操作时都相应地加上对应数据的锁,使得两次读取期间不能有其他线程对数据进行操作,保证读的结果是重复的(也即是保证读数据的一致性)。具体图就不画了,理解起来应该不难。
可重复读解决了读数据一致性的问题,但是却不能解决另外一个问题:幻读,下面是幻读的简单示意图:
总的来说,幻读是由于前面三种隔离级别都只是隔离了数据行,当涉及会影响到整个表的操作(插入,删除等会意向整个表的个数)时,便无法保证所有数据同步的目的,而幻读便是这样产生的。
4、串行化。上面也说了,前面三种级别,都只是隔离了要操作的相应数据行,无法杜绝幻读的现象,而串行化是mysql隔离最高级别,它锁定了整个表,是的所有线程的操作都是串行进行的,可彻底杜绝幻读的情况。当然,锁定整个表,性能会下降得非常厉害,所以实际中应该谨慎的选择。
总的来说,前三中隔离级别隔离的是行数据,他们的不同只是对应读、写之间的锁定关系不同而已,读未提交,事务进行写操作时并没有锁定禁止读的动作;读已提交在进行事务在进行写操作时锁定了行数据,禁止在写期间读数据;而可重复读则是在读期间禁止数据的写;串行化即锁定整个表的读写。