Проблема производительности запросов Oracle - PullRequest
0 голосов
/ 23 октября 2011

У меня есть таблица «обсуждение», структура которой выглядит следующим образом:

Название таблицы: обсуждение,

Id      name       parent_id   root_id  ...other columns
 1   discussion1      0            0
 2   discussion2      1            1
 3   discussion3      2            1
 4   discussion4      3            1
 5   discussion5      4            1

Очевидно, что id был определен как PK, parent_id и root_id были проиндексированы.

эти строки таблицы были построены как иерархические отношения (вроде parent-> child), а также обратите внимание, что столбец root_id используется для описания этих строк в одном и том же дереве.

Я написал два SQL для получения дерева потоков:

SQL 1

  SELECT * 
    from discussion 
   start with (parent_Id=0 AND id=?) 
 connect by prior Id=parent_Id

SQL 2

 SELECT * FROM (
           SELECT * FROM discussion WHERE root_id = ? or id = ?
          )START WITH (parent_Id=0 AND id=?) connect by prior Id=parent_Id

После моего теста, если наборы данных около 4000, SQL 1 работал немного лучше, чем SQL2. Но если обсуждение таблицы имеет очень большие данные, SQL 2 работал намного лучше, чем SQL1.

Когда в таблице 300 тысяч строк, план запроса сообщает, что стоимость SQL 1 равна 22126, стоимость SQL 2 - 6. SQL 1 включается в полную проверку таблицы или SQL 2 получает сканирование индекса диапазона.

Кто-нибудь может мне помочь объяснить, почему два SQL показывают разные результаты с разными номерами данных?

Более важно, я хочу, чтобы ваши ребята предложили улучшить производительность или другое решение?

Вот план запроса для SQL 1 , когда у нас 300 тысяч строк.

GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : staName52149
Tuning Task Owner  : CISCO
Tuning Task ID     : 54
Workload Type      : Single SQL Statement
Execution Count    : 1
Current Execution  : EXEC_44
Execution Type     : TUNE SQL
Scope              : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status  : COMPLETED
Started at         : 10/23/2011 23:23:31
Completed at       : 10/23/2011 23:23:59

-------------------------------------------------------------------------------
Schema Name: CISCO
SQL ID     : davhv6p4x6bu2
SQL Text   : SELECT * from discussion start with (parent_Id=0 AND id=6587)
             connect by prior Id=parent_Id

-------------------------------------------------------------------------------
FINDINGS SECTION (1 finding)
-------------------------------------------------------------------------------

1- SQL Profile Finding (see explain plans section below)
--------------------------------------------------------
  A potentially better execution plan was found for this statement.

  Recommendation (estimated benefit: 99.99%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name => 'staName52149',
            task_owner => 'CISCO', replace => TRUE);

  Validation results
  ------------------
  The SQL profile was tested by executing both its plan and the original plan
  and measuring their respective execution statistics. A plan may have been
  only partially executed if the other could be run to completion in less time.

                           Original Plan  With SQL Profile  % Improved
                           -------------  ----------------  ----------
  Completion Status:            COMPLETE          COMPLETE
  Elapsed Time(us):            14251990               121      99.99 %
  CPU Time(us):                 3463222                 0        100 %
  User I/O Time(us):           10821745                 0        100 %
  Buffer Gets:                   678361                 6      99.99 %
  Physical Read Requests:          1013                 0        100 %
  Physical Write Requests:         1081                 0        100 %
  Physical Read Bytes:        234168320                 0        100 %
  Physical Write Bytes:       223756288                 0        100 %
  Rows Processed:                     1                 1 
  Fetches:                            1                 1 
  Executions:                         1                 1 

  Notes
  -----
  1. The SQL profile plan was first executed to warm the buffer cache.
  2. Statistics for the SQL profile plan were averaged over next 9 executions.

-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------

1- Original
-----------
Plan hash value: 2811036397


------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |            |   348K|  2770M| 22162  (82)| 00:04:26 |
|*  1 |  CONNECT BY NO FILTERING WITH START-WITH|            |       |       |            |          |
|   2 |   TABLE ACCESS FULL                     | DISCUSSION |   348K|    79M|  4108   (1)| 00:00:50 |
------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   2 - SEL$2 / DISCUSSION@SEL$2

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("DISCUSSION"."PARENT_ID"=PRIOR NULL)
       filter("PARENT_ID"=0 AND "ID"=6587)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "DISCUSSION"."PARENT_ID"[NUMBER,22], "DISCUSSION"."ID"[NUMBER,22], STRDEF[22], 
       STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[4000], STRDEF[22], STRDEF[22], 
       STRDEF[150], STRDEF[7], STRDEF[7], STRDEF[4000], STRDEF[22], STRDEF[22], STRDEF[22], 
       STRDEF[4000], STRDEF[22], STRDEF[4000], STRDEF[7], STRDEF[22], STRDEF[32], STRDEF[22], 
       STRDEF[22], STRDEF[22], STRDEF[7], STRDEF[7], PRIOR NULL[22], LEVEL[4]
   2 - "ID"[NUMBER,22], "DISCUSSION"."CLASS"[NUMBER,22], "DISCUSSION"."SUBCLASS"[NUMBER,22], 
       "PARENT_ID"[NUMBER,22], "DISCUSSION"."ROOT_ID"[NUMBER,22], 
       "DISCUSSION"."MESSAGE"[VARCHAR2,4000], "DISCUSSION"."STATUS"[NUMBER,22], 
       "DISCUSSION"."PRIORITY"[NUMBER,22], "DISCUSSION"."AUTO_NUMBER"[VARCHAR2,150], 
       "DISCUSSION"."CREATE_DATE"[DATE,7], "DISCUSSION"."CLOSE_DATE"[DATE,7], 
       "DISCUSSION"."CLOSE_COMMENTS"[VARCHAR2,4000], "DISCUSSION"."ORGANIZATION"[NUMBER,22], 
       "DISCUSSION"."TYPE"[NUMBER,22], "DISCUSSION"."CREATOR"[NUMBER,22], 
       "DISCUSSION"."SUBJECT"[VARCHAR2,4000], "DISCUSSION"."REPLIES"[NUMBER,22], 
       "DISCUSSION"."NOTIFYLIST"[VARCHAR2,4000], "DISCUSSION"."LAST_REPLY_DATE"[DATE,7], 
       "DISCUSSION"."DELETE_FLAG"[NUMBER,22], "DISCUSSION"."FLAGS"[VARCHAR2,32], 
       "DISCUSSION"."PREVIEW_TEXT"[NUMBER,22], "DISCUSSION"."FILTER"[NUMBER,22], 
       "DISCUSSION"."OBJVERSION"[NUMBER,22], "DISCUSSION"."CREATED"[DATE,7], 
       "DISCUSSION"."LAST_UPD"[DATE,7]

2- Original With Adjusted Cost
------------------------------
Plan hash value: 2811036397


------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |            |   348K|  2770M| 22162  (82)| 00:04:26 |
|*  1 |  CONNECT BY NO FILTERING WITH START-WITH|            |       |       |            |          |
|   2 |   TABLE ACCESS FULL                     | DISCUSSION |   348K|    79M|  4108   (1)| 00:00:50 |
------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   2 - SEL$2 / DISCUSSION@SEL$2

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("DISCUSSION"."PARENT_ID"=PRIOR NULL)
       filter("PARENT_ID"=0 AND "ID"=6587)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "DISCUSSION"."PARENT_ID"[NUMBER,22], "DISCUSSION"."ID"[NUMBER,22], STRDEF[22], 
       STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[4000], STRDEF[22], STRDEF[22], 
       STRDEF[150], STRDEF[7], STRDEF[7], STRDEF[4000], STRDEF[22], STRDEF[22], STRDEF[22], 
       STRDEF[4000], STRDEF[22], STRDEF[4000], STRDEF[7], STRDEF[22], STRDEF[32], STRDEF[22], 
       STRDEF[22], STRDEF[22], STRDEF[7], STRDEF[7], PRIOR NULL[22], LEVEL[4]
   2 - "ID"[NUMBER,22], "DISCUSSION"."CLASS"[NUMBER,22], "DISCUSSION"."SUBCLASS"[NUMBER,22], 
       "PARENT_ID"[NUMBER,22], "DISCUSSION"."ROOT_ID"[NUMBER,22], 
       "DISCUSSION"."MESSAGE"[VARCHAR2,4000], "DISCUSSION"."STATUS"[NUMBER,22], 
       "DISCUSSION"."PRIORITY"[NUMBER,22], "DISCUSSION"."AUTO_NUMBER"[VARCHAR2,150], 
       "DISCUSSION"."CREATE_DATE"[DATE,7], "DISCUSSION"."CLOSE_DATE"[DATE,7], 
       "DISCUSSION"."CLOSE_COMMENTS"[VARCHAR2,4000], "DISCUSSION"."ORGANIZATION"[NUMBER,22], 
       "DISCUSSION"."TYPE"[NUMBER,22], "DISCUSSION"."CREATOR"[NUMBER,22], 
       "DISCUSSION"."SUBJECT"[VARCHAR2,4000], "DISCUSSION"."REPLIES"[NUMBER,22], 
       "DISCUSSION"."NOTIFYLIST"[VARCHAR2,4000], "DISCUSSION"."LAST_REPLY_DATE"[DATE,7], 
       "DISCUSSION"."DELETE_FLAG"[NUMBER,22], "DISCUSSION"."FLAGS"[VARCHAR2,32], 
       "DISCUSSION"."PREVIEW_TEXT"[NUMBER,22], "DISCUSSION"."FILTER"[NUMBER,22], 
       "DISCUSSION"."OBJVERSION"[NUMBER,22], "DISCUSSION"."CREATED"[DATE,7], 
       "DISCUSSION"."LAST_UPD"[DATE,7]

3- Using SQL Profile
--------------------
Plan hash value: 3458076016


---------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |     2 | 16686 |    11  (28)| 00:00:01 |
|*  1 |  CONNECT BY WITH FILTERING    |                   |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID | DISCUSSION        |     1 |   240 |     3   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN          | DISCUSSION_PK     |     1 |       |     2   (0)| 00:00:01 |
|   4 |   NESTED LOOPS                |                   |     1 |   253 |     5   (0)| 00:00:01 |
|   5 |    CONNECT BY PUMP            |                   |       |       |            |          |
|   6 |    TABLE ACCESS BY INDEX ROWID| DISCUSSION        |     1 |   240 |     3   (0)| 00:00:01 |
|*  7 |     INDEX RANGE SCAN          | DISC_IDX_PARENTID |     1 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   2 - SEL$4 / DISCUSSION@SEL$4
   3 - SEL$4 / DISCUSSION@SEL$4
   4 - SEL$3
   6 - SEL$3 / DISCUSSION@SEL$3
   7 - SEL$3 / DISCUSSION@SEL$3

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("DISCUSSION"."PARENT_ID"=PRIOR NULL)
   2 - filter("PARENT_ID"=0)
   3 - access("ID"=6587)
   7 - access("connect$_by$_pump$_002"."prior Id"="PARENT_ID")

Вот план запроса для SQL 2 , когда у нас 300 тысяч строк.

GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : staName64031
Tuning Task Owner  : CISCO
Tuning Task ID     : 55
Workload Type      : Single SQL Statement
Execution Count    : 1
Current Execution  : EXEC_45
Execution Type     : TUNE SQL
Scope              : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status  : COMPLETED
Started at         : 10/23/2011 23:30:22
Completed at       : 10/23/2011 23:30:26

-------------------------------------------------------------------------------
Schema Name: CISCO
SQL ID     : c741jfryv5m98
SQL Text   : SELECT * FROM (
                            SELECT * FROM discussion WHERE root_id = 6587 or
             id = 6587
                           )Start With (Parent_Id=0 And Id=6587) Connect By
             Prior Id=Parent_Id

-------------------------------------------------------------------------------
There are no recommendations to improve the statement.

-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------

1- Original
-----------
Plan hash value: 1202872009


-----------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |                 |     2 | 16686 |     6  (17)| 00:00:01 |
|*  1 |  CONNECT BY NO FILTERING WITH START-WITH|                 |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID           | DISCUSSION      |     2 |   480 |     5   (0)| 00:00:01 |
|   3 |    BITMAP CONVERSION TO ROWIDS          |                 |       |       |            |          |
|   4 |     BITMAP OR                           |                 |       |       |            |          |
|   5 |      BITMAP CONVERSION FROM ROWIDS      |                 |       |       |            |          |
|*  6 |       INDEX RANGE SCAN                  | DISCUSSION_PK   |       |       |     2   (0)| 00:00:01 |
|   7 |      BITMAP CONVERSION FROM ROWIDS      |                 |       |       |            |          |
|*  8 |       INDEX RANGE SCAN                  | DISC_IDX_ROOTID |       |       |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1       
   2 - SEL$E029B2FF / DISCUSSION@SEL$5

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("DISCUSSION"."PARENT_ID"=PRIOR NULL)
       filter("DISCUSSION"."PARENT_ID"=0 AND "DISCUSSION"."ID"=6587)
   6 - access("ID"=6587)
   8 - access("ROOT_ID"=6587)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "DISCUSSION"."PARENT_ID"[NUMBER,22], "DISCUSSION"."ID"[NUMBER,22], STRDEF[22], STRDEF[22], 
       STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[4000], STRDEF[22], STRDEF[22], STRDEF[150], STRDEF[7], 
       STRDEF[7], STRDEF[4000], STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[4000], STRDEF[22], 
       STRDEF[4000], STRDEF[7], STRDEF[22], STRDEF[32], STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[7], 
       STRDEF[7], PRIOR NULL[22], LEVEL[4]
   2 - "DISCUSSION".ROWID[ROWID,10], "ID"[NUMBER,22], "DISCUSSION"."CLASS"[NUMBER,22], 
       "DISCUSSION"."SUBCLASS"[NUMBER,22], "DISCUSSION"."PARENT_ID"[NUMBER,22], "ROOT_ID"[NUMBER,22], 
       "DISCUSSION"."MESSAGE"[VARCHAR2,4000], "DISCUSSION"."STATUS"[NUMBER,22], 
       "DISCUSSION"."PRIORITY"[NUMBER,22], "DISCUSSION"."AUTO_NUMBER"[VARCHAR2,150], 
       "DISCUSSION"."CREATE_DATE"[DATE,7], "DISCUSSION"."CLOSE_DATE"[DATE,7], 
       "DISCUSSION"."CLOSE_COMMENTS"[VARCHAR2,4000], "DISCUSSION"."ORGANIZATION"[NUMBER,22], 
       "DISCUSSION"."TYPE"[NUMBER,22], "DISCUSSION"."CREATOR"[NUMBER,22], 
       "DISCUSSION"."SUBJECT"[VARCHAR2,4000], "DISCUSSION"."REPLIES"[NUMBER,22], 
       "DISCUSSION"."NOTIFYLIST"[VARCHAR2,4000], "DISCUSSION"."LAST_REPLY_DATE"[DATE,7], 
       "DISCUSSION"."DELETE_FLAG"[NUMBER,22], "DISCUSSION"."FLAGS"[VARCHAR2,32], 
       "DISCUSSION"."PREVIEW_TEXT"[NUMBER,22], "DISCUSSION"."FILTER"[NUMBER,22], 
       "DISCUSSION"."OBJVERSION"[NUMBER,22], "DISCUSSION"."CREATED"[DATE,7], 
       "DISCUSSION"."LAST_UPD"[DATE,7]
   3 - "DISCUSSION".ROWID[ROWID,10]
   4 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 16116]
   5 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 16116]
   6 - "DISCUSSION".ROWID[ROWID,10]
   7 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 16116]
   8 - "DISCUSSION".ROWID[ROWID,10]

-------------------------------------------------------------------------------

Ответы [ 2 ]

1 голос
/ 23 октября 2011

Вы просто хотите объяснить, что вы видите?Или предложения по улучшению этого?

Предполагая первое:

С SQL1 база данных должна найти корневой элемент, затем все дочерние элементы, затем дочерние элементы этого и так далее.Он всегда должен работать с полной таблицей, поскольку общий фильтр недоступен.Она обрабатывает всю таблицу (или полный индекс).

В SQL2 первое, что может сделать система базы данных, - это уменьшить подмножество строк, над которыми она работает.

Разница в том,маленький (или даже в противоположных направлениях) для маленьких столов.Если ваша полная таблица помещается в один или два блока, фильтрация базы данных не очень полезна.В любом случае он должен вытянуть блоки в память.Даже больше, если он использует индексный доступ.Но с огромной таблицей с (просто составлением чисел) 100 блоков, уменьшающих их до 5, сначала следует обратиться к 2 индексным блокам.

Обновление: некоторые идеи для настройки этого:

  • Я бы попытался избавиться от или в SQL2.Это должно быть возможно, когда корневая запись имеет свой собственный идентификатор как root_id

  • Предполагая, что вам действительно нужен уровень, который, я думаю, обеспечивается иерархическим запросом, вы можете предварительно рассчитать уровень встолбец таблицы.Затем вы можете удалить предложение connect by, что приведет к очень простому запросу.Конечно, если это работает, многое зависит от остальной части вашего приложения.

  • Вам нужны все строки быстро?Или первые строки?Вы можете попытаться предоставить соответствующую подсказку (http://download.oracle.com/docs/cd/B10501_01/server.920/a96533/hintsref.htm#4924)

0 голосов
/ 23 октября 2011

В основном, первая строка ищет кандидатов для построения дерева потоков во всем «лесу» (т. Е. Во всей таблице обсуждений, т. Е. 300 000 строк. Конечно, она пытается сделать все возможное, но пространство возможных решений большой.

В запросе 2 в основном говорится: «получить только те части дерева, которые мне интересны, - которые вы можете идентифицировать благодаря идентификатору корня и построить из него целое дерево».

Посмотрите на количество строк в двух планах выполнения и убедитесь сами в огромной разнице в пространстве поиска.

В случае, если ваше пространство поиска было уже небольшим (скажем, 2 обсуждения с всего 4-5 элементами в каждом), время для второго запроса будет доминировать при поиске небольшого числа строк в маленькой таблице ... это объясняет почему второй запрос может быть «хуже» для почти пустой таблицы.

...