为什么mysql解释显示使用索引首选bigint列而不是int列

时间:2019-11-25 11:44:45

标签: mysql indexing int bigint

最近,我遇到一个奇怪的问题,我的测试表结构:

CREATE TABLE `index_test` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `a` varchar(64) NOT NULL DEFAULT '',
  `card_no` bigint(20) NOT NULL,
  `card_no2` bigint(20) NOT NULL,
  `optype` int(11) NOT NULL,
  `optype2` int(11) NOT NULL,
  `create_time` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
  `_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_a` (`a`),
  KEY `idx_card_no` (`card_no`),
  KEY `idx_card_no2` (`card_no2`),
  KEY `idx_optype` (`optype`),
  KEY `idx_optype2` (`optype2`)
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8;

5个主要列,一个varchar,cardno和cardno2是bigint,optype和optype2是int, 根据我的经验, mysql索引更喜欢选择基数大,数据类型小和非空列,但是当我运行解释查询语句时,发生了一些问题,这是我的初始化数据过程

DELIMITER ;;
CREATE DEFINER=`xx`@`%` PROCEDURE `simple_insert`( )
BEGIN
  DECLARE counter BIGINT DEFAULT 0;

  my_loop: LOOP
    SET counter=counter+1;

    IF counter=10000 THEN
      LEAVE my_loop;
    END IF;

    INSERT INTO `index_test` (`a`,`card_no`,`card_no2`,`optype`,`optype2`, `create_time`) VALUES (replace(uuid(), '-', ''),counter,counter%180, counter,counter%180,current_timestamp);

  END LOOP my_loop;
END;;
DELIMITER ;

插入10,000行数据,首先我执行统计查询

select * from information_schema.statistics where table_schema = 'test' and table_name = 'index_test';

输出

+---------------+--------------+------------+------------+--------------+--------------+--------------+-------------+-----------+-------------+----------+--------+----------+------------+---------+---------------+
| TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | NON_UNIQUE | INDEX_SCHEMA | INDEX_NAME   | SEQ_IN_INDEX | COLUMN_NAME | COLLATION | CARDINALITY | SUB_PART | PACKED | NULLABLE | INDEX_TYPE | COMMENT | INDEX_COMMENT |
+---------------+--------------+------------+------------+--------------+--------------+--------------+-------------+-----------+-------------+----------+--------+----------+------------+---------+---------------+
| def           | test         | index_test |          0 | test         | PRIMARY      |            1 | id          | A         |       10089 |     NULL | NULL   |          | BTREE      |         |               |
| def           | test         | index_test |          1 | test         | idx_a        |            1 | a           | A         |        9999 |     NULL | NULL   |          | BTREE      |         |               |
| def           | test         | index_test |          1 | test         | idx_card_no  |            1 | card_no     | A         |        9999 |     NULL | NULL   |          | BTREE      |         |               |
| def           | test         | index_test |          1 | test         | idx_card_no2 |            1 | card_no2    | A         |         180 |     NULL | NULL   |          | BTREE      |         |               |
| def           | test         | index_test |          1 | test         | idx_optype   |            1 | optype      | A         |        9999 |     NULL | NULL   |          | BTREE      |         |               |
| def           | test         | index_test |          1 | test         | idx_optype2  |            1 | optype2     | A         |         180 |     NULL | NULL   |          | BTREE      |         |               |
+---------------+--------------+------------+------------+--------------+--------------+--------------+-------------+-----------+-------------+----------+--------+----------+------------+---------+---------------+   

步骤2:

explain select * from index_test where  optype=9600 and a= 'e095af180f4911ea8d907036bd142a99';

输出:

+----+-------------+------------+------------+------+------------------+-------+---------+-------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys    | key   | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+------------+------+------------------+-------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | index_test | NULL       | ref  | idx_a,idx_optype | idx_a | 194     | const |    1 |     5.00 | Using where |
+----+-------------+------------+------------+------+------------------+-------+---------+-------+------+----------+-------------+

根据我的经验,varchar(64)空间大于int,所以使用int列是可以的

第3步:

explain select * from index_test where  optype=9600 and card_no = 9600;

输出

+----+-------------+------------+------------+------+------------------------+-------------+---------+-------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys          | key         | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+------------+------+------------------------+-------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | index_test | NULL       | ref  | idx_card_no,idx_optype | idx_card_no | 8       | const |    1 |     5.00 | Using where |
+----+-------------+------------+------------+------+------------------------+-------------+---------+-------+------+----------+-------------+

所以,问题是为什么mysql查询优化器更喜欢使用bigint列而不是int列,任何人都可以帮助我或提供有关此问题的官方文档链接,谢谢。

顺便说一句,我的测试环境是macos(10.14.6)x64,而mysql服务器版本是5.7.26

1 个答案:

答案 0 :(得分:0)

我认为INTBIGINT不是问题。首先,让我提及更好的索引:

对于

where  optype=9600 and a= 'e095af180f4911ea8d907036bd142a99'
这些“复合”索引中的

都是最佳的,并且比您拥有的更好:

INDEX(optype, a)
INDEX(a, optype)

对于

where  optype=9600
  and card_no = 9600
  and  a= 'e095af180f4911ea8d907036bd142a99'

从那三列开始的任何索引都是最佳的;任何两个都是“好”,单列索引会很差,但总比没有索引好。

优化器可能正在进行探查,以查看3个不良索引中哪一个是最佳的。

我无法解释为什么它没有将a列为“可能的钥匙”。