MYSQL排序ORDER BY
祖仙教小凡仙 海鲨数据库架构师
在我编写的数据库规范当中有禁止排序这一条的,虽然它是基于ORACLE的规范。不过这条基本是所有数据库的通用规范!
JAVA开发ORACLE的规范
为什么要禁止呢?因为排序需要数据库内存,需要消耗CPU时间,需要用到数据库的磁盘操作。就重要的是数据库端无法分布式,也就是说不仅仅是你一个程序的SQL要排序,其它SQL也要排序。这些SQL会因为并发用户的操作给数据库下达无数多的SQL排序工作任务。而数据库无法把这些工作分担给别的数据库服务器。不像应用服务器TOMCAT,通过NGINX无状态的负载均衡到不同的物理服务机上部署相同的TOMCAT服务器上。而数据库就一个主库,顶多通过多个备库来稍微分担点只读查询,而且对于数据及时性不高的SQL。
而这分担也需要开发人员在程序上进行调整。还不如直接在JAVA程序里完成排序操作来的简单方便明了可靠。
我们看下MYSQL的排序操作:
CITY有个二级索引,辅助索引。主键是ID。
这个语句的流程如下:
第一 我们根据索引CITY找到ID,
第二 然后根据ID回表找到行,再取出CITY,NAME,AGE 3个字段。
第三 把这3个字段放入排序内存,进行排序;
第四 把排序结果集 前1000条发给客户;
排序内存大小参数是:SORT_BUFFER_SIZE 。它是每线程的。
排序内存大小是每个字段的定义大小累加再乘以行数。
比如说NAME 定义VARCAHR(16) 存了中国人名字。根据中文占3个位置来算,很多行实际上有很多用不到那么多。可排序就定义来申请内存大小。
另外个参数 MAX_LENGTH_FOR_SORT_DATA 大意是说如果参与排序的字段累加大小超过该参数的值,那MYSQL就换一种排序算法。MYSQL就取出排序字段,又多少个排序字段就取出多少个,外加ID。
ORDER BY NAME …..
第1 根据索引CITY找到ID
第2 根据ID找到行,取出NAME+ID
第3 把NAME+ID放入排序内存,进行排序
第4 从排序好的内存里取出ID,回表取出相关的字段SELECT部分,发给客户。
下面操作是检查某个SQL排序操作情况
/* 打开optimizer_trace,只对本线程有效 */SET optimizer_trace=\'enabled=on\';/* @a保存Innodb_rows_read的初始值 */select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = \'Innodb_rows_read\';/* 执行语句 */select city, name,age from t where city=\'赣州\' order by name limit 1000;/* 查看 OPTIMIZER_TRACE 输出 */SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\\G/* @b保存Innodb_rows_read的当前值 */select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = \'Innodb_rows_read\';/* 计算Innodb_rows_read差值 */select @b-@a;
会显示如下结果:
排序行数,使用到临时文件个数,排序内存大小,排序算法。
OPTIMIZER_TRACE:SORT_MODE=<SOTR_KEY,ROWID>
而原来全字段排序模式:
SORT_MODE=<SOTR_KEY,PACKED_ADDITIONAL_FILEDS>