**Connectors**
:用于支持各种语言与 **SQL**
交互;**Management Services & Utilities**
:系统管理和控制工具(备份恢复、MySQL
复制、集群等);**Connection Pool**
:连接池,用户管理需要缓冲的资源(用户密码、权限、线程等);SQL Interface
:用于接收用户的 **SQL**
命令并返回用户需要的查询结果;**Parser**
:用于解析 **SQL**
语句;**Optimizer**
:查询优化器**Caches & Buffers**
:查询缓冲,除了行记录的缓存,还有表、**Key**
、权限缓存等;**Pluggable Storage Engines**
:插件式存储引擎,主要提供 **API**
给服务层使用,与具体的文件交互。可以把
**MySQL**
分为与客户端交互的连接层、执行操作的服务层和与硬件交互的存储引擎层
**MySQL**
服务器的 **3306**
端口时,就需要与服务端建立连接;在连接层中就完成所有连接的管理、客户端的身份和权限验证;**SQL**
语句传递给服务层;在服务层中会做查询缓冲的判断、根据 **SQL**
调用相对应的接口以及对 **SQL**
语句进行语法和词法的解析;最后在 **MySQL**
** **的底层会根据一些规则对 **SQL**
语句进行优化后交给执行器去执行;**MySQL**
中数据存放的地方,**MySQL**
里支持不同的存储引擎。
**MySQL**
支持多种通信协议,可以使用同步和异步的方式,还支持长连接和短连接;当使用**Linux**
服务器去登录**MySQL**
并没有指定**-h**
参数情况下,使用的是**socket**
方式登录。如果指定了**-h**
参数情况下就是使用的**TCP/IP**
协议。只能在**Windows**
上使用的通信方式还有命名管道和内存共享的方式。
单工:在两台计算机通信时,数据的传输方向是单向的;
半双工:在两台计算机之间的数据传输是双向的;在这个通讯连接里,同一时间只能有一台服务器在发送数据
全双工:在两台计算机之间的数据传输是双向的;并且可以同时传输。
在 **MySQL**
中使用的是半双工的通信方式,在客户端给服务端发送数据时,服务端不能想客户端发送数据,也就是说两个动作不能同时发生。所以在客户端发送 **SQL**
语句到服务端需要一次性发送,而不能分成小块发送。在使用 **MyBatis**
动态 **SQL
**生成一个批量插入的语句时,生成的语句太长会出现问题;这时就需要调整在 **MySQL**
中的参数配置 **max_allowed_packet**
值,因为他默认是 **4M**
,如果不调整就会出错。另外,对于服务端来说;不能在取得想要的数据时中断 **MySQL**
的操作,因为会对网络和内存产生大量的消耗。所以在程序里避免不带 **limit**
的操作,可以先 **count**
一下,如果有数据量就分批查询。
**SQL**
的执行时间;**SQL**
的执行都需要单独建立一个连接,避免数据混乱;但这样会给服务端增加压力(当创建新的连接的时候也会创建一个线程,线程之间切换会占用大量的 **CPU**
资源),除此之外,异步通信还带来了编码的复杂度。当要使用异步的时候就必须要使用连接池,排队从连接池获取连接而不是创建新的连接;一般情况下连接数据库都是同步连接。**MySQL**
同时支持长连接和短连接:
**MySQL**
服务器会断开(默认 8
小时)。-- 非交互式超时时间
show global variables like 'wait_timeout';
-- 交互式超时时间
show global variables like 'interactive_timeout';
-- 查看 MySQL 当前有多少个连接
-- Threads_cached:缓存中的线程连接数
-- Threads_connected:当前打开的连接数
-- Threads_created:为处理连接创建的线程数
-- Threads_running:非睡眠状态的连接数,通常指并发连接数
show global status like 'Threads_%';
-- 通过命令查看 MySQL 5.7 版本中默认的最大连接数是 151 个,最大可以设置成 16384(2^14)
-- show 命令默认是 session 级别,全局 global 级别;set 后重启会失效,可以通过修改 mycnf 让配置永久生效
show variables like 'max_connections';
**MySQL**
的内部自带了一个缓存模块;缓存主要是把数据以 **Key-Value**
的形式放到内存中,这样可以加快数据的读取速度,也可以减少服务器的处理时间。 **MySQL**
的缓存默认是关闭的,可以通过以下语句查看。show variables like 'query_cache%';
**MySQL**
中把自带的缓存给关闭的原因主要是因为 **MySQL**
中的缓存应用场景有限。在 **MySQL 8.0**
中的查询缓存已经被移除了,使用 **MySQL**
缓存的要求:
**SQL**
语句必须完全一致;**Paser**
解析器; **Paser**
解析器主要是对语句基于 **SQL**
语法进行语法和词法的解析:
**SQL**
语句拆分成一个个的单词;**SQL**
做一些语法的检查(单引号是否闭合,关键字是否错误等),然后根据 **MySQL**
定义的语法规则,然后根据 **SQL**
语句生成一个数据结构。**SQL**
语句时,但表或字段不存在的情况下,还是会在解析的时候报错。在解析 **SQL**
语句的环节中有预处理器;首先会检查生成的解析树,解决解析器无法解析的语义。预处理后会得到一个新的解析树。**MySQL**
中对一条 **SQL**
语句是有很多种执行方式,最终都是返回一致的结果;而选择执行方式的判断标准是根据 **MySQL**
的查询优化器模块,它的作用就是根据解析树生成不同的执行计划,然后选择一种最优的执行计划;在 **MySQL**
里使用的是基于开销的优化器,是选择使用开销最小的执行计划。-- 查看查询的开销
show status LIKE 'Last_query_cost';
-- 查看优化器的追踪状态
SHOW VARIABLES LIKE 'optimizer_trace';
-- 开启优化器的追踪
SET optimizer_trace='enabled=on';
-- 执行一条查询语句,然后查询优化器会生成执行计划
SELECT fileds FROM `table` WHERE condition;
-- 优化器分析的过程记录到系统表中,查询系统表中的优化器的追踪数据,保存的是 JSON 数据
-- 分为准备阶段、优化阶段和执行阶段
SELECT * FROM information_schema.optimizer_trace
-- 分析完后关闭
set optimizer_trace="enabled=off";
-- 再次查看是否关闭成功
SHOW VARIABLES LIKE 'optimizer_trace';
**MySQL**
中提供了一个执行计划的工具,在 **SQL**
语句前加上 **EXPLAIN**
就可以查看到执行计划的信息。EXPLAIN SELECT fileds FROM `table` WHERE condition;
**API**
去使用执行计划操作存储引擎,最后把数据返回给客户端。**MySQL**
中创建的每一张表都可以指定它的存储引擎,存储引擎的使用是以表为单位。-- 查看指定表的存储引擎
show table status from `tableName`;
-- 查看数据库存放数据的路径
show variables like 'datadir';
**MySQL**
的存放数据的路径中可以看到每个存储引擎都有一个 **.frm**
文件,该文件是表结构的定义文件。不同的存储引擎存储数据的方式是不一致的:
**InnoDB**
引擎产生 **1**
个文件;**MyISAM**
引擎 产生 **2**
个文件;**Memory**
引擎不产生文件;**MySQL 5.5**
版本之前默认使用的是 **MyISAM**
存储引擎,而在 **MySQL 5.5**
之后的默认存储引擎就修改为了 **InnoDB**
;主要原因是因为 InnoDB
支持事务和行级别的锁,更适合对于业务一致性要求比较高的情况。**SHOW ENGINES;**
命令查看数据库对存储引擎的支持情况;其中有存储引擎的描述、对事务、**XA**
协议以及 **Savepoints**
的支持:
**XA**
协议用来实现分布式事务(本地资源管理器、事务管理器)**SavePoints**
用来实现分布式子事务(嵌套事务),在创建 **SavePoints**
后,事务就可以回滚到这个保存点,不会影响到创建保存点之前的操作。Feature | MyISAM | Memory | InnoDB | Archive | NDB |
---|---|---|---|---|---|
B-tree indexes | Yes | Yes | Yes | No | No |
Backup/point-in-time recovery (note 1) | Yes | Yes | Yes | Yes | Yes |
Cluster database support | No | No | No | No | Yes |
Clustered indexes | No | No | Yes | No | No |
Compressed data | Yes (note 2) | No | Yes | Yes | No |
Data caches | No | N/A | Yes | No | Yes |
Encrypted data | Yes (note 3) | Yes (note 3) | Yes (note 4) | Yes (note 3) | Yes (note 3) |
Foreign key support | No | No | Yes | No | Yes (note 5) |
Full-text search indexes | Yes | No | Yes (note 6) | No | No |
Geospatial data type support | Yes | No | Yes | Yes | Yes |
Geospatial indexing support | Yes | No | Yes (note 7) | No | No |
Hash indexes | No | Yes | No (note 8) | No | Yes |
Index caches | Yes | N/A | Yes | No | Yes |
Locking granularity | Table | Table | Row | Row | Row |
MVCC | No | No | Yes | No | No |
Replication support (note 1) | Yes | Limited (note 9) | Yes | Yes | Yes |
Storage limits | 256TB | RAM | 64TB | None | 384EB |
T-tree indexes | No | No | No | No | Yes |
Transactions | No | No | Yes | No | Yes |
Update statistics for data dictionary | Yes | Yes | Yes | Yes | Yes |
**MyISAM**
** 存储引擎中有 **3**
个文件(.frm/.MYI/.MYD
**)并且是 **MySQL 5.5**
之前的默认存储引擎;其应用范围比较小,表级锁限制了读和写的性能;在 **Web**
和数据仓库配置中,通常用于只读或以读为主的工作。适合只读之类的数据分析的项目;
.MYD
:是 **MyISAM**
存储引擎中存放数据记录的文件;.MYI
:是 **MyISAM**
存储引擎中存放索引的文件;
**MyISAM**
存储引擎的 **B+ Tree**
里,叶子节点存储的是数据文件对应的磁盘地址,所以从索引文件 **.MYI**
中找到键值后就会到数据文件 **.MYD**
中获取相应的数据记录。**MyISAM**
存储引擎中的辅助索引也在 **.MYI**
文件里,辅助索引和主键索引存储和检索数据的方式是一致的,也在索引文件中找到磁盘地址,然后到数据文件中获取数据。**count**
速度更快);**InnoDB**
存储引擎中有 **2**
** **个文件(**.frm/.ibd**
)并且是 **MySQL 5.5**
之后的默认存储引擎;它是一个事务安全的 **MySQL**
存储引擎,具有提交、回滚和崩溃恢复功能来保护用户的数据。**InnoDB**
中的行级锁和 **Oracle**
的一致,对于非锁读提高了多用户并发性和性能;**InnoDB**
将用户数据存储在聚集索引中,用于减少基于主键的常见查询的 **I/O**
。为了保证数据的完整性,**InnoDB**
还支持外键引用完整性的约束。**InnoDB**
里是以主键为索引来组织数据的存储,所以索引文件和数据文件是同一个文件,都在 **.ibd**
文件里面。在 **InnoDB**
的主键索引的叶子节点上直接存储了数据。在 **InnoDB**
中的聚集索引(聚簇索引)是指索引键值的逻辑顺序和表数据行的物理存储顺序是一致的;它组织数据的方式叫做聚集索引组织表,所以主键索引就是聚集索引,非主键都是非聚集索引。**InnoDB**
存储引擎中主键索引和辅助索引有主次关系;辅助索引存储的是辅助索引和主键值,当使用辅助索引查询时,就会根据主键值在主键索引中查询,最终取得数据。比如在 **name**
字段上创建一个索引,然后查询 **name = 'John'**
,它会在叶子节点找到主键值,也就是 **id = 1**
,然后再到主键索引的叶子节点拿到数据。在辅助索引里存储的是主键值而不是主键的磁盘地址的原因是 **B Tree**
有分叉与合并的操作,这时键值的地址会发生变化,所以在辅助索引里不存存储地址。**InnoDB**
会选择主键作为聚集索引;**InnoDB**
就会选择第一个不包含有 **NULL**
值的唯一索引作为主键索引;**InnoDB**
会选择内置 **6**
个字节长的 **Rowid**
作为隐藏的聚集索引,它会随着记录的写入而主键递增。**MVCC**
);**I/O**
,提高查询效率;**Memory**
存储引擎中只有一个文件并将所有的数据存储在 **RAM**
中;为了在需要快速查找非关键数据的环境中快速地访问。该引擎也称之为堆引擎,它的使用率正在减少。在 **InnoDB**
的缓冲池内存区域中提供了一种通用和持久的方式去将大部分或所有数据保存在内存中,而 **ndbcluster**
为大型分布式数据集提供了快速 **KV**
查找。其特点是:
CSV
存储引擎中有 **3**
**个文件,它的表是带逗号分隔值的文本文件;**CSV**
表允许使用 **CSV**
格式导入或转储数据,以便与读写相同格式的脚本和应用程序交换数据。因为在 **CSV**
表中没有索引,所以通常在操作时将数据保存在 **InnoDB**
表中,并且只有在导入导出节点使用 **CSV**
表。它的特点是:
**CSV**
格式是通用的,可以直接编辑,适合在不同数据库之间导入导出。**Archive**
存储引擎中只有 **2**
个文件,这些紧凑并未索引的表用于存储和检索大量很少引用的历史、存档以及安全审计信息。它的特点是:
**update、delete**
操作**InnoDB**
存储引擎;**MyISAM**
存储引擎;**Memory**
存储引擎;**update**
操作包括了 **insert、delete、update**
,更新和查询的流程基本一致;也是需要经过解析器、优化器的处理,最后交给执行器;它们之间的区别是拿到符合条件的数据后的操作。**InnoDB**
存储引擎的数据都是放置在磁盘上的,**InnoDB**
操作数据有一个最小的逻辑单位(页:索引页和数据页);由于磁盘的速度太慢,所以对数据不是每次都直接操作磁盘。在 **InnoDB**
存储引擎中使用了一种缓冲池的技术,把磁盘读取到的页放到一块内存区域中 **Buffer Pool**
。下次读取相同的页会先判断是否在缓冲池中,如果是就直接读取,不用再次访问磁盘。在修改数据时,先修改缓冲池中的页;内存的数据页和磁盘数据不一致时,就称之为脏页。在 **InnoDB**
中有指定的后台线程把 **Buffer Pool**
的数据写入到磁盘中,每隔一段时间就一次性地把多个修改写入磁盘,这个动作称之为刷脏。**Buffer Pool**
是 **InnoDB**
中非常重要的一个结构,其内部主要分为 **Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer**
。**Buffer Pool**
:主要对页面信息(数据页、索引页)进行缓存;可以查看 **Buffer Pool**
状态信息,其默认大小是 **128M**
。当内存中的缓冲池写满了后,**InnoDB**
存储引擎会采用 **LRU**
算法来管理缓冲池,经过淘汰的数据就是热点数据。内存缓冲区对于读写上提升了很大的性能。-- 查看 Buffer Pool 信息
SHOW STATUS LIKE '%innodb_buffer_pool%';
**Change Buffer**
:在 **MySQL 5.5**
之前称之为 **Insert Buffer**
插入缓冲,现在叫做 **Change Buffer**
。当这个数据页不是唯一的索引时,不存在数据重复的情况,也就不需要从磁盘中加载索引页判断数据是否重复(检查唯一性)。在这种情况下为了提升更新语句的执行速度,可以先把修改记录在内存的缓冲池中。在最后把 **Change Buffer**
记录到数据页的操作称之为 **merge**
; 当访问这个数据页、或者通过后台线程、或数据库 **shut down、redo log**
写满时就触发。当数据库中的大部分索引都是非唯一索引且业务是写多读少的情况下,不会在写数据后立刻读取时,就可以使用 **Change Buffer**
。-- 查看 Change Buffer 占用 Buffer Pool 的比例:默认 25%
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';
**Adaptive Hash Index**
:
**Log Buffer**
:当 **Buffer Pool**
中的脏页还没刷入到磁盘时,数据库就宕机或重启,这些数据就为被丢失。当写操作写到一半时,甚至可能会破坏数据文件,从而导致数据库不可用。为了规避该问题,**InnoDB**
存储引擎中对所有页面的修改操作写入到一个日志文件中,并且在数据库启动时从这个文件进行恢复操作;主要使用它来实现事务的持久性。该文件就是磁盘的 **redo log**
,对应于 **ib_logfile0、ib_logfile1**
文件,每个文件的大小是 **48M**
。这个主要是先把操作写入日志,然后再写磁盘。
-- innodb_log_file_size:指定每个文件的大小,默认 48M
-- innodb_log_files_in_group:指定文件的数量,默认为 2
-- innodb_log_group_home_dir:指定文件所在路径,相对或绝对;不指定时为 datadir 路径
show variables like 'innodb_log%';
**DB file**
的原因是因为刷盘是随机 **I/O**
,而记录日志是顺序 **I/O**
,顺序 **I/O**
的效率比随机 **I/O**
高,所以先把修改写入日志就可以实现延迟刷盘的时机来提升系统的吞吐。先看一下随机 **I/O**
和顺序 **I/O**
。
**I/O**
:磁盘最小的组成单元是扇区,通常是 **512 byte**
;操作系统和内存交互的最小单位是 **page**
;操作系统和磁盘交互的最小单位是 **block**
。当所需要的数据是随机分散在不同页的不同扇区时,就要找到相应的数据需要等到磁臂旋转到指定的也,然后盘片寻找对应的扇区才能找到我们所需要的一块数据;一次进行此过程直到找完所有的数据,它的读取速度比较慢。**I/O**
:当已经找到了第一块数据且其他所需要的数据就在这块数据的后面时,不要重新寻址就可以依次拿到我们所需要的数据。**redo log**
也不是每次都直接写入到磁盘中,在 **Buffer log**
中有一块内存区域 **Log Buffer**
用来保存即将要写入日志文件的数据,默认大小是 **16M**
,它可以节省磁盘 I/O
。可以通过下面命令查看大小:-- 查看 Log Buffer 大小
SHOW VARIABLES LIKE 'innodb_log_buffer_size';
**redo log**
中的内容主要是用于崩溃恢复。磁盘的数据文件中的数据来自 **Buffer Pool**
。**redo log**
是写入磁盘而不是写入数据文件。当写入数据到磁盘时,操作系统本身是有缓存的,**flush**
操作就是把操作系统的缓冲区写入到磁盘。而 **Log Buffer**
写入磁盘时由一次参数进行控制的:默认为 **1**
。-- 0 表示延迟写:log buffer 每秒一次地写入 log file 中且 log file 的 flush 操作同时进行;
-- 当事务提交时,不会主动触发写入磁盘的操作;
-- 1 表示默认,实时写和刷:每次事务提交时, MySQL 都会把 log buffer 的数据写入 log file且刷到磁盘中
-- 2 表示实时写,延迟刷:每次事务提交时 MySQL 都会把 log buffer 的数据写入 log file;
-- 但 flush 操作并不会同时进行,MySQL 会每秒执行一次 flush 操作
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
**redo log**
又分为内存和磁盘两部分,其特点是:
**redo log**
是 **InnoDB**
存储引擎实现的,并不是所有存储引擎都拥有;**redo log**
的大小是固定的,前面的内容会被覆盖。**check point**
表示当前要覆盖的位置,当 **write pos**
与 **check point**
重叠时,说明 **redo log**
已满,这时就需要同步 **redo log**
到磁盘中。[表空间](https://dev.mysql.com/doc/refman/5.7/en/innodb-on-disk-structures.html)可以看成 `**InnoDB**` 存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。
**InnoDB**
存储引擎有一个共享表空间(系统表空间),在系统表空间中主要包含 **InnoDB**
数据字典、双写缓冲区、**Change Buffer**
和 **Undo Log**
;当没指定 **file-per-table**
时也包含了用户创建的表和索引数据。**InnoDB**
和操作系统的页大小不一致,**InnoDB**
页大小一般为 **16KB**
,而操作系统页大小为 **4K**
;所以 **InnoDB**
的页需要分成 **4**
次写入到磁盘。
**undo log**
是独立的表空间;**InnoDB**
存储引擎的特性。**partial page write**
,可能会导致数据丢失。虽然有 **redo log**
,但这个页本身已经损坏的情况下,使用它来做崩溃恢复是毫无意义的;所以在对于应用 **redo log**
前需要一个页的副本。如果出现了写入失效情况下,就使用页的副本来还原这个页,然后再应用 **redo log**
。这个页的副本其实就是 **InnoDB**
中的双写技术 **double write**
,通过该方式实现了数据页的可靠性。**double write**
与 **redo log**
类似,都是由两部分组成:一部分是内存的 **double write**
、一部分是磁盘的 **double write**
。因为 **double write**
是顺序写入的,所以不会带来很大的性能上开销。在默认的情况下,所有的表共享一个系统表空间,这个文件会越来越大,而且它的空间不会收缩。show variables like 'innodb_doublewrite';
**.ibd**
文件,该文件主要是存放表的索引和数据的。但是其他类型的数据(回滚信息、插入缓冲索引页、 系统事务信息、二次写缓冲等)还是存放在原来的共享表空间内。-- 查看表空间
SHOW VARIABLES LIKE 'innodb_file_per_table';
-- 创建一个通用的表空间
create tablespace ts2673 add datafile '/var/lib/mysql/ts2673.ibd' file_block_size=16K engine=innodb;
-- 在创建时可以指定表空间,使用 ALTER 修改表空间可以转移表空间,不同表空间的数据是可以移动的
create table t2673(id integer) tablespace ts2673;
-- 删除表空间是需要先删除里面所有的表
drop table t2673;
drop tablespace ts2673;
**ibtmp1**
文件,当数据服务器正常关闭时,该表空间被删除,下次重新产生**undo log**
来实现回滚操作(保持原子性)。在执行 **undo**
时,只是把数据从逻辑上恢复到事务发生前的状态,而不是从物理页面上操作实现的,属于逻辑格式的日志。**redo log**
和 **undo log**
统称为事务日志;**undo log**
的数据默认存放在系统表空间 **ibdata1**
文件中,因为共享表空间不会自动收缩,也可以单独地创建一个 **undo**
表空间。-- 查看 undo 表空间
show global variables like '%undo%';
**master thread**
:负责刷新缓存数据到磁盘并协调调度其他的后台进程;**IO thread**
:分为 **insert buffer、log、read、write**
进程,分别用来处理 **insert buffer**
、重做日志、读写请求的 **I/O**
回调;**purge thread**
:用于回收 **undo**
页**page cleaner thread**
: 用于刷新脏页**[Binary Log](https://dev.mysql.com/doc/refman/5.7/en/binary-log.html)**
是以事件的形式记录了所有的 **DDL、DML**
语句操作而不是数值,所以 **Binary Log**
属于逻辑日志;Binary Log
与 **redo log**
不同,它的文件内容是可以追加的且没有大小限制。一般可以使用 **Binlog**
** **实现的功能有:
**Binary Log**
功能的情况下,可以把它导出成 **SQL**
语句并重新执行一遍;**binlog**
后再执行一遍。更新操作的流程: **SQL 语句:UPDATE person SET name = John where pid = 1;**
,原 **name = Sky**
。
**Server**
的执行器;**John**
** **后再调用引擎的 **API**
接口写入这条数据到内存中;**name = Sky**
到 **undo log**
中和 **name = John**
到 **redo log**
中;这时 **redo log**
就进入到 **prepare**
状态,然后通知执行器,执行完成可以提交事务;**binlog**
,然后调用存储引擎的 **API**
设置 **redo log**
为 **commit**
状态;