**
首先,事务是什么?
**
是一系列或者一组sql操作看作一个整体,作为一个事务,这一组sql要么都执行,要么都不执行。当所有sql都能执行成功的时候,commit当前事务,当其中一个sql出现问题时,rollback回滚所有操作到事务开始前的状态。
事务要满足ACID
A:原子性,要么都成功执行,要么都不执行。
C:一致性,事务前后不能破坏数据库的结构。
I:持久性,事务成功,则更改的数据就是永久有效的。
D:隔离性,事务之间应该是互不影响的。
事务在并发执行的时候会出现很多问题,通俗的说就是有俩或者更多事务在操作同一些数据,就会造成数据混乱。这些数据混乱用专业术语表示就是脏读、幻读、不可重复读。
经常有人分不清幻读和不可重复读,“我刚第一次查这个性别还是男,第二次查怎么就是女了,脏读幻读不能重复读了”、“刚才查这条数据还没有,现在插入怎么提示有数据了,脏读幻读不能重复读了”,,,,,,,,
其实这仨字面意思感觉都能解释所有现象,但是对不同出现的问题情况是有不同的解释的。我说一下不同的地方在哪:
脏读:侧重点在于读取到别的事务没提交的数据。(这个大家还是比较清晰的)
幻读:侧重点在于读-写。先读了一个范围的数据,没有要准备插入的那条数据,但是在执行插入的时候,却提示已经存在了!
先读–没有–插入–失败,这就是幻读。
不可重复读:侧重的是读-读。先读某条数据性别是男,第二次读的时候变成女了。两次读的数据不一致,读–男--读–女。
这样解释我觉得还是很好理解的。那么mysql是如何解决这种问题的呢?或者我们在使用中应该如何避免出现这些问题呢?
mysql自己有四种隔离级别,来规定每个事务之间的隔离程度。
未提交读:查询的时候可以查询到别的事务还没提交的数据,会出现脏读幻读不可重读,毫无安全性可言,一般不会选择使用这种隔离级别,除非你的业务只有读操作!
提交读:字面意思,通过MVCC每次都读取最新的已提交的数据版本,只能读到事务提交后的数据。解决了脏读!
可重复读(默认):通过MVCC第一次读时候生成快照,后面再读就根据快照里的读取指定版本的数据,这样每次读取到的数据都是一样的(不保证最新,只保证一致)。
我们可以自己手动解决不可重复读,select时候加上读锁(lock in share mode共享锁),加上之后别的事务就不能修改,你不提交事务不释放这个读锁,这个数据你每次读都一样。(如果你在当前事务内还想更改这条数据,那你第一次查的时候就自己加上排他锁)。
其实在这个隔离级别下,innodb推出了next-key锁帮助解决幻读。在范围查的时候(BETWEEN、>、<、>=、<=等操作符进行查询),for update或者share in lock mode会加上next-key锁(record key+gap key),查询加上锁,再插入就不会出现幻读了。
以上是事务并行时会出现的问题和并行时的解决办法。那么当事务串行时乱七八糟读都不存在了!但是事务一个一个执行,可想而知你的程序会有多慢了!
串行化:这个隔离级别基本没人用!
补充一下,MVCC(多版本并发控制)
MVCC是什么?为了解决什么问题?ReadView是什么?看名称可以看出是事务并发时候用的,多版本指的是数据最后是由哪个事务操作的,版本号也可以理解成事务id。
ReadView快照记录的是你要查询的数据的版本号,根据这个版本号去查询指定版本的数据。
我的理解是MVCC+隔离级别 = 快照。快照是每次select查询时的数据版本号,增加读取性能。
MVCC给所有数据增加了版本号,隔离级别决定你可以读取哪个版本的数据。
未提交读会查询到其它事务没提交的数据,就是不区分版本,未提交的版本也会查到,所以没有必要生成快照。
MVCC+提交读 = 每次查都返回最新事务最后提交的版本快照,每次读取都是一个新的快照。(所以会出现幻读)
MVCC+重复读=只在第一次查的时候生成快照(在此事务之前提交的数据版本),后续查只返回快照里的版本数据。 不影响其它事务修改,其它事务修改了数据,当前事务也是返回快照里的版本数据。单纯保证每次读都是一样的数据。
以上mysql自己的隔离级别就可以解决大部分的使用情况,如果你不放心你要操作的数据,就自己手动加上锁!!!
但是一定要适度合理,加锁虽好,但是会影响性能,纯查的话就别瞎加锁!
解释的清晰吗?大家可以看完可以理解了吗?可以的话在下面扣个1!没有解释清楚的话大家可以一起讨论一下!然后我再补充!