Warum Seconds_Behind_Master immer noch 0 ist, wenn eine MySQL-Synchronisierungsverzögerung auftritt

Warum Seconds_Behind_Master immer noch 0 ist, wenn eine MySQL-Synchronisierungsverzögerung auftritt

Problembeschreibung

Der Benutzer hat einen Änderungsvorgang an der primären Datenbank durchgeführt, der etwa eine Stunde dauerte. Nachdem der Vorgang abgeschlossen ist, stellt die Slave-Datenbank fest, dass eine Synchronisierungsverzögerung vorliegt, aber der Indikator „Seconds_Behind_Master“ im Überwachungsdiagramm zeigt 0 an und die Verzögerungsdistanz des Binärprotokolls nimmt ständig zu.

Prinzipanalyse

Da wir die Verzögerungszeit analysieren, beginnen wir natürlich mit der Berechnung der Verzögerung. Der Einfachheit halber wird hier der Quellcode der offiziellen Version 5.7.31 zum Lesen zitiert. Hier finden Sie den Code zur Berechnung der Verzögerungszeit:

./sql/rpl_slave.cc

bool show_slave_status_send_data(THD *thd, Master_info *mi,
                                 char* io_gtid_set_buffer,
                                 char* sql_gtid_set_buffer)
......
wenn ((mi->get_master_log_pos() == mi->rli->get_group_master_log_pos()) &&
        (!strcmp(mi->get_master_log_name(), mi->rli->get_group_master_log_name())))
    {
      wenn (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT)
        Protokoll->Speichern(0LL);
      anders
        Protokoll->store_null();
    }
    anders
    {
      langer Zeitunterschied = ((lang)(Zeit(0) - mi->rli->letzter_Masterzeitstempel)
                       - mi->Uhrdifferenz mit Master);

      Protokoll->Speichern((langlang)(mi->rli->letzter_Master_Zeitstempel?
                                   max(0L, Zeitdifferenz) : 0));
    }
......

Aus der Berechnungsmethode von time_diff können wir feststellen, dass diese Verzögerung grundsätzlich eine Zeitdifferenz ist, und dann wird die Zeitdifferenz zwischen dem Master und dem Slave berechnet. Da es jedoch viele if-Anweisungen gibt, verwende ich die Kommentare in der Quellcodedatei:

  /*
     Der Pseudocode zur Berechnung von Seconds_Behind_Master:
     wenn (SQL-Thread läuft)
     {
       if (SQL-Thread hat alle verfügbaren Relay-Protokolle verarbeitet)
       {
         wenn (IO-Thread läuft)
            drucke 0;
         anders
            drucke NULL;
       }
        anders
          Berechnen Sie Seconds_Behind_Master.
      }
      anders
       drucke NULL;
  */

Es ist ersichtlich, dass die Berechnung von Seconds_Behind_Master in zwei Teile unterteilt ist:

  • Wenn der SQL-Thread normal ist und alle Relaylogs wiedergegeben werden, und wenn der IO-Thread normal ist, wird es direkt auf 0 gesetzt.
  • Wenn der SQL-Thread normal ist und alle Relaylogs wiedergegeben werden, der IO-Thread jedoch nicht normal ist, setzen Sie direkt NULL.
  • Wenn der SQL-Thread normal ist und nicht alle Relay-Protokolle erneut abgespielt wurden, wird die Verzögerungszeit berechnet.

Achten Sie dann bei der Berechnung der Verzögerungszeit am Ende auf die Bedeutung dieser Variablen:

  • Zeit (0): aktueller Zeitstempel im Zeitstempelformat.
  • last_master_timestamp: Der Zeitpunkt, zu dem dieses Ereignis in der Masterdatenbank ausgeführt wird, im Zeitstempelformat.
  • clock_diff_with_master: Die Zeitdifferenz zwischen Slave und Master, die beim Start des IO-Threads ermittelt wird.

Es ist ersichtlich, dass bei der Berechnung der Verzögerung der Wert tatsächlich dadurch erhalten wird, dass die Zeit, zu der das wiedergegebene Ereignis auf dem Master ausgeführt wird, von der Ortszeit des Slaves abgezogen und dann die Zeitdifferenz zwischen beiden ausgeglichen wird. Logischerweise gibt es kein Problem. Da time(0) und clock_diff_with_master normalerweise nicht falsch sein können, sollte das Problem dieses Mal bei last_master_timestamp liegen.

PS: Obwohl es meistens kein Problem gibt, nimmt time(0) die Ortszeit an. Wenn also ein Problem mit der Ortszeit des Slaves vorliegt, ist der Endwert auch falsch, aber das liegt nicht im Rahmen dieses Falls.

Finden Sie dann die Logik zur Berechnung des last_master_timestamp bei der Ausführung des Ereignisses. In Kombination mit den Kommentaren können wir feststellen, dass normale Replikation und parallele Replikation unterschiedliche Berechnungsmethoden verwenden. Die erste ist die normale Replikation und der Berechnungszeitpunkt liegt vor der Ausführung des Ereignisses:

./sql/rpl_slave.cc

......
  wenn (ev)
  {
    Aufzählung enum_slave_apply_event_and_update_pos_retval exec_res;

    ptr_ev= &ev;
    /*
      Auch wenn wir dieses Ereignis nicht ausführen, behalten wir den Master-Zeitstempel,
      damit die Sekunden hinter dem Master das richtige Delta zeigen (es gibt Ereignisse
      die nicht wiederholt werden, sodass wir immer wieder in Rückstand geraten).

      Handelt es sich um ein künstliches Ereignis oder um ein Relay-Log-Ereignis (vom IO-Thread generiert
      event) oder ev->when auf 0 gesetzt ist, oder ein FD vom Master, oder ein Heartbeat
      Ereignis mit Server-ID „0“, dann aktualisieren wir den letzten Master-Zeitstempel nicht.

      Bei paralleler Ausführung wird last_master_timestamp nur aktualisiert, wenn
      ein Job wird aus GAQ genommen. Wenn also last_master_timestamp 0 ist (was
      zeigt an, dass GAQ leer ist, alle Slave-Worker warten auf Ereignisse von
      der Koordinator), müssen wir es mit einem Zeitstempel vom ersten initialisieren
      Ereignis, das parallel ausgeführt werden soll.
    */
    wenn ((!rli->is_parallel_exec() || rli->last_master_timestamp == 0) &&
         !(ev->is_artificial_event() || ev->is_relay_log_event() ||
          (ev->common_header->when.tv_sec == 0) ||
          ev->get_type_code() == binary_log::FORMAT_DESCRIPTION_EVENT ||
          ev->server_id == 0))
    {
      rli->last_master_timestamp = ev->common_header->when.tv_sec +
                                  (Zeit_t) ev->Ausführungszeit;
      DBUG_ASSERT(rli->last_master_timestamp >= 0);
    }
......

Der Wert von last_master_timestamp ist die Startzeit des Ereignisses plus die Ausführungszeit. In 5.7 hatten viele Ereignisse keinen Ausführungszeitwert. 8.0 fügt diesen Wert vielen Ereignissen hinzu, sodass dies als Vorteil des Upgrades auf 8.0 angesehen werden kann.

Die Berechnungsmethode der parallelen Replikation lautet wie folgt:

./sql/rpl\_slave.cc

......
  /*
    Wir müssen sicherstellen, dass dies an dieser Stelle nie aufgerufen wird, wenn
    cnt ist Null. Dieser Wert bedeutet, dass die Checkpoint-Informationen
    wird komplett zurückgesetzt.
  */

  /*
    Aktualisieren Sie den rli->last_master_timestamp, um den korrekten Seconds_behind_master zu melden.

    Wenn GAQ leer ist, setzen Sie es auf Null.
    Andernfalls aktualisieren Sie es mit dem Zeitstempel des ersten Jobs der Slave_job_queue
    die in der Funktion Log_event::get_slave_worker() zugewiesen wurde.
  */
  ts = rli->gaq->leer()
    ? 0
    : neu interpretieren_cast<Slave_job_group*>(rli->gaq->head_queue())->ts;
  rli->reset_notified_checkpoint(cnt, ts, brauche_Datensperre, true);
  /* Ende von "Koordinator::"commit_positions" */

......

Wenn in der Commit_Positions-Logik des Koordinators die Gaq-Warteschlange leer ist, wird der Last_Master_Timestamp direkt auf 0 gesetzt, andernfalls wird der Zeitstempel des ersten Jobs in der Gaq-Warteschlange ausgewählt. Es sollte hinzugefügt werden, dass diese Berechnung nicht in Echtzeit, sondern intermittierend erfolgt. Vor der Berechnungslogik steht die folgende Logik:

  /*
    Derzeit wird die Checkpoint-Routine vom SQL-Thread aufgerufen.
    Aus diesem Grund heißt diese Funktion Aufruf von entsprechenden Punkten
    im Ausführungspfad des SQL-Threads und die verstrichene Zeit wird berechnet
    Überprüfen Sie hier, ob es Zeit ist, es auszuführen.
  */
  set_timespec_nsec(&aktuelle_Uhr, 0);
  ulonglong diff = diff_timespec(&aktuelle_Uhr, &rli->letzte_Uhr);
  wenn (!force && diff < Punkt)
  {
    /*
      Wir müssen den Checkpoint jetzt nicht ausführen, weil
      die verstrichene Zeit reicht nicht aus.
    */
    DBUG_RETURN(FALSE);
  }

Das heißt, innerhalb des Zeitintervalls dieses Zeitraums wird es direkt zurückgegeben und der last_master_timestamp wird nicht aktualisiert. Daher werden Sie manchmal feststellen, dass sich der Wert von Seconds_Behind_Master bei der parallelen Replikation von Zeit zu Zeit von 0 auf 1 ändert.

Der Vorgang der gaq-Warteschlange ähnelt wahrscheinlich den Push- und Pop-Vorgängen des Stapels, sodass die in gaq verbleibenden Transaktionen immer nicht abgeschlossene Transaktionen sind. Daher ist die Zeitberechnung aus der Perspektive allgemeiner Szenarien in Ordnung.

Problemanalyse

Die Prinzipanalyse erläutert kurz die Logik der gesamten Berechnung. Kommen wir nun zur eigentlichen Frage zurück. Bei Tencent Cloud Database MySQL ist die parallele Replikation standardmäßig aktiviert, sodass eine gaq-Warteschlange vorhanden ist und der Änderungsvorgang sehr lange dauert. Unabhängig davon, ob der Änderungsvorgang in einer Gruppe paralleler Transaktionen ausgeführt wird (höchstwahrscheinlich ist DDL immer eine separate Transaktionsgruppe), ist die gaq-Warteschlange schließlich leer und last_master_timestamp wird auf 0 gesetzt. In Bezug auf die Berechnungslogik von Seconds_Behind_Master wird der endgültige time_diff ebenfalls auf 0 gesetzt, sodass die Verzögerungszeit vor dem Ende des Änderungsvorgangs immer 0 beträgt. Nachdem der Änderungsvorgang ausgeführt wurde, wird die Gaq-Warteschlange mit neuen Ereignissen und Transaktionen gefüllt, sodass die Verzögerung vorher möglicherweise 0 beträgt, aber plötzlich auf einen sehr hohen Wert springt.

Expandieren

Wenn wir die Unterschiede in den Berechnungsmethoden zwischen normaler Replikation und paralleler Replikation vergleichen, können wir die folgenden Merkmale erkennen:

  • Nach dem Aktivieren der parallelen Replikation springt die Verzögerungszeit häufig zwischen 0 und 1.
  • Bei Änderungsvorgängen und einzelnen großen Transaktionen kommt es in parallelen Replikationsszenarien häufig zu ungenauen Verzögerungen, bei normaler Replikation hingegen nicht.
  • Da die Master-Slave-Zeitdifferenz beim Starten des IO-Threads berechnet wird, weicht die Verzögerungszeit ebenfalls ab, wenn die Slave-Zeit während dieses Zeitraums abweicht.

Um zusammenzufassen

Für eine strenge Beurteilung der Verzögerung ist es besser, sich auf die GTID-Lücke und die Binlog-Positionslücke zu verlassen. Gemessen an den Änderungen der Ereignisausführungszeit in 8.0 arbeiten zumindest die Verantwortlichen von Oracle noch hart daran. Ich hoffe, diese kleineren Probleme können so schnell wie möglich behoben werden.

Oben finden Sie ausführliche Informationen dazu, warum Seconds_Behind_Master bei einer MySQL-Synchronisierungsverzögerung immer noch 0 ist. Weitere Informationen zur MySQL-Synchronisierungsverzögerung Seconds_Behind_Master ist 0 finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung von MySQLs Seconds_Behind_Master
  • Implementierungsmethode für Python3-Dateikopier- und verzögerte Dateikopieraufgaben
  • Beispielcode zur Implementierung der MySQL-Master-Slave-Replikation in Docker
  • MySQL-Datenbank Daten laden, vielfältige Verwendungsmöglichkeiten
  • MySQL-Datenbank Shell import_table Datenimport
  • Master-Slave-Synchronisationskonfiguration der Mysql-Datenbank
  • Beispielcode zur Implementierung einer einfachen Suchmaschine mit MySQL
  • Lösung für das Problem, dass MySQL-Befehle nicht auf Chinesisch eingegeben werden können
  • Als der Interviewer nach dem Unterschied zwischen char und varchar in mysql fragte
  • Zusammenfassung der Verzögerungen der MySQL-Slave-Bibliothek „Seconds_Behind_Master“

<<:  Beispielcode zur Implementierung des Dunkelmodus mit CSS-Variablen

>>:  Detaillierte Erklärung der JS-Array-Methoden

Artikel empfehlen

Detaillierte Erklärung und Zusammenfassung der URL zur Datenbankverbindung

Detaillierte Erklärung und Zusammenfassung der UR...

So fügen Sie Vite-Unterstützung zu alten Vue-Projekten hinzu

1. Einleitung Ich habe vor zwei Jahren ein Projek...

So implementieren Sie eine steuerbare gepunktete Linie mit CSS

Vorwort Die Verwendung von CSS zum Generieren gep...

Unterschiede im Stundentrennzeichen zwischen Browsern

Beim Erstellen einer Webseite verwenden Sie manchm...

Detaillierte Erklärung des HTML-Bereichs-Tags

Der <area>-Tag definiert einen Bereich in e...

Detaillierte Erläuterung der Einführung in die JavaScript-Funktion

Inhaltsverzeichnis Funktionseinführung Funktion E...

Detaillierte Erklärung zur Installation der PHP-Curl-Erweiterung unter Linux

Dieser Artikel beschreibt, wie man die PHP-Curl-E...

js, um eine einfache Lotteriefunktion zu erreichen

In diesem Artikel wird der spezifische Code von j...

Zabbix-Überwachungslösung – die neueste offizielle Version 4.4 [empfohlen]

Zabbix 12.10.2019 Chenxin siehe https://www.zabbi...

MySQL-Lernprogramm Clustered Index

Das Clustering ist eigentlich relativ zur InnoDB-...

Apache Calcite-Code zur Dialektkonvertierung

Definition Calcite kann SQL vereinheitlichen, ind...

Verwenden von Streaming-Abfragen in MySQL, um Daten-OOM zu vermeiden

Inhaltsverzeichnis 1. Einleitung 2. JDBC implemen...

Detaillierte Erklärung der Set-Datenstruktur von JavaScript

Inhaltsverzeichnis 1. Was ist Set 2. Konstruktor ...