mysql事务底层原理

一、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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值