全部博文(38)
分类: DB2/Informix
2008-03-21 15:15:53
(), 软件工程师,Informix Dynamic
Server/Optimizer 团队, IBM
2008 年 1 月 03 日
随着数据量以惊人速度不断增长,数据库管理系统将继续关注性能问题。本文主要介绍一种名为函数索引(functional index)的性能调优技术。根据数据库使用情况的统计信息创建并使用函数索引,可以显著提升 SELECT 查询的性能。通过本文了解如何在 IBM ® Informix ® Dynamic Server 中创建和使用函数索引并最大限度提升查询性能。
在选择数据库管理系统(DBMS)时,性能是一个关键的考虑因素。在执行 SELECT、INSERT、UPDATE 和 DELETE 操作时,很多因素都会对性能产生影响。这些因素包括:
随着数据集不断变大,查询性能愈发变得重要。
通常,使用索引可以改善查询性能。索引将数据库中的行位置与一组有序数据子集和/或数据派生物关联在一起。索引可以减少 DBMS 在执行查询时检查的行(或
元组)数量,从而获得性能增益。有时,仅通过搜索索引即可完成查询,而不需要从表中取回任何元组(tuple)。例如,如果您在列 c1 中建有索引,并且发出查询
select c1 from t1 where c1 < 10
,那么索引中包含了可以满足查询的所有信息。
有趣的是,ANSI SQL 标准并没有说明如何创建、实现或维护索引。因此,数据库供应商可以按照自己的方式自由地实现索引。
本文讨论了 Informix Dynamic Server
的函数索引特性。要理解本文涉及的概念,您需要熟悉基本的数据库术语和概念,例如模式、表、行、列、索引和可扩展性。还需了解 Informix Dynamic
Server (IDS) 的基本配置以及如何启动和停止服务器、如何使用 ONCONFIG
文件进行配置。此外,还需熟悉基本的 SQL
命令以及如何使用 dbaccess
对服务器执行 SQL 命令。
本文的目的是帮助您理解函数索引的定义以及使用。此外,您还将了解如何创建和使用函数索引,以及在创建函数索引之前需要考虑的一些问题。
索引按照某种顺序保存列值。函数索引对列中的数据进行转换并按照顺序保存转换后的值。
假设某个表中保存了一个企业的员工名称,并且需要保留名称的大小写形式。那么,如果查询需要执行大小写不敏感的搜索(如下所示),则必须转换数据:
SELECT * FROM t1 WHERE toUpper(name) LIKE 'ANTHONY % HOPKINS'; |
如果没有为名称建立索引,那么 DBMS 将执行全表扫描并对每个元组(tuple)的 name
列应用
toUpper
函数。要确定元组(tuple)是否满足查询,必须调用 toUpper
函数。当表非常大或者大量会话发出这种类型的查询时,性能将会有所下降。
避免调用 toUpper
函数的一种方法是在表中同时保存大小写混合的名称和大写名称。应用程序查询大小写不敏感的列:
SELECT * FROM t1 WHERE ucname like 'ANTHONY % HOPKINS'; |
如果没有为 ucname
创建索引,DBMS
仍然执行全表扫描,但是不会对数据进行进一步处理来判断其是否满足查询。尽管这样做改善了性能,但并不是理想的解决方案,因为表非常大,而且所有需要操纵或访问数据的应用程序必须包括处理
ucname
的逻辑。
改善查询性能的一种更好的方法是对 name
创建函数索引:
CREATE FUNCTION toUpper( name VARCHER(100) ) RETURNS VARCHAR(100) |
当执行这种查询时,DBMS 可以使用函数索引判断哪些元组元组(tuple) 满足查询。DBMS 只获取并返回这些满足查询的元组(tuple),如下面的清单所示:
SELECT * FROM t1 WHERE toUpper(name) LIKE 'ANTHONY % HOPKINS'; |
DBMS 将自动管理函数索引以及不需要包含逻辑来管理大写形式数据的应用程序。通过使用
INSERT
、UPDATE
和 DELETE
操作对索引进行更新,DBMS
能够确保索引始终与表数据一致。
接下来将深入讨论如何创建和使用函数索引,以及如何生成和检验查询计划,并提供具体的示例。
函数索引是根据用户定义例程(User Defined Routine,UDR)返回的值创建的。这里的 UDR 一词通常用来指代返回值的函数。UDR
必须是不可变的。也就是说,对于给定的参数,UDR 始终返回相同的值,而且 UDR 不能修改数据库或变量状态。和随机数生成程序、当前日期/时间函数一样,UDR
通常是变化的,因此不能用于函数索引。当定义 UDR 并将之用于函数索引时,您必须显式将其指定为 NOT VARIANT
。
可以将 UDR 编写为一个存储过程语言(Stored Procedure Language,SPL)函数,或者使用外部语言(SQL、C/C++ 或 Java)编写为一个外部函数。
函数索引可以根据用户定义的类型创建。
函数索引可以使用以下任意一种访问方法:
可针对单个列、单个列的派生值、多个列和多个列的派生值创建索引。针对多个列创建的索引称为复合索引(composite index)。例如,下面的查询针对一个列的列值和第二个列的派生值创建了一个复合索引:
CREATE INDEX idx1 ON myTable( c1, f(c3) ); |
函数索引不能是内置的代数、指数、对数或十六进制函数。如果需要使用内置函数定义函数索引,那么必须从 SQL 或外部语言函数中调用该函数。
不能针对返回大对象的 UDR 创建函数索引。不允许使用大对象作为索引键,因为一般情况下不能够对大对象进行比较和排序。然而,需要注意,可以将大对象作为参数传递给 UDR。
如果将某个 UDR 用于函数索引,则该 UDR 不能使用集合数据类型作为参数类型。集合数据类型包括
SET
、MULTISET
和 LIST
。
对于用于函数索引的 UDR,传递给它的参数在数量上有所限制。根据所使用的 Informix Data Server (IDS) 版本以及实现 UDR 的具体语言的不同,这些限制也不尽相同。例如,对于 IDS 9.4,最多可以将 102 列作为参数传递给 C UDR,并且最多可以将 341 列作为参数传递给 Java 或 SPL UDR。有关此方面的详细内容,请参考您的文档。
在创建和使用方面,函数索引和非函数索引之间存在着大量差异。
诸如 UNIQUE 和 CLUSTER 等索引选项,以及 B-Tree、R-Tree 等访问方法和用户定义的访问方法,可以同时应用于函数索引和非函数索引。同样,这两种类型的索引都可以指定一个 FILLFACTOR,并可以指定存储选项来控制创建索引的位置以及是否对索引进行分段。这两种索引都可以指定索引操作符类并按照降序或升序排列。
oncheck 实用工具可以对两种类型的索引进行验证和修复。例如,下面的 oncheck 命令将对 db
数据库的
tbl
表中名为 i1
的索引进行验证: oncheck -ci
db:tbl#i1
。如果 oncheck 报告索引中存在一个问题,那么可以使用 oncheck 的 -y
选项进行修复。该选项可以同时对函数索引和非功能性索进行修复。
从用户的角度来看,函数索引和非功能性索之间的一个差异就是它们执行 CREATE
和 DROP
操作的方式不同。在创建或删除一个函数索引时,不能指定 ONLINE
关键字,如下所示:
CREATE INDEX ... ONLINE; <== Not valid |
这说明,在创建或删除函数索引时,执行索引的表中始终持有一个排他锁。在这段时间内,其他所有用户都无法访问这个表。
任何索引都存在开销。包括资源的使用和执行时间。所有索引都需要进行保存,并且,所有索引都需要制定执行时间并保存其键值。函数索引还会产生额外的函数执行开销。在创建函数索引时,必须针对表中的每一行执行相关的函数。并且,必须在 INSERT 和 UPDATE 操作期间执行函数。
在创建函数索引之前,总是需要对数据库进行详细的成本收益分析。分析的内容应该包括表中存储的数据量、执行数据查询的类型和频率。如果表非常小,或者不经常执行使用函数索引的查询,那么创建函数索引可能收效甚微。
查询计划被写入到 SQL EXPLAIN
文件中。下表展示了该文件的位置和名称。
平台 | IDS Server 位置 | SQL EXPLAIN 位置 | SQL EXPLAIN 名称 |
---|---|---|---|
UNIX | 本地 | 当前目录 | sqexplain.out |
UNIX | 远程 | 远程计算机的主目录 | sqexplain.out |
Windows | 本地和远程 |
现在,您已了解了函数索引的定义以及其使用方式和使用时机,接下来,将提供一些使用函数索引的例子。我们将为您提供分步指导,使您能够亲自实现函数索引。
您已经了解了函数索引的定义以及其使用方式和使用时机。下面的例子将展示一些具体应用。每个例子都附带了分步指导,根据这些指导,您将能够亲自实现函数索引。
本示例展示如何对圆的面积创建函数索引。如果数据集较大,或者经常发出请求圆面积的查询,那么创建函数索引将有助于提高性能。
首先,创建一个表。
CREATE TABLE circles ( radius FLOAT ); |
接着,创建一个 SPL 函数,它将返回给定半径的圆的面积。
CREATE FUNCTION circleArea( radius FLOAT ) RETURNS float |
对这个圆形区域创建一个函数索引。
CREATE INDEX areaOfCircleIndex on circles( circleArea( radius ) ); |
最后,执行查询,该查询将使用函数索引。DBMS 使用索引判断哪些元组(tuple)满足查询,并且仅将这些元组(tuple)作为查询结果返回。
SELECT radius FROM circle WHERE circleArea( radius ) < 500; |
在这个例子中,我们将针对一副数字图像的像素平均色值创建函数索引。UDR 使用 C 编写,可以处理简单的彩色 TIFF 图像。提供了以下函数:
avgRGB
:图像中所有像素的平均值
avgRed
:图像中所有红色像素的平均值
avgGreen
:图像中所有绿色像素的平均值
avgBlue
:图像中所有蓝色像素的平均值 这个例子演示了如何使用函数索引选择具有某些特征的图像。在创建函数索引时,将执行与其相关的 UDR
来对图像进行预处理,并将结果作为索引键保存。SELECT
查询在查询过滤器中指定图像处理函数,现在,这些查询的运行速度得到了提高,因为它们使用索引判断每个元组(tuple)是否满足查询。
我们针对运行在 64 位 Linux x86 上的 IDS version 11.10
对该示例进行了测试。可针对不同平台修改相应的指令。要想了解您的平台适合哪些编译器和连接器,请参考
$INFORMIXDIR/incl/dbdk
中的 makeinc.* 文件。
本示例的源代码以及相关文件可下载获得。可通过 一节下载代码。
设置以下环境变量:
INFORMIXDIR the location of your IDS installation |
为智能二进制大对象(smart blob)创建 sbspace。注意:sbspace 名称必须与 ONCONFIG
文件中 SBSPACENAME
值匹配。在本例中,名为 sbsp2
。
在与根 dbspace 相同的目录中,创建一个名为 sbsp2
的空文件(ONCONFIG
文件中
ROOTPATH
的值指定根 dbspace 的名称和位置)。
将文件所有权改为 informix:informix。
将文件权限改为 mode 660。
以用户 informix 的身份,创建 sbspace:
onspaces -c -S sbsp2 -p |
创建一个数据库,并将图像作为智能二进制大对象保存。loadImages.sql
脚本创建一个名为
imagedb
的数据库并加载一些图像。
cd |
编译 C UDR 并创建一个共享库。
cc -c -fPIC -I$INFORMIXDIR/incl/public imageUDR.c |
注册 C UDR。
根据您的 IDS 服务器的配置方式,您可能需要使用 EXTEND
角色创建 C UDR。如果您的服务器配置中的
IFX_EXTEND_ROLE
设置为 OFF
,那么即使不具备 EXTEND
角色也可以创建 C UDR。如果服务器配置中的 IFX_EXTEND_ROLE
设置为 ON
,则需要使用
EXTEND
角色才能创建 UDR。
您的数据库管理员可以使用下面的 SQL 命令向您授予 EXTEND
角色: GRANT EXTEND TO
'
。
您可以使用下面的命令检查服务器配置:onstat -c
。
将包含 C UDR 的共享库复制到 INFORMIXDIR:
Copy imageUDR.so to $INFORMIXDIR/extend |
将 C UDR 注册到 Informix。注意,我们指定了 HANDLESNULLS
,因此,当列值为
NULL
时,IDS 将允许函数返回一个值。
dbaccess imagedb - |
验证是否已加载数据以及是否能够访问 C UDR:
dbaccess imagedb - |
发出使用 UDR 的查询。此时,不存在任何索引。set explain on
指示 IDS 生成显示查询计划的文件。
dbaccess imagedb - |
检查 SQL EXPLAIN
输出。该查询计划显示了一个全表扫描。
QUERY: |
对 C UDR 提供的函数创建函数索引并再次发出查询:
dbaccess imagedb - |
检查 SQL EXPLAIN
输出。该查询计划展示了函数索引的使用。
QUERY: |
使用 SQL 指令实现查询计划。要详细了解与优化器相关的 SQL 指令,请参考 IDS 文档。
此处使用了 AVOID_INDEX
指令,指示查询优化器不要使用函数索引。
dbaccess imagedb - |
检查 SQL EXPLAIN
输出。该查询计划显示使用了 AVOID_INDEX 指令,并展示了全表扫描。
QUERY: |
在本例中,我们将向您展示如何创建使用 R-Tree 访问方法的函数索引。R-Tree 数据结构可以保存有关多维数据的信息,并用于有关空间或地理数据的索引。这类索引有助于改善针对空间数据的查询的性能。
要理解本示例,需要熟悉 IDS DataBlade 技术。具体来说,您应该能够安装和配置所使用的 Spatial DataBlade。有关 DataBlade 的详细信息,请参考 IBM Informix Spatial DataBlade 用户指南。
本例使用的数据包括一些城市的地理数据和 Oregon 州的海啸影响范围。本文下载部分没有提供这些地理数据,但是可以通过 Oregon Geospatial Enterprise Office 自由获取。可从该站点下载 City Limits 2007 和 Tsunami Inundation Line 数据集。
假设您希望查找即将受海啸影响的城市。为此,发出一个空间数据查询,搜索其中心包含在海啸线内的所有城市。要满足这个查询,必须计算地理城市中心,因此,需要创建一个函数索引来计算这个值。由于涉及到空间数据,您需要为这个索引指定 R-Tree 访问方法。
下面列出了 R-Tree 函数索引的一些有趣特性:
本例中使用的空间函数来自 Spatial DataBlade。因此,创建和使用索引不需要编写额外的代码。
该示例针对运行在 64 位 Linux x86 上的 IDS version 11.10 进行了测试。可针对不同平台对指令进行相应修改。
设置以下环境变量:
INFORMIXDIR the location of your IDS installation |
安装 Spatial DataBlade。安装指导可从 IDS 11.10 信息中心获得(参考本文末尾的链接)。
下面的指令假设您已经在默认位置安装好了 Spatial DataBlade version 8.21:
$INFORMIXDIR/extend/spatial.8.21.xC1
,其中 ‘x’ 表示特定于平台的字母。对于 64 位
Linux x86,默认位置为
$INFORMIXDIR/extend/spatial.8.21.FC1
。可根据您的环境对指令作出相应修改。
为空间数据创建 sbspace。注意:sbspace 名必须与 ONCONFIG
文件中的
SYSSBSPACENAME
值匹配。本例假设 sbspace 名为 syssbspace
。
在与根 dbspace 相同的目录中创建名为 syssbspace
的空文件(ONCONFIG
文件中
ROOTPATH
的值指定根 dbspace 的名称和位置)。
将文件所有权改为 informix:informix。
将文件权限改为 mode 660。
以用户 informix 的身份,创建 sbspace:
onspaces -c -S syssbspace -p |
创建您的数据库。
dbaccess - - |
注册 Spatial DataBlade。
使用 blademgr 实用工具注册 Spatial DataBlade。Spatial DataBlade 依赖于 R-Tree DataBlade,后者由 IDS 自动附带。因此,必须同时向您的数据库注册这两个 DataBlade。
blademgr |
加载空间数据。
从 City Limits 2007 和 Tsunami Inundation Line
下载中提取文件,并分别保存在两个目录中,即 citylim_2007
和 tsunami
:
ls citylim_2007 |
注意:在 UNIX 上,数据文件名称区分大小写,而且必须为小写。这是 Shapefile 数据的要求。如果文件名不正确,那么
loadshp
实用工具将报告错误,指出它无法找到数据文件。
关于如何正确配置投影数据超出本文讨论的范围。如果没有正确配置,loadshp
实用工具在尝试处理投影信息时将返回错误,因此,隐藏或删除扩展名为 'prj'
的文件:
rm citylim_2007/citylim_2007.prj |
使用 Spatial DataBlade 附带的 loadshp
实用工具将数据加载到 IDS 中。该实用工具位于
$INFORMIXDIR/extend/spatial.8.21.FC1/bin
中。 loadshp
创建了两个表:cities
和 tsunami
,并将空间数据分别加载到
citylim
和 inundation
列中。
$INFORMIXDIDR/extend/spatial.8.21.FC1/bin/loadshp -o create -D spatialdb2 \ |
发出查询。在这个查询中,
st_intersects
、st_centroid
和
st_envelope
由 Spatial DataBlade 提供
st_envelope
计算每条海啸线的边界,返回类型 st_multilinestring
st_centroid
计算每个城市的中心,返回类型 st_multipolygon
st_intersects
可确定两种结构是否相互交叉 这次没有定义函数索引。
dbaccess spatialdb - |
检查 SQL EXPLAIN
输出。该查询计划显示了全表扫描。针对每一个元组(tuple)执行
st_centroid()
计算。
QUERY: |
创建函数索引计算城市中心。
dbaccess spatialdb - |
有关这个索引,需注意以下几点:
R-Tree
访问方法。
st_centroid()
计算城市中心。
st_geometry_ops
指定操作符类。 将操作符类与访问方法结合使用,可以指定创建索引和优化查询时使用的操作符。操作符类通常为一组函数。对于 Spatial
DataBlade,st_geometry_ops
类由以下函数组成:
ST_Contains() |
执行相同的查询。
dbaccess spatialdb - |
注意:当索引表达式被用于关系表达式,或作为某个操作符类函数的参数时,查询优化器将考虑使用函数索引。由于
st_centroid()
是索引 st_geometry_ops
中指定的操作符类的成员,优化器在生成查询计划时将考虑使用索引。
注意:优化器不会对下面这个查询使用函数索引。这是因为 st_distance()
不属于
st_geometry_ops()
操作符类。
select city_name from cities, tsunami |
检查 SQL EXPLAIN
输出。该查询计划显示索引扫描使用了函数索引。
注意:查询优化器只有在表统计信息保持最新的情况下使用 R-Tree 索引。这意味着您应该在创建索引之前加载数据,或者在加载数据之后更新表的统计信息。
QUERY: |
函数索引是一种强大的性能调优工具。通过减少执行查询时需要检索的元组(tuple)的数量,以及减少查询所需的计算,函数索引能够显著改善性能。
除大型对象外,函数索引可以返回任何标准 SQL 类型。同样,函数索引还可以返回用户定义的类型。函数索引可以指定各种不同的访问方法,包括 B-Tree、R-Tree 和用户定义的访问方法。
函数索引的存储和维护成本较高。您应该对系统进行成本收益分析,并只在能保证获益的情况下创建函数索引。如果数据大小较小,或者索引对于数据库查询的性能改善帮助不大,则不适合使用函数索引。
本文提供了一些有关函数索引的示例。希望我们介绍的这些创建和使用函数索引的方法能够帮助您改善查询搜索的性能。