MySQL技术内幕-InnoDB存储引擎-读书笔记

2020-04-23  本文已影响0人  幸南

1.MySQL体系结构和存储引擎

1.1.定义数据库和实例

数据库和实例是不一样的.数据库即指磁盘上的文件,而实例是指的启动的一个数据库程序.

MySQL是一个单进程多线程的数据库

mysql --help |grep my.cnf可以查看到MySQL默认的配置文件加载顺序

1.2.MySQL体系结构

连接池组件

管理服务和工具组件

SQL接口组件

查询分析器组件

优化器组件

缓冲组件

插件式存储引擎

物理文件

1.3.MySQL存储引擎

存储引擎是MySQL区别于其他数据库的一个重要特性

1.3.1.InnoDB存储引擎

支持事务,主要面向在线事务处理.

行锁设计,支持外键,并支持类似Oracle的非锁定读.

将数据放在一个逻辑的表空间中.MySQL4.1之后放在一个ibd文件中.支持用裸设备(row disk)来建立表空间.

通过MVCC来获得高并发性,并实现了标准的四种隔离级别:读未提交,读已提交,可重复读,序列化.

使用next-key locking来避免幻读的产生.除此之外还提供了插入缓冲,二次写,自适应哈希索引,预读等高并发

和高可用的功能.

数据存储采用聚集的方式,即按照主键顺序进行存放.如果没有显示指定主键,InnoDB会为每一行产生一个6个

字节的ROWID,并以此作为主键.

1.3.2.MyISAM存储引擎

不支持事务,采用表锁设计,支持全文索引.在MySQL5.5.8之前是默认存储引擎.

数据库系统和文件系统最大的不同就是事务.但是有些应用就不需要事务,例如纯查询应用.

缓冲池只缓冲索引文件而不缓冲数据文件,这跟大多数存储引擎都不一样

数据文件由MYD和MYI组成,一个存储数据,一个存储索引

MySQL5.0版本之前最大数仅支持4G.

1.3.3.NDB存储引擎

集群存储引擎,类似Oracle的RAC集群.

数据全部放在内存中

1.3.4.Memory存储引擎

数据存储在内存中.使用哈希索引.

只支持表锁,所以并发性能差,不支持TEXT和BLOB列类型,存储变长字段采用char会浪费内存空间.

MySQL使用Memory存储引擎存放查询的中间结果集.

1.3.5.Archive存储引擎

只支持insert和select操作,使用zlib算法压缩数据行后存储.压缩比可达1:10,适合存储归档数据.

1.3.6.Federated存储引擎

并不存放数据,而是指向网络上的一台远程MySQL服务器上的表.

1.3.7.Maria存储引擎

新开发的存储以前,设计目标为取代原有的MyISAM存储引擎

1.3.8.其他存储引擎

1.4.各存储引擎之间比较

查看MySQL支持的存储引擎:

show engines;

1.5.连接MySQL

1.5.1.TCP/IP

mysql -uroot -proot -h127.0.0.1

在连接到MySQL之前,会检查一张权限试图.试图在mysql数据库中,表名为user

1.5.2.命名管道和共享内存

在配置文件中增加--enable-named-pipe选项

在MySQL4.1之后还提供了共享内存的方式,在配置文件添加--shared-menory

1.5.3.UNIX域套接字

show variables like "%socket%";

mysql -uroot -S /tmp/mysql.socket

2.InnoDB存储引擎

事务安全的MySQL存储引擎

2.1.InnoDB存储引擎概述

从MySQL5.5开始是默认的存储引擎.

第一个完整支持ACID事务的MySQL存储引擎,特点是行锁设计,支持MVCC,支持外键,提供一致性非锁定读

2.2.InnoDB存储引擎版本

早期版本随着MySQL的版本更新而更新.MySQL5.1开始允许存储引擎开发商以动态方式加载引擎.

MySQL5.1中支持两个版本的InnoDB,一个是静态编译的InnoDB,一个是以动态方式加载的InnoDB

称之为InnoDB Plugin

2.3.InnoDB体系结构

从图中可以看出InnoDB存储引擎有多个内存块,可以认为这些内存块组成了内存池,负责如下工作:

1.维护所有进程/线程需要访问的多个内部数据结构

2.维护磁盘上的数据,方便快速读取,同时在对磁盘文件修改之前在这里缓存

3.重做日志(redo log)缓冲

后台线程的主要作用是刷新内存池中的数据,保证缓冲池中的数据是最新的.

2.3.1.后台线程

InnoDB是多线程的存储模型.

1.Master Thread

非常核心的一个线程,负责将缓冲池中的数据异步刷新到磁盘,保证数据一致性,包括脏页的刷新,合并

插入缓冲,UNDO页的回收等.

2.IO Thread

InnoDB中大量使用了AIO来处理写IO请求.而IO Thread线程主要负责这些IO请求的回调处理.

InnoDB1.0版本之前有四个IO Thread,分别为write,read,insert buffer,log IO Thread.

Linux平台不能调整,但是Windows可以通过innodb_file_io_threads来增大IO Thread.

从1.0版本开始,write和read线程增加到了四个,并且去掉了innodb_file_io_threads参数,而使用

innodb_read_io_threads和innodb_write_io_threads参数进行设置.

show variables like "innodb_version";

show variables like "innodb%threads";

show engine innodb status;

3.Purge Thread

事务被提交之后,undolog可能已经不需要了,需要回收已经分配使用的undo页.

可以通过在配置文件增加innodb_purge_threads=x来启用独立的purge thread

4.Page Cleaner Thread

是InnoDB1.2.x版本引入的,刷新脏页的操作.

2.3.2.内存

1.缓冲池

页从缓冲池刷新回磁盘是通过checkpoint机制来实现的

大小可以通过innodb_buffer_pool_size来设置

show variables like "innodb_buffer_pool_size";

缓冲池缓存的数据类型:索引页,数据页,undo页,插入缓冲,自适应哈希索引,InnoDB存储的锁信息,数据字 典信息等.

从InnoDB1.0开始允许多个缓冲池实例.可以通过innodb_buffer_pool_instances设置.

2.LRU List,Free List和Flush List

数据库中的缓冲池是通过LRU算法来进行的.缓冲池中的页默认大小为16kb.跟其他的LRU算法相比,

InnoDB做了一些优化,加入了midpoint位置,新读取的页,虽然是最新访问的页,但是不会直接放入LRU

的首部,而是放入midpoint位置.这个算法在InnoDB中称为midpoint insertion strategy(中点插入策略)

默认在5/8处,大概37%.这个位置可以通过innodb_old_blocks_pct控制.

show variables like "innodb_old_blocks_pct";

midpoint之后的称为old列表,之前的称为new列表,那为什么不直接用朴素的LRU算法呢?

如果直接放入首部,那么可能某些sql操作会使得缓冲池中的数据被刷出.常见的这类操作为索引或数据

的扫描操作.这些操作需要使用内存中的所有页,会使得数据被从缓冲池中刷出.热点数据等到需要访问时

需要再次读取.为了解决这个问题引入了另外一个参数innodb_old_blocks_time,即读到mid位置之后多

久才会被放入LRU列表的热端.

LRU列表用来管理已经读取的页,但是在启动时是空的,需要从Free List中查询是否用空闲的页,如果有就 从Free List中取,取出后页从Free List中删除,如果没有没有则淘汰LRU列表中的页,当页从old列表进入

new列表时的操作称为page made young,从old列表进入new列表失败时称为page not made young.

通过show engine innodb status;可以查看LRU List和Free List的使用情况和运行情况.

这里有一个非常重要的观察变量:buffer pool hit rate,表示缓冲池的命中率,这个值不应该小于95%,如果

小于应该考虑是全表扫描引起的LRU列表被污染的情况.

InnoDB从1.0.x版本开始支持压缩页的功能,即原来的16KB压缩为1KB,2KB,4KB,8KB.对于非16KB的页

是通过unzip_LRU列表进行管理的.

在页上的数据被修改之后,该页被称为脏页,Flush列表中的页都是脏页.需要注意的是LRU List和Flush

List中都存在脏页.Flush列表用来管理将页刷会磁盘,二者互不影响.

3.重做日志缓冲

先将重做日志放入缓冲中,然后再刷新回redo log日志中.不需要设置的太大.

通过innodb_log_buffer_size来控制,默认大小为8MB.

三种情况下会刷新重做日志缓冲:

a.Master Thread每秒刷新一次

b.每个事务提交时会刷新

c.重做日志缓冲池空闲空间小于1/2时

4.额外的内存池

在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆的方式进行的.

2.4.Checkpoint技术

缓冲池的目的就是为了协调内存和磁盘的速度相差太大的问题,因此页的操作都是在缓冲池中完成的.如果一条

DML语句,如果update和delete改变了缓冲池中页的内容,则这个页就是脏页.但是如果每次一个页都改变了值

就提交,那么性能开销会很大.但是如果在写磁盘时发生了宕机,则数据会丢失,此时就需要应用重做日志.但是如

果重做日志太大则会非常困难.

当通过重做日志来恢复数据时会有两个问题:

1.缓冲池无法缓存数据库中的所有数据

2.重做日志无法做到无限大

checkpoint作用:

1.缩短数据库的恢复时间

2.缓冲池不够用时,将脏页刷新到磁盘

3.重做日志不可用时,刷新脏页

当发生故障时不需要应用所有的重做日志,因为checkpoint之前的已经刷新回磁盘了,只需要刷新checkpoint之

后的重做日志.

此外当重做日志不可用时,LRU算法会溢出最近最少使用的页,如果这个页是脏页,那么需要强制执行checkpoint

将脏页刷新回磁盘.

对于InnoDB来说,通过LSN(Log sequence number)来标记版本的,而LSN是8字节的数字,单位为字节.每个页有

LSN,重做日志有LSN,checkpoint也有LSN.可以通过show engine innodb status;查看checkpoint所做的事情

就是将脏页刷新回磁盘.

有两种checkpoint:

1.Sharp Checkpoint

发生在数据库关闭时将所有的脏页刷新回磁盘,这是默认的工作方式,即innodb_fast_shutdown=1

2.Fuzzy Checkpoint

但是如果运行时每次都把脏页刷新到磁盘,那么性能开销非常的大,所以会使用Fuzzy Checkpoint

来处理,而不是刷新所有的脏页

2.5.Master Thread工作方式

1.InnoDB1.0.x之前的Master Thread

2.InnoDB1.2.x之前的Master Thread

3.InnoDB1.2.x之后的Master Thread

2.6.InnoDB特性

1.插入缓冲

insert buffer是一种特殊的数据结构(B+ tree)并不是缓存的一部分,而是物理页,当受影响的索引页不 在buffer pool时缓存 secondary index pages的变化,当buffer page读入buffer pool时,进行合并操作, 这些操作可以是 INSERT, UPDATE, or DELETE operations (DML)

insert buffer只能用在非唯一索引上.

原理:

对于为非唯一索引,辅助索引的修改操作并非实时更新索引的叶子页,而是把若干对同一页面的更新缓 存起来做,合并为一次性更新操 作,减少IO,转随机IO为顺序IO,这样可以避免随机IO带来性能损耗, 提高数据库的写性能 .

先判断要插入非聚集索引页是否在缓冲池中,如果在,则直接插入,如果没有则先存入insert buffer,好似欺骗.

再以一定的频率插入.

需要满足的条件:

1.索引是辅助索引

2.索引不是唯一的

2.两次写

3.自适应哈希索引

哈希是一种非常快的查询方式.复杂度为o(1).而B+树的查找次数,取决于B+树的高度,生产环境一般为3-4层

InnoDB会监控表上的数据,当认为建立哈希索引可以带来性能提升时会自动建立哈希索引(AHI).对于(a,b)

这样的索引会有两种情况:where a = xxx,where a = xxx and b = xxx

4.异步IO

为了提高磁盘操作性能,当前的数据库采用异步IO进行操作.

innodb_use_native_aio

5.刷新邻接页

当刷新一个脏页时,会自动检测邻近的页,如果是脏页就进行刷新

3.文件

3.1.参数文件

动态参数

静态参数

3.2.日志文件

错误日志

二进制日志

慢查询日志

查询日志

3.3.套接字文件

3.4.pid文件

3.5.表结构定义文件

3.6.InnodDB存储引擎文件

表空间文件

重做日志文件

4.表

4.1.索引组织表

InnoDB存储引擎中,表都是根据主键顺序存放的,这种存储方式成为索引组织表.InnoDB存储引擎表中,每张

表中都有个主键,如果没有则会有个默认主键.

4.2.InnoDB逻辑存储结构

所有数据都被逻辑的存放在一个空间中,称之为表空间.表空间又由段(segment),区(extent),页(page)组成.

页在一些文档中也称之为块(block)

4.2.1.表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都放在表空间中.默认情况下innodb

存储引擎有一个默认的共享表空间,即所有的数据都会放到这个表空间里.

如果启用了innodb_file_per_table则每张表内的数据会单独放到一个表空间内,但是存放的是数据,索引

插入缓冲bitmap页.其他的数据如回滚信息,插入缓冲索引页,系统事务信息,二次写缓冲都还在原来的共享

表空间内,这说明了一个问题,开启了innodb_file_per_tabel之后,共享表空间还是会不断的增大.

4.2.2.段

表空间是由各个段组成,常见的段有数据段,索引段,回滚段等.InnoDB存储引擎是索引组织的,因此数据即索引

索引即数据.对段的管理都是由InnoDB存储引擎完成的.

4.2.3.区

区是由连续页组成的空间,任何情况下区的大小都为1MB.为了保证区中的页的连续性,一般InnoDB存储引擎 一次申请4-5个区.默认情况下一个页的大小为16KB,即一个区有64个页.

InnoDB1.2.x版本开始新增了innodb_page_size参数.

4.2.4.页

同大多数数据库一样,InnoDB有页的概念,每个页大小默认16KB,可以通过innodb_page_size设置页大小.

设置完成之后所有的页大小都为innodb_page_size大小,不能再次改变.除非通过mysqldump导入或者导出

操作产生新的表.

页类型:

数据业,undo页,系统页,事务数据页,插入缓冲位图页,插入缓冲空闲列表页,未压缩的二进制大对象页,压缩

的二进制大对象页.

4.3.InnoDB行记录格式

跟大多数数据库一样InnoDB也是以行来记录数据的.即页中存放着表中一行行的数据.在InnoDB1.0.x版本之 前提供了Compact和Redundant两种格式来存放行记录数据

4.3.1.Compact

4.3.2.Redundant

4.3.3.行溢出数据

4.3.4.Compressed和Dynamic记录格式

4.3.5.CHAR的行结构存储

4.4.InnoDB数据页结构

4.5.Named File Formats机制

4.6.约束

4.7.视图

4.8.分区表

5.索引与算法

5.1.InnoDB存储引擎索引概述

InnoDB存储引擎支持以下几种常见的索引:

B+树索引

全文索引

哈希索引

InnoDB支持的哈希索引是自适应的.InnoDB存储引擎会根据表的使用情况自动为表生成哈希索引.

不能认为干预是否在一张表中生成哈希索引.

6.锁

6.1.什么是锁

锁是数据库系统区别于文件系统的一个关键特性.用于管理对于共享资源的并发访问.InnoDB会在行级别

对表数据进行加锁.

6.2.lock与latch

在数据库中,lock和latch都可以称之为锁,但是两者有着截然不同的含义.

latch一般理解为闩锁,因为其要求锁定时间足够短,太长时间的锁定会造成性能会非常差.在InnoDB存储引擎

中,latch又分为mutex(互斥量)和rwlock(读写锁).

lock的对象是事务,用来锁定的是数据库中的对象,如表,页,行等.一般lock的对象仅在事务commit或者rollback

之后进行释放.

对于InnoDB中的latch可以通过show engine innodb mutex;查看.

6.3.InnoDB存储引擎中的锁

6.3.1.锁的类型

共享锁(S Lock):允许事务读一行数据

排它锁(X Lock):允许事务删除或更新一行数据

如果事务T1获取了行r的共享锁,那么事务T2可以立即获得行r的共享锁,因为读取并没有改变行r的数据,这种

情况称之为锁兼容.但是如果事务T1获得了行r的排它锁,那么事务T2,T3必须等到事务T1释放了行r之后才可

以获得行r的排它锁.

另外InnoDB支持多粒度锁定.这种特性允许事务在行级上的锁和表级上的锁同时存在.为了支持多粒度的锁

操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁.

只是对自己的读书过程做些笔记,如果有不对的地方,烦请各位指正.谢谢!

上一篇下一篇

猜你喜欢

热点阅读