这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
1、行锁(偏写)
偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度高。
InnoDB引擎和MyISAM引擎不同的是:InniDB支持事务,且是行级表。
建表
create table test_innodb_lock(a int(11),b varchar(16))engine=innodb;
insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1');
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);
行锁基本演示
终端A
# 关闭自动提交
SET autocommit = 0;
# 操作一
UPDATE test_innodb_lock SET b='4001' WHERE a = 4;
# 操作二
COMMIT;
# 操作三
UPDATE test_innodb_lock SET b='4002' WHERE a = 4;
# 操作四
COMMIT;
# 操作五
UPDATE test_innodb_lock SET b='4004' WHERE a = 4;
# 操作六
COMMIT;
终端B
# 关闭自动提交
SET autocommit = 0;
# 操作一、操作二
COMMIT;
SELECT * FROM test_innodb_lock;
# 操作三
UPDATE test_innodb_lock SET b = '4003' WHERE a = 4;
# 操作四
COMMIT;
# 操作五
UPDATE test_innodb_lock SET b='4005' WHERE a = 5;
#操作六
COMMIT;
结果:
终端A修改数据但未提交,终端B取查询数据会被阻塞。
终端A提交 数据,终端A查询成功。
终端A修改数据但未提交,终端B也去修改数据会被阻塞。
终端A提交,终端B执行成功并提交。
终端A修改a=4的数据,不提交。终端B 修改a=5的数据,没有被阻塞。
无索引行锁升级为表锁
终端A
# 这里的 b 是 varchar 类型的,此处故意不写引号,这样会自动类型转换,使索引失效
UPDATE test_innodb_lock SET a = 4 WHERE b = 4001;
终端B
UPDATE test_innodb_lock SET a = 9 WHERE b = 9000;
结果:
两个终端没有对同一行的数据进行修改,但是终端B执行后会进入阻塞状态,因为索引失效行锁自动转换为表锁了。
2、间隙锁的危害
什么是间隙锁:
当我们使用的是范围条件而不是相等条件检索数据,并请求共享数据排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内,但不存在记录的叫做间隙。
危害:
通过 Query 执行过程中通过范围查找了话,他会锁定整个范围内所有的索引键值,即使这个键值不存在。
问题描述:
一个线程对数据进行范围修改,然后索引不是连续的,另一个线程进行插入操作,插入的数据不在之前线程操作的范围内,最后插入的操作会进入阻塞。
端口A范围修改
UPDATE test_innodb_lock SET b = "1997" WHERE a > 1 and a < 6;
端口B新增数据
INSERT INTO test_innodb_lock VALUES (2,"2000");
解决方式:只锁一行
SELECT * FROM test_innodb_lock WHERE a = 8 FOR UPDATE
在SQL后面加上 FOR UPDATE 对当前操作的行上锁。其他操作会被阻塞,直到 COMMIT