Някои смъртни грехове спрямо производителността на вашата база от данни Автор: Владимир Андреев, Semantec GmbH Лектор: Стоян Иванов, Семантек България ООД.

Download Report

Transcript Някои смъртни грехове спрямо производителността на вашата база от данни Автор: Владимир Андреев, Semantec GmbH Лектор: Стоян Иванов, Семантек България ООД.

Slide 1

Някои смъртни грехове спрямо
производителността на вашата
база от данни
Автор: Владимир Андреев, Semantec
GmbH
Лектор: Стоян Иванов, Семантек
България ООД


Slide 2

За автора и лектора
• Съответно 7 и 11 години опит с Оракъл
• Голям опит в повишаване на производителността на
базата
• Оценка на производителността на много реално
действащи големи бази от данни
– Различни индустрии (automotive, pharmaceutical, chip
production, public sector, banking, machine building,
telecommunications, etc.)
– Различна натовареност (OLTP, DW, Web)
– Различни размери (100 MB – 500 GB)
– Различни архитектури (2-tier, 3-tier)
– Различни технологии (PL/SQL, C++, Java, Forms, ABAP,
Teamcenter, Vignette, …)


Slide 3

Основни проблеми
• Проблеми в архитектурата: “независимост” от
базата чрез isolation layers
= Под-оптимална производителност за всяка база
= По-висока цена за разработка и поддръжка

• Проблеми в дизайна: row-by-row processing
= Непреодолими лимити по отношение на
производителността и възможността за
разширяване

• Проблеми в кода: Un-sharable SQL
= Disaster waiting to happen


Slide 4

Middle layers
• Application servers
– Много качествени с мощен език за комуникация с
базата: SAP, BAAN
– Custom – съществува риск да са твърде
ограничени в комуникацията си с базата

• Database isolation layers
– Добри: DAO/OLEDB, ODBC, JDBC, …
– Подобряващи се: J2EE Persistency Layers
(например TopLink)
– Custom – обикновено са извор на проблеми


Slide 5

Database Isolation Layers
• API със ограничена или липсваща поддръжка
за:
– Не-стандартен SQL (with, merge, connect by,
analytics, hints, joins, …)
– Извикване на съхранени процедури и функции
– Не-поддържане на “ref cursors”, обектни типове, ...
– Неподдържане на bind променливи

• Поддръжка на модела на данните,
включително и индексите
• Генериране на под-оптимален SQL


Slide 6

Примери
• “Няма възможност за контрол на SQL”
• “Application server-а не може да работи
с CBO”
• “Налага се да лъжем междинния слой”


Slide 7

Обработка на записите ред по
ред
• Изглежда лесно за писане и разбиране
• Негативни последици:
– Излишни round-trip
– Превключване на контексти
– Увеличена натовареност на сървъра
– Увеличена натовареност на мрежата

• Row-by-row = Slow-by-slow


Slide 8

Slow-by-Slow Processing
• По мрежата
– Увеличен трафик
– Забавянията по мрежата се мултиплицират

• На същата машина (използвайки IPC)
– Доброволно превключване на контексти

• Вътре в базата
– “Превключване на контексти” между SQL
engine и PL/SQL engine


Slide 9

Пример
FOR EACH
SELECT c1
FROM t1
WHERE …
LOOP
SELECT c2 FROM t2 WHERE c3 = :c1;
SELECT * FROM t3 WHERE c4 = :c2;
END LOOP;

Може да бъде заменено с
SELECT * FROM t3
WHERE c4 IN
(SELECT c2 FROM t2
WHERE c3 IN
(SELECT c1 FROM t1
WHERE …));

SELECT *


Slide 10

Пример
For r in (select Data1, id1 from table1) loop
Select Data2 into d2 from table2 where id=r.id1;
/* do something with r.Data1 and Data2 */
End loop;

По-добре
For r in (select Data1, Data2 from table1, table2
where id=id1) loop
/* do something with Data1 and Data2 */
End loop;


Slide 11

Пример
Create or replace view VM01_WORKFLOW_STEPS_ACT as
SELECT T.ID, T.USER_ID, T.WORKFLOW_ID, T.CONTENT_ID
FROM TM01_WORKFLOW_STEPS T
WHERE NUMMER = FM01_WORKFLOW_STEPS_ACT(T.CONTENT_ID);
Create or replace
FUNCTION "FM01_WORKFLOW_STEPS_ACT" (vCONTENT_ID IN NUMBER)RETURN
NUMBER
IS
vNr NUMBER;
BEGIN
SELECT MAX(NUMMER) INTO vNr FROM TM01_WORKFLOW_STEPS WHERE
CONTENT_ID=vCONTENT_ID;
RETURN vNr;
EXCEPTION
WHEN OTHERS
THEN RETURN NULL;
END FM01_WORKFLOW_STEPS_ACT;


Slide 12

По-добре
Create or replace view VM01_WORKFLOW_STEPS_ACT as
SELECT T.ID, T.USER_ID, T.WORKFLOW_ID, T.CONTENT_ID
FROM TM01_WORKFLOW_STEPS T
WHERE NUMMER = (select max(nummer) from
TM01_WORKFLOW_STEPS where CONTENT_ID=T.CONTENT_ID);
или
Create or replace view VM01_WORKFLOW_STEPS_ACT as
select * from (
SELECT T.ID, T.USER_ID, T.WORKFLOW_ID, T.CONTENT_ID,
max(nummer) over (partition by CONTENT_ID) mNum
FROM TM01_WORKFLOW_STEPS T )
where nummer=mNum;


Slide 13

Пример
SELECT /*+ FIRST_ROWS */ED.EVENT_DETAIL_ID
FROM ASTCAL_EVENT_DETAIL ED, TM01_FOLGE_ASTCAL FA, TA01_CONTENT_CONTENT Y,
TM01_SENDUNG S, TM01_PROGRAMM P
WHERE ED.EVENT_MASTER_ID = FA.ASTCAL_EVENT_MASTER_ID
AND FA.CONTENT_ID = Y.LINK_CONTENT_ID AND Y.CONTENT_ID = S.CONTENT_ID
AND S.PROGRAMM_ID = P.ID AND ED.EVENT_DETAIL_ID != :b1
AND :b2 < ED.END_DATE AND :b3 > ED.START_DATE
AND FM01_PRG_COMPARE_ID(:b4,P.CONTENT_ID) = 1;
FUNCTION "FM01_PRG_COMPARE_ID" (vID1 IN NUMBER,vID2 IN NUMBER) RETURN NUMBER IS
vArt NUMBER;
begin
SELECT ART INTO vART FROM TM01_PROGRAMM WHERE CONTENT_ID=vID1;
IF vArt=2 THEN -- TV
SELECT ART INTO vART FROM TM01_PROGRAMM WHERE CONTENT_ID=vID2;
IF vArt=2 THEN -- auch TV,also ok
RETURN 1;
END IF;
ELSE -- Radio => ID's vergleichen
IF vID1=vID2 THEN
RETURN 1;
END IF;
END IF;
RETURN 0;
END FM01_PRG_COMPARE_ID;


Slide 14

По-добре
SELECT /*+ FIRST_ROWS */ED.EVENT_DETAIL_ID
FROM ASTCAL_EVENT_DETAIL ED,
TM01_FOLGE_ASTCAL FA, TA01_CONTENT_CONTENT Y,
TM01_SENDUNG S,TM01_PROGRAMM P2,
(select art from TM01_PROGRAMM where CONTENT_ID=:b4) P1
WHERE ED.EVENT_MASTER_ID = FA.ASTCAL_EVENT_MASTER_ID
AND FA.CONTENT_ID = Y.LINK_CONTENT_ID
AND Y.CONTENT_ID = S.CONTENT_ID
AND S.PROGRAMM_ID = P2.ID
AND ED.EVENT_DETAIL_ID != :b1
AND :b2 < ED.END_DATE AND :b3 > ED.START_DATE
and (p1.ART = 2 and p2.ART = 2 or p1.ART!=2 and
:b4=p2.CONTENT_ID);

Последния предикат може да бъде заменен от еквивалентен, но може
би по читаем:
and decode(p1.art,2, decode(p2.art,2,1),
decode(:b3,p2.CONTENT_ID,1,0))=1


Slide 15

Пример
cursor c1 is select id from t1

open c1;
loop
exit when c1%notfound;
select x1 into v1 from … where
select x2 into v2 from … where
insert into t2 values (v1, v2,
end loop;

where…;

p1=c1.id;
p2=c1.id;
…);

По-добре:
insert into t2
select x1, x2, …
from t1, (select x1,p1 from …), (select x2,p2 from …), …
where … /* conditions for t1 */
and p1=t1.id
and p2=t2.id
…;


Slide 16

Ефект върху 6-процесорна
система
100%

14000

CPU utilisation
90%
12000
%idle
%wio
%sys
%usr
pswch/s

70%

10000

60%
8000
50%
6000
40%

30%

4000

20%
2000
10%

16:24

16:14

16:04

15:54

15:44

15:34

15:24

15:14

15:04

14:54

14:44

14:34

14:24

14:14

14:04

13:54

13:44

13:34

0

13:24

0%

Context Switches per second

80%


Slide 17

Un-sharable SQL
• Най-лошия тип – литерали вместо bind
променливи
• Лош тип – Динамично изграждани inили or- списъци
• Почти безреден тип – Малки/големи
букви, излишни интервали и табулации,
коментари, различни имена на bind
променливите
• Изключение: Хистограми


Slide 18

Литерали и bind променливи –
PL/SQL
• Лош вариант:
execute immediate ‘delete from T where
id=‘||id_to_delete;

• По-добър вариант:
execute immediate ‘delete from T where
id=:dellID’ using id_to_delete;

• Най-добре:
delete from T where id=id_to_delete;


Slide 19

Литерали и bind променливи Java
• Лош вариант:
Statement stmt = conn.createStatement();
stmt.executeUpdate(“delete from T where
id=“+Integer.toString(id_to_delete);
• По-добър вариант:
PreparedStatement pstmt =
conn.prepareStatement(“delete from T
where id=?”);
pstmt.setInt(1, id_to_delete);
pstmt.execute();


Slide 20

Пример – динамичен SQL
/*Ermittlung g_ausbildung*/
source_cursor := DBMS_SQL.OPEN_CURSOR;
statement := 'SELECT t1.faktor
FROM loader_faktoren_pr_segment t1, dim_person t2
WHERE t2.kundennummer = '||val_p_kunden.kundennummer_echt||'
AND t1.art = ''Ausbildung''
AND t2.ausbildung_s = t1.schluessel';
DBMS_SQL.PARSE(source_cursor, statement, dbms_sql.NATIVE);
DBMS_SQL.DEFINE_COLUMN(source_cursor, 1, g_ausbildung_tmp);
dummy := DBMS_SQL.EXECUTE(source_cursor);
IF DBMS_SQL.FETCH_ROWS(source_cursor) > 0 THEN
DBMS_SQL.COLUMN_VALUE(source_cursor, 1, g_ausbildung);
ELSE
g_ausbildung := 1;
END IF;
DBMS_SQL.CLOSE_CURSOR(source_cursor);


Slide 21

Пример – native dynamic SQL
BEGIN
EXECUTE IMMEDIATE 'SELECT t1.faktor
FROM loader_faktoren_pr_segment t1, dim_person t2
WHERE t2.kundennummer = :kn
AND t1.art = ''Ausbildung''
AND t2.ausbildung_s = t1.schluessel
AND ROWNUM=1'
INTO g_ausbildung
USING val_p_kunden.kundennummer_echt;
EXCEPTION WHEN no_data_found THEN g_ausbildung := 1;
END;

Още по-добре – обикновен не-динамичен SQL:
BEGIN
SELECT t1.faktor INTO g_ausbildung
FROM loader_faktoren_pr_segment t1, dim_person t2
WHERE t2.kundennummer = val_p_kunden.kundennummer_echt
AND t1.art = 'Ausbildung'
AND t2.ausbildung_s = t1.schluessel
AND ROWNUM=1;
EXCEPTION WHEN no_data_found THEN g_ausbildung := 1;
END;


Slide 22

Пример
UPDATE ABRUFE
SET ENDE_ABRUF=
TO_DATE('27.01.2004 10:50:00','DD.MM.YYYY HH24:MI:SS')
WHERE ABRUF_ID = 242392;
begin PM01_EXPORT_PROG_BEITRAG(:result, '1209923'); end;
statement :=
'SELECT t1.faktor
FROM loader_faktoren_pr_segment t1, dim_person t2 WHERE t2.kundennummer =
'||val_p_kunden.kundennummer_echt||'
AND t1.art = ''Ausbildung'‘
AND t2.ausbildung_s = t1.schluessel';
DBMS_SQL.PARSE(source_cursor, statement, dbms_sql.NATIVE);
SELECT * FROM apttab
WHERE apttab.finr
= 1 AND apttab.artnr
AND apttab.varnr
= 0 AND apttab.appos

= '122-0147-0
= 30

Select * from ODIN_STACK where ODS_INDEX ='PODSB_5714733';

'


Slide 23

Пример
select A.CONTENT_ID, A.LINK, A.EXTENSION
from VA01_CONTENT_AUDIO_L AL, VA01_CONTENT_AUDIO
where AL.AUDIO1_ID=A.CONTENT_ID
and AL.ID = 11541;
select A.CONTENT_ID, A.LINK, A.EXTENSION
from VA01_CONTENT_AUDIO_L AL, VA01_CONTENT_AUDIO
where AL.AUDIO1_ID=A.CONTENT_ID
and AL.ID = 46309;
SELECT TITEL, AUTOR, '', '', '', TEASER, ''
from VA01_CONTENT_BEITRAG_CONT
where CONTENT_ID = 1249596;
SELECT TITEL, AUTOR, '', '', '', TEASER, ''
from VA01_CONTENT_BEITRAG_CONT
where CONTENT_ID = 1249924;
begin PD01_FRESH_ARTIKEL(:result, 7569, 1, 'and c.id not
(1272117,1270457) '); end;
begin PD01_FRESH_ARTIKEL(:result, 7569, 1, 'and c.id not
(1272117,1270457,1268878) '); end;
begin PD01_FRESH_ARTIKEL(:result, 7570, 1, 'and c.id not
end;
begin PM01_EXPORT_PROG_BEITRAG(:result, '1209923'); end;
begin PM01_EXPORT_PROG_BEITRAG(:result, '1209926'); end;
begin PM01_EXPORT_PROG_BEITRAG(:result, '1209930'); end;

A

A

in
in
in (1263619) ');


Slide 24

Как да откриваме un-sharable
SQL
Accept len prompt "Prefix length to consider:"
SELECT
*
FROM v$sql
WHERE UPPER (SUBSTR (sql_text, 1, &len)) IN
(SELECT
UPPER (SUBSTR (sql_text, 1, &len))
FROM v$sql
GROUP BY UPPER (SUBSTR (sql_text, 1, &len))
HAVING COUNT (*) > 1)
ORDER BY UPPER (sql_text);


Slide 25

Как да откриваме un-sharable
SQL

equivalent_sql.sh


Slide 26

Благодарим ви за вниманието
За контакти:
[email protected]
[email protected]