Не сме сами, част 4

 Общи  Коментарите са изключени за Не сме сами, част 4
Дек. 172009
 

(към част 3)

Основно правило при многопотребителската работа е да си заключваш ресурсите, с които работиш. Едно здраво заключване може да спести много главоболия. За съжаление за да направиш добро заключване, трябва 1) да си осъзнал, че ти трябва, и 2) да знаеш как да го направиш 🙂

Повечето хора се сещат за select ... for update. Обаче има и други заключваници, които могат да помогнат на съвестния db developer. Единият вариант е Lock table. Ще използвам примера от част 2:

create or replace procedure Process_New_Data is
  -- example code - not scalable!!!
begin
  -- lock the log table, so none will be able to add/remove batches
  lock table batch_log in exclusive mode;
  -- process the loaded batches
  insert /*+append*/
  into prod_table
    select -- do some processing
           batch_id, get_something(column1, column2), calculate_something(column3, column4),
           RANK() over(PARTITION BY column5, column6 order by column7) rn, 
           column8, column9, ...
      from loader_table
     where batch_id in (select batch_id
                          from batch_log
                         where processed = 0);

  -- delete the rows from loader table
  delete from loader_table
   where batch_id in (select batch_id
                        from batch_log
                       where processed = 0);

  -- mark the batches as processed
  update batch_log
     set processed = 1
   where processed = 0;

  commit;
end;

В случая още в началото на процедурата заключваме таблицата batch_log. Това означава, че никой няма да може да добавя, променя или премахва редове от нея. :
– ако има сесия, която вкарва нови данни, при опит да направи insert – ще чака.
– ако има друга сесия, която изпълнява нашата процедура за обработка/преместване на данни – ще гръмне или ще чака, в зависимост от това дали ще дадем wait на заключването. Това е и една от причините този lock да се прави в самото начало, преди да сме свършили някаква друга работа.

Самото заключване се „отключва“ автоматично в с първия commit или rollback. За това точно този подход е неприложим в ситуации като описаната в част 1.

По-лошото е, че този подход е адски не-скалируем. Това е brute force на защитата. Докато не приключим с всичката работа, никой няма да може да работи по тази таблица (освен да select-ва). С други думи, заменили сме един проблем с друг. И сега проблема ще се прояви при натоварване с конкурентни сесии. Може да се каже, че сме по-добре: проблемът ще е ясен, няма да има случайни загуби или дублиране. Но си е проблем.

Интересна дилема: колкото повече (и по-рестриктивни) заключваници използваме, толкова по-зле понася товар едно приложение. Но ако не ги използваме, добрата саклируемост води до грешни данни. Някой да си е помислил, че има просто решение? 🙂

(към част 5)

 Posted by at 11:54
Дек. 172009
 

Тази сутрин една БД ме посрещна с 5 сесии, които висят от няколко часа върху 'cursor: pin S wait on X'. Бърз поглед из нета ми показа, че има твърде много буболечки, свързани с тия мутекси.

Но на Коледа стават чудеса. Оказа се, че при мен проблема е много по-прозаичен. Трябваше само да намеря кой, по дяволите, е отговорен заключването. Оказа се, че всяка от 5-те сесии чака различна друга сесия. Коя по-точно се намира така:

SELECT inst_id, sid blocked_sid, p2raw, 
       to_number(substr(to_char(rawtohex(p2raw)), 1, 8), 'XXXXXXXX') blocking_sid
  FROM gv$session
 WHERE event = 'cursor: pin S wait on X'
 order by 1, 2;

Всички виновници се оказаха заспали долу-горе по едно време, все поради прекъсване по средата на distributed query. Причините за това хлъцване са ми ясни. Просто за пръв път виждам това да доведе до спор за мутекси…

Избих заспалите сесии и отидох да си направя чай.

А на вън вече не вали сняг.

 Posted by at 9:22