MySQL | MySQL中的锁机制(详细)

一、储存引擎

  1. InnoDB 支持事务,支持行锁,支持外键(通常不设置外键,在程序中保持数据的一致性),主流储存引擎,有更高的并发处理性能。
  2. MyIsam 不支持事务,支持全文索引,支持表锁,不支持外键,查改速度快,在最新版本的MYSQL中已经废弃。
  3. Memory 数据变化频繁,不需要入库,查改极快。

进入MySQL数据库通过 show engines; 查看存储引擎,我的版本是MySQL5.7
在这里插入图片描述

二、MySQL锁分类

  1. 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  2. 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  3. 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

三、事务处理

  1. A事务执行以下代码但不提交、
BEGIN;
UPDATE stu SET sname = '小明' WHERE id=1;
  1. B事务执行以下代码,可以正常执行
BEGIN;
update stu set sname = '小红' where id=3
COMMIT;
  1. 但B事务更新与A事务相同的记录则无法操作,执行过程发生阻塞
BEGIN;
UPDATE stu SET sname = '小刚' WHERE id=1;
...
  1. 当A执行执行COMMIT 提交后,解锁记录行这时B事务继续执行

1.非索引阻塞

使用非索引字段筛选时,将造成全表锁定即表级锁,应该避免这种情况发生,提升数据库的并发性能。

  1. 事务A执行以下代码,因为sname字段没有添加索引,造成锁定整个表
BEGIN;
UPDATE stu SET sname = '小红' WHERE sname ='小明';
  1. 现在事务B更新任何一条记录都会造成阻塞,因为现在是表锁状态
BEGIN;
update stu set sname = '小明' where id=1
-- 阻塞中...

2.范围锁

查询没有指定明确范围时也会造成大量记录的锁定

  1. 事务A筛选时使用了范围区间,将会造成表锁
BEGIN;
UPDATE goods SET num=100 WHERE id>1 AND id<3; 
  1. 事务B将不能修改表中的ID大于2的记录
BEGIN;
update goods set num =1 where id=2;
-- 阻塞中...

但可以更改ID为1的记录

update goods set num =1 where id=1;
  1. 执行添加时因为不在id为 1~3的范围内所以可以添加,但如果添加时指定ID为2将会阻塞。
insert into goods (name,num) values('冰箱',200);

四、悲观锁

非观锁指对数据被外界修改持保守态度,在整个数据处理过程中,将数据处于锁定状态,可以很好地解决并发事务的更新丢失问题。

  1. 事务A执行悲观锁操作后,其他事务执行同一代码时将阻塞
BEGIN;
SELECT * FROM goods WHERE id=1 FOR UPDATE;
UPDATE goods SET num=num-2 WHERE id=1; 
...
  1. 事务B执行以下代码将不能查询库存,必须等事务A提交或回滚事务
BEGIN;
SELECT * FROM goods WHERE id=1 FOR UPDATE;
-- 阻塞中...
  1. 事务A提交后,事务B会得到事务A操作后的结果

五、乐观锁

在每次去拿数据的时候认为别人不会修改,不对数据上锁,但是在提交更新的时候会判断在此期间数据是否被更改,如果被更改则提交失败。

  1. 事务A查询商品库存,获取了商品记录,记录中有VERSION字段用于记录版本号(目前为0)
BEGIN;
SELECT * FROM goods WHERE id = 1;
  1. 事务B同时查询,也获取了版本号为0的记录
BEGIN;
SELECT * FROM goods WHERE id = 1;
  1. 事务A更改库存,并增加版本号
UPDATE goods SET num=num-10,VERSION =VERSION+1 WHERE VERSION=0;
  1. 事务B更改数据,但使用的是事务B查询到的0号版本,因为事务A已经提交版本号为1,造成事务B修改失败,保证了数据的完整性。
UPDATE goods SET num=num-10,VERSION =VERSION+1 WHERE VERSION=0;

六、表锁机制

针对一些不支持事务的处理引擎可以使用锁表的方式控制业务。

#读锁

1.读锁

为表设置读锁后,当前会话和其他会话都不可以修改数据。

  1. 会话A对表goods设置了读锁,将不能修改该表,也不能操作其他表
LOCK TABLE goods READ;
UPDATE goods SET num=300 WHERE id=1;
SELECT * FROM stu;
  1. 因为会话A对表goods设置了读锁,所以会话B也不能修改
update goods set num=200 where id=1;
-- 阻塞
  1. 会话A解锁表后,其他会话又可以继续操作表了

2.写锁

为表设置了写锁后,当前会话可以修改,查询表,其他会话将无法操作。

  1. 会话A对表goods设置写锁,本会话可以正常操作表, 并不能操作其他表
LOCK TABLE goods WRITE;
INSERT INTO goods (name,num )VALUES('java',300);
  1. 会话B读取/写入/写入表数据都将阻塞
select * from goods
  1. 会话A解锁表数据后,其他会话都可以正常操作了
UNLOCK TABLES;

猜你喜欢

转载自blog.csdn.net/y1534414425/article/details/106167538
今日推荐