Tuxedo. xaorecover: xaofetch rtn -3

Short note.

In wery rare cases when Tuxedo server was starting you can get error ‘xaorecover: xaofetch rtn -3’.

Solution: Clear old XA transactions:

select 'rollback force '''||local_tran_id||''' ;' from dba_2pc_pending ;
select 'exec dbms_transaction.purge_lost_db_entry('''||local_tran_id||''' )' , 'commit;' from dba_2pc_pending ;

Manually Resolving In-Doubt Transactions: Different Scenarios (Doc ID 126069.1)

Tuxedo xaorecover: xaofetch rtn -3

Коротенькая заметка.

При старте сервера приложения Tuxedo получаем ошибку:

ORACLE XA: Version 19.0.0.0.0. RM name = 'Oracle_XA'.
 160307.273415.2676717376.0:
ORA-01405: fetched column value is NULL
  
160307.273415.2676717376.0:
xaorecover: xaofetch rtn -3.

Для решения проблемы нужно зачистить dba_2pc_pending :

select 'rollback force '''||local_tran_id||''' ;' from dba_2pc_pending ;
select 'exec dbms_transaction.purge_lost_db_entry('''||local_tran_id||''' )' , 'commit;' from dba_2pc_pending ;

Manually Resolving In-Doubt Transactions: Different Scenarios (Doc ID 126069.1)

Export из 19.13, Import в 19.9.  ORA-02376: invalid or redundant resource

Сделав экcпорт FULL=y CONTENT=metadata_only из бд 19.13, не получается залить дамп в 19.9 по причине ORA-02376: invalid or redundant resource.

На MOS по ошибке ORA-02376 есть нота,  DataPump Import Fails With Duplicated Resources In The Create Profile Statement (Doc ID 557383.1), но она не про этот случай.

Корень проблемы в том, что с 19.12 в профиле для пользователей появилась опция PASSWORD_ROLLOVER_TIME в рамках  Gradual Database Password Rollover.

При попытке провести импорт дампа, снятого из 19.12 и выше, в бд версии 19.11 и ниже (я проверил только 19.9) создание профиля ломается на ошибке ORA-02376, а далее не создаются пользователи и т.д.

Создать вручную профиль с опцией PASSWORD_ROLLOVER_TIME в 19.9 тоже не получится, эта версия просто не знает про такие опции.

Экспорт с параметром version=19.9, или 19.0 или 12.0 проблему не решает… Переносим профиля отдельно, и заливаем дамп.

“Это же экcпорт/импорт, что может пойти не так?” ©

Oracle DB мониторинг. Часть 1.

upd. 25.03.2025, данная статья устарела, но полезна для общего развития, т.к. здесь была положена идея(в 2021г), которая преобразовалась в kPerf for Oracle

—————————————————————————————————————

Контроль стабильности работы бд требует мониторинга и понимания метрик этого мониторинга. В связи с этим я написал свой небольшой снапер метрик бд и вложил в него ‘своё’ восприятие этих метрик для своих нужд, но возможно общая идея будит интересна ещё кому ни будь.

Мониторинг состоит из нескольких компонентов:

  1. Набор таблиц
  2. Задание по расписанию для заполнения таблиц из пункта 1
  3. Получение данных из таблиц пункта 1 и передача этих данных.
  4. Grafana
  5. Prometheus
  6. OracleDB exporter

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

  1. Набор таблиц.

1.1

CREATE SEQUENCE system.kks$sys_wait_event_sequence
  MINVALUE 1
  MAXVALUE 999999999999999999999999999
  START WITH 1
  INCREMENT BY 1
  CACHE 20;

create table system.kks$system_wait_event(
wait_class varchar2(256),
TOTAL_WAITS_sum number,
TIME_WAITED_MICRO_sum number,
snap_time date default sysdate,
snap_id number
)
tablespace sysaux;

create index SYSTEM.snap_time_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT (snap_time) tablespace sysaux;
create index SYSTEM.wait_class_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT (wait_class) tablespace sysaux;
create index SYSTEM.snap_id_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT (snap_id) tablespace sysaux;

1.2

CREATE SEQUENCE system.kks$sys_wait_event_sequence_p1
  MINVALUE 1
  MAXVALUE 999999999999999999999999999
  START WITH 1
  INCREMENT BY 1
  CACHE 20;
  
create table system.kks$system_wait_event_p1(
wait_event varchar2(256),
TOTAL_WAITS_sum number,
TIME_WAITED_MICRO_sum number,
snap_time date default sysdate,
snap_id number
)
tablespace sysaux;

create index SYSTEM.snap_time_p1_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT_p1 (snap_time) tablespace sysaux;
create index SYSTEM.wait_event_p1_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT_p1 (wait_event) tablespace sysaux;
create index SYSTEM.snap_p1_id_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT_p1 (snap_id) tablespace sysaux;

1.3

CREATE SEQUENCE system.kks$sys_wait_event_sequence_p2
  MINVALUE 1
  MAXVALUE 999999999999999999999999999
  START WITH 1
  INCREMENT BY 1
  CACHE 20;
  
create table system.kks$system_wait_event_p2(
wait_event varchar2(256),
TOTAL_WAITS_sum number,
TIME_WAITED_MICRO_sum number,
snap_time date default sysdate,
snap_id number
)
tablespace sysaux;

create index SYSTEM.snap_time_p2_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT_p2 (snap_time) tablespace sysaux;
create index SYSTEM.wait_event_p2_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT_p2 (wait_event) tablespace sysaux;
create index SYSTEM.snap_p2_id_ix on SYSTEM.KKS$SYSTEM_WAIT_EVENT_p2 (snap_id) tablespace sysaux;

2. Задание по расписанию для заполнения таблиц из пункта 1

2.1

Заполняем таблицу из пункта 1.1. Делаем снапшот суммарного количества ожиданий по каждому классу ожиданий(WAIT_CLASS) и суммарного количества времени отведённого для каждого класса ожиданий(WAIT_CLASS), устаревшие данные удалям.

declare
  seqno number;
  begin
    select system.kks$sys_wait_event_sequence.nextval into seqno from dual;

  insert into system.kks$system_wait_event(wait_class,total_waits_sum,time_waited_micro_sum,snap_id)
   select w.WAIT_CLASS,sum(w.TOTAL_WAITS) totalwaits,sum(w.TIME_WAITED_MICRO) mcsec,seqno from v$system_event w
  where w.WAIT_CLASS!='Idle'
  group by w.WAIT_CLASS;
  insert into system.kks$system_wait_event(wait_class,time_waited_micro_sum,snap_id)
  select o.STAT_NAME,o.VALUE,seqno from V$SYS_TIME_MODEL o where o.STAT_NAME='DB CPU';
  commit;
  delete from system.kks$system_wait_event p1 where p1.snap_time<=sysdate-30/24/60;
  commit;
  end;

2.2

Заполняем таблицу из пункта 1.2. Делаем снапшот суммарного количества ожиданий по каждому ожиданию(EVENT) и суммарного количества времени отведённого для каждого ожидания(EVENT), устаревшие данные удалям.

declare
  seqno number;
  begin
    select system.kks$sys_wait_event_sequence_p1.nextval into seqno from dual;

  insert into system.kks$system_wait_event_p1(wait_event,total_waits_sum,time_waited_micro_sum,snap_id)
   select w.EVENT,sum(w.TOTAL_WAITS) totalwaits,sum(w.TIME_WAITED_MICRO) mcsec,seqno from v$system_event w
  where w.WAIT_CLASS!='Idle' and w.EVENT!='SQL*Net message to client'
  group by w.EVENT;
  insert into system.kks$system_wait_event_p1(wait_event,time_waited_micro_sum,snap_id)
  select o.STAT_NAME,o.VALUE,seqno from V$SYS_TIME_MODEL o where o.STAT_NAME='DB CPU';
  commit;
  delete from system.kks$system_wait_event_p1 p1 where p1.snap_time<=sysdate-30/24/60;
  commit;
  end;

2.3

Заполняем таблицу из пункта 1.3. Делаем снапшот суммарного количества ожиданий по каждому ожиданию(EVENT) и суммарного количества времени отведённого для каждого ожидания(EVENT), устаревшие данные удалям.

declare
  seqno number;
  begin
    select system.kks$sys_wait_event_sequence_p2.nextval into seqno from dual;
  insert into system.kks$system_wait_event_p2(wait_event,total_waits_sum,time_waited_micro_sum,snap_id)
   select w.EVENT,sum(w.TOTAL_WAITS) totalwaits,sum(w.TIME_WAITED_MICRO) mcsec,seqno from v$system_event w
  where w.WAIT_CLASS!='Idle' and w.EVENT!='SQL*Net message to client'
  group by w.EVENT;
  commit;
  delete from system.kks$system_wait_event_p2 p1 where p1.snap_time<=sysdate-30/24/60;
  commit;
  end;

Я заполняю эти таблицы каждые 5сек. Возможно 2.3 избыточен, т.к. почти такие же данные есть в 2.2. Но тут кроется ‘моя’ особая логика, 2.2 нужен для анализа ожиданий в процентном отношении с учётом DB CPU.

3. Получение данных из таблиц пункта 1 и передача этих данных.

Далее, используется https://github.com/iamseth/oracledb_exporter в качестве передачи метрик в Prometheus.

3.1

  1. Получение значений ожиданий по каждому классу ожиданий, выраженное в процентах. Сумма всех классов ожиданий не превышает 100%
[[metric]]
    context = "db_wait_event"
    metricsdesc= { value="System wait event time Diff in %" }
	fieldtoappend= "wait_class"
	request = '''
    with 
    waitpcts1 as ( 
    select sum(p.time_waited_micro_sum) as timewait1, p.snap_id as snap_id1  from system.kks$system_wait_event p
    where p.snap_id=(select max(snap_id) from system.kks$system_wait_event)
    group by p.snap_id),
    waitpcts2 as ( 
    select sum(p.time_waited_micro_sum) as timewait2,p.snap_id as snap_id2  from system.kks$system_wait_event p
    where p.snap_id=(select max(snap_id)-1 from system.kks$system_wait_event)
    group by p.snap_id),
    wait1 as ( 
    select p.wait_class as wait_class1,p.time_waited_micro_sum as time_waited_micro_sum1,p.snap_id as snap_id1  from system.kks$system_wait_event p
    where p.snap_id=(select max(snap_id) from system.kks$system_wait_event)),
    wait2 as ( 
    select p.wait_class as wait_class2,p.time_waited_micro_sum as time_waited_micro_sum2,p.snap_id as snap_id12 from system.kks$system_wait_event p
    where p.snap_id=(select max(snap_id)-1 from system.kks$system_wait_event))
    select wait1.wait_class1 as "wait_class",
	round(((wait1.time_waited_micro_sum1-wait2.time_waited_micro_sum2)/decode((waitpcts1.timewait1-waitpcts2.timewait2),0,1,(waitpcts1.timewait1-waitpcts2.timewait2)))*100) as "value"
    from wait1,wait2,waitpcts1,waitpcts2
    where wait1.wait_class1=wait2.wait_class2
    '''

Как это выглядит:

Рис.1

3.2 Получение значения времени по каждому ожиданию(event)(сколько времени потрачено на одно ожидание) выраженное в процентах. Сумма всех ожиданий не превышает 100%.

[[metric]]
    context = "event_p1"
    metricsdesc= { value="System wait event P1 Diff in % }
	fieldtoappend= "wait_event_p1"
	request = '''
    with 
    waitpcts1 as ( 
    select sum(p.time_waited_micro_sum) as timewait1, p.snap_id as snap_id1  from system.kks$system_wait_event_p1 p
    where p.snap_id=(select max(snap_id) from system.kks$system_wait_event_p1)
    group by p.snap_id),
    waitpcts2 as ( 
    select sum(p.time_waited_micro_sum) as timewait2,p.snap_id as snap_id2  from system.kks$system_wait_event_p1 p
    where p.snap_id=(select max(snap_id)-1 from system.kks$system_wait_event_p1)
    group by p.snap_id),
    wait1 as ( 
    select p.wait_event as wait_event1,p.time_waited_micro_sum as time_waited_micro_sum1,p.snap_id as snap_id1  from system.kks$system_wait_event_p1 p
    where p.snap_id=(select max(snap_id) from system.kks$system_wait_event_p1)),
    wait2 as ( 
    select p.wait_event as wait_event2,p.time_waited_micro_sum as time_waited_micro_sum2,p.snap_id as snap_id12 from system.kks$system_wait_event_p1 p
    where p.snap_id=(select max(snap_id)-1 from system.kks$system_wait_event_p1))
    select replace(replace(replace(wait1.wait_event1,':','_'),'-','_'),'&','_')||'_p1' as "wait_event_p1",
	ceil(((wait1.time_waited_micro_sum1-wait2.time_waited_micro_sum2)/decode((waitpcts1.timewait1-waitpcts2.timewait2),0,1,(waitpcts1.timewait1-waitpcts2.timewait2)))*100) as "value"
    from wait1,wait2,waitpcts1,waitpcts2
    where wait1.wait_event1=wait2.wait_event2
    and ceil(((wait1.time_waited_micro_sum1-wait2.time_waited_micro_sum2)/decode((waitpcts1.timewait1-waitpcts2.timewait2),0,1,(waitpcts1.timewait1-waitpcts2.timewait2)))*100)>0
    '''

Как это выглядит:

Рис.2

3.3 Сколько времени потрачено на одно событие ожидания(event) в микросекундах.

[[metric]]
    context = "event_p2"
    metricsdesc= { value="System wait event time P2 Diff in micsec for 4sec." }
	fieldtoappend= "wait_event_p2"
	request = '''
    with 
    waitpcts1 as ( 
    select p.wait_event as waitevent1,
    p.time_waited_micro_sum as timewait1,
    p.total_waits_sum as totalwaits1,
    p.snap_id as snap_id1  
    from system.kks$system_wait_event_p2 p
    where p.snap_id=(select max(snap_id) from system.kks$system_wait_event_p2)),
    waitpcts2 as ( 
    select p.wait_event as waitevent2,
    p.time_waited_micro_sum as timewait2,
    p.total_waits_sum as totalwaits2,
    p.snap_id as snap_id2  
    from system.kks$system_wait_event_p2 p
    where p.snap_id=(select max(snap_id)-1 from system.kks$system_wait_event_p2))
    select replace(replace(replace(waitpcts1.waitevent1,':','_'),'-','_'),'&','_')||'_p2' as "wait_event_p2",
	ceil(((waitpcts1.timewait1-waitpcts2.timewait2)/decode((waitpcts1.totalwaits1-waitpcts2.totalwaits2),0,1,(waitpcts1.totalwaits1-waitpcts2.totalwaits2)))) as "value"
    from waitpcts1,waitpcts2
    where waitpcts1.waitevent1=waitpcts2.waitevent2
    and ceil(((waitpcts1.timewait1-waitpcts2.timewait2)/decode((waitpcts1.totalwaits1-waitpcts2.totalwaits2),0,1,(waitpcts1.totalwaits1-waitpcts2.totalwaits2))))>0
    '''

Как это выглядит:

Рис.3

3.3 Количество событий по каждому ожиданию(event) в секунду

[[metric]]
    context = "event_p3"
    metricsdesc= { value="System wait events P3 Diff for 4sec." }
	fieldtoappend= "wait_event_p3"
	request = '''
    with 
    waitpcts1 as ( 
    select p.wait_event as waitevent1,
    p.total_waits_sum as totalwaits1,
    p.snap_id as snap_id1  
    from system.kks$system_wait_event_p2 p
    where p.snap_id=(select max(snap_id) from system.kks$system_wait_event_p2)),
    waitpcts2 as ( 
    select p.wait_event as waitevent2,
    p.total_waits_sum as totalwaits2,
    p.snap_id as snap_id2  
    from system.kks$system_wait_event_p2 p
    where p.snap_id=(select max(snap_id)-1 from system.kks$system_wait_event_p2))
    select 
    replace(replace(replace(waitpcts1.waitevent1,':','_'),'-','_'),'&','_')||'_p2' as "wait_event_p3",
   ceil((waitpcts1.totalwaits1-waitpcts2.totalwaits2)/4) as "value"
    from waitpcts1,waitpcts2
    where waitpcts1.waitevent1=waitpcts2.waitevent2 and ceil(waitpcts1.totalwaits1-waitpcts2.totalwaits2)>0
'''

Как это выглядит:

Рис.4

upd. 25.03.2025, данная статья устарела, но полезна для общего развития, т.к. здесь была положена идея(в 2021г), которая преобразовалась в kPerf for Oracle

How to masking sensitive data(for example PAN) using GoldenGate.

In this short scratch i want to show you two rule to masking data using GoldenGate.

Source extract capture test data:
table tuser.*;

  1. Data masking on replicat level:
map TUSER.tab3, target TUSER.tab4, &
SQLEXEC (ID sub3, &
QUERY 'select decode(:PANval,null,''IsNull'',substr(:PANval,1,6))||decode(:PANval,null,''Value'',substr(:PANval,-4)) subpan from dual', &
PARAMS (PANval=PANID)), &
colmap(USEDEFAULTS,PANID=@getval(sub3.subpan));

MAP TUSER.tab3, TARGET TUSER.tab3, colmap(USEDEFAULTS,PANID=@STRCAT(@STREXT(PANID,1,6),'******',@STREXT(PANID,13,16)));

How it works:

On source i made inserts:

	insert into TUSER.tab3(PANID,DESCSTR) values('1444555566667779','testPAN');
	insert into TUSER.tab3(PANID,DESCSTR) values('2444555566667779','testPAN');
	insert into TUSER.tab3(PANID,DESCSTR) values('3444555566667779','testPAN');
commit;

Now, in receiver, target TUSER.tab4 use map rule with SQLEXEC and QUERY clause, and target TUSER.tab3 use map rule with only Goldengate function.

Result on receiver:

SQL> select panid, 'tab4' as tabname from TUSER.tab4
  2  union all
  3  select panid, 'tab3' as tabname from TUSER.tab3
  4  ;
 
PANID                TABNAME
-------------------- -------
1444557779           tab4
2444557779           tab4
3444557779           tab4
144455******7779     tab3
244455******7779     tab3
344455******7779     tab3
 
6 rows selected

How data looks like on Kafka side(can also be receiver) after masking if using rule:

MAP TUSER.tab3, TARGET TUSER.tab3, colmap(USEDEFAULTS,PANID=@STRCAT(@STREXT(PANID,1,6),'******',@STREXT(PANID,13,16)));

Result on Kafka:

{"table":"TUSER.TAB3","op_type":"I","op_ts":"2021-06-27 16:17:20.980191","current_ts":"2021-06-27T16:17:26.971000","pos":"00000000000000003108","after":{"PANID":"144455******7779","DESCSTR":"testPAN"}}
{"table":"TUSER.TAB3","op_type":"I","op_ts":"2021-06-27 16:17:23.980065","current_ts":"2021-06-27T16:17:29.983000","pos":"00000000000000003258","after":{"PANID":"244455******7779","DESCSTR":"testPAN"}}
{"table":"TUSER.TAB3","op_type":"I","op_ts":"2021-06-27 16:17:27.980179","current_ts":"2021-06-27T16:17:33.992000","pos":"00000000000000003409","after":{"PANID":"344455******7779","DESCSTR":"testPAN"}}

How to connect ASM disks to new one server 19.X ASM

Just a little ‘face lifting’ old one instruction how to connect asm disks from old to new server.

  1. Install Grid software only
  2. execute as root user(standalone instance): $ORACLE_HOME/perl/bin/perl -I $ORACLE_HOME/perl/lib -I $ORACLE_HOME/crs/install $ORACLE_HOME/crs/install/roothas.pl
  3. install asm OS rpm pakage
  4. connect disks from old server to new server
  5. check ORACLEASM_SCANORDER in /etc/sysconfig/oracleasm Doc ID 1384504.1, to discover your disks
  6. add asm instance in clusterware: srvctl add asm
  7. start asm: srvctl start asm
  8. open asmca or sqlplus and check/mount diskgroup

Note: Diskgroups across server have to be unique, if disks from old server has same names as on new one, you have to rename it.

Фрагментация таблиц в бд Oracle 12c, которой нет в 19с. Часть 1.

Окружение:

OS: OEL 7.8

DB: 12.1.0.2.190716 BP,  2. 19.7.0.0.200414 RU

При выполнений пакетных заданий и не только было замечено интересное — фрагментация таблиц, где казалось бы её не должно быть(или должно?).

Для проверки влияния был составлен тест из 3 циклов:

 1 цикл — главный.

2 цикл находится внутри цикла 1, выполняет вставку.

3 цикл находится внутри цикла 1, выполняет удаление.

Количество проходов циклов 2 и 3 одинаково и задаётся при старте, циклы выполняются друг за другом, commit выполняется после каждой удалённой или вставленной строки.

Тест проводится с участим трёх таблиц(KKS$FRAG_512B, KKS$FRAG_1024B, KKS$FRAG_2048B), в которых длина вставляемой строки заранее известна, это 512байт, 1024байт и 2048байта. Размер сегмента берётся из dba_segments.

Исходный код теста

Для таблиц KKS$FRAG_512B, KKS$FRAG_1024B, KKS$FRAG_2048B было выполнено:

Количество цикловКоличество операций вставок и удалений в каждом цикле
3032003
6061006
606706
606506
606106
Таб.1
  1. Результат для KKS$FRAG_512B:
12.1.0.2.190716 BP, занято байт в таблице KKS$FRAG_512B33554432
12.1.0.2.190716 BP, занято блоков в таблице KKS$FRAG_512B4096
19.7.0.0.200414 RU, занято байт в таблице KKS$FRAG_512B7340032
19.7.0.0.200414 RU, занято блоков в таблице KKS$FRAG_512B896
Таб.2 KKS$FRAG_512B

Различие в размере сегмента составляет 4,5 раза.

2. Результат для KKS$FRAG_1024B:

12.1.0.2.190716 BP, занято байт в таблице KKS$FRAG_1024B33554432
12.1.0.2.190716 BP, занято блоков в таблице KKS$FRAG_1024B4096
19.7.0.0.200414 RU, занято байт в таблице KKS$FRAG_1024B9437184
19.7.0.0.200414 RU, занято блоков в таблице KKS$FRAG_1024B1152
Таб.3 KKS$FRAG_1024B

Отличие скромнее, но оно по прежнему существенно — в 3,5 раза.

3. Результат для KKS$FRAG_2048B:

12.1.0.2.190716 BP, занято байт в таблице KKS$FRAG_2048B19922944
12.1.0.2.190716 BP, занято блоков в таблице KKS$FRAG_2048B2432
19.7.0.0.200414 RU, занято байт в таблице KKS$FRAG_2048B16777216
19.7.0.0.200414 RU, занято блоков в таблице KKS$FRAG_2048B2048
Таб.4 KKS$FRAG_2048B

Разница не велика, но она есть: 1,18 раза

4. Другой профиль нагрузки для KKS$FRAG_512B.

Количество цикловКоличество операций вставок и удалений в каждом цикле
20021002
Таб.5 KKS$FRAG_512B
12.1.0.2.190716 BP, занято байт в таблице KKS$FRAG_512B46137344
12.1.0.2.190716 BP, занято блоков в таблице KKS$FRAG_512B5632
19.7.0.0.200414 RU, занято байт в таблице KKS$FRAG_512B720896
19.7.0.0.200414 RU, занято блоков в таблице KKS$FRAG_512B88
Таб.6 KKS$FRAG_2048B

Разница: в 64 раза

5. Другой профиль нагрузки для KKS$FRAG_1024B.

Количество цикловКоличество операций вставок и удалений в каждом цикле
20021002
Таб.7 KKS$FRAG_1024B

Результат:

12.1.0.2.190716 BP, занято байт в таблице KKS$FRAG_1024B262144
12.1.0.2.190716 BP, занято блоков в таблице KKS$FRAG_1024B32
19.7.0.0.200414 RU, занято байт в таблице KKS$FRAG_1024B196608
19.7.0.0.200414 RU, занято блоков в таблице KKS$FRAG_1024B24
Таб.6 KKS$FRAG_1024B

Разница: в 1.3 раза

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

XDB SGA reset to NULL, ORA-00600: qmtb_init_len

Oracle: 12.1.0.2.190716 BP

OS: Sparc Solaris 11.4

После старта бд и подключения приложения в алерт логе появляются ошибки:

XDB SGA reset to NULL.

ORA-00600: internal error code, arguments: [qmtb_init_len], [277], [276]

А сессии приложения находятся в ожидании XDB SGA initialization.

Если поискать на MOS по этим входным данным, то все они сводятся к выставлению переменной окружения LD_LIBRARY_PATH / LIBPATH / SHLIB_PATH(в зависимости от платформы), либо, идёт указание на то, что компонент XDB повреждён, в следствии апгрейда либо ещё чего, и нужно его переставить/исправить.

Но в данном случае все выше перечисленные решения на MOS не рабочие.

Ларчик открывается легко, просто нужно подождать, 5-10мин после входа первой сессии от приложения, после чего в алертлоге фиксируется:

XDB installed.

XDB initialized.

после чего ошибка не повторяется.

Данное долгое инициализирование XDB замечено только на Solaris.

Oracle ASM. Что будет представлять из себя дисковая группа при различной конфигурации дисков. Часть 2.

Часть 1

Я буду отключать диски, и наблюдать, как поведёт себя ASM и база данных, а также данная статья призвана пролить свет на количественные характеристики показателей свободного, занятого, и прочего места в ASM, при проведение эксперимента и ответить на вопрос ‘а что если..?’.

Версия ASM 12.1.0.2.190716

Конфигурация: Дисковая группа из шести дисков и двух Failgroup, по 3 диска в каждой FG, с избыточностью Normal.

SQL> create diskgroup kksdg1 normal redundancy failgroup fg1 disk 'ORCL:DISK1','ORCL:DISK2','ORCL: DISK7' failgroup fg2 disk 'ORCL:DISK3','ORCL:DISK4','ORCL:DISK4N';

Diskgroup created.
рис1

Req_mir_free_MB  — зарезервировал по одному диску на каждую failgroup, и того зарезервировано 2 диска.

Расчет: Usable_file_MB  =( Free_MB  — Req_mir_free_MB  )/2= 10 183

1.1 Заполним дисковую группу:

SQL> create tablespace kkstbs1 datafile '+kksdg1' size 7168M;

Tablespace created.

Elapsed: 00:00:16.42

Взглянем на дисковую группу:

рис2

Все диски в дисковой группе заполнились равномерно(внимание на колонку Free_MB).

1.2 Проверим устойчивость дисковой группы к выходу из строя дисков:

Я выставил ‘DISK_REPAIR_TIME’=’1m’ для дисковой группы.

1.2.1 Отключаем первый диск(DISK1):

[root@kks grid12]# oracleasm querydisk -d DISK1
Disk "DISK1" is a valid ASM disk on device [8,81]
[root@kks grid12]# ls -l /dev/* | grep 8, | grep 81
brw-rw---- 1 root disk      8,  81 Aug  5 15:56 /dev/sdf1
[root@kks grid12]# echo 1 > /sys/block/sdf/device/delete

ASM Alert log:

Wed Aug 05 16:24:36 2020
Errors in file /grid/app/diag/asm/+asm/+ASM/trace/+ASM_gmon_2966.trc:
ORA-15186: ASMLIB error function = [kfk_asm_ioerror],  error = [0],  mesg = [I/O Error]
Wed Aug 05 16:24:36 2020
NOTE: PST update grp = 2 completed successfully 
Wed Aug 05 16:25:22 2020
WARNING: Started Drop Disk Timeout for Disk 0 (DISK1) in group 2 with a value 60
WARNING: Disk 0 (DISK1) in group 2 will be dropped in: (60) secs on ASM inst 1

DB Alert log:

Wed Aug 05 16:24:36 2020
NOTE: updating disk modes to 0x5 from 0x7 for disk 0 (DISK1) in group 2 (KKSDG1): lflags 0x0    
NOTE: disk 0 (DISK1) in group 2 (KKSDG1) is offline for reads
NOTE: updating disk modes to 0x1 from 0x5 for disk 0 (DISK1) in group 2 (KKSDG1): lflags 0x0    
NOTE: disk 0 (DISK1) in group 2 (KKSDG1) is offline for writes
ри3

ASM Alert log:

Wed Aug 05 16:28:29 2020
SUCCESS: alter diskgroup KKSDG1 drop disk DISK1 force /* ASM SERVER */
рис3

По прошествии нескольких минут диск был удалён, ребаланс прошел успешно.

1.2.2 Отключаем второй диск(DISK2):

[root@kks grid12]# oracleasm querydisk -d DISK2
Disk "DISK2" is a valid ASM disk on device [8,129]
[root@kks grid12]# ls -l /dev/* | grep 8, | grep 129
brw-rw---- 1 root disk      8, 129 Aug  5 15:56 /dev/sdi1
[root@kks grid12]# echo 1 > /sys/block/sdi/device/delete

ASM Alert log:

Wed Aug 05 16:34:44 2020
WARNING: Started Drop Disk Timeout for Disk 1 (DISK2) in group 2 with a value 60
WARNING: Disk 1 (DISK2) in group 2 will be dropped in: (60) secs on ASM inst 1
Errors in file /grid/app/diag/asm/+asm/+ASM/trace/+ASM_gmon_2966.trc:
ORA-15186: ASMLIB error function = [kfk_asm_ioerror],  error = [0],  mesg = [I/O Error]
…………………………………..
Wed Aug 05 16:37:51 2020
SUCCESS: alter diskgroup KKSDG1 drop disk DISK2 force /* ASM SERVER */

DB Alert log:

Wed Aug 05 16:32:47 2020
NOTE: updating disk modes to 0x5 from 0x7 for disk 1 (DISK2) in group 2 (KKSDG1): lflags 0x0    
NOTE: disk 1 (DISK2) in group 2 (KKSDG1) is offline for reads
NOTE: updating disk modes to 0x1 from 0x5 for disk 1 (DISK2) in group 2 (KKSDG1): lflags 0x0    
NOTE: disk 1 (DISK2) in group 2 (KKSDG1) is offline for writes
рис5

На данном этапе получилось, что ребаланc завешается ошибкой ORA-15041, недостаточно места в дисковой группе и в FG1 в частности, чтобы провести ребаланс.

Данный диск _DROPPED_0001_KKSDG1  не удаляется, а переименовался, бд по-прежнему доступна и таблицы в табличном пространстве kkstbs1 тоже живы, также Usable_file_MB  в минусе.

В Alert log BD:

Wed Aug 05 16:37:51 2020
SUCCESS: disk DISK2 (1.3916017828) renamed to _DROPPED_0001_KKSDG1 in diskgroup KKSDG

1.2.3 Отключаем третий диск(DISK7):

[root@kks grid12]# oracleasm querydisk -d DISK7
Disk "DISK2" is a valid ASM disk on device [8, 161]
[root@kks ~]# ls -l /dev/* | grep 8, | grep 161
brw-rw---- 1 root disk      8, 161 Aug  6 13:17 /dev/sdk1
[root@kks ~]# echo 1 > /sys/block/sdk/device/delete
рис5

Остановим бд и перемонтируем дисковую группу, проверим, не потеряю ли я группу на данном этапе:

SQL> alter diskgroup kksdg1 dismount;
Diskgroup altered.
SQL> alter diskgroup kksdg1 mount;
Diskgroup altered.
рис6

Может показаться, что если невозможно провести ребаланс, по причине ошибки ORA-15041, ASM не удалят диски, а переименовывает их.

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

SUCCESS: grp 2 disk _DROPPED_0000_KKSDG1 going offline 
SUCCESS: grp 2 disk _DROPPED_0001_KKSDG1 going offline
NOTE: Disk _DROPPED_0000_KKSDG1 in mode 0x0 marked for de-assignment
NOTE: Disk _DROPPED_0001_KKSDG1 in mode 0x0 marked for de-assignment

диски удалятся, но последующие несколько экспериментов показали, что это не всегда так.

Какой в этом смысл? Зачем держать эти диски, которых фактически нет.

Также, если удалять дисковую группу через drop diskgroup force including contents, то повторное использование дисков не требует зачистки их заголовков через dd, если использовать просто drop diskgroup, то нужно чистить заголовок диска, чтобы его повторно использовать(это особенность asm 12), force можно применить если группа не замонтирована.

И на сладкое, двойное удаление дисковой группы(тест на то, что заголовки дисков живы после удаления группы):

SQL> drop diskgroup kksdg1 force including contents;
drop diskgroup kksdg1 force including contents
*
ERROR at line 1:
ORA-15039: diskgroup not dropped
ORA-15230: diskgroup 'KKSDG1' does not require the FORCE option


SQL> drop diskgroup kksdg1;

Diskgroup dropped.

SQL> drop diskgroup kksdg1 force including contents;
drop diskgroup kksdg1 force including contents
*
ERROR at line 1:
ORA-15039: diskgroup not dropped
ORA-15063: ASM discovered an insufficient number of disks for diskgroup
"KKSDG1"


SQL> alter diskgroup kksdg1 mount;
alter diskgroup kksdg1 mount
*
ERROR at line 1:
ORA-15032: not all alterations performed
ORA-15017: diskgroup "KKSDG1" cannot be mounted
ORA-15040: diskgroup is incomplete


SQL> drop diskgroup kksdg1 force including contents;  <-----  Здесь я вернул отключенные диски(просканировав шину SCSI ) и смогу удалить дисковую группу ещё раз 0_о

Diskgroup dropped.

SQL>

Вероятно будет часть 3 и версия asm 19, с применением утилит ASM tools used by Support : KFOD, KFED, AMDU (Doc ID 1485597.1), т.к. как всегда(у меня), вопросов больше, чем ответов.

Резкий рост log file sync и buffer busy wait при незначительном повышении нагрузки.

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

  1. High log file sync waits? Check log parallelism!

2.  Finding the root cause of “CPU waits” using stack profiling

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

Описание проблемы: после того как бд переехала на сервер с 4мя сокетами, по достижению определённой нагрузки, резко вырастали ожидания  log file sync и buffer busy wait.

Окружение: RHEL 7, Oracle 12.1.0.2 + BP, ASM 12.1.0.2 + BP

Как это выглядит:

AWR:

Lab128:

Промежутки времени для AWR и Lab128 взяты немного разные, поэтому есть разница в цифрах, но общей картины это не меняет, всё очень плохо.

1.      Первым делом решено было бороться с log file sync(LFS).

Из ссылки 1 я узнаю про параметр _log_parallelism_max.

Критерии, по которым можно попробовать оценить, есть ли смыл в изменении его значения по умолчанию:

1.      Ожидание log file sync гораздо выше(более чем в 2 раза) log file parallel write

Например, log file sync= 1мс, а log file parallel write=2мс и выше.

2.      Оценить значении log file parallel write P3(Number of I/O requests) из v$active_session_history/dba_active_session_history в нагруженный и не нагруженный период. P3 должен изменяться, от 1 до _log_parallelism_max т.е. быть 2,3,4,5…

3.      При повышении значения log file parallel write P3(Number of I/O requests), log file sync должен расти, но возможно не обязан.

4.      Количество потоков CPU больше, либо равно 64.

5.      Log File Parallel Writes Take Longer with a Higher CPU Count (Doc ID 1980199.1)

Я применил значение _log_parallelism_max=2

Результат:

AWR:

log file sync снизился с 5.68мс до 1.15мс(по AWR), казалось бы победа, но нет, присутствует ожидание buffer busy wait, которое оказывает сильное влияние на работу приложения, да и само значение log file sync всё ещё выше, чем на старом сервере, даже после улучшения.

2.   Здесь переходим ко второй части.

Ожидания buffer busy wait с работой приложения никак не связано, профиль нагрузки практически не менялся. Kоличественные характеристики полезной работы бд: dml выполненных в секунду, количество коммитов в секунду, LIO в секунду в целом выросли на 5%-10% и после переезда на 4 сокета, buffer busy wait раскрылся во всей красе, опять таки только по достижению определённой нагрузки(превышение на 5%-10%), если её не превышать, то всё хорошо.

Всё хорошо — это означает, что ожидания buffer busy wait стремятся к нулю, а log file sync значительно меньше 1мс (0.3-0.6мс) для работы под нужной нагрузкой.

Вышеописанное должно привести на мысль, что что-то не так с железом, либо проблема на уровне OS. Здесь на помощь приходит вторая ссылка, из которой становится понятно, что вероятно дело в kernel.numa_balancing. И действительно, если поискать на MOS используя kernel.numa_balancing, то получим:

  1. Requirements for Installing Oracle Database 19c on OL7 or RHEL7 64-bit (x86-64) (Doc ID 2551169.1)
  2. (EX39) NUMA-enabled database servers experience continuously high load or reduced performance after updating to Exadata 12.2.1.1.0 or higher. (Doc ID 2319324.1)

А также из общедоступных источников: Optimal Configuration of Memory System

https://support.huawei.com/enterprise/en/doc/EDOC1000117211/1395f4a4/optimal-configuration-of-memory-system

Откуда следует вывод, что без kernel.numa_balancing=0 никуда. И действительно, после применения данного параметра, производительность бд значительно выросла:

В итоге, конфигурация имеет значение _log_parallelism_max=2 на уровне бд и kernel.numa_balancing=0 на уровне ОС.

Также эксперимент был проведён на Solaris Sparc:

 Solaris Sparc : 11.4+patch

Oracle 12.1.0.2 + BP

ASM 19.7

И _log_parallelism_max=2 VS_log_parallelism_max=1:

Нагрузка тут уже в 3 раза выше, чем на Linux, а снижение LFS c 0.77 мс до 0.60 мс дало прирост быстродействия на стороне приложения до 30%.

Также _log_parallelism_max=1 снизил потребление ЦПУ на 7%.

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