首先我们都知道,锁就是计算机用来控制多个线程并发访问同一个共享资源的机制。通过使用锁机制来确保并发访问的数据一致性。
在MySQL中不同的存储引擎的表锁支持的锁机制类型也不同:
MyIsSAM存储引擎----------采用表级锁
MEMORY存储引擎----------采用表级锁
InnoDB存储引擎-----------采用行级锁,支持表级锁
BDB存储引擎----------采用页面锁、行级锁
三种锁机制的特性:
1.行级锁:
缺点:
开销大、加锁慢、会出现死锁的情况
优点:
锁定的粒度小,发生锁冲突的可能性低,并发度高
2.表级锁:
缺点:
锁定粒度大,发生锁冲突的可能性大,并发度低
优点:
开销小,加锁快,不会出现死锁
3.页面锁:
开销和加锁的时间介于表级锁和行级锁之间,会出现死锁的情况,锁定粒度介于行级锁和表级锁之间,并发度一般般。
根据以上各种锁机制的特性来看:
表级锁更适合以查询为主,少量按照索引条件更新数据的情况。
行级锁更适合有大量按照索引条件并发更新少量不同数据,同时又有并发查询的需求的情况。
一、MyISAM表级锁表级锁是MySql数据库中所有的存储引擎中最大粒度的锁定机制。MEMORY存储引擎也使用了表级锁。表级锁的锁模式分为两类:共享读锁、独占写锁!
顾名思义,在表级锁中,当一个线程在对表的数据进行读取的时候不会阻塞其他线程对表中的数据的读取操作。
但是当一个线程在对表中的数据进行写操作的时候,就会阻塞其他线程对表的读取和写操作,当持有写锁的线程释放锁之后,其他线程才能继续访问或者更新
那如果是一个线程获取了读锁,另一个线程是否可以进行insert操作呢?
这里涉及到并发插入的问题,MyISAM有一个系统变量(concurrent inserts)并发插入,他的值可以分为:0、1、2
0:任何时候都不允许并发插入
1:在表的中间没有被删除的空行的时候,允许一个线程在读取数据的时候,另外一个线程在表的末尾进行insert操作;
2:不管表的中间有没有被删除的空行,都允许另外一个线程在表的末尾进行insert操作;
1.1添加表级锁
MyISAM存储引擎的表级锁是自动添加的,我们在做查询select之前或者更新操作(update,delete,insert)之前,MyISAM自动给涉及的表添加表级锁。所以我们没有必要再显式地添加锁
显式地添加读锁:lock tables 表名 read 释放锁:unlock tables
MyISAM表级锁在添加锁的时候是直接一次性获取SQL所需要的所有的锁,所以它不会发生死锁的原因;
二、InnoDB行级锁
InnoDB行级锁的锁模式分为两种:
共享锁(S):允许一个事务去读数据,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
需要注意的是在InnoDB存储引擎中还有两种意向锁,这两种意向锁都是表级锁,分别是意向共享锁(IS)和意向排他锁(IX),
事务在添加共享锁(行级)之前,需要先添加意向共享锁(表级)。在添加排他锁(行级)之前,需要先获取意向排他锁(表
级)。这样在InnoDB中,就实现了行级和表级多粒度锁共存。
注意:意向锁是InnoDB自动添加的,不需要我们手动的去添加。
添加共享锁:
select * from tb_trade where id = '1aasdf' lock in share mode
添加排他锁:
select * from tb_trade where id = 'afasf' for update
行级锁是通过给索引的索引项添加锁实现的,如果没有索引,则通过隐藏的聚簇索引来对行数据加锁。如果不通过索引条件查询数据,InnoDB将对表中的所有数据行加锁,相当于表级锁。
注意:行锁是针对索引添加的而不是对行数据纪录添加的。所以如果使用了同一个索引来查询不同的行数据纪录会出现锁冲突。只能等待释放锁之后其他线程才能继续执行。
死锁情况
在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,当两个事务都需要对方持有的排他锁才能继续完成事务的时候,就会出现循环锁等待,也就出现死锁的情况。
三、BDB的页面锁BDB存储引擎采用了页面锁,也支持表级锁。
页面锁开销和加锁时间和锁定粒度介于表级锁和行级锁之间,会出现死锁的情况,并发度一般。
简单来说,页面锁就是针对表级锁和行级锁的优缺点一个折中的锁定机制,表级锁的锁定时间快,但是粒度大,冲突多。行级锁的粒度小,冲突少,但是锁定的时间慢。而页面锁介于两者之间。