zoukankan      html  css  js  c++  java
  • order by 运行过程

    应用场景:

    很多时候会碰到需要根据指定字段排序来显示结果的需求。下面以市民表为例,简要说明 order by 在我们数据库中到底是怎么运行的。

    假设一张市民表,表的定义如下:

    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 * from t where city = 'Gary Berry' order by name limit 1000;

    我们的市民表 t 是存在city 的索引的,下面查看下这个语句的执行计划:

    root@172.16:  [test]> explain select * from t where city = 'Gary Berry' order by name limit 1000;
    +----+-------------+-------+------+---------------+------+---------+-------+------+----------------------------------------------------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                                              |
    +----+-------------+-------+------+---------------+------+---------+-------+------+----------------------------------------------------+
    |  1 | SIMPLE      | t     | ref  | city          | city | 66      const |   13 | Using index condition; Using where; Using filesort |
    +----+-------------+-------+------+---------------+------+---------+-------+------+----------------------------------------------------+

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

    通常这个执行流程如下:

    1、初始化sort_buffer,确定放入 name ,city ,age这是三个字段;

    2、从索引 city 找到第一个满足 city = ‘Gary Berry’ 条件的主键id;

    3、到主键 id索引取出整行,取出 name,city,age三个字段的值,存入sort_buffer 中;

    4、从索引 city 取下一个记录的主键 id;

    5、重复3、4直到 city 的值不满足查询条件为止;

    6、对 sort_buffer 中的数据按照字段 name 做快速排序;

    7、按照排序结果取前 1000行返回给客户。

    sort_buffer_size,就是MySQl为排序开辟的内存(sort_buffer)的大小,如果排序的数据量小于 sort_buffer_size,排序就在内存中完成,但如果排序数量太大,内存放不下的情况,就将用到磁盘临时文件辅助排序。

    所以就有了一个情况,当需要返回的字段很多的情况,sort_buffer中存放的字段太多,这时内存里能够存放的行数就会变少,要利用到很多个磁盘临时文件才能完成排序,这样就会严重影响性能~!

    MySQL 认为排序的单行长度太大会怎么做呢?

    介绍一个 MySQL 的系统参数:

    max_length_for_sort_data : 专门控制用于排序的行数据的长度的一个参数。如果单行的长度过大,MySQL就会选择新的算法(rowid 排序算法),不放入太多的字段到内存。

    下面我修改max_length_for_sort_data ,让MySQL 采用新的算法:

    SET max_length_for_sort_data = 16;

    city、name、age 这三个字段的定义总长度是 36 ,max_length_for_sort_data 设置为 16;

    整个的执行过程将会变成如下这个样子:

    1、初始化 sort_buffer,确定放入两个字段,即 name 和 id;

    2、从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;

    3、到主键 id 索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中;

    4、从索引 city 取下一个记录的主键 id;

    5、重复步骤 3、4 直到不满足 city='杭州’条件为止,也就是图中的 ID_Y;

    6、对 sort_buffer 中的数据按照字段 name 进行排序;

    7、遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。

    可以看出两种算法,第二种多访问了一次 t 表主键索引,会额外的造成磁盘读。

    而MySQl 做排序是一个成本很高的操作,那有什么好的方法数据库可以不需要创建临时表,也不需要排序?

    可以设想如果能保证从city这个索引取出的行,天然的就是按照name递增排序的话,是不是就可以不用再排序了呢?

    root@172.16:  [test]> alter table t add index idx_city_name(city,name);
     
    root@172.16:  [test]> explain select * from t where city = 'Gary Berry' order by name limit 1000;
    +----+-------------+-------+------+--------------------+---------------+---------+-------+------+-------------+
    | id | select_type | table | type | possible_keys      | key           | key_len | ref   | rows | Extra       |
    +----+-------------+-------+------+--------------------+---------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t     | ref  | city,idx_city_name | idx_city_name | 66      const |   13 | Using where |
    +----+-------------+-------+------+--------------------+---------------+---------+-------+------+-------------+
    1 row in set (0.00 sec)

    确实是这样的,并且这个查询的整个过程将变成如下方式:

    1、从索引(idx_city_name)中找到 第一个满足 city = 'Gary Berry' 条件的主键id;

    2、到主键 id 索引取出整行,取name,city,age 三个字段的值,作为结果集的一部分直接返回;

    3、从索引(idx_city_name)取出下一个记录主键id;

    4、重复步骤2、3,直到查到第 1000条记录,或者是不满足city = ‘Gary Berry’条件时循环结束。

    上面给大家总结的是MySQL里面order by语句的几种算法流程。

    开发系统过程中,总是不可避免的使用到 order by 语句,心里要清楚每个语句排序逻辑是怎么实现的,才能够分析出在最坏情况下,每个语句的执行对系统资源的消耗。

    如果提高order by的速度:

    1、order by 时 select * 是一个大忌(避免使用),order by的字段存在合适索引;

    2、尝试提高 sort_buffer_size 但是需要根据系统能力提高,这个参数是针对每个thread的,max_length_for_sort_data 的提高要根据 sort_buffer_size 的大小合理设定。

    下面列举几个order by 能用到索引以及order by 不能使用索引排序的情景:

    key a_b_c(a,b,c)

    order by 能使用索引最左前缀

    -- order by a

    -- order by a,b

    -- order by a,b,c

    -- order by a desc,b desc,c desc

    如果 where 使用索引的最左前缀定义为常量,则order by能使用索引

    -- where a = const order by b,c

    -- where a = const and b =const order by c

    -- where a = const order by b,c

    -- where a = const and b > const order by b,c

    不能使用索引进行排序

    -- order by asc,b,desc,c,desc      /*排序不一致*/

    -- where g = const order by b,c          /*丢失 a 索引*/

    -- where a = const order by c               /*丢失b 索引*/

    -- where a = const order by a,d         /* d 不是索引的一部分 */

    -- where a in (...) order by b,c              /* 对于排序来说,多个相等条件也是范围查询*/

  • 相关阅读:
    Web开发中的服务器跳转与客户端跳转
    Linux设置程序开机自启动
    RedHat Linux6.4下安装apache服务
    FTP错误 [ftp: connect: No route to host] 解决方法
    虚拟机ping不通主机,但是主机可以ping通虚拟机(转载)
    工作中常用的Linux命令:mkdir命令
    工作中常用的Linux命令:crontab命令
    工作中常用的Linux命令:目录
    工作中常用的Linux命令:ipcs/ipcrm命令
    【CentOS】在Centos7 下无图形界面安装 Oracle11g
  • 原文地址:https://www.cnblogs.com/Camiluo/p/12746534.html
Copyright © 2011-2022 走看看