数据库
首页 > 数据库> > Mysql——数据库表锁、行锁

Mysql——数据库表锁、行锁

作者:互联网

表锁

偏向MyISAM,开销小,加锁快,无死锁,锁粒度大,发生锁冲突的概率较高,并发度较低。

表锁分析

测试表,用于表加锁后的读写可能性验证

CREATE TABLE IF NOT EXISTS table_lock (
	id INT(10) PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '自增主键',
	`name` VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名'
) ENGINE=MyISAM;

INSERT INTO table_lock (`name`) 
VALUES
('Leo'),
('James'),
('Irving');

CREATE TABLE `book` (
  `book_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `card` int(10) unsigned NOT NULL,
  PRIMARY KEY (`book_id`),
  KEY `idx_book_idCard` (`card`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4;

INSERT INTO `test`.`book` (`book_id`, `card`) VALUES ('5', '2');
INSERT INTO `test`.`book` (`book_id`, `card`) VALUES ('12', '3');
INSERT INTO `test`.`book` (`book_id`, `card`) VALUES ('20', '3');
INSERT INTO `test`.`book` (`book_id`, `card`) VALUES ('4', '4');

表占用的状态

SHOW OPEN TABLES;

手动给表加锁

LOCK TABLE table_lock READ, book WRITE;

可以看到表已经被占用。

手动解锁

UNLOCK TABLES;

测试过程是省略了,就是分别加表的读写锁,在不同Mysql连接session中观察可读写性。

表读锁特点

表写锁特点

表锁定的分析

SHOW STATUS LIKE 'table_locks%';

MyISAM引擎的读写调度是写优先,所以MyISAM不适合作为写数据场景较多的引擎。当出现较多写操作的时候,会造成查询阻塞严重

行锁

偏向InnoDB,开销大,加锁慢,会出现死锁,锁的粒度小,发生锁冲突的概率低,并发度较高。

InnoDB与MyISAM最大的不同是,InnoDB支持事务,采用了粒度更小的行级锁。

InnoDB的默认隔离级别Repeatable Read(可重读)

行锁分析

测试表

CREATE TABLE IF NOT EXISTS table_lock2 (
	id INT(10) PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '自增主键',
	`name` VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名'
) ENGINE=innodb;

INSERT INTO table_lock2 (`name`) 
VALUES
('Leo'),
('James'),
('Irving');

CREATE INDEX table_lock_id_idx ON table_lock2(id);
CREATE INDEX table_lock_name_idx ON table_lock2(name);

Mysql默认是自动提交事务的

SHOW VARIABLES LIKE '%autocommit%';

关闭自动提交方便测试

SET autocommit = 0;

Mysql InnoDB写数据默认加锁

行锁测试

在连接A中修改某条记录:

UPDATE table_lock2 SET `name` = 'Iversen' WHERE id = 3;

此时并没有提交事务,在连接A中修改生效

在连接B中查询,并没有发生数据变化:

需要连接A执行 COMMIT; 之后,连接B中才能看到数据更新变化

首先在连接A中修改数据:

UPDATE table_lock2 SET `name` = 'AAA' WHERE id = 1;

之后在连接B中修改数据:

UPDATE table_lock2 SET `name` = 'BBB' WHERE id = 1;

此时连接A中没有 COMMIT;,连接B的修改操作将会阻塞,连接A长时间不提交,连接B中操作将会超时。

[SQL]UPDATE table_lock2 SET `name` = 'BBB' WHERE id = 1;
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction

在连接A COMMIT; 之后,连接B的写操作将会执行,连接B的操作也需要 COMMIT; 才会生效。

在连接A中修改数据2

UPDATE table_lock2 SET `name` = 'ABC' WHERE id = 2;

在连接B中修改数据1

UPDATE table_lock2 SET `name` = 'DEF' WHERE id = 1;

InnoDB是行锁,所以写操作不同行数据,互不影响

行锁升级为表锁问题

在测试表table_lock2创建的时候,分别创建了id和name字段的索引,此时如果连接A修改某条数据且name字段varchar类型没有加上引号,发生隐式的类型转换,没有commit的时候,连接B修改另一条数据将会阻塞,此时就是行锁升级为了表锁。

如原有数据为 id = 1,name = 2000

UPDATE table_lock2 SET id = 4 WHERE `name` = 2000;

原因是因为隐式的类型转换导致了索引失效。

间隙锁问题

当使用范围条件而不是相等条件检索数据的时候,InnoDB会给符合条件的已有数据记录的索引项进行加锁,对于键值在条件范围内但并不存在记录即为“间隙”

表table_lock2中没有id为2的数据时,执行下列SQL,InnoDB仍然会对id这列索引2的位置进行加锁

UPDATE table_lock2 SET `name` = 'ABC' WHERE id > 1 AND id < 6;

此时其他连接插入id = 2的数据时,将会阻塞。

InnoDB悲观锁——锁定指定数据

BEGIN;

SELECT * FROM table_lock2 WHERE id = 2 FOR UPDATE;

-- do something

COMMIT;

如上所示,在commit执行之前,id=2的数据将会被当前连接加锁,其他连接对该数据的操作将会进入阻塞。

 

 

 

 

 

 

 

标签:加锁,name,行锁,lock2,book,Mysql,table,表锁,id
来源: https://blog.csdn.net/qq_25805331/article/details/113523284