Oracle

性能优化

-SQL

优化

(

案例三

)

问题:

客户反馈取消记账耗时太长,10

分钟也结束不了。

问题分析:

问题重现后,远程查看主要慢在一条查询SQL

11df3edba2dc6c35d7c11fb536d3f002.png

查看执行计划gl_voucher

大表全表扫描,实际上

SQL

中谓词字段上有特别高效的索引,怀疑统计信息不准确。

查看gl_voucher

统计信息记录行数

0

行,显然

oracle

认为扫描一个

0

行的表不需要走索引。

SQL> Select * from user_tables where table_name in (

GL_VOUCHER

,

GL_TMP_TABLE

);

24d8ec5807453cd4fc0bbb1bef9859ad.png

解决方案:

手动重新收集gl_voucher

表统计信息

Analyze table gl_voucher compute statistics;

SQL> Select * from user_tables where table_name in (

GL_VOUCHER

,

GL_TMP_TABLE

);

a40f1821d9fc6b024949bcb38bfa8ae2.png

再次执行速度有很大提升。

注意:

统计信息不准确经常会导致性能问题,如果数据库自带收集统计信息的任务已经停了,可以考虑使用如下存储过程定期收集统计信息,此存储过程并不适用于所有场景,根据实际情况进行调整。

---

创建收集统计信息的存储过程:

---需要显式地赋予用户建表权限

grant

create

any

table

to

chen

;

---创建收集统计信息的存储过程

CREATE

OR

REPLACE

PROCEDURE

ANALYZE_TB

AS

OWNER_NAME

VARCHAR2

(

100

);

V_LOG

INTEGER

;

V_SQL1

VARCHAR2

(

800

);

V_TABLENAME

VARCHAR2

(

50

);

CURSOR

CUR_LOG

IS

SELECT

COUNT

(*)

FROM

USER_TABLES

WHERE

TABLE_NAME

=

'ANALYZE_LOG'

;

--1

BEGIN

--DBMS_OUTPUT.ENABLE (buffer_size=>100000);

--1.1

BEGIN

OPEN

CUR_LOG

;

FETCH

CUR_LOG

INTO

V_LOG

;

IF

V_LOG

=

0

THEN

EXECUTE

IMMEDIATE

'CREATE TABLE ANALYZE_LOG (USER_NAME VARCHAR(20),OP_TIME CHAR(19) DEFAULT to_char(sysdate,''yyyy-mm-dd hh24:mi:ss''),ERROR_TEXT VARCHAR(200),TABLE_NAME VARCHAR(40))'

;

END

IF

;

END

;

SELECT

USER

INTO

OWNER_NAME

FROM

DUAL

;

V_SQL1

:=

'INSERT INTO ANALYZE_LOG (USER_NAME,ERROR_TEXT,TABLE_NAME) VALUES ('''

||

OWNER_NAME

||

''',''ANALYZE BEGIN'',''ALL'')'

;

EXECUTE

IMMEDIATE

V_SQL1

;

sys.dbms_stats.gather_schema_stats

(

ownname

=>

UPPER

(

OWNER_NAME

),

estimate_percent

=>

100

,

method_opt

=>

'FOR ALL INDEXED COLUMNS'

,

cascade

=>

TRUE

);

V_SQL1

:=

'INSERT INTO ANALYZE_LOG (USER_NAME,ERROR_TEXT,TABLE_NAME) VALUES ('''

||

OWNER_NAME

||

''',''ANALYZE END'',''ALL'')'

;

EXECUTE

IMMEDIATE

V_SQL1

;

commit

;

--1.2 delete tmptb statitics and lock statistics

BEGIN

for

x

in

(

select

a.table_name

,

a.last_analyzed

,

b.stattype_locked

from

user_tables a

,

user_tab_statistics b

where

a.temporary

=

'Y'

and

a.table_name

=

b.table_name

and

(

b.STATTYPE_LOCKED

is

null

or

a.last_analyzed

is

not

null

))

LOOP

IF

x.last_analyzed

IS

NOT

NULL

THEN

--delete stats

dbms_stats.delete_table_stats

(

ownname

=>

user

,

tabname

=>

x.table_name

,

force

=>

TRUE

);

END

IF

;

IF

x.stattype_locked

IS

NULL

THEN

--lock stats

dbms_stats.lock_table_stats

(

ownname

=>

user

,

tabname

=>

x.table_name

);

END

IF

;

END

LOOP

;

end

;

EXCEPTION

WHEN

OTHERS

THEN

IF

CUR_LOG

%

ISOPEN

THEN

CLOSE

CUR_LOG

;

END

IF

;

commit

;

end

;

---

创建

job,

当天的凌晨2点开始更新统计信息,以后每2天的凌晨2点更新统计信息。

根据实际情况调整时间。

SQL

>

VARIABLE

JOBNO

NUMBER

;

SQL

>

VARIABLE

INSTNO

NUMBER

;

SQL

>

SQL

>

BEGIN

2

SELECT

INSTANCE_NUMBER

INTO

:

INSTNO

FROM

V$INSTANCE

;

3

DBMS_JOB.SUBMIT

(:

JOBNO

,

4

'ANALYZE_TB; '

,

5

TRUNC

(

SYSDATE

)

+

1

+

2

/

24

,

6

'TRUNC(SYSDATE)+2+2/24'

,

7

TRUE

,

8

:

INSTNO

);

9

COMMIT

;

10

END

;

11

/

PL

/

SQL

procedure

successfully completed

欢迎关注我的微信公众号"IT小Chen",共同学习,共同成长!!!

c108d7862b822a619a50eaed2ceeb401.png

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐