数据库之锁模块

MyISAM与InnoDB关于锁方面的区别

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

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

注:

实际上在不走索引的时候,InnoDB的实现方式和MyIsam的表锁方式不同,单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X锁(排他锁),此时想改变树型结构即索引结构的话,是会被锁住的,这个类似于表锁,但原理上和表锁是完全不同的

MyISAM适合的场景:

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

InnoDB适合的场景:

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

数据库锁的分类:

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

总结:

MyISAM默认使用的是表级锁,不支持行级锁。InnoDB默认用的是行级锁,也支持表级锁。无论是表级锁还是行级锁,均分为共享锁和排他锁,它们的关系如下表所示(X:排他锁,S:共享锁):


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

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

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锁来避免幻读。

原文地址:https://blog.51cto.com/zero01/2424370

时间: 2024-10-19 00:27:05

数据库之锁模块的相关文章

并发编程(四):也谈谈数据库的锁机制

http://www.2cto.com/database/201403/286730.html 1. 数据库并发的问题 数据库带来的并发问题包括: 1. 丢失更新. 2. 未确认的相关性(脏读). 3. 不一致的分析(非重复读). 4. 幻像读. 详细描述如下: 1.1.丢失更新 当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题.每个事务都不知道其它事务的存在.最后的更新将重写由其它事务所做的更新,这将导致数据丢失. e.g.事务A和事务B同时修改某行的值, 事务A

ORACLE查询数据库的锁表情况

  查询数据库的锁表情况语句如下: SELECT p.spid,a.serial#, c.object_name,b.session_id,b.oracle_username,b.os_user_name FROM v$process p,v$session a, v$locked_object b,all_objects c WHERE p.addr=a.paddr AND a.process=b.process AND c.object_id=b.object_id 如果表因为某些情况出现死

查看Oracle数据库被锁住的表,删除锁表的进程

锁表处理及查询 查看Oracle数据库被锁住的表,删除锁表的进程 1.查看被锁住的表 SELECT dob.object_name table_name,    lo.locked_mode, lo.session_id, vss.serial#, vss.action action, vss.osuser osuser, vss.logon_time, vss.process ap_pid, vps.spid db_pid FROM v$locked_object lo, dba_object

mysql数据库结合pam_mysql模块实现vsftpd虚拟用户

mysql数据库结合pam_mysql模块实现vsftpd虚拟用户登录 最近开始学mysql,自己做一个小实验,来个总结,比较容易理解,没什么太多理论性的东西. 一.实验环境的准备 1. 先下载需要用到的软件和依赖包 [[email protected] ~]# yum -y install mariadb-server mariadb-devel pam-devel gcc-c++ vsftpd 2. 安装pam_mysql模块(Plugable Authentication Module 插

数据库操作锁住

查看Oracle数据库被锁住的表,删除锁表的进程[@[email protected]]查看Oracle数据库被锁住的表,删除锁表的进程 --1.查看被锁住的表SELECT dob.object_name table_name,lo.locked_mode,lo.session_id,vss.serial#,vss.action action,vss.osuser osuser,vss.logon_time,vss.process ap_pid,vps.spid db_pidFROM v$loc

浅谈数据库的锁

数据库对于程序猿来 并不陌生,但是数据库的锁你知道多少?数据库的锁直接影响数据性能,在大并发的前提下,怎么保证数据不被死锁,提高数据库性能?如何加锁,何时加锁,加什么锁,你可以通过hint手工强行指定,但大多是数据库系统自动决定的.这就是为什么我们可以不懂锁也可以写SQL. 下面我们来简单谈一谈数据库中的锁,以sqlserver 为例: 数据库锁的种类: 1.共享锁(Shared lock) 何为共享锁,顾名思义意思就是资源共享,所以共享锁之间 是没有时间等待的可以同时执行一条或多条查询语句,共

数据库并发事务控制四:postgresql数据库的锁机制二:表锁

在博文<数据库并发事务控制四:postgresql数据库的锁机制 > http://blog.csdn.net/beiigang/article/details/43302947 中后面提到: 常规锁机制可以参考pg的官方手册,章节和内容见下面 13.3. Explicit Locking http://www.postgresql.org/docs/9.4/static/explicit-locking.html 这节分为:表锁.行锁.页锁.死锁.Advisory锁(这个名字怎么翻译好???

数据库 -- 悲观锁与乐观锁

锁( locking ) 业务逻辑的实现过程中,往往需要保证数据访问的排他性.如在金融系统的日终结算 处理中,我们希望针对某个 cut-off 时间点的数据进行处理,而不希望在结算进行过程中 (可能是几秒种,也可能是几个小时),数据再发生变化.此时,我们就需要通过一些机 制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓 的 " 锁 " ,即给我们选定的目标数据上锁,使其无法被其他程序修改. hibernate 支持两种锁机制:即通常所说的 " 悲

浅谈数据库并发控制 - 锁和 MVCC

在学习几年编程之后,你会发现所有的问题都没有简单.快捷的解决方案,很多问题都需要权衡和妥协,而本文介绍的就是数据库在并发性能和可串行化之间做的权衡和妥协 - 并发控制机制. 如果数据库中的所有事务都是串行执行的,那么它非常容易成为整个应用的性能瓶颈,虽然说没法水平扩展的节点在最后都会成为瓶颈,但是串行执行事务的数据库会加速这一过程:而并发(Concurrency)使一切事情的发生都有了可能,它能够解决一定的性能问题,但是它会带来更多诡异的错误. 引入了并发事务之后,如果不对事务的执行进行控制就会