MySQL可以分为 Server 层和存储引擎层。
Server 层包括连接器、查询缓存、解析器、优化器和执行器等,涵盖了 MySQL 大多数核心服务功能。
存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Mermory 等多个存储引擎。
MySQL如何工作
连接器
当你在客户端输入 mysql –u $user –p $pwd 连接 mysql 的时候,接待你的就是连接器。连接器的作用就是和客户端建立连接、获取权限、维持和管理连接。
查询缓存
建立连接后,就可以执行select语句了。首先MySQL会去查看查询缓存,看下之前是否已经执行过这条查询语句。如果命中缓存,就直接返回。否则就扔给解析器。
解析器
MySQL需要知道你想做什么,因此我们就来到了解析器。解析器会做词法分析和语法分析。词法分析主要是分析每个词的含义,语法分析会判断你写的 SQL 语句是否满足 SQL 语法。
优化器
经过解析器,MySQL就知道你想做什么了。但是在开始执行之前,还需要经过优化器的处理。优化器会优化你的SQL语句。生成最终的执行方案 (execution plan)。然后进入执行器阶段。
执行器
执行器首先会判断你对这张表有没有相应的权限。如果没有,就报错。如果有,就调用相应的存储引擎接口,执行语句。然后将结果集返回给客户端。
数据的存储和提取是由存储引擎负责的,它负责和文件系统打交道。
MySQL 的存储引擎是插件式的。不同的存储引擎支持不同的特性。
选择合适的存储引擎对应用非常重要 。
# 查看MySQL支持哪些存储引擎
SHOW ENGINES;
# 查看默认存储引擎
SHOW VARIABLES LIKE ‘%storage_engine%’;
# 查看某张表的存储引擎
SELECT ENGINE FROM information_schema.TABLES
WHERE TABLE_SCHEMA='$db'
AND TABLE_NAME='$table';
MyISAM
MySQL 5.5 之前默认的存储引擎。
特点:
a. 查询速度很快
b. 支持表锁
c. 支持全文索引
d. 不支持事务
使用 MyISAM 存储表,会生成三个文件.
.frm # 存储表结构,是任何存储引擎都有的
.myd # 存放数据
.myi # 存放索引
索引和数据是分开存放的,这样的索引叫非聚集索引。
*InnoDB
MySQL 5.5 以及以后版本默认的存储引擎。没有特殊应用,Oracle官方推荐使用InnoDB 引擎。
特点:
a. 支持事务
b. 支持行锁
c. 支持MVCC
d. 支持崩溃恢复
e. 支持外键一致性约束
使用 InnoDB 存储表,会生成两个文件.
.frm # 存储表结构,是任何存储引擎都有的
.ibd # 存放数据和索引
索引和数据存放在一起,这样的索引叫聚集索引。
Memory
特点:
a. 所有数据都存放在内存中,因此数据库重启后会丢失
b. 支持表锁
c. 支持Hash和BTree索引
d. 不支持Blob和Text字段
Memory由于数据都放在内存中,以及支持Hash索引,它的查询速度是最快的。
一般使用 Memory 存放临时表。
临时表:在单个连接中可见,当连接断开时,临时表也将不复存在。
磁盘上的数据可以用一个三维地址标识: 柱面号, 盘号, 块号(磁道上的扇区)
读/写数据的步骤:
移动磁头到指定的柱面号,这个过程被称为定位或查找。
由于是机械移动, 这部分耗时最高, 最大可达 0.1s.
根据盘面号确定从哪个磁盘读取数据
盘组开始旋转,将指定的块号移动到读/写头下
磁盘旋转的速度很快, 一般为7200rpm。旋转一圈大约需要 0.0083s.
读写数据
数据通过系统总线传送到内存。一般传输一个字节大概需要 0.02us.
读写 4KB 大约需要 80us.
磁盘读取数据是以盘块(block)为单位的, 一般为4KB。位于同一盘块的所有数据会被一次性全部读取出来。磁盘IO的代价主要花费在第 1 步。
结论:IO的速度是很慢很慢的,我们应该尽量少地读写磁盘!
页是 InnoDB 磁盘管理的最小单位。在 InnoDB 存储引擎中, 页默认大小为16KB。
可以通过参数 innodb_page_size 将页的大小设置为 4K、8K 和 16K。
InnoDB 每次至少会将 1 个页的数据从磁盘读取到内存,每次至少也会将 1 个页的数据从内存写到磁盘。
在InnoDB存储引擎中,有很多种页类型。其中最重要的是数据页,也叫 B-tree Node。里面存储了索引和数据的信息。
File Header
主要存储表空间相关信息
Page Header
主要存储数据页的元信息
Infimum + Supremum Records
每个数据页中有两个虚拟行记录用来限定记录的边界,infimum record 是数据页上最小的记录,supremum record 是数据页上最大的记录。
User Records
用户数据,实际存储的行记录。
Free Space
空闲空间。
Page Directory
页目录,存放了记录的相对位置。
File Trailer
位于数据页的最后,用来检测页是否完整地写入磁盘。
行记录是用链表形式组织的,最小最大记录相当于两个哨兵。Page Directory是一个数组,里面包含很多指向记录的指针(又叫 Slot),S0指向最小记录的链表, Sn指向最大记录的链表。S1 ~ Sn-1 的每条链的长度范围为 [4, 8]。
File Header 里面有两个字段:FIL_PAGE_PREV 和 FIL_PAGE_NEXT 用来表示上一个页和下一个页,因此,页与页之间是用双链表链接的。页内的记录是由单链表从大到小依次链接的。
索引:在 MySQL 中也叫做键 (key),是存储引擎用于快速找到记录的一种数据结构。
哪些数据结构可以作为索引?
有序数组
哈希表 (hash索引)
平衡二叉树
B树
B+树
能够快速查找的数据结构都可以作为索引。
能够讲讲作为索引时,各个数据结构的优缺点吗?
有序数组:等值查找,区间查找都比较块。但是增删需要移动大量的元素,会很慢。
哈希表:增加、删除,等值查询都很快。但是区间查找,排序等操作会很慢。
BST索引:
不管是,增加,删除,还是等值查找,时间复杂度都是O(logn),n 是数据页的数目。
并且支持范围查找。
但是当数据量比较大,页的数目很多时,二叉树的高度会比较高。IO 的次数会比较多。
查找效率低。
B树索引:
B 树,它是一颗多路平衡查找树。我们描述一颗 B 树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子,一般用字母m表示。一颗m阶的B树定义如下:
B树索引和数据是一起存放的。
缺点:a. 一行记录的数据很多时,比如1K。b. 范围查找。
B+树包含2种类型的结点:内部结点 (也称索引结点) 和叶子结点。根结点即可以是内部结点,也可以是叶子结点。根结点的关键字个数可以只有1个。
B+树与B树最大的不同是内部结点不保存数据,只保存关键字,所有数据 (记录) 都保存在叶子结点中。
m阶B+树表示了内部结点最多有m个关键字。至少有 ceiling(m/2)个关键字。
内部结点中的key都按照从小到大的顺序排列,叶子结点中的记录也按照key的大小排列。
每个叶子结点都存有相邻叶子结点的指针,叶子结点依关键字大小依次链接。
B+ 树由于内部节点只存储索引,因而每个节点可以有很多很多关键字。这样不管一行记录的数据大小,都可以保证树的高度很低。
而且由于叶子节点是由链表按大小依次连接的 (在InnoDB 中是双链表)。范围查找的时候,也可以避免过多的IO次数。
好处
提高数据检索的效率,降低数据库的IO成本。
a. 查找
b. 排序
c. 分组
d. 表的连接
坏处
a. 占用额外的空间。有时候索引占用的空间甚至比数据占用的空间还多。
b. 虽然索引大大提高了查询的速度,但同时也降低了更新表的速度。因为数据库不仅仅要更新数据,还要更新对应的索引信息。
PS:索引不是越多越好!索引太多,应用程序的性能可能会受到影响; 索引太少,查询速度会变慢。我们应该建立合适的索引,找到一个平衡点!
B+树索引
创建:
创建表的时候指定。会自动给 primary key 和 unique 创建索引。
CREATE [UNIQUE] INDEX 索引名 ON 表名(字段列表);
ALTER 表名 ADD [UNIQUE] INDEX 索引名 (字段列表);
删除:
DROP INDEX 索引名 ON 表名;
查看:
SHOW INDEX FROM 表名;
当通过辅助索引来寻找数据的时候,InnoDB会遍历辅助索引,并通过辅助索引的叶子节点,获取主键。然后再通过聚集索引来找到一个完整的行记录。这个过程我们称之为回表(遍历过程:辅助索引—>聚集索引)。
如果一个辅助索引的高度为3,聚集索引的高度为3。那么我们需要6次IO操作,才可以访问最终的数据。应该尽量避免回表!
查询优化器:通过计算分析系统收集的统计信息,提供它认为最优的执行计划(execution plan)。
explain:查看这个执行计划的信息。
语法:explain + select 语句
例子:explain select * from t;
我们发现explain的结果有以下列的信息:
id, select_type, table, partitions, type, possible_keys, key, ken_len, ref, rows, filtered, Extra
id: 每个 select 子句的标识。
select_type: select 语句的类型。simple, primary, union, subquery, derived。
table:显示这一行的数据是关于哪张表的。
partitions: 匹配的分表。
type: 连接类型,又叫 “访问类型”。
常用类型有:ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能越来越好)
possible_keys:可以选择的索引。
key: 实际选择的索引。
key_len: 使用索引的长度 (以字节为单位)。
ref: 与索引比较的字段
rows: 大概要检索的行数
Extra: 额外信息。
联合索引是指对表的多个列进行索引。前面的讨论的都是只对表上的一个列进行索引。类比于多个字段作为主键。
联合索引的创建方法和单个索引创建的方法一样,不同之处仅在于有多个索引列。
联合索引有什么好处?
最左前缀原则
B+ 树这种数据结构,可以利用索引的 ”最左前缀” 来定位记录。最左前缀原则指索引项是按照索引定义里面出现的字段顺序排序的,只要查询的是索引项的最左字段(相同则查询下一字段),就可以利用联合索引来加速检索。这里需要注意的是,如果直接查询后面的字段,索引根据最左字段排序,但实际上后面的字段是无序的
InnoDB 存储引擎支持覆盖索引 (covering index),即从辅助索引中就可以得到要查询的信息,而不需要回表。
使用覆盖索引的好处是,覆盖索引不包含整行记录的信息,故其大小一般情况下远小于聚集索引,因此可以减少大量的 IO 操作。
对于辅助索引而言,其叶子节点包含主键信息。
主键和唯一键会自动创建索引,外键要求必须是primary key 或者 unique。
频繁作为查询条件的字段应该创建索引。
与其他表关联的字段应该创建索引。
查询中用于经常用于排序的字段。
查询中经常用于分组的字段。
表的记录太少
经常更新的表
数据字段中包含太多的重复值,比如国籍,性别等字段。
尽量使用覆盖索引, 比如尽量不要使用 select *。
尽量使用最左前缀法则
不要在索引列上做运算
范围查找尽量不要太大
尽量不要使用不等于