Heim > Datenbank > Redis > Hauptteil

Was sind die Fragen und Antworten zum Redis-Interview?

WBOY
Freigeben: 2023-05-31 16:55:51
nach vorne
952 Leute haben es durchsucht

Sprechen wir über die grundlegenden Datentypen von Redis

  1. String: Redis verwendet nicht direkt die traditionelle String-Darstellung der C-Sprache, sondern implementiert einen eigenen abstrakten Typ namens Simple Dynamic String SDS. Die Zeichenfolge in der C-Sprache zeichnet keine eigenen Längeninformationen auf, aber SDS speichert die Längeninformationen, was die Zeit zum Erhalten der Zeichenfolgenlänge von O(N) auf O(1) verkürzt und gleichzeitig einen Pufferüberlauf vermeidet und die Notwendigkeit von Änderungen verringert Zeichen. Die Anzahl der Speicherneuzuweisungen, die für die Länge der Zeichenfolge erforderlich sind.

  2. Verknüpfte Liste: Die Redis-verknüpfte Liste ist eine bidirektionale azyklische verknüpfte Listenstruktur. Viele Veröffentlichungs- und Abonnement-, langsame Abfrage- und Überwachungsfunktionen werden mithilfe verknüpfter Listen implementiert. Die Knoten jeder verknüpften Liste werden durch eine listNode-Struktur dargestellt . Für jeden Knoten gibt es Zeiger auf den vorhergehenden Knoten und den nachfolgenden Knoten, und sowohl der vorhergehende als auch der nachfolgende Knoten des Header-Knotens zeigen auf NULL.

  3. Dictionary-Hashtabelle: eine abstrakte Datenstruktur, die zum Speichern von Schlüssel-Wert-Paaren verwendet wird. Redis verwendet Hash-Tabellen als zugrunde liegende Implementierung. Die Hash-Tabelle verwendet die Kettenadressmethode, um Schlüsselkonflikte zu lösen, und wird mehreren Schlüssel-Wert-Paaren an derselben Indexposition zugewiesen Wenn die Hash-Tabelle erweitert oder verkleinert wird, wird der Rehash-Prozess aus Gründen der Dienstverfügbarkeit nicht auf einmal, sondern schrittweise abgeschlossen.

  4. Sprungliste: Sprungliste ist eine der zugrunde liegenden Implementierungen geordneter Mengen. Sprunglisten werden in Redis verwendet, um geordnete Mengenschlüssel und die interne Struktur von Clusterknoten zu implementieren. Die Redis-Skip-Tabelle besteht aus zskiplist und zskiplistNode. Zskiplist wird zum Speichern von Skip-Tabelleninformationen (Header, Endknoten, Länge usw.) verwendet. Die Ebenenhöhe jeder Skip-Tabelle ist zufällig bis 32. Anzahl: In derselben Sprungtabelle können mehrere Knoten dieselbe Punktzahl enthalten, aber das Mitgliedsobjekt jedes Knotens muss eindeutig sein. Die Knoten werden nach der Größe der Punktzahl sortiert werden nach der Größe des Mitgliedsobjekts sortiert.

  5. Integer-Set-Intset: Eine festgelegte abstrakte Datenstruktur, die zum Speichern ganzzahliger Werte verwendet wird. Es gibt keine doppelten Elemente. Die zugrunde liegende Implementierung ist ein Array.

  6. Ziplist mit komprimierter Liste: Die komprimierte Liste ist eine sequentielle Datenstruktur, die zum Speichern von Speicher entwickelt wurde. Sie kann mehrere Knoten enthalten, und jeder Knoten kann ein Byte-Array oder einen ganzzahligen Wert speichern.

Basierend auf diesen grundlegenden Datenstrukturen kapselt Redis sein eigenes Objektsystem, einschließlich String-Objektzeichenfolge, Listenobjektliste, Hash-Objekt-Hash, Sammlungsobjektsatz und geordneter Sammlungsobjekt-Zset, von denen jedes mindestens eine Basis verwendet Datenstruktur.

redis legt die Codierungsform des Objekts über das Codierungsattribut fest, um die Flexibilität und Effizienz zu verbessern. Redis führt automatisch Optimierungen basierend auf verschiedenen Szenarien durch. Die Kodierung verschiedener Objekte ist wie folgt:

  1. String-Objektzeichenfolge: int integer, Embstr-codierte einfache dynamische Zeichenfolge, rohe einfache dynamische Zeichenfolge

  1. Listenobjektliste: Ziplist, Linkedlist

  1. Hash Objekt-Hash: Ziplist, Hashtable

  1. Sammlungsobjektsatz: Intset, Hashtable

  1. Geordnetes Sammlungsobjekt-Zset: Ziplist, Skiplist

Warum ist Redis schnell?

Redis ist sehr schnell und kann Zehntausende von Parallelitäten pro Sekunde unterstützen. Im Vergleich zu MySQL ist die Leistung um ein Vielfaches höher. Die Hauptgründe für die hohe Geschwindigkeit sind:

  1. Komplett basierend auf Speicheroperationen

  2. C-Sprachimplementierung, optimierte Datenstruktur, basierend auf mehreren grundlegenden Datenstrukturen, Redis hat viele Optimierungen vorgenommen und die Leistung ist extrem hoch

  3. Verwenden Sie einen einzelnen Thread, keine Kosten für den Kontextwechsel

  4. Basierend auf einem nicht blockierenden IO-Multiplexing-Mechanismus

Warum ist Redis nach 6.0 auf Multithreading umgestiegen?

Redis verwendet Multithreading Threads verzichten immer noch auf einzelne Threads, um Client-Anfragen zu verarbeiten. Es werden nur Multi-Threads zum Lesen und Schreiben von Daten und zum Parsen von Protokollen verwendet.

Der Zweck besteht darin, dass der Leistungsengpass von Redis im Netzwerk-IO und nicht in der CPU liegt. Durch die Verwendung von Multithreading kann die Effizienz des IO-Lesens und -Schreibens verbessert werden, wodurch die Gesamtleistung von Redis verbessert wird.

Wissen Sie, was ein Hotkey ist? Wie kann das Hotkey-Problem gelöst werden?

Das sogenannte Hotkey-Problem besteht darin, dass es plötzlich Hunderttausende Anfragen für den Zugriff auf einen bestimmten Schlüssel auf Redis gibt. Dies führt dazu, dass der Datenverkehr zu stark konzentriert wird und die Obergrenze der physischen Netzwerkkarte erreicht Der Redis-Server stürzt ab und löst eine Lawine aus.

Lösung für Hotkeys:

  1. Verteilen Sie die Hotkeys im Voraus auf verschiedene Server, um den Druck zu verringern.

  2. Fügen Sie den Cache der zweiten Ebene hinzu und laden Sie die Hotkey-Daten im Voraus in den Speicher. Gehen Sie zur Speicherabfrage

Was ist Cache-Aufschlüsselung, Cache-Penetration, Cache-Lawine?

Cache-Aufschlüsselung

Das Konzept der Cache-Aufschlüsselung besteht darin, dass der gleichzeitige Zugriff eines einzelnen Schlüssels zu hoch ist. Wenn dieser abläuft, werden alle Anforderungen direkt an die Datenbank gesendet. Dies ähnelt dem Problem von Hotkeys Der Punkt ist, dass bei Ablauf alle Anfragen gestellt werden. Klicken Sie einfach auf die Datenbank.

Lösung:

  1. Aktualisierung sperren. Wenn Sie beispielsweise die Abfrage von A anfordern und feststellen, dass er sich nicht im Cache befindet, sperren Sie den Schlüssel A und gehen Sie gleichzeitig zur Datenbank, um die Daten abzufragen und zu schreiben Legen Sie es in den Cache und geben Sie es dann an den Benutzer zurück, damit nachfolgende Anforderungen ausgeführt werden können. Die Daten können aus dem Cache abgerufen werden.

  2. Schreiben Sie die Ablaufzeitkombination als Wert und aktualisieren Sie die Ablaufzeit kontinuierlich auf asynchrone Weise, um diese Art von Phänomen zu verhindern.

Cache-Penetration

Cache-Penetration bedeutet, dass Daten abgefragt werden, die nicht im Cache vorhanden sind. Jede Anfrage trifft auf die Datenbank, als ob der Cache nicht vorhanden wäre.

Um dieses Problem zu beheben, fügen Sie eine Schicht Bloom-Filter hinzu. Der Operationsschritt des Bloom-Filters besteht darin, die Daten über eine Hash-Funktion auf K Punkte im Bitarray abzubilden und diese Punkte zum Speichern der Daten auf 1 zu setzen.

Auf diese Weise wird, wenn der Benutzer A erneut abfragt und der Bloom-Filterwert von A 0 ist, dieser direkt zurückgegeben, und es wird keine Aufschlüsselungsanforderung generiert und an die Datenbank gesendet.

Offensichtlich wird es nach der Verwendung des Bloom-Filters ein Problem geben, das eine Fehleinschätzung ist, da es sich um ein Array selbst handelt und theoretisch mehrere Werte in die gleiche Position fallen können, solange die Länge unseres Wenn das Array lang genug ist, wird eine Fehleinschätzung vermieden. Die Wahrscheinlichkeit einer Beurteilung ist geringer, und diese Art von Problem sollte auf der Grundlage der tatsächlichen Situation behandelt werden.

Cache-Lawine

Wenn zu einem bestimmten Zeitpunkt ein großer Cache-Ausfall auftritt, beispielsweise wenn Ihr Cache-Dienst ausgefallen ist, wird eine große Anzahl von Anforderungen eingehen und direkt die Datenbank treffen, was zum Zusammenbruch des gesamten Systems führen kann , was Lawine genannt wird. Im Gegensatz zu den Pannen- und Hotkey-Problemen bezieht sich das Lawinenproblem auf den gleichzeitigen Ablauf großer Caches.

Mehrere Lösungen für Lawinen:

  1. Stellen Sie unterschiedliche Ablaufzeiten für verschiedene Schlüssel ein, um einen gleichzeitigen Ablauf zu vermeiden.

  2. Strombegrenzung Wenn Redis ausgefallen ist, können Sie den Strom begrenzen, um einen Absturz der Datenbank bei einer großen Anzahl von Anfragen zu vermeiden gleichzeitig

  3. Level-2-Cache, die gleiche Hotkey-Lösung.

Was sind die Ablaufstrategien von Redis?

Redis verfügt hauptsächlich über zwei Strategien zum Löschen abgelaufener Daten.

Lazy Deletion. Wenn auf diese abgelaufenen Schlüssel nicht zugegriffen wurde, besteht ein offensichtlicher Nachteil darin, dass sie immer Speicher belegen und nicht gelöscht werden können.

Periodisches Löschen

In Redis dient das periodische Löschen dazu, die Schlüssel-Wert-Paare in der Datenbank zu überprüfen, wenn sie angegeben sind, und die abgelaufenen Schlüssel-Wert-Paare zu löschen. Redis kann bei Löschvorgängen nicht alle Schlüssel abfragen, daher werden einige Schlüssel zufällig zum Überprüfen und Löschen ausgewählt.

Was soll ich also tun, wenn die abgelaufenen Schlüssel trotz Regelmäßigkeit und Faulheit nicht gelöscht werden?

Unter der Annahme, dass Redis die Schlüssel nicht jedes Mal löscht, wenn die Schlüssel regelmäßig zufällig abgefragt werden, und diese Schlüssel nicht abgefragt werden, werden diese Schlüssel in Redis gespeichert und können zu diesem Zeitpunkt nicht gelöscht werden von Redis wird erreicht.

    volatile-lru: Entfernen Sie von den Schlüsseln mit festgelegter Ablaufzeit die zuletzt verwendeten Schlüssel zur Eliminierung.
  1. volatile-ttl: Entfernen Sie von den Schlüsseln mit festgelegter Ablaufzeit die Schlüssel, die bald ablaufen
  2. volatile-random: Wählen Sie zufällig den Schlüssel mit Ablaufzeit zur Eliminierung aus.

  3. allkeys-lru: Wählen Sie den zuletzt verwendeten Schlüssel aus dem Schlüssel zur Eliminierung aus Schlüssel vom Schlüssel. Eliminierung

  4. noeviction: Wenn der Speicher den Schwellenwert erreicht, wird ein Fehler für den neuen Schreibvorgang gemeldet

  5. Welche Persistenzmethoden gibt es? Was ist der Unterschied?
  6. Redis-Persistenzlösungen werden in zwei Typen unterteilt: RDB und AOF.

  7. RDB

RDB-Persistenz kann je nach Konfiguration manuell oder regelmäßig ausgeführt werden. Ihre Funktion besteht darin, den Datenbankstatus zu einem bestimmten Zeitpunkt in einer RDB-Datei zu speichern Der Zustand der Datenbank zu einem bestimmten Zeitpunkt wiederhergestellt. Da die RDB-Datei auf der Festplatte gespeichert wird, kann sie auch dann verwendet werden, wenn Redis abstürzt oder beendet wird, solange die RDB-Datei vorhanden ist, um den Status der Datenbank wiederherzustellen. Sie können RDB-Dateien über SAVE oder BGSAVE generieren.

Der SAVE-Befehl blockiert den Redis-Prozess, bis die RDB-Datei generiert wird. Während des Prozessblockierungszeitraums kann Redis keine Befehlsanfragen verarbeiten, was offensichtlich unangemessen ist.

BGSAVE gibt einen untergeordneten Prozess aus, und dann ist der untergeordnete Prozess für die Generierung der RDB-Datei verantwortlich. Der übergeordnete Prozess kann weiterhin Befehlsanforderungen verarbeiten, ohne den Prozess zu blockieren.

AOF

AOF unterscheidet sich von RDB und zeichnet den Datenbankstatus auf, indem es die vom Redis-Server ausgeführten Schreibbefehle speichert.

AOF implementiert den Persistenzmechanismus in drei Schritten: Anhängen, Schreiben und Synchronisieren.

Wenn die AOF-Persistenz aktiviert ist, wird der Schreibbefehl an das Ende des aof_buf-Puffers angehängt, nachdem der Server den Schreibbefehl ausgeführt hat

  • Bevor jede Ereignisschleife auf dem Server endet, wird die Funktion „flushAppendOnlyFile“ aufgerufen, um zu bestimmen, ob der Inhalt von aof_buf in der AOF-Datei gespeichert werden soll. Dies kann durch die Konfiguration von appendfsync bestimmt werden.

  • always ##aof_buf内容写入并同步到AOF文件
    everysec ##将aof_buf中内容写入到AOF文件,如果上次同步AOF文件时间距离现在超过1秒,则再次对AOF文件进行同步
    no ##将aof_buf内容写入AOF文件,但是并不对AOF文件进行同步,同步时间由操作系统决定
    Nach dem Login kopieren

    Wenn nicht festgelegt, ist die Standardoption „Everysec“, da immer die sicherste Option ist (es geht nur ein Ereignisschleifen-Schreibbefehl verloren), die Leistung jedoch schlecht ist und der Modus „Everysec“ möglicherweise nur 1 Sekunde Taktdaten verliert Während die Effizienz des No-Modus der von Everysec ähnelt, gehen jedoch alle Schreibbefehlsdaten nach der letzten Synchronisierung der AOF-Datei verloren.

    Wie erreicht man eine hohe Verfügbarkeit von Redis?

    Um eine hohe Verfügbarkeit zu erreichen, reicht eine Maschine definitiv nicht aus. Um eine hohe Verfügbarkeit sicherzustellen, bietet Redis zwei Optionen.

    Master-Slave-Architektur

    Der Master-Slave-Modus ist die einfachste Lösung, um eine hohe Verfügbarkeit zu erreichen, und der Kern ist die Master-Slave-Synchronisation. Das Prinzip der Master-Slave-Synchronisation ist wie folgt:

    1. Slave sendet Synchronisierungsbefehl an Master

    2. Nachdem der Master die Synchronisierung erhalten hat, führt er bgsave aus und generiert eine vollständige RDB-Datei.

    3. Master zeichnet den Schreibbefehl des Slaves im Cache auf

    4. Nachdem bgsave ausgeführt wurde, senden Sie die RDB-Datei an den Slave und der Slave führt sie aus. Der Master sendet den Schreibbefehl im Cache an den Slave und der Slave führt ihn aus Nach der Redis2.8-Version wurde psync als Ersatz für sync verwendet. Der Grund dafür ist, dass der sync-Befehl Systemressourcen verbraucht und psync effizienter ist.

      Sentinel
    5. Die Mängel der Master-Slave-Lösung sind immer noch sehr offensichtlich. Wenn der Master ausfällt, können keine Daten geschrieben werden, der Slave verliert seine Funktion und die gesamte Architektur ist nicht verfügbar, es sei denn, Sie wechseln manuell. Der Hauptgrund dafür ist, dass es keinen automatischen Failover-Mechanismus gibt. Die Funktion von Sentinel ist viel umfassender als die einer einfachen Master-Slave-Architektur. Es verfügt über Funktionen wie automatisches Failover, Clusterüberwachung und Nachrichtenbenachrichtigung.

      Sentinel kann mehrere Master-Slave-Server gleichzeitig überwachen. Wenn der überwachte Master offline geht, wird automatisch ein Slave zum Master befördert, und der neue Master empfängt dann weiterhin Befehle. Der gesamte Vorgang ist wie folgt:

    Sentinel initialisieren, den normalen Redis-Code durch Sentinel-spezifischen Code ersetzen

    Master-Wörterbuch und Serverinformationen initialisieren, die Serverinformationen speichern hauptsächlich IP: Port und zeichnen die Adresse auf ID der Instanz

    1. Erstellen Sie zwei Verbindungen mit dem Master, eine Befehlsverbindung und eine Abonnementverbindung, und abonnieren Sie den Sentinel:Hallo-Kanal

    2. Senden Sie alle 10 Sekunden den Info-Befehl an den Master, um die aktuellen Informationen zu erhalten des Masters und aller darunter liegenden Slaves

    3. Wenn Sentinel feststellt, dass der Master einen neuen Slave hat, stellt er auch zwei Verbindungen mit dem neuen Slave her und sendet alle 10 Sekunden einen Infobefehl, um die Masterinformationen zu aktualisieren

    4. Sentinel sendet alle 1 Sekunde einen Ping-Befehl an alle Server. Wenn der Server innerhalb der konfigurierten Antwortzeit kontinuierlich ungültige Antworten sendet, wird er als offline markiert. Der Leader-Sentinel benötigt die Zustimmung von mehr als der Hälfte der Sentinels.

    5. Der führende Sentinel wird vom Offline-Master ausgewählt und in einen Master umgewandelt.

    6. Lassen Sie alle Slaves Daten vom neuen Master kopieren Legen Sie den ursprünglichen Master als Slave-Server des neuen Masters fest. Wenn der ursprüngliche Master die Verbindung wieder aufnimmt, wird er zum Slave-Server des neuen Masters. Sentinel sendet einen Ping-Befehl an alle Instanzen (einschließlich des Master-Slave-Servers). und andere Sentinels) alle 1 Sekunde und beurteilen Sie anhand der Antwort, ob es offline ist. Diese Methode wird als subjektive Offline-Methode bezeichnet. Wenn festgestellt wird, dass es subjektiv offline ist, werden andere Überwachungswächter befragt. Wenn mehr als die Hälfte der Stimmen glaubt, dass es offline ist, wird es als objektiv offline markiert und ein Failover ausgelöst.

    7. Können Sie über das Prinzip des Redis-Clusters sprechen?
    8. Wenn Sie sich auf Sentinel verlassen, um eine hohe Verfügbarkeit von Redis zu erreichen, und wenn Sie eine hohe Parallelität unterstützen und gleichzeitig große Datenmengen verarbeiten möchten, benötigen Sie einen Redis-Cluster. Redis-Cluster ist eine von Redis bereitgestellte verteilte Datenspeicherlösung. Der Cluster teilt Daten durch Daten-Sharding und bietet Replikations- und Failover-Funktionen.
    9. Knoten

      Ein Redis-Cluster besteht aus mehreren Knoten, und mehrere Knoten werden über den Cluster-Meet-Befehl verbunden. Der Handshake-Prozess des Knotens:
    10. Knoten A empfängt den Cluster-Meet-Befehl vom Client
    11. A sendet eine Meet-Nachricht an B basierend auf der empfangenen IP-Adresse und Portnummer. Knoten B empfängt die Meet-Nachricht und sendet Pong zurück. A weiß, dass B die Meet-Nachricht erhalten hat, sendet eine Ping-Nachricht und den Handshake zurück ist erfolgreich

    Schließlich verteilt Knoten A die Informationen von Knoten B über das Klatschprotokoll an andere Knoten im Cluster, und andere Knoten geben B auch die Hand

    Slot

    redis wird in Form von gespeichert Cluster-Sharding-Daten, die gesamte Cluster-Datenbank ist in 16384 Slots unterteilt. Wenn alle 16384 Slots in der Datenbank von Knoten verarbeitet werden, ist der Cluster solange online nicht verfügbar ist, wird bei der gesamten Verarbeitung der Offline-Status berücksichtigt. Verwenden Sie den Befehl „cluster addslots“, um den entsprechenden Knoten Slots zur Verarbeitung zuzuweisen.

    slot ist ein Bit-Array, die Länge des Arrays beträgt 16384/8 = 2048, und jedes Bit des Arrays wird durch 1 dargestellt, das vom Knoten verarbeitet werden soll, und 0 bedeutet, dass es nicht verarbeitet wird dass Knoten A die Slots 0–7 verarbeitet.

    Wenn der Client einen Befehl an den Knoten sendet und feststellt, dass der Steckplatz zum aktuellen Knoten gehört, führt der Knoten den Befehl aus. Andernfalls wird ein MOVED-Befehl an den Client zurückgegeben, um den Client dorthin zu leiten Richtiger Knoten. (Der VERSCHOBEN-Prozess erfolgt automatisch)

    Wenn Sie Knoten hinzufügen oder entfernen, ist es auch sehr praktisch, Slots neu zuzuweisen. Redis bietet Tools zur Realisierung der Slot-Migration. Der gesamte Prozess erfolgt vollständig online und erfordert keine Unterbrechung des Dienstes.

    Failover

    Wenn Knoten A eine Ping-Nachricht an Knoten B sendet und Knoten B nicht innerhalb der angegebenen Zeit auf Pong antwortet, markiert Knoten A Knoten B als pfail und vermutet, dass er offline ist, und gleichzeitig Senden Sie den Status von B in Form einer Nachricht an andere Knoten. Wenn mehr als die Hälfte der Knoten B als pfail markieren, wird B als Failover markiert und es wird ein Slave-Knoten mit mehr replizierten Daten angezeigt wird zuerst als Master-Knoten ausgewählt und übernimmt den Slot des Offline-Knotens. Der gesamte Prozess ist dem von Sentinel sehr ähnlich und basiert auf dem Raft-Protokoll.

    Verstehen Sie den Redis-Transaktionsmechanismus?

    redis implementiert den Transaktionsmechanismus durch MULTI, EXEC, WATCH und andere Befehle. Der Transaktionsausführungsprozess führt eine Reihe mehrerer Befehle nacheinander aus, und während der Ausführung wird die Transaktion nicht unterbrochen, und der Client wird auch nicht unterbrochen ausführen Weitere Anfragen werden gestellt, bis alle Befehle ausgeführt wurden. Der Ausführungsprozess der Transaktion ist wie folgt:

    1. Der Server empfängt die Clientanforderung und die Transaktion beginnt mit MULTI

    2. Wenn sich der Client im Transaktionsstatus befindet, wird die Transaktion in die Warteschlange gestellt und zurückgegeben an den Client in die Warteschlange gestellt, andernfalls wird er direkt ausgeführt. Führen Sie diesen Befehl aus

    3. Beim Empfang des Client-EXEC-Befehls überwacht der WATCH-Befehl, ob der Schlüssel in der gesamten Transaktion geändert wurde. Wenn ja, gibt er eine leere Antwort an den Client zurück Andernfalls durchläuft Redis die gesamte Transaktionswarteschlange und gibt die Ergebnisse schließlich an den Client zurück. Der WATCH-Mechanismus selbst ist ein CAS-Mechanismus Wenn ein Schlüssel geändert wird, wird das Flag REDIS_DIRTY_CAS geöffnet, woraufhin der Server die Ausführung der Transaktion verweigert.

    Das obige ist der detaillierte Inhalt vonWas sind die Fragen und Antworten zum Redis-Interview?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Verwandte Etiketten:
    Quelle:yisu.com
    Erklärung dieser Website
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
    Beliebte Tutorials
    Mehr>
    Neueste Downloads
    Mehr>
    Web-Effekte
    Quellcode der Website
    Website-Materialien
    Frontend-Vorlage