mysql锁机制总结,以及优化建议
一、锁概述和分类

二、表锁
偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
【手动增加表锁】
lock table 表名字1 read(write),表名字2 read(write),其它;
【查看表上加过的锁】
show open tables;
【释放表锁】
unlock tables;
演示:
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec) # 给mylock表加读锁,给t1表加写锁
mysql> lock table mylock read, t1 write;
Query OK, 0 rows affected (0.02 sec) # 查看已经加锁的表, 下面的结果省略了很多行
mysql> show open tables;
+--------------------+------------------------------------------------------+--------+-------------+
| Database | Table | In_use | Name_locked |
+--------------------+------------------------------------------------------+--------+-------------+
| mysqlad | t1 | 1 | 0 |
| performance_schema | events_transactions_current | 0 | 0 |
| performance_schema | events_statements_summary_by_program | 0 | 0 |
| performance_schema | events_waits_summary_by_host_by_event_name | 0 | 0 |
| mysqlad | mylock | 1 | 0 |
| performance_schema | file_sum
121 rows in set (0.00 sec) # 释放表锁
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
读锁案例:下面通过两个会话窗口来演示对mylock表加读锁之后的效果:
| session_1 | session_2 |
|---|---|
获得表mylock的READ锁定![]() |
连接终端 |
当前session_1可以查询该表记录![]() |
其他session(session_2)也可以查询该表![]() |
当前session_1不能查询其它没有锁定的表。![]() |
其他session_2可以查询或者更新未锁定的表![]() |
当前session_1中插入或者更新锁定的表都会提示错误:![]() |
其他session_2插入或者更新锁定表会一直等待获得锁:(阻塞)![]() |
| 释放锁。 mysql> unlock tables; |
session_2立即释放阻塞,马上获得锁。![]() |
演示对mylock加写锁:
| seession_1 | session_2 |
|---|---|
获得表mylock的WRITE锁定, 当前session对锁定表的查询+更新+插入操作都可以执行:![]() |
其他session对锁定表的查询被阻塞, 需要等待锁被释放: ![]() 在锁表前,如果session2有数据缓存, |
| 释放锁 mysql> unlock tables; |
session_2立即被释放得到锁![]() |
通过上面的实验,可以发现:
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁。
MySQL的表级锁有两种模式:
- 表共享读锁(Table Read Lock)
- 表独占写锁(Table Write Lock)
| 锁类型 | 他人可读 | 他人可写 |
|---|---|---|
| 读锁 | 是 | 否 |
| 写锁 | 否 | 否 |
结合上表,所以对MyISAM表进行操作,会有以下情况:
- 1、对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
- 2、对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。
简而言之,就是读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。
总结:
- 可以通过
show open tables来查看哪些表被枷锁了; - 如何分析表锁定,可以通过检查
table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定;

这里有两个状态变手记录MySQL内部表级锁定的情况,两个变量说明如下:
Table_locks_immediate: 产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1 ;
Table_locks_waited: 出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况;
总结: MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎。 因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。
三、行锁
特点:
- 偏向InnoDB存储引擎,开销大,加锁慢;
- 会出现死锁;
- 锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。
Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,Innodb的整体性能和MyISAM相比就会有比较明显的优势了。
但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。
演示案例,建表SQL:
create table test_innodb_lock (a int(11),b varchar(16))engine=innodb; insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1'); # 创建两个索引
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b); # 查询结果
mysql> select * from test_innodb_lock;
+------+------+
| a | b |
+------+------+
| 1 | b2 |
| 3 | 3 |
| 4 | 4000 |
| 5 | 5000 |
| 6 | 6000 |
| 7 | 7000 |
| 8 | 8000 |
| 9 | 9000 |
| 1 | b1 |
+------+------+
9 rows in set (0.00 sec)
测试: 读己之所写。

然后看session_1和session_2同时更新a = 4的情况:
更新但是不提交,即没有commit![]() |
session_2被阻塞,只能等待
![]() |
|---|---|
提交更新mysql> commit; |
解除阻塞![]() |
但是如果两个会话不是更新同一行呢?
如果不是更新同一行,则就算在session_1没有commit的时候,session_2也不会阻塞。

尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。
举个例子: 因为我们的b是varchar类型的,更新的时候我故意将b的单引号去掉,此时MYSQL底层自动类型转换,但是此时就会导致索引失效,然后我们看下面,就会导致我们的行锁变成了表锁,从而导致阻塞等待。

间隙锁带来的插入问题:

【什么是间隙锁】
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,
InnoDB也会对这个“间隙”加锁(不放过一个),这种锁机制就是所谓的间隙锁(GAP Lock)。
【危害】
因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。
间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。
面试题:常考如何锁定一行。
使用
for update。
【如何分析行锁定】
通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况
mysql>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:系统启动后到现在总共等待的次数;
对于这5个状态变量,比较重要的主要是
Innodb_row_lock_time_avg(等待平均时长),
Innodb_row_lock_waits(等待总次数)
Innodb_row_lock_time(等待总时长)这三项。
尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。
最后可以通过SELECT * FROM information_schema.INNODB_TRX\G;来查询正在被锁阻塞的sql语句。
四、优化建议
- 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁;
- 尽可能较少检索条件,避免间隙锁;
- 尽量控制事务大小,减少锁定资源量和时间长度;
- 锁住某行后,尽量不要去调别的行或表,赶紧处理被锁住的行然后释放掉锁;
- 涉及相同表的事务,对于调用表的顺序尽量保持一致;
- 在业务环境允许的情况下,尽可能低级别事务隔离;
免费Java高级资料需要自己领取,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G。
传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q
mysql锁机制总结,以及优化建议的更多相关文章
- mysql锁机制(转载)
锁是计算机协调多个进程或线程并发访问某一资源的机制 .在数据库中,除传统的 计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所 ...
- 再谈mysql锁机制及原理—锁的诠释
加锁是实现数据库并发控制的一个非常重要的技术.当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁.加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更 ...
- Mysql锁机制介绍
Mysql锁机制介绍 一.概况MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制.比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking ...
- mysql锁机制详解
前言 大概几个月之前项目中用到事务,需要保证数据的强一致性,期间也用到了mysql的锁,但当时对mysql的锁机制只是管中窥豹,所以本文打算总结一下mysql的锁机制. 本文主要论述关于mysql锁机 ...
- mysql锁机制 读书笔记
目录 MySQL锁机制 1.什么是锁 2.lock与latch 3.InnoDB存储引擎中的锁 3.1锁的类型 3.2 一致性非锁定读 3.3 一致性锁定读 4 锁的算法 4.1行锁的3中算法 4.2 ...
- Mysql锁机制--索引失效导致行锁变表锁
Mysql 系列文章主页 =============== Tips:在阅读本文前,最好先阅读 这篇(Mysql锁机制--行锁)文章~ 在上篇文章中,我们看到InnoDB默认的行锁可以使得操作不同行时不 ...
- 对mysql锁机制的学习
1.对于mysql学习,经常翻看一些博客,论坛,好像或多或少有mysq锁机制的学习与总结,所以今天有必要 对mysql锁机制的一些个人的总结,以便以后深入的学习. 2.学习这件事,从来都是“深入浅出” ...
- mysql锁机制和事务隔离
mysql事务 1.InnoDB事务原理 事务(Transaction)是数据库区别于文件系统的重要特性之一,事务会把数据库从一种一致性状态转换为另一种一致性状态. 在数据库提交时,可以确保要么所有修 ...
- (三)MySQL锁机制 + 事务
转: (三)MySQL锁机制 + 事务 表锁(偏读) 偏向MyISAM存储引擎.开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低. 查看当前数据库中表的上锁情况,0表示未上锁. sh ...
随机推荐
- py2,py3区别 ,基础网络常识,多线程,
1.编译型:把代码编译成机器码,计算机找虚拟机执行代码,机器码交给计算技巧去运行 :C语言,java c# 解释型:边解释边执行 5.只有列表才能reverse 字符串需要索引[::-1] 6.py2 ...
- c# 第10节 表达式
本节内容: 1:表达式是什么 2:表达式实例 1:表达式是什么 2:表达式实例 3:运算符分类
- jenkins slave上执行脚本报错
jenkins slave上执行脚本报错 解决方法:在系统配置中设置shell execuate C:\Windows\system32\cmd.exe 保存即可
- python27期day10:函数的动态参数、函数的注释、函数的名称空间、函数的嵌套、global(修改全局的)、nonlocal(修改局部的)、函数名的第一类对象及使用、作业题。
1.动态参数的作用: 能够接收不固定长度参数 位置参数过多时可以使用动态参数 * args是程序员之间约定俗称(可以更换但是不建议更换) * args获取的是一个元组 ** kwargs获取的是一个字 ...
- USACO Telephone Lines
洛谷 P1948 [USACO08JAN]电话线Telephone Lines https://www.luogu.org/problem/P1948 JDOJ 2556: USACO 2008 Ja ...
- vue系列---Vue组件化的实现原理(八)
_ 阅读目录 一. 什么是Vue组件? 如何注册组件? 1.1 全局注册组件 1.2 局部注册组件 二:组件之间数据如何传递的呢? 1) props 2) $emit 3) 使用$ref实现通信 4) ...
- 组件注册-自定义TypeFilter指定过滤规则
组件注册-自定义TypeFilter指定过滤规则 4.1 FilterType.ANNOTATION 按照注解方式 4.2 FilterType.ASSIGNABLE_TYPE 按照给定的类型 @Co ...
- DNS用的是TCP协议还是UDP协议
DNS占用53号端口,同时使用TCP和UDP协议.那么DNS在什么情况下使用这两种协议? DNS在区域传输的时候使用TCP协议,其他时候使用UDP协议. DNS区域传输的时候使用TCP协议: 1.辅域 ...
- [LeetCode] 272. Closest Binary Search Tree Value II 最近的二分搜索树的值之二
Given a non-empty binary search tree and a target value, find k values in the BST that are closest t ...
- Linux设置定时任务方法
linux下定时执行任务的方法: 在LINUX中你应该先输入crontab -e,然后就会有个vi编辑界面,再输入0 3 * * 1 /clearigame2内容到里面 :wq 保存退出. 在LI ...














