Tema: Re: Need nuomonės iš šono - DB
Autorius: 2x50
Data: 2011-12-09 12:25:25
Keletas teiginiu:

1. Serializable visada bus letesnis nei kiti isolation leveliai del vienos 
paprastos priezasties - DBVS turi atlikti papildomus veiksmus tam, kad 
serializuoti transakcijas. O papildomi veiksmai reikalauja laiko, galbut 
labai labai mazai, bet visvien jie jo prideda, ne atima.
2. Zodziai serializable ir performance yra antonimai, ypac high concurency 
aplinkoje.
3. Zodziai deadlock ir performance yra antonimai, cia net nematau reikalo 
kazka bandyti spresti deadlocku pagalba, tai savizudybe.
4. Serializable apsaugo nuo phantomu. Phantomai is principo neimanomi 
dirbant su vienu irasu. Phantomas gali atsirasti tik tada, kai tas pats 
selectas kartojamas vienoj transakcijoj bent 2 kartus, + to selecto atrankos 
kriterijai leidzia jam grazinti daugiau nei viena irasa (select * from tbl 
where pk_value = :constant, negali grazinti daugiau nei vieno iraso).

Kadangi phantomas cia niekaip negali atsirasti, vadinasi, norint pagerint 
greitaveika(!), nera reikalo naudoti serial transakciju.

Prie viso to anot MySQL dokumentacijos (nepykit jei nepataikiau i versija, 
ziurejau pirma pasitaikiusia):
http://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-model.html#innodb-lock-modes 
cia ziuriu apacioj i lentele lock type compatibility matrix, noriu atkreipti 
demesi i tai, kad S ir S kombinacija yra compatible.
http://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-model.html#innodb-locks-set 
cia skaitau SELECT ... FROM is a consistent read, reading a snapshot of the 
database and setting no locks unless the transaction isolation level is set 
to SERIALIZABLE. For SERIALIZABLE level, the search sets shared next-key 
locks on the index records it encounters. Esme tame, kad "sets shared(!) 
next-key locks", kas leidzia ta pati padaryti kitai serial transakcijai, nes 
S ir S pora yra compatible (zinoma as darau prielaida, kad konkreciam 
vapyzdy id yra pirminis raktas) t.y. 2 transakcijos

begin transaction
select * from tbl where id = 5
delete from tbl where id = 5
end transaction

tarpusavyje nekonfliktuoja tol, kol nei viena is ju nepasieke delete 
sakinio. Konfliktas atsiranti tik tada, kai bent viena transakcija sukurs X 
locka. Taigi, potencialiai visos transakcijos po pirmos, kurios pataike 
prasideti tarp pirmosios transakcijos begin transaction ir X locko sukurimo, 
is esmes yra siuksles, nereikalingai apkraunancios sistemos darba, nes jos 
ivykdys delete tik tuo atveju, jei ankstesne transakcija buvo abortinta.

Prie viso to, takrim pirmoji transakcija jau sukure X locka, bet uzklausimu 
skaicius nesustoja, sistemai toliau kuriami uzklausimai su naujomis tokiomis 
paciomis transakcijomis. Sios naujosios transakcijos, negali sukurti ir S 
locko, tam, kad ivykdyti selecta, nes pirmoji yra sukurusi X locka tam 
paciam irasui (vel ziuriu lock type compatibility matrix, matau X ir S pora 
yra conflicting). Kaip MySQL elgsis tokiu atveju as nzn, reikia ziureti, bet 
variantai, kuriuos dabar galiu sugalvoti yra tokie:
1. Restartuoti naujausias transakcijas rysium su tuo, kad irasas, kuriam jos 
negalejo sukurti S locku, nebeegzistuoja (restartuoti=rollback+pradeti nuo 
pradziu).
2. Grazinti exceptiona row does not exist ar pan.
3. Abortinti transakcija.

Noreciau atkreipti demesi, kad bet kuris is situ variantu reikalaus 
papildomu veiksmu is DBVS, kuri savo algoritmais tures atpazinti tokias 
situacijas t.y. dar papildomai apkraus sistema. Taigi, visos transakcijos, 
kurios yra sukurtos tuo metu kai yra vykdoma pirmoji transakcija yra tik 
papildoma apkrovima sistemai, kuri realiai neduoda jokio rezultato, nes 
neatlieka jokiu veiksmu. Tiketis greitaveikos pagerejimo galima tik is MySQL 
vidiniu resursu, t.y. darant prielaida, kad InnoDB susitvarkys greiciau su 
transakcijomis (iskaitanta tas, kurios sukasi be jokio rezultato), palyginus 
su ISAM write lock komanda. Man tai labiau panasu yra lavono reanimavima, 
negu sprendima t.y. takrim ISAM pradeda stabdyti nuo 30 konkurentiniu 
vartotoju, na gerai InnoDB prades stabdyti nuo 100. Esme tame, kad pasiekus 
ta 100 visvien bus sakes. Tada lieka tik pasiulymas - nusipirkit greitesni 
proca. Man asmeniskai butu zema siulyti nusipirkti greitesni proca, 
nepasiulius kitu alternatyvu :)

Neturiu po ranka MySQL, galbut cia kasnors galetu pagelbeti ir atlikti tai, 
ka as pabandziau su Oracle. Tai labai paprasta ir uzima gerokai maziau laiko 
negu man parasyti sita posta :)
Stai pavyzdiai is Oracle

create table TST
(
  ID   NUMBER not null,
  NOTE VARCHAR2(2000),
  constraint TST_PK primary key (ID)
)

insert into TST (ID, NOTE)
values (1, null);
insert into TST (ID, NOTE)
values (2, null);
insert into TST (ID, NOTE)
values (3, null);
insert into TST (ID, NOTE)
values (4, null);
insert into TST (ID, NOTE)
values (5, null);
commit;

susikuriau 3 sesijas (3 tam, kad patikrinti ar nebus deadlocku) su vienodom 
transakcijom ir paleidau jas vienu metu

READ COMMITED atveju

session 1
SQL> set serveroutput on;
SQL> declare
  2    i number;
  3  begin
  4    set transaction isolation level read committed;
  5    select id into i from tst where id = (select min(id) from tst) ;
  6    dbms_output.put_line('id=' || i);
  7    delete tst where id = i;
  8    dbms_lock.sleep(10);
  9    dbms_output.put_line(sql%rowcount || ' row(s) deleted');
10    commit;
11  end;
12  /
id=1
1 row(s) deleted

PL/SQL procedure successfully completed.

session 2
SQL> set serveroutput on;
SQL> declare
  ..... tas pats kas auksciau
id=1
0 row(s) deleted

session 3
SQL> set serveroutput on;
SQL> declare
  ..... tas pats kas auksciau
id=1
0 row(s) deleted

t.y. READ COMMITED atveju oracle tiesiog abortina 2 ir 3 transakcijas


SERIALIZABLE atvejis

session 1
SQL> ed
Wrote file afiedt.buf

  1  declare
  2    i number;
  3  begin
  4    set transaction isolation level serializable;
  5    select id into i from tst where id = (select min(id) from tst) ;
  6    dbms_output.put_line('id=' || i);
  7    delete tst where id = i;
  8    dbms_lock.sleep(10);
  9    dbms_output.put_line(sql%rowcount || ' row(s) deleted');
10    commit;
11* end;
SQL> /
id=2
1 row(s) deleted

session 2
SQL> declare
  ... tas pats kas auksciau
id=2
declare
*
ERROR at line 1:
ORA-08177: can't serialize access for this transaction
ORA-06512: at line 7

session 3
SQL> declare
  ... tas pats kas auksciau
id=2
declare
*
ERROR at line 1:
ORA-08177: can't serialize access for this transaction
ORA-06512: at line 7

jokiu deadlocku, serialines transakcijos tiesiog taip neveikia (bent jau 
Oracle atveju). Jei MySQL grazins taip pat exception, bus katastrofa, nes, 
speju, exceptionais pasibaigusiu transakciju bus daugiau negu normaliai 
pasibaigusiu...

Tam, kad pasiketi FIFO principa, reikia ne isolation level keisti, o 
selectui deti FOR UPDATE (ka turi ir MySQL), tam, kad sis selectas sukurtu X 
locka, vietoj S locko

FOR UPDATE atvejis

session 1
SQL> ed
Wrote file afiedt.buf

  1  declare
  2    i number;
  3  begin
  4    select id into i from tst where id = (select min(id) from tst) FOR 
UPDATE;
  5    dbms_output.put_line('id=' || i);
  6    delete tst where id = i;
  7    dbms_lock.sleep(10);
  8    dbms_output.put_line(sql%rowcount || ' row(s) deleted');
  9    commit;
10* end;
11  /
id=3
1 row(s) deleted

PL/SQL procedure successfully completed.

session 2
SQL> declare
.... tas pats kas auksciau
id=4
1 row(s) deleted

PL/SQL procedure successfully completed.

session 3
SQL> declare
.... tas pats kas auksciau
id=5
1 row(s) deleted

PL/SQL procedure successfully completed.

Va dabar viskas buvo ivykdyta is eiles, kadangi kiekviena sekanti 
transakcija prasidejo tik po to, kai baigesi ankstesnioji. Visos 3 ivyko 
leciau nei pirmi 2 atvejai, nes buvo vykdomos grieztai is eiles.

Tiesiog siuo konkreciu atveju su tokiu select for update, nera jokio 
skirtumo ar rakinti viena irasa ar visa lenta, greitaveika bus ta pati...