本章要点
1.锁类型
2.锁范围
3.死锁
1.锁类型
1.1 锁粒度分类
MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类。
全局锁
- 全局锁:就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。unlock tables可以解除。
表级锁
MySQL 里面表级别的锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)。
-
表锁:表锁的语法是 lock tables … read/write;与 FTWRL 类似,可以用 unlock tables 主动释放锁。
-
MDL(metadata lock)锁:MDL 不需要显式使用,在访问一个表的时候会被自动加上。MDL 的作用是,保证读写的正确性。
表锁: 开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低。
行锁
InnoDB只有通过索引条件检索数据才使用行级锁,否则,InnoDB使用表锁也就是说,InnoDB的行锁是基于索引的。
行锁: 开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高 不同的存储引擎支持的锁粒度是不一样的。
InnoDB和MyISAM有两个本质的区别:InnoDB支持行锁、InnoDB支持事务。
InnoDB实现了以下两种类型的行锁:
-
共享锁(S锁、读锁): 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。即多个客户可以同时读取同一个资源,但不允许其他客户修改。
-
排他锁(X锁、写锁): 允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的读锁和写锁。写锁是排他的,写锁会阻塞其他的写锁和读锁。
另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁:
-
意向共享锁(IS): 事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
-
意向排他锁(IX): 事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
InnoDB行锁和表锁都支持、MyISAM只支持表锁!
间隙锁
当用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁(左开右开),这种锁机制就是所谓的间隙锁,即Next-Key锁(左开右闭)。
InnoDB间隙锁解决了幻读问题
2.锁范围
-
2.1行锁
能够精准匹配某一条数据(一般是唯一索引或主键),就用记录锁(行锁)
image.png -
2.2 间隙锁
范围查询,没有命中任何记录,用间隙锁
image.png
2.2 临键锁
-
范围查询,命中了某些记录
image.png
3. 死锁
死锁:指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的InnoDB。
3.1锁相关信息
*** 默认锁等待时间***
MySQL 有一个参数来控制获取锁的等待时间,默认是 50 秒
show VARIABLES like 'innodb_lock_wait_timeout';
锁相关信息查询
show status like 'innodb_row_lock_%';
image.png
- Innodb_row_lock_current_waits:当前正在等待锁定的数量;
- Innodb_row_lock_time :从系统启动到现在锁定的总时间长度,单位 ms;
- Innodb_row_lock_time_avg :每次等待所花平均时间;
- Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间;
- Innodb_row_lock_waits :从系统启动到现在总共等待的次数。
select * from information_schema.INNODB_TRX; -- 当前运行的所有事务 ,还有具体的语句
select * from information_schema.INNODB_LOCKS; -- 当前出现的锁
select * from information_schema.INNODB_LOCK_WAITS; -- 锁等待的对应关系
3.2 避免死锁
- 在程序中,操作多张表时,尽量以相同的顺序来访问(避免形成等待环路);
- 批量操作单张表数据的时候,先对数据进行排序(避免形成等待环路);
- 申请足够级别的锁,如果要操作数据,就申请排它锁;
- 尽量使用索引访问数据,避免没有 where 条件的操作,避免锁表;
- 如果可以,大事务化成小事务;
- 使用等值查询而不是范围查询查询数据,命中记录,避免间隙锁对并发的影响
网友评论