从对数据操作的粒度分 :
1) 表锁:操作时,会锁定整个表。
2) 行锁:操作时,会锁定当前操作行。
从对数据操作的类型分:
1) 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
2) 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁。
MyISAM 表锁
MyISAM 存储引擎只支持表锁,这也是MySQL开始几个版本中唯一支持的锁类型。
加读锁 : lock table table_name read;
加写锁 : lock table table_name write;
演示读锁
环境:
CREATE TABLE `tb_book` (
`id` INT(11) auto_increment,
`name` VARCHAR(50) DEFAULT NULL,
`publish_time` DATE DEFAULT NULL,
`status` CHAR(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=myisam DEFAULT CHARSET=utf8 ;
INSERT INTO tb_book (id, name, publish_time, status) VALUES(NULL,'java编程思想','2088-08-01','1');
INSERT INTO tb_book (id, name, publish_time, status) VALUES(NULL,'solr编程思想','2088-08-08','0');
CREATE TABLE `tb_user` (
`id` INT(11) auto_increment,
`name` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=myisam DEFAULT CHARSET=utf8 ;
INSERT INTO tb_user (id, name) VALUES(NULL,'令狐冲');
INSERT INTO tb_user (id, name) VALUES(NULL,'田伯光');
myisam在创建表后自动加表锁
1)获得tb_book 表的读锁
lock table tb_book read;
2) 执行查询操作
select * from tb_book;
正常执行查询出数据,另外开启客户端也可以正常查出数据,原因是读锁是共享锁,针对同一份数据,多个读操作可以同时进行而不会互相影响。而在当前未释放锁的客户端执行查询另外一张未获取锁的表则报错,当前的锁没有释放前无法操作其他表。
update tb_user set name = 'haha' where id = 1
当前客户端执行修改和添加当前表操作报错,另外开启客户端执行修改和添加则阻塞,直到锁释放。所以读锁是阻塞其他连接写不阻塞读。
3)获得tb_book 表的写锁
lock TABLE tb_book write;
4)执行修改添加操作
update tb_book set name = 'heihei' where id = 1;
insert into tb_book (id,name) VALUES (null,'lala')
在当前客户端可以正常的读取数据,而在其他客户端执行查询会一直阻塞,直到释放锁,因为写锁是排他锁,在当前任务执行完之前,无法获取表自带的读锁,所以无法读取数据。在其他客户端也无法进行修改和添加操作。所以写锁阻塞的是其他连接的读和写。
总结
myisam行锁阻塞其他线程写,读锁阻塞其他线程读和写,MyISAM 的读写锁调度是写优先,这也是MyISAM不适合做写为主的表的存储引擎的原因。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。
查看锁的争用情况
show open tables;--
In_user : 表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。
Name_locked:表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作。
show status like 'Table_locks%';
Table_locks_immediate : 指的是能够立即获得表级锁的次数,每立即获取锁,值加1。
Table_locks_waited : 指的是不能立即获取表级锁而需要等待的次数,每等待一次,该值加1,此值高说明存在着较为严重的表级锁争用情况。