| Autor |
Beitrag |
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mi 02.02.11 01:36
Moin!
Ich hätte da gerne mal ein Problem.
Folgende Situation: es gibt ein Universum mit vielen Planeten, das Raumsonden erkunden. Die Raumsonden ermitteln die auf einem Planeten vorhandenen Resourcen und melden diese an eine zentrale DB auf der Erde (ist nur ein Beispiel, kommt nicht auf die technischen Details an). Die DB auf der Erde enthält drei Tabellen:
(A) Rohstoffe: res_id, res_name
(B) Planeten: plt_id, plt_name
(C) Kreuz: x_res_id, x_plt_id
Zusammenhang ist denke ich offensichtlich, sonst nachfragen.
Wenn nun eine Sonde einen Planeten entdeckt hat, dann sendet sie den Namen des Planeten und die gefundenen Rohstoffe als Liste los. Der DB-Manager-Prozess auf der Erde holt sich nun eine Liste der schon bekannten Rohstoffe aus der Tabelle A und fügt neue Rohstoffe ein, wenn unbekannt. Dann wird in Tabelle B der Planet angelegt und abschließend in C jeweils ein Verknüpfungs-DS für die Rohstoffe.
Soweit, so gut. Nun bekommt aber der DB-Manager u.U. mehr als eine Nachricht auf einmal zur Verarbeitung. Da die Rohstoffe aber nicht direkt auf DB-Ebene verarbeitet werden, sondern über eine Lookup-Tabelle in der PHP-Schicht über der DB (aus Performance-Gründen, real passiert da was anderes, als im Beispiel), kann es zu Problemen kommen. Beim Eintreffen der Sonden-Nachricht werden die Tabellen gesperrt, die Lookup-Tabelle aufgebaut und dann neue Rohstoffe, der Planet und die Verknüpfungen eingefügt. Das mache ich so:
SQL-Anweisung 1:
| LOCK TABLES Rohstoffe WRITE, Planeten WRITE, Kreuz WRITE | Wenn die Nachricht verarbeitet ist, entsperre ich die Tabellen wieder:
SQL-Anweisung Sollte alles kein Hexenwerk sein.
Leider kriege ich aber trotzdem noch Fehler beim Einfügen (Rohstoffe.res_name ist unique; es gibt theoretisch auch einen Update-Prozess, ein Planet könnte auch mehrfach untersucht werden und dann auch andere Resourcen haben), was eigentlich nicht sein sollte. Oder?  Was mache ich falsch?
cu
Narses
//EDIT: Darstellung korrigiert: Der Fehler tritt beim Einfügen der Rohstoffe auf, nicht bei den Planeten. 
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 02.02.11 03:28
Write locks sperren zwar Schreibzugriffe, aber keine Lese-Zugriffe (AFAIR). D.h. wenn eine Resource noch nicht existiert, sperrst Du zwar die Tabelle für andere Prozesse zum Schreiben, wenn aber eine andere Resource nicht verfügbar ist, wird u.U. der falsche Wert für das nächste INSERT erzeugt. Die Datenbank-Operation ist dementsprechend nicht atomar.
Zusätzlich kommt hinzu, dass Du einen Index-Fehler provozierst, wenn Du zweimal versuchst, die gleiche Resource hinzuzufügen und auf dem Namen ein Unique gesetzt ist.
Eine Lösung wäre hier, das Serialisieren aller Operationen auf den gelockten Tabellen zu erzwingen, bzw. generell eine zentrale Instanz zu haben, die das Queueing der Lese- und Schreiboperationen sicherstllt.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Für diesen Beitrag haben gedankt: Narses
|
|
Narses 
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Do 03.02.11 17:32
Moin!
BenBE hat folgendes geschrieben : | | Write locks sperren zwar Schreibzugriffe, aber keine Lese-Zugriffe (AFAIR). D.h. wenn eine Resource noch nicht existiert, sperrst Du zwar die Tabelle für andere Prozesse zum Schreiben, wenn aber eine andere Resource nicht verfügbar ist, wird u.U. der falsche Wert für das nächste INSERT erzeugt. Die Datenbank-Operation ist dementsprechend nicht atomar. |
So ist es, deshalb half das Einfügen eines READ LOCKS:
SQL-Anweisung 1:
| LOCK TABLES Rohstoffe AS dummy READ, Rohstoffe WRITE, Planeten WRITE, Kreuz WRITE |
BenBE hat folgendes geschrieben : | | Zusätzlich kommt hinzu, dass Du einen Index-Fehler provozierst, wenn Du zweimal versuchst, die gleiche Resource hinzuzufügen und auf dem Namen ein Unique gesetzt ist. |
Das ist nicht "zusätzlich", sondern beim Problem gewesen.
BenBE hat folgendes geschrieben : | | Eine Lösung wäre hier, das Serialisieren aller Operationen auf den gelockten Tabellen zu erzwingen, bzw. generell eine zentrale Instanz zu haben, die das Queueing der Lese- und Schreiboperationen sicherstllt. |
Tja, aber wie soll man das in PHP auf einem Apachen tun...  Die Instanzen der Requests unterhalten sich ja nicht miteinander...
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 03.02.11 18:15
Siehe sem_get, sem_acquire und sem_release.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Narses 
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 04.02.11 15:35
Moin!
BenBE hat folgendes geschrieben : | | Siehe sem_get, sem_acquire und sem_release. |
Tja, netter Ansatz, aber dann ist man auf Linux angenagelt.  Ich habe das jetzt mit "beratenden Sperren" auf MySQL-Ebene gelöst (SELECT GET_LOCK() und SELECT RELEASE_LOCK()).
Es steckte aber noch ein Bock drin, und den zu finden war wirklich nicht einfach...
Die Tabelle Rohstoffe hat eine ci-Collation, was offensichtlich dafür sorgt, dass der Unique-Index auf der Spalte Rohstoffe.res_name auch ci ist!  Und so ist "Gold" und "gold" leider für die DB das Gleiche, aber in PHP nicht...  Also wurde das nochmal eingefügt -> Exception.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Fr 04.02.11 15:45
Narses hat folgendes geschrieben : | Moin!
BenBE hat folgendes geschrieben : | | Siehe sem_get, sem_acquire und sem_release. | Tja, netter Ansatz, aber dann ist man auf Linux angenagelt.  |
Jain. Windows bietet auch Semaphores/Mutexes an, die man verwenden kann. Ähnliches lässt sich aber z.B. auch mit Lockfiles realisieren, was aber unter Windows unnötig bremst.
Narses hat folgendes geschrieben : | Ich habe das jetzt mit "beratenden Sperren" auf MySQL-Ebene gelöst (SELECT GET_LOCK() und SELECT RELEASE_LOCK()).  |
Die kannt ich jetzt noch nicht
Narses hat folgendes geschrieben : | Es steckte aber noch ein Bock drin, und den zu finden war wirklich nicht einfach...
Die Tabelle Rohstoffe hat eine ci-Collation, was offensichtlich dafür sorgt, dass der Unique-Index auf der Spalte Rohstoffe.res_name auch ci ist! Und so ist "Gold" und "gold" leider für die DB das Gleiche, aber in PHP nicht... Also wurde das nochmal eingefügt -> Exception.
cu
Narses |
Entweder die Collation auf z.B. utf8_binary setzen ODER bei der Abfrage, ob etwas bereits existiert, die DB ran lassen. Alternativ kann man aber mit den mb_string-Funktionen (ab PHP6 nativ verwendet), bzw. den iconv-Funktionen den Collation-Vergleich in PHP machen lassen. Der Weg über die DB dürfte hier aber u.U. einfacher sein, zumal er sich dann auch transparent in Stored Procedures u.ä. verpacken lässt.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
|