29 августа 2008 г.

Тюнинг базы на Oracle 10g

Давно не писала в блоге, сегодня решила написать хоть что-нить. Раньше всегда пыталась писать в блоге чисто с технической точки зрения, не включая мое личное отношение к разным фичерам и особенностям Оракла. Мне кажется в последующих моих постингах будет больше "меня".

Недавно оптимизировала базу на Oracle 10.2.0.3, которая используется для CRM-системы. Провели нагрузочное тестирование, по результатам которого выяснили, что на 300-400 одновременных пользователях активно выполняющих те или иные операции, база начинает виснуть. Удалось поднять производительность более чем в 4 раза.

Если честно, очень понравилось оптимизировать базу на 10ке: даже самые незначительные новые особенности облегчают работу по оптимизации. Например, динамические представления V$SESSION и V$SQL значительно расширены и содержат много полезной и необходимой информации.

В Oracle 10g в V$SESSION можно посмотреть ожидания каждой сессии, не связывая его с V$SESSION_WAIT. То есть V$SESSION содержит все столбцы V$SESSION_WAIT:

SQL> set pagesize 100
SQL> column event# format 999
SQL> column wait_class format A15
SQL> column event format A30
SQL> select event#, event, wait_class, state
2 from v$session where username is not null;

EVENT# EVENT WAIT_CLASS STATE
------ ------------------------------ --------------- -------------------
256 SQL*Net message from client Idle WAITING
30 Backup: sbtwrite2 Administrative WAITING
256 SQL*Net message from client Idle WAITING
256 SQL*Net message from client Idle WAITING
252 SQL*Net message to client Network WAITED SHORT TIME

SQL>

5 августа 2008 г.

Ожидания при чтении и записи LOBов

Вчера я пыталась создать копию таблицы, у которой был столбец типа BLOB. Заливка данных повисла, и я оставила ее висеть до утра :D К тому же это было не к спеху. Таблица была средненькая: около 700 тыс. записей, весит 2 гига. Знала, что это из-за LOBа так висит, и сегодня решила докапаться до истины. И так все по-порядку.

Сперва пыталась создать копию таблицы обычным "create table ... as select", думала средненькая таблица скопируется быстро, но не тут-то было. Висела больше часа, пока не кильнула сессию. С опцией nologging тоже самое, через несколько часов и ее убила:

create table new_blob_table as select * from orig_blob_table;

create table new_blob_table nologging as select * from orig_blob_table;
А direct path insert командой insert /*+ append */ тоже висела пару часов, и я, добрая, оставила ее висеть до утра:
create table new_blob_table as select * from orig_blob_table where 1=2;

insert /*+ append */ into new_blob_table select * from orig_blob_table;
Во время всех этих попыток, сессия ожидала события direct path write (lob):
SQL> select event
from v$session_wait
where sid=969;

EVENT
-------------------------
direct path write (lob)
При прямой записи - direct path write - запись происходит В ОБХОД буферного кеша прямо в файлы данных. Запись выполняется серверным процессом (а не процессом DBWR, как в обычной записи), который в pga сессии подготавливает блоки и записывать их за отметкой HWM.

Бурлесон перечисляет список операций, которые могут выполнять операцию прямой записи (direct path write) и вызывать соответствущие ожидания:
Operations that could perform direct path writes include when
1) a sort goes to disk,
2) during parallel DML operations,
3) direct-path inserts,
4) parallel create table as select, and
5) some LOB operations.
Наш случай - последний, связанный с LOBами. Вот что пишут в документации Oracle о прямой записи (direct path write) и параметре CACHE/NOCACHE LOB-сегментов:
With the CACHE option, LOB data reads show up as wait event 'db file sequential read', writes are performed by the DBWR process.

With the NOCACHE option, LOB data reads/writes show up as wait events:
direct path read (lob)/direct path write (lob).

Corresponding statistics are:
physical reads direct (lob) and physical writes direct (lob).
О параметре CACHE BLOB-сегментов, я писала недавно здесь. Получается, что когда я создавала таблицу командой create table ... as select ..., LOB-сегмент таблицы создались с параметром NOCACHE, который и ведет к ожиданиям direct path write (lob).
SQL> select table_name, column_name, cache
from dba_lobs
where table_name='NEW_BLOB_TABLE';

TABLE_NAME COLUMN_NAME CACHE
-------------------- -------------------- --------
NEW_BLOB_TABLE BLOB_COLUMN NO
То есть новую таблицу нужно либо изначально создать с параметром CACHE LOB-сегмента:
create table new_blob_table
lob (blob_column) store as (cache)
as select * from orig_blob_table where 1=2;
или после создания пустой таблицы (where 1=2), изменить параметр CACHE LOB-сегмента:
alter table new_blob_table modify lob (request_content) (cache);
Теперь, заливка длится чуть больше 1 минуты (!!!):
SQL> create table new_blob_table
lob (blob_column) store as (cache)
as select * from orig_blob_table where 1=2;

Table created.

SQL> set timing on
SQL> insert /*+ append */ into new_blob_table select * from orig_blob_table;

642966 rows created.

Elapsed: 00:01:12.83
Даже обычный инсерт отработал всего 4 минуты!
SQL> insert into new_blob_table select * from orig_blob_table;

642966 rows created.

Elapsed: 00:04:18.85
Странно, что если создавать НЕпустую таблицу командой create table ... as select ... с параметром CACHE LOB-сегмента, он все равно зависает с ожиданиями direct path write (lob):
create table new_blob_table
lob (blob_column) store as (cache)
as select * from orig_blob_table;
Теперь, при заливке командой insert /*+ append */, ожидания записи direct path write (lob) исчезли, есть коротенькие ожидания, но это уже ожидания чтения db file sequential read и db file scattered read. А статистика physical writes direct (lob) заменилась на physical writes direct.

Вот. Получается, что с insert /*+ append */ происходит прямая запись, но видимо эта запись уже отличается прямой записи БЛОБов. Если у кого-то есть более подробное описание всего этого, буду рада послушать.

1 августа 2008 г.

Партиционирование существующей таблицы

Как лучше всего партиционировать существующую таблицу, которая уже содержит большой объем данных?

1) Создать новую партиционированную таблицу и залить данные из старой
CREATE TABLE ... PARTITION BY (RANGE)...
INSERT INTO ... SELECT * FROM not_partitioned_table;
или
INSERT /*+ append */INTO ... SELECT * FROM not_partitioned_table;

2) Создать новую партиционированную таблицу из старой таблицы
CREATE TABLE partitioned_table
PARTITION BY (RANGE) ...
AS SELECT * FROM not_partitioned_table;

3) С помощью EXCHANGE PARTITION
ALTER TABLE partitioned_table
EXCHANGE PARTITION calls_01012008
WITH TABLE not_partitioned_table;

4) С помощью пакета DBMS_REDEFITION

Во всех вышеперечисленных вариантах партиционирования существующей таблицы для активной системы по любому понадобится даунтайм таблицы, кроме 4 пункта(использование DBMS_REDEFINITION).

Теперь, попробую более подробно описать каждый из вариантов.
Скажем, у нас есть партиционированная таблица NOT_PARTITIONED_TABLE, в которой хранятся данные о звонках.

SQL> create table not_partitioned_table (
id number,
dialed_number varchar2(100),
call_date date
);

Table created.

SQL> --- заполним таблицу звонковыми данными: 1 миллион записей, маловато, но пойдет
SQL> declare
start_date date;
begin
start_date:=to_date('01.01.2008','DD.MM.YYYY');
for i in 1..1000000 loop
insert into not_partitioned_table values
(i, '1234567890', start_date + (i-1)/24/60/10);
end loop;
commit;
end;
/

PL/SQL procedure successfully completed.
В итоге, у нас есть непартиционированная таблица NOT_PARTITIONED_TABLE с 1 миллионом записей о звонках с 1 по 7 января 2008 года:
SQL> select trunc(call_date), count(*)
from not_partitioned_table
group by trunc(call_date);

TRUNC(CAL COUNT(*)
--------- ----------
01-JAN-08 144000
02-JAN-08 144000
03-JAN-08 144000
04-JAN-08 144000
05-JAN-08 144000
06-JAN-08 144000
07-JAN-08 136000

7 rows selected.
Наша цель: партиционировать эту таблицу с наименьшим даунтаймом таблицы.

Вариант 1: Создать новую партиционированную таблицу и залить данные из старой

План действий такой:
- Создаем новую партиционированную таблицу
- Можно заблокировать непартиционированной таблицу, чтоб никто не смог изменить данные во время заливки
- Заливаем данные из непартиционированной таблицу
- Удаляем непартиционированную таблицу
- Переименовываем новую партиционированную таблицу
SQL> create table partitioned_table (
id number,
dialed_number varchar2(100),
call_date date)
partition by range (call_date)
(
partition calls_01012008 values less than (to_date('02.01.2008','DD.MM.YYYY')),
partition calls_02012008 values less than (to_date('03.01.2008','DD.MM.YYYY')),
partition calls_03012008 values less than (to_date('04.01.2008','DD.MM.YYYY')),
partition calls_04012008 values less than (to_date('05.01.2008','DD.MM.YYYY')),
partition calls_05012008 values less than (to_date('06.01.2008','DD.MM.YYYY')),
partition calls_06012008 values less than (to_date('07.01.2008','DD.MM.YYYY')),
partition calls_maxvalue values less than (maxvalue)
);
Table created.
Перед заливкой заблокируем таблицу в режиме EXCLUSIVE командой LOCK TABLE, чтоб пока мы заливаем данные, никто не смог изменить данные в них или добавить новые.
SQL> set timing on
SQL> set autotrace on statistics
SQL> lock table not_partitioned_table in exclusive mode;

Table(s) Locked.

Elapsed: 00:00:00.07
Теперь зальем данные командой INSERT. Сперва посмотрим заливку обычным INSERTом:
SQL> insert into partitioned_table select * from not_partitioned_table;

1000000 rows created.

Elapsed: 00:07:53.31

Statistics
----------------------------------------------------------
5666 recursive calls
42061 db block gets
11930 consistent gets
4035 physical reads
35909076 redo size
357 bytes sent via SQL*Net to client
345 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1000000 rows processed
Заливка 1 миллиона записей длилась почти 8 минут. Инсерт сгенерил 35 909 076 байт redo - это почти равно размеру непартиционированной таблицы, таблица весит 33 554 432 байт.
SQL> select bytes from dba_segments
where segment_name like 'NOT_PARTITIONED_TABLE';

BYTES
----------
33554432
Теперь попробуем залить те же данные прямым инсертом (direct path insert), командой INSERT /*+ APPEND */.

Direct Path Insert отличается от обычного инсерта тем, что вставка происходит:
1) в обход буферного кеша
2) новые блоки добавляются за отметкой HWM

То есть новые блоки данных подготавливаются в pga сессии и в обход буферного кеша добавляются ЗА отметкой HWM, если даже до отметки HWM есть свободное место для данных. Как объясняет Том Кайт, неправильно считать, что при таком инсерте (direct path insert) вообще не генерится redo. Redo по-любому генерится, но совсем малюсенький по сравнению с обычным инсертом, что существенно увеличивает скорость заливки.
После таких заливок следует сделать полный бэкап базы на всякий случай.
SQL> insert /*+ append */ into partitioned_table
select * from not_partitioned_table;

1000000 rows created.

Elapsed: 00:00:03.15

Statistics
----------------------------------------------------------
5710 recursive calls
3735 db block gets
5928 consistent gets
3670 physical reads
343352 redo size
351 bytes sent via SQL*Net to client
359 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
1000000 rows processed
Заливка "инсерт аппендом" закончилась за 3 минуты (почти в 3 раза быстрей обычного инсерта), и сгенерил 343352 байтов redo (почти в 100 раз меньше чем при обычном инсерте).

В три раза быстрей чем обычный инсерт, но как можно еще как-то ускорить заливку? Есть один способ: можно заранее выделить необходимый объем экстентов партициям, чтоб во время заливки на это не тратилось время.
SQL> alter table partitioned_table
modify partition calls_01012008 allocate extent (size 5M);

Table altered.
....
SQL> alter table partitioned_table
modify partition calls_maxvalue allocate extent (size 5M);

Table altered.
После предварительного выделения эктентов партициям, инсерт аппенд отработал всего за 5 секунд!!!!
SQL> insert /*+ append */ into partitioned_table
select * from not_partitioned_table;

1000000 rows created.

Elapsed: 00:00:05.06
А обычный инсерт отработал за 11 секунд:
SQL> insert into partitioned_table select * from not_partitioned_table;

1000000 rows created.

Elapsed: 00:00:11.13
Для ускорения заливки и уменьшения даунтайма таблицы, можно заранее выделить необходимые эктенты партициям, чтоб во времы заливки на эту рекурсивную операцию не тратилось время.

Теперь осталось удалить старую таблицу и переименовать партиционированную таблицу.
SQL> drop table not_partitioned_table;

Table dropped.

SQL> alter table partitioned_table rename to new_table;

Table altered.
Можно и партиции переименовать (но их можно было бы создать с нужным именем изначально):
SQL> alter table new_table rename partition calls_01012008 to new_calls_01012008;

Table altered.
...

В следующие варианты - в следующем выпуске новостей.

31 июля 2008 г.

Параметр CACHE LOB-сегмента

На прошлой неделе при поддержке запуска одной системы, столкнулась с резким снижением производительности в системе. Администраторы приложения доложили, что процессы еле-еле шевелятся. Я начала копать, правда не сразу докопалась до истины. Большинство сессий этих процессов ожидали объекта SYS_LOB0000086738C00025$$ - LOB сегмента по одному из столбцов (скажем PAYMENT_INFO) одной таблицы (скажем PAYMENT). Содержимое столбца извлекались этим запросом:

select PAYMENT_INFO
from PAYMENT
where PAYMENT_ID = :1 AND PAYMENT_TYPE = :2
for update;
Выяснилось, что сегмент SYS_LOB0000086738C00025$$ имеет параметр NOCACHE в отличие от аналогичной базы данных, в котором этот же LOB-сегмент имеет параметр CACHE. Вот, что пишут в документации Oracle об этом параметре LOB-сегментов:
CACHE, NOCACHE

Definition

The CACHE storage parameter causes LOB data blocks to be read/written via buffer cache.

With the NOCACHE storage parameter, LOB data is read/written using direct reads/writes. This means that the LOB data blocks are never in the buffer cache and the Oracle server process performs the reads/writes.
Если честно, до этого не знала, что по умолчанию, LOB сегменты читаются в обход буфферного кеша. А установкой параметра LOB-сегмента в CACHE, можно попросить Oracle, чтоб он извлекал блоки этого сегмента через буфферный кеш, что увеличивает производительность системы. Только нужно быть осторожным с LOB-сегментами больших размеров, чтоб они не забили весь буферный кеш. Там же пишут, что использование параметра CACHE вместо NOCACHE улучшает производительность операций ввода-вывода:
The CACHE option gives better read/write performance than the NOCACHE option.
В нашем случае, изменения параметра LOB-сегмента в CACHE помогло:
alter table PAYMENT modify lob ("PAYMENT_INFO") (cache);
Прежние ожидания исчезли и приложение вернулось на прежний высокий уровень производительности. Обожаю хеппи енды.

Working days between two dates

Функция Оказывается в Oracle нет такой стандартной функции для подсчета количества рабочих дней между двумя датами. Есть функия для подсчета количества месяцев между двумя датами MONTHS_BETWEEN:

MONTHS_BETWEEN
Calculates the number of months between two dates.

В инете поискала такие самописные функции, которые бы считали количество месяцев между двумя датами. Больше всех понравилась этот метод подсчета, описанная в Tips of the Week на сайте Oracle, мне кажется он самый оптимальный из тех, что я посмотрела:

create table date_test (start_dt date, end_dt date);

select start_dt,
end_dt,
trunc(end_dt - start_dt) age,
(trunc(end_dt - start_dt) - ((case
WHEN (8 - to_number(to_char(start_dt, 'D'))) >
trunc(end_dt - start_dt) + 1 THEN 0
ELSE
trunc((trunc(end_dt - start_dt) - (8 - to_number(to_char(start_dt, 'D')))) / 7) + 1
END) + (case
WHEN mod(8 - to_char(start_dt, 'D'), 7) >
trunc(end_dt - start_dt) - 1 THEN
0
ELSE
trunc((trunc(end_dt - start_dt) -
(mod(8 - to_char(start_dt, 'D'), 7) + 1)) / 7) + 1
END))) workingdays
from date_test

Здесь он считает суммарное количество суббот и воскресений и отнимает от общего количества дней между двумя датами. Правда, глубоко вникать не стала, на тествых датах работает правильно. Можно переписать в функцию:
create or replace function working_days_between (start_dt in date, end_dt in date) return number is
wdays number;
begin
select (trunc(end_dt - start_dt) - ((case
WHEN (8 - to_number(to_char(start_dt, 'D'))) >
trunc(end_dt - start_dt) + 1 THEN 0
ELSE
trunc((trunc(end_dt - start_dt) - (8 - to_number(to_char(start_dt, 'D')))) / 7) + 1
END) + (case
WHEN mod(8 - to_char(start_dt, 'D'), 7) >
trunc(end_dt - start_dt) - 1 THEN 0
ELSE
trunc((trunc(end_dt - start_dt) - (mod(8 - to_char(start_dt, 'D'), 7) + 1)) / 7) + 1
END))) into wdays from dual;
return wdays;
end working_days_between;

Сравнение двух схем

Недавно столкнулась с необходимостью сравнить объекты двух схем, что-то влом было самой писать скрипты по сравнению, поэтому поискала готовые методы сравнения:
1) Сравнение схем с помощью Oracle Change Manager

Сегодня еще рассказали, что в Тоаде тоже есть такая возможность:
2) С помощью Toad for Oracle
Правда начиная с поздних версий, например в 9.1 точно есть, а в 6.4 - нет.
Database -> Compare -> Schemas

23 июля 2008 г.

"Кем была вызвана процедура или функция?"

Мой коллега нашел метод позволяющий в процедуре узнать кем она была вызвана, например, в таком примере:

Есть 3 процедуры: процедура 1, процедура 2 и процедура 3
При этом процедура 3 вызывается как из процедуры 1, так и из процедуры 2
Может ли процедура 3 не используя входные параметры понять из какой процедуры она вызвана, из 1-й или 2-й?
Он нашел функцию пакета dbms_utility.format_call_stack, который оказывается возвращает полную цепочку процедур, функций или анонимного PL/SQL блока, которая вызвала эту процедуру:
----- PL/SQL Call Stack -----
object line object
handle number name
3902e1880 4 procedure MYSCHEMA.PROC3
3902e8470 3 procedure MYSCHEMA.PROC1
3902d3400 2 anonymous block
В этом примере, анонимный блок вызвал процедуру PROC1, а PROC1 вызвала процедуру PROC3.
Процедура 3, которая вызывается процедурой 1 или 2:
create or replace procedure proc3 is
call_stack varchar2(4096);
begin
call_stack := dbms_utility.format_call_stack;
dbms_output.put_line(call_stack);
end;
Процедуры 1 и 2, которые вызывают процедуру 3:
create procedure proc1 is
begin
proc3;
end;

create procedure proc2 is
begin
proc3;
end;
Вызов процедур 1 и 2:
SQL> exec proc1;

----- PL/SQL Call Stack -----
object line object
handle number name
3902e1880 4 procedure MYSCHEMA.PROC3
3902e8470 3 procedure MYSCHEMA.PROC1
390175a80 1 anonymous block

PL/SQL procedure successfully completed

SQL> exec proc2;

----- PL/SQL Call Stack -----
object line object
handle number name
3902e1880 4 procedure MYSCHEMA.PROC3
3902db938 3 procedure MYSCHEMA.PROC2
390180160 1 anonymous block

PL/SQL procedure successfully completed
Только, из стэка вызывающих процедур и функций нужно вытащить то, что нужно. В данном случае вторую строку стэка. У Тома Кайта есть процедура who_called_me, которая парсит этот стэк и возвращяет кем была вызвана процедура или функция.

22 июля 2008 г.

Мои второй тренинг

Вчера во второй раз прочитала свой тренинг SQL Tuning Workshop, правда только одну треть своего тренинга. Мне показалось, что мне уже легче читается, ведь после первого тренинга много чего добавила и улучшила. Темы были такие:

  • Краткое описание архитектуры Oracle
  • Шаги выполнения SQL запросов
  • Совместное использование курсоров
  • Построение и чтение плана выполнения
  • Трассировка, чтение трассировочного файла
  • Форматирование файла трассировки утилитой TKPROF, чтение отформатированного файла.
Теперь, своей первой группе прочитаю вторую часть тренинга и сделаю выводы о том, что можно улучшить.

11 июля 2008 г.

Дамп структуры индекса

Иногда очень полезно посмотреть на внутреннюю структуру индекса. Для этого можно выгрузить индекс в дамп следующей командой:

alter session set events 'immediate trace name treedump level nnnn;

где nnnn равен object_id индекса, который можно посмотреть в dba_objects:

SQL> column object_name format A15
SQL> select object_id, object_name
from dba_objects a
where owner = 'SCHEMA' and
object_type = 'INDEX' and
object_name = 'CUSTOMERS_PK';

OBJECT_ID OBJECT_NAME
---------- ---------------
33243 CUSTOMERS_PK

SQL> alter session set events 'immediate trace name treedump level 33243';

Session altered.
Последняя команда выгрузит структуру указанного индекса в файл в папку user_dump_dest, который обычно указывает на $ORACLE_BASE/admin/SID/udump.
Содержимое файла выглядит приблизительно так (заголовок не включен):
----- begin tree dump
branch: 0x2402004 37756932 (0: nrow: 104, level: 1)
leaf: 0x2402005 37756933 (-1: nrow: 517 rrow: 517)
leaf: 0x2402006 37756934 (0: nrow: 514 rrow: 514)
leaf: 0x2402007 37756935 (1: nrow: 481 rrow: 481)
leaf: 0x2402008 37756936 (2: nrow: 481 rrow: 481)
leaf: 0x2403309 37761801 (3: nrow: 481 rrow: 481)
...
leaf: 0x240336d 37761901 (97: nrow: 481 rrow: 481)
leaf: 0x240336e 37761902 (98: nrow: 482 rrow: 482)
leaf: 0x240336f 37761903 (99: nrow: 481 rrow: 481)
leaf: 0x2403370 37761904 (100: nrow: 481 rrow: 481)
leaf: 0x2403372 37761906 (101: nrow: 481 rrow: 481)
leaf: 0x2403373 37761907 (102: nrow: 377 rrow: 377)
----- end tree dump
По дампу видно, что индекс состоит из одного корневого блока и 104 листовых блоков (с -1 до 102 включительно). Строки начинающиеся на branch - это либо корневой блок, либо блоки ветвления. Листовые блоки начинаются на leaf. Вторая цифра после него 37756932 - это адрес блока. Используя этот адрес блока, можно получить id файла и блока функциями пакета dbms_utility и выгрузить содержимое конкретных блоков индекса. Об этом чуть позже. Дальше, nrow - это суммарное количество ключей или строк включая строки, которые отмечены как удаленные, а rrow - количество ключей или строк.

В этом дампе мы увидели общую структуру индекса и сколько ключей хранятся в каждом узле индекса, но в ней нет самих значений ключей. Я пока не видела одношагового решения для выгрузки всех узлов вместе со значениями ключей, но можно выгрузить содержимое отдельного узла, то есть блока индекса следующим образом:
SQL> select dbms_utility.data_block_address_file(37756932) file_id,
dbms_utility.data_block_address_block(37756932) block_id
from dual;

FILE_ID BLOCK_ID
---------- ----------
9 8196

SQL> alter system dump datafile 9 block 8196;

System altered.

10 июля 2008 г.

Index Skip Scan

Думаю, об индексах в Oracle можно писать и писать. Индексы - это одна из моих любимых тем по базам данных Oracle. До 9 версии Оракла, при поиске по составному индексу не использовался индекс если в запросе не было поиска по лидирующему (leading) столбцу индекса. Поэтому и рекомендовалось в качестве лидирующего столбца выбирать столбец наиболее часто использующийся для поиска.

В 9 версии появился новые индексный доступ INDEX SKIP SCAN, который позволяет использовать составной индекс для поиска данных, даже если в условиях поиска отсутствует лидирующий столбец.

SQL> create table t (a number, b number);

Table created.

SQL> create index t_ix on t(a, b);

Index created.

SQL>
SQL> begin
2 for i in 1 .. 10000
3 loop
4 insert into t values(mod(i, 2), i);
5 end loop;
6 commit;
7 end;
8 /

PL/SQL procedure successfully completed.

SQL>
SQL>
SQL>
SQL> begin
2 dbms_stats.gather_table_stats(user, 'T', cascade=>true);
3 end;
4 /

PL/SQL procedure successfully completed.

SQL> explain plan set statement_id='SKIP'
2 for select * from t where b=5;

Explained.

SQL>
SQL> select * from table(dbms_xplan.display('PLAN_TABLE','SKIP', 'ALL'));

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


--------------------------------------------------------------------
Id Operation Name Rows Bytes Cost
--------------------------------------------------------------------
0 SELECT STATEMENT 1 6 3
* 1 INDEX SKIP SCAN T_IX 1 6 3
--------------------------------------------------------------------

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


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

1 - access("T"."B"=5)
filter("T"."B"=5)

Note: cpu costing is off

15 rows selected.

SQL>
В этом примере использовался индексный доступ INDEX SKIP SCAN по составному индексу, при отсутствии поиска по лидирующему столбцу.

Так, как же работает INDEX SKIP SCAN? На одной из публикаций на сайте Оракла, пишут:
During a skip scan, the composite index is accessed once for each distinct value of the leading column(s). For each distinct value, the index is searched to find the query's target values.
То есть для каждого различного значения лидирующего столбца выполняется поиск значений "второстепенных" столбцов (не нашла более подходящего слова для описания остальных, "лидируемых", ведомых, "менее значимых" столбцов составного индекса). Практически, выполняется INDEX RANGE SCAN для каждого различного значения лидирующего столбца.

В документации Оракла пишут, что INDEX SKIP SCAN эффективен в тех случаях, когда лидирующий столбец составного индекса имеет минимальное количество различных значений.
Skip scanning is advantageous if there are few distinct values in the leading column of the composite index and many distinct values in the nonleading key of the index.
В нашем примере лидирующий столбец имеет всего 2 различных значения (1 и 0), но если бы он был более селективным, то есть содержал больше различных значений, то скорее всего оптимизатор предпочел бы TABLE FULL SCAN. Как, например, здесь:
SQL> create table t2 (a number, b number);

Table created.

SQL> create index t2_ix on t2(a, b);

Index created.

SQL>
SQL> begin
2 for i in 1 .. 10000
3 loop
4 insert into t2 values(mod(i, 10), i);
5 end loop;
6 commit;
7 end;
8 /

PL/SQL procedure successfully completed.

SQL>
SQL>
SQL>
SQL> begin
2 dbms_stats.gather_table_stats(user, 'T2', cascade=>true);
3 end;
4 /

PL/SQL procedure successfully completed.

SQL>
SQL> explain plan set statement_id='NOSKIP'
2 for select * from t2 where b=5;

Explained.

SQL>
SQL> select * from table(dbms_xplan.display('PLAN_TABLE','NOSKIP', 'ALL'));

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


--------------------------------------------------------------------
Id Operation Name Rows Bytes Cost
--------------------------------------------------------------------
0 SELECT STATEMENT 1 6 5
* 1 TABLE ACCESS FULL T2 1 6 5
--------------------------------------------------------------------

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


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

1 - filter("T2"."B"=5)

Note: cpu costing is off

14 rows selected.

SQL>
Мы всего лишь увеличили количество различных значений лидирующего столбца с 2 до 10, и оптимизатор посчитал, что в этом случае ему выгодней использовать TABLE FULL SCAN чем INDEX SKIP SCAN. Есть хинт index_ss(алиас_таблицы название_индекса), который можно использовать для принудетельного использования индексного доступа INDEX SKIP SCAN:
SQL> explain plan set statement_id='INDEX_SS'
2 for select /*+ index_ss (t2 t2_ix) */* from t2 where b=5;

Explained.

SQL> select * from table(dbms_xplan.display('PLAN_TABLE','INDEX_SS', 'ALL'));

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


--------------------------------------------------------------------
Id Operation Name Rows Bytes Cost
--------------------------------------------------------------------
0 SELECT STATEMENT 1 6 11
* 1 INDEX SKIP SCAN T2_IX 1 6 11
--------------------------------------------------------------------

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


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

1 - access("T2"."B"=5)
filter("T2"."B"=5)

Note: cpu costing is off

15 rows selected.

SQL>
Как уже было сказано выше, INDEX SKIP SCAN эффективен в тех случаях, когда лидирующий столбец имеет минимальное количество различных значений. INDEX SKIP SCAN - это не "silver bullet", то есть не идеальное и эффективнейшее решение для всех поисков по составному ключу без лидирующего столбца. Джонатан Льюс (Jonathan Lewis) в своей книге о стоимостном оптимизаторе (кстати, это одна из моих любимых книг по Ораклу) пишет, что INDEX SKIP SCAN может наоборот снижать производительность:
This is a beneficial feature in some cases, particularly if you have compressed indexes with a very low number of distinct values in the first column(s). But occasionally you may find that a skip-scan is causing performance problems.
Есть еще скрытый параметр _optimizer_skip_scan_enabled, который можно установить в false, чтоб отключить индексный путь доступа INDEX SKIP SCAN.
alter session set "_optimizer_skip_scan_enabled"=false;

20 мая 2008 г.

Подключение к iSQL*Plus как SYSDBA или SYSOPER в Oracle 9i

Как sysdba или sysoper можно подключиться через iSQL*Plus в Oracle 9i можно по другому url:
http://hostname:7776/isqlplusdba

По умолчанию на /isqlplusdba включена базовая аутентификация и при перед открытием страницы, запросит имя пользователя и пароль.
Чтоб добавить пользователя для базовой аутентификации в Oracle 9i используется стандартная утилита Apache - htpasswd (в Oracle10g - джава утилита jazn, о ней можно почитать здесь):

oracle@myhost $ cd $ORACLE_HOME/Apache/Apache/bin
oracle@myhost $ ./htpasswd -b $ORACLE_HOME/sqlplus/admin/iplusdba.pw username password
Тепер, при открытие в http://hostname:7776/isqlplusdba можно ввести логин (username) и пароль пользователя (password) и подключиться к базе данных как SYSDBA и SYSOPER.

Или же можно отключить базовую аутентификация в /isqlplusdba закомментировав 4 строчки в ORACLE_HOME/sqlplus/admin/isqlplus.conf:
<Location /isqlplusdba>
SetHandler fastcgi-script
Order deny,allow
#AuthType Basic
#AuthName 'iSQL*Plus DBA'
#AuthUserFile /u01/app/oracle/product/9.2.0.8.0/sqlplus/admin/iplusdba.pw
#Require valid-user
</Location>

Не забудьте перегрузить Apache после изменений:
oracle@hostname $ cd $ORACLE_HOME/Apache/Apache/bin
oracle@hostname $ ./apachectl stop
./apachectl stop: httpd stopped
oracle@hostname $ ./apachectl start
./apachectl start: httpd started
Теперь при открытии http://hostname:7776/isqlplusdba не будет запрашивать логин и пароль.

15 мая 2008 г.

DBMS_STATS или ANALYZE? (3)

Это продолжение темы "DBMS_STATS или ANALYZE?" Здесь я написала о том, почему Oracle рекомендует использовать dbms_stats вместо analyze, а здесь - о том, отличаются ли статистические данные собраные с помощью dbms_stats от данных, собранных с помощью analyze и если отличаются, то чем именно и кому из них верить.

Очередной вопрос: как определить, каким образом была собрана статистика? Пакетом dbms_stats или оператором analyze?

Столбец global_stats в представлениях dba_tables, dba_indexes, dba_tab_cols, dba_tab_columns, dba_tab_col_statistics определяет собрана глобальная статистика или нет.

Так как analyze не умеет собирать глобальную статистику (ее может собрать только dbms_stats), то можем сделать вывод, что если глобальная статистика собрана, то статистика была собрана с помощью dbms_stats, а если нет глобальной статистики - то статистика была собрана оператором analyze:

YES - собрана глобальная статистика, то есть статистика собрана с помощью dbms_stats
NO - статистика собрана с помощью аггрегирования статистики низлежащих партиций, субпартиций, то есть с помощью analyze

Вырезка из документации:

GLOBAL_STATS
For partitioned tables, indicates whether statistics were
collected for the table as a whole (YES) or were estimated from statistics on
underlying partitions and subpartitions (NO)
Подробней можно прочитать об этом на металинке Note: 236935.1.
SQL> analyze table emp compute statistics;
Table analyzed.

SQL> select table_name, partitioned, global_stats
2 from user_tables where table_name='EMP';
TABLE_NAME PARTITIONED GLOBAL_STATS
------------ ----------- ------------
EMP NO NO

SQL> exec dbms_stats.gather_table_stats(user, 'EMP', cascade=>true);
PL/SQL procedure successfully completed.

SQL> select table_name, partitioned, global_stats
2 from user_tables where table_name='EMP';
TABLE_NAME PARTITIONED GLOBAL_STATS
------------ ----------- ------------
EMP NO YES

DBMS_STATS или ANALYZE? (1)

И dbms_stats и analyze используются для сбора статистики, которая используются стоимостным оптимизатором (Cost-Based Optimizer) для построения планов выполнения. То есть их правильное использование оказывает очень большое влияние на производительность всей системы.
Но почему Oracle "настоятельно" рекомендует использовать пакет dbms_stats для сбора статистики вместо analyze?
Из документации Oracle:

"Oracle Corporation strongly recommends that you use the DBMS_STATS package rather than ANALYZE to collect optimizer statistics."
Так что же может dbms_stats, чего не может analyze:
- с помощью dbms_stats можно собирать системную статистику (analyze нет такой возможности)
(о системной статистике напишу отдельно, но "Oracle Corporation highly recommends that you gather system statistics.")
- с помощью dbms_stats можно собирать статистику в удаленной базе данных (через дблинк)
SQL> exec dbms_stats.gather_table_stats@remotedb('REMOTEUSER','REMOTETABLE',cascade=>true);
и еще:
"That package lets you collect statistics in parallel, collect global statistics for partitioned objects, and fine tune your statistics collection in other ways."
Кажется, что если Oracle рекомендует использовать dbms_stats для сбора статистики и имеет ряд преимуществ перед analyze, то зачем нужен analyze?

Здесь Том Кайт объясняет, что dbms_stats собирает статику только для оптимизатора (CBO).
А такие статистические данные, как количество перенесенных строк (chained rows), средний объем свободного места в блоке, количество неиспользованных блоков можно собрать только с помощью analyze.
Из документации Oracle :
"... you must use the ANALYZE statement rather than DBMS_STATS for statistics collection not related to the cost-based optimizer, such as:
To use the VALIDATE or LIST CHAINED ROWS clauses
To collect information on freelist blocks"
Если dbms_stats и analyze собирают одну и ту же статистику по объектам, то они собирают одинаковые данные?
Или эти данные отличаются?
Если отличаются, то кому из них верить?


Updated: 20 февраля, 2009
Попытаюсь ответить на эти вопросы: Некоторые столбцы/компоненты статистики, собранные dbms_stats-ом отличаются от той, которые собраны командой analyze.
Например кол-во строк (num_rows), высота индекса (blevel), конечно же НЕ отличаются.
А такие столбцы как avg_row_len, avg_col_len - отличаются: dbms_stat добавляет 1 байт за хранение длины столбца, а analyze - не учитывает байт, в котором хранится длина столбца. Вот что пишет Джонатан Льюис об этом:
As a general rule, the figures for bytes in execution plans are derived from the avg_col_len columns of user_tab_columns. The deprecated analyze command excludes the length byte(s) for the column, but the call to dbms_stats.gather_table_stats includes the length byte(s). Since the choice of build table in a hash join is affected by the size of the data sets involved, a switch from analyze to dbms_stats could (in principle) change the order of a hash join, or even cause the optimizer to use a different join mechanism.
В-общем, он говорит, что, в принципе, из-за этой разницы в статистике, оптимизатор может изменить план выполнения в зависимости от метода сбора статистики, так как статистика немного отличается.
Ответ на последний вопрос "Если отличаются, то кому из них верить?": Думаю достаточно иметь ввиду, что некоторые столбцы/компоненты статистики собранные dbms_stats-ом отличаются от той, которая собрана командой analyze. И, к тому же статистика нужна только оптимизатору, а вам она не нужна. Поэтому долго не думайте и собирайте статистику dbms_stats-ом, а чтоб увеличить скорость сбора, распараллельте или же собирайте с небольшим estimate-ом.

Здесь я написала о том, как определить, каким образом была собрана статистика: пакетом dbms_stats или оператором analyze.

14 мая 2008 г.

Где именно находится оптимизатор
в архитектуре Oracle RDBMS?

Oracle DBA обычно не интересуются вопросом "Где именно находится оптимизатор в архитектуре Oracle RDBMS?" Некоторые из моих знакомые ДБА отнесли его к категории вопросов о смысле жизни.
Но самый популярный ответ был "в ядре Oracle".

В книге Oracle Database 10g Insider Solutions пишут тоже самое:

"The Cost Based Optimizer is at the heart of the Oracle kernel and plays a large part in the efficient execution of SQL statements in Oracle Database 10g."
Но где именно находится это ядро (kernel)? Это обычный процесс? Если да, то можно ли его увидеть в юниксе в списке процессов командой "ps"?

Отрывок из книги Тома Кайта "Oracle для профессионалов: Архитектура и основные особенности":
"При получении запроса SELECT * FROM EMP именно выделенный/разделяемый сервер Oracle будет разбирать его и помещать в разделяемый пул (или находить соответствующий запрос в разделяемом пуле). Именно этот процесс создает план выполнения запроса. Этот процесс реализует план запроса, находя необходимые данные в буферном кеше или считывая данные в буферный кеш с диска. Такие серверные процессы можно назвать "рабочими лашадками" СУБД. Часто именно они потребляют основную часть процессорного времени в системе, поскольку выполняют сортировку, суммирование, соединения - в общем, почти все."
То есть функции оптимизатора выполняются серверными процессами и их мы можем увидеть в списке процессов:
oracle@myhost$ ps -ef  grep ora  grep LOCAL  more
oracle 22790 1 0 09:13:33 ? 0:03 oracleTESTDB (LOCAL=NO)
oracle 4426 1 0 11:20:58 ? 0:02 oracleTESTDB (LOCAL=NO)
oracle 29167 1 0 11:11:32 ? 0:01 oracleTESTDB (LOCAL=NO)
oracle 12778 1 0 09:53:02 ? 0:03 oracleTESTDB (LOCAL=NO)
oracle 14349 1 0 12:26:34 ? 0:01 oracleTESTDB (LOCAL=NO)
oracle 21141 1 0 11:47:35 ? 0:01 oracleTESTDB (LOCAL=NO)
oracle 11220 1 0 09:49:18 ? 0:06 oracleTESTDB (LOCAL=NO)
oracle 16823 1 0 11:40:49 ? 0:01 oracleTESTDB (LOCAL=NO)
oracle 26760 1 0 11:57:20 ? 0:01 oracleTESTDB (LOCAL=NO)
oracle 20814 1 0 09:09:42 ? 0:01 oracleTESTDB (LOCAL=NO)
oracle 17374 1 0 12:32:28 ? 0:02 oracleTESTDB (LOCAL=NO)
oracle 8911 1 0 22:14:38 ? 0:00 oracleTESTDB (LOCAL=NO)
Если серверные процессы разбирают все запросы (выполняют все функции оптимизатора), значит ли это, что:

Код самого оптимизатора находится в каждом серверном процессе?
Или серверные процессы всего лишь вызывают эти функции из ярда Oracle?
Или ядро Oracle - это и есть серверные процессы?


Для меня этот вопрос все еще остается открытым, если у кого-то есть идеи, буду рада их услышать.

Добавлено 16 мая, 2008:
Это ответ Джонатана Льюиса на этот вопрос (публикую с его разрешения):
There is one main executable for the database in Oracle distribution, and that is called oracle (on Unix systems, but oracle.exe on Windows).
This is the program that becomes pmon, smon, dbwr, s000, and all the other background processes when the instance starts up. The bits of code run from that executable vary across the different roles played in the instance.

As such, the optimiser is just part of the code that is called only by a program which is taking on the role of a dedicated server (oracle_{SID}_nnn in unix variants) or a shared server (oracle_{SID}_Snnn).

When people talk about the 'Oracle kernel' it's actually a very informal and inaccurate expression - they are trying to give a vague impression of the most commonly used part of the code with an emphasis, perhaps, on the code segments that do a lot of synchronised work in the shared memory area. But there is no specific process that you can see that is "the" kernel.

Regards

Jonathan Lewis
http://jonathanlewis.wordpress.com

Author: Cost Based Oracle: Fundamentals
http://www.jlcomp.demon.co.uk/cbo_book/ind_book.html

The Co-operative Oracle Users' FAQ
http://www.jlcomp.demon.co.uk/faq/ind_faq.html
Перевод:

Есть один основной бинарник в дистрибутиве Oracle, который так и называется oracle (в юникс системах, и oracle.exe в Windows). Когда стартуется инстанс, эта программа превращается в фоновые процессы pmon, smon, dbwr, s000 и тд.
В зависимости от роли, которую он выполняет в составе инстанса, выполняются отдельные биты кода этого бинарника.

Оптимизатор - это всего лишь кусочек кода, который вызывается программой, выполняющей роль выделенного сервера (oracle_{SID}_nnn в юниксе) или разделяемого сервера (oracle_{SID}_Snnn).

Когда люди говорят о "ядре Oracle", они на самом деле используют неформальное и не совсем точное выражение - они стараются дать смутное ощущение о часто используемой части кода, возможно имея ввиду сегменты кода, которые выполняют кучу синхронизированных операций в разделяемой памяти.
Но на самом деле нет отдельного процесса, которого можно увидеть и который являлся бы "ядром".

Прикольно, значит код самого оптимизатора находится в каждом серверном процессе.
Спасибо всем, кто участвовал в процессе выяснения местонахождения оптимизатора в архитектуре Oracle. Отдельное спасибо Джонатану Льюису, кстати всем, кто занимается тюнингом, рекомендую почитать его книжку Основы Стоимостной Оптимизации (Cost-Based Oracle Fundamentals).

6 мая 2008 г.

Одноблочное и многоблочное чтения
(single-block & multi-block read)

Если при полном просмотре таблицы (full table scan) выполняется многоблочное чтение, то это положительно влияет на производительность. То есть, за одну операцию ввода/вывода читается несколько блоков, и вся таблица будет прочитана с файла в буферный кэш за минимальное количество операций ввода/вывода.

Кол-во блоков, которое будет прочитано из файла в буферный кэш за одну системную операцию ввода/вывода устанавливается параметром db_file_multiblock_read_count. В OLTP системах он обычно равен от 8 до 16, а в OLAP системах от 32 и выше. На большинстве платформ значение db_file_multiblock_read_count может быть равен максимум 128, об это написано в предыдущей теме.

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

FILE# Number of the file
PHYRDS Number of physical reads done
PHYBLKRD Number of physical blocks read
SINGLEBLKRDS Number of single block reads
В списке полей нет поля о кол-ве многоблочных чтений, но его мы можем получить отняв SINGLEBLKRDS (так как, за одну операцию одноблочного чтения читается один блок) от суммарного количества физических чтений PHYRDS:
MULTIRDS = PHYRDS-SINGLEBLKRDS
А кол-во блоков прочитанных в режиме многоблочного чтения можно получить отняв SINGLEBLKRDS от суммарного кол-ва прочитанных блоков PHYBLKRD:
MULTIBLKRDS = PHYBLKRD-SINGLEBLKRDS

Еще один важный параметр, который можно получить - это среднее количество блоков прочитанных в многоблочном чтении. Если этот параметр близок к db_file_multiblock_read_count, то это хорошо. Это значит, что при многоблочном чтении из файла в буферный кэш, за минимальное количество операций ввода/вывода, читается максимальное количество блоков.
BLKSMULTIBLKRDS = MULTIBLKRDS/MULTIRDS

Скрипт:
select round(SINGLEBLKRDS/PHYBLKRD,2) SINGLEBLKRDS_PCT,
round((PHYBLKRD-SINGLEBLKRDS)/PHYBLKRD,2) MULTIBLKRDS_PCT,
round((PHYBLKRD-SINGLEBLKRDS)/(PHYRDS-SINGLEBLKRDS),2) BLKSMULTIBLKRDS,
k.tablespace_name
from v$filestat t, dba_data_files k
where t.file#=k.file_id
order by 1

Здесь приблизительный результат:
SINGLEBLKRDS_PCT MULTIBLKRDS_PCT BLKSMULTIBLKRDS TABLESPACE_NAME
0 1 7,74 CCCTBS
0 1 7,83 CCCTBS
0 1 7,88 CCCTBS
0 1 15,73 USERS
0,01 0,99 7,88 CCCTBS
0,01 0,99 14,65 DDDDTBS
0,02 0,98 11,82 DATA_MED
0,03 0,97 11,73 DATA_MED
0,05 0,95 13,54 AAA_BBB
0,07 0,93 1,03 EEEETBS
0,26 0,74 14,49 DATA_SML
0,3 0,7 10,32 GGGDATA
0,31 0,69 9,35 SYSTEM
0,49 0,51 11,32 DATA_BIG
0,5 0,5 11,41 DATA_BIG
0,52 0,48 10 DATA_BIG
0,52 0,48 11,47 DATA_BIG
0,53 0,47 10,21 DATA_BIG
0,68 0,32 14,52 DATA_BIG
0,91 0,09 14,68 DATA_BIG_IX
0,91 0,09 15,28 DATA_BIG_IX
0,92 0,08 1 UNDO
0,92 0,08 14,81 DATA_BIG_IX
0,92 0,08 15,08 DATA_BIG_IX
0,93 0,07 15,32 DATA_BIG_IX
0,98 0,02 1 AAA_BBB_IX
0,99 0,01 1,65 FFFFTBS
1 0 1 DATA_SML_IX
1 0 1 DATA_MED_IX
Как показывает отчет, в табличных пространствах, где хранятся индексы (_IX) почти нет (или очень низкий процент) многоблочного чтения, думаю это из-за того, что в них нет таблиц, соответсвенно не бывает полных просмотров таблиц (table full scan) и очень низок процент fast full index scan.

5 мая 2008 г.

Максимальное значение db_file_multiblock_read_count

Параметр db_file_multiblock_read_count определяет количество блоков, которые будут считаны за одну системную операцию чтения.

Он используется стоимостным оптимизатором (Cost-based Optimizer) для вычисления суммарного кол-ва операций чтения для полного просмотра таблицы (full table scan).

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

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

SQL> select name, value from v$parameter
2 where name = 'db_file_multiblock_read_count';

NAME VALUE
------------------------------ ----------
db_file_multiblock_read_count 8

SQL> alter session set db_file_multiblock_read_count=100000000;

System altered

SQL>
SQL> select name, value from v$parameter
2 where name = 'db_file_multiblock_read_count';

NAME VALUE
------------------------------ ----------
db_file_multiblock_read_count
128

Этот запрос был выполнен на Oracle 9.2.0.8, размер блока БД - 8K, Solaris 5.9.
Максимальное значение этого параметра зависит от внутреннего параметра (константы) SSTIOMAX, который зависит от версии Oracle и размера блока базы данных. Это вырезка из Металинка Note:131530.1:
What is SSTIOMAX?
-----------------

SSTIOMAX is an internal parameter/constant used by oracle, which limits the
maximum amount of data transfer in a single IO of a read or write operation.
This parameter is fixed and cannot be tuned/changed.


Relationship between SSTIOMAX and db_file_multiblock_read_count (MBRC)
----------------------------------------------------------------------

More often than not, DBAs try to increase the db_file_multiblock_read_count
parameter (which can be set in the init.ora), in an attempt to optimize the
IO performance of the read and write operations.

Normally, with a higher value of MBRC, the IO performance is expected to be
better. So, users tend to increase this parameter to a higher value, in case
they find it beneficial. But, there is a limitation on this.

The limitation is, the product of db_block_size and MBRC cannot exceed the
SSTIOMAX. For example:

db_block_size * db_file_multiblock_read_count <= SSTIOMAX (which is predefined for a particular version of oracle) If the value of the product exceeds this, then the value of db_file_multiblock_read_count set in the init.ora is ignored and it is set as follows: db_file_multiblock_read_count = SSTIOMAX/db_block_size (rounded)

В Oracle 9i SSTIOMAX = 1M, то есть если
размер блока БД 16K, то максимальный размер db_file_multiblock_read_count = 1M / 16K = 64;
размер блока БД 8K, то максимальный размер db_file_multiblock_read_count = 1M / 8K = 128;
размер блока БД 4K, то максимальный размер db_file_multiblock_read_count = 1M / 4K = 256;

Для сравнения, в Oracle 7.3 SSTIOMAX был равен 128K.

4 мая 2008 г.

Создание табличного пространства, размер блока которого отличается от размера блока БД

Для создания табличного пространства, размер блока которого отличается от размера блока БД, необходимо выделить память под соответствующий db_Xk_cache_size
(db_2k_cache_size,db_4k_cache_size,db_8k_cache_size, db_16k_cache_size, db_32k_cache_size).

SQL> show parameter db_block_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_size integer 8192
SQL> create tablespace tbs4k blocksize 4096
2 datafile '/u01/oradata/TESTDB/tbs4kTESTDB01.dbf'
3 size 10M autoextend on next 1M;
create tablespace tbs4k blocksize 4096
*
ERROR at line 1:
ORA-29339: tablespace block size 4096 does not match configured block sizes

SQL> show parameter cache_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_16k_cache_size big integer 0
db_2k_cache_size big integer 0
db_32k_cache_size big integer 0
db_4k_cache_size big integer 0
db_8k_cache_size big integer 0
db_cache_size big integer 536870912
db_keep_cache_size big integer 0
db_recycle_cache_size big integer 0

SQL> alter system set db_4k_cache_size=16M scope=spfile;

System altered.

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL>
SQL>
SQL> startup
ORACLE instance started.

Total System Global Area 1159537920 bytes
Fixed Size 730368 bytes
Variable Size 603979776 bytes
Database Buffers 553648128 bytes
Redo Buffers 1179648 bytes
Database mounted.
Database opened.

SQL> show parameter cache

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_16k_cache_size big integer 0
db_2k_cache_size big integer 0
db_32k_cache_size big integer 0
db_4k_cache_size big integer 16777216
db_8k_cache_size big integer 0
db_cache_advice string ON
db_cache_size big integer 536870912
db_keep_cache_size big integer 0
db_recycle_cache_size big integer 0
object_cache_max_size_percent integer 10
object_cache_optimal_size integer 102400

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
session_cached_cursors integer 100
SQL> create tablespace tbs4k blocksize 4096
2 datafile '/u01/oradata/TESTDB/tbs4kTESTDB01.dbf'
3 size 10M autoextend on next 1M;

Tablespace created.

28 апреля 2008 г.

Загрузка данных из Агента в OMS


Oracle Management Agent 10g был установлен на узле с помощью скрипта agentDownload script. Несмотря на то, что установка и настройка агента прошла успешно, новый узел не появился в списке хостов в веб-интерфейсе Enterprise Manager Grid Control.

Статус агента:
gc@agenthost $ emctl status agent
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
Agent Version : 10.2.0.4.0
OMS Version : 10.2.0.1.0
Protocol Version : 10.2.0.0.0
Agent Home : /u01/app/gc/product/agent10g
Agent binaries : /u01/app/gc/product/agent10g
Agent Process ID : 26774
Parent Process ID : 26765
Agent URL : https://agenthost.domain.com:3872/emd/main/
Repository URL : https://omshost:1159/em/upload
Started at : 2008-04-24 12:20:17
Started by user : gc
Last Reload : 2008-04-24 12:20:17
Last successful upload : (none)
Last attempted upload : (none)

Total Megabytes of XML files uploaded so far : 0.00
Number of XML files pending upload : 6
Size of XML files pending upload(MB) : 5.88
Available disk space on upload filesystem : 18.10%
Last successful heartbeat to OMS : 2008-04-24 12:20:26
---------------------------------------------------------------
Agent is Running and Ready
В статусе агента видно, что Агент не может загрузить данные в OMS, а если вручную попробывать загрузить данные, то выводит такое сообщение:
gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload error: uploadXMLFiles skipped :: OMS version not checked yet..
В логах агента, нашла такие ошибки в ORACLE_HOME/sysman/log/emagent.trc:
2008-04-23 19:54:26,063 Thread-20 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
2008-04-23 19:54:26,064 Thread-20 ERROR pingManager: nmepm_pingReposURL: Cannot connect to https://omshost:1159/em/upload: retStatus=-32
2008-04-23 19:54:26,067 Thread-20 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
2008-04-23 19:54:26,067 Thread-20 ERROR pingManager: nmepm_pingReposURL: Cannot connect to https://omshost:1159/em/upload: retStatus=-32
2008-04-23 19:54:26,359 Thread-5 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
2008-04-23 19:54:26,359 Thread-5 ERROR command: nmejcn: failed http connection to https://omshost:1159/em/upload: retStatus=-32
2008-04-23 19:54:28,368 Thread-5 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)

Причина ошибки оказалась простой, из узла agenthost не определялся omshost:
gc@agenthost $ ping -a omshost
ping: unknown host omshost

После, прописания omshost в /etc/hosts (555.555.555.555 omshost), загрузка данных в OMS прошла успешно

gc@agenthost $ ping -a omshost
omshost (555.555.555.555) is alive

gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload error: Upload timed out before completion.
Number of files to upload before the upload: 13, total size (MB): 5.25.
Remaining number of files to upload: 13, total size (MB): 5.25.

gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload error: Upload timed out before completion.
Number of files to upload before the upload: 6, total size (MB): 1.38.
Remaining number of files to upload: 6, total size (MB): 1.38.

gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload completed successfully
Теперь в статусе агента видно, что загрузка прошла успешно:
gc@agenthost $ emctl status agent
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
Agent Version : 10.2.0.4.0
OMS Version : 10.2.0.1.0
Protocol Version : 10.2.0.0.0
Agent Home : /u01/app/gc/product/agent10g
Agent binaries : /u01/app/gc/product/agent10g
Agent Process ID : 26774
Parent Process ID : 26765
Agent URL : https://agenthost.domain.com:3872/emd/main/
Repository URL : https://omshost:1159/em/upload
Started at : 2008-04-24 12:20:17
Started by user : gc
Last Reload : 2008-04-24 12:20:17
Last successful upload : 2008-04-24 12:39:02
Total Megabytes of XML files uploaded so far : 7.44
Number of XML files pending upload : 0
Size of XML files pending upload(MB) : 0.00
Available disk space on upload filesystem : 18.13%
Last successful heartbeat to OMS : 2008-04-24 12:39:27
---------------------------------------------------------------
Agent is Running and Ready

Problem with Management Agent 10g Upload


I had a problem adding another target to Oracle Grid Control 10g. The Management Agent was installed by agentDownload script, the installation finished successfully, but I the target didn’t show up in the Host list of Grid Control web page.

I checked the status of agent on target host:
gc@agenthost $ emctl status agent
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
Agent Version : 10.2.0.4.0
OMS Version : 10.2.0.1.0
Protocol Version : 10.2.0.0.0
Agent Home : /u01/app/gc/product/agent10g
Agent binaries : /u01/app/gc/product/agent10g
Agent Process ID : 26774
Parent Process ID : 26765
Agent URL : https://agenthost.domain.com:3872/emd/main/
Repository URL : https://omshost:1159/em/upload
Started at : 2008-04-24 12:20:17
Started by user : gc
Last Reload : 2008-04-24 12:20:17
Last successful upload : (none)
Last attempted upload : (none)

Total Megabytes of XML files uploaded so far : 0.00
Number of XML files pending upload : 6
Size of XML files pending upload(MB) : 5.88
Available disk space on upload filesystem : 18.10%
Last successful heartbeat to OMS : 2008-04-24 12:20:26
---------------------------------------------------------------
Agent is Running and Ready
The status report showed that Agent could NOT upload to OMS, so I tried to manually upload to OMS:
gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload error: uploadXMLFiles skipped :: OMS version not checked yet..
I checked the agent logs and came across this message in ORACLE_HOME/sysman/log/emagent.trc:
2008-04-23 19:54:26,063 Thread-20 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
2008-04-23 19:54:26,064 Thread-20 ERROR pingManager: nmepm_pingReposURL: Cannot connect to https://omshost:1159/em/upload: retStatus=-32
2008-04-23 19:54:26,067 Thread-20 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
2008-04-23 19:54:26,067 Thread-20 ERROR pingManager: nmepm_pingReposURL: Cannot connect to https://omshost:1159/em/upload: retStatus=-32
2008-04-23 19:54:26,359 Thread-5 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
2008-04-23 19:54:26,359 Thread-5 ERROR command: nmejcn: failed http connection to https://omshost:1159/em/upload: retStatus=-32
2008-04-23 19:54:28,368 Thread-5 ERROR http: snmehl_connect: Failed to get address for omshost: Non-Authoritive Host not found (error = 2)
The omshost could not be resolved from agenthost:
gc@agenthost $ ping -a omshost
ping: unknown host omshost
After configuring /etc/hosts (adding 555.555.555.555 omshost), and re-trying the upload, upload was successful:
gc@agenthost $ ping -a omshost
omshost (555.555.555.555) is alive

gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload error: Upload timed out before completion.
Number of files to upload before the upload: 13, total size (MB): 5.25.
Remaining number of files to upload: 13, total size (MB): 5.25.

gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload error: Upload timed out before completion.
Number of files to upload before the upload: 6, total size (MB): 1.38.
Remaining number of files to upload: 6, total size (MB): 1.38.

gc@agenthost $ emctl upload
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
EMD upload completed successfully
The agent status shows that upload is successful now:
gc@agenthost $ emctl status agent
Oracle Enterprise Manager 10g Release 4 Grid Control 10.2.0.4.0.
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
---------------------------------------------------------------
Agent Version : 10.2.0.4.0
OMS Version : 10.2.0.1.0
Protocol Version : 10.2.0.0.0
Agent Home : /u01/app/gc/product/agent10g
Agent binaries : /u01/app/gc/product/agent10g
Agent Process ID : 26774
Parent Process ID : 26765
Agent URL : https://agenthost.domain.com:3872/emd/main/
Repository URL : https://omshost:1159/em/upload
Started at : 2008-04-24 12:20:17
Started by user : gc
Last Reload : 2008-04-24 12:20:17
Last successful upload : 2008-04-24 12:39:02Total Megabytes of XML files uploaded so far : 7.44
Number of XML files pending upload : 0
Size of XML files pending upload(MB) : 0.00
Available disk space on upload filesystem : 18.13%
Last successful heartbeat to OMS : 2008-04-24 12:39:27
---------------------------------------------------------------
Agent is Running and Ready

Installation of Oracle Application Server 10g in Windows Vista

Installation of Oracle Application Server 10g in Windows Vista failed with the following error:

Starting Oracle Universal Installer...

Checking installer requirements...

Checking operating system version: must be 5.0, 5.1 or 5.2 . Actual 6.0
Failed <<<<

Exiting Oracle Universal Installer, log for this session can be found at C:\User
s\Raku\AppData\Local\Temp\OraInstall2008-03-22_02-00-54AM\installActions2008-03-
22_02-00-54AM.log

Please press Enter to exit...


It's a pity that OAS 10g is not supported in Windows Vista.
However, you can add 6.0 (for Vista) in install/oraparam file:
Windows=5.0,5.1,5.2,6.0
and restart the installation.
There is, of course, no guarantee that it will work error-free.
I installed the same way, I will look if it works correctly.