在项目支持过程中,通过SQL日志分析,发现一条SQL语句执行时间较长。把SQL语句拿出来进行分析。
一、查询SQL语句
SELECT
r.*
FROM
HN_DEP_GA_XN_TEST.DEP_ETL_JOB_RECORDERS r
INNER JOIN HN_DEP_GA_XN_TEST.DIC_DEP_ETL_JOB J
ON
J.JOB_ID=r.JOB_ID
WHERE
J.SCHEDULED = 1
AND r.CURR_JOB_KEY IS NOT NULL
AND r.JOB_STATUS =2
AND r.IS_EMPTY_ELEMENT=0
AND r.RETRY_COUNT <?
AND EXISTS
(
SELECT
r2.id
FROM
HN_DEP_GA_XN_TEST.DEP_ETL_JOB_RECORDERS r2
WHERE
r2.PRE_JOB_KEY=r.CURR_JOB_KEY
AND r2.JOB_STATUS =1
);
以下是SQL语句的执行计划。
如上图红色部分所示,其中[exp_cast(1),exp_cast(1)]提供的信息提示我们,该索引所在的字段,在查询过程中作类型转换了,该查询条件是J.SCHEDULED=1。
来分析一下J.SCHEDULED对应的DIC_DEP_ETL_JOB表和SCHEDULED字段。信息如下所示:
(1)SCHEDULED字段的定义如下所示:
“SCHEDULED” NUMBER NOT NULL
(2)SCHEDULED字段存在索引:
CREATE INDEX “IDX_DIC_SCHEDULED” ON “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”(“SCHEDULED” ASC);
(3)SCHEDULED字段注释
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“SCHEDULED” IS ‘是否定时任务(0-不是定时;1-定时)’;
二、查询语句中两张表的相关信息
为了更好的分析这个SQL语句,我们把查询语句中涉及的两张表相关信息也列举出来。字段注释已做脱敏处理。
(1)HN_DEP_GA_XN_TEST"."DEP_ETL_JOB_RECORDERS表信息
CREATE TABLE “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”
(
“ID” VARCHAR2(150) NOT NULL,
“JOB_ID” VARCHAR2(150) NOT NULL,
“JOB_NAME” VARCHAR2(150) NOT NULL,
“JOB_TYPE” NUMBER NOT NULL,
“JOB_PARAM” CLOB,
“START_TIME” TIMESTAMP(0) NOT NULL,
“END_TIME” TIMESTAMP(0),
“JOB_STATUS” NUMBER NOT NULL,
“PRE_JOB_KEY” VARCHAR2(150),
“CURR_JOB_KEY” VARCHAR2(150),
“IS_EMPTY_ELEMENT” NUMBER DEFAULT 0,
“JOB_RESULT_INFO” CLOB,
“PACKAGE_ID” VARCHAR2(220),
“DEST_END_TIME” TIMESTAMP(0),
“DEST_CALLBACK_INFO” VARCHAR2(1000),
“PRE_PACKAGE_ID” VARCHAR2(1000),
“RETRY_COUNT” NUMBER DEFAULT 0 NOT NULL,
“WORK_ID” VARCHAR2(100),
“MONITOR_ID” NUMBER(20,0),
“APP_GROUP” VARCHAR2(30),
“MATERIAL_CURR_ID” VARCHAR2(300),
“SEND_BACK_PKG_ID” VARCHAR2(440),
“NOTSTRUCT_COUNT” NUMBER(22,0),
“NOTSTRUCT_FAIL_COUNT” NUMBER(22,0),
“INFO_FAIL” VARCHAR2(2000),
CONSTRAINT “PK_DEP_ETL_JOB_RECORDERS” NOT CLUSTER PRIMARY KEY(“ID”));
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“APP_GROUP” IS ‘所XX用’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“DEST_CALLBACK_INFO” IS ‘目标节点XX息(JSON数组,包含以下信息:目标节点appId, jobStatus, endTime)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“DEST_END_TIME” IS ‘最XX节点入库结束时间’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“END_TIME” IS ‘结束时间’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“ID” IS ‘主键’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“INFO_FAIL” IS ‘失败信息’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“IS_EMPTY_ELEMENT” IS ‘是否是空包(0-非空包,1-空包)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“JOB_ID” IS ‘JOBID’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“JOB_NAME” IS ‘JOB名称’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“JOB_PARAM” IS ‘JOB参数’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“JOB_RESULT_INFO” IS ‘JOB执行结果信息’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“JOB_STATUS” IS ‘job状态(0-RUNNING,1-DONE,2-FAILED)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“JOB_TYPE” IS ‘JOB类型(0-outJob,1-inJob)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“MATERIAL_CURR_ID” IS ‘当前子材料ID’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“MONITOR_ID” IS ‘调度XXID’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“NOTSTRUCT_COUNT” IS ‘出XX总数’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“NOTSTRUCT_FAIL_COUNT” IS ‘出XX数’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“PACKAGE_ID” IS ‘数据主包名,不包含扩展名’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“RETRY_COUNT” IS ‘ETL-JOB重试次数’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“SEND_BACK_PKG_ID” IS ‘回执包PKG’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“START_TIME” IS ‘开始时间’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”.“WORK_ID” IS ‘唯一标识’;
CREATE INDEX “IDX_RECORDERS_PACKAGE_ID” ON “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”(“PACKAGE_ID” ASC);
CREATE INDEX “IDX_RECORDERS_END_TIME” ON “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”(“END_TIME” ASC) ;
CREATE INDEX “IDX_RECORDERS_START_TIME” ON “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”(“START_TIME” ASC) ;
CREATE INDEX “IDX_JOB_ID” ON “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”(“JOB_ID” ASC) ;
CREATE INDEX “IDX_PRE_JOB_KEY” ON “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”(“PRE_JOB_KEY” ASC) ;
CREATE INDEX “IDX_JOB_NAME” ON “HN_DEP_GA_XN_TEST”.“DEP_ETL_JOB_RECORDERS”(“JOB_NAME” ASC) ;
(2)HN_DEP_GA_XN_TEST"."DIC_DEP_ETL_JOB表信息
CREATE TABLE “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”
(
“JOB_ID” VARCHAR2(150) NOT NULL,
“JOB_NAME” VARCHAR2(150) NOT NULL,
“CREATE_TIME” TIMESTAMP(0) NOT NULL,
“SCHEDULED” NUMBER NOT NULL,
“SCHEDULED_CRON” VARCHAR2(30),
“SCHEDULED_PARAM” VARCHAR2(1000),
“JOB_REMARK” VARCHAR2(250),
“FIXED_DELAY” NUMBER DEFAULT 2000000,
“IS_UP_NOT_STRUCT” VARCHAR2(10),
“JNDI_NAME” VARCHAR2(50),
“OUT_PKG_INTERVAL” NUMBER,
“DEPS_PRE_JOB_KEY” VARCHAR2(1) DEFAULT ‘1’ NOT NULL,
“JOB_ZIP_LOCAL_PATH” VARCHAR2(300),
“JOB_ZIP_PUBLISH_STATUS” NUMBER DEFAULT 0,
“DATA_SOURCE_MARK” VARCHAR2(100),
“UPDATE_TIME” TIMESTAMP(6),
“DATA_SOURCE_TYPE” NUMBER(1,0) DEFAULT 0 NOT NULL,
CONSTRAINT “PK_DIC_DEP_ETL_JOB” NOT CLUSTER PRIMARY KEY(“JOB_ID”),
CONSTRAINT “UNIQUE_DIC_DEP_ETL_JOB_NAME” UNIQUE(“JOB_NAME”)) STORAGE(ON “HN_DEP_GA_XN_TEST”, CLUSTERBTR) ;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“CREATE_TIME” IS ‘创XX间’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“DATA_SOURCE_MARK” IS ‘数据源ID,外键’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“DEPS_PRE_JOB_KEY” IS ‘定XX否依赖前序 0-不依赖 1-依赖 默认依赖’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“FIXED_DELAY” IS ‘jobXX间隔’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“JOB_ID” IS ‘主键’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“JOB_NAME” IS ‘JOB名称’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“JOB_ZIP_LOCAL_PATH” IS ‘管理XX传JOB包后XX径’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“JOB_ZIP_PUBLISH_STATUS” IS ‘JXX否已发布(0-未发布;1-已发布)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“OUT_PKG_INTERVAL” IS ‘新XX包的时间长度(单位毫秒)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“SCHEDULED” IS ‘是否定时任务(0-不是定时;1-定时)’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“SCHEDULED_CRON” IS ‘定XX达式cron’;
COMMENT ON COLUMN “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”.“SCHEDULED_PARAM” IS ‘定时XX的参数’;
CREATE INDEX “IDX_DIC_JOB_ID” ON “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”(“JOB_ID” ASC) ;
CREATE INDEX “IDX_DIC_SCHEDULED” ON “HN_DEP_GA_XN_TEST”.“DIC_DEP_ETL_JOB”(“SCHEDULED” ASC);
通过对两张表的相关信息进行分析得知。SCHEDULED字段的数据类型是NUMBER,这个字段的取值就两个(0或1),在这个字段上创建了索引IDX_DIC_SCHEDULED。虽然查询语句执行计划走了索引,但在执行过程中做了类型转换,也损耗了性能,整体上影响了语句的执行时间。
通过与开发人员沟通得知,SCHEDULED字段只取两个值(0或1),不存在取其它整数、小数或负值。通过查询DM官方开发手册,NUMBER数据类型用于存储零、正负定点数,在DIC_DEP_ETL_JOB表中SCHEDULED字段数据类型设置为NUMBER,基本可以满足业务需求。SCHEDULED字段只取两个值(0或1),不存储任何其它值。可以尝试把SCHEDULED字段的数据类型设置为BIT,BIT类型用于存储整数数据1、0或NULL。
经过与开发人员协商,将DIC_DEP_ETL_JOB表和相关数据进行备份后,把SCHEDULED字段的数据类型改为BIT,再将数据恢复到表中。
再次查询语句后,得到新的执行计划。
如上红色部分所示,可以看到DIC_DEP_ETL_JOB表的SCHEDULED字段没有类型转换。
通过以上优化后,避免了类型转换问题,SQL语句执行时间不超过1秒钟。
针对以上类型转换问题,有两种处理方法可供参考:
(1)SCHEDULED字段数据类型不做改变,给J.SCHEDULED等于1加引号。在这个语句中,需要在应用程序中做调整,让程序给值加引号。开发人员最终放弃了这个方案,选择修改SCHEDULED字段数据类型。
(2)SCHEDULED字段数据类型由NUMBER改为BIT。
文章
阅读量
获赞