Entwickler-Ecke
Datenbanken - Table-LOCKS - Verständnisfrage
Narses - Mi 02.02.11 01:36
Titel: Table-LOCKS - Verständnisfrage
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:
Sollte alles kein Hexenwerk sein. :nixweiss:
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? :gruebel:
cu
Narses
//EDIT: Darstellung korrigiert: Der Fehler tritt beim Einfügen der Rohstoffe auf, nicht bei den Planeten. :idea: :oops:
BenBE - 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.
Narses - 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 |
:idea:
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... :gruebel: Die Instanzen der Requests unterhalten sich ja nicht miteinander... :nixweiss:
cu
Narses
BenBE - Do 03.02.11 18:15
Siehe sem_get, sem_acquire und sem_release.
Narses - 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. :nixweiss: Ich habe das jetzt mit "beratenden Sperren" auf MySQL-Ebene gelöst (SELECT GET_LOCK() und SELECT RELEASE_LOCK()). :idea:
Es steckte aber noch ein Bock drin, und den zu finden war wirklich nicht einfach... 8)
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! :shock: Und so ist "Gold" und "gold" leider für die DB das Gleiche, aber in PHP nicht... :autsch: Also wurde das nochmal eingefügt -> Exception. :nixweiss:
cu
Narses
BenBE - 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. :nixweiss: |
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()). :idea: |
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... 8)
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! :shock: Und so ist "Gold" und "gold" leider für die DB das Gleiche, aber in PHP nicht... :autsch: Also wurde das nochmal eingefügt -> Exception. :nixweiss:
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.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!