mysql5.7 锁简介(全局锁,表锁,行锁,间隙锁)

全局锁

        对整个数据库实例加锁,一般用于全库逻辑备份。myisam不支持事务做备份时会用到,innodb做备份可以用mysqldump --single-transcation进行备份保持数据一致性。

        开启全局锁:Flush tables with read/write lock;

        解锁:unlock tables;

表级锁

        表级锁分为表锁和元数据锁(meta data lock)简称MDL。表锁一般是在数据库引擎不支持行锁的时候才会被用到的,MDL锁用于表结构变更。

        开启表锁:lock table xx表 read/write; 对表加读锁后,自己和其他线程只能读取该表,不能对其修改。 对表执加写锁后,该线程可以对这个表进行读写,其他线程对该表的读和写都受到阻塞。

        解锁:unlock tables;

        MDL锁不需要显示调用,访问表的时候会自动加上,使用MDL锁可以保证读写正确性,当对一个表做增删改查操作的时候,加 MDL 读锁,读锁之间不互斥,可以多个线程对一个表进行增删改查。当要对表做结构变更操作的时候,加 MDL 写锁,读写锁,写锁之间互斥,保证变更表结构安全性。

行锁

       行锁由引擎层实现,innodb支持行锁,myisam不支持。不支持行锁只能使用表锁,在同一个表中只能有一个写线程在执行,并发粒度不如行锁,行锁分为共享锁,排他锁。

       共享锁:又称读锁(S锁),共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

       排他锁:又称写锁(X锁),排他锁不能与其他锁共存,如一个事务获取行记录的排他锁,其他事务就不能再获取该行的其他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

        创建student表

CREATE TABLE `student` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(16) DEFAULT NULL,
  `number` bigint(20) DEFAULT NULL COMMENT '普通索引编号',
  `unique_number` bigint(20) DEFAULT NULL COMMENT '唯一索引编号',
  `no_index_number` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index2` (`unique_number`),
  KEY `index1` (`number`)
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('2', '张三', '2', '2', '2');
INSERT INTO `student` VALUES ('5', '李四', '5', '5', '5');
INSERT INTO `student` VALUES ('8', '王五', '8', '8', '8');
INSERT INTO `student` VALUES ('11', '怀少飞', '11', '11', '11');

共享锁验证

共享锁与共享锁   

线程1线程2

BEGIN;

SELECT  * from student LOCK IN SHARE MODE;

SELECT  * from student LOCK IN SHARE MODE; -- 共享锁查询成功

共享锁与无锁

线程1线程2

BEGIN;

SELECT  * from student LOCK IN SHARE MODE;

SELECT  * from student;     -- 无锁查询成功

共享锁与排他锁

线程1线程2

BEGIN;

SELECT  * from student LOCK IN SHARE MODE;

SELECT  * from student FOR UPDATE; -- 排他锁查询阻塞
commit;
查询成功

排他锁验证

排他锁与无锁   

线程1线程2

BEGIN;

SELECT  * from student FOR UPDATE;

SELECT  * from student;  -- 无锁查询成功

排他锁与共享锁

线程1线程2

BEGIN;

SELECT  * from student FOR UPDATE;

SELECT  * from student LOCK IN SHARE MODE; -- 共享锁查询阻塞
commit;
查询成功

排他锁与排他锁

线程1线程2

BEGIN;

SELECT  * from student FOR UPDATE;

SELECT  * from student FOR UPDATE; -- 排他锁查询阻塞
commit;
查询成功

        行锁特性:在事务中行锁在需要时mysql自动添加,事务结束时释放,所以一个事务中多个sql应该把锁冲突的记录往后放。

间隙锁(Gap Lock)

        查看是否开启间隙锁:show variables like 'innodb_locks_unsafe_for_binlog'; 

        开启间隙锁:编辑my.cnf文件,[mysqld]中将innodb_locks_unsafe_for_binlog 修改为0,1表示关闭间隙锁。

        可重复读隔离级别通过间隙锁解决幻读,间隙锁指锁两个行之间的间隙,比如student表number字段会产生(-∞,2),(2,5),(5,8),(8,11),(11,+∞)这5个间隙锁。间隙锁与行锁组成next-key lock 为(]前开后闭区间。

        间隙锁在不同索引情况与查询条件不同时有以下特性:

        1.等值查询时

        主键索引与唯一索引锁存在记录使用行锁,锁不存在记录时构成[不存在记录,下一条存在记录)前闭后开区间。

        普通索引锁存在记录或不存在记录是时构成[上一条存在记录,下一条存在记录)前闭后开区间

        无索引字段锁全部区间。

        2.范围查询时

         情况比较复杂,写着有点麻烦,感兴趣可以试下,比如唯一索引:x>=存在记录and x<=不存在记录,x>存在记录and x<不存在记录,x>=不存在记录and x<不存在记录。x>存在记录 and x <不存在记录

        总体来说间隙锁这块也看了网上的规律,总感觉网上总结的不太全面,有些偏差。目前我还没总结出通用的规律

等值查询

主键索引或唯一索引,锁存在记录

线程1线程2
BEGIN;
SELECT id from student WHERE id = 5 for update

INSERT INTO  student  (id) VALUES (6); -- 插入成功

INSERT INTO  student  (id) VALUES (4); -- 插入成功

INSERT INTO  student  (id) VALUES (5); -- 阻塞

主键索引或唯一索引,锁不存在记录

线程1线程2
BEGIN;
SELECT id from student WHERE id = 6 for update

INSERT INTO  student  (id) VALUES (8); -- 插入成功

INSERT INTO  student  (id) VALUES (4); -- 插入成功

INSERT INTO  student  (id) VALUES (6); -- 阻塞

INSERT INTO  student  (id) VALUES (7); -- 阻塞

普通索引,锁存在记录

线程1线程2
BEGIN;
SELECT id from student WHERE number = 5 for update

INSERT INTO  student  (number) VALUES (XX); 

2<=x<8之间失败,其余成功

普通索引,锁不存在记录

线程1线程2
BEGIN;
SELECT id from student WHERE number= 6 for update

INSERT INTO  student  (number) VALUES (XX); 

5<=x<8之间失败,其余成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值