1226关于count(*)不走主键索引反而走二级索引

转自 http://www.2cto.com/database/201508/433975.html

mysqlcount(*)会选哪个索引?
2015-08-19      0个评论    来源:Database、Code  
收藏    我要投稿

今天在查询一个表行数的时候,发现count(1)和count(*)执行效率居然是一样的。这跟Oracle还是有区别的。遂查看两种方式的执行计划:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

mysql> select count(1) from customer;

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

| count(1) |

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

|   150000 |

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

1 row in set (0.03 sec)

mysql> flush tables;

Query OK, 0 rows affected (0.00 sec)

mysql> select count(*) from customer;

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

| count(*) |

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

|   150000 |

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

1 row in set (0.03 sec)

查看执行计划:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

mysql> explain select count(1) from customer;

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

| id | select_type | table    | type  | possible_keys | key           | key_len | ref  | rows   | Extra       |

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

|  1 | SIMPLE      | customer | index | NULL          | i_c_nationkey | 5       | NULL | 151191 | Using index |

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

1 row in set (0.00 sec)

mysql> explain select count(*) from customer;

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

| id | select_type | table    | type  | possible_keys | key           | key_len | ref  | rows   | Extra       |

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

|  1 | SIMPLE      | customer | index | NULL          | i_c_nationkey | 5       | NULL | 151191 | Using index |

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

1 row in set (0.00 sec)

mysql> show index from customer;

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

| Table    | Non_unique | Key_name      | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |

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

| customer |          0 | PRIMARY       |            1 | c_custkey   | A         |      150525 |     NULL | NULL   |      | BTREE      |         |               |

| customer |          1 | i_c_nationkey |            1 | c_nationkey | A         |          47 |     NULL | NULL   | YES  | BTREE      |         |               |

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

2 rows in set (0.08 sec)

发现不管是count(1)或count(*)都是走的i_c_nationkey这个索引。平时我们检索数据的时候肯定是主键索引效率高,那么我们强制主键索引来看看:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

mysql> select count(*) from customer force index(PRIMARY);

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

| count(*) |

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

|   150000 |

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

1 row in set (0.68 sec)

mysql> explain select count(*) from customer force index(PRIMARY);

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

| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |

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

|  1 | SIMPLE      | customer | index | NULL          | PRIMARY | 4       | NULL | 150525 | Using index |

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

1 row in set (0.00 sec)

可以看到走主键索引的时候效率比较差。那么是为什么呢。
平时我们检索一列的时候,基本上等值或范围查询,那么索引基数大的索引必然效率很高。但是在做count(*)的时候并没有检索具体的一行或者一个范围。那么选择基数小的索引对
count操作效率会更高。在做count操作的时候,mysql会遍历每个叶子节点,所以基数越小,效率越高。mysql非聚簇索引叶子节点保存的主键ID,所以需要检索两遍索引。但是这里相对于遍历主键索引。及时检索两遍索引效率也比单纯的检索主键索引快。
那么再以一个表作为证明:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

mysql> explain select count(*) from lineitem;

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

| id | select_type | table    | type  | possible_keys | key          | key_len | ref  | rows    | Extra       |

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

|  1 | SIMPLE      | lineitem | index | NULL          | i_l_shipdate | 4       | NULL | 6008735 | Using index |

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

1 row in set (0.00 sec)

mysql> show index from lineitem;

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

| Table    | Non_unique | Key_name              | Seq_in_index | Column_name   | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |

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

| lineitem |          0 | PRIMARY               |            1 | l_orderkey    | A         |     2997339 |     NULL | NULL   |      | BTREE      |         |               |

| lineitem |          0 | PRIMARY               |            2 | l_linenumber  | A         |     5994679 |     NULL | NULL   |      | BTREE      |         |               |

| lineitem |          1 | i_l_shipdate          |            1 | l_shipDATE    | A         |        5208 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_suppkey_partkey   |            1 | l_partkey     | A         |      428191 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_suppkey_partkey   |            2 | l_suppkey     | A         |     1998226 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_partkey           |            1 | l_partkey     | A         |      461129 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_suppkey           |            1 | l_suppkey     | A         |       19213 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_receiptdate       |            1 | l_receiptDATE | A         |          17 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_orderkey          |            1 | l_orderkey    | A         |     2997339 |     NULL | NULL   |      | BTREE      |         |               |

| lineitem |          1 | i_l_orderkey_quantity |            1 | l_orderkey    | A         |     1998226 |     NULL | NULL   |      | BTREE      |         |               |

| lineitem |          1 | i_l_orderkey_quantity |            2 | l_quantity    | A         |     5994679 |     NULL | NULL   | YES  | BTREE      |         |               |

| lineitem |          1 | i_l_commitdate        |            1 | l_commitDATE  | A         |        7836 |     NULL | NULL   | YES  | BTREE      |         |               |

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

12 rows in set (0.96 sec)

这里一看l_shipDATE并不是基数最小的呀,殊不知这个统计信息是不准确的。我们用sql看一下。

?


1

2

3

4

5

6

7

mysql> select count(distinct(l_shipDATE)) from lineitem;

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

| count(distinct(l_shipDATE)) |

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

|                        2526 |

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

1 row in set (0.01 sec)

那么比他小的那些列呢?

?


1

2

3

4

5

6

7

mysql> select count(distinct(l_receiptDATE)) from lineitem;

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

| count(distinct(l_receiptDATE)) |

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

|                           2554 |

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

1 row in set (0.01 sec)

其他就不看了,这里再次说明mysql选择了基数小的索引。

时间: 2024-10-06 02:16:11

1226关于count(*)不走主键索引反而走二级索引的相关文章

主键约束,唯一性约束,唯一性索引

1)主键列:比如我们在表A中指定ID为主键,Oracle数据库会自动创建一个同名的唯一索引 可以通过 select constraint_name,constraint_type from user_indexes ui where ui.table_name='A'来查看主键上的唯一索引,如果此时我们在给ID列去创建唯一性索引或者非唯一性索引的话,都会报错,当然指定主键之后自动也会生成主键约束,主键就是一种约束 (2)非主键列:我们在非主键列上创建一个唯一性约束,Oracle同样自动创建了一个

mysql 主键和默认 设为索引的规则

mysql 表中如果是单主键的话,那这个主键也会被 系统默认建为 索引 mysql 表中如果是复合主键的话,那系统会遵循左对齐原则,即如复合主键 a 和 b字段和c字段..., 那只有a是被默认建为索引,b和后边都没有被建为索引,由于a字段是在b字段前,索引遵循左对齐只会把a默认建个索引,查询中如 where a=?   或  where a=?  and  b=?   这2种 查询条件中只要有a就会用到a索引,即a.ab.abc.a... 只要有a就会用到索引,而且是a索引,而 b.bc.bd

MySQL 聚簇索引&&二级索引&&辅助索引

MySQL非聚簇索引&&二级索引&&辅助索引 mysql中每个表都有一个聚簇索引(clustered index ),除此之外的表上的每个非聚簇索引都是二级索引,又叫辅助索引(secondary indexes). 以InnoDB来说,每个InnoDB表具有一个特殊的索引称为聚集索引.如果您的表上定义有主键,该主键索引是聚集索引.如果你不定义为您的表的主键时,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚集索引

MySQL非聚簇索引&&二级索引&&辅助索引

MySQL非聚簇索引&&二级索引&&辅助索引 mysql中每个表都有一个聚簇索引(clustered index ),除此之外的表上的每个非聚簇索引都是二级索引,又叫辅助索引(secondary indexes). 以InnoDB来说,每个InnoDB表具有一个特殊的索引称为聚集索引.如果您的表上定义有主键,该主键索引是聚集索引.如果你不定义为您的表的主键时,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚集索引

count(1),count(*),count(主键) 性能对比

前言 前段时间关于统计数量的sql问题和朋友进行了讨论,网上关于这三种查询方式说法不一,主要有以下两种说法. count(*) = count(主键) > count(1) count(主键) > count(*) > count(1) 今天对这三种方式进行探究. 数据库为mysql 5.7.12,引擎为InnoDB. 建表 CREATE TABLE `user` (   `id` int(32) NOT NULL AUTO_INCREMENT,   `name` varchar(500

mysql中,主键与普通索引

一.什么是索引?索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表里面的记录数量越多,这个操作的代价就越高.如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置.如果表有1000个记录,通过索引查找记录至少要比顺序扫描记录快100倍. 假设我们创建了一个名为people的表: CREATE TABLE people ( pe

MySQL主键和索引的联系及区别

转载自:http://www.nowamagic.net/librarys/veda/detail/1954 关系数据库依赖于主键,它是数据库物理模式的基石.主键在物理层面上只有两个用途: 惟一地标识一行. 作为一个可以被外键有效引用的对象. 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针.下面是主键和索引的一些区别与联系. 1. 主键一定是唯一性索引,唯一性索引并不一定就是主键. 所谓主键就是能够唯一标识表中某一行的属性或属性组

SQL存储原理及聚集索引、非聚集索引、唯一索引、主键约束的关系(补)

索引类型 1.          唯一索引:唯一索引不允许两行具有相同的索引值 2.          主键索引:为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型.主键索引要求主键中的每个值是唯一的,并且不能为空 3.          聚集索引(Clustered):表中各行的物理顺序与键值的逻辑(索引)顺序相同,每个表只能有一个 4.          非聚集索引(Non-clustered):非聚集索引指定表的逻辑顺序.数据存储在一个位置,索引存储在另一个位置,索引中包含指

主键及主键索引的关系及相互影响

主键的定义:列或多列的集合,用于唯一的标识表中的一行.一个表上只允许有一个主键. 我们在数据库中指定主键时,是通过主键约束来定义的.而创建主键约束时,又是需要有相应的索引来配合实现的.所以,本文的目的是总结创建主键约束时,采用不同的方法创建索引后,主键与该索引之间的关系及相互影响. 为配合本文的描述和测试,首先创建如下测试表: create table test (id number,c1 varchar2(8)); 一. 首先,我们来看最常用的创建主键约束及索引的方法: alter table