暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Mysql中InnoDB学习总结

加耀 2019-08-01
265


       InnoDB是MySQL里最为常用的一种存储引擎,主要面向在线事务处理(OLTP)的应用。在学习InnoDB前我们需要先学习和理解数据结构B树,理解B树后我们才能更好的理解事务、理解锁等;

 

       在前面的学习中,我们已经知道mysql中事务的隔离性大部分都是通过锁来实现的,而锁又是及时索引来实现的,而索引又是基于B+树结构来实现的;那么,先抛出一个问题:mysql是如何解决事务原子性、一致性问题的?

 

       在开启新的学习前,我们先来对前面的知识点进行整理和总结一下;

 

1、事务的四大特征?

  1. 原子性:事务是一个原子操作,是一个不可再切割的最小单元,由一系列动作组成。事务的原子性确保动作要么全部执行,要么全部不执行;

  2. 一致性:事务前后数据保持一致,当事务回滚数据数据和原来的数据是一致的;

  3. 隔离性:事务与事务之间互不干扰,一个事务必须与另外一个事务的执行结果隔离开来;

  4. 持久性:执行成功,持久化数据;当插入一条数据,磁盘中必定会生成这条数据的记录;

 

2、事务的隔离级别有几种?

  1. 脏读

  2. 不可重复读

  3. 可重复读

  4. 串行化

 

3、每个事务隔离级别分别解决了什么问题?

  1. 脏读没有解决问题

  2. 不可重复读解决了一个事务读取到另一个事务未提交的数据的问题,即脏读问题;如:在一个事务范围内两次查询返回了两个不同的数据,读取到了另外一个事务未提交的数据;不可重复读对应的是修改;

  3. 可重复读解决了不可重复读的问题,即在一个事务中查询到了另一个事务已提交的数据;如:在一个事务范围内两次查询返回了两个不同的数据,读取到另外的事务已提交的数据;可重复读针对的是插入;

  4. 串行化解决了可重复读的问题,即使用读锁与写锁来达到不共存而实现;

 

4、为什么要加锁?

              使用锁从而实现了事务的隔离,从而保证了事务的隔离性;

 

       5、锁是基于什么来实现的?

              锁是基于索引来实现的,锁记录其实就是锁索引;在mysql中的索引是基于B+树来实现的,Oracle是基于B*树来实现的;

 

       6、频繁加锁会来带什么负面影响?

              在数据库中锁可以分为写锁和读锁两种,只有读锁与读锁可以并存;读数据的时候无法进行修改操作,修改数据的时候无法及时读取,极大的降低了数据库的性能;

 

       7、mylsq中是如何解决加锁后的性能问题的?

              在mysql的InnoDB引擎中是使用的一种MVCC的思想来实现的,MVCC思想其实就是一种多版本控制,通过对数据库记录进行多版本控制从而实现读取数据的时候可以修改,修改数据的时候允许读取,从而极大的提升了数据库性能;

 

 

     MVCC实现原理

InnoDB中MVCC是通过多版本控制来实现读取和写入分开,从而实现读的时候可以写,写的时候可以读,这样可以不用加锁,从而提高数据库性能;MVCC只在已提交读(Read Committed)和可重复读(Repeatable Read)两个隔离级别下工作

 

在MVCC中有几个核心知识点,那就是

 

       事务版本号:每次事务开启前都会从数据库获得一个自增长的事务ID,可以从事务ID判断事务的执行先后顺序。

 

       表的隐藏列:在表的隐藏列中最有关键的主要有以下三个字段,依次是DB_TRX_ID(操作该数据事务的事务id)、DB_ROLL_PTR(指向上一版本数据在undo log中的位置指针)、DB_ROW_ID(当表没有创建合适的索引作为聚集索引时,会使用该字段创建聚集索引);

 

       Read logRedo log 主要用于记录事务的日志信息,开启一个事务时,会记录一个日志序列号,当事务执行时会向日志缓冲(redo buffer)插入事务日志,并且在事务提交前会把redo buffer中的日志信息记录到磁盘中。因为事务commit后修改的数据并不会马上就回写到磁盘,而如果在数据回写磁盘的过程中数据库宕机了,那么可以通过redo log的日志信息对数据库数据进行重做,从而达到数据的一致性和持久性;

 

       Undo logUndo log 主要用于记录数据被修改之前的日志,在表信息修改之前先会把数据拷贝到undo log 里,当事务进行回滚时可以通过undo log 里的日志进行数据还原。保证事务进行rollback时的原子性和一致性,当事务进行回滚的时候可以用undo log的数据进行恢复。在MVCC多版本控制中,通过读取undo log的历史版本数据可以实现不同事务版本号都拥有自己独立的快照数据版本。  

 

       Read view在innodb 中每个SQL语句执行前都会得到一个read_view。副本主要保存了当前数据库系统中正处于活跃(没有commit)的事务的ID号,其实就是说这个副本中保存的是系统中当前不应该被本事务看到的其他事务id列表。

 

 

Read View返回数据条件

    1、数据事务ID小于up_limit_id显示

如果数据事务ID小于活跃事务的最小ID,则说明该数据是在所有活跃事务开启之前就已经存在的,可以显示。

 

    2、数据事务ID大于low_limit_id不显示

如果数据事务ID大于活跃事务的最大ID,则说明该数据是在所有活跃事务开始之后才创建的,所以数据不予显示。

 

    3、数据事务ID 大于up_limit_id并且小于low_limit_id

用数据事务ID 与trx_ids 集合中的事务ID进行匹配,如果事务ID不存在于活跃事务ID集合,或者数据事务ID等于creator_trx_id(这说明该数据就是当前事务修改的),满足上面的条件则可以显示。

 

    4、不满足read view条件时候,从undolog里面获取

当数据的事务ID不满足read view条件时候,从undolog里面获取数据的历史版本,然后数据历史版本事务号回头再来和read view 条件匹配 ,直到找到一条满足条件的历史数据,或者找不到则返回空结果;

 

数据库隐藏字段

 

       假设我们数据库中user表中有这样一条记录,当我们对这一条记录进行更改时会有如下流程:

  • 对表user执行update user set name = ‘李四’ where id = ‘1’ ; 会经过哪些过程呢?

  • 首先,开启一个新的事务,当前新的事务版本号在上一版本号基础上自增,也就是DB_TRX_ID = 12;

  • 然后将事务版本号为11的数据记录拷贝到undo log中进行记录;

  • 修改user表中的id为1的记录的name值为”李四”;

  • 将修改后的数据的事务版本号变更为当前事务版本号;

  • 将新的记录的指针指向undo log中存储的位置;

 

最后执行完的流程图如下:

 

       在前面我们提到过,MVCC解决了在写入的时候可以读的问题,那么我们一起来看一下MVCC是如何解决解决额的吧;

 

       假设数据库user表中有如下数据

 

       现在有两个事物,分别是事物A和事物B,事物A进行更新操作,事物B进行查询操作;

       首先,开始事物A,获得一个事务版本号为12,然后将id为1的数据读取到内存中,拷贝数据到undo log日志中,拷贝数据到read log中进行更新操作,等待提交事务;

       然后开启事务B,获得一个事务版本号13,然后获取一个read view(事务版本号为12的时候也会有一个read view此处省略);

       根据read view返回数据规则我们可以获取到事务版本号为12的数据,也就是在内存中的id为1事务版本号为12的数据;然后将此数据进行返回;如下图所示:

 

       通过事务版本号的不同及规则从而实现在写的时候允许读,并且不加锁,从而提高数据库性能问题;

      


文章转载自加耀,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论