0
点赞
收藏
分享

微信扫一扫

MySql 锁机制

Aliven888 2022-09-06 阅读 259


文章目录

  • ​​1、前言​​
  • ​​2、MySql的锁机制​​
  • ​​2.1 MySql的锁分类​​
  • ​​2.2 行锁实现方式​​
  • ​​2.3 三级封锁协议​​
  • ​​2.4 两段锁协议​​
  • ​​3、基于InnoDB的锁​​
  • ​​3.1 InnoDB简介​​
  • ​​3.2 InnoDB锁的种类​​
  • ​​3.3 InnoDB引擎的四大特性​​
  • ​​3.4 InnoDB引擎的行锁如何实现​​
  • ​​4、基于MyISAM的锁​​
  • ​​4.1 MyISAM简介​​
  • ​​4.2 MyISAM锁的种类​​
  • ​​5、选择InnoDB还是MyISAM​​
  • ​​6、乐观锁和悲观锁​​
  • ​​6.1 乐观锁​​
  • ​​6.1.1 版本号机制​​
  • ​​6.1.2 CAS操作方式​​
  • ​​6.2 悲观锁​​
  • ​​6.3 总结​​
  • ​​7、锁机制总结​​

1、前言

相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAMMEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

2、MySql的锁机制

2.1 MySql的锁分类

mysql提供了三种锁机制:

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。适用于以查询为主,只有少量按索引条件更新数据的应用,如web应用。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。适用于按大量索引条件并发更新少量不同数据,同时又有并发查询的应用。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般,依据具体场景确定。

2.2 行锁实现方式

InnoDB的行锁是通过索引上的索引项来实现的,这一点MySQL与Oracle不同,Oracle是通过在数据中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁!在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。

2.3 三级封锁协议

MySql 锁机制_mysql


三级封锁协议:解决事务更新丢失的问题

  1. 一级封锁协议:事务T中如果对数据R有写操作,必须在这个事务中对R的第一次读操作前对它加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。
    一级封锁协议可以防止丢失修改(因为事务结束前都禁止其它事务进行数据修改),并保证事务T是可恢复的。使用一级封锁协议可以解决丢失修改问题。
    在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,它不能保证可重复读不读“脏”数据
  2. 二级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后方可释放S锁。
    二级封锁协议除防止了丢失修改,还可以进一步防止读“脏”数据(确保在数据读取期间没有其它事务修改)。但在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。
  3. 三级封锁协议 :一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。
    三级封锁协议除防止了丢失修改不读“脏”数据外,还进一步防止了不可重复读。(因为直到事务结束前,由于加了S锁,始终不会有其它事务对其进行修改)

可见,三级锁操作一个比一个严厉(满足高级锁则一定满足低级锁)。但是效率非常低的,建议一般少用的好。

2.4 两段锁协议

两段锁协议是指所有事务必须分两个阶段对数据项加锁和解锁:

  1. 在对任何数据进行读、写操作之前,要申请并获得对该数据的封锁。
  2. 每个事务中,所有的封锁请求先于所有的解锁请求。

两段锁协议是指每个事务的执行可以分为两个阶段:生长阶段(加锁阶段)和衰退阶段(解锁阶段)。

  1. 加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁,在进行写操作之前要申请并获得X锁。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
  2. 解锁阶段:当事务释放了一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。

两段封锁法可以这样来实现:事务开始后就处于加锁阶段,一直到执行ROLLBACK和COMMIT之前都是加锁阶段。ROLLBACK和COMMIT使事务进入解锁阶段,即在ROLLBACK和COMMIT模块中DBMS释放所有封锁。

3、基于InnoDB的锁

3.1 InnoDB简介

InnoDB是mysql中使用最多的引擎,支持事物,它也提供了行锁。
InnoDB,是MySQL的数据库引擎之一,现为MySQL的默认存储引擎,为MySQL AB发布binary的标准之一。InnoDB由Innobase Oy公司所开发,2006年五月时由甲骨文公司并购。与传统的ISAM与MyISAM相比,InnoDB的最大特色就是支持了ACID兼容的事务(Transaction)功能,类似于PostgreSQL。

3.2 InnoDB锁的种类

InnoDB实现标准行级锁定,其中有两种类型的锁,

  • 共享(S)锁:共享(S)锁允许持有锁的事务去读取一行数据,防止其它持有X锁的事务读取同一行。
  • 排它(X)锁:排它(X)锁允许持有锁的事务更新或删除一行数据,防止持有S和X锁的事务获取同一行数据。

如果事务T1在R行上持有共享(S)锁,则事务T2请求对行R上进行加锁 进行如下处理:

T2可以即时对R行加S锁成功。最终T1和T2都保持R上的S锁。排它锁反之,T1持有了R行上的X锁时候,T2便不能对R行加X锁,直到T1被释放R行的X锁。

3.3 InnoDB引擎的四大特性

  1. 插入缓存(insert buffer)
  2. 二次写(double write)
  3. 自适应哈希索引
  4. 预读

3.4 InnoDB引擎的行锁如何实现

基于索引来完成行级锁。
只有通过索引条件检索数据,InnoDB才会使用行级锁,否则会使用表级锁。

4、基于MyISAM的锁

4.1 MyISAM简介

MyISAM是默认存储引擎(Mysql5.1前)。它基于更老的ISAM代码,但有很多有用的扩展。(注意MySQL 5.1不支持ISAM)。 每个MyISAM在磁盘上存储成三个文件,每一个文件的名字均以表的名字开始,扩展名指出文件类型。

4.2 MyISAM锁的种类

基于MyISAM表级锁有两种模式:表共享锁(Table Read Lock)和表独占写锁(Table Write Lock)

  • 对MyISAM的读操作,不会阻塞其他用户对同一表请求,但会阻塞对同一表的写请求;
  • 对MyISAM的写操作,则会阻塞其他用户对同一表的读和写操作;
  • MyISAM表的读操作和写操作之间,以及写操作之间是串行的。

当一个线程获得对一个表的写锁后,只有持有锁线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。

5、选择InnoDB还是MyISAM

  1. innodb支持事务,myisam不支持
  2. innodb支持行级锁,myisam支持表级锁
  3. innodb支持外键,myisam不支持
  4. innodb支持MVCC,myisam不支持
  5. 应用场景:
    MyISAM:做很多count计算、插入不频繁、查询非常频繁、没有事务、查询快
    InnoDB:对事务要求比较高、可靠性要求高、表更新相当频繁、并发写入高
  6. DELETE操作:
    MyISAM:先drop表,然后重建表
    InnoDB:一行一行删除
  7. 查询表的行数不同:
    MyISAM:只是简单的读出保存好的行数
    InnoDB:不保存具体行数,执行count(*)时要扫描一整个表来计算有多少行

两者比较重要的区别是:InnoDB支持事务,而MyISAM不支持事务
MyISAM相对简单,所以在效率上要优于InnoDB。
如果系统读多,写少,对原子性要求低。那么MyISAM最好的选择。且MyISAM恢复速度快。可直接用备份覆盖恢复。
如果系统读少,写多的时候,尤其是并发写入高的时候。InnoDB就是首选了。
两种类型都有自己优缺点,选择哪个完全要看自己的实际需求。

6、乐观锁和悲观锁

6.1 乐观锁

乐观锁(Optimistic Lock),:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号、CAS操作等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

6.1.1 版本号机制

一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加1。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

6.1.2 CAS操作方式

即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

6.2 悲观锁

悲观锁(Pessimistic Lock),:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

6.3 总结

  • 两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
  • 乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能
  • 乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方

7、锁机制总结

  • 表级锁:每次操作都锁定在整张表,开销少,加锁块,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低
  • 行级锁:每次操作锁定一行数据,开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突的概率最低,并发度最高
  • 页面锁:开销和加锁时间介于两者之间,会出现死锁,并发度一般
  • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
  • 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性

​乐观锁​​​更适合解决冲突概率极小的情况,而​​悲观锁​​​则适合解决并发竞争激烈的情况,尽量使用​​行锁​​​,缩小加锁粒度,以提高并发处理能力,即使加​​行锁​​​的时间比​​表锁​​要长


举报

相关推荐

0 条评论