`
talentluke
  • 浏览: 604480 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

函数索引

 
阅读更多

我们进行数据库检索优化的方法,通常是对特定条件列加索引,减少由于全表扫描带来的逻辑物理IO消耗。索引的种类很多,我们经常使用的B*树索引,由于结构简单、适应性强,可以应对大多数数据访问优化需求。除B*树索引外,其他一些索引类型,也在一些场合中扮演着独特的地位。本篇来介绍其中的函数索引

 

1、从B*树索引的失效谈起

 

和通常一样,我们准备实验环境。

 

 

SQL> select * from v$version where rownum<2;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database11gEnterpriseEdition Release11.2.0.1.0 - Production

 

SQL> create table t as select * from dba_objects;

Table created

//构建两个索引用作实验对象

SQL> create index idx_t_owner on t(owner);

Index created

 

SQL> create index idx_t_ddlt on t(last_ddl_time);

Index created

 

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

 

环境中,我们在数据表T上建立了一般意义的索引。当我们进行检索的时候,CBO会适时选择合适的索引执行计划。

 

 

SQL> explain plan for select * from t whereowner='SCOTT';

Explained

 

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 1516787156

--------------------------------------------------------------------------------

| Id | Operation                  | Name       | Rows | Bytes | Cost (%CPU)|

--------------------------------------------------------------------------------

|  0 | SELECT STATEMENT           |            | 2419 |  229K|   71  (0)|

|  1 | TABLE ACCESS BY INDEX ROWID| T          | 2419 |  229K|   71  (0)|

|* 2 |  INDEX RANGE SCAN         | IDX_T_OWNER | 2419 |      |    6  (0)|

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

  2 - access("OWNER"='SCOTT')

14 rows selected

 

 

但是,很多时候,我们可能会遇到在where条件里对索引列进行函数处理的情况。比如,选择owner列取值第二个字母是“c”的数据列,或者选取在特定天进行ddl操作的对象信息。这样的情况下,直接的想法就是在where条件列中加入列函数处理,但是这样做,会带来B*树索引的失效问题。

 

SQL> explain plan forselect * from t where substr(owner,2,1)='C';

Explained

 

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 1601196873

--------------------------------------------------------------------------

| Id | Operation        | Name | Rows | Bytes | Cost (%CPU)| Time    |

--------------------------------------------------------------------------

|  0 | SELECT STATEMENT |     |  726 | 70422 |  283  (1)| 00:00:04 |

|* 1 | TABLE ACCESS FULL| T   |  726 | 70422 |  283  (1)| 00:00:04 |

--------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

  1 - filter(SUBSTR("OWNER",2,1)='C')

13 rows selected

 

 

 

在对条件列owner进行substr操作之后,生成的执行计划就不会带有索引路径,出现全表扫描。如果列上的B*树普通索引就是为该查询对应的用例服务的,那么这个索引的存在就失去了意义。

 

 

那么,这种时候应该如何处理呢?答案是:在SQL语句本身不存在重构优化的空间时(此种情况通常出现在系统的运维阶段),可以考虑使用函数索引来解决问题。

 

2、函数索引

 

函数索引与通常B*树索引的结构,存在很大相似性。区别就在于形成树结构的叶子节点上,保存的不是索引列的取值,而是经过特定的函数处理过的索引列值。

 

 

这样的结构,进行搜索的时候,就可以直接使用到函数索引的叶子节点,获取到对应的rowid集合。要求是出现于构建函数索引完全相同的函数条件。

 

首先,我们来构建函数索引。

 

 

SQL> create index idx_t_ownerf on t(substr(owner,2,1));

Index created

 

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

 

构建函数索引的语法和一般索引的语法没有过多的区别,最大的差异就是在声明索引列的位置上,写清楚应用的函数语句。此时,数据字典视图系列中,已经反映出函数索引的不同。

 

 

SQL> select index_type from dba_indexes where index_name='IDX_T_OWNERF';

INDEX_TYPE

---------------------------

FUNCTION-BASEDNORMAL

 

 

此时,我们再进行查询,执行计划会发生变化。

 

 

SQL> explain plan for select * from t where substr(owner,2,1)='C';

Explained

 

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2485331276

--------------------------------------------------------------------------------

| Id | Operation                  | Name        | Rows | Bytes | Cost (%CPU)

--------------------------------------------------------------------------------

|  0 | SELECT STATEMENT           |             | 4839 |  467K|  135  (0)

|  1 | TABLE ACCESS BY INDEX ROWID| T           | 4839 |  467K|  135  (0)

|* 2 |  INDEX RANGE SCAN         | IDX_T_OWNERF | 4839 |      |    9  (0)

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

  2 - access(SUBSTR("OWNER",2,1)='C')

14 rows selected

 

 

 

加入函数索引之后,我们可以发现同样的SQL语句,执行计划发生变化。函数索引开始起效。

 

那么,函数索引的本质是什么呢?我们检查数据字典视图,就可以发现函数索引的本质。

 

SQL> col table_name for a20;

SQL> col table_owner for a20;

SQL> col column_name for a30;

SQL> select table_owner, table_name, column_name from dba_ind_columns where index_name='IDX_T_OWNERF';

 

TABLE_OWNER         TABLE_NAME          COLUMN_NAME

-------------------- -------------------- ------------------------------

SYS                 T                   SYS_NC00016$

 

SQL> select column_expression from dba_ind_expressions where index_name = 'IDX_T_OWNERF';

 

COLUMN_EXPRESSION

-------------------------------------

SUBSTR("OWNER",2,1)

 

SQL> select column_name,data_type,data_default from dba_tab_cols where wner='SYS' and table_name='T' and column_name='SYS_NC00016$';

 

COLUMN_NAME         DATA_TYPE           DATA_DEFAULT

-------------------- -------

SYS_NC00016$        VARCHAR2            SUBSTR("OWNER",2,1)

 

 

检查了三个视图的情况,我们可以清楚的看出Oracle函数索引的本质。Oracle建立函数索引之后,就会先建立出一个不可见的内部列(SYS_NC00016$)。之后,对这个列建立普通的B*树索引。为了保证该列在不受影响的情况下进行数据生成,使用默认值技术,在数据插入或者变化的时候,进行同步。

 

 

3、函数索引使用

 

函数索引是一种很特殊的索引类型,可以应对开发阶段出现的对数据列加函数处理SQL优化。但是,笔者以为,函数索引的使用还是应当注意一些细节的,在大部分场合下,函数索引可以作为一种应急或者是不得为之的策略。

 

首先,函数索引的综合消耗要大于普通的B*树索引。相对于传统索引,函数索引要保证创造的函数列数据一致性和多次进行函数计算。这样的消耗要远大于普通B*树索引;

 

其次,函数索引的适应范围较小。函数索引其效果的最大要素就是函数的使用和定义是100%相同。如第二部分的例子中,取字符串的第二位字串。如果有一个变更的需求,要求取第三位,这样原来的那个函数索引就不能发挥效应了。而相对来说,普通的B*树索引参与各种SQL的能力要很多。应该说,函数索引的针对性很强,如果这个需求不属于关键需求,这样性价比略差。

 

 

最后,函数索引通常是一种事后补救措施。笔者认为,一个良好设计的应用,一个划分合理的数据库逻辑结构,应该是可以避免函数操作数据列的SQL大量出现的。只有在系统上线之后,开发团队开发的问题暴露出来,但是也没有精力进行修改时,运维人员才开始使用函数索引,保证系统功能能够实现。

 

 

对开发人员和开发DBA而言,函数索引通常是不得已为之的方案,要保证在SQL和数据表结构权衡无效的情况下,再考虑使用函数索引。

 

首先,考虑SQL结构的优化。这个方法可以消灭掉很多看似不得不使用函数索引的场合。如字符串类型比较、日期匹配等等,都可以通过代码检查和SQL改写来避免进入函数索引的状况。下面一个例子:

 

 

//获取前一天进行ddl操作的对象列表

SQL> select count(*) from t wheretrunc(last_ddl_time)=trunc(sysdate)-1;

 

 COUNT(*)

----------

        9

 

 

日期型操作最大的问题就是时分秒结构的处理。Date类型本身是带有时分秒信息的,而进行查询的时候,常常是使用特定的年月日。这样,就会带来一些检索条件的问题。很多开发人员,就是直接使用trunc函数,将数据列上的时分秒信息进行裁剪。这样的确简单,而且满足需求。但是也留下了列索引失效的隐患。

 

 

正确的解决方式,应该将SQL进行改写,变等于条件为范围条件。如下:

 

 

SQL> explain plan for select count(*) from t wherelast_ddl_time between to_date('2011-5-20 00:00:00','yyyy-mm-dd hh24:mi:ss')

 2  and to_date('2011-5-20 23:59:59','yyyy-mm-dd hh24:mi:ss');

 

Explained

 

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 3824876144

--------------------------------------------------------------------------------

| Id | Operation        | Name      | Rows | Bytes | Cost (%CPU)| Time    |

--------------------------------------------------------------------------------

|  0 | SELECT STATEMENT |           |    1 |    8 |    2  (0)| 00:00:01 |

|  1 | SORT AGGREGATE  |           |    1 |    8 |           |         |

|* 2 |  INDEXRANGESCAN| IDX_T_DDLT |   91 |  728 |    2  (0)| 00:00:01 |

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

  2 - access("LAST_DDL_TIME">=TO_DATE(' 2011-05-20 00:00:00',

             'syyyy-mm-dd hh24:mi:ss') AND "LAST_DDL_TIME"<=TO_DATE('2011-05-2

             23:59:59', 'syyyy-mm-dd hh24:mi:ss'))

 

16 rows selected

 

 

经过改写,没有使用函数索引,原有的B*树索引起效。很多时候,经过SQL的重新思考,是可以避免函数索引使用场合出现的。特别是在项目的开发阶段,这个尤为重要。

 

 

其次,就是对设计表的改进。我们常说一范式:列不可分。如果出现很多的对数据列的函数处理,我们就需要重新审视我们的设计表方案。是不是存在设计不合理、没有考虑到实际业务技术需求的方面。当SQL没有优化空间时,设计表的重构,冗余字段的加入可能是比较好的思路方法。

 

 

 

4、结论

 

本篇从一般的函数索引,谈到了SQL的改写和设计表优化。核心要义就是一点,慎用函数索引。而且,在绝大多数的情况下,我们是不需要使用函数索引的。只要能够理智冷静的分析实际需求和SQL结构,通常都可以获取到一个折中的方案。

分享到:
评论

相关推荐

    函数索引使用

    ### 函数索引使用 #### 一、函数索引(Function-Based Index)概念与作用 函数索引(Function-Based Index, FBI),是Oracle数据库自Oracle 8i版本引入的一种特殊类型的索引。它允许在创建索引时使用任意内置或...

    PHP 函数索引 函数库 语法

    本文将深入探讨PHP函数索引、函数库以及语法相关的知识,帮助开发者更好地理解和利用PHP的强大功能。 首先,PHP函数是预定义的代码块,可以接受输入(参数),执行特定任务,并返回结果。函数是PHP编程的基础,它们...

    OpenCV 3.0函数索引

    OpenCV 3.0函数索引方便编写代码搜索到相关的函数。。。。。。。。。。。。

    VB 指令、函数 索引表.VB 指令、函数 索引表.

    这个"VB 指令、函数 索引表"很可能是为程序员提供了一个方便的参考资源,包含了VB中的各种指令和函数,帮助他们在编程过程中快速查找和理解所需的功能。 VB指令是控制程序流程的命令,例如`If...Then...Else`用于...

    PHP 函数索引.doc

    PHP 函数索引 本资源摘要信息为 PHP 函数索引的知识点总结,该索引共收录了 967 个 PHP 函数,涵盖了字符串处理、数组操作、数学计算、文件操作、数据库操作、网络操作等多个方面。 1. 字符串处理函数 PHP 提供...

    VB 可用指令、函数 索引表

    VB 可用指令、函数索引表 VB 可用指令、函数索引表是 VB 编程语言中提供的一份详细的指令和函数索引表,涵盖了 VB 中的基本指令和函数,按字母顺序排列。下面是 VB 可用指令、函数索引表中的部分内容: 1. Abs 求...

    PMAC编程函数索引手册.pdf

    PMAC编程函数索引手册 PMAC编程函数索引手册是一份详细的编程指南,提供了PMAC编程函数的索引和使用说明。下面是根据手册内容总结的知识点: 1. PMACPComm32:PMACPComm32是PMAC编程函数索引手册中的一部分,介绍...

    Php函数索引(很全的)

    【PHP函数索引详解】 PHP是一种广泛使用的开源脚本语言,尤其在Web开发领域中扮演着重要角色。它提供了一套丰富的内置函数,方便开发者处理各种任务。在本篇文章中,我们将详细介绍几个重要的PHP数学运算函数和...

    PHP函数索引 967 个函数

    从给定的文件标题“PHP函数索引 967 个函数”和描述“php常用函数的查询,PHP函数索引 967 个函数”,我们可以看出这份文档是关于PHP编程语言中的函数大全,涵盖了967个常用的PHP函数。下面,我们将对部分列出的函数...

    精选_内核内存映射文件之获取SSDT函数索引号_源码打包

    本文将深入探讨如何在Windows内核编程中获取SSDT函数索引号,并通过源码打包文件`get-ssdt-function-index`来展示具体的实现步骤和技术细节。 首先,我们需要理解SSDT的基本结构。在32位Windows系统中,SSDT是一个...

    SQLServer性能优化–间接实现函数索引或者Hash索引

    在SQL Server中,虽然没有直接提供函数索引或哈希索引的功能,但可以通过一些技巧间接实现类似的效果,以优化查询性能。以下两种方法分别针对不同的场景提供了解决方案。 1. **计算列上建立索引,实现“函数索引”...

    sql学习 函数索引陷阱之函数变更与影响.sql

    sql学习 函数索引陷阱之函数变更与影响.sql

    sql学习 函数索引与各种列的函数转换.sql

    sql学习 函数索引与各种列的函数转换.sql

    sql学习 系统有哪些函数索引.sql

    sql学习 系统有哪些函数索引.sql

    sql学习 函数索引妙用之部分记录建索引.sql

    sql学习 函数索引妙用之部分记录建索引.sql

    MySQL 函数索引的优化方案

    MySQL5.7版本中不支持函数索引,因此 遇到函数索引的时候需要进行修改,否则即使查询的字段上有索引,执行时也无法使用索引而进行全表扫描,数据量大的表查询时间会比较长。具体案例如下: 1.1 创建测试表及数据 ...

    VB 可用指令、函数 索引表.chm,学asp用的到。

    VB 可用指令、函数 索引表.chm,学asp用的到。

    matlab 函数索引

    ### MATLAB函数索引详解 MATLAB作为一款强大的数学计算与数据可视化软件,拥有庞大的内置函数库,涵盖了数学、工程、科学等众多领域的功能。本文将根据提供的“MATLAB函数索引”部分内容,深入解析其中的关键知识点...

    sql学习 函数索引陷阱之30553的错误.sql

    sql学习 函数索引陷阱之30553的错误.sql

Global site tag (gtag.js) - Google Analytics