此次咱们只谈数据库。以往遇到并发事务问题,总想着站在应用层面考虑问题,该如何实现悲观锁和乐观锁?但并不很清楚数据库中底层的实现原理。找几个晚上从新温习了一下大学课堂上的《数据库系统概览》,借这篇文章记下来此次读书的概要。mysql
若是想要说明一个数据库或者一个框架支持事务性操做,则必需要知足下面的四大特性。sql
在数据库事务的ACID四个属性中,隔离性是一个最常放松的一个。能够在数据操做过程当中利用数据库的锁机制或者多版本并发控制机制获取更高的隔离等级。可是,随着数据库隔离级别的提升,数据的并发能力也会有所降低。因此,如何在并发性和隔离性之间作一个很好的权衡就成了一个相当重要的问题。数据库
实际开发过程当中,咱们绝大部分的事务都是有并发状况。当多个事务并发运行,常常会操做相同的数据来完成各自的任务。在这种状况下可能会致使如下的问题:并发
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。oracle
ANSI/ISO SQL定义的标准隔离级别有四种,从低到高依次为:未提交读(Read uncommitted) < 提交读(Read committed) < 可重复读(Repeatable reads) < 序列化(Serializable)。框架
mysql:默认隔离级别是 Repeatable Read,会出现幻读的问题。oracle/sql server:默认隔离级别是 Read Committed,会出现不可重复读和幻读的问题。spa
产生幻读的缘由是,行锁只能锁住行,可是新插入记录这个动做,要更新的是记录之间的“间隙”。所以,为了解决幻读问题,mysql InnoDB 只好引入新的锁,也就是间隙锁 (GapLock)。间隙锁,锁的就是两个值之间的空隙。值得注意的是,间隙锁只在隔离级别是Repeatable read 下才会生效。线程
默认状况下,InnoDB工做在"可重复读"的隔离状况下,而且以Next-Key Lock的方式对数据进行加锁,这样就能够有效地防止"幻读"的发生。Next-key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。若是一个间隙被事务T1加了锁,其余事务是不能在这个间隙插入记录的。例以下面的sql,会对id取值范围大于100的间隙加锁。当增删数据库时若是id大于100则会阻塞住,若是小于100则不会有影响。code
select * from emp where id > 100 for update;
锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。粗略的分有行级锁、表级锁和页级锁,行级锁再分共享锁和排他锁等。要记住悲观锁和乐观锁是两个抽象的概念,并不是实际数据库中的锁。server
共享锁(S锁):容许一个事务去读一行,会阻止其余事务获取相同数据集的排他锁(读取数据的时候不容许修改),也被称为读锁。当没有其余线程对查询结果集中的任何一行使用排他锁时,能够成功申请共享锁,不然会被阻塞。其余线程也能够读取使用了共享锁的表,并且这些线程读取的是同一个版本的数据。
mysql的共享锁支持行级锁,在查询语句后面增长LOCK IN SHARE MODE,mysql会对查询结果中的每行都加共享锁。
SELECT ... LOCK IN SHARE MODE;
oracle的共享锁不支持行级锁,只能是表级锁。
LOCK TABLE <表名>[,<表名>]... IN SHARE MODE [NOWAIT]
有一些误区须要记住,单纯的查询SELECT不是共享锁,其实是没有上任何锁。而 SELECT ... FOR UPDATE 是排他锁。
排他锁(X锁):容许获取排他锁去作更新操做,阻止其余事务获取相同数据的共享锁和排他锁(一个事务修改数据的时候,阻止其余事务对相同数据集作更新或者查询操做),也被称为写锁。
排他锁的使用方式比较一致,新增、更新和删除等DML操做语句会自动加上排他锁。并且SELECT ... FOR UPDATE 也是排他锁。
为了容许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁:
意向共享锁(IS):事务打算给数据行加共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加排他锁,事务再给一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是数据库隐式帮咱们作了,咱们不须要操心。
悲观锁:是从数据库层面加锁。老是假设最坏的状况,每次去拿数据的时候都认为别人会修改,因此每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它释放锁。
乐观锁:老是假设最好的状况,每次去拿数据的时候都认为别人不会修改,因此不会上锁,可是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
那么在数据库层面上,咱们利用锁来实现事务的,不管是共享锁仍是排他锁,都是悲观锁。而乐观锁是经过业务来实现,并不会利用到数据库的锁。