MySQL锁机制
MySQL表锁
表锁应该是听的最多的一种锁,因为实现起来比较简单,同时应用范围也比较广泛,几乎所有的存储引擎都会支持这个粒度的锁,比如常用的MyISAM、InnoDB、Memory等各大引擎都实现了表锁。 > 但要注意,不同引擎的表锁也在实现上以及加锁方式上有些许不同,但归根结底,表锁的意思也就以表作为锁的基础,将锁加在表上,一张表只能存在一个同一类型的表锁。 上面这段话中提到过,不同的存储引擎的表锁在使用方式上也有些不同,比如InnoDB是一个支持多粒度锁的存储引擎,它的锁机制是基于聚簇索引实现的,当SQL执行时,如果能在聚簇索引命中数据,则加的是行锁,如无法命中聚簇索引的数据则加的是表锁,比如: ```sql select * from `zz_users` for update; ``` 这条SQL就无法命中聚簇索引,此时自然加的就是表级别的排他锁,但是这个表级锁,并不是真正意义上的表锁,是一个“伪表锁”,但作用是相同的,锁了整张表。 而反观MyISAM引擎,由于它并不支持聚簇索引,所以无法再以InnoDB的这种形式去对表上锁,因此如若要在MyISAM引擎中使用表锁,又需要使用额外的语法,如下: ```sql -- MyISAM引擎中获取读锁(具备读-读可共享特性) LOCK TABLES `table_name` READ; -- MyISAM引擎中获取写锁(具备写-读、写-写排他特性) LOCK TABLES `table_name` WRITE; -- 查看目前库中创建过的表锁(in_use>0表示目前正在使用的表锁) SHOW OPEN TABLES WHERE in_use > 0; -- 释放已获取到的锁 UNLOCK TABLES; ``` 如上便是MyISAM引擎中,获取表级别的共享锁和排他锁的方式,但这里的关键词其实叫做READ、WEITE,翻译过来也就是读、写的意思,因此关于共享锁就是读锁、排他锁就是写锁的说法,估计就是因此而来的。 不过MyISAM引擎中,获取了锁还需要自己手动释放锁,否则会造成死锁现象出现,因为如果不手动释放锁,就算事务结束也不会自动释放,除非当前的数据库连接中断时才会释放。 除开最基本的表锁外,还有其他几种表锁,即元数据锁、意向锁、自增锁、全局锁,接下来一起来聊一聊这些特殊的锁。 #### 元数据锁(Meta Data Lock) Meta Data Lock元数据锁,也被简称为MDL锁,这是基于表的元数据加锁。所有存储引擎的表都会存在一个.frm文件,这个文件中主要存储表的结构(DDL语句),而DML锁就是基于.frm文件中的元数据加锁的。 对于这种锁是在MySQL5.5版本后再开始支持的,一般来说咱们用不上,因此也无需手动获取锁,这个锁主要是用于:更改表结构时使用,比如你要向一张表创建/删除一个索引、修改一个字段的名称/数据类型、增加/删除一个表字段等这类情况。 因为毕竟当你的表结构正在发生更改,假设此时有其他事务来对表做CRUD操作,自然就会出现问题,比如我刚删了一个表字段,结果另一个事务中又按原本的表结构插入了一条数据,这显然会存在风险,因此DML锁在加锁后,整张表不允许其他事务做任何操作。 #### 意向锁(Intention Lock) InnoDB引擎是一种支持多粒度锁的引擎,而意向锁则是InnoDB中为了支持多粒度的锁,为了兼容行锁、表锁而设计的,怎么理解这句话呢?先来看一个例子: >假设一张表中有一千万条数据,现在事务T1对ID=8888888的这条数据加了一个行锁,此时来了一个事务T2,想要获取这张表的表级别写锁,经过前面的一系列讲解,大家应该知道写锁必须为排他锁,也就是在同一时刻内,只允许当前事务操作,如果表中存在其他事务已经获取了锁,目前事务就无法满足“独占性”,因此不能获取锁。 那思考一下,由于T1是对ID=8888888的数据加了行锁,那T2获取表锁时,是不是得先判断一下表中是否存在其他事务在操作?但因为InnoDB中有行锁的概念,所以表中任何一行数据上都有可能存在事务加锁操作,为了能精准的知道答案,MySQL就得将整张表的1000W条数据全部遍历一次,然后逐条查看是否有锁存在,那这个效率自然会非常的低。 >有人可能会说,慢就慢点怎么了,能接受!但实际上不仅仅存在这个问题,还有另外一个致命问题,比如现在MySQL已经判断到了第567W行数据,发现前面的数据上都没有锁存在,正在继续往下遍历。 要记住MySQL是支持并发事务的,也就是MySQL正在扫描后面的每行数据是否存在锁时,万一又来了一个事务在扫描过的数据行上加了个锁怎么办?比如在第123W条数据上加了一个行锁。那难道又重新扫描一遍嘛?这就陷入了死循环,行锁和表锁之间出现了兼容问题。 >也正是由于行锁和表锁之间存在兼容性问题,所以意向锁它来了!意向锁实际上也是一种特殊的表锁,意向锁其实是一种“挂牌告知”的思想,好比日常生活中的出租车,一般都会有一个牌子,表示它目前是“空车”还是“载客”状态,而意向锁也是这个思想。 比如当事务T1打算对ID=8888888这条数据加一个行锁之前,就会先加一个表级别的意向锁,比如目前T1要加一个行级别的读锁,就会先添加一个表级别的意向共享锁,如果T1要加行级别的写锁,亦是同理。 此时当事务T2尝试获取一个表级锁时,就会先看一下表上是否有意向锁,如果有的话再判断一下与自身是否冲突,比如表上存在一个意向共享锁,目前T2要获取的是表级别的读锁,那自然不冲突可以获取。但反之,如果T2要获取一个表记的写锁时,就会出现冲突,T2事务则会陷入阻塞,直至T1释放了锁(事务结束)为止。 #### 自增锁(AUTO-INC Lock) 自增锁主要负责维护并发事务下自增列的顺序,也就是说,每当一个事务想向表中插入数据时,都要先获取自增锁先分配一个自增的顺序值。这个是专门为了提升自增ID的并发插入性能而设计的,通常情况下咱们在建表时,都会对一张表的主键设置自增特性。 当对一个字段设置AUTO_INCREMENT自增后,意味着后续插入数据时无需为其赋值,系统会自动赋上顺序自增的值。但想一想,比如目前表中最大的ID=88,如果两个并发事务一起对表执行插入语句,由于是并发执行的原因,所以有可能会导致插入两条ID=89的数据。因此这里必须要加上一个排他锁,确保并发插入时的安全性,但也由于锁的原因,插入的效率也就因此降低了,毕竟将所有写操作串行化了。 为了改善插入数据时的性能,自增锁诞生了,自增锁也是一种特殊的表锁,但它仅为具备AUTO_INCREMENT自增字段的表服务,同时自增锁也分成了不同的级别,可以通过innodb_autoinc_lock_mode参数控制。 #### 全局锁 全局锁其实是一种尤为特殊的表锁,其实将它称之为库锁也许更合适,因为全局锁是基于整个数据库来加锁的,加上全局锁之后,整个数据库只能允许读,不允许做任何写操作,一般全局锁是在对整库做数据备份时使用。 ```sql -- 获取全局锁的命令 FLUSH TABLES WITH READ LOCK; -- 释放全局锁的命令 UNLOCK TABLES; ``` 从上述的命令也可以看出,为何将其归纳到表锁范围,因为获取锁以及释放锁的命令都是表锁的命令。
顶部
收展
底部
[TOC]
目录
MySQL锁机制的由来与分类
MySQL共享锁与排他锁
MySQL表锁
MySQL行锁
MySQL页面锁、乐观锁与悲观锁
MySQL死锁
MySQL锁机制的底层实现原理
相关推荐
MySQL教程
MySQL命令
MySQL索引
MySQL事务
MySQL版本特性