Egy Postgresen működő programot portolok Oraclere. A konkurencia kezelés serializable isolation levelre van alapozva. Az Oracle ilyen hibákat csinál:
1) Update nem talál olyan rekordot, amiről biztosan lehet tudni, hogy létezik, mert épp az előbb lett kiírva (insert), még ugyanabban a tranzakcióban. Formailag nincs hibaüzenet, csak éppen az érintett rekordok száma 1 helyett 0, és persze nem hajtódik végre.
2) ORA-01403: no data found
Ez is olyankor jön, amikor nem talál egy éppen előbb kiírt adatot.
3) ORA-08177: can't serialize access for this transaction
Hogy ez mit jelent, az világos, csakhogy olyankor is jön ez a hiba, amikor nincs konkurencia, mert egyedül fut a program.
Ezer tranzakció közül kb. 6-8 romlik így el. Ha a serializable isolation levelt átállítom read comittedre (egyetlen sor módosul), akkor hibátlanul fut. Ebből azt gondolom, hogy valami nincs rendben az Oracle serializable módjával.
Mit lehet tudni erről? Használ valaki serializable isolation levelt, látott hasonlót?
- 3187 megtekintés
Hozzászólások
Oracle supportot hívnám a kérdéssel.
- A hozzászóláshoz be kell jelentkezni
Ha a felsorolt hibák után rollbackelek, és változatlan adatokkal ismétlem a tanzakciót, akkor az végrehajtódik. Ez természetesen nem megoldás, hanem mutatja a serializable mód defektjét.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Szia,
bocsánat, ha ordas nagy baromságokat írnék (beteg is vagyok, brü), de azért megosztom a véleményemet:
Először is, szerintem igenis konkurens tranzakcióid futnak.
Másodszor, a serializable isolation-nel ugye azt kéred, hogy a DB annak illúzióját biztosítsa, mintha a tranzakciók egymást kizárva, szigorúan sorosan futottak volna le. A DB ettől még párhuzamosan fogja futtatni a tranzakcióidat, csak commit idején ellenőrzi, hogy ez az illúzió valóban fenntartható-e. Ha nem, akkor kapod a kivételt (lásd még optimistic concurrency violation). Ilyenkor a rollback és az újrapróbálkozás tökéletes módszer (ha nincs nagy versengés); az elpukkant tranzakciót egyszerűen meg kell ismételni a friss DB állapoton.
Ha a read committed-et használod, akkor a kérdéses tranzakciód SQL utasításai más, véglegesített tranzakciók eredményéből fognak kiindulni; ami függőben van, azt nem látják, és valószínűleg COMMIT idején a DB nem törödik a sorosság látszatával.
Megoldási javaslatok:
- Maradj a szerializált változatnál, és simán próbáld meg újra a tranzakciót. Esetleg valami random backoff-fal :)
- Használj zárolást (pessimistic concurrency). REPEATABLE READS, ha több SQL utasítás között nem akarod, hogy a független, véglegesített, tranzakciók eredményei megjelenjenek.
- Vagy pedig READ COMMITTED (szintén zárolás, pessimistic concurrency), de ebben az esetben inkonzisztens végeredmények is születhetnek párhuzamos tranzakciókból, hacsak be nem vezetsz egyedi kulcsokat, és nem teszel kézzel írási zárat mindenre (SELECT FOR UPDATE, ahol csak olvasol). Asszem én ezt használtam "anno"; sajnos nem triviális jól megcsinálni.
Szóval, ismétlem, nekem az a véleményem, hogy párhuzamos tranzakcióid (session-jeid) futnak, és hogy az Oracle-nek nincs semmi baja, azt teszi, amit kérsz.
- A hozzászóláshoz be kell jelentkezni
A multiversion concurrency okozhatja.
- A hozzászóláshoz be kell jelentkezni
Amiről a belinkelt írás szól, az egy ismert jelenség, sok más helyen is le van írva.
Amire én panaszkodok, hogy egy insert utáni update (ugyanabban a serializable tranzakcióban) nem találja a rekordot. Ha a tranzakciót megismétlem, akkor átmegy. Ha az isolation level serializable helyett read committed, akkor elő sem fordul a hiba.
Arra gondolok, hogy ez egyszerűen egy feature az Oracleben. Végül is, ahol ilyen hiba van, ott lehetne "cant serialize transaction" hiba is, akkor viszont úgyis fel kell készíteni az alkalmazást a tranzakció ismétlésére. Csakhogy ez nem mindig elfogadható. Szerintem lesznek olyan esetek, amikor a hibát más okozza, amin az ismételgetés nem segít, az alkalmazás viszont nem tudja, hogy érdemes-e ismételni. Azaz összekutyulódik a hibakezelés.
Az érdekelne, hogy ismer-e valaki komoly Oracle alkalmazást, amiben a konkurencia kezelés a serializable isolation levelre van alapozva. Vagy esetleg van olyan infó, hogy ezt tanácsosabb elkerülni.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Ha valóban csak egyetlen tranzakciód / session-öd fut, akkor nincs is szükséged a serializable isolation-re; még a READ UNCOMMITTED-et is használhatod, pontosan ugyanaz kell legyen az eredménye. (A saját módosításaidat mindig látod, párhuzamos tranzakció meg nincs.)
Szerintem nem véletlen, hogy a pgsql program a serializable isolation-t választotta; bizonyára van konkurencia. Lehet, hogy a pgsql-nek más a "session ütemezője" vagy mittomén, és ott ezért nincsenek ilyen hibák.
- A hozzászóláshoz be kell jelentkezni
Köszönöm, hogy segíteni próbálsz, de nyomós okom van rá, hogy ezeket a dolgokat firtatom. Korlátozódjunk ennek megválaszolására: Jogos-e, lehetséges-e az, hogy az Oracle egy serializable tranzakcióban egy insertet közvetlenül követő updateben nem találja az előzőleg berakott rekordot?
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Mielőtt megkérdezi valaki, miért kell két lépés (insert+update), miért nem rögtön a végleges rekordot rakom be: Blob beírására egy szokásos technika, hogy először beteszünk egy olyan rekordot, amiben a blob helyén az empty_blob() függvény visszatérési értéke van. Ezt a rekordot egy selecttel visszaolvassuk, amivel megkapjuk a blob handlerét, amivel már írható a blob tartalma. Csakhogy néha elbukik a dolog azon, hogy (serializable módban) az insertet követő select nem találja a rekordot. Erről szól ez a topic. Ez csak egy példa, még számos oka lehet, hogy miért kell insert után update (vagy select), evvel nincs értelme itt foglalkozni.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Az empty_blob() által generált lokátort az INSERT utasításból azonnal visszanyerheted a RETURNING column_name INTO plsql_var klózzal; ezután az utóbbit használhatod a DBMS_LOB.WRITEAPPEND()-del. UPDATE, SELECT egyáltalán nem kell.
declare
NEW_ID number(18);
BLOB_LOC blob;
begin
insert into MYTABLE (ID, DUMP)
values (ID_SEQ.nextval, empty_blob())
returning ID, DUMP into NEW_ID, BLOB_LOC;
dbms_lob.writeappend(BLOB_LOC, ..., ...);
end;
/
http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/returnin…
http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_lob.ht…
Szerkesztés: a problémába már más is belefutott: http://forums.oracle.com/forums/thread.jspa?threadID=2125045
- A hozzászóláshoz be kell jelentkezni
ertem en hogy segiteni akarsz, de "Ez csak egy példa, még számos oka lehet, hogy miért kell insert után update (vagy select), evvel nincs értelme itt foglalkozni. "
Tyrael
- A hozzászóláshoz be kell jelentkezni
Nem, most már egyáltalán nem akarok segíteni. Azon túlmenően, hogy eredeti álláspontom tévességét belátva megkerestem az OP-nek azt az OTN szálat, ahol pontosan ezzel a blob-specifikus Oracle bug-gal foglalkozik egy Oracle fejlesztő, illetve azon túlmenően, hogy egy blob-kezelési apróságot megemlítettem, ami esetleg a hiba megkerülésében is segíthet (sajnos nem tudom ellenőrizni, most nem férek Oracle-höz), tényleg nincs értelme ezzel foglalkoznom.
Köszöntem, viszlát.
- A hozzászóláshoz be kell jelentkezni
Hasznos ez a topik, még ha a kérdező makacs is :)
- A hozzászóláshoz be kell jelentkezni
"However, SERIALIZABLE mode requires applications to check for the "can't serialize access" error, and can significantly reduce throughput in an environment with many concurrent transactions accessing the same data for update. Application logic that checks database consistency must take into account the fact that reads do not block writes in either mode.
Application Tips for Transactions
When a transaction runs in serializable mode, any attempt to change data that was changed by another transaction since the beginning of the serializable transaction causes an error:
ORA-08177: Can't serialize access for this transaction.
When you get this error, roll back the current transaction and execute it again. The transaction gets a new transaction snapshot, and the operation is likely to succeed.
To minimize the performance overhead of rolling back transactions and executing them again, try to put DML statements that might conflict with other concurrent transactions near the beginning of your transaction."
Ez az ora helpből van.
- A hozzászóláshoz be kell jelentkezni
Igen, de az OP-nek az a gondja, hogy neki sosem fut egynél több tranzakciója egyszerre, mégis ilyen kivételt kap. Nem akartam elhinni neki, de miután keresgéltem egy kicsit az oracle fórumain, úgy tűnik, tényleg létezik az a probléma ("Oracle bug"), hogy ha valaki SERIALIZABLE módban BLOB-ot bűvöl, az egyetlen tranzakcióval is belefuthat az ORA-08177-be (ami persze értelmezhetetlen). Valamelyik Ora ügyfél még az SR számát is kiírta a fórumba.
site:forums.oracle.com serializable blob ORA-08177
Lásd ezt a szálat.
- A hozzászóláshoz be kell jelentkezni
Aha, így teljesen érthető már, köszönöm az infót!
Még egy kerülő út: van egy másik megoldás, a createtemporary(blob_var, true) -> write(blob_variable, ..., clob|raw), ami talán használható lenne update helyett.
[blob_variable : lehet table.col(blob) ami rowtype eleme]
megj.:
Úgy hallottam, hogy ezt a dbms_lob pck-t átfaragják a következő verzióban.
- A hozzászóláshoz be kell jelentkezni
Részletesebb megfigyelések.
11g R1 szervert használok, 32 és 64 bites (kétféle) Debian Lennyre telepítve, a két rendszeren ugyanaz az eredmény. A kliens program OCI interfészt használ.
A tesztprogamban 500 tranzakció van, egy tranzakcióban kb. 20 insert, összesen kb. 10 ezer insert. A rekordoknak egyedi kulcsuk van. Az insert utasítás minden esetben hibaüzenet nélkül fut, és a _tapasztalat_ azt mutatja, hogy végre is hajtódik. Végrehajtódik, értsd: ha az insert után közvetlenül commitolnám a tranzakciót, akkor a rekord benn volna a táblában. A program egyedül fut, nincsenek konkurens tranzakciók.
A kísérlet:
1) Serializable mód. Közvetlenül az insert után select-tel visszaolvasom az éppen kiírt rekordot. Az esetek 0.5 százalékában (10 ezerből 50 eset) a select nem találja az éppen előbb berakott rekordot. Ha az insert után egy kicsit várok (sleep) vagy (tranzakción belül) az insertet vagy a selectet ismétlem, az hatástalan. Ha a tranzakciót abortálom, majd az egész tranzakciót megismétlem, akkor az lefut (és az insert utáni select talál).
2) Read committed mód. Ugyanaz a program, mint az előbb, csak az első utasítás nem serializable, hanem read committed módot állít be a sessionre. Nem jelentkezik hasonló hiba, az insert utáni select minden esetben talál.
Az a kérdés, hogy ezt a jelenséget bele lehet-e erőltetni a specifikációba (mármint elfogadni jogosnak), vagy hibának kell tekinteni. Workaround mindenesetre elképzelhető: Minden insert után visszaolvasó select, és ha az nem talál, akkor "cant serialize" error dobás. Az alkalmazás nyilván fel van készítve a "cant serialize" kivétel kezelésére (ami általában a tranzakció ismétlése).
Miért nem lehet elfogadni, hogy az insert berakta a rekordot? Kész-passz, nem olvasom vissza, minek is tenném, hiszen ismerem a tartalmát. Lehet, hogy furcsa a visszaolvasás, de az SQL specifikáció szerint nem tilos. Az SQL specifikáció szerint a select látja a saját tranzakcióban kiírt rekordot, még ha az nincs is commitolva. Ha a select nem látja a rekordot, akkor az SQL _specifikáció_ szerint a rekord nem is létezik.
Most képzeljük, hogy egy kétlábas könyvelési tranzakciót programozunk, a második lábat írjuk éppen kifele. Ellenőrzési mániában szenvedünk, és ezért egy selecttel visszaolvassuk a kiírt rekordot, ám a select nem talál. Abban a helyzetben vagyunk, hogy a _tapasztalat_ alapján úgy gondolhatjuk, hogy a rekord kiíródott, az SQL _specifikáció_ alapján azonban úgy kell gondolnunk, hogy a rekord nem íródott ki. Kell-e abortálni? Hint: A tét (a könyvelési tétel összege) 100 millió.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
szerintem a sajat sikeres inserted minden esetben latnod kellene, kiveve, ha egy masik read uncommited-ben futo tranzakcio kitorolte az inserted utan de a selected elott a rekordot.
de lehet hogy meg ez is csak akkor fordulhat elo, ha repetable reads-nel "lazabb" izolacios szinten vagy az insertes tranzakciodban.
szoval nekem mint oracle-hoz nem erto embernek furcsan hangzik, hogy a sajat insertemet nem latnam egy tranzakcioban.
Tyrael
- A hozzászóláshoz be kell jelentkezni
"Az SQL specifikáció szerint a select látja a saját tranzakcióban kiírt rekordot, még ha az nincs is commitolva."
Biztos ez? Hivatkozást tudsz mutatni?
- A hozzászóláshoz be kell jelentkezni
If a cursor is open, and the current SQL-transaction makes a change
to SQL-data other than through that cursor, and the <declare cur-
sor> for that cursor specified INSENSITIVE, then the effect of
that change will not be visible through that cursor before it is
closed.
Ez elegendő?
- A hozzászóláshoz be kell jelentkezni
Kösz.
"cursor is open"
Szerintem az insert utáni select az már másik cursor.
- A hozzászóláshoz be kell jelentkezni
Felfoghatjuk úgy is, de a hangsúly az "azonos tranzakción van".
- A hozzászóláshoz be kell jelentkezni
A cursor fogalma azért nem felfogás kérdése.
- A hozzászóláshoz be kell jelentkezni
Nem tudom, mi felel meg itt a cursornak. A kliens program OCI interfésszel működik, abban pedig "SQL statement handle" meg effélék vannak. A cursor talán egy select utasítás handlere lehet, ami kell addig, amíg fetchelni akarok belőle. A tranzakcióhatárok a fontosak.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Az a kérdés, hogy ezt a jelenséget bele lehet-e erőltetni a specifikációba (mármint elfogadni jogosnak), vagy hibának kell tekinteni.
Szerintem sokkal hasznosabb, ha gyakorlatiasabb megközelítésből nézed a dolgot:
- ha hibának kell tekinteni, és
- az Oracle is elfogadja ezt hibának,
akkor
lesz-e még az életben fix a hibára, vagy előbb öregszel és halsz meg, semmint fix készüljön a rá?
Én nem tennék rá nagy téteket, hogy valaha elkészül a fix.
A személyes, ~10 éves Oracle adatbázis tapasztalatom az, hogy a LOB-okat akkora ívben kell kerülni, amekkorában csak lehet.
- A hozzászóláshoz be kell jelentkezni
Én is arra tippelek, hogy az Oracle nem fogadná el hibának.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Ez egy nagyon jó példa arra, hogyan működik elméletben ill.
gyakorlatban valami, nem beszélve arról hogyan implementálták.
A read commited statment alapú, a serializable pedig
tranzakció szintű, ez utóbbi az "egész" adatbázis egy állapotát
veszi figyelembe és ezt látjuk a tranzakcióban(elvágtuk egy idővonallal). Ez jó is lenne,
de sok buktatóval is jár. Pl. itt van mindjárt a rollback
segmens, amiben a tárolt adatok nem őrződnek meg a végtelnségig,
hanem egyes tranzakciók ezt felül is írhatják(insert,.., rollback, commit) és
mindjárt olyan adattal dolgoznál, ami nincs is ott(ellenben a read committed-el aminél ez kevésbé jön elő)
"Az a kérdés, hogy ezt a jelenséget bele lehet-e erőltetni a specifikációba (mármint elfogadni jogosnak), vagy hibának kell tekinteni."
Elméletben ilyen nem fordulhat elő, de gyakorlati szinten igen.
megj.: használj read commited beállítást(default)(kevesebb álmatlan éjszaka, kevesebb javító update script)
Számos megoldás van az érzékeny adatok konzisztens kezelésére:
1. lock (mondjuk fejrekordon select col1, col2, ..from table for update)
2. éjszakai batch program végigszalad a teendőkön
3. dolgozd fel temp táblában->ellenőrizd->és egyetlen mozdulattal tegyed át a munka táblába, amit csak olvashatnak(+constraint)
- A hozzászóláshoz be kell jelentkezni
Hupos reggel nem rendelkező kollégám véleménye:
Nézegetve a doksit, meg amit a srác írt előszőr valami PL/SQL gondra gyanakodtam amivel én is szívtam korábban. De mivel ők OCI-ban küzdenek így ez kilőve.
Viszont minden olyan táblára amire serializable isolation level –ben tranzakciókat csinálnak az INITRANS paraméterét legalább 3-ra kell állítani.
>>Oracle stores control information in each data block to manage access by concurrent transactions. Therefore, if you set the transaction isolation level to serializable, then you must use the ALTERTABLE command to set INITRANS to at least 3. This parameter causes Oracle to allocate sufficient storage in each block to record the history of recent transactions that accessed the block. Higher values should be used for tables that will undergo many transactions updating the same blocks.<<
Ez még mondjuk a 9-es Oracle, de gondolom iránymutatásnak megfelel. Bár én egyből 100-ra alterálnék, az olyan szép szám :)
Viszont mivel ők 11-t használnak még egy csomó dolog bekavarhat, de indulásnak talán elég ennyi
Ja még az is egy jó tanács: azt a tranzakciót amivel néha baj van sima SQL-ben az SQLPLUS-al is tesztelgessék, hátha még sem az oracle szerver a szar, hanem az OCI-t nem trivi esetleg programozni kliens oldalon :)
- A hozzászóláshoz be kell jelentkezni
lacos kollega adott egy linket, amiben írva vagyon a hiba:
http://hup.hu/node/100133#comment-1237376
--
>>... to manage access by concurrent transactions->tehát akkor
1 tranzakció esetén is legyen legalább 3?
szerk.: tárolt eljárást akartam írni még délelőtt, de nem jutottam
odáig(így is jelentkezik-e a hiba?)
- A hozzászóláshoz be kell jelentkezni
"you must use the ALTERTABLE command to set INITRANS to at least 3"
Erről nem tudtam, ki fogom próbálni.
"az OCI-t nem trivi esetleg programozni kliens oldalon"
Ha nem is tudom matematikai szigorúsággal bizonyítani, hogy a tesztprogram hibátlan, mégis elég meggyőzően lehet érvelni mellette. Ugyanaz a program (egy utasítás különbséggel) read committed módban jó. Ugyanaz a program, ugyanazokkal az adatokkal a megismételt tranzakciót helyesen hajtja végre. Nehezen képzelhető el, hogy ez véletlen.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
alter table tablename INITRANS 32
alter index indexname INITRANS 32
Az összes táblára és indexre, nincs változás, de legalább megnyugtató az állandóság:)
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Kipróbáltam tárolt eljárásban, nálam nem volt hiba.
create table TST_BLOB
(
ID NUMBER not null,
MIEZ BLOB
)
tablespace valami_data
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
create or replace package seri_blob is
FUNCTION F_GENEREAL( P_HOWMANY NUMBER )
RETURN NUMBER;
end seri_blob;
/
create or replace package body seri_blob is
FUNCTION F_BEIR( P_TST_BLOB IN OUT NOCOPY TST_BLOB%ROWTYPE )
RETURN NUMBER
IS
BEGIN
INSERT INTO TST_BLOB VALUES P_TST_BLOB;
RETURN 0;
END F_BEIR;
--====================================================
FUNCTION F_BLOB_PREPARE( P_SIZE NUMBER,
P_RESULT IN OUT NOCOPY TST_BLOB%ROWTYPE )
RETURN NUMBER
IS
LRAW_X RAW(8000);
LN_ASC NUMBER;
I NUMBER DEFAULT 1;
L_ST VARCHAR2(8000);
LS_OUT VARCHAR2(10) DEFAULT 'EZKERULBE';
L_SZOVEG VARCHAR2(8000);
BEGIN
WHILE I <= FLOOR(P_SIZE / LENGTH(LS_OUT)) LOOP
L_ST := L_ST || LS_OUT ;
I := I +1;
END LOOP;
LRAW_X := UTL_RAW.CAST_TO_RAW(L_ST);
LN_ASC := LENGTH(L_ST);
DBMS_LOB.CREATETEMPORARY(P_RESULT.MIEZ, TRUE);
DBMS_LOB.WRITE(P_RESULT.MIEZ, LN_ASC, 1, LRAW_X);
RETURN 0;
END F_BLOB_PREPARE;
--====================================================
FUNCTION F_GENEREAL( P_HOWMANY NUMBER )
RETURN NUMBER
IS
L_ERROR_DB NUMBER DEFAULT 0;
L_TST_BLOB TST_BLOB%ROWTYPE;
L_DB NUMBER DEFAULT 1;
L_VAN NUMBER;
L_RET NUMBER;
SERIAL_HIBA EXCEPTION;
PRAGMA EXCEPTION_INIT(SERIAL_HIBA, -8177 );
BEGIN
COMMIT;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE NAME 'LEAKADE';
L_RET := F_BLOB_PREPARE( P_SIZE => 1000,
P_RESULT => L_TST_BLOB );
WHILE L_DB <= P_HOWMANY LOOP
BEGIN
L_TST_BLOB.ID := L_DB;
L_RET := F_BEIR( P_TST_BLOB => L_TST_BLOB );
SELECT 1 INTO L_VAN
FROM TST_BLOB
WHERE ID = L_DB
;
EXCEPTION WHEN SERIAL_HIBA THEN
L_ERROR_DB := L_ERROR_DB +1;
DBMS_OUTPUT.PUT_LINE(L_DB || ' ' ||SQLERRM);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(L_DB || ' EGYEB ' ||SQLERRM);
END;
L_DB := L_DB + 1;
END LOOP;
DBMS_LOB.FREETEMPORARY(L_TST_BLOB.MIEZ);
RETURN L_ERROR_DB;
END F_GENEREAL;
--====================================================
end seri_blob;
/
így teszteltem:
DECLARE
L_RET NUMBER;
BEGIN
L_RET := seri_blob.f_genereal(p_howmany => 100 );
END;
adtam neki 10000-et is, akkor sem láttam hibát.
- A hozzászóláshoz be kell jelentkezni
Kérdés, hogy a tárolt eljárás interpretálása a szerveren hogyan viszonyul egy OCI sql statement végrehajtásához. Lehetnek közbülső szintek, amik elfedhetnek ezt-azt. Azt sem tudom, hogy az OCI a legalacsonyabb szintű interfész-e, vagy más frontendek esetleg másra (az OCI-t elkerülve) építkeznek.
Nálam a tranzakciók bonyolultabbak. Nem csak egy táblába pakkolok, hanem van 10-15 tábla, egy tranzakcióban pedig átlagosan 20 insert és update. Vagyis jobban dolgoztatom a szervert.
Az eset amit leírtam nem kizárólag a bloboknál fordul elő. Először a bloboknál vettem észre, de aztán kiderült, hogy teljesen közönséges insert-select vagy insert-update páros is kihozza (nálam).
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Az egyik fent fut, a másik közvetett módon(oci). Azért küldtem el
a forrást, hogyha van időd nézd meg, hogy nálad jelentkezik-e a hiba,
és csak a példa miatt blob-os.
Ez egyáltalán nem jelenti azt, hogy ez megoldás a serializable
gondokra, sőt...több tranzakciónál meg sem találod az okok nagyrészét. Mindenképpen ajánlom a tárolt eljárást, ahol csak lehet(pl. nem kell klienst fordítani, ha a pck-ban ki lehet javítani).
Próbáld csökkenti a műveletek számát, 1000->800...akkor is jelentkeznek-e a hibák?
--
Használhatsz timestamp-ot is, ha nagyon érzékeny az app. a konzisztenciára.
- A hozzászóláshoz be kell jelentkezni
Hogy "fent fut", nem válasz a kérdésre. A kérdés, hogy a pl/sql interpreter belsőleg OCI-n keresztül dolgozik-e, vagy valahogy máshogy.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Nem, ezeket az adatbázis motor hajta végre, az oci köztes réteg.
- A hozzászóláshoz be kell jelentkezni
Még egy apróság
innen
"According to the manual, it converts every ‘select’ to a ‘select for update’ in ‘serializable’ mode:"
itt az ember azt mondja, hogy egy lehetséges módja a hibás működés
elkerülésére, ha mindegyik selectet for update(nowait)-el kiegészít.
Azért az szép történet...
- A hozzászóláshoz be kell jelentkezni
Köszönöm, hogy foglalkozol a témával.
Lefuttattam a programot, nem ad hibát. Illetve annyit látok, hogy nincs semmi kimenet vagy hibajelzés, és a táblában annyi rekord lesz, amennyi meg volt adva bemenetként.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Szívesen!
Nálam még csak a 9-es oracle van, akkor ebből a szempontból nincs
különbség(két session-ban is megfutattam, akkor sem volt hiba)
- A hozzászóláshoz be kell jelentkezni
Megírtam a programod megfelelőjét CCC-ben. Ez alatt a program alatt egy olyan könyvtár van, amelyik OCI interfészen keresztül SQL utasításokat küld a szervernek. A küldött SQL utasításokat kilistáztam és elláttam néhány megjegyzéssel. Sajnos a fetch utasítások nem látszanak a listában.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Hát ez szomorú.
Lehet, hogy az oci itt a hunyó?
Ez viszont ellentmondás, mert batch-ben futott valami és ott is jelentkezett a seri hiba, pedig egymagában volt a session.
--
Esetleg még megnézhetnéd, hogy ha a tárolt eljárást hívod,
előjön-e a hiba?
(
csak 1 fv. hivás és leteszi az összes rekordot, vagy
több fv. hívás és egyesével megy a rekord(persze itt megugrik a háló forgalma)
)
Initrans sem játszik ezek szerint(1 session-t alapul véve; nálad mennyi
a maxtrans értéke?)
- A hozzászóláshoz be kell jelentkezni
maxtrans=255 (default)
Nem a 8177 can't serialize hibára kell koncentrálni. El tudom fogadni, ha az Oracle néha ilyet dob. Ugyanis, ha egy alkalmazás serializable levelt használ, akkor fel van készítve az ilyen hibák kezelésére, és szükség esetén rollback után megismétli a tanzakciót. Tehát, ha a szerver (akár indokolatlanul) 8177 errort dob, akkor legfeljebb néhány tranzakció duplán fut. Nem különösebben érdekes.
Súlyosabb dologról van szó: insert után a select vagy update néha nem talál. Erre eddig azt a workaroundot találtam, hogy az insert után azonnal visszaolvasom a rekordot, és ha a visszaolvasás nem talál, akkor 8177 errort dobok, és lásd mint előbb. Így jön ide a can't serialize error. Jelenleg ez nekem bele van építve a magasabb szintű insert-be (a row objektum insert metódusába).
Példa nem triviális insert utáni selectre: Tegyük fel, hogy van egy árfolyam táblám, ahonnan néha előveszek egy árfolyamot. Ha a keresett árfolyam még nincs meg a táblában, akkor röptében elindítok egy árfolyam megszerző függvényt, ami email üzenetváltással, xmlrpc hívással, akárhogyan megszerzi az árfolyamot és beteszi az előbbi táblába, hogy a későbbi előfordulásoknál már meglegyen. Utána újra megpróbálom kiolvasni az árfolyamot.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Ez a "nem triviális" eset kezelése: rövid tranzakciók kellenek,
ha csak áll a session az nem gond. Előzetes vizsgálat az adatok
létezésére.
"Nem a 8177 can't serialize hibára kell koncentrálni."
A kettő nagyon összefügg, gondolj pl. a read committed szintre, ahol nincs
ilyen gond.
- A hozzászóláshoz be kell jelentkezni
Szintén kollégámtól, hátha:
Ez azt hiszem pont az érintett kérdés:
http://forums.oracle.com/forums/thread.jspa?threadID=593201
Szóval ha eldobják az összes indexet :), akkor tuti megy.
A hiba tahát az, hogy az indexet átrendeződik, mivel az adott block betelt és erről a tranzakció nem értesül ebben az izolációs szintben.
Tehát ezért van ez a “véletlenszerűség”.
Bár azt írják, hogy ez már javított bug, de ki tudja, meg csacsognak némi memory configról :)
- A hozzászóláshoz be kell jelentkezni
Kössz!
579 EGYEB ORA-01403: no data found
rátettem egy index-et a teszt táblára és máris eldobta magát :(
-szerk.
Megvárjuk mrev kollegát, neki 11-se van(a linken már a 10-ben is jó volt).
- A hozzászóláshoz be kell jelentkezni
Én is kipróbáltam indexszel.
create table TST_BLOB
(
ID NUMBER not null,
MIEZ BLOB
);
create unique index TST_BLOB_KEY on TST_BLOB (ID);
Nálam így is hiba nélkül fut fut.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Végre valami jó :)
- A hozzászóláshoz be kell jelentkezni
ezt en is megtalaltam, de ennek mar regota javitva kellene hogy legyen, nem?
Tyrael
- A hozzászóláshoz be kell jelentkezni
szerk: dupla
Tyrael
- A hozzászóláshoz be kell jelentkezni
Bár azt írják, hogy ez már javított bug, de ki tudja, meg csacsognak némi memory configról :)
A bug státuszmezőjében nem a 'kijavítva' szerepel, ergó szerintem ezt nem fixálták.
Viszont nagyon sok paramétertől függ, hogy előjön-e, ezért lehetett, hogy a fórumos usernek a 10g-ra váltás után nem jött elő.
- A hozzászóláshoz be kell jelentkezni
...de alapvetően sokkal jobb lenne (teljesítmény szempontjából is), ha átállnál a fentebb említett insert...returning megoldásra. Ráadásul a postgresql is ismeri.
- A hozzászóláshoz be kell jelentkezni
Akkor mindegy mire áll át, index nélkül nyugodtan
elindulhat vacsorázni délben a felhasználó :)
- A hozzászóláshoz be kell jelentkezni
Ha csak insertál és nincs select, még gyorsabb is index nélkül :)
- A hozzászóláshoz be kell jelentkezni
Ez nagyon igaz!
Mielőtt meg selectál tegyen rá indexet :)
(és már el is érkeztünk ahhoz a ponthoz, hogy minek adatbázis,
mikor egy txt állomány is megteszi)
- A hozzászóláshoz be kell jelentkezni
konkurens insertek kezelesere kenyelmesebb, meg adott esetben tudja aggragalni/bufferelni az irast, szoval szerintem manapsag nagyon keves helyen eri meg flatfile-lal szorakozni.
Tyrael
- A hozzászóláshoz be kell jelentkezni
Van az OCI-ban valami megfelelője az insert ... returning-nek? Szerintem nincs. Azonkívül nem csak a bloboknál fordul elő, hogy insert után select vagy update kell. Az alkalmazás megköveteli, hogy a táblának egyedi kulcsa legyen, ami egyenértékű egy unique indexszel. Úgyhogy ezek a kerülőutak nem járhatók.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Van az OCI-ban valami megfelelője az insert ... returning-nek? Szerintem nincs.
Szerintem kell lennie.
Ugyanazt a szintaxist nem enné meg? Valószerűtlenül hangzik.
- A hozzászóláshoz be kell jelentkezni
Összeraktam egy kis csomagot, amiben az sqlplus állítja elő ugyanazt a hibát, amiről itt szó volt (insert utáni select nem találja az éppen előbb berakott rekordot). Ez bizonyítja, hogy a hiba nem a kliens oldalon van. A projekt itt található. A projektet a futási eredményekkel együtt csomagoltam össze, hogy CCC nélkül is vizsgálható legyen.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Ha ezt reprodukálni tudod úgy, hogy ne select legyen a második, hanem egy másik insert, ami hivatkozik idegen kulcsként az első insert sorára (azaz insert + insert legyen a két parancs), és úgy is hasra esik, akkor nem lehetetlen, hogy kiverhető a kedves vendorból egy fix - ebben az esetben ugyanis nem workaroundolható a probléma. A selectes visszaolvasásra van workaround: insert into ... returning.
- A hozzászóláshoz be kell jelentkezni
Érzékletesébbé teszem a problémát. Van egy sqlplus scriptem, amit programmal csináltam, és így néz ki:
drop table proba;
create table proba( kulcs varchar(24) not null, megjegyzes varchar(128) );
create unique index proba_primarykey on proba (kulcs);
alter session set isolation_level=SERIALIZABLE;
insert into proba (kulcs,megjegyzes) values ('11:39:08!00000001','11:39:08');
update proba set megjegyzes='!!!!' where kulcs='11:39:08!00000001';
commit;
insert into proba (kulcs,megjegyzes) values ('11:39:08!00000002','11:39:08');
update proba set megjegyzes='!!!!' where kulcs='11:39:08!00000002';
commit;
...
insert into proba (kulcs,megjegyzes) values ('11:39:11!00000286','11:39:11');
update proba set megjegyzes='!!!!' where kulcs='11:39:11!00000286';
commit;
quit
Vagyis insert, update, commit parancsok ismétlődnek 286-szor. Az update mindig az utoljára insertált rekordot módosítja. Az eredmény:
SQL*Plus: Release 11.2.0.1.0 Production on Mon Mar 21 11:45:13 2011
Copyright (c) 1982, 2009, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Release 11.1.0.6.0 - 64bit Production
Table dropped.
Table created.
Index created.
Session altered.
1 row created.
1 row updated.
Commit complete.
1 row created.
1 row updated.
Commit complete.
Ilyenek ismétlődnek 285-ször. És 286-dikra jön a meglepetés.
1 row created.
0 rows updated.
Commit complete.
Disconnected from Oracle Database 11g Release 11.1.0.6.0 - 64bit Production
Vagyis az utolsó update nem hajtódik végre.
Ha a script elejéről kiveszem az indexet vagy nem állítom be a serializable isolation levelt, akkor a hiba megszűnik. A hiba determinisztikus, és 3 féle szerveren is konzisztensen jelentkezik: 11gR1(32bit), 11gR1(64bit), 11gR2(64bit). A hiba függ attól, hogy milyen hosszú a kulcs. Más hosszakkal máshol jön elő a hiba. Ahol az update hibázik ott a select sem működik. Szóval ki van ez próbálva sok variációban. Mit mondjunk akkor a király ruházatáról.
Ha valaki ki akarja próbálni, felraktam a scriptet ide.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Ezzel kéne megpróbálni. Ha megy a reprodukálás, akkor szerintem simán bejelenthető a supporton. Persze előtte ki kéne próbálni megemelt initrans-szal is.
Mellesleg nekem 10.2.0.3-n 32KB-s blokkmérettel nem jelentkezik a probléma még 10000 rekordnál se.
drop table p2;
drop table proba;
create table proba( kulcs varchar(24) not null primary key,
megjegyzes varchar(128) );
create table p2 ( kulcs varchar(24) not null
references proba(kulcs), megjegyzes2 varchar(128) );
create index p2_kulcs on p2 (kulcs);
alter session set isolation_level=SERIALIZABLE;
insert into proba (kulcs,megjegyzes) values ('abcdefghijkl/00000001', 'abcdefghijkl/00000001');
insert into p2 (kulcs, megjegyzes2) values ('abcdefghijkl/00000001', 'abcdefghijkl/xx');
commit;
insert into proba (kulcs,megjegyzes) values ('abcdefghijkl/00000002', 'abcdefghijkl/00000002');
insert into p2 (kulcs, megjegyzes2) values ('abcdefghijkl/00000002', 'abcdefghijkl/xx');
commit;
- A hozzászóláshoz be kell jelentkezni
Ez itt olyan, mint egy matematikai ellenpélda. Egy tétel érvénytelenségét lehet úgy is bizonyítani, hogy konstruálunk egy ellenpéldát. Egy esetet, ahol a tétel feltételei teljesülnek, de az állítás nem. Most a tétel feltételei azt jelentik, hogy tisztán Oracle eszközöket és szabályos SQL utasításokat használunk. Az állítás nem teljesülése azt jelenti, hogy az Oracle hibázik. Tehát megdől a tétel, hogy az Oracle hibátlan.
Egy ellenpélda akkor jó, ha minél egyszerűbb. Az én (ellen)példám nagyon egyszerű, mert kizárólag az SQL legegyszerűbb alapműveleteit tartalmazza (insert, update), egyetlenegy táblával állítható elő, és az az egy tábla is a lehető legegyszerűbb. Ha még egyszerűbb volna, akkor viszont már nem jönne elő a hiba. A példában alkalmazott tranzakciók is a lehető legegyszerűbbek, csak két műveletet fognak össze. Ha minden művelet külön commitolva volna, megint csak nem jönne elő a hiba. Ha nem teszek indexet a táblára, vagy nem írom elő az index unique minősítését, nem jön elő a hiba. Ha nem állítom, be a serializable módot nem jön elő a hiba.
Ha a hiba mindig az első rekordnál előjönne, akkor már rég észrevették és kijavították volna. Ennél trükkösebb a helyzet, a 286. rekord romlik el. Az általam elérhető összes szerver hibázik a 286-dik rekordnál. Ha másmilyen adatokat rakok a kulcs mezőbe (hosszabbat, rövidebbet), akkor ez a 286-os szám megváltozhat, de akkor az új szám lesz állandó a különféle szervereken.
Folytatva az egyszerűséggel: Az egyszerű ellenpéldának az a haszna, hogy erősebb állítás is megfogalmazható, mint hogy az Oracle nem hibátlan (mert ugye, ki látott már hibátlan szoftvert). Ez az erősebb állítás, hogy az Oracle az SQL olyan alapműveleteit sem tudja minden esetben megfelelően végrehajtani, mint az insert és update, még akkor sem, ha ezek a műveletek kizárólag egy táblát érintenek, és azon az egy táblán nincs előírva semmilyen ellenőrzés*, triggerek meg effélék. Ez így azért már súlyos, alig hiszem el.
--
CCC3
*Pontosabban, egy ellenőrzés mégis csak elő van írva: A unique index kényszeríti ki a kulcs egyediségét.
- A hozzászóláshoz be kell jelentkezni
Kipróbáltam, az insert/insert teszt nálam csak ORA-08177 hibát csinál.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Hát akkor tessék bejelenteni, hátha kiderül, hogy hol van a kutya elhantolva.
- A hozzászóláshoz be kell jelentkezni
Csak úgy vaktában, megújult érdeklődéssel: a végrehajtási terv mind a 286 esetben azonos?
- A hozzászóláshoz be kell jelentkezni
Nem tudom. De azért ez nem egy többszörösen egymásba ágyazott select, nincs benne több táblára kiterjedő join meg hasonlók.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Oracle 10g XE 32bit, Win7 32bit "szerver", default minden beállítás, előjött ugyanez.
Több insertnél, a 288.-nál is előjött.
Csináltam egy olyan változatot, aminek egy commit van a legvégén, ott a 286.-tól nem tud commitolni.
Ha átírom az egészet úgy, hogy begin-end blokkban legyen, és alter session helyett a set transaction isolation level SERIALIZABLE; paranccsal állítok, akkor:
Egy commit a végén verzió:
https://gist.github.com/880583/8f3a1cc3cd29b6cdfbdd529ab3220021216b72e9
Eredmény: 286. sortól elromlik
Minden UPDATE után külön commit:
https://gist.github.com/880583/da0d5c0dea221009d792fdf665ce100b81ca98e0
Eredmény: mind jó. Nem jó. Mert ugye mind külön tranzakció.
Itt az a verzió, ami ugyanúgy működik, mint a tied, de begin-end-es:
https://gist.github.com/880583/b08b80c563c52675dd4f3ac9f9e9dce3b664624b
Ez elszáll
ORA-08177: can't serialize access for this transaction
ORA-06512: at line 866
kivétellel.
ROWDEPENDENCIES használata elvileg (gyakorlatilag nem ritkult nálam) ritkítja az ilyen eseteket:
create table proba( kulcs varchar(24) not null, megjegyzes varchar(128) ) initrans 10 rowdependencies pctfree 5;
http://stackoverflow.com/questions/2326750/randomly-getting-ora-08177-w… illetve http://deniskrjuchkov.blogspot.com/2010/02/oracle-challenge.html itt is ezzel küzdöttek. (megoldás itt sincs)
- A hozzászóláshoz be kell jelentkezni
Minden UPDATE után külön commit:
https://gist.github.com/880583/da0d5c0dea221009d792fdf665ce100b81ca98e0
Eredmény: mind jó. Nem jó. Mert ugye mind külön tranzakció.
jó lenne ez; ezzel az a baj, hogy a gyakorlatban kevés
esetben lehet alkalmazni.
- A hozzászóláshoz be kell jelentkezni
Azért nem jó (nem ekvivalens) az eredeti scripttel, mert ott minden tranzakció serializable. Itt meg csak az első. Minden commit után be kell állítani újra, hogy a következő is serializable.
- A hozzászóláshoz be kell jelentkezni
alter session set isolation_level=SERIALIZABLE;
- A hozzászóláshoz be kell jelentkezni
A linkelt kódomban át van írva begin-endre, és ki van kommentezve az alter session...
Ehelyett van a begin-end-en belül set transaction isolation level SERIALIZABLE; ami csak az adott tranzakcióra vonatkozik, de elrontottam, mert csak az első tranzakcióra érvényes. A másodiknál már visszaáll read committed-re magától.
Ezért rossz az első hozzászólásomban az áthúzottal írt linken a kód.
- A hozzászóláshoz be kell jelentkezni
OK, kössz, igaz!
- A hozzászóláshoz be kell jelentkezni
http://stackoverflow.com/questions/2326750/randomly-getting-ora-08177-w… illetve http://deniskrjuchkov.blogspot.com/2010/02/oracle-challenge.html itt is ezzel küzdöttek. (megoldás itt sincs)
Az első link alatt van egy ilyen hozzászólás:
Shortly, sometimes during index restructurization undo data becomes unavailable. Any transaction that runs at serializable isolation level and requests the data that is somehow related with this index will get ORA-08177. This is a half-bug half-feature of Oracle.
Ebben a rész-szálban amúgy is kiderítették itt a hupperek, hogy ha nincs index, akkor nincs probléma. Javaslat (workaround):
- Az elején ne hozzunk létre indexet,
- Feltöltés közben az INSERT ... RETURNING INTO-t használjuk az UROWID visszakérésére,
- Minden sorra a további select/update-nél az UROWID-t használjuk a WHERE feltételben, esetleg úgy, hogy mellette még megköveteljük AND-del az éppen beszúrt kulcsértéket is. Az UROWID miatt az access path ROWID alapú lesz, vagyis villámgyors (O(1)), a PK érték pedig csak egy utólagos biztonsági szűrőként fog érvényesülni.
- A végén tegyük rá az indexet.
Volt olyan kérdés, hogy OCI-ból lehet-e INSERT ... RETURNING INTO-t használni. Igen, lehet:
http://download.oracle.com/docs/cd/B10500_01/appdev.920/a96584/oci05bnd…
(Egyébként érdekesség, hogy az INSERT ... RETURNING INTO-t még dinamikus SQL-lel is lehet használni, ahol a kimeneti PL/SQL változók rendesen bind-olhatók.)
- A hozzászóláshoz be kell jelentkezni
Azért ez az index nélküli megoldás elég necces.
Mondjuk akkor is van ilyen hiba, ha:
insert into table1 select * table2
ahol a két tábla oszlopai megegyeznek(index és index nélkül)?
- A hozzászóláshoz be kell jelentkezni
Ezek jók, kivéve, hogy az indexet nem lehet elhagyni, mert kell az egyediség kikényszerítéséhez. A primary key ugyanaz. A unique index helyett primary key ugyanúgy produkálja a hibát.
Most készülök feltúrni az OCI dokumentációt, hogy hogyan lehet megszerezni insert után a rowid-t.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Tehát akkor itt az index körül vannak a gondok.
declare
lr_fej table1%rowtype;
l_rowid rowid;
begin
lr_fej.unid := 1;
insert into table1 values lr_fej return rowid into l_rowid;
dbms_output.put_line(l_rowid);
end ;
Függvényeket tudsz hívni ccc-ből?
szerk>lacos kollega megtalálta a returning eset leírását
- A hozzászóláshoz be kell jelentkezni
nem rajottunk mar 1 hete hogy az index block split okozza a problemat?
http://hup.hu/node/100133#comment-1240136
Tyrael
- A hozzászóláshoz be kell jelentkezni
Melyik index típusra gondoltál?
- A hozzászóláshoz be kell jelentkezni
There was a long-standing bug in Oracle serializable isolation,
bug #440317, where if an insert causes an index block to split, any
subsequent search for this new row in the same tx uses the previous
unsplit index page (for read consistency) which does not have the new
row! I remember dealing with this in the 1990's, and I would be surprised
that it is not fixed yet. Can you try Oracle 10g?You would also need to configure your per-table memory to store all the
data in that huge test transaction.
bár csodálkoztam, hogy ez nincs még javítva, de a tünetek alapján pontosan illik a jelenlegi szituációra.
Tyrael
- A hozzászóláshoz be kell jelentkezni
Pontosan. Abból gondolom én is ezt, hogy a kulcshossz változásával változik az első hiba bekövetkezésének helye. A példában a 286. rekord, de ahogy írtam, más kulcshosszaknál másik rekord romlik el. A split-re pedig mikor kerül sor? Amikor betelik egy index page. Hogy mikor telik be, az pedig éppen a kulcshossztól függ.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
PL/SQL in/out/in-out paraméter bindingot csináltál már? Na ehhez egy out paramétert kell bindolni.
- A hozzászóláshoz be kell jelentkezni
Ebben a rész-szálban amúgy is kiderítették itt a hupperek, hogy ha nincs index, akkor nincs probléma.
Ezzel az a probléma, hogy csak DW-jellegű, egyszer-feltöltöm-aztán-nem-változik c. dolgokra jó, OLTP rendszereknél nem megy az, hogy nincs index.
- A hozzászóláshoz be kell jelentkezni
Most borzasztó gusztustalant fogok mondani, de amíg nem javítják a hibát, addig ebből is kisülhet valami: mivel csak egy session fut bármely pillanatban, azért elég lehetne egy nem egyedi index is (a probléma ugye csak unique index esetén lép fel), és az egyediség kézi ellenőrzése. A nem egyedi index ezt a manuális ellenőrzést gyorsítaná fel.
... Legalábbis akkor, ha DML csak az alkalmazáson keresztül éri az adatbázist (kb. "trusted procedure").
- A hozzászóláshoz be kell jelentkezni
1. Miért gondoljuk, hogy csak a unique indexeket érinti a probléma? Nekem ez elsőre nem világos.
2. Nem tudom, hogy más hogy van vele, de OLTP-nél referential integrity nélkül élni elég értelmetlenné teszi az Oracle használatát, az meg nemigen van meg primary key nélkül. Akkor ennyi erővel már sqlite is lehetne, az legalább pici és kiba gyors (KISS)...
- A hozzászóláshoz be kell jelentkezni
Konkrét próbák mutatják, hogy csak a unique indexet érinti a probléma. Szintén próba mutatja, hogy a primary keyt ugyanúgy érinti a probléma. Ebből a szempontból primary key==unique index.
Van értelme referential integrity nélkül élni.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
A unique indexben éppen a unique, ami lényeges, mert kikényszeríti az egyediséget. Ugyanaz, mint a primary key.
--
CCC3
- A hozzászóláshoz be kell jelentkezni
Ezeket lehet helyettesíteni triggerben, de lassabb
before insert..
begin
select 1 from table where uniquenak_kene_lennie_col = :new.uniquenak_kene_lennie_col
return már van
exception no_data_found then
return ok
end;
unique esetén:
Van-e külön táblatér az index-eknek? Esetleg úgy tesztelni,
hogy a storage másik táblatérben legyen, talán storage-el
lehetne még játszani
(
initial 2M ->1M ->500k...
next 1
minextents 1
bár ha határon van, akkor ennek átlépése előidézheti a fenti hibát..
- A hozzászóláshoz be kell jelentkezni
Összefoglalom, mi szűrődött le eddig.
Olyan kísérletekről van szó, ahol be van állítva a SERIALIZABLE isolation level.
Kétféle hiba van.
1) ORA-08177: can't serialize access for this transaction
Ezzel az a probléma, hogy olyankor is előjön, amikor biztosan tudható, hogy nincsenek konkurens tranzakciók. A jelenség alkalmazásfejlesztői szempontból azonban nem veszélyes. Ha ugyanis egy alkalmazás serializable isolation levelre alapozza a konkurencia kezelést, akkor fel kell legyen készítve az ilyen hibák kezelésére. Ez abból áll, hogy a hibát elkapjuk, rollbackelünk, és megismételjük a tranzakciót. Mondjuk 1000-ből egyet meg kell ismételni. Szót sem érdemel.
Megfigyelésem szerint a hiba véletlenszerűen jelentkezik. Valójában nem véletlenszerű, csak feltehetően függ a szerver egész előtörténetétől.
2) ORA-01403: no data found
Az insert utáni select hibázik így. Az update feltehetően ugyanúgy, ugyanazért nem találja a rekordot, mint a select, csak a hibaüzenet az update esetében "0 rows updated".
A hibára jellemző, hogy nem függ az szerver előtörténetétől, csak az adott tábla állapotától. Ugyanis, ha mindig drop table-val indítjuk a tesztet, akkor a hiba mindig ugyanott következik be. Ezen belül is úgy gondolom, hogy az index állapota számít. Ugyanis a hiba csak akkor jön elő, ha a táblán unique index van. Ha nincs index, vagy van index, de nem unique, akkor nem jön elő a hiba. A unique index lényege, hogy kikényszeríti a kulcs egyediségét. (Megj: unique index helyett primary key ugyanúgy produkálja a hibát.)
Megfigyelésem szerint a 01403-as hibán nem segít a várakozás és a select/update művelet ismétlése. Ami segít, az az egész tranzakció abortálása és ismétlése.
Alkalmazásfejlesztői szempontból ez nehezebb eset, mert az alkalmazás nem mindig tudja, hogy az Oracle hibája miatt fordult-e elő a jelenség, vagy tényleg nincs benne a sor a táblában.
--
CCC3
- A hozzászóláshoz be kell jelentkezni