MySQL之锁

发布时间:2023年12月17日

MySQL锁

MySQL中有几种锁?

常见的是7种锁,还有一种不常见的预测锁

  • 行锁(Record Locks)属于行级锁,悲观锁
  • 间隙锁(Gap Locks)属于行级锁,悲观锁
  • 临键锁(Next-key Locks)属于行级锁,悲观锁
  • (读)共享锁/(写)排他锁(Shared Locks/Exclusive Locks)属于行级锁,悲观锁
  • 意向共享锁/意向排他锁(Intention Shared Locks/Intention Exclusive Locks)属于表级锁,悲观锁
  • 插入意向锁(Insert Intention Locks)属于特殊的间隙锁,悲观锁
  • 自增锁(Auto-inc Locks)属于表级锁

MySQL中如何划分锁?

  • 按照对数据操作的锁粒度来分:(锁定粒度依次递增)
    • 1.行级锁
    • 2.间隙锁
    • 3.页级锁
    • 4.表级锁
  • 按照锁的共享策略来分:
    • 1.共享锁
    • 2.排他锁
    • 3.意向共享锁
    • 4.意向排他锁
  • 从加锁策略上来分:
    • 乐观锁
    • 悲观锁
  • 其他:
    • 自增锁

死锁

create table test(
    id int not null,
    name varchar(20) default null,
    primary key (id)
)engine = InnoDB default CHARSET = utf8mb4 collate = utf8mb4_bin;
insert into test values (1,'zhagnsan'),
                        (5,'lisi'),
                        (10,'wangwu'),
                        (15,'zhaoliu'),
                        (20,null);
时间窗口1窗口2
T1begin ;
T2begin;
T3select * from test where id = 11;
T4select * from test where id =12;
T5insert into test(id,name) values (11,‘test’);
T6insert into test(id,name) values (12,‘test’);
T7select * from test;(无12数据)
T8锁等待状态select * from test;(无11数据)
T9锁等待接触死锁,窗口2的事务被回滚了

解除死锁,查询数据

时间窗口1窗口2
T1begin ;
T2begin;
T3select * from test where id = 11;
T4select * from test where id =12;
T5insert into test(id,name) values (11,‘test’);
T6insert into test(id,name) values (12,‘test’);
T7select * from test;(无12数据)
T8select * from test;(无11数据)
T9commit;
T0commit;

并发状态下,两个事务都能提交,提交完成后不会产生死锁

行级锁(Record Lock)

行级锁(记录锁)是MySQL中锁定粒度最细的一种锁,表示单个数据上的锁,表示**单个行数据上的锁,行锁一定是作用在索引上的。**行级锁能够减少数据库操作的冲突,加锁粒度最小,但加锁的开销也是最大的

行级锁分为:

  • 共享锁
  • 排他锁

间隙锁(Gap lock)

间隙锁,锁定一个范围,但不包括数据本身(它的锁粒度要比行级锁粒度更大一些,)它是锁定了某个范围内的多行数据,也可以加一行数据,

Gap锁的目的,为了防止同一事务的两次或者多次当前读,出现幻读的情况。该锁只会在隔离级别是可重复读级别出现的。间隙锁的目的就是无法让其他事务在间隙中新增数据。

页级锁

是mysql中锁定粒度介于行级锁和表锁中间的一种锁,

页级锁特点:

  • 会出现死锁,并发度一般,用的少

临键锁(Next-key Lock)

是记录锁和间隙锁的结合,锁定一个范围,并且数据本身也在范围内的,对行的查询,采用临键锁,最主要解决了幻读的问题,

表级锁(偏读)

表级锁是mysql粒度最大的一种锁,表示当前操作的整张表的数据经进行加锁,资源消耗最小,但不灵活,常见的MYISAM和InnoDB都支持表级锁

表级锁的特点

开销小,加锁快,不会出现死锁,发生锁冲突的概率比较大,并发度最低;

读锁锁表,会阻塞其他事务修改表数据:lock table (表名) read;

写锁锁表,会阻塞其他事务读和写:lock table (表名) write;

不同存储引擎中的表级锁:

  • InnoDB存储引擎不会为了:select ,insert,delete,update语句添加表级锁,如果想加表级锁需要手动显式的声明;
  • MYISAM在执行select语句前,会自动给涉及到的所有表加读锁,在执行update,delete,insert语句前会给涉及到的表加写锁,自动添加读锁,和写锁,

手动加锁

lock tables test read  ;//读锁
lock tables test write ;//写锁

查看锁

show open tables;

删除锁

unlock tables;

共享锁/排他锁

共享锁/排他锁都是输入行锁,

  • 共享锁式一个事务并发读取某一行数据所需要持有的锁,针对同一份数据,多个读操作可以同时进行不会互相影响到,
  • 排他锁式一个事务并发条件下,更新,删除某一行数据所需要持有的锁,当前写操作没有完成之前,他会阻塞掉其他写锁和多锁。

读锁会阻塞写锁,但不会阻塞读,而写锁会把其他的线程的读和写都会阻塞掉

意向共享锁/意向排他锁

意向共享锁/意向排他锁属于表锁,且取得意向共享锁/意向排他锁取得共享锁的前置条件

  • (IS)意向共享锁Intention Shared Locks:当事务准备在某条数据上加锁时,需要现在表级上加IS锁
  • (IX)意向排他锁Intention Exclusive Locks:当事务准备在某条数据上加锁时,需要现在表级上加IX锁

他们的提出只是为了后期表级锁可以快速的判断表中的数据是否被上锁,以避免用遍历的凡是来查看锁中有没有上锁的记录,

插入意向锁(Insert Intention Locks)

插入意向锁是一种特殊的间隙锁,跟普通间隙锁不同是,该锁用于并发插入操作,如果间隙锁锁住的是一个区间,那么插入一项锁锁住的则是一个点

与间隙锁的差别:

  • 插入意向锁是输入间隙锁,
  • 两个事务不能在同一时间内拥有间隙锁,如果不在间隙锁区间内则可以操作,

悲观锁

悲观锁 认为对于同一个数据的并发操作,一定是会发生修改的(增删改多,查少),哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。

悲观锁用的就是数据库的行锁,认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至提交了当前事务。

乐观锁

乐观锁则认为对同一个数据的并发操作,是不会发生修改的(增删改少,查多),在更新数据的时候,会采用不断尝试更新的方式来修改数据,也就是先不管资源有没有被别的线程占用,直接取申请操作,如果没有产生冲突,那就操作成功,如果产生冲突了,有其他线程已经在使用了,就会轮流使用,乐观的认为不加锁的并发操作是没有任何问题的,就是通过记录一个数据历史记录的多个版本,如果修改完之后发现有冲突再回到没有修改的样子,好处就是减少上下文切换,坏处就是浪费cpu时间。

乐观锁数据表中的实现

利用数据版本号(version)机制是乐观锁最常见的实现方式,一般通过为数据库表增加一个数据类型的“version”字段,当读取数据时,将version字段的值一同读取,数据每更新一次,对此version值+1.

当提交更新的时候,判断数据库对应数据的当前版本信息,与第一次取出的version值进行对比,如果数据库表当前本版与第一次取出的version值相等,则更新,否则认为时过期数据,返回更新失败

乐观锁和悲观锁区别

乐观锁实现方式:

  • CAS机制
    • 需要读写的内从位置
    • 进行比较的预期值
    • 写入的新值
  • 版本机制

CAS机制会导致ABA问题

线程1修改数据值等于A,线程2修改数据后变成B,线程2再次修改数据后变成A,这样CAS检测时候,发现修改前后的值时一样的,就会认为没有修改,恢复原来值,

悲观锁的实现方式:

  • 通过加锁,行锁
  • 可以对代码快加锁,也可以对数据加锁
  • Java可以使用synchronized同步代码块一样
  • 数据库中可以使用排他锁
文章来源:https://blog.csdn.net/qq_44715376/article/details/134977449
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。