SQL.RU
 client/server technologies
Peoplemind  
 Главная | Документация | Статьи | Книги | Форум | Опросы | Рассылка | Работа | Поиск | FAQ |

Добро пожаловать в форум, Guest  >>  Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Oracle Новый топик  Ответить
 О дружбе CBO с partitioned tables   [new]
Andrew Max
Member

Откуда:
Сообщений: 1034
Наткнулся на простую ситуацию с секционированными таблицами, в которой оптимизатор Oracle 9i ведет себя, на мой взгляд, просто невменяемо.
Тестовый пример и пояснения – ниже.

SQL> select * from v$version where rownum = 1;

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.6.0 - Production

SQL> create table part
  2    (x number,
  3     key_col number,
  4     padding varchar2(500))
  5  partition by list(x)
  6    (
  7     partition p_0 values(0),
  8     partition p_1 values(1),
  9     partition p_2 values(2),
 10     partition p_3 values(3),
 11     partition p_4 values(4),
 12     partition p_5 values(5),
 13     partition p_6 values(6),
 14     partition p_7 values(7),
 15     partition p_8 values(8),
 16     partition p_9 values(9));

Table created.

SQL> insert into part
  2  select mod(rownum, 10),
  3         rownum,
  4         rpad('*', 500, '*')
  5    from dual
  6  connect by level <= 100000;

100000 rows created.

SQL> create unique index idx_part on part(x, key_col) local;

Index created.

SQL> alter table part add constraint idx_part primary key(x, key_col);

Table altered.

SQL> begin
  2    dbms_stats.gather_table_stats(
  3      ownname => user,
  4      tabname => 'PART',
  5      estimate_percent => null,
  6      method_opt => 'FOR ALL COLUMNS SIZE AUTO',
  7      granularity => 'ALL',
  8      cascade => true);
  9  end;
 10  /

PL/SQL procedure successfully completed.

Итак, мы создали секционированную таблицу с 10 секциями по 10000 строк каждая. Кроме того, мы создали уникальный локальный индекс с префиксом.
Посмотрим на план следующего простого запроса:

SQL> explain plan for
  2  select * from part
  3    where x = :bx;

Explained.

SQL> @utlxpls

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------
| Id  | Operation                          |  Name       | Rows  | Bytes | Cost (%CPU)| Pstart| Pstop |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |             | 10000 |  4960K|   802   (3)|       |       |
|   1 |  PARTITION LIST SINGLE             |             |       |       |            |   KEY |   KEY |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID| PART        | 10000 |  4960K|   802   (3)|   KEY |   KEY |
|*  3 |    INDEX RANGE SCAN                | IDX_PART    | 10000 |       |    32  (19)|   KEY |   KEY |
-------------------------------------------------------------------------------------------------------

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

   3 - access("PART"."X"=TO_NUMBER(:Z))

14 rows selected.

Немедленно возникает следующий вопрос: какой смысл сканировать целую секцию индекса, если единственный access-предикат здесь – это access("PART"."X"=TO_NUMBER(:Z))? Ведь совершенно ясно, что при этом будут обработаны все листовые блоки этой секции индекса, что, по идее, должно быть намного дороже обычного сканирования одной секции таблицы.

Однако, вот план запроса, в котором мы вынуждаем оптимизатор не использовать INDEX access path:

SQL> explain plan for
  2  select /*+ NO_INDEX(PART) */ * from part
  3    where x = :bx;

Explained.

SQL> @utlxpls

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------
| Id  | Operation             |  Name       | Rows  | Bytes | Cost (%CPU)| Pstart| Pstop |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             | 10000 |  4960K|  3048   (7)|       |       |
|   1 |  PARTITION LIST SINGLE|             |       |       |            |   KEY |   KEY |
|*  2 |   TABLE ACCESS FULL   | PART        | 10000 |  4960K|  3048   (7)|   KEY |   KEY |
------------------------------------------------------------------------------------------

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

   2 - filter("PART"."X"=TO_NUMBER(:Z))

13 rows selected.

Значение COST в этом плане весьма настораживает: 3048 – не слишком ли дорого для сканирования единственной секции? Чтобы разобраться в деталях, лучше включить трассировку с event 10053. CPU costing для упрощения последующих расчетов я отключил, эта компонента стоимости все равно не оказывает решающего влияния на суть проблемы.

SQL> alter session set tracefile_identifier = partcost;

Session altered.

SQL> alter session set "_optimizer_cost_model" = IO;

Session altered.

SQL> alter session set events='10053 trace name context forever, level 1';

Session altered.

SQL> explain plan for
  2  select * from part
  3    where x = :bx;

Explained.

SQL> alter session set events '10053 trace name context off';

Session altered.

Вот секция из trace-файла, которая нас интересует:

***************************************
SINGLE TABLE ACCESS PATH
Column:          X  Col#: 1      Table: PART   Alias: PART
    NDV: 10        NULLS: 0         DENS: 1.0000e-001 LO:  0  HI: 9
    NO HISTOGRAM: #BKT: 1 #VAL: 2
  TABLE: PART     ORIG CDN: 100000  ROUNDED CDN: 10000  CMPTD CDN: 10000
  Access path: tsc  Resc:  2852  Resp:  2852
  Skip scan: ss-sel 0  andv 10000  
    ss cost 10000 
    index io scan cost 0 
  Access path: index (scan)
      Index: IDX_PART
  TABLE: PART
      RSC_CPU: 0   RSC_IO: 779
  IX_SEL:  1.0000e-001  TB_SEL:  1.0000e-001
  BEST_CST: 779.00  PATH: 4  Degree:  1
***************************************

Для того, чтобы повторить расчет COST вслед за Oracle, нам потребуются следующие статистические данные:

SQL> select pname, pval1, pval2 from sys.aux_stats$;

PNAME                               PVAL1 PVAL2
------------------------------ ---------- -----------------
STATUS                                    COMPLETED
DSTART                                    12-24-2004 19:22
DSTOP                                     12-24-2004 20:26
FLAGS                                   1
SREADTIM                             ,556
MREADTIM                            2,754
CPUSPEED                              771
MBRC                                   13
MAXTHR                           49090560
SLAVETHR                               -1

10 строк выбрано.

SQL> select blocks from user_tables where table_name = 'PART';

    BLOCKS
----------
      7480

SQL> select blevel, leaf_blocks, clustering_factor
  2    from user_indexes
  3   where index_name = 'IDX_PART';

    BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
---------- ----------- -----------------
         1         249              7530

Для расчет стоимости используем следующую известную формулу:

Cost = (#SRds * sreadtim +
#MRds * mreadtim +
#CPUCycles / cpuspeed ) / sreadtim

В случае доступа по индексу все чтения – одноблочные, т.е:

Cost_IRS = #SRds

Далее, согласно известной формуле, получаем:

Cost_IRS = blevel + ceil(IX_SEL * leaf_blks) + ceil(TB_SEL * clu_f) = 
1 + ceil(0.1 * 249) + (0.1 * 7530) = 779

Для случая простого сканирования секции формула для расчета cost приобретает вид:

Cost_FTS = #MRds * mreadtim / sreadtim

Оказывается, в этом случае при расчете количества многоблочных чтений Oracle почему-то предполагает, что будут прочитаны все блоки всех секций таблицы. Тот факт, что во время выполнения запрос выберет данные максимум одной партиции, попросту игнорируется. Предлагаю убедиться:

Cost_FTS = ceil((7480 / 13) * 2.754 / 0.556) = 2851.

С учетом значения параметра _TABLE_SCAN_COST_PLUS_ONE = TRUE, получим Cost_FTS = 2852.

Мне известно, что используя bind variable peeking, оптимизатор девятки может подсмотреть значение :bx и построить хороший план выполнения. Тем не менее, как быть в ситуации, когда bind variable peeking ничем не поможет? Например, если вместо :bx используется SYS_CONTEXT?

Для данного конкретного примера последствия выбора неправильного плана не столь критичны:

SQL> set autot traceonly stat
SQL>
SQL> select * from part
  2    where x = (select 0 from dual);

10000 строк выбрано.


Статистика
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2066  consistent gets
        ...
      10000  rows processed

SQL> select /*+ NO_INDEX(PART) */ * from part
  2    where x = (select 0 from dual);

10000 строк выбрано.


Статистика
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1375  consistent gets
        ...
      10000  rows processed

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

Справедливости ради, нужно отметить, что десятка ведет себя гораздо адекватнее, но это слабо утешает: проблему хотелось бы как-то решить именно для девятой версии.

У кого-либо есть соображения или материал для чтения по этому поводу?
Заранее признателен.
1 авг 06, 15:43    [2951377] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
в вашем примере как раз таки все более-менее гладко получается :)
1. PARTITION LIST SINGLE - определен в обоих планов, индекс локальный
2. расчет стоимости для SINGLE PARTITION происходит с учетом статистики по полной таблицы и индексу, а не по секторам - тоже в обоих случаях.
какие тут еще могут быть варианты при неизвестных биндах?
- брать статистику по "наибольшему" сектору
- брать "среднюю температуту по больнице"
- брать статистику по сектору, подсмотренному для первого значения
или как Вы это видите ?
1 авг 06, 18:43    [2952573] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
Oleg Afanasiev
Member

Откуда: Киев
Сообщений: 3670
сильно напоминает ситуацию, описанную у льюиса в
Cost-Based Oracle
Fundamentals

на странице 34 есть глава Partitioning


-----------------------
Вечны налоги,
Смерть и потеря данных.
Что на этот раз?
Картинка с другого сайта.
1 авг 06, 19:06    [2952681] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
Andrew Max
Member

Откуда:
Сообщений: 1034
DВА
в вашем примере как раз таки все более-менее гладко получается :)

Это Вы называете гладко???

Еще раз: суть в том, что при оценке доступа без индекса Oracle считает, что ему придется считывать все блоки из таблицы, во всех без исключения секциях. Это, по-Вашему, нормально и разумно?

DВА
какие тут еще могут быть варианты при неизвестных биндах?
...
- брать "среднюю температуту по больнице"
...

А почему бы и нет? Согласен, вариант тоже спорный, но это все же лучше, чем полагать, что в runtime будут считываться все блоки.

Вот трейс с десятки, только тестовые данные немного изменены.
Очень похоже на то, что десятка поступает именно так:

SQL> select x, count(*) from part group by x;

         X   COUNT(*)
---------- ----------
         0       1000
         1       2000
         2       3000
         3       4000
         4       5000
         5       6000
         6       7000
         7       8000
         8       9000
         9      10000

10 строк выбрано.

SQL> alter session set tracefile_identifier = partcost;

Сеанс изменен.

SQL> alter session set events='10053 trace name context forever, level 1';

Сеанс изменен.

SQL> explain plan for
  2  select * from part
  3    where x = :bx;

Объяснено.

SQL> alter session set events '10053 trace name context off';

Сеанс изменен.

SQL> explain plan for
  2  select * from part
  3    where x = :bx;

Объяснено.

SQL> @utlxpls

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------
Plan hash value: 3671374662

----------------------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |  5500 |  2723K|   100  (17)| 00:00:01 |       |       |
|   1 |  PARTITION LIST SINGLE|      |  5500 |  2723K|   100  (17)| 00:00:01 |   KEY |   KEY |
|   2 |   TABLE ACCESS FULL   | PART |  5500 |  2723K|   100  (17)| 00:00:01 |   KEY |   KEY |
----------------------------------------------------------------------------------------------

9 строк выбрано.

SQL> select blocks from user_tables where table_name = 'PART';

    BLOCKS
----------
      4217

SQL> select * from sys.aux_stats$;

SNAME                          PNAME                               PVAL1 PVAL2
------------------------------ ------------------------------ ---------- ----------------------------
SYSSTATS_INFO                  STATUS                                    COMPLETED
SYSSTATS_INFO                  DSTART                                    04-06-2006 18:39
SYSSTATS_INFO                  DSTOP                                     04-06-2006 19:19
SYSSTATS_INFO                  FLAGS                                   1
SYSSTATS_MAIN                  CPUSPEEDNW                     1128,88601
SYSSTATS_MAIN                  IOSEEKTIM                              10
SYSSTATS_MAIN                  IOTFRSPEED                           4096
SYSSTATS_MAIN                  SREADTIM                             ,252
SYSSTATS_MAIN                  MREADTIM                             ,729
SYSSTATS_MAIN                  CPUSPEED                             1125
SYSSTATS_MAIN                  MBRC                                   15
SYSSTATS_MAIN                  MAXTHR                            6895616
SYSSTATS_MAIN                  SLAVETHR

13 строк выбрано.

SQL>

Как видим, значение COST гораздо ниже.
Вот, собственно, интересующая нас часть trace-файла:
***************************************
SINGLE TABLE ACCESS PATH
  COLUMN:          X(NUMBER)  Col#: 1      Table: PART   Alias: PART
    Size: 3  NDV: 10  Nulls: 0  Density: 1.0000e-01 Min: 0  Max: 9
    No Histogram: #BKT: 1
        (1 uncompressed buckets and 2 endpoint values)
  TABLE: PART  Alias: PART     
    Original Card: 55000   Rounded: 5500  Computed: 5500.00  Non Adjusted: 5500.00
  Access Path: table-scan  Resc:  100  Resp:  100
  Access Path: index (skip-scan)
    ss sel 1.0000e-01  andv 5500  
    ss cost 5500 vs. index scan io cost 0 
    Skip Scan rejected
  Access Path: index (scan)
    Index: IDX_PART
    rsc_cpu: 5057669   rsc_io: 409
    ix_sel:  1.0000e-01    ix_sel_with_filters:  1.0000e-01
  BEST_CST: 100.20  PATH: 2  Degree:  1
***************************************

Итак, в таблице 4217 блока, но 10-й Oracle предполагает, что сканироваться будет десятая часть от их числа.

Вот расчет стоимости, который это подтверждает:
SQL> select ceil((4217 * 0.1/15) * (0.729/0.252)) + 1 IOCost from dual;

    IOCOST
----------
        83

Остальные 17 единиц добавляет CPU Cost (так, как и показал EXPLAIN PLAN).

Какие-то цифры слишком круглые получись, блин... :)

Oleg Afanasiev
сильно напоминает ситуацию, описанную у льюиса в
Cost-Based Oracle
Fundamentals

на странице 34 есть глава Partitioning

Что-то не припомню похожей ситуации, хотя эту главу я читал давно, мог уже и подзабыть.
Спасибо, сейчас просмотрю еще раз...
1 авг 06, 20:06    [2952817] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
Andrew Max
Еще раз: суть в том, что при оценке доступа без индекса Oracle считает, что ему придется считывать все блоки из таблицы, во всех без исключения секциях. Это, по-Вашему, нормально и разумно?

скажем так, при том, что и при индексном доступе для PARTITION LIST SINGLE он так же берет статистику по полному индексу, как вы сами и показали - не так уж чтобы и совершенно глупо :)
Упрощение, которое в десятке похоже подкорректировали с учетом разного вклада в стоимость плана сканирования блоков таблицы и индекса.
Кстати, интересно как получилась стоимость в десятке для индексного доступа rsc_io: 409 ? нет под рукой десятки, киньте плиз статистику для индекса
2 авг 06, 20:59    [2957684] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
DВА
скажем так, при том, что и при индексном доступе для PARTITION LIST SINGLE он так же берет статистику по полному индексу, как вы сами и показали - не так уж чтобы и совершенно глупо :)

Магия слов...
Ошибка оценки стоимости индексного доступа при равномерном наполнении секций весьма невелика, в то время как для FTS она завышена в Npart раз, где Npart - количество разделов.
Просто потому, что количество блоков в таблице - величина кумулятивная, а оценка селективности - усредненная
2 авг 06, 21:38    [2957720] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
contr
DВА
скажем так, при том, что и при индексном доступе для PARTITION LIST SINGLE он так же берет статистику по полному индексу, как вы сами и показали - не так уж чтобы и совершенно глупо :)

Магия слов...
Ошибка оценки стоимости индексного доступа при равномерном наполнении секций весьма невелика, в то время как для FTS она завышена в Npart раз, где Npart - количество разделов.
Просто потому, что количество блоков в таблице - величина кумулятивная, а оценка селективности - усредненная

а leaf_blks типа уже не учитываем?
2 авг 06, 21:47    [2957728] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
Повторив эксперимент Andrew Max, но изменив GRANUILARITY с 'ALL' на 'PARTITION', я получил вполне ожидаемый результат:
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
| Id  | Operation             |  Name       | Rows  | Bytes | Cost  | Pstart| Ps
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             | 10000 |  4951K|  1623 |       |
|   1 |  PARTITION LIST SINGLE|             |       |       |       |   KEY |
|*  2 |   TABLE ACCESS FULL   |     PART    | 10000 |  4951K|  1623 |   KEY |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("    PART"."X"=TO_NUMBER(:Z))
Note: cpu costing is off

15 rows selected
Мораль: granularity придуман не зря :)
2 авг 06, 21:53    [2957736] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
DВА
а leaf_blks типа уже не учитываем?

Сделаем 20 разделов.
Стоимость FTS возрастет в 2 раза, в то время как стоимость индексного останется прежней - селективность-то должна в два раза уменьшится, скомпенсировав удвоение leaf...
2 авг 06, 21:55    [2957738] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
это на девятке?
не думаю, что у Andrew Max не хватало статистики :)
2 авг 06, 22:00    [2957742] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
contr
DВА
а leaf_blks типа уже не учитываем?

Сделаем 20 разделов.
Стоимость FTS возрастет в 2 раза, в то время как стоимость индексного останется прежней - селективность-то должна в два раза уменьшится, скомпенсировав удвоение leaf...

н-да, для уникального индекса токо так...
2 авг 06, 22:09    [2957748] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
DВА
это на девятке?
не думаю, что у Andrew Max не хватало статистики :)

На девятке. Но я поторопился - при более детальном рассмотрении оказалось, что granularity ('GLOBAL','PARTITION') не оказал никакого влияния на оценку стоимости.
В моем случае
Индексный - 1883
FTS - 1623

9.2.0.7
2 авг 06, 22:10    [2957750] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
а запустите плиз с events 10053
у меня на 9.2.0.6 расчеты стоимости аналогичны расчетам автора
2 авг 06, 22:16    [2957755] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
DВА
а запустите плиз с events 10053
у меня на 9.2.0.6 расчеты стоимости аналогичны расчетам автора

У меня просто параметры сервера другие.
А так все аналогично:
Введя небольшую неравномерность в распределение данных посредством
insert into part
select mod(mod(rownum,15), 10),
       rownum,
       rpad('*', 500, '*')
  from dual
connect by level <= 100000;

При granularity='PARTITION' получил:

-- Мелкий раздел:
SQL> explain plan for select /*+ full(part)*/ * from part where x = 8;

Explained

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  | Pstart| Pstop |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |  6667 |  3307K|   109 |       |       |
|   1 |  TABLE ACCESS FULL   | PART        |  6667 |  3307K|   109 |     9 |     9 |
------------------------------------------------------------------------------------
Note: cpu costing is off

9 rows selected

-- Толстый раздел:

SQL> explain plan for select /*+ full(part)*/ * from part where x = 0;

Explained

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  | Pstart| Pstop |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             | 13333 |  6601K|   218 |       |       |
|   1 |  TABLE ACCESS FULL   | PART        | 13333 |  6601K|   218 |     1 |     1 |
------------------------------------------------------------------------------------
Note: cpu costing is off

-- По глобальной статистике:
SQL> explain plan for select /*+ full(part)*/ * from part where x = :bx;

Explained

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
| Id  | Operation             |  Name       | Rows  | Bytes | Cost  | Pstart| Pstop |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             | 10000 |  4951K|  1623 |       |       |
|   1 |  PARTITION LIST SINGLE|             |       |       |       |   KEY |   KEY |
|*  2 |   TABLE ACCESS FULL   | PART        | 10000 |  4951K|  1623 |   KEY |   KEY |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("PART"."X"=TO_NUMBER(:Z))
Note: cpu costing is off

-- Стоимость завышена в 10 раз относительно (218+109)/2 = 163.5
------------------------------------------------------
-- Индекс:
-- Мелкий раздел:

SQL> explain plan for select /*+ index(part idx_part)*/ * from part where x = 8;

Explained

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
| Id  | Operation                         |  Name         | Rows  | Bytes | Cost  | Pstart| Pstop |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |               |  6667 |  3307K|  1252 |       |       |
|   1 |  TABLE ACCESS BY LOCAL INDEX ROWID| PART          |  6667 |  3307K|  1252 |     9 |     9 |
|*  2 |   INDEX RANGE SCAN                | IDX_PART      |  6667 |       |    36 |     9 |     9 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("PART"."X"=8)
Note: cpu costing is off

-- Толстый раздел:

SQL> explain plan for select /*+ index(part idx_part)*/ * from part where x = 0;

Explained

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
| Id  | Operation                         |  Name         | Rows  | Bytes | Cost  | Pstart| Pstop |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |               | 13333 |  6601K|  2509 |       |       |
|   1 |  TABLE ACCESS BY LOCAL INDEX ROWID| PART          | 13333 |  6601K|  2509 |     1 |     1 |
|*  2 |   INDEX RANGE SCAN                | IDX_PART      | 13333 |       |    66 |     1 |     1 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("PART"."X"=0)
Note: cpu costing is off

-- глобальная статистика:

SQL> explain plan for select /*+ index(part idx_part)*/ * from part where x = :bx;

Explained

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
| Id  | Operation                          |  Name         | Rows  | Bytes | Cost  | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |               | 10000 |  4951K|  1883 |       |       |
|   1 |  PARTITION LIST SINGLE             |               |       |       |       |   KEY |   KEY |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID| PART          | 10000 |  4951K|  1883 |   KEY |   KEY |
|*  3 |    INDEX RANGE SCAN                | IDX_PART      | 10000 |       |    53 |   KEY |   KEY |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("PART"."X"=TO_NUMBER(:Z))
Note: cpu costing is off

-- Стоимость похожа на (2509+1252)/2=1880.5

т.е. поведение идентично авторскому (оценка стоимости индексного доступа адекватна, FTS - завышена в Npart=10 раз).
2 авг 06, 22:44    [2957789] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
contr
т.е. поведение идентично авторскому (оценка стоимости индексного доступа адекватна, FTS - завышена в Npart=10 раз).

При этом для FTS количество строк, разумеется, считает правильно. Нахал :)
2 авг 06, 22:51    [2957795] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
Гыыыы... Самый смешной результат получился после
begin
  dbms_stats.delete_table_stats(
      ownname => user
    , tabname => 'PART'
    , cascade_parts => true
    , cascade_columns => true
    , cascade_indexes => true
    );
end;
/

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
| Id  | Operation             |  Name       | Rows  | Bytes | Cost  | Pstart| Pstop |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             |   687 |   186K|   164 |       |       |
|   1 |  PARTITION LIST SINGLE|             |       |       |       |   KEY |   KEY |
|*  2 |   TABLE ACCESS FULL   |     PART    |   687 |   186K|   164 |   KEY |   KEY |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("    PART"."X"=TO_NUMBER(:Z))
Оценка Rows никакая, зато cost выровнялся:
rows   cost
455 109
918 218
687 164

Мда... И вправду что-ли начать CBO изучать?
2 авг 06, 23:02    [2957803] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
DВА
Member

Откуда:
Сообщений: 2584
contr

Мда... И вправду что-ли начать CBO изучать?

«Это невозможно понять, это можно только запомнить» (с)
2 авг 06, 23:08    [2957811] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
Andrew Max
Member

Откуда:
Сообщений: 1034
Вау! Столько ответов появилось... я уже и не ожидал, честно говоря. Спасибо.
contr
оценка стоимости индексного доступа адекватна, FTS - завышена в Npart=10 раз

contr
При этом для FTS количество строк, разумеется, считает правильно. Нахал :)

Именно. Это как раз то, о чем я говорил.

В принципе, девятку в какой-то мере спасает bind variable peeking (если он возможен, разумеется).

SQL> explain plan for
  2  select * from part
  3    where x = :bx;

Объяснено.

SQL> @utlxpls

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------
| Id  | Operation                          |  Name       | Rows  | Bytes | Cost (%CPU)| Pstart| Pstop |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |             | 10000 |  4960K|   794   (1)|       |       |
|   1 |  PARTITION LIST SINGLE             |             |       |       |            |   KEY |   KEY |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID| PART        | 10000 |  4960K|   794   (1)|   KEY |   KEY |
|*  3 |    INDEX RANGE SCAN                | IDX_PART    | 10000 |       |    28   (8)|   KEY |   KEY |
-------------------------------------------------------------------------------------------------------

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

   3 - access("PART"."X"=TO_NUMBER(:Z))

14 строк выбрано.

Но:

SQL> var bx number
SQL>
SQL> exec :bx := 1

Процедура PL/SQL успешно завершена.

SQL> select /* tagged */ * from part
  2    where x = :bx;

...
...

10000 строк выбрано.

SQL> column operation format a30
SQL> column options format a30
SQL> column object_name format a30
SQL>
SQL> select p.id,
  2         lpad(' ', p.depth) || p.operation operation,
  3         p.options,
  4         p.object_name
  5    from v$sql s,
  6         v$sql_plan p
  7   where s.sql_text like 'select %af_src_comm_1%'
  8     and s.address = p.address
  9     and s.hash_value = p.hash_value
 10   order by p.id;

        ID OPERATION                      OPTIONS                        OBJECT_NAME
---------- ------------------------------ ------------------------------ -------------
         0 SELECT STATEMENT
         1  PARTITION LIST                SINGLE
         2   TABLE ACCESS                 FULL                           PART

SQL>

Однако, как я уже говорил, ситуация остается малоутешительной, если bind variable peeking бесполезен.
Конкретный пример: в нашей системе есть секционированная таблица с подобным локальным индексом. Кроме того, имеется представление, в условии WHERE которого фигурирует единственный предикат вида:

... where x = SYS_CONTEXT('USERCTX', 'VAL')

Ситуация, как я понимаю, досточно типовая, никакой экзотики. Однако получается, что в девятке все запросы вида SELECT * FROM <это_самое_view> выполняются не самым, мягко говоря, эффективным образом.

DBA
Кстати, интересно как получилась стоимость в десятке для индексного доступа rsc_io: 409 ? нет под рукой десятки, киньте плиз статистику для индекса

Постараюсь завтра найти время, сейчас под рукой тоже нет десятки :)
3 авг 06, 00:56    [2957928] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
Andrew Max
Member

Откуда:
Сообщений: 1034
SQL.RU forum
7 where s.sql_text like 'select %af_src_comm_1%'

Бр-р-р-р... Нет, я такой абракадабры не писал, если что.
Должно было быть следующее, разумеется:

  7   where s.sql_text like 'select /* tagged */%'
3 авг 06, 01:00    [2957932] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
Andrew Max
Ситуация, как я понимаю, досточно типовая, никакой экзотики. Однако получается, что в девятке все запросы вида SELECT * FROM <это_самое_view> выполняются не самым, мягко говоря, эффективным образом.

Andrew, ситуация интересна, но некритична.
Можно прибить хинтом, можно построить outline.

2SY: Я всего лишь просил убрать подстроку и техническую переписку.
3 авг 06, 01:43    [2957972] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
SY
Member

Откуда: Middlebury, CT USA
Сообщений: 4773
contr
2SY: Я всего лишь просил убрать подстроку и техническую переписку.


Yes, sir Картинка с другого сайта..

SY.
3 авг 06, 05:10    [2958083] Ответить | Цитировать    Сообщить модератору

 Re: О дружбе CBO с partitioned tables   [new]
contr
Member

Откуда:
Сообщений: 1897
SY
Yes, sir

Ну прямо так-то уж не стоит, но в любом случае - спасибо.
3 авг 06, 05:14    [2958085] Ответить | Цитировать    Сообщить модератору

Все форумы / Oracle Ответить
Rambler's Top100 Powered by ActualForum 1.5.2 Copyright (c) Alex Sibilev 2000-2010