一、简介
1.1 概述
MyISAM 和 Memory 存储引擎使用的是表级锁,BDB 引擎使用的是页级锁,也支持表级锁。由于 BDB 引擎基本已经成为历史,因此就不再介绍了。
InnoDB 存储引擎既支持行级锁,也支持表级锁,默认情况下使用行级锁。
所谓表级锁,它直接锁住的是一个表,开销小,加锁快,不会出现 死锁的情况,锁定粒度大,发生锁冲突的概率更高,并发度最低。
所谓行级锁,它直接锁住的是一条记录,开销大,加锁慢,发生锁冲突的概率较低,并发度很高。
所谓页级锁,它是锁住的一个页面,它的开销介于表级锁和行级锁中间,也可能会出现死锁,锁定粒度也介于表级锁和行级锁中间,并发度也介于表级锁和行级锁中间。
行级锁更适合大量按照索引条件并发更新少量不同的数据,同时还有并发查询的应用
看你的mysql现在已提供什么存储引擎:
mysql> show engines;
复制
看你的mysql当前默认的存储引擎:
mysql> show variables like '%storage_engine%';
复制
1.2 innoDB行级锁
行级锁本身与表级锁的实现差别就很大,而事务的引入也带来了很多新问题,尤其是事务的隔离性,与锁机制息息相关。
对于事务的基本操作,对于不同隔离级别可能引发的问题,像脏读、不可重复读等问题。
数据库实现事务隔离的方式,基本可以分为两种:
(1)在操纵数据之前,先对其加锁,防止其他事务对数据进行修改。这就需要各个事务串行操作才可以实现。
(2)不加任何锁,通过生成一系列特定请求时间点的一致性数据快照,并通过这个快照来提供一致性读取。
上面的第二种方式就是数据多版本并发控制,也就是多版本数据库,一般简称为 MVCC 或者 MCC,它是 Multi Version Concurrency Control 的简写。
数据库的事务隔离越严格,并发的副作用就越小,当然付出的代价也就越大,因为事务隔离机制实质上是使得事务在一定程度上”串行化”,这与并行是矛盾的。
InnoDB有两种类型的行级锁,两种内部使用的意向锁;
共享锁(S):允许一个事务读一行数据时,阻止其他的事务读取相同数据的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据的共享锁和排他锁。
意向共享锁(IS):事务打算给数据行加行共享锁。事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁。事务在给一个数据行加排他锁前必须先取得该表的IX锁。
悲观锁(抽象,不真实存在的锁)
乐观锁(抽象,不真实存在的锁)
InnoDB 行级锁是通过给索引上的索引项加锁来实现的,InnoDB行级锁只有通过索引条件检索数据,才使用行级锁;否则,InnoDB使用表锁
在不通过索引(主键)条件查询的时候,InnoDB是表锁而不是行锁
1.3 4种锁的共存逻辑关系表
共享锁语句
select * from table_name lock in share mode;
复制
排他锁语句
select * from table_name for update;
复制
1.4 innodb间隙锁
可以理解为是对于一定范围内的数据进行锁定,如果说这个区间没有这条数据的话也是会锁住的;主要是解决幻读的问题,如果没有添加间隙锁,如果其他事物中添加id在1到100之间的某条记录,此时会发生幻读;另一方面,视为了满足其恢复和赋值的需求。
默认情况下,innodb_locks_unsafe_for_binlog是0(禁用),这意味着启用了间隙锁定:InnoDB使用下一个键锁进行搜索和索引扫描。若要启用该变量,请将其设置为1。这将导致禁用间隙锁定:InnoDB只使用索引记录锁进行搜索和索引扫描
例如:
注意:间隙锁容易导致死锁
二、死锁
2.1 死锁的原因
官方定义如下:两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁。
MySQL有两种死锁处理方式:
等待,直到超时(innodb_lock_wait_timeout=50s)。
发起死锁检测,主动回滚一条事务,让其他事务继续执行(innodb_deadlock_detect=on)。
由于性能原因,一般都是使用死锁检测来进行处理死锁。
死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁。检测到死锁之后,选择插入更新或者删除的行数最少的事务回滚,基于 INFORMATION_SCHEMA.INNODB_TRX 表中的 trx_weight 字段来判断
2.2 对于锁与事务的建议
收集死锁信息:
1) 利用命令 SHOW ENGINE INNODB STATUS查看死锁原因。
2) 调试阶段开启 innodb_print_all_deadlocks,收集所有死锁日志。减少死锁:
1) 使用事务,不使用 lock tables 。
2) 保证没有长事务。
3) 操作完之后立即提交事务,特别是在交互式命令行中。
4) 如果在用 (SELECT … FOR UPDATE or SELECT … LOCK IN SHARE MODE),尝试降低隔离级别。
5) 修改多个表或者多个行的时候,将修改的顺序保持一致。
6) 创建索引,可以使创建的锁更少。
7) 最好不要用 (SELECT … FOR UPDATE or SELECT … LOCK IN SHARE MODE)。
5) 如果上述都无法解决问题,那么尝试使用 lock tables t1, t2, t3 锁多张表