首页 > 数据库 >mysql 简单进阶 ———— 重构查询[二]

mysql 简单进阶 ———— 重构查询[二]

时间:2023-07-30 19:22:37浏览次数:35  
标签:重构 进阶 查询 索引 字段 key mysql ref id

前言

简单整理一下重构查询。

正文

为什么我们需要重构查询,原因也很简单,那就是查询慢。

为什么会查询慢?

查询性能慢底下的最基本的原因是访问的数据太多。 某些查询不可避免地需要筛选大量的数据,但这并不常见。

大部分性能低下的查询都可以通过减少访问的数据流的方式进行优化。

  1. 确认应用程序是否检索大量超过需要的数据。这意味着访问了太多的行,但有时候也可能访问了太多的列。
  2. 确认mysql 服务器是否在分析大量超过需要的数据行。

最先想到的是是否向数据库请求了不需要的数据。

举个很低级的例子:

  1. 查询了多余的行
在c#的linq中通过tolist在执行了 "select * from payment",然后通过take 和 skip 来获取需要的数据,这个时候其实是查询了全部的行,然后才在应用层进行过滤了。
  1. 查询了多余的列

比如select * from payment中,通过查询了全部的列,但是很多列是不需要的。

但是这个也不能以偏概全,要看应用场景,比如查询出来做缓存,那么可能只查询了一次,然后用的是缓存的数据,但是通过查询多余的列出来可以供多个地方使用。

  1. 重复查询了通用的数据

比如一个数据高频访问,低更新,那么就应该考虑使用缓存了,这样避免大量的查询。

还有一个重要的指标,mysql 是否扫描了额外的记录。

是否扫描了额外的记录,有下面3个标准:

  1. 响应的时间

  2. 扫描的行数

  3. 返回的行数

没有哪个指标能够完美地衡量查询的开销,但是他们能够反映了mysql在内部执行查询时候需要访问的数据,并可以大概推算出运行的时间。

这三个指标都会记录到mysql的慢日志中,所以检查慢日志记录是找出扫描行数过多的查询的好办法。

  1. 响应时间

响应时间包括两个,一个是服务时间,一个是排队时间。

服务时间是指数据库处理这个查询真正花了多长时间。排队时间是指因为等待某些资源而没有真正的执行查询的时间,可能是等i/o 操作完成,也可能是等行锁。

  1. 扫描的行数和返回的行数

最理想的情况是扫描的行数等于返回的行数,这个就太理想了。一般是在1:1,和10:1之间,那么就是要尽量减少扫描的行数。

  1. 扫描的行数和访问的类型

这个访问的类型指的是explain 后,返回的类型。

1. id:表示查询执行计划中的每个步骤的唯一标识符。id的值越大,表示该步骤的执行顺序越靠后。

2. select_type:表示该步骤的查询类型。常见的查询类型包括SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)等。

3. table:表示该步骤涉及的表名。

4. partitions:表示该步骤涉及的分区。

5. type:表示该步骤使用的访问方法,也称为连接类型。常见的访问方法包括ALL(全表扫描)、index(使用索引扫描)、range(范围扫描)等。

6. possible_keys:表示该步骤可能使用的索引。

7. key:表示该步骤实际使用的索引。

8. key_len:表示该步骤使用的索引的长度。

9. ref:表示该步骤使用的索引的列与前一个步骤的关联条件。

10. rows:表示该步骤扫描的行数。

11. filtered:表示该步骤满足WHERE条件的行数占总扫描行数的比例。

12. Extra:表示该步骤的额外信息。常见的额外信息包括Using filesort(使用文件排序)、Using temporary(使用临时表)等。

上面说一下可能难以理解的几个元素:

key_len字段表示使用的索引的长度,它表示索引中被使用的字节数。索引长度越短,查询性能通常越好。

举个例子来说明key_len的含义:

假设有一个表table,其中有一个列name定义为VARCHAR(100),并且有一个非唯一索引idx_name(name)。如果在查询中使用了以下条件:

SELECT * FROM table WHERE name = 'John'

那么在执行EXPLAIN语句后,可以看到key_len字段的值为100。这是因为查询条件中的name列的长度为100,索引idx_name的长度也为100,所以key_len的值为100。

另外,如果查询条件中使用了name列的前缀,比如:

SELECT * FROM table WHERE name LIKE 'J%'

那么在执行EXPLAIN语句后,可以看到key_len字段的值会根据前缀的长度而变化。如果前缀长度为1,则key_len的值为1。如果前缀长度为2,则key_len的值为2。以此类推。

需要注意的是,key_len并不是表示索引的实际长度(比如字节数),而是表示索引中被使用的字节数。它可以用于比较不同查询条件对索引的利用程度,从而进行索引的优化。

这里很多人理解的可能就以为是索引的长度,其实是实际使用的索引的长度。

还有一个是ref:

在MySQL的EXPLAIN输出中,ref是一个关键字段,表示查询过程中使用的索引列与上一个表的列之间的引用关系。ref字段通常用于连接操作(JOIN)中,指示连接条件使用的索引列。

以下是一个具体的例子来解释ref字段的含义:

假设有两个表table1和table2,它们的结构如下:

table1:
- id (主键)
- name

table2:
- id (主键)
- table1_id (外键)

现在我们执行以下查询语句:

SELECT * FROM table1 JOIN table2 ON table1.id = table2.table1_id WHERE table1.name = 'John'

在执行EXPLAIN语句后,可以看到以下输出:

id | select_type | table | partitions | type | possible_keys | key           | key_len | ref              | rows | Extra
1  | SIMPLE      | table1| NULL       | ref  | PRIMARY       | PRIMARY       | 4       | const            | 1    | Using index
1  | SIMPLE      | table2| NULL       | ref  | table1_id     | table1_id     | 4       | table1.id        | 2    | Using index

- ref字段:在这个例子中,第二行的ref字段的值为table1.id,表示查询过程中使用了table1表的id列作为索引列与table2表的table1_id列进行连接。
- key字段:key字段的值为table1_id,表示查询使用了名为table1_id的索引。
- rows字段:rows字段的值为2,表示MySQL估计需要扫描的行数为2。

在这个例子中,我们可以看到ref字段指示了连接操作中使用的索引列之间的引用关系。通过理解ref字段的含义,我们可以更好地理解连接操作的执行过程,并进行性能优化和调整。

在MySQL的EXPLAIN输出中,当ref字段的值为const时,表示查询过程中使用的索引列与一个常量值之间的引用关系。这意味着查询使用了一个常量值来匹配索引列。

以下是一个具体的例子来解释ref字段为const的含义:

假设有一个表table,其中有两个列id和name,并且有一个唯一索引idx_name(name)。现在我们执行以下查询语句:

SELECT * FROM table WHERE name = 'John'

在执行EXPLAIN语句后,可以看到以下输出:

id | select_type | table | partitions | type | possible_keys | key      | key_len | ref   | rows | Extra
1  | SIMPLE      | table | NULL       | ref  | idx_name      | idx_name | 102     | const | 1    | Using index

- ref字段:在这个例子中,ref字段的值为const,表示查询过程中使用的索引列与常量值之间的引用关系。
- key字段:key字段的值为idx_name,表示查询使用了名为idx_name的索引。
- rows字段:rows字段的值为1,表示MySQL估计需要扫描的行数为1。

在这个例子中,ref字段为const表示查询使用了常量值来匹配索引列。这样的查询通常会非常高效,因为MySQL可以直接使用索引来定位匹配的行,而不需要进一步的比较操作。

总结来说,当ref字段为const时,表示查询使用了一个常量值来匹配索引列,这通常会带来高效的查询性能。

除了const之外,ref字段可能还有其他的取值,具体取决于查询语句和索引的使用情况。以下是一些常见的ref取值:

1. ref: 当查询使用索引列与另一个表的列进行连接时,ref字段的值可能是另一个表的列名。例如,在连接操作(JOIN)中,如果使用了索引列与另一个表的列进行连接,ref字段可能显示连接条件中使用的列名。

2. NULL: 当查询不使用索引列与其他表进行连接时,或者查询没有使用索引时,ref字段的值可能为NULL。这表示查询没有引用其他表的列。

3. Multiple values: 在某些情况下,ref字段可能会显示多个值,表示查询使用了多个索引列与其他表的多个列进行连接。这通常发生在复杂的连接操作中。

请注意,ref字段的具体取值取决于查询语句、索引的使用和表之间的关系。因此,在不同的查询中,ref字段可能会有不同的取值。了解和理解ref字段的含义可以帮助我们更好地理解查询执行过程,并进行性能优化和调整。

当查询不使用任何索引或者没有引用其他表的列时,ref字段的值可能为NULL。这种情况下,查询只涉及到单个表,没有进行连接操作或者索引的使用。

以下是一个示例来说明ref字段为NULL的情况:

假设有一个表table,其中有两个列id和name。我们执行以下查询语句:

SELECT * FROM table WHERE id = 1

在执行EXPLAIN语句后,可以看到以下输出:

id | select_type | table | partitions | type | possible_keys | key  | key_len | ref | rows | Extra
1  | SIMPLE      | table | NULL       | ALL  | NULL          | NULL | NULL    | NULL| 1    | NULL

- ref字段:在这个例子中,ref字段的值为NULL,表示查询没有引用其他表的列,也没有使用任何索引。

这种情况下,ref字段为NULL表示查询只涉及到了单个表,没有进行连接操作或者使用索引。这样的查询可能会导致全表扫描,对于大表来说性能较差。在优化查询性能时,可以考虑添加适当的索引或者修改查询语句来避免全表扫描。

那么这个type 有全表扫描、范围扫描、唯一索引查询、常数引用等。

举个例子:

EXPLAIN select film_id, actor_id
from sakila.film_actor
where  film_id =1;

这里的type 就是通过ref来实现了,也就是通过索引来实现。

如果没有索引会变成下面这样:

就是进行了全表扫描了。扫描了5073行,但是得到了才10行。

type 有下面这些类型:

1. system:表示只有一行的表,这是const类型的一个特例。例如,SELECT * FROM table WHERE primary_key = 1。

2. const:表示通过索引一次就能找到的常量值查询。例如,SELECT * FROM table WHERE primary_key = 1。

3. eq_ref:表示使用唯一索引查找。例如,SELECT * FROM table1 JOIN table2 ON table1.key = table2.key。

4. ref:表示使用非唯一索引查找。例如,SELECT * FROM table1 WHERE key = 'value'。

5. range:表示使用索引范围查找。例如,SELECT * FROM table WHERE key BETWEEN 1 AND 10。

6. index:表示全索引扫描。例如,SELECT * FROM table WHERE key LIKE 'value%'。

7. all:表示全表扫描。例如,SELECT * FROM table。

8. index_merge:表示使用多个索引合并的结果。例如,SELECT * FROM table WHERE key1 = 'value' OR key2 = 'value'。

9. unique_subquery:表示使用子查询的结果进行唯一性判断。例如,SELECT * FROM table WHERE key = (SELECT key FROM other_table)。

10. index_subquery:表示使用子查询的结果进行索引查找。例如,SELECT * FROM table WHERE key IN (SELECT key FROM other_table)。

那么如果我们遇到了一个复杂的查询我们应该如何处理呢?

  1. 可以将一个复杂的查询分成几个简单的查询,这样扫描的行数,查询的复杂度可能会降低,那么效率就有可能更高。

  2. 切分查询

举一个工作中遇到的删除的例子。比如要删除3个月前并且标记为deleted的数据,那么通过语句直接删除是不可能的。

那么可以通过一天一天的查找删除,并且每次删除固定的条数比如50条,这样每次扫描的索引会很少。 其实就是将大的操作,花费的时间分配到更小粒度里面去了。

  1. 分解关联查询

该系列持续更新。

标签:重构,进阶,查询,索引,字段,key,mysql,ref,id
From: https://www.cnblogs.com/aoximin/p/17588595.html

相关文章

  • MySQL使用binlog恢复数据
    1.什么是BinlogMySQL的二进制日志(Binlog)是一种事务日志,用于记录对数据库的更改操作。Binlog主要用于MySQL复制和恢复:复制:从库通过拉取主库的binlog实现主从数据一致恢复:通过重放binlog恢复数据丢失或误操作情况1.1.Binlog的工作原理在MySQL中,每个事务都会在提交后......
  • Flutter系列文章-Flutter进阶2
    这一节我将再详细地为您介绍Flutter进阶主题,包括导航和路由、状态管理、异步处理、HTTP请求和RestAPI,以及数据持久化。让我们逐个介绍这些主题。1.导航和路由在Flutter中,导航和路由是构建多页面应用的关键概念。导航是指从一个页面(或称为路由)切换到另一个页面的过程。每个......
  • 通过Redis+Mysql来自定义Spring-Statemachine的持久化
    我们在使用Spring状态机的时候,往往需要对于StateMachine持久化操作,但是官方为我们提供的基于redis的持久化并不是特别好,一方面是因为只存redis容易导致数据丢失,另一方面因为状态机的特性需要对应的StateMachine的数据永久有效,导致redis中的key永不过期。我现在希望实现将StateMac......
  • MySQL Group Replication 搭建
    目录参考资料环境准备说在前面单服务器部署1windows要求与限制1.1下载并解压mysql5.7.311.2创建数据库文件保存目录data1.3添加配置文件1.4初始化数据1.5依次启动3个实例1.6调整root用户名密码1.7创建复制使用的用户凭证1.8引导组复制1.9验证报错参考资料https......
  • 十三、ElasticSearch和Kibana进阶--谷粒商城
    导入样本测试数据准备一份顾客银行账户信息的虚构的JSON文档样本。每个文档都有下列的schema(模式)。{"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gen......
  • mysql 简单进阶 ———— 多列索引[一]
    前文整理一下mysql的一些简单进阶技巧,来源于高性能mysql,但不是根据书的序列来的。正文有一个问题,那就是我们为什么要创建多列索引呢?这是要思考的一个问题。为什么创建多列索引,而不是创建多个单列索引。需要弄明白这个问题,那么就需要知道索引是如何工作的。为什么有索引这个......
  • C++ 算法进阶系列之再聊聊动态规划的两把刷子
    1.前言递归和动态规划是算法界的两个扛把子,想进入算法之门,则必须理解、掌握这两种算法的本质。一旦参悟透这2种算法的精髓,再加上对树、图等复杂数据结构的深入理解,可以解决大部分的算法问题。本文通过几个典型案例,再次聊聊动态规划算法。其实动态规划算法也就2把刷子。找到......
  • Mysql数据库中数据量特别大,读取特别慢,已经做了索引,怎么优化
    当MySQL数据库中的数据量特别大,读取操作变得特别慢,即使已经添加了索引,仍然需要进一步优化。下面是一些建议以及示例代码来优化这种情况:使用合适的索引:确保为频繁查询的列添加了合适的索引。可以使用EXPLAIN语句来分析查询的执行计划,检查是否正确使用了索引。示例代码:sqlCopycodeE......
  • 雀魂07 进阶技巧
    在无人被飞的情况下,东场每个人一个庄位,而南场每个人是两个庄。东场运气>技术,南场正好相反 制定振听规则的意义在于防守判断与减少见逃行为的发生。所以,在出牌的后期,要如果自己的牌处于听的状态,但是为了防止其他人优势和。可以放弃和牌,进行防守。出牌的时候,参考别人打出过的牌,......
  • MySQL学习-DML(Data Manipulation Language)数据--select语句02
    表连接:分为内连接和外连接,常用内连接。当需要同时显示多个表中字段时,就可以用表连接。内连接:仅选出两张表中互相匹配的记录外连接:还会选出其他不匹配的记录外连接包含左连接和右连接左连接: ......