隔离级别定义了数据库系统中一个操做产生的影响何时以哪一种方式能够对其余并发操做可见,隔离性是事务的ACID中的一个重要属性,核心是对锁的操做。html
读锁,保证数据只能读取,不能被修改。
若是事务A对数据M加上S锁,则事务A能够读记录M但不能修改记录M,其余事务(这里用事务B)只能对记录M再加上S锁,不能加X锁,直到事务A释放了记录M上的S锁,保证了其余事务(事务B)能够读记录M,但在事务A释放M上的S锁以前不能对记录M进行任何修改。mysql
例子: MySql 5.5 证实S锁的特性。程序员
数据准备sql
CREATE TABLE `test1` (`id` bigint(1) NOT NULL DEFAULT 0 ,`name` varchar(1) CHARACTER SET utf8 COLLATEutf8_general_ci NULL DEFAULT NULL ,PRIMARY KEY (`id`)) INSERT INTO test1 VALUES(1,1),(2,2),(3,3)
此时事务B才获得响应。
数据库
说明了,只有释放了读锁,另一个事务才能加写锁,或者更新数据。segmentfault
写锁,若事务A对数据对象M加上X锁,事务A能够读记录M也能够修改记录M,其余事务(事务B)不能再对记录M加任何锁,直到事务A释放记录M上的锁,保证了其余事务(事务B)在事务A释放记录M上的锁以前不能再读取和修改记录M。并发
例子:Mysql 5.5,证实X锁的特性。性能
对数据被外界修改保持保守态度,在整个数据处理过程当中,数据处于锁定状态,依赖于数据库提供的锁机制。学习
采用宽松的加锁机制,基于数据版本记录机制,具体作法:数据库表增长一个"version"字段来实现,读取数据时,将版本号一同读出,以后更新,对版本号加1,将提交数据的版本数据与数据库对应记录的当前版本信息进行比对,若是提交的数据版本号大于数据库的数据,则予以更新,不然,被认为是过时数据。.net
事务A和事务B,同时得到相同数据,而后在各自的事务中修改数据M,事务A先提交事务,数据M假如为M+,事务B后提交事务,数据M变成了M++,最终结果变成M++,覆盖了事务A的更新。
例子:
事务A | 事务B |
---|---|
读取X=100 | 读取X=100 |
写入X=X+100 | |
事务结束X=200 | |
写入X=X+100 | |
事务结束X=300(事务A的更新丢失) |
容许事务B能够读到事务A修改而未提交的数据,可能会形成了脏读(脏读本质就是无效的数据,只有当事务A回滚,那么事务B读到的数据才为无效的,因此这里只是可能形成脏读,当事务A不回滚的时候,事务B读到的数据就不为脏数据,也就是有效的数据,脏数据会致使之后的操做都会发生错误,必定要去避免,不能凭借侥幸,事务A不能百分之百保证不回滚,因此这种隔离级别不多用于实际应用,而且它的性能也不比其余级别好多少)。
例子:
事务A | 事务B |
---|---|
写入X=X+100(x=200) | |
读取X=200(无效数据,脏读) | |
事务回滚X=100 | |
事务结束X=100 | |
事务结束 |
不可重复读是指在一个事务范围中2次或者屡次查询同一数据M返回了不一样的数据,例如:事务B读取某一数据,事务A修改了该数据M而且提交,事务B又读取该数据M(多是再次校验),在同一个事务B中,读取同一个数据M的结果集不一样,这个很蛋疼。
例子:
事务A | 事务B |
---|---|
读取X=100 | 读取X=100 |
写入X=X+100 | 读取X=100 |
事务结束,X=200 | |
读取X=200(在一个事务B中读X的值发生了变化) | |
事务结束 |
当用户读取某一个范围的数据行时,另外一个事务又在该范围内查询了新行,当用户再读取该范围的数据行时,会发现会有新的“幻影行”,例如:事务B读某一个数据M,事务A对数据M增长了一行并提交,事务B又读数据M,发生多出了一行形成的结果不一致(若是行数相同,则是不可重复读)。
例子:
事务A | 事务B |
---|---|
读取数据集M(3行) | |
在数据集M插入一行(4行) | |
事务结束 | |
读取数据M(4行) | |
事务结束 |
在事务B里,同一个数据集M,读到的条数不一致(新增,删除)。
在运用S锁和X锁对数据M加锁的时候,须要约定一些规则,例如什么时候申请S锁或者X锁,持锁时间,这些规则就是封锁协议。
其中不一样的封锁协议对应不一样的隔离级别。
一级封锁协议对应READ-UNCOMMITTED 隔离级别,本质是在事务A中修改完数据M后,马上对这个数据M加上共享锁(S锁)[当事务A继续修改数据M的时候,先释放掉S锁,再修改数据,再加上S锁],根据S锁的特性,事务B能够读到事务A修改后的数据(不管事务A是否提交,由于是共享锁,随时随地都能查到数据A修改后的结果),事务B不能去修改数据M,直到事务A提交,释放掉S锁。
缺点:
可能会形成以下后果
例子:MySql 5.5 证实一级封锁协议会形成脏读,不可重复读。
A客户端修改数据M,B客户端设置不一样的隔离级别去查看数据M,论证该级别下会发生脏读,不可重复读(至关于客户端A修改的数据已经写到表里,客户端B传不一样版本号[隔离级别],去查看数据M,所得的查询结果也不一样)。
在同一个事务B里,查询同一个数据M,竟然2次不同,形成不可重复读,其中有一次数据是无效的数据,脏读了。
假如事务A不回滚呢? 那么事务B就没形成脏读,不可重复读。
例子:MySql 5.5 证实一级封锁协议会形成更新丢失
此时事务A对数据M的修改被事务B给覆盖,形成了更新丢失。
例子:MySql 5.5 证实一级封锁协议会形成幻读
事务B第二次查询的时候,数据M多了一行,像是发生了幻觉似的,有可能这一行是无效数据(当事务A回滚)。
二级封锁协议对应READ-COMMITTED隔离级别,本质是事务A在修改数据M后马上加X锁,事务B不能修改数据M,同时不能查询到最新的数据M(避免脏读),查询到的数据M是上一个版本(Innodb MVCC快照)的。
优势:
1.避免脏读。
缺点:
可能会形成以下后果
例子:MySql 5.5 证实二级封锁协议不会形成脏读,可是会形成不可重复读(幻读,丢失更新,和上面证实方式同样,这里暂不证实了)
A客户端修改数据M,B客户端设置不一样的隔离级别去查看数据M,论证该级别下会发生不可重复读(至关于客户端A修改的数据已经写到表里,客户端B传不一样版本号[隔离级别],去查看数据M,所得的查询结果也不一样)。
在同一个事务B中,查询数据M,2次结果不一致,证实发生了不可重复读。
三级封锁协议对应REPEATABLE-READ隔离级别,本质是二级封锁协议基础上,对读到的数据M瞬间加上共享锁,直到事务结束才释放(保证了其余事务没办法修改该数据),这个级别是MySql 5.5 默认的隔离级别。
优势:
1.避免脏读。
2.避免不可重复读。
缺点:
例子:MySql 5.5 证实三级封锁协议不会形成脏读,不可重复读(形成幻读,丢失更新,和上面证实方式同样,可是要在非mysql数据库上证实,后面有解释,这里暂不证实了)
这个时候事务B才能查询到最新的数据M+。
例子:MySql 5.5 证实Mysql Innodb引擎的三级封锁协议不会形成幻读
mysql innodb的reapetable read级别是避免了幻读,mysql的实现和标准定义的RR隔离级别有差异,详情见 how-to-produce-phantom-reads。
看不到事务A新增长的一条数据,说明避免了幻读。
明明刚刚查询到没有ID为4的,如今竟然插不进去,哈哈哈哈哈。
例子:MySql 5.5 证实三级封锁协议在读到数据的瞬间加上共享锁,等事务结束才释放以及三级封锁协议会形成更新丢失
事务B的修改把事务A的修改给覆盖了,形成了更新丢失。
最强封锁协议对应Serialization隔离级别,本质是从MVCC并发控制退化到基于锁的并发控制,对事务中全部读取操做加S锁,写操做加X锁,这样能够避免脏读,不可重复读,幻读,更新丢失,开销也最大,会形成读写冲突,并发程度也最低。
例子:MySql 5.5 证实三级封锁协议不会形成幻读
证实了Serialization级别下写操做是对数据M加的是X锁。
ANSI SQL 隔离级级别
隔离性 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
READ-UNCOMMITTED | Y | Y | Y | N |
READ-COMMITTED | N | Y | Y | N |
REPEATABLE-READ | N | N | Y | N |
SERIALIZABLE | N | N | N | Y |
感谢您的耐心阅读,若是您发现文章中有一些没表述清楚的,或者是不对的地方,请给我留言,您的鼓励是做者写做最大的动力,