到了四种隔离级别,当时以为大多数数据库都为read committed,结果没想到mysql是个例外。在此作一下隔离级别和各类数据库锁的使用。mysql
首先说一下ACID四大特性:算法
· 原子性
事务必须是原子工做单元;对于其数据修改,要么全都执行,要么全都不执行。一般,与某个事务关联的操做具备共同的目标,而且是相互依赖的。若是系统只执行这些操做的一个子集,则可能会破坏事务的整体目标。原子性消除了系统处理操做子集的可能性。
· 一致性
事务在完成时,必须使全部的数据都保持一致状态。在相关数据库中,全部规则都必须应用于事务的修改,以保持全部数据的完整性。事务结束时,全部的内部数据结构(如 B 树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制全部已知的完整性约束。例如,当开发用于转账的应用程序时,应避免在转账过程当中任意移动小数点。
· 隔离性
由并发事务所做的修改必须与任何其它并发事务所做的修改隔离。事务查看数据时数据所处的状态,要么是另外一并发事务修改它以前的状态,要么是另外一事务修改它以后的状态,事务不会查看中间状态的数据。这称为可串行性,由于它可以从新装载起始数据,而且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将得到最高的隔离级别。在此级别上,从一组可并行执行的事务得到的结果与经过连续运行每一个事务所得到的结果相同。因为高度隔离会限制可并行执行的事务数,因此一些应用程序下降隔离级别以换取更大的吞吐量。
· 持久性
事务完成以后,它对于系统的影响是永久性的。该修改即便出现致命的系统故障也将一直保持。 sql
主要说说这个描述很抽象一致性,举个例子:A向B转帐,假设转帐以前这两个用户的钱加起来总共是2000,那么A向B转帐以后,无论这两个帐户怎么转,A用户的钱和B用户的钱加起来的总额仍是2000,这个就是事务的一致性。数据库
四种隔离级别数据结构
Read Uncommitted(读取未提交内容)并发
在该隔离级别,全部事务均可以看到其余未提交事务的执行结果。本隔离级别不多用于实际应用,由于它的性能也不比其余级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)高并发
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它知足了隔离的简单定义:一个事务只能看见已经提交事务所作的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),由于同一事务的其余实例在该实例处理其间可能会有新的commit,因此同一select可能返回不一样结果。
Repeatable Read(可重读)性能
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到一样的数据行。不过理论上,这会致使另外一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另外一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎经过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。spa
Serializable(可串行化)
这是最高的隔离级别,它经过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每一个读的数据行上加上共享锁。在这个级别,可能致使大量的超时现象和锁竞争。.net
这四种隔离级别采起不一样的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:
脏读(Drity Read):某个事务已更新一份数据,另外一个事务在此时读取了同一份数据,因为某些缘由,前一个RollBack了操做,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这多是两次查询过程当中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例若有一个事务查询了几列(Row)数据,而另外一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
在MySQL中,实现了这四种隔离级别,分别有可能产生问题以下所示:
而后是关于数据库的各类锁的总结:
1.共享锁(又称读锁)、排它锁(又称写锁):
InnoDB引擎的锁机制:InnoDB支持事务,支持行锁和表锁用的比较多,Myisam不支持事务,只支持表锁。
共享锁(S):容许一个事务去读一行,阻止其余事务得到相同数据集的排他锁。
排他锁(X):容许得到排他锁的事务更新数据,阻止其余事务取得相同数据集的共享读锁和排他写锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
说明:
1)共享锁和排他锁都是行锁,意向锁都是表锁,应用中咱们只会使用到共享锁和排他锁,意向锁是mysql内部使用的,不须要用户干预。
2)对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁,事务能够经过如下语句显示给记录集加共享锁或排他锁。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
**对于锁定行记录后须要进行更新操做的应用,应该使用Select...For update 方式,获取排它锁。(用共享锁,在读了以后再写会阻塞,会致使死锁)
这里说说Myisam:MyISAM在执行查询语句(SELECT)前,会自动给涉及的全部表加读锁,在执行更新操做(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁。
3)InnoDB行锁是经过给索引上的索引项加锁来实现的,所以InnoDB这种行锁实现特色意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁!
2.乐观锁、悲观锁:
悲观锁:悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其余事务,以及来自外部系统的事务处理)修改持保守态度,所以,在整个数据处理过程当中,将数据处于锁定状态。悲观锁的实现,每每依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,不然,即便在本系统中实现了加锁机制,也没法保证外部系统不会修改数据)
1)使用悲观锁,咱们必须关闭mysql数据库的自动提交属性,采用手动提交事务的方式,由于MySQL默认使用autocommit模式,也就是说,当你执行一个更新操做后,MySQL会马上将结果进行提交。
2)须要注意的是,在事务中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一笔数据时会等待其它事务结束后才执行,通常SELECT ... 则不受此影响。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X)。
3)补充:MySQL select…for update的Row Lock与Table Lock
使用select…for update会把数据给锁住,不过咱们须要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,因此只有「明确」地指定主键(或有索引的地方),MySQL 才会执行Row lock (只锁住被选取的数据) ,不然MySQL 将会执行Table Lock (将整个数据表单给锁住)。
乐观锁:
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据通常状况下不会形成冲突,因此在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,若是发现冲突了,则让返回用户错误的信息,让用户决定如何去作(通常是回滚事务)。那么咱们如何实现乐观锁呢,通常来讲有如下2种方式:
1).使用数据版本(Version)记录机制实现,这是乐观锁最经常使用的一种实现方式。何谓数据版本?即为数据增长一个版本标识,通常是经过为数据库表增长一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当咱们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,若是数据库表当前版本号与第一次取出来的version值相等,则予以更新,不然认为是过时数据。
2).乐观锁定的第二种实现方式和第一种差很少,一样是在须要乐观锁控制的table中增长一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version相似,也是在更新提交的时候检查当前数据库中数据的时间戳和本身更新前取到的时间戳进行对比,若是一致则OK,不然就是版本冲突。
总结:两种锁各有优缺点,不可认为一种好于另外一种,像乐观锁适用于写比较少的状况下,即冲突真的不多发生的时候,这样能够省去了锁的开销,加大了系统的整个吞吐量。但若是常常产生冲突,上层应用会不断的进行retry,这样反却是下降了性能,因此这种状况下用悲观锁就比较合适。
另外,高并发状况下我的认为乐观锁要好于悲观锁,由于悲观锁的机制使得各个线程等待时间过长,极其影响效率,乐观锁能够在必定程度上提升并发度。
3.表锁、行锁
表级锁(table-level locking):MyISAM和MEMORY存储引擎
行级锁(row-level locking) :InnoDB存储引擎
页面锁(page-level-locking):BDB存储引擎
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的几率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度通常。