Mysql InnoDB行锁实现方式

mysql中InnoDB存储引擎的行锁和表锁

InnoDB行锁是经过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不一样,后者是经过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特色意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁!
 
在实际应用中,要特别注意InnoDB行锁的这一特性,否则的话,可能致使大量的锁冲突,从而影响并发性能。下面经过一些实际例子来加以说明。
(1)在不经过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。
 
在如表20-9所示的例子中,开始tab_no_index表没有索引:html

mysql> create table tab_no_index(id int,name varchar(10)) engine=innodb;
Query OK, 0 rows affected (0.15 sec)
mysql> insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

表20-9         InnoDB存储引擎的表在不使用索引时使用表锁例子
session_1mysql

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where id = 1 ;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)

mysql> select * from tab_no_index where id = 1 for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)

session_2sql

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where id = 2 ;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
mysql> select * from tab_no_index where id = 2 for update;
等待

在如表20-9所示的例子中,看起来session_1只给一行加了排他锁,但session_2在请求其余行的排他锁时,却出现了锁等待!缘由就是在没有索引的状况下,InnoDB只能使用表锁。当咱们给其增长一个索引后,InnoDB就只锁定了符合条件的行,如表20-10所示。
 
建立tab_with_index表,id字段有普通索引:bash

mysql> create table tab_with_index(id int,name varchar(10)) engine=innodb;
Query OK, 0 rows affected (0.15 sec)
mysql> alter table tab_with_index add index id(id);
Query OK, 4 rows affected (0.24 sec)
Records: 4  Duplicates: 0  Warnings: 0

表20-10    InnoDB存储引擎的表在使用索引时使用行锁例子
  www.2cto.com  
session_1
session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 ;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 2 ;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
mysql> select * from tab_with_index where id = 1 for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
 
mysql> select * from tab_with_index where id = 2 for update;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
(2)因为MySQL的行锁是针对索引加的锁,不是针对记录加的锁,因此虽然是访问不一样行的记录,可是若是是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。
 
在如表20-11所示的例子中,表tab_with_index的id字段有索引,name字段没有索引:
 
mysql> alter table tab_with_index drop index name;
Query OK, 4 rows affected (0.22 sec)
Records: 4  Duplicates: 0  Warnings: 0
mysql> insert into tab_with_index  values(1,'4');
Query OK, 1 row affected (0.00 sec)
mysql> select * from tab_with_index where id = 1;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
| 1    | 4    |
+------+------+
2 rows in set (0.00 sec)
表20-11    InnoDB存储引擎使用相同索引键的阻塞例子
  www.2cto.com  
session_1
session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 and name = '1' for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
 
虽然session_2访问的是和session_1不一样的记录,可是由于使用了相同的索引,因此须要等待锁:
mysql> select * from tab_with_index where id = 1 and name = '4' for update;
等待
(3)当表有多个索引的时候,不一样的事务可使用不一样的索引锁定不一样的行,另外,不管是使用主键索引、惟一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
  www.2cto.com  
在如表20-12所示的例子中,表tab_with_index的id字段有主键索引,name字段有普通索引:
 
mysql> alter table tab_with_index add index name(name);
Query OK, 5 rows affected (0.23 sec)
Records: 5  Duplicates: 0  Warnings: 0
表20-12    InnoDB存储引擎的表使用不一样索引的阻塞例子
 
·          session_1
·          session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
| 1    | 4    |
+------+------+
2 rows in set (0.00 sec)
   www.2cto.com  
Session_2使用name的索引访问记录,由于记录没有被索引,因此能够得到锁:
mysql> select * from tab_with_index where name = '2' for update;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
 
因为访问的记录已经被session_1锁定,因此等待得到锁。:
mysql> select * from tab_with_index where name = '4' for update;
(4)即使在条件中使用了索引字段,可是否使用索引来检索数据是由MySQL经过判断不一样执行计划的代价来决定的,若是MySQL认为全表扫描效率更高,好比对一些很小的表,它就不会使用索引,这种状况下InnoDB将使用表锁,而不是行锁。所以,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。关于MySQL在什么状况下不使用索引的详细讨论,参见本章“索引问题”一节的介绍。
  www.2cto.com  
在下面的例子中,检索值的数据类型与索引字段不一样,虽然MySQL可以进行数据类型转换,但却不会使用索引,从而致使InnoDB使用表锁。经过用explain检查两条SQL的执行计划,咱们能够清楚地看到了这一点。
 
例子中tab_with_index表的name字段有索引,可是name字段是varchar类型的,若是where条件中不是和varchar类型进行比较,则会对name进行类型转换,而执行的全表扫描。
 
mysql> alter table tab_no_index add index name(name);
Query OK, 4 rows affected (8.06 sec)
Records: 4  Duplicates: 0  Warnings: 0
mysql> explain select * from tab_with_index where name = 1 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tab_with_index
type: ALL
possible_keys: name
key: NULL
key_len: NULL
ref: NULL
rows: 4
Extra: Using where
1 row in set (0.00 sec)
mysql> explain select * from tab_with_index where name = '1' \G
*************************** 1. row ***************************
id: 1  www.2cto.com  
select_type: SIMPLE
table: tab_with_index
type: ref
possible_keys: name
key: name
key_len: 23
ref: const
rows: 1
Extra: Using where
1 row in set (0.00 sec)session

相关文章
相关标签/搜索