锁是用来管理对共享文件的并发访问。innodb会在行级别上对数据库上锁。不过innodb存储引擎会在数据库内部其他多个地方使用锁,从而允许对不同资源提供并发访问。例如操作缓冲池中的lru列表,删除,添加,移动lru列表中的元素,为了保证一致性,必须有锁的介入。myisam引擎是表锁,而innodb提供一致性的非锁定读、行级锁,且行级锁没有相关额外的开销。
锁
table-level locking(表级锁)
整个表被客户锁定。根据锁定的类型,其他客户不能向表中插入记录,甚至从中读数据也受到限制myisam、memory默认锁级别,个别时候,innodb也会升级为表级锁
row-level locking(行级锁)
只有线程当前使用的行被锁定,其他行对于其他线程都是可用的innodb默认行级锁。是基于索引数据结构来实现的,而不是像oracle的锁,是基于block的。innodb也会升级为表级锁,全表/全索引更新,请求autoinc锁等
page-level locking(页级锁)
锁定表中某些行集合(称做页),被锁定的行只对锁定最初的线程是可行。如果另外一个线程想要向这些行写数据,它必须等到锁被释放。不过其他页的行仍然可以使用bdb默认页级锁
lock与latch
latch称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在innodb存储引擎中,又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。latch可以通过命令show engine innodb mutex来进行查看。如图:
由上图可以看出列type显示的总是innodb,列name显示latch的信息以及所在源码的行数,列status中显示的os_waits表示操作系统等待的次数。
lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或者rollback后释放(不同事务隔离级别释放的时间可能不一样)。有死锁机制。二则的区别如下:
特点:
innodb是通过对索引上的索引项加锁来实现行锁。这种特点也就意味着,只有通过索引条件检索数据,innodb才使用行级锁,否则,innodb将使用表锁。
锁的类型:
有两种标准的行级锁:
共享锁(s lock):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁.select * from table_name where ... lock in share mode
排它锁(x lock):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他锁.select * from table_name where ... for update
innodb存储引擎支持意向锁且设计比较简练,分为两种内部使用的意向锁(intention locks),这两种意向锁都是表锁。(意向锁是innodb自动加的)
意向共享锁(is):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的is锁.
意向独占锁(ix):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的ix锁.
表级意向锁与行级锁的兼容情况如下图:
锁的查看
在innodb1.0版本之前只能通过show engine innodb status(transactions行中查看) 或者 show full processlist来查看当前库中锁的请求。但是在这之后在information_schema架构下新增innodb_trx、innodb_locks和innodb_lock_waits三张表记录当前库中锁的情况。
三个表的字段说明如下图vcq9wls2wcihtbhhsna00ndksbzkyv2+3b/i1tdq0lxeyv2+3agjyoe5+7bbykg1xndq1f3u2ta00nbkzwxldgw78txfdxbkyxrlstnx96os1elksbbbykgy2df3sru74dlytmvipbxitp3q0mnpy/i1xmrnt8who8/gt7s12kossw5ub0rctoa0otl9x+a74ciltshioddqtctsu7j2v+zv1cr9vt0otbhhsndqyv2+3bxewprkt7dmsb4poao/7nxvyv2+3crh1ri4w9dqtctwrsewsoaxvrxeyv2+3aosumpktc/wysfnqln9dw5kb7bowltn6rpjoao2+hvuzg/tw8c01nrkws7xu9i59sr9vt2jrnlytmu/7nxvyv2+3bg+ye3kx8o709c27s3iv6rp+qgjtvjh0qostshiob/s1dxk/b7dyseyu9do0qrjz8v4tcsho9k71slq1lfhy/i2qlbbysdjbm5vrek05rsi0v3h5rxexkziz7bbykg3vcq9kntatshiobk7u+hvvnpdus21ylt9se3jz7xey/gpoao1q8rh1nqyu82syslo8bj0wou8trhwz8kjrlbbykg1xle9yr2yu82so6yyork7ysfu2so/upbkws7xupta67y2sfdpwra8yseyydpdt8fl+laotctsu9bc0ns2wagjvltkuba8ysfkudpdt8fl+laotctsu9bc0ns2waostavkx7bu09q/7nxvyv2+3bxetqjs5syjmjy2odq7yr3ssrj3srvp4m2soapu2srczvg49mdrvlax8fjfquqkienptu1jvfrfrchsqym6zvjfuevbvefcteugukvbrchsuixjbm5vrek05rsi0v3h5rxexkziz8rczvg49mdrvlax8cnpwqossw5ub0rctoa0otl9x+bkudpdt8fl+laotctsu9bc0ns2wagjylu2+kostttt2r/s1dxk/b7dtcs2qnllykwyu8/gzayho9taukpkws7xupta67y2sfdpwqostttt2r/s1dxk/b7do6y3x9k71slq1lbb19zkx7bbykgxu8v4tqjq0lxe1+7qwtk7t92/7nxvyv2+3agjtvju2ljsyslo8bj0wou94rdzz8kjrlbu09q/7nxvyv2+3aost8fsu9bc0ns2wdfcyse2wcihyslo8b+qyrzksbxe0ndk/b7dsoaxvqgjpgjypgogicag0rvwwtduy/i2qlbbpgjypgogicagicaginpqyc/oxnaqtccjrmssym+1xmrczvg49mdrvlax8chsuineo8q9z8kjrelubm9eqrtmtkls/cfmtcrzzwxly3sy2df3yrntw9k71slq1lfhy/i2qlbboao1q8rh1nres9cpx+m/9s/co6ztw7un0ojsqs/uyr212lbuyv2+3b/itsgy2df3vnpl+nlusapwpmr9vt3c37yttctsu9bc0nsho0lubm9eqrtmtkls/cfmtttt2nnlbgvjdnpvvutwp7pwwb3w1tk71slq1lxey/i2qlbbstnx9zo8yni+ciagicagicagicagihnlbgvjdcauli4gzm9yihvwzgf0zao6tts2wcihtctq0lzhwry801jl+kosxutl+8rczu+yu8tctts4w9dqvnpizrroy/ihozxicj4kicagicagicagicagc2vszwn0ic4ulibsb2nrigluihnoyxjlig1vzgwjurbutshiobxe0nc8x8k8vnnty/ijrmbky/vkws7vv8ns1lbuumpq0lztu8v4o6y1q8rhyoe5+7ztwmv4o6zu8rvhsbvx6mj7oam8yni+ciagicdx1nt2s6tt68v4pgjypgogicagicagintasw5ub0rctoa0otl9x+a1xmtatoa94bm51tcjrlbuw7+49rqs09dx1nt2s6qmiziwntqwo7xese22vnpq0ru49tfu1pazplzgyv3g9yhhdxrvlwluy3jlbwvudcbjb3vudgvykagjtbg21lqs09dx1nt2s6s1xlzgyv3g97xese29+ndqsuxi67lz1/fkx6os1ek49rzgyv3g97vhsbuz9cq8u6+jrna00ndi58/ctctt777kwls1w7w9vmbk/cb3tcqmiziwntqwozpzzwxly3qgbwf4kgf1dg9faw5jx2nvbckgznjvbsb0igzvcib1cgrhdgwho7llyouy2df3u+hswl7d1ek49tfu1pazplxevmbk/cb3jimymdu0mdu80zg4s9po19tu9rokwdcho9xiupbktc/wt73kvbpgzqpbvvrpluloqybmb2nraw5no6zv4srh0rvw1szyyuk1xlhty/i7+tbgo6zoqshlzog437llyou1xnduxnyjrmv4srvkx9ta0ru49srczvhn6rpj1q6687lfys23xaostvjkx9tazeqzybbu19tu9rokjimymdu0mduy5cjrtcrtuuzt777kuvo74cgivltkzbffoanbvvrpluloqwogtg9ja2luz9ta0ru2qlpmtsjjz8zhun/by7kit6ky5cjrtctqp8lko6y1q8rhu7m05tta0rvqqdduxnzjz7xezsrm4qgjytfpykostttt2tpq19tu9rokjimymdu0mdu1xmhqtcsyoreisuxi69duxny9z7luo6zkws7xsdjq67xitp3hsnk7upay5cjrtctn6rpjklk708o1ylt9yslo8bxezeqzysmho8bktm6jrlbu09ppbnnlcnqgli4uihnlbgvjdlxetppk/b7dwb+1xlllyou74dowz+yy5cjrtctq1mtco6zs8s6qwe3su7j2yslo8dbqtcsy5cjru+gxu9foypuho7tttxltuuw1ljeumjkw5rg+v6rkvkossw5ub0rctoa0otl9x+bm4bmpwcvsu9bwx+hbv7y2u6wz4sg/tctx1nt2s6tktc/wu/rwxqos1elw1rv61sa087tzzog438hl19tu9rokjimymdu0mduy5cjrtctq1mtcoapnqln9ss7k/wlubm9kyl9hdxrvaw5jx2xvy2tfbw9kzcc0v9jwxtfu1pazplxexkpkvsjermjpzqoxkagj19tu9roktcsy5cjrvfjq0lfwwodi5828ojxpbwcgc3jjpq==http://www.bitscn.com/uploadfile/collfiles/20141220/20141220092504215.jpg alt=\>
innodb_autoinc_lock_mode的参数值及其对自增长的影响如下图:
myisam存储引擎是表锁,自增长不用考虑并发插入的问题。需要注意的是:在innodb存储引擎中,自增长值的列必须是索引,同时必须是索引的第一个列,如果不是第一个列,mysql是会抛出异常的。异常如图
外键与锁
外键主要用于完整性的约束检查。在innodb存储引擎中,对于一个外键列,如果没有显式地对这个列加索引,innodb存储引擎会自动对其加一个索引,避免表锁。对于外键值的插入或者更新,首先需要查询父表中的记录,对于父表的select操作,不是使用的一致性非锁定读的方式,因为这样会发生数据不一致的问题,所以这时使用的是select ... lock in share mode方式,即主动给父表加一个s锁。
锁的问题
dirty read 脏读
脏读就是读取到脏数据(未提交的数据)。一个事务(a)读取到另一个事务(b)中修改后但尚未提交的数据,并在这个数据的基础上操作。这时,如果b事务回滚,那么a事务读到的数据是无效的。不符合一致性。如图
首先事务的隔离级别有默认的rr改为ru,由上述例子可以看出会话b中两次select操作取得了不同的结果,并且这2条记录是会话a中并未提交的数据,这就产生了脏读。由此可以得出结论:脏读发生的条件是事务的隔离级别为ru。
unrepeatable read 不可重复读
事务(a)读取到了另一个事务(b)已经提交的更改数据,不符合隔离性。不可重复读和脏读的区别是:脏读是读到未提交的数据,而不可重复读则读到的是已经提交的数据。首先将事务隔离级别调整为rc,然后操作下边的例子:
phantom read 幻读
事务(a)读取到了另一个事务(b)提交的新增数据,不符合隔离性。
锁的范围(锁的算法):
1.record lock :单个记录上的锁,总是会去锁住索引记录,如果innodb存储引擎表在建立的时候没有设置任何一个索引,那么这时innodb存储引擎会使用隐式的主键来进行锁定。
2.gap lock:间隙锁,锁定一个范围,但不包含记录本身。
3.next-key lock: 锁定一个范围和本身 record lock + gap lock,防止幻读。
主键索引和唯一辅助索引 = record lock
非唯一辅助索引 = next-key lock
阻塞
不同锁之间的兼容性关系,在有些时刻一个事务中的锁需要等待另外一个事务中的锁释放它所占用的资源,这就是阻塞。阻塞并不是一件坏事,其是为了确保事务可以并发且正常地运行。在innodb存储引擎中,参数innodb_lock_wait_timeout用来动态的控制等待的时间(默认50秒),innodb_rollback_on_timeout用来静态的设定释放在等待超时时对进行的事务进行回滚操作(默认off,代表不回滚)。
死锁
死锁是指两个或者两个以上的事务在执行过程中,因争夺资源而造成的一种相互等待的现象。解决死锁最简单的一种方式是超时,即当两个事务相互等待是,当一个等待时间超过设置的某一阀值时,其中一个事务进行回滚,另外一个等待的事务就能继续进行。在innodb存储引擎中,参数innodb_lock_wait_timeout用来设置超时时间。但若超时的事务所占权重比较大,如果事务操作更新了很多行,占用了较多的undo log,这时采用fifo的方式就不合适啦,因为回滚这个事务的时间相对于另一个事务所占用的时间可能会很多。因此,除了超时机制,当前数据库都普遍采用wait-for graph(等待图)的方式来进行死锁检测。要求数据库报错以下两种信息:a.锁的信息链表;b.事务等待链表。通过上述链表可以构造一张图,而在这个图中若存在回路,就代表存在死锁。在wait-for graph中,事务为图中的节点。如图:
如图可以发现存在回路(1,2),因此存在死锁,这时innodb存储引擎选择回滚undo量最小的事务。wait-for graph的死锁检测通常采用深度优先的算法实现。
注意:
1.s x is ix,表示的是,本锁和其他锁共存的方式,是互斥还是兼容
2.record lock、gap lock、next-key lock,表示的是,这些锁要加载的范围,是行记录本身,还是行记录+间隙,甚至更大的范围
重要的结论:
1、任何辅助索引上的锁,或者非索引列上的锁,最终都要回溯到主键上,在主键上也要加一把锁
2、任何叶子节点上的s或x锁之前,都会在根节点加一个is或ix锁,也就是表级别的is、ix锁
3、主键索引上的锁,都是record lock
4、唯一索引辅助索引上的锁,也都是record lock
5、非唯一索引辅助索引上的锁,则是next-key lock
6、不会有单独的gap lock出现,只会伴随着record lock出现,依附于它
