mysql中innodb引擎的行锁是通过加在啥上完成
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql中innodb引擎的行锁是通过加在啥上完成相关的知识,希望对你有一定的参考价值。
参考技术A行锁的等待
在介绍如何解决行锁等待问题前,先简单介绍下这类问题产生的原因。产生原因简述:当多个事务同时去操作(增删改)某一行数据的时候,mysql 为了维护 ACID 特性,就会用锁的形式来防止多个事务同时操作某一行数据,避免数据不一致。只有分配到行锁的事务才有权力操作该数据行,直到该事务结束,才释放行锁,而其他没有分配到行锁的事务就会产生行锁等待。如果等待时间超过了配置值(也就是 innodb_lock_wait_timeout 参数的值,个人习惯配置成 5s,MySQL 官方默认为 50s),则会抛出行锁等待超时错误。
如上图所示,事务 A 与事务 B 同时会去 Insert 一条主键值为 1 的数据,由于事务 A 首先获取了主键值为 1 的行锁,导致事务 B 因无法获取行锁而产生等待,等到事务 A 提交后,事务 B 才获取该行锁,完成提交。这里强调的是行锁的概念,虽然事务 B 重复插入了主键,但是在获取行锁之前,事务一直是处于行锁等待的状态,只有获取行锁后,才会报主键冲突的错误。当然这种 Insert 行锁冲突的问题比较少见,只有在大量并发插入场景下才会出现,项目上真正常见的是 update&delete 之间行锁等待,这里只是用于示例,原理都是相同的。
根据我之前接触到的此类问题,大致可以分为以下几种原因:
1. 程序中非数据库交互操作导致事务挂起将接口调用或者文件操作等这一类非数据库交互操作嵌入在 SQL 事务代码之中,那么整个事务很有可能因此挂起(接口不通等待超时或是上传下载大附件)。
2. 事务中包含性能较差的查询 SQL事务中存在慢查询,导致同一个事务中的其他 DML 无法及时释放占用的行锁,引起行锁等待。
3. 单个事务中包含大量 SQL通常是由于在事务代码中加入 for 循环导致,虽然单个 SQL 运行很快,但是 SQL 数量一大,事务就会很慢。
4. 级联更新 SQL 执行时间较久这类 SQL 容易让人产生错觉,例如:update A set ... where ...in (select B) 这类级联更新,不仅会占用 A 表上的行锁,也会占用 B 表上的行锁,当 SQL 执行较久时,很容易引起 B 表上的行锁等待。
5. 磁盘问题导致的事务挂起极少出现的情形,比如存储突然离线,SQL 执行会卡在内核调用磁盘的步骤上,一直等待,事务无法提交。综上可以看出,如果事务长时间未提交,且事务中包含了 DML 操作,那么就有可能产生行锁等待,引起报错。
参考技术B 概念:锁是用来管理对共享文件的并发访问。innodb会在行级别上对数据库上锁。不过innodb存储引擎会在数据库内部其他多个地方使用锁,从而允许对不同资源提供并发访问。例如操作缓冲池中的LRU列表,删除,添加,移动LRU列表中的元素,为了保证一致性,必须有锁的介入。MyISAM引擎是表锁,而InnoDB提供一致性的非锁定读、行级锁,且行级锁没有相关额外的开销。
锁
table-level locking(表级锁)
整个表被客户锁定。根据锁定的类型,其他客户不能向表中插入记录,甚至从中读数据也受到限制MyISAM、MEMORY默认锁级别,个别时候,InnoDB也会升级为表级锁
row-level locking(行级锁)
只有线程当前使用的行被锁定,其他行对于其他线程都是可用的InnoDB默认行级锁。是基于索引数据结构来实现的,而不是像ORACLE的锁,是基于block的。InnoDB也会升级为表级锁,全表/全索引更新,请求autoinc锁等
page-level locking(页级锁)
锁定表中某些行集合(称做页),被锁定的行只对锁定最初的线程是可行。如果另外一个线程想要向这些行写数据,它必须等到锁被释放。不过其他页的行仍然可以使用BDB默认页级锁
lock与latch
latch称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。latch可以通过命令show engine innodb mutex来进行查看。如图:
由上图可以看出列Type显示的总是InnoDB,列Name显示latch的信息以及所在源码的行数,列Status中显示的os_waits表示操作系统等待的次数。
lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或者rollback后释放(不同事务隔离级别释放的时间可能不一样)。有死锁机制。二则的区别如下:
特点:
InnoDB是通过对索引上的索引项加锁来实现行锁。这种特点也就意味着,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
锁的类型:
有两种标准的行级锁:
共享锁(S lock):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁.SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排它锁(X lock):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他锁.SELECT * FROM table_name WHERE ... FOR UPDATE
InnoDB存储引擎支持意向锁且设计比较简练,分为两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。(意向锁是InnoDB自动加的)
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁.
意向独占锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁.本回答被提问者采纳
从头开始搞懂 MySQL(05)行锁表锁全局锁
1、行锁
行锁是针对数据表中行记录的锁,MySQL 的行锁是在引擎层实现的,并不是 所有的引擎都支持行锁,比如 MyISAM 就不支持,InnoDB 支持行锁,避免了并发控制时使用表锁
1.1 两阶段锁
在 InnoDB 事务中,行锁是在需要的时候才被加上的,但并不是不需要了就立刻释放,而是等待事务结束后释放,这个就是两阶段锁协议
如果我们的事务中需要锁多个行,需要把可能造成锁冲突、可能影响并发度的锁尽量往后放,根据两阶段锁协议,不论怎样安排语句顺序,所有操作需要的行锁都是在事务提交的时候释放的,我们要最大程度的减少事务之间的锁等待,提高并发度。
1.2 死锁和锁检测
当系统中不同的线程中出现了循环资源依赖,涉及的线程等都在等待别的线程释放资源,就会导致线程进入无限循环等待的状态,称为死锁
下图举例数据库中的死锁
事务 A 在等待事务 B 释放 id = 2 的行锁,事务 B 在等待事务 A 释放 id = 1 的行锁,互相等待对方的资源释放,进入了死锁状态。
减少死锁的主要方向就是控制访问相同资源的并发事务量
进入死锁状态有两种策略:
-
进入等待,直到超时,超时时间通过 innodb_lock_wait_timeout 来设置,默认 50s
-
主动检测死锁,发现死锁后主动回滚某一个事务,让其它事务继续执行,参数 innodb_deadlock_detect 设置为 on 开启,默认为 on
2、表锁
MySQL 里面的表级别的锁有,一种是表锁,一种是元数据锁(meta data lock,MDL)
表锁一般在数据库引擎不支持行锁的时候才会使用到
2.1 lock
表锁的语法是
lock tables ...... read / write
,与 FTWRL 类似,可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。lock tables 除了会限制别的线程的读写外,也限定了本线程接下来的操作对象
比如线程 A 执行 lock tables test1 read, test2 write;
的话,其他线程写 test1、读写 test2 的语句都会被阻塞,同时,线程 A 在 unlock tables 之前,也只能执行读 test1、读写 test2 的操作,也不能访问其它表
2.2 MDL(meta data lock)
MDL 不用显式使用,在访问一个表的时候会自动加上。MDL 作用是保证读写的正确性,对一个表做 CRUD 的时候,加 MDL 读锁,对表结构变更时,加 MDL 写锁
MDL 会直到事务提交才释放,在做表结构变更时,要注意,不要导致锁住线上查询和更新
MDL 读锁之间不互斥,所以可以有多个线程对一张表进行 CRUD,写锁是互斥的,保证变更表结构的安全性
3、全局锁
全局锁就是对整个数据库实例加锁,MySQL 让整个数据库处于只读的命令
flush tables with read lock(FTWRL)
使用该命令后,其他线程的更新语句、数据定义语句和更新类事务提交语句都会被阻塞
3.1 FTWRL
使用 FTWRL 命令后,如果由于客户端异常而断开连接,MySQL 会自动释放这个全局锁,整个库回到正常状态
全局锁的典型使用场景就是做全库逻辑备份,通过 FTWRL 保障不会有其它线程对数据库做更新,数据库处于只读状态,然后对整个库做备份
如果这样的话,在主库上备份,备份期间都不能执行更新,在从库上备份,从库不能执行主库同步的 binlog,导致主从延迟
如果不加锁的话,备份系统备份得到的库不是一个逻辑时间点,视图是逻辑不一致的
3.2 mysqldump
当然,FTWRL 比较适用于不支持事务的引擎,对于支持事务的引擎,我们可以使用官方自带的逻辑备份工具 mysqldump,当 mysqldump 使用参数 -single-transaction
的时候,导数据之前就会启动一个事务,确保拿到一致性视图。由于 MVCC 的支持,整个过程是可以正常更新的
以上是关于mysql中innodb引擎的行锁是通过加在啥上完成的主要内容,如果未能解决你的问题,请参考以下文章