这个博客记录的是本身学习过程和理解,有些细节可能写的不见得准确,想了解概念,谷歌能够搜出不少不错的博文去学习.我把它记下来,则是为了记录学习的过程,由于有些知识是须要本身创造实践的机会才能真正理解其中的细节.mysql
由ANSI/ISO定义的SQL-92标准定义的四种隔离级别sql
读取未提交内容/脏读(READ UNCOMMITTED/Dirty Read):无效数据的读出 . SELECT语句是在无锁的模式下运行,一个事务能够读到另一个事务的未提交的数据。数据库
提交读(READ COMMITTED):一个事务不能读到另一个事务未提交的数据。在这种隔离级别下,对于更新语句来讲(SELECT FOR UPDATE,UPDATE和DELETE),Innodb只会对where条件中索引能覆盖到的行进行上锁
,不会上gap锁和next key lock
,因此就避免不了不可重复读和幻读
。session
可重复读(REPEATABLE READ):是Innodb默认的隔离级别。它使用了gap locks
或者next-key locks
来避免不可重复读的现象,可是仍然避免不了幻读
。若是SELECT FOR UPDATE,UPDATE以及DELETE的where条件能用到惟一索引,INNODB只会对惟一索引能覆盖到的行上行锁;不然就上gap locks或者Next-key locks。并发
串行化(SERIALIZABLE):这是最高的隔离级别,避免了幻读。若是auto commits不启用,则innodb把每一个select转换成select ... lock in share mode
(使用共享锁);若是启用,则每一个SELECT语句也是事务。性能
隔离解别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | Y | Y | Y |
Read Committed | N | Y | Y |
Repeatable(default) | N | N | Y |
Serializable | N | N | N |
一部分概括可能并不许确,望斧正学习
这里先介绍几个概念优化
行锁的一种,共享锁又称读锁,在对任何数据进行读操做以前要申请并得到共享锁 ,若事务1对数据对象A加共享锁,则事务1能够读A但不能修改A
(其余事务只能再对1加共享锁,而不能加排他锁,),其余事务只能再对A加共享锁,而不能加排他锁,直到事务1释放A上的共享锁。这保证了其余事务能够读A,但在事务1释放A上的S锁以前不能对A作任何修改。 共享锁的英文名是Share Locks,其实若是叫作读锁就好理解多了(毕竟字面上'共享'这个词好像没有这东西就不能改的意思嘛) 在查询语句后面增长LOCK IN SHARE MODE
,Mysql会对查询结果中的每行都加共享锁翻译
session1 | session2 |
---|---|
begin tran<br>select * from table1 holdlock where B='b2'<br>waitfor delay '00:00:30'<br>commit tran | begin tran<br>select A,C from table1 where B='b2'<br>update table1 set A='aa' where B='b2'<br>commit tran |
行锁的一种,排他锁又称写锁,互斥锁,独占锁(名字可真多,但都是一个意思嘛)。在进行写操做以前要申请并得到排他锁,若事务1对数据对象A加上排他锁,事务1能够读A也能够修改A,其余事务不能再对A加任何锁,直到1释放A上的锁。这保证了其余事务在1释放A上的锁以前不能再读取和修改A。 排他锁英文名字是Exclusive Locks(我英语很差,不太明白为啥叫X锁不叫E锁),这个字面好理解,排除其余的事务,只能本身读写,虽然叫写锁,实际上事务1 rw的权限都是有的 在查询语句后面增长FOR UPDATE
,Mysql会对查询结果中的每行都加排他锁 对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X)code
两段锁协议是指每一个事务的执行能够分为两个阶段:生长阶段(加锁阶段)和衰退阶段(解锁阶段)。
在该阶段能够进行加锁操做。在对任何数据进行读操做以前要申请并得到共享锁,在进行写操做以前要申请并得到排他锁。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
当事务释放了一个封锁之后,事务进入解锁阶段,在该阶段只能进行解锁操做不能再进行加锁操做。
两段封锁法能够这样来实现:事务开始后就处于加锁阶段,一直到执行ROLLBACK和COMMIT以前都是加锁阶段
。ROLLBACK和COMMIT使事务进入解锁阶段,即在ROLLBACK和COMMIT模块中DBMS释放全部封锁。
为了方便检测表级锁和行级锁之间的冲突,减小加锁时封锁检查的工做量,支持多粒度的锁定,基于两种基本的锁类型,能够派生出以下两种意向锁: 概念有点抽象,简单的一下为何要意向锁: 意向锁是为了不逐行检查行是否上锁,解决行锁和表锁之间的矛盾 好比事务1要在一个表上加共享锁,若是表中的一行已被事务2加了排他锁,那么该锁的申请也应被阻塞。若是表中的数据不少,逐行检查锁标志的开销将很大,系统的性能将会受到影响。 若是表中记录1亿,事务1把其中有几条记录上了行锁了,这时事务2须要给这个表加表级锁,若是没有意向锁的话,那就要去表中查找这一亿条记录是否上锁了。若是存在乎向锁,那么假如事务1在更新一条记录以前,先加意向锁,再加独占锁
,事务2先检查该表上是否存在乎向锁
,存在的意向锁是否与本身准备加的锁冲突,若是有冲突,则等待直到事务1释放,而无须逐条记录去检测。事务2更新表时,其实无须知道到底哪一行被锁了,它只要知道反正有一行被锁了就好了。 意向锁是InnoDB自动加的,不须要用户干预
。
事务打算给数据行加共享锁,事务在给一个数据行加共享锁前必须先给该表加上IS锁。
事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先给该表加上IX锁。 意向锁主要目的是为了在一个事务中揭示下一行将被请求的锁类型。若是没意向锁,须要表锁时,要一行行检查某个表是否发生了行锁,进而判断可否表锁成功,若是有了意向锁,不用一个个去检查,直接从表的层次就可判断。
表级锁
,但表示的是事务正在操做某一行记录
。冲突检测是在加行锁时发生
。对整个表加锁,影响表的全部记录。一般用在DDL语句中,如ALTER、DROP、TRUNCATE等。
对一行记录加锁(准确的说应该是对索引加锁而非记录自己),只影响一条记录。一般用在DML语句中,如INSERT、UPDATE、DELETE等。
锁住某一段范围中的记录 ,在索引记录之间的间隙中加锁,或者是在某一条索引记录以前或者以后加锁,并不包括该索引记录自己,去解决幻读
和得到基于语句模式下复制的一致性
是Record lock和gap lock的结合,即除了锁住记录自己,还要再锁住索引之间的间隙 行锁和GAP锁结合造成的的Next-Key锁共同解决了RR级别在写数据时的幻读
问题
正如其名,它指的是对数据被外界(包括本系统当前的其余事务,以及来自外部系统的事务处理)修改持保守态度,所以,在整个数据处理过程当中,将数据处于锁定状态。悲观锁的实现,每每依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,不然,即便在本系统中实现了加锁机制,也没法保证外部系统不会修改数据)。
在悲观锁的状况下,为了保证事务的隔离性,就须要一致性锁定读。读取数据时给加锁,其它事务没法修改这些数据。修改删除数据时也要加锁,其它事务没法读取这些数据
相对悲观锁而言,乐观锁机制采起了更加宽松的加锁机制。悲观锁大多数状况下依靠数据库的锁机制实现,以保证操做最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销每每没法承受。
而乐观锁机制在必定程度上解决了这个问题。乐观锁,大可能是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增长一个版本标识,在基于数据库表的版本解决方案中,通常是经过为数据库表增长一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,以后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,若是提交的数据版本号大于数据库表当前版本号,则予以更新,不然认为是过时数据。
要说明的是,多版本并发控制(MVCC)的实现没有固定的规范,每一个数据库都会有不一样的实现方式
这个事务隔离级别任何操做都不会加锁
在RC级别中,数据的读取都是不加锁的,可是数据的写入、修改和删除是须要加锁的,这样能够避免脏读问题 若是where的条件是没有索引的,存储引擎层面就会将全部记录
加锁后返回,再由MySQL Server层进行过滤。但在实际使用过程中,MySQL作了一些改进,在MySQL Server过滤条件,发现不知足后,会调用unlock_row方法,把不知足条件的记录释放锁 (违背了二段锁协议的约束)。 虽然mysql作了优化,咱们仍是尽可能使用索引避免锁住不相关的记录
举个栗子:若是事务1和事务2都是插入了同一条记录,由于行锁的存在,有一个一定抛异常.虽然抛了异常,但不影响失败的那个事务获得了成功事务释放掉的排它锁.若是这时不作回滚,这时忽然冒出个事务3 读取记录,这就会致使死锁现象 因此代码里若是是由于主键冲突抛出了异常,是要作回滚来释放独占锁的 引用别人的表格加以说明 | session1 | session2 | session3 | | :-- | :-- | :-- | | Session_1得到for update的共享锁:<br>mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update;| 因为记录不存在,session_2也能够得到for update的共享锁:<br>mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update; | | | Session_1能够成功插入记录:<br> mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom'); | | | | | Session_2插入申请等待得到锁:<br>mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom'); | | | Session_1成功提交: <br>mysql> commit; | | | | | Session_2得到锁,发现插入记录主键重,这个时候抛出了异常,可是并无释放共享锁: <br>mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom'); | | | | | Session_3申请得到共享锁,由于session_2已经锁定该记录,因此session_3须要等待: <br>mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update; |
这是MySQL中InnoDB默认的隔离级别。 在同一个事务内的查询都是事务开始时刻一致的 对选定对象的读锁和写锁一直保持到事务结束,但不要求“范围锁”,所以可能会发生幻读 带惟一搜索条件使用惟一索引的SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE 和DELETE语句只锁定找到的索引记录,而不锁定记录前的间隙 用其它搜索条件,这些操做采用next-key锁定,用next-key锁定或者间隙锁定锁住搜索的索引范围,而且阻止其它用户的新插入。 select ... for update 是行级锁,也是悲观锁
每次读都须要得到表级共享锁,写加排他锁,读写互斥。使用的悲观锁
简单的select操做
,没有lock in share mode或for update,快照读不会加任何的锁,并且因为MySQL的一致性非锁定读的机制存在,任何快照读也不会被阻塞
。可是若是事务的隔离级别是SERIALIZABLE的话,那么快照读也会被加上共享的next-key锁
insert,update,delete,select..in share mode和select..for update,当前读会在全部扫描到的索引记录上加锁
,无论它后面的where条件到底有没有命中对应的行记录。当前读可能会引发死锁。
事务1:更新一条数据 ------------->事务2:读取事务1更新的记录 事务1:调用commit进行提交
名字有点抽象,很差理解,不但是不能仍是不要的意思?为何不可?我就补充一下主谓宾解释一下吧 [同一条记录]不可重[被同一个事务]读[,由于两次读取的结果可能不同] 举个栗子:
事务1:查询一条记录 -------------->事务2:更新事务1查询的记录 -------------->事务2:调用commit进行提交 事务1:再次查询上次的记录
由于中间事务2作了改动,致使事务1查两次的结果是不同的 不可重复读重点在于update和delete
[同一条记录]可重[被同一个事务]读[,由于不管多少次结果都是同样滴]
名字有点抽象,翻译过来叫作"幻想读"(有点像游戏技能名字) 在事务执行过程当中,当两个彻底相同的查询语句执行获得不一样的结果集。这种现象称为幻读 举个栗子:小时候看 《哈克贝利·费恩历险记》记得有个小桥段,萨莱姨妈数叉子个数时汤姆和哈克趁她不注意放回或拿走叉子,让萨莱姨妈每次数出来叉子的个数都不同.幻读基本就是这个意思,仍是用文字表示一下这现象是怎么发生的: 事务1:查询表中全部记录 -------------->事务2:插入一条记录 -------------->事务2:调用commit进行提交 事务1:再次查询表中全部记录 因为事务2(汤姆和哈克)偷摸加了一条记录(叉子),结果致使事务1(萨莱姨妈)数出的个数和上次的比多了一个,结果就让她直接懵了 幻读的重点在于insert