你真的了解MySQL中的锁吗

数据库 MySQL
锁是并发访问同一个共享资源时的同步机制,Synchronized、ReentrantLock和ReentrantReadWriteLock都用过的吧,都是一样的道理.

[[436834]]

大家在学习mysql的时候,估计也听说过数据库中的锁

锁,大家应该是很熟悉的了吧,就是多个线程同时对共享资源的访问的竞争

那么大家对于mysql中的锁又有多少了解呢

先说一下mysql中的几种语言,SQL语言共分为四大类

数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL。

1. 数据查询语言DQL:数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE

2 .数据操纵语言DML:数据操纵语言DML主要有三种形式,插入,更新,删除。

3. 数据定义语言DDL:数据定义语言DDL用来创建数据库中的各种对象如:表 视图 索引 同义词 簇。DDL操作是隐性提交的,不能rollback

4. 数据控制语言DCL:数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效果,对数据库实行监视等。

锁是并发访问同一个共享资源时的同步机制,Synchronized、ReentrantLock和ReentrantReadWriteLock都用过的吧,都是一样的道理

MySQL的锁是在服务器层或者存储引擎层实现的,保证数据访问的一致性和有序性

  • 按模式分类为:乐观锁与悲观锁。
  • 按粒度分可以分为全局锁、表级锁、页级锁、行级锁。
  • 按属性可以分为:共享锁、排它锁。
  • 按状态分为:意向共享锁、意向排它锁。
  • 按算法分为:间隙锁、临键锁、记录锁。

乐观锁

一种思想,乐观锁建设数据一般情况下不会产生冲突,在数据的操作过程中不会对数据做任何锁定,只有当数据进行提交跟新的时候,才会正式对数据的冲突与否进行检测

如果发生冲突了,则返回错误,调用者决定如何操作,是回滚还是重试

比较适用于读多写少的情况,如果写场景比较多,写冲突的可能性比较高,可能需要不断重试,这样会大大降低系统性能

这种可以通过增加一个数据版本字段Version来实现,读取数据的时候把这个字段一起读出来,数据每更新一次,对Version字段加一,当我们提交更新数据的时候,判断数据库表中的对应记录的版本信息和第一次取出来的Version是否一致,一致则可以更新,不一致则过期

悲观锁

这个也是一种思想,悲观的看法,认为每次去取数据的时候都会有别人去修改,所以在整个数据处理过程中,数据处于锁定状态

适用于并发量不大,写入操作比较频繁,数据一致性比较高的场景,MySQL中,共享锁和排他锁都是属于悲观锁的不同实现

全局锁

对整个数据库实例进行加锁,一般用于全库的逻辑备份

MySQL 提供了一个加全局读锁的方法,命令是Flush tables with read lock (FTWRL)。使用这个命令之后,整个库处于只读状态,其它线程的更新语句都会被阻塞

主库备份,需要考虑影响业务系统,从库备份,在备份期间不能执行主库同步过来的binlog,主从同步会有延迟

解决办法,mysqldump使用参数--single-transaction,启动一个事务,确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。

表级锁

对操作的数据表加锁,MyISAM和InnoDB引擎都支持表级锁定,这里分为两种,一种是表锁,一种是元数据锁,即meat data lock,MDL锁

  1. lock tables 表名 read #该表可以读,不能ddl 和 dml 中增删改,只能读取表数据 
  2. lock tables 表名 write # 既不能读,也不能写 

表锁的语法是 lock tables … read/write。与 FTWRL 类似,可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。需要注意,lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。

MDL锁

防止DDL和DML并发的冲突,你想啊,一个查询正在遍历表中数据,而执行期间另一个线程对这个表结构进行了变更,删除了一列,查询线程拿到的结果跟表结构对不上,就乱套了

MDL锁不是显示的,MDL锁是在5.5版本引入的

对一个表做增删改查操作的时候,加MDL读锁,读锁之间不冲突,所以多个线程可以同时对一个表进行增删改查

当要对表结构变更操作的时候,加MDL写锁,读锁和写锁、写锁和写锁都是冲突的,用来保证变更结构操作的安全性

两个线程同时对一个表中加字段,其中一个要等另一个执行完才可以开始。一个线程A先在查询数据,另一个线程B想要加一列数据,需要等A线程执行完才可以执行线程B,就解决了上面的问题

MDL锁是系统默认加的,我们理解了上面的机制之后,一定要注意MDL写锁之后的读锁和写锁都会阻塞,所以在给一些表加字段的时候一定要注意,尽量避开业务系统比较繁忙的时候

即使小表,操作不慎,如果一个表的查询语句频繁,而且客户端有重试机制,也就是超时之后还会再起一个新session,库的线程很容易就慢了,这时系统就崩了

千万不要在长事务中对表结构进行修改,事务不提交会一直占用MDL写锁,那后面的语句就需要一直等待

页级锁

页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折衷的页级锁,一次锁定相邻的一组记录。BDB 引擎支持页级锁。

行级锁

MySQL中只有InnoDB支持行级锁,行级锁分为共享锁和排他锁。

行级锁是粒度最低的锁,锁冲突概率最低。但加锁慢、开销大,容易发生死锁现象。

行锁并不是直接锁记录,而是锁索引

索引分为主键索引和非主键索引,一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引

共享锁

共享锁,也就是我们常说的读锁,一个事务对数据加上读锁之后,其它事务只能对该数据加读锁,不能做任何修改,不能加写锁

这样可以更好的支持并发中的读取数据,读取数据的时候,不允许其他事物对当前数据进行修改操作,从而避免不可重复读的问题的出现

  1. select … lock in share mode 

排它锁

排它锁,也就是写锁,当对数据加上写锁之后,其它事务不能对该数据读写,这个时候读锁和写锁都不可以加了,也就是全部阻塞了

写锁就是为了解决在数据修改的时候,不允许其它事务对当前数据进行修改和读取操作,从而可以避免脏读问题的产生

共享锁可以避免不可重复读的问题,排它锁可以避免脏读问题的产生

意向共享锁和意向排它锁

意向锁的出现就是为了协调表锁和行锁,支持多粒度的并存

事务A有行锁的时候,MySQL会自动给该表加上意向锁,事务B如果想申请整个表的写锁,就不用遍历去每一行判断是否存在行锁,只需要判断是否存在意向锁,即可决定是否可以加表的写锁

意向锁的互斥性

当然,表格中的共享锁和排他锁都是表锁,即表锁和意向锁的关系

意向锁是不会和行级的共享排他锁互斥的

给大家再解释一下,就是有行级共享锁,那就加上意向共享锁,当需要加表级的共享锁的时候,兼容,即行读表读共存;相反,表级的排他锁加不上,也就是行读表写不共存

相应的行级的排他锁,也就是写锁加上之后,表级的读锁和写锁都是不能加上的了,也就是行写表既不可读也不可写

总结

  • 行读表读共存
  • 行读表写不共存
  • 行写既不可读也不可写

记录锁

记录锁是封锁记录,记录锁也叫行锁

间隙锁

间隙锁基于非唯一索引,它锁定一段范围内的索引记录。使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据

临键锁

临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间,是一个左开右闭区间。临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。

每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。

需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

 

责任编辑:姜华 来源: Java贼船
相关推荐

2024-01-29 10:09:59

数据库INT(3)INT(11)

2014-04-17 16:42:03

DevOps

2022-07-26 00:00:22

HTAP系统数据库

2021-01-15 07:44:21

SQL注入攻击黑客

2021-11-09 09:48:13

Logging python模块

2018-12-21 11:24:55

Java时间处理编程语言

2014-11-28 10:31:07

Hybrid APP

2020-02-27 10:49:26

HTTPS网络协议TCP

2019-09-16 08:40:42

2023-03-16 10:49:55

2023-11-01 13:48:00

反射java

2023-10-24 08:53:24

FutureTas并发编程

2017-10-18 22:01:12

2012-05-31 09:56:54

云安全

2019-11-06 09:52:01

JavaScript单线程非阻塞

2022-03-14 07:53:27

ELTETL大数据

2015-07-31 10:35:18

实时计算

2022-12-12 08:46:11

2018-01-06 10:38:51

Ping抓包 ICMP协议

2018-04-27 15:30:53

Java三目运算符
点赞
收藏

51CTO技术栈公众号