一. 什么是数据库隔离级别?
ANSI(美国国家标准学会:AMERICAN NATIONAL STANDARDS INSTITUTE)在多个事务并发的时候能够正确的处理数据所定义的规范。事务隔离级别越高,数据的正确性和数据库的完整性也就越高,但是数据并发处理的效率就会有所降低。
二.数据隔离级别及其可能导致的问题?
Ⅰ.数据隔离级别
①.Read UnCommitted:允许当前事务读取其它事务没有提交的数据,最低的隔离级别
②.Read Committed:允许当前事务读取其它事务提交的数据
③.Repeatable Read:在同一事务内两次相同查询结果集保持一致
④.Serializable:强制所有事务串行化,最高的隔离级别
Mysql的锁定机制是行锁->间隙锁->表锁,当无法唯一的锁定单个记录的时候,就需要执行范围锁定的间隙锁,最后才是表锁。上面的事务级别维持下表中对应关系的同时,不同隔离级别采用不同的锁定机制,甚至同一隔离级别随着不同的查询语句锁定机制也会有所变化。后面讲间隙锁时会有所提到
Ⅱ.不同的事务隔离级别可能导致的问题:ANSI SQL92 P68
①.脏读:A事务读取了B事务没有提交的数据,B事务回滚,则导致A事务读取的数据无效
②.不可重复读:A事务读取数据,B事务修改了A事务读取的数据,A事务再次读取相同数据,则导致A事务在同一事务内同样的查询数据结果不一致,如行内数据变化或行消失
③.幻读:A事务根据条件读取数据,B事务插入了其它数据,A事务再次读取相同数据,则导致A事务在同一事务内同样的查询数据结果不一致,如多了一行
Ⅲ.它们之间的联系?:ANSI SQL92 P69
如下是SQL92给出的标准,不同的隔离级别可能导致的问题也不一样,这里就不再详细介绍每一种问题了,仔细想一想其实就能明白。
三. 行锁?间隙锁?
CREATE TABLE `lock_test`.`person` (
`id` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC)); //mysql自动为主键创建的唯一索引
update person set name="李子" where id>0 and id<5;
//间隙锁锁定范围,其它事务无法执行0~5范围内的更改操作
update person set name="李子" where name="李";
//无法使用索引,行锁升级为表锁
①.行锁:数据库默认存储引擎InnoDB(show variables like ‘%engine%’;命令查看)的默认隔离级别Repeatable Read(select @@tx_isolation;命令查看)的默认锁定方式,之所以说明存储引擎,是因为Mysql中每一种数据引擎的锁定方式以及实现都是相互独立的。
②.间隙锁:间隙锁是实现行锁的一种方式,通过指向数据记录的第一个索引键之前和最后一个索引键之后的空域空间标记锁定信息实现的,你可以认为就是范围内的行锁定。范围锁定会造成无法插入锁定键值范围内的任何数据,InnoDB认为这样就有效避免了幻读的出现。注意这里提到了索引,要是查询无法使用索引的时候,InnoDB就会放弃使用行级锁定而转向表级锁定。
题外话:在不知道数据库隔离级别的时候,只知道Spring的书屋隔离级别配置有以下默认值,Spring支持多种数据库,为什么每一种数据库都最多有以下几种数据隔离级别呢?现在才终于明白,它们都是按照SQL标准来实现的。事务隔离级别是在数据库层次实现的,而事务传播行为是在Spring层次实现的。
<xsd:enumeration value="DEFAULT"/>
<xsd:enumeration value="READ_UNCOMMITTED"/>
<xsd:enumeration value="READ_COMMITTED"/>
<xsd:enumeration value="REPEATABLE_READ"/>
<xsd:enumeration value="SERIALIZABLE"/>
<!--通知,不同的方法对应不同的事务隔离级别和事务传播行为-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" isolation="REPEATABLE_READ"
propagation="REQUIRED" read-only="false" />
<tx:method name="update*" isolation="REPEATABLE_READ"
propagation="REQUIRED" read-only="false" />
<tx:method name="delete*" isolation="REPEATABLE_READ"
propagation="REQUIRED" read-only="false" />
<tx:method name="find*" isolation="REPEATABLE_READ"
propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
参考资料:
《Mysql性能调优与架构设计》 简朝阳著