UPDATE GLOBAL INDEXES

 Без категория  1 Response »
авг. 302010
 

Отдавна не съм споделял oracle-related приключения. Пусти мързел…

Днес ще разкажа за следната случка. На всеки час едно приложение „замръзва“ за няколко секунди. Всъщност идентифицирането на причините отне доста усилия. Видимите симптоми се появяват на ниво application server. Ровихме из настройките там, ровихме из мрежата, ОС, базите…

На края успяхме да идентифицираме следната зависимост: това приложение (покрай другите неща) използва и една супер натоварена таблица. Информацията в тази таблица се пази няколко часа, след това се изтрива; за това пък за тези няколко часа се натрупват доста данни – гигабайти. Операциите са предимно Insert и Select. Таблицата е разделена на partitions по часове, като на всеки час един job минава и затрива данните от най-стария partition с

alter table ... truncate partition ... UPDATE GLOBAL INDEXES

Това UPDATE GLOBAL INDEXES се налага, защото първичния ключ на таблицата има глобален индекс. Това е така, защото на partitioned таблици, уникалния индекс или трябва да е глобален, или да съдържа partition key колоните (логично).

Оказа се, обаче, че в тази схема има голям проблем. Странното „замръзване“ на приложението съвпада точно с времето, когато се стартира този този TRUNCATE job.

По-внимателно вглеждане в базата, съдържаща описаната таблица, показа, че в продължение на няколко секунди (почти) всички сесии, които се опитват да INSER-ват в тази натоварена таблица, чакат cursor: pin S wait on X. Има и малко library cache lock. Въпреки че говорим за времена от рода на 4-5-6 секунди, се натрупват един забележим брой сесии, които „висят“.

* * *

Какво се случва всъщност?

Disclaimer: следва обяснението, което аз приемам за вярно. Може и да бъркам нещо. Ако греша някъде, моля, поправете ме.

truncate (или в случая, alter table ... truncate partition) e DDL заявка. Като такава тя инвалидира всички курсори, които работят със съответната таблица. И когато дойде време за поредния insert (а такива има по десетки в секунда), първата сесия, която види невалидния курсор, тръгва да parse-ва. Тук говорим за parse на един елементарен insert на един ред в една таблица – би трябвало да стане за милисекунди. Обаче за да се случи parse, сесията трябва да „заключи“ за момент дефиницията на таблицата в library cache. В противен случай има опасност да parse-не, да измисли някакъв план, а някой друг да промени структурата на таблицата и плана да излезе неверен. Това заключване се случва с library cache lock.

Да, обаче в това време truncate-a си върви. insert сесията трябва да изчака truncate-a да свърши. Чак тогава може да получи заключваницата върху структурата на таблицата и да си направи parse-а. Цялата тази дандания е на пръв поглед ненужна при truncate – нали не променя по никакъв начин структурата на таблицата. Обаче е DDL. За това първия insert започва да чака.

Няколко милисекунди след това идва друга сесия, която също иска да insert-ва. Тази сесия иска да види дали има готов parse-нат план за заявката или не. Изчислява си hash на заявката, поглежда в library cache и за да е сигурна, удря един адски бърз lock на този hash. Във 10g/11g това става с използването на мутекси. Да, обаче в това време предишния insert още се мъчи да parse-не (всъщност чака). За това имаме невероятния шанс да видим заключване на мутекс – една операция, която трябва да става за микросекунди. Това чакане е известно като cursor: pin S wait on X.

Същото става и със следващата сесия, която опита insert. И със следващата. И със следващата…

* * *

По принцип truncate е много бърза операция. В крайна сметка тя е само промяна в data dictionary и не трябва да зависи от обема данни. Обаче в случая имаме един изроден truncate, който, освен нулирането на HWM, се занимава да оправя и разни индекси.

След като tuncate приключи, кълбото моментално се разплита в обратен ред. Но вече сме имали няколко секунди downtime на приложението. И това става на всеки час.

От Oracle са идентифицирали това положение като проблемно в bug 7640597. Даже са направили и пач. Идеята на пача е гениално проста и ефективна: инвалидирането на курсорите се случва в самия край на truncate операцията. Така те могат веднага да бъдат re-parse-нати.

За съжаление при пускането на този patch се оказва, че той има нежелани „странични ефекти“. За това да го оттеглили. Проблема е решен генерално в бъг 8441239, който е оправен в 11.2. Обаче няма backport за по-стари версии.

* * *

В този случай имаме няколко опции:

1. Да се откажем от TRUNCATE (и други алтернативни варианти, включващи DDL – примерно alter table exchange partition). Вместо това да използваме DELETE. Това ще доведе до много по-голямо натоварване на тази критична база; ще генерира огромно количество redo; и ще работи много по-бавно. Но понеже е прост DML, няма да инвалидира курсори.

2. Може да направим TRUNCATE по-бърз като направим partitions на половин час, или даже по-малко. Така всеки TRUNCATE ще има по-малко редове за оправяне в индекса, респективно – ще минава по-бързо. Това, обаче, само отлага проблема. При нарастване на обемите, или в някой по-натоварен момент, ще се озовем пред същата драма, а може и по-голяма.

3. Може да добавим колоната, по която се прави partitionig, като част от първичния ключ. В този случай индекса може да стане локален и TRUNCATE ще минава с обичайната си страшна скорост. Но това решение не е винаги възможно – зависи от самите данни и начина на използването им.

4. Може да upgrade-нем към 11.2. Oracle обещават, че там проблема е отстранен. Това, обаче, не е решение от днес за утре.

5. Може да настояваме за backport на patch-а. За целта трябва „a serious business justification“. Това, обаче, може да отнеме седмици или месеци; а и ще бъдем първите, които да изпитат новия patch в production.

Кой е верния път зависи от конкретната бизнес ситуация. За нуждите на блога няма да изпадам в подробности за нашия избор. Ще покажа, обаче, как може да тествате възможните решения, ако се натъкнете на този проблем.

Stay tuned!

 Posted by at 9:58

Ком

 Общи  Коментарите са изключени за Ком
авг. 162010
 

Успях да завърШа описанието на живописното изкачване на връх Ком, което предприехме предния уикенд. и то малко преди да ми се счупи блога 🙂

Който няма търпение, може да чете. Две от буквите в блога ми са счупени тотално – голямо „и“ на кирилица и малко „Ш“. Но не знам кога ще мога да ги оправя. До тогава може да се насладите на неземно красивите снимки, които направихме от върха 😉

 Posted by at 10:53

Слънчогледи

 Без категория  7 Responses »
юли 142010
 

Видях слънчогледите още на влизане в ХИТ-а. Бяха много и се продаваха на промоция – 5 броя за ? лева.

Обиколих из магазина и напазарувах всичко, което бях планирал, че и още толкова. Разтоварих количката в багажника и хукнах пак към входа, за да взема букет за Нея. Тя толкова обича слънчогледи…

Отидох при продавачката, която тъкмо правеше поредната връзка от 5 слънчогледа, и казах:
– Добър ден. Искам да купя слънчогледи, но 7, а не 5.
Не ме питайте защо исках 7. Просто букет от 7 ми изглежда по-добре. Но не и на каката, която ме отряза:
– Не може. Продават се само по 5.
Това наистина не го очаквах. Първо, аз съм клиент. Искам да купя и даже на питам за цената. Няма нещо като опашка и недостиг на слънчогледи, така че ограничаването на покупката ми прозвуча странно. Ако бях поискал по-малко, до някъде щях да разбера извратената им логика (да се опитат да ми продадат не по-малко от 5). Ако бяха всичките навързани, пак бих разбрал – мързи я да среже панделката. Но случая не бе такъв – тя продължи да си ги навързва по 5 пред очите ми.
– Ама… това сериозно ли? Не може да си взема 7? – попитах невярващо. Може пък и да е неуместна шега…
– Да. Продават се само по 5.
Мисълта да взема 2 букета по 5 и да изхвърля 3 от тях мина за малко през ума ми, но си изпари под напора на клиентската ми чест.
– Ами добре, приятен ден! – казах и се врътнах бързо-бързо. Даже не погледнах каква е реакцията от резкият ми отказ.

* * *

А Тя получи цветя от друг магазин…

 Posted by at 15:22

Сияние

 Без категория  2 Responses »
юни 182010
 

Не бях виждал колата преди. Пък и сега виждах само единият светещ фар и нищо друго. Но въпреки това усещах, че точно с тази кола идва нашата докторка.

По-рано през деня, при последният преглед, беше казала на Боби, че щастливото събитие се очаква всеки момент – „днес или утре“. Може би за това се чувствах доста нервен когато се прибрах. Хиляда въпроса се въртяха за хиляден път в главата ми – кой маршрут да избера? Как да карам – бързо или внимателно. И най-вече – а ще имаме ли достатъчно време?

Боби каза, че и се ядат пържени картофки и си направихме. А след вечеря уж гледах с едно око мача, ама хич нищо не виждах. Опитвах се всячески да не предавам моята нервност на Боби – все пак моята част е по-лесната. Дори, за да демонстрирам спокойствие, легнах да спя. Предната нощ съм спал само четири часа и половина, така че даже успях да задремя. Но после Боби ме събуди и каза да се обличам. И се обади на докторката.

Това с изтичането на някакви води май не е задължително. По пътя към Вита все пак карах хипер-внимателно, въпреки че исках да бързам. Но въпреки моето старание, и въпреки комфорта на Лагуната, Боби пъшкаше все по-тежко при всяка неравност по пътя.

И така, спрях пред болницата и слязохме. Веднага излезе един чичко да ни обясни, че така не се спира. Но аз му казах „ние сме само за малко – за едно раждане :)“ и той веднага ни вкара вътре. По-точно вкара Боби, а мен остави да чакам на рецепцията, от където и пиша сега.

10 минути по-късно пристигна и нашата докторка. Бях я срещал още по време на първия 3D / 4D преглед, когато цял час ни наблюдава ни обяснява какво виждаме. Но сякаш не я бях видял истински.

Първото нещо, което видях, когато влезе в болницата, беше здравата смес от спокойствие и увереност, които сякаш излъчва. И лека усмивка, сякаш е щастлива да изроди поредното бебе. А вероятно наистина е щастлива от работата си. Без значение, че я извикахме почти в полунощ, тя крачеше уверено, без да бърза, но и без да се бави. Поздрави ме с „добър вечер“, без да спира, но и без да бърза. И продължи към асансьора.

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

 Posted by at 3:13
юни 102010
 

Много полезно, за всички, които си пазят AWR snapshots за по-дълъг период от време. От тази таблица може да се изкара trend за нарастването на базата. Примерно по месеци изглеждат така:

select tsu.snap_id, to_char(sn.mon, 'Mon.YYYY'), round(sum(tsu.tablespace_size * nvl(ts.blocksize, p.value))/1024/1024/1024, 2) GB_size
  from dba_hist_tbspc_space_usage tsu,
       sys.ts$ ts,
       v$parameter p,
       (select trunc(begin_interval_time, 'MONTH') Mon, min(snap_id) snap_id
          from dba_hist_snapshot
         group by trunc(begin_interval_time, 'MONTH')) sn
 where p.name = 'db_block_size'
   and tsu.tablespace_id = ts.ts#(+)
   and sn.snap_id = tsu.snap_id
 group by tsu.snap_id, sn.mon
 order by 1

Този SQL идва с няколко забележки:
– малко ми е тъпо размера на tablespace да се пази в блокове, а не байтове. Отне ми известно време да схвана какви са числата. Документацията не помага много 🙂
– ако в миналото е имало tablespace, който в последствие е drop-нат, се приема, че е бил с default-ния block size. Аз, лично не намерих друг начин
– още нещо леко тъпо: v$tablespace няма изведена колонка blocksize, докато dba_tablespaces няма ts#. Заради тази… хм… неконсистентност в поведението, предпочетох да използвам директно sys.ts$. На който не му харесва това, може да си ги join-не по tablespace_name

 Posted by at 11:15

„В Механата“ – Христо Ботев, 1873

 Общи  Коментарите са изключени за „В Механата“ – Христо Ботев, 1873
юни 022010
 

Тежко, тежко! Вино дайте!
Пиян дано аз забравя
туй, що, глупци, вий не знайте
позор ли е, или слава!

Да забравя край свой роден,
бащина си мила стряха
и тез, що в мен дух свободен,
дух за борба завещаха!

Да забравя род свой беден,
гробът бащин, плачът майчин, –
тез, що залъкът наеден
грабят с благороден начин, –

грабят от народът гладен,
граби подъл чорбаджия,
за злато търговец жаден
и поп с божа литургия!

Грабете го, неразбрани!
Грабете го! Кой ви бърка?
Скоро тоя не ще да стане:
ний сме синца с чаши в ръка!

Пием, пеем буйни песни
и зъбим се на тирана;
механите са нам тесни –
крещим: „Хайде на Балкана!“

Крещим, но щом изтрезнеем,
забравяме думи, клетви,
и немеем и се смеем
пред народни свети жертви!

А тиранинът върлува
и безчести край наш роден:
коли, беси, бие, псува
и глоби народ поробен!

О, налейте! Ще да пия!
На душа ми да олекне,
чувства трезви да убия,
ръка мъжка да омекне!

Ще да пия на пук врагу,
на пук и вам, патриоти!
Аз веч нямам мило, драго,
а вий… вий сте идиоти!

 Posted by at 18:27

В чест на Ботев

 Без категория  2 Responses »
юни 022010
 

Би Ти Ви с гордост представя новият турски сериал „Двама завинаги“, с участието на звездата от „Перла” – Къванч Татлъту и чаровната Седеф Авджъ. Очаквайте премиерата на 2 юни, денят на Ботев и загиналите за свободата и независимостта на България.

 Posted by at 9:55

DG Switchover мотичка

 Без категория  Коментарите са изключени за DG Switchover мотичка
май 212010
 

Напоследък съм влюбен в DataGuard Broker. Страшно полезен инструмент! Е, има си своите странности, все пак…

Днес тествах switcover при конфигурация от една primary, един physical и един logical standby. Предварително се подготвих с research на възможните бъгове и техните workarounds. За щастие сме на доста стабилна версия. Примерно една от грозните мотики е описана в note 761661.1. Абе убих си един ден ровене…

И все пак не бях съвсем изряден. Когато се засилих да прехвърлям, се случи следното:

DGMGRL> connect /
Connected.
DGMGRL> show configuration

Configuration
Name: My_guard
Enabled: YES
Protection Mode: MaxPerformance
Fast-Start Failover: DISABLED
Databases:
mydb_prim – Primary database
mydb_lsby – Logical standby database
mydb_psby – Physical standby database

Current status for „My_guard“:
SUCCESS

DGMGRL> switchover to mydb_psby
Performing switchover NOW, please wait…
Operation requires shutdown of instance „mydb“ on database „mydb_prim“
Shutting down instance „mydb“…
ORA-01031: insufficient privileges

You are no longer connected to ORACLE
Please connect again.
Unable to shut down instance „mydb“
You must shut down instance „mydb“ manually
Operation requires shutdown of instance „mydb“ on database „mydb_psby“
You must shut down instance „mydb“ manually
Operation requires startup of instance „mydb“ on database „mydb_prim“
You must start instance „mydb“ manually
Operation requires startup of instance „mydb“ on database „mydb_psby“
You must start instance „mydb“ manually
Switchover succeeded, new primary is „mydb_psby“

После нещата са леееко омазани, но се оправят с рестарт на mydb_prim (до MOUNT) и на mydb_psby.

Сега, не че е голяма драма, даже може да се каже, че всичко е ОК, нещата се оправят лесно. Ама все пак ми стана чудно какво не му уйдисва. Нали уж всичко правя като по книгите. Пък и, да си кажа честно, не можах да открия паричната за това гърмежче в Metalink My Oracle Support. Може би защото само се чудят какви бози да правят по визията, а така и не му оправиха search-а.

И все пак, да е жив и здрав чичко гугъл, решение се намери тук. Оказва се, че за връзка към брокера трябва да се използва connect sys/password, вместо connect /. Иди ги разбери.

DGMGRL> connect sys
Password:
Connected.
DGMGRL> show configuration

Configuration
Name: My_guard
Enabled: YES
Protection Mode: MaxPerformance
Fast-Start Failover: DISABLED
Databases:
mydb_prim – Physical standby database
mydb_lsby – Logical standby database
mydb_psby – Primary database

Current status for „My_guard“:
SUCCESS

DGMGRL> switcover to mydb_prim
Unrecognized command „switcover“, try „help“
DGMGRL> switchover to mydb_prim
Performing switchover NOW, please wait…
Operation requires shutdown of instance „mydb“ on database „mydb_psby“
Shutting down instance „mydb“…
ORA-01109: database not open

Database dismounted.
ORACLE instance shut down.
Operation requires shutdown of instance „mydb“ on database „mydb_prim“
Shutting down instance „mydb“…
ORA-01109: database not open

Database dismounted.
ORACLE instance shut down.
Operation requires startup of instance „mydb“ on database „mydb_psby“
Starting instance „mydb“…
ORACLE instance started.
Database mounted.
Operation requires startup of instance „mydb“ on database „mydb_prim“
Starting instance „mydb“…
ORACLE instance started.
Database mounted.
Switchover succeeded, new primary is „mydb_prim“
DGMGRL> show configuration

Configuration
Name: My_guard
Enabled: YES
Protection Mode: MaxPerformance
Fast-Start Failover: DISABLED
Databases:
mydb_prim – Primary database
mydb_lsby – Logical standby database
mydb_psby – Physical standby database

Current status for „My_guard“:
SUCCESS

А сега си представете ако не е брокера, колко къртовски труд е това. Първо направи еди-какво-си на едната база, после на другата, да не забравяме пренастройването на логическта… Вместо 5 минути отива на към 25, ако е добра предварителната подготовка.
Абе едва ли вече ще пусна Data Guard без Broker.

 Posted by at 17:02
май 212010
 

– Извинете, господине, Вие как влязохте?
Превключвам от режим „дълбоко замислен за лични проблеми“ на по-комуникативен. Преценявам ситуацията, все още леко отнесен. „Хм, странна работа, да ме спира проверка на ескалатора. И не ме ли видя, че съм с карта…“
– Ми с картата си влязох. Защо? – отговарям леко торснато. Тази вечер не съм в учтиво настроение. Напоследък твърде много неща ме дърпат на много страни. Като започнем от една кифла…
– Ами Вие влязохте с моя билет… – леко сърдито, настъпателно.
Отне ми известно време да възстановя в главата си последните 60 секунди. Влязох в подлеза, насочих се към бариерите, през които се влиза за метрото. Даа… имаше там един, който се опитваше да си вкара билета в едната бариера, а да мине през тази вляво от нея. Аз го финтирах още по-вляво, лепвайки картата си от правилната за тази бариера страна… Май е точно този…
– Не е вярно. Имам си карта, влязох с нея… – започвам да се обяснявам, но той ме прекъсва, повишавайки тон:
– Не! Вие влязохте с моя билет. Заради Вас трябваше да прескачам… – усещам как се набира. На мен също не ми е до учтивости.
– Глупости. Имам си карта. А Вие обърнете внимание, че билета се вкарва с лявата ръка и се минава вдясно то него.
То като се замислиш, наистина не е много логично. Да си вкарваш билета с лявата ръка. Ама някой умник така го е измислил, виновен ли съм?
Вече слизаме от ескалатора, долу на перона. Моя опонент продължава да набира инерция:
– Не е вярно! Не е така. Вие влязохте с моя билет! Писна ми от тарикати! За това сме на тоя хал! Е*аси държавата, е*аси хората!
– Не е така – все още се опитвам да обясня, но вече съм доста афектиран – Аз минавам тук всеки ден и знам как се минава. Освен това си имам карта. Но това не Ви пречи да ме обвинявате, без да знаете какво е станало…
– Не е така…
– МИНАВА СЕ ВДЯСНО! Аман от хора, които си мислят, че знаят всичко. И когато сгрешат, все друг им е виновен. „За това сме на този хал“, нали? „Е*аси държавата“, нали?
Не мисля, че го убедих. И един час да му обяснявам, няма да ми повярва. За щастие той пътува в обратна посока.

P.S. Благодарение на Иван открих, че хората наистина се замислят по въпроса от коя страна да се слага билетчето. Но май няма единно мнение.

 Posted by at 9:29