【问题标题】:Mysql index on view not working视图上的Mysql索引不起作用
【发布时间】:2016-11-18 09:43:30
【问题描述】:

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

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

如您所见,视图选择时没有调整索引。

【问题讨论】:

    标签: mysql performance indexing view union


    【解决方案1】:

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

    解决方法

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

    如上所述创建物化视图模拟看起来不错, 唯一的问题是我们继承了 MySQL 缺乏索引 视图暴露。

    我的解决方案是根据我的创建一个正确索引的表 需要,具有与视图完全相同的结构,然后运行 像这样:

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

    这样,materializedView 中的所有索引都会保留在每个 “刷新”。

    我打算在我现在正在做的应用程序中使用它,其中 我们将有比插入/更新更多的选择。如果我保持一个 我的 SELECT 的常规视图,我将要求服务器制作大量 每次有人需要知道有多少物品时进行计算 产品“A”的库存,相反,我将所有 SELECT 用于 具有正确 SKU、Store 和 Period 索引的“materializedView”。

    每次有人运行 INSERT 或 更新,这将是 20 比 1 的比率。 (每次更新 20 个选择 或插入)

    我希望事情能像我计划的那样顺利。问候;-)

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

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

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

    索引可用于使用合并算法处理的视图。 但是,使用 temptable 算法处理的视图是 无法利用其基础表上的索引(尽管 在生成临时表时可以使用索引)。

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

    来源

    question in mysql forum for the same problem回答:

    我猜联合导致视图使用 temptable 算法,它创建一个临时表,然后将 where 条件应用于临时表。

    bugreport "DO NOT CREATE TEMPORARY TABLES FOR UNION ALL"

    目前,联合查询总是使用临时表来存储 结果返回给用户之前。 [...]

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

    服务器不再为 UNION 语句使用临时表 满足一定的条件。相反,它从临时表中保留 仅创建执行结果列所需的数据结构 类型转换。[...]

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

    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 |
    +--------------------------------+----------+
    

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

    【讨论】:

    • 请记住,这样的物化视图不会与原始数据保持同步;您必须手动启动 Philipp 提到的同步。
    • 感谢您的及时回复。如您所知,我不想在视图上创建索引,我只是想知道为什么 table_a 和 table_b 的索引不起作用。
    • @Philipp 我还添加了更多信息以便您更好地理解。谢谢。
    • 好的,所以我正确理解了场景。但这意味着,要解决您的问题,您必须使用我在答案开头提到的解决方法,或者尝试使用 mysql 5.7 版,看看它是否在那里工作。
    • 在现实世界中,为什么我们在表上使用索引,那是因为表太大所以查询性能更好。要根据视图制作另一个表,需要一段可接受的时间。如果基表频繁发生CRUD操作怎么办,是不能接受的。
    猜你喜欢
    • 2014-09-15
    • 2018-01-30
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 2013-03-07
    • 2011-04-30
    相关资源
    最近更新 更多