Бъг с Updateable MViews

 Без категория
ян. 062009
 

В началото на годината ще ви запозная с една мотика, от която много боли. Някои фирми (примерно Стемо) използват т.нар. basic replication за репликация на данни между Oracle сървъри. Ние го правим от 1999, когато с Oracle 8i (8.1.5) успяхме да преточим 10 таблици между 1 master site и 9 snapshot sites през едни много разпадащи се връзки с модеми. За времето си това решение беше идеално Още тогава успяхме да докараме 15 минути време за репликиране на данните в едната посока (когато има връзка). Сега, близо 10 години и 3 версии на Oracle по-късно, това вече е legacy система, но все още търкаля (и ще продължава да търкаля дълги години) с ~1800 materialized views между 1 master и 12 mview сървъра.

С две думи, ако ви кажа, че съм много дълго съм сърбал попарата на всякакви mviews, може да ми вярвате. Аз се занимавах с тази система около 7 години. От началото на 2006 един колега постепенно пое товара от моите плещи за няма и година навлезе толкова добре, че започна да се справя съвсем сам. А работата хич не е лека – това… хммм… абе нека кажем, че не е най-съвършения механизъм в Oracle Database.

В теорията на базите от данни, замисъла на тези snapshots е да копират данни от mater към snapshot таблица. Естествено Oracle ги натоварват с още много функционалност (може би за това от 9i ги прекръстиха в mviews). Всеки е чувал за query rewrite механизмите, дъвчат се из целия нет. Но един друг не по-малко вълнуващ механизъм е копирането на данни в двете посоки. Идеята е следната: има един master сайт, в който стоят данните. Има един (или повече) snapshot сайт(ове), на които има цяло или частично копие на тези данни. Това копие се опреснява през определен период от master таблица с т.нар. refresh (няма да изпадам в подробности за complete и fast refresh). До тук имаме едно копие на данните, което е read-only. Ако ни е обхванал дълбок мазохизъм (или сме притиснати от обстоятелствата), можем да дефинираме че едно mview е updateable. Това означава, че освен че изтегля данни от master таблицата, то може да приема и DML. Всички DML заявки към Updateable MViews се събират в една опашка и периодично се изпращат към master сайта с механизъм, известен като push. Клопката тук е, че докато опресняването (refresh) от master към mview става mview по mvew (може да се прави и на групи), то в обратна посока (push) върви поток от транзакции, аналогичен на този при Advanced replication.

И тук идва времето за днешното бъгче. Понякога (обикновено по време на създаване) едно mview може да стане не updateable, а writeable. Това означава, че всички DML операции си работят върху него (не е read-only), но транзакциите не се прихващат и не се изпращат към master сайта. При следващия refresh данните, въведени върху mview-то изчезват, защото refresh-а синхронизира данните е mvew с тези в master таблицата. А там новите данни ги няма.

Първия път като ми се случи това се стреснах много силно. Това си е жива загуба на данни! И тъжното е, че на пръв поглед е много трудно да се идентифицира едно такова mview. Ако имаш едно и искаш да го пробваш е лесно – правиш някакъв dml и гледаш дали се прихваща в таблицата deftrandest. Но в една жива система с 12 mvew сървъра, на всеки от които има ~1800 mviews нещата загрубяват. dba_mviews не подсказва нищо, нито има някаква грешка в dba_objects.

Ето и решението:

select d.owner, object_name, trigflag, mv.compile_state, mv.updatable
from dba_objects d, sys.tab$ t, dba_mviews mv
where d.object_id = t.obj#
and mv.mview_name = d.object_name
and mv.owner = d.owner
and mv.updatable = 'Y';

Тази подла заявка ни показва една колонка trigflag, по която можем да идентифицираме какви internаl triggers има таблицата нa едно mview. Тази колонка е побитова маска със следните стойности:
0x01 => deferred RPC queue
0x02 => mview log
0x04 => updateable mview log

С други думи едно updateable mview трябва да има стойност 5 (или 7, ако само по себе си е и master за друго mview). Проблемните (writeable) mviews имат стойност 4 (или 6), т.е. това mview позволява updates, но не ги enqueue-ва като defcalls (леле какво изречение!).

След като идентифицираме такова mview, тригера може да се създаде със следната секретна процедура:
exec dbms_internal_trigger.make(owner, mview_name, 1)

Убеден съм, че това ще помогне на някого!

NB! Това не е документирано официално. Тествано и работи на 10.1 и 10.2. Използваме го в production от години. Но гаранция за бъдещите версии няма.

 Posted by at 12:41

  6 Responses to “Бъг с Updateable MViews”

  1. First!

    и съдейки по темата – предполагам the only 🙂

  2. Здравей, виждам че се занимашаш сериозно с Оracle и си наясно с някои тънкости. Понеже срещам трудности с намирането на информация за някакъв начин да се refresh-нат едновременно mview-то и прилежащите му текст индекси се чудя дали можеш да ме насочиш къде да прочета повече по въпроса (който, надявам се, не е свръх тъп)? И благодаря предварително!

  3. Честно казано, не съм се борил точно с такъв проблем. Но ми хрумват някои неща.
    Ако mview-то ти се refresh-ва on demand (било то като част от refresh група или по друг начин), това винаги става с някакъв job, който в крайна сметка е pl/sql. Може смело да добавиш там и опресняването на текстовия индекс.

    Ако mveiw-то ти е refresh on commit, то тогава имаш същата ситуация като текстов индекс върху таблица – ако не е от само-поддържащите се, ще трябва да го бориш с job. Това, че е върху mview в случая не играе.

    Малко по-интересно става ако имаш complete refresh на mveiw-то. В старите версии това се случва с truncate и наливане на всичко, което води до един период на „празно“ mvew. От няколко години, в повечето случаи се прави delete вместо truncate за да се избегне такъв „празен“ период (при това силно страда performance на цялата операция). При всички случаи ще е добре да си rebuild-неш индекса наново след complete refresh – дори може би да го „изключиш“ преди такъв refresh.

    Може ли да споделиш конкретно кой вид текстов индекс те мори, и в кой от случаите?

  4. Здравей, много ти благодаря за бързия и изчерпателен отговор. Както казва баба – „ученото си е учено“ ;).
    Идеята за опресняването в job ми хрумна и на мен (refresh-а е on demand), но мисля, че всъщност от мен се искаше да намеря някакъв начин да се ускори процеса по refresh. Ама като не може, не може.
    Благодаря ти, че отдели от времето си да се занимаваш с едно newbie :).
    Хубав ден!
    P.S. Малко спам – би ли ми препоръчал някоя книга за прохождащи в Oracle (ако не много нахално от моя страна). Благодаря отново!

  5. Благодаря ти, че отдели от времето си да се занимаваш с едно newbie :).

    Благодаря ти че отдели време да зададеш въпрос в блога 🙂

    За съжаление аз не съм чел книги за oracle за новобранци и не се сещам за такава, камо ли да препоръчам. Ако трябва да предложа една книга за Oracle, това ще е Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions. Не мога да преценя, обаче, колко ще е смилаема за някой с „основни“ познания. Според моят субективен и изкривен поглед, може и да стане – тя започва от основите: какви са процесите, какви са memory structures и т.н.

  6. Ще се повторя, но … БЛАГОДАРЯ :)! Желая ти слънчев и усмихнат ден!

Sorry, the comment form is closed at this time.