精华内容
下载资源
问答
  • MySQL事务底层实现原理
    千次阅读
    2019-10-23 16:37:36

    事务特性
    事务特性分为:
    原子性 每一个事务都是不可分割的整体,要么全部成功要么全部失败;
    一致性 事务执行后的结果是和预设的规则完全符合的,不会因为出现系统意外等原因和预测的结果不一致;
    隔离性 事务与事务之间是相互独立的,互不影响;也是在事务并发时实现一致性的一个前提,可以设置4种隔离级别。级别越高一致性越强,但并发性越低;

    1.读未提交 会读到其他事务未提交的数据,产生脏读
    2.读已提交 解决脏读,但在同一事务中多次读取单行数据会得到不同结果,即不可重复读的问题
    3.可重复读 解决不可重复读,但会在多次范围查询时,得到数量不同的结果,即幻读。在innodb引擎中,不会存在此问题,实现原理是临键锁。
    4.可串行化 解决幻读
    

    持久性 事务一旦提交就会永久保存,不会因为系统意外原因而丢失

    innodb事务的这些特性如何实现?锁、MVCC


    innodb一共有5种锁:共享锁(行)、排它锁(行)、意向共享锁(表)、意向排它锁(表)、自增锁。行锁实现有三种算法,又称为:临键锁next-key、间隙所gap和记录锁record。默认隔离级别不可重复读使用临键锁作为行锁的实现算法。
    InnoDB的行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件进行数据检索,InnoDB才使用行级锁,否则InnoDB 将使用表锁(锁住全部索引)。

    锁的类型
    1.共享锁:也称为读锁,可以让多个事务同时对数据进行操作时可以访问到数据,但不能修改。使用方式:

    select语句+LOCK IN SHARE MOD
    

    2.排它锁:又称为写锁。一个事务持有了一行数据的排它锁时,其他事务不能再访问和修改这行数据。innodb默认update delete insert上会加排它锁,select使用方式:

    select语句+for update
    

    3.意向共享锁:在事务对某一行加共享锁时,要先给该表加上意向共享锁。
    4.意向排它锁:在事务对某一行数据加排它锁时,必须要先给该表加上意向排它锁。
    作用是,当某一个事务需要去锁表时,不用判断每一行上是否有不兼容的锁,只需要判断有没有意向锁。如写操作锁表,判断某行数据是否存在共享锁,如果拿不到意向锁就可直接阻塞操作。
    5.自增锁:针对自增列自增长的一个特殊的表级别锁。

    锁的算法
    1.临键锁next-key:当sql语句按照范围查询,并且有数据命中的时候会给索引加上临键锁,锁住命中索引项的前一个索引到命中索引项的后一个索引之间的一个左开右闭区间。因为锁住区间,所以避免了幻读。
    2.间隙锁gap:当sql语句查找未命中时,会锁住查询条件值附近的间隙。
    3.记录锁record:使用唯一索引查询并精准匹配到数据,则只会锁住该索引项。
    这三种算法锁的原理基于b+tree索引。

    死锁
    事务并发时,每个事务都持有锁(或者是已经在等待锁),每个事务都需要再继续持有锁,然后事务之间产生加锁的循环等待,形成死锁。避免死锁:
    1)类似的业务逻辑以固定的顺序访问表和行。
    2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
    3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概 率。
    4)降低隔离级别,如果业务允许,将隔离级别调低也是较好的选择 。
    5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添 加上锁(或者说是表锁)

    MVCC多版本并发控制
    并发访问(读或写)数据库时,对正在事务内处理的数据做 多版本的管理。以达到用来避免写操作的堵塞,从而引发读操 作的并发问题。
    新增:会给行数据添加两个隐藏列,数据版本号和删除版本号。数据版本号值为插入时的事务id,删除版本号默认为null。
    删除:会给行数据的删除版本号设一个当前事务id值。
    修改:会先拷贝一份行数据,再把原先行数据的删除版本号设值,再修改拷贝的数据,并改变数据版本号值。
    查询:必须保证当前事务ID大于等于该行数据的数据版本号,并且删除版本号必须为null或者大于当前事务ID值。

    undo log
    undo log作用是保证了事务的原子性和普通select的快照读。当事务开启的时候会拷贝当前数据到undo log中,此时有其他事务中的select读取数据直接从undo log中获取,若事务回滚可根据undo log恢复原始数据。

    redo log
    redo log保证了事务的持久性。事务开启后,只要开始改变数据信息就会持续写入redo buffer中,具体落盘可以指定不同的策略。在数据库发生意外故障时,尚有修改的数据未写入磁盘,在重启mysql服务的时候,根据redo log恢复事务修改后的新数据。
    Redo buffer 持久化到Redo log的策略有三种:
    取值 0 每秒一次进行提交持久化[可能丢失一秒内 的事务数据]
    取值 1 默认值,每次事务提交执行Redo buffer --> Redo log OS cache -->flush cache to disk [最安全,性能最差的方式]
    取值 2 每次事务提交到系统缓存OS cache,再每一秒从系统缓存中执行持久化 操作

    mysql配置优化
    最大连接数配置 max_connections

    connection内存参数配置:
    sort_buffer_size connection排序缓冲区大小 建议256K(默认值)-> 2M之内 当查询语句中有需要文件排序功能时,马上为connection分配配置的内存大小。
    join_buffer_size connection关联查询缓冲区大小 建议256K(默认值)-> 1M之内 当查询语句中有关联查询时,马上分配配置大小的内存用这个关联查 询,所以有可能在一个查询语句中会分配很多个关联查询缓冲区。
    上述配置4000连接占用内存: 4000*(0.256M+0.256M) = 2G

    Innodb_buffer_pool_size缓冲池大小配置
    innodb buffer/cache的大小(默认128M)
    Innodb_buffer_pool缓冲池包含:数据缓存 索引缓存 缓冲数据 内部结构
    大的缓冲池可以减小多次磁盘I/O访问相同的表数据以提高性能
    参考计算公式: Innodb_buffer_pool_size = (总物理内存 - 系统运行所用 - connection 所用)* 90%

    其他参数配置
    wait_timeout 服务器关闭非交互连接之前等待活动的秒数
    innodb_open_files 限制Innodb能打开的表的个数
    innodb_write_io_threads innodb_read_io_threads innodb:使用后台线程处理innodb缓冲区数据页上的读写 I/O(输入输出)请求的数量
    innodb_lock_wait_timeout InnoDB事务在被回滚之前可以等待一个锁定的超时秒数

    更多相关内容
  • MySQL事务底层实现原理

    千次阅读 2020-10-15 09:33:28
    MySQL事务底层实现原理 特点: 原子性(Atomicity) 一致性(Consistency) 隔离型(Isolation) 持久性(Durability) 一、事务的目的 1、可靠性和并发处理 **可靠性:**数据库要保证当insert或update操作时抛...

    MySQL事务的底层实现原理

    特点:

    • 原子性(Atomicity)

    • 一致性(Consistency)

    • 隔离型(Isolation)

    • 持久性(Durability)

    一、事务的目的

    1、可靠性和并发处理

    **可靠性:**数据库要保证当insert或update操作时抛异常或者数据库crash的时候需要保障数据的操作前后的一致,想要做到这个,我需要知道我修改之前和修改之后的状态,所以就有了undo log和redo log。

    并发处理:也就是说当多个并发请求过来,并且其中有一个请求是对数据修改操作的时候会有影响,为了避免读到脏数据,所以需要对事务之间的读写进行隔离,至于隔离到啥程度得看业务系统的场景了,实现这个就得用MySQL 的隔离级别。

    二、实现事务功能的三个技术

    • 日志文件(redo log 和 undo log)

    • 锁技术

    • MVCC

    2.1 redo log 与 undo log介绍

    2.1.1 redo log

    什么是redo log ?

    redo log叫做重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。

    当事务提交之后会把所有修改信息都会存到该日志中。假设有个表叫做tb1(id,username) 现在要插入数据(3,ceshi)

    img

    start transaction;                                     
    select balance from bank where name="zhangsan";         
    
    // 生成 重做日志 balance=600                                 
    
    update bank set balance = balance - 400;                
    
    // 生成 重做日志 amount=400                               
    
    update finance set amount = amount + 400;          
    
    commit
    

    redo log 有什么作用?

    mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。

    那么问题来了,如果还没来的同步的时候宕机或断电了怎么办?这样会导致丢部分已提交事务的修改信息!

    所以引入了redo log来记录已成功提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后在读取redo log恢复最新数据。

    总结:redo log是用来恢复数据的 用于保障已提交事务的持久化特性。

    2.1.2undo log

    什么是 undo log ?

    undo log 叫做回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志所记录的相反,重做日志记录数据被修改后的信息。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

    每次写入数据或者修改数据之前都会把修改前的信息记录到 undo log。

    undo log 有什么作用?

    undo log 记录事务修改之前版本的数据信息,因此假如由于系统错误或者rollback操作而回滚的话可以根据undo log的信息来进行回滚到没被修改前的状态。

    总结:undo log是用来回滚数据的用于保障 未提交事务的原子性

    2.2mysql锁技术

    2.2.1 mysql锁技术

    当有多个请求来读取表中的数据时可以不采取任何操作,但是多个请求里有读请求,又有修改请求时必须有一种措施来进行并发控制。不然很有可能会造成不一致。

    读写锁

    解决上述问题很简单,只需用两种锁的组合来对读写请求进行控制即可,这两种锁被称为:

    共享锁(shared lock),又叫做"读锁"

    读锁是可以共享的,或者说多个读请求可以共享一把锁读数据,不会造成阻塞。

    排他锁(exclusive lock),又叫做"写锁"

    写锁会排斥其他所有获取锁的请求,一直阻塞,直到写入完成释放锁。

    img

    总结:通过读写锁,可以做到读读可以并行,但是不能做到写读,写写并行,事务的隔离性就是根据读写锁来实现的。

    2.3MVCC基础

    MVCC (MultiVersion Concurrency Control) 叫做多版本并发控制。

    InnoDB的 MVCC ,是通过在每行记录的后面保存两个隐藏的列来实现的。

    这两个列,一个保存了行的创建时间,一个保存了行的过期时间,当然存储的并不是实际的时间值,而是系统版本号

    他的主要实现思想是通过数据多版本来做到读写分离。从而实现不加锁读进而做到读写并行.。

    MVCC在mysql中的实现依赖的是undo log与read view

    undo log :undo log 中记录某行数据的多个版本的数据。

    read view :用来判断当前版本数据的可见性

    img

    三、事务的实现

    • 事务的原子性是通过undolog来实现的

    • 事务的持久性性是通过redolog来实现的

    • 事务的隔离性是通过(读写锁+MVCC)来实现的

    • 事务的终极大 boss 一致性是通过原子性,持久性,隔离性来实现的!!!

    原子性,持久性,隔离性的目的也是为了保障数据的一致性!

    总之,ACID只是个概念,事务最终目的是要保障数据的可靠性,一致性。

    3.1.原子性的实现

    什么是原子性:

    一个事务必须被视为不可分割的最小工作单位,一个事务中的所有操作要么全部成功提交,要么全部失败回滚,对于一个事务来说不可能只执行其中的部分操作,这就是事务的原子性。

    以上概念相信大家伙儿都了解,那么数据库是怎么实现的呢?就是通过回滚操作。所谓回滚操作就是当发生错误异常或者显式的执行rollback语句时需要把数据还原到原先的模样,所以这时候就需要用到undo log来进行回滚,接下来看一下undo log在实现事务原子性时怎么发挥作用的

    1.1 undo log 的生成

    假设有两个表 bank和finance,当进行插入,删除以及更新操作时生成的undo log。

    img

    从上图可以了解到数据的变更都伴随着回滚日志的产生:

    (1) 产生了被修改前数据(zhangsan,1000) 的回滚日志

    (2) 产生了被修改前数据(zhangsan,0) 的回滚日志

    根据上面流程可以得出如下结论:

    1.每条数据变更(insert/update/delete)操作都伴随一条undo log的生成,并且回滚日志必须先于数据持久化到磁盘上

    2.所谓的回滚就是根据回滚日志做逆向操作,比如delete的逆向操作为insert,insert的逆向操作为delete,update的逆向为update等。

    1.2 根据undo log 进行回滚

    为了做到同时成功或者失败,当系统发生错误或者执行rollback操作时需要根据undo log 进行回滚

    img

    回滚操作就是要还原到原来的状态,undo log记录了数据被修改前的信息以及新增和被删除的数据信息,根据undo log生成回滚语句,比如:

    (1) 如果在回滚日志里有新增数据记录,则生成删除该条的语句

    (2) 如果在回滚日志里有删除数据记录,则生成生成该条的语句

    (3) 如果在回滚日志里有修改数据记录,则生成修改到原先数据的语句

    3.2持久性的实现

    事务一旦提交,其所做的修改会永久保存到数据库中,此时即使系统崩溃修改的数据也不会丢失。

    先了解一下MySQL的数据存储机制,MySQL的表数据是存放在磁盘上的,因此想要存取的时候都要经历磁盘IO,然而即使是使用SSD磁盘IO也是非常消耗性能的。为此,为了提升性能InnoDB提供了缓冲池(Buffer Pool),Buffer Pool中包含了磁盘数据页的映射,可以当做缓存来使用:

    读数据:会首先从缓冲池中读取,如果缓冲池中没有,则从磁盘读取再放入缓冲池;

    写数据:会首先写入缓冲池,缓冲池中的数据会定期同步到磁盘中;

    上面这种缓冲池的措施虽然在性能方面带来了质的飞跃,但是它也带来了新的问题,当MySQL系统宕机,断电的时候可能会丢数据!!!

    因为我们的数据已经提交了,但此时是在缓冲池里头,还没来得及在磁盘持久化,所以我们急需一种机制需要存一下已提交事务的数据,为恢复数据使用。

    于是 redo log就派上用场了。下面看下redo log是什么时候产生的

    img

    既然redo log也需要存储,也涉及磁盘IO为啥还用它?

    (1)redo log 的存储是顺序存储,而缓存同步是随机操作。

    (2)缓存同步是以数据页为单位的,每次传输的数据大小大于redo log。

    3.3隔离性实现

    隔离性是事务ACID特性里最复杂的一个。在SQL标准里定义了四种隔离级别,每一种级别都规定一个事务中的修改,哪些是事务之间可见的,哪些是不可见的。

    级别越低的隔离级别可以执行越高的并发,但同时实现复杂度以及开销也越大。

    MySQL隔离级别有以下四种(级别由低到高):

    1、READUNCOMMITED(未提交读)

    2、READCOMMITED(提交读)

    3、REPEATABLEREAD(可重复读)

    4、SERIALIZABLE (可重复读)

    只要彻底理解了隔离级别以及他的实现原理就相当于理解了ACID里的隔离型。前面说过原子性,隔离性,持久性的目的都是为了要做到一致性,但隔离型跟其他两个有所区别,原子性和持久性是为了要实现数据的可性保障靠,比如要做到宕机后的恢复,以及错误后的回滚。

    那么隔离性是要做到什么呢?

    隔离性是要管理多个并发读写请求的访问顺序。 这种顺序包括串行或者是并行

    说明一点,写请求不仅仅是指insert操作,又包括update操作。

    img

    总之,从隔离性的实现可以看出这是一场数据的可靠性与性能之间的权衡:

    可靠性性高的,并发性能低(比如Serializable)。可靠性低的,并发性能高(比如 Read Uncommited)
    

    READ UNCOMMITTED

    在READ UNCOMMITTED隔离级别下,事务中的修改即使还没提交,对其他事务是可见的。事务可以读取未提交的数据,造成脏读。

    因为读不会加任何锁,所以写操作在读的过程中修改数据,所以会造成脏读。好处是可以提升并发处理性能,能做到读写并行。

    换句话说,读的操作不能排斥写请求。

    img

    优点:读写并行,性能高

    缺点:造成脏读

    READ COMMITTED

    一个事务的修改在他提交之前的所有修改,对其他事务都是不可见的。其他事务能读到已提交的修改变化。在很多场景下这种逻辑是可以接受的。

    InnoDB在 READ COMMITTED,使用排它锁,读取数据不加锁而是使用了MVCC机制。或者换句话说他采用了读写分离机制。

    但是该级别会产生不可重读以及幻读问题。

    什么是不可重读?

    在一个事务内多次读取的结果不一样。

    为什么会产生不可重复读?

    这跟 READ COMMITTED 级别下的MVCC机制有关系,在该隔离级别下每次 select的时候新生成一个版本号,所以每次select的时候读的不是一个副本而是不同的副本。

    在每次select之间有其他事务更新了我们读取的数据并提交了,那就出现了不可重复读

    REPEATABLE READ(Mysql默认隔离级别)

    在一个事务内的多次读取的结果是一样的。这种级别下可以避免,脏读,不可重复读等查询问题。mysql 有两种机制可以达到这种隔离级别的效果,分别是采用读写锁以及MVCC。

    采用读写锁实现:

    img

    为什么能可重复读?只要没释放读锁,在次读的时候还是可以读到第一次读的数据。

    优点:实现起来简单

    缺点:无法做到读写并行

    采用MVCC实现:

    为什么能可重复读?因为多次读取只生成一个版本,读到的自然是相同数据。

    优点:读写并行

    缺点:实现的复杂度高

    但是在该隔离级别下仍会存在幻读的问题,关于幻读的解决我打算另开一篇来介绍。

    SERIALIZABLE

    该隔离级别理解起来最简单,实现也最简单。在隔离级别下除了不会造成数据不一致问题,没其他优点。

    img

    img

    img

    3.4一致性的实现

    数据库总是从一个一致性的状态转移到另一个一致性的状态。

    下面举个例子,zhangsan 从银行卡转400到理财账户:

    start transaction;                                     
    
    select balance from bank where name="zhangsan";         
    
    // 生成 重做日志 balance=600                             
    
    update bank set balance = balance - 400;                
    
    // 生成 重做日志 amount=400                              
    
    update finance set amount = amount + 400;           
    
    commit;
    

    1.假如执行完 update bank set balance = balance - 400;之发生异常了,银行卡的钱也不能平白无故的减少,而是回滚到最初状态。

    2.又或者事务提交之后,缓冲池还没同步到磁盘的时候宕机了,这也是不能接受的,应该在重启的时候恢复并持久化。

    3.假如有并发事务请求的时候也应该做好事务之间的可见性问题,避免造成脏读,不可重复读,幻读等。在涉及并发的情况下往往在性能和一致性之间做平衡,做一定的取舍,所以隔离性也是对一致性的一种破坏。

    总结

    实现事务采取了哪些技术以及思想?

    原子性:使用 undo log ,从而达到回滚

    持久性:使用 redo log,从而达到故障后恢复

    隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行

    一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。

    展开全文
  • mysql事务底层原理

    2021-02-22 09:54:28
    第三层:存储引擎层,负责mysql中数据的存储和提取,mysql事务是由存储引擎实现并管理的。 mysql支持事务的存储引擎有InnoDB,NDB Cluster等,InnoDB使用最为广泛。 二、mysql事务使用方法 start transaction;#...

    一、mysql逻辑架构

    第一层:连接层,处理客户端连接,以及授权认证,安全检测。

    第二层:服务器层,负责查询语句的解析,优化,缓存以及内置函数的实现、存储过程实现等

    第三层:存储引擎层,负责mysql中数据的存储和提取,mysql事务是由存储引擎实现并管理的。

    mysql支持事务的存储引擎有InnoDB,NDB Cluster等,InnoDB使用最为广泛。

    二、mysql事务使用方法

    start transaction;#事务开始的标识
    ...#一条或多条sql语句
    commit;#提交事务
    

    当sql执行出现问题时,事务会回滚,包括执行成功的sql语句,mysql中采用自动提交模式,在自动提交模式下,每个sql语句都会被当做一个事务来执行并提交操作。

    可以关闭自动提交模式,自动提交模式是针对连接的,在连接中修改参数不会对其他连接产生影响,关闭了自动提交模式,所有的sql语句都在一个事务中执行,直到遇到commit或rollback,事务结束的同时开始另外一个事务。

    如果在事务中执行DDL语句(create table/drop table/alter table)或lock tables将会强制执行commit进行事务提交。

    三、事务ACID特性

    ACID是衡量事务的四个特性:

     原子性 (Atomicity,或称不可分割性)

    一致性  (Consistency)

    隔离性    (Isolation)

    持久性  (Durability)

    mysql的日志文件有:二进制日志,错误日志,查询日志,慢查询日志等,InnoDB存储引擎提供了两种事务日志:redo log(重做日志)和 undo log(回滚日志),redo log 用于保证事务持久性,undo log则是事务原子性和隔离性实现的基础。

    原子性:一个事务是不能分割的工作单位,要么操作全都执行,要么操作全都不执行;如果事务中一个sql语句执行失败,则已执行的语句必须回滚,数据库会回退到事务前的状态,对数据库修改时,InnoDB会生成对应的undo log,如果事务失败,可以利用undo log 存储的信息将数据回滚到修改之前的状态。undo log 属于逻辑日志,记录sql执行相关的信息,发生回滚时,存储引擎会执行相反的操作。

    一致性:是指事务执行结束后,数据库的完整约束没有被破坏,事务执行的前后都是合法的数据状态,一致性是事务追求的最终目标:原子性,持久性,隔离性都是为了保证数据库状态的一致性。

    隔离性:事务内部的操作与其他事务是隔离开来的,并发执行的各个事务之间不互相干扰,主要靠锁机制和MVCC保证隔离性,锁机制基本原理可以概括为:事务在修改数据之前,需要先获得相应的锁,获得锁后可以修改数据,其他事务如果也想操作相同的数据,就必须等待这个事务提交或回滚释放锁。锁分为行锁和表锁、间隙锁、自增锁。表锁操作数据时会锁住整张表,并发性能较差,行锁则只锁定需要操作的数据,并发性能好,间隙锁锁定的是一个范围内的数据,自增锁模式下必须等待前一个事务的插入完成后才能进行下一个事务的插入操作。但是加锁本身需要消耗资源,因此在锁定数据较多的情况下使用表锁可以节省大量资源,不同的数据库存储引擎支持的锁也不一样,InnoDB同时支持表锁和行锁,出于性能考虑,绝大多数情况都是行锁。

    并发情况下,读操作可能存在三类问题

    1)脏读:当前事务中读取到其他事务未提交的数据。

    2)不可重复读:事务读取到同一个数据,但是读取的结果不一样,与脏读不同,不可重复读读到的数据时其他事务已提交的数据。

    3)幻读:事务按照某一条件读取数据两次,两次查询得到的条数不同,与不可重复读的区别是,前者数据变化了,后者是数据的行数变化了。

    事务隔离级别有四种:

    1)Read UnCommitted 读未提交,可能会出现脏读,不可重复读,幻读问题

    2)Read Committed 读已提交,可能出现不可重复读,幻读问题

    3)Repeatable Read 可重复读,可能出现幻读问题

    4)Serializable 可串行化

    这四种隔离级别的开销依次递增,并发性能依次递减,大多数数据库系统中的隔离级别默认为读已提交或可重复读,只有对数据一致性要求较高时才会使用可串行化。

    InnoDB默认的隔离级别是可重复读,InnoDB使用MVCC多版本并发控制协议来避免脏读、不可重复读、幻读问题,MVCC的特点:在同一时刻,不同的事务读取到的数据可能是不同的,MVCC最大的优点就是读不加锁,因此读写不冲突,并发性能高。

    InnoDB实现MVCC,可以保证多个版本数据可以共存,主要基于以下技术和数据结构:

    1)隐藏列:InnoDB每行数据中都有隐藏列,隐藏列中包含本行数据的事务id、指向undo log的指针

    2)基于undo log的版本链

    3)ReadView:通过隐藏列和版本链可以让数据恢复到指定版本,具体恢复到哪个版本由ReadView来确定,判断数据的事务id大小来确定数据是否对ReadView可见。

    判断可见性的方法如下:

    1)low_limit_id:表示生成ReadView时系统中应该分配给下一个事务的id,如果数据的事务id大于等于low_limit_id,则对该ReadView不可见

    2)up_limit_id:表示生成ReadView时当前系统中活跃的读写事务中的最小事务id。如果数据的事务id小于up_limit_id,则对该ReadView可见

    3)rw_trx_ids:表示生成ReadView时当前系统中活跃的读写事务id列表。如果数据事务id在low_limit_id和up_limit_id之间,则需要判断是否在rw_trx_ids中,如果在说明生成ReadView时事务仍在活跃中,因此数据对ReadView不可见,如果不在,说明事务已提交,

    数据对ReadView可见

    1)如何解决脏读?

    当前事务读取数据前,会生成ReadView,其他事务没有提交仍然处于活跃状态,其他事务的修改对当前事务的ReadView不可见,当前事务根据指针指向的undo log查询上一版本数据,从而避免脏读

    2)如何解决不可重复读?

    当前事务在读取数据前,会生成ReadView,其他事务已开始未提交,或者是其他未开始,都会使其他事务的修改对当前事务的ReadView不可见,当前事务再次读取时根据指针指向的undo log查询上一版本数据,从而避免不可重复读

    3)如何解决幻读?

    MVCC解决幻读的机制和避免不可重复读的机制类似。

    mysql读取数据可以分为两种:

    1)非加锁读,这种情况使用MVCC避免脏读、不可重复读、幻读,保证了隔离性

    2)加锁读,在查询是使用共享锁或排它锁,保证其他事务无法对数据进行写操作,因此可以避免脏读和不可重复读,而避免幻读就需要通过next-key-lock(行锁的一种)。

    持久性:一个事务一旦提交成功,对数据库的改变是永久的,后续的数据库操作或故障不会对事务执行的结果有任何影响,InnoDB提供了缓存机制(buffer pool),可以提高数据读写的效率,buffer pool 修改的数据会定期存储到磁盘中,如果mysql宕机,buffer pool 中修改的数据没有及时刷新到磁盘中,就会导致数据丢失,为了保证数据不丢失和事务的持久性,引入redo log记录下修改buffer pool数据的操作,redo log 是预写式日志,会将所有的修改操写入到日志中。当mysql宕机时重启时,调用fsync接口将redo log中的数据修改操作重新更新到buffer pool 中,这样保证了数据不会在mysql宕机时丢失,从而保证数据持久性要求。事务提交时,redo log也会写入到磁盘中,速度比buffer pool快,因为redo log写入磁盘是追加操作,属于顺序IO,而buffer pool  存储数据则是以数据页为单位(数据页大小为16KB)写入,写入位置也是随机的,而且是整页写入,redo log 只写入真实的部分,无效IO占比不多。

    总结:

    原子性:语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;实现主要基于undo log

    一致性:事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障

    隔离性:保证事务执行尽可能不受其他事务影响;InnoDB默认的隔离级别是RR,RR的实现主要基于锁机制(包含next-key lock)、MVCC(包括数据的隐藏列、基于undo log的版本链、ReadView)

    持久性:保证事务提交后不会因为宕机等原因导致数据丢失;实现主要基于redo log

    展开全文
  • 为了保证事务操作的原子性,必须实现基于日志的REDO/UNDO机制:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯...

    一、ACID特性

    持久性,我们就不讲了,易懂。

    1、原子性

    在同一个事务内部的一组操作必须全部执行成功(或者全部失败)。

    为了保证事务操作的原子性,必须实现基于日志的REDO/UNDO机制:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯日志,将已经执行成功的操作撤销,从而达到“全部操作失败”的目的。 最常见的场景是,数据库系统崩溃后重启,此时数据库处于不一致的状态,必须先执行一个crash recovery的过程:读取日志进行REDO(重演将所有已经执行成功但尚未写入到磁盘的操作,保证持久性),再对所有到崩溃时尚未成功提交的事务进行UNDO(撤销所有执行了一部分但尚未提交的操作,保证原子性)。crash recovery结束后,数据库恢复到一致性状态,可以继续被使用。

    某个应用在执行转帐的数据库操作时,必须在同一个事务内部调用对帐户A和帐户B的操作,才能保证数据的一致性。

    但是,原子性并不能完全保证一致性。在多个事务并行进行的情况下,即使保证了每一个事务的原子性,仍然可能导致数据不一致的结果。 例如,事务1需要将100元转入帐号A:先读取帐号A的值,然后在这个值上加上100。但是,在这两个操作之间,另一个事务2修改了帐号A的值,为它增加了100元。那么最后的结果应该是A增加了200元。但事实上,事务1最终完成后,帐号A只增加了100元,因为事务2的修改结果被事务1覆盖掉了。

    简而言之,就是:原子性仅能够保证单个事务的一致性。就像redis一样,也只能保证单操作的线程安全,并不能保证多操作下的线程安全。

    2、一致性

    按照我个人的理解,在事务处理的ACID属性中,一致性是最基本的属性,其它的三个属性都为了保证一致性而存在的。

    我们举个反例来理解下一致性概念。例如:从帐户A转一笔钱到帐户B上,如果帐户A上的钱减少了,而帐户B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。

    为了保证并发情况下的一致性,引入了隔离性,即保证每一个事务能够看到的数据总是一致的,就好象其它并发事务并不存在一样。

    3、隔离性

    数据库四种隔离级别,以及常见的几种读异常,大家应该都是耳熟能详的,但数据库底层是怎么实现隔离性的呢?都采用了哪些技术呢? 主要有两个技术:MVCC(多版本并发控制)和锁。

    (1)MVCC(多版本并发控制)

    多版本并发控制,顾名思义,在并发访问的时候,数据存在版本的概念,可以有效地提升数据库并发能力,常见的数据库如MySQL、MS SQL Server、IBM DB2、Hbase、MongoDB等等都在使用。

    简单讲,如果没有MVCC,当想要读取的数据被其他事务用排它锁锁住时,只能互斥等待;而这时MVCC可以通过提供历史版本从而实现读取被锁的数据的历史版本,从而避免了互斥等待。

    InnoDB采用的MVCC实现方式是:在需要时,通过undo日志构造出历史版本。

    (2)锁

    1) 锁的分类

    • Shared Locks(共享锁/S锁)

    若事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

    • Exclusive Locks(排它锁/X锁)

    若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。在更新操作(INSERT、UPDATE 或 DELETE)过程中始终应用排它锁。

    注意:排他锁会阻止其它事务再对其锁定的数据加读或写的锁,但是不加锁的就没办法控制了。

    • Record Locks(行锁)

    行锁,顾名思义,是加在索引行(对!是索引行!不是数据行!)上的锁。比如select * from user where id=1 and id=10 for update,就会在id=1id=10的索引行上加Record Lock。

    • Gap Locks(间隙锁)

    间隙锁,它会锁住两个索引之间的区域。比如select * from user where id>1 and id<10 for update,就会在id为(1,10)的索引区间上加Gap Lock。

    • Next-Key Locks(间隙锁)

    也叫间隙锁,它是Record Lock + Gap Lock形成的一个闭区间锁。比如select * from user where id>=1 and id<=10 for update,就会在id为[1,10]的索引闭区间上加Next-Key Lock。

    这样组合起来就有,行级共享锁,表级共享锁,行级排它锁,表级排它锁。

    2) 什么时候会加锁?

    在数据库增删改查四种操作中,insert、delete和update都是会加排它锁(Exclusive Locks)的,而select只有显式声明才会加锁:

    • select: 即最常用的查询,是不加任何锁的
    • select ... lock in share mode: 会加共享锁(Shared Locks)
    • select ... for update: 会加排它锁

    3) 四种隔离级别

    不同的隔离级别是在数据可靠性和并发性之间的均衡取舍,隔离级别越高,对应的并发性能越差,数据越安全可靠。

    • READ UNCOMMITTED

    顾名思义,事务之间可以读取彼此未提交的数据。机智如你会记得,在前文有说到所有写操作都会加排它锁,那还怎么读未提交呢?

    机智如你,前面我们介绍排它锁的时候,有这种说明: 排他锁会阻止其它事务再对其锁定的数据加读或写的锁,但是对不加锁的读就不起作用了。

    READ UNCOMMITTED隔离级别下, 读不会加任何锁。而写会加排他锁,并到事务结束之后释放。

    实例1:

    查-写:查并没有阻止写,表明查肯定并没有加锁,要不写肯定就阻塞了。写很明显,会加排它锁的。

    实例2: 写-写:阻塞,表明,写会加排它锁。

    • READ COMMITTED

    顾名思义,事务之间可以读取彼此已提交的数据。

    InnoDB在该隔离级别(READ COMMITTED)写数据时,使用排它锁, 读取数据不加锁而是使用了MVCC机制。

    因此,在读已提交的级别下,都会通过MVCC获取当前数据的最新快照,不加任何锁,也无视任何锁(因为历史数据是构造出来的,身上不可能有锁)。

    但是,该级别下还是遗留了不可重复读和幻读问题: MVCC版本的生成时机: 是每次select时。这就意味着,如果我们在事务A中执行多次的select,在每次select之间有其他事务更新了我们读取的数据并提交了,那就出现了不可重复读,即:重复读时,会出现数据不一致问题,后面我们会讲解超支现象,就是这种引起的。

    • REPEATABLE READ

    READ COMMITTED级别不同的是MVCC版本的生成时机,即:一次事务中只在第一次select时生成版本,后续的查询都是在这个版本上进行,从而实现了可重复读

    但是因为MVCC的快照只对读操作有效,对写操作无效,举例说明会更清晰一点: 事务A依次执行如下3条sql,事务B在语句1和2之间,插入10条age=20的记录,事务A就幻读了。

    1. select count(1) from user where age=20;
    -- return 0: 当前没有age=20的
    2. update user set name=test where age=20;
    -- Affects 10 rows: 因为事务B刚写入10条age=20的记录,而写操作是不受MVCC影响,能看到最新数据的,所以更新成功,而一旦操作成功,这些被操作的数据就会对当前事务可见
    3. select count(1) from user where age=20;
    -- return 10: 出现幻读
    

    REPEATABLE READ级别,可以防止大部分的幻读,但像前边举例读-写-读的情况,使用不加锁的select依然会幻读。

    • SERIALISABLE

    大杀器,该级别下,会自动将所有普通select转化为select ... lock in share mode执行,即针对同一数据的所有读写都变成互斥的了,可靠性大大提高,并发性大大降低。

    读-写,写-写均互斥。

    4)总结:几类读异常

    读-写-读,引起的异常

    • 脏读:读取了脏数据(不存在的数据)。 事务一读 事务二写(未提交) 事务二读(脏数据) 事务二回滚

    • 不可重复读:既可以读取修改的数据,也可以读取新增的数据(幻读)。 事务一读 事务二写(更新已提交) 事务二读(数据不一致,不可重复读)

    • 幻读:仅可以读取新增的数据,但是无法读取修改的数据; 事务一读 事务二写(新增已提交) 事务二读(数据不一致,幻读)

    • 附命令

    查看表的加锁情况: select * from information_schema.INNODB_LOCKS; 事务状态 select * from information_schema.INNODB_TRX;

    展开全文
  • 一:MySQL底层事务实现原理
  • 二:隔离级别的底层实现 首先需要说明一下mysql更新数据时的版本链设计。 在操作一条数据时,mysql会在日志文件中生成一条对应的版本记录(即使事务未提交,也会生成记录)。该条记录除了含有当前的最新数据外,还...
  • MySQL事务实现原理

    千次阅读 2020-01-14 20:24:42
    点击上方“Java之间”,选择“置顶或者星标”你关注的就是我关心的!来源:rrd.me/fDv2c上一篇:不耍流氓,有答案的Zookeeper面试题开篇相信大家都用过事务以及了解他的特点...
  • 深入理解spring事务底层实现原理

    万次阅读 多人点赞 2018-11-04 14:43:27
    事务 相信大家都在ATM机取过钱,但是是否有人考虑过它的流程是怎样的呢? 我们都知道,假如我们取300块钱,那么当这三百块钱从ATM机出来时,我们的账户相应的会减少300。这两个过程一定是要同时成功才算成功的。...
  • MySql事务简介 数据库事务是指一组sql语句组成的数据库逻辑单元,在这组的sql操作中,要么全部执行成功,要么全部执行失败; 例如:转账,事务A中要进行转账,那么转出的账号要扣钱,转入的账号要加钱,这两个操作...
  • MySQL innoDB底层基础原理 前言:由于正在准备之后的实习面试,故总结了一部分golang语言基础的问题,回答全为自己组织的语言,若有错各位大佬可及时指出,大家共同进步,谢谢。 1.innoDB索引实现原理 innoDB存储...
  • mysql数据库底层原理

    千次阅读 2020-05-18 21:47:19
    一、MySQL 的基本定义: A.术语介绍 1. 数据库(Database) 是按照数据结构来组织、存储和管理数据的仓库。 每个数据库都有一个或多个不同的API用于创建、访问、管理、搜索和复制所保存的数据。 2. RDBMS...
  • MySQL 中 ACID 底层内部实现原理详解

    千次阅读 2020-03-24 08:22:04
    “ACID 事务底层内部实现原理”Hello,大家好。我是公众号“八点半技术站”的小编-Bruce.D。今天是周二(2020-03-24),还是那句俗...
  • 浅析Innodb事务底层实现原理

    千次阅读 2019-07-11 16:42:57
    如果对事务的基本知识不是太了解,可以先去看这篇文章【MySQL事务知识小结 MySQL只有innodb引擎支持事务,因此这篇文章也是以innodb为背景写的。 一、事务并发会带来什么问题? 脏读:读到了其他事务未提交的...
  • mysql索引底层原理分析

    万次阅读 多人点赞 2018-09-23 00:01:40
    大家都知道索引的重要性,基本用法在上章《最全面的mysql索引知识大盘点》已分享过,本章主要是探索索引的底层实现原理。当然了,我们还是以mysql为基准进行探讨。 目录 前言:innodb和myisam的区别 1.物理磁盘...
  • MySQL底层原理综述

    万次阅读 多人点赞 2019-02-02 23:17:17
    默认MySQL中自动提交时开启的(start transaction)事务的ACID特性如下: 原子性:事务中的所有操作要么全部提交成功,要么全部失败回滚。场景:UPDATE cs_user SET age = 18 , gender = '女' WHERE id ...
  • spring事务底层掌握其实现原理

    千次阅读 2019-09-16 13:55:13
    众所周知,Spring事务采用AOP的方式实现,我们从TransactionAspectSupport这个类开始分析。 获取事务的属性(@Transactional注解中的配置) 加载配置中的TransactionManager. 获取收集事务信息TransactionInfo 执行...
  • 由于Mysql 默认的隔离级别是Repeatable Read、所以本篇文章的部分结论是基于RR隔离级别得出 事务具有四大特性 ACID: 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability) ...
  • MySQL MVCC底层原理详解

    万次阅读 多人点赞 2020-07-25 05:46:44
    MySQL MVCC底层原理详解
  • mysql查询底层原理及join的底层分析

    千次阅读 2020-05-23 16:17:32
    mysql查询底层原理
  • 数据库底层实现原理

    千次阅读 2021-06-15 16:45:58
    备受互联网公司的喜爱,在做项目的时候,大部分接触的也都是基于Mysql作为底层数据的存储,CRUD用的比较多,稍微复杂一点就是多条查询,各种内外连接以及group by操作,对于Mysql数据库原理特性以及底层机制的研究...
  • mysql隔离级别实现原理

    千次阅读 2020-10-20 21:58:48
    (一)事务四大属性 分别是原子性、一致性、隔离性、持久性。 1、原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果...
  • 文章目录1. 什么是mysql?2. mysql存储引擎2.1 mysql存储引擎有哪些2.2 各存储引擎的区别3.... mysql事务日志5.1 redo log和undo log5.2 undo日志实现记录版本线性表(链表)6. Read View(读视图)6.1 什么是Read View
  • MySQL系列之事务日志Undo log学习笔记

    千次阅读 多人点赞 2020-10-15 14:59:14
    undo log是mysql中两种比较重要的事务日志,另外一种是redo log,undo log顾名思义,是一种用于撤销回退的日志,用于事务没提交之前,会将要修改的记录存放到 Undo 日志文件里,当事务回滚时或者数据库崩溃时,可以...
  • 【JavaP6大纲】MySQL篇:四大属性底层实现原理

    千次阅读 多人点赞 2021-04-07 13:55:44
    底层实现原理:undo log(当这个事务对数据库进行修改的时候,innodb 生成对应 undo log,他会记录这个 SQL 执行的相关信息,如果 SQL 执行失败发生这个回滚,innodb 根据这个 undo log 内容去做相反的工作,比如说...
  • 本文主要讲解并发事务底层实现原理

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,089
精华内容 18,835
关键字:

mysql事务底层实现原理

mysql 订阅
友情链接: i2c-powermac.rar