MySQL 9.7 LTS in der Praxis #02: OpenTelemetry ohne Enterprise-Lizenz, was liefert MySQL 9.7 wirklich?
Alle Beispiele basieren auf der Beispieldatenbank Sakila und sind mit MySQL 9.7.2 Community getestet, dem zum Testzeitpunkt aktuellen offiziellen Container-Image. Für den Betrieb empfehle ich den neuesten Patchstand, derzeit 9.7.4. Enterprise-Funktionen sind nicht Teil der Serie.
Die Laborskripte für Teil 2 für Podman findet ihr hier:
Wie bekommt man MySQL in dasselbe Observability-Dashboard wie den Rest des Stacks, ohne eine Enterprise-Lizenz zu kaufen? Bis 9.6 war die Telemetry-Komponente der Enterprise Edition vorbehalten, mit 9.7 ist sie in der Community Edition angekommen. Sie exportiert Traces, Metriken und Logs gemäß dem OpenTelemetry-Standard an jedes kompatible Backend.
Ich habe das mit MySQL 9.7.2 und Sakila im Labor durchgespielt. Technisch funktioniert es erfreulich unkompliziert. Die eigentliche Erkenntnis liegt woanders, nämlich bei der Frage, wie viel SQL-Text samt Kundendaten dabei euren Server verlässt.
Was ist neu?
Die Komponente „component_telemetry“ exportiert drei Signale. Traces zeigen einzelne Statements mit Laufzeit und Kennzahlen, Metriken liefern die Zähler des Servers und Logs übertragen Ereignisse an ein Log-Backend. Der Transport läuft über OTLP. Als Standardprotokoll für den Export kommt „http/protobuf“ zum Einsatz. Im offiziellen Community-Image liegt die Komponente bereits im Plugin-Verzeichnis, eine Zusatzinstallation braucht es nicht.
Der Testaufbau
Als Backend nutze ich das Image „grafana/otel-lgtm“. Es bündelt einen OpenTelemetry-Collector, Tempo für Traces, Prometheus für Metriken, Loki für Logs und Grafana in einem einzigen Container und eignet sich damit gut für Tests. MySQL 9.7.2 läuft in einem zweiten Container im selben Podman-Netzwerk und sendet an den Collector auf Port 4318. In der Produktion gehört an diese Stelle ein eigener Collector, der die Daten filtert und weiterverteilt.
Installation und Konfiguration
Eine Abweichung vorab, die Zeit sparen kann. Laut Upgrade Checker aus Teil 1 sind „telemetry.trace_enabled“ und „telemetry.metrics_enabled“ in 9.7 standardmäßig ausgeschaltet und die Endpoints leer. Die Installationsseite der Doku zeigt dagegen eingeschaltete Signale und localhost als Ziel. Ich setze deshalb alles ausdrücklich in einer Optionsdatei.
[mysqld] loose-telemetry.trace_enabled = ON loose-telemetry.metrics_enabled = ON loose-telemetry.log_enabled = ON loose-telemetry.otel_exporter_otlp_traces_endpoint = http://lgtm:4318/v1/traces loose-telemetry.otel_exporter_otlp_metrics_endpoint = http://lgtm:4318/v1/metrics loose-telemetry.otel_exporter_otlp_logs_endpoint = http://lgtm:4318/v1/logs loose-telemetry.otel_resource_attributes = service.name=mysql97-lab
Danach genügt die Installation der Komponente.
INSTALL COMPONENT 'file://component_telemetry'; SHOW VARIABLES LIKE 'telemetry%enabled'; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | telemetry.log_enabled | ON | | telemetry.metrics_enabled | ON | | telemetry.query_text_enabled | ON | | telemetry.trace_enabled | ON | +------------------------------+-------+
Das Präfix „lose“ sorgt dafür, dass der Server beim ersten Start nur warnt (MY-000067, unknown variable), solange die Komponente noch fehlt. Beim INSTALL COMPONENT übernimmt er die Werte aus der Optionsdatei sofort.
Nicht alles lässt sich zur Laufzeit ändern. „trace_enabled“ und „query_text_enabled“ sind dynamisch, „metrics_enabled“ und die Endpoints sind schreibgeschützt und brauchen einen Neustart.
SET GLOBAL telemetry.metrics_enabled = ON; -- ERROR 1238 (HY000) at line 1: Variable 'telemetry.metrics_enabled' is a read only variable
Traces zeigen jedes Statement einzeln
Pro Statement entsteht ein eigener Trace mit einem Span namens „stmt“. Er verweist per Span-Link auf eine Span-Session, die die Verbindung beschreibt, mit Benutzer:in, Host, Processlist-ID und den Connection Attributes des Clients. In Tempo findet man die Statements mit einer einfachen TraceQL-Abfrage.
{ resource.service.name = "mysql97-lab" && name = "stmt" }
Der stmt-Span ist für Performance-Analysen erstaunlich reichhaltig. Er trägt 28 Attribute, darunter mysql.event_name, mysql.cpu_time, mysql.lock_time, mysql.rows_examined, mysql.rows_sent, mysql.no_index_used, mysql.created_tmp_disk_tables und mysql.sort_merge_passes. Das sind im Wesentlichen die Kennzahlen, die man aus dem Performance-Schema kennt, jetzt direkt am einzelnen Statement.
Dazu kommt der SQL-Text in zwei Formen. „mysql.digest_text“ enthält immer den normalisierten Digest, in dem Literale durch Fragezeichen ersetzt sind. Ist „telemetry.query_text_enabled“ eingeschaltet, kommt „mysql.sql_text“ mit dem vollständigen Statement hinzu, inklusive aller Literale. Im Labor habe ich das mit zwei Abfragen auf „sakila.customer“ verglichen, jeweils in einer eigenen Verbindung und mit einem eindeutigen Spaltenalias als Marker, der anders als ein Literal auch im Digest erhalten bleibt. Mit query_text_enabled = ON trug der Span beide Attribute.
mysql.digest_text SELECT ? AS `marker_133553_on` , `email` FROM `sakila` . `customer` WHERE `last_name` = ?
mysql.sql_text SELECT 'MARKER_QT_ON' AS marker_133553_on, email
FROM sakila.customer WHERE last_name = 'SMITH'
Mit OFF fehlte mysql.sql_text, der Digest blieb.
mysql.digest_text SELECT ? AS `marker_133553_off` , `email` FROM `sakila` . `customer` WHERE `last_name` = ?
Der Schalter wirkt sofort, sogar schon für das SET-Statement, mit dem man ihn umlegt.
Rund 420 Metriken ohne Präfix
Im Labor kamen 422 Metriken in Prometheus an. Inhaltlich sind das weitgehend die bekannten Statuszähler von „connections_accepted_total“ und „threads_running“ über InnoDB-Pufferseiten und Redo Log bis zu den Lost-Zählern des Performance-Schemas. Zwei Eigenheiten solltet ihr kennen. Die Namen tragen kein Präfix wie „mysql_“ und Befehlstypen erscheinen als eigene Metriken wie „select_total“ oder „insert_total“ statt als Label. Eine Abfrage wie die folgende zeigt die Last des Servers.
rate(queries_total[1m])
Wer mehrere Dienste in einem gemeinsamen Prometheus sammelt, filtert deshalb am besten über die Labels „service_name“ oder „job“, die die Komponente mitliefert.
Logs sind ein General Log über das Netzwerk
Hier wird es heikel. Im Labor stand „general_log“ auf OFF, trotzdem erhielt Loki Ereignisse mit „mysql_event_name logger/sql/general_log“. Jede Zeile trägt die Felder „command_type“, „user“, „priv_user“, „host“, „thread“ und „sql_text“. In einer Stunde waren das 411 Query-, 25 Connect- und 25 Quit-Ereignisse, also praktisch jede Abfrage im Klartext.
Im Vergleichslauf habe ich das gezielt geprüft. Die Abfrage mit dem Marker „marker_133553_off“ lief, während „telemetry.query_text_enabled“ auf OFF stand und ihr Trace enthält deshalb kein „mysql.sql_text“. In Loki kam sie trotzdem vollständig an.
{service_name="mysql97-lab"} |= "marker_133553_off"
sql_text SELECT 'MARKER_QT_OFF' AS marker_133553_off, email
FROM sakila.customer WHERE last_name = 'SMITH'
thread 73
user root
Der Schalter wirkt also nur auf die Traces, die Logs enthalten den SQL-Text immer.
Logs und Traces lassen sich zuordnen, allerdings nur indirekt. Das Loki-Feld „thread“ entspricht der CONNECTION_ID() und damit „mysql.processlist_id“ im Session-Span. Eine Trace-ID steht nicht in den Logzeilen, eine direkte Verlinkung in Grafana ist deshalb nicht möglich.
Fallstricke und Datenschutz
In Sakila stehen in diesen Literalen Namen und E-Mail-Adressen fiktiver Kunden. In einer produktiven Datenbank sind es echte personenbezogene Daten. Wer Telemetrie an eine Observability-Plattform schickt, womöglich an einen externen Dienst, überträgt diese Daten dorthin. Das ist keine technische Detailfrage, sondern eine Entscheidung, die in das Datenschutzkonzept gehört.
Die Stellschrauben sind ungleich verteilt. Für Traces gibt es mit query_text_enabled einen Schalter, für die Logs habe ich außer „log_enabled“ keinen gefunden, der den SQL-Text unterdrückt. Hinzu kommt, dass sich „metrics_enabled“ und die Endpoints nur mit Neustart ändern lassen. Wer Telemetrie einführt, sollte die Konfiguration deshalb vorher festlegen und nicht im laufenden Betrieb ausprobieren.
Empfehlungen
- Mit Traces und Metriken beginnen und „query_text_enabled“ bewusst auf
OFFsetzen. Der Digest reicht für die meisten Performance-Analysen. - „log_enabled“ nur einschalten, wenn die Logs wirklich gebraucht werden und dann im Collector das Attribut „sql_text“ entfernen oder maskieren.
- Immer einen eigenen Collector vorschalten, statt direkt an ein externes Backend zu senden. Dort lassen sich Daten filtern, bevor ihr euer Netz verlasst.
- Über „telemetry.otel_resource_attributes“ einen sprechenden „service.name“ setzen, damit die Daten im Backend eindeutig zuzuordnen sind.
- Die Konfiguration in der Optionsdatei pflegen, weil sich ein Teil der Variablen nicht per
SET GLOBALändern lässt.
Fazit
Mit 9.7 schließt die Community Edition eine echte Lücke im Betrieb. MySQL lässt sich ohne Zusatzwerkzeuge in einen OpenTelemetry-Stack einbinden und die Traces liefern pro Statement Kennzahlen, für die man bisher das Performance-Schema abfragen musste. Wer die Logs einschaltet, exportiert allerdings faktisch einen General-Log samt Literalen, auch wenn „general_log“ aus ist und „query_text_enabled“ auf OFF steht. Diese Entscheidung sollte bewusst fallen und nicht nebenbei.
Nutzt ihr OpenTelemetry bereits für eure Datenbanken oder setzt ihr auf andere Monitoring-Werkzeuge? Ich freue mich über eure Erfahrungen. In Teil 3 geht es um Group Replication. Wer die Themen der Serie vertiefen möchte, findet sie auch im ORDIX-Seminar „MySQL Administration“, in dem ich als Referent die Neuerungen der aktuellen LTS-Version behandle.
Quellen
Seminarempfehlung
MYSQL ADMINISTRATION [DB-MY-01]
Mehr erfahrenChief Transformation Officer & Principal Consultant bei ORDIX
Kommentare