在前面一系列关于Mysql的博文中讲述了Mysql的底层存储机制,以及在此之上的redo、undo日志和基于这些底层结构之上的索引以及事务控制。其中对事务的控制就是在多并发场景下的问题,在Mysql的机制中对多并发的控制还有一个重要的手段就是锁机制。
锁如果是从操作类型上分的话可以分为读锁、写锁,这里说的读写锁的概念与我们Java中的是相似的,可以理解为是共享锁以及独占锁。从粒度上分可以分为行锁、页锁、表锁,平常中我们使用最多的是行锁与表锁,这里面说的主要指的是锁的作用范围的一个大小,锁作用范围的大小也会直接影响并发程度。行锁的并发程度是最高的但是他的加锁成本大常见于Innodb的引擎上,表锁的加锁成本低,但是锁定的范围也大,并发度最低,常见于MySIAM引擎上,根据读的特性——共享的,MySIAM是跟适合于偏向查询的场景。
❓ 我们知道锁以及事务级别实际上都是为了解决并发场景的,事务级别的理解可以借助于redo、undo日志那么他们之间与锁又是有什么样的关联呢?跟人理解是锁机制主要是做的一个粗粒度上的控制但是数据的读写因为存储结构的存在他并不是一下子就成功的,这就造成了那几个脏读、脏写、不可重复读、幻读的问题,而这些问题的解决又是借助于MVVC机制实现的。
建表语句:
create table test_innodb_lock (a int(11),b varchar(16))engine=innodb;
insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1');
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);
select * from test_innodb_lock;
锁表语句:
lock table mylock read;
这时候如果通过其他的窗口在去做产寻的时候是可以查询到数据的但是不能够进行写入与更改,这里面使我们自己手动加锁了,在实际的操作过程中如果使用MySIAM引擎,引擎的底部会根据操作的类型自动的对所涉及的表进行加锁的。
执行释放锁语句:
unlock tables;
建表语句:
create table test_innodb_lock (a int(11),b varchar(16))engine=innodb;
insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1');
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);
select * from test_innodb_lock;
在mysql较新的版本中都设置了是自动提交所以想要演示Mysql锁的例子,首先需要去除自动提交的设置:
set autocommit = 0;
在这里关于行锁的演示,锁都是由引擎自己给我们加上的,为了人眼可见通过设置不自动提交,来实现。**正常情况下Innodb锁定的是只有一行但是当我们where之后如果没有索引或是索引失效的话将会引发行锁升级为表锁。**在select语句中我们也可以显式的添加锁:
select * from test_innodb_lock where a<3 lock in share mode;
在查询语句后面增加 LOCK IN SHARE MODE ,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表(行?),而且这些线程读取的是同一个版本的数据。
如果要是想要加写锁的话可以使用:
SELECT ... FOR UPDATE;
在行锁使用的过程中还有一种间隙锁,他是由于一种范围操作引起的,通过这个间隙锁所造成的问题我们也可以大概了解一下锁的机制,不管是对数据的读取还是写入实际上才上来都是需要定位到数据的位置的,而锁的作用机制就发生在这个时候,这也给范围的操作造成间隙锁造成了可乘之机,这里个人大胆的猜测,这里的锁实际上并没有作用与真实的数据上一定程度来说应该是一种类似于逻辑锁的概念。
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,