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'这一行也被锁了呢?这是因为行锁升级为表锁了。

  InnoDB 行级锁是通过给索引上的索引项加锁来实现的,InnoDB行级锁只有通过索引条件检索数据,才使用行级锁;否则,InnoDB使用表锁 在不通过索引(主 键)条件查询的时候,InnoDB是表锁而不是行锁。
  而session1通过c=‘60’来编辑,c是没有索引的,所以加的是表锁。
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-锁的更多相关文章

  1. 使用redis构建可靠分布式锁

    关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...

  2. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  3. java中的锁

    java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...

  4. 分布式锁1 Java常用技术方案

    前言:       由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...

  5. 如何在高并发环境下设计出无锁的数据库操作(Java版本)

    一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...

  6. 如何定位Oracle数据库被锁阻塞会话的根源

    首先再次明确下,数据库因为要同时保证数据的并发性和一致性,所以操作有锁等待是正常的. 只有那些长时间没有提交或回滚的事物,阻塞了其他业务正常操作,才是需要去定位处理的. 1.单实例环境 2.RAC环境 ...

  7. java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)

    一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...

  8. Android 死锁和重入锁

    死锁的定义: 1.一般的死锁 一般的死锁是指多个线程的执行必须同时拥有多个资源,由于不同的线程需要的资源被不同的线程占用,最终导致僵持的状态,这就是一般死锁的定义. package com.cxt.t ...

  9. Xcode 锁终端

    锁终端 输入: <1>cd /Applications/Xcode.app 回车 结果显示: Xcode.app 输入: <2>sudo chown -hR root:whee ...

  10. mysql 行级锁的使用以及死锁的预防

    一.前言 mysql的InnoDB,支持事务和行级锁,可以使用行锁来处理用户提现等业务.使用mysql锁的时候有时候会出现死锁,要做好死锁的预防. 二.MySQL行级锁 行级锁又分共享锁和排他锁. 共 ...

随机推荐

  1. 抓包整理————ip 协议一[十二]

    前言 简单介绍一下ip协议. 正文 先来看下ip协议在网络层的哪一层: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 ip 层就在网络层: 其实很好想象哈,就是因为每台机器起码有一个ip ...

  2. [linux]非root账户 sudo cd 无法切换目录解决办法

    在Centos上有个账户A(非root),有sudo权限(权限比较大),想要用 cd 命令切换到 B账号的 /home/B 下的目录,结果没作用 [liuzz ~]$ sudo cd /home/xi ...

  3. 记录下批处理bat脚本获取打包发布问题

    最近做了个Jenkins配合Gitlab自动部署Java项目到Windows Server服务器. Jenkins和Gitlab在Linux下,好一顿折腾,先记录下脚本,其余后续补充吧. 把Java项 ...

  4. ATM项目详解

    内容概要: ATM项目 代码实操流程 ATM项目 # 需求: """ - 额度15000或自定义 - 支持多账户登录 - 可以查看账户余额 - 可以提现(可自定义手续费比 ...

  5. Django批量插入(自定义分页器)

    目录 一:批量插入 1.常规批量插入数据(时间长,效率低 不建议使用) 2.使用orm提供的bulk_create方法批量插入数据(效率高 减少操作时间) 3.总结 二:自定义分页器 1.自定义分页器 ...

  6. 【深入浅出SpringCloud原理及实战】「SpringCloud-Alibaba系列」微服务模式搭建系统基础架构实战指南及版本规划踩坑分析

    Spring Cloud Alibaba Nacos Discovery Spring Boot 应用程序在服务注册与发现方面提供和 Nacos 的无缝集成. 通过一些简单的注解,您可以快速来注册一个 ...

  7. jQuery类库

    jQuery介绍 1. jQuery是一个轻量级的.兼容多浏览器的JavaScript库. 2. jQuery使用户能够更方便地处理HTML Document.Events.实现动画效果.方便地进行A ...

  8. 编译安装nmon

    nmon 是什么? nmon(Nigel's performance Monitor for Linux)是一种Linux性能监视工具,当前它支持 Power/x86/x86_64/Mainframe ...

  9. Centos7下vim最新版本安装

    一直以来用的都是vim,因为之前都是系统自带的vim没有研究过怎么自己安装,今天趁着刚装完新系统,顺便装下vim. 同样vim也有两种安装方法: 一.yum安装,centos下安装软件最简单的方法了, ...

  10. [机器学习] PCA主成分分析原理分析和Matlab实现方法

    转载于http://blog.csdn.net/guyuealian/article/details/68487833 网上关于PCA(主成分分析)原理和分析的博客很多,本博客并不打算长篇大论推论PC ...