精华内容
下载资源
问答
  • 本文准备通俗的讲解MySQL的InnoDB存储引擎事务实现原理。 首先,我们知道事务具有ACID四个特性。也即:原子性,一致性,隔离性,持久性。 这四个性质我们不用干瘪的文字去阐述,我们只需要知道事务保证了一系列的...

    本文准备通俗的讲解MySQL的InnoDB存储引擎事务的实现原理。

    首先,我们知道事务具有ACID四个特性。也即:原子性,一致性,隔离性,持久性。

    这四个性质我们不用干瘪的文字去阐述,我们只需要知道事务保证了一系列的操作要么全部执行,要么一个也不执行,同时一旦事务提交,则其所做的修改会永久保存到数据库即可。

    接下来我们一起看看InnoDB怎么实现的事务。

    ACD三个特性是通过Redo log(重做日志)和Undo log 实现的。 而隔离性是通过锁来实现的。由于隔离性和锁在之前的文章讲过了。所以本文重点关注Redo log 和Undo log。

    一、Redo log

    重做日志用来实现事务的持久性,即D特性。它由两部分组成:

    ①内存中的重做日志缓冲
    ②重做日志文件

    一看有内存和磁盘上的两个对应实体,我们就知道这样做一定是为了效率考虑,因为内存的读写效率要比磁盘读写效率高太多。

    Innodb是支持事务的存储引擎,在事务提交时,必须先将该事务的所有日志写入到redo日志文件中,待事务的commit操作完成才算整个事务操作完成。在每次将redo log buffer写入redo log file后,都需要调用一次fsync操作,因为重做日志缓冲只是把内容先写入操作系统的缓冲系统中,并没有确保直接写入到磁盘上,所以必须进行一次fsync操作。因此,磁盘的性能在一定程度上也决定了事务提交的性能。

    关于fsync这个操作用户是可以干预的,因为每次提交事务都执行一次fsync,确实影响数据库性能。通过innodb_flush_log_at_trx_commit来控制redo log刷新到磁盘的策略。该参数的默认值为1,表示每次提交事务时都执行一次fsync操作。0则表示事务提交时不进行写入重做日志文件,这个写入操作由master thread进程来完成,master thread每一秒会进行一次重做日志文件的fsync操作。2则表示事务提交时将重做日志写入重做日志文件,但仅写入文件系统的缓存中,并不进行fsync操作。用户可以通过设置0或者2啦提高事务提交的性能,也可以设置1来要求确保redo log是写入文件中的,总之三种方法各有利弊。

    还有需要了解的是:
    redo log buffer将内存中的log block刷新到磁盘是有一定的规则的:事务提交时(前面已经提到)、当log buffer中有一半的内存空间被使用时、log checkpoint时。

    那接下来我们就需要看看redo log file存储的内容到底是什么了。

    为了避免大家懵圈,不打算把存储格式一个一个细钻(我也没那实力,哈哈)。我们只需要知道他大致是怎么设计的就行了。这样,我们以后如果自己设计一个类似场景的产品,就完全可以借鉴它的设计思想啦。

    好,开始:
    在InnoDB存储引擎中,重做日志都是以512字节为单位进行存储的,这意味着重做日志缓存、重做日志文件块都是以块(block)的方式进行保存的,称为重做日志块(redo log block)。每块的大小512字节。由于重做日志块的大小和磁盘扇区大小一样,都是512字节,因此重做日志的写入可以保证原子性,不需要double write技术。

    每个重做日志块的内容快除了日志记录本身之外,还由日志块头(log block header)及日志块尾(log block tailer)两部分组成。重做日志头一共占用12字节,重做日志尾占用8字节。这两部分是固定的。故每个重做日志块实际可以存储的大小为492字节(512-12-8),如下图显示重做日志块缓存的结构:

    5679451-b06bb15d6c151b4a.jpg

    在图中标注出来不用太过关注这几个字段的含义,因为他们对理解Redo log实现事务的机制没有太大影响,反而如果关注这些,容易让人看到这些大写字母的变量感到头晕。

    ps:这些变量是维护log block状态的一些变量。比如表示log block当前使用量,当前redo block的第一个redo log开始位置等等。举个例子吧:

    事务T1的重做日志1占用762字节,事务T2的重做日志占用100字节,。由于每个log block实际只能保存492字节,因此其在log buffer的情况应该如下图所示:

    5679451-8f782e0558dea90d.png

    实现这个功能就是靠log block的头部的字段来实现的。好了,这不是我们关注的问题,讲这个只是为了满足大家的好奇心以及对这些变量的初步认识。

    重做日志块中出去header和tailer的内容就是具体的redo log了。不同的数据库操作会有对应的重做日志格式。此外,由于InnoDB存储引擎的存储管理是基于页的,故其重做日志格式也是基于页的。虽然有着不同的重做日志格式,但他们有着通用的头部格式,如图:


    5679451-b486340f971d5690.png

    通用的头部格式由一下3部分组成
    redo_log_type: 重做日志类型
    space:表空间ID
    page_no 页的偏移量即页的位置

    之后是redo log body ,根据重做日志类型的不同,会有不同的存储内容,例如,对于页上记录的插入和删除操作,分别对应的如图的格式(同样,不要细扣每一个字段的含义,这不是我们要抓的重点):

    5679451-8acf4dae731525b2.png

    大体上的redo log结构介绍完了。在说从redo log file恢复之前,还要说一个LSN的概念,LSN是Log Sequence Number的缩写,其代表的是日志序列号,在InnoDB存储引擎中,LSN占用8个字节,并且单调递增。

    LSN表示事务写入重做日志字节的总量。例如当前重做日志的LSN为1000,有一个事务T1写入了100字节的重做日志,那么LSN就变成1100,若又有事务T2写入200字节的重做日志,那么LSN就变为1300。

    LSN不仅记录在重做日志中,还存在每个页中,在每个页的头部,有一个值FIL_PAGE_LSN,记录了该页的LSN,在页中,LSN表示该页最后刷新时LSN的大小。因为重做日志记录的是每个页的日志,因此页中的LSN可以判断页是否需要进行恢复操作。例如,页P1的LSN为10000,而数据库启动时,InnoDB检测到写入重做日志中的LSN为13000,并且事务已经提交,那么数据库需要进行恢复操作。将重做日志应用到P1页中,同样的,对于重做日志中LSN小于P1页的LSN,不需要进行重做,因为P1页中的LSN表示已经被刷新到该位置,在此位置之前的内容已经被成功的处理了。

    接下来就是恢复操作了:
    InnoDB存储引擎在启动时不管上次数据运行是否正常关闭,都会尝试进行恢复操作,因为重做日志记录的是物理日志(不要纠结这个),因此恢复的速度比逻辑日志,如二进制日志要快的多,于此同时,InnoDB存储引擎自身也对恢复进行了一定程度的优化,如顺序读取及并行应用重做日志,这样可以进一步提高数据库恢复的速度

    由于checkpoint表示已经刷新到磁盘页上的LSN,因此在恢复过程中仅需恢复checkpoint开始的日志部分。对于图中的例子,当数据库在checkpoint的LSN为10 000时发生宕机,恢复操作仅恢复LSN 10000~13000范围内的日志。

    5679451-cacc0f122b85dcdc.png

    物理日志
    举个例子,对于Insert操作,物理日志记录的是每个页的变化:
    若执行SQL语句:
    INSERT INTO t SELECT 1,2;
    其记录的重做日志大致类似这个样子:
    page(2,3),offset 32,value 1,2

    二、Undo log

    第二部分是Undo log,它可以实现如下两个功能:
    1.实现事务回滚
    2.实现MVCC

    undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

    当执行回滚时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,帮助用户实现一致性非锁定读取。我们举一个具体的例子。例子来自此文

    这个例子主要演示事务对某行记录的更新过程:

    在演示之前,补充一下:
    InnoDB为每行记录都实现了三个隐藏字段,用来实现MVCC:

    • 6字节的事务ID(DB_TRX_ID ,每处理一个事务,其值自动+1。
    • 7字节的回滚指针(DB_ROLL_PTR),指向写到rollback segment(回滚段)的一条undo log记录。
    • 隐藏的ID

    1. 初始数据行

    5679451-98d34fe237e0c655.png

    F1~F6是某行列的名字,1~6是其对应的数据。后面三个隐含字段分别对应该行的事务号和回滚指针,假如这条数据是刚INSERT的,可以认为ID为1,其他两个字段为空。

    2.事务1更改该行的各字段的值

    5679451-5e99c883192277c2.png

    当事务1更改该行的值时,会进行如下操作:

    • 用排他锁锁定该行
    • 记录redo log
    • 把该行修改前的值Copy(可以理解成Copy,不要纠结前面说反向更新这里说复制,原理是一样的)到undo log,即上图中下面的行
    • 修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行。

    3.事务2修改该行的值

    5679451-33270caba012a5bc.png

    与事务1相同,此时undo log,中有有两行记录,并且通过回滚指针连在一起。

    这些通过回滚指针联系起来的行相当于是数据的多个快照,从而实现MVCC的一致性非锁定读了。

    具体规则如下:

    InnoDB的MVCC,是通过上面我们说的每行纪录后面隐藏的列来实现的。他们保存了行的创建时间和行的过期时间(或删除时间),当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行纪录的版本号进行比较。在REPEATABLE READ隔离级别下,MVCC具体的操作如下:

    SELECT
    InnoDB会根据以下两个条件检查每行纪录:

    • InnoDB只查找版本早于当前事务版本的数据行,即,行的系统版本号小于或等于事务的系统版本号,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
    • 行的删除版本,要么未定义,要么大于当前事务版本号。这样可以确保事务读取到的行,在事务开始之前未被删除。
      只有符合上述两个条件的纪录,才能作为查询结果返回。

    INSERT

    • InnoDB为插入的每一行保存当前系统版本号作为行版本号。

    DELETE

    • InnoDB为删除的每一行保存当前系统版本号作为行删除标识。

    UPDATE

    • InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时,保存当前系统版本号到原来的行作为行删除标识。

    读到这里,也许会有一个疑问,考虑如下执行序列:


    5679451-961629618cb774fe.png
    011.png

    按照之前的Select规则,会话B 的事务是在 会话A的后面开启的,那么B的事务版本号大于A的事务版本号。这样在A中插入的数据在未提交的情况下,B可以读到A修改的数据,这不就自相矛盾了么?

    其实不是,InnoDB通过read view来确定一致性读时的数据库snapshot,InnoDB的read view确定一条记录能否看到,有两条法则 :
    1 看不到read view创建时刻以后启动的事务
    2 看不到read view创建时活跃的事务

    对于Session A,start transaction时并没有创建read view,而是在update语句才创建。所以Session A 的read view创建时间要比Session B的晚。所以B是不会看到A的操作的。因此防止了不可重复读。

    两条法则原文描述如下:
    Rule 1: When the read view object is created it notes down the smallest transaction identifier that is not yet used as a transaction identifier (trx_sys_t::max_trx_id). The read view calls it the low limit. So the transaction using the read view must not see any transaction with identifier greater than or equal to this low limit.

    Rule 2: The transaction using the read view must not see a transaction that was active when the read view was created.

    补充:如果undo log一直不删除,则会通过当前记录的回滚指针回溯到该行创建时的初始内容,所幸的时在Innodb中存在purge线程,它会查询那些比现在最老的活动事务还早的undo log,并删除它们,从而保证undo log文件不至于无限增长。

    关注公众号: “Java不睡觉”, 回复:“资源”。获取大数据全套视频和大量Java书籍

    展开全文
  • 之前的文章mysql锁机制详解中我们详细讲解了innodb的锁机制,锁机制是用来保证在并发情况下数据的准确性,而要保证数据准确通常需要事务的支持,而mysql存储引擎innodb是通过锁机制来巧妙地实现事务的隔离特性中的4...
  • show engine innodb status; -- 结果 -- -- history list length 12 :代表redo log的数量 --
  • 这里所说的MySQL事务是指使用InnoDB引擎时的事务。MySQL在5.5版本之前默认的数据库引擎时MyISAM,虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的...

       这里所说的MySQL事务是指使用InnoDB引擎时的事务。MySQL在5.5版本之前默认的数据库引擎时MyISAM,虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。

    redo log和undo log来保证事务的原子性、一致性和持久性,同时采用预写式日志(WAL)方式将随机写入变成顺序追加写入,提升事务性能。而隔离性是通过锁技术来保证的。

    这里我们不妨先来了解一下redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作。undo log不是redo log的逆向过程,其实它们都算是用来恢复的日志:

    • redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
    • undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。

    预写式日志:
    事务日志采用追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头。事务日志持久以后,内存中被修改的数据在后台可以慢慢的刷回到磁盘。我们通常称为预写式日志,修改数据需要写两次磁盘。
    如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。

    (一)redo log:

    redo log 又称为重做日志,它包含两部分:

    • 一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;
    • 二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

    当需要修改事务中的数据时,先将对应的redo log写入到redo log buffer中,然后才在内存中执行相关的数据修改操作。InnoDB通过“force log at commit”机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有redo log都写入到磁盘上的redo log file中,然后待事务的commit操作完成才算整个事务操作完成。

    在每次将redo log buffer中的内容写入redo log file时,都需要调用一次fsync操作,以此确保redo log成功写入到磁盘上(参考下图,内容的流向为:用户态的内存->操作系统的页缓存->物理磁盘)。因此,磁盘的性能在一定程度上也决定了事务提交的性能。这里还可以通过innodb_flush_log_at_trx_commit来控制redo log刷磁盘的策略,这里就不做赘述了。

    在这里插入图片描述

    (二)undo log:

       undo log有2个功能:实现回滚和多版本并发控制(MVCC, Multi-Version Concurrency Control)。

    在数据修改的时候,不仅记录了redo log,还记录了相对应的undo log,如果因为某些原因导致事务失败或回滚了,可以借助该undo log进行回滚。

    undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

    当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

    (三)MVCC:

       说到undo log,就不得不顺带提一下MVCC了,因为MVCC的实现依赖了undo log。当然,MVCC的实现还依赖了隐藏字段(DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID)、Read View等。

    MVCC的全称是多版本并发控制,它使得在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务下执行一致性读操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大技术,因为这样的一来的话查询就不用等待另一个事务释放锁,使不同事务的读-写、写-读操作并发执行,从而提升系统性能。

    这里的读指的是“快照读”。普通的SELECT操作就是快照读,有的地方也称之为“一致性读”或者“一致性无锁读”。它不会对表中的任何记录做加锁动作,即不加锁的非阻塞读。快照读的前提是隔离级别不是串行化级别,串行化级别下的快照读会退化成当前读。之所以出现快照读的情况,是基于提高并发性能的考虑,这里可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销。当然,既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。

    对应的还有“当前读”。类似UPDATE、DELETE、INSERT、SELECT…LOCK IN SHARE MODE、SELECT…FOR UPDATE这些操作就是当前读。为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

    (四)锁技术:

       并发事务的读-读情况并不会引起什么问题(读取操作本身不会对记录有任何影响,并不会引起什么问题,所以允许这种情况的发生),不过对于写-写、读-写或写-读这些情况可能会引起一些问题,需要使用MVCC或者加锁的方式来解决它们。

    在使用加锁的方式解决问题时,既要允许读-读情况不受影响,又要使写-写、读-写或写-读情况中的操作相互阻塞。这里引入了两种行级锁:

    • 共享锁:英文名为Shared Locks,简称S锁。允许事务读一行数据。
    • 排它锁:也常称独占锁,英文名为Exclusive Locks,简称X锁。允许事务删除或更新一行数据。

    假如事务A首先获取了一条记录的S锁之后,事务B接着也要访问这条记录:1) 如果事务B想要再获取一个记录的S锁,那么事务B也会获得该锁,也就意味着事务A和B在该记录上同时持有S锁;2) 如果事务B想要再获取一个记录的X锁,那么此操作会被阻塞,直到事务A提交之后将S锁释放掉。

    如果事务A首先获取了一条记录的X锁之后,那么不管事务B接着想获取该记录的S锁还是X锁都会被阻塞,直到事务A提交。

    除了 S锁 和 S 锁兼容,其他都不兼容。

    InnoDB存储引擎还支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为此,InnoDB存储引擎引入了意向锁(表级别锁):

    • 意向共享锁(IS 锁):事务想要获取一张表的几行数据的共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
    • 意向排他锁(IX 锁):事务想要获取一张表中几行数据的排它锁,事务在给一个数据行加排它锁前必须先取得该表的 IX 锁。

    当我们在对使用InnoDB存储引擎的表的某些记录加S锁之前,那就需要先在表级别加一个IS锁,当我们在对使用InnoDB存储引擎的表的某些记录加X锁之前,那就需要先在表级别加一个IX锁。IS锁和IX锁的使命只是为了后续在加表级别的S锁和X锁时判断表中是否有已经被加锁的记录,以避免用遍历的方式来查看表中有没有上锁的记录。

    锁粒度
    MySQL中不同的存储引擎支持不同的锁机制:MyISAM与MEMORY存储引擎采用表级锁;BDB存储引擎采用的是页级锁,也支持表级锁;InnoDB存储引擎既支持行级锁,也支持表级锁,默认采用行级锁。
    1:表级锁: MySQL中开销最小的策略,加锁速度快,锁定整张表,粒度大。不会出现死锁,发生锁竞争的概率最高,并发度最低,性能最差。
    2:行级锁: 开销大,加锁速度慢,锁定一行数据,粒度小。会出现死锁,发生锁竞争的概率最低,并发读最高,性能高。
    3:页级锁: 开销和加锁速度介于表锁和行锁之间,锁定一页数据。会出现死锁,锁竞争概率、并发性、性能均位于表锁和行锁之间。

    下表展示了X、IX、S、IS锁的兼容性:
    在这里插入图片描述

    这里还要了解一下的是,在InnoDB中有 3 种行锁的算法:

    • Record Locks(记录锁):单个行记录上的锁。
    • Gap Locks(间隙锁):在记录之间加锁,或者在第一个记录之前加锁,亦或者在最后一个记录之后加锁,即锁定一个范围,而非记录本身。
    • Next-Key Locks:结合 Gap Locks 和 Record Locks,锁定一个范围,并且锁定记录本身。主要解决的是
      REPEATABLE READ 隔离级别下的幻读问题。

    对于Next-Key Locks,如果我们锁定了一个行,且查询的索引含有唯一属性时(即有唯一索引),那么这个时候InnoDB会将Next-Key Locks优化成Record Locks,也就是锁定当前行,而不是锁定当前行加一个范围;如果我们使用的不是唯一索引锁定一行数据,那么此时InnoDB就会按照本来的规则锁定一个范围和记录。还有需要注意的点是,当唯一索引由多个列组成时,如果查询仅是查找其中的一个列,这时候是不会降级的。InnoDB存储引擎还会对辅助索引的下一个键值区间加Gap Locks(这么做也是为了防止幻读)。

    (五)总结:

    MySQL实现事务ACID特性的方式总结如下:

    • 原子性:使用 undo log来实现,如果事务执行过程中出错或者用户执行了rollback,系统通过undo
      log日志返回事务开始的状态。
    • 持久性:使用 redo log来实现,只要redo log日志持久化了,当系统崩溃,即可通过redo log把数据恢复。
    • 隔离性:通过锁以及MVCC来实现。
    • 一致性:通过回滚、恢复以及并发情况下的隔离性,从而实现一致性。
    展开全文
  • 浅析Innodb事务底层实现原理

    千次阅读 2019-07-11 16:42:57
    MySQL只有innodb引擎支持事务,因此这篇文章也是以innodb为背景写的。 一、事务并发会带来什么问题? 脏读:读到了其他事务未提交的数据 不可重复读:当前事务先进行了一次数据读取,然后再次读取到的数据是别的...

    如果对事务的基本知识不是太了解,可以先去看这篇文章【MySQL】事务知识小结

    MySQL只有innodb引擎支持事务,因此这篇文章也是以innodb为背景写的。

    一、事务并发会带来什么问题?

    • 脏读:读到了其他事务未提交的数据
    • 不可重复读:当前事务先进行了一次数据读取,然后再次读取到的数据是别的事务修改成功的数据,导致两次读取到的数据不一致(update、delete)
    • 幻读:当前事务读第一次取到的数据比后来读取到数据条目少(insert)

    解决上面的问题就需要设置相应的事务隔离性

    • Read Uncommitted(未提交读) :事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”、“幻读”和“不可重复读取”。
    • READ COMMITTED (提交读):保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。
    • REPEATABLE READ(重复读) :默认隔离级别,保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但不能避免“幻读”,但是带来了更多的性能损失。
    • Serializable (串行化):最严格的级别,事务串行执行,资源消耗最大,解决了所有问题。

    需要注意,在innoDB引擎中,当隔离级别为可重复读就可以解决幻读的情况,因为innodb

    • 在快照读读情况下,mysql通过mvcc来避免幻读。
    • 在当前读读情况下,mysql通过next-key来避免幻读

    因此innodb的默认隔离级别就是可重复度.

    二、如何实现事务隔离级别?

    首先有两种方式:

    • 加锁:在读取数据前,对其加锁,防止其他事务对数据进行修改。
    • MVCC:多版本并发控制,指的是一种提高并发的技术,生成一个数据请求时间点的一致性数据快照,并用这个快照来提供一定级别的一致性读取.

    最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,InnoDB是在undolog中实现的,通过undolog可以找回数据的历史版本。

    三、锁内容详解

    1、锁的分类

    1.1表锁和行锁

    首先来看看表锁和行锁,顾名思义,表锁是锁住整张表,行锁是锁住一行的数据,下面是它们的区别

    • 锁定粒度:表锁>行锁
    • 加锁效率:表锁>行锁
    • 冲突概率:表锁>行锁
    • 并发性能:表锁<行锁

    MyISAM存储引擎仅支持表锁;innodb存储引擎既支持行锁又支持表锁。

    1.2共享锁和排他锁(行锁)

    共享锁:又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同意数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

    共享锁的加锁释放锁方式:

    BEGIN;
    
    select * from student where id =1 LOCK INSHARE MODE;
    
    commit/rollback;

    排他锁:又称为写锁,排他锁不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事物就不能再获取该行的锁,只有该获取了排他锁的事务才能对数据进行读取和修改。

    排他锁的加锁释放锁方式:

    • 自动:delete/update/insert 默认加上排他锁
    • 手动:select * from student where id = 1 FOR UPDATE;

    1.3意向共享锁和意向排他锁(表锁)

    首先需要知道意向锁是由数据引擎自己维护的,用户无法手动操作意向锁。

    意向共享锁:表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的意向共享锁。

    意向排他锁:表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的意向排他锁。

    思考:为什么需要(表级别的)意向锁?

    因为在加表锁时,如果此时表中已经有其他事务锁定了这张表的任一行,表锁就不能成功,因此在加表锁时会先去判断每一行有没有加锁,然后再看加不加表锁,但是判断每一行加没加锁显然效率太低了,因此就需要意向锁,使用意向锁就会在加表锁时直接判断是否有意向锁即可,提高了效率。

    2、锁的作用

    锁是为了解决资源竞争并发的问题,在Java中资源就是对象,在Mysql中就是数据表、数据行

    3、锁到底锁住了什么?

    当数据库中没有索引时,加锁会锁住整张表.

    其实数据库中的锁是锁住索引项实现的!对于innodb而言,如果没有索引它定义一个主键来作为聚簇索引。那也就是在我们没有创建索引时,对innodb存储引擎而言,我们加锁表中的数据,锁住的就是这个隐式聚簇索引,InnoDB行锁对唯一索引或主键索引的数据生效,若我们没有手动的去加索引,那么对于加锁时只有sql语句能用到默认的聚簇索引也就是默认的主键索引时才会锁住行,因此我们常说如果没有建主键索引,使用innodb时会锁整张表。

    4、行锁的算法

    分为三种:记录锁、间隙锁、临键锁

    记录锁:总是会锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。

    间隙锁: 只在RR级别存在,是为了解决Phantom Problem(幻象/幻读),利用这种锁技术,锁定的不是单个值,而是一个范围。

    临键锁: 是结合了记录锁和间隙锁的一种锁定算法,在此种算法下,InnoDB对于行的查询都是采用这种锁定算法。

     

    在mysql中就是使用锁+MVCC实现事务,当只有写写时才会阻塞。

    展开全文
  • MySQL InnoDB事务ACID实现原理

    千次阅读 2019-04-05 21:20:38
    隔离性的实现原理就是锁,因而隔离性也可以称为并发控制、锁等。事务的隔离性要求每个读写事务的对象对其他事务的操作对象能互相分离。 再者,比如操作缓冲池中的 LRU 列表,删除,添加、移动 LRU 列表中的元素,...
    • 原子性(atomicity)
    • 一致性(consistency)
    • 隔离性(isolation)
    • 持久性(durability)

    隔离性

    隔离性的实现原理就是锁,因而隔离性也可以称为并发控制、锁等。事务的隔离性要求每个读写事务的对象对其他事务的操作对象能互相分离。

    再者,比如操作缓冲池中的 LRU 列表,删除,添加、移动 LRU 列表中的元素,为了保证一致性那么就要锁的介入。

    InnoDB 使用锁为了支持对共享资源进行并发访问,提供数据的完整性和一致性。

    那么到底 InnoDB 支持什么样的锁呢?我们先来看下 InnoDB 的锁的介绍:

    InnoDB 中的锁

    你可能听过各种各样的 InnoDB 的数据库锁,Gap 锁,共享锁,排它锁,读锁,写锁等等。但是 InnoDB 的标准实现的锁只有 2 类,一种是行级锁,一种是意向锁。

    InnoDB 实现了如下两种标准的行级锁:

    • 共享锁(读锁 S Lock),允许事务读一行数据。
    • 排它锁(写锁 X Lock),允许事务删除一行数据或者更新一行数据。

    行级锁中,除了 S 和 S 兼容,其他都不兼容。

    InnoDB 支持两种意向锁(即为表级别的锁):

    • 意向共享锁(读锁 IS Lock),事务想要获取一张表的几行数据的共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
    • 意向排他锁(写锁 IX Lock),事务想要获取一张表中几行数据的排它锁,事务在给一个数据行加排它锁前必须先取得该表的 IX 锁。

    首先解释一下意向锁,以下为意向锁的意图解释:

    The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.

    大致意思是加意向锁为了表明某个事务正在锁定一行或者将要锁定一行数据。

    首先申请意向锁的动作是 InnoDB 完成的,怎么理解意向锁呢?例如:事务 A 要对一行记录 R 进行上 X 锁,那么 InnoDB 会先申请表的 IX 锁,再锁定记录 R 的 X 锁。

    在事务 A 完成之前,事务 B 想要来个全表操作,此时直接在表级别的 IX 就告诉事务 B 需要等待而不需要在表上判断每一行是否有锁。

    意向排它锁存在的价值在于节约 InnoDB 对于锁的定位和处理性能。另外注意了,除了全表扫描以外意向锁都不会阻塞。

    锁的算法

    InnoDB 有 3 种行锁的算法:

    • Record Lock:单个行记录上的锁。
    • Gap Lock:间隙锁,锁定一个范围,而非记录本身。
    • Next-Key Lock:结合 Gap Lock 和 Record Lock,锁定一个范围,并且锁定记录本身。主要解决的问题是 RR 隔离级别下的幻读。

    这里主要讲一下 Next-Key Lock。MySQL 默认隔离级别 RR 下,这时默认采用 Next-Key locks。

    这种间隙锁的目的就是为了阻止多个事务将记录插入到同一范围内从而导致幻读。注意了,如果走唯一索引,那么 Next-Key Lock 会降级为 Record Lock。

    前置条件为事务隔离级别为 RR 且 SQL 走的非唯一索引、主键索引。如果不是则根本不会有 Gap 锁!先举个例子来讲一下 Next-Key Lock。

    首先建立一张表:

    mysql> show create table m_test_db.M; 
    +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
    | Table | Create Table | 
    +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
    | M | CREATE TABLE `M` ( 
     `id` int(11) NOT NULL AUTO_INCREMENT, 
     `user_id` varchar(45) DEFAULT NULL, 
     `name` varchar(45) DEFAULT NULL, 
     PRIMARY KEY (`id`), 
     KEY `IDX_USER_ID` (`user_id`) 
    ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 | 
    +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
    1 row in set (0.00 sec) 
    

    首先 Session A 去拿到 user_id 为 26 的 X 锁,用 force index,强制走这个非唯一辅助索引,因为这张表里的数据很少。

    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> select * from m_test_db.M force index(IDX_USER_ID) where user_id = '26' for update; 
    +----+---------+-------+ 
    | id | user_id | name | 
    +----+---------+-------+ 
    | 5 | 26 | jerry | 
    | 6 | 26 | ketty | 
    +----+---------+-------+ 
    2 rows in set (0.00 sec) 
    

    然后 Session B 插入数据:

    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> insert into m_test_db.M values (8,25,'GrimMjx'); 
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 
    

    明明插入的数据和锁住的数据没有毛线关系,为什么还会阻塞等锁最后超时呢?这就是 Next-Key Lock 实现的。

    画张图你就明白了:

     

    一文快速搞懂MySQL InnoDB事务ACID实现原理

     

     

    Gap 锁锁住的位置,不是记录本身,而是两条记录之间的间隔 Gap,其实就是防止幻读(同一事务下,连续执行两句同样的 SQL 得到不同的结果)。

    为了保证图上 3 个小箭头中间不会插入满足条件的新记录,所以用到了 Gap 锁防止幻读。

    简单的 Insert 会在 Insert 的行对应的索引记录上加一个 Record Lock 锁,并没有 Gap 锁,所以并不会阻塞其他 Session 在 Gap 间隙里插入记录。

    不过在 Insert 操作之前,还会加一种锁,官方文档称它为 Intention Gap Lock,也就是意向的 Gap 锁。

    这个意向 Gap 锁的作用就是预示着当多事务并发插入相同的 Gap 空隙时,只要插入的记录不是 Gap 间隙中的相同位置,则无需等待其他 Session 就可完成,这样就使得 Insert 操作无须加真正的 Gap Lock。

    Session A 插入数据:

    mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> insert into m_test_db.M values (10,25,'GrimMjx');Query OK, 1 row affected (0.00 sec)

    Session B 插入数据,完全没有问题,没有阻塞:

    mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> insert into m_test_db.M values (11,27,'Mjx');Query OK, 1 row affected (0.00 sec)

    死锁

    了解了 InnoDB 是如何加锁的,现在可以去尝试分析死锁。死锁的本质就是两个事务相互等待对方释放持有的锁导致的,关键在于不同 Session 加锁的顺序不一致。

    不懂死锁概念模型的可以先看一幅图:

     

    一文快速搞懂MySQL InnoDB事务ACID实现原理

     

     

    左鸟线程获取了左肉的锁,想要获取右肉的锁,右鸟的线程获取了右肉的锁。

    右鸟想要获取左肉的锁。左鸟没有释放左肉的锁,右鸟也没有释放右肉的锁,那么这就是死锁。

    接下来还用刚才的那张 M 表来分析一下数据库死锁,比较好理解:

     

    一文快速搞懂MySQL InnoDB事务ACID实现原理

     

     

    四种隔离级别

    那么按照最严格到最松的顺序来讲一下四种隔离级别:

    ①Serializable(可序列化)

    最高事务隔离级别。主要用在 InnoDB 存储引擎的分布式事务。强制事务排序,串行化执行事务。

    不需要冲突控制,但是慢速设备。根据 Jim Gray 在《Transaction Processing》一书中指出,Read Committed 和 Serializable 的开销几乎是一样的,甚至 Serializable 更优。

    Session A 设置隔离级别为 Serializable,并开始事务执行一句 SQL:

    mysql> select @@tx_isolation; 
    +----------------+ 
    | @@tx_isolation | 
    +----------------+ 
    | SERIALIZABLE | 
    +----------------+ 
    1 row in set, 1 warning (0.00 sec) 
     
    mysql> start transaction; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> select * from m_test_db.M; 
    +----+---------+-------+ 
    | id | user_id | name | 
    +----+---------+-------+ 
    | 1 | 20 | mjx | 
    | 2 | 21 | ben | 
    | 3 | 23 | may | 
    | 4 | 24 | tom | 
    | 5 | 26 | jerry | 
    | 6 | 26 | ketty | 
    | 7 | 28 | kris | 
    +----+---------+-------+ 
    7 rows in set (0.00 sec) 
    

    Session Binsert 一条数据,超时:

    mysql> start transaction; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> insert into m_test_db.M values (9,30,'test'); 
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 
    

    ②Repeatable Read(可重复读)

    一个事务按相同的查询条件读取以前检索过的数据,其他事务插入了满足其查询条件的新数据,产生幻读。

    InnoDB 存储引擎在 RR 隔离级别下,已经使用 Next-Key Lock 算法避免了幻读,了解概念即可。

    InnoDB 使用 MVCC 来读取数据,RR 隔离级别下,总是读取事务开始时的行数据版本。

    Session A 查看 id=1 的数据:

    mysql> set tx_isolation='repeatable-read'; 
    Query OK, 0 rows affected, 1 warning (0.00 sec) 
     
    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> select * from m_test_db.M where id =1; 
    +----+---------+---------+ 
    | id | user_id | name | 
    +----+---------+---------+ 
    | 1 | 20 | GrimMjx | 
    +----+---------+---------+ 
    1 row in set (0.01 sec) 
    

    Session B 修改 id=1 的数据:

    mysql> set tx_isolation='repeatable-read'; 
    Query OK, 0 rows affected, 1 warning (0.00 sec) 
     
    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> update m_test_db.M set name = 'Mjx'; 
    Query OK, 7 rows affected (0.00 sec) 
    Rows matched: 7 Changed: 7 Warnings: 0 
    

    然后现在 Session A 再查看一下 id=1 的数据,数据还是事务开始时候的数据。

    mysql> select * from m_test_db.M where id =1; 
    +----+---------+---------+ 
    | id | user_id | name | 
    +----+---------+---------+ 
    | 1 | 20 | GrimMjx | 
    +----+---------+---------+ 
    1 row in set (0.00 sec) 
    

    ③Read Committed(读已提交)

    事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。

    InnoDB 使用 MVCC 来读取数据,RC 隔离级别下,总是读取被锁定行最新的快照数据。

    Session A 查看 id=1 的数据:

    mysql> set tx_isolation='read-committed'; 
    Query OK, 0 rows affected, 1 warning (0.00 sec) 
     
    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> select * from m_test_db.M where id =1; 
    +----+---------+------+ 
    | id | user_id | name | 
    +----+---------+------+ 
    | 1 | 20 | Mjx | 
    +----+---------+------+ 
    1 row in set (0.00 sec) 
    

    Session B 修改 id=1 的 Name 并且 Commit:

    mysql> set tx_isolation='repeatable-read'; 
    Query OK, 0 rows affected, 1 warning (0.00 sec) 
     
    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> update m_test_db.M set name = 'testM' where id =1; 
    Query OK, 1 row affected (0.00 sec) 
    Rows matched: 1 Changed: 1 Warnings: 0 
     
    // 注意,这里commit了! 
    mysql> commit; 
    Query OK, 0 rows affected (0.00 sec) 
    

    Session A 再查询 id=1 的记录,发现数据已经是最新的数据:

    mysql> select * from m_test_db.M where id =1; 
    +----+---------+-------+ 
    | id | user_id | name | 
    +----+---------+-------+ 
    | 1 | 20 | testM | 
    +----+---------+-------+ 
    1 row in set (0.00 sec) 
    

    ④Read Uncommitted(读未提交)

    事务中的修改,即使没有提交,对其他事务也都是可见的。

    Session A 查看一下 id=3 的数据,没有 Commit:

    mysql> set tx_isolation='read-uncommitted'; 
    Query OK, 0 rows affected, 1 warning (0.00 sec) 
     
    mysql> select @@tx_isolation; 
    +------------------+ 
    | @@tx_isolation | 
    +------------------+ 
    | READ-UNCOMMITTED | 
    +------------------+ 
    1 row in set, 1 warning (0.00 sec) 
     
    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> select * from m_test_db.M where id =3; 
    +----+---------+------+ 
    | id | user_id | name | 
    +----+---------+------+ 
    | 3 | 23 | may | 
    +----+---------+------+ 
    1 row in set (0.00 sec) 
    

    Session B 修改 id=3 的数据,但是没有 Commit:

    mysql> set tx_isolation='read-uncommitted'; 
    Query OK, 0 rows affected, 1 warning (0.00 sec) 
     
    mysql> begin; 
    Query OK, 0 rows affected (0.00 sec) 
     
    mysql> update m_test_db.M set name = 'GRIMMJX' where id = 3; 
    Query OK, 1 row affected (0.00 sec) 
    Rows matched: 1 Changed: 1 Warnings: 0 
    

    Session A 再次查看则看到了新的结果:

    mysql> select * from m_test_db.M where id =3; 
    +----+---------+---------+ 
    | id | user_id | name | 
    +----+---------+---------+ 
    | 3 | 23 | GRIMMJX | 
    +----+---------+---------+ 
    1 row in set (0.00 sec) 
    

    这里花了很多笔墨来介绍隔离性,这是比较重要,需要静下心来学习的特性。所以也是放在第一个的原因。

    原子性、一致性、持久性

    事务隔离性由锁实现,原子性、一致性和持久性由数据库的 redo log 和 undo log 实现。

    redo log 称为重做日志,用来保证事务的原子性和持久性,恢复提交事务修改的页操作。

    undo log 来保证事务的一致性,undo 回滚行记录到某个特性版本及 MVCC 功能。两者内容不同。redo 记录物理日志,undo 是逻辑日志。

    redo

    重做日志由重做日志缓冲(redo log buffer)和重做日志文件(redo log file)组成,前者是易失的,后者是持久的。

    InnoDB 通过 Force Log at Commit 机制来实现持久性,当 Commit 时,必须先将事务的所有日志写到重做日志文件进行持久化,待 Commit 操作完成才算完成。

    当事务提交时,日志不写入重做日志文件,而是等待一个事件周期后再执行 Fsync 操作,由于并非强制在事务提交时进行一次 Fsync 操作,显然这可以提高数据库性能。

    请记住 3 点:

    重做日志是在 InnoDB 层产生的。

    重做日志是物理格式日志,记录的是对每个页的修改。

    重做日志在事务进行中不断被写入。

    undo

    事务回滚和 MVCC,这就需要 undo。undo 是逻辑日志,只是将数据库逻辑恢复到原来的样子,但是数据结构和页本身在回滚之后可能不同。

    例如:用户执行 insert 10w 条数据的事务,表空间因而增大。用户执行 ROLLBACK 之后,会对插入的数据回滚,但是表空间大小不会因此收缩。

    实际的做法就是做与之前想法的操作,Insert 对应 Delete,Update 对应反向 Update 来实现原子性。

    InnoDB 中 MVCC 的实现就是靠 undo,举个经典的例子:Bob 给 Smith 转 100 元,那么就存在以下 3 个版本,RR 隔离级别下,对于快照数据,总是读事务开始的行数据版本见黄标。

    RC 隔离级别下,对于快照数据,总是读最新的一份快照数据见红标:

     

    一文快速搞懂MySQL InnoDB事务ACID实现原理

     

     

    undo log 会产生 redo log,因为 undo log 需要持久性保护 。

    最后,你会发现姜承尧的 MySQL InnoDB 书上的很多内容都是官方手册的翻译,无论是看源码还是学习新框架,最好看原汁原味的。

    只要你坚持,一步一步来,总归会成功的。切忌,学技术急不来,快就是稳,稳就是快。

    展开全文
  • Innodb中的事务隔离级别实现原理

    千次阅读 2018-10-08 17:34:34
    前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,...这里通过分析MySQL中InnoDB引擎的加锁机制,来抛砖引玉,让读者更好的理解,在事务处理中数据库到底做了什么。 # 一次封锁o...
  • InnoDB实现事务原理

    2021-03-30 23:04:47
    因此要想理清InnoDB事务实现原理,我们很有必要了解一下MySQL的相关知识。 MySQL的逻辑架构 从图1可以看出MySQL有以下几部分组成 连接池组件 管理服务和工具组件 SQL接口组件 查询分析器组件 优化器...
  • MySQL InnoDB事务原理实现1. 概念1.1 ACID1.2 隔离性与隔离级别1.2.1 并发问题1.2.2 隔离级别1.3 分类1.4 事务的启动方式1.5 事务InnoDB中的实现2 隔离性的实现2.1 版本2.2 视图2.3 当前读和快照读(一致性非...
  • 浅析Mysql InnoDB存储引擎事务原理

    万次阅读 2015-08-23 11:01:50
    innoDB存储引擎事务与日志,日志在系统恢复过程中的作用剖析
  • 在Mysql中MVCC是在Innodb存储引擎中得到支持的,Innodb为每行记录都实现了三个隐藏字段: 6字节的事务ID(DB_TRX_ID) 7字节的回滚指针(DB_ROLL_PTR) 隐藏的ID(DB_ROW_ID) 1. Innodb事务相关概念 为了支持...
  • 事务四个特征及实现原理 事务会把数据库从一种状态转换到另一种状态的一种一致状态。在数据库提交工作时,可以保证要么所有的修改都保存了,要么所有的修改都不保存。 InnoDB存储引擎具有事务的ACID的特性: 原子性...
  • 使用锁和锁协议来实现相应的隔离级别来进行并发控制,味道虽好但因为锁会造成事务阻塞,导致并发性能会受到一定的影响。而多版本并发控制使得对同一行记录做读写的事务之间不用相互阻塞等待(写写还是要阻塞等待,...
  • MySQL InnoDB 引擎现在广为使用,它提供了事务,行锁,日志等一系列特性,本文分析下 InnoDB 的内部实现机制,MySQL 版本为 5.7.24,操作系统为 Debian 9。 1、InnoDB 架构 Innodb 架构图 InnoDB 的架构分为...
  • 目录一、InnoDB事务实现二、日志恢复技术1、redo log1)格式2)落盘3)LSN4)恢复2、undo log三、并发控制1、锁1)锁的分类2)使用锁3)作用规则4)InnoDB行级锁2、并发控制1)一致性非锁定读(快照读)2)一致...
  • 点击上方Java后端,选择设为星标优质文章,及时送达来源:七把刀www.jianshu.com/p/d4cc0ea9d097MySQL InnoDB 引擎现在广为使用,它提供了事务,...
  • 面试官:”你们是用mysql数据库吧,能简单说说innodb中怎么实现这四大特性的么?“ 你:”我只知道隔离性是怎么做的balabala~~” 面试官:”还是回去等通知吧~” OK,回到正题。说到事务的四大特性原子性(Atomicity)...
  • 原文链接:InnoDB MVCC实现原理及源码解析 1、原理介绍 数据多版本(MVCC)是MySQL实现高性能的一个主要的一个主要方式,通过对普通的SELECT不加锁,直接利用MVCC读取指版本的值,避免了对数据重复加锁的过程。...
  • MySQL Innodb的MVCC实现原理

    千次阅读 2018-06-29 22:56:45
    之前面试时有被问到mysql的mvcc,虽然对mvcc的原理大概了解,但是mysql中是怎么实现mvcc的确实没有认真去了解过。今天就完整的学习一下。
  • 概述事务(Transaction)是访问和更新数据库的程序执行单元;事务中可能包含一个或多个语句,这些语句要么都执行,要么都不执行,事务是保证数据一致...MySQL的事务是由存储引擎实现的,支持事务的数据库引擎包括InnoD...
  • Innodb原理

    千次阅读 2018-12-24 10:50:14
    一、Innodb锁 1.1 锁介绍  innodb存储引擎是mysql5.1之后的默认存储引擎,相对于之前的默认存储引擎MyISAM,它有两个不同点:一是支持事物,二是采用了行级锁。下面介绍一下innodb的锁。  共享锁(S):其实就....
  • InnoDB事务实现事务隔离性一致性原子性持久性 事务 数据库的事务有四大特性,即:原子性,隔离性,一致性和持久性.要保证这四个特性,InnoDB存储引擎做了一系列的操作,下面来看一下InnoDB是怎么保证数据库事务的...
  • MySQL事务底层实现原理

    千次阅读 2019-10-23 16:37:36
    事务特性 事务特性分为: ...也是在事务并发时实现一致性的一个前提,可以设置4种隔离级别。级别越高一致性越强,但并发性越低; 1.读未提交 会读到其他事务未提交的数据,产生脏读 2.读已提交 解...
  • redo log叫做重做日志,是用来实现事务的持久性(用于数据库的崩溃恢复),当事务提交之后会把所有修改信息都会存到该日志中。该日志由两部分组成,一个是在内存里的redo log buffer,另一个是在磁盘里的redo log...
  • MySQL事务实现原理

    千次阅读 2020-02-27 22:42:45
    今天想跟大家一起研究下事务内部到底是怎么实现的,在讲解前我想先抛出个问题: 事务想要做到什么效果? 按我理解,无非是要做到可靠性以及并发处理。 可靠性:数据库要保证当insert或update操作时抛异常或者数据...
  • MySQL的事务实现原理

    千次阅读 2020-06-18 17:06:56
    参考文章:MySQL的事务实现原理 1 开篇 事务的目的是要实现可靠性以及并发处理。 可靠性:数据库要保证当insert或update操作时抛异常或者数据库crash的时候需要保障数据的操作前后的一致,想要做到这个,我需要...
  • 分布式事务实现原理

    千次阅读 2019-01-16 15:12:53
    事务是数据库系统中非常有趣也非常重要的概念,它是数据库管理系统执行过程中的一个逻辑单元,它...在 SOA 与微服务架构大行其道的今天,在分布式的多个服务中保证业务的一致性就需要我们实现分布式事务。 在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,238
精华内容 14,495
关键字:

innodb事务实现原理