Sziasztok!
PHP-ben fejlesztek egy leltár programot amiben van egy leltári szám, ami 2 féle képpen generálódik:
1. Év+Hónap+futó sorszám. Pl.: 11050001, 11050002, 11050003...
2. Csak futó sorszám, Pl.: 00000001, 00000002, 00000003...
Ezen az oszlopon index van, és mellette van egy auto increment primary key is.
A Probléma a következő: ebbe a táblába sok insert történik egy időben, mert a php progit sokan használják egyszerre. A php egy mysql userrel csatlakozik a mysql-hez és hajtja végre a lekérdezéseket. Ebben az oszlopban adott esetben előfordulhat duplikáció, mert van olyan hogy egy leltári számon kell tárolnom szorosan összefüggő termékeket. Ez miatt nem lehet unique, illetve a fentebb leírt leltári szám generálás miatt auto increment sem.
Amikor új elemet viszek a leltárba akkor ki kell olvasnom az adott számozási eljárásnak megfelelő legmagasabb leltári számot, majd az INSERT-nél ehhez adok egyet és beszúrom a többi adattal együtt. Ezzel az a probléma hogy ha az adott pillanatban más is elindít egy beszúrást, akkor ugyanazt a legmagasabb leltári számot fogja beolvasni, majd növelni egyel mint a konkurens user, és így ugyanarra a leltári számra kerülhet 2 teljesen külömböző termék.
Hogy lehet ezt kiküszöbölni?
- 4055 megtekintés
Hozzászólások
Köszi a választ, még egy kérdés ezzel kapcsolatban. Ha a tábla olvasásra lockolva van és közben érkezik egy select utasítás, akkor az hibával vissza dobódik, vagy kivár a mysql míg felszabadul a tábla és utánna visszaadja a select kimenetét?
- A hozzászóláshoz be kell jelentkezni
Ha read lock-ot teszel rá, akkor mindenki tudja továbbra is olvasni, viszont te, mint zároló sem fogod tudni írni, kivéve a local read lock és nem konkurrens insert esetén. Ha viszont write lockot teszel rá, akkor csak te fogod tudni írni/olvasni, a többi tranzakció addig várakozik, míg te nem unlockolod a táblákat. Ha unlock tables-szel leveszed a lockot a táblákról, akkor az eddig várakozó thread-ek már az új, módosított adatokat kapják vissza és írhatnak/olvashatnak.
--
A gyors gondolat többet ér, mint a gyors mozdulat.
- A hozzászóláshoz be kell jelentkezni
Köszi a választ!
Ezek szerint nekem write lockot kell használnom. A kérdés már csak az hogy hogy tudja megkülömböztetni a mysql, hogy én lockoltam-e a táblát vagy más. A program ugyanazt a mysql usert használja az adatok beszúrására lekérdezéseére függetlenül attól hogy magába a programba ki authentikált be.
Vagy ez úgy működik hogy amikor az apacsnak beérkezik egy kérés akkor ő teljesen új szálon fogja kiszolgálni azt mint az előző kérések, ez által maga a php kód is függetlenül fut a többitől? Ha így van akkor logikus hogy a mysql lekérés is új szálon fog beérkezni a mysqld-nek.
Bocsi ha túl triviális dolgokat kérdezek, de szeretném ezt tisztába tenni magamban.
Köszi a választ mégegyszer, sokat tanultam belőle!
- A hozzászóláshoz be kell jelentkezni
Amikor phpből kapcsolódsz kap egy egyedi erőforrás-azonosítót. Ez fogja végezni a lockolást, és tudja feloldani.
Fejlesztésnél azért veszélyes, mert ha végtelen ciklusba futsz, akkor kézzel kell a lock-ot felszabadítani :)
- A hozzászóláshoz be kell jelentkezni
A user tökmindegy. Minden egyes megnyitott session, egy egyedi entitás, ami külön thread-et kap. A többi a mysql (-fejlesztők) dolga. Tudni fogja, hogy ki és mikor, milyen zárolást kért.
--
A gyors gondolat többet ér, mint a gyors mozdulat.
- A hozzászóláshoz be kell jelentkezni
Ilyet nem szabad lockolni, se select max-al visszaolvasni, csinalsz egy sequence tablat autoincrement-el, es last_insert_id()-val visszaolvasod a beszurt id-t.
- A hozzászóláshoz be kell jelentkezni
PostgreSQL + szekvencia...
- A hozzászóláshoz be kell jelentkezni
+tranzakció :)
- A hozzászóláshoz be kell jelentkezni
Szekvencia csak addig jo, ameddig nem kell sorfolytonosnak lennie. Onnan kezdve bukta.
----------------
Lvl86 Troll
- A hozzászóláshoz be kell jelentkezni
Ezredmásodpercről van szó nem? (gondolom közvetlen a beszúrás előtt kérdezed le a legnagyobbat)
Erre szerinted van esély, hogy kétszer adod ki ugyanazt?
Ha van, akkor nem ezredmásodpercről beszélünk, mert user interakció van a lekérdezés és a beszúrás között, az meg a Te hibád.
- A hozzászóláshoz be kell jelentkezni
Igen ezredmásodpercről van szó. De a lehetőség akkor is fenn áll, és szeretném elkerülni a hibát a helyett hogyha egy ilyen becsúszik akkor manuálisan kelljen az adatbázist hackelni...
- A hozzászóláshoz be kell jelentkezni
"Erre szerinted van esély, hogy kétszer adod ki ugyanazt?"
/o\
--
Fontos feladatot soha ne bizz olyan gepre, amit egyedul is fel tudsz emelni!
- A hozzászóláshoz be kell jelentkezni
Gondolom KKV-nak lesz, ahol max 2-5 ember klikkelget.
...és a rendszer nem 1 db lekérdezésből áll.
ps.: nekem van ilyen (ill. ehhez hasonló) megoldásom és 10 év alatt egy duplázás sem volt még, persze az elvi lehetősége meglenne, de a 0,001 másodperc az elég jó. :)
- A hozzászóláshoz be kell jelentkezni
Ertem en, valahol jogos is, de ez jozan mernoki gondolkodassal szornyu dolognak tunik. Kulonosen ha ket mozdulattal megoldhato a problema.
--
Fontos feladatot soha ne bizz olyan gepre, amit egyedul is fel tudsz emelni!
- A hozzászóláshoz be kell jelentkezni
Nagyvállalatnak lesz de amúgy ez teljesen mind1. Egyszerűen ha már csinálok valamit akkor azt rendesen szeretném megcsinálni, és szeretném megtanulni hogy hogy kell az ilyet normálisan megvalósíteni, mert később még szükségem lehet rá.
- A hozzászóláshoz be kell jelentkezni
Akárhogy akarod csinálni, ne Oregon tanácsát kövesd, mert az tényleg nem bolond biztos, csak bolondoknak való..:))
Egyébként meg semmi szükség tábla szintű LOCK-ra, sőt a legrosszabb megoldás amit valaki ajánlani tud... ha jól gondolom MySQL alatt is van szerver oldali tárolt eljárás és SELECT .. FOR UPDATE, ha egy táblából szeded a növekményt.
- A hozzászóláshoz be kell jelentkezni
Már van, de 10 éve nem volt. :)
- A hozzászóláshoz be kell jelentkezni
Részemről én is SELECT ... FOR UPDATE párti vagyok
Egy külön táblát szoktam fenntartani az egyes folyamatok lockolásához, a tábla tartalmazza azokat a folyamatokat amelyek zárolást igényelnek, pl: számlázás, raktár készlet változás, ... Egy folyamat alatt ebből szükség szerint akár többet is zárolok.
Lépések:
1. Tranzakció nyitása
2. Megpróbálom zárolni a szükséges folyamatot (select ... for update)
2a. Ha nem sikerül (dead lock) akkor várok egy kicsit és újra próbálom, ha pl. 5 alkalommal nem jön össze akkor hibaüzenet
3. max-al a legnagyobb érték lekérése, adatok felírása, ....
4a. Tranzakció lezárása (a lezárás automatikusan feloldja a zárolást is)
4b. Ha bárhol hiba van rollback (zárolás automatikusan feloldódik)
- Szerintem nem célszerű a sorszámot külön táblában tárolni, ha pl évente újra kell indítani a sorszámot akkor elég a "select max(sorszam) +1 from tabla where ev = :aktualisev" igy maris automatikusan 1-est oszt ki
- Tárolt eljárások jók, de amennyiben a program és az adatbázis egy gépen van, szerintem több problémát okoz mint amennyit használ, sebességben nem lesz sokkal gyorsabb, főleg ha csak sima adatfeltöltögetésekről van szó, a kód viszont nehezebben karbantartható, hiszen a query-k egy része php-ben vannak másik része meg az adatbázisban
- A hozzászóláshoz be kell jelentkezni
> - Szerintem nem célszerű a sorszámot külön táblában tárolni..
A módszerednek (szerintem) több rákfenéje van.. így hirtelen:
.. az egyik, hogy nem elég rugalmas: pölö.: ha havonta kell új sorszám és analitika végett ki kell mutatni mekkora a "növekmény".. a select count(*) költséges..
.. a másik szintén költség, a megoldásodnál a max() jóval erőforrás igényesebb mint, ha külön tárolja, akár van rá index akár nem..
.. a select for update record szintű zárolásra legjobb.. a Te esetben őszintén meg mondom gőzöm sincs mit lockolhatsz, lehet szintén az egész táblát, ami ugye azért gáz,
mert akkor túl sok mindent blokkolsz
> - Tárolt eljárások jók, de amennyiben..
Ezzel sem értek egyet.. nyilván programozási nyelv és adatbázis kérdése is, de nagyságrendekkel gyorsabb mint nélkülük, a karbantarthatóság részben bukik, mert szerintem pont hogy jól szétválik a két réteg, így ha kell X adatbázisra tudod könnyen teljesen optimalizálni, nem kell egy gagyi köztes réteg, hogy más DB-n is használj. Egy esetben lehetnek komoly problémák, ha adatbázis klaszterrel dolgozol.. más esetekben, szerintem teljesen korrekt, főleg, ha a "programod" nem csak egy nyelven működik/működhet.
- A hozzászóláshoz be kell jelentkezni
Lehet hülyeség, de a MAX() helyett én inkább ezt szoktam használni:
ORDER BY kivantmezo DESC LIMIT 0,1
- A hozzászóláshoz be kell jelentkezni
Konkrétan tudok olyan - egyébként klasszis - dbkezelőt, amelynek optimalizálója számára a LIMIT (pontosabban az annak megfelelő elem) felér egy szívlapátos fejbeveréssel. Míg a MAX minden motor (és optimalizáló, ha van olyanja) számára egyértelműen elmondja, hogy egyetlen értékre kell felkészülni
- A hozzászóláshoz be kell jelentkezni
.. az egyik, hogy nem elég rugalmas: pölö.: ha havonta kell új sorszám és analitika végett ki kell mutatni mekkora a "növekmény".. a select count(*) költséges..
Ezt nem értem, minek a növekményéről beszélünk? Ha külön táblában tárolod a legmagasabb sorszámot az miben segít kimutatás készítésekor?
.. a másik szintén költség, a megoldásodnál a max() jóval erőforrás igényesebb mint, ha külön tárolja, akár van rá index akár nem..
Már miért lenne erőforrás igényes egy max() egy indexelt tábla esetén? Az hogy egy ilyen jellegű sorszámra index kell szerintem egyértelmű.
.. a select for update record szintű zárolásra legjobb.. a Te esetben őszintén meg mondom gőzöm sincs mit lockolhatsz, lehet szintén az egész táblát, ami ugye azért gáz, mert akkor túl sok mindent blokkolsz
Valóban lehet nem voltam egyértelmű, természetesen rekord szintű lock-ra gondolok
Egy tábla amiben a folyamatokat reprezentáló rekordok vannak, ezeket a rekordokat lock-olom nem az adatokat tároló táblát
|--------------------|
|Locktabla |
|--------------------|
| 1. Számlázás |
| 2. Raktár kezelés |
| 3. ... |
| |
|--------------------|
Tárolt eljárások, nem azt mondom, hogy egyértelműen kerülendő, hiszen ez feladat és környezet függő. Abban hogy az előnyök és a hátrányok milyen súllyal esnek latba nem értünk egyet, szerintem maradjunk ennyiben :)
- A hozzászóláshoz be kell jelentkezni
> miben segít kimutatás készítésekor?
Hozzádobsz egy valamely dátum mezőt és máris meg van, hogy X évben Y hónapban mennyit vittek be, de schema tárolásra is szokták használni (miképp képezik a "id"-t), stb..
> Már miért lenne erőforrás igényes egy max()..
Adatbázisa válogatja, de a max, count és aggregátor társai "nem mindig" használják az index-t, hiába teszel rá.. tény nem tudom mysql esetében, hogy van,
én már jártam pórul.
Egyébként jó a max-os megvalósítás is.. csak nekem nem tetszik valamiért, nem érzem jónak.. a megérzésekre meg hallgatni kell.:)
Így már érthető mit lockolsz..:)
Te is egy külön táblában lockolsz, az már részlet kérdés másra is van-e használva.
- A hozzászóláshoz be kell jelentkezni
Mysql-ben altalaban rendezett btree indexeid vannak, ezert a max egy nagyon olcso muvelet. Ha indexelve van a max, akkor a jobb szelso leaf node-ot fogja csak elolvasni a select az indexbol. Ha hash indexed van memory vagy ndb engine-ju tablan, akkor mar nyilvan nem ez a szitu, hasonloan hogyha mas adatbaziskezeloben probal az ember ilyet csinalni nem btree index-szel.
- A hozzászóláshoz be kell jelentkezni
Kicsit fapad, de miért nem csinálsz egy táblát, amiben 1 db autoincrementes mező van, arra csinálsz insert-et majd kéred le mysql_insert_id -vel az utolsó beszúrt ID-t?
- A hozzászóláshoz be kell jelentkezni
csinálhatnád úgy is hogy az adatbázisban a leltári számot két mező adja. Egyik Év+hó a másik meg mehet egy autoincrement futo szám.
Ha a két generálási módban a futo szám nem ugyanazt a számot jelenti akkor hagyd figyelmen kívül amit írtam.
- A hozzászóláshoz be kell jelentkezni
A két módszernek teljesen külön jelentése van. Ez az ötlet nem valósítható meg, mert ügye van 4 karakterem az év+hó-ra, plusz 4 a futó sorszámra. De utóbbinak minden hónapban 1-ről kell indulnia.
Azért köszi.
- A hozzászóláshoz be kell jelentkezni
"Ezzel az a probléma hogy ha az adott pillanatban más is elindít egy beszúrást, akkor ugyanazt a legmagasabb leltári számot fogja beolvasni, majd növelni egyel mint a konkurens user, és így ugyanarra a leltári számra kerülhet 2 teljesen külömböző termék."
"Hogy lehet ezt kiküszöbölni?"
Pl. a jo oreg tranzakciokkal? :)
--
Fontos feladatot soha ne bizz olyan gepre, amit egyedul is fel tudsz emelni!
- A hozzászóláshoz be kell jelentkezni
Ha jól értem lekéred php val az legújabb leltári számot majd generálsz egy annál 1 el nagyobbat és vissza inserteled. Ugye? És a problémád az, hogy ha párhuzamosan történik mindez akkor azonos számra kerülhet két cikk aminek nem kéne. Megoldás az, ha az egészet mysql-ben oldod meg. Kezeli az időt és egy insert en belül lehet select is. Nézz utána.
Ha lockolod a táblát akkor meg a másik rögzítés megy pocsékba! Kivéve ha phpval ellenőrzöd, hogy sikerült-e és ha nem akkor újra próbálod n szer. ( Ez egyszerűbb talán )
Sok sikert!
- A hozzászóláshoz be kell jelentkezni
az ilyet anno az 1900-as evekben btrieve alapokon rekordszintu lockolassal oldottuk meg.
set_lock();
while get_last_record();
inc(egyediazon);
insert_new_record();
ulock();
masik megfejtes volt a kulon fileben/tablaban tartott szamlalo.
param_lock();
szamlalo_kiolvas-megnovel-visszair();
param_unlock();
azota biztos vannak fejlettebb modszerek is, tizenx ( de lehet 20 ) eve nem programoltam.
- A hozzászóláshoz be kell jelentkezni
A második felét hívják manapság szekvenciának, ami a probléma felvetője szerint nem jó, mert általában egyedi azonosító kell, de nem minden esetben...
- A hozzászóláshoz be kell jelentkezni
Igy nem tudod?
INSERT INTO xxx (leltari_szam, ...) VALUES ((SELECT max ... FROM ...) + 1, ...)
Ez egy tranzakcioban fog lefutni, es garantalja hogy nincs concurrent select/insert.
Bocs az ekezetek hianya miatt, telefonrol irok.
- A hozzászóláshoz be kell jelentkezni
biztos?
- A hozzászóláshoz be kell jelentkezni
serializable isolation level esetén :)
--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.
- A hozzászóláshoz be kell jelentkezni
Ez mit jelent? :)
- A hozzászóláshoz be kell jelentkezni
Na ezt lássa meg bármilyen épeszű DB-programozó... Tudom, dibézben, meg házihákományban ez a módi, de azért 2011-et írunk...
- A hozzászóláshoz be kell jelentkezni
Zeller, ez a megoldás szerinted miért nem jó?
- A hozzászóláshoz be kell jelentkezni
Srácok bocs de hogy jönnek ide a tranzakciók? Azon kívül, hogy vannak olyan mellékhatások, amiket fel lehet használni a probléma megoldására, de kb. oldalakon keresztül lehetne sorolni, hogy melyik adatbáziskezelő melyik verziója milyen izolációs szint / cursor stability mellett mit is lockol és mit nem..
Azt is leírta az OP, hogy a szekvencia nem megfelelő..
Javasolt megoldás:
Egy segédtáblában a teljes leltári szám a mező tartalmát képezi (meg esetleg lehetnek egyéb mezők amik segíthetnek a selectben, pl. dátum) és minden felhasznált leltári számot beinsertálsz. Ha új leltári szám kell, kiválasztod a megfelelő max értéket, hozzáadsz egyet, beinsertálod. Ha nem sikerült (unique!), akkor közben már valaki berakott valamit, hozzáadsz még egyet és újra megpróbálod. Ciklusból. Az alkalmazásból. Mivel tudod, hogy az alkalmazásodnak mire van szüksége és mit csinál, ez egy teljesen korrekt megoldás.
Mielőtt pure sqlből próbálnád megoldani:
http://forums.oracle.com/forums/thread.jspa?threadID=681931
google select max for update (bug, problems, stb)
- A hozzászóláshoz be kell jelentkezni
Abban az esetben, ha csak egyetlen rekordot kell felvinni, valóban elhagyható lenne a tranzakció, de mi van ha a leltári ív felviteléhez több táblába is kell írogatni, kézzel törlöd őket, ha valahol elakad a folyamat?
- A hozzászóláshoz be kell jelentkezni
Szia! Természetesen kell tranzakciót használni, mint mindig, ebből az okból, de a tranzakciónak nincs köze a felvetett problémáhz (sorszámozás)
- A hozzászóláshoz be kell jelentkezni
"Srácok bocs de hogy jönnek ide a tranzakciók?"
Hogyhogyhogy? Egy serializable tranzakcio mellett nem fordulhat elo ez: "ha az adott pillanatban más is elindít egy beszúrást, akkor ugyanazt a legmagasabb leltári számot fogja beolvasni, majd növelni egyel mint a konkurens user, és így ugyanarra a leltári számra kerülhet 2 teljesen külömböző termék".
Ami ugyebar maga a Problema.
--
Fontos feladatot soha ne bizz olyan gepre, amit egyedul is fel tudsz emelni!
- A hozzászóláshoz be kell jelentkezni
Szerinted a serializable azt jelenti, hogy ha nyomtál egy begint, akkor a következő begin várni fog, amíg be nem fejezed? :) Magában nem, lockolnod kell
Egyébként meg serializable szintnél kell a kódba egy ugyanolyan ellenőrzés az unable to serialize esetre, mint amikor ráfutsz a segédtáblás módszernél már létező sorszámra, a különbség, hogy ennek, ha ránéz valaki a kódra, semmi köze nem lesz az üzleti logikához, ezért nem annyira átlátható. Aki kihagyja az ellenőrzést (~mindenki), az meg fail
- A hozzászóláshoz be kell jelentkezni
de, igen, hatásában azt jelenti a serializable, sőt, sokkal többet.
konfliktus-szerializáció, megvan?
--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.
- A hozzászóláshoz be kell jelentkezni
Ezt írtam:
"Szerinted a serializable azt jelenti, hogy ha nyomtál egy begint, akkor a következő begin várni fog, amíg be nem fejezed? :) Magában nem, lockolnod kell"
"de, igen, hatásában azt jelenti a serializable, sőt, sokkal többet."
Szóval nem kell kulon lockolni?
Serializable mod:
Thread1 Thread2
begin
select * from blabla begin # csak share lock
leltarid eloallitasa programban select * from blabla # csak share lock
leltarid eloallitasa programban
insert into blabla
insert into blabla # var, amig a masik tranz. lezarul
commit
commit # ket ugyanolyan leltarid lesz
- A hozzászóláshoz be kell jelentkezni
az utasítás ütemezésed rossz, talán read committed, esetleg.
--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.
- A hozzászóláshoz be kell jelentkezni
???
Ha a fejed tetejére állsz, a select akkor is csak share lockot fog rátenni. A doksi szerint is. Ami a jelen problémán nem segít. Vagy mire gondoltál?
Ha exkluziv lockot nyitsz (for update), az oke, de arrol volt szo, hogy nem kell.
Edit: de ne ezen ne vitatkozzunk, értem, mit akarsz mondani, és így rossz a példám is. Visszatérve az eredetileg feltett kérdéshez, szerinted nem jobb, ha a leltárid-ket inkább letárolja unique módon?
- A hozzászóláshoz be kell jelentkezni
"Elvileg" szerintem, ha egyáltalán erről van szó, akkor neki van igaza:
http://en.wikipedia.org/wiki/Isolation_%28database_systems%29
A második művelet el se fog indulni, ha van már egy futó művelet.
- A hozzászóláshoz be kell jelentkezni
Nemrég terveztem egy anyázó postot hogyan is mennek néha ezek a lockolások DB2-ben.. főleg amióta MVCC-znek.. de aztán elfáradtam.
Egyébként ha papírforma szerint csinálod akkor serializable szinten bejön a deadlock és unable to serialize kezelés (ami ezen az izolációs szinten nem hiba, hanem normál működés), ezt meg lehet úszni pl a fentebb írt megoldással, iagz, ebben az elszigetelt problémában ezt nem lenne jelentős kezelni, de van alkalmazás, ahol igen
- A hozzászóláshoz be kell jelentkezni
Nem vagyok formában, most esett le csak igazán miről van szó.. ott a pont megint.:)
A módszered, hogy külön táblát használsz és abban az össze id-t letárolod megoldás, jobb mint amit a linkeden olvastam (sequence, sequence_within_parent).
Egyébként valahol röhej, hogy a "bombabiztos" megoldáshoz 2010-ben még mindig mennyit "kell" gályázni.
- A hozzászóláshoz be kell jelentkezni
A linken nem megoldás volt, hanem egy nagyon hasonló probléma Oracle alatt és egy konklúzió, hogy szar az egész és csináld meg programmatikusan. :)
(arra próbálva utalni, hogy igencsak DBM függő ez az egész tranzakciósdi)
- A hozzászóláshoz be kell jelentkezni
Az "elvileg" részhez: http://pastebin.ca/2066326
Ez a kód egy random itteni PostgreSQL-en (8-as) a következő outputot adja:
% ./test.pl
1
2
3
4
4
9-esben elvileg unable to serialize lenne (helyesen). MySQL 5.5 -ben mindig deadlock-ra fut (tehát a második művelet elindul, ahogy a doksijuk is írja), van egy korábbi MySQL verzióm, ahol viszont hasonló duplikátum lesz az eredmény.
Ha valami fatálisat elírtam a kódban akkor mea maxima culpa és szóljatok!
- A hozzászóláshoz be kell jelentkezni
Srácok ez szarabb mint gondoltam. MySQL esetén csak azért várt az első insert, mert ebben a speciális tartalmú táblában page lockra konvertálódott (optimalizálódott?) a dolog innodb statisztikában mókolás alapján. Raktam bele jó sok cuccot meg mindenféle selectet, így simán jönnek a duplikációk. 5.0.51-en alapból is lefut és duplikál, 5.1.54 -en megint a page lockos történet de ott van egy row lock is, de most egyelőre meguntam a reszelgetést
Szerk. 5.1.54-en is reprodukálva, csak véletlenül benne hagytam egy for update-et. Csak azért áll meg és lesz lock wait timeout alapból (.. nem igazán korrekt), mert az insert a teljes page-et ahova rakni akarja, ellátná egy exkluzív lock-kal, amely page-ben előfordulhat (de nem biztos, sőt!), hogy ott vannak a másik thread select-jéből származó shared row lock-ok. Tehát még DBM-en belül és verzión belül se kiszámítható, hogy mi lesz. :)
Szerk2. ez alapján készült egy változat, ami az összes mysql verzión működik. (nyilván éles adatokon magától előjönne könnyen, mert a tábla kellően diverzifikált lenne)
- A hozzászóláshoz be kell jelentkezni
én helyből postgresql-t használnék :)
jól tervezett szekvencia kezeléssel a gondjai nagy része elszállna :)
--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.
- A hozzászóláshoz be kell jelentkezni
Cserébe másfajta gondokat venne a vállára..:)
- A hozzászóláshoz be kell jelentkezni
"Szerinted a serializable azt jelenti, hogy ha nyomtál egy begint, akkor a következő begin várni fog, amíg be nem fejezed? :)"
A hatasat tekintve majdnem pontosan ezt jelenti ("sorosithato az utemezes, ha valamennyi hatasa ekvivalens valamely soros utemezessel"), a megvalositast tekintve pedig nyilvan azok a tranzakciok parhuzamosan futnak, amelyek nem nyulnak keresztbe egymasnak.
"Egyébként meg serializable szintnél kell a kódba egy ugyanolyan ellenőrzés az unable to serialize esetre"
Vagy altalanosabban: kell egy ellenorzes akarmilyen okbol torteno tranzakcio-abortalas esetere. Tranziens hibak akarmikor fellephetnek, tehat ilyen esetekben altalaban amugy sem hulyeseg ujraprobalni.
--
Fontos feladatot soha ne bizz olyan gepre, amit egyedul is fel tudsz emelni!
- A hozzászóláshoz be kell jelentkezni
Ugye még nem hallottál a "szigorú sorszámozás"-ról?:)
- A hozzászóláshoz be kell jelentkezni
Azt hiszem, félreértettük egymást, nagyon is hallottam, arra gondoltam, hogy attól, hogy tranzakcióba rakod, még magában nem lesz megoldott ez a fajta sorszámozás, márpedig néhányan ezt írták.
- A hozzászóláshoz be kell jelentkezni
Ha így nézzük akkor teljesen igazad van.
- A hozzászóláshoz be kell jelentkezni
úgy, hogy tranzakciókat kéne használnia.
--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.
- A hozzászóláshoz be kell jelentkezni
Ez a megoldás nem járható, mivel előfordulhat hogy két szorosan összekapcsolódó termék egy leltári számot kap.
- A hozzászóláshoz be kell jelentkezni
Szervusz !
az egyik feladatomnál egy ilyen jellegű problémát úgy oldottam meg, hogy indításnak kiadtam egy INSERT-et, és a felhasználó session_id-je + datetime kombináció MD5-jével kapott egy ideiglenes azonosítót. ezt követően erre az ideiglenes azonosítóra hivatkozva SELECTáltam, megkapva a rekord sorszámát (auto_increment)m és a következő körben végeztem el az általad vázolthoz hasonló paraméterek beírását.
Így nincs LOCK-olás, és _elvileg_ nem keveredik az adat, mert az ideiglenes kódolási algoritmus teljesen más jellegű karakterláncot ad, mint a végleges; emellett a session_id és az INSERTálás időpontja is behatárolja a felhasználót és kérését.
CSZ
- A hozzászóláshoz be kell jelentkezni
"a felhasználó session_id-je + datetime kombináció MD5-jével kapott egy ideiglenes azonosítót"
Az md5 nem egyedi azt ugye tudod?
- A hozzászóláshoz be kell jelentkezni
Miután vásároltam egy hasonló elven működő alkalmazást és használtam egy hónapot, na az után írtam egy számlázó appot magamnak.
Egyszerűsítés! Egyszerűsítés! Egyszerűsítés!
- A hozzászóláshoz be kell jelentkezni
És hogyan oldottad meg a szigorú sorszámozást?
(Érdekes, hogy a sok nem-megoldás mellett elhangzott megoldások sem közelítik meg energiatakarékosságban azt, ahogyan ezt a problémát valóban kezelni kell).
- A hozzászóláshoz be kell jelentkezni
Amikor megoldottam még se subselect, se tranzakció nem volt, így simán:
lekérdezés->növelés->beszúrás
Tudom, hogy elméletileg előfordulhatna így a dupla sorszám.
Gyakorlatilag viszont: nálam 5 ember számlázz és 8 év alatt egy duplázás sem volt eddig.
- A hozzászóláshoz be kell jelentkezni
"Gyakorlatilag viszont..."
Fu, mennyi ilyen esetet lattam, ahol valtig gyozkodtek, hogy gyakorlatilag nem fordulhat elo, mert nekik sose volt. Aztan hopp, egyszer megis elofordult. Es akkor csodalkoznak, hogy nem mindig a legprimitivebb megoldast valasztom, hanem a bonyolultabb, uzembiztosabbat.
----------------
Lvl86 Troll
- A hozzászóláshoz be kell jelentkezni
Tudnád esetleg részletezni?
- A hozzászóláshoz be kell jelentkezni
*Szerintem 5 ember helyett 50 embernek kellene számláznia ahhoz, hogy a 8 évre jusson 1-2 eset amikor ez megtörténik.
*becslés: has/sacc
- A hozzászóláshoz be kell jelentkezni
Tisztelettel mondom, hogy ezt nem tudhatod. Nálam hasonló megoldásból 2 embernek napi párszáz tételéből évi 2 összeakadás volt és nem volt az inserteknél interakcióra várakozás vagy bármi ami miatt nagyobb lenne a valószínűsége.
Olvastam ezt a hash dolgot, szerintem felesleges hasht gyártani. Én egy sima autoincrement unique oszlopra gondolok, mint insertID, bedobálja az ember az adatokat ez alapján és a végén lockolva beszúrom a következő sorszámot. Lockolva lehetne mindjárt a sorszám alapján kezdeni, de ha az utolsó mozzanatként teszed be, akkor tudod azt is, hogy a teljes sor érvényes és befejezett insertek után van, szerintem ez hasznos.
- A hozzászóláshoz be kell jelentkezni
ezt követően erre az ideiglenes azonosítóra hivatkozva SELECTáltam, megkapva a rekord sorszámát
LAST_INSERT_ID()..? :S
----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"
- A hozzászóláshoz be kell jelentkezni
Valami nem stimmel a feladat megfogalmazásában.
Szabál: Minden tételnek kell hogy legyen egyedi azonosítója - unique.
"mert van olyan hogy egy leltári számon kell tárolnom szorosan összefüggő termékeket" - akkor a felhasználónak, először ki kell hogy válassza melyik leletári számra is akarja testálni a tételt.
"Futó sorszám" - ha egyidejűleg visznek be több tételt, és folytonos sorszám ... mihez képest? Ha X felhasználó azt "látta" hogy amikor Ő bevitte a tételt, akkor ez a 9342 -es volt de miután frissült a kijelző akkor ez mégis a 9345 lett, akkor mi van? Az autoincrement (szerintem) csak úgy lehet "foghíjas" ha közben törölnek belőle - a "futó sorszámot" az autoincrementből kell származtatni, ha muszáj akkor az insert után egy update -el betenni.
Attól hogy tranzakcióba teszed, szerintem csak azt éred el, hogy ha nem jön össze akkor rollback?
Radikális megoldásként, azt lehetne csinálni, hogy a központi oldalon az insert kéréseket sorba állítod - nem ezt csinálja az SQL szerver?
* Én egy indián vagyok. Minden indián hazudik.
- A hozzászóláshoz be kell jelentkezni
Áh, mind1
- A hozzászóláshoz be kell jelentkezni
Ez való ilyen problémákra:
http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#fun…
- A hozzászóláshoz be kell jelentkezni
Mindent összefoglalva nekem eddig az INSERT-be ágyazott SELECT utasítás tűnik a legegyszerűbb járható útnak. Ebben az esetben ha jól értem nem fordulhat elő, hogy két felhasználóm ugyanazt a leltári számot próbálja beszúrni a táblába.
Kérlek javítsatok ki ha rosszul gondolom. Köszi!
- A hozzászóláshoz be kell jelentkezni
Egy biztos, ezt nem a php kódnak kellene rendezni, hanem a szerver oldalnak.
* Én egy indián vagyok. Minden indián hazudik.
- A hozzászóláshoz be kell jelentkezni
Nem tudom, milyen izolációs szinteket tud a mysql, de általánosságban elmondható, hogy rosszul megválasztott szintnél a commitálatlan (A) inserttel párhuzamosan futó (B) insert select része ugyanazt a maxot látja és inkrementálja, mint az (A), jól megválasztott szintnél meg kénytelen megvárni, amíg (A) befejezi, és új maxot kap.
Szóval ahogy írta itt a kolléga, ez már nem a <? ?> területe.
- A hozzászóláshoz be kell jelentkezni
Ez csak addig megoldás (fogjuk rá), amíg egy adatfelvitel során csak egy rekordot akarsz felvinni (más táblát nem kell töltened, pl leltári ív tételei, ... )
- A hozzászóláshoz be kell jelentkezni
Ez addig sem megoldás, hiszen egy adattáblát olvashat egyszerre tetszőleges számú konnekció, és insert-et is tetszőleges számban lehet egyszerre végrehajtani.
A fenti megoldás tehát többfelhasználós környezetben időzített bombaként viselkedik.
- A hozzászóláshoz be kell jelentkezni
Az elemek egyedi azonosítót kapjanak (itemID), és ezt rendeld hozzá a leltári számhoz. Mindkettő lehet autoincrement, a kapcsolótáblában meg a pároknak együttesen kell egyedinek lenni.
- A hozzászóláshoz be kell jelentkezni
"a kapcsolótáblában meg a pároknak együttesen kell egyedinek lenni."
Hmm, ez nekem új. Ha jól értem valami ilyenre gondolsz:
------------------
|temékek |
------------------
|id |
|név |
------------------
------------------
|kapcsoló tábla |
------------------
|termek_id |
|leltari_szam |
------------------
utóbbi táblában a termék id mindig egyedi, mivel a termékek táblából jön ,ahol az egy auto increment érték.
A leltári szám viszont lehet azonos.
És a kapcsoló tábla sorainak kell unique-nak lenniük. Létezik ilyen hogy két (vagy több) mezőt együttvéve definiálok unique-nak?
- A hozzászóláshoz be kell jelentkezni
persze.
- A hozzászóláshoz be kell jelentkezni
Más adatbázis enginekben régóta használom, de hogy fest ennek a szintaxisa/deklarációja? Nem tudom hogy kell ezt SQL -ben deklarálni (restellem de nagyon kíváncsivá tettél).
Légyszíves írj egy példát - vagy link is jó lesz.
* Én egy indián vagyok. Minden indián hazudik.
- A hozzászóláshoz be kell jelentkezni
oracle:
SQL> create table x (col1 number, col2 number);
Table created.
SQL> alter table x add constraint c unique (col1, col2);
Table altered.
SQL>
persze lehet rögtön a table create-ből is csinálni...
- A hozzászóláshoz be kell jelentkezni
Köszönöm! - ez még jól jöhet :)
* Én egy indián vagyok. Minden indián hazudik.
- A hozzászóláshoz be kell jelentkezni