Mysql

数据库

  • 1.索引
    • 1.1 MyISAM与InnoDB区别(难度:★ 频率:★★★★)
    • 1.2 什么是索引
    • 1.3 索引的优缺点
    • 1.4 哪些字段适合加索引
    • 1.5 索引有哪几种类型?
    • 1.6索引的数据结构
      • 1.6.1 B+树
        • 1.6.1.1 二叉树
        • 1.6.1.2 红黑树(平衡二叉树)
        • 1.6.1.3 B树
      • 1.6.2 B树和B+树的区别
        • 1.6.2.1 使用B树的好处
        • 1.6.2.2 使用B+树的好处
    • 1.7 建立索引的原则
    • 1.8 in-place ALTER
    • 1.9 前缀索引
      • 1.9.1 如何创建前缀索引
      • 1.9.2 查询条件超出前缀长度, 会走前缀索引吗?
    • 1.10 最左前缀原则
    • 1.11 联合索引
      • 1.11.1 联合索引是怎么查找数据的?
      • 1.11.2 有联合索引后, 还有必要去创建单例索引吗?
    • 1.12 什么情况下会索引失效?
      • 1.12.1 like不走索引的解决方法
    • 1.13 索引优化
      • 1.13.1 ORDER BY
  • 2.SQL
    • 2.1 数据表关联
    • 2.2 什么是子查询
    • 2.3 exists的使用
    • 2.4 in和exists的区别
  • 3.SQL优化
    • 3.1 执行计划
    • 3.2 表数据过大的解决方案
      • 3.2.1 限定数据的范围
      • 3.2.2 读写分离
      • 3.2.3 分库分表

1.索引

1.1 MyISAM与InnoDB区别(难度:★ 频率:★★★★)

MyISAM InnoDB
事务支持 不支持事务 支持事务
事务支持 使用表级锁定,当一个查询需要修改表中的数据时,会锁定整个表,这可能导致并发性能下降。 使用行级锁定,只锁定需要修改的行,允许多个事务同时处理同一表的不同部分,提高并发性能。
外键 不支持 支持
性能 在读密集型工作负载下表现较好,因为它的表级锁定和简单的结构。 在写密集型工作负载和需要事务支持的应用中表现较好,因为它的行级锁定和更复杂的结构。
全文索引 支持 不支持

如何选择存储引擎?

  • MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。
  • Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。

1.2 什么是索引

索引相当于目录, 为了方便查询书中的内容, 通过对内容建立索引形成目录., 其中包含对数据表中所有记录的引用指针, 索引是一个文件, 它会占用物理空间

1.3 索引的优缺点

  • 优点:
    • 可以大大加快数据的检索速度
  • 缺点:
    • 时间方面: 创建索引和维护索引都要耗费时间, 对表中的数据进行增加、修改、删除操作, 都会动态维护索引, 会降低增、删、改的执行效率
    • 空间方面: 索引需要占用物理空间

1.4 哪些字段适合加索引

  1. 主键字段: 主键字段设置主键索引,是为了确保主键列中的值唯一性,提高数据检索效率
  2. 外键字段: 外键一般是另一表的主键列唯一键列的值。外键通常用来进行表关联操作, 设置索引可以提高连接操作的性能
# 学生信息表
create table stuInfo(	
		Scode int primary key,
		Sname char(10),
		Saddress varchar(50),
		Sgrade int,
		Semail varchar(50),
		Sbrith date	
)DEFAULT CHARSET='utf8';

# 分数表
create table score(
studentID	int,
coureseID int,	
score int,	
scoreID int primary key,	
foreign	key(studentID) references stuInfo(Scode)
)DEFAULT charset='utf8';
  1. 经常用于查询的字段: 指SQL查询语句中WHERE子句中使用的字段
  2. 经常用于排序的字段: 指SQL查询语句中ORDER BY子句中使用的字段
    • 排序字段没有索引: 将数据从硬盘分批读取到内存后, 在内存中进行排序, 最后合并排序结果
    • 排序字段有索引: 可以按照索引的顺序直接提取数据,而无需进行显式的排序
  3. 经常用于分组的字段: 指SQL查询语句中GROUP BY子句中使用的字段
    • 分组字段没有索引: 将数据从硬盘分批读取到内存后, 在内存中进行排序和分组
    • 分组字段有索引: 会直接使用索引来执行排序和分组操作,而不是在内存中进行排序和分组。
  4. 经常用于连接的字段: 对JOIN语句匹配关系ON涉及的字段, 建立索引能提高检索效率
  5. 范围查询字段: 范围查询是指查询条件中包含范围条件的查询,例如使用BETWEEN>, <, <=, >= 等比较运算符的查询。
  6. 全文检索字段: 全文检索适用于包含自然语言文本的字段,比如文章的内容、评论等。通过建立FULLTEXT索引提高检索效率, 当然, 更推荐使用全文检索引擎来实现全文检索的功能, 例如Elasticsearch等

当然即使满足了上述条件, 还是需要结合索引原则来判断是否需要建立索引

1.5 索引有哪几种类型?

  1. 主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
  2. 唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
    • 可以通过ALTER TABLE table_name ADD UNIQUE (column);创建唯一索引
    • 可以通过ALTER TABLE table_name ADD UNIQUE (column1,column2);创建唯一组合索引
  3. 普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。
    • 可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
    • 可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引
  4. 全文索引: 是目前搜索引擎使用的一种关键技术。
    • 可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引

1.6索引的数据结构

InnoDB的索引类型目前只有两种, 经常使用的InnoDB存储引擎的默认索引实现为:B+树索引

  • B+树
  • Hash

1.6.1 B+树

B+树, 存在三类节点: 根节点内部节点叶子节点
Mysql_第1张图片

  • 根节点: B+树的根节点是树的入口,它储存了整个树的信息,包括指向其他节点的指针和相关的关键字。
  • 内部节点: 内部节点是B+树中非叶子节点,它的主要作用是连接其他节点,用于索引和导航。内部节点通常包含一组关键字和指向其子节点的指针。
  • 叶子节点: 叶子节点是B+树中存储数据的节点。每个叶子节点都包含一个或多个数据项,同时也保存了对应的关键字。叶子节点之间通过指针连接在一起,形成一个有序链表,方便范围查询和遍历。

参考: 数据结构之“树”——二叉树、红黑树、B树、B+树、B*树

1.6.1.1 二叉树

每个节点最多有两个子节点。称为左子节点右子节点
Mysql_第2张图片

二叉树的缺点: 难以保证树的平衡, 顺序插入时, 会变成一个链表, 查询性能大大降低
Mysql_第3张图片

1.6.1.2 红黑树(平衡二叉树)

红黑树是一种自平衡的二叉树, 红黑树能够实现自平衡和保持红黑树特征的主要手段是:变色、左旋和右旋
Mysql_第4张图片

1.6.1.3 B树

B树属于多叉树又名平衡多路查找树(查找路径不只两个)
Mysql_第5张图片

1.6.2 B树和B+树的区别

  • 在B树中, 你可以将键和值存放在内部节点和叶子节点; 但在B+树中, 内部节点都是键, 没有值, 叶子节点同事存放键和值
  • B+树的叶子节点有一条链相连, 而B树的叶子节点各自独立

Mysql_第6张图片
B树中的键和值分别是什么意思?

  • 键(key)类似于字典中的词条
  • 值(value)则是与键关联的数据项,可以是任何类型的数据。
1.6.2.1 使用B树的好处

B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效。

1.6.2.2 使用B+树的好处

由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

1.7 建立索引的原则

  1. 不同值较少的列不适合建立索引(例如:性别)
  2. 长字符串优先考虑使用前缀索引, 这样能节省索引空间、提高检索性能
  3. 不要过度建立索引, 索引需要额外的磁盘空间
  4. 最左前缀匹配原则,组合索引非常重要的原则
  5. 较频繁作为查询条件的字段才去创建索引
  6. 更新频繁字段不适合创建索引
  7. 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可(in-place ALTER)
  8. 定义有外键的数据列一定要建立索引
  9. 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引
  10. 对于定义为text、image和bit的数据类型的列不要建立索引

经验法则
1. 推荐在设计表结构时将列设置为NOT NULL,除非有特殊需求需要存储NULL值
在mysql中,含有NULL值的列往往使得查询变得更加复杂,在涉及到索引的情况下,NULL值需要额外的处理,可能导致索引失效或者性能下降。你应该用0、一个特殊的值或者一个空串代替空值;

2.离散程度高(值之间的差异程度高)的列放到联合索引的前面
对于联合索引,数据库系统会按照索引的顺序来优化查询计划。如果将离散程度高的列放在前面,可以减小检索的数据集,提高查询性能。

  • 减小检索数据集: 数据库系统会按照索引的顺序逐级查找。如果离散程度高的列放在前面,可以更快地定位到满足查询条件的行,从而减小了需要检索的数据集大小。
  • 提高查询性能: 由于减小了检索数据集,整体查询性能可能会提高。这在大型表中尤为重要,因为检索大量数据通常会导致较长的查询时间。

3.索引字段越小越好
数据库的数据存储以页为单位, 一页存储的数据越多, 一次IO操作获取的数据越大效率越高。

  • 更好的内存效率: 索引存储在内存中,较小的索引字段意味着可以容纳更多的索引条目在有限的内存空间内,从而提高查询性能。
  • 减小磁盘空间需求: 较小的索引字段占用更少的磁盘空间,这对于大型数据库来说是重要的考虑因素,可以减小存储成本。
  • 减小I/O操作的成本: 当索引字段较小时,一次 I/O 操作可以获取更多的索引页,从而减小了磁盘 I/O 操作的成本,提高查询效率。

1.8 in-place ALTER

假设已经在数据库表中有一个针对列a的单列索引。现在,需要为列a和列b创建一个联合索引。

-- 假设原来已经有索引名称为idx_a的索引包含列a
-- 如果没有,可以替换为实际的索引名称

-- 1. 删除原有的索引
ALTER TABLE your_table_name DROP INDEX idx_a;

-- 2. 添加新的扩展索引
ALTER TABLE your_table_name ADD INDEX idx_a_b (a, b);

在Mysql中, 当你执行DROP INDEX语句时,MySQL会从表的元数据中删除索引的定义,但它并不会立即从磁盘上删除索引文件。MySQL会在后台的某个时刻,根据自身的调度和优化策略,清理不再需要的索引文件。这可以包括磁盘空间的回收等操作。

通过ADD INDEX添加新索引,如果新的索引包含了原索引的所有列,并且顺序相同,MySQL在某些情况下可能会选择重新使用原索引的数据结构,而不是创建全新的索引文件, 这被称为“in-place ALTER”

尽管有可能进行in-place ALTER,但并不是所有情况下都会发生。具体的优化行为是由MySQL的优化器和底层存储引擎共同决定的。

要注意的是,尽管in-place ALTER可能提高效率,但并不是适用于所有场景。在某些情况下,创建一个全新的索引可能更为合适,尤其是当表的大小较小,资源充足时。

1.9 前缀索引

对长字符串建立索引时, 可以只对字符串的前几个字符创建索引, 对于那些长字符串, 但只有前几个字符不同的情况非常有效, 可以节省大量的索引空间, 提升查询性能, 这种方式称为"前缀索引"或者"前缀长度索引"

ALTER TABLE table_name ADD KEY index_name(column_name(prefix_length));
# 例如:对book表中的type字段的前5个字符建立索引
ALTER TABLE book ADD KEY type_prefix(type(5));

优点:

  • 节省索引空间
    长字符串可能占用大量的索引空间,通过使用前缀索引,可以大幅减少索引的大小,降低存储和维护成本
  • 提供查询性能
    查询时,由于索引的大小减小,可以减少磁盘 I/O,提高查询性能
  • 适用于某些查询模式
    如果你的查询模式只涉及到字符串的前几个字符,那么前缀索引可能非常适合

缺点:

  • 无法使用前缀索引进行ORDER BYGROUP BY
  • 当字符串本身可能比较长,而且前几个字符完全相同,这个时候前缀索引的优势已经不明显了,就没有创建前缀索引的必要了

1.9.1 如何创建前缀索引

语法:

ALTER TABLE table_name ADD KEY(column_name(prefix_length));

怎么确定prefix_length?
prefix_length这个参数,就是前缀长度的意思,我们可以通过下面的方式来判断设置多少长度的前缀

  1. 先计算某字段全列的区分度
SELECT COUNT(DISTINCT column_name) / COUNT(*) FROM table_name;
  1. 然后再计算前缀长度为多少时和全列的区分度最相似
SELECT COUNT(DISTINCT LEFT(column_name, prefix_length)) / COUNT(*) FROM table_name;
  1. 不断地调整prefix_length的值,直到和全列计算出区分度相近,最相近的那个值,就是我们想要的值

我们看个案例
下面以某个测试表为例,数据体量在 100 万以上,表结构如下!

CREATE TABLE `tb_test` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

Mysql_第7张图片

第一步: 以name作为条件查询,查询一条记录, 查询时间为0.3秒

select * from tb_test where name like '1805.59281427%'

Mysql_第8张图片

第二步: 为name字段建立前缀索引,找出最合适的prefix_length值。首先,我们大致计算一下name字段全局不同率, 算下来是99.45%
Mysql_第9张图片

第三步: 设置不同的prefix_length值,查看对应的数据不重复比例

  • 当prefix_length为5,区分度为0.2237
    Mysql_第10张图片
  • 当prefix_length为10,区分度为0.9944
    Mysql_第11张图片
  • 当prefix_length为11,区分度为0.9945
    Mysql_第12张图片

第四步: 通过对比,我们发现当prefix_length为11,最接近全局区分度,因此可以为name创建一个长度为11的前缀索引,创建索引语句如下:

alter table tb_test add key(name(11));

Mysql_第13张图片

1.9.2 查询条件超出前缀长度, 会走前缀索引吗?

问题: 如果在字段上建立了前缀长度为5的前缀索引, 当查询条件长度超过5时, 会不会走索引呢?

select * from book where type like 'TPCCB-132%' 

步骤一: 对tpye字段建立前缀索引(长度为5), 并添加3条数据

在这里插入图片描述

步骤二: 对type字段进行模糊匹配, 匹配内容超出5个字符

explain select * from book where type like 'TPCCB-132%' 

在这里插入图片描述

  • 前缀索引起到了作用, mysql先使用前缀索引过滤出前缀为TPCCB的数据, 一共有2条, 对应上了rows为2
  • 接着对筛选出的数据一个接一个进行匹配(相当于all扫描), 得到符合TPCCB-132%的数据

接下来, 尝试建立其他几类索引, 来更直观的体会前缀索引的作用

  • type字段不建立索引
    不走索引, 对所有数据进行比对
    此时
  • type建立的前缀索引长度为2
    使用了索引, 不过依旧对所有数据进行比对, 效果等同于1, 说明需要设置合适的前缀长度
    在这里插入图片描述
  • type建立普通索引
    根据索引, 过来出1条数据, 走的弯路最少, 缺点是存在空间性能上的缺陷, 设置合适的前缀索引可以达到同样的效果-

1.10 最左前缀原则

1.创建联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边
通常情况下,优先考虑最频繁用于WHERE子句的列。这是因为索引的主要目的之一是加速查询,而最频繁用于WHERE子句的列往往是最经常用于过滤数据的列。通过将这个列放在最左侧,可以最大程度地提高索引的效用,加速查询的速度。

2.如果第一个字段是范围查询需要单独建一个索引

CREATE TABLE example (
    column1 INT,
    column2 INT,
    column3 INT,
    PRIMARY KEY (column1, column2, column3)
);

SELECT * FROM example WHERE column1 BETWEEN 100 AND 200 AND column2 = 5;

可以考虑单独为column1建立一个索引, 这并不是说联合索引不起作用,但单独为范围查询的字段建立索引可以更好地支持这种类型的查询

  • 索引大小: 联合索引的大小通常比单独索引大。在进行范围查询时,要遍历索引的一部分或全部,如果联合索引的其他列在范围查询中没有被用到,那么这部分索引的大小可能会成为不必要的开销。
  • 缓存效果: 单独为特定字段建立索引可以提高缓存效果。当范围查询涉及到的字段只是单独的索引列时,数据库系统可以更有效地利用缓存,因为缓存中只需存储单一列的索引页,而不是整个联合索引的页。
  • 避免冗余信息: 联合索引中的其他列可能包含一些冗余信息,而在范围查询中并不需要。如果只需用到单一列的索引信息,单独为该列建立索引可以减少冗余信息的处理开销。

3.使用联合索引时, 会从最左边的列一直向右匹配, 直到遇到范围查询(例如 ><BETWEENLIKE
(a,b,c,d)字段建立了索引, 并添加5条数据
Mysql_第14张图片
执行下面的sql, 查看执行计划

EXPLAIN select * from example where a = 1 and b = 2 and c > 3 and d = 4

rows列代表Mysql在执行查询时, 预计扫描的行数, 最终显示为2行
Mysql_第15张图片
原因: a = 1 and b = 2 and c > 3通过索引过滤出2条记录, 执行d=4不会走索引了, 所以是对这两条数据进行比对的

4.带头大哥不能死, 中间兄弟不能断

1.11 联合索引

联合索引是指在数据库表中利用多个列来创建一个索引的方式。在查询数据时,可以使用联合索引来加快查询的速度。与单个列的索引相比,联合索引可以更准确地定位到符合条件的数据。

创建联合索引后,数据库系统会按照索引的顺序来存储索引的值,这样就可以在查询时快速定位到需要的数据。联合索引的顺序非常重要,查询时使用的列需要与索引的顺序一致,才能充分发挥索引的作用。

联合索引跟单例索引的性能对比

  • 进行多个列联合查询时, 联合索引的查询效率更高
  • 联合索引因为要存储多个列的索引信息, 所以占用的磁盘空间更大, 在插入、更新、删除数据时, 由于要维护多个列的索引, 会增加维护成本

单例索引的索引结构
Mysql_第16张图片
联合索引的索引结构
Mysql_第17张图片

1.11.1 联合索引是怎么查找数据的?

例如: where name=‘Bob’ and phone = ‘132xxx’

B+Tree会先去比较name=‘Bob’, 如果name找到, 再去比较phone = ‘132xxx’

如果查询条件中没有name(跳过最左列), 就不知道第一步应该去哪里比对了, 因为建立索引树时name是第一个比较因子

1.11.2 有联合索引后, 还有必要去创建单例索引吗?

CREATE INDEX idx_name_phoneonuser_innodb(name,phone);

当我们创建一个联合索引的时候,用左边的字段name去查询的时候,也能用到索引,所以单独为name创建一个索引完全没必要。

如果我们创建三个字段的索引index(a,b,c),相当于创建三个索引:index(a)、index(a,b)、index(a,b,c)。

where b=?where b=? and c=?where a=? and c=?是不能使用到索引的。因为不能不用第一个字段,不能中断。

1.12 什么情况下会索引失效?

1.使用函数(replace\SUBSTR\CONCAT\sum\count\avg)、表达式、计算(+ - * /), 因为当前值改变后就无法与索引存的值匹配山

SELECT * FROM user_innodb where left(name, 3)='张三'-- left函数是一个字符串函数,它返回具有指定长度的字符串的左边部分

2.使用范围查询(!=,<=>,in)会导致右边列失效。因为二叉树的查找是=查找,若是一个范围的话无法继续下探

  • 最左列使用范围查询,该列也不会使用索引,全部索引列失效
  • 其余列用范围,当前列仍会使用索引,但右边索引列失效
SELECT * FROM user_innodb where name='张三' and age > 22

3.like以通配符开头("%abc"), 索引会失效, 变为全表扫描操作, 因为无法判断%代表多少字符

4.少用or, 用它了连接时可能会导致索引失效
因为or操作符要求同时满足多个条件,数据库无法有效地使用索引来筛选数据。当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效。

可以考虑使用其他方法来优化查询性能,例如使用"union"操作符将多个子查询的结果合并,或者重新设计查询语句以避免使用"or"操作符。

5.is null,is not null 无法使用索引

1.12.1 like不走索引的解决方法

SELECT object_name from t1 WHERE object_name LIKE '%ABC';

解决方法:

CREATE INDEX idx_t1_objectname2 ON t1(reverse(object_name));
SELECT object_name FROM t1 WHERE REVERSE(object_name) LIKE REVERSE('%ABC');

1.13 索引优化

1.13.1 ORDER BY

1.ORDER BY的索引优化

SELECT id, name FROM `user` ORDER BY id;

id建立索引即可

2.WHERE + ORDER BY的索引优化

SELECT name, sex, age FROM `user` WHERE sex = '男' ORDER BY age;

建立一个联合索引(sex, age)来实现order by优化

如果sex对应多个值, 就无法利用索引来实现order by的优化

SELECT name, sex, age FROM `user` WHERE sex in ('男', '女') ORDER BY age;

3. WHERE+ 多个字段ORDER BY

SELECT name, sex, age, height FROM `user` WHERE sex = '男' ORDER BY age, height LIMIT 0,10;

建立联合索引(sex, age, height)实现order by的优化, 比建立(age, height, sex)索引效果要好很多

2.SQL

2.1 数据表关联

  • 交叉连接(CROSS JOIN): 返回被连接的两个表所有数据行的笛卡尔积
  • 内连接(INNER JOIN)
  • 外连接(LEFT JOIN/RIGHT JOIN)
  • 联合查询(UNION与UNION ALL)
  • 全连接(FULL JOIN)

1.内连接(INNER JOIN)
内连接分为两种: 显式的隐式的

  • 隐式的内连接,没有INNER JOIN,形成的中间表为两个表的笛卡尔积
select * from a, b where a.id = b.cid
  • 显示的内连接, 有INNER JOIN,形成的中间表为两个表经过ON条件过滤后的笛卡尔积。
select * from a inner join b on a.id = b.cid

更推荐使用显式内连接

  • 性能方面
    MySQL查询优化器可以更好地优化显示连接,使用适当的连接顺序和连接类型来提高查询执行效率
  • 可读性和维护性方面
    显示连接通常比隐式连接更易读和维护

2.全连接(FULL JOIN)
FULL JOIN关键字返回左表右表中所有的行。

3.union all和union的区别

  • 显示结果不同

    • union: 对两个结果集进行并集操作, 不包括重复行,相当于distinct, 同时进行默认规则的排序;
    • union all: 对两个结果集进行并集操作, 包括重复行, 即所有的结果全部显示, 不管是不是重复;
  • 对重复结果的处理不同

    • union all是直接连接,取到得是所有值,记录可能有重复;
    • union 是取唯一值,记录没有重复。所以union在进行表链接后会筛选掉重复的记录
  • 对排序的处理不同

    • union将会按照字段的顺序进行排序;
    • union all只是简单的将两个结果合并后就返回

2.2 什么是子查询

一条SQL语句的查询结果作为另一条SQL查询语句的条件或者查询结果
多条SQL语句嵌套使用, 内部的SQL查询语句称为子查询

子查询的三种情况

  • 单行单例
    子查询的结果集是一个值, 父查询使用:=、 <、 > 等运算符
select  * from employee where salary=(select max(salary) from employee);   
  • 多行单例
    子查询的结果集是一个数组, 父查询使用:in 运算符
select  * from employee where salary in (select salary/10 from employee);    
  • 多行多列
    子查询的结果集类似于一张虚拟表, 不能用于where条件,用于select子句中做为子表
select * from dept d,  (select dept_id, salary from employee where join_date > '2011-1-1') e where e.dept_id =  d.id;    

2.3 exists的使用

Mysql_第18张图片

select * from student s WHERE exists (select 1 from class c where s.class_id = c.id)

Mysql_第19张图片
1.exists语句的执行顺序

  1. 首先执行循环(select * from student)
  2. 外循环返回的结果每一行都会拿到内层循环执行

select class_id from student查询的为c1,c1,c2,c3, 然后依次拿这些值去class表中对比

  • c1 √
  • c1 √
  • c2 √
  • c3 ×

2.4 in和exists的区别

  • 子查询表大的用exists, 子查询表小的用in
  • not innot exists, 如果查询语句使用not in, 那么内外表都进行全表扫描, 没有用到索引; 而not exists的子查询依然用到比表上的索引, 所以无论哪个表大, not exists都比not in更快

3.SQL优化

3.1 执行计划

对于低性能的SQL的定位, 最重要最有效的方法就是使用执行计划
Mysql_第20张图片

1.id
id标识查询执行的顺序
id列的值只有两种情况: 数字null

  • ID相同时,从上到下执行
  • ID不同时,从大到小执行
  • ID为null, 表示结果集, 不需要使用它查询, 常出现在含union等查询语句中

2.select_type

select_type description
SIMPLE 简单的 select 查询,查询中不包含子查询或者UNION
PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为Primary
SUBQUERY 在SELECT或WHERE列表中包含了子查询
DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生);MySQL会递归执行这些子查询, 把结果放在临时表里
UNION 若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
UNION RESULT 从UNION表获取结果的SELECT
DEPENDENT SUBQUERY 在SELECT或WHERE列表中包含了子查询,子查询基于外层
UNCACHEABLE SUBQUREY 无法被缓存的子查询

(1) SIMPLE 类型
简单的 select 查询,查询中不包含子查询或者UNION
Mysql_第21张图片
(2) PRIMARY类型
查询中若包含任何复杂的子部分,最外层查询则被标记为Primary
Mysql_第22张图片
(3) SUBQUERY类型
在SELECT或WHERE列表中包含了子查询
Mysql_第23张图片
(4) DERIVED类型
在FROM列表中包含的子查询被标记为DERIVED(衍生);MySQL会递归执行这些子查询, 把结果放在临时表里
Mysql_第24张图片
(5) UNION类型
若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
Mysql_第25张图片
(6) UNION RESULT类型
从UNION表获取结果的SELECT
Mysql_第26张图片
(7) DEPENDENT SUBQUERY类型
在SELECT或WHERE列表中包含了子查询,子查询基于外层
Mysql_第27张图片

3.table
指明是从那个表中获取数据

4.type

含义
system 这是const连接类型的特例,当查询的表只有一行时使用
const 表中有且只有一个匹配的行时使用,如对主键或唯一索引的查询,这是效率最高的链接方式
eq_ref 唯一索引或主键查询,对应每个索引建,表中只有一条记录与之匹配 【A表扫描每一行B表只有一行匹配满足】
ref 非唯一索引查找,返回匹配某个单独值的所有行
ref_or_null 类似于ref类型的查询,但是附加了对NULL值列的查询
index_merge 该链接类型表示使用了索引合并优化方法
range 索引范围扫描,常见于between、>、< 这样的查询条件
index FULL index Scan全索引扫描,同ALL的区别是,遍历的是索引树
ALL FULL TABLE Scan全表扫描 ,这是效率最差的链接方式
CREATE TABLE employees (
    id INT AUTO_INCREMENT PRIMARY KEY,
    department_id INT NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    last_name VARCHAR(30) NOT NULL,
    salary DECIMAL(10, 2) NOT NULL,
    hire_date DATE NOT NULL,
    INDEX idx_department_id (department_id),
    INDEX idx_last_name (last_name)
);

(1) system
当查询仅涉及到一行时,会出现system类型,例如只有一行数据的表。

SELECT * FROM employees LIMIT 1;

(2) const
表示常量, 查询仅涉及到主键或唯一索引的一行时,会出现const类型。

SELECT * FROM employees WHERE id = 1;

(3) eq_ref
当使用唯一索引或主键作为连接条件时,会出现eq_ref类型。

CREATE TABLE departments (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30) NOT NULL
);

SELECT e.*, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id;

(4) ref
当通过非唯一索引进行连接或查询时,会出现ref类型

SELECT * FROM employees WHERE department_id = 1;

(5) range
当使用范围条件进行查询时,会出现range类型

ALTER TABLE employees ADD INDEX idx_hire_date (hire_date);
SELECT * FROM employees WHERE hire_date BETWEEN '2022-01-01' AND '2022-12-31';

(6) index
当查询中使用到了全索引扫描时,会出现index类型

SELECT last_name FROM employees ORDER BY last_name;

全索引扫描(index)是一种比全表扫描(all)更高效的查询方式, 因为它仅扫描索引, 而非整个表

由于 last_name 列有索引,MySQL 可以直接从索引中获取数据并按照索引的顺序进行排序。这种情况下,索引扫描(index)比全表扫描(all)更高效,因为它只需要扫描索引而不是整个表。

(7) all
当查询需要全表扫描时,会出现all类型

SELECT * FROM employees WHERE salary > 5000;

5.possible_keys
可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 NULL时就要考虑当前的SQL是否需要优化了。

6.key
显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。

7.key_length
索引长度

3.2 表数据过大的解决方案

3.2.1 限定数据的范围

禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。

3.2.2 读写分离

将数据库分为主库从库, 主库负责写, 从库负责读

3.2.3 分库分表

包括冷热数据分离、历史数据归档等

你可能感兴趣的:(mysql,数据库)