MySQL的各种锁汇总

113 阅读4分钟

​本文已参与「新人创作礼」活动,一起开启掘金创作之路。

 前言

锁是什么?

锁,是计算机协调多个线程/多进程 访问某资源的一种机制。

何为资源?

  • 计算资源:CPU , RAM( 一般指随机存取存储器。随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器) ,I/O 等。
  • 数据:共享的资源,数据库要保证共享资源的一致性和有效性。

有什么弊端?

锁冲突,影响了数据库并发的性能。


锁分类

按性能分类:

悲观锁乐观锁
实现方式用版本比对来实现
举例读锁、写锁

按数据库操作分类:

读锁写锁
别名共享锁/S锁(Shared)独享锁/X锁(eXclusive)
解释同一份数据,多个读操作互不影响。当前写操作没有完成,阻断其他读写
效果当前session和其他session都可以读;当前session写操作报错;其他session写操作等待;当前session读写可以;其他session读写等待/阻塞;

按粒度分类:

行锁表锁
解释锁一行锁整张表
mysql中Innodb表默认MyISAM表默认
加锁性能加锁慢,开销大加锁快,开销小
并发度
锁冲突粒度小,冲突少粒度大,冲突多
死锁不会
使用场景整表前移

附:不同引擎的锁和事务

InnoDBMyISAM
事务支持不支持
行锁默认:select时候不加锁(串行例外),update、delete、insert会加行锁。不支持
表锁操作不当会锁表(后文介绍):无索引行锁会升级为表锁默认:执行select给表加读锁,update \delete\insert 给表加写锁。

InnoDB中锁(行锁)和事务关系密切

回顾MySQL事务隔离级别

级别脏读 不可重复读幻读
read uncommitted    读未提交
read committed        读已提交
repeatable read        可重复读
serializable                串行化

MySQL默认可重复读事务下的锁详解

MySQL 为保证可重复读在锁上使用了间隙锁和临键锁

间隙锁(Gap Lock):为避免幻读,范围写操作时会锁定范围,举个例子:

  • sessionA执行:update product set product_amount = 10 where id>1 and id<100;
  • sessionB执行:update product set product_name = '鸿星尔克男鞋' where id = 56;

此时sessionB是无法执行成功的,因为sessionA的操作,在可重复读模式下,实现了间隙锁:即(1,100] 不包含1到100,且包含100,范围内都被锁定。注:仅会在可重复读的事务下触发间隙锁。

临键锁:

就是:行锁+间隙锁 = 临键锁

使用锁的命令

注:在不使用数据库连接工具时,仅用命令行查询时,命令末尾使用\G 使得查询结果的展示结构旋转90度变成纵向

【表锁】相关命令

查看是否有表锁

show OPEN TABLES where In_use > 0;

删除表锁

unlock tables

使用MyISAM引擎

CREATE TABLE `表的名字` ( 
 `id` INT (32) NOT NULL AUTO_INCREMENT, 
 `NAME` VARCHAR (20) DEFAULT NULL, 
 PRIMARY KEY (`id`) 
 ) ENGINE = MyISAM DEFAULT CHARSET = utf8;

手动添加表锁(共享锁/S锁/读锁)

lock table 表的名字 read

手动添加表锁(独占锁/X锁/写锁)

lock table 表名 write 

【锁】相关命令 

查看所有表上加过的锁

show open tables

删除表锁

unlock tables

【行锁】相关命令 

查看全部行锁信息

show status like 'innodb_row_lock%';

 查看全部行锁信息的解释

名称解释 (单位ms)
innodb_row_lock_current_waits当前正在等待锁的会话有多少个
innodb_row_lock_time从系统启动到现在锁定总时间
innodb_row_lock_time_avg平均每次等待所需时间
innodb_row_lock_time_max从系统启动到现在等待时长最长的时间
innodb_row_lock_waits系统启动到现在共计等待次数

查看INFORMATION_SCHEMA系统库得知信息

 ‐‐ 查看事务 
select * from INFORMATION_SCHEMA.INNODB_TRX;

查看当前出现的锁  

 ‐‐ 查看当前出现的锁 
select * from information_schema.innodb_locks\G;

查看锁等待的对应关系  

 ‐‐ 查看锁等待的对应关系 
select * from information_schema.innodb_lock_waits\G;

 释放锁

‐‐ 释放锁,trx_mysql_thread_id可以从INNODB_TRX表里查看到 
kill trx_mysql_thread_id

查看锁等待,死锁日志 

‐‐ 查看锁等待详细信息 查看死锁日志
show engine innodb status\G;

查看进程

show processlist\G

杀死对应进程ID 

kill 140

锁的性能优化

死锁

场景举例

SessionA执行:select * from product where id=1 for update; 
SessionB执行:select * from product where id=2 for update; 
SessionA执行:select * from product where id=2 for update; 
SessionB执行:select * from product where id=1 for update;

 解决方案

  • mysql一般情况会自动察觉死锁,并回滚事务。特殊情况需手动处理。
  • 查看锁日志,杀进程。

性能优化

使用索引检索:避免行锁升级为表锁

合理设计索引:缩小锁范围,避免间隙锁

涉及事务加锁的SQL放最后执行

尽可能低事务级别