持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
MySQL中的锁-MyISAM篇
mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 5.7.21 |
+-----------+
1 row in set (0.01 sec)
MySQL的表级锁有两种模式:表共享读锁(Table Read Lock) 和表独占写锁(Table Write Lock) 。
对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;MyISAM表的读操作与写操作之间,以及写操作之间是串行的!
/*
* 新建一个MyISAM的表mylock 来测试
*/
CREATE TABLE `mylock` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`NAME` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('1', 'a');
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('2', 'b');
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('3', 'c');
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('4', 'd');
MyISAM写锁阻塞读的案例:
当一个线程获得对一个表的写锁之后,只有持有锁的线程可以对表进行更新操作。其他线程的读写操作都会等待,直到锁释放为止。
session1 | session2 |
---|---|
获取表的write锁 lock table mylock write; | |
当前session对表的查询,插入,更新操作都可以执行 select * from mylock; insert into mylock values(5,'e'); | 当前session对表的查询/插入都会被阻塞 select * from mylock; insert into mylock values(6,'f'); |
释放锁: unlock tables; | 当前session能够立刻执行,并返回对应结果 |
如下图两个session测试
读测试:
写测试:
MyISAM读阻塞写的案例:
一个session使用lock table给表加读锁,这个session可以锁定表中的记录,但更新和访问其他表都会提示错误,同时,另一个session可以查询表中的记录,但更新就会出现锁等待。
session1 | session2 |
---|---|
获得表的read锁定 lock table mylock read; | |
当前session可以查询该表记录: select * from mylock; | 当前session可以查询该表记录: select * from mylock; |
当前session不能查询没有锁定的表 select * from person Table 'person' was not locked with LOCK TABLES | 当前session可以查询或者更新未锁定的表 select * from mylock insert into person values(1,'zhangsan'); |
当前session插入或者更新表会提示错误 insert into mylock values(7,'h') Table 'mylock' was locked with a READ lock and can't be updated update mylock set name='aa' where id = 1; Table 'mylock' was locked with a READ lock and can't be updated | 当前session插入数据会等待获得锁 insert into mylock values(7,'h'); |
释放锁 unlock tables; | 获得锁,更新成功 |
注意:
MyISAM在执行查询语句之前,会自动给涉及的所有表加读锁,在执行更新操作前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此用户一般不需要使用命令来显式加锁,上例中的加锁时为了演示效果。
MyISAM的并发插入问题
MyISAM表的读和写是串行的,这是就总体而言的,在一定条件下,MyISAM也支持查询和插入操作的并发执行(增加local关键字,类似增加了事务功能)
session1 | session2 |
---|---|
获取表的read local锁定 lock table mylock read local | |
当前session不能对表进行更新或者插入操作 insert into mylock values(6,'f') Table 'mylock' was locked with a READ lock and can't be updated update mylock set name='aa' where id = 1; Table 'mylock' was locked with a READ lock and can't be updated | 其他session可以查询该表的记录 select* from mylock |
当前session不能查询没有锁定的表 select * from person Table 'person' was not locked with LOCK TABLES | 其他session可以进行插入操作,但是更新会阻塞 update mylock set name = 'aa' where id = 1; |
当前session不能访问其他session插入的记录; | |
释放锁资源:unlock tables | 当前session获取锁,更新操作完成 |
当前session可以查看其他session插入的记录 |
可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺:
mysql> show status like 'table%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Table_locks_immediate | 352 |
| Table_locks_waited | 2 |
+-----------------------+-------+
-- 如果Table_locks_waited的值比较高,则说明存在着较严重的表级锁争用情况。
总结
对于MyISAM的表锁,主要讨论了以下几点: (1)共享读锁(S)之间是兼容的,但共享读锁(S)与排他写锁(X)之间,以及排他写锁(X)之间是互斥的,也就是说读和写是串行的。 (2)在一定条件下,MyISAM允许查询和插入并发执行,我们可以利用这一点来解决应用中对同一表查询和插入的锁争用问题。 (3)MyISAM默认的锁调度机制是写优先,这并不一定适合所有应用,用户可以通过设置LOW_PRIORITY_UPDATES参数,或在INSERT、UPDATE、DELETE语句中指定LOW_PRIORITY选项来调节读写锁的争用。 (4)由于表锁的锁定粒度大,读写之间又是串行的,因此,如果更新操作较多,MyISAM表可能会出现严重的锁等待,可以考虑采用InnoDB表来减少锁冲突。
我是纪先生,用输出倒逼输入而持续学习,持续分享技术系列文章,以及全网值得收藏好文,欢迎关注公众号,做一个持续成长的技术人。
mysql进阶系列历史文章
(也可以在掘金专栏中看其他相关文章)
3. MySQL进阶系列:mysql中MyISAM和InnoDB有什么区别;
4. MySQL进阶系列:mysql中表设计如何更好的选择数据类型;
5. MySQL进阶系列:数据库设计中的范式究竟该如何使用;
6. MySQL进阶系列:一文详解explain各字段含义;
7. MySQL进阶系列:为什么mysql使用B+作为索引的数据结构;