执行GROUP BY子句的最一般的方法:先扫描整个表,然后创建一个新的临时表,表中每个组的所有行应为连续的,最后使用该临时表来找到组

并应用聚集函数。在某些情况中,MySQL通过访问索引就可以得到结果,此类查询的 EXPLAIN 输出显示 Extra 列的值为 Using index for group-by。

一、松散索引扫描

The most efficient way to process GROUP BY is when an index is used to directly retrieve the grouping columns.

With this access method, MySQL uses the property of some index types that the keys are ordered (for example, BTREE).

This property enables use of lookup groups in an index without having to consider all keys in the index that satisfy all WHERE conditions.

This access method considers only a fraction of the keys in an index, so it is called a loose index scan.

When there is no WHERE clause, a loose index scan reads as many keys as the number of groups, which may be a much smaller number than that of all keys.

If the WHERE clause contains range predicates ,  a loose index scan looks up the first key of each group that satisfies the range conditions,

and again reads the least possible number of keys. This is possible under the following conditions:

  • The query is over a single table.

  • The GROUP BY names only columns that form a leftmost prefix of the index and no other columns.

(If, instead of GROUP BY, the query has a DISTINCT clause, all distinct attributes refer to columns that form a leftmost prefix of the index.)

For example, if a table t1 has an index on (c1,c2,c3),

loose index scan is applicable if the query has GROUP BY c1, c2,.

It is not applicable if the query has GROUP BY c2, c3 (the columns are not a leftmost prefix) or GROUP BY c1, c2, c4 (c4 is not in the index).

  • The only aggregate functions used in the select list (if any) are MIN() and MAX(), and all of them refer to the same column. The column must be in the index and must immediately follow the columns in the GROUP BY.

  • Any other parts of the index than those from the GROUP BY referenced in the query must be constants (that is, they must be referenced in equalities with constants), except for the argument of MIN() or MAX() functions.

  • For columns in the index, full column values must be indexed, not just a prefix. For example, with c1 VARCHAR(20), INDEX (c1(10)), the index cannot be used for loose index scan.

mysql5.7示例如下:

CREATE TABLE `sm_wechat_binding` (
`id` bigint(20) NOT NULL,
`company_id` bigint(20) DEFAULT NULL,
`date_created` datetime NOT NULL,
`is_big_account` bit(1) NOT NULL,
`last_updated` datetime NOT NULL,
`open_id` varchar(64) NOT NULL,
`phone` varchar(14) DEFAULT NULL,
`deleted` datetime DEFAULT NULL,
`imported` datetime DEFAULT NULL,
`client_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id_idx` (`company_id`),
KEY `openid_phone_index` (`open_id`,`phone`),
CONSTRAINT `FK_f95swnll9d3myf1pl7o5cxtws` FOREIGN KEY (`company_id`) REFERENCES `sm_company` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

mysql> EXPLAIN SELECT distinct company_id FROM sm_wechat_binding;
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
| 1 | SIMPLE | sm_wechat_binding | range | company_id_idx | company_id_idx | 9 | NULL | 699 | Using index for group-by |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.02 sec) mysql> EXPLAIN SELECT COUNT( company_id) FROM sm_wechat_binding GROUP BY company_id;
+----+-------------+-------------------+-------+----------------+----------------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+-------+-------------+
| 1 | SIMPLE | sm_wechat_binding | index | company_id_idx | company_id_idx | 9 | NULL | 39130 | Using index |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+-------+-------------+
1 row in set (0.00 sec) mysql> EXPLAIN SELECT COUNT(distinct company_id) FROM sm_wechat_binding GROUP BY company_id;
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
| 1 | SIMPLE | sm_wechat_binding | range | company_id_idx | company_id_idx | 9 | NULL | 699 | Using index for group-by |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.00 sec) mysql> EXPLAIN SELECT COUNT(distinct company_id) as num, company_id FROM sm_wechat_binding GROUP BY company_id;
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
| 1 | SIMPLE | sm_wechat_binding | range | company_id_idx | company_id_idx | 9 | NULL | 699 | Using index for group-by |
+----+-------------+-------------------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.00 sec) mysql> EXPLAIN SELECT max(company_id), min(company_id) FROM sm_wechat_binding force index(company_id_idx);
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
1 row in set (0.01 sec)

示例二

mysql> CREATE TABLE `loose_index_scan` (
->
-> `c1` int(11) DEFAULT NULL,
-> `c2` int(11) DEFAULT NULL,
-> `c3` int(11) DEFAULT NULL,
-> `c4` int(11) DEFAULT NULL,
-> KEY `idx_g` (`c1`,`c2`,`c3`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.90 sec) mysql>
mysql>
mysql> explain select c1,c2 from loose_index_scan group by c1,c2;
+----+-------------+------------------+-------+---------------+-------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+-------+---------------+-------+---------+------+------+-------------+
| 1 | SIMPLE | loose_index_scan | index | idx_g | idx_g | 15 | NULL | 1 | Using index |
+----+-------------+------------------+-------+---------------+-------+---------+------+------+-------------+
1 row in set (0.06 sec) mysql>
mysql>
mysql> EXPLAIN SELECT COUNT(DISTINCT c1) FROM loose_index_scan GROUP BY c1;
+----+-------------+------------------+-------+---------------+-------+---------+------+------+-------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+-------+---------------+-------+---------+------+------+-------------------------------------+
| 1 | SIMPLE | loose_index_scan | range | idx_g | idx_g | 5 | NULL | 2 | Using index for group-by (scanning) |
+----+-------------+------------------+-------+---------------+-------+---------+------+------+-------------------------------------+
1 row in set (0.02 sec)

参考:

官方文档:group-by-optimization

MySQL如何优化GROUP BY :松散索引扫描 VS 紧凑索引扫描的更多相关文章

  1. MySQL优化GROUP BY-松散索引扫描与紧凑索引扫描

    满足GROUP BY子句的最一般的方法是扫描整个表并创建一个新的临时表,表中每个组的所有行应为连续的,然后使用该临时表来找到组并应用累积函数(如果有).在某些情况中,MySQL能够做得更好,即通过索引 ...

  2. MySQL松散索引扫描与紧凑索引扫描

    什么是松散索引? 答:实际上就是当MySQL 完全利用索引扫描来实现GROUP BY 的时候,并不需要扫描所有满足条件的索引键即可完成操作得出结果. 要利用到松散索引扫描实现GROUP BY,需要至少 ...

  3. mysql数据库优化之 如何选择合适的列建立索引

    1. 在where 从句,group by 从句,order by 从句,on 从句中出现的列: 2. 索引字段越小越好: 3. 离散度大的列放到联合索引的前面:比如: select * from p ...

  4. MySQL架构优化实战系列1:数据类型与索引调优全解析

    一.数据类型优化 数据类型 整数   数字类型:整数和实数 tinyint(8).smallint(16).mediuint(24).int(32).bigint(64) 数字表示对应最大存储位数,如 ...

  5. MySql数据库 优化

    MySQL数据库优化方案 Mysql的优化,大体可以分为三部分:索引的优化,sql慢查询的优化,表的优化. 开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更 ...

  6. mysql 松散索引与紧凑索引扫描(引入数据结构)

    这一篇文章本来应该是放在 mysql 高性能日记中的,并且其优化程度并不高,但考虑到其特殊性和原理(索引结构也在这里稍微讲一下) 一,mysql 索引结构 (B.B+树) 要问到 mysql 的索引用 ...

  7. mysql 通过使用联全索引优化Group by查询

    /*SELECT count(*) FROM (*/ EXPLAIN SELECT st.id,st.Stu_name,tmpgt.time,tmpgt.goutong FROM jingjie_st ...

  8. MySQL性能优化——索引

    原文地址:http://blog.codinglabs.org/articles/theory-of-mysql-index.html InnoDB使用B+Tree作为索引结构 最左前缀原理与相关优化 ...

  9. mysql性能优化-慢查询分析、优化索引和配置

    一.优化概述 二.查询与索引优化分析 1性能瓶颈定位 Show命令 慢查询日志 explain分析查询 profiling分析查询 2索引及查询优化 三.配置优化 1)      max_connec ...

随机推荐

  1. LuaStudio编辑调试软件

    该编辑调试器最大特点就是能够注入到宿主程序内对lua脚本进行调试.还能够设置断点观察变量的值,功能很强大. 如今已有的编辑器有Notepad++, Editplus, luaforwindows, S ...

  2. AIX下RAC搭建 Oracle10G(六)dbca建库

    AIX下RAC搭建系列 AIX下RAC搭建 Oracle10G(六)dbca建库 环境 节点 节点1 节点2 小机型号 IBM P-series 630 IBM P-series 630 主机名 AI ...

  3. atitit.TokenService  token服务模块的设计

    atitit.TokenService  token服务模块的设计 1. Token的归类1 2. Token的用途2 2.1. 访问控制2 2.2. 编译原理术语)编辑2 2.3. 数据处理2 1. ...

  4. Atitit.获取swing ui 按钮控件的id 与名字 与JPDA 调试体系

    Atitit.获取swing ui 按钮控件的id 与名字 与JPDA 调试体系 1. Swing Inspector是一个Java Swing/AWT用户界面分析和调试工具,功能与firebug类似 ...

  5. Unity插件之NGUI学习(4)—— 创建UI2DSprite动画

    创建一个新的Scene.并按 Unity插件之NGUI学习(2)创建UI Root,并在UI Root的Camera下创建一个Panel. 然后在选中Panel,在菜单中选择NGUI->Crea ...

  6. lamp环境编译安装curl扩展

    Linux编译安装php扩展包curl 1.curl,主要用于发送http请求,是php的一个扩展包. 2.安装过程: (1)curl下载:http://curl.haxx.se/download.h ...

  7. AAC终结者Opus音频编码器的瑞士军刀,编译android ios

    AAC-LD/ELD it is either 480 or 512 PCM samples per frame and channel. http://opus-codec.org/download ...

  8. PHP递归实现无限级分类

    在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性.那么PHP是如何实现无限级分类的呢?我们在本文中使用递归算法并结合mysql数据表实现无限级分类. 在一些复杂的系统中,要求对信 ...

  9. Style Lessons in Clarity and Grace (11th Edition)中文翻译

    Joseph, Williams. "Style: Lessons in Clarity and Grace." Pearson Schweiz Ag (2014). 下载地址:h ...

  10. MySQL - 统计每个月生日的人数

    Person表定义如下: create table person(id int primary key auto_increment, birthday datetime); Person 数据如下: ...