Cra*_*lus 15 mysql innodb index optimization explain
我运行一个EXPLAIN:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
我表中的索引:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
Run Code Online (Sandbox Code Playgroud)
last_name 上有一个索引,但优化器不使用它。
所以我这样做:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
但仍然没有使用索引!我在这里做错了什么?
这与索引是 的事实有关NON_UNIQUE吗?顺便说一句,last_name 是VARCHAR(1000)
@RolandoMySQLDBA 请求的更新
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
Run Code Online (Sandbox Code Playgroud)
Mic*_*bot 20
实际上,这里的问题是这看起来像一个前缀索引。我在问题中没有看到表定义,但是sub_part= 700?您还没有索引整列,因此索引不能用于排序,也不能用作覆盖索引。它只能用于查找“可能”匹配 a 的行,WHERE并且服务器层(存储引擎之上)必须进一步过滤匹配的行。姓氏真的需要 1000 个字符吗?
更新以说明:我有一个表测试表,其中有 500 多行,每行在列中包含网站的域名domain_name VARCHAR(254) NOT NULL,没有索引。
mysql> alter table keydemo add key(domain_name);
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0
Run Code Online (Sandbox Code Playgroud)
索引完整列后,查询使用索引:
mysql> explain select domain_name from keydemo order by domain_name;
+----+-------------+---------+-------+---------------+-------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+-------------+
| 1 | SIMPLE | keydemo | index | NULL | domain_name | 764 | NULL | 541 | Using index |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+-------------+
1 row in set (0.01 sec)
Run Code Online (Sandbox Code Playgroud)
所以,现在,我将删除该索引,只索引 domain_name 的前 200 个字符。
mysql> alter table keydemo drop key domain_name;
Query OK, 0 rows affected (0.11 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table keydemo add key(domain_name(200));
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select domain_name from keydemo order by domain_name;
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | keydemo | ALL | NULL | NULL | NULL | NULL | 541 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)
mysql>
Run Code Online (Sandbox Code Playgroud)
瞧。
另请注意,200 个字符的索引比列中最长的值长...
mysql> select max(length(domain_name)) from keydemo;
+--------------------------+
| max(length(domain_name)) |
+--------------------------+
| 43 |
+--------------------------+
1 row in set (0.04 sec)
Run Code Online (Sandbox Code Playgroud)
……但这没有任何区别。使用前缀长度声明的索引只能用于查找,不能用于排序,也不能用作覆盖索引,因为根据定义,它不包含完整的列值。
此外,上述查询是在 InnoDB 表上运行的,但在 MyISAM 表上运行它们会产生几乎相同的结果。在只有在这种情况下,不同的是,InnoDB的计数rows稍微偏离(541),而MyISAM的显示行的确切数量(563),这是正常的行为,因为这两个存储引擎处理指数潜水非常不同。
我仍然会断言 last_name 列可能比需要的要大,但如果您使用 InnoDB 并运行 MySQL 5.5 或 5.6 ,仍然可以索引整个列:
默认情况下,单列索引的索引键最多可达 767 字节。相同的长度限制适用于任何索引键前缀。见第 13.1.13 节,“
CREATE INDEX语法”。例如,假设一个字符集和每个字符最多 3 个字节,您可能会在 aTEXT或VARCHAR列上超过 255 个字符的列前缀索引达到此限制UTF-8。当innodb_large_prefix配置选项的功能,这个长度的限制提高到3072字节为单位InnoDB,使用该表DYNAMIC和COMPRESSED行格式。— http://dev.mysql.com/doc/refman/5.5/en/innodb-restrictions.html
看查询
select last_name from employees order by last_name;
Run Code Online (Sandbox Code Playgroud)
我没有看到有意义的 WHERE 子句,MySQL 查询优化器也没有。没有使用索引的动机。
看查询
select last_name from employees force index(idx_last_name) order by last_name;
Run Code Online (Sandbox Code Playgroud)
你给了它一个索引,但查询优化器接管了。我以前见过这种行为(如何强制 JOIN 使用 MySQL 中的特定索引?)
为什么会发生这种情况?
如果没有WHERE子句,查询优化器会对自己说以下内容:
WHERE条款?查询优化器选择了阻力最小的路径。
您会感到有点震惊,但事实就是这样:您知道查询优化器会以完全不同的方式处理 MyISAM 吗?
你可能会说 HUH ???? 如何 ????
MyISAM 将数据存储在一个.MYD文件中,并将所有索引存储在该.MYI文件中。
相同的查询将产生不同的 EXPLAIN 计划,因为索引与数据位于不同的文件中。为什么 ?原因如下:
last_name列)已在.MYIlast_name从索引访问该列怎么能这么肯定呢?我已经测试了这个关于使用不同存储将如何生成不同 EXPLAIN 计划(有时更好)的工作理论:索引是否必须覆盖所有选定的列才能用于 ORDER BY?