第五章 优化查询性能(二)
使用索引
索引通过维护常见请求数据的排序子集,提供了一种优化查询的机制。
确定哪些字段应该被索引需要一些思考:太少或错误的索引和关键查询将运行太慢;
太多的索引会降低插入和更新性能(因为必须设置或更新索引值)。
什么索引
要确定添加索引是否会提高查询性能,请从管理门户SQL接口运行查询,并在性能中注意全局引用的数量。
添加索引,然后重新运行查询,注意全局引用的数量。
一个有用的索引应该减少全局引用的数量。
可以通过在WHERE
子句或ON
子句条件前使用%NOINDEX
关键字来防止使用索引。
应该为联接中指定的字段(属性)编制索引。左外部联接从左表开始,然后查看右表;因此,应该为右表中的字段建立索引。在下面的示例中,应该为T2.f2
编制索引:
FROM Table1 AS T1 LEFT OUTER JOIN Table2 AS T2 ON T1.f1 = T2.f2
内部联接应该在两个ON
子句字段上都有索引。
执行“显示计划”,然后找到第一张map。
如果查询计划中的第一个项目是“Read master map”
,或者查询计划调用的模块的第一个项目是“Read master map”
,则查询的第一个映射是主映射,而不是索引映射。
因为主映射读取数据本身,而不是数据索引,这总是表明查询计划效率低下。
除非表相对较小,否则应该创建一个索引,以便在重新运行该查询时,查询计划的第一个映射表示“读取索引映射”。
应该索引在WHERE
子句equal
条件中指定的字段。
可能希望索引在WHERE
子句范围条件中指定的字段,以及GROUP BY
和ORDER BY
子句中指定的字段。
在某些情况下,基于范围条件的索引可能会使查询变慢。如果绝大多数行满足指定的范围条件,则可能会发生这种情况。例如,如果将QUERY
子句WHERE Date < CURRENT_DATE
用于大多数记录来自以前日期的数据库,则在DATE
上编制索引实际上可能会降低查询速度。这是因为查询优化器假定范围条件将返回相对较少的行数,并针对此情况进行优化。可以通过在范围条件前面加上%noindex
来确定是否发生这种情况,然后再次运行查询。
如果使用索引字段执行比较,则比较中指定的字段的排序规则类型应与其在相应索引中的排序规则类型相同。例如,SELECT
的WHERE
子句或联接的ON
子句中的Name
字段应该与为Name
字段定义的索引具有相同的排序规则。如果字段排序规则和索引排序规则之间存在不匹配,则索引可能效率较低或可能根本不使用。
索引配置选项
以下系统范围的配置方法可用于优化查询中索引的使用:
- 要将主键用作
IDKey
索引,请设置$SYSTEM.SQL.Util.SetOption()
方法,如下所示SET status=$SYSTEM.SQL.Util.SetOption("DDLPKeyNotIDKey",0,.oldval)
. 默认为1
- 要将索引用于
SELECT DISTINCT
查询,请设置$SYSTEM.SQL.Util.SetOption()
方法,如下所示:SET status=$SYSTEM.SQL.Util.SetOption("FastDistinct",1,.oldval)
. 默认为1
索引使用情况分析
可以使用以下任一方法按SQL缓存查询分析索引使用情况:
- 管理门户索引分析器SQL性能工具。
-
%SYS.PTools.UtilSQLAnalysis
方法indexUsage()
、tableScans()
、tempIndices()
、joinIndices()
和outlierIndices()
。、
索引分析
可以使用以下任一方法从管理门户分析SQL查询的索引使用情况:
- 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择索引分析器。
- 选择系统资源管理器,选择SQL,然后从工具下拉菜单中选择索引分析器。
索引分析器提供当前命名空间的SQL语句计数显示和五个索引分析报告选项。
SQL语句计数
在SQL索引分析器的顶部有一个对命名空间中的所有SQL语句进行计数的选项。按收集SQL语句按钮。SQL索引分析器显示“正在收集SQL语句...”当计票进行时,然后“完成!”当清点完毕后。SQL语句分为三类进行计数:缓存查询计数、类方法计数和类查询计数。这些计数针对整个当前命名空间,不受架构选择选项的影响。
对应的方法是%SYS.PTools.UtilSQLAnalysis
类中的getSQLStmts()
。
可以使用清除语句按钮删除当前命名空间中收集的所有语句。该按钮调用clearSQLStatements()
方法。
报告选项
可以检查当前命名空间中选定架构的缓存查询报告,也可以(通过不选择架构)检查当前命名空间中所有缓存查询的报告。可以在此分析中跳过或包括系统类查询、INSERT
语句和/或IDKEY
索引。“架构选择”和“跳过选项”复选框是用户自定义的。
指数分析报告选项包括:
- 索引使用:此选项获取当前名称空间中的所有缓存查询,为每个查询生成显示计划,并记录每个查询使用每个索引的次数以及名称空间中所有查询对每个索引的总使用量。这可用于显示未使用的索引,以便可以删除或修改这些索引以使其更有用。结果集从最少使用的索引到最常使用的索引排序。
对应的方法是%SYS.PTools.UtilSQLAnalysis
类中的indexUsage()
。要导出此方法生成的分析数据,请使用exportIUAnalysis()
方法。
- 使用表扫描的查询:此选项标识当前名称空间中执行表扫描的所有查询。如果可能,应避免表扫描。表扫描并不总是可以避免的,但是如果一个表有大量的表扫描,那么应该检查为该表定义的索引。通常,表扫描列表和临时索引列表会重叠;修复其中一个会删除另一个。结果集按从最大块计数到最小块计数的顺序列出表格。提供了显示计划链接以显示对帐单文本和查询计划。
对应的方法是%SYS.PTools.UtilSQLAnalysis
类中的tableScans()
。要导出此方法生成的分析数据,请使用exportTSAnalysis()
方法。
- 带临时索引的查询:此选项标识当前名称空间中构建临时索引以解析SQL的所有查询。有时,使用临时索引会有所帮助并提高性能,例如,基于范围条件构建一个小索引,然后InterSystems IRIS可以使用该索引按顺序读取主映射。有时,临时索引只是不同索引的子集,可能非常有效。其他情况下,临时索引会降低性能,例如,扫描
master may
以在具有条件的特性上构建临时索引。这种情况表明缺少所需的索引;应该向与临时索引匹配的类添加索引。结果集按从最大块计数到最小块计数的顺序列出表格。提供了显示计划链接以显示对帐单文本和查询计划。
对应的方法是%SYS.PTools.UtilSQLAnalysis
类中的tempIndices()
。要导出此方法生成的分析数据,请使用exportTIAnalysis()
方法。
- 缺少联接索引的查询:此选项检查当前名称空间中具有联接的所有查询,并确定是否定义了支持该联接的索引。它将可用于支持联接的索引从0(不存在索引)排序到4(索引完全支持联接)。外部联接需要一个单向索引。内联接需要双向索引。默认情况下,结果集只包含
JoinIndexFlag<4
的行。JoinIndexFlag=4
表示有完全支持联接的索引。
相应的方法是%SYS.PTools.UtilSQLAnalysis
类中的joinIndices()
,它提JoinIndexFLAG
值的描述。要导出此方法生成的分析数据,请使用exportJIAnalysis()
方法。默认情况下,exportJIAnalysis()
不会列出JoinIndexFlag=4
值,但可以选择列出这些值。
- 带离群值索引的查询:此选项标识当前名称空间中具有离群值的所有查询,并确定是否定义了支持该离群值的索引。它将可用于支持离群值的索引从
0
(不存在索引)到4
(索引完全支持离群值)进行排序。默认情况下,结果集只包含OutlierIndexFlag<4
的行。OutlierIndexFlag=4
表示存在完全支持离群值的索引。
对应的方法是%SYS.PTools.UtilSQLAnalysis
类中的outlierIndices()
。要导出此方法生成的分析数据,请使用exportOIAnalysis()
方法。默认情况下,exportOIAnalysis()
不会列出OutlierIndexFlag=4
值,但可以选择列出这些值。
当选择其中一个选项时,系统自动执行操作并显示结果。
第一次选择一个选项或调用相应的方法时,系统生成结果数据;
如果选择该选项或再次调用该方法,InterSystems IRIS将重新显示相同的结果。
要生成新的结果数据,必须使用Gather SQL Statements按钮重新初始化Index Analyzer结果表。
为%SYS.PTools
生成新的结果数据。
在UtilSQLAnalysis
方法中,必须调用gettsqlstmts()
来重新初始化索引分析器结果表。
更改“跳过所有系统类和例程”或“跳过插入语句”复选框选项也会重新初始化索引分析器结果表。
IndexUsage()方法
下面的示例演示indexUsage()
方法的用法:
/// w ##class(PHA.TEST.SQL).CountingCachedQueries2()
ClassMethod CountingCachedQueries2()
{
DO ##class(%SYS.PTools.UtilSQLAnalysis).indexUsage(1,1)
SET utils = "SELECT %EXACT(Type), Count(*) As QueryCount "_
"FROM %SYS_PTools.UtilSQLStatements GROUP BY Type"
SET utilresults = "SELECT SchemaName, Tablename, IndexName, UsageCount "_
"FROM %SYS_PTools.UtilSQLAnalysisDB ORDER BY UsageCount"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(utils)
IF qStatus'=1 {
WRITE "%Prepare 失败:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET rset = tStatement.%Execute()
DO rset.%Display()
WRITE !,"实用程序结束数据",!!
SET qStatus = tStatement.%Prepare(utilresults)
IF qStatus'=1 {
WRITE "%Prepare 失败:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET rset = tStatement.%Execute()
DO rset.%Display()
WRITE !,"实用程序结束数据"
}
注意,由于结果是按UsageCount
排序的,因此带有UsageCount > 0
的索引列在结果集的末尾。
可以从ObjectScript或SQL调用或SELECT
命令调用该类中的方法。
SQL命名约定是指定包名%SYS_PTools
,然后在以小写字母开头的方法名前加上前缀“PT_”
。
如下面的例子所示:
ObjectScript:
DO ##class(%SYS.PTools.UtilSQLAnalysis).indexUsage()
SQL:
CALL %SYS_PTools.PT_indexUsage()
SELECT %SYS_PTools.PT_indexUsage()
索引优化选项
默认情况下,InterSystems SQL查询优化器使用复杂而灵活的算法来优化涉及多个索引的复杂查询的性能。在大多数情况下,这些默认值可提供最佳性能。但是,在极少数情况下,可能希望通过指定OPTIMIZE-OPTION
关键字为查询优化器提供“提示”。
FROM
子句支持%ALLINDEX
和%IGNOREINDEX OPTIME-OPTION
关键字。这些优化选项关键字控制查询中使用的所有索引。
可以使 %NOINDEX
条件级别提示指定对特定条件使用索引的例外情况。 %NOINDEX
提示放在每个不应使用索引的条件之前。例如,WHERE %NOINDEX hiredate < ?
当绝大多数数据被条件选中(或未选中)时,这是最常用的。对于小于(<
)或大于(>
)的条件,使用%NOINDEX
条件级别提示通常是有益的。对于相等条件,使用%NOINDEX
条件级别提示不会带来任何好处。使用联接条件时,ON
子句联接支持%NOINDEX
。
%NOINDEX
关键字可用于覆盖在FROM
子句中建立的索引优化。在下面的示例中,%ALLINDEX
优化关键字适用于除E.Age
条件之外的所有条件测试:
SELECT P.Name,P.Age,E.Name,E.Age
FROM %ALLINDEX Sample.Person AS P LEFT OUTER JOIN Sample.Employee AS E
ON P.Name=E.Name
WHERE P.Age > 21 AND %NOINDEX E.Age < 65