深入学习MySQLSQL优化集群搭建
Posted 程序dunk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入学习MySQLSQL优化集群搭建相关的知识,希望对你有一定的参考价值。
个人博客欢迎访问
总结不易,如果对你有帮助,请点赞关注支持一下
日拱一卒,不期速成,厚积薄发
目录
- 基础部分
- 进阶部分
- MySQL面试题
基础部分
修改数据库密码
alter user root@localhost IDENTIFIED BY '123456';
跳过密码验证,在my.cnf中添加skip-grant-tables
数据库三大范式
- 第一范式:每个列不可再拆分
- 第二范式:在第一范式的基础上,非主键完全依赖于主键,而不是依赖于主键的一部分
- 第三范式:在第二范式的基础上,非主键只依赖于主键,不依赖于其他非主键
在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由,比如性能,事实上,我们经常会为了性能而妥协数据库的设计。
数据类型
使用策略:
- 对于经常变更的数据来说,CHAR比VARCHAR好,因为CHAR不容易产生碎片
- 对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
- 尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。
SQL语句
数据定义语言DDL(Data Ddefinition Language):CREATE,DROP,ALTER。主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引
数据查询语言DQL(Data Query Language)SELECT。这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL
数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE。主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。
数据控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK。主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。
键
- 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
- 候选键:是最小超键,即没有冗余元素的超键。
- 主键:数据库表中对存储数据对象予以唯一和完整标识的数据列或数据属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,既不能为null
- 外键:在一个表中存在的另一个表的主键称为此表的外键
SQL约束
- NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
- UNIQUE: 控件字段内容不能重复,一个表允许有多个 Unique 约束。
- PRIMARY KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
- FOREIGN KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
- CHECK: 用于控制字段的值范围。
关联查询
- 交叉连接(CROSS JOIN)
- 内连接(INNER JOIN)
- 外连接(LEFT JOIN / RIGHT JOIN)
- 联合查询(UNION / UNION ALL)
- 全连接 FULL JOIN
内连接分为三类
- 等值连接:ON A.id=B.id
- 不等值连接:ON A.id > B.id
- 自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid
外连接(LEFT JOIN/RIGHT JOIN)
- 左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
- 右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN
笛卡尔积现象:当两张表进行连接查询的时候,没有任何条件进行限制,最终的查询结果条数是两张表记录条数的乘积。
联合查询
- 就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并
- 如果使用UNION ALL,不会合并重复的记录行
- 效率 UNION 高于 UNION ALL
全连接(FULL JOIN)
- mysql不支持全连接
- 可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用
事务
什么是数据库事务
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态转变到另一种一致性状态,事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务的四大特性(ACID)
- 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用
- 一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
- 隔离性(Isolation):并发访问数据库,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
- 持久性(Durability):一个事务提交后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该有其他任何影响
脏读、幻读、不可重复读
- 脏读(Di rty Read):某个事务更新一份数据,另一个事务读取到了同一份事务,前一个事务回滚了操作,则后一个事务读取的数据就会不正确
- 不可重复读(Non-repeatable read):一个事务的两次查询之中的结果不一致,可能两次查询中间插入了一个事务更新的原有数据
- 幻读((Phantom Read)):在一个事务两次查询中数据列数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
幻读和不可重复读的区别
不可重复读的重点是修改:
同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件, 第1次和第2次读出来的记录数不一样
事务的隔离级别
为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
MySQL默认采用的是REPEATABLE-READ隔离级别,Oracle默认采用的是READ-COMMITTED隔离级别
子查询
什么是子查询
- 条件:一条SQL语句的查询结果作为另一条查询语句的条件或查询结果
- 嵌套:多条SQL语句嵌套使用,内部的SQL查询语句称为子查询
扩展问题
in和exists的区别
MySQL的in语句是吧外表和内表做hash连接,而exists语句是对外表做loop循环,每次loop循环再对内表进行查询,一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。
- 如果查询的两个表大小相当,那么用in和exists差别不大。
- 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。
- not in 和not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not exists的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
varchar与char的区别
char的特点
- char表示定长字符串,长度固定的
- 如果插入数据的长度小于char的长度,则用空格填充
- 因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正是因为其长度固定,所以会占用多余空间,是空间换时间的做法
- 对于char来说,最多能存放255个字符,和编码无关
varchar的特点
- varchar表示可变长字符串,长度是可变的
- 插入的数据是多长,就按照多长来存储;
- varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;
- 对于varchar来说,最多能存放的字符个数为65532
总之,结合性能角度(char更快)和节省磁盘空间角度(varchar更小),具体情况还需具体来设计数据库才是妥当的做法。
varchar(50)中50的含义
最多存放50个字符,varchar(50)和(200)存储hello所占的空间一样,但后者在排序时消耗更多的内存,应为order by col采用fixed_length计算col长度(Memory引擎也是)在早期MySQL版本中,50代表字节数,现在代表字符数。
int(20)中20的含义
是指显示字符的长度,20表示最大显示宽度为20,但仍占4个字节存储,存储范围不变,不影响内部存储,只是影响带zerofill定义的int,前面补多少个0,易于显示。
设计的意义:只是规定一些工具来显示字符的个数,int(1)和int(20)存储和计算是一样的
float和double的区别
- FLOAT类型数据可以存储至多8位十进制数,并在内存中占4字节。
- DOUBLE类型数据可以存储至多18位十进制数,并在内存中占8字节。
drop、delete和truncate的区别
Delete | Truncate | Drop | |
---|---|---|---|
类型 | 属于DML | 属于DDL | 属于DDL |
回滚 | 可回滚 | 不可回滚 | 不可回滚 |
删除内容 | 表结构还在,删除表的全部或者一部分数据行 | 表结构还在,删除表中的所有数据 | 从数据库中删除表,所有的数据行,索引和权限也会删除 |
删除速度 | 删除速度慢,需要逐行删除 | 删除速度快 | 删除速度最快 |
因此,在不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate。
Union和Union All的区别
- 如果使用UNION ALL,不会合并重复的记录行
- 效率 UNION 高于 UNION ALL
进阶部分
引擎
存储引擎(Storage engine):MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现,存储引擎是基于表的,不是基于库的
查看MySQL数据库默认的存储引擎
show engines 查询当前数据库支持的存储引擎
showvariableslike'%storage_engine%';
存储引擎
- InnoDB引擎:InnoDB引擎提供了对数据库ACID事务的支持,并且还提供了行级锁和外键约束。它设计的目标就是处理大数据容器的数据库系统
- MyISAM引擎(原本MySQL的默认引擎):不提供事务支持,也不支持行级锁和外键
- MEMORY引擎:所有数据都在内存中,数据的处理速度快,但是安全性不高
InnoDB引擎
支持事务
start transaction;
commit
rollback
外键约束
在创建外键的时候,要求必须有对应的索引,字表在创建的时候,也会自动创建对应的索引
create table country_innodb(
country_id int NOT NULL AUTO_INCREMENT,
country_name varchar(100) NOT NULL,
primary key(country_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table city_innodb(
city_id int NOT NULL AUTO_INCREMENT,
city_name varchar(50) NOT NULL,
country_id int NOT NULL,
primary key(city_id), key idx_fk_country_id(country_id),
CONSTRAINT `fk_city_country` FOREIGN KEY(country_id) REFERENCES country_innodb(country_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
创建索引的时,可以指定在删除、更新父表时,对子表进行相应的操作,包括RESTRICT、CASCADE、SET NULL、NOT ACTION
- RESTRICT、NOT ACTION相同,是指限制在子表有关联记录的情况下,父表不能更新
- CASCADE表示父表在更新或者删除时更新或者删除子表对应的记录
- SET NULL表示父表在更新或者删除时,子表的对应字段被SET NULL
子表的外键指定是ON DELETE RESTRICT ON UPDATE CASCADE 方式的, 那么在主表删除记录的时候, 如果子表有对应记录, 则不允许删除, 主表在更新记录的时候, 如果子表有对应记录, 则子表对应更新 。
存储方式
InnoDB存储表和索引有以下两种方式:
- 使用共享空间存储,这种方式创建的表的表结构保存在*.frm文件中,数据和索引保存在innodb_data_home_dir 和 innodb_data_fifile_path定义的表空间中,可以是多个文件。
- 使用多表空间存储,这种方式创建的表的表结构仍然存在*.frm文件中,但是每个数据和索引单独保存在*.ibd中
MyISAM和InnoDB区别
MyISAM | InnoDB | |
---|---|---|
存储结构 | frm-表格定义、MYD(MYData)-数据文件、MYI(MYINDEX)-索引文件 | 所有的表都保存在同一个数据文件中(也可能是多个文件,或是独立的表空间文件)表大小受限于操作系统文件的大小,一般为2GB |
存储空间 | 可被压缩,存储空间小 | 表需要更多的存储空间,他会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引 |
可移植性、备份及恢复 | 由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作 | 免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了 |
文件格式 | *.MYI、 *.MYD | 表结构存在*.frm文件,数据和索引都是集中存储的*.ibd |
外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) | 表级锁定 | 行级锁定、表级锁定,锁定力度小并发能力高 |
SELECT | MyISAM更优 | |
INSERT、UPDATE、DELETE | InnoDB更优 | |
索引的实现方式 | B+树索引,MyISAM表 | B+树索引、InnoDB是索引组织表 |
哈希索引 | 不支持 | 支持 |
全文索引 | 不支持 | 支持 |
记录存储顺序 | 按记录插入顺序保存 | 按主键大小有序插入 |
select count(*) | MyISAM更快,因为MyISAM内部维护了一个计数器,可以直接调取。 |
MyISAM索引与InnoDB索引的区别?
- InnoDB索引是聚簇索引,MyISAM是非聚簇索引
- InnoDB主键索引的叶子节点存储着行数据,因此主键索引效率非常高
- MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据
- InnoDB非主键索引的叶子节点存储的是主键和其他索引的列数据,因此查询时做到覆盖索引会非常高效
InnoDB引擎的4大特性
- 插入缓冲(insert buffer)
- 二次写(double write)
- 自适应哈希索引(ahi)
- 预读(read ahead)
存储引擎选择
如果没有特别的需求,使用默认的Innodb
即可。
MyISAM:以读写插入为主的应用程序,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不高。比如博客系统、新闻门户网站。允许少量的数据丢失
Innodb:用于事务处理应用程序,支持外键,日过要保证数据的完整性;在并发条件下要求数据的一致性,数据操作除了插入和查询外,还包很多更新、删除操作,那么InnoDB存储引擎是比较合适的,InnoDB存储引擎降低由于删除和更新导致的锁定,还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择
MEMORY:将所有数据保存在RAM中,在需要快速定位记录和其他类似数据环境下,可以提供几块的访问。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY表通常用于更新不太频繁的小表,用以快速得到访问结果。
MERGE:用于将一系列等同的MyISAM表以逻辑方式组合在一起,并作为一个对象引用他们。MERGE表的优点在于可以突破对单个MyISAM表的大小限制,并且通过将不同的表分布在多个磁盘上,可以有效的改善MERGE表的访问效率。这对于存储诸如数据仓储等VLDB环境十分合适。
索引
索引概述
MySQL官方对索引的定义为:索引(indexes)是帮助MySQL高级获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这个数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往存储在磁盘上的文件中。(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。
我们通常所说的索引,包括聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特定说明,默认使用的是B+树结构组织的索引。
为了加快Col2的查询,可以维护一个右边的二叉搜索树,每个节点分别包含一索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉搜索树获取到响应数据。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
索引的优势劣势
优势
- 提高了数据检索的效率,降低了数据库IO成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗
- 被索引的列会自动进行排序,包括【单例索引】和【组合索引】,只是组合索引的排序要复杂
- 如果按照索引列的顺序进行排序,对应的order by语句来说,效率会提高很多
劣势
- 空间方面:索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引也是要占用空间的
- 时间方面:创建索引和维护索引要耗费时间,虽然索引大大提高了查询效率,同时却降低了更新表的速率。如对表INSERT、UPDATE、DELETE。因为更新时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
索引的结构
分析AVL树的问题
为什么不使用平衡二叉树
- 时间复杂度和树高相关,树有多高就需要检索多少次,每个节点的读取,都对应一次磁盘IO操作。树的高度就等于每次查询数据是磁盘IO操作的次数。在数据量很大时,查询性能就会很差
- 平衡二叉树不支持范围查询快速查找,范围查询时需要从根节点多次遍历,查询效率不高。
索引是MySQL的存储引擎层中实现的,而不是在服务器层实现的,所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的
-
BTREE索引:最常见的索引类型,大部分索引都支持B树索引
-
HASH索引:只有Memory引擎支持,使用场景简单,类似于数据结构中简单实现的HASH表(散列表)一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。当然这只是简略模拟图。
value可以存储行记录或者行磁盘地址,Hash表在等值查询而定时候效率很高O(1);但是不支持范围快速查找,范围查找只能通过扫描全表方式。
-
R-tree索引(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,主要用于物理空间数据类型,通常使用较少
-
Full-test(全文索引):全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从MySQL5.6版本后开始支持全文索引
索引 | InnoDB | MyISAM | Memory |
---|---|---|---|
BTREE | 支持 | 支持 | 支持 |
HASH | 不支持 | 不支持 | 支持 |
R-tree | 不支持 | 支持 | 不支持 |
Full-test | 5.6版本后支持 | 支持 | 不支持 |
聚集索引、复合索引、前缀索引、唯一索引默认都是使用B+tree树(多路搜索树,不一定是二叉的)索引,统称索引
分析MySQL的读取过程
分析MySQL的存储情况
MySQL的数据是存储在磁盘文件中的,查询处理数据时,需要需要先把磁盘中的数据加载到内存中,磁盘IO 操作非常耗时,所以我们优化的重点就是尽量减少磁盘 IO 操作。访问二叉树的每个节点就会发生一次IO,如果想要减少磁盘IO操作,就需要尽量降低树的高度。那如何降低树的高度呢?
假设key为bigint = 8byte,每个节点有两个指针,每个指针为4byte,一个节点占用的空间16byte(8 + 4 * 2 = 16)。
因为在MySQL的InnoDB存储引擎一次IO会读取的一页(默认一页16K)的数据量,而二叉树一次IO有效数据量只有16字节,空间利用率极低。为了最大化利用一次IO空间,一个简单的想法是在每个节点存储多个元素,在每个节点尽可能多的存储数据。
每个节点可以存储1000个索引(16k/16=1000),这样就将二叉树改造成了多叉树,通过增加树的叉树,将树从高瘦变为矮胖。构建1百万条数据,树的高度只需要2层就可以(1000*1000=1百万),也就是说只需要2次磁盘IO就可以查询到数据。磁盘IO次数变少了,查询数据的效率也就提高了。
BTREE结构
BTREE又叫多路平衡搜索树,一颗m叉的BTree特性如下:
- 树中每个节点最多包含m个孩子
- 除根节点与叶子节点外,每个节点至少有[ceil(m / 2)]个孩子
- 若根节点不是叶子节点,则至少有两个孩子
- 所有叶子节点在同一层,叶节点具有相同的深度,叶子节点之间没有指针相连
- 节点中的元素包含键值和数据,节点中的键值从大到小排列。也就是说,在所有的节点都储存数据。
- 每个非叶子节点有n个key与n + 1个指针组成,其中[ceil(m / 2)] <= n <= m - 1
插入案例
以5叉BTree为例,key的数量:2 <= n <= 4,当n > 4时,中间节点分裂到父节点,两边节点分裂。
插入 C N G A H E K Q M F W L T Z D P R X Y S
插入 C N G A 不需要分裂
插入H ,G向上分裂
插入E K Q不需要分裂
插入M,M向上分裂
插入F W L T 不需要分裂
插入 Z,T向上分裂
插入D P R X Y不需要分裂
插入 S M向上分裂
最终插入完成后
BTREE树和二叉树相比,查询数据的效率更高,因为对于相同的数据量来说,BTREE的层级结构比二叉树小,搜索速度快
分析B树存在的问题
- B树不支持范围查询的快速查找,假如要查询一个范围的数据,需要回到根节点重新遍历查找,需要从根节点进行多次遍历,查询效率有待提高。
- 如果data存储的是行记录,行的大小会随着列数的增多,所占空间就会变大,这是,一个页中可存储的数据量就会变少,树相应就会变高,磁盘IO次数就会变大。
B+TREE结构
B+Tree性质
- n叉B+Tree最多含有n个key,不是用来保存数据而是保存数据的索引
- B+Tree的叶子节点保存所有key的信息,及指向含这些关键字记录的指针,且叶子节点本身依key的大小排序
- 所有的非叶子节点都可以看做key的索引部分,节点中仅含其子树中最大或最小的关键字
- B+树中,数据对象的插入和删除仅在叶子节点上进行
- B+树有两个头指针,一个是树的根节点,一个是最小key的叶子节点
B+树和B树的区别
B+树和B树最主要的区别在于非叶子节点是否存储数据的问题
- B树:非叶子节点和叶子节点都会存储数据
- B+树:只有叶子节点才会存储数据,非叶子节点只存储键值,叶子检点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。
B+树查询过程
B+树的最底层叶子节点包含了所有的索引项,B+树在查找的时候,由于数据都存放在最底层的叶子节点,所以每次查询都需要检索到叶子节点才能查询到数据,所以在需要查询数据的情况下每次的磁盘的IO跟树高有直接的关系,但是从另一方面来说,由于数据都被放到了叶子节点,所以放索引的磁盘块锁存放的索引数量是会跟这增加的,所以相对于B树来说,B+树的树高理论上情况下是比B树要矮的。也存在索引覆盖查询的情况,在索引中数据满足了当前查询语句所需要的全部数据,此时只需要找到索引即可立刻返回,不需要检索到最底层的叶子节点。(这里需要区分的是在InnoDB中Data存储的为行数据,而MyIsam中存储的是磁盘地址。)
可以看到B+树可以保证等值和范围查询的快速查找,MySQL的索引就采用了B+树的数据结构。
索引分类
-
主键索引:数据列不允许重复,不允许为NULL,一个表只能有一个主键。
-
唯一索引:数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
-
普通索引:基本的索引类型,没有唯一性的限制,允许为NULL值。
-
全文索引:即一个索引包含多个列,是目前搜索引擎使用的一种关键技术。
-
空间索引:MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。
-
前缀索引:在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。
-
按照索引数量来分类
-
单例索引
-
组合索引
组合索引的使用,需要遵循最左前缀匹配原则(最左匹配原则)。一般情况下在条件允许的情况下使用组合索引代替多个单例索引使用
-
MySQL的索引实现
MyIsam索引
主键索引
表的索引存储在索引文件*.MYI中,数据文件存储在数据文件*.MYD中。
等值查询
select * from user where id = 28;
- 先在主键树中从根节点开始检索,将根节点加载到内存,比较28<75,走左路。(1次磁盘IO)
- 将左子树节点加载到内存中,比较16<28<47,向下检索。(1次磁盘IO)
- 检索到叶节点,将节点加载到内存中遍历,比较16<28,18<28,28=28。查找到值等于30的索引项。(1次磁盘IO)
- 从索引项中获取磁盘地址,然后到数据文件user.MYD中获取对应整行记录。(1次磁盘IO)
- 将记录返给客户端。
磁盘IO次数:3次索引检索+记录数据检索。
范围查询
select * from user where id between 28 and 47;
- 先在主键树中从根节点开始检索,将根节点加载到内存,比较28<75,走左路。(1次磁盘IO)
- 将左子树节点加载到内存中,比较16<28<47,向下检索。(1次磁盘IO)
- 检索到叶节点,将节点加载到内存中遍历比较16<28,18<28,28=28<47。查找到值等于28的索引项。
- 根据磁盘地址从数据文件中获取行记录缓存到结果集中。(1次磁盘IO)
- 我们的查询语句时范围查找,需要向后遍历底层叶子链表,直至到达最后一个不满足筛选条件。
- 向后遍历底层叶子链表,将下一个节点加载到内存中,遍历比较,28<47=47,根据磁盘地址从数据文件中获取行记录缓存到结果集中。(1次磁盘IO)
- 最后得到两条符合筛选条件,将查询结果集返给客户端。
磁盘IO次数:4次索引检索+记录数据检索。
在MyISAM查询时,会将索引节点缓存在MySQL缓存中,而数据依赖于操作系统自身的缓存,所以并不是每次都是走的磁盘
辅助索引
在MyISAM中,辅助索引和主键索引的结构是一样的,没有任何区别,叶子节点的数据存储的都是行记录的磁盘地址。只是主键索引的键值是唯一的,而辅助索引的键值可以重复。
查询数据时,由于辅助索引的键值不唯一,可能存在多个拥有相同的记录,所以即使是等值查询,也需要按照范围查询的方式在辅助索引树中检索数据。
InnoDB索引
主键索引(聚簇索引)
每个InnoDB表都有一个聚簇索引,聚簇索引使用B+树构建,叶子节点存储的数据是整行记录。一般情况下,聚簇索引等同于主键索引,当一个表没有创建主键索引时,InnoDB会自动创建一个ROWID字段来构建聚簇索引。具体规则
- 在表上定义主键,InnoDB将主键索引引用做聚簇索引
- 如果表没有定义主键,InnoDB会选择第一个不为NULL的唯一索引列作聚簇索引
- 如果以上都没有,InnoDB会使用一个6byte长整型的隐式字段ROWID子段来构建聚簇索引,该ROWID字段会在插入新行时自动递增。
除了聚簇索引之外的所有索引都称为辅助索引,在InnoDB,辅助索引中的叶子节点存储的数据是该行的主键值,在检索时,InnoDB使用此主键值在聚簇索引中搜索记录。
InnoDB的数据和索引存储在一个文件t_user_innodb.ibd中。InnoDB的数据组织方式,是聚簇索引。
select * from user_innodb where id = 28;
- 先在主键树中从根节点开始检索,将根节点加载到内存,比较28<75,走左路。(1次磁盘IO)
- 将左子树节点加载到内存中,比较16<28<47,向下检索。(1次磁盘IO)
- 检索到叶节点,将节点加载到内存中遍历,比较16<28,18<28,28=28。查找到值等于28的索引项,直接可以获取整行数据。将改记录返回给客户端。(1次磁盘IO)
磁盘IO数量:3次。
辅助索引
除聚簇索引之外的所有索引都称为辅助索引,InnoDB的辅助索引只会存储主键而非磁盘地址
select * from t_user_innodb where age=19;
根据在辅助索引树中获取的主键id,到主键索引树检索数据的过程称为回表查询。
磁盘IO数:辅助索引3次+获取记录回表3次
组合查询
最左匹配原则:
最左前缀匹配原则和联合索引的索引存储结构和检索方式是有关系的。
在组合索引树中,最底层的叶子节点按照第一列a列从左到右递增排列,但是b列和c列是无序的,b列只有在a列值相等的情况下小范围内递增有序,而c列只能在a,b两列相等的情况下小范围内递增有序。
就像上面的查询,B+树会先比较a列来确定下一步应该搜索的方向,往左还是往右。如果a列相同再比较b列。但是如果查询条件没有a列,B+树就不知道第一步应该从哪个节点查起。
可以说创建的idx_abc(a,b,c)索引,相当于创建了(a)、(a,b)(a,b,c)三个索引。
组合索引的最左前缀匹配原则:使用组合索引查询时,mysql会一直向右匹配直至遇到范围查询(>、<、between、like)就停止匹配。
覆盖索引
覆盖索引并不是说是索引结构,覆盖索引是一种很常用的优化手段。因为在使用辅助索引的时候,我们只可以拿到主键值,相当于获取数据还需要再根据主键查询主键索引再获取到数据。但是试想下这么一种情况,在上面abc_innodb表中的组合索引查询时,如果我只需要abc字段的,那是不是意味着我们查询到组合索引的叶子节点就可以直接返回了,而不需要回表。这种情况就是覆盖索引。
索引语法
创建索引
- 在CREATE TABLE时创建索引
CREATE TABLE user_index2 (
id INT auto_increment PRIMARY KEY,
first_name VARCHAR (16),
last_name VARCHAR (16),
id_card VARCHAR (18),
information text,
KEY name (first_name, last_name),
FULLTEXT KEY (information),
UNIQUE KEY (id_card)
);
- 使用ALTER TABLE命令去增加索引
添加主键索引 ,索引值必须唯一,为空
ALTER TABLE table_name ADD primary key index_name (column_list);
添加普通索引
ALTER TABLE table_name ADD 深入学习Redis:集群