本篇文章是用来记录我在阅读《MySQL技术内幕:InnoDB存储引擎》这本书时所做的笔记。数据库是我们在学习和工作中都必须熟悉的一块内容,但是实际我们更多的是停留在写SQL语句层面,最多还涉及到优化SQL这一层面,对MySQL底层的结构和底层的知识了解不多,可能大家普遍认为开发人员知道写SQL和优化SQL就差不多了,至于数据库的问题排查、日志和数据库运行状态跟踪、数据修复这些是DBA的事情,我觉得作为一名开发工程师有必要了解更多底层的原理和结构,才能帮助我们更好的应用在实际的工作当中,同样也会提升自己各方面的能力,本文章是摘抄一些重要的知识点,希望对大家也有帮助!有兴趣的同学可以阅读书本,学习更全面的书本内容。(本文章的章节是和书本上对应的,有些章节没有摘抄内容就直接跳过了)
数据库: 文件集合(由一个个文件组成)
数据库实例:应用程序(进程),是位于数据库的用户与操作系统之间的一层数据管理软件,用户对数据库数据的任何操作,包括数据库定义、数据查询、数据维护、数据库运行控制等,都是在数据库实例下进行的,我们用的应用程序只有通过数据库实例才能和数据库打交道。(就比如我们要执行SELECT、INSERT、UPDATE、DELETE之类的操作,都是通过数据库实例来完成对数据库的操作的)
直白的解释:数据库是由一个个文件组成(一般来说都是二进制文件)的,如果要对这些文件执行诸如select、insert、update和delete之类的操作,不能通过简单的操作文件方式来更改数据库的内容,需要通过数据库实例来完成对数据库的操作。
可以从突出看出MySQL体系结构由如下部分组成:连接池组件、管理服务和工具组件、SQL接口组件、查询分析器组件、优化器组件、缓存组件、插件式存储引擎、物理文件。
从MySQL体系结构图可以看出,MySQL区别于其他数据的最重要的特点就是插件式的表存储引擎(注意是表的存储引擎,存储引擎是基于表的,而不是数据库),MySQL插件式的存储引擎架构提供了一系列标准的管理和服务支持,这些标准与存储引擎本身无关,可能是每个数据库系统本身都必须的,如SQL分析器和优化器等,而存储引擎是底层物理结构的实现,每个存储引擎开发者都可以按照自己的意愿来进行开发。
常用的:InnoDb存储引擎、MyISAM存储引擎
大致区别:
① InnoDb存储引擎:支持事务
② MyISAM存储引擎:不支持事务、表锁、全文索引
其实就是应用进程和数据库实例进行连接,也就是进程间的通信,那通常的进程通信方式有管道、命名管道、TCP/IP套接字、Unix域名套接字等。那上面这些进程通信方式分具体的实际情况,比如TCP/IP就是不在同一个服务器上面比较常用的,利用Ip和端口,用户名密码进行连接的
体系结构如下图,InnoDB存储引擎内存池有多个具体的内存块,后台线程就是进行数据库相关操作的,主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。此外将已修改的缓存数据文件刷新到磁盘文件当中,同时保证在数据库发生异常情况下InnoDB能恢复到正常运行状态。
各内存区域介绍:
缓冲池:
日志缓冲池:日志缓冲将重做日志信息放入这个缓冲池中,然后按一定频率将其刷新到重做日志文件。
额外的内存池:在对一些数据结构本身分配内存时,需要从额外的内存池中申请,当该区域内存不够时,会从缓冲池中申请。InnoDB实例会申请缓冲池的空间,但是每个缓冲池中的帧缓冲还有对应的缓冲控制对象,而且这些对象记录了诸如LRU、锁、等待等方面的信息,而这个对象的内存需要从额外内存池中申请。因此当你申请了很大的InnoDB缓冲池时,额外的内存池的大小也应该响应增加。
MySQL数据库和对应存储引擎InnoDB的各种类型文件,每个文件都至关重要,各有各的用处,知道了每种类型的文件的职责、存放的文件内容,才能更好地了解MySQL数据库的结构,有以下文件:
参数文件:启动或者运行时的一些参数,innodb_buffer_pool_size这个配置。 可以用show variables查看所有参数
参数类型:
动态参数:意味着数据库实例在运行中可以修改的参数
静态参数:整个实例声明周期内都不得进行更改的参数
日志文件:记录整个数据库各种活动的数据。日志文件有错误日志、二进制日志、慢查询日志、查询日志,如果要对数据库优化、问题查找的话就能在这些日志里面寻找
错误日志:出现问题(比如数据库不能正常启动时)第一时间看的日志,该日志就记录了出错的信息,还记录了一些告警信息,也有正确的信息。可以通过show variables like 'log_error';来定位该文件。
慢查询日志:该日志文件可以很好的帮助进行SQL语句优化,慢查询的慢定义可以设置时间阈值,大于这个阈值就定义为慢查询的SQL,参数为:long_query_time。另外还有一个参数log_queries_not_using_indexes表示SQL语句执行的时候没用到索引的话,也会打印慢查询日志,该参数为on时表示打开,可以用show variables like 'log_queries_not_using_indexes';
查询日志:记录所有对MySQL数据库请求的信息
二进制日志:默认情况下是没有启动的。记录对数据库执行更改的所有操作,但是不包括select和show这类操作,因为这类操作并没有对数据库进行更改,而是查询,可以去查询日志进行查看。该文件主要有两种作用:
恢复:某些数据恢复就需要借助二进制日志
复制:如果要进行两个数据库的数据同步,就复制其中一台的二进制文件,在另一台中执行就可以进行进行同步了
MySQL表结构文件:用来存放表结构定义文件,所以每个表都会有与之对应的文件,以frm为后缀的文件。另外我们创建v_a视图的话也会产生一个v_a.frm的文本文件用来记录视图的定义。
存储引擎文件:存储引擎是对于表来说的,每个存储引擎都会有自己的文件来保存各种数据。这些存储引擎文件真正存储了数据和索引等数据,其他的几个文件是MySQL数据库本身的文件,和存储引擎无关。
存储引擎文件里面包括了表空间文件、重做日志文件:
表空间文件:存放表数据、索引和插入缓冲等信息。默认配置下会有一个初始化大小为10MB,名为ibdata1的默认表空间文件。也可以通过参数innodb_file_per_table生成单独的表空间,文件名为表名.ibd的命名格式,这样存放表数据就不会存放到默认的表空间当中,要注意的是生成的单独的表空间文件仅存储该表的数据、索引和插入缓冲等信息,其余信息还是存放在默认的表空间中(这个其余信息包括:撤销信息、系统事务信息,二次写缓冲等,书本第4章的内容)。下图3-1显示了Innodb表存储引擎文件结构。
重做日志文件(redo log):默认情况会有两个文件(ib_logfile0和ib_logfile1),该日志文件记录了InnoDB存储引擎的事务日志。主要目的就是万一数据库实例或者磁盘介质失败,如数据库由于所在的主机挂掉导致实例失败,这时候InnoDB存储引擎会使用重做日志恢复到挂掉之前的节点时刻,来保证数据的正确性和完整性,起到容灾的效果。
这里可能会想到既然是记录数事务的日志文件,那和之前说的二进制文件又有啥区别呢?
- 首先,二进制日志会记录所有MySQL有关的日志记录,这里就不单单是InnDB存储引擎的日志了,毕竟还有其他表,可能存在其他存储引擎的日志,相当于是一个全局的事务日志文件,而InnoDB存储引擎的重做日志只记录有关其本身的事务日志,也就是记录的是对某张表(使用的是存储引擎的表)的所有事务日志。
- 其次,记录的日志内容也不一样,二进制文件记录的都是关于一个事务的具体操作内容,而InnoDB存储引擎的重做日志记录的是关于每个页的更改的物理情况。
- 此外,两种日志写入的时间也不同,二进制日志文件是在事务提交前进行记录,而在事务进行的过程中,不断有重做日志被写入重做日志文件中。
对于重做日志的操作不是直接写,而是先写入一个重做日志缓冲,然后再按照一定的条件写入日志文件,见下图3-3:
这里额外讲下Undo log:
Undo log:提供回滚和多个行版本控制。在数据修改的时候,不仅记录了redo log,还会记录响应的undo log,如果因为某些原因导致事务失败或回滚了,可以借助Undo log进行回滚,undo log记录的是逻辑日志,可以认为当delete一条记录时,就会在undo log中记录一条对应的insert记录,反之亦然,又比如当update一条记录时,会记录一条对应相反的update记录,这样的话如果要回滚就可以执行undo log里面的内容。undo存放在数据库内部的一个特殊段中,称为undo段,位于共享表空间内,下面第四章中的图4-1有显示。
socket文件:Unix系统下本地连接MySQL可以采用Unix域套接字方式,这种方式需要一个套接字文件。
pid文件:MySQL实例启动时,会将自己的进程ID写入pid文件。
对比Oracle支持的各种表类型,InnoDB存储引擎更像是Oracle中的索引组织表。在InnoDB存储引擎表中,每张表都会有个主键。那主键的构建方式会有两类:
1、可以显示地在创建表的时候指定主键;
2、如果没有显示地定义主键,则InnoDB存储引擎会按如下方式选择或创建主键:
①首先看表中是否存在非空的唯一索引,如果有,则该列即为主键;
②不符合第①点条件的话,InnoDB存储引擎会自动创建一个6个字节大小的指针列。
InnoDB存储引擎的逻辑存储结构和Oracle大致相同,所有数据都被逻辑的存放在一个空间中,我们称之为表空间(tablespace)。表空间又由:段(segment)、区(extent)、页(page)组成。逻辑存储结构大致如下图4-1
从物理意义上来看,InnoDB表由共享表空间、日志文件组(更准确的说应该是重做日志(Redo log)文件组)、表结构定义文件组成。如果将innodb_file_per_table设置为on(前文讲过),则每个表将独立地产生一个表空间文件,以ibd结尾,数据、索引、表的内部数据字典信息将保存在这个单独的表空间文件中。表结构文件是以frm结尾,这个与存储引擎无关的,任何存储引擎的表结构文件都是一样.frm文件。
InnoDB存储引擎实现了如下两种标准的行级锁:
当一个事务已经获得了行r的共享锁,那么另外的事务可以立即获得行r的共享锁,因为读取并没有改变行r的数据,我们称这种情况为锁兼容。如果有事务获取行r的排他锁,则他必须等待事务释放行r上的共享锁----这种情况我们称为锁不兼容。下表6-1为共享锁和排他锁的兼容性:、
InnoDB存储引擎支持多粒度锁定,这种锁定允许在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,我们称之为意向锁。意向锁是表级别的的锁,其设计目的主要是为了在一个事务中揭示下一行将被请求的锁的类型。InnoDB存储引擎支持两种意向锁:
因为InnoDB存储引擎支持的是行级别的锁,所以意向锁其实不会阻塞除全表扫以外的任何请求。
InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决 定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突 时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。https://www.cnblogs.com/leedaily/p/8378779.html
一致性的非锁定行读是指InnoDB存储引擎通过行 多版本控制的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行delete、update操作,这时读操作不会因此而等待行上锁的释放,相反,InnoDB存储引擎会去读取行的一个快照数据。下图6-1就是对一致性的非锁定读操作的流程描述:
上图直观地展现了InnoDB存储引擎一致性的非锁定读。之所以称其为非锁定读,因为不需要等待访问的行上X锁的释放。快照数据是指该行之前版本的数据,该实现是通过Undo段来实现。而Undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销的。此外,读取快照数据是不需要上锁的,因为没有必要对历史的数据进行修改。
可以看到,非锁定读的机制大大提高了数据读取的并发性,在InnoDB存储引擎默认设置下,这是默认的读取方式,即读取不会占用和等待表上的锁。但是在不同事物隔离级别下,读取的方式不同,并不是每个事物隔离级别下读取的都是一致性读,同时,即使是使用一致性读,但是对于快照数据的定义也不相同。
通过图6-1我们知道,快照数据其实就是当前行数据之前的历史版本,可能有多个版本。就图6-1显示的,一个行可能有不止一个快照数据,我们称这种技术为行 多版本技术。由此带来的并发控制,称之为多版本并发控制。
在Read Committed(读已提交)和Repeatble Read(可重复读)下,InnoDB存储引擎使用非锁定的一致性读。然而对于快照的定义却不相同。在Read Committed事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新的一份快照数据,在Repatable事务隔离级别下和Repeatable Read事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。
6.2.2小节讲到,在默认情况下,InnoDB存储引擎的SELECT操作使用一致性非锁定读,但是在某些情况下, 我们需要对读取操作进行加锁。InnoDB存储引擎对于SELECT语句支持两种加锁操作:
上述两种加锁操作必须在一个事务当中,所以在使用的时候务必加上BEGIN、START TRANSACTION或者SET AUTOCOMMIT=0。
一张图展示死锁:
事务会把数据库从一种一致状态转换为另一种一致状态。在数据库提交工作时,可以确保其要么所有修改都已经保存了,要么所有修改都不保存。
InnoDB存储引擎中的事务符合ACID的特性:原子性、一致性、隔离性、 持久性,具体可以看我另外一篇文章数据库事务的复习笔记。
ok~,该篇文章目前就大概记录以上类容,其他的内容我觉得也没什么必要详细记录啦,上面内容已经基本涵盖本书的关键知识点,后续有必要的话会在这里追加。