MySQL锁问题学习

MySQL锁的机制相对其他数据库而言比较简单,显著特点是不同存储引擎支持不同锁机制,如下图:

存储引擎 表级锁     页面锁 行级锁
MyISAM 支持 不支持 不支持
InnoDB 支持 不支持 支持
BDB 支持 支持 不支持

而对于这三种锁各自特性,主要如下:

  • 表级锁,开销小,加锁快;不会出现死锁;锁的粒度大,发生锁冲突概率最高,并发性最低。
  • 页面锁,开销和加锁时间处于表锁和行锁之间,会出现死锁;锁定粒度界于表锁与行锁。
  • 行锁,开销大,加锁慢,会出现死锁,锁的粒度最小,发生锁冲突概率最低,并发性最高。

接下来先看看MyISAM存储引擎的表锁吧:

表锁有两种形态-表共享锁(Table Read Lock)和表独占写锁(Table Write Lock);

  1. 对MyISAM表读操作不会阻塞其他用户对同一表读请求;但是会阻塞对同一表写请求;
  2. 对MyISAM表写请求则会阻塞其他用户对同一表读和写请求

那如何显示对表加锁和释放锁呢?答案很简单----

加锁:lock table tablename write(read);

释放锁:unlock tables; 

MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行更新操作(update,delete,insert等)前,会自动给涉及表加写锁,一般不需要用户干涉,我们这里显示加锁只是为了看得到而已。

扫描二维码关注公众号,回复: 1731413 查看本文章
OK有了上面一些知识我们来看看MyISAM存储引擎的写阻塞例子

session_1 session_2

获得表test的write锁定(就是独占写锁)


 

当前session对锁定表查询,更新,插入操作都可以执行

查询:


新增:


修改:


其他session对锁定表查询会被阻塞,需要等待锁被释放


释放锁:


等待

session_1释放锁之后session_2获得锁,查询返回


接下来是MyISAM的读阻塞例子:

session_1 session_2

获得表test的READ锁定


 

当前session可以查询锁定的该表记录:


当前session不能查询没有锁定的表


其他的seesion也可以查询锁定的该表记录:


其他的session可以查询或者更新没有锁定的表:


当前session新增或者更新会报错:


其他的session更新锁定的表会等待获得锁:


释放锁;unlock tables;

等待
 

session获得锁,更新操作完成



   

emmmm,那MyISAM对于读和写是串行的,但是有一定条件下,我们可以支持查询和新增并发进行:

在MyISAM存储引擎中有一个系统变量concurrent_insert,我们可以这样查看他的默认值--


默认为auto,意思是如果MyISAM表中没有数据空洞(也就是表的中间没有被删除的行,连续的),这样就允许一个进程读表的同时,另一个进程从表尾插入数据;

如果把这个变量值设置为0,就是不允许并发插入;

设置为2,即使数据空洞,也允许表尾并发插入记录。

注意:并发插入锁表是要在末尾加local选项的

举个栗子吧哈哈:

session_1 session_2

获得test的READ LOCAL锁定:


 
当前session不能对锁定的表进行更新或者插入操作(这就像上面读锁表一样):
其他session可以进行插入操作,但是更新操作会进行锁等待:

当前session不能访问到其他session插入的记录


其他session可以查询到新增的数据:


释放锁unlock tables;  
当前session可以获取到其他session新增的记录 session获得锁,可以继续上面更新要求。

MyISAM的锁调度:

上面几个例子,我们已经知道了这个存储引擎的读锁和写锁是互斥的,n那么如果我同一时间有一个写请求和一个读请求,会如何处理呢?

答案就是写请求先获取到锁,即使是写请求比读请求慢进入等待队列依然如此,这是因为MySQL认为写请求权重更大,更重要,所以这也是为什么MyISAM不适合更新操作比较多的应用原因了^_^

emmm这样子我们有没有办法改变默认的调度呢?答案是肯定有-看下面


如上就是low_priority_updates 这个系统变量

  • 通过指定启动参数low_priority_updates是读请求优先
  • 通过执行命令set low_priority_updates=1使该连接发出的更新请求优先级降低
  • 通过指定insert,update,delete语句的low_priority属性,降低该语句优先级

最后一个问题,我们如何查看一张表的锁争用状态呢?---

show status like 'table_lock%';就是这个啦

Table_locks_immediate 指的是能够立即获得表级锁的次数 

Table_locks_waited 指的是不能立即获取表级锁而需要等待的次数,如果数量大,说明锁等待多,有锁争用情况


show OPEN TABLES where In_use > 0;  可以查看正在被锁定的表


好了了解完MyISAM的表锁我们来看看InnoDB的锁吧

emmm^-^在这里我假设你们已经知道事务和ACID属性基础知识了


InnoDB的行锁模式及加锁方法:

共享锁(S):允许事务去读一行,阻止其他事务获得相同数据集排它锁

select * from tbname where……lock in share mode;

排它锁(X):允许获得排它锁事务更新数据,阻止其他事务取得相同数据集共享锁和排它锁

select * from tbname where……for update;

另外为了允许锁和表锁共存,InnoDB还有两种内部使用的意向锁,他们都是表锁,是InnoDB自动加的,用户干预不了

意向共享锁(IS):事务打算给数据行加行共享锁,在这之前必须先取得该表IS锁;

意向排他锁(IX):事务打算给数据行加行排他锁,在这之前必须先取得该表IX锁;


举个例子谈谈InnoDB的共享锁吧^_^

session_1 session_2

set autocommit=0(因为innodb默认开始事务并且自动提交,

所以设置自动提交关闭需要手动commit或者rollback)


set commit=0


当前session对test_1的id=1记录加共享锁


 
 

其他的session仍然可以查询记录,并且对该记录加共享锁


当前session对锁定表记录进行更新操作,由于session_2也加了共享锁,所以需要等待锁



 
  其他的session对该记录进行更新操作,则会导致死锁退出

获得锁成功,可以更新


 

InnoDB排它锁的栗子^-^

session_1    session_2
 
 

其他的session可以查询该记录,但是不能对该记录加共享锁,会等待获得锁


当前session可以对锁定记录进行更新,更新之后释放锁


 
  其他的session获得锁,得到其他session提交记录
   

InnoDB行锁实现方式

InnoDB行锁是通过给索引加锁实现的,这句话隐藏信息很多:

1、在不通过索引条件查询时候,InnoDB使用的是表锁,而不是行锁



session_1 session_2
 
 

等待


现在为表tb_without_index增加主主键


再去执行session_2的最后一步


2、MySQL行锁因为是针对索引加的锁,所以虽然访问不同行记录,但是如果使用相同索引键,也会出现锁冲突的

session_1 ssession_2

3、当表有多个索引时候,不同事务可以使用不同的索引锁定不同的行--这个不举例了^-^

4、即使在条件中使用了索引字段,但是是否使用索引检索是由MySQL判断不同执行计划代价决定的


间隙锁

当我们使用范围条件而不是相等条件检索数据时候,并请求共享或者排他锁,他还会给在条件范围内但是实际不存在记录加锁,这种锁机制就叫做间隙锁

select * from tbname where id>100 for update;

其实如果使用相等条件请求给不存在记录加锁依然会使用间隙锁。

假设我们的数据记录只有1-130条记录,他不仅会对符合条件记录加锁,还会对大于130的间隙加锁。

之所以有这种锁一方面是为了防止出现幻读,满足相关隔离要求


最后我们应该如何去查看innodb行锁争用情况呢?

show status like 'innodb_row_lock%';

在这里如果InnoDB_row_lock_time_avg和InnoDB_row_lock_time_wait比较高说明争用情况严重,我们可以用以下分析:

create table innodb_monitor(a int) engine=innodb;

打开InnoDB Monitor监视器,向err日志中记录监控的内容

show engine innodb status;

查看内容

drop table innodb_monitor;

长时间打开,会导致.err文件变得非常的巨大。因此监控完后,一定要记得关闭监视器:


ok以上就是我个人浅陋学习,如果有不同地方还望指出共同学习!

ps:以上部分内容参考自深入浅出mysql数据库开发 优化与管理维护,这是一本很不错的书,大家可以买来学习^-^


猜你喜欢

转载自blog.csdn.net/m0_37752084/article/details/79833654