概要


Locking read( SELECT ... FOR UPDATE or SELECT ... LOCK IN SHARE MODE),UPDATE以及DELETE语句通常会在他扫描的索引所有范围上加锁,忽略没有用到索引的那部分where语句。
举个例子:

CREATE TABLE `test` (
`id` int(11) NOT NULL DEFAULT '0',
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 select * from test where id > 3 and name <'A' for update;

这条SQL语句的会将所有id>3的记录进行加锁,而不是id>3 and name <'A' 进行加锁,因为name上面没有索引。

如果一个SQL通过二级索引进行扫描,并且在二级索引上设置了一个锁,那么innodb将会在对应的聚簇索引记录上也加上一把锁。

如果一个SQL语句无法通过索引进行Locking readUPDATEDELETE,那么MySQL将扫描整个表,表中的每一行都将被锁定(在RC级别,通过semi-consistent read,能够提前释放不符合条件的记录,在RR级别,需要设置innodb_locks_unsafe_for_binlog为1,才能打开semi-consistent read)。在某些场景下,锁也不会立即被释放。例如一个union查询,生成 了一张临时表,导致临时表的行记录和原始表的行记录丢失了联系,只能等待查询执行结束才能释放。

SQL分析


1.SELECT ... FROM 是一个快照读,通过读取数据库的一个快照,不会加任何锁,除非将隔离级别设置成了 SERIALIZABLE 。在 SERIALIZABLE 隔离级别下,如果索引是非唯一索引,那么将在相应的记录上加上一个共享的next key锁。如果是唯一索引,只需要在相应记录上加index record lock

2. SELECT ... FROM ... LOCK IN SHARE MODE 语句在所有索引扫描范围的索引记录上加上共享的next key锁。如果是唯一索引,只需要在相应记录上加index record lock

3. SELECT ... FROM ... FOR UPDATE 语句在所有索引扫描范围的索引记录上加上排他的next key锁。如果是唯一索引,只需要在相应记录上加index record lock。这将堵塞其他会话利用SELECT ... FROM ... LOCK IN SHARE MODE 读取相同的记录,但是快照读将忽略记录上的锁。

4. UPDATE ... WHERE ...语句在所有索引扫描范围的索引记录上加上排他的next key锁。如果是唯一索引,只需要在相应记录上加index record lock

UPDATE 操作修改主键记录的时候,将在相应的二级索引上加上隐式的锁。当进行重复键检测的时候,将会在插入新的二级索引记录之前,在其二级索引上加上一把共享锁。

5. DELETE FROM ... WHERE ... 语句在所有索引扫描范围的索引记录上加上排他的next key锁。如果是唯一索引,只需要在相应记录上加index record lock

6. INSERT 语句将在插入的记录上加一把排他锁,这个锁是一个index-record lock,并不是next-key锁,因此就没有gap 锁,他将不会阻止其他会话在该条记录之前的gap插入记录。

在插入记录之前,将会加上一种叫做 insert intention gap 的 gap 锁。这个 insert intention gap表示他有意向在这个index gap插入记录,如果其他会话在这个index gap中插入的位置不相同,那么将不需要等待。假设存在索引记录4和7,会话A要插入记录5,会话B要插入记录6,每个会话在插入记录之前都需要锁定4和7之间gap,但是他们彼此不会互相堵塞,因为插入的位置不相同。

如果出现了重复键错误,将在重复键上加一个共享锁。如果会话1插入一条记录,没有提交,他会在该记录上加上排他锁,会话2和会话3都尝试插入该重复记录,那么他们都会被堵塞,会话2和会话3将尝试在该记录上加一个共享锁。如果此时会话1回滚,将发生死锁。

例子如下:
表结构:
CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;

Session 1:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

Session 2:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

Session 3:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

Session 1:

ROLLBACK;

为什么会发生死锁呢?当会话1进行回滚的时候,记录上的排他锁释放了,会话2和会话3都获得了共享锁。然后会话2和会话3都想要获得排他锁,进而发生了死锁。

还有一个类似的例子

Session 1:

START TRANSACTION;
DELETE FROM t1 WHERE i = 1;

Session 2:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

Session 3:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

Session 1:

COMMIT;

会话1在该记录上拥有一把排他锁,会话2和会话3都碰到了重复记录,因此都在申请共享锁。当会话1提交之后,会话1释放了排他锁,之后的会话2会话3先后获得了共享锁,此时他们发生了死锁,因为会话2和会话3都无法或者排他锁,因为彼此都占用了该记录的共享锁。

7. INSERT ... ON DUPLICATE KEY UPDATE 和普通的INSERT并不相同。如果碰到重复键值,INSERT ... ON DUPLICATE KEY UPDATE 将在记录上加排他的 next-key锁。

8. REPLACE 在没有碰到重复键值的时候和普通的INSERT是一样的,如果碰到重复键,将在记录上加一个排他的 next-key锁。

9. INSERT INTO T SELECT ... FROM S WHERE ... 语句在插入T表的每条记录上加上 index record lock 。如果隔离级别是 READ COMMITTED, 或者启用了 innodb_locks_unsafe_for_binlog 且事务隔离级别不是SERIALIZABLE,那么innodb将通过快照读取表S(no locks)。否则,innodb将在S的记录上加共享的next-key锁。

CREATE TABLE ... SELECT ... 和 INSERT INTO T SELECT ... FROM S WHERE ... 一样,在S上加共享的next-key锁或者进行快照读取((no locks)

10. REPLACE INTO t SELECT ... FROM s WHERE ... 和 UPDATE t ... WHERE col IN (SELECT ... FROM s ...) 中的select 部分将在表s上加共享的next-key锁。

11. 当碰到有自增列的表的时候,innodb在自增列的索引最后面加上一个排他锁,叫AUTO-INC table lock AUTO-INC table lock会在语句执行完成后进行释放,而不是事务结束。如果AUTO-INC table lock被一个会话占有,那么其他会话将无法在该表中插入数据。innodb可以预先获取sql需要多少自增的大小,而不需要去申请锁,更多设置请参考参数innodb_autoinc_lock_mode.

12.如果一张表的外键约束被启用了,任何在该表上的插入、更新、删除都将需要加共享的record-level locks来检查是否满足约束。如果约束检查失败,innodb也会加上共享的record-level locks

13. lock tables 是用来加表级锁,但是是MySQL的server层来加这把锁的。当innodb_table_locks = 1 (the default) 以及 autocommit = 0的时候,innodb能够感知表锁,同时server层了解到innodb已经加了row-level locks。否则,innodb将无法自动检测到死锁,同时server无法确定是否有行级锁,导致当其他会话占用行级锁的时候还能获得表锁。

锁测试注意点


1. 当使用begin开启一个事务的时候,之后的查询并不是获取的begin 命令的时间点快照,而且begin命令之后第一个查询的时间点快照。

CREATE TABLE T2 (id INT,name varchar(10) ,PRIMARY KEY (id)) ENGINE = InnoDB;
INSERT INTO T2 VALUES(1,'zhangsan'),(2,'lisi'); SESS1: SESS2:
BEGIN;
BEGIN;
INSERT INTO T2 values(3,'wangwu');
COMMIT;
SELECT * FROM T2;
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | wangwu |
+----+----------+

在可重复读的情况下,为什么会出现这样的情况呢?

原因就是SESS1 没有BEGIN之后开启一个查询,导致SESS1的select * from t2 查询的快照是执行该SQL的快照,而不是BEGIN那个时间点的快照,而此时,SESS2已经提交。

2. 我们来看一个例子:

CREATE TABLE T3 (id INT,name varchar(10) ,PRIMARY KEY (id)) ENGINE = InnoDB;
INSERT INTO T3 VALUES(1,'a'),(2,'b'),(3,'c'); SESS1: SESS2:
BEGIN;
SELECT * FROM T3;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
+----+------+
BEGIN;
INSERT INTO T3 values(4,'a');
COMMIT;
SELECT * FROM T3;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
+----+------+
UPDATE T3 SET NAME='aa' where name ='a';
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
SELECT * FROM T3;
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 2 | b |
| 3 | c |
| 4 | aa |
+----+------+
SELECT * FROM T3;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | a |
+----+------+

在可重复读的情况下,为什么SESS1先开启事务的情况下,还能更新会话2后来提交的数据呢?然后之后的select还能看到更新后的数据?我们查看下官方解释。

A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time. The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions. The exception to this rule is that the query sees the changes made by earlier statements within the same transaction. This exception causes the following anomaly: If you update some rows in a table, a SELECT sees the latest version of the updated rows, but it might also see older versions of any rows. If other sessions simultaneously update the same table, the anomaly means that you might see the table in a state that never existed in the database.

默认情况下,innodb使用MVCC进行快照读。查询只能查看到之前提交的事务更改的数据,之后提交的数据或者没有提交的事务数据是没办法看到的。但是这里有个例外就是同一个事务的查询能看到同一个事务里面的更改。因此当SESS1 在进行UPDATE的时候,会进行当前读,也就是读取所有已经提交的数据,相当于读取的是select * from t3 where where name ='a' for update;的结果集,然后进行UPDATE。之后的select * from t3 where where name ='a'看到的就是当前UPDATE之后的结果了。

参考资料:http://docs.fordba.com/mysql/refman-5.6-en/innodb-storage-engine.html#innodb-locks-set

MySQL innodb中各种SQL语句加锁分析的更多相关文章

  1. SQL语句加锁分析

    背景 MySQL中SQL加锁的情况十分复杂,不同隔离级别.不同索引类型.索引是否命中的SQL加锁各不相同. 然而在分析死锁过程当中,熟知各种情况的SQL加锁是分析死锁的关键,因此需要将MySQL的各种 ...

  2. InnoDB 中不同SQL语句设置的锁

    锁定读.UPDATE 或 DELETE 通常会给在SQL语句处理过程扫描到的每个索引记录上设置记录锁.语句中是否存在排除该行的WHERE条件并不重要.InnoDB不记得确切的WHERE条件,但只知道哪 ...

  3. MySQL中一条SQL的加锁分析

    MySQL中一条SQL的加锁分析 id主键 + RC id唯一索引 + RC id非唯一索引 + RC id无索引 + RC id主键 + RR id唯一索引 + RR id非唯一索引 + RR id ...

  4. 在phpmyadmin中执行sql语句出现的错误:Unknown storage engine 'InnoDB'

    在phpmyadmin中执行sql语句出现的错误:Unknown storage engine 'InnoDB' 解决方法:解决方法:             1.关闭MySQL数据库       2 ...

  5. Shell脚本中执行sql语句操作mysql的5种方法【转】

    对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用sql语句的几种方法,供大家参考.对于脚本输出的 ...

  6. MySQL中执行sql语句错误 Error Code: 1093. You can't specify target table 'car' for update in FROM clause

    MySQL中执行sql语句错误 Error Code: 1093. You can't specify target table 'car' for update in FROM clause 201 ...

  7. 浅谈MySQL中优化sql语句查询常用的30种方法 - 转载

    浅谈MySQL中优化sql语句查询常用的30种方法 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使 ...

  8. MySql InnoDB中的锁研究

    # MySql InnoDB中的锁研究 ## 1.InnoDB中有哪些锁### 1. 共享和排他(独占)锁(Shared and Exclusive Locks) InnoDB实现标准的行级锁定,其中 ...

  9. 在mybatis中写sql语句的一些体会

    本文会使用一个案例,就mybatis的一些基础语法进行讲解.案例中使用到的数据库表和对象如下: article表:这个表存放的是文章的基础信息 -- ------------------------- ...

随机推荐

  1. CNN网络架构演进

    卷积神经网络可谓是现在深度学习领域中大红大紫的网络框架,尤其在计算机视觉领域更是一枝独秀.CNN从90年代的LeNet开始,21世纪初沉寂了10年,直到12年AlexNet开始又再焕发第二春,从ZF ...

  2. Mysql的select in会自动过滤重复的数据

    默认使用 SELECT 语句: 当加上in范围后,结果如下图: in范围内的数据,如果有重复的,只会选择第一个数据. 所以如果不是直接使用SQL语句来查询,而是在代码中来查询时,记得使用 distin ...

  3. postman参数化 接口响应数据获取符合条件的内容参数化给后面的接口使用

    一:主要内容 从响应结果中找到满足条件的key,获取其value,参数化给后面的接口使用 二:参数化获取想要的value值,传给后面的接口使用 有时我们获取的响应数据,需要的那个字段可能在一个数组里面 ...

  4. SQL SERVER存储过程中使用事务

    存储过程格式: CREATE PROCEDURE YourProcedure AS BEGIN SET NOCOUNT ON; BEGIN TRY---------------------开始捕捉异常 ...

  5. swift 基础小结01 --delegate、Optional、GCD的使用、request请求、网络加载图片并保存到沙箱、闭包以及桥接

    本文主要记录swift中delegate的使用.“?!”Optional的概念.GCD的使用.request请求.网络加载图片并保存到沙箱.闭包以及桥接. 一.delegate的使用 swift中de ...

  6. 使用Hibernate Validator来帮你做数据校验

    数据校验是贯穿所有应用程序层(从表示层到持久层)的常见任务.通常在每个层中实现相同的验证逻辑,这是耗时且容易出错的.这里我们可以使用Hibernate Validator来帮助我处理这项任务.对此,H ...

  7. oracle安装与备份导入

    win10安装oracle因运行版本问题导致安装时提示错误(可能win10未被甲骨文公司认证)  跳过的问题 需要更改配置文件: 配置位置在 : 具体操作如下图: 在安装时win10跳过了 许是因为环 ...

  8. Software-Defined Networking A Comprehensive Survey --阅读_day2

    3. 什么是SDN?(WHAT IS SOFTWARE-DEFINED NETWORKING?) The term SDN was originally coined to represent the ...

  9. thinkphp5+qrcode生成二维码

    1.下载二维码插件Phpqrcode,地址 https://sourceforge.net/projects/phpqrcode/files/,把下载的文件夹放到\thinkphp\vendor下 2 ...

  10. 设计模式(9)--Composite(组合模式)--结构型

    1.模式定义: 组合模式属于对象的结构模式,有时又叫做“部分——整体”模式.组合模式将对象组织到树结构中,可以用来描述整体与部分的关系.组合模式可以使客户端将单纯元素与复合元素同等看待. 2.模式特点 ...