mysql19-锁
1.什么是锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。
2.锁的分类
2.1从操作来分
2.1.1读锁
共享锁,针对同一份数据,多个读操作可以同时进行而不会互相影响。
2.1.2写锁
排它锁,当前写操作没有完成前,它会阻断其他写锁和读锁。
2.2从锁粒度来分
2.2.1表锁
锁定整张表。开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低。
2.2.2行锁
只锁定需要的行数据。开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高
2.2.3说明
为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁是很耗资源的事情(涉及获取,检查,释放锁等动作),因此数据库系统需要在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度(Lock granularity)”的概念。
一种提高共享资源并发发性的方式是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。更理想的方式是,只对会修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。
3.mysql隔离级别
https://www.cnblogs.com/jthr/p/15218682.html
4.MyISAM和InnoDB
mysql中使用比较多的两种引擎是MyISAM和InnoDB。MyISAM只使用表级锁。InnoDB默认使用行级锁,也支持表锁。
4.1MyISAM
MyISAM不支持事务。
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。
用户也可以用LOCK
TABLES给表显式加表锁时,必须同时取得所有涉及到表的锁,并且MySQL不支持锁升级。也就是说,在执行LOCK
TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表;同时,如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。其实,在自动加锁的情况下也基本如此,MyISAM总是一次获得SQL语句所需要的全部锁。这也正是MyISAM表不会出现死锁(Deadlock
Free)的原因。
4.2InnoDB
InnoDB支持事务。InnoDB默认使用行锁。InnoDB无索引行锁会升级为表锁
5.MyIsam表锁
5.1表锁语法
1)手动增加表锁
lock table 表名字1 read(write),表名字2 read(write),其它;
2)查看表上加的锁
show open tables;
3)释放表锁
unlock tables;
5.2示例准备
5.2.1建表
CREATE TABLE `mylock` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`address` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
这里引擎用的是myisam
5.2.2插入数据
insert into mylock(name,phone,address) values('a','pa','da');
insert into mylock(name,phone,address) values('b','pb','db');
insert into mylock(name,phone,address) values('c','pc','dc');
insert into mylock(name,phone,address) values('d','pd','dd');
insert into mylock(name,phone,address) values('e','pe','de');
5.3读锁示例
5.3.1打开两个session
5.3.2session1
手动给表mylock加读锁
LOCK TABLE mylock READ;
5.3.32session1
查询mylock,可以查询,说明加读锁的当前session可以读取本表
SELECT * FROM mylock;
5.3.4session2
查询mylock,可以查询,说明其它session也可以读取本表
SELECT * FROM mylock
5.3.5session1
查询其他表,不能查询,说明当前session,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表
SELECT * FROM jcustomer;
5.3.6session2
查询其他表,可以查询
5.3.7session1
修改mylock某条数据,不能修改,读锁不允许写
UPDATE mylock SET `name` = 'aa' WHERE id = 1;
5.3.7session2
1)修改mylock某条数据,发现阻塞了,它会等待读锁被释放后再执行
UPDATE mylock SET `name` = 'aa' WHERE id = 1;
2)在session释放锁
UNLOCK TABLES;
3)再看session,发现修改语句已执行成功
4)session2查看修改的数据,发现数据确实已修改
5.3.8session1
1)上面锁释放了,先把读锁再次加上
LOCK TABLE mylock READ;
2)修改其他表,不能修改,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表
UPDATE jcustomer SET custremark1 = 'aa' WHERE CustomerNo = 1;
5.3.9session2
修改其他表,可以修改
UPDATE jcustomer SET custremark1 = 'aa' WHERE CustomerNo = 1;
5.3.10session1
释放锁
UNLOCK TABLES
5.4读锁小结
当前session给某些表(锁表)加了读锁,访问权限小结
读取锁表 | 读取其他表 | 修改锁表 | 修改其它表 | |
当前session | 可以 | 不可以 | 不可以 | 不可以 |
其它session | 可以 | 可以 | 阻塞 | 可以 |
在执行LOCK TABLES后,当前session只能访问显式加锁的这些表-锁表,不能访问未加锁的表
5.5写锁示例
5.5.1打开两个session
5.5.2session1
给mylock表加写锁
LOCK TABLE mylock WRITE;
5.5.3session1
查询表mylock,可以查询
5.5.4
1)session2:查询表mylock,阻塞了,需要释放锁后才能查询结果
SELECT * FROM mylock;
2)session1:释放锁
SELECT * FROM mylock;
3)再看session2,发现已查询出结果
5.5.5
1)session1添加写锁
LOCK TABLE mylock WRITE;
2)session1查询其它表
不能查询,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表
SELECT * FROM jcustomer;
5.5.6session2查询其它表
可以查询
SELECT * FROM jcustomer;
5.5.7session1
修改mylock表的数据,修改成功
UPDATE mylock SET `name` = 'update_1' WHERE id = 1;
5.5.8
1)session2修改mylock表数据,阻塞了,锁释放后才能执行
2)session1释放锁
UNLOCK TABLES;
3)再看session2,发现已执行成功
5.5.9
1)session1给mylock加写锁
LOCK TABLE mylock WRITE;
2)session1对其它表数据进行修改
不能修改,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表
UPDATE jcustomer SET custremark1 = 'update_1' WHERE CustomerNo = 1;
5.5.10session2
修改其它表,可以修改
UPDATE jcustomer SET custremark1 = 'update_2' WHERE CustomerNo = 1;
5.6写锁小结
当前session给某些表(锁表)加了写锁,访问权限小结
读取锁表 | 读取其他表 | 修改锁表 | 修改其它表 | |
当前session | 可以 | 不可以 | 可以 | 不可以 |
其它session | 阻塞 | 可以 | 阻塞 | 可以 |
在执行LOCK TABLES后,当前session只能访问显式加锁的这些表-锁表,不能访问未加锁的表
5.7表锁分析
show status like 'table%';
table_locks-waited:需要等待的表锁数
Table_locks_immediate:产生表级锁的次数,可立即释放表锁数
如果Table_locks_immediate / Table_locks_waited > 5000,最好采用InnoDB引擎
因为InnoDB是行锁而MyISAM是表锁,对于高并发写入的应用InnoDB效果会好些
6.InnoDB表锁示例
6.1建表
CREATE TABLE `test_innodb_lock` (
`a` int(11) DEFAULT NULL,
`b` varchar(16) DEFAULT NULL,
`c` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
6.2插入数据
insert into test_innodb_lock values(1,'1000','10');
insert into test_innodb_lock values(3,'2000','20');
insert into test_innodb_lock values(4,'4000','40');
insert into test_innodb_lock values(5,'5000','50');
insert into test_innodb_lock values(6,'6000','60');
insert into test_innodb_lock values(7,'7000','70');
insert into test_innodb_lock values(8,'8000','80');
insert into test_innodb_lock values(9,'9000','90');
insert into test_innodb_lock values(1,'10000','100');
6.3创建索引
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);
6.4简要说明
innoDB支持事务,且每一个sql语句默认是自动提交的。一个事务内,对表数据进行编辑,会对编辑涉及到的表的相关行数据加上行锁,特殊情况下特会加表锁,在事务提交后会释放。它的默认的隔离级别是可重复度。
6.4示例1
1)打开两个session
2)session1取消自动提交
SET autocommit = 0;
3)session1查询test_innodb_lock
查询出结果
SELECT * FROM test_innodb_lock;
4)session2查询test_innodb_lock
可以查询
5)session1修改test_innodb_lock数据
执行成功,但是没有commit
UPDATE test_innodb_lock SET b = 'update_1000' WHERE a = 1;
6)session1查询test_innodb_lock
查询成功,且可以看到修改后的数据。说明即便自己还没有commit,也可以查询到修改的数据
SELECT * FROM test_innodb_lock;
7)session2查询test_innodb_lock
查询成功,但是查询到的数据还没有被修改,说明
8)session1提交
COMMIT;
9)session2查询test_innodb_lock
查询成功,查询到的是修改后的数据
SELECT * FROM test_innodb_lock;
10)简要说明
innodb的默认的隔离级别是可重复度,不会出现脏读,一个事务不能读取到另一个事务未提交的数据
6.5示例2
1)打开两个session
2)session1和session2取消自动提交
SET autocommit = 0;
3)session1修改test_innodb_lock数据
执行成功,但是没有commit
UPDATE test_innodb_lock SET b = 'update_3000' WHERE a = 3;
4)session1查询
查询成功,可以查到自己已修改未提交的数据
SELECT * FROM test_innodb_lock WHERE a = 3;
5)session2修改test_innodb_lock
阻塞了,需要session1释放行锁才会执行
UPDATE test_innodb_lock SET c = 'update_300' WHERE a = 3;
6)session1提交
COMMIT;
7)此时查看session2,由于session1已释放行锁,session2不在阻塞,修改的sql已成功执行
8)session2查询
查询成功,可以看到session1已提交的修改数据和自己已修改未提交的数据
SELECT * FROM test_innodb_lock WHERE a = 3;
9)session1查询
查询成功,可以看到session2提交的修改的数据(这里如果查询不到session2修改的数据,就再commit一下再查询)
SELECT * FROM test_innodb_lock WHERE a = 3;
10)说明
一个事务在对数据进行修改时,会对涉及的行自动上行锁,其它的事务对上了行锁的数据的修改会阻塞,等待行锁释放后再执行。
7.5示例3
1)打开两个session
2)session1和session2取消自动提交
SET autocommit = 0;
3)session1修改test_innodb_lock数据
执行成功,但是没有commit
UPDATE test_innodb_lock SET b = 'update_4000' WHERE a = 4;
4)session1查询test_innodb_lock
可以看到自己已修改未提交的数据
5)session2修改test_innodb_lock其它行的数据
成功执行,没有阻塞,证明只有a=4的加了行锁,其它行美影响
UPDATE test_innodb_lock SET c = 'update_500' WHERE a = 5;
6)session2提交再查询
查询成功,可以看到a=5数据已修改,看不到a=4数据的修改,因为session1还没有提交
COMMIT;
SELECT * FROM test_innodb_lock;
7)session提交再查询
查询成功,可以看到a=4,a=5的数据的修改
COMMIT;
SELECT * FROM test_innodb_lock ;
8)说明
行锁锁的是行,对其它行的数据没有有效
7.6示例4
1)打开两个session
2)session1和session2取消自动提交
SET autocommit = 0;
3)session1编辑数据
执行成功,未提交
UPDATE test_innodb_lock SET b = 'update_6000' WHERE c = '60';
4)session2编辑数据
阻塞了。为什么会阻塞呢?session1是对c='60这条数据进行修改',而这里session2是对a='7'这条数据进行修改,不是同一行数据,为什么a='7'这一行也被锁了呢?这是因为行锁升级为表锁了。
UPDATE test_innodb_lock SET b = 'update_7000' WHERE a = 7;
5)说明
在没有使用索引的情况下InnoDB就会使用表级锁(共享锁不会有这个情况)
7.7示例5
1)打开两个session
2)session1和session2取消自动提交
SET autocommit = 0;
3)session1编辑数据
执行成功,未提交
UPDATE test_innodb_lock SET b = 'update_6000' WHERE a > 1 and a < 6;
4)session2编辑数据
执行成功
UPDATE test_innodb_lock SET b = 'update_8000' WHERE a = 8;
5)session2新增数据
插入成功
INSERT INTO test_innodb_lock VALUES (12,'1200','120');
6)session2再次插入数据
阻塞了。命名表中都没有这条数据,为什么还会被锁了。这是session1因为使用范围产生了间隙锁。
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,
InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(GAP Lock)。
session1使用a>1 and a <6,即便没有a=2,这条数据。也会对a=2这个间隙上锁。
INSERT INTO test_innodb_lock VALUES (2,'200','20');
7)说明
因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。也就是间隙锁。
间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害
7.8行锁小结
1) innobd默认使用行锁
2)当我们对表的某些数据进行修改插入时,会默认对这些数据行加上行锁排它锁
3)在没有使用索引的情况下InnoDB就会使用表级锁(排它锁才会这样)
4)当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,也就是间隙锁
7.9行锁分析
1)通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况
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(等待总时长)这三项。
尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。
2)可以通过下面语句查询正在被锁阻塞的sql语句。
SELECT * FROM information_schema.INNODB_TRX\G
3)查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
4)查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
5)查看表使用情况
SHOW OPEN TABLES; 列举在表缓存中当前被打开的非TEMPORARY表
SHOW OPEN TABLES In_use > 0;
- Table:表名称。
- In_use:表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。
- Name_locked:表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作
7.10手动加上锁
1)读锁
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
用法
SELECT ... LOCK IN SHARE MODE;
2)写锁
排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
用法
SELECT ... FOR UPDATE;
在查询语句后面增加 FOR UPDATE ,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
mysql19-锁的更多相关文章
- 使用redis构建可靠分布式锁
关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- java中的锁
java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
- 分布式锁1 Java常用技术方案
前言: 由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...
- 如何在高并发环境下设计出无锁的数据库操作(Java版本)
一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...
- 如何定位Oracle数据库被锁阻塞会话的根源
首先再次明确下,数据库因为要同时保证数据的并发性和一致性,所以操作有锁等待是正常的. 只有那些长时间没有提交或回滚的事物,阻塞了其他业务正常操作,才是需要去定位处理的. 1.单实例环境 2.RAC环境 ...
- java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)
一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...
- Android 死锁和重入锁
死锁的定义: 1.一般的死锁 一般的死锁是指多个线程的执行必须同时拥有多个资源,由于不同的线程需要的资源被不同的线程占用,最终导致僵持的状态,这就是一般死锁的定义. package com.cxt.t ...
- Xcode 锁终端
锁终端 输入: <1>cd /Applications/Xcode.app 回车 结果显示: Xcode.app 输入: <2>sudo chown -hR root:whee ...
- mysql 行级锁的使用以及死锁的预防
一.前言 mysql的InnoDB,支持事务和行级锁,可以使用行锁来处理用户提现等业务.使用mysql锁的时候有时候会出现死锁,要做好死锁的预防. 二.MySQL行级锁 行级锁又分共享锁和排他锁. 共 ...
随机推荐
- Codeforces Round #817 (Div. 4)
CF传送门 因为洛谷题库未更新,所以给出的题面都是CF的. 现场打真是太卡了(梯子挂了,codeforc.es也崩了),所以五六分钟才打开题目 \(qwq\) A. Spell Check 萌萌题,把 ...
- 带你了解S12直播中的“黑科技”
摘要:让精彩更流畅.让较量更清晰.让参与更沉浸.让体验更有趣,幕后的舞台,从来都是技术的战场,S12背后的名场面同样场场高能. 本文分享自华为云社区<用硬核方式打开S12名场面>,作者:华 ...
- 算法5: LeetCode_单链表_两数相加
题目: * 给你两个 非空 的链表,表示两个非负的整数.它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字. * 请你将两个数相加,并以相同形式返回一个表示和的链表. * 你可 ...
- 写一个frida通杀脚本
1. 前言 过年对我来说和平常没什么区别,该干什么干什么. 之前没接触过 frida 这个工具,前几天用了一些时间学习了一下,相比于 xposed hook 框架,frida 相对于调试方面真的很方便 ...
- SDK怎么测试?俺不会啊
转载请注明出处️ 作者:测试蔡坨坨 原文链接:caituotuo.top/7bc8d1c8.html 你好,我是测试蔡坨坨. 众所周知,在云产品和SaaS蓬勃发展的当下,企业中有许多系统和环节都是依赖 ...
- 【企业流行新数仓】Day03:SuperSet图表,Ranger权限、脱敏、行级别过滤,Atlas元数据、查询和查看全表/字段血缘依赖,Zabbix告警
一.SuperSet-图表展示 1.概念 (1)概念 通过dashboard(仪表盘)对图表中的数据进行展示 BI工具:根据配置的要求,进行数据源的配置即可 是准商业级别的BI web应用 (2)原理 ...
- Redis Lettuce长时间超时问题
1. 背景 新上线了一个服务,在压测的时候大量返回错误,查看报错是io.lettuce.core.RedisCommandTimeoutException: Command timed out aft ...
- Django框架三板斧本质-jsonResponse对象-form表单上传文件request对象方法-FBV与CBV区别
目录 一:视图层 2.三板斧(HttpResponse对象) 4.HttpResponse() 5.render() 6.redirect() 7.也可以是一个完整的URL 二:三板斧本质 1.Dja ...
- typora软件下载跟安装
typora软件介绍 typora是一款文本编辑器 是目前非常火爆的文本编辑器 [下载地址](Typora 官方中文站 (typoraio.cn)) 安装操作 pj链接 注意:不要更新!!! 安装 路 ...
- Navicat Premium无法连接到oracle数据库的解决方法
原因:Navicat Premium连不上oracle数据库一般是因为oci.dll文件的问题 解决方法:找到oracle安装路径中的oci.dll文件或者PL/SQL Developer安装路径中的 ...