View 上的 Mysql 索引不起作用

标签 mysql performance indexing view union

我创建了一个名为“myview”的 View ,如下所示。

create view myview
as select 'a' source,col1,col2
     from table_a
   union
   select source,col1,col2
     from table_b
;

table_acol1 上有一个索引,table_bsource 上有一个索引,col1。当我如下查询 myview 时,没有使用索引。

select *
  from myview
 where source = a
   and col1 = 'xxx'
 ;

如何让索引在这个查询上起作用?


创建代码

CREATE TABLE `table_a` (
    `col1` VARCHAR(50) NULL DEFAULT NULL,
    `col2` VARCHAR(50) NULL DEFAULT NULL,
    INDEX `table_a_idx01` (`col1`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
;

CREATE TABLE `table_b` (
    `source` VARCHAR(50) NULL DEFAULT NULL,
    `col1` VARCHAR(50) NULL DEFAULT NULL,
    `col2` VARCHAR(50) NULL DEFAULT NULL,
    INDEX `table_b_idx01` (`source`, `col1`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
;

create view myview
as select 'a' source,col1,col2
     from table_a
   union
   select source,col1,col2
     from table_b  

INSERT INTO table_a (col1, col2) 
VALUES 
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2');

INSERT INTO table_b (source,col1, col2) 
VALUES 
('b','test2', 'testcol2'),
('b','test2', 'testcol2'),
('b','test2', 'testcol2'),
('b','test2', 'testcol2'),
('b','test2', 'testcol2'),
('b','test2', 'testcol2');

解释

explain
select *
  from table_a
 where col1 = 'test'

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,SIMPLE,table_a,ref,table_a_idx01,table_a_idx01,153,const,5,Using index condition

explain
select *
  from table_b
 where source = 'b'
   and col1 = 'test'

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,SIMPLE,table_b,ref,table_b_idx01,table_b_idx01,306,const,const,1,Using index condition

在myview上解释

explain
select *
  from myview
 where source = 'b'
   and col1 = 'test'

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,<derived2>,ref,<auto_key0>,<auto_key0>,306,const,const,1,Using where
2,DERIVED,table_a,ALL,\N,\N,\N,\N,6,\N
3,UNION,table_b,ALL,\N,\N,\N,\N,6,\N
\N,UNION RESULT,<union2,3>,ALL,\N,\N,\N,\N,\N,Using temporary

如您所见,在 View 选择时没有调整任何索引。

最佳答案

您不能在 View 上创建索引:http://dev.mysql.com/doc/refman/5.7/en/view-restrictions.html ,所以你必须希望索引被使用。 https://stackoverflow.com/a/7922711/3595565

解决方法

文档另一部分的评论中提到了一个解决方法:https://dev.mysql.com/doc/refman/5.5/en/create-view.html您可以在其中创建一个常规表并设置您的专用索引并将数据从 View 加载到表中。

Creating a materialized view emulation as described above looks good, the only problem is that we are inheriting the lack of indexes MySQL views expose.

My solution is to create a correctly indexed table according to my needs, having the exact same structure as the view, and then running something like this:

LOCK TABLES materializedView WRITE; 
TRUNCATE materializedView; 
INSERT INTO materializedView SELECT * FROM regularView;
UNLOCK TABLES;

That way all indexes from materializedView are preserved on every "refresh".

I'm planning to use this in an application I'm doing right now, where we will have a lot more SELECTs than inserts/updates. If I keep a regular view for my SELECTs, I'll be asking the server to make tons of calculations every time someone needs to know how many items are on stock for product "A", instead, I'll have all SELECTs towards the "materializedView" with correct SKU, Store and Period indexes.

The view "refresh" will occur every time someone runs an INSERT or UPDATE, which will be on a 20 to 1 ratio. (20 Selects for every Update or Insert)

I hope things go as smooth as I'm planning. Greetings ;-)

为什么您的查询不使用索引?

当在 SELECT 中使用 UNION 时,mysql 会创建一个临时表来保存数据。因此,由于 View 是更复杂查询的“快捷方式”,因此在调用选择时,它将再次执行联合,使用临时表......使用 temptable alghorithm 来处理数据。

再次查看手册:http://dev.mysql.com/doc/refman/5.7/en/view-restrictions.html

Indexes can be used for views processed using the merge algorithm. However, a view that is processed with the temptable algorithm is unable to take advantage of indexes on its underlying tables (although indexes can be used during generation of the temporary tables).

结论:查询中的 UNION 阻碍了 View 使用索引。

来源

question in mysql forum for the same problem回答:

I guess the union causes the view to use the temptable algorithm, it creates a temporary table and then apply the where condition to the temporary table.

bugreport "DO NOT CREATE TEMPORARY TABLES FOR UNION ALL"

Currently, union queries always use a temporary table to store the result before it is returned to the user. [...]

已在 MySQL 5.7 中修复 http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-3.html

The server no longer uses a temporary table for UNION statements that meet certain qualifications. Instead, it retains from temporary table creation only the data structures necessary to perform result column typecasting.[...]

一些用于检查分析器的测试数据

CREATE TABLE test1 (
    id int auto_increment PRIMARY KEY,
  col1 varchar(50),
  col2 varchar(50)
);

CREATE TABLE test2 (
    id int auto_increment PRIMARY KEY,
  col1 varchar(50),
  col2 varchar(50)
);

INSERT INTO test1 (col1, col2) 
VALUES 
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2'),
('test', 'testcol2');


INSERT INTO test2 (col1, col2) 
VALUES 
('test2', 'testcol2'),
('test2', 'testcol2'),
('test2', 'testcol2'),
('test2', 'testcol2'),
('test2', 'testcol2'),
('test2', 'testcol2');

CREATE VIEW testview AS
SELECT * FROM test1
UNION
SELECT * FROM test2;

检查分析器:

SET PROFILING = 1;
SELECT * FROM testview WHERE id = 1;
+----+-------+----------+
| id | col1  | col2     |
+----+-------+----------+
|  1 | test  | testcol2 |
|  1 | test2 | testcol2 |
+----+-------+----------+
SHOW PROFILE;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000017 |
| Waiting for query cache lock   | 0.000004 |
| checking query cache for query | 0.000029 |
| checking permissions           | 0.000006 |
| Opening tables                 | 0.000121 |
| System lock                    | 0.000012 |
| checking permissions           | 0.000014 |
| checking permissions           | 0.000032 |
| optimizing                     | 0.000004 |
| statistics                     | 0.000007 |
| preparing                      | 0.000006 |
| executing                      | 0.000003 |
| Sending data                   | 0.000046 |
| optimizing                     | 0.000003 |
| statistics                     | 0.000004 |
| preparing                      | 0.000003 |
| executing                      | 0.000002 |
| Sending data                   | 0.000023 |
| optimizing                     | 0.000003 |
| statistics                     | 0.000003 |
| preparing                      | 0.000003 |
| executing                      | 0.000002 |
| Sending data                   | 0.000008 |
| removing tmp table             | 0.000005 |
| Sending data                   | 0.000005 |
| Waiting for query cache lock   | 0.000002 |
| Sending data                   | 0.000024 |
| init                           | 0.000011 |
| optimizing                     | 0.000006 |
| statistics                     | 0.000004 |
| preparing                      | 0.000006 |
| executing                      | 0.000002 |
| Sending data                   | 0.000021 |
| end                            | 0.000003 |
| query end                      | 0.000004 |
| closing tables                 | 0.000002 |
| removing tmp table             | 0.000004 |
| closing tables                 | 0.000006 |
| freeing items                  | 0.000005 |
| Waiting for query cache lock   | 0.000003 |
| freeing items                  | 0.000013 |
| Waiting for query cache lock   | 0.000002 |
| freeing items                  | 0.000002 |
| storing result in query cache  | 0.000003 |
| logging slow query             | 0.000002 |
| cleaning up                    | 0.000003 |
+--------------------------------+----------+

我不能从配置文件中提取太多信息,但它确实提到了临时表,足以(对我而言)验证我的结论。

关于 View 上的 Mysql 索引不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38391726/

相关文章:

java - spring data jpa 和 hibernate 之间的性能差距 - jHipster

sql - Oracle 数字字符串列和索引

mysql - 如何从 MySQL 中的 COUNT(*) 中减去整数

mysql - 如何根据列值连接不同的表

c - 用于循环速度 c 的英特尔编译器

python - 查找按原点分隔的 2 个集合的对称差异

delphi - 反转 ClientDataSet 索引的顺序

c++ - 如何在 C++ 中对 vector 进行排序和排名(不使用 C++11)

php - 用于插入和选择的不同表

mysql - 索引,为什么不直接索引所有内容以及何时使用索引?