MySQL InnoDB存储引擎锁机制实验

标签: mysql innodb 引擎 | 发表时间:2013-04-04 21:36 | 作者:shenzhen_liubin
出处:http://blog.csdn.net

之前的文章提到MySQL的InnoDB存储引擎使用的是行级锁,并且默认的事务隔离级别为可重复读,而不同于Oracle默认的事务隔离级别提交读。那么MySQL的InnoDB存储引擎的锁机制的具体表现是怎样的呢?实验如下:

首先建立一张测试使用的表:

CREATE TABLE`test_innodb_lock` (

  `a` int(11) DEFAULT NULL,

  `b` varchar(16) DEFAULT NULL,

  KEY `test_innodb_lock_a_IDX` (`a`)

) ENGINE=InnoDB

        然后再往这张表里插入一些数据,以备使用,最终表数据如下:

+------+------+

| a   | b    |

+------+------+

|   1 | a    |

|   1 | x    |

|   1 | y    |

|   2 | b    |

|   2 | w    |

|   2 | z    |

|   3 | c    |

|   4 | d    |

|   5 | e    |

|   8 | ff   |

|   8 | f    |

|  10 | g    |

+------+------+

 

首先我们来看看行级锁的情况:

实验一:

打开两个MySQL客户端,

在客户端1执行:

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

修改客户端1的事务提交方式为手动提交;

 

在客户端2执行:

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

同样修改客户端2的事务提交方式为手动提交;

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where  a = 1 and b = 'y';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

同时使用索引字段a和非索引字段b更新一条数据;

 

在客户端2执行:

mysql> update test_innodb_lock set b ='xxx' where a=1 and b = 'x';

同时使用索引字段a(并且索引值同客户端1的值相同)和非索引字段 更新另外一条数据

结果发现客户端2的update语句被阻塞,需要客户端1提交或回滚才能继续执行。说明, 虽然两个事务最终更新的数据不是同一条数据,但然后可能被锁定,这是因为两条SQL语句都使用了相同的索引值(a=1),行级锁上升为页级锁。

实验二:

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验一的操作;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验一的操作;

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where  a = 1 and b = 'a';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

同时使用索引字段a和非索引字段b更新一条数据;

 

在客户端2执行:

mysql> update test_innodb_lock set b ='xxx' where a=2 and b = 'b';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

同时使用索引字段a(索引值不同于客户端1SQL语句的索引值)和非索引字段b更新一条数据;

更新顺利进行,执行并没有被阻塞;

说明,同是根据索引和非索引字段进行更新数据,当两个事务的SQL语句中的索引条件值不一样时,更新仍然能够顺利进行。

 

实验三:

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验一的操作;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验一的操作;

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where b = 'd';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

通过 非索引字段更新唯一的一条数据记录,

 

在客户端2执行:

mysql> update test_innodb_lock set b='xxx' where b ='e';

通过非索引字段更新另外一条唯一的一条数据记录,update语句被阻塞;

说明, 一个事务根据非索引字段更新数据时,InnoDB会将整个表给锁住,行级锁此时上升为表级锁。

 

实验四:

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验三的操作;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验三的操作;

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where a=4;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

只使用索引更新数据记录

 

在客户端2执行:

mysql> update test_innodb_lock set b ='xxx' where a=4;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

只使用索引更新数据记录,同时索引值与客户端1的索引值相同(a=4),此时,客户端2的update语句被阻塞。

说明,这个现象的行级锁,于我们理解的行级锁一致,即真正只是锁定了一条记录。

 

实验五:

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验四的操作;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验四的操作;

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where a=4;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1 Warnings: 0

只使用索引更新数据记录

 

在客户端2执行:

mysql> update test_innodb_lock set b ='xxx' where b=’g’;

只使用非索引字段更新数据记录,客户端2的update语句被阻塞,这是因为 客户端2的update语句由于没有使用索引,需要在数据表上加意向排他锁,但在a=4这条记录上,已经存在排他锁了,索引客户端2的update语句只能被阻塞。

 

以上实验说明:

1、       InnoDB的行级锁在有些情况下是会自动上升为页级锁和表级锁的,此时数据库的写性能会急剧下降,并可能出现大量的死锁(关于死锁的情况,很容易模仿出来,这里不在举例);

2、       真正的行级锁,只发生在所有的事务都是通过索引来进行检索数据的。

 

下面我们继续实验与间隙锁相关的情况:

实验六:

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验五的操作;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验五的操作;

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where a=8;

Query OK, 2 rows affected (0.00 sec)

Rows matched: 2  Changed: 2 Warnings: 0

通过索引更新数据记录,索引值为8;

 

在客户端2执行:

mysql> insert into test_innodb_lock(a,b)values(8,'xxx');

向数据表中插入一条数据,插入数据的索引列的值与客户端1的SQL语句的索引值相同,都为8,此时,insert语句被阻塞。

 

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

mysql> update test_innodb_lock set b ='xxx' where a=8;

Query OK, 2 rows affected (0.00 sec)

Rows matched: 2  Changed: 2 Warnings: 0

通过索引更新数据记录,索引值为8;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into test_innodb_lock(a,b)values(5,'xxx');

向数据表中插入一条数据,插入数据的索引列的值小于客户端1的SQL语句的索引值,但 大于或等于已有数据记录中最大小于检索索引(a=8)的索引值5,此时,insert语句被阻塞。

 

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

mysql> update test_innodb_lock set b ='xxx' where a=8;

Query OK, 2 rows affected (0.00 sec)

Rows matched: 2  Changed: 2 Warnings: 0

通过索引更新数据记录,索引值为8;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into test_innodb_lock(a,b)values(9,'xxx');

向数据表中插入一条数据,插入数据的索引列的值大于客户端1的SQL语句的索引值,但 小于已有数据记录中最小大于检索索引(a=8)的索引值10,此时,insert语句被阻塞。

 

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

mysql> update test_innodb_lock set b ='xxx' where a=8;

Query OK, 2 rows affected (0.00 sec)

Rows matched: 2  Changed: 2 Warnings: 0

通过索引更新数据记录,索引值为8;

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into test_innodb_lock(a,b)values(10,'xxx');

 

向数据表中插入一条数据,插入数据的索引列的值大于客户端1的SQL语句的索引值,且 大于或等于已有数据记录中最小大于检索索引(a=8)的索引值10,此时,insert语句顺利执行。

 

以上系列的动作说明,当一个事务在通过索引更新数据时,它会将该索引的前后紧紧相邻的索引记录锁住,包括那些根本就不存在的索引值,锁定的区间为左闭右开区间,即[x,y),其中x为小于事务中SQL语句索引值的最大值,y为大于事务中SQL语句索引值的最小值,在本例中,事务中SQL语句索引值为8,索引其锁定的区间为[5,10),所以另外一个事务在做insert操作时,索引值大于或等于5且小于10的索引记录都将被阻塞。需要注意的是,当更新事务的索引值为已有记录中最大值时,这时所有大于该索引值的记录,其他事务的insert操作都将被阻塞。这就是InnoDB间隙锁的具体表现。所以说,InnoDB的间隙锁避免了部分幻读,但不是全部,因为它锁定的是一个区间,而不是整张表。

 

实验七:

在客户端1执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验六的操作

 

在客户端2执行:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

回滚实验六的操作

 

在客户端1执行:

mysql> update test_innodb_lock set b ='xxx' where b=’a’;

Query OK, 2 rows affected (0.00 sec)

Rows matched: 2  Changed: 2 Warnings: 0

通过 非索引字段更新一条记录;

 

在客户端2执行:

mysql> insert into test_innodb_lock(a,b)values(10,'xxx');

插入一条完全不相关的数据,该insert语句被阻塞;

说明,当事务1通过 非索引字段更新一条数据是,整张表就会被锁住,即使是insert操作,也将被阻塞。

 

以上实验说明:

1、       InnoDB的间隙锁是可以避免数据出现幻读,但只是避免部分出现幻读,当一个事务是通过索引来更新数据是,另外一个事务在前一个事务索引值前后的左闭右开区间是不能并行插入数据的,必须等待上一个事务提交或回滚;

2、       当前一个事务不是通过索引字段来进行更新操作时,那么InnoDB的这种间隙锁就能够完全避免幻读的出现,因为它会将整个表锁住,在当前事务提交或回滚之前,阻塞所以insert操作。

 

说明:

1、        以上实验的所以update操作,更换为delete操作,效果完全一样;

2、        如果修改InnoDB的默认事务隔离级别,由可重复读修改为读已提交,那么以上现象均不会出现,所以这样的锁机制只在可重复读这一事务隔离级别出现,或者说这是InnoDB可重复读事务隔离级别的一种实现方式。


 

 

作者:shenzhen_liubin 发表于2013-4-4 21:36:13 原文链接
阅读:100 评论:0 查看评论

相关 [mysql innodb 引擎] 推荐:

Mysql Innodb 引擎优化

- 彦强 - 阿辉的空间
作/译者:吴炳锡,来源:http://imysql.cn/ & http://www.mysqlsupport.cn 转载请注明作/译者和出处,并且不能用于商业用途,违者必究. InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎. InnoDB锁定在行级并且也在SELECT语句提供 一个Oracle风格一致的非锁定读.

MySQL 5.1安装InnoDB引擎

- - Gsion's Blog
安装 innodb 引擎(mysql5.1默认不安装). 可以在编译安装时,在configrue的时候,加上--with-plugins=innobase这个参数. 如果之前已经安装过,也可补装innodb引擎. 首先确定,在mysql的'plugin_dir'下有ha_innodb_plugin.so和ha_innodb.so两个文件.

MySQL Innodb 存储引擎参数优化

- jinbiaozhao - 服务器运维与网站架构|Linux运维|互联网研究
InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎. InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风格一致的非锁定读. 这些特色增加了多用户部署和性能. 没有在InnoDB中扩大锁定的需要,因为在InnoDB中行级锁定适合非常小的空间.

[MySQL] 浅谈InnoDB存储引擎

- - CSDN博客数据库推荐文章
InnoDB是事务安全的存储引擎,设计上借鉴了很多Oracle的架构思想,一般而言,在OLTP应用中,InnoDB应该作为核心应用表的首先存储引擎. InnoDB是由第三方的Innobase Oy公司开发,现已被Oracle收购,创始人是Heikki Tuuri,芬兰赫尔辛基人,和著名的Linux创始人Linus是校友.

MySQL 預設儲存引擎: InnoDB 介紹

- - 小惡魔 - 電腦技術 - 工作筆記 - AppleBOY
MySQL 是一套眾所皆知的 Database System,今天來簡介 InnoDB 儲存引擎,在 MySQL 5.5.5 之前預設的儲存引擎是 MyISAM,但是為什麼在 5.5 之後官方要將預設儲存引擎換成 InnoDB 呢. 開始升級伺服器,一起體驗 InnoDB 的功能,如果已經上線很久的網站,作者不建議轉換,因為可能會遇到很多雷.

MySQL存储引擎--MyISAM与InnoDB区别

- - CSDN博客互联网推荐文章
MyISAM 和InnoDB 讲解   InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定. 基本的差别为:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持. MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能.

Mysql InnoDB锁

- - 数据库 - ITeye博客
抄自:http://www.cnblogs.com/qq78292959/archive/2013/01/30/2882745.html. Mysql常用存储引擎的锁机制. MyISAM和MEMORY采用表级锁(table-level locking). BDB采用页面锁(page-leve locking)或表级锁,默认为页面锁.

MySQL存储引擎MyISAM与InnoDB的主要区别对比

- timo - 服务器运维与网站架构|Linux运维|互联网研究
本文整理了Mysql 两大常用的存储引擎MyISAM,InnoDB的六大常见区别,来源于Mysql手册以及互联网的资料. InnoDB与Myisam的六大区别. MyISAM InnoDB 构 成上的区别: 每个MyISAM在磁盘上存储成三个文件. 第一个 文件的名字以表的名字开始,扩展名指出文件类型.

MySQL InnoDB存储引擎锁机制实验

- - CSDN博客数据库推荐文章
之前的文章提到MySQL的InnoDB存储引擎使用的是行级锁,并且默认的事务隔离级别为可重复读,而不同于Oracle默认的事务隔离级别提交读. 那么MySQL的InnoDB存储引擎的锁机制的具体表现是怎样的呢. 首先建立一张测试使用的表:.         然后再往这张表里插入一些数据,以备使用,最终表数据如下:.

mysql技术内幕-innodb存储引擎读书笔记(上)

- - MySQLOPS 数据库与运维自动化技术分享
第一章、mysql体系结构和存储引擎. 1.1、数据库和实例的区别. 数据库:物理操作系统或其他形式文件类型的集合. 在mysql下数据库文件可以是frm,myd,myi,ibd结尾的文件. 数据库实例:由数据库后台进程/线程以及一个共享内存区组成. mysql数据库是但进程多线程的程序. 1.2、mysql的体系结构.