MySQL锁概述

相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

MySQL这3种锁的特性可大致归纳如下。
开销、加锁速度、死锁、粒度、并发性能
l 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
l 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
l 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

仅从锁的角度来说:表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

MyISAM表锁(偏读)

MyISAM存储引擎只支持表锁

查询表级锁争用情况

show status like 'table%';

mysql对应的 lateral view mysql leading_间隙锁

Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1

Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,没等待一次锁值加1)。此值比较高则说明存在着较严重的表级锁争用情况

MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。锁模式的兼容性如下表:

兼容性

None(请求锁模式)

读锁(请求锁模式)

写锁(请求锁模式)

读锁(当前锁模式)




写锁(当前锁模式)




可见:
对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;

MyISAM表的读操作与写操作之间,以及写操作之间是串行的。当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。

InnoDB表锁(偏写)

InnoDB行锁是通过给索引上的索引项加锁来实现的,InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

获取InnoDB行锁争用情况

show status like 'innodb_row_lock%';

mysql对应的 lateral view mysql leading_mysql_02

对各状态量的说明如下:

mysql对应的 lateral view mysql leading_表锁_03

示例

mysql对应的 lateral view mysql leading_mysql_04

通过非索引条件检索数据,InnoDB使用表级锁例子

session1

session2

mysql对应的 lateral view mysql leading_mysql_05

mysql对应的 lateral view mysql leading_表锁_06

mysql对应的 lateral view mysql leading_间隙锁_07

-

-

mysql对应的 lateral view mysql leading_mysql_08

// 等待挂起

mysql对应的 lateral view mysql leading_mysql_09

-

-

mysql对应的 lateral view mysql leading_间隙锁_10

// 超时

通过索引条件检索数据,InnoDB使用行级锁例子

session1

session2

mysql对应的 lateral view mysql leading_表锁_11

mysql对应的 lateral view mysql leading_间隙锁_12

mysql对应的 lateral view mysql leading_表锁_13

-

-

mysql对应的 lateral view mysql leading_行锁_14

索引失效 行锁变表锁

mysql对应的 lateral view mysql leading_数据_15

session1

session2

mysql对应的 lateral view mysql leading_数据_16

-

-

mysql对应的 lateral view mysql leading_mysql_17

// 等待挂起

间隙锁

间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。

间隙锁是innodb中行锁的一种,但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围。间隙锁的主要作用是为了防止出现幻读,但是它会把锁定范围扩大,在某些场景下这可能会对性能造成很大的危害。

示例:如果一个间隙被事务T1加了锁,其它事务是不能在这个间隙插入记录的。

session1

session2

mysql对应的 lateral view mysql leading_数据_18

-

-

mysql对应的 lateral view mysql leading_数据_19

// 等待挂起

mysql对应的 lateral view mysql leading_行锁_20

mysql对应的 lateral view mysql leading_行锁_21

间隙锁的目的是为了防止幻读,其主要通过两个方面实现这个目的:
(1)防止间隙内有新数据被插入
(2)防止已存在的数据,更新成间隙内的数据(例如防止numer=3的记录通过update变成number=5)

innodb自动使用间隙锁的条件:
(1)必须在RR级别下
(2)检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)

如何锁定一行

FOR UPDATE仅适用于InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效。

select * from test_innodb where id = 1 for update;

假设有个表单products ,里面有id跟name二个栏位,id是主键。

例1: (明确指定主键,并且有此记录,row lock)
SELECT * FROM products WHERE id=’3’ FOR UPDATE;
SELECT * FROM products WHERE id=’3’ and type=1 FOR UPDATE;

例2: (明确指定主键,若查无此记录,无lock)
SELECT * FROM products WHERE id=’-1’ FOR UPDATE;

例2: (无主键,table lock)
SELECT * FROM products WHERE name=’Mouse’ FOR UPDATE;

例3: (主键不明确,table lock)
SELECT * FROM products WHERE id<>’3’ FOR UPDATE;

例4: (主键不明确,table lock)
SELECT * FROM products WHERE id LIKE ‘3’ FOR UPDATE;