深入MySQL底层1-存储引擎与索引

简介: MySQL体系结构分为连接层、服务层、引擎层和存储层,其中引擎层采用插件式架构,支持多种存储引擎。InnoDB是默认引擎,支持事务、行锁和外键,其数据以B+树结构存储;MyISAM适合读多写少场景;Memory引擎将数据存于内存。索引是提高查询效率的关键数据结构,主要包括B+树和Hash两种结构,B+树适合范围查询,Hash适合等值查询。合理使用索引需遵循最左前缀法则,避免索引失效情况,并考虑覆盖索引、前缀索引和联合索引的应用。索引设计原则包括:选择高区分度列、控制索引数量、优先使用联合索引等,以平衡查询性

1.MySQL体系结构介绍

image.gif 编辑

1). 连接层

最上层是一些客户端和链接服务,包含本地socket通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

2). 服务层

第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如 过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等, 最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。

3). 引擎层

存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。数据库中的索引是在存储引擎层实现的。

4). 存储层

数据存储层, 主要是将数据(: redologundolog、数据、索引、二进制日志、错误日志、查询日志、慢查询日志等)存储在文件系统之上,并完成与存储引擎的交互。

和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。 这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

2.MySQL存储引擎

2.1 存储引擎创建

存储引擎是MySQL数据库的核心,我们需要在合适的场景选择合适的存储引擎。

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。我们可以在创建表的时候,来指定选择的存储引擎,如果没有指定将自动选择默认的存储引擎。

1). 建表时指定存储引擎

CREATE TABLE 表名(
字段1 字段1类型 [ COMMENT 字段1注释 ] ,
......
字段n 字段n类型 [COMMENT 字段n注释 ]
) ENGINE = INNODB [ COMMENT 表注释 ] ;

image.gif

2). 查询当前数据库支持的存储引擎

show engines;

image.gif

创建表时,即使我们没有指定存储疫情,数据库也会自动选择默认的存储引擎。

2.2 常见存储引擎

常见存储引擎有三种,InnoDBMyISAMMemory。

2.2.1 InnoDB

1). 介绍

InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB是默认的

MySQL 存储引擎。

2). 特点

DML操作遵循ACID模型,支持事务;

行级锁,提高并发访问性能;

支持外键FOREIGN KEY约束,保证数据的完整性和正确性;

3). 文件

xxx.ibdxxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结

构(frm-早期的 、sdi-新版的)、数据和索引。

参数:innodb_file_per_table

show variables like 'innodb_file_per_table';

image.gif

image.gif 编辑

如果该参数开启,代表对于InnoDB引擎的表,每一张表都对应一个ibd文件。 我们直接打开MySQL的数据存放目录: C:\ProgramData\MySQL\MySQL Server 8.0\Data , 这个目录下有很多文件夹,不同的文件夹代表不同的数据库。

image.gif 编辑

可以看到里面有很多的ibd文件,每一个ibd文件就对应一张表,比如:我们有一张表 account,就

有这样的一个account.ibd文件,而在这个ibd文件中不仅存放表结构、数据,还会存放该表对应的

索引信息。 而该文件是基于二进制存储的,不能直接基于记事本打开,我们可以使用mysql提供的一 个指令 ibd2sdi ,通过该指令就可以从ibd文件中提取sdi信息,而sdi数据字典信息中就包含该表的表结构。

4). 逻辑存储结构

image.gif 编辑

表空间 :

InnoDB存储引擎逻辑结构的最高层,ibd文件其实就是表空间文件,在表空间中可以

包含多个Segment段。

:

表空间是由各个段组成的, 常见的段有数据段、索引段、回滚段等。InnoDB中对于段的管

理,都是引擎自身完成,不需要人为对其控制,一个段中包含多个区。

:

区是表空间的单元结构,每个区的大小为1M。 默认情况下, InnoDB存储引擎页大小为

16K, 即一个区中一共有64个连续的页。

:

页是组成区的最小单元,也是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默

认为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。

:

InnoDB 存储引擎是面向行的,也就是说数据是按行进行存放的,在每一行中除了定义表时

所指定的字段以外,还包含两个隐藏字段,trx_id:每次对某条记录进行改动时,会把对应的事务id赋值给Trt_id隐藏列,roll_pointer:每次对某条引记录进行改动时,都会把旧版本放到Undo日志中,然后这个隐藏列就相当于一个指针,通过它找到记录修改前的值。

2.2.2 MyISAM

1). 介绍

MyISAMMySQL早期的默认存储引擎。

2). 特点

不支持事务,不支持外键

支持表锁,不支持行锁

访问速度快

3). 文件

xxx.sdi:存储表结构信息

xxx.MYD: 存储数据

xxx.MYI: 存储索引

image.gif 编辑

2.2.3 Memory

1). 介绍

Memory引擎的表数据时存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。

2). 特点

内存存放,hash索引(默认),不支持事务,行级锁,外键

3).文件

xxx.sdi:存储表结构信息

总结:

image.gif 编辑

2.2.4 存储引擎选择

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

InnoDB:

Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要

求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操

作,那么InnoDB存储引擎是比较合适的选择。

MyISAM

如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完

整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。

MEMORY

将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。

3.索引

3.1 索引介绍

索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

在无索引情况下,对某张表查询数据就需要从第一行开始扫描,一直扫描到最后一行,我们称之为全表扫描,性能很低,如果我们针对这张表建立了索引,查询数据就可以按索引结构去查询数据,无需全表扫描,极大提高了查询效率。

image.gif 编辑

3.2 索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的索引结构,主要包含以下几种:

image.gif 编辑

不同的存储引擎对于索引结构的支持情况: image.gif 编辑

3.2.1 B+树结构

image.gif 编辑

绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据。

红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据。

特点:

所有的数据都会出现在叶子节点,叶子节点形成一个单向链表,非叶子节点仅仅起到索引数据作用,具体的数据都是在叶子节点存放的。

MySQL中优化之后的B+Tree结构:

image.gif 编辑

在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能,利于排序。

3.2.2 Hash结构

1). 结构

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。

image.gif 编辑

2). 特点

Hash索引只能用于对等比较(=in),不支持范围查询(between>< ...

无法利用索引完成排序操作

查询效率高,通常(不存在hash冲突的情况)只需要一次检索即可,效率通常要高于B+tree索引

MySQL中,支持hash索引的是Memory存储引擎。 而InnoDB中具有自适应hash功能,hash索引是InnoDB存储引擎根据B+Tree索引在指定条件下自动构建的。

3.3 索引分类

在MySQL数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引。 image.gif 编辑

聚集索引&二级索引

InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:

image.gif 编辑

聚集索引选取规则:

如果存在主键,主键索引就是聚集索引。

如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。

如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引

image.gif 编辑

具体过程如下:

. 由于是根据name字段进行查询,所以先根据name='Arm'name字段的二级索引中进行匹配查

找。但是在二级索引中只能查找到 Arm 对应的主键值 10. 由于查询返回的数据是*,所以此时,还需要根据主键值10,到聚集索引中查找10对应的记录,最终找到10对应的行row

. 最终拿到这一行的数据,直接返回即可。

回表查询: 这种先到二级索引中查找数据,找到主键值,然后再到聚集索引中根据主键值,获取

数据的方式,就称之为回表查询。

思考:

以下两条SQL语句,那个执行效率高? 为什么?

A. select * from user where id = 10 ;

B. select * from user where name = 'Arm' ;

备注: id为主键,name字段创建的有索引;

A 语句的执行性能要高于B 语句。因为A语句直接走聚集索引,直接返回数据。 而B语句需要先查询name字段的二级索引,然后再查询聚集索引,也就是需要进行回表查询。

InnoDB主键索引的B+tree度为多高呢?

image.gif 编辑

B+树一个数据节点对应一个数据页,一行数据大小为1k,一页中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键即使为bigint,占用字节数为8

高度为2

n * 8 + (n + 1) * 6 = 16*1024 , 算出n约为 1170,即根节点最多存1170个主键

每个指针指向一个叶子节点,每个叶子节点存16行数据,且总叶子节点的个数=根节点指针数

1171* 16 = 18736(总记录数=叶子节点个数*每个叶子节点行数)

也就是说,如果树的高度为2,则可以存储 18000 多条记录。

高度为3

1171 * 1171 * 16 = 21939856

也就是说,如果树的高度为3,则可以存储 2200w 左右的记录。

3.4 索引语法

1). 创建索引

CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (
index_col_name,... ) ;

image.gif

2). 查看索引

SHOW INDEX FROM table_name ;

image.gif

3). 删除索引

DROP INDEX index_name ON table_name ;

image.gif

实例:

A. name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。

CREATE INDEX idx_user_name ON tb_user(name);

image.gif

B. phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。

CREATE UNIQUE INDEX idx_user_phone ON tb_user(phone);

image.gif

C. professionagestatus创建联合索引

CREATE INDEX idx_user_pro_age_sta ON tb_user(profession,age,status);

image.gif

D. email建立合适的索引来提升查询效率。

CREATE INDEX idx_email ON tb_user(email);

image.gif

3.5 SQL性能分析

3.5.1 SQL执行频率

通过 show [session|global] status 命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERTUPDATEDELETESELECT的访问频次:

-- session 是查看当前会话 ;
-- global 是查询全局数据 ;
SHOW GLOBAL STATUS LIKE 'Com_______';

image.gif

image.gif 编辑

Com_delete: 删除次数,Com_insert: 插入次数,Com_select: 查询次数,Com_update: 更新次数

那么通过查询SQL的执行频次,我们就能够知道当前数据库到底是增删改为主,还是查询为主。 那假如说是以查询为主,我们又该如何定位针对于那些查询语句进行优化呢? 次数我们可以借助于慢查询日志。

3.5.2 慢日志查询

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有

SQL语句的日志。MySQL的慢查询日志默认没有开启,我们可以查看一下系统变量 slow_query_log

如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

# 开启MySQL慢日志查询开关
slow_query_log=1
# 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2

image.gif

配置完毕之后,通过以下指令重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息

/var/lib/mysql/localhost-slow.log

systemctl restart mysqld

image.gif

通过慢查询日志,就可以定位出执行效率比较低的SQL,从而有针对性的进行优化。

3.5.3 profile详情

show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling

参数,能够看到当前MySQL是否支持profile操作:

SELECT @@have_profiling ;

image.gif

image.gif 编辑

可以通过set语句在session/global级别开启profiling

SET profiling = 1;

image.gif

开关已经打开了,接下来,我们所执行的SQL语句,都会被MySQL记录,并记录执行时间消耗到哪儿去了。

执行一系列的业务SQL的操作,然后通过如下指令查看指令的执行耗时:

-- 查看每一条SQL的耗时基本情况
show profiles;
-- 查看指定query_id的SQL语句各个阶段的耗时情况
show profile for query query_id;
-- 查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;

image.gif

3.5.4 explain

EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。

-- 直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;

image.gif

Explain 执行计划中各个字段的含义: image.gif 编辑

3.6 索引使用

3.6.1 最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)

例如,在 tb_user 表中,有一个联合索引,这个联合索引涉及到三个字段,顺序分别为:profession,age,status。只要联合索引最左边的字段 profession存在,索引就会生效,只不过索引的长度不同。

注:下面代码中是走联合索引的,最左前缀法则中指的最左边的列,是指在查询时,联合索引的最左边的字段(即是第一个字段)必须存在,与我们编写SQL时,条件编写的先后顺序无关

explain select * from tb_user where age = 31 and
status = '0' and profession = '软件工程';

image.gif

3.6.2 范围查询

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。范围查询整体走索引了,但是范围查询右边的status段是没有走索引的。

explain select * from tb_user where profession = '软件工程' and age > 30 and status
= '0';

image.gif

当范围查询使用>= <= 时,所有的字段都是走索引的。所以,在业务允许的情况下,尽可能的使用类似于 >= <= 这类的范围查询,而避免使用 > <。

3.6.3 索引失效情况

索引列运算:

在索引列上进行运算操作, 索引将失效。

字符串不加引号:

字符串类型字段使用时,不加引号,索引将失效。

注:如果字符串不加单引号,对于查询结果,没什么影响,但是数据库存在隐式类型转换,索引将失效。

模糊查询:

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。在关键字后面加%,索引可以生效。而如果在关键字前面加了%,索引将会失效。

explain select * from tb_user where profession like '%工程';

image.gif

or连接条件:

or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到,当or连接的条件,左右两侧字段都有索引时,索引才会生效。

数据分布影响:

如果MySQL评估使用索引比全表更慢,则不使用索引。MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。

3.6.4 SQL提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

1). use index : 建议MySQL使用哪一个索引完成此次查询(仅仅是建议,mysql内部还会再次进

行评估)。

explain select * from tb_user use index(idx_user_pro) where profession = '软件工
程';

image.gif

2). ignore index : 忽略指定的索引。

explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工
程';

image.gif

3). force index : 强制使用索引。

explain select * from tb_user force index(idx_user_pro) where profession = '软件工
程';

image.gif

3.6.5 覆盖索引

覆盖索引是指 查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到 。

image.gif 编辑

id是主键,是一个聚集索引。 name字段建立了普通索引,是一个二级索引(辅助索引)。

image.gif 编辑

根据id查询,直接走聚集索引查询,一次索引扫描,直接返回数据,性能高。

C. 执行SQL:selet id,name from tb_user where name = 'Arm'; image.gif 编辑

虽然是根据name字段查询,查询二级索引,但是由于查询返回在字段为 idname,在name的二级索引中,这两个值都是可以直接获取到的,因为覆盖索引,所以不需要回表查询,性能高。

image.gif 编辑

由于在name的二级索引中,不包含gender,所以,需要两次索引扫描,也就是需要回表查询,性能相对较差一点。

思考:一张表, 有四个字段(id, username, password, status), 由于数据量大, 需要对以下SQL语句进行优化, 该如何进行才是最优方案:

针对于 username, password建立联合索引, sql: create index idx_user_name_pass on tb_user(username,password); 这样可以避免上述的SQL语句,在查询的过程中,出现回表查询。

3.6.6 前缀索引

当字段类型为字符串(varchartextlongtext等)时,有时候需要索引很长的字符串,这会让

索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

1). 语法

create index idx_xxxx on table_name(column(n)) ;

image.gif

示例:

tb_user表的email字段,建立长度为5的前缀索引。

create index idx_email_5 on tb_user(email(5));

image.gif

2). 前缀长度

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

select count(distinct email) / count(*) from tb_user ;
select count(distinct substring(email,1,5)) / count(*) from tb_user ;

image.gif

3). 前缀索引的查询流程

image.gif 编辑

3.6.7 单列索引与联合索引

单列索引:即一个索引只包含单个列。

联合索引:即一个索引包含了多个列。

在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,

而非单列索引

image.gif 编辑

3.6.8 索引设计原则

1). 针对于数据量较大,且查询比较频繁的表建立索引。

2). 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索

引。

3). 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。

区分度 = 索引列唯一值数量 / 表总行数

4). 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。

5). 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间, 避免回表,提高查询效率。

6). 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。

7). 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含 NULL值时,它可以更好地确定哪个索引最有效地用于查询。

4.InnoDB引擎深度剖析

4.1 逻辑存储结构

InnoDB引擎的逻辑存储结构如下:

image.gif 编辑

1). 表空间

表空间是InnoDB存储引擎逻辑结构的最高层, 如果用户启用了参数 innodb_file_per_table(在8.0版本中默认开启) ,则每张表都会有一个表空间(xxx.ibd),一个mysql实例可以对应多个表空

间,用于存储记录、索引等数据。

2).

段,分为数据段(Leaf node segment)、索引段(Non-leaf node segment)、回滚段(Rollback segment),InnoDB是索引组织表,数据段就是B+树的叶子节点, 索引段即为B+树的非叶子节点。段用来管理多个Extent(区)。

3).

区,表空间的单元结构,每个区的大小为1M。 默认情况下, InnoDB存储引擎页大小为16K, 即一个区中一共有64个连续的页。

4).

页,是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。

5).

行,InnoDB 存储引擎数据是按行进行存放的。在行中,默认有两个隐藏字段:

Trx_id每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。

Roll_pointer每次对某条引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

4.2 架构

MySQL5.5 版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。下面是InnoDB架构图,左侧为内存结构,右侧为磁盘结构。

image.gif 编辑

4.2.1 内存结构

image.gif 编辑

主要分为四大块儿: Buffer PoolChange BufferAdaptive Hash Index、Log Buffer

1). Buffer Pool

InnoDB存储引擎基于磁盘文件存储,访问物理硬盘和在内存中进行访问,速度相差很大,为了尽可能弥补这两者之间的I/O效率的差值,就需要把经常使用的数据加载到缓冲池中,避免每次访问都进行磁盘I/O

InnoDB的缓冲池中不仅缓存了索引页和数据页,还包含了undo页、插入缓存、自适应哈希索引以及InnoDB的锁信息等等。

缓冲池 Buffer Pool,是主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后再以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度。

缓冲池以Page页为单位,底层采用链表数据结构管理Page。根据状态,将Page分为三种类型:

• free page:空闲page,未被使用。

• clean page:被使用page,数据没有被修改过。

• dirty page:脏页,被使用page,数据被修改过,也中数据与磁盘的数据产生了不一致。

在专用服务器上,通常将多达80%的物理内存分配给缓冲池 。参数设置: show variables like 'innodb_buffer_pool_size';

2). Change Buffer

Change Buffer,更改缓冲区(针对于非唯一二级索引页),在执行DML语句时,如果这些数据Page 没有在Buffer Pool中,不会直接操作磁盘,而会将数据变更存在更改缓冲区 Change Buffer

中,在未来数据被读取时,再将数据合并恢复到Buffer Pool中,再将合并后的数据刷新到磁盘中。

image.gif 编辑

与聚集索引不同,二级索引通常是非唯一的,并且以相对随机的顺序插入二级索引。同样,删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作磁盘,会造成大量的磁盘IO。有了ChangeBuffer之后,我们可以在缓冲池中进行合并处理,减少磁盘IO

3). Adaptive Hash Index

自适应hash索引,用于优化对Buffer Pool数据的查询。MySQLinnoDB引擎中虽然没有直接支持

hash索引,但是给我们提供了一个功能就是这个自适应hash索引。因为前面我们讲到过,hash索引在进行等值匹配时,一般性能是要高于B+树的,因为hash索引一般只需要一次IO即可,而B+树,可能需要几次匹配,所以hash索引的效率要高,但是hash索引又不适合做范围查询、模糊匹配等。

InnoDB存储引擎会监控对表上各索引页的查询,如果观察到在特定的条件下hash索引可以提升速度,则建立hash索引,称之为自适应hash索引。

自适应哈希索引,无需人工干预,是系统根据情况自动完成。

参数: adaptive_hash_index

4). Log Buffer

Log Buffer:日志缓冲区,用来保存要写入到磁盘中的log日志数据(redo log undo log), 默认大小为 16MB,日志缓冲区的日志会定期刷新到磁盘中。如果需要更新、插入或删除许多行的事务,增加日志缓冲区的大小可以节省磁盘 I/O

参数:

innodb_log_buffer_size:缓冲区大小

innodb_flush_log_at_trx_commit:日志刷新到磁盘时机,取值主要包含以下三个:

1: 日志在每次事务提交时写入并刷新到磁盘,默认值。

0: 每秒将日志写入并刷新到磁盘一次。

2: 日志在每次事务提交后写入,并每秒刷新到磁盘一次

4.2.2 磁盘结构

image.gif 编辑

1). System Tablespace

系统表空间是更改缓冲区的存储区域。如果表是在系统表空间而不是每个表文件或通用表空间中创建的,它也可能包含表和索引数据。(MySQL5.x版本中还包含InnoDB数据字典、undolog)

参数:innodb_data_file_path

2). File-Per-Table Tablespaces

如果开启了innodb_file_per_table开关 ,则每个表的文件表空间包含单个InnoDB表的数据和索

引 ,并存储在文件系统上的单个数据文件中。 开关参数:innodb_file_per_table ,该参数默认开启。我们创建一个表,都会产生一个表空间文件。

3). General Tablespaces

通用表空间,需要通过 CREATE TABLESPACE 语法创建通用表空间,在创建表时,可以指定该表空间。

A. 创建表空间

CREATE TABLESPACE ts_name ADD DATAFILE 'file_name' ENGINE = engine_name;

image.gif

B. 创建表时指定表空间

CREATE TABLE xxx ... TABLESPACE ts_name;

image.gif

4). Undo Tablespaces

撤销表空间,MySQL实例在初始化时会自动创建两个默认的undo表空间(初始大小16M),用于存储undo log日志。

5). Temporary Tablespaces

InnoDB 使用会话临时表空间和全局临时表空间。存储用户创建的临时表等数据。

6). Doublewrite Buffer Files

双写缓冲区,innoDB引擎将数据页从Buffer Pool刷新到磁盘前,先将数据页写入双写缓冲区文件

中,便于系统异常时恢复数据。

image.gif 编辑

7). Redo Log

重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log,前者是在内存中,后者在磁盘中。当事务提交之后会把所

有修改信息都会存到该日志中, 用于在刷新脏页到磁盘时,发生错误时, 进行数据恢复使用。 以循环方式写入重做日志文件,涉及两个文件:

image.gif 编辑

image.gif 编辑

4.2.3 后台线程 image.gif 编辑

InnoDB的后台线程中,分为4类,分别是:Master Thread IO ThreadPurge Thread

Page Cleaner Thread

1). Master Thread

核心后台线程,负责调度其他线程,还负责将缓冲池中的数据异步刷新到磁盘中, 保持数据的一致性,还包括脏页的刷新、合并插入缓存、undo页的回收 。

2). IO Thread

InnoDB存储引擎中大量使用了AIO来处理IO请求, 这样可以极大地提高数据库的性能,而IO

Thread主要负责这些IO请求的回调。

image.gif 编辑

我们可以通过以下的这条指令,查看到InnoDB的状态信息,其中就包含IO Thread信息。

show engine innodb status \G;

image.gif

3). Purge Thread

主要用于回收事务已经提交了的undo log,在事务提交之后,undo log可能不用了,就用它来回收。

4). Page Cleaner Thread

协助 Master Thread 刷新脏页到磁盘的线程,它可以减轻 Master Thread 的工作压力,减少阻 塞。

相关文章
|
3月前
|
SQL 关系型数据库 MySQL
深入MySQL底层3-事务与锁机制
本文系统介绍了MySQL事务机制及锁实现原理。主要内容包括:1. 事务基础:定义事务为不可分割的操作集合,详解ACID四大特性(原子性、一致性、隔离性、持久性)及其实现机制,分析脏读、不可重复读、幻读等并发问题及四种隔离级别的解决方案。2. 日志系统:阐述redo log(重做日志)保障持久性的WAL机制,以及undo log(回滚日志)实现原子性和MVCC的逻辑。3. MVCC原理:通过隐藏字段(DB_TRX_ID,DB_ROLL_PTR)、undo log版本链和ReadView实现多版本并发控制,详细
|
3月前
|
消息中间件 存储 Kafka
详解Kafka1-基础使用
Kafka消息队列技术指南 本文系统介绍了Kafka消息队列的核心概念与应用实践。主要内容包括: 消息队列基础 两种模型:生产者-消费者模型和发布-订阅模型 应用场景:异步处理、系统解耦、流量削峰等 Kafka核心架构 重要组件:Broker、Zookeeper、Topic、Partition等 消费者组机制实现负载均衡 消息存储与分区策略 实践操作 集群搭建与环境配置 基准测试方法与性能指标 Java API编程示例(生产者/消费者) 事务编程实现原子操作 高级特性 生产者幂等性原理与配置 事务API与隔
|
3月前
|
人工智能 自然语言处理 Java
大模型应用开发5-SpringAIalibaba实战
本文介绍了SpringAIAlibaba开源项目,该项目基于SpringAI构建,为阿里云通义系列模型提供Java开发实践。主要内容包括: 基础使用:配置模型API、依赖引入、调用示例,支持同步和流式调用; 多种集成方式:对接本地Ollama模型、ChatClient高级API、SSE流式输出; 核心功能实现:提示词模板、结构化输出、持久化内存、文本生成图片/语音; 高级能力:向量数据库、RAG增强检索、工具调用(Tool Calling); MCP协议:标准化工具调用方案,实现服务端工具共享;
|
3月前
|
监控 Java Maven
深入浅出地理解SpringBoot自动装配原理
摘要: SpringBoot通过起步依赖和自动配置简化了JavaWeb开发。起步依赖(如spring-boot-starter-web)集成关联依赖,减少手动配置;自动配置基于@Conditional条件装配Bean,避免繁琐声明。文章解析了自动配置源码(如@EnableAutoConfiguration导入AutoConfigurationImportSelector),并演示如何手写自定义starter。
|
3月前
|
存储 人工智能 NoSQL
大模型应用开发3-LangChain4j实战
本文介绍了LangChain4j框架的使用方法,主要包括以下内容:1. 基础配置:创建SpringBoot项目并配置OpenAI聊天模型;2. AIServices工具类:简化模型调用,支持流式和阻塞式两种调用方式;3. 会话记忆功能:实现多轮对话记忆,支持会话隔离和Redis持久化存储;4. RAG检索增强:通过向量数据库存储和检索专业领域知识,提升大模型回答质量;5. Tools工具:通过Function Calling机制实现业务功能调用。文章详细讲解了每个功能的实现步骤,包括代码示例和配置方法,帮助
1767 3
|
3月前
|
消息中间件 存储 监控
详解Kafka2-进阶机制
本文摘要: 文章详细介绍了Kafka的分区与副本机制,包括生产者分区写入策略(轮询、随机、按key分配、自定义)、消费者组Rebalance机制及其影响,以及消费者分区分配策略(Range、RoundRobin、Sticky)。同时,阐述了Kafka的副本机制、数据存储形式(Segment结构)、消息不丢失机制(生产者ACK、消费者Offset管理)和数据积压问题。此外,还介绍了Kafka的数据清理策略(日志删除与压缩)、配额限速机制(Quotas)及监控工具Kafka-Eagle的使用方法。通过原理分析与
|
3月前
|
存储 JSON 自然语言处理
大模型应用开发-LangChain框架基础
本文摘要: 文章系统介绍了大模型技术应用与开发的全流程,涵盖云端/本地模型部署、Prompt工程、LangChain框架及RAG项目实战。主要内容包括: 模型部署 阿里云百炼平台API接入与安全配置 Ollama本地模型部署方案 OpenAI兼容SDK的多平台调用方法 Prompt工程 Zero-shot/Few-shot提示技巧 金融文本分类/信息抽取实战案例 JSON数据结构处理与模板设计 LangChain框架 组件化架构:Models/Prompts/Memory/Vectorstores 链式调用
1162 12
|
3月前
|
人工智能 NoSQL Java
大模型应用开发2-SpringAI实战
本文介绍了SpringAI框架如何整合大语言模型,并详细讲解了应用开发的关键技术。主要内容包括: 核心功能 支持OpenAI、Ollama等主流平台 封装对话模型、向量计算等功能 提供同步/异步调用方式 关键技术实现 会话记忆管理(内存/Redis) 工具调用(Function Calling) 知识增强(RAG)架构 多模态交互(文本/图像) 典型应用场景 文献阅读助手实现 智能客服系统 文档知识库问答 开发实践 配置向量数据库 处理PDF文档 实现工具调用 兼容阿里云平台 该框架显著简化了大模型应用开发
|
3月前
|
NoSQL 应用服务中间件 Redis
Ubantu下的几个常用部署
在Ubuntu系统中设置本地Redis的密码涉及几个步骤,主要是编辑Redis的配置文件,并重启Redis服务以应用更改。4、在宿主机连接redis 根据以上步骤安装启动redis后,默认只能在虚拟机内访问redis,如果在其他机器上访问,需要修改配置文件。在终端输入redis-cli,会进入到redis的命令行模式,这时候就可以愉快地使用redis的各种命令了。默认情况下,redis的配置文件在/etc/redis/redis.conf,打开这个文件,将下面内容修改。默认情况下,这一行是被注释掉的。
|
3月前
|
SQL 关系型数据库 MySQL
深入MySQL底层2-SQL优化与数据库运维管理
本文系统介绍了MySQL数据库优化与集群部署的关键技术。主要内容包括:1. SQL优化技巧:插入数据优化、主键设计原则、order by/group by/limit/count/update等操作的性能优化方法。2. 日志管理:详细解析错误日志、二进制日志、查询日志和慢查询日志的配置与使用。3. 主从复制:阐述MySQL主从复制原理,提供主库和从库的详细配置步骤。4. 分库分表:介绍垂直拆分和水平拆分策略,分析MyCat中间件的实现技术及分片规则。5. 读写分离:讲解一主一从和双主双从架构的实现方法。全文