mysql知识总结 基础篇
Mysql知识总结
- 1. 执行一条sql语句 期间发生了什么?
- 1. 如何查看mysql服务被多少个客户端链接了
- 2. 空闲链接会一直闲置嘛?
- 3. mysql的链接数量有限制嘛?
- 4. 我们如何知道mysql要使用哪个索引
- 5. 什么是覆盖索引
- 2. MySQL 一行记录是怎么存储的?
- 1. mysql的数据存储放在哪个文件?
- 2. 表结构的空间是什么样的
- 3. innodb的默认行格式 是什么
- 4. 记录格外的数据
- 5. 记录真实的数据
- 6. varchar(n)中的n最大取值
- 7. 行溢出后 mysql是如何处理的
本文是阅读 小林coding 后的读书笔记
原文可以点击上面超链接到达
也可以直接百度搜索 小林coding
1. 执行一条sql语句 期间发生了什么?
这道题目其实问的就是mysql的执行流程嘛
我们可以看到mysql分为两层嘛 一个是server层 一个是存储引擎层
server层复杂负责链接以及sql语句的分析 执行
存储引擎层负责数据的存储和提取
下面我们一步步来看它是怎么执行的嘛
第一步 建立链接
首先第一步是链接 我们mysql的客户端输入账号密码会链接mysql的服务器嘛 它底层是基于tcp协议 如果链接失败就会报错的
然后如果账号密码都没有问题 mysql就会保存它的权限并且管理起来 后续用户进行的任何操作都会基于该保存的权限进行操作
总结下连接器的工作
- 和客户端三次握手建立链接
- 验证账号密码是否正确
- 读取用户的权限 并且后续操作都基于此权限
第二步 查询缓存
之后第二步就是查询缓存 查询缓存实际上就是分析下当前的sql语句 看啊可能之前有没有执行过 如果执行过把缓存的数据返回给连接器 之后返回给客户端嘛
但是这个操作实际上是很鸡肋的 缓存要在多次使用同一条语句的时候才会有比较大的作用
但是一旦表进行了更新操作 这个缓存就会被清楚 相当于缓存了个寂寞
所以说mysql的8.0版本直接将这一步给删除掉了
对于之前的版本 我们也可以通过设置参数来解决这个问题
第三步 解析sql
在正式执行sql语句之前 解析器会对于sql语句进行解析 包括语法分析 然后词法分析
词法分析就是将其中的关键字和非关键字提取出来嘛
语法分析就是分析你这个sql语句是否符合sql语法嘛 如果符合的话 就会简历一个语法树
如果我们的sql语句不对 就会在这个阶段报错
第四步 执行sql
它分为预处理 优化 执行
预处理器
预处理阶段会做两件事
- 判断我们要查询的表是否存在
- 把 * 替换为表中的各列
优化器
优化器的主要作用就是将sql语句的执行计划确定下来 比如说表里面有多个索引 然后它会确定用哪个索引
执行器
执行器执行的时候会和存储引擎交互 从存储引擎读取数据返回给连接器
1. 如何查看mysql服务被多少个客户端链接了
我们可以使用show processlist
来查看 mysql服务被多少个客户端链接了
2. 空闲链接会一直闲置嘛?
不会 我们有一个参数 show variables like 'wait_timeout';
来查看空闲时间 如果超过了这个时间就会被断开链接
如何修改这个参数
SET SESSION wait_timeout = 600;
SET GLOBAL wait_timeout = 600;
- 永久修改的话 可以修改配置文件
3. mysql的链接数量有限制嘛?
有的 我们可以使用 show variables like 'max_connections';
来查看这些链接数量闲置
mysql和http一样 也有长短链接的概念 区别如下 我们使用长连接可以减少过程中链接和断开的过程
但是我们维护长连接也会消耗一定的资源嘛 所以说我们也要想办法去解决这个问题嘛
解决问题的方式有两种
- 定期的去断开链接
- 客户端自己主动重制链接 调用
mysql_reset_connection()
4. 我们如何知道mysql要使用哪个索引
我们可以使用 explain 加在一条sql语句之前 来查看sql语句使用了什么索引嘛
它里面有一行是key 如果是null则说明无任何索引 如果是primary则是主键
如果说我们一条查询语句有两个索引 那么编译器就会选择最佳的索引
5. 什么是覆盖索引
覆盖索引是指表中包含了查找所需要的所有字段 不需要再回表查询了
2. MySQL 一行记录是怎么存储的?
1. mysql的数据存储放在哪个文件?
mysql的数据自然是保存在磁盘中嘛 关于具体保存在哪里 这个要根据存储引擎来区分
以为innob为例 我们每创建一个database数据库 他就会创建一个名叫datebase的文件
我们每创建一个表 实际上就是再datebase目录中再创建一个文件(表名)
当我们进入这个目录的时候我们会发现里面有三个文件
分别是
- db.opt
- name.frm
- name.idb
其中opt文件用来存储默认字符集和校验规则
frm文件用来保存表结构的元数据
而文件的表数据则会保存在 ibd 文件中
2. 表结构的空间是什么样的
表结构的空间如下
表由许多段组成 段由许多区组成 区由许多页组成 页里面有许多行数据
- 行
我们的每条记录就是一行 每行记录根据不同的格式有不同的存储结构
- 页
虽然说我们的存储是根据行来存储的 但是如果根据行读取的话效率就太低了 所以说innodb数据是根据页来存储的 页的大小有16kb
页是管理的最小单元 也就是说每次最少从磁盘中读取16kb的数据
- 区
我们都知道innodb是用b+数来组织存储数据的
B+树的每一层都是通过双向链表链接起来的 如果以页来分配单位的话 那么两个相邻的页的物理位置不是连续的 可能会离得非常远 那么磁盘查询的时候可能就会有大量的随机IO 而随机IO是非常慢的
如何解决这个问题呢? 很简单 让他们的物理空间也连续就好了
所以说当表中数据量很大的时候 就不以页来分配单位了 而是按照区来分配单位 区的大小是1mb左右 大概能容纳64个页
- 段
表空间是由段组成的 而段由多个区组成 段一般分为下面几个部分
- 索引段 存放B+树非叶子节点的区的集合
- 数据段 存放B+树叶子节点的区的集合
- 回滚段 存放的是回滚数据的区的集合
3. innodb的默认行格式 是什么
innodb的默认行格式为 compact
我们可以看到 一条记录可以分为两个部分 记录额外的信息和记录真实的数据
4. 记录格外的数据
记录额外的信息
我们首先来看记录额外的信息 它分为三个部分
- 变长字段长度列表
我们存储数据的时候 需要把这个数据存储的大小给存起来放到变长字段长度列表里面 读取数据的时候才能根据这个变长字段长度列表去读取对应长度的数据
假设我们有这三条记录
其中name和phone是变长字段 长度为11
接下来我们分别看他们是如何存储的
name的真实数据大小是1 phone的真实数据大小是3
他们是按照逆序存放的 这里涉及到一个mysql调优的问题 在下面
行2和行1类似
行3和行1的区别主要是phone值是none null是不会存放在行格式记录的真实数据里面 所以变长字段里面也不会有(置0处理)
mysql调优
msql 有一种潜规则优化,就是靠前的字段有更好的访问性能。
这是mysql对于用户使用策略的一种假设,就是假设用户会更频繁的使用靠前的字段。并且这种策略更符合人的直觉。
优化手段有很多,我这里介绍一种,对于变长字段长度的反向存储优化
在一个行里面变长资源是反向存储的 因为这样更容易让这些数据和对应的真实数据同时存放在一个cpu cauch line中 这样子可以提高cpu canch的命中率
设计库函数是有艺术成分在里面的。
因为有很多trade off的事情需要考虑。
就好比操作系统设计内存分配的页表管理有很多种策略,先进先出还是先进后出等等。
这种行为比较重要的可以通过统计数据来得出。
比较轻度的可以基于一种假设去设计。只要这种假设是符合直觉的,一般都是合理的
再好的库函数,也会在面临不同策略下有不同的优势。
库函数sort再快,你针对单独case优化也可能超过库函数
每个数据库的行格式 都有变长数据列表嘛
不是的 假如说我们没有变长字段的话行格式里面就不会有了
- Null值列表
表中的某些列可能会存储NULL值 如果把这些NULL值都放到记录真实数据中会比较浪费空间 (其实Linux中的很多地方也用到了这种涉及 存在和不存在只需要一个位就可以)
如果存在NULL值 则每个列对应一个二进制比特位 按照顺序逆序存放
每个数据库的行格式都必须要有NULL值列表嘛
NULL值不是必须的 当数据表的字段都定义成NOT NULL的时候 可以至少节省1字节的空间
NULL值列表是固定一字节嘛 如果有一条记录有九条记录没有设置为not null呢?
如果有九条记录的话就null这一字段就对应为2个字节 以此类推
- 记录头信息
记录头信息中的内容很多 这里列举几个
- delete_mask 表示这条数据是否被删除 我们删除一条数据的时候 并不会真正的删除记录 而是会将这个记录的delete_mask标记为1
- next_record 下一条记录的位置
5. 记录真实的数据
我们可以看到 记录真实数据中 除了我们定义的字段 还有额外的三个字段
- row_id
如果说我们没有定义主键或者唯一约束列 那么innode引擎就会生成row_id列
这个列不是必须的 大小为6个字节
- trx_id
表示事务的id 表示数据是由哪个事务生成的 它是必须的 占用六个字节
- roll_pointer
记录指向上一个版本的指针 大小为7个字节
6. varchar(n)中的n最大取值
这里我们要知道一个概念 除了隐藏列和记录投信息列之外 所有列加起来不超过65535字节
知道了这个前提之后我们再去推算 varchar(n)中n的最大取值
前面我们知道了一行记录最大的存储值是65535字节 那么我们使用varchar(65535) 是否可以创建成功呢?
答案是否定的
因为我们这里还有两个列的长度没有进行计算
- 变长字段长度列表
- null值列表
变长字段列表 当允许存储的列表小于255字节 用一个字节表示 大于255用2个字节表示
null值列表占用空间为1字节 变长字段长度列表为2字节 所以说实际上最大的大小为65532字节
7. 行溢出后 mysql是如何处理的
我们知道mysql中交互的基本单位是页 页的大小是16kb 而65535字节是大于16kb的
这个时候一个页存储不了一行的记录 此时就会发生行溢出
当发生溢出的时候在记录真实数据处会保存该列的一部分数据 并且用20字节指向溢出页