SQL Server unter Linux tunen: Was Microsoft empfiehlt und was davon wirkt
Ganz oben in Ihren Wartezeiten steht PREEMPTIVE_OS_FLUSHFILEBUFFERS. Unter Windows wäre das ein seltener Gast. Unter Linux ist es meist der Normalbetrieb: SQL Server bittet das Betriebssystem nach dem Schreiben ausdrücklich, die Daten auf den Datenträger zu bringen, und wartet darauf. In unserem Lab geschah das nach jedem einzelnen Commit.
Diese Seite geht durch, was unter Linux die Leistung und den stabilen Betrieb von SQL Server bestimmt: die Kernel- und Systemeinstellungen, die Microsoft empfiehlt, das Flushen beim Schreiben, die Grenzen für Speicher und CPU, den Ort der Datenbankdateien und das Überwachen. Was wir messen konnten, haben wir auf SQL Server 2017, 2019, 2022 und 2025 in Linux-Containern gemessen. Was wir nicht messen konnten, steht als Empfehlung von Microsoft da, mit Quelle.
In 60 Sekunden
- Microsoft empfiehlt für SQL-Server-Hosts eine Reihe von Kernel-Einstellungen, gebündelt im TuneD-Profil
mssql: Transparent Huge Pages an,swappiness1, NUMA-Balancing aus, großer Read-ahead,noatime, XFS. Viele Standardinstallationen weichen davon ab, auch unser Lab-Server. Der Host-Check auf dieser Seite zeigt Ihre Abweichungen. - Schreiben kostet unter Linux einen Flush je Commit. Das ist der sichere Standard. Schneller geht es nur mit Speicher, der FUA beherrscht, und den passenden Einstellungen.
writethrough = 0kann Daten kosten. - Speicher in drei Stufen begrenzen: Container- oder Host-Grenze,
memory.memorylimitmbfür den ganzen SQL-Server-Prozess,max server memoryfür den Puffer-Pool. Dazwischen 10 bis 20 Prozent Kopfraum. SQL Server 2017 und 2019 ohne Einstellung wurden in unserem 4-GB-Container vom Kernel beendet, ohne eine Zeile im Errorlog. - Eine CPU-Quote sieht SQL Server nicht.
PROCESS AFFINITYauf so viele CPUs, wie die Quote erlaubt, war in unserem Lab 16 bis 20 Prozent schneller und hob die Drosselung fast ganz auf. Trace Flag 8002 brachte zusätzlich nichts Messbares. - Container-Images richten sich nur teilweise ein: Ab 2019 legen sie tempdb-Dateien nach CPUs an (höchstens acht), MAXDOP bleibt aber 0 und die Kostenschwelle 5. Das gehört nach dem ersten Start angepasst.
- Wo die Dateien im Container liegen, war für die Leistung egal: Schreibschicht, Bind-Mount und Volume lagen gleichauf. Die Schreibschicht scheidet trotzdem aus, aus Betriebsgründen.
1 · Microsofts Liste für den Host
Microsoft beschreibt die empfohlenen Einstellungen auf der Seite „Performance best practices and configuration guidelines for SQL Server on Linux“ und fasst sie im TuneD-Profil mssql zusammen (Red Hat liefert es mit, unter Ubuntu legt man es von Hand an). Alle diese Werte gehören dem Host: In einem Container lassen sie sich lesen, aber nicht ändern.
| Einstellung | Empfehlung (Microsoft) | Linux-Standard (Ubuntu 24.04, unser Lab) | Wozu |
|---|---|---|---|
| Transparent Huge Pages | always (mehrere Instanzen: madvise) | madvise | große Speicherseiten für den Puffer-Pool |
vm.swappiness | 1 | 60 | Puffer-Pool nicht auslagern |
vm.dirty_ratio / dirty_background_ratio | 80 / 3 | 20 / 10 | Schreibpuffer des Kernels |
vm.max_map_count | 1.600.000 (mindestens 262.144) | 1.048.576 | viele Speicherbereiche des Prozesses |
kernel.numa_balancing | 0 bei mehreren NUMA-Knoten | 1 (2 Knoten) | SQL Server verwaltet NUMA selbst |
| TuneD-Profil | mssql | keines | bündelt die Werte oben |
| Read-ahead | 4096 Sektoren je Gerät | 128 KB | größere Lesevorgänge |
| Mount-Option | noatime | relatime | kein Zugriffszeit-Schreiben |
| Dateisystem | XFS (ext4 unterstützt) | ext4 | |
| Swap | vorhanden, als Sicherheitsnetz | keiner | Schutz vor dem OOM-Killer |
Wie viel jede Abweichung kostet, haben wir nicht gemessen: Unser Lab-Server wird von weiteren Messungen geteilt, Änderungen am Kernel hätten sie beeinflusst. Die Empfehlungen gelten für Server, auf denen SQL Server die Hauptlast ist; auf einem Host mit mehreren Anwendungen oder Instanzen nennt Microsoft selbst Ausnahmen (etwa madvise statt always).
Unterschiede gibt es schon zwischen Microsofts Seite und dem Profil, das Red Hat ausliefert: Das Upstream-Profil setzt die Dirty-Werte als Byte-Anteile und lässt CPU-Scheduler-Werte aktiv, die Microsoft für neuere Kernel weglässt. Wer TuneD unter Ubuntu nachbaut, sollte sich für eine Fassung entscheiden und sie dokumentieren.
Host-Check
Das folgende Skript liest die Werte aus und vergleicht sie mit der Empfehlung. Es ändert nichts.
#!/usr/bin/env bash
# PSG Stack · SQL Server unter Linux und Docker · Host-Check
# Vergleicht Linux-Einstellungen eines SQL-Server-Hosts mit den Empfehlungen von Microsoft
# (Learn: „Performance best practices: Storage, kernel, CPU, and network“, Stand 05/2026).
#
# !!! WARNUNG — bitte vor dem Ausführen lesen !!!
# - Nur lesend. Das Skript ändert keine Einstellung und schreibt keine Datei.
# - Aufruf: bash host-check-de.sh [Datenverzeichnis] [Logverzeichnis]
# Standard: /var/opt/mssql/data für beides. Ohne root fehlen evtl. einzelne Werte (dann „?“).
# - Kernel-Werte gelten für den ganzen Host. In einem Container zeigt das Skript die Werte des Hosts;
# ändern lassen sie sich nur auf dem Host, nicht im Container.
# - „prüfen“ heißt: abweichend von Microsofts Empfehlung. Ob die Abweichung bei Ihnen etwas kostet,
# sagt das Skript nicht; Empfehlungen gelten für dedizierte SQL-Server-Hosts.
# - Geprüft im PSG-Lab auf Ubuntu 24.04 (Host) und in den Containern von SQL Server 2017 bis 2025
# (13.11.2026); auf Red Hat und SUSE nicht geprüft.
set -u
DATA=${1:-/var/opt/mssql/data}
LOG=${2:-$DATA}
lies() { cat "$1" 2>/dev/null | tr -d '\n' || true; }
zeile() { printf '%-34s %-28s %-30s %s\n' "$1" "$2" "$3" "$4"; }
pruef() { if [ "$1" = "$2" ]; then echo ok; elif [ -z "$1" ] || [ "$1" = "?" ]; then echo "?"; else echo prüfen; fi; }
echo "Host-Check SQL Server unter Linux · $(date '+%Y-%m-%d %H:%M') · $(hostname)"
. /etc/os-release 2>/dev/null; echo "System: ${PRETTY_NAME:-?} · Kernel $(uname -r)"
if [ -f /.dockerenv ] || grep -qa docker /proc/1/cgroup 2>/dev/null; then echo "Hinweis: läuft in einem Container; Kernel-Werte sind die des Hosts."; fi
echo
zeile "Einstellung" "Ist" "Empfehlung (Microsoft)" "Bewertung"
zeile "-----------" "---" "----------------------" "---------"
thp=$(lies /sys/kernel/mm/transparent_hugepage/enabled | sed -n 's/.*\[\(.*\)\].*/\1/p'); thp=${thp:-?}
zeile "Transparent Huge Pages" "$thp" "always (mehrere Instanzen: madvise)" "$(pruef "$thp" always)"
sw=$(lies /proc/sys/vm/swappiness); sw=${sw:-?}
zeile "vm.swappiness" "$sw" "1" "$(pruef "$sw" 1)"
mm=$(lies /proc/sys/vm/max_map_count); mm=${mm:-?}
if [ "$mm" != "?" ] && [ "$mm" -ge 262144 ] 2>/dev/null; then b=ok; [ "$mm" -lt 1600000 ] && b="ok (Profil: 1600000)"; else b=$(pruef "$mm" 1600000); fi
zeile "vm.max_map_count" "$mm" "1600000 (mindestens 262144)" "$b"
dr=$(lies /proc/sys/vm/dirty_ratio); dbr=$(lies /proc/sys/vm/dirty_background_ratio)
zeile "vm.dirty_ratio / background" "${dr:-?} / ${dbr:-?}" "80 / 3 (TuneD-Profil mssql)" "$( [ "${dr:-}" = 80 ] && [ "${dbr:-}" = 3 ] && echo ok || echo prüfen )"
numa=$(ls -d /sys/devices/system/node/node* 2>/dev/null | wc -l)
nb=$(lies /proc/sys/kernel/numa_balancing); nb=${nb:-?}
if [ "$numa" -gt 1 ]; then b=$(pruef "$nb" 0); else b="ok (1 NUMA-Knoten)"; fi
zeile "kernel.numa_balancing" "$nb ($numa NUMA-Knoten)" "0 bei mehreren NUMA-Knoten" "$b"
if command -v tuned-adm >/dev/null 2>&1; then t=$(tuned-adm active 2>/dev/null | sed -n 's/.*: //p'); else t="kein TuneD"; fi
zeile "TuneD-Profil" "${t:-?}" "mssql" "$( [ "${t:-}" = mssql ] && echo ok || echo prüfen )"
gov=$(lies /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor); gov=${gov:-"n/a (VM?)"}
zeile "CPU-Governor" "$gov" "performance" "$( [ "$gov" = performance ] && echo ok || { [ "${gov#n/a}" != "$gov" ] && echo info || echo prüfen; } )"
sw_on=$(awk 'NR>1' /proc/swaps 2>/dev/null | wc -l)
zeile "Swap eingerichtet" "$( [ "$sw_on" -gt 0 ] && echo ja || echo nein )" "ja (Sicherheitsnetz, keine Kapazität)" "$( [ "$sw_on" -gt 0 ] && echo ok || echo prüfen )"
for pfad in $(printf "%s\n%s\n" "$DATA" "$LOG" | awk '!s[$0]++'); do
[ -d "$pfad" ] || { zeile "Verzeichnis $pfad" "fehlt" "" "?"; continue; }
read -r dev fs opts < <(awk -v p="$(df -P "$pfad" 2>/dev/null | awk 'NR==2{print $6}')" '$2==p{print $1, $3, $4}' /proc/mounts | tail -1)
if [ "${fs:-}" = overlay ]; then
zeile "Dateisystem $pfad" "overlay" "Volume oder Bind-Mount" "prüfen (Container-Schreibschicht)"; continue; fi
zeile "Dateisystem $pfad" "${fs:-?}" "xfs (ext4 unterstützt)" "$( [ "${fs:-}" = xfs ] && echo ok || { [ "${fs:-}" = ext4 ] && echo ok || echo prüfen; } )"
zeile " Mount-Option atime" "$(echo "${opts:-?}" | grep -o 'noatime\|relatime\|strictatime' | head -1)" "noatime" "$(echo "${opts:-}" | grep -q noatime && echo ok || echo prüfen)"
blk=$(basename "$(readlink -f "${dev:-x}" 2>/dev/null)" | sed 's/[0-9]*$//; s/p$//')
if [ -n "$blk" ] && [ -d "/sys/block/$blk/queue" ]; then
ra=$(lies /sys/block/$blk/queue/read_ahead_kb); sch=$(lies /sys/block/$blk/queue/scheduler | sed -n 's/.*\[\(.*\)\].*/\1/p')
fua=$(lies /sys/block/$blk/queue/fua); wc=$(lies /sys/block/$blk/queue/write_cache)
zeile " Read-ahead ($blk)" "${ra:-?} KB" "4096 (blockdev --setra, Sektoren)" "info"
zeile " I/O-Scheduler ($blk)" "${sch:-?}" "keine Vorgabe (Multiqueue)" "info"
zeile " FUA / Write-Cache ($blk)" "${fua:-?} / ${wc:-?}" "FUA=1 nur dann TF 3979" "info"
else
zeile " Blockgerät" "nicht ermittelbar" "" "?"
fi
done
CONF=/var/opt/mssql/mssql.conf
echo
if [ -r "$CONF" ]; then
echo "mssql.conf ($CONF), Einträge mit Leistungsbezug:"
grep -E '^\s*(writethrough|alternatewritethrough|memorylimitmb|traceflag[0-9]*)\s*=' "$CONF" | sed 's/^/ /' || echo " (keine; es gelten die Standardwerte)"
else
echo "mssql.conf nicht lesbar oder nicht vorhanden (Standardwerte; im Container ggf. MSSQL_*-Variablen)."
fi
echo "Standard unter Linux: Forced Flush (Trace Flag 3982), control.writethrough = 1, control.alternatewritethrough = 1."
echo "Nie writethrough = 0 setzen, solange nicht belegt ist, dass der Speicher O_DIRECT-sicher ist."Beispielausgabe
Aus dem Lab: unser Host mit Ubuntu 24.04, Daten und Log auf getrennten virtuellen Datenträgern. Fast jede Kernel-Einstellung weicht von Microsofts Liste ab; das ist der Normalfall einer Standardinstallation.
Host-Check SQL Server unter Linux · 2026-11-13 10:33 · psg-sql-lab01
System: Ubuntu 24.04.5 LTS · Kernel 6.8.0-147-generic
Einstellung Ist Empfehlung (Microsoft) Bewertung
----------- --- ---------------------- ---------
Transparent Huge Pages madvise always (mehrere Instanzen: madvise) prüfen
vm.swappiness 60 1 prüfen
vm.max_map_count 1048576 1600000 (mindestens 262144) ok (Profil: 1600000)
vm.dirty_ratio / background 20 / 10 80 / 3 (TuneD-Profil mssql) prüfen
kernel.numa_balancing 1 (2 NUMA-Knoten) 0 bei mehreren NUMA-Knoten prüfen
TuneD-Profil kein TuneD mssql prüfen
CPU-Governor n/a (VM?) performance info
Swap eingerichtet nein ja (Sicherheitsnetz, keine Kapazität) prüfen
Dateisystem /srv/sql/data ext4 xfs (ext4 unterstützt) ok
Mount-Option atime relatime noatime prüfen
Read-ahead (sdc) 128 KB 4096 (blockdev --setra, Sektoren) info
I/O-Scheduler (sdc) none keine Vorgabe (Multiqueue) info
FUA / Write-Cache (sdc) 0 / write back FUA=1 nur dann TF 3979 info
Dateisystem /srv/sql/log ext4 xfs (ext4 unterstützt) ok
Mount-Option atime relatime noatime prüfen
Read-ahead (sdd) 128 KB 4096 (blockdev --setra, Sektoren) info
I/O-Scheduler (sdd) none keine Vorgabe (Multiqueue) info
FUA / Write-Cache (sdd) 0 / write back FUA=1 nur dann TF 3979 info
mssql.conf nicht lesbar oder nicht vorhanden (Standardwerte; im Container ggf. MSSQL_*-Variablen).
Standard unter Linux: Forced Flush (Trace Flag 3982), control.writethrough = 1, control.alternatewritethrough = 1.
Nie writethrough = 0 setzen, solange nicht belegt ist, dass der Speicher O_DIRECT-sicher ist.2 · Schreiben und Flushen
Ein Commit ist erst fertig, wenn der Log-Eintrag sicher auf dem Datenträger liegt. Unter Windows verlangt SQL Server das beim Schreiben mit einem Flag. Unter Linux hängt es davon ab, ob der Datenträger „Forced Unit Access“ (FUA) beherrscht, also einzelne Schreibvorgänge am Cache vorbei dauerhaft machen kann.
| Variante | Einstellungen | Wann |
|---|---|---|
| Forced Flush (Standard) | Trace Flag 3982 (intern), control.writethrough = 1, control.alternatewritethrough = 1 | immer, solange der Speicher kein FUA nachweislich kann |
| FUA-Schreiben | Trace Flag 3979, writethrough = 1, alternatewritethrough = 0 | nur mit FUA-fähigem Speicher (XFS ab Kernel 4.18, ext4 ab 5.6), nicht auf overlayfs |
| unsicher | writethrough = 0 | nie, solange nicht belegt ist, dass der Speicher O_DIRECT-sicher ist |
Im Standard ruft SQL Server nach dem Schreiben fdatasync auf; das ist der Wartetyp PREEMPTIVE_OS_FLUSHFILEBUFFERS. In unserem Lab (Daten und Log auf getrennten virtuellen Datenträgern, ext4, Datenträger ohne FUA) sah das bei vielen kleinen Transaktionen so aus:
| Commits/s (eine Sitzung / vier Sitzungen) | 2017 | 2019 | 2022 | 2025 |
|---|---|---|---|---|
| Standard (Flush je Commit, 6,4 ms im Mittel) | 141 / 542 | 141 / 463 | 140 / 427 | 140 / 380 |
Trace Flag 3979, alternatewritethrough = 0 (kein Flush) | 293 / 777 | 293 / 931 | 291 / 662 | 290 / 894 |
Alle vier Versionen verhalten sich gleich: 20.000 Commits, rund 20.000 Aufrufe von fdatasync. Bei SQL Server 2025 dauerte ein Commit im Standard rund 7,1 ms, davon 6,4 ms Flush; ohne Flush rund 3,4 ms.
Die zweite Zeile ist keine Empfehlung: Unsere Datenträger melden kein FUA, und Microsoft nennt Trace Flag 3979 nur für Speicher, der es beherrscht. Ob der Kernel in diesem Fall für jeden Schreibvorgang den Cache des Datenträgers wirklich leert, haben wir nicht geprüft. Die Messung zeigt, was der Flush je Commit kostet, nicht, dass man ihn abschalten sollte.
Zwei Beobachtungen für den Betrieb:
DBCC TRACESTATUSzeigt den Standard nicht: Trace Flag 3982 erscheint mit Status 0, obwohl Forced Flush aktiv ist. Ist 3979 gesetzt, steht es mit Status 1 da und im Errorlog als Startparameter-T 3979.control.writethroughundcontrol.alternatewritethroughsieht SQL Server nicht; sie stehen in/var/opt/mssql/mssql.confim Abschnitt[control], gesetzt mitmssql-conf set control.alternatewritethrough 0. Fehlt der Abschnitt, gelten die Standardwerte.
Hohe WRITELOG-Zeiten haben unter Linux dieselben Ursachen wie unter Windows: ein langsamer Log-Datenträger oder sehr viele sehr kleine Transaktionen. Mehrere Zeilen in einer Transaktion zusammenzufassen spart unter Linux zusätzlich je einen Flush.
3 · Speicher in drei Stufen
| Stufe | Begrenzt | Gesetzt mit |
|---|---|---|
| Host oder Container | alles | VM-Größe bzw. docker run --memory |
memory.memorylimitmb | den ganzen SQL-Server-Prozess (Puffer-Pool, SQLPAL, Agent, Full-Text) | mssql-conf set memory.memorylimitmb, im Container MSSQL_MEMORY_LIMIT_MB |
max server memory | nur den Puffer-Pool | sp_configure |
Microsoft empfiehlt zwischen Container-Grenze und memory.memorylimitmb 10 bis 20 Prozent Kopfraum und max server memory darunter. Ohne Einstellung nimmt SQL Server 80 Prozent des kleineren Werts aus Host-Speicher und Container-Grenze, aber erst ab 2022 CU20 auch unter cgroup v2; 2017 und 2019 sehen die Grenze eines Containers dort nicht (Teil 3).
Was passiert, haben wir mit einem 4-GB-Container (--memory=4g, ohne Swap) und einer Last nachgestellt, die eine 4,7 GB große Tabelle mehrfach liest und gruppiert:
| Version | ohne Einstellung | memory.memorylimitmb 20 % unter der Grenze (3.277 MB) |
|---|---|---|
| 2017 | sieht 64 GB, Ziel 60 GB: vom Kernel beendet | Last fertig, Spitze 2,8 GB |
| 2019 | sieht 64 GB, Ziel 61 GB: vom Kernel beendet | Last fertig, Spitze 3,2 GB |
| 2022 | sieht 3,2 GB (80 % der Grenze): Last fertig, Spitze 3,3 GB | Last fertig, Spitze 3,3 GB |
| 2025 | sieht 3,2 GB (80 % der Grenze): Last fertig, Spitze 3,3 GB | Last fertig, Spitze 3,4 GB |
memory.memorylimitmb 20 Prozent unter der Grenze lief die Last in allen geprüften Versionen durch.SQL Server 2022 und 2025 überstanden bei dieser Last sogar memory.memorylimitmb gleich der Container-Grenze (Spitze 3,87 bzw. 3,76 von 4 GB). Das heißt nicht, dass es ohne Kopfraum sicher ist: Agent, CLR oder große Speicherzuteilungen für Abfragen haben wir nicht belastet.
Ein OOM-Kill hinterlässt im Errorlog von SQL Server keine Zeile. Das Errorlog endete in unserem Lab mit den normalen Startmeldungen, dann nichts mehr. Belege stehen an zwei anderen Stellen: im Kernel-Log (dmesg bzw. journalctl -k: oom-kill:constraint=CONSTRAINT_MEMCG … oom_memcg=/system.slice/docker-<id>.scope) und bei Docker im Container-Zustand (docker inspect: OOMKilled: true, Exitcode 137). Wer nur das Errorlog überwacht, sieht einen Neustart ohne Grund.
Ein Fehler, nach dem der Prozess mehr Speicher belegte als memory.memorylimitmb erlaubt, ist in SQL Server 2019 CU27 und 2022 CU14 behoben (KB5042369).
4 · CPU und NUMA
Eine CPU-Quote (docker run --cpus, Kubernetes-CPU-Limits) sieht SQL Server nicht: Er legt für jede CPU des Hosts einen Scheduler an und plant Parallelität danach (Teil 3). Microsoft nennt als Gegenmittel ALTER SERVER CONFIGURATION SET PROCESS AFFINITY auf so viele CPUs, wie die Quote erlaubt, unter cgroup v2 zusammen mit Trace Flag 8002 (weiche Affinität: die Scheduler dürfen die CPU wechseln). Wir haben das mit einer parallelen Aggregation in einem Container mit --cpus=4 auf einem Host mit 24 CPUs gemessen:
| Laufzeit (Median aus 5) und vom Kernel gedrosselte Zeit | 2019 | 2022 | 2025 |
|---|---|---|---|
| Standard: 24 Scheduler, Parallelität 24 | 775 ms, 51 s | 873 ms, 60 s | 991 ms, 58 s |
PROCESS AFFINITY CPU = 0 TO 3: 4 Scheduler | 648 ms, 0 s | 697 ms, 0 s | 791 ms, 0 s |
| Affinität und Trace Flag 8002 | 637 ms, 0 s | 707 ms, 0 s | 776 ms, 0 s |
| nur Trace Flag 8002 | 790 ms, 49 s | 874 ms, 61 s | 984 ms, 62 s |
PROCESS AFFINITY passend zur Quote nimmt die Drosselung weg und macht die Abfrage 16 bis 20 Prozent schneller. Trace Flag 8002 ändert daran kaum etwas.Ohne Affinität plant SQL Server 24 parallele Threads auf vier erlaubte CPUs; der Kernel bremst den Container dann immer wieder für den Rest seiner Zeitscheibe aus. Mit Affinität auf vier CPUs passt die Parallelität zur Quote, die Drosselung verschwindet, die Abfrage wird 16 bis 20 Prozent schneller. Trace Flag 8002 (weiche Affinität) änderte den Median um höchstens 2 Prozent, bei 2022 und 2025 mit starken Ausreißern in beide Richtungen. In einem kleineren Vorlauf mit 800.000 Zeilen war Affinität allein langsamer; das hing an der kleinen Datenmenge.
Unsere Empfehlung für Container mit CPU-Quote: PROCESS AFFINITY passend zur Quote setzen; Trace Flag 8002 nur, wenn Microsofts Hinweis für Ihre Umgebung gilt und Sie es messen. Einfacher sind feste CPUs (--cpuset-cpus), die SQL Server 2022 und 2025 erkennen; 2017 und 2019 sehen auch dann alle CPUs des Hosts.
NUMA: Auf Hosts mit mehreren NUMA-Knoten empfiehlt Microsoft, das automatische NUMA-Balancing des Kernels abzuschalten (kernel.numa_balancing = 0), weil SQL Server seine Speicherzuordnung selbst steuert. Gemessen haben wir das nicht.
Voreinstellungen im Container: So richten sich die Images beim ersten Start auf unserem Host mit 24 CPUs ein:
| 2017 | 2019 | 2022 | 2025 | |
|---|---|---|---|---|
tempdb-Datendateien, ohne Grenze oder mit --cpus=6 | 1 | 8 | 8 | 8 |
tempdb-Datendateien, mit 6 festen CPUs (--cpuset-cpus) | 1 | 8 | 6 | 6 |
| MAXDOP / Kostenschwelle | 0 / 5 | 0 / 5 | 0 / 5 | 0 / 5 |
Ab 2019 legt das Image tempdb also wie eine Paketinstallation an: eine Datei je CPU, höchstens acht. Dabei zählt, was SQL Server als CPU sieht; eine Quote sieht er nicht, feste CPUs erst ab 2022. MAXDOP und Kostenschwelle stehen dagegen in allen Versionen auf der alten Voreinstellung. Setzen Sie beides nach dem ersten Start, wie in unserem TempDB-Ratgeber und unter Windows auch.
Eine Falle für Skripte: SQL Server meldet sich im Container schon als bereit („SQL Server is now ready for client connections“), bevor tempdb fertig ist. Das Errorlog zeigt erst „The tempdb database has 1 data file(s).“ und ein bis zwei Sekunden später die endgültige Zahl. Wer gleich nach der ersten Anmeldung prüft oder Last startet, sieht einen Zwischenstand oder bekommt Fehler 904 („cannot be autostarted during server shutdown or start“). Warten Sie in Startskripten auf „Recovery is complete“ im Errorlog, nicht nur auf eine erfolgreiche Anmeldung.
5 · Wo die Datenbankdateien liegen
Im Container können Datenbankdateien in der Schreibschicht des Containers liegen (overlayfs), auf einem Bind-Mount eines Host-Verzeichnisses oder in einem benannten Docker-Volume. Docker empfiehlt Volumes für schreibintensive Last; Microsoft verlangt für den FUA-Weg ausdrücklich einen Mount, nicht overlayfs. Gemessen haben wir dieselbe Last in vier Varianten:
| SQL Server 2025 | Commits/s (1 / 4 Sitzungen) | Bulk-Insert 1 Mio. Zeilen | Backup |
|---|---|---|---|
| Schreibschicht des Containers (overlayfs) | 141 / 422 | 3,21 s | 3,23 s |
| Bind-Mount, gleicher Datenträger | 141 / 442 | 3,21 s | 3,28 s |
| benanntes Volume | 141 / 424 | 3,23 s | 3,35 s |
| Bind-Mounts, Daten und Log auf getrennten Datenträgern | 141 / 472 | 3,16 s | 3,43 s |
SQL Server 2022 zeigte dasselbe Bild (141 Commits/s in allen Varianten, Bulk 2,99 bis 3,17 s); die Werte mit vier Sitzungen streuten zwischen 334 und 482 ohne Muster. Auf unserem Host war der Ort also für die Leistung egal. Trotzdem gehören Datenbankdateien nicht in die Schreibschicht: Sie verschwinden mit dem Container, und für Updates muss der Container neu erstellt werden (Teil 3). Getrennte Datenträger für Daten und Log bleiben sinnvoll, wenn die Datenträger selbst getrennt sind; bei virtuellen Datenträgern auf derselben SSD wie hier bringt das wenig.
Dateisystem: Microsoft empfiehlt XFS für Daten und Log, ext4 ist unterstützt; dazu noatime. Eine Messung XFS gegen ext4 haben wir nicht gemacht, eine veröffentlichte haben wir nicht gefunden.
6 · Überwachen unter Linux
Was in der Datenbank passiert, sehen Sie unter Linux wie unter Windows: Query Store, DMVs, Extended Events, das Performance Dashboard in SSMS. Anders ist die Sicht auf das Betriebssystem:
- Kein Performance Monitor. Die SQL-Server-Zähler liefert
sys.dm_os_performance_counters; Raten und Prozentwerte muss man aus den Rohwerten selbst berechnen. - Betriebssystem-Werkzeuge:
top/htop,vmstat,iostat,sar(Paketsysstat). Red Hat liefert mit Performance Co-Pilot ein Plug-in für SQL Server (pmdamssql), das DMV-Werte als Metriken sammelt. - Wartetypen, die unter Linux anders aussehen:
PREEMPTIVE_OS_FLUSHFILEBUFFERSsteht oft weit oben und ist für sich allein kein Befund (Abschnitt 2). - Das Management Data Warehouse (Data Collector) in SSMS wird gegen Linux-Instanzen nicht unterstützt.
Betriebs-Check: Wie steht meine Instanz da?
Die folgende Abfrage bewertet die Punkte dieser Seite für eine Instanz, eine Zeile je Prüfpunkt. Was die Instanz von Plattform und Container-Grenzen sieht, zeigt Abfrage 2 in Teil 3; welche unter Linux fehlenden Funktionen sie nutzt, Abfrage 1 in Teil 1.
Abfrage: Betriebs-Check
/* PSG Stack · SQL Server unter Linux und Docker · Abfrage 4 „Betriebs-Check“ Prüft Einstellungen und Wartezeiten, die unter Linux für Leistung und stabilen Betrieb zählen: Flush-Trace-Flags, Speichergrenzen und Speicherdruck, CPUs und Scheduler, MAXDOP, tempdb, Wartezeiten beim Schreiben des Transaktionsprotokolls. Eine Zeile je Prüfpunkt mit Bewertung. !!! WARNUNG — bitte vor dem Ausführen lesen !!! - Nur lesend. Die Abfrage ändert keine Daten und keine Einstellung. - Rechte: VIEW SERVER STATE (ab 2022 genügt für die meisten Punkte VIEW SERVER PERFORMANCE STATE); ohne VIEW ANY DEFINITION meldet der tempdb-Punkt „nicht geprüft“. - MAXDOP 1: Jede Teilabfrage läuft seriell und belegt keine weiteren Kerne. - Wartezeiten zählen seit dem letzten Start oder seit dem letzten Leeren der Statistik. - Bewertung „prüfen“ heißt: genauer hinsehen, nicht automatisch ändern. Richtwerte nach Microsoft Learn (Linux-Performance-Seiten, Stand 05/2026) und PSG-Lab; sie ersetzen keine Analyse. - Ab SQL Server 2017. Geprüft im PSG-Lab auf 2017, 2019, 2022 und 2025 in Linux-Containern, als sa und ohne sysadmin (13.11.2026); unter Windows nicht geprüft (Linux-Punkte zeigen dort n/a).*/SET NOCOUNT ON; DECLARE @r TABLE (nr int, bereich nvarchar(20), pruefpunkt nvarchar(80), wert nvarchar(200), richtwert nvarchar(200), bewertung nvarchar(10), hinweis nvarchar(400));DECLARE @linux bit = 0, @cpu int, @sched int, @mem_mb bigint, @ziel_mb bigint, @modell nvarchar(60), @maxmem bigint, @maxdop int, @ctp int, @sql nvarchar(max), @container nvarchar(60) = N'n/a'; IF OBJECT_ID(N'sys.dm_os_host_info') IS NOT NULL SELECT @linux = CASE WHEN host_platform = N'Linux' THEN 1 ELSE 0 END FROM sys.dm_os_host_info OPTION (MAXDOP 1); SELECT @cpu = cpu_count, @mem_mb = physical_memory_kb / 1024, @ziel_mb = committed_target_kb / 1024, @modell = sql_memory_model_descFROM sys.dm_os_sys_info OPTION (MAXDOP 1);IF EXISTS (SELECT 1 FROM sys.all_columns WHERE object_id = OBJECT_ID(N'sys.dm_os_sys_info') AND name = N'container_type_desc') EXEC sys.sp_executesql N'SELECT @c = container_type_desc FROM sys.dm_os_sys_info OPTION (MAXDOP 1);', N'@c nvarchar(60) OUTPUT', @c = @container OUTPUT;SELECT @sched = COUNT(*) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE' OPTION (MAXDOP 1);SELECT @maxmem = CONVERT(bigint, value_in_use) FROM sys.configurations WHERE name = N'max server memory (MB)' OPTION (MAXDOP 1);SELECT @maxdop = CONVERT(int, value_in_use) FROM sys.configurations WHERE name = N'max degree of parallelism' OPTION (MAXDOP 1);SELECT @ctp = CONVERT(int, value_in_use) FROM sys.configurations WHERE name = N'cost threshold for parallelism' OPTION (MAXDOP 1); /* 1 · Schreiben und Flushen (nur Linux) */DECLARE @tf TABLE (TraceFlag int, Status int, Global int, Session int);BEGIN TRY INSERT @tf EXEC (N'DBCC TRACESTATUS(3979, 3982, 8002, -1) WITH NO_INFOMSGS');END TRYBEGIN CATCHEND CATCH;INSERT @r SELECT 1, N'Flushen', N'Flush-Modus (Trace Flag 3979)', CASE WHEN @linux = 0 THEN N'n/a (Windows)' WHEN NOT EXISTS (SELECT 1 FROM @tf) THEN N'nicht lesbar' WHEN EXISTS (SELECT 1 FROM @tf WHERE TraceFlag = 3979 AND Global = 1) THEN N'FUA-Schreiben (3979 an)' ELSE N'Forced Flush (Standard, 3979 aus)' END, N'Forced Flush, außer der Speicher kann nachweislich FUA', N'info', N'Forced Flush ist der Linux-Standard, auch wenn DBCC TRACESTATUS 3982 nicht als aktiv zeigt (PSG-Lab). 3979 nur mit FUA-fähigem Speicher und alternatewritethrough = 0.';INSERT @r SELECT 2, N'Flushen', N'writethrough / alternatewritethrough', N'in mssql.conf (SQL Server zeigt sie nicht)', N'1 / 1 (Standard) oder 1 / 0 mit Trace Flag 3979', N'info', N'Mit cat /var/opt/mssql/mssql.conf prüfen, Abschnitt [control]; fehlt er, gelten die Standardwerte. writethrough = 0 kann Daten kosten.'; /* 2 · Wartezeiten beim Schreiben (seit Start) */DECLARE @gesamt bigint, @flush bigint, @wl_ms bigint, @wl_n bigint;SELECT @gesamt = SUM(wait_time_ms) FROM sys.dm_os_wait_statsWHERE wait_type NOT LIKE N'SLEEP%' AND wait_type NOT LIKE N'BROKER%' AND wait_type NOT LIKE N'XE%' AND wait_type NOT LIKE N'%QUEUE%' AND wait_type NOT LIKE N'HADR%' AND wait_type NOT LIKE N'SQLTRACE%' AND wait_type NOT LIKE N'CLR%' AND wait_type NOT LIKE N'FT_%' AND wait_type NOT LIKE N'%IDLE%' AND wait_type NOT IN (N'WAITFOR', N'CHECKPOINT_QUEUE', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE', N'LOGMGR_QUEUE', N'REQUEST_FOR_DEADLOCK_SEARCH', N'SP_SERVER_DIAGNOSTICS_SLEEP', N'QDS_ASYNC_QUEUE', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'PWAIT_EXTENSIBILITY_CLEANUP_TASK', N'SOS_WORK_DISPATCHER', N'PVS_PREALLOCATE', N'PREEMPTIVE_XE_DISPATCHER', N'PREEMPTIVE_OS_FLUSHFILEBUFFERS_IDLE')OPTION (MAXDOP 1);SELECT @flush = SUM(CASE WHEN wait_type = N'PREEMPTIVE_OS_FLUSHFILEBUFFERS' THEN wait_time_ms ELSE 0 END), @wl_ms = SUM(CASE WHEN wait_type = N'WRITELOG' THEN wait_time_ms ELSE 0 END), @wl_n = SUM(CASE WHEN wait_type = N'WRITELOG' THEN waiting_tasks_count ELSE 0 END)FROM sys.dm_os_wait_stats OPTION (MAXDOP 1);INSERT @r SELECT 3, N'Flushen', N'Anteil PREEMPTIVE_OS_FLUSHFILEBUFFERS an den Wartezeiten', CASE WHEN ISNULL(@gesamt, 0) = 0 THEN N'keine Wartezeiten' ELSE REPLACE(CONVERT(nvarchar(20), CONVERT(decimal(5, 1), 100.0 * @flush / @gesamt)), N'.', N',') + N' %' END, N'unter Linux oft weit oben, für sich allein kein Befund', N'info', N'Entspricht dem fdatasync-Aufruf von SQL Server unter Linux (Paul Randal, Bob Dorr). Erst zusammen mit hohen WRITELOG-Zeiten ein Thema.';INSERT @r SELECT 4, N'Flushen', N'WRITELOG, mittlere Wartezeit je Commit-Warten', CASE WHEN ISNULL(@wl_n, 0) = 0 THEN N'keine' ELSE REPLACE(CONVERT(nvarchar(20), CONVERT(decimal(9, 2), 1.0 * @wl_ms / @wl_n)), N'.', N',') + N' ms' END, N'wenige Millisekunden', CASE WHEN @wl_n > 0 AND 1.0 * @wl_ms / @wl_n > 5 THEN N'prüfen' ELSE N'ok' END, N'Hohe Werte: Log-Datenträger langsam, Log in der Container-Schreibschicht (overlayfs) oder sehr viele kleine Transaktionen.'; /* 3 · Speicher */INSERT @r SELECT 5, N'Speicher', N'max server memory gegen sichtbaren Speicher', CONVERT(nvarchar(20), @maxmem) + N' MB von ' + CONVERT(nvarchar(20), @mem_mb) + N' MB', N'gesetzt, unter dem sichtbaren Speicher, mit Kopfraum', CASE WHEN @maxmem >= 2147483647 OR @maxmem > @mem_mb THEN N'prüfen' ELSE N'ok' END, N'Unter Linux begrenzt memory.memorylimitmb (bzw. MSSQL_MEMORY_LIMIT_MB) den ganzen Prozess, max server memory nur den Puffer-Pool. Im Container 10–20 % Kopfraum zum Container-Limit lassen.';INSERT @r SELECT 6, N'Speicher', N'Speicherziel (committed_target)', CONVERT(nvarchar(20), @ziel_mb) + N' MB', N'unter dem Container- oder Host-Limit', N'info', N'2017 und 2019 sehen das cgroup-v2-Limit eines Containers nicht und planen mit dem Host-Speicher (PSG-Lab, Teil 3).';INSERT @r SELECT 7, N'Speicher', N'Speichermodell', @modell, N'CONVENTIONAL ist unter Linux normal', N'info', N'Für „Lock Pages in Memory“ gibt es unter Linux keine dokumentierte Entsprechung.';DECLARE @tief int, @proz int;SELECT @tief = CONVERT(int, system_low_memory_signal_state) FROM sys.dm_os_sys_memory OPTION (MAXDOP 1);SELECT @proz = CONVERT(int, process_physical_memory_low) FROM sys.dm_os_process_memory OPTION (MAXDOP 1);INSERT @r SELECT 8, N'Speicher', N'Speicherdruck jetzt (System / Prozess)', CONVERT(nvarchar(5), @tief) + N' / ' + CONVERT(nvarchar(5), @proz), N'0 / 0', CASE WHEN @tief = 1 OR @proz = 1 THEN N'prüfen' ELSE N'ok' END, N'1 heißt: SQL Server meldet gerade zu wenig Speicher. Bleibt es dauerhaft 1, Grenzen und speicherhungrige Abfragen prüfen.'; /* 4 · CPU, Scheduler, Parallelität */INSERT @r SELECT 9, N'CPU', N'CPUs und sichtbare Scheduler', CONVERT(nvarchar(10), @cpu) + N' CPUs, ' + CONVERT(nvarchar(10), @sched) + N' Scheduler' + CASE WHEN @container <> N'n/a' AND @container <> N'NONE' THEN N' · ' + @container ELSE N'' END, N'so viele Scheduler, wie CPUs tatsächlich zur Verfügung stehen', N'info', N'Eine CPU-Quote (docker --cpus) sieht SQL Server nicht. Gegenmittel laut Microsoft: PROCESS AFFINITY und unter cgroup v2 Trace Flag 8002.';INSERT @r SELECT 10, N'CPU', N'Trace Flag 8002 (weiche Prozessoraffinität)', CASE WHEN @linux = 0 THEN N'n/a (Windows)' WHEN NOT EXISTS (SELECT 1 FROM @tf) THEN N'nicht lesbar' WHEN EXISTS (SELECT 1 FROM @tf WHERE TraceFlag = 8002 AND Global = 1) THEN N'an' ELSE N'aus' END, N'Microsoft: unter cgroup v2 zusammen mit PROCESS AFFINITY; vorher messen', N'info', N'Ohne 8002 bindet PROCESS AFFINITY die Scheduler fest an einzelne CPUs. Im PSG-Lab (2019–2025, Quote 4 CPUs) brachte 8002 zur Affinität keinen messbaren Gewinn.';INSERT @r SELECT 11, N'CPU', N'max degree of parallelism', CONVERT(nvarchar(10), @maxdop), N'höchstens 8 je NUMA-Knoten, nicht 0 bei mehr als 8 Schedulern', CASE WHEN @maxdop = 0 AND @sched > 8 THEN N'prüfen' ELSE N'ok' END, N'MAXDOP 0 nutzt so viele Threads, wie Scheduler sichtbar sind, auch wenn der Container weniger CPU bekommt.';INSERT @r SELECT 12, N'CPU', N'cost threshold for parallelism', CONVERT(nvarchar(10), @ctp), N'nicht die Voreinstellung 5', CASE WHEN @ctp = 5 THEN N'prüfen' ELSE N'ok' END, N'Die Voreinstellung 5 lässt schon sehr kleine Abfragen parallel laufen.'; /* 5 · tempdb */IF HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW ANY DEFINITION') = 1BEGIN DECLARE @n int, @gleich bit, @prozent int; SELECT @n = COUNT(*), @gleich = CASE WHEN MIN(size) = MAX(size) AND MIN(growth) = MAX(growth) THEN 1 ELSE 0 END, @prozent = MAX(CONVERT(int, is_percent_growth)) FROM sys.master_files WHERE database_id = 2 AND type = 0 OPTION (MAXDOP 1); INSERT @r SELECT 13, N'tempdb', N'Datendateien', CONVERT(nvarchar(10), @n) + N' Dateien, ' + CASE WHEN @gleich = 1 THEN N'gleich groß' ELSE N'unterschiedlich' END + CASE WHEN @prozent = 1 THEN N', Wachstum in Prozent' ELSE N'' END, N'so viele wie Scheduler, höchstens 8, gleich groß, festes Wachstum', CASE WHEN @n < CASE WHEN @sched > 8 THEN 8 ELSE @sched END OR @gleich = 0 OR @prozent = 1 THEN N'prüfen' ELSE N'ok' END, N'Ab 2019 legt eine Linux-Installation bis zu acht Dateien an (Microsoft); die Container-Images legen beim ersten Start eine je CPU an, höchstens acht (PSG-Lab; 2017: eine). Nicht bei einem In-place-Upgrade.';ENDELSE INSERT @r SELECT 13, N'tempdb', N'Datendateien', N'nicht geprüft', N'', N'info', N'VIEW ANY DEFINITION fehlt (sys.master_files).'; SELECT nr, bereich, pruefpunkt, wert, richtwert, bewertung, hinweis FROM @r ORDER BY nr OPTION (MAXDOP 1);Beispielausgabe
Aus dem Lab: SQL Server 2025 CU10 im Container mit 6 festen CPUs und 6 GB Grenze, als sa, nach einer kurzen Schreiblast (2.000 Einzel-Inserts).
nr|bereich|pruefpunkt|wert|richtwert|bewertung|hinweis
--|-------|----------|----|---------|---------|-------
1|Flushen|Flush-Modus (Trace Flag 3979)|Forced Flush (Standard, 3979 aus)|Forced Flush, außer der Speicher kann nachweislich FUA|info|Forced Flush ist der Linux-Standard, auch wenn DBCC TRACESTATUS 3982 nicht als aktiv zeigt (PSG-Lab). 3979 nur mit FUA-fähigem Speicher und alternatewritethrough = 0.
2|Flushen|writethrough / alternatewritethrough|in mssql.conf (SQL Server zeigt sie nicht)|1 / 1 (Standard) oder 1 / 0 mit Trace Flag 3979|info|Mit cat /var/opt/mssql/mssql.conf prüfen, Abschnitt [control]; fehlt er, gelten die Standardwerte. writethrough = 0 kann Daten kosten.
3|Flushen|Anteil PREEMPTIVE_OS_FLUSHFILEBUFFERS an den Wartezeiten|22,9 %|unter Linux oft weit oben, für sich allein kein Befund|info|Entspricht dem fdatasync-Aufruf von SQL Server unter Linux (Paul Randal, Bob Dorr). Erst zusammen mit hohen WRITELOG-Zeiten ein Thema.
4|Flushen|WRITELOG, mittlere Wartezeit je Commit-Warten|0,56 ms|wenige Millisekunden|ok|Hohe Werte: Log-Datenträger langsam, Log in der Container-Schreibschicht (overlayfs) oder sehr viele kleine Transaktionen.
5|Speicher|max server memory gegen sichtbaren Speicher|2147483647 MB von 4916 MB|gesetzt, unter dem sichtbaren Speicher, mit Kopfraum|prüfen|Unter Linux begrenzt memory.memorylimitmb (bzw. MSSQL_MEMORY_LIMIT_MB) den ganzen Prozess, max server memory nur den Puffer-Pool. Im Container 10–20 % Kopfraum zum Container-Limit lassen.
6|Speicher|Speicherziel (committed_target)|4267 MB|unter dem Container- oder Host-Limit|info|2017 und 2019 sehen das cgroup-v2-Limit eines Containers nicht und planen mit dem Host-Speicher (PSG-Lab, Teil 3).
7|Speicher|Speichermodell|CONVENTIONAL|CONVENTIONAL ist unter Linux normal|info|Für „Lock Pages in Memory“ gibt es unter Linux keine dokumentierte Entsprechung.
8|Speicher|Speicherdruck jetzt (System / Prozess)|0 / 0|0 / 0|ok|1 heißt: SQL Server meldet gerade zu wenig Speicher. Bleibt es dauerhaft 1, Grenzen und speicherhungrige Abfragen prüfen.
9|CPU|CPUs und sichtbare Scheduler|6 CPUs, 6 Scheduler · LINUX CONTAINER|so viele Scheduler, wie CPUs tatsächlich zur Verfügung stehen|info|Eine CPU-Quote (docker --cpus) sieht SQL Server nicht. Gegenmittel laut Microsoft: PROCESS AFFINITY und unter cgroup v2 Trace Flag 8002.
10|CPU|Trace Flag 8002 (weiche Prozessoraffinität)|aus|Microsoft: unter cgroup v2 zusammen mit PROCESS AFFINITY; vorher messen|info|Ohne 8002 bindet PROCESS AFFINITY die Scheduler fest an einzelne CPUs. Im PSG-Lab (2019–2025, Quote 4 CPUs) brachte 8002 zur Affinität keinen messbaren Gewinn.
11|CPU|max degree of parallelism|0|höchstens 8 je NUMA-Knoten, nicht 0 bei mehr als 8 Schedulern|ok|MAXDOP 0 nutzt so viele Threads, wie Scheduler sichtbar sind, auch wenn der Container weniger CPU bekommt.
12|CPU|cost threshold for parallelism|5|nicht die Voreinstellung 5|prüfen|Die Voreinstellung 5 lässt schon sehr kleine Abfragen parallel laufen.
13|tempdb|Datendateien|6 Dateien, gleich groß|so viele wie Scheduler, höchstens 8, gleich groß, festes Wachstum|ok|Ab 2019 legt eine Linux-Installation bis zu acht Dateien an (Microsoft); die Container-Images legen beim ersten Start eine je CPU an, höchstens acht (PSG-Lab; 2017: eine). Nicht bei einem In-place-Upgrade.Methode und Grenzen
SQL Server 2017 CU31, 2019 CU32, 2022 CU27 und 2025 CU10 (Docker-Images von Microsoft), Hauptlauf am 13.11.2026; je Variante ein frischer Container. Gemessen in Linux-Containern auf einer Ubuntu-24.04-VM unter Hyper-V (24 vCPU, 2 NUMA-Knoten, ext4 auf virtuellen Datenträgern einer SATA-SSD, Datenträger ohne FUA, kein Swap, Docker mit overlayfs und cgroup v2). Grenzen:
- Keine Änderung am Host: Die Wirkung von THP, swappiness, NUMA-Balancing, Read-ahead, TuneD,
noatimeund XFS ist nicht gemessen; dafür gelten Microsofts Empfehlungen. - Container statt Installation: Voreinstellungen einer Paketinstallation (
mssql-conf setup) haben wir nicht geprüft. - Virtuelle Datenträger ohne FUA: Der FUA-Weg ist hier nur als Messwert zu verstehen.
- Synthetische Lasten, je Fall wenige Läufe; Zeiten zeigen Größenordnungen.
Weiterlesen
- Microsoft Learn: „Performance best practices and configuration guidelines for SQL Server on Linux“ (Speicher, Kernel, CPU, Netzwerk), „Performance best practices: SQL Server memory on Linux“, „tempdb database“ (Abschnitt Linux).
- Bob Dorr: „SQL Server on Linux: Forced Unit Access (FUA) Internals“ (Microsoft Tech Community).
- Paul Randal: „PREEMPTIVE_OS_FLUSHFILEBUFFERS waits on Linux“ (SQLskills).
- In dieser Reihe: Übersicht, Teil 1, Teil 2: Für Windows-Admins, Teil 3: Was im Container schiefgeht.
- Bei PSG: TempDB-Ratgeber, SQL Server Performance.
Sie betreiben SQL Server unter Linux?
Drei Dinge zuerst: Lassen Sie den Host-Check laufen und entscheiden Sie bewusst, welche Abweichungen Sie behalten. Prüfen Sie mit Abfrage 4 Speichergrenzen, Scheduler und tempdb. Und schauen Sie, bevor Sie am Flushen drehen, ob Ihr Speicher FUA überhaupt kann.