MySQL wählt den passenden Datentyp für die ID

MySQL wählt den passenden Datentyp für die ID

Bei der Auswahl des Datentyps der ID müssen Sie nicht nur den Datenspeichertyp berücksichtigen, sondern auch, wie MySQL diesen Typ berechnet und vergleicht. MySQL speichert beispielsweise ENUM- und SET-Typen intern als Ganzzahlen, vergleicht sie in Zeichenfolgenszenarien jedoch als Zeichenfolgen. Sobald der Datentyp der ID ausgewählt ist, müssen Sie sicherstellen, dass die Datentypen der zugehörigen Datentabellen, die auf die ID verweisen, konsistent und vollständig konsistent sind, einschließlich Eigenschaften wie Länge und ob sie vorzeichenbehaftet sind! Das Mischen unterschiedlicher Datentypen kann zu Leistungsproblemen führen, und selbst wenn keine Leistungsprobleme vorliegen, können implizite Datenkonvertierungen während Vergleichen subtile Fehler verursachen. Werden allerdings im eigentlichen Entwicklungsprozess die unterschiedlichen Datentypen vergessen, kann es plötzlich zu unerwarteten Problemen kommen.

Auch bei der Wahl der Länge sollten Sie darauf achten, dass die Feldlänge möglichst klein bleibt und etwas Spielraum für späteres Wachstum bleibt. Wenn es beispielsweise zum Speichern von Provinzen verwendet wird, haben wir nur Dutzende von Werten. Zu diesem Zeitpunkt ist es besser, TINYINT anstelle von INT zu verwenden. Wenn die zugehörige Tabelle auch diese ID speichert, ist der Effizienzunterschied sehr groß.

Hier sind einige typische Typen, die für IDs gelten:

  • Ganzzahl: Ganzzahl ist normalerweise die beste Wahl, da Ganzzahloperationen und -vergleiche schnell sind und Sie das Attribut AUTO_INCREMENT auf automatisches Inkrementieren einstellen können.
  • ENUM und SET: Aufzählungen und Sets werden üblicherweise nicht als IDs gewählt, eignen sich aber gut für Spalten die „Typ“, „Status“ und „Geschlecht“ enthalten. Wenn wir beispielsweise eine Tabelle zum Speichern eines Dropdown-Menüs benötigen, gibt es normalerweise einen Wert und einen Namen. In diesem Fall ist es auch möglich, eine Aufzählung als Primärschlüssel zu verwenden.
  • Zeichenfolgen: Vermeiden Sie möglichst die Verwendung von Zeichenfolgen als IDs. Erstens benötigen Zeichenfolgen mehr Platz und zweitens sind sie normalerweise langsamer als Ganzzahlen. Bei der Auswahl einer Zeichenfolge als ID müssen Sie auch besonders auf Funktionen wie MD5, SHA1 und UUID achten. Jeder Wert ist ein zufälliger Wert in einem großen Bereich ohne Reihenfolge, was das Einfügen und Abfragen verlangsamt:
    • Da der Index während der Einfügen an einer zufälligen Stelle erstellt wird (was zu Paging, wahlfreiem Festplattenzugriff und Fragmentierung des Clusterindex führt), verringert sich die Einfügegeschwindigkeit.
    • Bei Abfragen können sich benachbarte Datenzeilen über eine große Distanz auf der Festplatte oder im Speicher erstrecken, was ebenfalls zu einer langsameren Leistung führen kann.

Wenn Sie einen UUID-Wert verwenden möchten, sollten Sie das Zeichen "-" entfernen oder ihn mit der Funktion UNHEX in eine 16-Byte-Zahl umwandeln und mit BINARY(16) speichern. Anschließend können Sie die HEX-Funktion verwenden, um es im Hexadezimalformat zu erhalten. Es gibt viele Möglichkeiten, UUIDs zu generieren. Einige sind zufällig verteilt und andere geordnet. Aber selbst die geordneten Methoden weisen keine so gute Leistung auf wie Ganzzahlen.

Zusammenfassung der Distributed-ID-Lösungen

ID ist die eindeutige Kennung der Daten. Der traditionelle Ansatz besteht darin, UUID und die Autoinkrement-ID der Datenbank zu verwenden. Heutzutage wird MySQL immer häufiger verwendet, und da es Transaktionsunterstützung erfordert, wird normalerweise die Innodb-Speicher-Engine verwendet. UUID ist zu lang und ungeordnet, daher ist es nicht als Primärschlüssel in Innodb geeignet. Autoinkrement-ID ist geeigneter, aber mit der Geschäftsentwicklung wird die Datenmenge immer größer und die Daten müssen in Tabellen aufgeteilt werden. Nachdem die Tabellen aufgeteilt wurden, werden die Daten in jeder Tabelle in ihrem eigenen Tempo automatisch inkrementiert, und es besteht eine hohe Wahrscheinlichkeit von ID-Konflikten. Derzeit ist ein separater Mechanismus zum Generieren einer eindeutigen ID erforderlich. Die generierte ID kann auch als verteilte ID oder globale ID bezeichnet werden. Lassen Sie uns die Mechanismen zur Generierung verteilter IDs analysieren.

Auto-Inkrement-ID der Datenbank

Diese Methode basiert auf der Auto-Increment-ID der Datenbank. Sie müssen eine separate Datenbankinstanz verwenden und in dieser Instanz eine separate Tabelle erstellen:

Die Tabellenstruktur ist wie folgt:

DATENBANK `SEQID` ERSTELLEN;

CREATE TABLE SEQID.SEQUENCE_ID (
	id bigint(20) unsigned NOT NULL auto_increment, 
	stub char(10) NICHT NULL Standard '',
	Primärschlüssel (ID),
	UNIQUE KEY-Stub (Stub)
)ENGINE=MyISAM;

Mit der folgenden Anweisung können Sie eine Auto-Increment-ID generieren und abrufen.

beginnen;
ersetzen durch SEQUENCE_ID (Stub) VALUES ('beliebiges Wort');
wählen Sie last_insert_id();
begehen;

Das Stub-Feld hat hier keine besondere Bedeutung. Es dient lediglich der Vereinfachung beim Einfügen von Daten. Nur wenn Daten eingefügt werden können, kann eine Auto-Increment-ID generiert werden. Zum Einfügen verwenden wir Ersetzen. Ersetzen prüft zunächst, ob Daten mit demselben Wert wie der angegebene Stub vorhanden sind. Wenn dies der Fall ist, werden sie zuerst gelöscht und dann eingefügt. Wenn dies nicht der Fall ist, werden sie direkt eingefügt.

Dieser Mechanismus zum Generieren verteilter IDs erfordert eine separate MySQL-Instanz. Dies ist zwar machbar, reicht jedoch in Bezug auf Leistung und Zuverlässigkeit nicht aus. Jedes Mal, wenn ein Geschäftssystem eine ID benötigt, muss es die Datenbank anfordern, um sie abzurufen, was eine geringe Leistung aufweist. Wenn diese Datenbankinstanz offline geht, sind alle Geschäftssysteme davon betroffen. ; Daher weisen die auf diese Weise erhaltenen Daten einen gewissen Grad an Unzuverlässigkeit auf.

Datenbank-Multimastermodus

Wenn unsere beiden Datenbanken einen Master-Slave-Cluster bilden, kann das Datenbankzuverlässigkeitsproblem unter normalen Umständen gelöst werden. Wenn jedoch die Master-Datenbank ausfällt und die Daten nicht rechtzeitig mit der Slave-Datenbank synchronisiert werden, kommt es zu einer ID-Duplikation. Aus diesem Grund können wir einen Cluster im Multi-Master-Modus ☞ Dual-Master-Modus verwenden, d. h. beide MySQL-Instanzen können unabhängig voneinander Auto-Increment-IDs generieren, was die Effizienz verbessern kann. Wenn jedoch keine anderen Änderungen vorgenommen werden, generieren die beiden MySQL-Instanzen wahrscheinlich dieselbe ID. Sie müssen für jede MySQL-Instanz unterschiedliche Startwerte und Auto-Inkrement-Schritte konfigurieren.

Konfiguration der ersten MySQL-Instanz (mysql_01):

setze @@auto_increment_offset = 1; -- Startwert setze @@auto_increment_increment = 2; -- Schrittlänge

Konfiguration der zweiten MySQL-Instanz (mysql_02):

setze @@auto_increment_offset = 2; -- Startwert setze @@auto_increment_increment = 2; -- Schrittlänge

Nach der obigen Konfiguration lauten die von den beiden MySQL-Instanzen generierten ID-Sequenzen wie folgt:
mysql_01: Der Startwert ist 1, die Schrittlänge ist 2 und die Sequenz der ID-Generierung ist: 1, 3, 5, 7, 9, …
mysql_02: der Startwert ist 2, die Schrittlänge ist 2 und die Sequenz der ID-Generierung ist: 2, 4, 6, 8, 10, …

Für diese Lösung zum Generieren verteilter IDs müssen Sie eine neue Anwendung zum Generieren verteilter IDs hinzufügen, z. B. DistributIdService. Diese Anwendung bietet eine Schnittstelle für Geschäftsanwendungen zum Abrufen von IDs. Wenn eine Geschäftsanwendung eine ID benötigt, fordert sie DistributIdService über RPC an. DistributIdService erhält die ID zufällig von den beiden oben genannten MySQL-Instanzen.

Selbst wenn nach der Implementierung dieser Lösung eine der MySQL-Instanzen offline geht, wirkt sich dies nicht auf DistributIdService aus und DistributIdService kann weiterhin ein anderes MySQL zum Generieren von IDs verwenden.

Diese Lösung ist jedoch nicht gut skalierbar. Wenn zwei MySQL-Instanzen nicht ausreichen und Sie eine neue MySQL-Instanz hinzufügen müssen, um die Leistung zu verbessern, wird dies problematisch.

Wenn Sie jetzt eine neue Instanz mysql_03 hinzufügen möchten, wie gehen Sie vor?

  • Zuerst muss die Schrittgröße von mysql_01 und mysql_02 auf 3 geändert werden. Dies kann nur manuell geändert werden, was zeitaufwändig ist.
  • Zweitens müssen wir möglicherweise einen größeren Startwert für mysql_03 festlegen, da mysql_01 und mysql_02 ständig zunehmen, um ausreichend Zeit zum Ändern der Schrittweite von mysql_01 und mysql_02 zu haben.
  • Drittens können beim Ändern der Schrittweite doppelte IDs auftreten. Um dieses Problem zu lösen, müssen Sie möglicherweise die Maschine herunterfahren.

Zahlensegmentmodus

Dieser Modus kann als Stapelerfassung verstanden werden. Wenn beispielsweise DistributIdService IDs aus der Datenbank abruft und mehrere IDs stapelweise abgerufen und lokal zwischengespeichert werden können, verbessert dies die Effizienz der Geschäftsanwendungen beim Abrufen von IDs erheblich.

Beispielsweise erhält DistributIdService jedes Mal, wenn es eine ID aus der Datenbank abruft, ein Zahlensegment, z. B. (1,1000). Dieser Bereich stellt 1000 IDs dar. Wenn die Geschäftsanwendung DistributIdService auffordert, eine ID bereitzustellen, muss DistributIdService die Zahl nur lokal von 1 erhöhen und zurückgeben, ohne jedes Mal die Datenbank anzufordern. Es greift erst dann auf die Datenbank zu, um das nächste Zahlensegment abzurufen, wenn die lokale Zahl auf 1000 erhöht wurde, d. h. wenn das aktuelle Zahlensegment aufgebraucht ist.

Daher müssen wir die Datenbanktabelle wie folgt ändern:

Tabelle erstellen id_generator (
  id int(10) NICHT NULL,
  current_max_id bigint(20) NOT NULL COMMENT 'Aktuelle maximale ID',
  increment_step int(10) NOT NULL COMMENT 'Schritt erhöhen',
  PRIMÄRSCHLÜSSEL (`id`)
)ENGINE=InnoDB STANDARD-CHARSET=utf8;

In dieser Datenbanktabelle werden der Auto-Increment-Schritt und der aktuelle Maximalwert der Auto-Increment-ID (also der letzte Wert des aktuell verwendeten Nummernsegments) aufgezeichnet. Da die Auto-Increment-Logik in den DistributIdService verschoben wurde, benötigt die Datenbank diese Logik nicht mehr.

Diese Lösung ist nicht mehr stark von der Datenbank abhängig. Selbst wenn die Datenbank nicht verfügbar ist, kann DistributIdService sie für einen bestimmten Zeitraum weiterhin unterstützen. Wenn der DistributIdService jedoch neu gestartet wird, geht ein ID-Segment verloren, was zu ID-Lücken führt.

Um die hohe Verfügbarkeit von DistributIdService zu verbessern, ist ein Cluster erforderlich. Wenn ein Unternehmen den DistributIdService-Cluster anfordert, um eine ID abzurufen, wählt es nach dem Zufallsprinzip einen DistributIdService-Knoten aus, um diese abzurufen. Für jeden DistributIdService-Knoten ist die Datenbank mit derselben Datenbank verbunden, sodass mehrere DistributIdService-Knoten die Datenbank gleichzeitig anfordern können, um das Nummernsegment abzurufen. In diesem Fall ist zur Steuerung eine optimistische Sperre erforderlich. Beispielsweise wird der Datenbanktabelle ein Versionsfeld hinzugefügt. Beim Abrufen des Nummernsegments wird das folgende SQL verwendet:

Aktualisiere ID_Generator, setze current_max_id=#{newMaxId}, Version=Version+1, wobei Version = #{Version}

Da newMaxId im DistributIdService basierend auf oldMaxId + Schrittgröße berechnet wird, bedeutet dies, dass das Zahlensegment erfolgreich abgerufen wurde, solange das obige Update erfolgreich ist.

Um eine hohe Verfügbarkeit der Datenbankschicht zu gewährleisten, muss die Datenbank im Multi-Master-Modus bereitgestellt werden. Für jede Datenbank muss sichergestellt werden, dass die generierten Zahlensegmente nicht wiederholt werden. Dazu muss die ursprüngliche Idee verwendet und der Startwert und die Schrittweite zur Datenbanktabelle hinzugefügt werden. Wenn beispielsweise zwei MySQL-Server vorhanden sind, gilt Folgendes:
mysql_01 generiert ein Zahlensegment (1,1001), und die Sequenz beim Erhöhen ist 1, 3, 4, 5, 7 ...
mysql_02 generiert ein Zahlensegment (2,1002] und die Sequenz beim Erhöhen ist 2, 4, 6, 8, 10 ...

Den spezifischen Implementierungscode finden Sie unter: tinyid

Snowflake-Algorithmus

Die drei Modi Datenbank-Selbstinkrement-ID-Modus, Datenbank-Multimastermodus und Zahlensegmentmodus basieren alle auf der Idee der Selbstinkrementierung. Die Idee des Schneeflockenalgorithmus kann unten kurz verstanden werden.
Snowflake ist Twitters Open-Source-Algorithmus zur verteilten ID-Generierung. Es handelt sich um einen Algorithmus, der sich von den drei oben genannten Mechanismen zur verteilten ID-Generierung unterscheidet. Er ist nicht auf eine Datenbank angewiesen.

Die Kernidee besteht darin, dass die verteilte ID eine feste lange Zahl ist. Eine lange Zahl belegt 8 Bytes, also 64 Bits. Die Zuordnung der Bits im ursprünglichen Snowflake-Algorithmus ist wie folgt:

  • Das erste Bit ist der Identifikationsteil. In Java ist das höchste Bit von long das Vorzeichenbit, daher sind positive Zahlen 0 und negative Zahlen 1. Im Allgemeinen ist die generierte ID eine positive Zahl und daher auf 0 festgelegt.
  • Der Zeitstempelteil belegt 41 Bit, also die Millisekundenzeit. Im Allgemeinen wird nicht der aktuelle Zeitstempel gespeichert, sondern die Differenz des Zeitstempels (aktuelle Zeit – feste Startzeit), sodass die generierte ID von einem kleineren Wert aus beginnen kann; ein 41-Bit-Zeitstempel kann für 69 Jahre verwendet werden, (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69 Jahre
  • Die Arbeitsmaschinen-ID belegt 10 Bits, was relativ flexibel ist. Beispielsweise können die ersten 5 Bits als Kennung für den Computerraum des Rechenzentrums und die letzten 5 Bits als Kennung für die einzelne Computerraummaschine verwendet werden. Es können 1024 Knoten bereitgestellt werden.
  • Der Seriennummernteil belegt 12 Bit und unterstützt den gleichen Knoten dabei, 4096 IDs in der gleichen Millisekunde zu generieren.

Gemäß der Logik dieses Algorithmus müssen wir diesen Algorithmus nur in der Java-Sprache implementieren und in eine Tool-Methode kapseln. Dann kann jede Geschäftsanwendung diese Tool-Methode direkt verwenden, um die verteilte ID zu erhalten. Wir müssen nur sicherstellen, dass jede Geschäftsanwendung ihre eigene Arbeitsmaschinen-ID hat, ohne eine separate Anwendung erstellen zu müssen, um die verteilte ID zu erhalten. Es ist außerdem nicht auf eine Datenbank angewiesen.

Spezifische Codeimplementierung

Paket com.yeming.tinyid.application;

importiere static java.lang.System.*;

/**
 * @Autor yeming.gao
 * @Description: Implementierung des Snowflake-Algorithmus* <p>
 * Der SnowFlake-Algorithmus wird zum Generieren von 64-Bit-IDs verwendet, die in langen Ganzzahlen gespeichert und zum Generieren eindeutiger IDs in verteilten Systemen verwendet werden können.
 * Und die generierten IDs haben eine ungefähre Reihenfolge. In dieser Implementierung kann die generierte 64-Bit-ID in fünf Teile aufgeteilt werden:
 * 0 - 41-Bit-Zeitstempel - 5-Bit-Rechenzentrums-ID - 5-Bit-Maschinen-ID - 12-Bit-Seriennummer * @date 2020/07/28 16:15
 */
öffentliche Klasse SnowFlake {
    /**
     * Startzeitstempel */
    privates statisches endgültiges langes START_STMP = 1480166465631L;

    /**
     * Anzahl der Ziffern der Maschinen-ID */
    privates statisches endgültiges langes MACHINE_BIT = 5;
    /**
     * Anzahl der vom Rechenzentrum belegten Bits */
    privat statisch endgültig lang DATACENTER_BIT = 5;
    /**
     * Anzahl der Ziffern der Seriennummer */
    privates statisches endgültiges langes SEQUENCE_BIT = 12;

    /**
     * Maximalwert der Maschinen-ID*/
    privates statisches endgültiges langes MAX_MACHINE_NUM = ~ (-1L << MACHINE_BIT);
    /**
     * Maximalwert des Rechenzentrums */
    privates statisches endgültiges langes MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
    /**
     * Maximaler Seriennummernwert */
    privates statisches endgültiges langes MAX_SEQUENCE = ~ (-1L << SEQUENCE_BIT);
    /**
     * Die Verschiebung jedes Teils nach links*/
    privates statisches endgültiges langes MACHINE_LEFT = SEQENCE_BIT;
    privat statisch endgültig lang DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    privat statisch endgültig lang TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId; //Rechenzentrumprivate long machineId; //Maschinen-IDprivate long sequence = 0L; //Seriennummerprivate long lastStmp = -1L; //Letzter Zeitstempelprivate SnowFlake(long datacenterId, long machineId) {
        wenn (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId darf nicht größer als MAX_DATACENTER_NUM oder kleiner als 0 sein");
        }
        wenn (Maschinen-ID > MAX_MASCHINENANZAHL || Maschinen-ID < 0) {
            throw new IllegalArgumentException("machineId darf nicht größer als MAX_MACHINE_NUM oder kleiner als 0 sein");
        }
        this.datacenterId = Datencenter-ID;
        this.machineId = Maschinen-Id;
    }

    /**
     * Generieren Sie die nächste ID
     *
     * @return lang
     */
    private synchronisierte lange nextId() {
        lange currStmp = System.currentTimeMillis();
        wenn (aktuellerStmp < letzterStmp) {
            throw new RuntimeException("Uhr wurde zurückgestellt. ID-Generierung wird abgelehnt");
        }
        wenn (aktuellerStmp == letzterStmp) {
            //In der gleichen Millisekunde erhöht sich die Sequenznummer automatisch sequence = (sequence + 1) & MAX_SEQUENCE;
            //Die Anzahl der Sequenzen in derselben Millisekunde hat das Maximum erreicht if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } anders {
            //In verschiedenen Millisekunden wird die Sequenznummer auf 0 gesetzt
            Sequenz = 0L;
        }
        letzterStmp = aktuellerStmp;
        return (currStmp - START_STMP) << TIMESTMP_LEFT //Zeitstempelteil | datacenterId << DATACENTER_LEFT //Rechenzentrumteil | machineId << MACHINE_LEFT //Maschinen-ID-Teil | sequence; //Seriennummernteil}

    private long getNextMill() {
        lange Mühle = System.currentTimeMillis();
        während (Mühle <= letzterStmp) {
            Mühle = System.currentTimeMillis();
        }
        Rücklaufmühle;
    }

    öffentliche statische void main(String[] args) {
        Schneeflocke Schneeflocke = neue Schneeflocke (2, 3);
        // Maximalwert der Rechenzentrumskennung long maxDatacenterNum = ~(-1L << DATACENTER_BIT);
        //Maximaler Wert der Maschinenkennung long maxMachineNum = ~(-1L << MACHINE_BIT);
        // Maximalwert der Sequenznummer long maxSequence = ~(-1L << SEQUENCE_BIT);
        out.println("Maximaler Wert der Rechenzentrums-ID: " + maxDatacenterNum + "; Maximaler Wert der Maschinen-ID: " + maxMachineNum + "; Maximaler Wert der Sequenznummer: " + maxSequence);
        für (int i = 0; i < (1 << 12); i++) {
            out.println(snowFlake.nextId());
        }
    }
}

Der Snowflake-Algorithmus kann sich beziehen auf:

  • Baidu (UID-Generator)
  • Meituan (Blatt)

Oben finden Sie Einzelheiten zur Auswahl des geeigneten Datentyps für die MySQL-ID. Weitere Informationen zur Auswahl des geeigneten Datentyps für die MySQL-ID finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Detaillierte Erläuterung der MySql-Datentyp-Tutorialbeispiele
  • Allgemeine Datentypen in MySQL 5.7
  • Grundsätze zur Auswahl von MySQL-Datentypen
  • Implementierung der MySQL-Dezimaldatentypkonvertierung
  • Implementierung der MySQL-Datentypkonvertierung
  • Detaillierte Erklärung zur Verwendung des MySQL-Datentyps DECIMAL
  • Detaillierte Erläuterung des Dezimal-Padding-Problems des Dezimal-Datentyps in MySQL
  • Details zum MySQL-Datentyp

<<:  3 verschiedene Möglichkeiten zum Löschen der Optionsoptionen im Auswahltag

>>:  Implementierungsschritte für die Docker-Bereitstellung von Springboot- und Vue-Projekten

Artikel empfehlen

So unterstützen Sie ApacheBench mehrere URLs

Da der Standard-AB nur Stresstests für eine einze...

Analyse der Nutzungsszenarien und Konfigurationsmethoden von Nginx Rewrite

Nginx Rewrite-Nutzungsszenarien 1. Sprung der URL...

Vue implementiert eine Komponente zur dynamischen Abfrageregelgenerierung

1. Dynamische Abfrageregeln Die dynamischen Abfra...

Installationsschritte für die chinesische Eingabemethode von Ubuntu 20.04

Dieser Artikel installiert die Google-Eingabemeth...

So teilen Sie Daten in MySQL-Tabellen und -Datenbanken auf

Inhaltsverzeichnis 1. Vertikales (längsseitiges) ...

Zwei Möglichkeiten zum Deklarieren privater Variablen in JavaScript

Vorwort JavaScript unterscheidet sich von anderen...

JS ES neue Funktionen Vorlagenzeichenfolge

Inhaltsverzeichnis 1. Was ist eine Vorlagenzeiche...

Detaillierte Erklärung des Nginx Reverse-Proxy-Beispiels

1. Reverse-Proxy-Beispiel 1 1. Erzielen Sie den E...

Installieren Sie MySQL offline mit RPM unter CentOS 6.4

Verwenden Sie das RPM-Installationspaket, um MySQ...

Fallstudie zu MySQL-Berechtigungen und Datenbankdesign

Berechtigungen und Datenbankdesign Benutzerverwal...