1、锁的分类
从对数据的操作进行分类:
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会相互影响。
- 写锁(排它锁):当前写操作没有完成前,它会阻断其他线程的读和写操作。
从对数据操作的粒度进行分类:
- 表锁:对整张表进行加锁,直到操作结束才会释放锁。
- 行锁:对需要操作的行进行上锁。
2、表锁(偏读)
表锁偏向与MyISAM存储引擎,开销小、加锁快;无死锁;锁粒度大,发生锁冲突的概率最高,并发度最低。
2.1 手动加表锁
MYISAM在执行查询语句前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁,下面是手动加锁、查看锁、释放锁代码:
LOCK TABLE tbl_dept WRITE -- 加写锁,如果要加读锁则改成READ
SHOW OPEN TABLES -- 查看锁
UNLOCK TABLES -- 释放锁
分析:执行第一条语句是,会对tbl_dept表加写锁,这个时候别的线程如果对tbl_dept表进行读写操作时,将会被阻塞,直到释放锁才会执行。但是加锁的线程是可以执行读写操作;如果第一条语句执行的是读锁(READ),那么别的线程可以对该表进行读操作,但不能进行写操作。
执行第二条语句,会显示下图数据,对应表的In_use列为1则表示此时表已被加锁
简而言之,就是读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。
2.2 表锁分析
可以通过检查 table locks waited和 table locks immediate状态变量来分析系统上的表锁定:
SHOW STATUS LIKE 'table%'
分析:执行这条语句,会出现下图数据,两个状态变量记录 MYSQL内部表级锁定的情况,两个变量说明如下:
- Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1。
- Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在较严重的表级锁争用情况。
此外, MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎,因为加写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。
2.3 表锁总结
1、对MyISAM表的读操作(加读),不会阻塞其他进程对同一表的读请求,但会阻对同一表的写请求。只有当读释放后才会执行其它进程的写操作。
2、对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操。
3、行锁(偏写)
偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生冲突的概率最低,并发度也最高。InnoDB与MyISAM的最大不同有两点:一是支持事务、二是采用了行级锁。
3.1 锁定一行
对某行数据进行加锁时,则该线程可以查询也可以更新被加锁的数据行,其它线程只能查询但不能更新被加锁的数据行。
BEGIN
SELECT * FROM tbl_dept WHERE id = 1 FOR UPDATE
COMMIT
分析:首先是执行BEGIN表示开始,接着是在查询语句后面加上FOR UPDATE,表示锁定id = 1的行。下面我们可以执行我们需要的操作,而不受别的线程影响。最后执行COMMIT提交,表示释放锁。
3.2 间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时, InnoDB会给符合条件的已有数据记录的索引项加锁;对于
键值在条件范围内但并不存在的记录,叫做“间隙”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所调的间隙锁。
间隙锁的危害:因为执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。
3.3 行锁分析
通过检查 INNODB row lock状态变量来分析系统上的行锁的争夺情况,执行下面的语句会显示该数据:
show status like'innodb_row_lock%'
对下图各个状态量的说明如下:
Innodb_row_lock_current_waits:当前正在等待锁定的数量。
Innodb_row_lock_time:从系统启动到现在锁定总时间长度。
Innodb_row_lock_time_avg:每次等待所花平均时间。
Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间。
Innodb_row_lock_waits:系统启动后到现在总共等待的次数。
对于这5个状态变量,比较重要的主要是:
Innodb_row_lock_time_avg(等待平均时长)
Innodb_row_lock_waits(等待总次数)
Innodb_row_lock_time(等待总时长)
尤其是当等待次数很高,而且每次等待时长也不小的候,我们就需要分析系統中为什么会有如此多的等待,然后根据分析结果着手定制优化计划。
3.4 行锁总结
Innob存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并
发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势了。
但是, Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让 Innodb的整体性能表现不仅不能比 MyISAM高,甚至可能会更差,比如索引失效可能会使行锁升级为表锁(VARCHAR不加引号)。
4、死锁
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。死锁发生以后,只有部分或者完全回滚其中一个事务,才能打破死锁。