【MySQL数据库】八、锁机制

一、锁机制基本概念

数据库是一个多用户使用的共享资源,当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况,若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。

加锁是实现数据库并发控制的一个非常重要的技术,当事务在某个数据对象操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他事务不能对此数据进行操作。

二、锁的分类

(一) 读锁(S共享锁)

读锁又称为共享锁当事务对数据对象加上读锁后,允许其他事务添加读锁,但是不能添加写锁,添加写锁会阻塞。

给表test添加读锁:

lock table test read;

释放锁:

unlock tables;

(二) 写锁(X排他锁)

写锁又称为排他锁若事务对数据对象加上写锁,不允许其他事务添加任何所,操作会阻塞。

给表test添加写锁:

lock table test write;

(三) 表锁

表锁会给一张表加锁,锁住整张表,粒度大,可以选择给表加读锁或写锁。加锁时,会给整张表加锁,其他行也不能被操作。MyISAM和InnoDB存储引擎都支持表锁,MyISAM默认使用表锁。

如给test表加读锁:

lock table test read;//读锁,写锁就是write

其他用户可以查询表,但是如果有其他用户进行修改,那么就会一直阻塞,因为此时test表被加锁,只有解锁后,其他用户的执行才可以正常执行。

(四) 行锁

行锁给一行数据加锁,粒度小,包含读锁,写锁。

给一行加读锁:

SQL语句选择一行 lock in share mode;
select * from test where id=1 in share mode;

给一行数据加写锁:

SQL语句选择一行 for update;
如:
select * from test where id=1 for update;//给此行加写锁

加锁只给一行数据加锁,其他行可以被操作,InnoDB默认支持行锁。

(五) 乐观锁

每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过,可以通过版本标识实现,自身数据有一个版本标识,数据更新一次,版本标识自动+1,标识不一样代表匹配失败,数据更新了

  • 如果标识匹配失败,表示被修改过,则不进行数据更新。
  • 如果标识匹配,表示数据没有被其他事务修改,则进行数据更新,

特点:

  1. 因为数据没有进行加锁,期间该数据可以被其他事务进行读写操作。
  2. 适合读取操作比较频繁的场景。
  3. 不适合大量的写入操作,因为数据发生冲突的可能性增大。
  4. 为了保证数据的一致性,应用层需要不断重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

(六) 悲观锁

认为每个操作都会出现错误,所以每个操作之前都提前加锁,确保在自己使用的过程中,数据不会被别人修改,使用完成后进行数据解锁,其他事务阻塞的等待, 如:

  • MyISAM:表锁(包含读锁,写锁)
  • InnoDB:行锁(包含读锁,写锁)

特点:

  • 适合写入操作比较频繁的场景。
  • 如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

(七) 意向锁

意向锁的目的是防止死锁出现,MyISAM表锁粒度大,不容易发生死锁。InnoDB行锁,粒度小,容易发生死锁。

意向锁就是,在加读锁,写锁之前,先加意向锁,意向锁可以共存。判断是否满足一个兼容规则:

  • 加上意向锁后满足兼容规则,表示继续加锁不会出现死锁。
  • 如果不满足兼容规则,表示继续加锁会出现死锁的情况,需要进行等待,等待兼容后,可以进行加锁。

(八) 间隙锁

间隙锁:给每次操作的间隙加上锁,将数据固定住,常用来解决幻读问题。如:

在这里插入图片描述
给第一次操作结果:4,5加上间隙锁后。再进行插入后,查询结果应该是4,5,6,但是因为4,5之间有间隙锁,所以不能将6插入结果集中,最后的结果只能从间隙锁中读,故还是4,5。

解决了幻读问题,幻读就是因为insert,delete操作导致一个连接,在一次事务中读到两次不一样的数据,将一次的数据锁住,那么第二次,第三次后面的操作不能影响最后的结果。

对于MySQL,隔离级别预告,加锁越严格,产生锁冲突可能性就越高,并发性事务处理性能降低。所以,在应用中,应该尽量使用中等的隔离级别,减少锁征用的几率。

三、MyISAM 表锁详解

MyISAM存储引擎支持表锁,加锁会给一张表加锁,所有行。有读锁,写锁两种锁:

  • 读锁称为共享读锁。读-读。
  • 写锁独占一张表,称为独占写锁。

底层系统自动SQL语句加锁,即在执行语句时,系统会自动先加一个锁,再开始运行,执行完毕解锁,属于悲观锁:

  • select 加读锁;
  • insert,delete,update加写锁。

【1. 给表加读锁:】

保证将当前表的存储引擎设置为MyISAM。

  1. 给A事务给test表手动加读锁:
    在这里插入图片描述

  2. B事务,可以进行select查询,但是不能进行修改操作,会阻塞,因为update修改操作,系统自动加写锁,此时表被A事务加了读锁,B事务加写锁,会阻塞,直到A事务释放读锁,才可以操作成功:

在这里插入图片描述

  1. A事务释放读锁,B事务立马加写锁成功,正常运行:
    在这里插入图片描述

  • 读锁可以在同一张表上共存,即读锁共享。
  • 不能加写锁,所以读锁和写锁不能共存。

将读锁称为共享读锁。

【2. 给表加写锁】

  1. A事务给test表加写锁,此时B事务的查询,修改都不能执行,都会阻塞:

在这里插入图片描述

  1. 只有A释放锁才可以正常执行:
    在这里插入图片描述

故:

  • 写-读互斥
  • 写-写互斥

所以写锁独占一张表,所以写锁又称为独占写锁。

四、InnoDB 锁详解

InnoDBz支持行锁和表锁,默认行锁:

  • 行锁:InnoDB必须有一个主键索引,叶子节点存放一行数据,给一行数据加行锁,数据在索引上,所以行锁加在了索引结构上。使用主键进行查询,就是索引查询,用到了索引结构,所以使用索引查询时可以加行锁。给一行数据加锁,不影响其他行,粒度小。
  • 表锁使用非索引查询,用不到索引结构,只能加表锁。

包含读锁,写锁:

  • 读锁称为共享锁。
  • 写锁称为排他锁。

底层系统给SQL语句自动加锁,存在超时机制,如果阻塞操作超时,就自动取消此次操作:

  • select不加锁,因为SQL语句是一个事务,可以通过事务的隔离性和一致性保证事务并发执行。
  • insert,delete,update加写锁

注意: InnoDB存储引擎支持事务,一条SQL语句就是一个事务,碰到分号;语句执行完成,事务结束,所有资源释放,所以当A事务执行加锁SQL语句后,分号结束事务结束,锁被释放。 为了看到行锁的效果,我们取消事务的自动提交功能:

set autocommit=0;

那么每次写完SQL语句后,事务不会提交到数据库,此时锁还存在,最后需要手动commit提交,事务才完成,资源才被释放。

(一)加行锁

只有主键索引可以加行锁,即查询中使用到了主键,才能加行锁,如果查询不是用的主键,加的锁称为表锁,A加锁后,B不能对整张表操作,如test表结构:

在这里插入图片描述
只有使用name进行查询时加的锁称为行锁,因为用到了索引结构,可以加行锁;但是如果查询使用salary字段,加的锁就是表锁。

取消自动提交事务,否则分号结束,锁被释放:

在这里插入图片描述

【1. 加读锁】

  1. A给通过主键name进行索引查询,可以加行锁,给第一行加读锁,share mode:

在这里插入图片描述

  1. B可以给第一行加读锁,可以成功,但是B事务修改第一行数据,底层加写锁,阻塞,如果超过一定时间,系统会取消此次操作,不会出现一直阻塞的情况。
    在这里插入图片描述
    超过一定时间,A事务没有释放释,那么系统取消B事务此次操作:

在这里插入图片描述

  1. 但是B事务可以修改其他行数据,如第二行等:

在这里插入图片描述

表示读锁-写锁不允许共存。

【2. 加写锁】

  1. A事务,利用主键索引查询,加的是行锁,给lisi行加锁,for update表示加写锁:

在这里插入图片描述

  1. B查询第一行,查询成功,因为select不加锁,所以可以读取数据。 B事务手动给同一行加读锁,阻塞,超过时间,系统取消此次操作:

在这里插入图片描述

  1. B修改其他行,可以执行,表示加的行锁:

在这里插入图片描述

故行级别加锁:

  • 读锁-读锁共存,共享读锁
  • 读锁-写锁不允许共存。
  • 写锁-写锁不允许共存
  • 写锁称为排他锁。(行级别)

(二)加表锁

根据非主键进行查询时,不会用到索引结构,无法给一行加锁,建立的锁只能是表锁。

  1. 如根据薪水进行查询时,不会用到索引,建立表锁,A建立了写锁,锁定了整张表:

在这里插入图片描述

  1. 此时B事务只能查询数据,不能对整张表中的任意行进行修改或加读锁、写锁,所以B不能对第二行操作,因为整张表都被锁了:

在这里插入图片描述

故表级加锁(和MyISAM一样)

  • 读锁共享
  • 写锁互斥,整张表被独占。

五、锁的粒度

锁具有粒度,可以对不同的资源进行加锁,资源大锁粒度大;资源小锁粒度小,粒度包含:行,页,表,库等资源。

  • 锁定在较小的粒度的资源(如行锁)上可以增加系统的并发量,但需要较大的系统开销,因为锁的粒度较小,导致锁数量增加,容易发生死锁。
  • 锁定在较大的粒度(例如表锁)并发难实现,如表锁,锁定了整张表,限制了其他事务对表中任意部分进行访问,但要求的开销较低,因为维护的锁少。

加油哦!🥓。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值