万网如何建设网站,深圳市网络公司,阜宁网站建设公司,导购返利网站开发----以MySQL的InnoDB介绍 目录 前言事务#xff0c;事务到底是什么#xff1f; 一、事务的特征#xff1a;二、事务特征具体保证1、Redo Log(重做日志) ---保证事务的持久性1.1、#x1f7e1;刷盘时机1.2、redo log记录形式1.3、redo log日志的好处 2、undo log(回滚日志)…----以MySQL的InnoDB介绍 目录 前言事务事务到底是什么 一、事务的特征二、事务特征具体保证1、Redo Log(重做日志) ---保证事务的持久性1.1、刷盘时机1.2、redo log记录形式1.3、redo log日志的好处 2、undo log(回滚日志)---保证事物的原子性2.1、undo log格式 3、MVCC(多版本并发控制)---保证事务的隔离性3.1、隔离级别问题概述 3.2、MVCC概述3.3、ReadView3.4、undo log 版本链3.5、举例演示引用 前言
事务事务到底是什么
事务其实就是以一组访问或者更新数据库的各种数据项的一个执行单元可以是一条也可以是多条执行操作。
在针对查询时只要是在本事务内执行的查询操作不管数据库被如何被其他事务修改了只要本事务内没修改事务内查询的结果都会返回一致的结果.针对修改操作时不管多少条修改语句在执行过程中会出现三种情况执行成功提交事务数据库全部修改成功系统中断全部回滚全部回退到事务开启之前的结果一条执行失败全部回滚全部回退到事务开启之前的结果可以通过设置事务保存点让只回退到指定保存点可以避免语句过多时全部回退 理论上说事务有着极其严格的定义它必须同时满足四个特性即通常所说的事务的ACID特性。值得注意的是虽然理论上定义了严格的事务要求但是数据库厂商出于各种目的并没有严格去满足事务的ACID标准。例如对于MySQL的NDB Cluster引擎来说虽然其支持事务但是不满足D的要求即持久性的要求。对于Oracle数据库来说其默认的事务隔离级别为READ COMMITTED不满足I的要求即隔离性的要求。虽然在大多数的情况下这并不会导致严重的结果甚至可能还会带来性能的提升但是用户首先需要知道严谨的事务标准并在实际的生产应用中避免可能存在的潜在问题。对于InnoDB存储引擎而言其默认的事务隔离级别为READ REPEATABLE完全遵循和满足事务的ACID特性。 一、事务的特征
事务四个典型特性即ACID原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Durability
原子性 事务作为一个整体被执行包含在其中的对数据库的操作要么全部都执行要么都不执行。 ——原子性的实现主要是通过undo log日志进行实现的如果事务中一个SQL语句执行失败则已执行的语句也必须回滚数据库退回到事务前的状态。事务的原子性表明事务就是一个整体当事务无法成功执行的时候需要将事务中已经执行过的语句全部回滚使得数据库回归到最初未开始事务的状态。 一致性 指在事务开始之前和事务结束以后数据不会被破坏假如A账户给B账户转10块钱不管成功与否A和B的总金额是不变的。 ——其他三个特性保证的数据的一致性。换句话说ACID里的AID都是数据库的特征也就是依赖数据库的具体实现。而唯独这个C实际上它依赖于应用层也就是依赖于开发者。这里的一致性是指系统从一个正确的状态迁移到另一个正确的状态。什么叫正确的状态呢就是当前的状态满足预定的约束就叫做正确的状态。而事务具备ACID里C的特性是说通过事务的AID来保证我们的一致性。 隔离性 多个事务并发访问时事务之间是相互隔离的一个事务不应该被其他事务干扰多个并发事务之间要相互隔离。 ——隔离性主要通过锁机制进行保证事务之间的隔离是通过锁机制实现的。当一个事务需要对数据库中的某行数据进行修改时需要先给数据加锁。加了锁的数据其它事务是不运行操作的只能等待当前事务提交或回滚将锁释放。通过悲观锁、乐观锁MVCC 持久性 表示事务完成提交后该事务对数据库所作的操作更改将持久地保存在数据库之中。 ——redo log是实现持久性的关键通过redo log一直写入磁盘保证数据的持久性
二、事务特征具体保证
1、Redo Log(重做日志) —保证事务的持久性
物理级别——只针对InnoDB
重做日志用来实现事务的持久性即事务ACID中的D。其由两部分组成一是内存中的重做日志缓冲redo log buffer其是易失的二是重做日志文件redo log file其是持久的而通过执行操作时一直往redo log buffer中记录数据然后再写入到redo log file中去使得数据库可以在数据丢失或系统崩溃时通过重新执行日志中的操作来还原数据从而保证数据的持久性。
因为在计算机操作系统中用户空间(user space)下的缓冲区数据一般情况下是无法直接写入磁盘的中间必须经过操作系统内核空间(kernel space)缓冲区(OS Buffer)。因此redo log buffer写入redo log file实际上是先写入OS Buffer然后再通过系统调用fsync()将其刷到redo log file中因此redo log file包括写入的数据和写入的位置。过程如下
1.1、刷盘时机
理想情况下事务一提交就会进行刷盘操作但是实际上是刷盘的时机是根据策略来决定的。
InnoDB存储引擎为redo log的刷盘策略提供了innodb_flush_log_at_trx_commit参数它支持三种策略
0设置为0的时候事务提交时不会将redo log buffer(缓存)中日志写入到os buffer而是每秒写入os buffer(page cache)并调用fsync()写入到redo log file(文件)中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的 当mysql服务崩溃时会丢失1秒钟的数据。 1设置为1的时候事务每次提交都会将redo log buffer中的日志写入os buffer并调用fsync()刷到redo log file中 这种方式即使系统崩溃也不会丢失任何数据但是因为每次提交都写入磁盘1O的性能较差。 2设置为2的时候每次提交事务时都只把redo log buffer写入os buffer然后是每秒调用fsync()将os buffer中的日志写入到redo log file这就是说如果mysql挂了 因为事务一提交已经写到系统缓存了所以不会出现数据丢失但如果操作系统挂了的话就会出现1秒的数据丢失 每1秒写入是通过Innodb存储引擎后台的一个线程进行实现的他会每秒去把redo log buffer中的内容写入到文件系统缓存os buffer然后调用fsync刷盘 1.2、redo log记录形式
redo log是固定大小的所以只能循环写从头开始写写到末尾就又回到开头相当于一个环形。当日志写满了就需要对旧的记录进行擦除但在擦除之前需要确保这些要被擦除记录对应在内存中的数据页都已经刷到磁盘中了。在redo log满了到擦除旧记录腾出新空间这段期间是不能再接收新的更新请求所以有可能会导致MySQL卡顿。所以针对并发量大的系统适当设置redo log的文件大小非常重要
1.3、redo log日志的好处
redo日志降低了刷盘频率redo日志占用的空间非常小
存储表空间ID、页号、偏移量以及更新的值所需的存储空间是很小的刷盘快。 特点 redo日志是顺序写入磁盘的 在执行事物的过程中每执行一条语句就可能产生若千条redo日志这些日志是按照产生的顺序写入磁盘的也就是顺序IO效率比直接写表的随机IO快 事务执行过程中redo log不断记录 redo log跟bin log的区别redo log是存储引擎层产生的而bin log是数据库层产生的。假设一个事务对表做10万行的记录插入在这个过程中一直不断的往redo log顺序记录而bin log不会记录直到这个事务提交才一次写入bin log文件中。 2、undo log(回滚日志)—保证事物的原子性
逻辑级别——只针对InnoDB
重做日志记录了事务的行为可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作这时就需要undo。因此在对数据库进行修改时InnoDB存储引擎不但会产生redo还会产生一定量的undo。这样如果用户执行的事务或语句由于某种原因失败了又或者用户用一条ROLLBACK 语句请求回滚就可以利用这些undo信息将数据回滚到修改之前的样子。
undo是逻辑日志因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了但是数据结构和页本身在回滚之后可能大不相同。这是因为在多用户并发系统中可能会有数十、数百甚至数千个并发事务。数据库的主要任务就是协调对数据记录的并发访问。比如一个事务在修改 当前一个页中某几条记录同时还有别的事务在对同一个页中另几条记录进行修改。因此不能将一个页回滚到事务开始的样子因为这样会影响其他事务正在进行的工作也就是会把原来执行的操作撤销而不是单纯的回退到之前那个时间节点去。
事务提交后并不能马上删除undo log及undo log所在的页。这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本。故事务提交时将undo log放入一个链表中是否可以最终删除undo log及undo log所在页由purge线程来判断。 存放位置 undo存放在数据库内部的一个特殊段segment中这个段称为undo段undosegment。undo段位于共享表空间内。可以通py_innodb_page_info.py工具来查看当前共享表空间中undo的数量。 2.1、undo log格式 insert undo log insert undo log是指在insert操作中产生的undo log, 仅用于事务回滚. 因为insert操作的记录, 只对事务本身可见, 对其它事务不可见, 所以该日志可以在事务commit后直接删除. 不需要进行purge(后台清除线程)操作 update undo log update undo log是对delete和update操作产生的的undo log. 该undo log可能需要提供MVCC机制, 因此不能在事务commit后就进行删除等待purge线程(后台清除线程)进行最后的删除
3、MVCC(多版本并发控制)—保证事务的隔离性
3.1、隔离级别
先了解MVCC之前我们得先知道事务的隔离级别才好知道方便是如何解决事务的隔离性问题的 在mysql中事务一共分为了四个隔离级别并且分别可以依次排除掉不同的问题
隔离级别允许脏读允许不可重复读允许幻影读并发性性能READ UNCOMMITTED(未提交读)是是是高高READ COMMITTED(已提交读)否是是中中REPEATABLE READ(可重复读)否否是(InnoDB不会出现)中中SERIALIZABLE(串行化)否否否低低
问题概述 脏读 脏读指的是读到了其他事务未提交的数据未提交意味着这些数据可能会回滚也就是可能最终不会存到数据库中也就是不存在的数据。读到了并一定最终存在的数据这就是脏读。 解决方案 通过把隔离级别设置为READ COMMITTED实现在读取数据时避免了去读取未提交的事务的数据有效的解决了脏读的问题。 不可重复读 对比可重复读不可重复读指的是在同一事务内不同的时刻读到的同一批数据可能是不一样的可能会受到其他事务的影响比如其他事务改了这批数据并提交了。通常针对数据更新UPDATE操作。 解决方案 通过把隔离级别设置为REPEATABLE READ通过mvcc具体实现为每个事务创建一个数据快照并在事务提交时释放旧的数据快照从而解决了不可重复读的问题。这确保了事务之间的隔离性和数据一致性使得每个事务都可以在不受其他事务干扰的情况下进行读取和修改数据。 幻影读 幻读是针对数据**插入INSERT**操作来说的。假设事务A对某些行的内容作了更改但是还未提交此时事务B插入了与事务A更改前的记录相同的记录行并且在事务A提交之前先提交了而这时在事务A中查询会发现好像刚刚的更改对于某些数据未起作用但其实是事务B刚插入进来的让用户感觉很魔幻感觉出现了幻觉这就叫幻读。 解决方案 在InnoDB中把隔离级别设置为REPEATABLE READ也会解决掉幻影读的问题在生成快照的基础上事务会对读取的数据通过加锁的方式以防止其他事务对这些行进行修改。这意味着当一个事务正在读取某一行时其他事务不能修改该行从而避免了幻读问题。而如果是范围查询时也会通过加间隙锁(Next-Key Locking)的方式对查询范围内的间隙不存在的数据进行锁定以防止其他事务在该范围内插入新数据从而避免了幻读。如果是SERIALIZABLE则是每个事务对一个数据进行查询等操作时其他的事务不会进行等待等待该事务执行完后在执行以串行的方式执行操作. 在数据库中读取的方式主要有两种方式 当前读官方叫做Locking Reads锁定读取,读取数据的最新版本. 常见的update/insert/delete、还有 select … for update、select … lock in share mode 都是当前读.快照读官方叫做 Consistent Nonlocking Reads一致性非锁定读取, 也就是 MVCC 生成的 ReadView, 用于普通的 select 的语句. 3.2、MVCC概述
MVCC的工作原理是使用数据在某个时间点的快照来实现的。这意味着无论事务运行多长时间都可以看到数据的一致视图实现方式主要是通过ReadView 和undo log版本链实现。也意味着不同的事务可以在同一时间看到同一张表中的不同数据如果你之前没有这方面的概念这句话听起来可能有点让人迷惑熟悉了以后你会发现还是很容易理解的
3.3、ReadView
ReadView 的主要作用是为每个事务提供一致性的数据视图—-该视图存储的主要是readview[m_ids], m_low_limit_id 格式存放m_ids存放的是事务id的list集合
读以提交和可重复读的时候都提到了一个词叫做快照学名叫做一致性视图说的就是ReadView这也是可重复读和不可重复读的关键可重复读是在事务开始执行第一次查询的时候生成一个当前事务全局性的快照而读提交则是每次执行语句的时候都重新生成一次快照。
它主要包括
m_ids当前有哪些事务正在执行且还没提交这些事务的id就会存在这里 min_trx_id**m_up_limit_id**是指 m_ids 里最小的值(当前未提交事务列表中的第一个) max_trx _idm_low_limit_id是指下一个要生成的事务 id。下一个要生成的事务 id 肯定比现在所有事务的 id 都大creator_trx_id每开启一个事务都会生成一个 ReadView而 creator_trx_id 就是这个开启的事务的 id。
对于一个快照来说它能够读到那些版本数据要遵循以下规则
当前事务内的更新可以读到版本未提交不能读到版本已提交但是却在快照创建后提交的不能读到版本已提交且是在快照创建前提交的可以读到
3.4、undo log 版本链 undo log 版本链是基于 undo log 实现的。MySQL可从 undo log 日志中读取到原插入、修改、删除之前的值最终把值重新变回去这就是回滚操作。 undo log 中主要保存了数据的基本信息比如说日志开始的位置、结束的位置主键的长度、表id日志编号、日志类型 undo log 还包含两个隐藏字段 trx_id事务id 和 roll_pointer回滚指针。trx_id 表示当前这个事务的 idMySQL 会为每个事务分配一个 id这个 id 是递增的。roll_pointer 是一个指针指向这个事务之前的 undo log。 undo log日志只有在更新聚集索引记录时才写undo log 举例每当一个事务修改了数据行他执行的修改记录会先保持到undo buffer中然后在持久化到磁盘的undo log文件中直到该事务提交并且该事务的undo log没有被别的事物引用到当一个新的事物对该数据进行操作时就会生成一个快照引用 多个日志会根据生成时间组成一条undo log链表 purge线程 purge 线程是一个周期运行的垃圾收集线程 对于没有事务引用的undo log进行清除, 但当purge线程发现undo log没有事务引用时将自动清除 因为insert undo log在事务完成时直接删除 所以需要清除的都是update undo log, 3.5、举例演示
实现在InnoDB下的repeatable read隔离级别下ReadView 和undo log如何
事务100 对user表执行 insert 提交
说明这步就是为了构建原始记录
begin;
insert into user(id,name) values(1,张三);
commit;然后, 事务101 对user表执行 update 提交同时, 事务102 在事务101提交前, 查询了该记录说明有事务使用的undo log, purge线程不会清除这条记录
begin;
update user set name 李四 where id 1;commit;begin;
-- 读到的name为张三
select * from user where id 1;然后事务103 对user表执行 update 未提交同时事务102 再次读取: 在RR级别, 后面所做的更改依然不可见同时, 事务104 执行了查询, 读取到了事务101提交的“李四”因为事务103尚未提交然后事务103提交 然后, 事务105来了, 它读取到的却是“王五”然后三个查询等都提交由此发现3个事务分别看到了3个版本
引用
https://zhuanlan.zhihu.com/p/190886874 https://blog.csdn.net/scm_2008/article/details/127985117 MySQL技术内幕InnoDB存储引擎(第2版)_姜承尧