Hallo!
... sind mir langsam zu viele Andreas hier im Forum ;-)
Das ist dann mein Problem. Wenn es > 9 Standorte geben sollte mach ich ein Faß auf und besauf mich, zumindest sehe ich zur Zeit (leider) nicht in Ansätzen das die Zahl erreicht wird.
Da du nicht wirklich "Standorte" hast, sondern im Prinzip nur "Datenbankenstandorte", könnte es ganz schnell passieren, dass du mehr als 9 "Standorte" hast. Jetzt sind es schon 4. Dann gib noch 5 Mitarbeitern einen Laptop für die mobile Datenerfassung in die Hand -
da sprichst Du was an - ab MySQL 4.x gibts das so zu sagen als Stand-Alone Client version, das wird interessant...
schon hast du 9 Standorte. Wenn dann noch ein Backup-System für den Shop dazukommt (zweiter Server, kriegt bis zum Ernstfalleinsatz immer nur Daten geliefert - Nummer 10)
ein Server der nur loggt braucht keien ID, denn da kanes zu keinen Konflikten kommen!
und noch zwei Filialen (Nummer 11 und 12)... Naja, es ist jedenfalls Blödsinn, irgendeine Begrenzung einzubauen, die sich irgendwann durch irgendwelche Umstände als außerst störend erweisen könnten - sieh' dir einfach nur das Jahr2000-Problem an!
Ja, aber mir ist aufgefallen das das Problem ja überhaupt gar nicht besteht. Die einzige Beschränkung besteht in der begrenzten Anzahl an Datensätzen! Denn ich verwende das ja immer wie folgt, um die höchte ID des Standortes 5 zu ermitteln:
SELECT ID FROM tabelle WHERE ID >=50000000 AND ID < 600000000 ORDER bY ID DESC
So, das kann ich nach oben ausbauen wie ich will! Das ganze wird regelmäßig nur bei Inserts benötigt, und bei der unregelmäßigen Ermittlung der Daten zur Synchronisation(was natürlich so oft wie möglich passieren soll!)
Und nochwas, die Int-Felder in MySQL sind an sich auf 2147483647 begrenzt, das ist gar nicht so viel mehr als ich jedem Standort zur Verfügung stelle! Man muß sich nur noch über das Szenario Gedanken machen, was denn ist wenn es denn tatsächlich mal dazu kommen sollte, aber das wird man schon lösen! Aber da die Insert-Funktion jeweils zentral nur einmal im Code steht, müßte ich ja nur bei jedem Standort hier was verändern, also halb so wild!
Aber das Problem, wenn man zwei Spalten(Standort-ID, Datensatz-ID) verwendet, dann ändern sich auch alle Zugriffe auf die Datenbank, man kann nicht merh abfragen "WHERE ID =123", man braucht dann "WHERE ID1=12 AND ID2=3232"
Und das will ich unbedingt vermeiden, indem ich nur eine einzige ID verwende aber eine eigene insert-Funktion baue, die zuerst die Tabelle lockt, danach die höchste ID mit der eigenen StandortID am Anfang ermittelt, diese um 1 erhöht, und in den Query-String schreibt!
Warum willst du das Unvermeidliche vermeiden? Du brauchst eine ID zur Identifikation des Datensatzes, und eine ID zur Identifikation der Erfassungsdatenbank - die Kombination beider ist eine systemweit eindeutige ID. Ein SELECT mit zwei Bedingungen ist zwar etwas langsamer als ein SELECT mit nur einer Bedingung - aber immer noch wesentlich schneller, als ein SELECT mit concat() (um die zwei IDs zu verbinden, bevor eine Bedingung wirksam wird).
von concat() war keine Rede - wieso das? Bei SELECT brauche ich das eben nicht! Es geht mir hier aber weniger um die Geschwindigkeit, mehr um den Aufwand der Implementierung. Wenn irgend möglich sollte das auch für bestehende Projekte anwendbar sein. Und da finde ich es erheblich einfacher die paar INSERTs über eine neu Funktion zu schreiben, als alle SELECTs, alle Links.... und was alles daran hängt neu zu schreiben, der Aufwand dürfte 20-50 mal so groß sein!
Ich möchte nach Möglichkeit vermeiden das man das komplett für die Synchronisierung programmieren muß. Ich suche eher die Möglichkeit eine X-belibige Tabelle synchronisieren zu können, indem ich einfach die Methode zum schreiben von Datensätzen verändere, am besten über ein Klasse global. Wenn ich mich dann halt auf 10 Mio Datensätze beschränken muß - Pech, das Leben ist hart, aber wie gesagt, das ist extrem unwahrscheinlich. Ich weiß, das sollte man nicht machen, aber sei doch mal realistisch - ist das wirklich der Aufwand wert?
Allerdings ist das nicht die einzige Möglichkeit...
die da wären? ;-)
Ich war insgesamt auf folgende Modelle gekommen
1. wie beschreiben, eine ID die sich aus Standort_ID und Datensatz-ID zusammensetzt
2. Master-Server der IDs verteilt, Client-Server haben sowohl eigene IDs, und die des Master
3. Direkte Anbindung an Master-Server, direkt IDs holen
4. Nur Master-DB, rest nur Spiegelungen zur Sicherheit
Die 2. Variante hatte ich zunächst favorisiert, ich hatte das wie folgt geplant:
Es geht wieder um die Tabelle "Bestellungen" die auf dem Master und den Clients zur Verfügung steht.
Die Tabellen des Master und der Clients sind fast gleich nur hat der Master die Master-ID mit autoincrement, und Standort-ID als Felder, dafür haben die Clients eine jeweils eigene Client-ID mit autoincrement, und ein Feld Master-ID, die ggfs, bei der Synchronisation zugewiesen wird, oder bei jedem INSERT versucht wird zu bekommen.
Bei der Synchronisation mit dem Server wird wie gesagt einem neuen Eintrag eines Clients die Master-ID zugewisen, solange hat der nur seine Client-ID. Der Server speichert dafür die Standort-ID, um zu wissen von welchenm Standort das kam (nicht unbedingt notwendig, aber vereinfacht die Synchronisation)
Das doofe daran, ich habe eine andere Tabelle auf dem Master als auf dem Client, und das an sich ist schonmal großer Mist! Das verkompliziert einige Dinge ganz schön!
Außerdem - wie mache ich das jetzt bei den Clients, welche ID verwende ich hier? die Client-ID? Dann verwende ich als 2 verschiedene IDs für denselben Datensatz, das ist auch nicht gerade gut. Dazu kommt - obwohl ich hier dieselbe Anwendung habe wie auf dem Master, muß ich das Interface speziell programmieren, auch ein erheblicher Nachteil! und es gab noch mehr Probleme die mir jetzt nicht einfallen...
Die eigentliche Synchronisation führe ich dann mit curl durch, hiermit übertrage ich die Daten über SSL zum anderen Server, wo ein Script auf die Daten wartet, die neuen Daten schreibt und die eigenen Änderungen ermittelt und zurückschickt. Erst war noch eine GPG-FTP Kombination im Gespräch, nachdem SCP ausgeschieden war, aber die curl-Variante ist wohl die einfachste! Die zu synchronisierenden Daten werden per SQL aus der jeweils lokalen Datenbank ausgelsen.
Erkläre eine Datenbank, die dauerhaft erreichbar ist, zur Master-Datenbank. Zu dieser Datenbank werden alle extern eingegebenen Daten hochgeladen, und sie hat im Zweifel die einzig gültigen Daten.
Genau das mache ich auch, das was ich vorhabe ist bis auf die Begrenzung und das ich kein Autoincrement verwenden kann perfekt:
Ich habe gleiche Tabellen überall, ich habe gleiche IDs überall, das macht das ganze System _erheblich_ einfacher und nicht annährend so Fehleranfällig wie die anderen Varianten!
Wie oben beschreiben Frage ich beim Synchronisieren und schreiben die höchste ID des eigenen Standortes ab und generiere die neu ID halt selbst. Sollte auch nicht wirklich wild sein.
Ich synchronisiere immer über den Linix-Server, der beim Provider steht immmer ständig an das Internet angebunden ist. Diesen hätte ich dann auch als Master genommen. Aber dadurch das ich immer über den einen Server abgleiche kann ich einfach den letzten Sync-Zeitpunkt loggen, und alle Datensätze auslesen, deren Timestamp neuer ist, und die schreibe ich dann einfach in die eigene DB. Genau dasselbe umgekehrt, ich logge auch lokal den letzten Sync-Zeitpunkt, alle neuen Datensätze werden zum "Master" übertragen und der trägt das ein. Kein Problem mit Zeitdifferenzen, perfekt.
Mir fallen zwei Methoden ein, die beide dasselbe Prinzip verfolgen.
1.) Du hast zwei ID-Felder. Eine ID ist die endgültige ID. Diese ID wird vergeben, wenn der Datensatz in die Master-DB eingetragen. Bis es soweit ist, werden in den dezentralen Datenbanken temporäre IDs vergeben.
Genau das war mein vorheriger Plan(s.o.), der auch zur Zeit so läuft, aber damit bin ich nicht glücklich.
2.) Um das Gehampel mit zwei ID-Feldern zu vermeiden, könntest du auch ein ID-Feld und ein Herkunftsfeld definieren. Das Herkunftsfeld definiert, welche dezentrale Datenbank zur Eingabe verwendet wurde. Wenn der Datensatz zur zentralen Datenbank hochgeladen wird, wird das Herkunftsfeld geändert und eine neue ID vergeben. Die Änderung wird dann zurückgesendet, bzw. generell komplett verteilt.
Die Idee gefällt mir wiederum _SEHR_ gut! Vor allem könnte ich in jeder DB das Herkunfsfeld standardmäßig auf die StandortID stellen, also merke ich das nichtmal! Fast perfekt! Der einzige Wehrmutstropfen: Angenommen ich habe wirklich Probleme mit meiner Internetvernbindung, ein Kunde bestellt, ich gebe das manuell ein, und will direkt eine Rechnung drucken - was verwende ich dann für eine Rechnungsnummer? Die ID die es einige Stunden später nicht mehr gibt? Eine eindeutige Rechnungsnummer hat wieder dasselbe Problem. Das Problem besteht bei meiner favorisierten Variante wiederum nicht - die ID ist sofort eindeutig!
Mit diesem System kannst du anhand der Herkunfts-ID sehen, ob der Datensatz schon zentral gespeichert wurde.
Ich bin sicher, dass tausend Gründe, die du noch nicht genannt hast, in irgendeiner Weise gegen diese Vorgehensweise sprechen - dann bleibt eben nur die Möglichkeit, ID und Herkunfts-DB einmal beim ersten Erfassen des Datensatzes zu vergeben und immer beizubehalten. Ich würde davon abraten, diese zwei Informationen zu vermischen.
Das Problem läßt sich darauf herunterbrechen, dass eine eindeutige ID benötigt wird. Das geht solang gut wie ein zentraler Rechner diese Verteilt. Aber sobald ein Standort aus welchem, Grund auch immer darauf keinen Zugriff hat, gibts Probleme. Prinzipiell gibt es 2 Lösungsansätze:
1. den ID Raum vorher zwischen den Standorten aufteilen
2. ein 2. Feld mit der Standort-ID zur eindeutigen Identifizierung
Die Zweite ist ohne Zweifel sauberer, und die letzte von Dir vorgeschlagene Variante schmeckt mir wirklich gut, aber die erste Variante umgeht sehr viele andere Probleme bei der Programmierung der eigentlichen Anwendung. Bleibt die Frage ob die Beschränkung des ID-Raums wirklich problematisch ist, oder ob es nur unschön programmiert ist, halt ob die Vorteile die Nachteile aufwiegen.
Man MUSS irgendwo beschränken, ich könnte ja dafür auch eine BIGINT mit 12 oder noch mehr Stellen nehmen. Meinst Du das ist wirklich so schlimm? Oder solte man die Bereiche lieber über Bit-Bereiche beschränken? Was meinst Du?
Auf alle Fälle Danke für Deine Antwort, Deine Idee ist wirklich gut und vielleicht kommt sie sogar zum Einsatz, aber ich weiß es einfach nicht. Ich will auch nicht einfach anfangen um dann später alles umzuschmeißen ;-)
Viele Grüße
Andreas