一. 背景知识

二. 步入正题:表锁和行锁

1.1. 表锁 vs 行锁

在 MySQL 中锁的种类有很多,但是最基本的还是表锁和行锁:表锁指的是对一整张表加锁,一般是 DDL 处理时使用,也可以自己在 SQL 中指定;而行锁指的是锁定某一行数据或某几行,或行和行之间的间隙。行锁的加锁方法比较复杂,但是由于只锁住有限的数据,对于其它数据不加限制,所以并发能力强,通常都是用行锁来处理并发事务。表锁由 MySQL 服务器实现,行锁由存储引擎实现,常见的就是InnoDb,所以通常我们在讨论行锁时,隐含的一层意义就是数据库的存储引擎为 InnoDb ,而 MyISAM 存储引擎只能使用表锁。

1.2. 表锁

表锁由 MySQL 服务器实现,所以无论你的存储引擎是什么,都可以使用。一般在执行 DDL 语句时,譬               如 ALTER TABLE 就会对整个表进行加锁。在执行 SQL 语句时,也可以明确对某个表加锁。

1.2.1 操作演示(user_test创建表脚本见2.4.1)

-- 事物A中 显示对 user_test 加上读锁locktableuser_test read;select*fromuser_testwhereid=3;  -- 此时事物B中对其进行update或者write操作start TRANSACTION;-- 手动开启事物insertintouser_test(age,name)values(18,'Tom');

结论:此时事物B一致处于等待事物A释放锁的状态,最终会回去锁超时

2.1. 行锁(以下验证是在默认的隔离级别(可重复读的事务隔离级)下操作)

InnoDB  NEXT-KEY Locks,解决了在可重复读的事务隔离级别下出现幻读的问题。

什么是幻读?

幻读是在可重复读的事务隔离级别下会出现的一种问题,简单来说,可重复读保证了当前事务不会读取到其他事       务已提交的 UPDATE 操作。但同时,也会导致当前事务无法感知到来自其他事务中的 INSERT 或 DELETE 操       作,这就是幻读。

2.2. 关于行锁我们要知道的

行锁在 InnoDB 中是基于索引实现的,所以一旦某个加锁操作没有使用索引,那么该锁就会退化为表锁。

2.3 行锁分类

2.3.1 从加锁范围划分

a)记录锁(Record Locks):存在与唯一索引包括主键索引 顾名思义,记录锁就是为某行记录加锁,它封锁该行的索引记录:

b)间隙锁(Gap Locks):

存在与非唯一索引中,锁定开区间范围内的一段间隔,它是基于临键锁实现的。

间隙锁基于非唯一索引,它锁定一段范围内的索引记录。间隙锁基于下面将会提到的Next-Key Locking 算法,请务必牢记:使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。有时候又称为范围(Range Locks),这个范围可以跨一个索引记录,多个索引记录,甚至是空的。使用间隙锁可以防止其他事务在这个范围内插入或修改记录,保证两次读取这个范围内的记录不会变,从而不会出现幻读现象。很显然,间隙锁会增加数据库的开销,虽然解决了幻读问题,但是数据库的并发性一样受到了影响,所以在选择数据库的隔离级别时,要注意权衡性能和并发性,根据实际情况考虑是否需要使用间隙锁,大多数情况下使用 read committed 隔离级别就足够了,对很多应用程序来说,幻读也不是什么大问题。

产生间隙锁的条件(RR事务隔离级别下):

3.1.  使普通索引锁定;

3.2.  使用多列唯一索引;

3.3.  使用唯一索引锁定多行记录。

c)临键锁(Next-Key Locks)临键锁存在于非唯一索引中(主键中不存在临键锁),该类型的每条记录的索引上都存在这种锁,它是一种特殊的间隙锁,锁定一段左开右闭的索引区间。临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。

注意: Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法。通过临建锁可以解决幻读的问题。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁 , 但是存在间隙锁。临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。

2.3.2  从兼容性和存在形态划分

2.4. 操作演示

2.4.1 创建测试表并初始化数据

CREATETABLE`user_test`(`id`bigint(20)unsignedNOTNULLAUTO_INCREMENT COMMENT'自增id',`age`int(11)unsignedNOTNULLCOMMENT'年龄',`name`varchar(16)NOTNULLCOMMENT'姓名',PRIMARY KEY(`id`)COMMENT'主键')ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='行锁测试表';INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(1,10,'Lee');INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(2,24,'Ted');INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(3,32,'Zed');INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(4,45,'Talon');

2.4.2Record lock 记录锁验证

执行一下sql

-- 事物A 中更新数据 sql如下:-- 手动开启事物或者 beginSTART TRANSACTION;-- 根据非唯一索引列 UPDATE 某条记录  (只会产生记录锁,不会产生间隙锁)UPDATEuser_testSETname='LISHI'WHEREid=2;-- 等观看完下面事物B在开启事物commit;rollback;-- 事物B 也同时更新统一条记录START TRANSACTION;UPDATEuser_testSETname='ZHANGSAN'WHEREid=2;commit;rollback;

此时事物B会出现如图结果

事物A commit 或者 rollback 此时事物B会拿到锁执行成功;事物B更新其他id记录不受影响:

START TRANSACTION;UPDATEuser_testSETname='ZHANGSAN'WHEREid=1;commit;

2.4.2Next-Key Locks 临键锁验证

首先在age字段上创建普通索引

ALTERTABLE`user_test` ADD INDEX `index_age`(`age`);

此时该表中 age 列潜在的临键锁有:

(-∞, 10],

(10, 24],

(24, 32],

(32, 45],

(45, +∞],

事物A 中更新数据 sql如下:

START TRANSACTION;-- 根据非唯一索引列 UPDATE 某条记录select*fromuser_testwhereage=10forupdate-- 事物B此时插入或者更新age小于10大于1的一条记录-- 插入一条记录为age=7的记录start TRANSACTION;insertintouser_test(age,name)values(7,'Tom');COMMIT;

-- 事物B更新其中一条记录age为8也会被阻塞(虽然条件id是主键索引,更新的字段是普通索引,因此也会加上间隙锁)start TRANSACTION;UPDATEuser_testSETage=8WHEREid=2;COMMIT;

此时事物B会出现如图结果

事物A commit 或者 rollback 此时事物B会拿到锁执行成功

2.4.3. Gap Locks 间隙锁验证

打开间隙锁设置首先查看 innodb_locks_unsafe_for_binlog 是否禁用:

show variableslike'innodb_locks_unsafe_for_binlog';

innodb_locks_unsafe_for_binlog:默认值为OFF,即启用间隙锁。因为此参数是只读模式,如果想要禁用间隙锁,需要修改my.cnf(windows是my.ini)重新启动才行。

默认mac是没有my.cnf文件的,因此要在 etc文件下创建 my.cnf文件(etc/my.cnf)

my.cnf内容如下(主要添加:)

innodb_locks_unsafe_for_binlog=1

3.my.cnf内容如下:

# Example MySQL config file formediumsystems.  #  # Thisisfor a system with little memory(32M-64M)whereMySQL plays# an important part,orsystems up to128MwhereMySQLisused together with# other programs(suchasa web server)  #  # MySQL programs look for option filesinasetof# locations which dependonthe deployment platform.  # You can copy this option file to one of those  # locations. For information about these locations,see:# http://dev.mysql.com/doc/mysql/en/option-files.html  #  #Inthis file,you can use alllongoptions that a program supports.# If you want to know which options a program supports,run the program# with the"--help"option.  # The following options will be passed to all MySQL clients  [client]default-character-set=utf8#password=your_passwordport=3306socket=/tmp/mysql.sock  # Here follows entries for some specific programs    # The MySQL server  [mysqld]character-set-server=utf8init_connect='SET NAMES utf8  port        = 3306    socket      = /tmp/mysql.sock    skip-external-locking    key_buffer_size = 16M    max_allowed_packet = 1M    table_open_cache = 64    sort_buffer_size = 512K    net_buffer_length = 8K    read_buffer_size = 256K    read_rnd_buffer_size = 512K    myisam_sort_buffer_size = 8M    character-set-server=utf8    init_connect='SETNAMES utf8'  innodb_locks_unsafe_for_binlog = 1# Don't listenona TCP/IP port at all. This can be a security enhancement,# if all processes that need to connect to mysqld runonthe same host.# All interaction with mysqld must be made via Unix socketsornamed pipes.# Note that using this option without enabling named pipesonWindows#(via the"enable-named-pipe"option)will render mysqld useless!#  #skip-networking # Replication Master Server(default)# binary loggingisrequired for replicationlog-bin=mysql-bin # binary logging format-mixed recommendedbinlog_format=mixed # required unique idbetween1and2^32-1# defaults to1if master-hostisnotset# but willnotfunctionasa master if omittedserver-id=1 # Replication Slave(comment out master section to use this)    #  # To configure this hostasa replication slave,you can choosebetween# two methods:    #  #1)Use the CHANGE MASTER TO command(fully describedinour manual)-#    the syntaxis:    #  #    CHANGE MASTER TO MASTER_HOST=,MASTER_PORT=,#    MASTER_USER=,MASTER_PASSWORD=;    #  #whereyou replace,,byquoted stringsand#bythe master's port number (3306 by default).      #      #    Example:      #      #    CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,      #    MASTER_USER='joe', MASTER_PASSWORD='secret';      #      # OR      #      # 2) Set the variables below. However, in case you choose this method, then      #    start replication for the first time (even unsuccessfully, for example      #    if you mistyped the password in master-password and the slave fails to      #    connect), the slave will create a master.info file, and any later      #    change in this file to the variables'valuesbelow will be ignoredand#    overriddenbythe content of the master.infofile,unless you shutdown#    the slave server,deletemaster.infoandrestart the slaver server.#    For that reason,you may want to leave the lines below untouched#(commented)andinstead use CHANGE MASTER TO(see above)    #  # required unique idbetween2and2^32-1#(anddifferentfromthe master)# defaults to2if master-hostisset# but willnotfunctionasa slave if omitted#server-id=2    #  # The replication master for this slave-required#master-host=    #      # The username the slave will use for authentication when connecting  # to the master-required#master-user=    #      # The password the slave will authenticate with when connecting to  # the master-required#master-password=    #  mysqld# The port the masterislisteningon.# optional-defaults to3306#master-port=    #  # binary logging-notrequired for slaves,but recommended#log-bin=mysql-bin      # Uncomment the following if you are using InnoDB tables  #innodb_data_home_dir=/usr/local/mysql/data#innodb_data_file_path=ibdata1:10M:autoextend#innodb_log_group_home_dir=/usr/local/mysql/data# You canset.._buffer_pool_size up to50-80%      # of RAM but beware of setting memory usage too high  #innodb_buffer_pool_size=16M#innodb_additional_mem_pool_size=2M#Set.._log_file_size to25%of buffer pool size#innodb_log_file_size=5M#innodb_log_buffer_size=8M#innodb_flush_log_at_trx_commit=1#innodb_lock_wait_timeout=50 [mysqldump]        quick  max_allowed_packet=16M [mysql]no-auto-rehash# Remove the next comment character if you arenotfamiliar with SQL#safe-updatesdefault-character-set=utf8 [myisamchk]key_buffer_size=20Msort_buffer_size=20Mread_buffer=2Mwrite_buffer=2M [mysqlhotcopy]interactive-timeout

此时再次查看间隙锁是否开启

唯一索引的间隙锁

-- 创建test表并插入一些数据CREATETABLE`test`(`id`int(1)NOTNULLAUTO_INCREMENT,`name`varchar(8)DEFAULTNULL,PRIMARY KEY(`id`))ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERTINTO`test`VALUES('1','小罗');INSERTINTO`test`VALUES('5','小黄');INSERTINTO`test`VALUES('7','小明');INSERTINTO`test`VALUES('11','小红');

在进行测试之前,我们先来看看test表中存在的隐藏间隙:

(-∞, 1]

(1, 5]

(5, 7]

(7, 11]

(11, +∞]

-- 执行事物1如下START TRANSACTION;-- 查询 id 在 5 - 11 范围的数据并加记录锁 SELECT*FROM`test`WHERE`id`BETWEEN5AND11FORUPDATE;-- 事务B进行插入数据操作 --  注意:以下的语句不是放在一个事务中执行,而是分开多次执行,每次事务中只有一条添加语句START TRANSACTION;-- 事务2插入一条 id = 3,name = '小张1' 的数据 INSERTINTO`test`(`id`,`name`)VALUES(3,'小张1');# 正常执行-- 事务3插入一条 id = 4,name = '小白' 的数据INSERTINTO`test`(`id`,`name`)VALUES(4,'小白');# 正常执行-- 事务4插入一条 id = 6,name = '小东' 的数据INSERTINTO`test`(`id`,`name`)VALUES(6,'小东');# 阻塞-- 事务5插入一条 id = 8, name = '大罗' 的数据INSERTINTO`test`(`id`,`name`)VALUES(8,'大罗');# 阻塞-- 事务6插入一条 id = 9, name = '大东' 的数据INSERTINTO`test`(`id`,`name`)VALUES(9,'大东');# 阻塞-- 事务7插入一条 id = 11, name = '李西' 的数据INSERTINTO`test`(`id`,`name`)VALUES(11,'李西');# 阻塞-- 事务8插入一条 id = 12, name = '张三' 的数据INSERTINTO`test`(`id`,`name`)VALUES(12,'张三');# 正常执行-- 提交事务1,释放事务1的锁 COMMIT;

从上面我们可以看到,(5, 7]、(7, 11] 这两个区间,都不可插入数据,其它区间,都可以正常插入数据。所以我们可以得出结论:当我们给 (5, 7] 这个区间加锁的时候,会锁住 (5, 7]、(7, 11] 这两个区间。

我们再来测试如果我们锁住不存在的数据时,会怎样:

-- 开启事务1 START TRANSACTION;--  查询 id = 3 这一条不存在的数据并加记录锁 SELECT*FROM`test`WHERE`id`=3FORUPDATE;-- 延迟30秒执行,防止锁释放 SELECTSLEEP(30);-- # 注意:以下的语句不是放在一个事务中执行,而是分开多次执行,每次事务中只有一条添加语句--  事务2插入一条 id = 3,name = '小张1' 的数据 INSERTINTO`test`(`id`,`name`)VALUES(2,'小张1');# 阻塞--  事务3插入一条 id = 4,name = '小白' 的数据 INSERTINTO`test`(`id`,`name`)VALUES(4,'小白');# 阻塞-- 事务4插入一条 id = 6,name = '小东' 的数据 INSERTINTO`test`(`id`,`name`)VALUES(6,'小东');# 正常执行-- 事务5插入一条 id = 8, name = '大罗' 的数据 INSERTINTO`test`(`id`,`name`)VALUES(8,'大罗');# 正常执行-- 提交事务1,释放事务1的锁 COMMIT;

我们可以看出,指定查询某一条记录时,如果这条记录不存在,会产生间隙锁。

结论

对于指定查询某一条记录的加锁语句,如果该记录不存在,会产生记录锁和间隙锁,如果记录存在,则只会产生记录锁,如:WHERE `id` = 5 FOR UPDATE;

对于查找某一范围内的查询语句,会产生间隙锁,如:WHERE `id` BETWEEN 5 AND 7 FOR UPDATE;

总结:

上述文档是自己及其团队小伙伴们一起讨论的结果,自己做了一下总结,很感谢团队成员刘昌力、刘明远、朱文彬、祁世松、桑萌萌、石伟男以及彭绍翔等他们的讨论支持。

此文档是对行锁和表锁的一个粗略的认识,存在一定的不足、知识点的缺失、不完善等问题。希望大家能够一起再完善一下,共同学习进步,掌握知识技能,更好的正确高效的运用到平时的工作当中去,才是我们分享技术文档的意思所在。

mysql innodb 全表锁,Mysql InnoDB行锁及表锁分享相关推荐

  1. 004---css样式表(内部样式表、行内样式表、外部样式表)

    一.内部样式表 内嵌式是将CSS代码集中写在HTML文档的head头部中,并且用style标签定义,基本语法如下: 语法中,style标签一般位于head标签中title标签之后,也可以放在HTML文 ...

  2. CSS三种样式表(内部样式表、行内样式表、 外部样式表)

    引入CSS样式表(书写位置) CSS可以写到那个位置? 是不是一定写到html文件里面呢? 内部样式表(内嵌式) 内嵌式是将CSS代码集中写在HTML文档的head头部标签中,并且用style标签定义 ...

  3. mysql innodb事务中 出现_MySql InnoDB事务死锁问题

    这篇博客的文章内容发生于某天晚上服务端日志报了一大堆 Deadlock found when trying to get lock; try restarting transaction,当时通过字面 ...

  4. MySQL事务原理分析(ACID特性、隔离级别、锁、MVCC、并发读异常、并发死锁以及如何避免死锁)

    MySQL事务原理分析(ACID特性.隔离级别.锁.MVCC.并发读异常.并发死锁以及如何避免死锁) 一.事务 目的 组成 特征 事务空间语句 二.ACID特性 原子性(A) 隔离性(I) 持久性(d ...

  5. MySQL中有inserted表吗_使用 inserted 和 deleted 表

    创建和维护数据库 使用 inserted 和 deleted 表 触发器语句中使用了两种特殊的表:deleted 表和 inserted 表.Microsoft® SQL Server™ 2000 自 ...

  6. MySQL中有inserted表吗_SQL触发器中的inserted表和deleted表

    简介:开发也有年头了,但是触发器确实用的比较少,但是无容置疑触发器确实不错, 最近项目要求需要用到的触发器特别多.频繁,觉得很有必要记录和积累下. 在触发器语句中用两个特殊的表一个是deleted表和 ...

  7. mysql 连接 内存溢出_mysql - MySQL中止连接未知错误 - 堆栈内存溢出

    我使用多jdbc执行全表查询以进行数据迁移. 当我使用并行的75个jdbc查询每个表有近3000000行的差异表时,我发现MySQL在所有数据返回到jdbc之前中止了一个连接. 慢日志: Time: ...

  8. mysql 创建游标报错,MySQL游标概念与用法详解

    本文实例讲述了mysql游标概念与用法.分享给大家供大家参考,具体如下: 1.游标的概念(cursor) 一条sql,对应n条资源,取出资源的接口,就是游标,沿着游标,可以一次取出1行.如果开发过安卓 ...

  9. oracle查看dml锁表,【lock】我所认识的Oracle中的锁,DML锁、DDL锁、闩和内部锁。...

    这个问题很简单,你一定对锁有一个认识,但你未必对锁有一个明确的定义. 官方文档这样描述:Locks are mechanisms that prevent destructive interactio ...

  10. 数据库工作笔记017---还记得Oracle悲观锁和乐观锁嘛?以及hibernate对乐观悲观锁的封装

    技术交流QQ群[JAVA,.NET,BigData,AI]:170933152 今天又用到这个了,之前做分布式,很多表在设计的时候都不加各种限制以及关联了,因为加了以后,数据库以后维护,成本很大 但是 ...

最新文章

  1. 你离开学只差这个视频:李宏毅机器学习2020版正式开放上线
  2. applet操作本地文件
  3. 安装jenkins插件的两种方法
  4. linkedin总共能加30000个好友
  5. python顺序表数组_数据结构 | 顺序表
  6. pyppeteer:比selenium更高效的爬虫利器
  7. 物联网赚钱三大步骤浅析
  8. BZOJ4562: [Haoi2016]食物链
  9. 7月Chrome谷歌浏览器份额以68.60%位居榜首
  10. 清华大学开源迁移学习算法库:基于PyTorch实现已有算法
  11. 程序员的进阶课-架构师之路(6)-链表
  12. 丽水数据可视化设计价格_B端产品设计数据可视化图表选择篇
  13. Python中第三方库Requests库的高级用法详解
  14. java图形化元件竖直排列_Java:图形化比较排序
  15. PS教程:利用自定义形状快速打造图案
  16. 武汉大学计算机网络安全学院,消息︱武汉大学计算机学院(新)与国家网络安全学院正式组建...
  17. python2在线编译器_C/C++/Python在线编译器
  18. 一级计算机ppt切换效果怎么做,PPT翻页效果、倒计时怎么做?一分钟变电脑大神...
  19. Devtools下载添加到谷歌扩展程序
  20. 卷积神经网络前向及反向传播过程数学解析

热门文章

  1. 百旺智能编码_【百旺】票字版开票软件操作指南已为您备好,请查阅!
  2. Linux下删除非空文件目录
  3. 1040B. Shashlik Cooking
  4. c语言输入坐标判断位置,〓求助〓谁能帮忙编个程序:输入N个点坐标,判断能否构成多边形....
  5. mfc 设置子窗口只打开一遍_MFC 判断子窗体是不是已经打开,避免重复创建
  6. java integer最大值_五分钟学会java中的基础类型封装类
  7. html 微信发送给朋友,H5链接分享给微信好友,显示标题、描述、缩略图
  8. 学习select(函数)
  9. WINCE下I/O操作基础
  10. 设置最大值_变频器用远传压力表控制恒压供水参数设置