在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的知足。sql
在数据库的锁机制中介绍过,在DBMS中,能够按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎)。数据库
行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操做的行进行加锁。行级锁能大大减小数据库操做的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁
和排他锁
。多线程
只有经过索引条件检索数据,InnoDB才使用行级锁(MySQL的行锁是针对索引加的锁,不是针对记录加的锁,因此虽然是访问不一样行的记录,可是若是是使用相同的索引键,是会出现锁冲突的。),不然,InnoDB将使用表锁!并发
即使在条件中使用了索引字段,可是否使用索引来检索数据是由MySQL经过判断不一样执行计划的代价来决定的,若是MySQL认为全表扫描效率更高,好比对一些很小的表,它就不会使用索引,这种状况下InnoDB将使用表锁,而不是行锁。所以,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。性能
在MySQL中,行级锁并非直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,若是一条sql语句操做了主键索引,MySQL就会锁定这条主键索引;若是一条语句操做了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。(由于innodb要实现行级锁,非主键索引不能保证只锁该行,只是锁了改行的这条记录,而若是都遵循先锁非主索引,再锁主索引的方式,就能保证确确实实的锁了该行。因为这个特性,行级锁会产生死锁现象)spa
死锁举例:.net
例如,一个表db.tab_test,结构以下:线程
id:主键;code
state:状态;htm
time:时间;
索引:idx_1 (state, time)
当“update tab_test set state=1064,time=now() where state=1061 and time < date_sub(now(), INTERVAL 30 minute)”执行时,MySQL会使用idx_1索引,所以首先锁定相关的索引记录,由于idx_1是非主键索引,为执行该语句,MySQL还会锁定主键索引。
假设“update tab_test set state=1067,time=now () where id in (9921180)”几乎同时执行时,本语句首先锁定主键索引,因为须要更新state的值,因此还须要锁定idx_1的某些索引记录。
这样第一条语句锁定了idx_1的记录,等待主键索引,而第二条语句则锁定了主键索引记录,而等待idx_1的记录,这样死锁就产生了。
开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高。
表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操做的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁
(共享锁)与表独占写锁
(排他锁)。
开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的几率最高,并发度最低。
页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度通常
MyISAM和MEMORY采用表级锁(table-level locking)
BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
前面提到过,在Innodb引擎中既支持行锁也支持表锁,那么何时会锁住整张表,何时或只锁住一行呢?
InnoDB行锁是经过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不一样,后者是经过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特色意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁!
在实际应用中,要特别注意InnoDB行锁的这一特性,否则的话,可能致使大量的锁冲突,从而影响并发性能。
行级锁都是基于索引的,若是一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁。行级锁的缺点是:因为须要请求大量的锁资源,因此速度慢,内存消耗大。
MyISAM中是不会产生死锁的,由于MyISAM老是一次性得到所需的所有锁,要么所有知足,要么所有等待。而在InnoDB中,锁是逐步得到的,就形成了死锁的可能。
在MySQL中,行级锁并非直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,若是一条sql语句操做了主键索引,MySQL就会锁定这条主键索引;若是一条语句操做了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。 在UPDATE、DELETE操做时,MySQL不只锁定WHERE条件扫描过的全部索引记录,并且会锁定相邻的键值,即所谓的next-key locking。
当两个事务同时执行,一个锁住了主键索引,在等待其余相关索引。另外一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
发生死锁后,InnoDB通常均可以检测到,并使一个事务释放锁回退,另外一个获取锁完成事务。
有多种方法能够避免死锁,这里只介绍常见的三种
一、若是不一样程序会并发存取多个表,尽可能约定以相同的顺序访问表,能够大大下降死锁机会。
二、在同一个事务中,尽量作到一次锁定所须要的全部资源,减小死锁产生几率;
三、对于很是容易产生死锁的业务部分,能够尝试使用升级锁定颗粒度,经过表级锁定来减小死锁产生的几率;
1. http://www.hollischuang.com/archives/914
2. http://book.51cto.com/art/200803/68127.htm
3. http://www.programgo.com/article/76482297332/