Hallo Zusammen! Zum Loggen von Ereignissen auf mehreren Raspberry Pi's habe ich bisher über den Raspberry PI einen Zentralen Server mit der SQL Datenbank angesprochen. Die Datenbank war also einmal im System vorhanden. Aufgrund der Ausfallsicherheit möchte ich nun die Datenbank 1:1 gespiegelt auf jedem PI installieren und durch einen bidirektionalen Synchronisationsdienst den Austausch zwischen dem Server und den Clients regeln. Da die Datenbank nicht sonderlich groß ist, spielt die Systemperformance keine Rolle und so soll auf jedem Client und dem Server die Datenbank gespiegelt sein. Mein Problem ist, dass ich für die fortlaufende Logging-Tabelle einen Primary Key brauche, der einmalig in der Tabelle ist. Ich kann also nur schwer eine fortlaufende Nummerierung nehmen, da beim gleichzeitigen Auftreten zweier Ereignisse auf verschiedenen Clients es einen Konflikt mit einer gleichen ID (Primary Key) beim Synchronisieren untereinander gibt. Ich suche also eine Möglichkeit, dieses Problem zu umgehen. Eigentlich brauche ich gar keinen Primary Key, aber ohne geht es meines Wissens nach nicht? Ich suche also Tipps für eine mögliche Lösung dieses Problems. Vielen Dank für eure Antworten im Voraus!
:
Bearbeitet durch User
Multi-Master-Replikation ist heikel. Besser geht es mit einer Master- und einer Slave-Datenbank. Bei einem Ausfall wird dann auf die Slave-Datenbank umgeschaltet und diese so lange genutzt, bis der Master (nach manuellem Eingriff) wieder läuft. Falls es unbedingt Multi-Master sein muss: Fortlaufende ID für lokale Nutzung, zusätzlich eine systemweit eindeutige ID (z.B. durch getrennte Nummernbereiche) vergeben.
Ganz habe ich das nicht verstanden. Wenn jetzt alles zentral gespeichert wird, brauchst Du doch auch eine Möglichkeit, die Einträge auseinanderzuhalten, z.B. die Hostnamen oder so. Das, zusammen mit z.B. den Zeitstempeln ist der Primärschlüssel. Dann auf den Master senden und auf die Knoten replizieren. Wenn der Master ausfällt, wird einer der Knoten Master, bis der wieder da ist. Multimaster ist noch trickier, da musst Du konfliktlösungsstrategien implementieren. Es sollte auch klar sein, das keine der beiden Lösungen gegen das Split-Brain Problem gefeit ist, falls Netzwerkpartitionen auftreten und sich Knoten eine Weile nicht sehen.
GUID Wobei die anderen Lösungen hier (Nummernbereiche, Key aus mehreren Feldern, gar kein PK) auch gehen, aber GUID wäre mMn die schönste Lösung.
Ergo70 schrieb: > Ganz habe ich das nicht verstanden. Wenn jetzt alles zentral gespeichert > wird, brauchst Du doch auch eine Möglichkeit, die Einträge > auseinanderzuhalten, z.B. die Hostnamen oder so. Das, zusammen mit z.B. > den Zeitstempeln ist der Primärschlüssel. Meine Anwendung ist ein Zutrittskontrollsystem. Jeder Client verwaltet bis zu acht Türen mit entsprechendem Kartenleser etc.. Bisher werden die Ereignisse zentral im Server protokolliert. Nun möchte ich aber dass auch beim Ausfall des Netzwerkes das System weiter funktioniert. Daher die Idee, die Datenbank auf allen Clients zu spiegeln um so auch autark arbeiten zu können. Wenn das Netzwerk dann wieder verfügbar ist, so sollen die Ereignisse mit dem Server synchronisiert werden. Und genau hier liegt das Problem, einen eindeutigen Primary Key zu generieren. Jeder Client hat ja seine eigene IP und da wäre es mir am liebsten, anhand eines Zeitstempels und der IP einen Primary Key zu erstellen, der nur einmal auftreten kann.
Alexander K. schrieb: > Eigentlich brauche ich gar keinen Primary Key, aber ohne geht es meines > Wissens nach nicht? Du bist nicht gezwungen, einen Primary Key anzulegen. Gerade bei einer Logging-Tabelle fällt mir gerade kein Grund ein. PKs sind halt dann notwendig, wenn ich Daten auf mehrere Tabellen verteile und joinen muss oder sicherstellen muss, dass ein Eintrag eindeutig wird (z.B. ein Auftrag in einer Auftrags-Tabelle, da darf es nur einen mit der Nr 4711 geben). Wie sieht denn so ein Logging-Eintrag aus? merciless
Warum willst Du auf jedem Client eine Datenbank laufenlassen? Einfach bei einem Ausfall lokal cachen, im einfachsten Fall im RAM, und dann beim Auftauchen der Datenbank wegschreiben wie sonst auch.
Alexander K. schrieb: > Ergo70 schrieb: >> Ganz habe ich das nicht verstanden. Wenn jetzt alles zentral gespeichert >> wird, brauchst Du doch auch eine Möglichkeit, die Einträge >> auseinanderzuhalten, z.B. die Hostnamen oder so. Das, zusammen mit z.B. >> den Zeitstempeln ist der Primärschlüssel. > > [...] Und genau > hier liegt das Problem, einen eindeutigen Primary Key zu generieren. > Jeder Client hat ja seine eigene IP und da wäre es mir am liebsten, > anhand eines Zeitstempels und der IP einen Primary Key zu erstellen, der > nur einmal auftreten kann. Und (fast) genau so, solltest Du es eigentlich machen können und damit einen eindeutigen Schlüssel erhalten. Allerdings unter dem Vorbehalt, dass bei 8 Zugängen evtl. zwei oder mehr Zugänge im schlechtesten Fall gleichzeitig benutzt werden. Ich würde noch die Nummer (oder sonstige ID) des Zuganges zu dem Schlüssel hinzufügen (wobei die gleiche Nummer, wegen der zusätzlich vorhandenen IP durchaus bei mehreren RPis vergeben sein darf). Es scheint, als ob da bei Dir ein Zweifel über die Vorgehensweise besteht. Ich kann nur leider nicht erkennen, worin er genau liegt.
Ah. Die Frage ist wohl, ob man einen Primärschlüssel überhaupt braucht. Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein Eintrag mehrfach vorkommen darf. Daraus ergibt sich zwangsweise, dass es eine weitere Spalte (oder eine Kombination von Spalten) gibt, in denen die Werte niemals mehrfach in verschiedenen Einträgen auftreten.
gib doch jeden Gerät auf welchen eine Datenbank läuft eine uuid, dann Berechnest du einen Kryptographisch hinreichend sichern Hash aus der UUID + Timestamp + serialisierte Meldung, das ist dann dein Primär schlüssel. Wenn du dann auch noch den letzten Hash mit in den aktuellen Hash einfließen lässt, hast du auch schon eine Blockchain (TM). jetzt noch die letzten Bekannten Hashvalues der Anderen Instanzen, dann hast du auch eine verteilte Blockchain ;) Na das mit der Blockchain, ist natürlich nur so halb ernst gemeint, aber gerade Logs von Security relavanten Geräten ist einer der wenigen Fälle wo man über eine dedizierte Blockchain nachdenken kann, um bei einen Vorfall die Logeinträge qualifizieren zu können. Mein Vorschlag fehlen natürlich noch ein paar Signaturen um eine vollwertige Blockhain für logs zu bauen.
Theor schrieb: > Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein > Eintrag mehrfach vorkommen darf. Und dazu ist der Schlüssel da ;) Nimm eine fortlaufende Nummer und trage zusätzlich für jede Station eine eindeutige Nummer 1...x mit ein und beim kopieren lässt die fortlaufende Nummer aber weg sondern bildest in deiner Sammeltabelle einen index über Station, Zeitstempel. So hast du alles bei'nander
Dirk K. schrieb: > Wie sieht denn so ein Logging-Eintrag aus? Geloggt wird in jeweils einer Spalte: -Leser-ID (ist einmalig im System und wird aus einer anderen Tabelle anhand der IP und der internen Nummer 1-8 am Kartenleser-Bus des Zutritts-Clients bestimmt) -ID der Zutrittskarte -Name der Person -Datum und Uhrzeit -Ereignis (String) Theor schrieb: > Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein > Eintrag mehrfach vorkommen darf. > > Daraus ergibt sich zwangsweise, dass es eine weitere Spalte (oder eine > Kombination von Spalten) gibt, in denen die Werte niemals mehrfach in > verschiedenen Einträgen auftreten. Das dürfte bei der Kombination mehrerer Spalten kein Problem sein, lediglich eine einzelne einmalige wird schwierig. Daher die Frage, wie ich die Eindeutigkeit schaffe. Hmmm schrieb: > Warum willst Du auf jedem Client eine Datenbank laufenlassen? Einfach > bei einem Ausfall lokal cachen, im einfachsten Fall im RAM, und dann > beim Auftauchen der Datenbank wegschreiben wie sonst auch. Die Datenbank enthält neben dem Logging auch noch Informationen über die Zutrittsberechtigungen, welche allerdings nur zentral vergeben werden und daher in der Synchronisation kein Problem darstellen. Die Informationen müssen aber natürlich bei einem Ausfall des Netzwerkes trotzdem lokal vorhanden sein.
Dennsi W. schrieb: > Theor schrieb: >> Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein >> Eintrag mehrfach vorkommen darf. > > Und dazu ist der Schlüssel da ;) > Genau.:-) > Nimm [...] Mir ist bekannt, was man da macht. Ich bin nur nicht der TO. :-)
Alexander K. schrieb: > Dirk K. schrieb: >> Wie sieht denn so ein Logging-Eintrag aus? > > Geloggt wird in jeweils einer Spalte: > -Leser-ID (ist einmalig im System und wird aus einer anderen Tabelle > anhand der IP und der internen Nummer 1-8 am Kartenleser-Bus des > Zutritts-Clients bestimmt) > -ID der Zutrittskarte > -Name der Person > -Datum und Uhrzeit > -Ereignis (String) > > Theor schrieb: >> Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein >> Eintrag mehrfach vorkommen darf. >> >> Daraus ergibt sich zwangsweise, dass es eine weitere Spalte (oder eine >> Kombination von Spalten) gibt, in denen die Werte niemals mehrfach in >> verschiedenen Einträgen auftreten. > > Das dürfte bei der Kombination mehrerer Spalten kein Problem sein, > lediglich eine einzelne einmalige wird schwierig. Daher die Frage, wie > ich die Eindeutigkeit schaffe. > > [...] OK. Wie schon von anderen geschrieben und auch von Dir. Eine Kombination von Spalten wählen, die in jedem Fall eindeutig sind. (Das hängt in Deinen Beispiel auch davon ab, wovor man sich schützen will. Etwa, kopierte Zugangskarten etc.). Aber die Frage ist jetzt beantwortet, oder? Falls nicht, gib noch mal Laut. Vielleicht schreibst Du auch noch im Detail, was noch genauer beantwortet werden könnte, damit es Dir hilft.
Sorry. Schreibfehler: "Wie schon von anderen geschrieben und auch von Dir. Eine Kombination von Spalten wählen, die in jedem Fall eindeutig ist." Das Eindeutigkeitskriterium bezieht sich auf die Kombination.
Alexander K. schrieb: > Das dürfte bei der Kombination mehrerer Spalten kein Problem sein, > lediglich eine einzelne einmalige wird schwierig. Daher die Frage, wie > ich die Eindeutigkeit schaffe. das ist doch leicht.. autoincrement mit prefix und falls Du das prefix nicht via trigger setzen willst (weil es länger dauert ;)) setzt Du einen AI startwert einfach hoch genug an, sagen wir 5000000001 die andere startet schlicht bei 1 bis da ein Wert doppelt auftritt dauert es gewöhnlich eine ganze Weile. 'sid
Theor schrieb: > Aber die Frage ist jetzt beantwortet, oder? Falls nicht, gib noch mal > Laut. Vielleicht schreibst Du auch noch im Detail, was noch genauer > beantwortet werden könnte, damit es Dir hilft. Danke für eure Antworten! Ich habe nebenbei etwas recherchiert und kam auf folgende Möglichkeit (Variablennamen als Beispiel): CREATE TABLE tab( id INTEGER, id2 INTEGER, PRIMARY KEY (id,id2) ); Wenn ich dies für meine Tabelle mit den relevanten Werten umsetzte, dann brauche ich ja gar nicht zwingend eine separate Spalte für die ID als Primary Key sondern muss nur sicherstellen, dass die angegebenen Spalten in ihrer Konstellation jeweils einmalig sind? Ich kannte es bisher bloß so, als Primary Key eine Variable anzugeben.
Nein, du denkst falsch! Deine Datenbank auf dem Master-Pi wird zu einer bestimmten Uhrzeit zu den Slaves übertragen. Am Besten die Datenbank auf 7 Tage aufteilen, so kann man noch rechtzeitig das Backup fahren.
Alexander K. schrieb: > Theor schrieb: >> Aber die Frage ist jetzt beantwortet, oder? Falls nicht, gib noch mal >> Laut. Vielleicht schreibst Du auch noch im Detail, was noch genauer >> beantwortet werden könnte, damit es Dir hilft. > > Danke für eure Antworten! > Ich habe nebenbei etwas recherchiert und kam auf folgende Möglichkeit > (Variablennamen als Beispiel): > > CREATE TABLE tab( > id INTEGER, > id2 INTEGER, > PRIMARY KEY (id,id2) > ); Ganz grob ist das der Ansatz. Ja. Aber dazu ist Weiteres zu sagen. > > Wenn ich dies für meine Tabelle mit den relevanten Werten umsetzte, dann > brauche ich ja gar nicht zwingend eine separate Spalte für die ID als > Primary Key sondern muss nur sicherstellen, dass die angegebenen Spalten > in ihrer Konstellation jeweils einmalig sind? Richtig. Die meisten DBMS bieten an, einen Primärschlüssel selbst zu erzeugen. Oder eine Spalte alleine ist offensichtlich, und das passiert doch öfter mal, allein eindeutig. Das kann suggerieren, dass dies grundsätzlich hinreichend ist. In Deinem Fall aber geht das erste nur mit zusätzlichen Maßnahmen und das zweite gar nicht. Also suchst Du Dir eine Kombination von Spalten (was Du hier Variablen nennst), die so grundsätzlich und über alle Teilsysteme nur einmal auftreten kann. Z.B. auch:
1 | CREATE TABLE tab( |
2 | Leser-ID INTEGER, |
3 | MomentOfAttempt DATETIME, |
4 | PRIMARY KEY (Lesser-ID, MomentOfAttempt) |
5 | ); |
Der Gedanke ist: Es kann ein einem einzelnen Leser nur zu einem Zeitpunkt eine Versuch gemacht werden, sich zu authentifizieren. (Das muss nicht zutreffen, aber Du schriebst oben, dass in Deinem Fall alle Leser systemweit eine eigene ID haben). Kleiner Hinweis noch. Je nach dem wie lange ein Leseversuch dauert, musst Du evtl. auch die Sekunden in den Schlüssel mit aufnehmen. > Ich kannte es bisher bloß > so, als Primary Key eine Variable anzugeben. Wieder was dazu gelernt. Lächel. :-)
Selbst wenn das so wäre, z.B. bei einem Key/Value Store, kannst Du immer noch mehrere Werte zu einem konkatenieren oder einen Hash benutzen. Und relationale Datenbanken erzwingen keinesfalls, das identische Einträge nicht mehrfach vorkommen dürfen. Das kann man erzwingen, muß aber nicht.
Theor schrieb: > Die Frage ist wohl, ob man einen Primärschlüssel überhaupt braucht. Das kommt allein darauf an, was der Zweck der Daten ist. Par se benötigt man keinen Primärschlüssel. Will man gezielt auf einzelne Datensätze zugreifen können, braucht man einen. Bei Log-Daten sehe ich erstmal nichts, was zwingend Eindeutigkeit einzelner Datensätze erfordern würde. Eine zuverlässige und effiziente Replikation allerdings würde dies erfordern. > Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein > Eintrag mehrfach vorkommen darf. Nein, natürlich nicht. Weil es halt auch unzählige Anwendungen gibt, bei denen das durchaus zulässig sein könnte. Wie gesagt: es hängt vom Zweck der Daten ab.
Theor schrieb: > Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein > Eintrag mehrfach vorkommen darf. Das ist einfach nur falsch. Alexander K. schrieb: > Dirk K. schrieb: >> Wie sieht denn so ein Logging-Eintrag aus? > > Geloggt wird in jeweils einer Spalte: > -Leser-ID (ist einmalig im System und wird aus einer anderen Tabelle > anhand der IP und der internen Nummer 1-8 am Kartenleser-Bus des > Zutritts-Clients bestimmt) > -ID der Zutrittskarte > -Name der Person > -Datum und Uhrzeit > -Ereignis (String) Du könntest einen zusammengesetzten PK verwenden (Leser-ID, CardID, Timestamp), aber stellt sich mir immer noch die Frage nach dem warum? Was soll der verhindern oder verbessern? Wie werden die Daten ausgewertet? merciless
:
Bearbeitet durch User
Wieso soll das so kompliziert sein. So wie ich das verstanden habe, hat jeder Zugangspunkt eine eigene DB. Also musst du nur von den Zugangspunkt aus, regelmäßig (oder auf Anforderung) ein Abgleich mit den Hauptserver machen. Alternativ schickt man die Daten gleich zum Server. Dazu brauchst du nur die Zeitangabe (auf die Sekunde genau) und die Terminal-Nr. Die Zeitangabe und die restlichen Daten speicherst du pro Terminal auf den selbigen. In der Server Datenbank werden beim Abrufen der Daten überprüft ob diese Zeitangabe schon vorliegt. Wenn ja, vergiss es, wenn nein lege Datensatz an (mit Terminal-nr). Nun kann man einfach auf den Server eine Routine schreiben, mit der ein Terminal bei einen Komplettausfall mit seinen Daten wieder gefüttert wird. FERTIG. Dadurch hat man ein Gegenseitiges Backup-System. Und alle paar Monate brennst du mal eine DVD mit den alten Daten (vom Server) wegen Nachvollziehbarkeit und schickst ein Clear an die Terminals damit da die DB nicht überläuft. Gruß Pucki
Dirk K. schrieb: > Theor schrieb: >> Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein >> Eintrag mehrfach vorkommen darf. > Das ist einfach nur falsch. > > [...] Tatsächlich? Aha. Welche Datenbank lässt das zu?
Theor schrieb: > Dirk K. schrieb: >> Theor schrieb: >>> Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein >>> Eintrag mehrfach vorkommen darf. >> Das ist einfach nur falsch. >> >> [...] > > Tatsächlich? Aha. > Welche Datenbank lässt das zu? Ah. MySQL und MariaDB erlauben das offenbar. Wusste ich vorher nicht. Danke für den Hinweis. Hatte mich jahrzehntelang immer bemüht keine doppelten Einträge vorkommen zu lassen, weil das bei meinen Anwendungen ein Fehler gewesen wäre. Da habe ich mich dazu verleiten lassen, anzunehmen, dass die DBMS das nicht zulässt.
Theor schrieb: > Hatte mich jahrzehntelang immer bemüht keine doppelten Einträge > vorkommen zu lassen, weil das bei meinen Anwendungen ein Fehler gewesen > wäre. Ernsthaft: jahrzehntelang? Oh oh... Wer über Jahrzehnte hinweg nichts dazulernt, nur weil die konkreten Anwendungen das gerade nicht erfordern... Ich will nicht weiter ausführen, was das bedeutet. Würde eh' als "Beleidigung" gelöscht werden, auch wenn es nur die objektive Wahrheit ist...
Alexander K. schrieb: > Danke für eure Antworten! > Ich habe nebenbei etwas recherchiert und kam auf folgende Möglichkeit > (Variablennamen als Beispiel): > > CREATE TABLE tab( > id INTEGER, > id2 INTEGER, > PRIMARY KEY (id,id2) > ); Noch mal die Frage, andere haben sie auch schon gestellt: Was willst du mit dem Primary Key, besonders bei deiner Anwendung, wo es um das Aufzeichnen von Logging-Informationen geht? Zweitens, aus den von dir zu loggenden Daten: Alexander K. schrieb: > Geloggt wird in jeweils einer Spalte: > -Leser-ID (ist einmalig im System und wird aus einer anderen Tabelle > anhand der IP und der internen Nummer 1-8 am Kartenleser-Bus des > Zutritts-Clients bestimmt) > -ID der Zutrittskarte > -Name der Person > -Datum und Uhrzeit > -Ereignis (String) kann man sich direkt einen basteln (Leser-ID mit Datum/Uhrzeit). Nur, wozu? Drittens, "ID der Zutrittskarte" und "Name der Person" sind redundante Informationen. Das ist eine Todsünde im Datenbank-Entwurf. Die ID als Foreign Key reicht. Wenn man den Namen will sieht man mit der ID in einer Namenstabelle nach (dort die ID als Primary Key). Das nennt sich eine Relation. Dafür sind diese Relationalen Datenbanken gemacht. > Wenn ich dies für meine Tabelle mit den relevanten Werten umsetzte, dann > brauche ich ja gar nicht zwingend eine separate Spalte für die ID als > Primary Key Du brauchst für das Loggen alleine überhaupt keinen Primary Key in der Tabelle. Nochmal, hast du irgend einen Grund, warum du da einen Primary Key brauchst? Nicht haben möchtest, sondern brauchst?
Danke für eure zahlreichen Antworten! Hannes J. schrieb: > Noch mal die Frage, andere haben sie auch schon gestellt: > > Was willst du mit dem Primary Key, besonders bei deiner Anwendung, wo es > um das Aufzeichnen von Logging-Informationen geht? Ich will ja gar keinen Primary Key, wusste aber nicht, dass ich ihn weglassen kann. Daher hier die Frage. Dank euren Informationen werde ich das ganz weglassen. Kann ich daher einfach die Primary-Key-Anweisung entfallen lassen so wie hier im Beispiel?
1 | CREATE TABLE tab( |
2 | Leser-ID INTEGER, |
3 | MomentOfAttempt DATETIME |
4 | ); |
Alexander K. schrieb: > Kann ich daher einfach die Primary-Key-Anweisung entfallen lassen so wie > hier im Beispiel? Kann man wohl. Macht aber nur Sinn bei write-only-Datenbanken, denn ohne PK wird's schwierig aus der Datenbank etwas zu lesen. Wenn Du einen bestimmten Zeitpunkt suchst wird er jedesmal die komplette Tabelle sortieren müssen. Es tut auch nicht weh einen PK zu setzten, zumindest auf Datetime, Leser. Und einen einfachen Schlüssel auf Leser, Datetime, dann bist du gut versorgt ;)
Es kommt halt drauf an wie du auslesen willst. Interessiert ob sich jemand sich wann wo einloggt müssen in dieser Reihenfolge die Felder in den Schlüssel. (Person, Zeit) Willst du wissen was an einem Leser passiert, Key auf (Leser, Zeit). Willst Du wissen wie oft jemand an welchem Leser durchgeht Key auf (Person, Leser,( Zeit)). So einfach ist das. Wenn du schnell lesen willst macht es mitunter Sinn PrimaryKey und noch einen zweiten Index zu setzten, wobei jedes Feld Speicherplatz benötigt. Auch werden wie schon geschrieben wurde Namen nur einmal in ner Tabelle gespeichert und darauf über die ID verlinkt. Zeit ist immer in unixtime dann hast du kein Stress mit Sommer/Winterzeit.
Wieso muss es unbedingt SQL sein? Das klingt für mich nach Zeitreihendatenbank. Influx, Prometheus etc.
blib schrieb: > Macht aber nur Sinn bei write-only-Datenbanken, denn ohne PK wird's > schwierig aus der Datenbank etwas zu lesen. Wenn Du einen bestimmten > Zeitpunkt suchst wird er jedesmal die komplette Tabelle sortieren > müssen. Quatsch. Ein non-unique Index würde da reichen, dafür braucht es keinen PK.
Ergo70 schrieb: > Quatsch. Ein non-unique Index würde da reichen, dafür braucht es keinen > PK. LOL, Der braucht dann mehr Speicher als nur der PK ;) Die Reihenfolge in der die Grunddaten der Tabelle abgespeichert werden IST der PK ;) Danach kommen erst indexe. Man muss keinen PK setzten, es bringt aber viel Performance beim Lesen/Suchen.
Alexander K. schrieb: > Danke für eure zahlreichen Antworten! > > Hannes J. schrieb: >> Noch mal die Frage, andere haben sie auch schon gestellt: >> >> Was willst du mit dem Primary Key, besonders bei deiner Anwendung, wo es >> um das Aufzeichnen von Logging-Informationen geht? > > Ich will ja gar keinen Primary Key, wusste aber nicht, dass ich ihn > weglassen kann. Daher hier die Frage. Dank euren Informationen werde ich > das ganz weglassen. > > Kann ich daher einfach die Primary-Key-Anweisung entfallen lassen so wie > hier im Beispiel? > >
1 | > CREATE TABLE tab( |
2 | > Leser-ID INTEGER, |
3 | > MomentOfAttempt DATETIME |
4 | > ); |
5 | > |
Versuchs einfach. Nochmal kurz an Dich, Alexander, ein Sorry. Ich habe mich durch den Satz von Dir: "... dass ich für die fortlaufende Logging-Tabelle einen Primary Key brauche, ..." und durch meine eigene grundsätzliche Verwendung von Primary Keys irreführen lassen. Wie hier schon jemand schrieb: in der Regel verwendet man (oder jedenfalls ich) Datenbanken um die Daten hinterher abfragen zu können. Das geht deutlich schneller, wenn man PKs verwendet. Ausserdem sind in den Anwendungen, die ich hatte, wie ich schon schrieb, mehrfache gleiche Einträge aus dem Sachzusammenhang heraus, unmöglich gewesen. Wenn ich mich recht erinnere, dann hat Microsoft Access bei der Anlage von Tabellen grundsätzlich nach einem Primary Key gefragt. Das hat sich bei mir gedanklich festgesetzt.
Leg ein Primary Key an. Nenne ihn ID , integer und auf AUTOMATIK. !!! Dies führt dazu das die Datenbank automatisch ein Feld mit einer EINMALIGEN Zahl versieht. Dies macht immer Sinn bei Datenbanken. Der Grund ist einfach. Wenn man einen Datensatz ändern will liest man die Datensatz mit der ID, und schreibt ihn zurück mit "where ID = ****" . Auch gewisse Abfragen in der Datenbank brauchen die ID. Einige Datenbanken funktionieren nicht einmal ohne, da sie diese ID für temporäre Tabellen (erzeugt durch eine Abfrage) benötigen. Ergo man erspart sich unnötigen Stress. Du hast 2 Möglichkeiten. Entweder du schreibst ein ZWEITEN Primary Key selber, z.b. Terminal + Uhrzeit (sekundengenau) ODER du MUST bei vielen Abfragen ZWEI Felder abfragen. Ich persönlich würde BEIDE Varianten machen. Das macht die DB zwar ein bisschen größer aber mir als Programmierer das Leben einfacher und hinterher die Datenbank sogar schneller. Nämlich dann wenn du eine Abfrage nur mit einen Feld regeln kannst. Gruß Pucki
Nachtrag für Datenbankanfänger. Beim Anlegen einer Datenbank kann man JEDEN Feld Eigenschaften zuweisen. Eine davon ist "Einmalig" , eine andere "Groß und Kleinschreibung ignorieren". Es gibt da noch viel mehr. Wer also einen Datenbank aufbauen will, sollte sich vorher ganz genau überlegen wie er die Daten später behandeln will. Das entscheidet auch, wie viele und welche Tabellen er braucht. Merke : Eine Adressverwaltung ist eine Datenbank, aber eine Datenbank ist keine Adressverwaltung. Ich persönlich CODE meist eine Software um die Datenbank drumherum. Und mache nur Änderungen wenn ich zusätzliche Felder vergessen habe. Anders herum muss man dauernd sein Code anpassen an Stellen wo man schon fertig war. Gruß Pucki
Pucki schrieb: > Wenn man einen Datensatz ändern will liest man die Datensatz mit der ID, > und schreibt ihn zurück mit "where ID = ****" Das kann man in jedem Fall machen. Das zugreifen auf die ID erpsart Tipparbeit, man erkauft es sich dann dadurch dass der zusätzliche Index zusätzlich Speicher verbraucht. Pucki schrieb: > Entweder du schreibst ein ZWEITEN Primary Key selber, z.b. Terminal + > Uhrzeit (sekundengenau) ODER du MUST bei vielen Abfragen ZWEI Felder > abfragen. Ich hatte oben schon geschrieben was der PK ist. Es gibt nur EINEN, daher der Name;) Alles andere sind Indexe;) Pucki schrieb: > ODER du MUST bei vielen Abfragen ZWEI Felder abfragen grundsätzlich hat Pucki da Recht
Pucki schrieb: > Der Grund ist einfach. Wenn man einen Datensatz ändern will liest man > die Datensatz mit der ID, und schreibt ihn zurück mit "where ID = ****" Wirklich. Nicht. Diese Anwendung ist ein Musterbeispiel für eine write-only Datenbank!
blub schrieb: > LOL, Der braucht dann mehr Speicher als nur der PK ;) Wenn der zusätzlich zum PK ist, hast Du Recht, aber das macht überhaupt keinen Sinn, weil der PK ja seinen eigenen Index anlegt. Ich meine einen Index statt eines PK, wenn ein PK nicht möglich ist, weil die Daten nicht unique sind. Die Originalaussage war ja: > Macht aber nur Sinn bei write-only-Datenbanken, denn ohne PK wird's > schwierig aus der Datenbank etwas zu lesen. Wenn Du einen bestimmten > Zeitpunkt suchst wird er jedesmal die komplette Tabelle sortieren > müssen. Und das ist und bleibt Quatsch. Ich brauche auch ohne PK keinen full table scan, wenn ich einen Index auf mindestens einem Teil der Suchargumente anlege, und der_muß nicht_ unique sein. Aber mal sehen, 10 Millionen rows, PostgreSQL 12.x:
1 | create table sensor_log ( sensor_log_id serial, |
2 | location varchar not null, |
3 | reading bigint not null, |
4 | reading_date timestamp not null ); |
5 | |
6 | insert |
7 | into |
8 | sensor_log (location, |
9 | reading, |
10 | reading_date) |
11 | select |
12 | s.id % 1000, |
13 | round(random() * 100), |
14 | current_date + interval '1d' - ((s.id * 10)::text || 's')::interval |
15 | from |
16 | generate_series(1, 10000000) s(id); |
17 | |
18 | alter table public.sensor_log add constraint sensor_log_pk primary key (sensor_log_id); |
19 | |
20 | select |
21 | pg_size_pretty (pg_indexes_size('sensor_log')); |
22 | |
23 | -- 214 MB |
24 | |
25 | create table sensor_log ( sensor_log_id serial, |
26 | location varchar not null, |
27 | reading bigint not null, |
28 | reading_date timestamp not null ); |
29 | |
30 | insert |
31 | into |
32 | sensor_log (location, |
33 | reading, |
34 | reading_date) |
35 | select |
36 | s.id % 1000, |
37 | round(random() * 100), |
38 | current_date + interval '1d' - ((s.id * 10)::text || 's')::interval |
39 | from |
40 | generate_series(1, 10000000) s(id); |
41 | |
42 | create index idx_nonunique_sensor_log_id on |
43 | sensor_log |
44 | using btree(sensor_log_id) |
45 | select |
46 | pg_size_pretty (pg_indexes_size('sensor_log')); |
47 | |
48 | -- 214 MB |
49 | |
50 | create index idx_nonunique_sensor_log_id on |
51 | sensor_log |
52 | using brin(sensor_log_id); |
53 | |
54 | select |
55 | pg_size_pretty (pg_indexes_size('sensor_log')); |
56 | |
57 | -- 56 kB |
Also, 214 MB primary-key vs. 214 MB nonunique-index vs. 56 kB nonunique-index wenn man ein wenig nachdenkt und die Datenstruktur es hergibt. Natürlich sind die Daten in allen Indexen unique, sonst würde das mit dem PK ja nicht funktionieren, aber wenn man z.B. 'reading' indexiert sind es 215 MB, also ca. 0.5% mehr. Das hängt aber auch vom Anwendungsfall, dem verwendeten Datenbanksystem und von den verfügbaren Indextypen ab. Dann gibt es ja noch partial indexes, covering indexes... Also, Versuch macht kluch.
Jo also ID kann man immer haben wenn man nur inserts am Ende macht ist das Quasi ein flatfile. Dazu dann zusätzlich Indexe. Ist aber nicht MEINE persönliche beste Wahl. Man kann da etwas herumexperimentieren wie es nun am besten ist. Man kann zB. die Tabelle anlegen mit PK Uhrzeit, Station, User und dann weitere indizes für das was man abfragen will zB. User, Uhrzeit, Station Station, User, Uhrzeit Ein Puzzlespiel;)
Theor schrieb: > Soweit es mir bekannt ist, erzwingen alle Datenbanksysteme, dass kein > Eintrag mehrfach vorkommen darf. Nein, das erzwingen die gängigen DBMS nicht. Umgekehrt - mit einem Primärschlüssel erzwingst Du genau das. Einen Primärschlüssel oder einen UNIQUE Constraint benötigt man bei den meisten Systemen, um mit einem Fremdschlüssel darauf zu referenzieren. Außerdem benötigen viele GUI Tools einen Primärschlüssel oder einen UNIQUE Constraint, um Tabellen zu editieren. Auch für die reine Darstellung größerer Tabellen benötigt man in der Regel einen eindeutigen Schlüssel. Für ein reines Log brauchst Du daher im Grunde keinen Primärschlüssel. Aber eine GUID (UUID) als Primärschlüssel ist bei einem verteilten System spätestens dann nützlich, wenn einmal geprüft werden muss, ob bei der Synchronisation nichts doppelt impoortiert wurde.
ergo70 schrieb: > Und das ist und bleibt Quatsch. Ich brauche auch ohne PK keinen full > table scan, wenn ich einen Index auf mindestens einem Teil der > Suchargumente anlege, und der_muß nicht_ unique sein. Das stimmt aber ich gehe davon aus dass bevor man nen Index anlegt ein PK gesetzt wird. PK ist quasi der Erste Index und damit völlig umsonst denn in irgendeiner Reihenfolge müssen die Daten ja sowieso abgespeichert werden. Sparst also mit PK einen Index.
Eine GUID ist auch nix anderes als eine zufällige ID. Ja das mag teilweise sinnvoll sein, brauchts aber nicht da Station, Zeit ja immer eindeutig ist also auch den Zweck erfüllt.
ergo70 schrieb: > select > pg_size_pretty (pg_indexes_size('sensor_log')); > > -- 56 kB Ja merkste was? Der index kost' nix, weil er heimlich den PK als index hernimmt.
ID/GUID KANN als PK sinnvoll sein falls man der Uhr misstraut, kann ja sein dass die mal nen Tag zurück springt dann kann man die Daten trotzdem retten. Deshalb hat man oft eine fortlaufende Nummer als erstes Feld und kann darüber dann die Zeit nachträglich korrigieren. Eine zufällige GUID nutzt dann aber nichts da sie keine Information über die Reihenfolge enthält.
> Ja merkste was? Der index kost' nix, weil er heimlich den PK als index > hernimmt. Nein, denn ich habe die Tabelle natürlich dazwischen mal geDROPpt und beim zweiten Test gar keinen PK angelegt. Man beachte das fehlende ALTER TABLE ... ADD CONSTRAINT ... Allerdings habe ich mir die DROP TABLE statements nicht aufgeschrieben, daher war das vielleicht nicht ganz klar. Nur, warum sollte ich wohl exakt die selbe Tabelle zweimal anlegen wollen, wenn ich keine Veränderung daran vornehmen wollte? Und der letzte Index ist so klein, weil er kein BTREE, sondern ein BRIN ist. https://www.percona.com/blog/2019/07/16/brin-index-for-postgresql-dont-forget-the-benefits/ BRINs sind für natürlich geordnete Daten super, falls man kein UNIQUE braucht und mit partial table scans leben kann. Meistens kann man das im Zeitalter der SSD aber, zumal man die Blockgröße anpassen kann. >Eine >zufällige GUID nutzt dann aber nichts da sie keine Information über die >Reihenfolge enthält. Auch nicht ganz korrekt, vgl. COMB: https://www.informit.com/articles/article.aspx?p=25862 und https://github.com/tvondra/sequential-uuids
Hmmm schrieb: > Multi-Master-Replikation ist heikel. Besser geht es mit einer Master- > und einer Slave-Datenbank. Naja... > Bei einem Ausfall wird dann auf die > Slave-Datenbank umgeschaltet und diese so lange genutzt, bis der Master > (nach manuellem Eingriff) wieder läuft. Ja, aber: wie wird das umgeschaltet? Wie wird eine Split-Brain-Situation verhindert? Wie werden Clients über einen ausgefallenen Master informiert? > Falls es unbedingt Multi-Master sein muss: Fortlaufende ID für lokale > Nutzung, zusätzlich eine systemweit eindeutige ID (z.B. durch getrennte > Nummernbereiche) vergeben. Moderne geclusterte Softwaresysteme können das oft von Haus aus... Think Redis, Elasticsearch, PostgreSQL-XL, um nur ein paar zu nennen.
Ja, trivial ist es nicht, aber machbar: https://de.slideshare.net/mobile/srihari29691/on-the-building-of-a-postgresql-cluster
Ergo70 schrieb: > Ja, trivial ist es nicht, aber machbar: > > https://de.slideshare.net/mobile/srihari29691/on-the-building-of-a-postgresql-cluster Nicht alles, was machbar ist, ist auch klug. Die dort beschriebene Lösung skaliert nicht horizontal bezüglich der Schreiblast, und die gesamte Herangehensweise und Implementierung dort erscheinen mir, mit Verlaub, ein wenig... hemdsärmelig. Und obendrein bietet AWS, bei denen sie dieses Projekt hosten, ja selbst einen Haufen Datenbanken an, darunter Allrounder ähnlich den klassischen RDBMS, aber auch Key-Value-Stores, In-Memory-Datenbanken, Timeseries und weitere, von denen sehr viele Replikation (Hochverfügbarkeit) und Sharding (horizontale Skalierbarkeit) schon von Hause aus direkt unterstützen... Und, nunja, PostgreSQL hat schon seit vielen Jahren Features, mit denen sich eine Hochverfügbarkeit herstellen läßt, von einfachen Ansätzen mit DRBD über komplexe Setups mit Corosync, Heartbeat/Pacemaker, STONITH und Co.... Aber heutzutage gibt es stattdessen auch eine "echte" Clusterversion von PostgreSQL, nämlich PostgreSQL-XL, die keine klassische High Availability, sondern Distributed Computing nutzt. Daher kann sie durch ihre Replikation die Hochverfügbarkeit und durch Sharding horizontale Skalierbarkeit sicherstellen -- und bietet all die vielen feinen Features an, die dieser modernere Ansatz hat. Mir ist allerdings auch noch nicht ganz klar, warum etwas, das technisch gesehen im Wesentlichen ein Append-Only-Log ohne Relationen und Transaktionen ist, und daher auch keine relationale oder transaktionale Integrität garantieren muß, unbedingt in einer relationalen Datenbank gespeichert werden muß. Sowas ist unnötig kompliziert und ressourcenaufwändig, das kann man besser machen, Stichworte: NoSQL, Columnar Store, ... aber leider scheint es für viele Entwickler und Architekten nur zwei Mechanismen zur Datenspeicherung zu geben, das Dateisystem oder ein klassisches relationales Datenbankmanagementsystem, üblicherweise mit SQL-Frontend. Dabei gibt es heute doch mit Tools CouchDB, MongoDB, Neo4j, Redis, Elasticsearch, Apache Solr, Splunk, Apache Cassandra, Prometheus, InfluxDB, Aerospike, ... so viele modernere Möglichkeiten, und die meisten haben Replikation und Sharding bereits eingebaut. Mein persönlicher Favorit für Anwendungsfälle wie diesen ist Elasticsearch, das viel mehr als nur eine Volltext-Suchmaschine ist, und mit Kibana bereits ein Webfrontend für die interaktive Datenexploration, -Analyse und -Visualisierung bietet. Ich mag das Ding (trotz Java) sehr gerne, YMMV. Und, ach ja: wenn es wirklich ultraschnell, jedoch relativ einfach sein soll, könnte auch Redis eine gute Wahl sein...
Bei XL und XC braucht es aber auch einen zentralen Koordinator, der die xids vergibt. Ist aber schon länger her, dass ich mir das mal angesehen habe. Da finde ich den Ansatz von Spanner deutlich interessanter. Wenn die Uhren nicht so teuer wären...
Ergo70 schrieb: > Bei XL und XC braucht es aber auch einen zentralen Koordinator, der die > xids vergibt. Ja, richtig, aber bei den Genannten ist der Koordinator replikationsfähig. Das ist also NICHT die klassische HA, sondern echtes DC... ;-)
Das Problem löst doch die richtige query bzw. insert. Ich würde das mit einem zusätzlichem Feld eindeutige zufällige union ID machen.. zB. "0xA56B1ADE554F48DAE55F26605AEA2E229D529BA2" den Primärschlüssel verwaltet jeder Slave selbst und der Master hat nen Autoincrement bei Insert und produziert einen fehler beim insert einer gleichen union ID. So kann auch jeder Slave zurücksynchronisieren bzw. sogar untereinander synchen, die normale ID kann ja Links liegen gelassen werden.
Das aller wichtigste bei Datenbanksachen: Sich niemals auf "externe" Primary-IDs verlassen. Das wird über kurz oder lang immer schief gehen. Irgendwelche IDs, Pointer, Kennungen etc. sind intern. Eine andere wichtige Regel: Das Modell einer Datenbank, egal ob Relational, Hierarchisch, Objekt-orientiert oder Dokumeten-orientiert, sind nicht die Daten, sondern nur eine Abbildung der Daten. Konkret für Deine Aufgaben: Was sind die Daten? Die Log-Einträge! Ein Log-Eintrag besteht in Deiner Architektur aus: - System-Kennung - Zeitstempel - Meldung Da kommt kein Primärschlüssel vor. Dass Du in der SQL-Datenbank aus verschiedenen Gründen einen Primärschlüssel gerne hättest, ist den Daten egal. Das ist ein Detail Deiner gewählten Daten-Architektur. Also wäre ein einfache Architektur:
1 | create table entries (id primary key autoincrement, systemId, timestamp, message); |
Aber es wäre auch möglich, dass Du zwei Tabellen hast, eine für die eigenen Log-Enträge, diese Tabelle hätte keine System-ID, und eine für Fremdeinträge mit System-ID. Dann könntest Du auch einen View anlegen der die beiden Tabellen kombiniert, samt Trigger etc. Oder Du hast doch nur eine Tabelle und legst Dir einen View für Deine eigenen Einträge an. Aber das ist alles egal. Die Synchronisierung ist von der Datenbank-Implementierung unabhängig: Jedes System kennt zu einer bestimmten System-ID den Eintrag mit dem neusten Zeitstempel. Danach können sich die System untereinander synchronisieren. Will man es robuster haben, kann man auch mit Sequenznummern arbeiten die man auch auf Lücken prüfen kann. Dann bestünde ein Log-Eintrag aus: - System-Kennung - Sequenz-Nummer - Zeitstempel - Meldung Und noch ein Tipp: Die System-ID sollte nicht die IP-Adresse des Knoten sein, sondern eine feste Kennung, z.B. generiert aus einer Prozessor-ID, MAC-Adresse, o.ä.
Also ein Log-Eintrag hat auf jedem System eine andere Primary-ID. Die Primary-ID wird nur intern verwendet und nicht zum Datenaustausch der Systeme untereinander.
Hm, die XL Doku sagt: „Der globale Transaktionsmanager (GTM) kann einen GTM-Standby haben.„ Inwiefern ist das jetzt besser als z.B. mehrere repmgr? Abgesehen davon, das XL auch die Schreiblast verteilen kann. Aber das kann bucardo auch. Nicht so elegant, aber dafür muss man auch keine Interna patchen. Selbst bei Citus finde ich einen zentralen Koordinator.
Ergo70 schrieb: > Hm, die XL Doku sagt: „Der globale Transaktionsmanager (GTM) kann einen > GTM-Standby haben.„ Inwiefern ist das jetzt besser als z.B. mehrere > repmgr? Abgesehen davon, das XL auch die Schreiblast verteilen kann. > Aber das kann bucardo auch. Nicht so elegant, aber dafür muss man auch > keine Interna patchen. Selbst bei Citus finde ich einen zentralen > Koordinator. repmgr und bucardo können AFAIK nur Replikation, aber kein Sharding. Horizontale Skalierbarkeit kannst Du damit also leider vergessen. Und ob in Perl entwickelte Software (bucardo) der Performance Deiner Datenbank zuträglich ist, wage ich als jemand, der viele Jahre lang Software in Perl entwickelt hat, zu bezweifeln. Das kannst Du machen, wenn Du viele Ressourcen und wenig Arbeit dafür hast, aber für hochperformante und -effiziente Setups wird das schwierig. Citus dagegen spielt in einer anderen Liga und ähnelt Postgres-XL, ist allerdings meines Wissens eine kommerzielle Software, und scheint (!) mir seit der Übernahme durch Microsoft im Wesentlichen auf die Azure-Cloud ausgerichtet zu werden. Neben Citus hat Microsoft aber noch ein eigenes, durchaus brauchbares RDBMS im Angebot: nämlich den Microsoft SQL Server. Nach meiner Erfahrung mit derartigen Übernahmen beschleicht mich daher leider eine gewisse Skepsis für die Zukunftsfähigkeit... Aber ja, Du hast Recht: wenn Performance keine große Rolle spielt (und auf kleinen Plattformen wie den RasPi ist das noch viel wichtiger), kann man mit allen diesen Ansätzen eine Replikation und mit Citus sogar Replikation und Sharding erreichen. Aber das ändert ja am Ende nichts an meinem eigentlichen Argument, daß derartige Systeme für die Speicherung von Append-Only-Logs schlecht geeignet sind. Influx, Prometheus und Druid haben neben Replikation und Sharding viele Features, die es deutlich vereinfachen, Zeitseriendaten wie Append-Only-Logs zu verarbeiten. Und wer sich nicht für jedes Fitzelchen Daten in eine neue Software einarbeiten möchte, der nimmt halt einen Allrounder wie Elasticsearch. Es hat sicherlich so seine guten Gründe, warum das als Datenbackend vieler Werkzeuge von Verinice -- einem Werkzeug des Bundesamtes für Sicherheit in der Informationstechnik, BSI -- sowie einem spezialisierten Log-Aggregator wie Graylog dient. Zudem hat es ein wunderbares Webfrontend, Kibana, für die Analyse und Visualisierung der Daten.
Evt. solltet ihr euch zwischendurch an eine Randbedingung erinnern: Alexander K. schrieb: > auf mehreren Raspberry Pi's die wahrscheinlich garkeinen lokalen Massenspeicher haben, außer ihrer SD-Karte, also keinen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.