目录
- [1 装作自己是个小白 ------ 重新认识MySQL](#1 装作自己是个小白 —— 重新认识MySQL)
- [2 MySQL的调控按钮 ------ 启动选项和系统变量](#2 MySQL的调控按钮 —— 启动选项和系统变量)
- [3 乱码的前世今生------字符集和比较规则](#3 乱码的前世今生——字符集和比较规则)
- [4 从一条记录说起------ InnoDB 记录结构](#4 从一条记录说起—— InnoDB 记录结构)
- [5 盛放记录的大盒子 ------ InnoDB 数据页结构](#5 盛放记录的大盒子 —— InnoDB 数据页结构)
- [6 快速查询的秘籍 ------ B+ 树索引](#6 快速查询的秘籍 —— B+ 树索引)
- 索引优化
- [7 好东西也得先学会怎么用 ------ B+ 树索引的使用](#7 好东西也得先学会怎么用 —— B+ 树索引的使用)
- [8 数据的家 ------ MySQL 的数据目录](#8 数据的家 —— MySQL 的数据目录)
- [9 存放页面的大池子 ------ InnoDB 的表空间](#9 存放页面的大池子 —— InnoDB 的表空间)
- [10 条条大路通罗马 ------ 单表访问方法](#10 条条大路通罗马 —— 单表访问方法)
- [11 两个表的亲密接触 ------ 连接的原理](#11 两个表的亲密接触 —— 连接的原理)
- [12 谁最便宜就选谁 ------ MySQL 基于成本的优化](#12 谁最便宜就选谁 —— MySQL 基于成本的优化)
- [13 兵马未动,粮草先行 ------ InnoDB 统计数据是如何收集的](#13 兵马未动,粮草先行 —— InnoDB 统计数据是如何收集的)
- [14 不好看就要多整容 ------ MySQL 基于规则的优化(内含关于子查询优化二三事儿)](#14 不好看就要多整容 —— MySQL 基于规则的优化(内含关于子查询优化二三事儿))
- [15 查询优化的百科全书 ------ Explain 详解(上)](#15 查询优化的百科全书 —— Explain 详解(上))
- [16 查询优化的百科全书 ------ Explain 详解(下)](#16 查询优化的百科全书 —— Explain 详解(下))
- [17 神兵利器 ------ optimizer trace 的神器功效](#17 神兵利器 —— optimizer trace 的神器功效)
- [18 调节磁盘和CPU的矛盾 ------ InnoDB 的 Buffer Pool](#18 调节磁盘和CPU的矛盾 —— InnoDB 的 Buffer Pool)
- [19 从猫爷被杀说起 ------ 事务简介](#19 从猫爷被杀说起 —— 事务简介)
- [20 说过的话就⼀定要办到 ------ redo 日志(上)](#20 说过的话就⼀定要办到 —— redo 日志(上))
- [21 说过的话就⼀定要办到 ------ redo 日志(下)](#21 说过的话就⼀定要办到 —— redo 日志(下))
- [22 后悔了怎么办 ------ undo 日志 (上)](#22 后悔了怎么办 —— undo 日志 (上))
- [23 后悔了怎么办 ------ undo 日志 (下)](#23 后悔了怎么办 —— undo 日志 (下))
- [24 一条记录的多幅面孔 ------ 事务的隔离级别与MVCC](#24 一条记录的多幅面孔 —— 事务的隔离级别与MVCC)
- [25 工作面试老大难 ------ 锁](#25 工作面试老大难 —— 锁)
在系统学习《MySQL是怎样运行的》过程中,写下了这篇学习笔记,会持续更新。
如果有一定的MySQL基础,会基本增删改查语法,可以直接看这本书,如果纯纯0小白,建议先看《MySQL是怎样使用的》
原书链接
在学习过程中配合官方文档食用更佳
MySQL官方文档英文版
MySQL官方文档中文版
1 装作自己是个小白 ------ 重新认识MySQL
日常使用 MySQL 的情景一般是这样的:
- 启动 MySQL 服务器程序。
- 启动 MySQL 客户端 程序并连接到服务器程序。
- 在客户端 程序中输入一些命令语句作为请求发送到服务器 程序,服务器 程序收到这些请求后,会根据请求的内容来操作具体的数据并向客户端返回操作结果。
MySQL的服务器 和客户端 本质上就是一个实例,是计算机中的进程,每次打开一个新的进程,操作系统都会给这个进程分配一个唯一的新进程号和一个名称,这个名称是我们自己定义的,比如MySQL服务器 进程的默认名称就是mysqld,MySQL客户端进程的默认名称就是mysql
安装MySQL
网上安装MySQL的教程一抓一大把,自行学习(踩坑),这里主要唠一些需要注意的地方:
- 安装一个完整的MySQL需要把服务器和客户端都安装好,大部分安装包两者都包含,但Linux下的RPM包需要分别安装两者
- MySQL可以安装在各种各样的操作系统上,不管装在哪,一定要记住安装目录,非常重要!!!!!
- MySQL的安装目录下有一个很重要的bin目录,里面有很多后缀为.exe的可执行文件
如何执行.exe文件?
对于有可视化界面的操作系统来说,鼠标双击就可以执行这些.exe文件(比如最常用的Windows系统就可以双击exe文件来执行);
如果没有可视化界面,可以在命令行(小黑屋)中执行这些.exe文件(Windows系统用cmd打开,UNIX系统用shell打开)
如何用命令行执行.exe文件?
如果没有配置环境变量 ,每次都需要从相对/绝对路径执行.exe文件,就很麻烦。比如你想执行mysqld.exe:要么打开MySQL安装目录下的bin目录,在路径中输cmd,才能正常执行mysqld;
要么直接打开cmd,然后切目录切切切,切到这个bin目录下,再正常执行mysqld;
这两种方法本质上是没区别的,只是切换到bin目录下的方法不一样而已,但都非常麻烦,必须找对路径才能执行这个mysqld.exe。
如果配置了环境变量 ,把bin目录的路径加入到环境变量PATH中,就省事多了,一劳永逸。环境变量PATH就是一系列路径的集合,各个路径之间会隔开,当你在任意目录下的命令行中输入mysqld命令时,系统就会从PATH的一系列路径中依次寻找有没有mysqld这个命令,如果有,就执行mysqld。是不是非常省事,不用每次都纠结路径了。配置环境变量也很简单
启动/关闭MySQL服务器程序
- Windows系统
要么mysqld命令启动
要么把mysqld注册成服务,通过net start/stop MySQL
命令启动/关闭
注册成服务的好处?
操作系统会自动管理它,如果一台计算机需要长时间运行MySQL,且计算机一启动MySQL就需要启动,就赶紧把它注册成服务吧!!!
如何把mysqld注册成服务?假设你已经配置好环境变量了,执行
mysqld --install [-manual][服务名]
命令就可以把mysqld命令注册成服务了[-manual] 参数可以省略,不写表示开机自动启动,写表示开机不自动启动
[服务名] 参数也可以省略,不写是默认的服务名------MySQL
- UNIX系统
mysqld 命令
运行一次启动一个服务器进程,但不常用,因为有更高级的命令
mysqld_safe 命令
间接调用mysqld命令,还会自动启动监控进程,还会生成出错日志,更高级
mysqld.server start/stop 命令
不知道高级在哪,可能是不光可以开启,还能关闭吧,它是一个链接文件,一般安装的时候会自动在bin目录下面创建这个链接文件,如果没有,自己手动创建一个就行
mysqld_multi 命令
可以监控多个服务器
启动/关闭MySQL客户端程序
- 关闭客户端很简单,直接执行
quit / exit / \q 命令
,然后出现Bye
就说明关了 - 开启客户端还是要看bin目录,bin有很多客户端的exe文件,最重要的是mysql.exe,它可以实现客户端和服务器交互,也就是发送请求,接收结果,用法:
mysql -h主机名 -u用户名 -p密码
比如mysql -hlocalhost -uroot -p123456
就启动了一个MySQL客户端,并连接到了一个服务器,服务器是本机localhost,用户名是root,密码是123456。
注意事项:
-h
表示域名/IP地址,如果服务器运行在本机,就可以省略这个参数,或者写成-hlocalhost / -h127.0.0.1
只有一个英文字母的参数前面加一个短横线就行,比如
-h -u -p
,如果大于一个英文字母前面需要加两个短横线,比如--host --user --password
,但效果是一样的,所以-hlocalhost
等价于--host=localhost
,其它也一样最好不要直接
mysql -hlocalhost -uroot -p123456
输入密码,这样是明文,不安全,先执行mysql -hlocalhost -uroot -p
,会弹出来Enter password:
,这个时候再输入密码,显示的就是加密的星号,更安全此外,mysql命令的各个参数的顺序没有硬性规定
连接客户端和服务器
客户端和服务器启动成功后,下一步就要把它们两个连接
MySQL客户端和服务器其实就是两个进程,所以它们的连接本质上就是进程通信而已。
问题就可以转化为:进程的通信方式?主要有三种
- TCP/IP
MySQL服务器在启动的时候可以向操作系统申请一个端口号(0~65535的整数),客户端可以通过IP地址+端口号
的方式和服务器连接通信
服务器启动的时候会自动默认申请3306端口号,也可以用
mysqld -P3307 命令
自定义端口号。同样,客户端在启动的时候会默认连接3306端口,也可以用
mysql -hlocalhost -uroot -P3307 -p 命令
自定义端口号(注意区分大小写,大写的P才是端口号,小写的p是密码!!!)
- 命名管道和共享内存
Windows系统可以用这种方法
命名管道:在启动服务器的命令中加上--enable-named-pipe
参数,然后在启动客户端程序的命令中加上--pipe
或者--protocol=pipe
参数
共享内存:在启动服务器的命令中加上--shared-memory
参数,然后在启动客户端程序的命令中加上--protocol=memory
参数。但是这种方法前提条件是客户端和服务器在同一台主机中。 - Unix域套接字文件
我暂时不用unix系统,先不研究了
服务器处理请求
客户端和服务器连接成功而且可以正常通信后,服务器就会处理一堆堆客户端发来的请求
这三部分就是服务器处理客户端流程的大致过程
连接管理
这块涉及了很多知识,比如线程池、限流、认证、数据传输安全、文本消息...先说个大概,后续再展开
线程池 :每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互。当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来。另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。
限流 :线程分配的太多会严重影响系统性能,所以也需要限制可以同时连接的客户端数量
认证 :在客户端程序发起连接的时候,需要携带主机信息、用户名、密码,服务器程序会对客户端程序提供的这些信息进行认证,如果认证失败,服务器程序会拒绝连接。
数据传输安全 :如果客户端程序和服务器程序不运行在一台计算机上,可以采用使用了 SSL (安全套接字)的网络连接进行通信,来保证数据传输的安全性
文本消息:MySQL 服务器接收到的请求只是一个文本消息,该文本消息还要经过各种处理才能被服务器利用起来进行下一步真正的处理
解析与优化
这块也涉及到了很多知识,比如查询缓存、语法解析、查询优化...同样先说个大概,后续再展开
查询缓存 :MySQL 服务器程序处理查询请求的过程也是这样,会把刚刚处理过的查询请求和结果 缓存起来,如果下一次有一模一样的请求过来,直接从缓存中查找结果就好了,就不用再傻呵呵的去底层的表中查找了。这个查询缓存可以在不同客户端之间共享,也就是说如果客户端A刚刚查询了一个语句,而客户端B之后发送了同样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据。
缓存远没有这么简单,会有各种各样的问题,比如缓存大量不命中如何处理?数据不一致怎么办?缓存失效怎么办?维护缓存造成的开销如何平衡?
因此,从MySQL 5.7.20开始,不推荐使用查询缓存,MySQL 8.0已经删除了缓存
语法解析 :假如没有命中缓存,就要真正去数据库中查询数据了。但客户端发来的请求只是一段文本,所以服务器会先分析文本,判断语法对错,然后再提取要查询的表和条件啥的,放到服务器内部,就像编译一样,这个过程封装的很彻底,基本不会亲手编写的
查询优化 :执行效率不高的时候,需要考虑优化来提升效率,比如建立索引、外连接改内连接、子查询改为连接啥的,优化完后会生成一个执行计划,可以用EXPLAIN语句
查询这个计划,然后分析优化的效果
存储引擎
-
MySQL 服务器把数据的存储和提取操作都封装到了存储引擎里。我们知道 表 是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎负责的。
-
索引index是在存储引擎层实现的,所以不同的引擎有不同的索引结构。引擎是基于表结构的,所以不同的表可以用不同的存储引擎
-
在建表时可以用
engine = 引擎名
来指定引擎 -
可以用
SHOW ENGINES 命令
查看当前服务器支持哪些存储引擎 -
可以用
CREATE TABLE table_name(建表语句;) ENGINE = 存储引擎名称;
指定某个引擎 -
可以使用
ALTER TABLE table_name ENGINE = 存储引擎名称;
来修改表的存储引擎 -
InnoDB引擎:对事务完整性有较高要求,要求数据一致性,更新删除操作较多时,选它!!!
-
MyISAM引擎:事务完整性数据一致性要求不高,更新删除操作较少时,选它!!!
-
MEMORY引擎:数据存储在内存中,速度很快,但持久化不太行,只能做临时表对安全性要求不高,对速度要求较高,数据量不大时,选它!!!
InnoDB :
MySQL5.5版本之后的默认引擎
默认B+索引
InnoDB的每张表都会对应一个ibd文件,存储该表的表结构、数据、索引
一个ibd文件是一个表空间tablespace,一个表空间分多个段segment,一个段分多个区extent,一个区分多个页page,一个页分多个行row,一行中有多个字段
DML增删改操作遵循ACID模型,支持事务,支持行级锁,支持外键约束
MyISAM:MySQL早期版本的默认引擎
默认B树索引
每张表会生成三个文件
不支持事务,不支持外键,不支持行锁,但支持表锁,访问速度快
2 MySQL的调控按钮 ------ 启动选项和系统变量
命令行设置启动项
我们可以在命令行设置MySQL的一堆东西,比如连入的客户端数量、客户端和服务器通
信方式、表的默认存储引擎、查询缓存的大小等,称为设置项,这些设置项一般都有默认值。
启动选项是啥?在程序启动时指定的设置项和参数就是启动项
大多数程序提供了--help
,可以查看该程序支持的全部启动选项以及它们的默认值。
例如, mysql --help
可以看 mysql 程序支持的启动选项, mysqld_safe --help
可以看 mysqld_safe 程序支持的启动选项, mysqld --verbose --help
可以查看 mysqld 支持的启动选项
在启动服务器程序的命令行后边指定启动选项的通用格式是这样的:--启动选项1[=值1] --启动选项2[=值2] ... --启动选项n[=值n]
常见启动选项:
- 指定客户端和服务器之间通过TCP/IP网络通信
-h服务器IP地址
- 禁止通过TCP/IP网络通信
mysqld --skip-networking / mysqld --skip_networking
skip-networking是一个整体,是选项名,短横线改成下划线也可以 - 修改默认引擎
mysqld --default-storage-engine=MyISAM
,注意等号周围不能有空格
在命令行中设置启动选项只对当次启动生效,也就是说如果下一次重启程序的时候我们还想保留这些启动选项的话,还得重复把这些选项写到启动命令行中,这样真的神烦唉!于是设计 MySQL 的大叔们提出一种配置文件/选项文件 的概念,我们把需要设置的启动选项都写在这个配置文件中,每次启动服务器的时候都从这个文件里加载相应的启动选项。由于这个配置文件可以长久的保存在计算机的硬盘里,所以只需我们配置一次,以后就都不用显式的把启动选项都写在启动命令行中了,所以我们推荐使用配置文件的方式来设置启动选项。
配置文件设置启动项(推荐)
配置文件其实就是一个普通文件而已,Windows是后缀.ini文件,UNIX是后缀.cnf文件,如果安装目录下没有这个文件,手动创建一个,然后把配置内容复制进文件里即可
配置文件中的启动选项被划分为若干个组,每个组有一个组名,用中括号[] 扩起来,就像这样:
[server]
(具体的启动选项...)
[mysqld] # 这里还可以改成 [mysqld-5.7],表示只有版本号为 5.7 的 mysqld 程序才能使用这个选项组中的选项
# 设置3306端口
port = 3306
# 设置mysql的安装路径
basedir = xxx
# 设置mysql数据库的数据的存放目录
datadir = xxx\data
# 允许最大连接数
max_connections = 200
# 允许连接失败的次数。
max_connect_errors = 10
# 服务端使用的字符集默认为utf8
character-set-server = utf8
# 创建新表时使用的默认存储引擎
default-storage-engine = INNODB
[mysqld_safe]
(具体的启动选项...)
......
需要注意,配置文件中不能用一个字母的参数名,参数名前面不用加 -- 前缀,每行只能设置一个参数,而且 = 周围可以有空白字符。对比一下,命令行设置上面配置文件中的内容大概就是这样:
-P3306 --basedir=xxx --character_set_server=utf8
- mysql.server的启动参数必须用配置文件设置
- 如果同一个启动选项既出现在命令行中,又出现在配置文件中,那么以命令行为准
- 如果同一个启动选项出现在多个配置文件中,那以最后一个配置文件中的为准
- 在命令行上指定的绝大部分启动选项都可以放到配置文件中,但有一些选项是专门为命令行设计的,比如 defaults-extra-file 、 defaults-file 是指定配置文件路径的,必须在命令行中设置,具体哪些启动选线必须用命令行请自行食用官方文档
那么操作系统是如何找到配置文件的?有Windows系统和UNIX系统两种情况,我先择一探索,想看UNIX版本可以看《MySQL是怎样运行的》P21:
系统变量
每个系统变量都有一个默认值,我们可以用命令行或者配置文件在启动服务器时改变一些系统变量的值。大多数的系统变量的值也可以在程序运行过程中修改,而且无需停止并重新启动程序。
- 查看系统变量 用
SHOW VARIABLES [LIKE 匹配的模式]
命令,like是用来筛选的,避免系统变量太多刷屏,看眼花了都
比如SHOW VARIABLES LIKE 'default%'
命令就可以筛选所有 default 开头的系统变量值
再比如SHOW VARIABLES LIKE 'default_storage_engine'
命令就可以精准定位 default_storage_engine 这个系统变量的值 - 设置系统变量可以在启动服务器的时候通过设置启动选项的方式设置,如何设置启动选项请倒回去看上两个小节。
一个短横线两个短横线下划线傻傻分不清?下面是常用场景
- 只有一个英文字母的参数前面加一个短横线就行,比如
-h -u -p
,如果大于一个英文字母前面需要加两个短横线,比如--host / --skip-networking / --skip_networking
- 不管前面是一个短横线还是两个短横线,后面跟着的都有一个完整的参数名。
如果参数是启动项,项名有好几个单词,各个单词之间用短横线 - 或者下划线 _ 连接起来都可以,比如mysqld --skip-networking / mysqld --skip_networking
都行。
如果参数是系统变量,变量名有好几个单词,必须用下划线 _,比如SHOW VARIABLES LIKE 'default_storage_engine'
,这里的'default_storage_engine'就必须用下划线连接
so,为了省事,其实可以全部统一用下划线_来分割过长的参数名
常见系统变量,更多的系统变量自行食用官方文档
- 允许同时连入的客户端数量
max_connections
- 表的默认存储引擎
default_storage_engine
- 查询缓存的大小
query_cache_size
看到这儿肯定会疑惑一个问题,启动项和系统变量的区别?
大部分系统变量值可以在服务器运行过程中动态修改,不用重启服务器就能生效,启动项不行,启动项启动项,这个名字就注定了它只能在启动的时候修改
系统变量的作用范围
- GLOBAL :全局变量,影响服务器的整体操作。
- SESSION :会话变量,影响某个客户端连接的操作。(别名 LOCAL )
其实和Java中局部变量和全局变量的概念很像
在服务器启动时,会将每个全局变量初始化为其默认值(可以改默认值)。然后服务器还为每个连接的客户端维护一组会话变量,客户端的会话变量在连接时初始化为全局变量的值
如何设置系统变量的作用范围?
SET [GLOBAL|SESSION] 系统变量名 = 值;
SET [@@(GLOBAL|SESSION).]var_name = XXX;
比如,想让之后新连接到服务器的客户端都用 MyISAM 作为默认的存储引擎,可以这么写
SET GLOBAL default_storage_engine = MyISAM;
SET @@GLOBAL.default_storage_engine = MyISAM;
再比如,想让本客户端用 MyISAM 作为默认的存储引擎,可以这么写
SET SESSION default_storage_engine = MyISAM;
SET @@SESSION.default_storage_engine = MyISAM;
SET default_storage_engine = MyISAM; # 不写就是默认session
如何查看指定作用范围的系统变量?
SHOW [GLOBAL|SESSION] VARIABLES [LIKE 匹配的模式];
比如,查看带有 default 的系统会话变量值可以这么写
SHOW SESSION VARIABLES LIKE 'default%';
需要注意的是,并不是所有系统变量都具有 GLOBAL 和 SESSION 的作用范围。
有一些系统变量只具有 GLOBAL 作用范围,比方说 max_connections ,表示服务器程序支持同时最多有多少个客户端程序进行连接。
有一些系统变量只具有 SESSION 作用范围,比如 insert_id ,表示在对某个包含 AUTO_INCREMENT 列的表进行插入时,该列初始的值。
3 乱码的前世今生------字符集和比较规则
字符集是用来映射字符串和二进制的
字符集超多,常用的有ASCII和utf-8,utf-8收录了地球上所有字符,还在不断扩充
MySQL中有两个新的概念
utf8mb3 (在MySQL中就叫utf8):阉割过的 utf8 字符集,只使用1~3个字节表示字符。
utf8mb4 :正宗的 utf8 字符集,使用1~4个字节表示字符。
如何查看当前 MySQL 中支持的字符集?
SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式];
# CHARACTER SET 和 CHARSET 是同义词,用哪个都可以
# 返回结果是一个表格,其中 Default collation 列表示默认的比较规则,Maxlen列表示一个字符最多需要几个字节
如何查看 MySQL 中支持的比较规则?
SHOW COLLATION [LIKE 匹配的模式];
4 从一条记录说起------ InnoDB 记录结构
5 盛放记录的大盒子 ------ InnoDB 数据页结构
6 快速查询的秘籍 ------ B+ 树索引
索引优化
- 关系数据库中的一种数据结构,牺牲空间换时间,它将数据提前按照一定的规则进行排序和组织(例如现实生活中的目录等),能够帮助快速定位数据,加快数据库表中数据的查找和访问速度,降低访问磁盘IO的频率
- 优点:提高检索效率,用默认的升序时可以自动排序,降低排序成本
- 缺点:创建和维护索引需要耗费时间,索引需要占物理空间,每次增删改索引都需要动态维护,会降低表增删改的效率
- 适合:查询(where)较频繁、排序(order by)较频繁、分组(group by)较频繁的字段
- 不适合:字段值的唯一性太差不适合单独做索引,增删频繁的字段不适合,不会出现在where中的字段不需要做索引
- 尽量选择区分度较高的字段作为索引,尽量建立唯一索引,多用联合索引(覆盖索引概率更大,减少回表),少用单列索引。字段长度较长且为字符串类型的建立前缀索引
- 性能分析指标:show global status like 'Com_________',可以查看数据库的查询次数和插入删除次数,如果查询较频繁就可以通过建立索引进行优化
- 慢查询日志:记录了所有执行时间超过指定参数的所有SQL语句的日志,可以针对慢查询日志中效率较低的SQL语句进行优化
- profile详情:能告诉我们时间都耗费到哪里了,针对性进行优化
- explain执行计划:用explain命令可以知道MySQL执行查询语句的具体信息,包括如何连接表,连接顺序等,比较常用
- 创建索引:create index 索引名 on 表名(字段名)
- 查看索引:show index from 表名
- 删除索引:drop index 索引名 on 表名
按数据结构分类
B+树索引(重点)
- MySQL的默认存储结构
- 从二叉树------平衡二叉树------红黑树------B树------B+树演化而来
- 二叉树存在问题:当数据有序插入时,会出现极端情况,二叉树退化成链表,查找效率低下
- 平衡二叉树存在问题:虽然可以自动降低树的高度,但严格的左旋右旋操作较费时
- 红黑树存在问题:不是非常严格的平衡二叉树,兼顾了树的高度和左旋右旋效率,但高度依旧不够低
- B树存在问题:数据冗余、范围查询效率低下
- B+树:本质上是平衡多路查找树,树的高度更低,还优化了B树存在的问题
- 关键优化点1:B树不只将所有数据存储在叶子节点,为了方便寻址,还存了一份在非叶子节点中,会造成数据冗余。而B+树将所有数据存储在叶子节点,非叶子节点并没有存储数据
- 关键优化点2:B树的叶子节点是独立的,在一定程度上不利于范围查询,而B+树将叶子节点的多个数据用双向指针链接起来,方便范围查询 。例如当需要查询图中的1-5数据时,对于B+树来说,只需要从根节点出发,找到1,再从1出发根据指针直接找到2-5即可,而B树则需要从根节点出发查询5次
- 如果一个表没有主键索引还会创建B+树吗?:会。InnoDB(MySQL中的一种存储引擎)会为每个表创建一个主键索引,如果没有会用一个隐藏的自动生成的主键来创建索引(即B+树结构)
Hash索引
- InnoDB不支持显式地创建Hash索引,Memory才支持
- 解决冲突用的链表
- 不支持排序,用的比较少
按物理存储分类
聚集索引
- 索引和数据存储在一起
- InnoDB的存储方式
- 必须有,而且只有一个
- 默认主键索引是聚集索引
- 如果不存在主键,使用第一个唯一索引作为聚集索引
- 如果不存在主键,也没有合适的唯一索引,InnoDB自动生成一个rowid作为隐藏的聚集索引
非聚集索引
- 索引和数据分开存储,索引和指针存储在一起,根据指针去找数据
- MyISAM的存储方式
覆盖索引
- 算是一种聚集索引,例如 select id from 'user' where age=35 ,会另外建立一个关于age的二级索引B+树,叶子节点是age和主键id,但需要查询的字段id就在二级索引的叶子节点中,因此不需要回表 ,即索引列(叶子节点一整列,包括age和id)已经覆盖了查询字段id,查询速度更快,在实际开发中应尽量使用覆盖索引,避免 select *
前缀索引
- 字段类型为字符串,长度超级长时,可以简历前缀索引
- 只将字符串的一部分前缀建立索引
- 前缀长度如何决定:根据索引的选择性来决定,即不重复的索引值和数据表的记录总数比值,选择性越高查询效率越高。最好的情况是选择性为1的唯一索引
- 选择性如何计算:用聚合函数count
二级索引
- 一种非聚集索引,例如 select * from 'user' where age=35 ,也会另外建立一个关于age的二级索引B+树,叶子节点是age和主键id,通过二级索引查询所有数据时,需要先查到age字段对应的主键id,再通过回表去主键索引查所有信息。这里的二级索引属于非聚集索引(age、id和其他信息分开存储),主键索引就是聚集索引(id和对应的所有数据放在一起)
- 可以存在多个
索引下推ICP
- MySQL5.6针对二级索引做的优化
- 适用于MYISAM和INNODB
- 解决二级索引进行范围查询时需要频繁回表的问题
按字段个数分类
单列索引
- 只有一个字段,如下图的右边子图的叶子节点,只有年龄和id,年龄就是索引列,也叫单列索引
- MySQL会自动根据索引列的值进行排序,数字和字典都可以
联合索引/复合索引/组合索引
- 索引列由多个字段组成,如上图的左边子图的叶子节点,索引列有id名字和年龄多个索引列,但排序是先按id排序,id相同时再以名字排序
- 最左前缀原则:带头大哥字段不能死,中间兄弟字段不能断。针对联合索引写查询语句时,必须把第一个有序的字段带上,放在最左边,而且不能跳过中间字段,因为后面的字段实际上在整体上是无序的。如在上图左子图中查 rob,不能直接 where name=rob,必须写 where id=5,name=rob,必须带上第一个字段id
- 减少开销:假设需要频繁查询 a b c三个字段,建三个单列索引就需要建三个B+树,而建一个联合索引只需要一个B+树,可以减少写操作的开销和磁盘的开销
- 覆盖索引:联合索引的叶子节点上的数据更全,查询时直接拿到数据无需回表的概率更大,可以减少io操作
索引失效情况
- 索引列进行了运算
- 字符串类型不加引号
- 模糊查询:头部模糊会失效,比如查以 工程 结尾的某个字段就会失效,like '%工程'
- or后面的条件没有索引:where 条件1 or 条件2; 如果条件2没有建立索引,即使条件1有索引也会失效
- Mysql评估:MySQL评估后发现用索引比不用还慢,就会选择不使用索引
7 好东西也得先学会怎么用 ------ B+ 树索引的使用
8 数据的家 ------ MySQL 的数据目录
9 存放页面的大池子 ------ InnoDB 的表空间
10 条条大路通罗马 ------ 单表访问方法
11 两个表的亲密接触 ------ 连接的原理
12 谁最便宜就选谁 ------ MySQL 基于成本的优化
插入数据
- insert 语句
- 优化情况:插入多条数据
- 优化方案1:不要单条插入,最好批量插入。批量插入建议不要一次性插入1000条以上
- 优化方案2:手动提交事务。自动提交事务会频繁开关事务,建议将多条批量插入语句手动放在一个事务中提交
- 优化方案3:主键顺序插入。顺序插入性能高于乱序插入
- 优化方案4:当数据量高达百万,insert批量插入性能很差,建议使用MySQL提供的 load 指令
主键优化
- 数组组织方式:对于InnoDB引擎,表数据都是根据主键顺序组织存放的,组织起来的表叫索引组织表
- 页分裂(其实就是B+树插入一个数据时,该插入的页位置不够,需要增加一页的操作):InnoDB引擎是以页为单位进行管理的,主键顺序插入时会依次放入页中,满了再插一个无序数据时数据会将应插入的位置的前面一页的数据从中间分裂,用两页存储,称为页分裂。主键乱序插入会导致频繁发生页分裂,效率低下
- 页合并(其实就是B+树删除一个数据时,删后的页只剩50%(默认值)以下的数据,就会检测前后页有没有空位能放下这些数据,如果能就合并页)
- 主键的设计原则1:满足业务需求的前提下,主键长度尽可能短。因为所有二级索引都会在叶节点中存储字段+主键,主键若太长会很浪费空间,降低效率
- 主键的设计原则2:尽量选择顺序插入,降低页分裂现象
- 主键的设计原则3:尽量不用UUID做主键,UUID是乱序生成的,不利于顺序插入,且一般都比较长
- 主键的设计原则4:尽量避免修改主键,一改就会牵引所有的索引修改
排序优化
- order by语句
- 两种排序方法:Using filesort 和 Using index
- Using filesort:不走索引,全表扫描后在排序缓冲区中进行排序,然后直接返回排序结果
- Using index:走索引,扫描有序索引,无需额外排序,直接返回有序数据,效率更高
- 将排序尽量优化成 index 排序方法
- 数据量很大,无法避免要用 filesort 时,可以增大排序缓冲区大小,避免因容量不够去磁盘中排序
- 根据排序字段建立索引,尽量使用覆盖索引
- 多字段排序时也要遵循最左前缀法则,若一个升序一个降序,注意联合索引在创建时的规则即可(ASC/DESC)