(1)MySQL的客户端以及服务端介绍。
(2)多种操作系统下MySQL的安装与启动。
(3)MySQL服务端处理请求的具体过程,如下图所示
(4)常见的存储引擎
主要是一个引入,没什么干货,但这两张图挺好的,展示了请求流程与常见MySQL引擎。
1.存储引擎会影响数据的存储方式,不要轻易更改表的存储引擎。一般开发中常用innodb存储引擎,着重了解这个。
(1)启动命令后添加
例如:
mysql.server start --default-storage-engine=MyISAM
(2)使用配置文件(推荐)
不同系统下mysql的寻找配置文件方式
window系统
类unix
配置文件的编写方法
# server组代表下面的启动选项将作用于所有的服务器程序
[server]
# 禁止tcp
skip-networking
# 使用MyISAM作为存储引擎
default-storage-engine=MyISAM
(1)系统变量的作用范围
全局变量(GLOBAL):全局变量,影响服务器的整体操作。
会话变量(SESSION):会话变量,影响某个客户端连接的操作。(注:SESSION有个别名叫LOCAL)
相关SQL命令:
# 查看default_storage_engine的全局变量
SHOW SESSION VARIABLES LIKE 'default_storage_engine';
# 设置default_storage_engine的会话变量MyISAM
SET SESSION default_storage_engine = MyISAM;
(2)状态变量
MySQL为了让用户能够直观了解服务器的运行情况,维护了许多程序运行状态的变量。状态变量也有全局以及会话的作用范围之分。
查看状态变量的命令如下:
# 查看线程相关的状态变量
SHOW STATUS LIKE 'thread%'
主要介绍了MySQL的启动选项,系统变量以及状态变量的知识。指定启动选项一般有两种方法----启动命令后追加或者写在配置文件中,系统变量,状态变量存在不同的作用范围,我们可以通过系统变量查询MySQL的一些配置项,利用状态变量查询服务器的运行状况。这块知识还是挺有实用价值的。
1.有时候我们需要修改MySQL的某些配置。可以有两种方式来实现,第一种通过修改系统变量(一般这种操作适用于测试环境,不建议使用),第二种通过修改配置文件重启服务。
2.状态变量真的很有用。如果我们需要对MySQL服务进行简单的监控,通过状态变量就可以很快的得知MySQL服务的一些运行状态参数。
(1)字符集以及乱码介绍
为什么需要字符集:计算机底层存储数据都是二进制格式。那么如何存储生活中的一些字符例如’a’呢?我们可以定义一个字符集,里面记录字符以及二进制的对应关系,存储字符时,通过字符集找到对应的二进制表示,存储二进制即可。读取字符时,取出二进制,再根据字符集找到二进制的字符即可。
为什么会有乱码:字符集不对应,编码的字符集和解码的字符集不同。
(2)编程世界常见的字符集
ASCII字符集
共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符。总共才128个字符,使用1个字节来进行编码。我们熟悉的java进行char字符的大小比较,实际上就是默认使用ASCII字符集的十进制比较。
GB2312字符集
收录了大部分汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母。同时这种字符集又兼容ASCII字符集,所以在编码方式采用变长编码:如果该字符在ASCII字符集中,则采用1字节编码;否则采用2字节编码。
GBK字符集
GBK字符集只是在收录字符范围上对GB2312字符集作了扩充,编码方式上兼容GB2312。
utf8字符集
收录地球上能想到的所有字符,而且还在不断扩充。这种字符集兼容ASCII字符集,采用变长编码方式,编码一个字符需要使用1~4个字节。
mysql支持的常用字符集如下:
此外,由于存储数据需要按照顺序,字符集又有着自己的各种排序规则,常见的排序规则有utf8_general_ci(不区分大小写)
(1)运用场景
MySQL有4个级别的字符集和比较规则,分别是:服务器级别,数据库级别,表级别,列级别。我们可以显示指定具体单位的字符集以及比较规则,不指定默认采用上一单位的默认字符集级别。
(2)修改字符集
我们可以通过alter命令修改某个单位的字符集,但是要注意。有些不兼容的字符集转换会发生错误。
涉及到的各个系统变量含义如下:
从发送请求到接收结果过程中发生的字符集转换:
(1)客户端使用操作系统的字符集编码请求字符串,向服务器发送的是经过编码的一个字节串。
(2)服务器将客户端发送来的字节串采用character_set_client代表的字符集进行解码,将解码后的字符串再按照character_set_connection代表的字符集进行编码。
(3)如果character_set_connection代表的字符集和具体操作的列使用的字符集一致,则直接进行相应操作,否则的话需要将请求中的字符串从character_set_connection代表的字符集转换为具体操作的列使用的字符集之后再进行操作。
(4)将从某个列获取到的字节串从该列使用的字符集转换为character_set_results代表的字符集后发送到客户端。
(5)客户端使用操作系统的字符集解析收到的结果集字节串。
主要介绍了字符集在MySQL中的应用,介绍了各种常见的字符集及其在MySQL中的应用。还介绍了MySQL接收请求过程中做的字符集转换。总之一句话,编码,解码字符集要对应,字符集排序规则也要知道。
1.一定要知道自己业务用的数据库都在用哪个字符集,用哪种排序规则。
2.一般不要轻易更改数据的字符集以及排序规则,不兼容的字符集转换会报错或者乱码,更改排序规则可能会引发索引的重建。
3.'order by 字符串字段’要注意,确保排序规则是你想要的。
为什么需要页作为MySQL读取操作最小单位?
由于读取磁盘的速度很慢,如果我们以行为单位1条1条去读取,会产生超高频率的磁盘IO操作。为了减少IO次数,将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。
InnoDB存储引擎设计了4种不同类型的行格式,分别是Compact、Redundant、Dynamic和Compressed行格式,这里以Compact格式为例,其他的格式大同小异。
(1)直观图解
(2)记录的额外信息
变长字段长度列表(只有存在变长字段才有该信息)
在Compact行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表,各变长字段数据占用的字节数按照列的顺序逆序存放。
NULL值列表(只有存在null字段才有该信息)
每个允许存储NULL的列对应一个二进制位,二进制位按照列的顺序逆序排列。二进制位的值为1时,代表该列的值为NULL,否则为0。
记录头信息
它是由固定的5个字节组成。5个字节也就是40个二进制位,不同的位代表不同的意思,如图:
(3)记录的真实数据
记录的真实数据除了c我们自己定义的列的数据以外,MySQL会为每个记录默认的添加一些列(也称为隐藏列),具体的列如下
列名 | 占用空间 | 生成描述 |
---|---|---|
DB_ROW_ID | 6字节 | 表无主键会生成,唯一记录标识 |
DB_TRX_ID | 6字节 | 系统默认添加,事务ID |
DB_ROLL_PTR | 7字节 | 系统默认添加,回滚指针 |
(4)几个小疑问?以varchar(M)展示字段存储规则
存储一个VARCHAR(M)类型的列,到底需要多少存储空间?
(1)真实数据(2)真实数据占用字节的长度(3)NULL值标识,如果该列有NOT NULL属性则可以没有这部分存储空间
M过大比,一页放不下咋办?将记录的真实数据处变为其他页面的地址,用多个页面表示。
本节主要展示了innodb的存储底层,innodb到底是怎么存储我们的行记录。其中innodb以页为基本单位来申请存储空间,每条行记录都会通过额外信息+真实数据来存储。为了更好的节省空间,innodb做了许多优化----null值列表(节省存储null值的空间),变长字段列表等等。感觉这一块开始才深入MySQL底层,开始学干货!!!
1.如果业务能确保某些字段不会出现null值,可以把字段定义为NOT NULL,节省存储空间。
2.建表最好显示定义一个主键,作为记录唯一标识。并且不要将主键作为业务字段。
3.本节出现了很多字段以及设计,但文章并没有详细解释,可以先记录下来,后续再看发现可以与后文呼应。
###(1)数据页结构
其中各字段含义如下
名称 | 占用空间 | 描述 |
---|---|---|
File Header | 38字节 | 页的一些通用信息 |
Page Header | 56字节 | 数据页专有的一些信息 |
Infimum + Supremum | 26字节 | 两个虚拟的行记录 |
User Records | 不确定 | 实际存储的行记录内容 |
Free Space | 不确定 | 页中尚未使用的空间 |
Page Directory | 不确定 | 页中的某些记录的相对位置 |
File Trailer | 8字节 | 校验页是否完整 |
这里关注next_record字段,它表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量。我们可以根据这个字段直接找到下一条记录。下一条记录指得并不是按照我们插入顺序的下一条记录,而是按照主键值由小到大的顺序的下一条记录。而且规定 Infimum记录(也就是最小记录) 的下一条记录就是本页中主键值最小的用户记录,而本页中主键值最大的用户记录的下一条记录就是 Supremum记录(也就是最大记录),所以页中的行记录关系如下:
这里顺序是按照主键大小排序。
前面我们已经知道了页内记录是一个根据主键排序的单向链表,那我们查找某个主键的记录暂时只能通过遍历列表来实现。因为通常一个页的行数据量还是挺多的,innodb为了避免遍历,在页中维护了Page Directory字段,简单来说,innodb会把一定数量行记录作为一个分组,在Page Directory记录每个分组的位置。这样我们先通过Page Directory进行一次查找得到要查询记录所在组,再去组里查询。(相当于二分法先查找组,再去组里找数据)
分组规则如下:每个分组中的记录条数是有规定的:对于最小记录所在的分组只能有 1 条记录,最大记录所在的分组拥有的记录条数只能在 1~8 条之间,剩下的分组中记录的条数范围只能在是 4~8 条之间。所以分组是按照下面的步骤进行的:
初始情况下一个数据页里只有最小记录和最大记录两条记录,它们分属于两个分组。
之后每插入一条记录,都会从页目录中找到主键值比本记录的主键值大并且差值最小的槽,然后把该槽对应的记录的n_owned值加1,表示本组内又添加了一条记录,直到该组中的记录数等于8个。
在一个组中的记录数等于8个后再插入一条记录时,会将组中的记录拆分成两个组,一个组中4条记录,另一个5条记录。这个过程会在页目录中新增一个槽来记录这个新增分组中最大的那条记录的偏移量。
页如图所示:
Page Header主要是记录数据页的状态信息,字段含义如下表
名称 | 占用空间大小 | 描述 |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4字节 | 页的校验和(checksum值) |
FIL_PAGE_OFFSET | 4字节 | 页号 |
FIL_PAGE_PREV | 4字节 | 上一个页的页号 |
FIL_PAGE_NEXT | 4字节 | 下一个页的页号 |
FIL_PAGE_LSN | 8字节 | 页面被最后修改时对应的日志序列位置(英文名是:Log Sequence Number) |
FIL_PAGE_TYPE | 2字节 | 该页的类型 |
FIL_PAGE_FILE_FLUSH_LSN | 8字节 | 仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4字节 | 页属于哪个表空间 |
特别注意FIL_PAGE_PREV,FIL_PAGE_NEXT,这个实际上把所有的数据页变成了一个双向链表。
本章节主要介绍了innodb中的数据页结构,也是后续知识较为核心的知识点。通过组内单向链表,页内双向链表,我们可以发现,innodb实际上按照主键顺序将行记录很好的组织的起来。后续的范围查询,倒序查询,就会体会这种组织方式的优点。
1.因为主键不一定是连续的,只是按照从小到大排序,所以Page Directory实际上就是维护了一个利于二分查找的数据结构。
2.每个页都会存在上一个页和下一个页的编号,所以实际上所有数据页是一个双链表。
3.由于主键是按照从小到大的顺序在底层存储的,所以采用递增的主键生成方法往往要比完全随机的方法要更好。因为这样插入数据时,直接往后续增加即可,减少了底层操作。
(1)前三章主要是MySQL的一个热手知识。有许多实用的小细节知识,熟练掌握可以更好了解MySQL的运行状态。
(2)第四章、第五章开始慢慢深入数据底层存储。这个也是这本书的重点,为是后续章节的基础。里面很多的设计会在后续的章节体现。看懂了行结构,页结构,后续我们就能更好的理解MySQL执行查询的逻辑了。