Mysql锁学习
注意:内容来源是其他网友的总结,个人进行的总结学习,忘记记录原来的网址了,之后有机会我找出并进行标记,抱歉。
一、Mysql锁的类型
- 共享/排它锁(Shared and Exclusive Locks)
- 设置共享锁:select * from user where id = 1 LOCK IN SHARE MODE; 读取某行数据
- 设置排他锁:select * from user where id = 1 FOR UPDATE;修改某些数据
- S锁和X锁
- S shared lock 共享锁 读锁
其他只能增加S锁,不能增加X锁,只允许读,S锁解除之前,不允许修改。 - X exclusive lock 排他锁 写锁
其他不能操作,得到锁的对象可以增删改查。
- S shared lock 共享锁 读锁
- 意向锁(Intention Locks) 使得行锁和表锁能够共存
意向锁是表级别的锁,用来说明事务稍后会对表中的数据行加哪种类型的锁(共享锁或独占锁) - 记录锁(Record Locks) 索引记录上的锁
锁定索引记录,没有索引记录,就去锁定隐式(聚集索引) - 间隙锁(Gap Locks)
间隙锁是一个在索引记录之间的间隙上的锁 - 临键锁(Next-key Locks)
Next-Key Locks是行锁与间隙锁的组合。当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上记录锁(Record Lock),然后再对索引记录两边的间隙加上间隙锁(Gap Lock) - 插入意向锁(Insert Intention Locks)
插入意向锁是在数据行插入之前通过插入操作设置的间隙锁定类型 - 自增锁(Auto-inc Locks)
自增锁是事务插入到有自增列的表中而获得的一种特殊的表级锁
二、锁的实现方式
- InnoDB行锁是通过给索引加锁来实现的,如果没有索引,InnoDB会通过隐藏的聚簇索引来对记录进行加锁(全表扫描,也就是表锁)
- 语句操作,有主键的列(锁主键索引),无主键的列(先锁无主键索引,再锁主键索引)
三、隔离级别
- 读未提交(Read Uncommitted)
无锁 - 读提交(Read Committed, RC)
select是快照读,update\delete 显示加锁的select
1、外键约束检查和重复键封锁区间
2、其他记录锁 - 可重复读(Repeated Read, RR)
select是快照读,update\delete 显示加锁的select
1、索引上使用唯一的查询条件,使用记录锁
2、唯一索引上使用范围查询,使用间隙锁和临键锁 - 串行化(Serializable)
此时所有select语句都会被隐式加锁:select … in share mode.
四、快照读、当前读
- 快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。
- 当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
五、MVCC:multi-version concurrency control
- MVCC定义:多版并发控制系统。可认为是行级锁的一个变种,它能够避免更多情况下的加锁操作。
- 作用:避免一些加锁操作,提升并发性能
- 实现:通过在每行记录的后面保存行的创建时间和过期时间或删除时间(它们是隐藏的),这两个时间实际都是系统的版本号。每开始一个新的事务,版本号都会自动增加
- 具体原理
4.1) select:innoBD查询时会检查以下两个条件:一个是数据行的版本号早于当前事务的版本号;另一个是行的删除版本号,要么没有,要么大于当前事务的版本号。
4.2)insert/delete:innoDB将当前的系统版本号作为新插入(删除)的数据行的版本号。
4.3)update:先新插入一行数据,并将当前系统版本号作为行的版本号,同时将当前系统版本号作为原来行的删除版本号。更新主键时,聚集索引和普通索引都会产生两个版本;而更新非主键时,只有普通索引会产生两个版本。 - 注意:MVCC只在read committed和repeatable read两个隔离级别下工作
快照读:RR 重复读 select
当前读 insert into update delete 显示加锁的select
六、行级锁的争用情况
- 显示争用情况
show status like ‘InnoDB_row_lock%’; - 设置监视器:mysql> create table InnoDB_monitor(a INT) engine=InnoDB;
- 查看:mysql> show engine InnoDB status;
- 停止查看:mysql> drop table InnoDB_monitor;
七、死锁:
- 什么是死锁:一般是由于两个事务同时操作两个表,但加锁的顺序是不一致出现的。比如A事务先锁a表,B事务先锁b表;当A去锁b表的时候发现b表被B事务锁住了,要等待;当B事务去锁a表的时候发现a表被A锁住了。于是出现了死锁
- 解决方式:回滚较小的那个事务
当产生死锁的场景中涉及到不止InnoDB存储引擎的时候,InnoDB是没办法检测到该死锁的,这时候就只能通过锁定超时限制参数InnoDB_lock_wait_timeout来解决。
八、优化行级锁
-
要想合理利用InnoDB的行级锁定,做到扬长避短,我们必须做好以下工作:
a)尽可能让所有的数据检索都通过索引来完成,从而避免InnoDB因为无法通过索引键加锁而升级为表级锁定;
b)合理设计索引,让InnoDB在索引键上面加锁的时候尽可能准确,尽可能的缩小锁定范围,避免造成不必要的锁定而影响其他Query的执行;
c)尽可能减少基于范围的数据检索过滤条件,避免因为间隙锁带来的负面影响而锁定了不该锁定的记录;
d)尽量控制事务的大小,减少锁定的资源量和锁定时间长度;
e)在业务环境允许的情况下,尽量使用较低级别的事务隔离,以减少MySQL因为实现事务隔离级别所带来的附加成本。 -
由于InnoDB的行级锁定和事务性,所以肯定会产生死锁,下面是一些比较常用的减少死锁产生概率的小建议:
a)类似业务模块中,尽可能按照相同的访问顺序来访问,防止产生死锁;
b)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
c)对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。
查看sql的锁信息 -
查看sql句的锁信息前,需要做如下几件事:
-
查看事务的隔离级别:
- 通过show global variables like “tx_isolation”; 命令查看。
- 可通过执行set session transaction isolation level repeatable read;
- 更改成我们想要隔离级别,隔离级别取值如下:
- read uncommitted、read committed、repeatable read、serializable
-
保证事务为手动提交:
- 通过show global variables like “autocommit”;查看。
- 如果为ON,则通过执行set session autocommit=0;改为手动提交。
-
保证间隙锁开启:
- 通过show global variables like “innodb_locks%”;查看
OFF时表示开启。默认是OFF
- 通过show global variables like “innodb_locks%”;查看
-
九、MVCC讲解
-
Mysql有三种级别的锁定:表级锁定、页级锁定、行级锁定
每次锁定的是一行数据的锁机制就是行级别锁定(row-level)。行级锁定不是MySQL自己实现的锁定方式,而是由其他存储引擎自己所实现的 -
优缺点:由于锁粒度小,争用率低,并发高
实现复杂,开销大,加锁慢、容易出现死锁
使用行级锁定的主要有InnoDB存储引擎,以及MySQL的分布式存储引擎NDBCluster扫描二维码关注公众号,回复: 13117141 查看本文章 -
行级锁 共享锁和排他锁
推荐阅读
(原始引用位置已经丢失,抱歉,推荐找到的好的路径)
[1] mysql 查询通过时间预定_阿里达摩院推荐Mysql面试题答案,助你“过五关斩六将”