MyISAM和MEMORY存储引擎采取的是表级锁
InnoDB既支持表级锁,又支持行级锁,但默认是行级锁。
表级锁适合以查询为主,只有少量按索引条件更新数据的应用。
行级锁适合有大量按索引条件并发,更新少量数据,同时又有并发查询的应用。
由MySQL SQL layer层实现。
MySQL的表级锁有两种:
一种是表锁。一种是元数据锁(meta data lock,MDL)。
show status like ‘table%;
-- able_locks_immediate:产生表级锁定的次数;
-- able_locks_waited:出现表级锁定争用而发生等待的次数
表共享读锁(Table Read Lock)
表独占写锁(Table Write Lock)
lock table 表名称 read(write),表名称2 read(write);
show open tables;
unlock table;
-- 新建表
CREATE TABLE mylock (
id int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(20) DEFAULT NULL,
PRIMARY KEY (id));
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‘);
session1(Navicat)、session2(mysql)
1、session1: lock table mylock read; -- 给mylock表加读锁
2、session1: select * from mylock; -- 可以查询
3、session1:select * from tdep; --不能访问非锁定表
4、session2:select * from mylock; -- 可以查询 没有锁
5、session2:update mylock set name=‘x‘ where id=2; -- 修改阻塞,自动加行写锁
6、session1:unlock tables; -- 释放表锁
7、session2:update mylock set name=‘x‘ where id=2; -- 这次就修改执行完成
8、session1:select * from tdep; --也可以访问
1、session1: lock table mylock write; -- 给mylock表加写锁
2、session1: select * from mylock; -- 实测不可以查询
3、session1:select * from tdep; --不能访问非锁定表
4、session1:update mylock set name=‘y‘ where id=2; --可以执行
5、session2:select * from mylock; --别进程写锁, 查询阻塞
6、session1:unlock tables; -- 释放表锁
7、session2:select * from mylock; -- 别进程解锁后,才能查询
8、session1:select * from tdep; --可以访问
作用是保护表的元数据信息一致性,解决或者保证DDL操作与DML操作之间的一致性。
当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。
1、session1: begin; --开启事务
select * from mylock;--加MDL读锁
2、session2: alter table mylock add f int; -- 修改表结构阻塞
3、session1:commit; --提交事务 或者 rollback 释放读锁
4、session2:alter table mylock add f int; --修改完成
InnoDB存储引擎实现,行锁必须有索引才能实现,不然会自动锁全表,那么就不是行锁了。
共享读锁(S):当一个事务对某几行上读锁时,允许其他事务对这几行进行读操作。
但不允许进行写操作,也不允许其他事务给这几行上排它锁,但允许上读锁。
SELECT * FROM 表 WHERE 条件 LOCK IN SHARE MODE ;-- 共享读锁 手动添加
排他写锁(X):一个事务对某几行上写锁时,其他事务允许读,但不能写,更不允许上锁。
对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁
SELECT * FROM 表名 WHERE 条件 FOR UPDATE;
示例:
1.
BEGIN
select * from testdemo where id =1 for update
在另外一个session中
update testdemo set c1 = ‘1‘ where id = 2 成功
update testdemo set c1 = ‘1‘ where id = 1 等待
2.BEGIN
update testdemo set c1 = ‘1‘ where id = 1
在另外一个session中
update testdemo set c1 = ‘1‘ where id = 1 等待
3.
BEGIN
update testdemo set c1 = ‘1‘ where c1 = ‘1‘ 在另外一个session中
update testdemo set c1 = ‘2‘ where c1 = ‘2‘ 等待
InnoDB行锁是通过给索引上的索引项加锁来实现的
只有通过索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁
-- 查看行锁状态
show STATUS like ‘innodb_row_lock%‘;
注:使用索引加行锁 ,未锁定的行可以访问
总结:索引的行读锁,所有session的这个索引条件都锁死,修改不了。
1、session1: begin;-- 开启事务未提交
select * from mylock where id=1 lock in share mode; -- 手动加id=1的行读锁,使用索引
2、session2:update mylock set name=‘y‘ where id=2; -- 未锁定该行可以修改
3、session2:update mylock set name=‘y‘ where id=1; -- 锁定该行修改阻塞 锁定超时
4、session1:update mylock set name=‘y‘ where id=1; -- 锁定该行修改阻塞 锁定超时
5、session1: commit; --提交事务 或者 rollback 释放读锁
6、session2:update mylock set name=‘y‘ where id=1; --修改成功
注:未使用索引行锁升级为表锁
总结:非索引的行读锁,行锁失效,锁表,自己session所有条件啥影响都没,别session所有条件修改锁的死死。
1、session1: begin;-- 开启事务未提交
select * from mylock where name=‘c‘ lock in share mode;-- 手动加name=‘c‘的行读锁,未使用索引
2、session2:update mylock set name=‘y‘ where id=2; -- 修改阻塞 未用索引行锁升级为表锁
3、session1: commit; --提交事务 或者 rollback 释放读锁
4、session2:update mylock set name=‘y‘ where id=2; --修改成功
主键索引产生记录锁
总结:索引键的行写锁,条件不失效,所有session查询都没影响,但别session加锁影响,当前session没
1、session1: begin;--开启事务未提交
select * from mylock where id=1 for update;--手动加id=1的行写锁
2、session2:select * from mylock where id=2 ; -- 可以读 不加锁
3、session2: select * from mylock where id=1 ; -- 可以读 不加锁
4、session2: select * from mylock where id=1 lock in share mode; --加读锁被阻塞
5、session1: select * from mylock where id=1 lock in share mode; --不阻塞
6、session1:commit; -- 提交事务 或者 rollback 释放写锁
7、session2:执行成功
间隙锁是一个在索引记录之间的间隙上的锁。当我们采用范围查询时,InnoDB会对这个范围内的数据进行加锁。
示例1:
比如有id为:1、3、5、7 的4条数据,我们查找1-7范围的数据。
那么1-7都会被加上锁。2、4、6也在 1-7的范围中,但是不存在这些数据记录,这些 2、4、6就被称为间隙。
示例2:
我们向表中新增一条数据age=20,这条数据在本来是没有的,在insert还没有提交的时候查询age>15 and a.age<25,这个时候就会触发间隙锁,我们必须等待insert提交后才能执行select语句。
在MySQL的innoDB引擎中,如果操作的是一个区间的数据,会锁住这个区间所有的记录。
即使这个记录不存在,这个时候另一个会话去插入这个区间的数据,就必须等待上一个结束。
注:对于主键索引或唯一索引,如果where条件不能全命中,也会加间隙锁,全命中则加记录锁。
间隙锁的作用:
保证某个间隙内的数据在锁定情况下不会发生任何变化。
当使用唯一索引来搜索唯一行的语句时,不需要间隙锁定。
两个 session 互相等等待对方的资源释放之后,才能释放自己的资源,造成了死锁.
1、session1: begin;-- 开启事务未提交
update mylock set name=‘m‘ where id=1;-- 手动加行写锁 id=1 ,使用索引
2、session2:begin;-- 开启事务未提交
update mylock set name=‘m‘ where id=2;-- 手动加行写锁 id=2 ,使用索引
3、session1: update mylock set name=‘nn‘ where id=2; -- 加写锁被阻塞
4、session2:update mylock set name=‘nn‘ where id=1; -- 加写锁会死锁,不允许操作
系统运行一段时间后,数据量已经很大,这时A表需要增加个字段,并发量白天晚上都很大,请问怎么修改表结构?
考点:修改表结构会导致表锁,数据量大,修改数据时间久,可能会导致大量用户阻塞,无法访问。
原文:https://www.cnblogs.com/fuyublog/p/14024744.html