阅读本文须要对MySQL事务,隔离级别有些概念上的了解,关于这些概念你还不够了解的话,请你当即中止阅读!接下来的内容会让你头疼欲裂安全
在处理并发读或者写时,能够经过实现一个由两种类型的锁组成的锁系统来解决并发问题。这两种类型的锁一般被称为共享锁(shared lock)和排他锁(exclusive lock),也叫读锁(read lock)和写锁(write lock)服务器
这里先不讨论锁的具体实现,描述一下锁的概念以下:读锁是共享的,或者说是相互不阻塞的。多个客户在同一时刻能够同时读取同一个资源,而互不干扰。写锁则是排他的,也就是说一个写锁会阻塞其余的写锁和读锁,这是出于安全策略的考虑,只有这样,才能确保在给定的时间里,只有一个用户能执行写入,并防止其余用户读取正在写入的同一资源并发
简而言之,容许同时读,可是不容许边读边写,也不容许同时写性能
表锁3d
表锁是MySQL中最基本的锁策略,而且是开销最小的策略。 它会锁定整张表。一个用户在对表进行写操做(插入、删除、更新等)前,须要先得到写锁,这会阻塞其余用户对该表的全部读写操做。 只有没有写锁时,其余读取的用户才能得到读锁,读锁之间是不相互阻塞的cdn
在特定的场景中,表锁也可能有良好的性能。例如,READ LOCAL表锁支持某些类型的并发写操做blog
尽管存储引擎能够管理本身的锁,MySQL自己仍是会使用各类有效的表锁来实现不一样的目的。例如,服务器会为诸如ALTER TABLE之类的语句使用表锁,而忽略存储引擎的锁机制事务
行锁资源
行级锁能够最大程度地支持并发处理(同时也带来了最大的锁开销)。众所周知,在InnoDB和XtraDB,以及其余一些存储引擎中实现了行级锁。行级锁只在存储引擎层实现,而MySQL服务器层有实现it
提出意向锁概念以前,先试想一个场景。当一个事务1已经对表A的某一行加了读锁,此时事务2想在表A上加写锁,那么这种状况应该容许吗?
固然不该该容许,事务2在表A上加写锁,那么是否是意味着事务2能够对表A的任意行进行修改?那么事务1加的读锁又有什么用?因此这就矛盾了
最简单的解决方案是,事务2再加表锁以前对表A进行全表扫描,看一看有没有行锁,若是有的话,还要判断要加的表锁和行锁会不会冲突。这种方案真的夸张,加个锁有必要全表扫描一遍吗?
事实上InnoDB确定不会这么干的,这里新增了一种锁,叫作意向锁
在InnoDB中,意图锁定是表锁定。它的用途就是说告诉其它的表锁,如今这个表中有行锁;若是是读锁,那么就会给表加意图共享锁;若是是写锁,就会给表加意图占有锁;
事先明确
意图锁协议以下:
这些结果能够方便地用一个锁类型兼容矩阵来总结:
X | IX | S | IS | |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容 | 冲突 | 兼容 |
S | 冲突 | 冲突 | 兼容 | 兼容 |
IS | 冲突 | 兼容 | 兼容 | 兼容 |
这个表很明了了,假设给表加了IS,那么就不能给表加写锁(X),以前提的问题就迎刃而解了
在事务执行过程当中,随时均可以执行锁定,锁只有在执行COMMIT或者ROLLBACK的时候才会释放,而且全部的锁是在同一时刻被释放。诸如SELECT... UPDATE... DELETE... 的是隐式锁定,InnoDB会根据隔离级别在须要的时候自动加锁
另外,InnoDB也支持经过特定的语句进行显式锁定,这些语句不属于SQL规范
InnoDB存储引擎实现了行锁,这确实提升了并发性能,但这只是提升了表层面的并发性能,若是单行数据被频繁访问,使用行锁的话,并发性能仍是不够理想。MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提高并发性能的考虑,它们通常都同时实现了多版本并发控制(MVCC)
关于MVCC的博文不少,再也不花时间啰嗦,本身搜去吧
若是你不了解MVCC,另外你对脏读,幻读,不可重复读的概念不理解的话,请你当即中止阅读!接下来的内容会让你产生头晕眼花,恶心干呕等不良反应
InnoDB实现了四种隔离级别:
READ UNCOMMITTED(未提交读)
这个级别中,SELECT语句以非锁定方式被执行。事务中的修改,即便没有提交,对其余事务也都是可见的。事务能够读取未提交的数据,这也被称为脏读(Dirty Read)。
READ COMMITTED(提交读)
一个事务从开始直到提交以前,所作的任何修改对其余事务都是不可见的。这个级别有时候也叫作不可重复读(nonrepeatable read),由于两次执行一样的查询,可能会获得不同的结果
REPEATABLE READ(可重复读)
可重复读隔离级别仍是没法解决另一个幻读(Phantom Read)的问题。InnoDB和XtraDB存储引擎经过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别。它经过强制事务串行执行,避免了前面说的幻读的问题。简单来讲,SERIALIZABLE会在读取的每一行数据上都加锁,因此可能致使大量的超时和锁争用的问题。
READ UNCOMMITTED(未提交读)的实现
SELECT语句以非锁定方式被执行
READ COMMITTED(提交读)的实现
这个隔离级别是经过MVCC实现的,事务每次读的时候,都会读刷新过的快照版本,也就是最新的数据。
REPEATABLE READ(可重复读)的实现
这个隔离级别也是经过MVCC实现的,事务每次读的时候,都会读快照版本,也就是旧的版本数据。
SERIALIZABLE(可串行化)的实现
这个级别相似REPEATABLE READ,可是全部无格式SELECT语句被 隐式转换成SELECT ... LOCK IN SHARE MODE,也就是默认的加共享锁!
InnoDB的默认隔离级别是RR(REPEATABLE READ),这个级别使用了MVCC+快照读,完美的解决了不可重复读和幻读的问题
虽然RC(READ COMMITTED)也是MVCC实现的,可是每次读的时候,都会刷新快照,因此会存在不可重复读和幻读的现象
PS:关于幻行,在RR级别,事务A虽然SELECT不到事务B的INSERT,可是能够经过UPDATE,DELETE感知到事务B事务B的INSERT,能够去实验一下
上面说到了使用MVCC+快照读实际上是能够避免幻读的,不过还有一种状况没有考虑到
在InnoDB下,使用SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE时,如何避免幻读?
单纯的使用行锁的话,是没法阻止幻读的(想一想为何);解决这个问题,蠢办法就是使用表锁;InnoDB设置者固然不会这么干,这里须要先了解间隙锁
关于间隙锁和next-lock。。。。百度去吧,累了