数据库之锁模块

MyISAM与InnoDB关于锁方面的区别

MyISAM与InnoDB关于锁方面的区别:mysql

  • MyISAM默认使用的是表级锁,不支持行级锁
  • InnoDB默认用的是行级锁,也支持表级锁
  • InnoDB支持事务,在事务中被加锁的数据行须要 等事务commit以后才会统一解锁,不然不会解锁。而MyISAM不支持事务,因此不会有这个问题
  • MyISAM和InnoDB都支持共享锁和排他锁,读锁共享,写锁排他
  • InnoDB在开启事务时,若select语句不走索引的状况会锁住整张表,也就是说InnoDB在SQL没有利用到索引的时候使用的是表级锁,而SQL用到索引的时候则是使用行级锁和gap锁,gap锁是走普通非惟一索引时用到的
  • InnoDB除了支持行级锁以外,还支持表级的意向锁,意向锁分为共享读锁(IS)和排他写锁(IX)

注:算法

实际上在不走索引的时候,InnoDB的实现方式和MyIsam的表锁方式不一样,单条索引记录上加锁,record lock锁住的永远是索引,而非记录自己,即便该表上没有任何索引,那么innodb会在后台建立一个隐藏的汇集主键索引,那么锁住的就是这个隐藏的汇集主键索引。因此说当一条sql没有走任何索引时,那么将会在每一条汇集索引后面加X锁(排他锁),此时想改变树型结构即索引结构的话,是会被锁住的,这个相似于表锁,但原理上和表锁是彻底不一样的sql

MyISAM适合的场景:数据库

  • 频繁执行全表count语句
  • 对数据进行增删改的频率不高,而查询很是频繁的场景
  • 没有事务场景

InnoDB适合的场景:并发

  • 数据进行增删改查都至关频繁的系统
  • 可靠性要求比较高,须要事务特性的系统

数据库锁的分类:mvc

  • 按锁的粒度划分,可分为表级锁、行级锁、页级锁
  • 按锁级别划分,可分为共享锁、排他锁
  • 按加锁方式划分,可分为自动锁、显式锁
  • 按操做划分,可分为DML锁、DDL锁
  • 按使用方式划分,可分为乐观锁、悲观锁;悲观锁一般须要利用数据库提供的锁机制来实现;而乐观锁一般用版本号或时间戳来实现

总结:ide

MyISAM默认使用的是表级锁,不支持行级锁。InnoDB默认用的是行级锁,也支持表级锁。不管是表级锁仍是行级锁,均分为共享锁和排他锁,它们的关系以下表所示(X:排他锁,S:共享锁):
数据库之锁模块3d


事务隔离级别以及各级别下的并发访问问题以及事务隔离机制

事务并发访问引发的问题以及如何避免:指针

1.更新丢失:日志

即一个事务的更新覆盖了另外一个事务的更新;因为如今主流数据库都会自动加锁来避免更新丢失的状况,因此在数据库层面一般不会发生这个问题。例如mysql全部事务隔离级别在数据库层面上都可避免更新丢失

下图模拟了更新丢失的过程:
数据库之锁模块

2.脏读(Dirty read):

即一个事务读到另外一个事务的未提交数据;该问题在READ-COMMITTED(读已提交)以上的事务隔离级别可避免

3.不可重复读(Non-repeatable read):

即事务A屡次读取同一数据,但事务B在事务A屡次读取的过程当中对该数据作了更新操做并提交,致使事务A屡次读取同一数据时结果不一致;该问题在REPEATABLE-READ(可重复读)以上的事务隔离级别可避免,这也是MySQL的默认隔离级别

4.幻读(Phantom read):

事务A读取以搜索条件相匹配的若干行数据,而事务B则对事务A查询匹配的数据进行了插入或删除操做,致使事务A屡次读取的结果集行数不一致;该问题在SERIALIZABLE(串行化)以上的事务隔离级别可避免,须要注意的是:在MySQL数据库中,REPEATABLE-READ事务隔离级别下也能够避免幻读

总结:
数据库之锁模块


当前读和快照读

表象:快照读(非阻塞读)-- 伪MVCC(多版本并发控制)
内在:next-key锁(行级锁+gap锁)

首先咱们须要知道两个概念:当前读和快照读;当前读其实就是加了锁的增删改查语句,例:

  • select ... lock in share mode;select ... for update
  • update,delete,insert(自动加锁)

之因此叫当前读,是由于读取的是当前记录的最新版本,而RR事务隔离级别下在读取数据以后还须要保证其余事务不能修改当前记录,那么就会对读取的记录加next-key锁,因此RR事务隔离级别下的当前读能够避免发生幻读现象:
数据库之锁模块

快照读则是不加锁的非阻塞读,例如不加锁的普通select操做。但须要注意的是在串行化的事务隔离级别下,任何的增删改查操做都会被加锁。

在mysql中,读已提交隔离级别下,快照读和当前读都是读到一样的数据。而在可重复读隔离级别下,快照读读到的是开启事务时第一条select语句读到的快照版本数据,当前读则是会读到当前数据库中最新的数据。

RC、RR级别下的InnoDB的快照读(非阻塞读)是如何实现的:

  • 一是依靠数据行里的隐藏字段:DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段
    • DB_TRX_ID:最后修改本行数据的事务id
    • DB_ROLL_PTR:回滚指针,指向undo日志的历史版本数据
    • DB_ROW_ID:行号,即密集索引维护的自增id
  • 二是undo日志,当咱们对数据进行变动操做时就会产生undo日志,undo日志中存储的是历史数据,当一个旧事务须要读取数据时,会顺着undo链找到知足其可见性的数据;undo日志还分为insert undo日志和update undo日志
    • insert undo日志:记录insert操做产生的undo日志,该日志记录只在事务回滚时须要,而在事务提交后会当即丢弃
    • update undo日志:记录update或delete操做产生的undo日志,该日志记录不只在事务回滚时须要,快照读也须要,因此不会立刻被删除,只有当数据库所使用的快照中不涉及该日志记录才会被删除
  • 三是read view,它主要用来作可见性判断的,即当咱们去执行快照读时,会针对咱们查询的数据建立一个read view,以此来决定该事务能看到的是哪一个版本的数据。read view的建立时机是开启事务后执行的第一条select语句
    • read view遵循一个可见性算法,该算法会先取出将要变动数据行的DB_TRX_ID,与系统其余活跃的事务id作对比,若是大于等于这些活跃的事务id就会经过DB_ROLL_PTR去undo日志里取出DB_TRX_ID小于当前活跃事务id的历史数据

事务对行的更新过程:
数据库之锁模块
数据库之锁模块


RR事务隔离级别下是如何避免幻读的

在以前的小节中,咱们了解到在MySQL的RR事务隔离级别下,是能够避免幻读的。但并不意味着快照读是避免发生幻读现象的根本,由于快照读只是读的发生变化前的历史数据。实际在RR及SERIALIZABLE事务隔离级别下真正防止幻读发生的缘由是事务对数据加上了next-key锁,而next-key锁由行锁和gap锁两部分组成。行锁就很少说了,gap锁才是重点,所谓gap就是索引树中插入新记录的间隙,而gap锁是用于锁定一个间隙范围但不包括记录自己,gap锁的目的是为了防止同一事务的两次当前读而致使出现幻读的状况。

gap锁只在RR和SERIALIZABLE事务隔离级别中存在,其余的隔离级别是没有的,因此RC和RU是没法避免幻读的。这里咱们主要讨论RR事务隔离级别下gap锁出现的场景:

  • 增删改查及当前读若用到主键索引或惟一索引会对其加gap锁吗?
    • 答:视状况而定,若是where条件所有命中,则不会用gap锁,只会加行锁;而where条件部分命中或者全不命中,则会加gap锁;因此gap锁会用在非惟一索引或者不走索引的当前读中

where条件所有命中,只会加行锁:
数据库之锁模块

走非惟一索引时会对该索引间隙加gap锁:
数据库之锁模块

不走索引则会对表里全部的间隙加gap锁,其效果就相似于表级锁了,可是其代价比表级锁更大:
数据库之锁模块

总结:

不管是当前读仍是快照读,在innodb的RR的事务隔离级别下均可以免幻读。在快照读的状况下,innodb经过mvcc来避免幻读;在当前读的状况下,innodb经过next-key锁来避免幻读。

相关文章
相关标签/搜索