关于MySQL锁的机制。
锁的概念:锁机制用于管理对共享资源的并发访问,是数据库系统区别文件系统的关键特性。
通常,我们根据对数据操作的类型(读/写)或对数据操作的粒度(行/表)来对锁进行分类。
a、按类型分:
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
- 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
b、按粒度分:
- 行锁(偏写):操作时只锁某一行,不对其它行有影响。
- 表锁(偏读):即使操作一条记录也会锁住整个表。
这里,我们主要了解行锁和表锁。
一、表锁
表锁的话,偏向MyISAM存储引擎,因为它开销小,加锁快;无死锁;锁定粒度大(针对表),发生锁冲突的概率最高,并发度最低。
了解表锁,在这里我们通过开启两个会话来了解,我们在会话一中开启锁或解除锁的机制。
首先,给出开启锁、解除锁以及显示所有锁的语法:
-- 给表添加读/写锁:
lock table 表名1 read, 表名2 write;
-- 释放表锁
unlock table 表名;
-- 显示库中所有表中锁的情况
show open tables;
-- 分析表锁定
-- 通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定:
show status like 'table%';
-- table_locks_immediate:产生表级锁定的次数,表示可以立即获得锁的查询次数,每立即获取锁,值加1
-- table_locks_waited:出现表级锁定争用而发生等待的次数(即不能立即获得锁的次数,每等待一次锁值
-- 加1),这个值高则说明存在着较严重的表级锁争用情况。
假定库中有mylock、book两张表:
了解表锁中的读锁特点:
会话一 | 会话二 |
![]() | |
上锁后当前会话可以查询表中内容 | 其他会话同样可以查询上读锁后的内容 |
当前会话不能访问其他表 | 其他会话可以访问其他表 |
因为有读锁,当前会话不能对表进行增删改 | 其他会话增删改时会进入无限的等待。 |
释放表锁 | 释放的同时,该会话的任务完成。 |
了解表锁中的写锁特点:
会话一 | 会话二 |
![]() | |
当前会话不能访问未上锁的其他表。 | 其他会话可以访问未上锁的其他表。 |
当前会话对未上锁的其他表可以增删改 | |
当前会话可以访问写锁的表内容 | 其他会话访问时进入无限的等待。 |
当前会话可以对写锁中内容进行增删查改 | |
释放写锁 | 与此同时,会话二的查询语句得到结果 |
简而言之,就是读锁会阻塞写,但是不会堵塞读,而写锁则会把读和写都堵塞。
二、行锁
行锁的话,偏向于InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。(题外话:InnoDB与MyISAM的最大不同点在于支持事务和采用行锁)
首先,建立两张表:
-- 建表
create table if not exists innodb_lock(
a int (11),
b varchar(16)
)engine innodb charset utf8;
-- 插入数据
insert into innodb_lock
values
(1,'a2'),
(2,'2'),
(3,'hate'),
(4,'ugly'),
(5,'cute'),
(6,'6000'),
(7,'a1');
-- 建立索引
create index idx_innodb_a on innodb_lock(a);
create index idx_innodb_b on innodb_lock(b);
--手动开启行锁
begin;
select * from innodb_lock where a = 1 for update;
--针对该行数据的增删改操作
commit;
--行锁分析
show status like 'innodb_row_lock%';
-- innodb_row_lock_current_waits:当前正在等待锁定的数量
-- innodb_row_lock_time:系统启动到现在锁定总时间长度
-- innodb_row_lock_time_avg:每次等待所花的平均时间
-- innodb_row_lock_time_max:系统启动到现在等待最长的一次所花的时间
-- innodb_row_lock_waits:系统启动后到目前总共等待的次数
引擎innoDB,包含事务与行级锁,在事务中默认以分号为自动提交。
同样,我们开启两个会话来比较:
会话一 | 会话二 |
![]() | ![]() |
更新,但是没有提交 | 此时,另一会话中查询结果无变化 |
手动提交 | 手动提交 |
提交后查询,结果已经更新 | |
会话更新,但未手动提交 | 另一会话更新同一行记录,进入无限的等待 |
手动提交 | 立即执行语句
|
完成更新 | |
两个会话更新不同行记录,不会发生阻塞,手动提交后执行 | 两个会话更新不同行记录,不会发生阻塞,手动提交后执行 |
索引失效,导致行锁升级为表锁 | 0行受影响,说明行锁已升级为表锁 |
注意项:
间隙锁:用范围条件检索数据,并请求共享或排它锁时,innoDB会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,innoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-key锁)。
间隙锁通常会带来危害:因为语句执行过程中通过范围查找时,会锁住整个范围内所有的索引键值,即使键值并不存在。