Jump to content
php.lv forumi

Kā pareizāk veidojat db modeļus?


codez

Recommended Posts

Es parasti lietās, kur nav nauda iepīta saglabāju visu kolonnu vecās vērtības un tad attiecīgi:

UPDATE .... WHERE col1=old_col1 AND col2=old_col2 utt - ja affected rows ir = 1 tad viss kārtībā, ja nav - tātad pa vidu kāds paspējis ātrāk izmainīt.

Link to comment
Share on other sites

  • Replies 35
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Pieņemot, ka notiek optimistiskā lokošana Oraclē tas izpaudīsies šādi:

Abi requesti nolasa, ka 3.lietotājam nauda ir 400, kamēr neviens no viņiem vēl nav veicis update.

Abi pie reizes arī nolasa transakcijas idu šim ierakstam, pieņemsim, ka tas ir 10.

 

Tad abi palielina šo 400 par 100 uz 500 un viens pēc otra pārglabā 400 vietā 500

Nu šitas nevar notikt tāpēc, ka:

Pieņemsim, ka pirmais, kurš ir paķēris šo ierakstu palielina summu. Kamēr pirmais nav beidzis transakciju jeb pateicis commit, tikmēr otrais šo ierakstu nedabū labot. Tb otrais var izpildīt savu SQL Update teikumu, bet tas gaida. Un gaida mūžīgi, jeb precīzāk izsakoties līdz pirmais pateiks commit vai rollback. Ja pirmais pasaka commit, tad viņš pie reizes ir palielinājis arī transakcijas id uz 11 un otrais tagad mēģina palielināt naudas ierakstu ar iepriekš nolasīto transakcijas id 10. Tā kā tāda ieraksta nav, tad updeits notiek 0 rindiņām un tu saproti, ka ir ziepes.

Savukārt, ja pirmais ir pateicis rollback, t.i., atsaucis transakciju, tad updeits notiks, jo transakcijas id tam ierakstam būs tas pats.

 

Gints Plivna

http://datubazes.wordpress.com/

 

 

Jā nospiedu pirms vēl pateicu domu līdz galam:

Tā kā šī nav Oracle, tad diemžēl es nezinu kā 100% precīzi to risina MySQLā, bet man ir baigās aizdomas, ka to var risināt līdzīgi, vismaz optimistisko lokošanu noteikti var izmantot.

Edited by Gints Plivna
Link to comment
Share on other sites

Tas, ko tu raksti ir aptuveni skaidrs, bet tad problēma ir šeit:

 

Tā kā tāda ieraksta nav, tad updeits notiek 0 rindiņām un tu saproti, ka ir ziepes.

 

Ko darīt?

Rollbackot visas darbības un vienkārši neizpildīt lietotāja requestu?

Vai mēģināt vēlreiz visu to pašu, bet šeit atkal vajadzīgs kaut cik universāls mehānisms kā atkārtot neizdevušās darbības no paša sākuma, jauns read, jauns modify un save.

 

Man gan vairāk simpatizē variants, kad row nolocko pret lasīšanu un nākošais request, ja gadījumā pieprasa tos pašus datus, gaida. Bet nepatīk tas, ka tā lokošana reāli nepieciešama tikai dadījumos, kad dati tiek updeitoti, bet tie ir mazāk par 10%.

Link to comment
Share on other sites

Ko darīt?

Rollbackot visas darbības un vienkārši neizpildīt lietotāja requestu?

Es domāju, ka tas ir atkarīgs no konteksta. Ja tas ir automātisks process, kam gadījusies šāda bēda, tad 1kārt - būtu jācenšas automātiskos procesos no šādas bēdas izvairīties ;) , piemēram, atsitot visiem citiem iespēju kaut ko mainīt, kamēr tas izpildās, 2kārt - ja tas nav iespējams (aplikācija 24*7 utml probzas ), tad protams, šai automātiskai procai ar to būtu jātiek galā, iespējams jāsāk konkrētais pārskaitījums no sākuma.

Savukārt, ja tā ir forma, kurā lietotājs ir kaut ko mainījis, tad jau neko nevar zināt - jo atkarībā no cita lietotāja veiktajām izmaiņām, tas lietotājs, kuram nesanāca saglabāšana, var vēlēties gan datus pārrakstīt ar jau vienreiz ievadītajām vērtībām, gan varbūt nedarīt neko, gan varbūt savadīt pavisam jaunas vērtības, to nekādi algoritmiski uzzināt nevar.

 

Gints Plivna

http://datubazes.wordpress.com/

Link to comment
Share on other sites

Izveidoju nelielu piemēru, lai paspēlētos ar tiem pašiem naudas pārskaitījumiem.

tabula users ar laukiem id un m. Ieliekam 3 ūzerus 1|100,2|100,3|100;

DROP TABLE IF EXISTS `test12`.`users`;
CREATE TABLE  `test12`.`users` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `m` int(10) unsigned NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO `test12`.`users` (m) VALUES (100,100,100);

 

Tālāk 2 faili.

Ajax fails uz kuru izsauc requestus un lapa, kura izsauc requestus.

 

test12_ajax.php

test12.php

 

piemērā parasts variants, kurā lasīšana notiek ar FOR UPDATE kverija beigās un divās vietās iestarpināts pa sekundei aizture, kas simulē kaut cik vienlaicīgu kveriju izsaukšanu. Kveriji tiek izsaukti vienā transakcijā. Piespiežot "both requests" redzam kā viens no requestiem atpaliek par sekundi, jo pie otrā read viņš gaida kamēr otrs request atbrīvos 3 ūzera rowu.

 

Tālāk var pie selektiem noņemt FOR UPDATE un redzēt kā, uzspiežot "both requests" rodas kļūda un kopējā naudas summa visiem 3 ūzeriem vairs nav 300.

 

 

Nākošais variants ir pamainīt kas kam sūta (ar variantu ar FOR UPDATE) - šijā gadījumā tiek simulēts, ka 1. sūta 3. un vienlaicīgi 3. sūta 1.

 

test12.php

 

Ko mēs iegūstam.

1. konekcija noloko ūzeri 1

2. konekcija noloko ūzeri 3

pēc sekundes

1. konekcija griežas pie ūzera 3, kurš ir nolokots

2. konekcija griežas pie ūzera 1, kurš ir nolokots, mysql detektē deadlocku un izmet kļūdu

"1213:Deadlock found when trying to get lock; try restarting transaction"

 

Tātad pavisam vienkāršās situācija mēs varam nonākt pie tā, ka elementārā veidā nav iespējams izveidot pareizi strādājošu aplikāciju.

Ja neizmantos FOR UPDATE, tad būs kļūda aplikācijas loģikā un tā sāks ģenerēt nepareizus datus, ja izmanto FOR UPDATE, tad pat šādā elementārā situācijā varam iegūt deadlock un viena no konekcijām izmet kļūdu, kuras pareizai apstrādei jau būs jābūvē diezgan sarežģīta struktūra, kas pamatīgi sarežģi visu aplikācijas izstrādi.

 

Vispār ir iespējams normāli strādāt ar MYSQL?

Edited by codez
Link to comment
Share on other sites

lieto nevis absolūto set, bet relatīvo increment

UPDATE test12 SET m = m + 100 WHERE id = 1

tad vari kaut vai 10x vienlaicīgus update taisīt un beigās tik un tā būs +1000 :))

 

kkad pasen lasīju, kā šādi iztikt vsp bez lokošanas ;)

Edited by 2easy
Link to comment
Share on other sites

šijā gadījumā protams piemērs ir triviāls un 'modify' daļā ir parasta saskaitīšan, ko var viegli realizēt ar SQL, bet reālos piemēros aprēķini ir daudz sarežģītāki un UPDATE tos nevarēs realizēt, tāpēc reāli bez struktūras read->modify->save neiztikt.

 

Bez tam arī ar UPDATE šeit varētu iekrist, piemeram:

 

users

id|m

1|10

3|100

 

transfērojam 2 paralēlos requestos 10 naudas no 1 uz 3,

 

1.konekcija

SELECT m FROM users WHERE id=1

10

 

2.konekcijas

SELECT m FROM users WHERE id=1

10

 

1.konekcijas requests

Pārbauda vai 10>=10, lai izpildītu updeitus

 

2.konekcijas requests

Pārbauda vai 10>=10, lai izpildītu updeitus

 

1.konekcija

UPDATE users SET m=m-10 WHERE id=1;
UPDATE users SET m=m+10 WHERE id=3;

 

2.konekcija

UPDATE users SET m=m-10 WHERE id=1;
UPDATE users SET m=m+10 WHERE id=3;

 

Beigās

users

id|m

1|-10

2|120

Edited by codez
Link to comment
Share on other sites

nju re, īstā applikācijas sarežģītība neslēpjas ne pp, ne oop, bet out there... tobish pašos algoritmos, nevis kkādās paradigmās ;)

 

principā ja ir kkas sarežģīts, tad sasniegums jau ir tad, kad vsp izdodas to piedabūt, lai tas pareizi strādā. un tad ir pofig, cik tas ir neērti/neefektīvi/nepareizi/nesmuki/... kr4 visas ambīcijas par optimizēšanu var pieturēt kādai citai reizei :P

Edited by 2easy
Link to comment
Share on other sites

1.konekcijas requests

Pārbauda vai 10>=10, lai izpildītu updeitus

 

2.konekcijas requests

Pārbauda vai 10>=10, lai izpildītu updeitus

jā, jā, jā, tāds gudrinieks! ;)

šīs pārbaudes taču ir jāliek sql WHERE daļā (kā tehnisks check), nevis kkur applikācijas loģikā

 

tipa šādi

UPDATE users SET m=m-10 WHERE id=1 AND m >= 10

 

vsp šādos gadījumos Ginta ieteiktā update menedžēšana, izmantojot transakcijas id kolonnu, realy makes sense. malacis Gint. labs ieteikums. bet tā jau laikam ir diezgan izplatīta/standarta prakse. tikai mēs te web developeri līdz tik sarežģītām lietām ne vnm nonākam... :D:D:D

Edited by 2easy
Link to comment
Share on other sites

Pag,pag ir ne tikai userim 1 jāsamazina par 10, bet arī userim 3 jāpalielina par 10.

Pamēģini uzrakstīt to vienā kverijā.

 

Un kāpēc jāizmanto transakcija kollona, ja tiek izmantots jau iebūvētais transakciju mehānisms, ko piedāvā innodb dzinējs

Edited by codez
Link to comment
Share on other sites

Pag,pag ir ne tikai userim 1 jāsamazina par 10, bet arī userim 3 jāpalielina par 10.

Pamēģini uzrakstīt to vienā kverijā.

pēc pirmā update

if (mysql_affected_rows()) ...  // un tad tikai izpilda otro update

 

Un kāpēc jāizmanto transakcija kollona, ja tiek izmantots jau iebūvētais transakciju mehānisms, ko piedāvā innodb dzinējs

ok, lai nebrauktu dziļāk auzās, par šo labāk paklusēšu :D:D:D

bet anyway man patika Ginta lekcija ;)

Link to comment
Share on other sites

Vērta vai ne, es nezinu, bet kāda jēga dračīt ajax pa sekundei, ja tā pat to sagremos vienā vietā.

Vēl lauzīt galvu kā optimizēt kveriju, ja iespēja ir potenciāli arī iebraukt auzās, īsti man saprotami nav.

Es neesmu nekāds spečuks, bet nav tā kā par sasālīts tas viss?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...