• 郑州软件开发

    |

    郑州网站建设

    |

    郑州网站制作

    |

    APP开发

    |
  • 郑州软件开发电话: 15937160052
  • 我不得不告诉大家的MySQL优化“套路”
  • 发表日期:2018-06-03   浏览:1740   关键词: 数据库优化 MySQL优化 MySQL数据库优化
  • 说起 MySQL 的查询优化,相信大家收藏了一堆奇技淫巧:不能使用 SELECT *、不使用 NULL 字段、合理创建索引、为字段选择合适的数据类型.....

    你是否真的理解这些优化技巧?是否理解它背后的工作原理?在实际场景下性能真有提升吗?我想未必。

    因而理解这些优化建议背后的原理就显得尤为重要,希望本文能让你重新审视这些优化建议,并在实际业务场景下合理的运用。

    MySQL 逻辑架构

    如果能在头脑中构建一幅 MySQL 各组件之间如何协同工作的架构图,将有助于深入理解 MySQL 服务器。下图是 MySQL 的逻辑架构图:

    MySQL 逻辑架构

    MySQL 的逻辑架构整体分为三层,最上层为客户端层,并非 MySQL 所独有,诸如:连接处理、授权认证、安全等功能均在这一层处理。

    MySQL 的大多数核心服务均在中间这一层,包括查询解析、分析、优化、缓存、内置函数(比如:时间、数学、加密等函数)。所有的跨存储引擎的功能也在这一层实现:存储过程、触发器、视图等。

    最下层为存储引擎,负责 MySQL 中的数据存储和提取。和 Linux 下的文件系统类似,每种存储引擎都有其优势和劣势。

    中间的服务层通过 API 与存储引擎通信,这些 API 接口屏蔽了不同存储引擎间的差异。

    MySQL 查询过程

    我们总是希望 MySQL 能够获得更高的查询性能,最好的办法是弄清楚 MySQL 是如何优化和执行查询的。

    一旦理解了这一点,就会发现:很多的查询优化工作实际上就是遵循一些原则让 MySQL 的优化器能够按照预想的合理方式运行。

    当向 MySQL 发送一个请求的时候,MySQL 到底做了些什么呢?

    MySQL 查询过程

    客户端/服务端通信协议

    MySQL 的客户端/服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。

    一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。

    客户端用一个单独的数据包将查询请求发送给服务器,所以当查询语句很长的时候,需要设置 max_allowed_packet 参数。

    但是需要注意的是,如果查询实在是太大,服务端会拒绝接收更多数据并抛出异常。

    与之相反的是,服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。

    因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用 SELECT * 以及加上 LIMIT 限制的原因之一。

    查询缓存

    在解析一个查询语句前,如果查询缓存是打开的,那么 MySQL 会检查这个查询语句是否命中查询缓存中的数据。

    如果当前查询恰好命中查询缓存,在检查一次用户权限后直接返回缓存中的结果。这种情况下,查询不会被解析,也不会生成执行计划,更不会执行。

    MySQL 将缓存存放在一个引用表(不要理解成 table,可以认为是类似于 HashMap 的数据结构),通过一个哈希值索引。

    这个哈希值通过查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息计算得来。

    所以两个查询在任何字符上的不同(例如:空格、注释),都会导致缓存不会命中。

    如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果都不会被缓存。

    比如函数 NOW() 或者 CURRENT_DATE() 会因为不同的查询时间,返回不同的查询结果。

    再比如包含 CURRENT_USER 或者 CONNECION_ID() 的查询语句会因为不同的用户而返回不同的结果,将这样的查询结果缓存起来没有任何的意义。

    既然是缓存,就会失效,那查询缓存何时失效呢?MySQL 的查询缓存系统会跟踪查询中涉及的每个表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。

    正因为如此,在任何的写操作时,MySQL 必须将对应表的所有缓存都设置为失效。

    如果查询缓存非常大或者碎片很多,这个操作就可能带来很大的系统消耗,甚至导致系统僵死一会儿。

    而且查询缓存对系统的额外消耗也不仅仅在写操作,读操作也不例外:

    • 任何的查询语句在开始之前都必须经过检查,即使这条 SQL 语句永远不会命中缓存。

    • 如果查询结果可以被缓存,那么执行完成后,会将结果存入缓存,也会带来额外的系统消耗。

    基于此,我们要知道并不是什么情况下查询缓存都会提高系统性能,缓存和失效都会带来额外消耗,只有当缓存带来的资源节约大于其本身消耗的资源时,才会给系统带来性能提升。

    但如何评估打开缓存是否能够带来性能提升是一件非常困难的事情,也不在本文讨论的范畴内。

    如果系统确实存在一些性能问题,可以尝试打开查询缓存,并在数据库设计上做一些优化,比如:

    • 用多个小表代替一个大表,注意不要过度设计。

    • 批量插入代替循环单条插入。

    • 合理控制缓存空间大小,一般来说其大小设置为几十兆比较合适。

    • 可以通过 SQL_CACHE 和 SQL_NO_CACHE 来控制某个查询语句是否需要进行缓存。

    最后的忠告是不要轻易打开查询缓存,特别是写密集型应用。如果你实在是忍不住,可以将 query_cache_type 设置为 DEMAND。

    这时只有加入 SQL_CACHE 的查询才会走缓存,其他查询则不会,这样可以非常自由地控制哪些查询需要被缓存。

    当然查询缓存系统本身是非常复杂的,这里讨论的也只是很小的一部分,其他更深入的话题没有涉及,比如:缓存是如何使用内存的?如何控制内存的碎片化?事务对查询缓存有何影响等等。

    语法解析和预处理

    MySQL 通过关键字将 SQL 语句进行解析,并生成一棵对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如 SQL 中是否使用了错误的关键字或者关键字的顺序是否正确等等。

    预处理则会根据 MySQL 规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。

    查询优化

    经过前面的步骤生成的语法树被认为是合法的了,并且由优化器将其转化成查询计划。

    多数情况下,一条查询可以有很多种执行方式,最后都返回相应的结果。优化器的作用就是找到这其中最好的执行计划。

    MySQL 使用基于成本的优化器,它尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。

    在 MySQL 可以通过查询当前会话的 last_query_cost 的值来得到其计算当前查询的成本。

    mysql> select * from t_message limit 10;

    ...省略结果集

    mysql> show status like 'last_query_cost';

    +-----------------+-------------+

    | Variable_name   | Value       |

    +-----------------+-------------+

    | Last_query_cost | 6391.799000 |

    +-----------------+-------------+

    示例中的结果表示优化器认为大概需要做 6391 个数据页的随机查找才能完成上面的查询。

    这个结果是根据一些列的统计信息计算得来的,这些统计信息包括:每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的分布情况等等。

    有非常多的原因会导致 MySQL 选择错误的执行计划,比如统计信息不准确、不会考虑不受其控制的操作成本(用户自定义函数、存储过程)。

    MySQL 认为的最优跟我们想的不一样(我们希望执行时间尽可能短,但 MySQL 值选择它认为成本小的,但成本小并不意味着执行时间短)等等。

    MySQL 的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划:

    • 重新定义表的关联顺序(多张表关联查询时,并不一定按照 SQL 中指定的顺序进行,但有一些技巧可以指定关联顺序)。

    • 优化 MIN() 和 MAX() 函数(找某列的最小值,如果该列有索引,只需要查找 B+Tree 索引最左端,反之则可以找到最大值,具体原理见下文)。

    • 提前终止查询(比如:使用 LIMIT 时,查找到满足数量的结果集后会立即终止查询)。

    • 优化排序(在老版本 MySQL 会使用两次传输排序,即先读取行指针和需要排序的字段在内存中对其排序,然后再根据排序结果去读取数据行,而新版本采用的是单次传输排序,也就是一次读取所有的数据行,然后根据给定的列排序。对于 I/O 密集型应用,效率会高很多)。

    随着 MySQL 的不断发展,优化器使用的优化策略也在不断的进化,这里仅仅介绍几个非常常用且容易理解的优化策略,其他的优化策略,大家自行查阅吧。

  • 相关新闻
    • 如何搭建景区网站建设?
      搭建一个景区网站建设的很有必要的,因为做了网站可以增加客户对旅游景点的信任,还有一方面是能让旅游企业获得更多
      日期:2022-09-02 浏览:2542
      电商网站建设为什么要独立开发?
      线上购物的基础就来源于企业的电商网站建设,做好电商网站,才能让客户在线上购物,而且现在市面上有很多的电商网站
      日期:2022-03-24 浏览:3223
      郑州建网站细节分析说明
      郑州建网站要重视产品介绍和展示,作为网站建设,其最重要的一个特点就是能够较好的进行产品的推广和宣传
      日期:2021-12-26 浏览:3368
  • 一个企业的成长,从无到有,从一个人到一群人,从一种元素到元素集合;或多或少、都要有品牌意识的存在……