一、缘由
众所周知MySQL从5.5.8开始,Innodb就是默认的存储引擎,Innodb最大的特点是:支持事务、支持行级锁。
既然支持事务,那么就会有处理并发事务带来的问题:更新丢失、脏读、不可重复读、幻读;相应的为了解决这四个问题,
就产生了事务隔离级别:未提交读(Read uncommitted),已提交读(Read committed),可重复读(Repeatable read),可序列化(Serializable)。
之前在看事务隔离级别的时候,基本是一知半解。再次碰到,还是浆糊,所以这里打算彻底搞明白、清楚,也好后续的学习。
二、解决办法:
1、概念说明:
更新丢失:最后的更新覆盖了其他事务之前的更新,而事务之间并不知道,发生更新丢失。更新丢失,可以完全避免,应用对访问的数据加锁即可。
脏读:(针对未提交的数据)一个事务在更新一条记录,未提交前,第二个事务读到了第一个事务更新后的记录,那么第二个事务就读到了脏数据,会产生对第一个未提交
数据的依赖。一旦第一个事务回滚,那么第二个事务读到的数据,将是错误的脏数据。
不可重复读:(读取数据本身的对比)一个事务在读取某些数据后的一段时间后,再次读取这个数据,发现其读取出来的数据内容已经发生了改变,就是不可重复读。
幻读:(读取结果集条数的对比)一个事务按相同的查询条件查询之前检索过的数据,确发现检索出来的结果集条数变多或者减少(由其他事务插入、删除的),类似产生幻觉。
2、不可重复读和幻读的区别:
不可重复读
重点是修改:同样的条件下,你读取过的数据,再次读取发现值不一样了。
例子:(只是为了说明区别,没有事务隔离级别约束)
事务1:Jack读取自己的工资为1000,事务并没有结束:
select salary from employee empId ="Jack";
事务2:财务人员修改了Jack的工资为2000,并提交事务(事务1不知道):
update employee set salary = 2000;
commit;
在事务1中,再次读取Jack的工资时,工资变为2000
select salary from employee empId ="Jack";
在一个事务中前后两次读取的结果值并不一致,导致了不可重读读
幻读
重点在于新增或者删除(数据条数的变化):同样条件下,第一次和第二次读出来的记录条数不一样。
例子:
目前工资为1000的员工有10人。
事务1:读取所有工资为1000的员工,事务进行中
select count(*) from employee where salary = 1000;
共读取到10条记录。
事务2:同时插入一条新的员工记录,工资也为1000
insert into employee(empId, salary) values('Tom', 1000);
commit;
事务1:再次读取所有工资为1000的员工
select count(*) from employee where salary = 1000;
共读取到了11条记录,这样就像产生了幻读。
三、事务隔离级别:
并发处理带来的问题中,更新丢失可以完全避免,由应用对数据加锁即可。脏读、不可重读度、幻读,其实都是数据库的一致性问题,必须由一定的事务隔离机制来解决。
其中一种方法是:不用加锁,通过一定的机制生成一个数据请求时间点的一致性快照,并用这个快照来提供一个界别的一致性读取。从用户的角度看,好像是数据库提偶拱了
统一数据的多个版本。这种技术叫做:数据库多版本并发控制,MVCC 多版本数据库。
事务隔离的本质是使事务在一定程度上串行化执行,显然和并发机制是矛盾的。数据库的事务隔离越严格,并发负作用越小,代价越高(影响并发访问了呗)。
为了解决隔离和并大的矛盾,IOS SQL92规定了4个隔离级别。(隔离==串行)
MySQL 默认的级别是:Repeatable read 可重复读。