zoukankan      html  css  js  c++  java
  • MySQL学习(二十五)order by 逻辑

    文章图片来自《MySQL 45讲》非原创

    概述

    本文将会介绍 order by 排序中的两种情况

    • 内存充足,全字段排序
    • 内存不充裕,rowid 排序

    什么意思呢?全字段排序讲的是整行拿到 sort buffer 中进行排序,然后返回正确的结果给客户端,而 rowid 排序只是取一行中部分字段到 sort buffer 中进行排序,而是否使用到了临时文件则是sort buffer 不够(也有可能需要排序的行数过多,总的来说就是需要排序的行数相对于sort buffer 太大了)这时就会使用到临时文件,使用到临时文件肯定相对于使用内存速度会慢,假如出现这种情况可以优化 SQL 语句对查询进行优化。

    示例

    例子来自 《MySQL 45讲》非原创 加入我们存在以下表 :

    CREATE TABLE `t` (
      `id` int(11) NOT NULL,
      `city` varchar(16) NOT NULL,
      `name` varchar(16) NOT NULL,
      `age` int(11) NOT NULL,
      `addr` varchar(128) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `city` (`city`)
    ) ENGINE=InnoDB;
    

    查询语句如下

    select city,name,age from t where city='杭州' order by name limit 1000  ;
    
    

    然后我们使用 explain 查看一下这条语句的执行逻辑。

    1297993-20200714224831707-1447576144.png

    Extra这个字段中的“Using filesort”表示的就是需要排序,MySQL会给每个线程分配一块内存用于排序,称为sort_buffer。

    为了说明这个SQL查询语句的执行过程,我们先来看一下city这个索引的示意图。

    1297993-20200714225009348-911255689.png

    全排序排序

    通常情况下,这个语句执行流程如下所示 :

    1. 初始化sort_buffer,确定放入name、city、age这三个字段;
    2. 从索引city找到第一个满足city='杭州’条件的主键id,也就是图中的ID_X;
    3. 到主键id索引取出整行,取name、city、age三个字段的值,存入sort_buffer中;
    4. 从索引city取下一个记录的主键id;
    5. 重复步骤3、4直到city的值不满足查询条件为止,对应的主键id也就是图中的ID_Y;
    6. 对sort_buffer中的数据按照字段name做快速排序;
    7. 按照排序结果取前1000行返回给客户端。

    我们暂且把这个排序过程,称为全字段排序,执行流程的示意图如下所示,下一篇文章中我们还会用到这个排序。

    1297993-20200714230755769-1627334590.jpg

    rowid 排序

    在全字段排序中,我们是取整一行数据放在 sort buffer 中进行排序,而 rowid 排序则是取部分字段,然后排好后,再回表找到想要返回的字段,回表的操作必定比不回表的操作慢,假如需要我们优化查询语句可以使用覆盖索引进行优化查询语句

    1297993-20200715223029835-727369301.jpg

    补充:

    参考

    • 《MySQL 45讲》

    { "steps": [ { "join_preparation": { "select#": 1, "steps": [ { "expanded_query": "/* select#1 */ select t1.city AS city,t1.name AS name,t1.age AS age from t1 where (t1.city = '苏州') order by t1.name limit 1000" } ] } }, { "join_optimization": { "select#": 1, "steps": [ { "condition_processing": { "condition": "WHERE", "original_condition": "(t1.city = '苏州')", "steps": [ { "transformation": "equality_propagation", "resulting_condition": "(t1.city = '苏州')" }, { "transformation": "constant_propagation", "resulting_condition": "(t1.city = '苏州')" }, { "transformation": "trivial_condition_removal", "resulting_condition": "(t1.city = '苏州')" } ] } }, { "substitute_generated_columns": { } }, { "table_dependencies": [ { "table": "t1", "row_may_be_null": false, "map_bit": 0, "depends_on_map_bits": [ ] } ] }, { "ref_optimizer_key_uses": [ { "table": "t1", "field": "city", "equals": "'苏州'", "null_rejecting": false } ] }, { "rows_estimation": [ { "table": "t1", "range_analysis": { "table_scan": { "rows": 14963, "cost": 1524.3 }, "potential_range_indexes": [ { "index": "PRIMARY", "usable": false, "cause": "not_applicable" }, { "index": "city", "usable": true, "key_parts": [ "city", "id" ] } ], "setup_range_conditions": [ ], "group_index_range": { "chosen": false, "cause": "not_group_by_or_distinct" }, "skip_scan_range": { "potential_skip_scan_indexes": [ { "index": "city", "usable": false, "cause": "query_references_nonkey_column" } ] }, "analyzing_range_alternatives": { "range_scan_alternatives": [ { "index": "city", "ranges": [ "苏州 <= city <= 苏州" ], "index_dives_for_eq_ranges": true, "rowid_ordered": true, "using_mrr": false, "index_only": false, "rows": 3058, "cost": 1121.5, "chosen": true } ], "analyzing_roworder_intersect": { "usable": false, "cause": "too_few_roworder_scans" } }, "chosen_range_access_summary": { "range_access_plan": { "type": "range_scan", "index": "city", "rows": 3058, "ranges": [ "苏州 <= city <= 苏州" ] }, "rows_for_plan": 3058, "cost_for_plan": 1121.5, "chosen": true } } } ] }, { "considered_execution_plans": [ { "plan_prefix": [ ], "table": "t1", "best_access_path": { "considered_access_paths": [ { "access_type": "ref", "index": "city", "rows": 3058, "cost": 383.4, "chosen": true }, { "access_type": "range", "range_details": { "used_index": "city" }, "chosen": false, "cause": "heuristic_index_cheaper" } ] }, "condition_filtering_pct": 100, "rows_for_plan": 3058, "cost_for_plan": 383.4, "chosen": true } ] }, { "attaching_conditions_to_tables": { "original_condition": "(t1.city = '苏州')", "attached_conditions_computation": [ ], "attached_conditions_summary": [ { "table": "t1", "attached": "(t1.city = '苏州')" } ] } }, { "optimizing_distinct_group_by_order_by": { "simplifying_order_by": { "original_clause": "t1.name", "items": [ { "item": "t1.name" } ], "resulting_clause_is_simple": true, "resulting_clause": "t1.name" } } }, { "finalizing_table_conditions": [ { "table": "t1", "original_table_condition": "(t1.city = '苏州')", "final_table_condition ": null } ] }, { "refine_plan": [ { "table": "t1" } ] }, { "considering_tmp_tables": [ { "adding_sort_to_table": "t1" } ] } ] } }, { "join_execution": { "select#": 1, "steps": [ { "sorting_table": "t1", "filesort_information": [ { "direction": "asc", "expression": "t1.name" } ], "filesort_priority_queue_optimization": { "limit": 1000, "chosen": true }, "filesort_execution": [ ], "filesort_summary": { "memory_available": 262144, "key_size": 32, "row_size": 138, "max_rows_per_buffer": 1001, "num_rows_estimate": 47566, "num_rows_found": 3058, "num_initial_chunks_spilled_to_disk": 0, "peak_memory_used": 146146, "sort_algorithm": "std::stable_sort", "unpacked_addon_fields": "using_priority_queue", "sort_mode": "<fixed_sort_key, additional_fields>" } } ] } } ] }

  • 相关阅读:
    SGU180:Inversions(树状数组)
    Android布局中match_parent和fill_parent的差别
    上拉电阻和下拉电阻
    iOS应用性能调优的25个建议和技巧
    《拼音字母》 蓝桥杯复试试题
    构建轻量级的Table View注意事项[UIKit]
    Spark学习(一) 基本操作
    水滴状的自己定义视图,让您摆脱单调的Dialog
    【Scala】使用Option、Some、None,避免使用null
    基于tornado实现web camera
  • 原文地址:https://www.cnblogs.com/Benjious/p/13308806.html
Copyright © 2011-2022 走看看