Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2796813
  • 博文数量: 389
  • 博客积分: 4177
  • 博客等级: 上校
  • 技术积分: 4773
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-16 23:29
文章分类

全部博文(389)

分类: Mysql/postgreSQL

2015-03-15 17:17:39

                            数据库多索引合并查询

     当数据库在处理带有where条件的查询时,在查询中有多个谓词,且每个谓词都
有索引时,对于oracle来说会选择过滤性最强的索引,而mysql使用了索引合并的技术
对两个索引进行合并,然后再查询.

创建以下测试数据,并分别在object_id,object_name两个列上创建索引   

SQL> select object_id,object_name from t1 where object_id<20 order by object_id;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
         1 C_OBJ#
         1 C_FILE#_BLOCK#
         1 I_TS#
         1 I_OBJ#
         1 I_FILE#_BLOCK#
         1 TAB$
         1 C_TS#
         1 CLU$
        10 test
        11 test
        12 test

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
        13 test
        14 test
        15 test
        16 test
        17 test
        18 test
        19 test

18 rows selected.


SQL> create index ix_object_id on t1(object_id);

Index created.

SQL> create index ix_object_name on t1(object_name);

Index created.

SQL> select *
  2  from t1
  3  where object_id=1
  4  and object_name='test';

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 1509370966

----------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |              |     1 |   115 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1           |     1 |   115 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IX_OBJECT_ID |     8 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_NAME"='test')
   2 - access("OBJECT_ID"=1)


从执行计划来看,oracle选择了IX_OBJECT_ID这个索引,而另一个where条件中的谓词使用过滤操作来实现.


 我们可以通过对每个where条件索引列去过滤,然后找出满足条件的rowid,再在rowid上做一个连接,那么
这样找出的rowid就是满足两个条件的,然后根据这个rowid去访问表,但是oracle没有这样的优化,手动写出
这种算法的sql如下.


SQL> select /*+ NO_QUERY_TRANSFORMATION */
  2  *
  3  from t1
  4  where rowid in
  5   (select x.id
  6     from (select rowid as id from t1 where object_id=1) x,(select rowid as id from t1 where object_name='test') y
  7     where x.id=y.id)
  8  ;

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 1542144853

--------------------------------------------------------------------------------------
| Id  | Operation                     | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |      |     1 |   115 |   174K  (1)| 00:00:29 |
|*  1 |  FILTER                       |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL           | T1   | 91570 |    10M|   419   (1)| 00:00:01 |
|*  3 |   HASH JOIN SEMI              |      |     1 |    24 |     2   (0)| 00:00:01 |
|   4 |    VIEW                       |      |     1 |    12 |     1   (0)| 00:00:01 |
|*  5 |     TABLE ACCESS BY USER ROWID| T1   |     1 |    17 |     1   (0)| 00:00:01 |
|   6 |    VIEW                       |      |     1 |    12 |     1   (0)| 00:00:01 |
|*  7 |     TABLE ACCESS BY USER ROWID| T1   |     1 |    37 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter( EXISTS (SELECT 0 FROM  (SELECT ROWID "ID" FROM "T1" "T1" WHERE
              ROWID=:B1 AND "OBJECT_NAME"='test') "Y", (SELECT ROWID "ID" FROM "T1" "T1"
              WHERE ROWID=:B2 AND "OBJECT_ID"=1) "X" WHERE "X"."ID"="Y"."ID"))
   3 - access("X"."ID"="Y"."ID")
   5 - filter("OBJECT_ID"=1)
   7 - filter("OBJECT_NAME"='test')


再看来看mysql的处理,mysql要想实现索引合并,需要打开insert_merge的提示开关.


mysql> show global variables like '%optimizer_switch%';
+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name    | Value                                                                                                                                                                                                                                                                                                                                            |
+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| optimizer_switch | index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on |
+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)


mysql> explain select *
    -> from t1
    -> where object_id=1
    -> and object_name='test';
+----+-------------+-------+-------------+-----------------------------+-----------------------------+---------+------+------+------------------------------------------------------------------------+
| id | select_type | table | type        | possible_keys               | key                         | key_len | ref  | rows | Extra                                                                  |
+----+-------------+-------+-------------+-----------------------------+-----------------------------+---------+------+------+------------------------------------------------------------------------+
|  1 | SIMPLE      | t1    | index_merge | ix_object_id,ix_object_name | ix_object_id,ix_object_name | 5,303   | NULL |    4 | Using intersect(ix_object_id,ix_object_name); Using where; Using index |
+----+-------------+-------+-------------+-----------------------------+-----------------------------+---------+------+------+------------------------------------------------------------------------+
1 row in set (0.00 sec)


从extra和key的两个栏位信息来看,mysql合并了两个索引,然后再做查询,row是4

 如果我们关闭了索引合并,index_merge=off.这样的情况下,mysql的行为就很类似于oracle了.

mysql> set optimizer_switch='index_merge=off,index_merge_union=on,index_merge_sort_union=on,
index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on';
Query OK, 0 rows affected (0.00 sec)


mysql> explain select *
    -> from t1
    -> where object_id=1
    -> and object_name='test';
+----+-------------+-------+------+-----------------------------+--------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys               | key          | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+-----------------------------+--------------+---------+-------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | ix_object_id,ix_object_name | ix_object_id | 5       | const |    8 | Using where |
+----+-------------+-------+------+-----------------------------+--------------+---------+-------+------+-------------+
1 row in set (0.00 sec)


可以看到mysql只选择了一个索引ix_object_id,rows已经评估为8了.

阅读(2833) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~