{"index.json":{"schemaVersion":2,"app":"DBAtlas","updated":"2026-07-04","engines":"engines.json","rosetta":["rosetta/sessions.json","rosetta/storage.json","rosetta/performance.json","rosetta/backup.json","rosetta/security.json","rosetta/maintenance.json","rosetta/schema.json","rosetta/replication.json","rosetta/connect.json","rosetta/objects.json","rosetta/datamove.json","rosetta/monitoring.json","rosetta/automation.json","rosetta/lifecycle.json","rosetta/migration.json","rosetta/tuning.json","rosetta/tuning-adv.json","rosetta/advanced-features.json","rosetta/sql-syntax.json"],"cloud":["cloud/cloud-ops-1.json","cloud/cloud-ops-2.json"],"guide":{"bestPractices":["guide/best-practices.json","guide/best-practices-2.json","guide/best-practices-3.json","guide/best-practices-4.json"],"glossary":["guide/glossary.json","guide/glossary-2.json","guide/glossary-3.json"]},"rosettaOverlays":["rosetta/overlay-elasticsearch.json"],"types":"types/types-functions.json","errors":["errors/oracle.json","errors/postgres.json","errors/mysql.json","errors/mssql.json","errors/mongodb.json","errors/redis.json","errors/elasticsearch.json","errors/oracle-more.json","errors/postgres-more.json","errors/mysql-more.json","errors/mssql-more.json","errors/mongodb-more.json","errors/redis-more.json","errors/elasticsearch-more.json","errors/oracle-more2.json","errors/postgres-more2.json","errors/mysql-more2.json","errors/mssql-more2.json","errors/mongodb-more2.json","errors/redis-more2.json","errors/elasticsearch-more2.json","errors/oracle-more3.json","errors/postgres-more3.json","errors/mysql-more3.json","errors/mssql-more3.json","errors/mongodb-more3.json","errors/redis-more3.json","errors/elasticsearch-more3.json","errors/oracle-more4.json","errors/postgres-more4.json","errors/mysql-more4.json","errors/mssql-more4.json","errors/mongodb-more4.json","errors/redis-more4.json","errors/elasticsearch-more4.json"],"health":["health/oracle.json","health/postgres.json","health/mysql.json","health/mssql.json","health/mongodb.json","health/redis.json","health/elasticsearch.json"],"recipes":["recipes/oracle.json","recipes/oracle-ha.json","recipes/oracle-ops.json","recipes/postgres.json","recipes/postgres-ha.json","recipes/postgres-ops.json","recipes/mysql.json","recipes/mysql-ext.json","recipes/mssql.json","recipes/mssql-ext.json","recipes/mongodb.json","recipes/mongodb-ext.json","recipes/redis.json","recipes/redis-ext.json","recipes/elasticsearch.json","recipes/elasticsearch-ext.json"],"playbooks":["playbooks/playbooks.json","playbooks/hadr.json","playbooks/security.json","playbooks/cloud.json","playbooks/automation.json"],"solver":["solver/slow-query.json","solver/deadlock.json","solver/disk-full.json","solver/connection-storm.json","solver/replication-lag.json","solver/ora-01555.json","solver/backup-restore.json","solver/upgrade-wizard.json","solver/migration-wizard.json","solver/high-cpu.json","solver/memory-pressure.json","solver/install-setup.json","solver/corruption-recovery.json","solver/failover-drill.json","solver/security-incident.json","solver/audit-setup.json","solver/decide-ha.json","solver/decide-isolation.json","solver/decide-sqlnosql.json","solver/decide-shard-replica.json","solver/decide-backup.json","solver/decide-index.json","solver/decide-partition.json","solver/decide-pooling.json","solver/decide-cache.json","solver/decide-datatype.json","solver/decide-capacity.json","solver/slow-write.json","solver/io-bottleneck.json","solver/undo-temp-pressure.json","solver/long-transaction.json","solver/plan-regression.json","solver/replica-resync.json","solver/cache-hit-low.json","solver/arch-oltp.json","solver/arch-dwh.json","solver/arch-microservice.json","solver/arch-eventdriven.json","solver/arch-multiregion.json","solver/arch-highscale.json"],"reference":{"params":["reference/params-oracle.json","reference/params-postgres.json","reference/params-mysql.json","reference/params-mssql.json","reference/params-mongodb.json","reference/params-redis.json","reference/params-elasticsearch.json"],"waits":["reference/waits-oracle.json","reference/waits-postgres.json","reference/waits-mysql.json","reference/waits-mssql.json","reference/waits-mongodb.json","reference/waits-redis.json","reference/waits-elasticsearch.json"],"alerts":["reference/alerts-oracle.json","reference/alerts-postgres.json","reference/alerts-mysql.json","reference/alerts-mssql.json","reference/alerts-mongodb.json","reference/alerts-redis.json","reference/alerts-elasticsearch.json"]},"install":"install/index.json"},"engines.json":{"schemaVersion":1,"updated":"2026-07-03","engines":[{"id":"oracle","name":"Oracle","full":"Oracle Database","color":"#C74634","family":"sql","cli":"sqlplus","docs":"https://docs.oracle.com/en/database/"},{"id":"postgres","name":"PostgreSQL","full":"PostgreSQL","color":"#3461B5","family":"sql","cli":"psql","docs":"https://www.postgresql.org/docs/"},{"id":"mysql","name":"MySQL","full":"MySQL / MariaDB","color":"#00758F","family":"sql","cli":"mysql","docs":"https://dev.mysql.com/doc/"},{"id":"mssql","name":"SQL Server","full":"Microsoft SQL Server","color":"#6E3FC4","family":"sql","cli":"sqlcmd","docs":"https://learn.microsoft.com/en-us/sql/"},{"id":"mongodb","name":"MongoDB","full":"MongoDB","color":"#2E7D32","family":"document","cli":"mongosh","docs":"https://www.mongodb.com/docs/"},{"id":"redis","name":"Redis","full":"Redis","color":"#B45309","family":"keyvalue","cli":"redis-cli","docs":"https://redis.io/docs/"},{"id":"elasticsearch","name":"Elasticsearch","full":"Elasticsearch","color":"#BE2A6E","family":"search","cli":"curl / Kibana","docs":"https://www.elastic.co/guide/"}]},"rosetta/sessions.json":{"category":"Sessions & Aktivität","categoryId":"sessions","order":1,"concepts":[{"id":"active-sessions","title":"Aktive Sessions / laufende Abfragen anzeigen","aliases":["active sessions","running queries","who is connected","current activity","active session","running query","who is connected now","processlist","current op"],"engines":{"oracle":{"command":"SELECT sid, serial#, username, status, sql_id, machine, program\nFROM v$session\nWHERE type = 'USER' AND status = 'ACTIVE';","note":"v$session liefert die aktuell aktiven Sessions. Für den vollständigen SQL-Text v$sql über sql_id joinen.","danger":"safe"},"postgres":{"command":"SELECT pid, usename, state, wait_event_type, query\nFROM pg_stat_activity\nWHERE state <> 'idle';","note":"state = 'active' zeigt die Sessions, die gerade eine Abfrage ausführen. Laufzeiten aus backend_start / query_start berechnen.","danger":"safe"},"mysql":{"command":"SELECT id, user, host, db, command, time, state, info\nFROM information_schema.processlist\nWHERE command <> 'Sleep';\n-- Shortcut: SHOW FULL PROCESSLIST;","note":"Unter MySQL 8 / MariaDB hält performance_schema.processlist weniger Locks.","danger":"safe"},"mssql":{"command":"SELECT r.session_id, s.login_name, r.status, r.command,\n       r.cpu_time, r.total_elapsed_time, t.text AS sql_text\nFROM sys.dm_exec_requests r\nJOIN sys.dm_exec_sessions s ON r.session_id = s.session_id\nCROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE s.is_user_process = 1;","note":"Für einen schnellen Überblick: EXEC sp_who2 'active';","danger":"safe"},"mongodb":{"command":"db.currentOp({ \"active\": true })","note":"Lang laufende Operationen filtern: db.currentOp({ active: true, secs_running: { $gt: 5 } })","danger":"safe"},"redis":{"command":"CLIENT LIST\n# For a summary: INFO clients","note":"Da Redis single-threaded ist, werden statt „laufender Abfragen“ die verbundenen Clients aufgelistet. Für langsame Befehle SLOWLOG GET verwenden.","danger":"safe"}}},{"id":"blocking-sessions","title":"Blockierende / auf Locks wartende Sessions finden","aliases":["blocking","locks","lock wait","deadlock source","blocking session","lock waiting","who is blocking","who is blocking me","blocked","lock holder","what am I waiting on"],"engines":{"oracle":{"command":"SELECT blocking_session, sid, serial#, username,\n       sql_id, seconds_in_wait, event\nFROM v$session\nWHERE blocking_session IS NOT NULL;","note":"Eine Session mit gesetztem blocking_session wird von der SID im Wert von blocking_session blockiert.","danger":"safe"},"postgres":{"command":"SELECT pid, usename, pg_blocking_pids(pid) AS blocked_by,\n       wait_event, query\nFROM pg_stat_activity\nWHERE cardinality(pg_blocking_pids(pid)) > 0;","note":"Das Array blocked_by liefert die PIDs, die diese pid blockieren.","danger":"safe"},"mysql":{"command":"SELECT * FROM sys.innodb_lock_waits\\G\n-- MySQL 8: SELECT * FROM performance_schema.data_lock_waits;","note":"Das sys-Schema zeigt wartende und blockierende trx_id / Threads in lesbarer Form.","danger":"safe"},"mssql":{"command":"SELECT r.session_id, r.blocking_session_id,\n       r.wait_type, r.wait_time, r.wait_resource\nFROM sys.dm_exec_requests r\nWHERE r.blocking_session_id <> 0;","note":"Requests mit blocking_session_id <> 0 sind blockiert. Mit einer rekursiven Abfrage lässt sich die Kette verfolgen.","danger":"safe"},"mongodb":{"command":"db.currentOp({ \"waitingForLock\": true })","note":"WiredTiger sperrt auf Dokument-/Collection-Ebene. Lange Operationen lassen sich mit killOp beenden.","danger":"safe"},"redis":{"command":"# Not applicable: Redis commands run atomically one at a time; there are no row locks.\n# Closest diagnostic: SLOWLOG GET 10","note":"Blockierungen entstehen meist durch einen einzelnen lang laufenden Befehl (z. B. KEYS *, ein großes SORT); SLOWLOG zeigt sie.","danger":"safe"}}},{"id":"kill-session","title":"Session / Abfrage beenden (kill)","aliases":["kill session","terminate","cancel query","terminate session","cancel the query","kill query","killop"],"engines":{"oracle":{"command":"ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;\n-- To cancel only the running statement (18c+):\n-- ALTER SYSTEM CANCEL SQL 'sid,serial#';","note":"sid und serial# aus v$session holen. IMMEDIATE startet den Rollback sofort.","danger":"danger"},"postgres":{"command":"-- Cancel only the running query (the session stays):\nSELECT pg_cancel_backend(pid);\n-- Close the session entirely:\nSELECT pg_terminate_backend(pid);","note":"Zuerst pg_cancel_backend versuchen; wenn das nicht wirkt, pg_terminate_backend verwenden.","danger":"danger"},"mysql":{"command":"-- Kill only the query:\nKILL QUERY <id>;\n-- Close the connection entirely:\nKILL CONNECTION <id>;","note":"Der Wert <id> ist die Spalte Id in der Processlist.","danger":"danger"},"mssql":{"command":"KILL <session_id>;\n-- To see rollback progress:\n-- KILL <session_id> WITH STATUSONLY;","note":"KILL rollt die offene Transaktion zurück; bei großen Transaktionen kann der Rollback lange dauern.","danger":"danger"},"mongodb":{"command":"db.killOp(<opid>)","note":"Den opid-Wert aus dem Feld opid der Ausgabe von db.currentOp() übernehmen.","danger":"danger"},"redis":{"command":"CLIENT KILL ID <client-id>\n# or by address: CLIENT KILL ADDR 10.0.0.5:6379","note":"Die client-id mit CLIENT LIST ermitteln. Ein bereits laufender einzelner Befehl lässt sich nicht unterbrechen (außer einem Lua-Skript: SCRIPT KILL); man muss warten, bis er fertig ist. Um einen Client, der auf einen blockierenden Befehl wie BLPOP/XREAD BLOCK wartet, freizugeben, ohne seine Verbindung zu trennen, CLIENT UNBLOCK <client-id> [TIMEOUT|ERROR] verwenden. CLIENT UNPAUSE setzt nur Clients fort, die mit CLIENT PAUSE angehalten wurden.","danger":"caution"}}},{"id":"connection-count","title":"Verbindungsanzahl und Verbindungslimit anzeigen","aliases":["connections","max connections","connection limit","connection count","how many connections","is the connection pool full"],"engines":{"oracle":{"command":"SELECT COUNT(*) AS current_sessions FROM v$session WHERE type = 'USER';\nSELECT name, value FROM v$parameter WHERE name IN ('processes','sessions');","note":"Die Limits sind die Parameter processes/sessions. Zur Auslastung kann auch die View resource_limit geprüft werden.","danger":"safe"},"postgres":{"command":"SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';\nSHOW max_connections;","note":"Da pg_stat_activity auch Hintergrundprozesse enthält, werden nur Client-Verbindungen (backend_type = 'client backend') gezählt. Slots aus superuser_reserved_connections sind für Admins reserviert.","danger":"safe"},"mysql":{"command":"SHOW STATUS LIKE 'Threads_connected';\nSHOW VARIABLES LIKE 'max_connections';","note":"Der Status Max_used_connections zeigt den Spitzenwert.","danger":"safe"},"mssql":{"command":"SELECT COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process = 1;\nSELECT @@MAX_CONNECTIONS AS configured_max;","note":"@@MAX_CONNECTIONS ist die theoretische Obergrenze; das praktische Limit hängt von Speicher und Worker-Threads ab.","danger":"safe"},"mongodb":{"command":"db.serverStatus().connections","note":"Liefert die Felder current, available und totalCreated. Das Limit wird über net.maxIncomingConnections gesetzt.","danger":"safe"},"redis":{"command":"INFO clients\nCONFIG GET maxclients","note":"connected_clients ist die aktuelle Anzahl; maxclients die konfigurierte Obergrenze (Standard 10000).","danger":"safe"}}}]},"rosetta/storage.json":{"category":"Speicherplatz & Storage","categoryId":"storage","order":2,"concepts":[{"id":"database-size","title":"Gesamtgröße der Datenbank anzeigen","aliases":["database size","db size","database size","how much space does it use","disk usage","total size"],"engines":{"oracle":{"command":"SELECT ROUND(SUM(bytes)/1024/1024/1024, 2) AS total_gb\nFROM dba_data_files;","note":"Nur für belegte Segmente dba_segments summieren. Für TEMP dba_temp_files hinzunehmen.","danger":"safe"},"postgres":{"command":"SELECT pg_size_pretty(pg_database_size(current_database()));","note":"Für alle Datenbanken: SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY 2 DESC;","danger":"safe"},"mysql":{"command":"SELECT table_schema AS db,\n       ROUND(SUM(data_length + index_length)/1024/1024, 1) AS mb\nFROM information_schema.tables\nGROUP BY table_schema\nORDER BY mb DESC;","note":"Bei InnoDB ist dieser Wert eine Schätzung; für die exakte Dateigröße ibdata/die Dateien auf OS-Ebene prüfen.","danger":"safe"},"mssql":{"command":"EXEC sp_spaceused;\n-- All databases:\nSELECT DB_NAME(database_id) AS db,\n       CAST(SUM(size) * 8.0 / 1024 AS DECIMAL(10,1)) AS mb\nFROM sys.master_files\nGROUP BY database_id;","note":"Die Spalte size ist in 8-KB-Seiten angegeben; * 8 / 1024 rechnet in MB um.","danger":"safe"},"mongodb":{"command":"db.stats(1024*1024)\n// returns the dataSize, storageSize, indexSize fields in MB","note":"storageSize ist die komprimierte Größe auf der Platte; dataSize die unkomprimierte logische Größe.","danger":"safe"},"redis":{"command":"INFO memory\n# the used_memory_human line gives the total size in memory\nDBSIZE  # number of keys","note":"Redis ist speicherbasiert; „Größe“ bedeutet RAM-Verbrauch. Die Größe der RDB/AOF-Datei ist der persistierte Stand auf der Platte.","danger":"safe"}}},{"id":"largest-tables","title":"Größte Tabellen / Collections auflisten","aliases":["largest tables","biggest tables","top tables by size","largest table","tables using the most space","bigkeys"],"engines":{"oracle":{"command":"SELECT segment_name, segment_type,\n       ROUND(bytes/1024/1024, 1) AS mb\nFROM dba_segments\nWHERE segment_type IN ('TABLE','TABLE PARTITION')\nORDER BY bytes DESC\nFETCH FIRST 10 ROWS ONLY;","note":"Den Filter segment_type entfernen, um auch Indizes zu sehen.","danger":"safe"},"postgres":{"command":"SELECT relname AS table,\n       pg_size_pretty(pg_total_relation_size(relid)) AS total\nFROM pg_catalog.pg_statio_user_tables\nORDER BY pg_total_relation_size(relid) DESC\nLIMIT 10;","note":"pg_total_relation_size ist die Summe aus Tabelle + Indizes + TOAST; nur für die Tabelle pg_relation_size verwenden.","danger":"safe"},"mysql":{"command":"SELECT table_name,\n       ROUND((data_length + index_length)/1024/1024, 1) AS mb\nFROM information_schema.tables\nWHERE table_schema = DATABASE()\nORDER BY (data_length + index_length) DESC\nLIMIT 10;","note":"Für ein bestimmtes Schema WHERE table_schema = 'name' verwenden.","danger":"safe"},"mssql":{"command":"SELECT TOP 10 OBJECT_NAME(object_id) AS table_name,\n       SUM(reserved_page_count) * 8 / 1024 AS mb\nFROM sys.dm_db_partition_stats\nGROUP BY object_id\nORDER BY mb DESC;","note":"reserved_page_count umfasst alle reservierten Seiten (Daten + Indizes).","danger":"safe"},"mongodb":{"command":"db.getCollectionNames()\n  .map(c => ({ name: c, mb: +(db[c].stats().storageSize/1048576).toFixed(1) }))\n  .sort((a, b) => b.mb - a.mb)\n  .slice(0, 10)","note":"storageSize ist die komprimierte Größe auf der Platte. stats() ist auch bei großen Collections günstig.","danger":"safe"},"redis":{"command":"redis-cli --bigkeys\n# To rank by memory (bytes): redis-cli --memkeys","note":"Beide durchsuchen den gesamten Keyspace mit SCAN (sie blockieren den Server nicht, erzeugen aber Last); der Unterschied ist die Metrik: --bigkeys meldet die größten Keys je Typ nach Elementanzahl/Länge, --memkeys nach Speicherverbrauch über MEMORY USAGE. Für einen einzelnen Key MEMORY USAGE <key> verwenden.","danger":"caution"}}},{"id":"free-space","title":"Freien Speicher / Belegungsgrad prüfen","aliases":["free space","disk full","free space","usage","space left","tablespace usage","disk is full"],"engines":{"oracle":{"command":"SELECT df.tablespace_name,\n       ROUND(SUM(df.bytes)/1024/1024) AS total_mb,\n       ROUND(NVL(fs.free, 0)/1024/1024) AS free_mb,\n       ROUND((1 - NVL(fs.free,0)/SUM(df.bytes)) * 100, 1) AS pct_used\nFROM dba_data_files df\nLEFT JOIN (SELECT tablespace_name, SUM(bytes) free\n           FROM dba_free_space GROUP BY tablespace_name) fs\n  ON df.tablespace_name = fs.tablespace_name\nGROUP BY df.tablespace_name, fs.free;","note":"Bei Dateien mit AUTOEXTEND ist maxbytes das tatsächliche Limit; für MAXSIZE dba_data_files.maxbytes prüfen.","danger":"safe"},"postgres":{"command":"-- Datafile usage is at the OS level (df -h). Tablespace sizes from inside the DB:\nSELECT spcname, pg_size_pretty(pg_tablespace_size(spcname))\nFROM pg_tablespace;","note":"PostgreSQL verlässt sich beim freien Plattenplatz auf das OS; Monitoring auf Betriebssystemebene (df) ist für kritische Schwellenwerte unverzichtbar.","danger":"safe"},"mysql":{"command":"SELECT table_schema,\n       ROUND(SUM(data_free)/1024/1024, 1) AS reclaimable_mb\nFROM information_schema.tables\nGROUP BY table_schema;","note":"data_free ist rückgewinnbarer (fragmentierter) Platz innerhalb der Tabelle; freier Plattenplatz liegt auf OS-Ebene.","danger":"safe"},"mssql":{"command":"SELECT name AS file_name,\n       size/128.0 AS size_mb,\n       size/128.0 - CAST(FILEPROPERTY(name,'SpaceUsed') AS int)/128.0 AS free_mb\nFROM sys.database_files;","note":"SpaceUsed ist in Seiten angegeben; geteilt durch 128 ergibt MB (8-KB-Seiten).","danger":"safe"},"mongodb":{"command":"var s = db.stats();\n({ fsTotalGB: +(s.fsTotalSize/1073741824).toFixed(1),\n   fsUsedGB:  +(s.fsUsedSize/1073741824).toFixed(1) })","note":"Bei WiredTiger liefern fsTotalSize / fsUsedSize die Gesamt- und belegte Größe der Platte auf OS-Ebene.","danger":"safe"},"redis":{"command":"INFO memory\nCONFIG GET maxmemory\n# Usage = used_memory / maxmemory","note":"Ist maxmemory 0, gibt es kein Limit, und wenn der RAM voll ist, greift der OOM Killer des OS ein. maxmemory-policy bestimmt das Eviction-Verhalten.","danger":"safe"}}},{"id":"row-count","title":"Zeilen- / Dokumentanzahl ermitteln (schnell)","aliases":["row count","count rows","row count","how many records","document count","how many rows"],"engines":{"oracle":{"command":"-- Statistics-based (fast, estimated):\nSELECT num_rows FROM dba_tables WHERE table_name = 'CUSTOMER';\n-- Exact: SELECT COUNT(*) FROM customer;","note":"num_rows spiegelt den letzten ANALYZE/DBMS_STATS-Lauf wider; für einen exakten Wert COUNT(*) verwenden.","danger":"safe"},"postgres":{"command":"-- Estimated (instant):\nSELECT reltuples::bigint FROM pg_class WHERE relname = 'customer';\n-- Exact: SELECT count(*) FROM customer;","note":"reltuples ist eine Schätzung zum Stand des letzten ANALYZE/VACUUM; ein vollständiges COUNT ist bei großen Tabellen teuer.","danger":"safe"},"mysql":{"command":"-- Estimated (InnoDB):\nSELECT table_rows FROM information_schema.tables\nWHERE table_name = 'customer';\n-- Exact: SELECT COUNT(*) FROM customer;","note":"Bei InnoDB ist table_rows eine Schätzung und kann bis zu 50% abweichen.","danger":"safe"},"mssql":{"command":"SELECT SUM(row_count) AS rows\nFROM sys.dm_db_partition_stats\nWHERE object_id = OBJECT_ID('dbo.customer')\n  AND index_id IN (0, 1);","note":"index_id 0 (Heap) oder 1 (Clustered) liefert die Zeilenanzahl; viel schneller als ein COUNT(*)-Scan.","danger":"safe"},"mongodb":{"command":"// Fast (from metadata):\ndb.customer.estimatedDocumentCount()\n// Exact (can count with a filter): db.customer.countDocuments({})","note":"estimatedDocumentCount liest Metadaten (sofort); countDocuments führt einen echten Scan aus.","danger":"safe"},"redis":{"command":"DBSIZE\n# Specific pattern: count roughly with SCAN (do not use KEYS * in production)","note":"DBSIZE liefert die Gesamtzahl der Keys in der ausgewählten Datenbank in O(1).","danger":"safe"}}}]},"rosetta/performance.json":{"category":"Performance","categoryId":"performance","order":3,"concepts":[{"id":"top-slow-queries","title":"Langsamste / teuerste Abfragen finden","aliases":["slow queries","top queries","slowest queries","top resource consumers","expensive queries","slowlog"],"engines":{"oracle":{"command":"SELECT sql_id,\n       ROUND(elapsed_time/1e6, 1) AS elapsed_sec,\n       executions,\n       ROUND(elapsed_time/1e6/NULLIF(executions,0), 3) AS sec_per_exec,\n       sql_text\nFROM v$sql\nORDER BY elapsed_time DESC\nFETCH FIRST 10 ROWS ONLY;","note":"Für historische Analysen AWR verwenden: dba_hist_sqlstat. Mit der sql_id zeigt DBMS_XPLAN.DISPLAY_CURSOR den Plan.","danger":"safe"},"postgres":{"command":"SELECT calls,\n       ROUND(total_exec_time::numeric, 1) AS total_ms,\n       ROUND(mean_exec_time::numeric, 2) AS mean_ms,\n       query\nFROM pg_stat_statements\nORDER BY total_exec_time DESC\nLIMIT 10;","note":"Erfordert die Extension pg_stat_statements (shared_preload_libraries). Zurücksetzen mit pg_stat_statements_reset().","danger":"safe"},"mysql":{"command":"SELECT digest_text,\n       count_star AS calls,\n       ROUND(sum_timer_wait/1e12, 2) AS total_sec,\n       ROUND(avg_timer_wait/1e9, 2) AS avg_ms\nFROM performance_schema.events_statements_summary_by_digest\nORDER BY sum_timer_wait DESC\nLIMIT 10;","note":"Alternative: das Slow Query Log mit slow_query_log = ON und long_query_time.","danger":"safe"},"mssql":{"command":"SELECT TOP 10\n       qs.execution_count,\n       qs.total_elapsed_time/1000 AS total_ms,\n       qs.total_elapsed_time/qs.execution_count/1000 AS avg_ms,\n       SUBSTRING(t.text, 1, 200) AS sql_text\nFROM sys.dm_exec_query_stats qs\nCROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t\nORDER BY qs.total_elapsed_time DESC;","note":"Ist Query Store aktiviert, sys.query_store_runtime_stats für umfangreichere historische Analysen verwenden.","danger":"safe"},"mongodb":{"command":"// Enable the profiler for operations over 100ms:\ndb.setProfilingLevel(1, { slowms: 100 })\n// Then read the slowest ones:\ndb.system.profile.find().sort({ millis: -1 }).limit(10)","note":"Der Profiler erzeugt Schreib-Overhead; nach der Diagnose mit db.setProfilingLevel(0) abschalten.","danger":"caution"},"redis":{"command":"SLOWLOG GET 10\n# Set the threshold (microseconds): CONFIG SET slowlog-log-slower-than 10000","note":"slowlog-log-slower-than ist in Mikrosekunden angegeben (10000 = 10 ms). Leeren mit SLOWLOG RESET.","danger":"safe"}}},{"id":"explain-plan","title":"Ausführungsplan einer Abfrage anzeigen (EXPLAIN)","aliases":["explain","execution plan","query plan","explain plan","show plan","explain analyze","why slow"],"engines":{"oracle":{"command":"EXPLAIN PLAN FOR\nSELECT * FROM customer WHERE city = 'Ankara';\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);\n-- For the actual executed plan:\n-- SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));","note":"EXPLAIN PLAN liefert den geschätzten Plan; für tatsächliche Zeilenzahlen DISPLAY_CURSOR + STATISTICS_LEVEL=ALL verwenden.","danger":"safe"},"postgres":{"command":"EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)\nSELECT * FROM customer WHERE city = 'Ankara';","note":"ANALYZE führt die Abfrage TATSÄCHLICH aus (Vorsicht bei INSERT/UPDATE/DELETE; in einer Transaktion testen und ROLLBACK ausführen).","danger":"caution"},"mysql":{"command":"EXPLAIN ANALYZE\nSELECT * FROM customer WHERE city = 'Ankara';\n-- Estimate only (does not execute): EXPLAIN FORMAT=JSON SELECT ...;","note":"EXPLAIN ANALYZE (MySQL 8.0.18+) führt die Abfrage aus; FORMAT=JSON liefert Details ohne Ausführung.","danger":"caution"},"mssql":{"command":"SET STATISTICS PROFILE ON;\nSELECT * FROM dbo.customer WHERE city = 'Ankara';\nSET STATISTICS PROFILE OFF;\n-- Estimated plan only (does not execute):\n-- SET SHOWPLAN_XML ON;","note":"In SSMS zeigt „Include Actual Execution Plan“ (Ctrl+M) den grafischen Plan.","danger":"caution"},"mongodb":{"command":"db.customer.find({ city: \"Ankara\" }).explain(\"executionStats\")","note":"executionStats zeigt die tatsächliche Anzahl zurückgegebener Dokumente und die Indexnutzung (IXSCAN vs. COLLSCAN).","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no query planner.\n# The complexity of each command is documented (e.g. GET O(1), SORT O(N+M*log(M))).","note":"Die Performance ergibt sich aus der Befehlskomplexität und der Größe der Keys/Datenstrukturen; mit SLOWLOG messen.","danger":"safe"}}},{"id":"unused-indexes","title":"Ungenutzte Indizes erkennen","aliases":["unused indexes","unused index","redundant index","index usage","drop index candidate"],"engines":{"oracle":{"command":"-- Start monitoring:\nALTER INDEX customer_city_idx MONITORING USAGE;\n-- Check after a while (12c+, all schemas):\nSELECT owner, index_name, used FROM dba_object_usage;","note":"Bei USED = NO wurde der Index im Überwachungszeitraum nicht verwendet. Überwachung mit NOMONITORING USAGE abschalten. Die alte v$object_usage zeigt nur die Indizes des Session-Users und ist seit 12c deprecated. Ab 12.2 wird die Indexnutzung automatisch in DBA_INDEX_USAGE erfasst.","danger":"safe"},"postgres":{"command":"SELECT relname AS table, indexrelname AS index, idx_scan\nFROM pg_stat_user_indexes\nWHERE idx_scan = 0\nORDER BY relname;","note":"idx_scan = 0 bedeutet, dass der Index seit dem letzten Zurücksetzen der Statistiken nicht verwendet wurde; UNIQUE/PK-Indizes nicht löschen.","danger":"safe"},"mysql":{"command":"SELECT * FROM sys.schema_unused_indexes;","note":"performance_schema muss aktiviert sein und der Server eine Weile gelaufen sein; UNIQUE-Constraints sind ausgenommen.","danger":"safe"},"mssql":{"command":"SELECT OBJECT_NAME(i.object_id) AS table_name, i.name AS index_name\nFROM sys.indexes i\nLEFT JOIN sys.dm_db_index_usage_stats s\n  ON i.object_id = s.object_id AND i.index_id = s.index_id\n  AND s.database_id = DB_ID()\nWHERE i.type_desc = 'NONCLUSTERED'\n  AND COALESCE(s.user_seeks + s.user_scans + s.user_lookups, 0) = 0;","note":"usage_stats wird beim Neustart des Servers zurückgesetzt; ausreichend langen Beobachtungszeitraum einplanen.","danger":"safe"},"mongodb":{"command":"db.customer.aggregate([{ $indexStats: {} }])\n// Indexes with low/0 accesses.ops are candidates","note":"accesses.ops zählt Zugriffe seit dem Zeitstempel accesses.since; der _id-Index kann nicht gelöscht werden.","danger":"safe"},"redis":{"command":"# Not applicable: core Redis has no secondary indexes.\n# If the RediSearch module is used, check statistics with FT.INFO <index>.","note":"Da das Datenmodell Key-Value ist, gibt es kein klassisches Indexkonzept.","danger":"safe"}}},{"id":"cache-hit-ratio","title":"Cache-Trefferquote messen","aliases":["cache hit ratio","buffer cache","cache hit","hit ratio","buffer pool hit"],"engines":{"oracle":{"command":"SELECT metric_name, ROUND(value, 2) AS pct\nFROM v$sysmetric\nWHERE metric_name = 'Buffer Cache Hit Ratio'\n  AND intsize_csec = (SELECT MAX(intsize_csec) FROM v$sysmetric);","note":"Eine niedrige Quote ist nicht immer schlecht; im Kontext des Workloads bewerten, nicht isoliert.","danger":"safe"},"postgres":{"command":"SELECT ROUND(sum(blks_hit) * 100.0 / NULLIF(sum(blks_hit) + sum(blks_read), 0), 2) AS cache_hit_pct\nFROM pg_stat_database;","note":"Ziel ist meist 99%+. Ist sie niedrig, shared_buffers und den OS-Cache prüfen.","danger":"safe"},"mysql":{"command":"SELECT ROUND((1 - (\n    (SELECT variable_value FROM performance_schema.global_status WHERE variable_name='Innodb_buffer_pool_reads') /\n    (SELECT variable_value FROM performance_schema.global_status WHERE variable_name='Innodb_buffer_pool_read_requests')\n  )) * 100, 2) AS buffer_pool_hit_pct;","note":"reads sind Lesezugriffe von der Platte, read_requests alle Lesezugriffe. Ist die Quote niedrig, innodb_buffer_pool_size erhöhen.","danger":"safe"},"mssql":{"command":"SELECT ROUND(\n  100.0 * (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Buffer cache hit ratio')\n        / NULLIF((SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Buffer cache hit ratio base'), 0), 2) AS pct;","note":"Auf modernen SQL-Server-Versionen ist Page Life Expectancy (PLE) ein aussagekräftigerer Indikator für Speicherdruck.","danger":"safe"},"mongodb":{"command":"var c = db.serverStatus().wiredTiger.cache;\n({ configuredMB: +(c['maximum bytes configured']/1048576).toFixed(0),\n   inCacheMB:    +(c['bytes currently in the cache']/1048576).toFixed(0),\n   readIntoCache: c['pages read into cache'],\n   evictedPages:  c['unmodified pages evicted'] })","note":"Hohe Werte bei „pages read into cache“ und Eviction deuten auf einen zu kleinen WiredTiger-Cache hin.","danger":"safe"},"redis":{"command":"INFO stats\n# keyspace_hits / (keyspace_hits + keyspace_misses) = hit ratio","note":"Eine dauerhaft niedrige Trefferquote zeigt, dass TTL-/Eviction-Einstellungen oder das Zugriffsmuster überprüft werden müssen.","danger":"safe"}}}]},"rosetta/backup.json":{"category":"Backup & Recovery","categoryId":"backup","order":4,"concepts":[{"id":"logical-dump","title":"Logisches Backup (Dump) erstellen","aliases":["backup","dump","export","take backup","logical dump","logical backup","pg_dump","mysqldump","mongodump"],"engines":{"oracle":{"command":"expdp system/password DIRECTORY=dpump_dir \\\n  SCHEMAS=hr DUMPFILE=hr_%U.dmp LOGFILE=hr_exp.log PARALLEL=4","note":"Verwendet Data Pump. Das DIRECTORY-Objekt muss vorher mit CREATE DIRECTORY angelegt werden. Für physische Backups RMAN verwenden.","danger":"safe"},"postgres":{"command":"pg_dump -U postgres -h localhost -Fc -f db.dump mydb\n# -Fc = compressed custom format (restored with pg_restore)","note":"-Fc ermöglicht flexible Restores (einzelne Tabellen/parallel). Für den gesamten Cluster (inklusive Rollen) pg_dumpall verwenden.","danger":"safe"},"mysql":{"command":"mysqldump -u root -p --single-transaction \\\n  --routines --triggers --events mydb > db.sql","note":"--single-transaction liefert bei InnoDB ein sperrfreies, konsistentes Backup. Parallele Optionen für große Datenmengen: mydumper/myloader oder MySQL Shell util.dumpInstance()/dumpSchemas() (mysqlpump wurde in 8.0.34 deprecated und in 8.4 entfernt).","danger":"safe"},"mssql":{"command":"BACKUP DATABASE [MyDB]\nTO DISK = N'C:\\backup\\MyDB_full.bak'\nWITH COMPRESSION, CHECKSUM, INIT;","note":"INIT überschreibt die vorhandene Datei; zum Anhängen NOINIT verwenden. Für Log-Backups BACKUP LOG verwenden (im Recovery-Modell FULL).","danger":"safe"},"mongodb":{"command":"mongodump --uri=\"mongodb://localhost:27017\" \\\n  --archive=db.archive.gz --gzip --db=mydb","note":"Bei einem Sharded Cluster für Konsistenz über mongos mit gestopptem Balancer erstellen.","danger":"safe"},"redis":{"command":"BGSAVE\n# Takes an RDB snapshot in the background (dump.rdb).\n# Non-blocking copy: redis-cli --rdb /backup/dump.rdb","note":"BGSAVE läuft per fork im Hintergrund; SAVE blockiert den Server (nicht in Produktion verwenden). Mit aktiviertem AOF gehen weniger Daten verloren.","danger":"safe"}}},{"id":"restore-dump","title":"Aus Backup wiederherstellen","aliases":["restore","import","restore backup","restore from backup","recover","pg_restore","mongorestore"],"engines":{"oracle":{"command":"impdp system/password DIRECTORY=dpump_dir \\\n  SCHEMAS=hr DUMPFILE=hr_%U.dmp LOGFILE=hr_imp.log","note":"REMAP_SCHEMA verwenden, um in ein anderes Schema zu laden, REMAP_TABLESPACE für einen anderen Tablespace.","danger":"danger"},"postgres":{"command":"# custom format:\npg_restore -U postgres -d target_db --clean --if-exists db.dump\n# plain SQL: psql -U postgres -d target_db -f db.sql","note":"--clean löscht vorhandene Objekte. Für einen parallelen Restore -j <n> verwenden.","danger":"danger"},"mysql":{"command":"mysql -u root -p target_db < db.sql","note":"Die Zieldatenbank muss bereits existieren (CREATE DATABASE). Bei großen Dateien ist der Befehl source vorzuziehen.","danger":"danger"},"mssql":{"command":"RESTORE DATABASE [MyDB]\nFROM DISK = N'C:\\backup\\MyDB_full.bak'\nWITH REPLACE, RECOVERY;","note":"Für Point-in-Time das Full Backup WITH NORECOVERY wiederherstellen, dann Log-Backups mit STOPAT einspielen.","danger":"danger"},"mongodb":{"command":"mongorestore --uri=\"mongodb://localhost:27017\" \\\n  --archive=db.archive.gz --gzip --drop","note":"--drop löscht die Ziel-Collections vor dem Restore. Für einen selektiven Restore --nsInclude verwenden.","danger":"danger"},"redis":{"command":"# 1) Stop Redis   2) Copy dump.rdb to the 'dir' location\n# 3) Start Redis (the RDB is loaded automatically at startup)\nredis-cli CONFIG GET dir   # find the target directory","note":"Mit aktiviertem AOF wird statt der RDB das AOF geladen; um aus der RDB wiederherzustellen, mit appendonly no starten oder das AOF neu aufbauen.","danger":"danger"}}},{"id":"last-backup-status","title":"Status / Zeitpunkt des letzten Backups prüfen","aliases":["last backup","backup status","when was the last backup","backup state","backup history","rpo"],"engines":{"oracle":{"command":"SELECT session_key, input_type, status,\n       start_time, end_time,\n       output_bytes_display\nFROM v$rman_backup_job_details\nORDER BY start_time DESC\nFETCH FIRST 10 ROWS ONLY;","note":"Die RMAN-Historie lässt sich auch mit RMAN> LIST BACKUP SUMMARY anzeigen.","danger":"safe"},"postgres":{"command":"-- WAL archiving health (for physical backup/PITR):\nSELECT last_archived_wal, last_archived_time,\n       failed_count, last_failed_time\nFROM pg_stat_archiver;","note":"Logische Backups (pg_dump) werden nicht in der DB protokolliert; die Logs des Backup-Tools/Jobs prüfen.","danger":"safe"},"mysql":{"command":"-- There is no built-in backup record; check the backup tool's log.\n-- For the binlog-based PITR window:\nSHOW BINARY LOGS;","note":"SHOW BINARY LOGS zeigt die verfügbaren Binlog-Dateien (das PITR-Fenster); die Backup-Historie steht im Log des Tools (Xtrabackup usw.).","danger":"safe"},"mssql":{"command":"SELECT database_name,\n       MAX(backup_finish_date) AS last_backup,\n       CASE type WHEN 'D' THEN 'Full' WHEN 'I' THEN 'Diff'\n                 WHEN 'L' THEN 'Log' END AS backup_type\nFROM msdb.dbo.backupset\nGROUP BY database_name, type\nORDER BY database_name;","note":"msdb.dbo.backupset protokolliert jedes von SQL Server erstellte Backup.","danger":"safe"},"mongodb":{"command":"// There is no built-in backup record. PITR window = oplog duration:\nrs.printReplicationInfo()  // oplog first/last event times","note":"Das Oplog-Fenster bestimmt, wie weit zurück PITR aus dem Replica Set möglich ist; die Backup-Historie steht im Log des Tools.","danger":"safe"},"redis":{"command":"INFO persistence\n# check the rdb_last_save_time (epoch), rdb_last_bgsave_status,\n# aof_last_bgrewrite_status lines","note":"rdb_last_save_time liefert den Zeitpunkt des letzten erfolgreichen RDB-Saves (Unix-Epoch); rdb_changes_since_last_save zeigt, wie viele Schreibvorgänge noch nicht gesichert sind.","danger":"safe"}}}]},"rosetta/security.json":{"category":"Benutzer & Berechtigungen","categoryId":"security","order":5,"concepts":[{"id":"list-users","title":"Benutzer / Rollen auflisten","aliases":["list users","show users","list users","roles","who has access","accounts"],"engines":{"oracle":{"command":"SELECT username, account_status, default_tablespace, created\nFROM dba_users\nORDER BY username;","note":"account_status zeigt die Zustände LOCKED/EXPIRED. Für Rollen dba_roles verwenden.","danger":"safe"},"postgres":{"command":"SELECT rolname, rolsuper, rolcanlogin, rolvaliduntil\nFROM pg_roles\nORDER BY rolname;\n-- Shortcut (psql): \\du","note":"In PostgreSQL ist ein Benutzer eine Rolle mit dem Attribut LOGIN; rolcanlogin unterscheidet sie.","danger":"safe"},"mysql":{"command":"SELECT user, host, account_locked, password_expired\nFROM mysql.user\nORDER BY user;","note":"In MySQL ist eine Identität ein Paar aus User + Host ('app'@'%' und 'app'@'localhost' sind verschieden).","danger":"safe"},"mssql":{"command":"-- Server logins:\nSELECT name, type_desc, is_disabled FROM sys.server_principals\nWHERE type IN ('S','U','G');\n-- Database users:\nSELECT name, type_desc FROM sys.database_principals\nWHERE type IN ('S','U','G');","note":"Ein Login gilt auf Serverebene, ein User auf Datenbankebene; beide werden über die SID zugeordnet.","danger":"safe"},"mongodb":{"command":"// Users in the current database:\ndb.getUsers()\n// All users in the system: use admin; db.system.users.find({}, {user:1, db:1, roles:1})","note":"Benutzer sind in der Datenbank definiert, in der sie angelegt wurden (authenticationDatabase).","danger":"safe"},"redis":{"command":"ACL LIST\n# Names only: ACL USERS\n# Your own identity: ACL WHOAMI","note":"Das ACL-System ab Redis 6. Davor gab es nur einen Benutzer „default“ und requirepass.","danger":"safe"}}},{"id":"create-user-grant","title":"Benutzer anlegen und Berechtigungen vergeben","aliases":["create user","grant","add user","create user","grant privilege","grant permission","new user"],"engines":{"oracle":{"command":"CREATE USER app IDENTIFIED BY \"Strong#Pass1\";\nGRANT CREATE SESSION TO app;\nGRANT SELECT, INSERT, UPDATE ON hr.employee TO app;","note":"Ohne CREATE SESSION kann sich der Benutzer nicht verbinden. Für eine Quota: ALTER USER app QUOTA 100M ON users;","danger":"caution"},"postgres":{"command":"CREATE ROLE app LOGIN PASSWORD 'Strong#Pass1';\nGRANT CONNECT ON DATABASE mydb TO app;\nGRANT USAGE ON SCHEMA public TO app;\nGRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app;","note":"Für künftig angelegte Tabellen: ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app;","danger":"caution"},"mysql":{"command":"CREATE USER 'app'@'%' IDENTIFIED BY 'Strong#Pass1';\nGRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app'@'%';","note":"'%' erlaubt Verbindungen von jedem Host; aus Sicherheitsgründen nach Möglichkeit eine bestimmte IP/einen Host angeben. CREATE USER/GRANT wirken sofort; FLUSH PRIVILEGES ist nur nötig, wenn DML direkt auf die Grant-Tabellen ausgeführt wurde.","danger":"caution"},"mssql":{"command":"CREATE LOGIN app WITH PASSWORD = 'Strong#Pass1';\nUSE mydb;\nCREATE USER app FOR LOGIN app;\nGRANT SELECT, INSERT, UPDATE ON SCHEMA::dbo TO app;","note":"Ein Login gilt serverweit; ein User wird in jeder Datenbank separat angelegt. Für eine Rolle: ALTER ROLE db_datareader ADD MEMBER app;","danger":"caution"},"mongodb":{"command":"db.createUser({\n  user: \"app\",\n  pwd: \"Strong#Pass1\",\n  roles: [ { role: \"readWrite\", db: \"mydb\" } ]\n})","note":"Die DB, in der der Benutzer angelegt wird, wird zu seiner Auth-DB. Eingebaute Rollen: read, readWrite, dbAdmin, clusterAdmin...","danger":"caution"},"redis":{"command":"ACL SETUSER app on >Strong#Pass1 ~app:* +@read +@write\n# To persist: ACL SAVE if an aclfile is defined, otherwise CONFIG REWRITE (users are written to redis.conf)","note":"on = aktiviert, >password fügt ein Passwort hinzu, ~app:* ist das Key-Muster, +@read/+@write sind Befehlskategorien. Ohne Persistierung geht es beim Neustart verloren: ACL SAVE funktioniert nur mit konfiguriertem aclfile und liefert sonst einen Fehler; in dem Fall CONFIG REWRITE verwenden.","danger":"caution"}}},{"id":"show-privileges","title":"Berechtigungen eines Benutzers anzeigen","aliases":["show grants","privileges","show privileges","who can access what","permissions","roles of user"],"engines":{"oracle":{"command":"SELECT privilege FROM dba_sys_privs WHERE grantee = 'APP'\nUNION ALL SELECT granted_role FROM dba_role_privs WHERE grantee = 'APP'\nUNION ALL SELECT privilege||' ON '||owner||'.'||table_name\n          FROM dba_tab_privs WHERE grantee = 'APP';","note":"Der Grantee wird in Großbuchstaben gespeichert. Für indirekte, über Rollen vergebene Berechtigungen auch role_role_privs durchlaufen.","danger":"safe"},"postgres":{"command":"SELECT grantee, table_schema, table_name, privilege_type\nFROM information_schema.role_table_grants\nWHERE grantee = 'app';\n-- Role memberships (direct): \\drg app (PG16+; earlier \\du app)","note":"Rollenmitgliedschaften (Vererbung) werden in pg_auth_members gespeichert; \\du/\\drg zeigen nur direkte Mitgliedschaften, für indirekte Mitgliedschaft pg_has_role('app', '<role>', 'MEMBER') verwenden.","danger":"safe"},"mysql":{"command":"SHOW GRANTS FOR 'app'@'%';","note":"Für Rollen (MySQL 8) zuerst SET ROLE ALL ausführen oder SHOW GRANTS FOR 'app'@'%' USING 'role_name'; verwenden.","danger":"safe"},"mssql":{"command":"SELECT dp.name AS principal, p.permission_name,\n       p.state_desc, o.name AS object\nFROM sys.database_permissions p\nJOIN sys.database_principals dp ON p.grantee_principal_id = dp.principal_id\nLEFT JOIN sys.objects o ON p.major_id = o.object_id\nWHERE dp.name = 'app';","note":"Für über Rollen vergebene Berechtigungen auch die Mitgliedschaften in sys.database_role_members prüfen.","danger":"safe"},"mongodb":{"command":"db.getUser(\"app\", { showPrivileges: true, showCredentials: false })","note":"showPrivileges listet auch die aufgelösten (effektiven) Berechtigungen der Rollen auf.","danger":"safe"},"redis":{"command":"ACL GETUSER app","note":"Liefert die erlaubten Befehle, Key-Muster (~), Pub/Sub-Channels (&) und Passwort-Hashes.","danger":"safe"}}},{"id":"change-password","title":"Passwort eines Benutzers ändern","aliases":["change password","reset password","change password","reset password","alter password"],"engines":{"oracle":{"command":"ALTER USER app IDENTIFIED BY \"New#Pass2\";","note":"Gesperrtes Konto entsperren: ALTER USER app ACCOUNT UNLOCK; Ist das Passwort abgelaufen, setzt derselbe Befehl es zurück.","danger":"caution"},"postgres":{"command":"ALTER ROLE app WITH PASSWORD 'New#Pass2';","note":"Damit das Passwort nicht in den Logs landet, hasht der Meta-Befehl \\password app (psql) das Passwort auf Client-Seite.","danger":"caution"},"mysql":{"command":"ALTER USER 'app'@'%' IDENTIFIED BY 'New#Pass2';","note":"Auf den richtigen Host achten; 'app'@'localhost' ist ein separates Konto. ALTER USER wirkt sofort; FLUSH PRIVILEGES ist nicht nötig.","danger":"caution"},"mssql":{"command":"ALTER LOGIN app WITH PASSWORD = 'New#Pass2';\n-- For a user changing their own password: add OLD_PASSWORD","note":"MUST_CHANGE kann eine Änderung beim ersten Login erzwingen: WITH PASSWORD='...' MUST_CHANGE, CHECK_POLICY=ON.","danger":"caution"},"mongodb":{"command":"db.changeUserPassword(\"app\", \"New#Pass2\")","note":"Sicherstellen, dass in die Auth-Datenbank des Benutzers gewechselt wurde (use mydb).","danger":"caution"},"redis":{"command":"# Clear old passwords and set the new one:\nACL SETUSER app resetpass >New#Pass2\n# Persistence: ACL SAVE if an aclfile is defined, otherwise CONFIG REWRITE","note":"resetpass entfernt alle vorhandenen Passwörter; > fügt nur eines hinzu. Für Persistenz ACL SAVE ausführen, wenn ein aclfile verwendet wird, oder CONFIG REWRITE, wenn die Benutzer in redis.conf definiert sind (ACL SAVE liefert ohne aclfile einen Fehler).","danger":"caution"}}}]},"rosetta/maintenance.json":{"category":"Wartung & Administration","categoryId":"maintenance","order":6,"concepts":[{"id":"update-statistics","title":"Statistiken aktualisieren (für den Optimizer)","aliases":["update statistics","analyze","update stats","gather stats","optimizer stats","analyze for planner"],"engines":{"oracle":{"command":"EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEE');\n-- Whole schema: EXEC DBMS_STATS.GATHER_SCHEMA_STATS('HR');","note":"Der AUTO-Task sammelt sie meist nachts; bei Problemen manuell sammeln. ESTIMATE_PERCENT=>AUTO_SAMPLE_SIZE wird empfohlen.","danger":"safe"},"postgres":{"command":"ANALYZE customer;\n-- Both cleanup and analyze: VACUUM ANALYZE customer;","note":"Autovacuum erledigt das automatisch; ein manuelles ANALYZE nach einem plötzlichen Datenimport sorgt für genaue Pläne.","danger":"safe"},"mysql":{"command":"ANALYZE TABLE customer;","note":"Setzt bei InnoDB kurz eine Lesesperre; bei großen Tabellen in Zeiten geringer Aktivität ausführen.","danger":"safe"},"mssql":{"command":"UPDATE STATISTICS dbo.customer WITH FULLSCAN;\n-- Whole database: EXEC sp_updatestats;","note":"FULLSCAN ist am genauesten, aber am teuersten; bei großen Tabellen kann Sampling (SAMPLE) vorzuziehen sein.","danger":"safe"},"mongodb":{"command":"// There are no manual statistics; the planner measures at runtime.\n// To clear a stale plan cache:\ndb.customer.getPlanCache().clear()","note":"Statt Kostenschätzung führt MongoDB echte Testläufe aus (Plan Racing); manuelle Statistiken sind nicht nötig.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no query optimizer / statistics.","note":"Die Befehlskomplexität ist fest; die Performance hängt von der Wahl der Datenstruktur ab.","danger":"safe"}}},{"id":"rebuild-index","title":"Index neu aufbauen / reorganisieren","aliases":["rebuild index","reindex","refresh index","reorganize","index maintenance","defrag index"],"engines":{"oracle":{"command":"ALTER INDEX customer_city_idx REBUILD ONLINE;","note":"ONLINE baut ohne Sperren der Tabelle neu auf (Enterprise Edition). Zum Verschieben in einen anderen Tablespace: ... REBUILD TABLESPACE new_ts.","danger":"caution"},"postgres":{"command":"REINDEX INDEX CONCURRENTLY customer_city_idx;\n-- Whole table: REINDEX TABLE CONCURRENTLY customer;","note":"CONCURRENTLY (PG 12+) baut neu auf, ohne Schreibzugriffe auf die Tabelle zu blockieren; langsamer, aber sicher.","danger":"caution"},"mysql":{"command":"-- Rebuild InnoDB indexes + table:\nALTER TABLE customer ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE;\n-- Alternative: OPTIMIZE TABLE customer;","note":"LOCK=NONE erlaubt gleichzeitige Schreibzugriffe; INPLACE ist nicht bei jedem ALTER möglich.","danger":"caution"},"mssql":{"command":"-- Rebuild for high fragmentation:\nALTER INDEX ALL ON dbo.customer REBUILD WITH (ONLINE = ON);\n-- Lighter option for low fragmentation:\n-- ALTER INDEX ALL ON dbo.customer REORGANIZE;","note":"Übliche Schwelle: 5-30% Fragmentierung REORGANIZE, >30% REBUILD. ONLINE ist ein Enterprise-Feature.","danger":"caution"},"mongodb":{"command":"db.customer.reIndex()","note":"Baut alle Indizes im Vordergrund neu auf und sperrt die Collection. Ab 6.0 läuft es nur auf einem Standalone (auf Replica-Set-Mitgliedern nicht erlaubt); im Replica Set einen einzelnen Index mit dropIndex + createIndex Knoten für Knoten (rolling) neu aufbauen.","danger":"danger"},"redis":{"command":"# Not applicable (core). If RediSearch is used:\n# FT.ALTER / rebuilding is done per the module documentation.","note":"Das Key-Value-Modell kennt keine klassische Indexwartung.","danger":"safe"}}},{"id":"reclaim-space","title":"Freien Platz zurückgewinnen (shrink / vacuum / compact)","aliases":["reclaim space","vacuum","shrink","compact","free up space","clean up bloat","bloat"],"engines":{"oracle":{"command":"ALTER TABLE customer ENABLE ROW MOVEMENT;\nALTER TABLE customer SHRINK SPACE;","note":"SHRINK senkt die High Water Mark (HWM). Um auch Indizes zu verkleinern, SHRINK SPACE CASCADE verwenden.","danger":"caution"},"postgres":{"command":"-- Online, light locking (does not return space to the OS):\nVACUUM (VERBOSE, ANALYZE) customer;\n-- Return space to the OS (locks the table!):\n-- VACUUM FULL customer;","note":"VACUUM FULL setzt einen AccessExclusiveLock und schreibt die Tabelle neu; nicht zu Spitzenzeiten ausführen. Alternative: pg_repack.","danger":"caution"},"mysql":{"command":"OPTIMIZE TABLE customer;","note":"Baut bei InnoDB die Tabelle neu auf und gibt den data_free-Platz zurück; dauert bei großen Tabellen lange.","danger":"caution"},"mssql":{"command":"-- Generally NOT RECOMMENDED (causes fragmentation). If really needed:\nDBCC SHRINKFILE (N'MyDB_data', 1024);  -- target 1024 MB","note":"Das Verkleinern einer Datendatei verursacht Indexfragmentierung; danach die Indizes mit REBUILD neu aufbauen. Beim Log zuerst ein Backup erstellen.","danger":"danger"},"mongodb":{"command":"db.runCommand({ compact: \"customer\" })","note":"Versucht bei WiredTiger, freien Platz an das Betriebssystem zurückzugeben (nicht immer möglich). Ab 4.4 blockiert es keine Lese-/Schreibzugriffe, nur bestimmte DDL-Operationen auf derselben Collection; da es hohe IO-Last erzeugt, ist die Ausführung auf einem Secondary und in einem Wartungsfenster sicherer.","danger":"danger"},"redis":{"command":"MEMORY PURGE\n# Tries to return jemalloc fragmentation to the OS","note":"Die Einstellung activedefrag yes defragmentiert online und automatisch; MEMORY DOCTOR liefert Empfehlungen.","danger":"caution"}}},{"id":"version-uptime","title":"Version und Uptime anzeigen","aliases":["version","uptime","server version","how long has it been running","server version","days since startup"],"engines":{"oracle":{"command":"SELECT banner_full FROM v$version;\nSELECT startup_time, ROUND(SYSDATE - startup_time, 1) AS up_days\nFROM v$instance;","note":"v$instance liefert auch Instanzdetails wie status und host_name.","danger":"safe"},"postgres":{"command":"SELECT version();\nSELECT pg_postmaster_start_time() AS started,\n       now() - pg_postmaster_start_time() AS uptime;","note":"server_version_num eignet sich für numerische Vergleiche (z. B. 160002 = 16.2).","danger":"safe"},"mysql":{"command":"SELECT VERSION();\nSHOW GLOBAL STATUS LIKE 'Uptime';  -- seconds","note":"Uptime ist in Sekunden angegeben; geteilt durch 86400 ergibt Tage.","danger":"safe"},"mssql":{"command":"SELECT @@VERSION;\nSELECT sqlserver_start_time,\n       DATEDIFF(DAY, sqlserver_start_time, GETDATE()) AS up_days\nFROM sys.dm_os_sys_info;","note":"SERVERPROPERTY('ProductVersion') und ('Edition') liefern Details zu Version/Edition.","danger":"safe"},"mongodb":{"command":"db.version()\ndb.serverStatus().uptime   // seconds","note":"serverStatus() enthält auch Informationen zu host, process und connections.","danger":"safe"},"redis":{"command":"INFO server\n# redis_version and uptime_in_seconds / uptime_in_days lines","note":"INFO server liefert auch die Informationen os, run_id und tcp_port.","danger":"safe"}}},{"id":"show-config-param","title":"Konfigurationsparameter anzeigen","aliases":["show config","parameter","show setting","config parameter","setting","memory setting","variable"],"engines":{"oracle":{"command":"SHOW PARAMETER sga_target;\n-- or: SELECT name, value, isdefault FROM v$parameter WHERE name = 'sga_target';","note":"Zum Ändern: ALTER SYSTEM SET ... SCOPE=BOTH; (manche nur mit SCOPE=SPFILE + Neustart).","danger":"safe"},"postgres":{"command":"SHOW shared_buffers;\n-- Details: SELECT name, setting, unit, context FROM pg_settings WHERE name = 'shared_buffers';","note":"Die Spalte context gibt an, ob die Änderung einen Neustart oder ein Reload erfordert (SELECT pg_reload_conf()).","danger":"safe"},"mysql":{"command":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';","note":"Auf den Unterschied GLOBAL vs. SESSION achten: SHOW GLOBAL VARIABLES zeigt den serverweiten Wert.","danger":"safe"},"mssql":{"command":"SELECT name, value, value_in_use, is_dynamic\nFROM sys.configurations\nWHERE name LIKE '%max server memory%';","note":"Zum Ändern: sp_configure + RECONFIGURE; Optionen mit is_dynamic=0 erfordern einen Neustart.","danger":"safe"},"mongodb":{"command":"db.adminCommand({ getParameter: 1, cursorTimeoutMillis: 1 })\n// All startup options: db.adminCommand({ getCmdLineOpts: 1 })","note":"Für zur Laufzeit änderbare Parameter setParameter verwenden; persistente Einstellungen stehen in der Datei mongod.conf.","danger":"safe"},"redis":{"command":"CONFIG GET maxmemory\n# Wildcard: CONFIG GET max*","note":"Zur Laufzeit mit CONFIG SET ändern; für Persistenz CONFIG REWRITE (schreibt in redis.conf).","danger":"safe"}}}]},"rosetta/schema.json":{"category":"Schema & Objekte","categoryId":"schema","order":7,"concepts":[{"id":"list-tables","title":"Tabellen / Collections auflisten","aliases":["list tables","show tables","list all tables","table list","list collections","collections","which tables exist"],"engines":{"oracle":{"command":"SELECT table_name FROM user_tables ORDER BY table_name;\n-- All schemas: SELECT owner, table_name FROM dba_tables ORDER BY owner, table_name;","note":"user_tables liefert die Tabellen im aktuellen Schema, all_tables die zugänglichen.","danger":"safe"},"postgres":{"command":"SELECT table_schema, table_name\nFROM information_schema.tables\nWHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog','information_schema')\nORDER BY 1, 2;\n-- psql shortcut: \\dt *.*","note":"Nur für das Schema public: WHERE table_schema = 'public'.","danger":"safe"},"mysql":{"command":"SHOW TABLES;\n-- Another schema: SHOW TABLES FROM mydb;","note":"Für Details: SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema = DATABASE();","danger":"safe"},"mssql":{"command":"SELECT s.name AS schema_name, t.name AS table_name\nFROM sys.tables t\nJOIN sys.schemas s ON t.schema_id = s.schema_id\nORDER BY s.name, t.name;","note":"Abkürzung: SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';","danger":"safe"},"mongodb":{"command":"show collections\n// or: db.getCollectionNames()","note":"Um auch System-Collections zu sehen: db.getCollectionInfos().","danger":"safe"},"redis":{"command":"SCAN 0 MATCH * COUNT 100\n# Continue with SCAN <cursor> ... using the returned cursor","note":"Redis hat keine „Tabellen“; es werden Keys aufgelistet. KEYS * NIEMALS in Produktion verwenden (blockiert); SCAN arbeitet cursorbasiert.","danger":"caution"}}},{"id":"describe-table","title":"Spalten / Struktur einer Tabelle anzeigen (describe)","aliases":["describe","desc table","columns","table columns","table structure","show schema","field types","fields"],"engines":{"oracle":{"command":"SELECT column_name, data_type, data_length, nullable\nFROM user_tab_columns\nWHERE table_name = 'EMPLOYEE'\nORDER BY column_id;\n-- Shortcut (SQL*Plus): DESC employee","note":"Tabellennamen werden in Großbuchstaben gespeichert (wenn ohne Anführungszeichen angelegt).","danger":"safe"},"postgres":{"command":"SELECT column_name, data_type, is_nullable, column_default\nFROM information_schema.columns\nWHERE table_name = 'customer'\nORDER BY ordinal_position;\n-- psql shortcut: \\d customer","note":"\\d zeigt auch Indizes, Constraints und Foreign Keys.","danger":"safe"},"mysql":{"command":"DESCRIBE customer;\n-- or: SHOW COLUMNS FROM customer;","note":"Für die vollständige Definition SHOW CREATE TABLE customer; verwenden (siehe das Konzept „DDL erzeugen“ unten).","danger":"safe"},"mssql":{"command":"SELECT c.name, t.name AS type, c.max_length, c.is_nullable\nFROM sys.columns c\nJOIN sys.types t ON c.user_type_id = t.user_type_id\nWHERE c.object_id = OBJECT_ID('dbo.customer')\nORDER BY c.column_id;\n-- Shortcut: EXEC sp_help 'dbo.customer';","note":"sp_help listet neben den Spalten auch Indizes und Constraints auf.","danger":"safe"},"mongodb":{"command":"// The schema is flexible; infer it from a sample document:\ndb.customer.findOne()\n// For field coverage: db.customer.aggregate([{ $sample: { size: 100 } }])","note":"MongoDB hat kein festes Schema; Felder können von Dokument zu Dokument variieren. Ist ein $jsonSchema-Validator definiert, lässt er sich mit db.getCollectionInfos anzeigen.","danger":"safe"},"redis":{"command":"TYPE mykey        # string | list | set | hash | zset | stream\nOBJECT ENCODING mykey   # internal encoding","note":"Es gibt kein festes Schema; Typ und Encoding jedes Keys werden separat abgefragt. Für Hash-Felder HGETALL verwenden.","danger":"safe"}}},{"id":"list-indexes","title":"Indizes einer Tabelle auflisten","aliases":["list indexes","show indexes","show table indexes","which indexes","table indexes","getindexes"],"engines":{"oracle":{"command":"SELECT index_name, column_name, column_position\nFROM user_ind_columns\nWHERE table_name = 'EMPLOYEE'\nORDER BY index_name, column_position;","note":"Für Indextyp/Eindeutigkeit mit user_indexes joinen.","danger":"safe"},"postgres":{"command":"SELECT indexname, indexdef\nFROM pg_indexes\nWHERE tablename = 'customer';","note":"indexdef liefert das vollständige CREATE INDEX-Statement; \\d customer in psql listet sie ebenfalls auf.","danger":"safe"},"mysql":{"command":"SHOW INDEX FROM customer;","note":"Die Spalten Non_unique, Seq_in_index und Cardinality zeigen Indexstruktur und Selektivität.","danger":"safe"},"mssql":{"command":"EXEC sp_helpindex 'dbo.customer';\n-- Details: join sys.indexes + sys.index_columns","note":"sp_helpindex liefert Indexname, Typ und Spalten; für gefilterte/eingeschlossene Spalten die sys-Views verwenden.","danger":"safe"},"mongodb":{"command":"db.customer.getIndexes()","note":"Liefert die Keys jedes Index sowie Optionen wie Eindeutigkeit und TTL/partial.","danger":"safe"},"redis":{"command":"# Not applicable (core). If the RediSearch module is present:\n# FT._LIST        (all indexes)\n# FT.INFO idx     (details of one index)","note":"Das Key-Value-Modell hat keine Sekundärindizes; diese gibt es nur mit Modulen wie RediSearch/RedisJSON.","danger":"safe"}}},{"id":"create-index","title":"Index anlegen","aliases":["create index","add index","build index","new index","createindex"],"engines":{"oracle":{"command":"CREATE INDEX customer_city_idx ON customer(city) ONLINE;","note":"ONLINE baut ihn ohne Sperren der Tabelle auf. Für eindeutige Indizes CREATE UNIQUE INDEX verwenden; (a, b) für zusammengesetzte.","danger":"caution"},"postgres":{"command":"CREATE INDEX CONCURRENTLY customer_city_idx ON customer(city);","note":"CONCURRENTLY blockiert keine Schreibzugriffe auf die Tabelle, kann aber nicht in einem Transaktionsblock laufen und ist langsamer.","danger":"caution"},"mysql":{"command":"CREATE INDEX customer_city_idx ON customer(city);\n-- Lock-free: ... ALGORITHM=INPLACE, LOCK=NONE;","note":"InnoDB legt die meisten Indizes online an; bei großen Tabellen Schreibzugriffe mit LOCK=NONE erlauben.","danger":"caution"},"mssql":{"command":"CREATE NONCLUSTERED INDEX IX_customer_city\nON dbo.customer (city)\nWITH (ONLINE = ON);","note":"ONLINE ist ein Enterprise-Feature. Für abdeckende Spalten INCLUDE (col1, col2) verwenden.","danger":"caution"},"mongodb":{"command":"db.customer.createIndex({ city: 1 })\n// Unique: { city: 1 }, { unique: true }\n// Built in the background without blocking (default behavior in 4.2+)","note":"1 ist aufsteigende, -1 absteigende Reihenfolge. Bei großen Collections reduziert der Aufbau zuerst auf einem Secondary die Last.","danger":"caution"},"redis":{"command":"# Not applicable (core). If RediSearch is present:\n# FT.CREATE idx ON HASH PREFIX 1 customer: SCHEMA city TAG","note":"Sekundärindizes gibt es nur mit dem Modul RediSearch; Core-Redis greift über den Key zu.","danger":"safe"}}},{"id":"show-ddl","title":"DDL eines Objekts erzeugen (CREATE-Statement)","aliases":["show ddl","get ddl","create statement","generate ddl","table definition","show create table","generate script"],"engines":{"oracle":{"command":"SELECT DBMS_METADATA.GET_DDL('TABLE', 'EMPLOYEE', 'HR') FROM dual;","note":"'INDEX' für Indizes, 'PACKAGE' für Packages usw. verwenden. Abschneiden langer Ausgaben mit SET LONG 100000 verhindern.","danger":"safe"},"postgres":{"command":"-- There is no single function in SQL; from the shell:\npg_dump -t public.customer --schema-only mydb","note":"In psql zeigt \\d customer die Struktur gut lesbar; für vollständige DDL pg_dump --schema-only verwenden.","danger":"safe"},"mysql":{"command":"SHOW CREATE TABLE customer\\G","note":"Liefert das vollständige CREATE TABLE (Spalten, Indizes, Engine, Charset) mit einem einzigen Befehl.","danger":"safe"},"mssql":{"command":"-- T-SQL has no single built-in command. Options:\n-- 1) SSMS: right-click > Script Table as > CREATE To\n-- 2) EXEC sp_help 'dbo.customer';  (structure summary)\n-- 3) mssql-scripter tool (command line)","note":"Für den Rumpf einer Prozedur/View: SELECT OBJECT_DEFINITION(OBJECT_ID('dbo.usp_name'));","danger":"safe"},"mongodb":{"command":"// There is no DDL (schemaless). View collection options/validator:\ndb.getCollectionInfos({ name: \"customer\" })","note":"Ist options.validator ($jsonSchema) definiert, ist das die einzige „Schemadefinition“; Indizes werden separat mit getIndexes() abgerufen.","danger":"safe"},"redis":{"command":"# Not applicable: there is no schema/DDL concept.","note":"Datenstrukturen werden im Anwendungscode definiert; die Persistenz liegt in RDB/AOF-Dateien.","danger":"safe"}}}]},"rosetta/replication.json":{"category":"Replikation & HA","categoryId":"replication","order":8,"concepts":[{"id":"replication-status","title":"Replikationsstatus und Lag anzeigen","aliases":["replication status","replica lag","replication status","lag","slave lag","standby lag","how far behind"],"engines":{"oracle":{"command":"-- Role and mode:\nSELECT database_role, open_mode FROM v$database;\n-- Data Guard lag:\nSELECT name, value, unit FROM v$dataguard_stats\nWHERE name LIKE '%lag%';","note":"Apply Lag und Transport Lag sind die wichtigsten Data-Guard-Lag-Metriken; auf dem Standby ausführen.","danger":"safe"},"postgres":{"command":"-- On the PRIMARY:\nSELECT client_addr, state, sync_state,\n       write_lag, flush_lag, replay_lag\nFROM pg_stat_replication;\n-- On the STANDBY:\nSELECT now() - pg_last_xact_replay_timestamp() AS replay_lag;","note":"replay_lag ist die Replay-Verzögerung; sync_state = 'sync' kennzeichnet ein synchrones Replikat.","danger":"safe"},"mysql":{"command":"SHOW REPLICA STATUS\\G\n-- Before 8.0.22: SHOW SLAVE STATUS\\G","note":"Seconds_Behind_Source liefert den Lag; Replica_IO_Running und Replica_SQL_Running müssen 'Yes' sein. SHOW SLAVE STATUS ist in 8.0.22–8.3 ein Alias und wurde in 8.4 entfernt.","danger":"safe"},"mssql":{"command":"SELECT ar.replica_server_name, drs.synchronization_state_desc,\n       drs.log_send_queue_size, drs.redo_queue_size\nFROM sys.dm_hadr_database_replica_states drs\nJOIN sys.availability_replicas ar ON drs.replica_id = ar.replica_id;","note":"Für Always On. Eine große redo_queue_size bedeutet, dass das sekundäre Replikat zurückfällt.","danger":"safe"},"mongodb":{"command":"rs.printSecondaryReplicationInfo()\n// Detailed status: rs.status()","note":"Zeigt, wie viele Sekunden jedes Secondary hinter dem Primary liegt; die optimeDate-Werte werden verglichen.","danger":"safe"},"redis":{"command":"INFO replication\n# Check the role, connected_slaves, master_repl_offset,\n# and slaveN:...,offset=... lines","note":"Die Differenz zwischen master_repl_offset und dem Slave-Offset ergibt den Lag; master_link_status muss 'up' sein.","danger":"safe"}}},{"id":"list-replicas","title":"Replikate / Cluster-Knoten auflisten","aliases":["list replicas","show replicas","list replicas","nodes","cluster members","slaves","standby list"],"engines":{"oracle":{"command":"-- Standby destinations:\nSELECT dest_id, destination, status, target\nFROM v$archive_dest\nWHERE status <> 'INACTIVE';\n-- RAC instances: SELECT inst_id, instance_name, status FROM gv$instance;","note":"Die Data-Guard-Konfiguration lässt sich auch mit DGMGRL> SHOW CONFIGURATION anzeigen.","danger":"safe"},"postgres":{"command":"SELECT application_name, client_addr, state, sync_state\nFROM pg_stat_replication;","note":"Listet verbundene Replikate nur auf dem PRIMARY auf; jede Zeile ist ein Standby.","danger":"safe"},"mysql":{"command":"SHOW REPLICAS;\n-- Before 8.0.22: SHOW SLAVE HOSTS;","note":"Auf dem Source-Server ausführen; zeigt Replikate mit gesetztem report-host. SHOW SLAVE HOSTS ist in 8.0.22–8.3 ein Alias und wurde in 8.4 entfernt.","danger":"safe"},"mssql":{"command":"SELECT ar.replica_server_name, ars.role_desc,\n       ars.connected_state_desc, ars.synchronization_health_desc\nFROM sys.dm_hadr_availability_replica_states ars\nJOIN sys.availability_replicas ar ON ars.replica_id = ar.replica_id;","note":"role_desc unterscheidet PRIMARY/SECONDARY; synchronization_health_desc liefert den Gesamtzustand.","danger":"safe"},"mongodb":{"command":"rs.status().members.map(m => ({ name: m.name, state: m.stateStr, health: m.health }))","note":"stateStr liefert PRIMARY/SECONDARY/ARBITER; health 1 bedeutet gesund.","danger":"safe"},"redis":{"command":"INFO replication\n# If Sentinel is used:\n# SENTINEL replicas <master-name>","note":"Die Zeilen slave0, slave1... liefern ip/port/offset jedes Replikats.","danger":"safe"}}},{"id":"promote-failover","title":"Replikat hochstufen / Failover (promote)","aliases":["promote","failover","switchover","promote","role change","stepdown","make master","make primary"],"engines":{"oracle":{"command":"-- Data Guard broker (DGMGRL):\nSWITCHOVER TO 'standby_db';     -- planned, no data loss\n-- FAILOVER TO 'standby_db';     -- on loss of the primary, emergency","note":"SWITCHOVER ist ein geplanter Rollenwechsel; FAILOVER erfolgt, wenn der Primary nicht erreichbar ist, und birgt ein Risiko von Datenverlust.","danger":"danger"},"postgres":{"command":"-- Promote the standby to primary:\nSELECT pg_promote();\n-- or from the shell: pg_ctl promote -D /var/lib/pgsql/data","note":"Vor dem Hochstufen sicherstellen, dass der alte Primary wirklich gestoppt ist (Split-Brain-Risiko). Um den alten Primary nach dem Promote als Replikat des neuen Primary wieder einzubinden, ist pg_rewind nötig; dafür müssen wal_log_hints = on oder Data Checksums aktiviert sein.","danger":"danger"},"mysql":{"command":"-- On the replica (manual):\n-- 1) Stop receiving new events, wait until the received relay log is fully applied:\nSTOP REPLICA IO_THREAD;\nSELECT RECEIVED_TRANSACTION_SET FROM performance_schema.replication_connection_status;\nSELECT @@GLOBAL.gtid_executed;  -- the received set must be fully contained here\n-- 2) Remove replication and enable writes:\nSTOP REPLICA;\nRESET REPLICA ALL;\nSET GLOBAL read_only = OFF;  -- also turns off super_read_only\n-- 3) Fence the old source against writes, then redirect the application.","note":"Wird RESET REPLICA ALL ausgeführt, bevor das Relay Log vollständig angewendet ist, gehen empfangene, aber nicht angewendete Transaktionen verloren. Replikate laufen meist mit aktiviertem read_only/super_read_only; Schreibzugriffe werden erst nach dem Abschalten akzeptiert. Ist die alte Source noch beschreibbar, besteht Split-Brain-Risiko. Die Syntax gilt ab 8.0.22 (davor STOP SLAVE / RESET SLAVE ALL; die alte Form wurde in 8.4 entfernt). Für automatisches und sicheres Failover in Produktion InnoDB Cluster/Group Replication, Orchestrator oder MHA bevorzugen.","danger":"danger"},"mssql":{"command":"-- On the target secondary replica (synchronous):\nALTER AVAILABILITY GROUP [AG_Name] FAILOVER;\n-- Force it, with risk of data loss:\n-- ALTER AVAILABILITY GROUP [AG_Name] FORCE_FAILOVER_ALLOW_DATA_LOSS;","note":"Ein geplantes Failover auf ein synchrones Replikat erfolgt ohne Datenverlust; FORCE wird nur im Desaster-Szenario verwendet.","danger":"danger"},"mongodb":{"command":"// Trigger an election on the current primary:\nrs.stepDown(60)   // step down from primary for 60 s","note":"stepDown startet eine neue Election; um einen bestimmten Knoten zu bevorzugen, die priority-Werte der Mitglieder anpassen.","danger":"danger"},"redis":{"command":"# Make the replica an independent primary:\nREPLICAOF NO ONE\n# With Sentinel (recommended): SENTINEL FAILOVER <master-name>","note":"Bei Einsatz von Sentinel/Cluster SENTINEL FAILOVER statt eines manuellen REPLICAOF verwenden; sonst besteht Split-Brain-Risiko.","danger":"danger"}}},{"id":"open-transactions","title":"Offene / lang laufende Transaktionen finden","aliases":["open transactions","long running transaction","open transaction","long transaction","idle in transaction","oldest transaction","undo"],"engines":{"oracle":{"command":"SELECT s.sid, s.serial#, s.username,\n       t.start_time, t.used_ublk AS undo_blocks\nFROM v$transaction t\nJOIN v$session s ON t.ses_addr = s.saddr\nORDER BY t.start_time;","note":"Ein hoher used_ublk bedeutet, dass eine große Transaktion viel Undo verbraucht; sie wartet möglicherweise auf Commit/Rollback.","danger":"safe"},"postgres":{"command":"SELECT pid, usename, state,\n       now() - xact_start AS xact_age, query\nFROM pg_stat_activity\nWHERE xact_start IS NOT NULL\nORDER BY xact_start;","note":"Lange Transaktionen im Zustand 'idle in transaction' blockieren VACUUM und das Aufräumen von Tupeln; überwachen.","danger":"safe"},"mysql":{"command":"SELECT trx_id, trx_state, trx_started,\n       TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS age_sec,\n       trx_mysql_thread_id\nFROM information_schema.innodb_trx\nORDER BY trx_started;","note":"trx_mysql_thread_id zur Zuordnung zur PROCESSLIST verwenden und bei Bedarf mit KILL beenden.","danger":"safe"},"mssql":{"command":"DBCC OPENTRAN;\n-- Detailed:\nSELECT st.session_id, dt.database_transaction_begin_time\nFROM sys.dm_tran_database_transactions dt\nJOIN sys.dm_tran_session_transactions st ON st.transaction_id = dt.transaction_id\nWHERE dt.database_id = DB_ID()\nORDER BY dt.database_transaction_begin_time;","note":"Lange offene Transaktionen verhindern das Log-Truncation und blähen die Logdatei auf.","danger":"safe"},"mongodb":{"command":"db.adminCommand({\n  currentOp: 1,\n  \"transaction\": { $exists: true }\n})","note":"Lange Multi-Document-Transaktionen werden nach transactionLifetimeLimitSeconds (Standard 60 s) automatisch abgebrochen.","danger":"safe"},"redis":{"command":"# Not applicable: MULTI/EXEC transactions are short and atomic; they do not stay open.","note":"Redis-Transaktionen halten keine Locks; bei lang laufenden Einzelbefehlen SLOWLOG prüfen.","danger":"safe"}}}]},"rosetta/connect.json":{"category":"Verbindung & CLI","categoryId":"connect","order":9,"concepts":[{"id":"connect-cli","title":"Über die Kommandozeile verbinden (CLI / Connection String)","aliases":["connect","connection string","connect","how do i connect","cli connect","login","dsn","uri"],"engines":{"oracle":{"command":"sqlplus app_user/password@//host:1521/service_name\n# As SYSDBA: sqlplus sys/password@//host:1521/orcl AS SYSDBA","note":"//host:port/service ist das Format „Easy Connect“; alternativ einen TNS-Alias aus tnsnames.ora verwenden.","danger":"safe"},"postgres":{"command":"psql -h host -p 5432 -U postgres -d mydb\n# With a URI: psql \"postgresql://app_user:password@host:5432/mydb?sslmode=require\"","note":"Um das Passwort nicht auf der Kommandozeile einzugeben, eine Datei ~/.pgpass oder die Umgebungsvariable PGPASSWORD verwenden.","danger":"safe"},"mysql":{"command":"mysql -h host -P 3306 -u root -p mydb\n# Require TLS: --ssl-mode=REQUIRED","note":"-p allein angeben (das Passwort wird abgefragt); die Form 'mysql -pPassword' macht das Passwort in der Prozessliste sichtbar.","danger":"safe"},"mssql":{"command":"sqlcmd -S host,1433 -U sa -P password -d mydb\n# Windows authentication: sqlcmd -S host -E -d mydb","note":"Im neueren go-sqlcmd können für TLS die Flags -C (Serverzertifikat vertrauen) und -N (verschlüsseln) nötig sein.","danger":"safe"},"mongodb":{"command":"mongosh \"mongodb://app_user:password@host:27017/mydb?authSource=admin\"\n# Replica set: ...mongodb://h1,h2,h3/db?replicaSet=rs0","note":"authSource ist meist die Datenbank, in der der Benutzer definiert ist (meistens admin).","danger":"safe"},"redis":{"command":"redis-cli -h host -p 6379 -a password\n# With TLS: redis-cli -h host -p 6379 --tls -a password\n# Redis 6+ ACL: redis-cli -h host --user app --pass password","note":"-a übergibt das Passwort auf der Kommandozeile (gibt eine Warnung aus); für eine sichere Anmeldung die Umgebungsvariable REDISCLI_AUTH verwenden.","danger":"safe"}}},{"id":"list-databases","title":"Datenbanken / Schemas auflisten","aliases":["list databases","show databases","list databases","which databases","list schemas","pdbs","show dbs"],"engines":{"oracle":{"command":"-- Schemas (users):\nSELECT username FROM dba_users ORDER BY username;\n-- Multitenant (CDB) PDBs:\nSELECT name, open_mode FROM v$pdbs;","note":"In Oracle entspricht eine „Datenbank“ der Instanz; die Trennung erfolgt auf Schema-(User-)Ebene oder auf PDB-Ebene innerhalb einer CDB.","danger":"safe"},"postgres":{"command":"SELECT datname FROM pg_database WHERE datistemplate = false;\n-- psql shortcut: \\l","note":"Für Schemas: SELECT schema_name FROM information_schema.schemata;","danger":"safe"},"mysql":{"command":"SHOW DATABASES;","note":"In MySQL sind „Database“ und „Schema“ Synonyme.","danger":"safe"},"mssql":{"command":"SELECT name, state_desc FROM sys.databases ORDER BY name;","note":"state_desc zeigt den Datenbankzustand, z. B. ONLINE/OFFLINE/RESTORING.","danger":"safe"},"mongodb":{"command":"show dbs\n// or: db.adminCommand({ listDatabases: 1 })","note":"Leere Datenbanken (noch keine Daten geschrieben) erscheinen nicht in der Liste.","danger":"safe"},"redis":{"command":"INFO keyspace\n# db0:keys=..., db1:keys=... lines show the logical DBs in use","note":"Redis hat nummerierte logische DBs (Standard 0-15); im Cluster-Modus wird nur DB 0 verwendet.","danger":"safe"}}},{"id":"switch-database","title":"Aktive Datenbank / aktives Schema wechseln","aliases":["switch database","use database","change database","switch","change schema","change schema","use db"],"engines":{"oracle":{"command":"-- Change the default schema:\nALTER SESSION SET CURRENT_SCHEMA = hr;\n-- Switch to a PDB (inside a CDB):\nALTER SESSION SET CONTAINER = pdb1;","note":"CURRENT_SCHEMA ändert nur die Auflösung von Objektnamen, nicht die Berechtigungen.","danger":"safe"},"postgres":{"command":"-- Switch to another DB inside psql (reconnects):\n\\c mydb\n-- Schema search path (same connection):\nSET search_path TO hr, public;","note":"In PostgreSQL ist eine Verbindung an eine einzige Datenbank gebunden; DB wechseln bedeutet neu verbinden.","danger":"safe"},"mysql":{"command":"USE mydb;","note":"Wechselt das aktive Schema, ohne die Verbindung zu trennen.","danger":"safe"},"mssql":{"command":"USE mydb;","note":"Ändert den Batch-Kontext; nachfolgende Befehle laufen in dieser Datenbank.","danger":"safe"},"mongodb":{"command":"use mydb","note":"Wechselt auch, wenn die Datenbank nicht existiert; sie wird tatsächlich angelegt, sobald das erste Dokument geschrieben wird.","danger":"safe"},"redis":{"command":"SELECT 1\n# Switches to a logical DB index between 0 and 15","note":"Im Cluster-Modus unterstützt SELECT nur 0; statt logischer DB-Trennung Key-Präfixe verwenden.","danger":"safe"}}}]},"rosetta/objects.json":{"category":"Objektverwaltung","categoryId":"objects","order":10,"concepts":[{"id":"create-partition","title":"Partition anlegen / teilen / löschen","aliases":["partition","partition","split","split partition","drop partition","add partition","remove partition"],"engines":{"oracle":{"command":"-- Add a RANGE partition\nALTER TABLE sales ADD PARTITION p2026 VALUES LESS THAN (TO_DATE('2027-01-01','YYYY-MM-DD'));\n\n-- Split an existing partition\nALTER TABLE sales SPLIT PARTITION pmax AT (TO_DATE('2027-01-01','YYYY-MM-DD')) INTO (PARTITION p2026, PARTITION pmax);\n\n-- Drop a partition\nALTER TABLE sales DROP PARTITION p2020 UPDATE INDEXES;","note":"Unterstützt RANGE/LIST/HASH-Partitionierung. DROP PARTITION löscht alle Zeilen dieser Partition; mit UPDATE INDEXES bleiben globale Indizes gültig. Interval-Partitionierung kann Partitionen automatisch hinzufügen.","danger":"caution","priv":"ALTER auf Tabelle","minver":"Partitioning Option"},"postgres":{"command":"-- Declarative partitioning: parent table\nCREATE TABLE sales (id bigint, created_at date, amount numeric) PARTITION BY RANGE (created_at);\n\n-- Add a new partition\nCREATE TABLE sales_2026 PARTITION OF sales FOR VALUES FROM ('2026-01-01') TO ('2027-01-01');\n\n-- Attach a standalone (existing) table as a partition\nCREATE TABLE sales_2027 (LIKE sales INCLUDING ALL);\nALTER TABLE sales ATTACH PARTITION sales_2027 FOR VALUES FROM ('2027-01-01') TO ('2028-01-01');\n\n-- Detach a partition (the table is not dropped; it remains a standalone table)\nALTER TABLE sales DETACH PARTITION sales_2026;","note":"Core-PostgreSQL hat kein SPLIT/MERGE PARTITION (Stand PG17); zum Teilen die Partition per DETACH lösen, zwei Partitionen mit den neuen Grenzen anlegen, die Daten verschieben und per ATTACH einhängen. DETACH ... CONCURRENTLY (PG14+) verkürzt die Sperrdauer.","danger":"caution","priv":"Tabelleneigentümer","minver":"PG10"},"mysql":{"command":"-- Partitioned table\nCREATE TABLE sales (id BIGINT, created_at DATE) PARTITION BY RANGE (YEAR(created_at)) (\n  PARTITION p2024 VALUES LESS THAN (2025),\n  PARTITION p2025 VALUES LESS THAN (2026),\n  PARTITION pmax VALUES LESS THAN MAXVALUE\n);\n\n-- Add / split / drop a partition\nALTER TABLE sales REORGANIZE PARTITION pmax INTO (\n  PARTITION p2026 VALUES LESS THAN (2027),\n  PARTITION pmax VALUES LESS THAN MAXVALUE\n);\nALTER TABLE sales DROP PARTITION p2024;","note":"Die Partitionierungsspalte muss Teil jedes PRIMARY KEY / UNIQUE-Keys sein. Bei RANGE erfolgt Teilen/Zusammenführen mit REORGANIZE PARTITION. DROP PARTITION löscht auch die Zeilen (RANGE/LIST).","danger":"caution","priv":"ALTER, DROP auf Tabelle","minver":"5.7"},"mssql":{"command":"-- 1) Partition function and scheme\nCREATE PARTITION FUNCTION pf_date (date) AS RANGE RIGHT FOR VALUES ('2025-01-01','2026-01-01');\nCREATE PARTITION SCHEME ps_date AS PARTITION pf_date ALL TO ([PRIMARY]);\n\n-- 2) Add a new boundary (split)\nALTER PARTITION SCHEME ps_date NEXT USED [PRIMARY];\nALTER PARTITION FUNCTION pf_date() SPLIT RANGE ('2027-01-01');\n\n-- 3) Remove a boundary (merge)\nALTER PARTITION FUNCTION pf_date() MERGE RANGE ('2025-01-01');","note":"Es werden eine Partition Function + ein Partition Scheme definiert, und die Tabelle wird mit CREATE TABLE ... ON ps_date(created_at) auf diesem Scheme angelegt. Vor SPLIT muss die NEXT USED-Filegroup gesetzt sein. Zum Entfernen von Daten diese mit SWITCH PARTITION in eine Staging-Tabelle auslagern.","danger":"caution","priv":"ALTER auf Tabelle, ALTER ANY DATASPACE","minver":"2016 SP1 (alle Editionen)"},"mongodb":{"command":"// MongoDB uses sharding instead of table partitioning\nsh.shardCollection(\"db.sales\", { created_at: 1 });\n\n// Native time-series collection for time series\ndb.createCollection(\"measurements\", { timeseries: { timeField: \"ts\", granularity: \"hours\" } });","note":"Partitionierung im klassischen Sinn gibt es nicht. Horizontale Aufteilung erfolgt beim Sharding über Chunks; für zeitbasierte Daten bieten Time-Series-Collections automatisches Bucketing.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no table/partition concept; scaling is done with Redis Cluster hash slots (16384).","note":"Redis Cluster verteilt Keys auf 16384 Hash-Slots; das ist Aufteilung auf Anwendungsebene, keine Tabellenpartitionierung. Einen manuellen Partitionsbefehl gibt es nicht.","danger":"safe"},"elasticsearch":{"command":"# Time-based automatic splitting (rollover) with an ILM policy + data stream\nPUT _ilm/policy/logs-policy\n{ \"policy\": { \"phases\": { \"hot\": { \"actions\": { \"rollover\": { \"max_size\": \"50gb\", \"max_age\": \"30d\" } } } } } }\n\nPUT _index_template/logs-tmpl\n{ \"index_patterns\": [\"logs*\"], \"data_stream\": {}, \"template\": { \"settings\": { \"index.lifecycle.name\": \"logs-policy\" } } }","note":"In Elasticsearch erfolgt die Aufteilung, indem ein Index in Shards zerlegt wird, und bei Zeitreihen durch neue Backing Indices mit ILM + Data Streams (Rollover). Alte Indizes lassen sich mit ILM löschen.","danger":"safe"}}},{"id":"sequence","title":"Sequence / Auto-Increment-Wert anlegen","aliases":["sequence","sequence","auto increment","identity","auto-increment","nextval","serial"],"engines":{"oracle":{"command":"-- Sequence\nCREATE SEQUENCE customer_seq START WITH 1 INCREMENT BY 1 NOCACHE;\nSELECT customer_seq.NEXTVAL FROM dual;\n\n-- or an identity column (12c+)\nCREATE TABLE customer (id NUMBER GENERATED ALWAYS AS IDENTITY, name VARCHAR2(100));","note":"Die klassische Methode ist CREATE SEQUENCE + NEXTVAL/CURRVAL. Seit 12c werden auch GENERATED AS IDENTITY-Spalten unterstützt. Ein CACHE-Wert verbessert die Performance, es können aber Lücken entstehen.","danger":"safe","priv":"CREATE SEQUENCE","minver":"12c für IDENTITY"},"postgres":{"command":"-- Sequence\nCREATE SEQUENCE customer_seq START 1 INCREMENT 1;\nSELECT nextval('customer_seq');\n\n-- Modern method: identity column\nCREATE TABLE customer (id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text);\n-- Classic: serial (creates a sequence behind the scenes)\nCREATE TABLE logs (id serial PRIMARY KEY, message text);","note":"GENERATED ... AS IDENTITY ist SQL-Standard und empfohlen; serial ist die Legacy-Kurzform. Manueller Zugriff mit nextval/currval. Der Wert lässt sich mit setval zurücksetzen.","danger":"safe","priv":"CREATE auf Schema","minver":"PG10 für IDENTITY"},"mysql":{"command":"-- AUTO_INCREMENT column\nCREATE TABLE customer (id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100));\n\n-- Set the starting value\nALTER TABLE customer AUTO_INCREMENT = 1000;\n\n-- Get the last inserted value\nSELECT LAST_INSERT_ID();","note":"MySQL hat kein eigenständiges (standardkonformes) Sequence-Objekt; es wird eine AUTO_INCREMENT-Spalte verwendet (eine pro Tabelle, muss indiziert sein). MariaDB 10.3+ unterstützt CREATE SEQUENCE.","danger":"safe","priv":"CREATE, ALTER auf Tabelle"},"mssql":{"command":"-- IDENTITY column\nCREATE TABLE customer (id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(100));\n\n-- Standalone sequence object\nCREATE SEQUENCE customer_seq AS INT START WITH 1 INCREMENT BY 1;\nSELECT NEXT VALUE FOR customer_seq;","note":"IDENTITY(seed,increment) ist an eine Tabellenspalte gebunden. CREATE SEQUENCE (2012+) liefert einen tabellenunabhängigen Zähler, der von mehreren Tabellen genutzt werden kann; Werte werden mit NEXT VALUE FOR abgerufen.","danger":"safe","priv":"CREATE SEQUENCE / ALTER auf Tabelle","minver":"2012 für SEQUENCE"},"mongodb":{"command":"// MongoDB has no auto counter; counter pattern with findOneAndUpdate\ndb.counters.findOneAndUpdate(\n  { _id: \"customer\" },\n  { $inc: { seq: 1 } },\n  { upsert: true, returnDocument: \"after\" }\n);","note":"Die Standard-_id ist eine ObjectId (bereits eindeutig und annähernd steigend). Wird ein fortlaufender Integer benötigt, diesen atomar mit $inc in einer separaten counters-Collection erzeugen.","danger":"safe"},"redis":{"command":"# Atomic counter\nINCR customer:id\n# or with a specific step\nINCRBY customer:id 10","note":"INCR/INCRBY sind atomar und ideal, um in verteilten Umgebungen eindeutige, steigende IDs zu erzeugen. Existiert der Key nicht, beginnt er bei 0.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: Elasticsearch does not offer a sequence object. Document IDs are assigned automatically;\n# the internal _seq_no and _primary_term fields are used for concurrency control.\nPOST logs/_doc\n{ \"message\": \"sample\" }","note":"Bei POST ohne ID vergibt ES eine zufällige eindeutige _id (nicht steigend). _seq_no ist eine interne Sequenznummer, die mit jedem Schreibvorgang steigt, dient aber der Optimistic Concurrency und nicht als Anwendungszähler.","danger":"safe"}}},{"id":"add-constraint","title":"Constraint hinzufügen oder deaktivieren (FK/CHECK/UNIQUE)","aliases":["constraint","constraint","foreign key","check","unique","foreign key","disable constraint"],"engines":{"oracle":{"command":"-- Add a foreign key\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\n-- CHECK\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n-- Disable / enable\nALTER TABLE orders DISABLE CONSTRAINT fk_customer;\nALTER TABLE orders ENABLE CONSTRAINT fk_customer;","note":"Oracle-Constraints lassen sich mit DISABLE/ENABLE vorübergehend abschalten (nützlich bei Bulk Loads). ENABLE VALIDATE prüft vorhandene Zeilen, NOVALIDATE nicht.","danger":"caution","priv":"ALTER auf Tabelle, REFERENCES auf Ziel"},"postgres":{"command":"-- Foreign key and CHECK\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n\n-- Instead of disabling the FK: NOT VALID + validate later\nALTER TABLE orders ADD CONSTRAINT fk2 FOREIGN KEY (c_id) REFERENCES customer(id) NOT VALID;\nALTER TABLE orders VALIDATE CONSTRAINT fk2;\nALTER TABLE orders DROP CONSTRAINT fk2;","note":"PostgreSQL kennt kein DISABLE für Constraints. Um Sperren auf einer großen Tabelle zu reduzieren, den Constraint mit NOT VALID hinzufügen und später mit VALIDATE prüfen. ALTER TABLE ... DISABLE TRIGGER ALL deaktiviert auch die internen Trigger des FK und erfordert Superuser; währenddessen eingefügte Daten werden nie validiert, und das erneute Aktivieren prüft nicht rückwirkend (Risiko stillen Integritätsverlusts). Stattdessen den Constraint mit DROP entfernen, mit NOT VALID neu hinzufügen und dann VALIDATE CONSTRAINT ausführen.","danger":"caution","priv":"Tabelleneigentümer, REFERENCES auf Ziel"},"mysql":{"command":"-- Foreign key and CHECK\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n\n-- Temporarily disable FK checks for the session\nSET FOREIGN_KEY_CHECKS = 0;\n-- ... bulk operation ...\nSET FOREIGN_KEY_CHECKS = 1;","note":"Erfordert InnoDB. Statt einen einzelnen Constraint zu deaktivieren, wird das sessionweite SET FOREIGN_KEY_CHECKS=0 verwendet. CHECK-Constraints werden erst ab MySQL 8.0.16 tatsächlich durchgesetzt (vorher ignoriert).","danger":"caution","priv":"ALTER, REFERENCES auf Tabelle","minver":"8.0.16 für CHECK"},"mssql":{"command":"-- Foreign key and CHECK\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n\n-- Disable / re-enable (validate with WITH CHECK)\nALTER TABLE orders NOCHECK CONSTRAINT fk_customer;\nALTER TABLE orders WITH CHECK CHECK CONSTRAINT fk_customer;","note":"In SQL Server werden FK/CHECK-Constraints mit NOCHECK CONSTRAINT deaktiviert. Wird beim erneuten Aktivieren nicht WITH CHECK CHECK CONSTRAINT verwendet, bleibt der Constraint „not trusted“ und der Query Optimizer kann ihn nicht nutzen.","danger":"caution","priv":"ALTER auf Tabelle, REFERENCES auf Ziel"},"mongodb":{"command":"// Constraint-like checks with schema validation rules\ndb.runCommand({ collMod: \"product\",\n  validator: { $jsonSchema: { required: [\"price\"], properties: { price: { bsonType: \"number\", minimum: 0 } } } },\n  validationLevel: \"strict\"\n});\n// Unique index for uniqueness\ndb.customer.createIndex({ email: 1 }, { unique: true });","note":"Es gibt keine Foreign Keys (Durchsetzung in der Anwendungsschicht). Das UNIQUE-Äquivalent ist ein Unique Index. Statt CHECK $jsonSchema-Validierung verwenden; validationAction: 'warn' wirkt wie ein Deaktivieren.","danger":"caution"},"redis":{"command":"# Not applicable: Redis has no FK/CHECK/UNIQUE constraints. Uniqueness is enforced at the application level with SET or\n# SETNX/conditional writes.\nSET customer:email:sample@x.com 1 NX","note":"Ein Konzept für relationale Integrität oder Constraints gibt es nicht. Eindeutigkeit wird auf Anwendungsseite mit SET ... NX (nur schreiben, wenn nicht vorhanden) oder durch Prüfen eines separaten SET/HASH sichergestellt.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: Elasticsearch has no FK/CHECK. Field types are enforced with the mapping,\n# and uniqueness with a fixed _id.\nPUT product/_doc/ABC-123\n{ \"price\": 100 }","note":"Relationen und Constraints gibt es in der Engine nicht. Die Typprüfung erfolgt über das Mapping. Für einen UNIQUE-Effekt den natürlichen Schlüssel als _id verwenden (Schreibvorgänge auf dieselbe _id überschreiben) oder Konflikte mit op_type=create abweisen.","danger":"safe"}}},{"id":"create-view","title":"View anlegen / ändern","aliases":["view","view","create view","alter view","or replace"],"engines":{"oracle":{"command":"CREATE OR REPLACE VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\n-- Drop\nDROP VIEW active_customer;","note":"CREATE OR REPLACE VIEW legt an und ändert zugleich. Für unter bestimmten Bedingungen aktualisierbare Views kann WITH CHECK OPTION ergänzt werden.","danger":"safe","readonly":true,"priv":"CREATE VIEW"},"postgres":{"command":"CREATE OR REPLACE VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\nDROP VIEW active_customer;","note":"Mit CREATE OR REPLACE können neue Spalten nur am Ende hinzugefügt werden; das Ändern von Typ/Reihenfolge einer Spalte erfordert vorher DROP. Einfache Views sind automatisch aktualisierbar.","danger":"safe","readonly":true,"priv":"CREATE auf Schema"},"mysql":{"command":"CREATE OR REPLACE VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\n-- Modify\nALTER VIEW active_customer AS SELECT id, name FROM customer;\nDROP VIEW active_customer;","note":"Die Definition wird mit CREATE OR REPLACE VIEW oder ALTER VIEW aktualisiert. Die Ausführungsmethode lässt sich mit ALGORITHM = MERGE/TEMPTABLE festlegen; WITH CHECK OPTION schützt aktualisierbare Views.","danger":"safe","readonly":true,"priv":"CREATE VIEW auf Tabelle"},"mssql":{"command":"CREATE OR ALTER VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\nDROP VIEW active_customer;","note":"CREATE OR ALTER VIEW (2016 SP1+) legt an/ändert in einem einzigen Statement. In älteren Versionen zuerst CREATE, danach ALTER VIEW verwenden. Das View-Statement muss das erste Statement im Batch sein.","danger":"safe","readonly":true,"priv":"CREATE VIEW","minver":"2016 SP1 für OR ALTER"},"mongodb":{"command":"// Read-only view (on top of an aggregation pipeline)\ndb.createView(\"active_customer\", \"customer\", [ { $match: { status: \"ACTIVE\" } }, { $project: { name: 1, email: 1 } } ]);\n\ndb.active_customer.drop();","note":"db.createView legt eine schreibgeschützte View über einer Quell-Collection und einer Aggregation Pipeline an. In Views kann nicht geschrieben werden; zum Ändern der Definition löschen und neu anlegen (oder collMod verwenden).","danger":"safe","readonly":true},"redis":{"command":"# Not applicable: Redis has no view concept; derived views are provided on the application\n# side or with RedisJSON/RediSearch indexes.","note":"Über Abfragen definierte Views werden nicht unterstützt. Für einen ähnlichen Bedarf vorberechnete Keys vorhalten oder mit RediSearch einen durchsuchbaren Index aufbauen.","danger":"safe"},"elasticsearch":{"command":"# Filtered logical view with an alias\nPOST _aliases\n{ \"actions\": [ { \"add\": { \"index\": \"customer\", \"alias\": \"active_customer\",\n  \"filter\": { \"term\": { \"status\": \"ACTIVE\" } } } } ] }","note":"Eine klassische SQL-View gibt es nicht; ein gefilterter Alias kommt dem am nächsten und wendet den Filter automatisch auf Abfragen an. Für die Spaltenauswahl _source-Filtering in der Abfrage verwenden.","danger":"safe","readonly":true}}},{"id":"materialized-view","title":"Materialized View anlegen und aktualisieren","aliases":["materialized view","mview","refresh","refresh","cached view","materialize"],"engines":{"oracle":{"command":"-- FAST refresh first requires an MV log on the source table:\nCREATE MATERIALIZED VIEW LOG ON sales\n  WITH ROWID, SEQUENCE (region, amount) INCLUDING NEW VALUES;\n\nCREATE MATERIALIZED VIEW mv_revenue\nBUILD IMMEDIATE REFRESH FAST ON COMMIT\nAS SELECT region, SUM(amount) total, COUNT(*) cnt, COUNT(amount) amount_cnt\n   FROM sales GROUP BY region;\n\n-- Manual refresh\nEXEC DBMS_MVIEW.REFRESH('MV_REVENUE', 'C');","note":"REFRESH FAST erfordert ein Materialized View Log; COMPLETE ('C') berechnet vollständig neu, FAST ('F') inkrementell. ON COMMIT/ON DEMAND bestimmt, wann der Refresh erfolgt.","danger":"caution","priv":"CREATE MATERIALIZED VIEW"},"postgres":{"command":"CREATE MATERIALIZED VIEW mv_revenue AS\nSELECT region, SUM(amount) AS total FROM sales GROUP BY region;\n\n-- Refresh (CONCURRENTLY requires a unique index)\nCREATE UNIQUE INDEX ON mv_revenue (region);\nREFRESH MATERIALIZED VIEW CONCURRENTLY mv_revenue;","note":"REFRESH berechnet alles neu (nicht inkrementell). CONCURRENTLY blockiert keine Lesezugriffe, erfordert aber einen Unique Index und ist langsamer.","danger":"caution","priv":"Tabelleneigentümer","minver":"PG9.3 (CONCURRENTLY 9.4)"},"mysql":{"command":"-- MySQL/MariaDB has no built-in materialized views.\n-- Approach: summary table + refresh with a trigger or a scheduled event\nCREATE TABLE mv_revenue (region VARCHAR(50) PRIMARY KEY, total DECIMAL(18,2));\n\nCREATE EVENT ev_mv_revenue ON SCHEDULE EVERY 1 HOUR DO\n  REPLACE INTO mv_revenue SELECT region, SUM(amount) FROM sales GROUP BY region;","note":"Native Materialized Views werden nicht unterstützt. Eine Summentabelle wird gepflegt und entweder sofort per Trigger oder periodisch mit dem EVENT SCHEDULER neu befüllt.","danger":"caution","priv":"CREATE, EVENT auf Schema"},"mssql":{"command":"-- Indexed view (materialized view equivalent)\nCREATE VIEW dbo.mv_revenue WITH SCHEMABINDING AS\nSELECT region, SUM(ISNULL(amount, 0)) AS total, COUNT_BIG(*) AS cnt\nFROM dbo.sales GROUP BY region;\nGO\nCREATE UNIQUE CLUSTERED INDEX ix_mv_revenue ON dbo.mv_revenue (region);","note":"SQL Server verwendet Indexed Views: WITH SCHEMABINDING ist Pflicht; sobald ein Unique Clustered Index angelegt ist, werden die Daten physisch gespeichert und automatisch gepflegt (kein separates REFRESH nötig). Mit GROUP BY ist COUNT_BIG(*) erforderlich; ist die Spalte in SUM nullable, SUM(ISNULL(amount, 0)) schreiben (sonst Fehler 8662). Die View muss mit QUOTED_IDENTIFIER und ANSI_NULLS ON angelegt werden. Automatisches View Matching durch den Query Optimizer gibt es nur in der Enterprise- (und Developer-)Edition; in Standard die View mit dem Hint WITH (NOEXPAND) referenzieren.","danger":"caution","priv":"CREATE VIEW, ALTER auf Schema"},"mongodb":{"command":"// On-demand materialized view: write the result to a collection with $merge\ndb.sales.aggregate([\n  { $group: { _id: \"$region\", total: { $sum: \"$amount\" } } },\n  { $merge: { into: \"mv_revenue\", whenMatched: \"replace\", whenNotMatched: \"insert\" } }\n]);","note":"Eine On-Demand-Materialized-View erhält man, indem man das Aggregationsergebnis mit $out oder $merge in eine persistente Collection schreibt. Zum Aktualisieren die Pipeline erneut ausführen (manuell oder per Scheduler).","danger":"caution"},"redis":{"command":"# Not applicable: Redis has no materialized views. The computed result is written to a key\n# and cached with a TTL.\nSET mv:revenue:region1 12345 EX 3600","note":"Ein Konzept für Materialized Views gibt es nicht; am nächsten kommt es, vorberechnete Werte in Keys (String/Hash/Sorted Set) zu halten und mit EX/TTL zeitlich begrenzt zu cachen.","danger":"safe"},"elasticsearch":{"command":"# Continuous/materialized summary index with a transform\nPUT _transform/mv_revenue\n{ \"source\": { \"index\": \"sales\" },\n  \"dest\": { \"index\": \"mv_revenue\" },\n  \"pivot\": { \"group_by\": { \"region\": { \"terms\": { \"field\": \"region\" } } },\n            \"aggregations\": { \"total\": { \"sum\": { \"field\": \"amount\" } } } } }\nPOST _transform/mv_revenue/_start","note":"Ein Transform pivotiert den Quellindex und materialisiert ihn in einen zusammenfassenden Zielindex. Dieses Beispiel ist ein Batch-Transform: Es läuft einmal mit _start und stoppt. Für einen kontinuierlichen Transform, der sich bei neuen Daten automatisch aktualisiert, \"sync\": { \"time\": { \"field\": \"<time_field>\", \"delay\": \"60s\" } } mit einem Zeitfeld zur Konfiguration hinzufügen (eine Einstellung continuous: true gibt es nicht).","danger":"caution"}}},{"id":"rename-object","title":"Tabelle / Objekt umbenennen","aliases":["rename","rename","change name","rename table","sp_rename"],"engines":{"oracle":{"command":"-- Table\nALTER TABLE old_name RENAME TO new_name;\n-- or\nRENAME old_name TO new_name;\n-- Column\nALTER TABLE customer RENAME COLUMN name TO full_name;","note":"Für Tabellen/Views/Sequences wird RENAME oder ALTER ... RENAME TO verwendet. Abhängige Objekte (Views, Synonyme) können ungültig werden und müssen neu kompiliert werden.","danger":"caution","priv":"ALTER auf Objekt"},"postgres":{"command":"ALTER TABLE old_name RENAME TO new_name;\nALTER TABLE customer RENAME COLUMN name TO full_name;\nALTER VIEW old_view RENAME TO new_view;","note":"ALTER ... RENAME TO funktioniert für die meisten Objekttypen. Foreign-Key- und Indexreferenzen bleiben auch bei Namensänderung automatisch erhalten (OID-basiert).","danger":"caution","priv":"Tabelleneigentümer"},"mysql":{"command":"RENAME TABLE old_name TO new_name;\n-- or\nALTER TABLE old_name RENAME TO new_name;\n-- Column (8.0+)\nALTER TABLE customer RENAME COLUMN name TO full_name;","note":"RENAME TABLE ist atomar und kann mehrere Tabellen in einem Statement verschieben. Zum Umbenennen von Spalten ab 8.0 RENAME COLUMN verwenden; davor muss CHANGE verwendet und auch der Typ angegeben werden.","danger":"caution","priv":"ALTER, DROP auf alt; CREATE, INSERT auf neu"},"mssql":{"command":"-- Table\nEXEC sp_rename 'dbo.old_name', 'new_name';\n-- Column\nEXEC sp_rename 'dbo.customer.name', 'full_name', 'COLUMN';","note":"SQL Server hat kein RENAME TABLE-Statement; es wird die Systemprozedur sp_rename verwendet. Das Schemapräfix wird im Tabellennamen angegeben, nicht im neuen Namen. Abhängige Views/Prozeduren müssen manuell angepasst werden.","danger":"caution","priv":"ALTER auf Objekt, CONTROL"},"mongodb":{"command":"// Rename a collection\ndb.old_name.renameCollection(\"new_name\");\n// or the admin command\ndb.adminCommand({ renameCollection: \"db.old_name\", to: \"db.new_name\" });","note":"renameCollection ändert den Collection-Namen. Zum Umbenennen eines Felds den Operator $rename mit updateMany verwenden. Sharded Collections lassen sich ab 5.0 umbenennen; ältere Versionen unterstützen das nicht.","danger":"caution","priv":"renameCollectionSameDB / clusterManager"},"redis":{"command":"# Rename a key\nRENAME old_key new_key\n# Without overwriting if the target exists\nRENAMENX old_key new_key","note":"RENAME benennt den Key um; existiert das Ziel bereits, wird es überschrieben. RENAMENX funktioniert nur, wenn das Ziel nicht existiert. Existiert der Quell-Key nicht, wird ein Fehler zurückgegeben.","danger":"caution"},"elasticsearch":{"command":"# An index cannot be renamed directly; reindex + alias approach\nPOST _reindex\n{ \"source\": { \"index\": \"old_name\" }, \"dest\": { \"index\": \"new_name\" } }\nDELETE old_name\n# or use an alias for a seamless switch\nPOST _aliases\n{ \"actions\": [ { \"add\": { \"index\": \"new_name\", \"alias\": \"customer\" } } ] }","note":"Einen Befehl zum Umbenennen von Indizes gibt es nicht. Mit _reindex in den neuen Namen umindizieren und den alten löschen. Empfohlen wird der Betrieb hinter einem stabilen Alias, damit die Anwendung von Indexnamen unabhängig bleibt.","danger":"caution"}}}]},"rosetta/datamove.json":{"category":"Datenbewegung","categoryId":"datamove","order":11,"concepts":[{"id":"bulk-import","title":"Daten per Bulk Load laden (CSV / aus Datei)","aliases":["import","bulk load","load csv","bulk insert","load data","copy from","import data"],"engines":{"oracle":{"command":"-- SQL*Loader (from the shell)\nsqlldr app_user/password control=customer.ctl log=load.log\n\n-- Contents of customer.ctl:\n-- LOAD DATA INFILE 'customer.csv'\n-- INTO TABLE customer FIELDS TERMINATED BY ','\n-- (id, name, email)\n\n-- Alternative: with an external table\nCREATE TABLE ext_customer (id NUMBER, name VARCHAR2(100))\nORGANIZATION EXTERNAL (TYPE ORACLE_LOADER DEFAULT DIRECTORY dir_csv\n  ACCESS PARAMETERS (FIELDS TERMINATED BY ',') LOCATION ('customer.csv'));","note":"Das gängigste Tool ist SQL*Loader (mit Control File). Um die Datei wie eine Abfrage zu lesen, eine External Table verwenden. Für große Mengen ist Direct Path Load (DIRECT=TRUE) schnell.","danger":"caution","priv":"INSERT auf die Tabelle, ein DIRECTORY-Objekt"},"postgres":{"command":"-- Server side (file on the server; requires superuser/pg_read_server_files)\nCOPY customer (id, name, email) FROM '/data/customer.csv' WITH (FORMAT csv, HEADER true);\n\n-- Client side (psql, file on the client)\n\\copy customer (id, name, email) FROM 'customer.csv' WITH (FORMAT csv, HEADER true)","note":"COPY liest eine Datei auf dem Server und erfordert Berechtigungen; psql \\copy liest eine Client-Datei und braucht keine zusätzlichen Berechtigungen. Es ist der schnellste Weg, CSV zu laden.","danger":"caution","priv":"INSERT auf die Tabelle (pg_read_server_files für COPY)"},"mysql":{"command":"-- Server side\nLOAD DATA INFILE '/data/customer.csv' INTO TABLE customer\nFIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n' IGNORE 1 LINES;\n\n-- From a client file\nLOAD DATA LOCAL INFILE 'customer.csv' INTO TABLE customer\nFIELDS TERMINATED BY ',' IGNORE 1 LINES;","note":"LOAD DATA INFILE ist der schnellste Weg für Bulk Loads. LOCAL liest eine Client-Datei und muss sowohl auf dem Server (local_infile=ON) als auch auf dem Client aktiviert sein. secure_file_priv beschränkt den Serverpfad.","danger":"caution","priv":"FILE (INSERT für LOCAL)"},"mssql":{"command":"-- T-SQL BULK INSERT\nBULK INSERT customer FROM 'C:\\data\\customer.csv'\nWITH (FORMAT='CSV', FIRSTROW=2, FIELDTERMINATOR=',', ROWTERMINATOR='0x0a');\n\n-- bcp from the shell\nbcp mydb.dbo.customer in customer.csv -c -t, -S server -T","note":"BULK INSERT liest die Datei aus einem Pfad, auf den das Dienstkonto von SQL Server zugreifen kann. bcp ist ein Kommandozeilentool; -c ist der Zeichenmodus, -t das Feldtrennzeichen. Für maximale Geschwindigkeit mit TABLOCK Minimal Logging anstreben.","danger":"caution","priv":"INSERT, ADMINISTER BULK OPERATIONS"},"mongodb":{"command":"# Import CSV / JSON (from the shell)\nmongoimport --db=shop --collection=customer --type=csv --headerline --file=customer.csv\n\n# For a JSON array\nmongoimport --db=shop --collection=customer --file=customer.json --jsonArray","note":"mongoimport lädt CSV/TSV/JSON; --headerline behandelt die erste Zeile als Feldnamen. Um vorhandene Dokumente zu aktualisieren, --mode=upsert --upsertFields verwenden. Bei großen Daten beschleunigt --numInsertionWorkers den Vorgang.","danger":"caution","priv":"insert (readWrite)"},"redis":{"command":"# Fastest: feed commands in protocol format and pipe them\ncat commands.txt | redis-cli --pipe\n\n# Example lines in commands.txt:\n# SET customer:1 \"Alice\"\n# SET customer:2 \"Bob\"","note":"Für Bulk Loads redis-cli --pipe (Mass Insert) verwenden; sehr schnell, da Befehle im RESP-Protokollformat über eine einzige Verbindung gestreamt werden. Alternativ werden Befehle per Pipelining gruppiert.","danger":"caution"},"elasticsearch":{"command":"# _bulk API (NDJSON: an action line + a source line per document)\ncurl -s -H 'Content-Type: application/x-ndjson' -XPOST 'localhost:9200/customer/_bulk' --data-binary @data.ndjson\n\n# Contents of data.ndjson:\n# { \"index\": {} }\n# { \"id\": 1, \"name\": \"Alice\" }\n# { \"index\": {} }\n# { \"id\": 2, \"name\": \"Bob\" }","note":"Die _bulk-API nimmt NDJSON entgegen (pro Dokument eine Aktionszeile + eine Datenzeile; die Datei muss mit \\n enden). Für CSV Logstash, Filebeat oder elasticdump verwenden. Das Deaktivieren von refresh_interval während des Ladens beschleunigt den Vorgang.","danger":"caution","priv":"Berechtigung index / create index"}}},{"id":"bulk-export","title":"Daten exportieren (CSV / in Datei)","aliases":["export","export data","export csv","copy to","outfile","dump","extract data"],"engines":{"oracle":{"command":"-- CSV with SQL*Plus (spool)\nSET MARKUP CSV ON\nSPOOL customer.csv\nSELECT id, name, email FROM customer;\nSPOOL OFF\n\n-- Logical export with Data Pump (from the shell)\nexpdp app_user/password tables=customer directory=dir_dmp dumpfile=customer.dmp","note":"Für CSV SQL*Plus SET MARKUP CSV ON + SPOOL verwenden (12.2+). Zum Verschieben von Schemas/Tabellen erzeugt Data Pump (expdp) eine binäre .dmp, die mit impdp in eine andere Oracle-Datenbank importiert wird.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle","minver":"12.2 für MARKUP CSV"},"postgres":{"command":"-- Server side\nCOPY (SELECT id, name, email FROM customer) TO '/data/customer.csv' WITH (FORMAT csv, HEADER true);\n\n-- Client side (psql)\n\\copy (SELECT id, name, email FROM customer) TO 'customer.csv' WITH (FORMAT csv, HEADER true)","note":"COPY ... TO schreibt ein Abfrageergebnis in eine Datei (serverseitig, erfordert Berechtigungen); psql \\copy schreibt in eine Client-Datei. HEADER true fügt eine Kopfzeile hinzu.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mysql":{"command":"-- To a file on the server\nSELECT id, name, email FROM customer\nINTO OUTFILE '/data/customer.csv'\nFIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n';\n\n-- To the client: mysql -e ... > file (tab-separated)\n-- mysql -e \"SELECT * FROM customer\" shop > customer.tsv","note":"SELECT ... INTO OUTFILE legt die Datei auf dem Server an (erfordert den secure_file_priv-Pfad und die FILE-Berechtigung; kann nicht überschreiben). Für clientseitige Ausgabe mit mysql -e umleiten.","danger":"safe","readonly":true,"priv":"SELECT, FILE"},"mssql":{"command":"-- Export with bcp (from the shell)\nbcp \"SELECT id, name, email FROM shop.dbo.customer\" queryout customer.csv -c -t, -S server -T\n\n-- or the whole table\nbcp shop.dbo.customer out customer.dat -n -S server -T","note":"bcp out/queryout schreibt Daten in eine Datei: -c Zeichenmodus, -t, Feldtrennzeichen, -n natives Binärformat. Für ein Abfrageergebnis queryout verwenden. „Results to File“ in SSMS ist ebenfalls eine Option.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mongodb":{"command":"# Export CSV (fields must be specified)\nmongoexport --db=shop --collection=customer --type=csv --fields=id,name,email --out=customer.csv\n\n# As JSON\nmongoexport --db=shop --collection=customer --out=customer.json","note":"mongoexport erzeugt CSV/JSON; für CSV müssen die Spalten explizit mit --fields angegeben werden. Zum Filtern --query verwenden. Für ein vollständiges Binär-Backup ist mongodump vorzuziehen.","danger":"safe","readonly":true,"priv":"find (read)"},"redis":{"command":"# Scan keys (SCAN instead of KEYS in production)\nredis-cli --scan --pattern 'customer:*' > keys.txt\n\n# For a full data backup, download an RDB snapshot to the client side (non-blocking)\nredis-cli --rdb ./dump.rdb","note":"Ein strukturierter CSV-Export ist nicht eingebaut. Für eine Key-Liste --scan (nicht blockierend) und für die vollständigen Daten eine RDB-Datei verwenden: redis-cli --rdb lädt einen auf dem Server im Hintergrund erzeugten Snapshot in eine lokale Datei herunter; soll die Datei auf dem Server liegen, BGSAVE verwenden. SAVE blockiert den Main Thread; nicht in Produktion verwenden. Um auch die Werte zu erhalten, das Scan-Ergebnis in einer Schleife auslesen.","danger":"safe","readonly":true},"elasticsearch":{"command":"# Export with elasticdump\nelasticdump --input=http://localhost:9200/customer --output=customer.json --type=data\n\n# For large data, paged reads with scroll / point-in-time\nGET customer/_search?scroll=1m\n{ \"size\": 1000, \"query\": { \"match_all\": {} } }","note":"Einen direkten CSV-Export gibt es nicht. Um alle Daten zu erhalten, elasticdump verwenden oder _search mit scroll/PIT (Point-in-Time) seitenweise durchlaufen. Die Umwandlung in CSV erfordert den csv-Output von Logstash oder ein externes Tool.","danger":"safe","readonly":true,"priv":"read auf Index"}}},{"id":"copy-between","title":"Daten zwischen Tabellen / Umgebungen kopieren","aliases":["copy","copy","insert select","ctas","reindex","move data","between tables"],"engines":{"oracle":{"command":"-- Into an existing table\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl WHERE status = 'ACTIVE';\n\n-- By creating a new table (CTAS)\nCREATE TABLE target_tbl AS SELECT * FROM source_tbl;\n\n-- From a remote database via a database link\nINSERT INTO target_tbl SELECT * FROM source_tbl@remote_db;","note":"Innerhalb derselben Datenbank INSERT ... SELECT oder CREATE TABLE AS SELECT (CTAS) verwenden. Für Kopien zwischen Umgebungen sind ein Database Link (@remote_db) oder Data Pump vorzuziehen. Der Hint APPEND beschleunigt per Direct Path.","danger":"caution","priv":"SELECT auf Quelle, INSERT/CREATE auf Ziel"},"postgres":{"command":"-- Same database\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl;\nCREATE TABLE target_tbl AS SELECT * FROM source_tbl;\n\n-- Between environments (psql pipe)\n-- pg_dump -t source_tbl sourcedb | psql targetdb\n-- or read the remote table with postgres_fdw and INSERT","note":"Innerhalb derselben DB INSERT ... SELECT / CTAS verwenden. Zwischen Servern eine Pipeline pg_dump | psql verwenden oder über eine Foreign Table mit postgres_fdw kopieren.","danger":"caution","priv":"SELECT auf Quelle, INSERT/CREATE auf Ziel"},"mysql":{"command":"-- Same database\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl;\nCREATE TABLE target_tbl AS SELECT * FROM source_tbl;\n\n-- Between servers (mysqldump pipe)\n-- mysqldump sourcedb source_tbl | mysql -h targethost targetdb","note":"Innerhalb derselben DB INSERT ... SELECT oder CREATE TABLE ... AS SELECT. Zwischen verschiedenen Servern ist es üblich, die Ausgabe von mysqldump in den mysql-Client zu pipen. CTAS kopiert keine Indizes und Constraints.","danger":"caution","priv":"SELECT auf Quelle, INSERT/CREATE auf Ziel"},"mssql":{"command":"-- Into an existing table\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl;\n\n-- By creating a new table\nSELECT * INTO target_tbl FROM source_tbl;\n\n-- Between servers: linked server\nINSERT INTO target_tbl SELECT * FROM [REMOTE_SRV].shop.dbo.source_tbl;","note":"In SQL Server erfolgt das Kopieren in eine neue Tabelle mit SELECT ... INTO (das CTAS-Äquivalent). Zwischen Servern einen Linked Server (vierteiliger Name), bcp out/in oder SSIS verwenden.","danger":"caution","priv":"SELECT auf Quelle, INSERT / CREATE TABLE auf Ziel"},"mongodb":{"command":"// Write an aggregation result to another collection\ndb.source_tbl.aggregate([\n  { $match: { status: \"ACTIVE\" } },\n  { $out: \"target_tbl\" }\n]);\n\n// Merge into an existing collection (upsert)\ndb.source_tbl.aggregate([ { $merge: { into: \"target_tbl\", whenMatched: \"merge\", whenNotMatched: \"insert\" } } ]);","note":"$out ersetzt die Ziel-Collection vollständig; $merge führt mit vorhandenen Dokumenten zusammen bzw. fügt ein (gleiche oder andere DB). Für Verschiebungen zwischen Servern mongodump | mongorestore oder mongoexport/mongoimport verwenden.","danger":"caution","priv":"find auf Quelle, insert auf Ziel"},"redis":{"command":"# Move a single key to another server (deleted from the source)\nMIGRATE target_host 6379 customer:1 0 5000\n\n# To copy (without deleting): DUMP + RESTORE\nDUMP customer:1          # returns the serialized value\nRESTORE customer:1new 0 \"<dump-data>\"","note":"MIGRATE verschiebt den Key atomar in die Zielinstanz und löscht ihn in der Quelle (mit der Option COPY wird nicht gelöscht). Zum Kopieren innerhalb derselben Instanz den Befehl COPY (6.0+) oder DUMP+RESTORE verwenden.","danger":"caution"},"elasticsearch":{"command":"# Within the same cluster\nPOST _reindex\n{ \"source\": { \"index\": \"source_tbl\" }, \"dest\": { \"index\": \"target_tbl\" } }\n\n# From a remote cluster (remote reindex; requires the whitelist setting)\nPOST _reindex\n{ \"source\": { \"remote\": { \"host\": \"http://remote:9200\" }, \"index\": \"source_tbl\" },\n  \"dest\": { \"index\": \"target_tbl\" } }","note":"_reindex kopiert zwischen Indizes; mit einer Query lässt sich filtern und mit einem Script transformieren. Zwischen Clustern Remote Reindex verwenden (reindex.remote.whitelist muss auf dem Ziel definiert sein). Große Jobs werden mit slices parallelisiert.","danger":"caution","priv":"read auf Quelle, write auf Ziel"}}},{"id":"generate-insert","title":"Daten als INSERT-Skript erzeugen","aliases":["generate insert","insert script","generate script","data dump","inserts","generate script"],"engines":{"oracle":{"command":"-- DBMS_METADATA for object DDL\nSELECT DBMS_METADATA.GET_DDL('TABLE','CUSTOMER') FROM dual;\n\n-- Generating row data as INSERTs is not built in;\n-- use SQL Developer 'Export > Insert' or SQLcl 'SET SQLFORMAT INSERT':\n-- SET SQLFORMAT INSERT\n-- SELECT * FROM customer;","note":"Oracle bietet keinen SQL-Befehl, der Daten direkt in ein fertiges INSERT-Skript umwandelt. Für DDL DBMS_METADATA.GET_DDL verwenden; für Zeilen-INSERTs SQLcl (SET SQLFORMAT INSERT) oder den Export-Assistenten von SQL Developer.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"postgres":{"command":"-- Data only, as INSERT statements\npg_dump --data-only --inserts --table=customer shop > insert.sql\n\n-- With column names (more robust to schema differences)\npg_dump --data-only --column-inserts --table=customer shop > insert.sql","note":"pg_dump --inserts schreibt jede Zeile als separates INSERT; --column-inserts fügt auch die Spaltennamen hinzu (robust gegenüber Schemaunterschieden). Der Standard (ohne Flag) erzeugt das COPY-Format, das viel schneller lädt.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mysql":{"command":"-- Data only (INSERTs), without the table definition\nmysqldump --no-create-info shop customer > insert.sql\n\n-- One INSERT per row (more readable)\nmysqldump --no-create-info --skip-extended-insert shop customer > insert.sql","note":"mysqldump erzeugt standardmäßig INSERT-Statements; --no-create-info überspringt CREATE TABLE und gibt nur Daten aus. --skip-extended-insert macht jede Zeile zu einem separaten INSERT (standardmäßig werden viele Zeilen in einem Statement zusammengefasst).","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mssql":{"command":"-- SSMS: right-click > Tasks > Generate Scripts >\n--   Advanced > 'Types of data to script' = Data only (or Schema and data)\n\n-- Command-line alternative (sqlpackage / mssql-scripter tool)\n-- mssql-scripter -S server -d shop -t customer --data-only > insert.sql","note":"SQL Server hat dafür keinen eingebauten T-SQL-Befehl. Am gängigsten ist es, im SSMS-Assistenten „Generate Scripts“ Data only zu wählen. Auf der Kommandozeile erzeugt das Microsoft-Tool mssql-scripter INSERTs mit --data-only.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mongodb":{"command":"# Produces a binary/BSON backup instead of INSERT SQL\nmongodump --db=shop --collection=customer --out=./backup\n\n# As JSON text (can be adapted to insertMany)\nmongoexport --db=shop --collection=customer --jsonArray --out=customer.json","note":"Ein SQL-INSERT-Konzept gibt es nicht. Für ein portables Backup mongodump verwenden (BSON, Restore mit mongorestore). Für ein in Code eingebettetes insertMany kann die Ausgabe von mongoexport --jsonArray verwendet werden.","danger":"safe","readonly":true,"priv":"find (read)"},"redis":{"command":"# Portable serialization of a single key\nDUMP customer:1\n# The output is restored with RESTORE:\n# RESTORE customer:1 0 \"<dump-data>\"\n\n# RDB snapshot for all data\nredis-cli --rdb dump.rdb","note":"Es wird kein SQL-INSERT-Skript erzeugt. DUMP + RESTORE-Serialisierung pro Key und für alle Daten eine RDB-Datei (--rdb) verwenden. Wird eine Befehlsliste benötigt, mit SCAN iterieren und für jeden Typ die passenden SET/HSET-Befehle erzeugen.","danger":"safe","readonly":true},"elasticsearch":{"command":"# The INSERT equivalent is _bulk NDJSON; produce it with elasticdump\nelasticdump --input=http://localhost:9200/customer --output=customer_bulk.json --type=data\n\n# Manual: converting a _search result to _bulk NDJSON requires an external script","note":"Es wird kein SQL-INSERT erzeugt; in Elasticsearch ist die Ladeeinheit _bulk-NDJSON. Um vorhandene Daten in wieder ladbarer Form zu extrahieren, elasticdump (type=data) verwenden; die Ausgabe von _search + PIT lässt sich mit einem externen Skript in NDJSON umwandeln.","danger":"safe","readonly":true,"priv":"read auf Index"}}}]},"rosetta/monitoring.json":{"category":"Monitoring & Diagnose","categoryId":"monitoring","order":12,"concepts":[{"id":"current-locks","title":"Aktuelle Locks auflisten","aliases":["locks","lock list","lock list","current locks","active locks"],"engines":{"oracle":{"command":"SELECT s.sid, s.serial#, l.type, DECODE(l.lmode,0,'None',1,'Null',2,'Row-S',3,'Row-X',4,'Share',5,'S/Row-X',6,'Exclusive') AS lmode, o.object_name, s.username, s.blocking_session\nFROM v$lock l\nJOIN v$session s ON s.sid = l.sid\nLEFT JOIN v$locked_object lo ON lo.session_id = l.sid\nLEFT JOIN dba_objects o ON o.object_id = lo.object_id\nORDER BY s.sid;","note":"Zeigt über v$lock und v$locked_object aktive Locks und die blockierende Session (blocking_session).","danger":"safe","readonly":true,"priv":"SELECT auf v$lock, v$session"},"postgres":{"command":"SELECT l.locktype, l.mode, l.granted, a.pid, a.usename, a.state, a.wait_event_type, a.query\nFROM pg_locks l\nJOIN pg_stat_activity a ON a.pid = l.pid\nWHERE l.pid <> pg_backend_pid()\nORDER BY l.granted, a.pid;","note":"Joint pg_locks mit pg_stat_activity; Zeilen mit granted=false zeigen wartende (blockierte) Locks.","danger":"safe","readonly":true,"priv":"Rolle pg_monitor (um alle Abfragen zu sehen)"},"mysql":{"command":"SELECT ENGINE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS, THREAD_ID\nFROM performance_schema.data_locks;\n-- For waiters: SELECT * FROM performance_schema.data_lock_waits;","note":"performance_schema.data_locks listet aktuelle InnoDB-Locks, data_lock_waits die Wartebeziehungen.","danger":"safe","readonly":true,"priv":"SELECT auf performance_schema","minver":"8.0"},"mssql":{"command":"SELECT tl.resource_type, tl.resource_database_id, tl.request_mode, tl.request_status, tl.request_session_id, es.login_name, er.blocking_session_id\nFROM sys.dm_tran_locks tl\nLEFT JOIN sys.dm_exec_sessions es ON es.session_id = tl.request_session_id\nLEFT JOIN sys.dm_exec_requests er ON er.session_id = tl.request_session_id\nORDER BY tl.request_session_id;","note":"sys.dm_tran_locks zeigt aktuelle Lock-Anforderungen; solche mit request_status = WAIT sind blockierte Anforderungen.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.currentOp({ \"$or\": [ { \"waitingForLock\": true }, { \"locks\": { \"$exists\": true } } ] })","note":"db.currentOp zeigt Operationen, die auf einen Lock warten (waitingForLock), und Operationen, die Locks halten; MongoDB sperrt auf Dokument-/Collection-Ebene, nicht auf Zeilenebene.","danger":"safe","priv":"Rolle inprog / clusterMonitor"},"redis":{"command":"# Not applicable: Redis has no row/table lock concept; commands run sequentially on a single thread. For blocking analysis, use the SLOWLOG and LATENCY commands.","note":"Da Redis single-threaded ist, gibt es keine klassische Lock-Liste.","danger":"safe"},"elasticsearch":{"command":"GET _cluster/pending_tasks","note":"Elasticsearch hat keine Zeilensperren; hier werden die auf dem Master in der Warteschlange stehenden Cluster-Änderungstasks (Pending Tasks) aufgelistet.","danger":"safe","readonly":true}}},{"id":"long-operations","title":"Fortschritt lang laufender Operationen anzeigen","aliases":["progress","operation progress","long operations","long-running operations","longops"],"engines":{"oracle":{"command":"SELECT sid, serial#, opname, target, sofar, totalwork,\n       ROUND(sofar / NULLIF(totalwork,0) * 100, 2) AS pct_done,\n       time_remaining, elapsed_seconds\nFROM v$session_longops\nWHERE totalwork > 0 AND sofar < totalwork\nORDER BY start_time;","note":"v$session_longops zeigt den prozentualen Fortschritt und die Restzeit (time_remaining) von Operationen, die länger als 6 Sekunden laufen.","danger":"safe","readonly":true,"priv":"SELECT auf v$session_longops"},"postgres":{"command":"SELECT pid, phase, heap_blks_total, heap_blks_scanned,\n       ROUND(heap_blks_scanned::numeric / NULLIF(heap_blks_total,0) * 100, 2) AS pct\nFROM pg_stat_progress_vacuum;\n-- Others: pg_stat_progress_create_index, _cluster, _analyze, _copy, _basebackup","note":"Die Views pg_stat_progress_* melden den Fortschritt von VACUUM, CREATE INDEX, CLUSTER, ANALYZE und COPY.","danger":"safe","readonly":true,"minver":"9.6"},"mysql":{"command":"SELECT t.processlist_id, st.event_name, st.work_completed, st.work_estimated,\n       ROUND(st.work_completed / NULLIF(st.work_estimated,0) * 100, 2) AS pct\nFROM performance_schema.events_stages_current st\nJOIN performance_schema.threads t ON t.thread_id = st.thread_id\nWHERE st.work_estimated > 0;","note":"Bei aktivierter Stage-Instrumentierung zeigt events_stages_current den Fortschritt über work_completed/work_estimated (z. B. ALTER TABLE).","danger":"safe","readonly":true,"priv":"SELECT auf performance_schema"},"mssql":{"command":"SELECT session_id, command, percent_complete,\n       DATEADD(ms, estimated_completion_time, GETDATE()) AS est_finish,\n       total_elapsed_time / 1000 AS elapsed_sec\nFROM sys.dm_exec_requests\nWHERE percent_complete > 0\nORDER BY percent_complete;","note":"sys.dm_exec_requests.percent_complete liefert den Prozentwert für Operationen wie BACKUP, RESTORE, DBCC und Index-Rebuilds.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.currentOp({ \"active\": true, \"secs_running\": { \"$gte\": 3 } })","note":"In der Ausgabe von db.currentOp zeigen lang laufende Operationen und bei Index-Builds das Feld 'progress' (done/total) den Fortschritt.","danger":"safe","priv":"Rolle inprog / clusterMonitor"},"redis":{"command":"INFO persistence\n# Keys: rdb_bgsave_in_progress, aof_rewrite_in_progress, rdb_last_bgsave_status, loading, loading_loaded_perc","note":"Der Abschnitt INFO persistence meldet, ob RDB/AOF-Save- oder Rewrite-Vorgänge laufen, sowie den Ladefortschritt in Prozent.","danger":"safe","readonly":true},"elasticsearch":{"command":"GET _tasks?detailed=true&actions=*reindex,*update/byquery,*delete/byquery\n# For recovery/shard relocation: GET _cat/recovery?v&active_only=true","note":"_tasks?detailed zeigt den Status lang laufender Tasks (reindex, update_by_query); _cat/recovery zeigt den Fortschritt aktiver Shard-Recoverys.","danger":"safe","readonly":true}}},{"id":"session-trace","title":"Session / Abfrage tracen (Trace / Profiling aktivieren)","aliases":["trace","enable tracing","profiling","session trace","query tracing"],"engines":{"oracle":{"command":"-- For a specific session (10046 event tracing):\nBEGIN\n  DBMS_MONITOR.SESSION_TRACE_ENABLE(session_id => 123, serial_num => 4567, waits => TRUE, binds => TRUE);\nEND;\n/\n-- To disable: DBMS_MONITOR.SESSION_TRACE_DISABLE(123, 4567);","note":"DBMS_MONITOR.SESSION_TRACE_ENABLE erzeugt eine 10046-Tracedatei für die Session; danach immer deaktivieren (Tracing erzeugt Overhead und Plattenverbrauch).","danger":"caution","priv":"EXECUTE auf DBMS_MONITOR"},"postgres":{"command":"LOAD 'auto_explain';\nSET auto_explain.log_min_duration = 0;\nSET auto_explain.log_analyze = true;\n-- Simple alternative: SET log_min_duration_statement = 0;","note":"auto_explain schreibt Abfragepläne und log_min_duration_statement langsame/alle Abfragen in die Logdatei; ein Wert von 0 protokolliert jede Abfrage, daher auf das Logvolumen achten.","danger":"caution","priv":"SUPERUSER (für LOAD) oder SET auf Session-Ebene"},"mysql":{"command":"-- performance_schema approach (recommended):\nUPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME LIKE 'events_statements%';\n-- Legacy/simple method (being deprecated):\nSET profiling = 1;\n-- run the query, then:\nSHOW PROFILES;","note":"Abfrage-Tracing erfolgt mit performance_schema events_statements; die Methode SET profiling ist alt (deprecated), liefert aber eine schnelle Messung pro Session.","danger":"caution","priv":"SELECT/UPDATE auf performance_schema"},"mssql":{"command":"CREATE EVENT SESSION [trace_slow] ON SERVER\nADD EVENT sqlserver.sql_statement_completed(\n  WHERE duration > 1000000)\nADD TARGET package0.event_file(SET filename=N'trace_slow.xel');\nGO\nALTER EVENT SESSION [trace_slow] ON SERVER STATE = START;","note":"Extended Events (XEvents) ist die moderne Tracing-Methode; die Session anlegen, mit START starten und danach mit STOP/DROP beenden.","danger":"caution","priv":"ALTER ANY EVENT SESSION"},"mongodb":{"command":"db.setProfilingLevel(2)\n// 1 = slow operations only (slowms threshold), 0 = off\n// Results: db.system.profile.find().sort({ ts: -1 }).limit(10)","note":"setProfilingLevel(2) zeichnet alle Operationen in der Collection system.profile auf; in Produktion ist 1 (nur langsame) vorzuziehen, da Stufe 2 Overhead erzeugt.","danger":"caution","priv":"dbAdmin / enableProfiler"},"redis":{"command":"MONITOR\n# Streams all commands arriving at the server in real time. Exit: Ctrl-C","note":"MONITOR zeigt alle Befehle live, verursacht aber einen deutlichen Performanceeinbruch; in Produktion nur kurz und vorsichtig einsetzen. Alternative: SLOWLOG GET.","danger":"caution"},"elasticsearch":{"command":"GET my-index/_search\n{\n  \"profile\": true,\n  \"query\": { \"match\": { \"title\": \"elastic\" } }\n}\n// Persistent slow query tracing: the index.search.slowlog.threshold.query.warn setting","note":"\"profile\": true liefert die Zeitaufschlüsselung von Query/Aggregation einer einzelnen Suche; für kontinuierliches Monitoring Slowlog-Schwellenwerte konfigurieren.","danger":"safe","readonly":true}}},{"id":"top-wait-events","title":"Top Wait Events anzeigen","aliases":["wait events","wait event list","top waits","waits"],"engines":{"oracle":{"command":"SELECT event, total_waits, time_waited, ROUND(time_waited/100,2) AS sec_waited\nFROM v$system_event\nWHERE wait_class <> 'Idle'\nORDER BY time_waited DESC\nFETCH FIRST 20 ROWS ONLY;\n-- For real-time analysis: v$active_session_history (ASH)","note":"v$system_event liefert kumulierte Wait Events; v$active_session_history liefert Active Session Sampling der letzten Minuten (erfordert die Diagnostics-Pack-Lizenz).","danger":"safe","readonly":true,"priv":"SELECT auf v$system_event"},"postgres":{"command":"SELECT wait_event_type, wait_event, count(*)\nFROM pg_stat_activity\nWHERE wait_event IS NOT NULL AND state = 'active'\nGROUP BY 1, 2\nORDER BY 3 DESC;","note":"pg_stat_activity zeigt den aktuellen Wartezustand; wiederholtes Abfragen (Polling) zeigt die häufigsten Wait Events.","danger":"safe","readonly":true,"priv":"Rolle pg_monitor"},"mysql":{"command":"SELECT event_name, count_star, sum_timer_wait,\n       ROUND(sum_timer_wait/1000000000000, 2) AS sec_waited\nFROM performance_schema.events_waits_summary_global_by_event_name\nWHERE count_star > 0\nORDER BY sum_timer_wait DESC\nLIMIT 20;","note":"events_waits_summary_global_by_event_name liefert die gesamten Wartezeiten pro Event (in Pikosekunden); Wait-Instrumente müssen aktiviert sein.","danger":"safe","readonly":true,"priv":"SELECT auf performance_schema"},"mssql":{"command":"SELECT wait_type, waiting_tasks_count, wait_time_ms,\n       signal_wait_time_ms, wait_time_ms - signal_wait_time_ms AS resource_wait_ms\nFROM sys.dm_os_wait_stats\nWHERE wait_time_ms > 0\nORDER BY wait_time_ms DESC;","note":"sys.dm_os_wait_stats zeigt kumulierte Wait Types seit dem Start der Instanz; mit DBCC SQLPERF zurücksetzbar.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.serverStatus().wiredTiger.concurrentTransactions\n// For general waits/congestion:\n// db.serverStatus().globalLock  and  db.serverStatus().wiredTiger.cache","note":"WiredTiger hat keine klassischen Wait Events; erschöpfte concurrentTransactions (Tickets), globalLock.currentQueue und Cache-Druck deuten auf Engpässe hin.","danger":"safe","priv":"Rolle clusterMonitor"},"redis":{"command":"CONFIG SET latency-monitor-threshold 100\nLATENCY LATEST\nLATENCY HISTORY command\n# For the slowest commands: SLOWLOG GET 10","note":"Sobald der Schwellenwert des Latency Monitors gesetzt ist, melden LATENCY LATEST/HISTORY die Events mit der höchsten Latenz.","danger":"safe"},"elasticsearch":{"command":"GET _nodes/hot_threads","note":"_nodes/hot_threads deckt Engpässe auf, indem es Stack Traces der Threads mit dem höchsten CPU-Verbrauch liefert.","danger":"safe","readonly":true}}},{"id":"os-resource","title":"Ressourcennutzung des Servers aus der DB heraus anzeigen (CPU/IO)","aliases":["os resource","cpu io","resource usage","server resources","system metrics"],"engines":{"oracle":{"command":"SELECT stat_name, value FROM v$osstat\nWHERE stat_name IN ('NUM_CPUS','BUSY_TIME','IDLE_TIME','IOWAIT_TIME','PHYSICAL_MEMORY_BYTES');\n-- Real-time metrics: SELECT metric_name, value FROM v$sysmetric WHERE group_id = 2;","note":"v$osstat liefert CPU-/Speicherzähler des OS; v$sysmetric liefert Echtzeit-DB-Metriken (CPU, IOPS) für das letzte Intervall.","danger":"safe","readonly":true,"priv":"SELECT auf v$osstat, v$sysmetric"},"postgres":{"command":"SELECT backend_type, object, context, reads, writes, hits, evictions\nFROM pg_stat_io\nORDER BY reads DESC;\n-- Note: CPU usage is not visible from inside the DB; an OS tool (top, sar) is required.","note":"pg_stat_io liefert Zähler für Lesen/Schreiben/Cache-Treffer; PostgreSQL gibt die CPU-Nutzung nicht aus, daher sind Tools des Betriebssystems nötig.","danger":"safe","readonly":true,"minver":"16"},"mysql":{"command":"SELECT * FROM sys.host_summary;\n-- For InnoDB I/O and thread details:\nSHOW ENGINE INNODB STATUS\\G","note":"Die Summary-Views des sys-Schemas (host_summary) liefern eine IO-/Latenzaufschlüsselung; SHOW ENGINE INNODB STATUS zeigt Buffer-Pool- und IO-Aktivität.","danger":"safe","readonly":true,"priv":"SELECT auf sys, PROCESS"},"mssql":{"command":"SELECT object_name, counter_name, cntr_value\nFROM sys.dm_os_performance_counters\nWHERE counter_name LIKE '%CPU%' OR counter_name LIKE '%Page life expectancy%';\n-- CPU history: sys.dm_os_ring_buffers (RING_BUFFER_SCHEDULER_MONITOR)","note":"sys.dm_os_performance_counters liefert DB-Performancezähler; dm_os_ring_buffers liefert das CPU-Verhältnis SQL/andere Prozesse für die letzten ~256 Minuten.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.serverStatus().metrics\n// Relevant fields: .opcounters, .network, .wiredTiger.cache, .mem, .extra_info.page_faults","note":"serverStatus() liefert Metriken zu Speicher, Netzwerk, opcounters und WiredTiger-Cache; für detaillierte OS-CPU/IO mongostat oder Tools des Betriebssystems verwenden.","danger":"safe","priv":"Rolle clusterMonitor"},"redis":{"command":"INFO cpu\nINFO stats\n# used_cpu_sys, used_cpu_user, instantaneous_ops_per_sec, total_net_input_bytes","note":"INFO cpu meldet den CPU-Verbrauch des Redis-Prozesses, INFO stats die Operationen pro Sekunde und den Netzwerkverkehr.","danger":"safe","readonly":true},"elasticsearch":{"command":"GET _nodes/stats/os,process\n// Look at the os.cpu.percent, os.mem, process.cpu.percent, fs (disk) fields","note":"_nodes/stats/os,process liefert CPU-Prozent, Speicher und Prozessmetriken pro Knoten; für die Platte den Abschnitt fs verwenden.","danger":"safe","readonly":true}}}]},"rosetta/automation.json":{"category":"Automatisierung & Auditing","categoryId":"automation","order":13,"concepts":[{"id":"schedule-job","title":"Geplanten Job anlegen","aliases":["schedule job","scheduled job","cron","create job","scheduled task"],"engines":{"oracle":{"command":"BEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n    job_name        => 'NIGHTLY_MAINT',\n    job_type        => 'PLSQL_BLOCK',\n    job_action      => 'BEGIN my_maintenance_proc; END;',\n    start_date      => SYSTIMESTAMP,\n    repeat_interval => 'FREQ=DAILY; BYHOUR=3',\n    enabled         => TRUE);\nEND;\n/","note":"DBMS_SCHEDULER.CREATE_JOB definiert einen geplanten Job; repeat_interval legt die Wiederholung mit einem Calendaring-Ausdruck fest.","danger":"caution","priv":"CREATE JOB"},"postgres":{"command":"SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM ANALYZE');\n-- Requires the pg_cron extension. Alternative: pgAgent.","note":"Die Extension pg_cron definiert Jobs in Standard-Cron-Syntax mit cron.schedule; ist die Extension nicht installiert, pgAgent oder den Cron des Betriebssystems verwenden.","danger":"caution","priv":"pg_cron installiert; meist Superuser/Extension-Eigentümer"},"mysql":{"command":"SET GLOBAL event_scheduler = ON;\nCREATE EVENT nightly_cleanup\nON SCHEDULE EVERY 1 DAY STARTS '2026-01-01 03:00:00'\nDO\n  DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY;","note":"CREATE EVENT läuft über den Event Scheduler; die globale Variable event_scheduler muss ON sein, sonst werden Events nicht ausgelöst.","danger":"caution","priv":"EVENT"},"mssql":{"command":"USE msdb;\nEXEC dbo.sp_add_job @job_name = N'NightlyMaint';\nEXEC dbo.sp_add_jobstep @job_name = N'NightlyMaint', @step_name = N'Step1',\n     @subsystem = N'TSQL', @command = N'EXEC dbo.usp_Maintenance;';\nEXEC dbo.sp_add_schedule @schedule_name = N'Daily3AM',\n     @freq_type = 4, @freq_interval = 1, @active_start_time = 030000;\nEXEC dbo.sp_attach_schedule @job_name = N'NightlyMaint', @schedule_name = N'Daily3AM';\nEXEC dbo.sp_add_jobserver @job_name = N'NightlyMaint';","note":"SQL-Server-Agent-Jobs werden mit msdb-Prozeduren angelegt: Job, Step und Schedule werden definiert und dem Server zugeordnet. Der Dienst SQL Server Agent muss laufen.","danger":"caution","priv":"SQLAgentUserRole oder sysadmin"},"mongodb":{"command":"# Not applicable: MongoDB has no built-in scheduler. Use external cron/systemd timers, an application scheduler or a MongoDB Atlas Scheduled Trigger.","note":"Die Community Edition hat keine geplanten Jobs; ein externer Scheduler oder ein Atlas Trigger ist erforderlich.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no built-in job scheduler. Use EXPIRE/keyspace notifications for key expiry and external cron for periodic tasks.","note":"Redis führt keine geplanten Jobs aus; das wird mit TTL/Keyspace Notifications und externem Cron gelöst.","danger":"safe"},"elasticsearch":{"command":"PUT _slm/policy/daily-snapshot\n{\n  \"schedule\": \"0 30 1 * * ?\",\n  \"name\": \"<snap-{now/d}>\",\n  \"repository\": \"backup_repo\",\n  \"config\": { \"indices\": [\"*\"] },\n  \"retention\": { \"expire_after\": \"30d\" }\n}","note":"SLM (Snapshot Lifecycle Management) erstellt geplante Snapshots; ILM dient dem Datenlebenszyklus und Watcher fortgeschrittener Automatisierung.","danger":"caution","priv":"Cluster-Berechtigung manage_slm"}}},{"id":"list-jobs","title":"Geplante Jobs auflisten / Status anzeigen","aliases":["list jobs","show jobs","job status","scheduled jobs","job history"],"engines":{"oracle":{"command":"SELECT job_name, enabled, state, last_start_date, next_run_date\nFROM dba_scheduler_jobs\nORDER BY next_run_date;\n-- Run history: SELECT job_name, status, actual_start_date, run_duration\n--                 FROM dba_scheduler_job_run_details ORDER BY log_date DESC;","note":"dba_scheduler_jobs zeigt die definierten Jobs und ihre Zustände; dba_scheduler_job_run_details zeigt Erfolg/Fehler vergangener Läufe.","danger":"safe","readonly":true,"priv":"SELECT auf dba_scheduler_jobs"},"postgres":{"command":"SELECT jobid, schedule, command, nodename, active FROM cron.job;\n-- Run history:\nSELECT jobid, status, return_message, start_time, end_time\nFROM cron.job_run_details ORDER BY start_time DESC LIMIT 20;","note":"Mit pg_cron listet cron.job die definierten Jobs auf, und cron.job_run_details liefert Status und Rückmeldung jedes Laufs.","danger":"safe","readonly":true,"priv":"pg_cron installiert; Zugriff auf das Schema cron"},"mysql":{"command":"SELECT event_schema, event_name, status, interval_value, interval_field, last_executed\nFROM information_schema.events\nORDER BY last_executed DESC;","note":"information_schema.events listet die definierten Events, ihren Status (ENABLED/DISABLED) und die letzte Ausführungszeit auf.","danger":"safe","readonly":true,"priv":"EVENT / SELECT"},"mssql":{"command":"SELECT j.name, j.enabled, h.run_status, h.run_date, h.run_time\nFROM msdb.dbo.sysjobs j\nLEFT JOIN msdb.dbo.sysjobhistory h ON h.job_id = j.job_id AND h.step_id = 0\nORDER BY h.run_date DESC, h.run_time DESC;","note":"msdb.dbo.sysjobs liefert die definierten Agent-Jobs, sysjobhistory die Ausführungshistorie und das Ergebnis (run_status).","danger":"safe","readonly":true,"priv":"SQLAgentReaderRole / Zugriff auf msdb"},"mongodb":{"command":"# Not applicable: there is no built-in scheduler, so there are no jobs to list. If Atlas Triggers are used, view them in the Atlas UI/API.","note":"Community-MongoDB hat keine Job-Tabelle; Atlas Triggers werden in der Atlas-Oberfläche verwaltet.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no concept of scheduled jobs, so there are no jobs to list.","note":"Redis enthält keinen Job Scheduler; es gibt keine Jobs zum Auflisten.","danger":"safe"},"elasticsearch":{"command":"GET _slm/policy\nGET _ilm/policy\n// For Watcher states: GET _watcher/stats  and  GET _watcher/_query/watches","note":"_slm/policy listet Snapshot-Policies, _ilm/policy Lifecycle-Policies auf, und _watcher/stats zeigt den Ausführungszustand von Watcher.","danger":"safe","readonly":true,"priv":"Berechtigung read_slm / read_ilm"}}},{"id":"enable-audit","title":"Auditing / Logging aktivieren","aliases":["audit","auditing","logging","enable audit","audit log"],"engines":{"oracle":{"command":"-- Unified Auditing (12c+):\nCREATE AUDIT POLICY table_audit\n  ACTIONS SELECT, INSERT, UPDATE, DELETE ON hr.employees;\nAUDIT POLICY table_audit;\n-- Monitoring: SELECT * FROM unified_audit_trail ORDER BY event_timestamp DESC;","note":"Mit Unified Auditing wird eine Policy angelegt und mit AUDIT aktiviert; die Einträge stehen in der View unified_audit_trail.","danger":"caution","priv":"Rolle AUDIT SYSTEM / AUDIT_ADMIN","minver":"12.1"},"postgres":{"command":"-- pgaudit extension (recommended; first shared_preload_libraries = 'pgaudit' + restart + CREATE EXTENSION pgaudit):\nALTER SYSTEM SET pgaudit.log = 'write, ddl';\nSELECT pg_reload_conf();\n-- Simple built-in method:\nALTER SYSTEM SET log_statement = 'ddl';\nSELECT pg_reload_conf();","note":"pgaudit bietet detailliertes Audit-Logging; mit ALTER SYSTEM setzen, damit es serverweit gilt (ein SET auf Session-Ebene auditiert nur diese Session). Ohne die Extension wird einfaches Logging mit log_statement ('ddl','mod','all') aktiviert. Auf das Logvolumen achten.","danger":"caution","priv":"Superuser (ALTER SYSTEM)"},"mysql":{"command":"-- Enterprise/MariaDB audit plugin:\nINSTALL PLUGIN audit_log SONAME 'audit_log.so';\nSET GLOBAL audit_log_policy = 'ALL';\n-- Simple alternative (all queries):\nSET GLOBAL general_log = 'ON';","note":"Das Plugin audit_log (Enterprise, bzw. server_audit in MariaDB) führt einen detaillierten Audit Trail; general_log protokolliert alle Abfragen, verschlechtert aber die Performance.","danger":"caution","priv":"SUPER / SYSTEM_VARIABLES_ADMIN"},"mssql":{"command":"USE master;\nCREATE SERVER AUDIT Audit_A TO FILE (FILEPATH = 'C:\\\\audit\\\\');\nALTER SERVER AUDIT Audit_A WITH (STATE = ON);\nUSE mydb;\nCREATE DATABASE AUDIT SPECIFICATION db_spec\n  FOR SERVER AUDIT Audit_A\n  ADD (SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo BY public)\n  WITH (STATE = ON);","note":"Zuerst ein SERVER AUDIT-Ziel (Datei/Event Log) anlegen und aktivieren, dann mit einer Server- oder Datenbank-AUDIT SPECIFICATION festlegen, welche Events erfasst werden.","danger":"caution","priv":"ALTER ANY SERVER AUDIT / CONTROL SERVER"},"mongodb":{"command":"# MongoDB Enterprise / Atlas only. Configuration (mongod.conf):\n#   auditLog:\n#     destination: file\n#     format: JSON\n#     path: /var/log/mongodb/audit.json\n#     filter: '{ atype: { $in: [\"authCheck\",\"createCollection\",\"dropCollection\"] } }'\n# mongod --auditDestination file --auditPath /var/log/mongodb/audit.json","note":"Auditing gibt es nur in Enterprise und Atlas; es wird über den Abschnitt auditLog der mongod-Konfiguration aktiviert. In der Community Edition existiert es nicht.","danger":"caution"},"redis":{"command":"ACL LOG\n# Shows rejected authentication/command/key access attempts.\n# To reset: ACL LOG RESET\n# For general command auditing: an external proxy or MONITOR (with care).","note":"ACL LOG zeichnet Sicherheitsereignisse automatisch auf (fehlgeschlagene Authentifizierung, unberechtigte Befehle/Keys); vollständiges Befehls-Auditing ist nicht eingebaut.","danger":"safe","priv":"ACL-Berechtigung (standardmäßig admin)"},"elasticsearch":{"command":"# elasticsearch.yml (on all nodes, restart required):\nxpack.security.audit.enabled: true\n# Optional event filter:\nxpack.security.audit.logfile.events.include: [ \"authentication_failed\", \"access_denied\" ]","note":"Audit-Logging wird mit xpack.security.audit.enabled aktiviert; es wird in elasticsearch.yml gesetzt und erfordert einen Neustart des Knotens (nicht dynamisch). Erfordert eine kommerzielle Lizenz.","danger":"caution"}}},{"id":"current-config-diff","title":"Vom Standard abweichende Einstellungen auflisten","aliases":["config diff","changed settings","non-default settings","modified settings","modified config"],"engines":{"oracle":{"command":"SELECT name, value, isdefault\nFROM v$parameter\nWHERE isdefault = 'FALSE'\nORDER BY name;","note":"Zeilen in v$parameter mit isdefault = 'FALSE' zeigen Parameter, die vom Standard abweichen (explizit gesetzt).","danger":"safe","readonly":true,"priv":"SELECT auf v$parameter"},"postgres":{"command":"SELECT name, setting, unit, source\nFROM pg_settings\nWHERE source NOT IN ('default', 'override')\nORDER BY name;","note":"Ist pg_settings.source etwas anderes als 'default' und 'override', wurde die Einstellung durch eine Konfigurationsdatei, die Kommandozeile oder die Session geändert.","danger":"safe","readonly":true},"mysql":{"command":"SHOW GLOBAL VARIABLES;\n-- MySQL does not automatically compare variables with defaults; with performance_schema\n-- you can see the source of each setting:\nSELECT VARIABLE_NAME, VARIABLE_SOURCE, VARIABLE_VALUE\nFROM performance_schema.variables_info\nWHERE VARIABLE_SOURCE <> 'COMPILED';","note":"SHOW GLOBAL VARIABLES liefert alle Einstellungen; die Abweichung vom Standard zeigt es nicht direkt. Die Spalte VARIABLE_SOURCE von performance_schema.variables_info zeigt Änderungen gegenüber dem kompilierten Standard (Datei, Kommandozeile, dynamisch).","danger":"safe","readonly":true,"minver":"8.0"},"mssql":{"command":"SELECT name, value, value_in_use, is_dynamic, is_advanced\nFROM sys.configurations\nWHERE value <> value_in_use\nORDER BY name;\n-- value <> value_in_use: changes not yet RECONFIGUREd or awaiting a restart (is_dynamic = 0).","note":"sys.configurations liefert die Servereinstellungen; unterscheiden sich value und value_in_use, gibt es eine nicht mit RECONFIGURE übernommene Änderung (oder sie wartet auf einen Neustart). sys.configurations speichert den Standardwert nicht (minimum/maximum ist nur der zulässige Bereich); um Abweichungen vom Standard zu sehen, die Werte mit den dokumentierten Standardwerten vergleichen.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.adminCommand({ getCmdLineOpts: 1 })\n// The 'parsed' field shows settings explicitly provided (configuration file/command line) beyond the defaults.","note":"Der Abschnitt 'parsed' des Befehls getCmdLineOpts listet nur explizit gesetzte (nicht standardmäßige) Einstellungen.","danger":"safe","priv":"Privileg clusterMonitor / hostInfo"},"redis":{"command":"CONFIG GET *\n# Redis lists all settings but does not compare them with defaults.\n# To see differences, compare the output with the default redis.conf values for your version.","note":"CONFIG GET * liefert alle laufenden Einstellungen; einen eingebauten Vergleich mit den Standardwerten gibt es nicht, vergleiche die Ausgabe daher mit den Standardwerten der redis.conf der jeweiligen Version.","danger":"safe","readonly":true},"elasticsearch":{"command":"GET _cluster/settings\n// Returns only explicitly set persistent and transient settings.\n// For everything including defaults: GET _cluster/settings?include_defaults=true&flat_settings=true","note":"_cluster/settings liefert nicht standardmäßige (persistent/transient) Cluster-Einstellungen; mit include_defaults=true werden zum Vergleich auch die Standardwerte angezeigt.","danger":"safe","readonly":true}}}]},"rosetta/lifecycle.json":{"category":"Installation & Upgrade","categoryId":"lifecycle","order":14,"concepts":[{"id":"check-version","title":"Installierte Version und Patch-Stand anzeigen","aliases":["version","patch level","server version","patch level check","release","build"],"engines":{"oracle":{"command":"SELECT * FROM v$version;\n-- Registered RU/patch history (SQL layer):\nSELECT patch_id, action, status, action_time\nFROM dba_registry_sqlpatch ORDER BY action_time;\n-- From the OS shell (patches installed in ORACLE_HOME):\n-- $ORACLE_HOME/OPatch/opatch lspatches","note":"Das Banner von v$version zeigt die Version. dba_registry_sqlpatch zeigt, ob eingespielte Release Updates (RU) und One-off-Patches in der SQL-Schicht registriert wurden, 'opatch lspatches' zeigt die Binärpatches im Home.","danger":"safe","readonly":true,"priv":"SELECT auf v$version, dba_registry_sqlpatch"},"postgres":{"command":"SELECT version();\n-- Numeric version only:\nSHOW server_version;\nSELECT current_setting('server_version_num');","note":"version() liefert den vollständigen Text inklusive Build-Informationen. PostgreSQL hat keine separate Patch-Nummer; die Minor-Version (z. B. 16.3) gibt den Fix-Stand an.","danger":"safe","readonly":true},"mysql":{"command":"SELECT VERSION();\n-- Detailed build information:\nSHOW VARIABLES LIKE 'version%';","note":"VERSION() liefert z. B. '8.0.36'; die letzte Stelle ist der Patch-(Release-)Stand. Die Variable version_comment zeigt, ob es sich um einen Community-/Enterprise-Build oder MariaDB handelt.","danger":"safe","readonly":true},"mssql":{"command":"SELECT @@VERSION;\n-- Structured version fields:\nSELECT SERVERPROPERTY('ProductVersion')       AS version,\n       SERVERPROPERTY('ProductLevel')         AS service_pack,\n       SERVERPROPERTY('ProductUpdateLevel')   AS cu_level,\n       SERVERPROPERTY('ProductUpdateReference') AS kb_number,\n       SERVERPROPERTY('Edition')              AS edition;","note":"ProductUpdateLevel zeigt das eingespielte Cumulative Update (z. B. CU12), ProductUpdateReference die zugehörige KB-Nummer. ProductLevel zeigt 'RTM' bzw. bei älteren Versionen 'SP'.","danger":"safe","readonly":true},"mongodb":{"command":"db.version()\n// Detailed build information:\ndb.serverBuildInfo()\n// or: db.adminCommand({ buildInfo: 1 })","note":"db.version() liefert den Versionsstring (z. B. '7.0.11'). In MongoDB werden Fixes als neue Patch-Version veröffentlicht; ein separates Konzept 'Patch-Stand' gibt es nicht.","danger":"safe"},"redis":{"command":"INFO server\n# redis-cli one-liner: redis-cli INFO server | grep redis_version","note":"Das Feld redis_version in der Ausgabe von INFO server zeigt die installierte Version. In Redis werden Fixes als neue Patch-Version ausgeliefert (z. B. 7.2.5); das Feld redis_git_sha1 zeigt zusätzlich die Build-Kennung.","danger":"safe"},"elasticsearch":{"command":"GET /\n# curl: curl -s http://localhost:9200/","note":"Der Root-Endpunkt liefert version.number (z. B. '8.14.1'), lucene_version und build_flavor. Versionen aller Knoten anzeigen: GET _cat/nodes?v&h=name,version","danger":"safe","readonly":true}}},{"id":"check-upgrade-readiness","title":"Kompatibilitäts-/Bereitschaftsprüfung vor dem Upgrade","aliases":["upgrade readiness","preupgrade","compatibility check","readiness","compatibility check","deprecation"],"engines":{"oracle":{"command":"-- Recommended method for 19c and later (AutoUpgrade analyze mode):\n-- $ java -jar autoupgrade.jar -config db.cfg -mode analyze\n-- Legacy/alternative: preupgrade script (produces a report only):\n-- $ $NEW_ORACLE_HOME/jdk/bin/java -jar $NEW_ORACLE_HOME/rdbms/admin/preupgrade.jar FILE TEXT DIR /tmp","note":"Der Modus 'analyze' von AutoUpgrade erstellt einen Pre-Upgrade-Bericht, ohne Daten zu ändern; er listet Warnungen, die behoben werden müssen, und Punkte, die automatisch gelöst werden. Führe ihn immer vor dem eigentlichen Upgrade aus.","danger":"safe"},"postgres":{"command":"# With the new version's binaries, compatibility check only (moves no data):\n$ pg_upgrade \\\n    --old-datadir=/var/lib/pgsql/16/data \\\n    --new-datadir=/var/lib/pgsql/17/data \\\n    --old-bindir=/usr/pgsql-16/bin \\\n    --new-bindir=/usr/pgsql-17/bin \\\n    --check","note":"--check prüft, ob das Upgrade durchführbar ist, ohne Daten zu ändern (meldet fehlende Extensions, inkompatible Datentypen usw.). --check kann auch bei laufendem altem Cluster ausgeführt werden; das eigentliche Upgrade erfolgt bei gestoppten Clustern.","danger":"safe"},"mysql":{"command":"-- 8.0 and later: Upgrade Checker utility (MySQL Shell):\n-- $ mysqlsh -- util check-for-server-upgrade { --user=root --host=localhost }\n-- Table consistency for legacy 5.7->8.0:\n$ mysqlcheck --all-databases --check-upgrade -u root -p","note":"util.checkForServerUpgrade() der MySQL Shell meldet entfernte Features, Konflikte mit reservierten Wörtern und inkompatible Definitionen. Ab 8.0 führt der Server das Dictionary-Upgrade beim Start selbst durch; ein separates mysql_upgrade ist nicht mehr nötig.","danger":"safe"},"mssql":{"command":"-- Run from the desktop with Data Migration Assistant (DMA):\n-- DmaCmd.exe /AssessmentName=... /AssessmentDatabases=...\n-- (Produces compatibility/deprecated feature and blocker reports.)","note":"Data Migration Assistant (DMA) hat den alten Upgrade Advisor abgelöst; er meldet pro Datenbank veraltete Features, Breaking Changes und Blocker. Führe ihn vor dem Upgrade aus und behebe die Befunde.","danger":"safe"},"mongodb":{"command":"// Verify the current Feature Compatibility Version (FCV):\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })\n// Compatibility items (deprecations) in the release notes are reviewed manually.","note":"Vor dem Upgrade der Binärversion muss die FCV der aktuellen Version entsprechen, sonst wird das Upgrade blockiert. MongoDB hat keinen automatischen 'Deprecation-Scanner'; die Kompatibilitätshinweise der Release Notes werden manuell geprüft.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no separate pre-upgrade readiness tool.\n# Checklist: read the target version's release notes for RDB/AOF persistence format,\n# changed configuration directives, and removed commands/arguments.","note":"Redis-Upgrades sind in der Regel vorwärtskompatibel; da es keinen offiziellen Bereitschaftsscanner gibt, prüfe RDB-/AOF-Dateiformat, entfernte Konfigurationsdirektiven und Befehlsänderungen manuell anhand der Release Notes.","danger":"safe"},"elasticsearch":{"command":"GET _migration/deprecations\n# For a specific component: GET <index>/_migration/deprecations","note":"Die Deprecation Info API listet Cluster-, Knoten-, Index- und ML-Einstellungen, die beim Wechsel auf die nächste Major-Version Probleme verursachen, als 'critical' / 'warning'. Der Kibana Upgrade Assistant visualisiert dieselben Daten.","danger":"safe","readonly":true}}},{"id":"apply-patch","title":"Patch einspielen","aliases":["apply patch","patching","patch","minor upgrade","hotfix","ru apply"],"engines":{"oracle":{"command":"# 1) Shut down the database, apply the binary patch with OPatch:\n$ $ORACLE_HOME/OPatch/opatch apply\n# 2) Open the database and load the SQL layer changes:\n$ $ORACLE_HOME/OPatch/datapatch -verbose","note":"opatch apply aktualisiert die Binärdateien im Oracle Home; datapatch spielt anschließend die Dictionary-/SQL-Änderungen in der geöffneten Datenbank ein und vermerkt sie in dba_registry_sqlpatch. Bei RAC lassen sich rolling-fähige Patches Knoten für Knoten einspielen.","danger":"caution","priv":"OS: Eigentümer des ORACLE_HOME; SQL: SYSDBA"},"postgres":{"command":"# Minor version patch = replace binaries + restart (no dump needed):\n$ sudo systemctl stop postgresql-16\n$ sudo dnf update postgresql16-server   # or apt upgrade\n$ sudo systemctl start postgresql-16","note":"Minor-Patches innerhalb derselben Major-Versionsreihe (16.3 -> 16.4) ändern das Datenformat nicht: Paket aktualisieren und Dienst neu starten genügt; pg_upgrade oder Dump/Restore ist nicht nötig.","danger":"caution"},"mysql":{"command":"# Minor/patch update: update the package and restart the service\n$ sudo systemctl stop mysqld\n$ sudo dnf update mysql-community-server   # or apt upgrade\n$ sudo systemctl start mysqld","note":"MySQL 8.0+ aktualisiert das Data Dictionary beim Start automatisch; ein separater Schritt mysql_upgrade ist nicht nötig. Bei Replikation zuerst die Replicas patchen, dann die Source.","danger":"caution"},"mssql":{"command":"-- Run the Cumulative Update (CU) or GDR security update installer:\n-- SQLServer2022-KB<no>-x64.exe /quiet /IAcceptSQLServerLicenseTerms\n--   /Action=Patch /InstanceName=MSSQLSERVER\n-- The instance then restarts; verify the version with SERVERPROPERTY.","note":"CU- (Cumulative Update) und GDR-Updates (General Distribution Release) werden mit dem Setup-Installer eingespielt und erfordern einen Neustart der Instanz. In einer Always On-Gruppe zuerst die sekundären Replicas patchen und per Failover umschalten, um die Ausfallzeit zu minimieren.","danger":"caution"},"mongodb":{"command":"// 'Rolling' patch on a replica set: one node at a time\n// 1) Stop mongod, replace the binary with the new patch version, restart\n// 2) Secondaries first, the primary last\n// Step down the primary manually before patching it:\nrs.stepDown()","note":"Patch-Updates innerhalb derselben Major-Version (7.0.x) erfolgen rollierend, zuerst die Secondaries. Der Primary wird gepatcht, nachdem er mit rs.stepDown() abgegeben wurde; so bleibt die Schreibunterbrechung minimal.","danger":"caution"},"redis":{"command":"# Patch via rolling restart in a replicated deployment:\n# 1) Stop one replica node, install the new version, start it, wait for sync\n# 2) Repeat for the remaining replicas\n# 3) Finally, fail over the master and patch it:\nredis-cli -h <replica> REPLICAOF NO ONE   # promote the upgraded replica","note":"Patch-Versionen (7.2.x) werden per Rolling Restart eingespielt: Zuerst werden die Replicas aktualisiert und neu synchronisiert, dann wird eine Replica zum Master befördert und der alte Master gepatcht. Sentinel/Cluster automatisiert den Failover.","danger":"caution"},"elasticsearch":{"command":"# Rolling upgrade (patch within the same major version), per node:\nPUT _cluster/settings\n{ \"persistent\": { \"cluster.routing.allocation.enable\": \"primaries\" } }\n# -> stop the node, update the package, start the node, then:\nPUT _cluster/settings\n{ \"persistent\": { \"cluster.routing.allocation.enable\": null } }","note":"Patch-/Minor-Upgrades erfolgen per Rolling Upgrade: Die Shard-Allokation wird eingeschränkt, die Knoten werden einzeln aktualisiert und neu gestartet, dann wird die Allokation wieder aktiviert und gewartet, bis der Cluster wieder green ist.","danger":"caution"}}},{"id":"upgrade-major","title":"Major-Upgrade","aliases":["major upgrade","major version","version upgrade","release upgrade","version upgrade"],"engines":{"oracle":{"command":"# Recommended: AutoUpgrade (analyze + fixups + deploy in a single flow):\n$ java -jar $NEW_ORACLE_HOME/rdbms/admin/autoupgrade.jar -config db.cfg -mode deploy\n# Graphical alternative: DBUA (Database Upgrade Assistant)\n$ $NEW_ORACLE_HOME/bin/dbua","note":"AutoUpgrade führt Analyse, Pre-Fixups, Upgrade und Nacharbeiten mit einem einzigen Befehl aus; es kann mehrere Datenbanken nacheinander verarbeiten, um die Ausfallzeit zu verkürzen. DBUA macht dasselbe mit geführter Oberfläche. Ein vorheriges Vollbackup ist Pflicht.","danger":"danger","priv":"SYSDBA + OS: Eigentümer des neuen ORACLE_HOME"},"postgres":{"command":"# pg_upgrade to the new major version (with both clusters stopped):\n$ pg_upgrade \\\n    --old-datadir=/var/lib/pgsql/16/data \\\n    --new-datadir=/var/lib/pgsql/17/data \\\n    --old-bindir=/usr/pgsql-16/bin \\\n    --new-bindir=/usr/pgsql-17/bin \\\n    --link   # fast migration with hard links\n# Then gather statistics (analyze_new_cluster.sh was removed in PG14):\n$ vacuumdb --all --analyze-in-stages","note":"pg_upgrade führt die Migration auf eine neue Major-Version (16 -> 17) in place durch; der Modus --link verkürzt die Dauer, indem er Hardlinks statt Dateikopien anlegt, macht den alten Cluster aber unbrauchbar. Nach der Migration müssen die Statistiken neu erhoben werden.","danger":"danger"},"mysql":{"command":"# In-place upgrade (same datadir, new binaries):\n$ sudo systemctl stop mysqld\n$ sudo dnf install mysql-community-server-8.4   # replacing the old package\n$ sudo systemctl start mysqld   # the server upgrades the dictionary automatically","note":"Ab MySQL 8.x werden In-place-Major-Upgrades unterstützt: Ein mit den neuen Binärdateien gestarteter Server konvertiert Data Dictionary und Systemtabellen automatisch (der früher separate Schritt mysql_upgrade entfällt). Führe Upgrades jeweils nur um eine Major-Version durch.","danger":"danger"},"mssql":{"command":"-- In-place major version upgrade (with setup):\n-- setup.exe /q /ACTION=upgrade /INSTANCENAME=MSSQLSERVER\n--   /IAcceptSQLServerLicenseTerms\n-- Alternative: install a new instance side by side and move the databases (restore/attach).","note":"setup /ACTION=upgrade aktualisiert die Instanz in place auf die neue Major-Version. Sicherer ist eine Side-by-side-Installation, danach die Datenbanken per Backup/Restore umziehen und den Kompatibilitätsgrad in einem separaten Schritt anheben.","danger":"danger"},"mongodb":{"command":"// Stepwise major upgrade (e.g. 6.0 -> 7.0), one major version at a time:\n// 1) Roll the binary to 7.0 on all nodes\n// 2) Once the cluster is stable, raise the FCV:\ndb.adminCommand({ setFeatureCompatibilityVersion: \"7.0\", confirm: true })","note":"Ein Major-Upgrade in MongoDB hat zwei Phasen: Zuerst werden die Binärdateien aller Knoten rollierend auf die neue Version gebracht, dann wird bei stabilem Cluster die FCV auf die neue Version angehoben. Es kann jeweils nur um eine Major-Version aufgestiegen werden; die FCV wird für die Downgrade-Fähigkeit separat verwaltet.","danger":"danger"},"redis":{"command":"# Major upgrade via a replica (no downtime):\n# 1) Create a replica on the new version, wait for full sync from the master\n# 2) Prepare the application; fail over the master:\nredis-cli -h <new-replica> REPLICAOF NO ONE   # make the new version the master\n# 3) Upgrade the old master, attach it as a replica of the new master","note":"Eine Redis-Migration auf eine neue Major-Version (6.x -> 7.x) erfolgt, indem eine Replica mit der neuen Version aufgesetzt, synchronisiert und dann zum Master befördert wird; die Schreibunterbrechung sinkt so nahezu auf null. RDB-Dateien sind mit späteren Versionen kompatibel.","danger":"danger"},"elasticsearch":{"command":"# Major versions cannot be skipped; one major at a time, via the last minor first (e.g. 6.8 -> 7.17 -> 8.x):\n# 1) Upgrade to the last minor (7.17), resolve the GET _migration/deprecations warnings\n# 2) Rolling upgrade, per node: set allocation to \"primaries\" -> update the node -> start -> wait for green\n# Alternative: full-cluster restart (stop all nodes -> upgrade -> start together); this also moves only one major","note":"Elasticsearch unterstützt mit keiner Methode das Überspringen von Major-Versionen: Sowohl Rolling Upgrade als auch Full-Cluster-Restart wechseln nur auf die nächste Major-Version (n -> n+1) und setzen voraus, dass die Quelle auf der letzten Minor-Version ist (z. B. 7.17). Indizes, die zwei Major-Versionen früher (z. B. in 6.x) angelegt wurden, lassen sich in der neuen Version nicht öffnen; sie müssen vor der Migration mit _reindex neu aufgebaut werden (8.x kann sie nur eingeschränkt als schreibgeschützte Archivindizes lesen). Erstelle vor der Migration einen Snapshot und behebe die Deprecation-Warnungen.","danger":"danger"}}},{"id":"set-compatibility","title":"Kompatibilitätsgrad / Feature-Version setzen","aliases":["compatibility level","database compatibility level","feature version","fcv","compatible","compat level"],"engines":{"oracle":{"command":"-- Raise the COMPATIBLE initialization parameter (irreversible!):\nALTER SYSTEM SET COMPATIBLE = '19.0.0' SCOPE=SPFILE;\n-- Then restart the instance. To see the current value:\nSHOW PARAMETER compatible;","note":"Der Parameter COMPATIBLE bestimmt, welche Versionsfeatures und Dateiformate aktiviert sind. Einmal angehoben und die Instanz neu gestartet, ist er NICHT UMKEHRBAR (Downgrade wird blockiert); hebe ihn daher schrittweise an, bis der Erfolg des Upgrades bestätigt ist.","danger":"danger","priv":"ALTER SYSTEM (SYSDBA)"},"postgres":{"command":"# Not applicable: PostgreSQL has no database compatibility level.\n# If legacy behavior is needed, set the relevant GUCs (e.g. SET standard_conforming_strings=off)\n# or manage the major migration with dump/restore.","note":"PostgreSQL kennt keinen 'Kompatibilitätsgrad' wie SQL Server/Oracle; das Versionsverhalten hängt direkt an der Binärversion. Die Migration zwischen Versionen erfolgt mit pg_dump/pg_restore oder pg_upgrade.","danger":"safe"},"mysql":{"command":"# Not applicable: MySQL has no configurable database compatibility level.\n# Behavioral differences are managed with sql_mode:\nSET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';","note":"MySQL hat keinen versionsbasierten Kompatibilitätsgrad; das SQL-Verhalten hängt überwiegend von der Binärversion und der Einstellung sql_mode ab. Passe sql_mode an, um altes Anwendungsverhalten beizubehalten.","danger":"caution"},"mssql":{"command":"-- Set the database compatibility level (e.g. SQL Server 2022 = 160):\nALTER DATABASE Sales SET COMPATIBILITY_LEVEL = 160;\n-- To see the current value:\nSELECT name, compatibility_level FROM sys.databases;","note":"COMPATIBILITY_LEVEL bestimmt, wie sich Query Optimizer und Sprachfeatures verhalten (150=2019, 160=2022). Nach dem Upgrade der Instanz können Datenbanken auf dem alten Grad bleiben; empfohlen ist, ihn schrittweise anzuheben und Plan-Regressionen zu beobachten. Er lässt sich wieder absenken.","danger":"caution"},"mongodb":{"command":"// Set the Feature Compatibility Version (FCV):\ndb.adminCommand({ setFeatureCompatibilityVersion: \"7.0\", confirm: true })\n// Read the current value:\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })","note":"Die FCV steuert, wann persistente Änderungen am Datenformat und neue Features einer Version aktiviert werden. Hebe sie nach dem Binär-Upgrade bei stabilem Cluster an; ein Absenken ist nur möglich, wenn dieselbe Binärversion die niedrigere FCV noch unterstützt.","danger":"caution","priv":"Rolle clusterManager / root"},"redis":{"command":"# Not applicable: Redis has no compatibility level / feature version concept.\n# Behavior depends directly on the running binary version.","note":"Redis hat keinen einstellbaren Kompatibilitätsgrad; der Funktionsumfang hängt vollständig von der laufenden Version ab. Kompatibilität besteht nur auf Ebene des RDB-/AOF-Dateiformats, und zwar vorwärts.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: Elasticsearch has no configurable compatibility level.\n# An index is tied to the format of the version it was created in; very old indices\n# cannot be read in the new version and require a reindex.","note":"Elasticsearch hat keine Einstellung für einen Kompatibilitätsgrad; jeder Index trägt das Format der Major-Version, in der er angelegt wurde. Indizes aus der vorherigen Major-Version sind lesbar, ältere müssen mit _reindex in die neue Version überführt werden.","danger":"safe"}}},{"id":"post-upgrade-tasks","title":"Aufgaben nach dem Upgrade (Recompile/Analyze)","aliases":["post upgrade","post-upgrade","recompile","analyze","refresh statistics","invalid objects"],"engines":{"oracle":{"command":"-- 1) Compile invalid objects in parallel:\n@?/rdbms/admin/utlrp.sql\n-- 2) Refresh dictionary/fixed object statistics:\nEXEC DBMS_STATS.GATHER_DICTIONARY_STATS;\nEXEC DBMS_STATS.GATHER_FIXED_OBJECTS_STATS;\n-- Check remaining invalid objects:\nSELECT owner, object_name, object_type FROM dba_objects WHERE status='INVALID';","note":"utlrp.sql kompiliert Packages/Views/Trigger neu, die nach dem Upgrade INVALID geworden sind. Werden danach Dictionary- und Fixed-Object-Statistiken erhoben, erzeugt der Optimizer in der neuen Version solide Pläne.","danger":"caution","priv":"SYSDBA"},"postgres":{"command":"# 1) Regather statistics after pg_upgrade (mandatory):\n$ vacuumdb --all --analyze-in-stages\n-- 2) Bring extensions to the new version:\nALTER EXTENSION postgis UPDATE;\n-- 3) If the collation changed, rebuild text indexes (can take long):\nREINDEX DATABASE database_name;","note":"pg_upgrade (PG17 und früher) übernimmt keine Planner-Statistiken; analyze-in-stages macht das System schnell nutzbar, indem zuerst grobe, dann vollständige Statistiken erhoben werden. Extensions müssen mit ALTER EXTENSION ... UPDATE aktualisiert werden, Indizes bei geänderter Collation mit REINDEX.","danger":"caution"},"mysql":{"command":"-- 8.0+ dictionary upgrade happens automatically at startup; additionally:\nANALYZE TABLE customer;   -- refresh statistics of critical tables\n-- Legacy 5.7 -> if needed on first startup:\n-- $ mysql_upgrade -u root -p   (before 8.0.16)","note":"Ab MySQL 8.0.16 ist die Funktion von mysql_upgrade in den Server gewandert und wird daher nicht separat ausgeführt. Nach dem Upgrade erhält ein Aktualisieren der Statistiken mit ANALYZE TABLE auf großen/kritischen Tabellen die Planqualität.","danger":"safe"},"mssql":{"command":"-- 1) Correct page/row usage counters:\nDBCC UPDATEUSAGE(Sales);\n-- 2) Refresh statistics (sp_updatestats uses sampling; FULLSCAN for critical tables):\nEXEC sp_updatestats;\n-- UPDATE STATISTICS dbo.CriticalTable WITH FULLSCAN;\n-- 3) Raise the compatibility level to the new version (while monitoring):\nALTER DATABASE Sales SET COMPATIBILITY_LEVEL = 160;","note":"Nach dem Upgrade korrigiert DBCC UPDATEUSAGE aus älteren Versionen übernommene ungenaue Speicher-/Zeilenzähler, sp_updatestats aktualisiert die Statistiken. Beobachte beim Anheben des Kompatibilitätsgrads Plan-Regressionen, die der neue Cardinality Estimator (CE) verursachen kann.","danger":"caution"},"mongodb":{"command":"// 1) Verify the FCV upgrade:\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })\n// 2) Check indexes and compatibility warnings in the server logs:\ndb.customer.getIndexes()\n// If an index is incompatible: dropIndex + createIndex (rolling); do NOT use reIndex","note":"Prüfe nach einem Major-Upgrade, ob die FCV die Zielversion erreicht hat. Das Neuaufbauen von Indizes ist kein routinemäßiger Upgrade-Schritt; wird ein Index in den Release Notes genannt oder erzeugt er eine Warnung im Log, lege ihn mit dropIndex + createIndex neu an. Ab 6.0 funktioniert reIndex nur auf einem Standalone und ist auf Replica-Set-Mitgliedern nicht erlaubt.","danger":"caution"},"redis":{"command":"# Usually no extra task is needed. Recommended: verify persistence and replication:\nredis-cli INFO replication   # is the master/replica link healthy\nredis-cli INFO persistence   # rdb_last_bgsave_status / aof_last_bgrewrite_status\nredis-cli DBSIZE             # is the key count as expected","note":"Nach einem Redis-Upgrade gibt es keine Schema-Compile-/Analyze-Aufgabe; zu prüfen ist, ob die Replikation wiederhergestellt ist, die RDB-/AOF-Persistenz fehlerfrei läuft und die Schlüsselanzahl erhalten geblieben ist.","danger":"safe"},"elasticsearch":{"command":"# Verify cluster health:\nGET _cluster/health\n# Move indices created in an old major version that can no longer be read:\nPOST _reindex\n{ \"source\": { \"index\": \"old_index\" }, \"dest\": { \"index\": \"new_index\" } }","note":"Prüfe nach dem Upgrade, ob der Clusterstatus 'green' ist und alle Shards zugewiesen sind. Indizes in einem Format, das älter als die vorherige Major-Version ist, sind in der neuen Version nicht lesbar und müssen daher mit _reindex in das aktuelle Format überführt werden.","danger":"caution"}}}]},"rosetta/migration.json":{"category":"Migration & Transfer","categoryId":"migration","order":15,"concepts":[{"id":"compare-schema","title":"Zwei Schemas/Umgebungen vergleichen (Schema-Diff)","aliases":["schema diff","diff","compare schemas","compare","schema difference","ddl diff"],"engines":{"oracle":{"command":"-- Extract the schema DDL from both environments and take a text diff\n-- (generate deterministic DDL with DBMS_METADATA)\nEXEC DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'SQLTERMINATOR',TRUE);\nEXEC DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'STORAGE',FALSE);\nSELECT DBMS_METADATA.GET_DDL('TABLE', table_name) FROM user_tables ORDER BY table_name;\n-- Spool the output to a file in both environments and compare with diff/fc.\n\n-- Export the schema as a changelog with SQLcl's Liquibase integration:\n-- SQL> lb generate-schema\n-- Commercial: the Oracle SQL Developer 'Database Diff' tool is also used.","note":"Oracle hat keinen eingebauten Schema-Diff per Einzelbefehl. Üblich ist, in beiden Umgebungen mit DBMS_METADATA.GET_DDL deterministische DDL zu erzeugen (STORAGE-/Segmentattribute deaktiviert) und einen Text-Diff zu bilden. Für visuellen/automatisierten Vergleich nutze den Database-Diff-Assistenten von SQL Developer oder SQLcl.","danger":"safe","readonly":true,"priv":"SELECT_CATALOG_ROLE oder Lesezugriff auf das Schema"},"postgres":{"command":"# Dump only the schema from both databases and diff them\npg_dump --schema-only --no-owner --no-privileges sourcedb > source_schema.sql\npg_dump --schema-only --no-owner --no-privileges targetdb  > target_schema.sql\ndiff -u source_schema.sql target_schema.sql\n\n# Tool that generates applicable ALTERs: apgdiff (Another PostgreSQL Diff Tool)\n# apgdiff source_schema.sql target_schema.sql > migration.sql\n# Alternatives: migra, Liquibase diffChangeLog","note":"Am einfachsten erstellst du in beiden Umgebungen eine Ausgabe mit pg_dump --schema-only und vergleichst sie mit diff. apgdiff und migra sind Tools, die die Differenz direkt in ausführbare ALTER-Anweisungen umsetzen. Ergänze --no-owner --no-privileges für deterministische Ausgabe.","danger":"safe","readonly":true,"priv":"SELECT / Schema-Lesezugriff (Objektzugriff für pg_dump)"},"mysql":{"command":"# Schema/object comparison with MySQL Utilities (5.x):\nmysqldbcompare --server1=root:password@host1 --server2=root:password@host2 \\\n  shop:shop --difftype=sql --run-all-tests\n\n# Built-in method: take CREATE statements from both environments and diff them\nmysqldump --no-data --skip-comments shop > source_schema.sql\n# SHOW CREATE TABLE customer;  (to see a single table definition)","note":"Das Tool mysqldbcompare aus den MySQL Utilities meldet Objekt-/Schemaunterschiede zwischen zwei Servern (mit difftype=sql als anwendbare Anweisungen). Ohne Tool erstellst du in beiden Umgebungen eine Ausgabe mit mysqldump --no-data (nur Schema) oder SHOW CREATE TABLE und vergleichst mit diff. mysqldbcompare ist inzwischen veraltet; für MariaDB/8.x werden Liquibase oder Skeema bevorzugt.","danger":"safe","readonly":true,"priv":"SELECT, SHOW VIEW auf beiden Schemas"},"mssql":{"command":"-- Schema comparison/deploy report with SqlPackage (what will change before applying):\nsqlpackage /Action:DeployReport /SourceFile:source.dacpac \\\n  /TargetConnectionString:\"Server=target;Database=shop;Integrated Security=true\" \\\n  /OutputPath:diff_report.xml\n\n-- For data differences (row level), the tablediff utility:\ntablediff -sourceserver S1 -sourcedatabase shop -sourcetable customer \\\n  -destinationserver S2 -destinationdatabase shop -destinationtable customer","note":"Für Schemaunterschiede liefert SqlPackage /Action:DeployReport die auf das Ziel anzuwendenden Änderungen als XML-Bericht; /Action:Script erzeugt ein ausführbares Diff-Skript. 'Schema Compare' in Visual Studio / Azure Data Studio nutzt dieselbe Engine. Für Datenunterschiede auf Zeilenebene gibt es das Kommandozeilentool tablediff.","danger":"safe","readonly":true,"priv":"VIEW DEFINITION (Lesezugriff auf das Ziel für DeployReport)"},"mongodb":{"command":"// The schema is implicit; extract and compare the field structure of both environments.\n// Read the $jsonSchema validator definition (collection rules):\ndb.getCollectionInfos({ name: \"customer\" })[0].options.validator\n\n// External tools for inferred schema comparison by sampling:\n// mongodb-schema (npm) or the Compass 'Schema' tab\n// Take samples with mongoexport and diff the field/type distribution of both environments.","note":"MongoDB ist schemalos; es gibt keine feste DDL. Verglichen wird auf zwei Wegen: die auf der Collection definierten $jsonSchema-Validierungsregeln vergleichen (über getCollectionInfos) oder mit mongodb-schema / Compass ein aus den Daten abgeleitetes Schema erzeugen und Feld- und Typverteilungen vergleichen. Für Indizes die Ausgabe von db.collection.getIndexes() per Diff vergleichen.","danger":"safe","readonly":true,"priv":"read (getCollectionInfos, listIndexes)"},"redis":{"command":"# Not applicable: Redis is a schemaless key-value store; there are no\n# table/column definitions (DDL) to compare.\n# Closest equivalent: compare the key space (namespace) structure\n# redis-cli --scan --pattern 'customer:*' | sort > source_keys.txt","note":"Nicht anwendbar: Redis kennt kein Schemakonzept (Tabelle/Spalte/DDL), ein klassischer Schema-Diff entfällt daher. Wird ein struktureller Vergleich benötigt, lässt sich nur das Namensmuster der Schlüssel (Namespace) mit SCAN auflisten und vergleichen.","danger":"safe","readonly":true},"elasticsearch":{"command":"# Fetch the index mapping from both clusters and compare\ncurl -s 'http://source:9200/customer/_mapping?pretty' > source_mapping.json\ncurl -s 'http://target:9200/customer/_mapping?pretty'  > target_mapping.json\ndiff -u source_mapping.json target_mapping.json\n\n# To compare settings as well:\n# GET customer/_settings   and   GET customer/_mapping","note":"In Elasticsearch entspricht das Index-Mapping dem Schema. Standard ist, die Ausgabe von GET <index>/_mapping (bei Bedarf auch _settings) aus beiden Clustern zu holen und mit diff zu vergleichen. Da Mapping-Felder größtenteils unveränderlich sind, kann ein gefundener Unterschied einen neuen Index + _reindex erfordern.","danger":"safe","readonly":true,"priv":"view_index_metadata / monitor"}}},{"id":"export-for-migration","title":"Konsistenter Export für die Migration","aliases":["export","consistent export","consistent export","take a dump","migration export","snapshot"],"engines":{"oracle":{"command":"# Consistent logical export with Data Pump (at a single SCN)\nexpdp system/password schemas=shop directory=DIR_DMP \\\n  dumpfile=shop_%U.dmp logfile=shop_exp.log \\\n  flashback_scn=<scn> parallel=4\n\n# Current SCN for point-in-time consistency:\n# SELECT current_scn FROM v$database;\n# Alternative: FLASHBACK_TIME=SYSTIMESTAMP","note":"Data Pump (expdp) ist das Standardtool für den logischen Export bei Migrationen. Für Lesekonsistenz übergib FLASHBACK_SCN=<scn> (oder FLASHBACK_TIME), damit alle Tabellen konsistent zu einer einzigen SCN exportiert werden; auf einem Live-System ist das entscheidend. PARALLEL beschleunigt, %U erzeugt mehrere Dateien.","danger":"caution","readonly":true,"priv":"DATAPUMP_EXP_FULL_DATABASE, DIRECTORY-Objekt","minver":"10g (Data Pump)"},"postgres":{"command":"# Single compressed file in custom format (restorable in parallel)\npg_dump -Fc -Z6 -f shop.dump shop\n\n# Directory format (parallel dump + parallel restore):\npg_dump -Fd -j4 -f shop_dir shop\n\n# For the whole cluster + roles/tablespaces:\n# pg_dumpall --globals-only > roles.sql","note":"Für die Migration wird das Format pg_dump -Fc (custom) oder -Fd (directory) bevorzugt; beide erlauben selektives/paralleles Laden mit pg_restore. Da pg_dump in einer einzigen Transaktion läuft, ist seine Ausgabe automatisch ein konsistenter Snapshot. Clusterweite Objekte wie Rollen und Tablespaces werden separat mit pg_dumpall --globals-only gesichert.","danger":"safe","readonly":true,"priv":"SELECT (auf allen zu sichernden Objekten)"},"mysql":{"command":"# Consistent, lock-free export for InnoDB\nmysqldump --single-transaction --quick --routines --triggers --events \\\n  --source-data=2 shop > shop.sql\n\n# Note: older versions use --master-data=2 instead of --source-data.\n# Parallel, fast alternative: mydumper -B shop -o ./backup --threads 4","note":"Bei InnoDB liefert --single-transaction einen einzigen konsistenten Snapshot und sperrt keine Tabellen. --source-data=2 (früher --master-data=2) schreibt die zum Start von Replikation/CDC nötige Binlog-Position/GTID als Kommentar an den Anfang der Ausgabe; für eine Umschaltung ohne Ausfallzeit ist das unerlässlich. Bei großen Datenmengen arbeitet mydumper parallel.","danger":"safe","readonly":true,"priv":"SELECT, LOCK TABLES, RELOAD, REPLICATION CLIENT (für --source-data)"},"mssql":{"command":"-- Full backup for physical migration (to the same/compatible version):\nBACKUP DATABASE shop TO DISK = 'C:\\backup\\shop.bak' WITH COMPRESSION, CHECKSUM;\n\n-- Portable/version-independent logical export (BACPAC = schema+data):\n-- sqlpackage /Action:Export /SourceConnectionString:\"...\" /TargetFile:shop.bacpac\n\n-- For a single table: bcp shop.dbo.customer out customer.dat -n -S server -T","note":"Bei der Migration auf denselben oder einen neueren SQL Server ist BACKUP ... RESTORE der zuverlässigste Weg (Integrität per CHECKSUM geprüft). Für versions-/plattformunabhängige Migration oder den Umzug nach Azure SQL wird der Export von Schema+Daten in eine einzige .bacpac-Datei mit SqlPackage /Action:Export bevorzugt. bcp wird für einzelne Tabellen genutzt.","danger":"safe","readonly":true,"priv":"BACKUP DATABASE (db_backupoperator) oder Lesezugriff für Export"},"mongodb":{"command":"# Consistent binary (BSON) export; point-in-time consistency with the oplog\nmongodump --uri=\"mongodb://source:27017\" \\\n  --oplog --out=./backup --numParallelCollections=4\n\n# --oplog captures changes made during the dump (requires a replica set);\n# it works only with a FULL dump and cannot be combined with --db / --collection.\n# On restore, mongorestore --oplogReplay brings it to a single point in time.\n# If you need a single database: take it with --db=shop (without the oplog) or use --nsInclude='shop.*' on restore.","note":"mongodump ist der Standard-Binärexport (BSON) für Migrationen. Auf einem Replica Set erfasst die Option --oplog die während des Dumps geschriebenen Änderungen aus dem Oplog und liefert so einen konsistenten Zeitpunkt (zusammen mit mongorestore --oplogReplay); die Option gilt nur für einen vollständigen Dump. Für die Migration großer Cluster auf Clusterebene eignen sich mongosync/Live-Tools besser.","danger":"safe","readonly":true,"priv":"read (read auf der local-DB für das Oplog / clusterMonitor)"},"redis":{"command":"# Create an RDB snapshot in the background (non-blocking)\nredis-cli BGSAVE\n# Check for completion:\nredis-cli INFO persistence | grep rdb_bgsave_in_progress\n\n# Move the resulting file (path: CONFIG GET dir + dbfilename)\n# e.g. /var/lib/redis/dump.rdb  ->  copy to the target server","note":"In Redis ist der konsistente Export der RDB-Snapshot. BGSAVE erzeugt per Fork im Hintergrund eine konsistente dump.rdb, ohne den Hauptprozess zu blockieren (SAVE blockiert und sollte nicht verwendet werden). Den Dateipfad liefern CONFIG GET dir und dbfilename; migriert wird, indem diese dump.rdb in das Datenverzeichnis des Zielservers kopiert wird.","danger":"safe","readonly":true},"elasticsearch":{"command":"# 1) Register a snapshot repository (shared file system / S3)\nPUT _snapshot/migration_repo\n{ \"type\": \"fs\", \"settings\": { \"location\": \"/mnt/es_backup\" } }\n\n# 2) Take a consistent snapshot\nPUT _snapshot/migration_repo/migration_1?wait_for_completion=true\n{ \"indices\": \"customer,orders\", \"include_global_state\": false }","note":"In Elasticsearch ist der Snapshot die konsistente Exportmethode für Migrationen. Zuerst wird ein Repository (fs, S3, GCS usw.) registriert, dann der Snapshot erstellt; Snapshots sind inkrementell und erfassen einen konsistenten Zustand zum Erstellungszeitpunkt. Für kleine Datenmengen/einen einzelnen Index lässt sich auch elasticdump nutzen, Standard ist aber der Snapshot.","danger":"safe","readonly":true,"priv":"manage / create_snapshot"}}},{"id":"import-for-migration","title":"Import ins Ziel","aliases":["import","import data","restore","load","load into target","load"],"engines":{"oracle":{"command":"# Import with Data Pump; remap schema and tablespace\nimpdp system/password directory=DIR_DMP dumpfile=shop_%U.dmp \\\n  logfile=shop_imp.log \\\n  remap_schema=shop:shop_new \\\n  remap_tablespace=USERS:DATA parallel=4\n\n# For existing objects: TABLE_EXISTS_ACTION=TRUNCATE|APPEND|REPLACE","note":"impdp lädt die mit expdp erstellte .dmp-Datei ins Ziel. REMAP_SCHEMA verschiebt die Quelle in ein anderes Schema, REMAP_TABLESPACE in einen anderen Tablespace; beides wird bei Migrationen zwischen Umgebungen häufig genutzt. Das Verhalten bei vorhandenen Tabellen wird mit TABLE_EXISTS_ACTION gesteuert, die Geschwindigkeit mit PARALLEL.","danger":"caution","priv":"DATAPUMP_IMP_FULL_DATABASE, Quota auf dem Ziel-Tablespace"},"postgres":{"command":"# Parallel restore (from custom/directory format)\npg_restore -j4 -d targetdb --no-owner --clean --if-exists shop.dump\n\n# A specific table only: pg_restore -d targetdb -t customer shop.dump\n# For a plain .sql dump:  psql -d targetdb -f shop.sql","note":"Mit pg_restore -j werden Dumps im custom-/directory-Format parallel (mehrere Prozesse) geladen; bei großen Datenbanken beschleunigt das den Ladevorgang erheblich. --clean --if-exists löscht die Objekte im Ziel zuerst, --no-owner passt sich an eine andere Rollenstruktur an. Plain-SQL-Dumps werden mit psql -f geladen (nicht parallel).","danger":"caution","priv":"CREATE-/Eigentümerrechte auf der Zieldatenbank"},"mysql":{"command":"# Load a plain SQL dump\nmysql --binary-mode shop < shop.sql\n\n# Parallel/fast load (if taken with mydumper):\nmyloader --directory=./backup --database=shop --threads=4 --overwrite-tables\n\n# Tip: SET FOREIGN_KEY_CHECKS=0; unique_checks=0 before loading speeds it up.","note":"Ein SQL-Dump von mysqldump wird geladen, indem er an den mysql-Client umgeleitet wird (mysql shop < dump.sql). Für paralleles und deutlich schnelleres Laden wird ein mit mydumper erstelltes Verzeichnis mit myloader --threads wiederhergestellt. FOREIGN_KEY_CHECKS und UNIQUE_CHECKS während des Ladens zu deaktivieren verbessert die Performance.","danger":"caution","priv":"INSERT, CREATE (und DROP für --overwrite/--clean)"},"mssql":{"command":"-- Restore from a physical backup (move file paths to the new environment)\nRESTORE DATABASE shop FROM DISK = 'C:\\backup\\shop.bak'\nWITH MOVE 'shop_data' TO 'D:\\data\\shop.mdf',\n     MOVE 'shop_log'  TO 'D:\\log\\shop.ldf', REPLACE;\n\n-- Import from a BACPAC (schema+data):\n-- sqlpackage /Action:Import /TargetConnectionString:\"...\" /SourceFile:shop.bacpac","note":"Ein .bak-Backup wird mit RESTORE DATABASE geladen; bei einem anderen Plattenlayout leitet WITH MOVE die Daten-/Logdateien auf neue Pfade um. Für ein versionsunabhängiges Ziel/Azure SQL legt SqlPackage /Action:Import die .bacpac-Datei (Schema + Daten) als neue Datenbank an.","danger":"caution","priv":"dbcreator/sysadmin für RESTORE; CREATE DATABASE für Import"},"mongodb":{"command":"# Restore the BSON backup; replay the oplog to land on a single point in time\nmongorestore --uri=\"mongodb://target:27017\" --oplogReplay \\\n  --numParallelCollections=4 ./backup\n\n# Renaming: --nsFrom='shop.*' --nsTo='shop_new.*'\n# Drop existing data before loading: --drop","note":"mongorestore lädt die BSON-Ausgabe von mongodump ins Ziel. --oplogReplay spielt die Oplog-Änderungen eines mit --oplog erstellten Backups nach und bringt den Dump so auf einen einzigen konsistenten Zeitpunkt. Datenbank-/Collection-Namen werden mit --nsFrom/--nsTo geändert, vorhandene Daten lassen sich mit --drop leeren.","danger":"caution","priv":"readWrite (restore/clusterAdmin für oplogReplay)"},"redis":{"command":"# Stop the server, place dump.rdb in the data directory, start it again\n# 1) systemctl stop redis  (or SHUTDOWN NOSAVE)\n# 2) cp dump.rdb  <path found with CONFIG GET dir>/dump.rdb\n# 3) systemctl start redis   -> Redis loads the RDB into memory at startup\n\n# Alternative (live): feed a RESP command stream with redis-cli --pipe","note":"Redis lädt die RDB nur beim Start. Daher wird der Zielserver gestoppt, die migrierte dump.rdb ins Datenverzeichnis (CONFIG GET dir) gelegt und der Server neu gestartet; beim Start wird der Speicher aus der RDB befüllt. Um ohne Stoppen des Servers zu laden, streame die Schlüssel im RESP-Format mit redis-cli --pipe.","danger":"caution"},"elasticsearch":{"command":"# Register the same repository on the target cluster, then restore the snapshot\nPUT _snapshot/migration_repo\n{ \"type\": \"fs\", \"settings\": { \"location\": \"/mnt/es_backup\" } }\n\nPOST _snapshot/migration_repo/migration_1/_restore\n{ \"indices\": \"customer,orders\",\n  \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_$1\" }","note":"Registriere auf dem Zielcluster dasselbe Snapshot-Repository wie auf der Quelle (die Dateien müssen erreichbar sein) und stelle dann die Indizes mit _snapshot/.../_restore wieder her. Indexnamen lassen sich mit rename_pattern/rename_replacement ändern. Vorhandene Indizes können nicht im geöffneten Zustand wiederhergestellt werden; schließe oder benenne sie zuerst um.","danger":"caution","priv":"manage (create_index, restore)"}}},{"id":"heterogeneous-migration","title":"Migration auf eine andere Datenbank (heterogen)","aliases":["heterogeneous","heterogeneous migration","cross-database","different database","convert database","convert"],"engines":{"oracle":{"command":"# Oracle -> PostgreSQL: Ora2Pg (schema + data + PL/SQL conversion)\nora2pg -c ora2pg.conf -t TABLE   -o schema.sql\nora2pg -c ora2pg.conf -t COPY    -o data.sql   # data in COPY format\nora2pg -t SHOW_REPORT --estimate_cost           # migration cost estimate\n\n# Oracle -> (multiple targets) managed/online: AWS DMS or Oracle GoldenGate","note":"Für die heterogene Migration von Oracle (vor allem nach PostgreSQL) ist Ora2Pg das verbreitetste Open-Source-Tool: Es konvertiert Tabellen, Constraints, PL/SQL und Daten in den Zieldialekt; --estimate_cost schätzt den Migrationsaufwand. Für Online-Migration bzw. Migration mit geringer Ausfallzeit werden AWS DMS oder Oracle GoldenGate (mit CDC) eingesetzt.","danger":"caution","readonly":true,"priv":"SELECT auf dem Quellschema (Lesezugriff auf DBA-Views)"},"postgres":{"command":"# MySQL -> PostgreSQL: pgloader (schema+data+type conversion in one command)\npgloader mysql://app_user:password@source/shop \\\n         postgresql://app_user:password@target/shop\n\n# Also from SQLite/MSSQL sources: with a pgloader ./migration.load script\n# For a managed/online PostgreSQL target: AWS DMS","note":"pgloader ist das Standardtool für die Migration aus anderen Engines (MySQL, SQLite, MS SQL) nach PostgreSQL: Mit Verbindungsstrings legt es das Schema mit einem einzigen Befehl an, bildet Typen ab und kopiert Daten schnell; eigene Transformationen/Typzuordnungen werden in einem .load-Skript definiert. Ist die Quelle Oracle, wird Ora2Pg bevorzugt; für Online-Migration AWS DMS.","danger":"caution","priv":"CREATE auf dem Ziel; SELECT auf der Quelle"},"mysql":{"command":"# PostgreSQL/Oracle/MSSQL -> MySQL: AWS DMS (managed) is the most common path\n# or adapt the source-specific export for MySQL:\n#   pg_dump --inserts ... | (type/syntax fixes) | mysql shop\n\n# MySQL <-> MariaDB (compatible): mysqldump output loads directly\nmysqldump --single-transaction shop | mysql -h target_mariadb shop","note":"Für die Migration aus anderen Engines nach MySQL gibt es kein einzelnes dominierendes offenes Tool; am verbreitetsten ist AWS DMS (verwaltet, CDC-fähig) oder den Quelldump an Syntax/Typen anzupassen und mit dem mysql-Client zu laden. MySQL und MariaDB sind weitgehend kompatibel; mysqldump-Ausgaben lassen sich meist direkt übernehmen.","danger":"caution","priv":"CREATE, INSERT auf dem Ziel"},"mssql":{"command":"# SQL Server -> PostgreSQL: with pgloader\npgloader mssql://app_user:password@source/shop \\\n         postgresql://app_user:password@target/shop\n\n# SQL Server -> PostgreSQL compatibility layer: Babelfish for Aurora PostgreSQL\n# (runs T-SQL and the TDS protocol on top of PostgreSQL)\n# To other targets: SSMA (SQL Server Migration Assistant) or AWS DMS","note":"Für die Migration von SQL Server nach PostgreSQL wird pgloader (Quelle mssql://) genutzt; wer T-SQL ohne Anwendungsänderung behalten will, bekommt mit Babelfish for Aurora PostgreSQL eine Kompatibilitätsschicht. Microsofts Tool SSMA automatisiert die Migration aus anderen Engines (Oracle, MySQL, DB2) nach SQL Server. Für Online-Migration AWS DMS.","danger":"caution","priv":"SELECT auf der Quelle; CREATE auf dem Ziel"},"mongodb":{"command":"# Relational <-> MongoDB conversion requires schema mapping (ETL):\n#   - Relational Migrator (official MongoDB tool): maps tables to the document model\n#   - or generate JSON from the source -> load with mongoimport\nmongoexport --db=shop --collection=customer --type=csv --fields=id,name > c.csv\n# (To an SQL target: mongoexport CSV -> relational COPY/LOAD DATA)","note":"Die Migration zwischen MongoDB und SQL-Engines erfordert eine Umwandlung zwischen Zeilen- und Dokumentmodell (ETL); einen automatischen Einzelbefehl gibt es nicht. Von SQL nach MongoDB bildet MongoDB Relational Migrator Tabellen auf ein Dokumentschema ab; in einfachen Fällen aus der Quelle CSV/JSON erzeugen und mit mongoimport laden. In umgekehrter Richtung wird die Ausgabe von mongoexport mit COPY/LOAD DATA in die relationale Zieltabelle geladen.","danger":"caution","priv":"read auf der Quelle, write auf dem Ziel"},"redis":{"command":"# With RIOT (Redis Input/Output Tools), to/from Redis from other sources:\n#   Relational -> Redis:\nriot db-import --jdbc-url \"jdbc:postgresql://host/shop\" \\\n  --sql \"SELECT * FROM customer\" hset --keyspace customer --keys id\n#   Redis -> another Redis / file:  riot replicate / riot file-export","note":"Bei heterogener Migration ist Redis meist die Cache-Schicht als Ziel/Quelle; um Daten aus relationalen oder Dateiquellen in Redis zu laden, wird das offizielle Redis-Tool RIOT genutzt (db-import bildet ein SQL-Ergebnis auf HSET/SET-Strukturen ab). Für Redis zu Redis oder in eine Datei gibt es die Befehle riot replicate / file-export.","danger":"caution"},"elasticsearch":{"command":"# Relational/other source -> Elasticsearch: Logstash (JDBC input)\n# logstash.conf: input { jdbc { statement => \"SELECT * FROM customer\" } }\n#                output { elasticsearch { index => \"customer\" } }\n\n# ES -> ES (version/cluster upgrade): reindex from a remote source\nPOST _reindex\n{ \"source\": { \"remote\": { \"host\": \"http://old:9200\" }, \"index\": \"customer\" },\n  \"dest\": { \"index\": \"customer\" } }","note":"Das verbreitetste Tool für die Migration aus anderen Datenbanken nach Elasticsearch ist Logstash (JDBC-/Datei-Input -> elasticsearch-Output); auch Elastics eigene Connectors/Beats werden genutzt. Beim Umzug von einem alten auf einen neuen Elasticsearch-Cluster ist Remote Reindex (_reindex mit Quelle 'remote') üblich; auf dem Ziel muss reindex.remote.whitelist definiert sein.","danger":"caution","priv":"write/create_index auf dem Ziel"}}},{"id":"setup-cdc-replication","title":"Kontinuierliche Replikation / CDC einrichten (Umschaltung ohne Ausfallzeit)","aliases":["cdc","replication","replication","zero-downtime migration","live migration","change data capture"],"engines":{"oracle":{"command":"# CDC-based replication with Oracle GoldenGate (outline)\n# 1) Enable supplemental logging on the source:\nALTER DATABASE ADD SUPPLEMENTAL LOG DATA;\n# 2) Extract (capture changes from redo) and Replicat (apply to the target) processes:\n#   GGSCI> ADD EXTRACT ext1, INTEGRATED TRANLOG, BEGIN NOW\n#   GGSCI> ADD REPLICAT rep1, INTEGRATED\n# Alternative: initial load with Data Pump + incremental capture with GoldenGate","note":"In Oracle ist Oracle GoldenGate das Standardtool für CDC ohne Ausfallzeit bzw. heterogene CDC: Der Extract-Prozess erfasst Änderungen aus den Redo-Logs, Replicat wendet sie auf das Ziel an; Supplemental Logging muss aktiviert sein. Typische Migration: Die Erstbeladung erfolgt mit Data Pump, danach streamt GoldenGate die Änderungen ins Ziel, was eine Umschaltung mit nahezu null Ausfallzeit ermöglicht.","danger":"caution","priv":"GoldenGate-Administratorrechte, Supplemental Logging"},"postgres":{"command":"-- Source (publisher): create a publication (wal_level must be logical)\nCREATE PUBLICATION migration_pub FOR ALL TABLES;\n\n-- Target (subscriber): create a subscription (initial copy + continuous stream)\nCREATE SUBSCRIPTION migration_sub\n  CONNECTION 'host=source dbname=shop user=repl password=***'\n  PUBLICATION migration_pub;","note":"PostgreSQL bietet CDC mit eingebauter logischer Replikation: CREATE PUBLICATION auf der Quelle, CREATE SUBSCRIPTION auf dem Ziel. Beim Start kopiert die Subscription die vorhandenen Daten (Initial Copy) und streamt danach fortlaufend Änderungen aus dem WAL; wal_level=logical ist erforderlich. Für große/komplexe Szenarien werden pglogical oder Debezium bevorzugt.","danger":"caution","priv":"Rolle REPLICATION auf der Quelle; Superuser auf dem Ziel für CREATE SUBSCRIPTION (ab PG16 genügt auch die Rolle pg_create_subscription)","minver":"10 (logische Replikation)"},"mysql":{"command":"-- Source: binlog must be enabled and in ROW format (log_bin, binlog_format=ROW)\n-- Grant a replication user: GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';\n\n-- Define the source on the target (start from the binlog position of the initial load):\nCHANGE REPLICATION SOURCE TO\n  SOURCE_HOST='source', SOURCE_USER='repl', SOURCE_PASSWORD='***',\n  SOURCE_LOG_FILE='binlog.000001', SOURCE_LOG_POS=1547;\nSTART REPLICA;","note":"In MySQL erfolgt eine Umschaltung ohne Ausfallzeit per Binlog-basierter Replikation. Die Erstbeladung wird mit mysqldump --source-data erstellt und die Binlog-Position/GTID notiert; auf dem Ziel wird die Replikation ab dieser Position mit CHANGE REPLICATION SOURCE TO gestartet (Syntax ab 8.0.23; früher CHANGE MASTER TO). START REPLICA wendet Änderungen fortlaufend an. Ein GTID-basiertes Setup ist sicherer.","danger":"caution","priv":"REPLICATION SLAVE (repl-Benutzer), REPLICATION CLIENT","minver":"8.0.23 (CHANGE REPLICATION SOURCE)"},"mssql":{"command":"-- Option A: Transactional Replication (publisher->distributor->subscriber) — SSMS wizard\n\n-- Option B: Change Data Capture (track changes as tables)\nEXEC sys.sp_cdc_enable_db;              -- enable CDC at database level\nEXEC sys.sp_cdc_enable_table\n  @source_schema = N'dbo', @source_name = N'customer',\n  @role_name = NULL;                    -- enable CDC at table level\n-- Changes are read from the cdc.dbo_customer_CT table and moved to the target.","note":"SQL Server bietet zwei Hauptwege für kontinuierliche Replikation: Für einen direkten Stream ins Ziel richte Transactional Replication (Publisher/Subscriber) ein; willst du Änderungen mit eigenem ETL übertragen, aktiviere Change Data Capture (sp_cdc_enable_db + sp_cdc_enable_table) und lies aus den _CT-Tabellen. Für heterogene Ziele wird CDC mit AWS DMS/Debezium kombiniert.","danger":"caution","priv":"sysadmin (zum Aktivieren von CDC); SQL Server Agent muss laufen"},"mongodb":{"command":"// Watch changes live with Change Streams (requires a replica set/sharded cluster)\nconst cs = db.customer.watch([], { fullDocument: \"updateLookup\" });\nwhile (cs.hasNext()) { printjson(cs.next()); }  // apply to the target\n\n// Official tool for managed cluster-to-cluster replication/migration: mongosync\n// mongosync --cluster0 \"mongodb://source\" --cluster1 \"mongodb://target\"","note":"In MongoDB werden für CDC Change Streams genutzt: db.collection.watch() streamt insert-/update-/delete-Ereignisse live aus dem Oplog, die auf das Ziel angewendet werden (ein Replica Set ist erforderlich). Für vollständige kontinuierliche Migration/Replikation von Cluster zu Cluster verwaltet mongosync, MongoDBs offizielles Tool Cluster-to-Cluster Sync (nicht Atlas-spezifisch; funktioniert auch mit selbst verwalteten Clustern), Erstbeladung + kontinuierliche Synchronisation.","danger":"caution","priv":"changeStream (read); clusterAdmin für mongosync"},"redis":{"command":"# Make the target instance a replica of the source (live synchronization)\n# On the target Redis:\nREPLICAOF source_host 6379\n# Monitor sync status:\nINFO replication   # master_link_status must be up\n\n# Once the cutover is complete, make the target standalone:\nREPLICAOF NO ONE","note":"In Redis erfolgt eine Umschaltung ohne Ausfallzeit mit nativer Replikation: Führe auf dem Zielserver REPLICAOF <source host port> aus (früher SLAVEOF); das Ziel erhält zuerst die vollständige RDB und bleibt dann über den Befehlsstrom fortlaufend synchron. Prüfe master_link_status:up mit INFO replication und mache das Ziel nach abgeschlossener Umschaltung mit REPLICAOF NO ONE eigenständig.","danger":"caution"},"elasticsearch":{"command":"# Incremental reindex from the old cluster to the new one within the same version family\nPOST _reindex?wait_for_completion=false\n{ \"source\": { \"remote\": { \"host\": \"http://old:9200\" }, \"index\": \"customer\",\n    \"query\": { \"range\": { \"updated_at\": { \"gt\": \"<last-cursor>\" } } } },\n  \"dest\": { \"index\": \"customer\" } }\n\n# Licensed feature: Cross-Cluster Replication (CCR) — continuous following\n# On the target (follower) cluster: PUT /customer/_ccr/follow { \"remote_cluster\": \"old\", \"leader_index\": \"customer\" }","note":"Die echte kontinuierliche Replikation in Elasticsearch ist Cross-Cluster Replication (CCR): Der Zielcluster folgt dem Leader-Index auf der Quelle und zieht Änderungen fortlaufend (erfordert eine kommerzielle/Platinum-Lizenz). Ohne Lizenz wird eine nahezu kontinuierliche Synchronisation per periodischem Remote Reindex erreicht (inkrementell, anhand eines Zeit-/Sequenzfelds).","danger":"caution","priv":"manage/read_ccr für CCR; Whitelist für Remote Reindex"}}},{"id":"verify-migration","title":"Prüfung nach der Migration (Zeilenzahl/Prüfsumme)","aliases":["verify","verification","row count","checksum","row count","consistency check"],"engines":{"oracle":{"command":"-- Compare row counts on source and target\nSELECT 'customer' table_name, COUNT(*) row_count FROM customer;\n\n-- To find content differences at object level, DBMS_COMPARISON (via db link)\nBEGIN DBMS_COMPARISON.CREATE_COMPARISON(\n  comparison_name => 'COMPARE_CUSTOMER',\n  schema_name => 'SHOP', object_name => 'CUSTOMER',\n  dblink_name => 'TARGET_DB'); END;\n/\n-- SELECT DBMS_COMPARISON.COMPARE('COMPARE_CUSTOMER', scan_info) FROM dual;","note":"Am schnellsten vergleichst du COUNT(*) pro Tabelle auf beiden Seiten. Um echte inhaltliche Unterschiede auf Zeilenebene zu finden, nutze das Package DBMS_COMPARISON: Es vergleicht Quell- und Zieltabellen zeilenweise über einen Database Link und kann die Unterschiede melden/angleichen. Für Prüfsummen lässt sich mit ORA_HASH oder STANDARD_HASH ein Spalten-Digest bilden.","danger":"safe","readonly":true,"priv":"SELECT auf beiden Seiten; EXECUTE auf DBMS_COMPARISON"},"postgres":{"command":"-- Row count\nSELECT count(*) FROM customer;\n\n-- Ordered digest (checksum) of the whole table content — compare on both sides\nSELECT md5(string_agg(t.row_md5, '' ORDER BY t.row_md5)) AS digest\nFROM (SELECT md5(customer::text) AS row_md5 FROM customer) t;","note":"Der erste Schritt ist, count(*) jeder Tabelle zu vergleichen. Zur inhaltlichen Prüfung bildest du das md5 der Textdarstellung jeder Zeile, verkettest sie sortiert und hashst erneut; so entsteht eine reihenfolgeunabhängige Prüfsumme für beide Seiten. Bei großen Datenmengen bilde den Digest nur über die veränderlichen/Schlüsselspalten; die Sortierung nach PK ist für Wiederholbarkeit wichtig.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mysql":{"command":"-- Built-in table checksum (run on both servers and compare)\nCHECKSUM TABLE customer;\n\n-- Row count\nSELECT COUNT(*) FROM customer;\n\n-- Preferred tool for live/large environments (compares row chunks online):\n-- pt-table-checksum h=source,D=shop  (Percona Toolkit)","note":"In MySQL liefert CHECKSUM TABLE eine einzige Prüfsumme des Tabelleninhalts; führe es auf Quelle und Ziel aus und vergleiche (Engine/Version müssen gleich sein). Für die Zeilenzahl nutze COUNT(*). In laufender Produktion und für Replikationskonsistenz ist pt-table-checksum aus dem Percona Toolkit der Branchenstandard; es vergleicht chunkweise, online und mit geringer Last.","danger":"safe","readonly":true,"priv":"SELECT auf die Tabelle"},"mssql":{"command":"-- Full row-level comparison (official tool that produces differences)\ntablediff -sourceserver S1 -sourcedatabase shop -sourcetable customer \\\n  -destinationserver S2 -destinationdatabase shop -destinationtable customer\n\n-- Quick aggregate checksum (single query, compare on both sides):\nSELECT COUNT_BIG(*) row_count, CHECKSUM_AGG(BINARY_CHECKSUM(*)) digest FROM dbo.customer;","note":"In SQL Server ist tablediff das offizielle Kommandozeilentool für Unterschiede auf Zeilenebene (es meldet hinzugefügte/geänderte Zeilen und schreibt mit optionalem -f ein Korrekturskript). Für eine schnelle Gesamtprüfung liefert CHECKSUM_AGG(BINARY_CHECKSUM(*)) einen einzigen Tabellen-Digest; vergleiche beide Seiten zusammen mit COUNT_BIG(*) (unabhängig von der Zeilenreihenfolge).","danger":"safe","readonly":true,"priv":"SELECT auf beiden Tabellen"},"mongodb":{"command":"// Compare document counts (on source and target)\ndb.customer.countDocuments({});\n\n// For a content checksum, dbHash (md5 digest at collection/DB level):\ndb.runCommand({ dbHash: 1, collections: [\"customer\"] });\n// -> compare the md5 field across both environments.","note":"Die erste Prüfung ist, die Dokumentanzahl auf beiden Seiten mit countDocuments() zu vergleichen (estimatedDocumentCount ist schnell, stützt sich aber auf Metadaten; für eine exakte Zählung countDocuments nutzen). Zur inhaltlichen Prüfung erzeugt der Befehl dbHash einen md5-Digest auf Collection-/Datenbankebene; vergleiche die Hashes beider Umgebungen. Indizes separat mit getIndexes() prüfen.","danger":"safe","readonly":true,"priv":"read (Zugriff für dbHash)"},"redis":{"command":"# Compare key counts (on source and target)\nredis-cli DBSIZE\n\n# For a memory/data summary and key distribution:\nredis-cli INFO keyspace     # db0:keys=... information\n# For full content verification: DUMP each key and compare with the target\n# (e.g. redis-cli --scan | xargs -I{} redis-cli DUMP {})","note":"In Redis besteht die Grundprüfung im Vergleich der Gesamtzahl der Schlüssel mit DBSIZE; die Verteilung pro Datenbank zeigt INFO keyspace. Redis hat keine eingebaute Tabellenprüfsumme; ist eine exakte Inhaltsprüfung nötig, hole pro Schlüssel die DUMP-Serialisierung und vergleiche beide Seiten (TTL-Unterschiede beachten). Auch das Tool riot compare lässt sich nutzen.","danger":"safe","readonly":true},"elasticsearch":{"command":"# Compare document counts (on source and target clusters)\nGET customer/_count\n\n# Details: for health/segment/storage comparison\nGET _cat/indices/customer?v&h=index,docs.count,store.size\n# Content by sampling: compare GET customer/_doc/<id> for the same _ids","note":"In Elasticsearch besteht die Grundprüfung im Vergleich der Dokumentanzahl auf beiden Clustern mit GET <index>/_count (_cat/indices docs.count liefert ebenfalls eine schnelle Übersicht). Stütze den Vergleich auf _count: _cat/indices docs.count zählt auf Lucene-Ebene (inklusive nested Unterdokumente, z. B. zählt ein Dokument mit zwei nested Elementen als 3) und kann daher von _count abweichen. Zur inhaltlichen Prüfung vergleiche stichprobenartig Dokumente über dieselbe Menge von _ids oder über ein Hash-Feld.","danger":"safe","readonly":true,"priv":"read / monitor auf dem Index"}}}]},"rosetta/tuning.json":{"category":"Performance-Tuning","categoryId":"tuning","order":16,"concepts":[{"id":"plan-baseline","title":"Ausführungsplan fixieren / erzwingen (Plan Baseline / Hint)","aliases":["plan baseline","force plan","pin plan","hint","plan stability","query store force"],"engines":{"oracle":{"command":"-- Load the good plan from the cursor cache as a baseline:\nDECLARE n NUMBER; BEGIN\n  n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => '&sql_id');\nEND;\n/","note":"SQL Plan Management fixiert den Plan; der stärkste Schutz gegen Regressionen. Auch /*+ ... */-Hints sind möglich.","danger":"caution","priv":"ADMINISTER SQL MANAGEMENT OBJECT"},"postgres":{"command":"-- There is no built-in plan pinning; the pg_hint_plan extension:\n/*+ IndexScan(customer customer_city_idx) */ SELECT ... ;","note":"Alternative: auf Session-Ebene mit Einstellungen wie enable_seqscan steuern; für dauerhaftes Fixieren pg_hint_plan nutzen.","danger":"caution"},"mysql":{"command":"SELECT /*+ INDEX(customer customer_city_idx) */ * FROM customer WHERE city='Ankara';","note":"Optimizer-Hints werden in die Abfrage eingebettet. Für dauerhaftes Umschreiben das Query-Rewrite-Plugin nutzen.","danger":"safe"},"mssql":{"command":"-- Force the good plan in Query Store:\nEXEC sp_query_store_force_plan @query_id = 42, @plan_id = 17;","note":"Query Store muss aktiviert sein. Alternativ OPTION (USE PLAN N'...') oder ein Plan Guide.","danger":"caution","priv":"ALTER auf der Datenbank"},"mongodb":{"command":"// Force an index for the query:\ndb.customer.find({ city: \"Ankara\" }).hint({ city: 1 })\n// Persistent: db.customer.getPlanCache() / index filter","note":"hint erzwingt einen bestimmten Index; einen veralteten Plan mit planCacheClear löschen.","danger":"safe"},"redis":{"command":"# Not applicable: there is no query planner.","note":"Die Performance ergibt sich aus Datenstruktur und Befehlskomplexität.","danger":"safe"},"elasticsearch":{"command":"# There is no plan pinning; optimize the query/index and routing.\nGET <index>/_search { \"profile\": true, \"query\": {...} }","note":"Mit profile siehst du, welche Abfrage teuer ist; Steuerung über Shards/Routing.","danger":"safe"}}},{"id":"gather-stats-advanced","title":"Statistiken mit erweiterten Optionen erheben (Histogramme usw.)","aliases":["gather stats","histogram","statistics","analyze advanced","method_opt","estimate percent"],"engines":{"oracle":{"command":"EXEC DBMS_STATS.GATHER_TABLE_STATS(\n  ownname=>'HR', tabname=>'EMPLOYEE',\n  estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE,\n  method_opt=>'FOR ALL COLUMNS SIZE AUTO', degree=>4);","note":"method_opt steuert Histogramme, degree die Parallelität. Mit LOCK_TABLE_STATS werden Statistiken gesperrt.","danger":"safe"},"postgres":{"command":"ALTER TABLE customer ALTER COLUMN city SET STATISTICS 500;\nANALYZE customer;","note":"Sampling-Ziel pro Spalte; default_statistics_target setzt den globalen Standard.","danger":"safe"},"mysql":{"command":"ANALYZE TABLE customer UPDATE HISTOGRAM ON city WITH 32 BUCKETS;","note":"Histogrammunterstützung ab MySQL 8.0; nützlich bei Spalten, nach denen gefiltert wird, die aber nicht selektiv sind.","danger":"safe"},"mssql":{"command":"UPDATE STATISTICS dbo.customer WITH FULLSCAN;\n-- Filtered statistics: CREATE STATISTICS ... WHERE ...","note":"FULLSCAN ist am genauesten; bei großen Tabellen SAMPLE nutzen. AUTO_UPDATE_STATISTICS für automatische Aktualisierung.","danger":"safe"},"mongodb":{"command":"// There are no manual statistics; the planner measures at runtime.\ndb.customer.getPlanCache().clear()","note":"Abgesehen vom Leeren eines veralteten Plan-Caches sind keine manuellen Statistiken nötig.","danger":"safe"},"redis":{"command":"# Not applicable.","note":"Es gibt keine Statistiken/keinen Optimizer.","danger":"safe"},"elasticsearch":{"command":"# There is no statistics gathering; refresh searchability:\nPOST <index>/_refresh","note":"Field Data und Doc Values werden zur Abfragezeit genutzt; Force Merge reduziert die Segmente.","danger":"safe"}}},{"id":"perf-report","title":"Performance-Bericht erstellen (AWR / ASH und Entsprechungen)","aliases":["awr","ash","performance report","performance report","profile","workload report"],"engines":{"oracle":{"command":"-- AWR report (SQL*Plus):\n@?/rdbms/admin/awrrpt.sql\n-- ASH report: @?/rdbms/admin/ashrpt.sql\n-- Live: SELECT * FROM v$active_session_history WHERE sample_time > SYSDATE-1/24;","note":"AWR/ASH erfordern die Lizenz für das Diagnostics Pack. Statspack ist die lizenzfreie Entsprechung.","danger":"safe","priv":"SELECT_CATALOG_ROLE"},"postgres":{"command":"-- pg_stat_statements summary + pg_profile/pgBadger:\nSELECT queryid, calls, total_exec_time, mean_exec_time, query\nFROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20;","note":"Als AWR-Entsprechung die Extension pg_profile (periodische Snapshots) oder Log-Analyse mit pgBadger nutzen.","danger":"safe"},"mysql":{"command":"-- sys schema summaries:\nSELECT * FROM sys.statement_analysis LIMIT 20;\nSELECT * FROM sys.io_global_by_file_by_bytes LIMIT 10;","note":"Das sys-Schema macht performance_schema zu lesbaren Berichten; Enterprise Monitor ist umfassender.","danger":"safe"},"mssql":{"command":"-- Query Store reports (SSMS) or:\nSELECT TOP 20 * FROM sys.query_store_runtime_stats ORDER BY avg_duration DESC;","note":"Query Store hält Pläne/Performance über die Zeit fest; sp_BlitzFirst (Community) liefert eine schnelle Diagnose.","danger":"safe"},"mongodb":{"command":"# Live from the shell: mongostat --rowcount 10 ; mongotop\n// or Atlas Performance Advisor / profiler:\ndb.system.profile.find().sort({millis:-1}).limit(20)","note":"mongostat/mongotop zeigen die aktuelle Last; der Profiler zeichnet langsame Operationen auf.","danger":"safe"},"redis":{"command":"INFO stats\nredis-cli --latency-history\nSLOWLOG GET 20","note":"--latency und --latency-history liefern das Latenzprofil; INFO liefert alle Zähler.","danger":"safe"},"elasticsearch":{"command":"GET _nodes/stats\nGET _cat/thread_pool?v&h=node_name,name,active,queue,rejected\nGET _nodes/hot_threads","note":"hot_threads zeigt die teuersten Threads; _nodes/stats liefert ganzheitliche Metriken. Monitoring (Kibana) ist historisch.","danger":"safe"}}},{"id":"optimizer-params","title":"Optimizer-/Parallelitätseinstellungen anzeigen/ändern","aliases":["optimizer","parallelism","parallel","maxdop","optimizer_switch","cost","planner settings"],"engines":{"oracle":{"command":"SHOW PARAMETER optimizer;\n-- Session: ALTER SESSION SET optimizer_mode = FIRST_ROWS_10;\n-- Parallelism: ALTER SESSION FORCE PARALLEL QUERY PARALLEL 4;","note":"Einstellungen wie optimizer_index_cost_adj und parallel_degree_policy beeinflussen die Planwahl; ändere sie mit Bedacht.","danger":"caution"},"postgres":{"command":"SHOW random_page_cost;\nSHOW max_parallel_workers_per_gather;\n-- Session: SET enable_seqscan = off;","note":"Auf SSDs wird random_page_cost ~1.1 empfohlen. enable_*-Flags dienen der Diagnose, nicht als dauerhafte Lösung.","danger":"caution"},"mysql":{"command":"SELECT @@optimizer_switch;\nSET SESSION optimizer_switch='index_merge=off';","note":"optimizer_switch schaltet einzelne Optimierungen ein/aus; auf Session-Ebene testen.","danger":"caution"},"mssql":{"command":"-- Instance-level MAXDOP and cost threshold:\nEXEC sp_configure 'max degree of parallelism', 8; RECONFIGURE;\n-- Database level: ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 8;","note":"MAXDOP=0 ist unbegrenzt; für OLTP meist 4-8. Der Standardwert 5 für 'cost threshold for parallelism' ist für die meisten zu niedrig.","danger":"caution"},"mongodb":{"command":"# There are no optimizer parameters; indexes and query shape are decisive.\ndb.customer.find({...}).explain('allPlansExecution')","note":"allPlansExecution zeigt alle vom Planner bewerteten Pläne.","danger":"safe"},"redis":{"command":"# Not applicable.","note":"Es gibt keinen Planner; io-threads und die Wahl der Datenstruktur bestimmen die Performance.","danger":"safe"},"elasticsearch":{"command":"GET <index>/_settings?include_defaults=true\n# search limits such as search.max_buckets, index.max_result_window","note":"Es gibt keinen Optimizer; Shard-Anzahl, Routing und Abfragestruktur bestimmen die Performance.","danger":"safe"}}},{"id":"sql-monitor","title":"Laufendes/aktuelles SQL in Echtzeit überwachen","aliases":["sql monitor","live monitoring","real time sql","live query","current sql","monitor active queries"],"engines":{"oracle":{"command":"SET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 250 PAGESIZE 0\nSELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(sql_id=>'&sql_id', type=>'TEXT') AS report FROM dual;\n-- or: SELECT sql_id, status, elapsed_time FROM v$sql_monitor WHERE status='EXECUTING';","note":"Real-Time SQL Monitoring zeigt lange/parallele Abfragen live (Tuning Pack).","danger":"safe"},"postgres":{"command":"SELECT pid, now()-query_start AS dur, state, wait_event, query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start;","note":"Mit auto_explain lassen sich Pläne langsamer Abfragen automatisch protokollieren.","danger":"safe"},"mysql":{"command":"SELECT thread_id, event_name, timer_wait/1e9 AS ms, sql_text\nFROM performance_schema.events_statements_current;","note":"events_statements_current zeigt die aktuell laufenden Anweisungen.","danger":"safe"},"mssql":{"command":"SELECT r.session_id, r.status, r.wait_type, r.percent_complete, t.text\nFROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON s.session_id = r.session_id CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE s.is_user_process = 1;","note":"'Live Query Statistics' in SSMS zeigt den Planfortschritt live.","danger":"safe"},"mongodb":{"command":"db.currentOp({ active: true, secs_running: { $gt: 1 } })","note":"Zeigt lang laufende Operationen live; sie lassen sich mit killOp beenden.","danger":"safe"},"redis":{"command":"MONITOR","note":"Streamt ALLE Befehle live – beeinträchtigt in Produktion die Performance, nur kurz verwenden. Besser SLOWLOG nutzen.","danger":"caution"},"elasticsearch":{"command":"GET _tasks?detailed=true&group_by=parents","note":"Zeigt laufende Tasks live; ein langer Task wird mit POST _tasks/<id>/_cancel abgebrochen.","danger":"safe"}}}]},"rosetta/tuning-adv.json":{"category":"Fortgeschrittenes Tuning","categoryId":"tuning-adv","order":17,"concepts":[{"id":"parallel-query","title":"Parallele Abfrage ausführen / Parallelitätsgrad setzen","aliases":["parallel","parallelism","dop","degree of parallelism","parallel query"],"engines":{"oracle":{"command":"-- At query level with a hint:\nSELECT /*+ PARALLEL(t, 4) */ * FROM big_table t;\n-- Force at session level:\nALTER SESSION FORCE PARALLEL QUERY PARALLEL 4;","note":"Der Hint PARALLEL wendet einen parallelen Scan auf die Tabelle an; degree=DOP. Ein zu hoher DOP erschöpft die Serverressourcen, wähle ihn sorgfältig. parallel_degree_policy=AUTO verwaltet ihn automatisch.","danger":"caution"},"postgres":{"command":"-- Set the number of parallel workers for the session:\nSET max_parallel_workers_per_gather = 4;\n-- The planner adds a Gather/Gather Merge node:\nEXPLAIN (ANALYZE) SELECT count(*) FROM big_table;","note":"Erscheint Gather/Parallel Seq Scan im Plan, ist Parallelität aktiv. Auch max_parallel_workers und min_parallel_table_scan_size spielen eine Rolle.","danger":"safe"},"mysql":{"command":"-- Limited InnoDB parallelism (certain operations only):\nSET GLOBAL innodb_parallel_read_threads = 4;\n-- Effective for COUNT(*) and primary key scans.","note":"MySQL bietet keine allgemeine parallele Abfrageausführung; innodb_parallel_read_threads parallelisiert nur Clustered-Index-Scans (z. B. COUNT(*)). Komplexe JOINs laufen single-threaded.","danger":"safe"},"mssql":{"command":"-- MAXDOP hint at query level:\nSELECT * FROM dbo.big_table OPTION (MAXDOP 4);\n-- Instance level: sp_configure 'max degree of parallelism', 4;","note":"MAXDOP begrenzt die Anzahl paralleler Threads; MAXDOP 1 deaktiviert Parallelität. Wird 'cost threshold for parallelism' überschritten, wird der Plan parallel.","danger":"safe"},"mongodb":{"command":"// Allow disk use for a large aggregation:\ndb.sale.aggregate([ { $group: { _id: \"$city\", t: { $sum: \"$amount\" } } } ], { allowDiskUse: true })\n// A sharded cluster processes each shard in parallel.","note":"In einem Sharded Cluster läuft die Abfrage auf jedem Shard parallel; auf einem einzelnen Knoten laufen Aggregation-Stages meist sequenziell. allowDiskUse ermöglicht Operationen, die das Speicherlimit überschreiten.","danger":"safe"},"redis":{"command":"# Not applicable: there is no concept of query parallelism.","note":"Die Befehlsausführung ist single-threaded; skaliere mit Cluster (Sharding) oder nutze io-threads für Parallelität bei Netzwerk-I/O.","danger":"safe"},"elasticsearch":{"command":"# Shard count = unit of parallelism; a query runs in parallel across shards.\n# Parallelism by slicing within a single shard (with scroll/PIT):\nGET <index>/_search\n{ \"slice\": { \"id\": 0, \"max\": 4 }, \"query\": { \"match_all\": {} } }","note":"Eine Suche läuft gleichzeitig auf allen relevanten Shards; die Anzahl der Primary Shards bestimmt die Grundparallelität. slices teilen große Scroll-/Reindex-Operationen auf.","danger":"safe"}}},{"id":"bind-variables","title":"Bind-Variablen / Parameter Sniffing verwalten","aliases":["bind","bind variable","parameter","parameter sniffing","cursor sharing","prepared statement"],"engines":{"oracle":{"command":"-- Use binds instead of literals (for plan sharing):\nSELECT * FROM customer WHERE city = :city;\n-- Share legacy code that uses literals:\nALTER SESSION SET cursor_sharing = FORCE;","note":"Bind-Variablen reduzieren Hard Parses. Adaptive Cursor Sharing entschärft Sniffing-Probleme, indem es für ungleich verteilte Bind-Werte mehrere Pläne vorhält. cursor_sharing=FORCE mit Vorsicht verwenden.","danger":"caution"},"postgres":{"command":"-- Parameterize with a prepared statement:\nPREPARE q (text) AS SELECT * FROM customer WHERE city = $1;\nEXECUTE q('Ankara');\n-- Set plan cache behavior:\nSET plan_cache_mode = force_custom_plan;","note":"plan_cache_mode=auto wählt zwischen generischen und benutzerdefinierten Plänen; force_custom_plan erzeugt bei jeder Ausführung einen parameterspezifischen Plan (Abhilfe bei sniffing-ähnlichen Problemen).","danger":"safe"},"mysql":{"command":"-- Server-side prepared statement:\nPREPARE stmt FROM 'SELECT * FROM customer WHERE city = ?';\nSET @city = 'Ankara';\nEXECUTE stmt USING @city;\nDEALLOCATE PREPARE stmt;","note":"Prepared Statements verhindern SQL Injection und senken die Parsing-Kosten. MySQL unterstützt Parametrisierung auch auf Treiberebene.","danger":"safe"},"mssql":{"command":"-- Parameterized query (sp_executesql):\nEXEC sp_executesql N'SELECT * FROM dbo.customer WHERE city = @city', N'@city nvarchar(50)', @city = N'Ankara';\n-- Handle sniffing with a hint:\nSELECT ... OPTION (RECOMPILE);\n-- or OPTION (OPTIMIZE FOR (@city = 'Ankara'));","note":"OPTION(RECOMPILE) erzeugt bei jeder Ausführung einen parameterspezifischen Plan (CPU-intensiv). OPTIMIZE FOR fixiert den Plan auf einen bestimmten/typischen Wert; OPTIMIZE FOR UNKNOWN nutzt die durchschnittliche Selektivität.","danger":"caution"},"mongodb":{"command":"// The query shape is the key of the plan cache:\ndb.customer.find({ city: \"Ankara\" }).explain(\"queryPlanner\")\n// Clear a stale plan:\ndb.customer.getPlanCache().clear()","note":"Abfragen mit derselben Form (gleiche Felder/Operatoren) teilen sich den gecachten Plan; andere Literalwerte erzeugen keinen neuen Plan. Ist die Werteverteilung stark ungleich, mit planCacheClear zurücksetzen.","danger":"safe"},"redis":{"command":"# Not applicable: there is no query planner or parameter sniffing.","note":"Befehle werden direkt ausgeführt; das Schlüsseldesign, nicht die Parametrisierung, bestimmt die Performance.","danger":"safe"},"elasticsearch":{"command":"# Reuse the query with a parameterized search template:\nPOST _scripts/customer_city\n{ \"script\": { \"lang\": \"mustache\", \"source\": { \"query\": { \"term\": { \"city\": \"{{city}}\" } } } } }\nGET <index>/_search/template\n{ \"id\": \"customer_city\", \"params\": { \"city\": \"Ankara\" } }","note":"Ein Search Template (Mustache) parametrisiert den Abfragerumpf; die Anwendung ruft ein festes Template mit unterschiedlichen Werten auf, was Abfragekonsistenz und Cache-Nutzung verbessert.","danger":"safe"}}},{"id":"partition-pruning","title":"Partition Pruning prüfen","aliases":["partition pruning","partition elimination","partition","pruning","pstart pstop"],"engines":{"oracle":{"command":"EXPLAIN PLAN FOR\nSELECT * FROM sale WHERE sale_date = DATE '2026-01-15';\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);\n-- Look for 'PARTITION RANGE ITERATOR/SINGLE' and the Pstart/Pstop columns in the plan.","note":"Pstart/Pstop zeigen, dass nur die relevanten Partitionen gelesen werden (ideal ist eine Zahl statt KEY). Eine Funktion/implizite Konvertierung auf dem Partitionsschlüssel verhindert Pruning.","danger":"safe"},"postgres":{"command":"SET enable_partition_pruning = on;\nEXPLAIN (ANALYZE)\nSELECT * FROM sale WHERE sale_date = DATE '2026-01-15';\n-- Verify in the plan that only the relevant partition (child table) is scanned.","note":"Ist enable_partition_pruning aktiv, erscheinen nur passende Partitionen im Plan; die übrigen werden als 'never executed' angezeigt. Konstante Werte werden zur Planzeit, Parameter zur Ausführungszeit gepruned.","danger":"safe"},"mysql":{"command":"-- Show which partitions are scanned:\nEXPLAIN SELECT * FROM sale WHERE sale_date = '2026-01-15';\n-- 5.7 and earlier: EXPLAIN PARTITIONS SELECT ... (removed in 8.0)","note":"Ab MySQL 5.7 listet die Spalte 'partitions' der einfachen EXPLAIN-Ausgabe die zu scannenden Partitionen (die Syntax EXPLAIN PARTITIONS wurde in 8.0 entfernt); es sollten nur die relevanten Partitionen erscheinen. Werden alle Partitionen aufgeführt, findet kein Pruning statt.","danger":"safe"},"mssql":{"command":"-- Find which partition a value falls into:\nSELECT $PARTITION.pf_sale_date('2026-01-15') AS partition_no;\n-- Examine 'Actual Partition Count' and 'Actual Partitions Accessed' in the actual plan.","note":"Die Funktion $PARTITION liefert die Partitionsnummer eines Werts. Liegt die Anzahl der im tatsächlichen Ausführungsplan zugegriffenen Partitionen nahe 1, ist Pruning aktiv; werden alle Partitionen gescannt, passt das Prädikat nicht.","danger":"safe"},"mongodb":{"command":"// Targeted query using the shard key in a sharded cluster:\ndb.sale.find({ shop_id: 42 }).explain(\"executionStats\")\n// In explain, only the relevant shard should appear under 'shards'.","note":"Enthält die Abfrage den Shard Key, leitet mongos sie nur an den relevanten Shard (targeted); andernfalls geht sie an alle Shards (Scatter-Gather). Prüfe die angesprochenen Shards in der explain-Ausgabe.","danger":"safe"},"redis":{"command":"# Not applicable: there is no table/partition concept.","note":"Die Daten werden über die Hash-Slots des Clusters verteilt; statt Query Pruning erfolgt der Schlüsselzugriff direkt.","danger":"safe"},"elasticsearch":{"command":"# Route to the relevant shard with routing:\nGET <index>/_search?routing=customer42\n{ \"query\": { \"term\": { \"customer_id\": 42 } } }\n# Query only the relevant indices with a time-based index pattern:\nGET sale-2026-01/_search","note":"Custom Routing beschränkt die Abfrage auf einen einzigen Shard (ähnlich Pruning). Bei Zeitreihendaten sorgt ein Indexname mit Datumsmuster (sale-2026-01) dafür, dass nur die relevanten Indizes durchsucht werden; nutze ein enges Muster statt eines Wildcards.","danger":"safe"}}},{"id":"result-cache","title":"Ergebnis-/Query-Cache nutzen","aliases":["result cache","result caching","query cache","query result cache","request cache"],"engines":{"oracle":{"command":"-- Result cache at query level:\nSELECT /*+ RESULT_CACHE */ city, COUNT(*) FROM customer GROUP BY city;\n-- Global mode: ALTER SYSTEM SET result_cache_mode = FORCE;","note":"Der Server Result Cache hält das Ergebnis in der SGA; er wird automatisch invalidiert, wenn sich die zugrunde liegenden Tabellen ändern. RESULT_CACHE_MODE=FORCE wendet ihn auf alle geeigneten Abfragen an, MANUAL nur per Hint. Bei häufig geänderten Tabellen bringt er wenig.","danger":"caution"},"postgres":{"command":"-- There is no built-in result cache.\n-- Precompute with a materialized view:\nCREATE MATERIALIZED VIEW mv_city_summary AS\n  SELECT city, COUNT(*) FROM customer GROUP BY city;\nREFRESH MATERIALIZED VIEW mv_city_summary;","note":"PostgreSQL bietet keinen Cache für Abfrageergebnisse; nutze eine Materialized View (manuelles/geplantes REFRESH) oder einen Cache auf Anwendungsebene (Redis). Hinweis: Der Memoize-Knoten bietet Caching auf Zeilenebene innerhalb eines Nested Loop, kein Caching von Abfrageergebnissen.","danger":"safe"},"mysql":{"command":"-- Query Cache was REMOVED in MySQL 8.0.\n-- Alternative: a materialized summary table or an application cache:\nCREATE TABLE city_summary AS SELECT city, COUNT(*) c FROM customer GROUP BY city;","note":"Das alte query_cache_type wurde in MySQL 8.0 vollständig entfernt (wegen Skalierungsproblemen). Nutze stattdessen den InnoDB Buffer Pool, Summentabellen oder einen externen Cache (Redis/Memcached).","danger":"safe"},"mssql":{"command":"-- There is no built-in query result cache.\n-- Precompute with an indexed (materialized) view:\nCREATE VIEW dbo.vw_city_summary WITH SCHEMABINDING AS\n  SELECT city, COUNT_BIG(*) c FROM dbo.customer GROUP BY city;\nCREATE UNIQUE CLUSTERED INDEX ix ON dbo.vw_city_summary(city);","note":"SQL Server hält keinen Ergebnis-Cache (er hält einen Plan-Cache). Für wiederholte Aggregationen wird eine Indexed View automatisch gepflegt; automatisches Matching in Plänen gibt es nur in der Enterprise- (und Developer-)Edition, unter Standard muss die View mit dem Hint WITH (NOEXPAND) abgefragt werden; zusätzlich hält der Buffer Pool häufig gelesene Seiten im Speicher.","danger":"safe"},"mongodb":{"command":"// There is no built-in query result cache.\n// Pre-aggregate and write to a collection ($merge):\ndb.sale.aggregate([\n  { $group: { _id: \"$city\", t: { $sum: \"$amount\" } } },\n  { $merge: \"city_summary\" }\n])","note":"MongoDB bietet keinen Ergebnis-Cache; WiredTiger hält häufig genutzte Daten im Speicher. Für teure Aggregationen nutze eine vorberechnete Summen-Collection per $merge/$out oder einen Anwendungscache (Redis).","danger":"safe"},"redis":{"command":"# Redis is already a cache layer; cache the result manually:\nSET query:city_summary \"{...}\" EX 300\nGET query:city_summary\n# If already computed, return it with GET; otherwise compute it and write it with SET.","note":"Das übliche Cache-aside-Muster: Existiert der Schlüssel, liefere ihn schnell mit GET; andernfalls aus der Quelle berechnen und mit SET samt EX (TTL) speichern. Redis dient meist als Ergebnis-Cache für andere Datenbanken.","danger":"safe"},"elasticsearch":{"command":"# Shard request cache (enabled by default for size:0 aggregations):\nGET <index>/_search?request_cache=true\n{ \"size\": 0, \"aggs\": { \"city\": { \"terms\": { \"field\": \"city\" } } } }\n# Setting: PUT <index>/_settings { \"index.requests.cache.enable\": true }","note":"Der Shard Request Cache cacht die Ergebnisse pro Shard für Anfragen mit size:0 (nur Aggregation); er wird beim Refresh des Index invalidiert. Abfragen mit size>0 (die Treffer liefern) werden standardmäßig nicht gecacht.","danger":"safe"}}}]},"rosetta/advanced-features.json":{"category":"Erweiterte Funktionen","categoryId":"advanced-features","order":18,"concepts":[{"id":"full-text-search","title":"Volltextsuche","aliases":["full text","full text","fts","search","text search"],"engines":{"oracle":{"command":"-- Oracle Text: create a CONTEXT index\nCREATE INDEX article_idx ON article(content) INDEXTYPE IS CTXSYS.CONTEXT;\n-- Search with CONTAINS (label 1 ties CONTAINS to SCORE):\nSELECT id, SCORE(1) FROM article\nWHERE CONTAINS(content, 'database AND performance', 1) > 0\nORDER BY SCORE(1) DESC;","note":"Oracle Text (früher interMedia) bietet sprachbewusste Volltextsuche über einen CONTEXT-Index. Der Operator CONTAINS unterstützt Suchoperatoren wie AND/OR/NEAR. Der Index wird mit CTX_DDL.SYNC_INDEX oder automatischem SYNC gepflegt.","danger":"safe"},"postgres":{"command":"-- Search with tsvector/tsquery, accelerated by a GIN index:\nCREATE INDEX article_fts_idx ON article\n  USING GIN (to_tsvector('english', content));\nSELECT id, ts_rank(to_tsvector('english', content), q) AS score\nFROM article, to_tsquery('english', 'database & performance') q\nWHERE to_tsvector('english', content) @@ q\nORDER BY score DESC;","note":"Die eingebaute Volltextsuche von PostgreSQL gleicht tsvector (Dokument) mit tsquery (Abfrage) über den Operator @@ ab. Ein GIN-Index beschleunigt Suchen in großen Texten. Die Sprachkonfiguration ('english') übernimmt Stemming und Stoppwortbehandlung.","danger":"safe"},"mysql":{"command":"-- Create a FULLTEXT index (InnoDB/MyISAM):\nALTER TABLE article ADD FULLTEXT INDEX ft_content (content);\n-- Search in natural language / boolean mode:\nSELECT id, MATCH(content) AGAINST('database performance') AS score\nFROM article\nWHERE MATCH(content) AGAINST('+database +performance' IN BOOLEAN MODE);","note":"Ein FULLTEXT-Index in MySQL wird mit der Syntax MATCH ... AGAINST abgefragt. IN NATURAL LANGUAGE MODE liefert einen Relevanzwert; IN BOOLEAN MODE erlaubt mit den Operatoren +/-/* eine feine Steuerung. InnoDB unterstützt Volltextsuche ab 5.6.","since":"MySQL 5.6","xnote":"Die InnoDB-Volltextsuche kam mit MySQL 5.6; davor gab es sie nur in MyISAM.","danger":"safe"},"mssql":{"command":"-- Create a Full-Text Catalog and index:\nCREATE FULLTEXT CATALOG ft_catalog;\nCREATE FULLTEXT INDEX ON dbo.article(content)\n  KEY INDEX PK_article ON ft_catalog;\n-- Search with CONTAINS / FREETEXT:\nSELECT id FROM dbo.article\nWHERE CONTAINS(content, '\"database\" AND \"performance\"');","note":"Die Volltextsuche von SQL Server benötigt zuerst einen Full-Text Catalog, dann einen Full-Text Index auf der Tabelle (ein eindeutiger KEY INDEX ist Pflicht). CONTAINS unterstützt exakte Suche/Umkreissuche und logische Operatoren; FREETEXT unterstützt bedeutungsbasierte (linguistische) Suche. Die Komponente Full-Text Search muss installiert sein.","danger":"safe"},"mongodb":{"command":"// Create a text index:\ndb.article.createIndex({ content: \"text\" })\n// Search with $text and sort by relevance score:\ndb.article.find(\n  { $text: { $search: \"database performance\" } },\n  { score: { $meta: \"textScore\" } }\n).sort({ score: { $meta: \"textScore\" } })","note":"Ein Text-Index in MongoDB bietet Volltextsuche über den Operator $text; eine Collection kann nur einen Text-Index haben. Das Metafeld textScore liefert die Relevanz. Für fortgeschrittenere Suche wird Atlas Search ($search, Lucene-basiert) genutzt.","danger":"safe"},"redis":{"command":"# Requires the RediSearch module (Redis Stack).\n# Define the index:\nFT.CREATE article_idx ON HASH PREFIX 1 article: SCHEMA content TEXT\n# Full-text search:\nFT.SEARCH article_idx \"database performance\"","note":"Core Redis bietet keine Volltextsuche; das Modul RediSearch (in Redis Stack enthalten) ermöglicht Indexierung mit FT.CREATE und Suche mit FT.SEARCH. Stemming, unscharfe Suche, Filterung und Sortierung werden unterstützt. Ohne installiertes Modul ist die Funktion nicht verfügbar.","danger":"safe"},"elasticsearch":{"command":"# Elasticsearch is natively a full-text search engine.\n# match query (analyzed text search):\nGET article/_search\n{\n  \"query\": {\n    \"match\": { \"content\": \"database performance\" }\n  }\n}","note":"Elasticsearch ist im Kern eine auf Lucene aufbauende Volltextsuchmaschine; text-Felder werden beim Indexieren analysiert (Tokenisierung, Stemming). Die match-Abfrage liefert nach Relevanzwert (_score) sortierte Ergebnisse. Sprachspezifische Analyzer (z. B. 'english') lassen sich zuweisen.","danger":"safe"}}},{"id":"json-query","title":"JSON-Daten abfragen","aliases":["json","json query","json query","jsonb","document query"],"engines":{"oracle":{"command":"-- Extract a scalar value with JSON_VALUE:\nSELECT JSON_VALUE(data, '$.customer.city') AS city FROM orders;\n-- Expand into rows with JSON_TABLE:\nSELECT t.product, t.quantity\nFROM orders s,\n     JSON_TABLE(s.data, '$.items[*]'\n       COLUMNS (product VARCHAR2(50) PATH '$.product',\n                quantity NUMBER PATH '$.quantity')) t;","note":"Oracle (12c+) fragt JSON-Daten mit SQL/JSON-Pfadausdrücken ab. JSON_VALUE liefert einen einzelnen Skalarwert, JSON_QUERY ein Objekt-/Array-Fragment, JSON_TABLE wandelt JSON in eine relationale Zeilen-Spalten-Struktur um. Ab 21c wird ein nativer JSON-Datentyp unterstützt.","since":"Oracle 12c","xnote":"SQL/JSON-Abfragen kamen mit Oracle 12c, der native JSON-Datentyp mit 21c.","danger":"safe"},"postgres":{"command":"-- jsonb operators:\nSELECT data->'customer'->>'city' AS city FROM orders;\n-- Containment (@>) and a GIN index:\nCREATE INDEX ix ON orders USING GIN (data);\nSELECT * FROM orders\nWHERE data @> '{\"status\": \"approved\"}';","note":"Der PostgreSQL-Typ jsonb bietet umfangreiche Operatoren: -> (json-Objekt/-Element), ->> (Textwert), #> (Pfad), @> (Enthaltensein), ? (Schlüssel existiert). Ein GIN-Index beschleunigt @>- und ?-Abfragen. Auch SQL/JSON-Pfadabfragen sind mit jsonb_path_query möglich.","since":"PostgreSQL 9.4","xnote":"Der Typ json kam mit PostgreSQL 9.2, der Typ jsonb mit 9.4.","danger":"safe"},"mysql":{"command":"-- JSON_EXTRACT or shorthand operators:\nSELECT JSON_EXTRACT(data, '$.customer.city') AS city FROM orders;\n-- ->> returns unquoted text:\nSELECT data->>'$.customer.city' AS city FROM orders\nWHERE data->>'$.status' = 'approved';","note":"MySQL (5.7+) fragt den nativen JSON-Typ mit JSON_EXTRACT() ab; der Operator -> ist eine Kurzform für JSON_EXTRACT, ->> für JSON_UNQUOTE(JSON_EXTRACT()). Suchen auf JSON-Feldern lassen sich mit Generated Column + Index beschleunigen.","since":"MySQL 5.7","xnote":"Der native JSON-Datentyp kam mit MySQL 5.7.","danger":"safe"},"mssql":{"command":"-- Extract a scalar value with JSON_VALUE:\nSELECT JSON_VALUE(data, '$.customer.city') AS city FROM orders;\n-- Expand into rows with OPENJSON:\nSELECT k.product, k.quantity\nFROM orders s\nCROSS APPLY OPENJSON(s.data, '$.items')\n  WITH (product NVARCHAR(50) '$.product', quantity INT '$.quantity') k;","note":"SQL Server (2016+) speichert JSON in einer NVARCHAR-Spalte. JSON_VALUE liefert einen Skalarwert, JSON_QUERY ein Objekt/Array, OPENJSON wandelt JSON in Tabellenform um. ISJSON() validiert es. Zugriffe auf JSON-Pfade lassen sich mit Computed Column + Index beschleunigen.","since":"SQL Server 2016","xnote":"Die JSON-Funktionen (JSON_VALUE/OPENJSON) kamen mit SQL Server 2016.","danger":"safe"},"mongodb":{"command":"// MongoDB is natively a document (BSON/JSON) database.\n// Access nested fields with dot notation:\ndb.orders.find({ \"customer.city\": \"Ankara\" })\n// Array element and projection:\ndb.orders.find(\n  { \"items.product\": \"pen\" },\n  { \"items.$\": 1 }\n)","note":"MongoDB speichert alle Daten als Dokumente (JSON-ähnliches BSON); die Abfrage ist nativ. Mit der Punktnotation erreichst du verschachtelte Felder und Array-Elemente. Komplexe Transformationen erfolgen mit der Aggregation Pipeline, Indizes ermöglichen schnelle Zugriffe auf verschachtelte Felder.","danger":"safe"},"redis":{"command":"# Requires the RedisJSON module (Redis Stack).\n# Write a JSON document:\nJSON.SET order:1 $ '{\"customer\":{\"city\":\"Ankara\"},\"status\":\"approved\"}'\n# Read a value with JSONPath:\nJSON.GET order:1 $.customer.city","note":"Core Redis hat keinen JSON-Typ; das Modul RedisJSON (Redis Stack) ermöglicht JSONPath-basiertes Lesen und Schreiben mit JSON.SET/JSON.GET. Zusammen mit RediSearch lassen sich JSON-Dokumente indexieren und abfragen. Ohne das Modul wird JSON als String gespeichert und in der Anwendung geparst.","danger":"safe"},"elasticsearch":{"command":"# Documents are already JSON; query fields directly:\nGET orders/_search\n{ \"query\": { \"term\": { \"customer.city\": \"Ankara\" } } }\n# For nested arrays, use the nested type and a nested query:\nGET orders/_search\n{ \"query\": { \"nested\": { \"path\": \"items\",\n  \"query\": { \"match\": { \"items.product\": \"pen\" } } } } }","note":"Elasticsearch speichert und indexiert Dokumente nativ als JSON. Um bei Arrays verschachtelter Objekte die Elementgrenzen zu erhalten, nutze den Feldtyp 'nested' und eine nested-Abfrage; andernfalls werden Arrays vom Typ object abgeflacht (Gefahr von Kreuztreffern). Die Unterscheidung object vs. nested ist für korrekte Ergebnisse entscheidend.","danger":"safe"}}},{"id":"spatial-geo","title":"Geografische / räumliche Abfrage","aliases":["spatial","geographic","geo","gis","location","map"],"engines":{"oracle":{"command":"-- Oracle Spatial: SDO_GEOMETRY column and spatial index\nCREATE INDEX location_sidx ON shop(location)\n  INDEXTYPE IS MDSYS.SPATIAL_INDEX;\n-- Shops within 5 km:\nSELECT name FROM shop\nWHERE SDO_WITHIN_DISTANCE(location,\n  SDO_GEOMETRY(2001, 4326,\n    SDO_POINT_TYPE(32.85, 39.92, NULL), NULL, NULL),\n  'distance=5 unit=KM') = 'TRUE';","note":"Oracle Spatial speichert Geodaten im Typ SDO_GEOMETRY; ein Eintrag in USER_SDO_GEOM_METADATA und ein Spatial Index sind erforderlich. Operatoren wie SDO_WITHIN_DISTANCE, SDO_RELATE und SDO_NN beantworten Distanz-, Schnitt- und Nearest-Neighbor-Abfragen. SRID 4326 ist das WGS84-System (Breite/Länge).","danger":"safe"},"postgres":{"command":"-- PostGIS extension:\nCREATE EXTENSION IF NOT EXISTS postgis;\nCREATE INDEX location_gix ON shop USING GIST (location);\n-- Shops within 5 km (geography, meters):\nSELECT name FROM shop\nWHERE ST_DWithin(location,\n  ST_SetSRID(ST_MakePoint(32.85, 39.92), 4326)::geography,\n  5000);","note":"PostGIS macht PostgreSQL zu einer vollwertigen räumlichen Datenbank. Es bietet die Typen geometry (planar) und geography (sphärisch) sowie Hunderte ST_-Funktionen wie ST_Distance/ST_DWithin/ST_Intersects/ST_Contains. Ein GiST-/SP-GiST-Index beschleunigt räumliche Abfragen.","danger":"safe"},"mysql":{"command":"-- Spatial column (with SRID) and SPATIAL index:\nALTER TABLE shop MODIFY location POINT NOT NULL SRID 4326;\nALTER TABLE shop ADD SPATIAL INDEX (location);\n-- Compute distance (meters, geography):\nSELECT name,\n  ST_Distance(location,\n    ST_SRID(POINT(32.85, 39.92), 4326)) AS distance\nFROM shop\nORDER BY distance LIMIT 10;","note":"MySQL (5.7+) unterstützt OGC-konforme räumliche Datentypen (POINT, POLYGON usw.) und ST_-Funktionen. Ein SPATIAL-Index (R-Tree) lässt sich nur unter InnoDB/MyISAM und auf NOT NULL-Spalten anlegen; damit der Optimizer ihn nutzt, muss die Spalte mit einem SRID-Attribut definiert sein (SRID 4326). Mit SRID 4326 liefert ST_Distance die geodätische Distanz in Metern. Bei POINT(x, y) + ST_SRID ist x der Längengrad und y der Breitengrad; bei WKT-Eingabe (ST_GeomFromText) mit SRID 4326 lautet die Reihenfolge Breite-Länge.","danger":"safe"},"mssql":{"command":"-- geography type and spatial index:\nCREATE SPATIAL INDEX location_sidx ON dbo.shop(location);\n-- Shops within 5 km (STDistance returns meters):\nSELECT name FROM dbo.shop\nWHERE location.STDistance(\n  geography::Point(39.92, 32.85, 4326)) <= 5000;","note":"SQL Server bietet zwei räumliche Typen: geography (sphärisch, Breite/Länge) und geometry (planar). Methoden wie STDistance, STIntersects und STContains beantworten die Abfragen. Ein Spatial Index (rasterbasiert) verbessert die Performance. Beachte die Argumentreihenfolge von geography::Point(latitude, longitude, SRID).","danger":"safe"},"mongodb":{"command":"// 2dsphere index (for GeoJSON):\ndb.shop.createIndex({ location: \"2dsphere\" })\n// Shops nearest to a given point ($near):\ndb.shop.find({\n  location: {\n    $near: {\n      $geometry: { type: \"Point\", coordinates: [32.85, 39.92] },\n      $maxDistance: 5000\n    }\n  }\n})","note":"MongoDB speichert Geodaten im GeoJSON-Format und führt sphärische Abfragen mit einem 2dsphere-Index aus. Die Operatoren $near (nach Distanz sortiert), $geoWithin (innerhalb eines Gebiets) und $geoIntersects werden unterstützt. Koordinaten stehen in der Reihenfolge [longitude, latitude]; $maxDistance ist in Metern.","danger":"safe"},"redis":{"command":"# Built-in geospatial commands (built on sorted sets):\nGEOADD shops 32.85 39.92 \"kizilay\"\nGEOADD shops 32.86 39.93 \"cankaya\"\n# Search within 5 km of a point:\nGEOSEARCH shops FROMLONLAT 32.85 39.92\n  BYRADIUS 5 km ASC","note":"Redis speichert geografische Positionen mit GEOADD (longitude latitude member) in einem Sorted Set. GEOSEARCH (Redis 6.2+, ersetzt das ältere GEORADIUS) liefert die Mitglieder innerhalb eines Radius oder Rechtecks, nach Distanz sortiert. GEODIST liefert die Distanz zwischen zwei Punkten. Dies ist eine Kernfunktion; ein Modul ist nicht nötig.","danger":"safe"},"elasticsearch":{"command":"# Map the field as geo_point:\nPUT shop\n{ \"mappings\": { \"properties\": { \"location\": { \"type\": \"geo_point\" } } } }\n# Query within 5 km:\nGET shop/_search\n{ \"query\": { \"geo_distance\": {\n  \"distance\": \"5km\",\n  \"location\": { \"lat\": 39.92, \"lon\": 32.85 } } } }","note":"Elasticsearch unterstützt die Feldtypen geo_point (Punkt) und geo_shape (Polygon/Linie). Gefiltert wird mit den Abfragen geo_distance (Radius), geo_bounding_box (Rechteck) und geo_polygon, sortiert mit geo_distance, aggregiert mit geohash_grid/geotile_grid. Koordinaten werden als lat/lon oder als Array [lon, lat] angegeben.","danger":"safe"}}},{"id":"window-functions-adv","title":"Fensterfunktionen (ROW_NUMBER/RANK/LAG)","aliases":["window function","window function","row_number","rank","lag","over","analytic"],"engines":{"oracle":{"command":"-- Rank by amount within each city and fetch the previous value:\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM sale;","note":"Oracle war Vorreiter bei analytischen (Fenster-)Funktionen; ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, SUM/AVG OVER, NTILE und FIRST_VALUE werden vollständig unterstützt. Innerhalb von OVER definiert PARTITION BY die Gruppen, ORDER BY die Reihenfolge und der Frame (ROWS/RANGE BETWEEN) das gleitende Fenster.","since":"Oracle 8i","danger":"safe"},"postgres":{"command":"-- Window functions with OVER():\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM sale;","note":"PostgreSQL unterstützt Fensterfunktionen vollständig mit Standard-SQL-Syntax. ROW_NUMBER/RANK/DENSE_RANK, LAG/LEAD, Aggregate mit OVER, NTILE und FIRST_VALUE/LAST_VALUE sind verfügbar. Wiederholte Fensterdefinitionen lassen sich mit der WINDOW-Klausel benennen und gemeinsam nutzen.","since":"PostgreSQL 8.4","danger":"safe"},"mysql":{"command":"-- MySQL 8.0+ window functions:\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM sale;","note":"MySQL hat Fensterfunktionen in Version 8.0 eingeführt (auch MariaDB 10.2+ unterstützt sie). ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, NTILE und Aggregate mit OVER sind verfügbar. Versionen vor 8.0 fehlen diese Funktionen; früher wurden sie mit Benutzervariablen nachgebildet.","since":"MySQL 8.0","xnote":"Vor MySQL 8.0 gab es keine Fensterfunktionen; sie wurden mit Benutzervariablen nachgebildet.","danger":"safe"},"mssql":{"command":"-- Window functions with OVER():\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM dbo.sale;","note":"SQL Server unterstützt Fensterfunktionen seit 2005 (ROW_NUMBER/RANK) und 2012 (LAG/LEAD, Framing, Aggregate mit OVER). PARTITION BY, ORDER BY und der ROWS/RANGE-Frame sind vollständig verfügbar. Für Paging sind OFFSET/FETCH oder ROW_NUMBER üblich.","since":"SQL Server 2005","xnote":"Ranking-Funktionen kamen 2005, LAG/LEAD und Framing 2012.","danger":"safe"},"mongodb":{"command":"// Window calculations with $setWindowFields:\ndb.sale.aggregate([\n  { $setWindowFields: {\n      partitionBy: \"$city\",\n      sortBy: { amount: -1 },\n      output: {\n        row_num: { $documentNumber: {} },\n        rnk: { $rank: {} },\n        prev_amount: { $shift: { output: \"$amount\", by: -1 } }\n      }\n  } }\n])","note":"Die in MongoDB 5.0 eingeführte Aggregation-Stage $setWindowFields bietet Fensterfunktionen: $documentNumber (ROW_NUMBER), $rank/$denseRank, $shift (LAG/LEAD), Aggregatfunktionen und gleitende Fenster (window: { documents/range }). partitionBy und sortBy entsprechen PARTITION BY/ORDER BY in SQL.","since":"MongoDB 5.0","xnote":"Die Stage $setWindowFields kam mit MongoDB 5.0.","danger":"safe"},"redis":{"command":"# Not applicable: there are no SQL-like window functions.","note":"Redis ist ein Key-Value-Store ohne analytische Fensterfunktionen. Für sortierte Ergebnisse lässt sich ein Sorted Set nutzen (rangähnlicher Zugriff mit ZADD/ZRANGE); relationale Fensteroperationen wie PARTITION BY/LAG erfolgen jedoch in der Anwendungsschicht.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: there are no SQL-like window functions.\n# Closest equivalent: bucket aggregations.\nGET sale/_search\n{ \"size\": 0, \"aggs\": { \"by_city\": {\n  \"terms\": { \"field\": \"city\" },\n  \"aggs\": { \"total\": { \"sum\": { \"field\": \"amount\" } } } } } }","note":"Elasticsearch bietet keine klassischen Fensterfunktionen (ROW_NUMBER/RANK/LAG). Gruppierung und Sortierung erfolgen über das Aggregation-Framework: Eine terms-Aggregation kommt PARTITION BY nahe, top_hits den Top-Datensätzen und derivative/serial_diff (Pipeline-Aggregationen) LAG-ähnlichen Differenzen aufeinanderfolgender Werte.","danger":"safe"}}},{"id":"columnstore-analytics","title":"Spaltenorientierte Speicherung / Analytik","aliases":["columnstore","columnar","column store","analytics","olap","columnar"],"engines":{"oracle":{"command":"-- Database In-Memory: keep the table in memory in columnar format\nALTER TABLE sale INMEMORY;\n-- With priority and compression:\nALTER TABLE sale INMEMORY PRIORITY HIGH\n  MEMCOMPRESS FOR QUERY HIGH;","note":"Oracle Database In-Memory (12.1.0.2+) beschleunigt analytische Abfragen, indem Tabellen spaltenorientiert im Speicher gehalten werden (In-Memory Column Store, IM), während auf Platte das Zeilenformat erhalten bleibt (Dual Format). INMEMORY ist eine separat lizenzierte Option. Der Parameter inmemory_size legt die Größe des IM-Bereichs fest.","since":"Oracle 12.1.0.2","xnote":"Der Database In-Memory Column Store kam mit Oracle 12.1.0.2 (separate Lizenz).","danger":"caution"},"postgres":{"command":"-- Core PostgreSQL is row-based; columnar storage needs an extension:\n-- Citus columnar access method:\nCREATE EXTENSION IF NOT EXISTS citus;\nCREATE TABLE sale_columnar (...) USING columnar;\n-- Alternatives: the Hydra/cstore_fdw extensions.","note":"Core-PostgreSQL bietet nur zeilenbasierte Speicherung (Heap). Für spaltenorientierte Speicherung werden die Access Method 'columnar' von Citus (USING columnar), Hydra oder die ältere Extension cstore_fdw genutzt; sie bieten Kompression und Scan-Geschwindigkeit für Analytik-/Data-Warehouse-Workloads.","danger":"safe"},"mysql":{"command":"-- Not applicable: core InnoDB does not offer columnar storage.\n-- For analytics: MySQL HeatWave (cloud, in-memory columnar)\n-- or integration with external ClickHouse/Apache Doris.","note":"Standard-MySQL (InnoDB) ist zeilenbasiert und hat keinen eingebauten Columnstore. Zur Analytikbeschleunigung wird MySQL HeatWave genutzt (ein spaltenorientierter In-Memory-Beschleuniger in Oracle Cloud); On-Premises-Lösungen setzen meist externe spaltenorientierte Engines wie ClickHouse oder Apache Doris ein.","danger":"safe"},"mssql":{"command":"-- Clustered columnstore index (for analytic tables):\nCREATE CLUSTERED COLUMNSTORE INDEX cci_sale ON dbo.sale;\n-- Nonclustered columnstore on a rowstore table (HTAP):\nCREATE NONCLUSTERED COLUMNSTORE INDEX ncci_sale\n  ON dbo.sale (city, product, amount, created_at);","note":"Columnstore-Indizes in SQL Server (2012 schreibgeschützt nonclustered; 2014 aktualisierbar clustered; 2016 aktualisierbar nonclustered) speichern Daten spaltenweise mit hoher Kompression; mit Batch Mode beschleunigen sie aggregations-/scanlastige analytische Abfragen stark. Clustered eignet sich für Data Warehouses, nonclustered für HTAP.","since":"SQL Server 2012","xnote":"Der Columnstore-Index kam 2012 (schreibgeschützt), aktualisierbarer Clustered Columnstore 2014, aktualisierbarer Nonclustered Columnstore 2016.","danger":"safe"},"mongodb":{"command":"-- Not applicable: MongoDB is document (row-like) based.\n-- For analytics: dedicated Analytics Nodes in Atlas\n-- or export to a columnar format ($out)/an external engine.","note":"MongoDB speichert Dokumente als zeilenähnliches BSON; eine eingebaute spaltenorientierte Speicherung (Columnstore) gibt es nicht. Für analytische Workloads werden dedizierte Analytics Nodes in einem Atlas Replica Set, Atlas Data Federation oder der Export in ein spaltenorientiertes System (z. B. Parquet) genutzt.","danger":"safe"},"redis":{"command":"-- Not applicable: there is no concept of columnar storage.","note":"Redis ist ein In-Memory-Key-Value-Store; die Unterscheidung zwischen Zeilen- und Spaltenspeicherung gibt es nicht. Analytische Aggregationen erfolgen mit speziellen Strukturen wie Sorted Set, HyperLogLog (approximatives Zählen) oder RedisTimeSeries; für OLAP im großen Maßstab ist Redis nicht geeignet.","danger":"safe"},"elasticsearch":{"command":"# doc_values: fields are stored column-wise on disk (enabled by default).\n# Required for sorting/aggregation; active on non-text fields.\nPUT sale\n{ \"mappings\": { \"properties\":\n  { \"amount\": { \"type\": \"double\", \"doc_values\": true } } } }","note":"Für Sortierungen und Aggregationen hält Elasticsearch Felder in einer plattenbasierten spaltenorientierten Struktur namens doc_values (bei numeric-/keyword-/date-Feldern standardmäßig aktiv). Dieses spaltenorientierte Layout beschleunigt Analytik-/Aggregationsabfragen; bei text-Feldern ist es standardmäßig deaktiviert.","danger":"safe"}}},{"id":"in-memory","title":"In-Memory-Tabellen / Beschleunigung","aliases":["in memory","in-memory","memory table","acceleration","in-memory oltp"],"engines":{"oracle":{"command":"-- Keep the table in memory with Database In-Memory:\nALTER TABLE orders INMEMORY;\n-- Memory size (init parameter):\nALTER SYSTEM SET inmemory_size = 4G SCOPE=SPFILE;","note":"Oracle Database In-Memory beschleunigt Analytik, indem häufig abgefragte Tabellen spaltenorientiert im Speicher gehalten werden (separate Lizenz). Für OLTP halten auch der Buffer Cache und der KEEP-Pool häufig genutzte Blöcke im Speicher. Die In-Memory-Lösung wahrt die Dauerhaftigkeit über das Zeilenformat auf Platte.","danger":"caution"},"postgres":{"command":"-- Fast table without durability (writes no WAL):\nCREATE UNLOGGED TABLE temp_calc (id int, data text);\n-- Session-private temporary table (tends to stay in memory):\nCREATE TEMP TABLE session_data (id int) ON COMMIT DELETE ROWS;","note":"PostgreSQL bietet keine dedizierte In-Memory-Tabellen-Engine; eine UNLOGGED-Tabelle überspringt WAL-Schreibvorgänge und beschleunigt so das Schreiben (Inhalt geht bei einem Crash verloren; nicht dauerhaft). TEMP-Tabellen leben für die Dauer der Session und werden über temp_buffers nach Möglichkeit im Speicher gehalten. Häufig genutzte Daten werden in shared_buffers gecacht.","danger":"caution"},"mysql":{"command":"-- Table using the MEMORY (HEAP) storage engine:\nCREATE TABLE cache_table (\n  id INT PRIMARY KEY,\n  data VARCHAR(100)\n) ENGINE=MEMORY;","note":"Die MySQL-Storage-Engine MEMORY (früher HEAP) hält Tabellendaten vollständig im RAM; sie ist sehr schnell, aber die Daten gehen beim Neustart des Servers verloren (die Struktur bleibt). Sie unterstützt kein BLOB/TEXT und nutzt eine feste Zeilengröße. Für dauerhafte häufig genutzte Daten wird der InnoDB Buffer Pool bevorzugt.","danger":"caution"},"mssql":{"command":"-- In-Memory OLTP: memory-optimized table\nCREATE TABLE dbo.session_state (\n  id INT NOT NULL PRIMARY KEY NONCLUSTERED,\n  data NVARCHAR(100)\n) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);","note":"SQL Server In-Memory OLTP (Hekaton) bietet hochgradig nebenläufiges OLTP mit speicheroptimierten Tabellen (MEMORY_OPTIMIZED=ON) und latch-freien Strukturen. DURABILITY=SCHEMA_AND_DATA hält die Daten dauerhaft; SCHEMA_ONLY bewahrt nur das Schema. Erfordert eine speicheroptimierte Dateigruppe.","since":"SQL Server 2014","xnote":"In-Memory OLTP (Hekaton) kam mit SQL Server 2014.","danger":"caution"},"mongodb":{"command":"-- Hot data is kept in the WiredTiger cache automatically.\n-- Set the cache size (default ~50% of RAM):\n// mongod --wiredTigerCacheSizeGB 8\n// or in the configuration: storage.wiredTiger.engineConfig.cacheSizeGB","note":"MongoDB hat keinen separaten In-Memory-Tabellentyp (die alte In-Memory Storage Engine gab es nur in Enterprise, sie ist veraltet). Die Standard-Engine WiredTiger verbessert die Lesegeschwindigkeit, indem sie häufig genutzte Daten und Indizes in ihrem Speicher-Cache hält; auch der Dateicache des Betriebssystems trägt bei.","danger":"safe"},"redis":{"command":"# Redis already runs entirely in memory; nothing extra is needed.\n# Persistence and memory policy settings:\nCONFIG SET maxmemory 4gb\nCONFIG SET maxmemory-policy allkeys-lru","note":"Redis hält alle Daten im RAM; darauf beruht seine Geschwindigkeit. Persistenz bieten RDB (Snapshots) und/oder AOF (Befehlsprotokoll). Das Limit maxmemory und die Eviction Policy (LRU/LFU/random) bestimmen das Verhalten bei vollem Speicher.","danger":"safe"},"elasticsearch":{"command":"-- Not applicable: there is no fully in-memory table mode.\n# Place hot data on fast nodes with a hot/cold architecture:\nPUT sale/_settings\n{ \"index.routing.allocation.require.data\": \"hot\" }","note":"Elasticsearch speichert Daten auf Platte (Lucene-Segmente); eine dedizierte In-Memory-Tabellen-Engine gibt es nicht. Für Geschwindigkeit stützt es sich auf den Dateicache des Betriebssystems (Page Cache), doc_values und eine Hot-Warm-Cold-Architektur. Ausreichend RAM und passende Heap-Einstellungen sind für die Performance entscheidend.","danger":"safe"}}},{"id":"time-series","title":"Zeitreihendaten","aliases":["time series","time series","timeseries","iot","metrics","sensor"],"engines":{"oracle":{"command":"-- Range partitioning by date:\nCREATE TABLE measurements (\n  ts TIMESTAMP, sensor_id NUMBER, value NUMBER)\nPARTITION BY RANGE (ts)\nINTERVAL (NUMTODSINTERVAL(1,'DAY'))\n( PARTITION p0 VALUES LESS THAN (TIMESTAMP '2026-01-01 00:00:00') );","note":"In Oracle werden Zeitreihendaten meist mit RANGE-(INTERVAL-)Partitionierung verwaltet; neue Partitionen entstehen automatisch, alte lassen sich einfach löschen (Partition Pruning beschleunigt Abfragen). Auch Kompression (ILM/ADO) und Materialized Views zur Aggregation werden genutzt.","danger":"safe"},"postgres":{"command":"-- Hypertable with the TimescaleDB extension:\nCREATE EXTENSION IF NOT EXISTS timescaledb;\nCREATE TABLE measurements (ts TIMESTAMPTZ, sensor_id INT, value DOUBLE PRECISION);\nSELECT create_hypertable('measurements', 'ts');\n-- Summarize with a continuous aggregate.","note":"PostgreSQL kann Zeitreihen mit nativer Partitionierung (PARTITION BY RANGE) halten; die Extension TimescaleDB ergänzt Hypertables mit automatischem zeitbasiertem Chunking, Kompression, Retention Policies und Continuous Aggregates. Sie ist eine gängige Lösung für große IoT-/Metrik-Workloads.","danger":"safe"},"mysql":{"command":"-- RANGE partitioning by date:\nCREATE TABLE measurements (\n  ts DATETIME, sensor_id INT, value DOUBLE,\n  PRIMARY KEY (ts, sensor_id))\nPARTITION BY RANGE (TO_DAYS(ts)) (\n  PARTITION p2026_01 VALUES LESS THAN (TO_DAYS('2026-02-01')),\n  PARTITION pmax VALUES LESS THAN MAXVALUE);","note":"MySQL bietet keine dedizierte Zeitreihen-Engine; RANGE-Partitionierung (mit TO_DAYS/UNIX_TIMESTAMP) ermöglicht schnelles Löschen alter Daten und Partition Pruning. Für Zeitreihen mit hohem Volumen werden meist externe Lösungen (TimescaleDB, InfluxDB) oder Summentabellen bevorzugt.","danger":"safe"},"mssql":{"command":"-- Partitioned table (partition function + scheme) or\n-- system-versioned temporal table (history kept automatically):\nCREATE TABLE dbo.measurements (\n  id INT PRIMARY KEY, value DECIMAL(10,2),\n  valid_from DATETIME2 GENERATED ALWAYS AS ROW START,\n  valid_to DATETIME2 GENERATED ALWAYS AS ROW END,\n  PERIOD FOR SYSTEM_TIME (valid_from, valid_to))\nWITH (SYSTEM_VERSIONING = ON);","note":"SQL Server verwaltet Zeitreihen per Partitionierung mit Partition Function/Scheme; das bietet Pruning und schnelle Archivierung für Daten über große Zeiträume. Zusätzlich halten systemversionierte temporale Tabellen (SYSTEM_VERSIONING) die Änderungshistorie der Zeilen automatisch fest und erlauben Abfragen zu einem Zeitpunkt (FOR SYSTEM_TIME).","since":"SQL Server 2016","xnote":"Systemversionierte temporale Tabellen kamen mit SQL Server 2016.","danger":"safe"},"mongodb":{"command":"// Time-series collection (MongoDB 5.0+):\ndb.createCollection(\"measurements\", {\n  timeseries: {\n    timeField: \"ts\",\n    metaField: \"sensor_id\",\n    granularity: \"seconds\"\n  }\n})","note":"Die in MongoDB 5.0 eingeführten Time-Series Collections speichern Daten automatisch in einem komprimierten, spaltenähnlichen (gebucketten) Format nach Zeit- und Metafeld; sie verringern Platten- und Indexnutzung und beschleunigen Bereichsabfragen. timeField ist Pflicht; metaField (Quelle/Tag) und granularity bestimmen die Performance.","since":"MongoDB 5.0","xnote":"Time-Series Collections kamen mit MongoDB 5.0.","danger":"safe"},"redis":{"command":"# Requires the RedisTimeSeries module (Redis Stack).\n# Create a series with a retention period:\nTS.CREATE sensor:1 RETENTION 86400000 LABELS type temperature\n# Add a sample (with an automatic timestamp):\nTS.ADD sensor:1 * 23.5","note":"Core Redis hat keinen Zeitreihentyp; das Modul RedisTimeSeries (Redis Stack) bietet schnelle Erfassung von Messwerten mit TS.CREATE/TS.ADD, automatische Aufbewahrung (RETENTION), Downsampling (TS.CREATERULE) und labelbasierte Bereichsabfragen mit TS.RANGE/TS.MRANGE. Ohne das Modul wird es mit einem Sorted Set nachgebildet.","danger":"safe"},"elasticsearch":{"command":"# Data stream: automatically rolling indexes for time series\nPUT _index_template/measurements-tpl\n{ \"index_patterns\": [\"measurements*\"], \"data_stream\": {},\n  \"template\": {\n    \"settings\": { \"index.mode\": \"time_series\", \"index.routing_path\": [\"sensor_id\"] },\n    \"mappings\": { \"properties\": {\n      \"@timestamp\": { \"type\": \"date\" },\n      \"sensor_id\": { \"type\": \"keyword\", \"time_series_dimension\": true },\n      \"temperature\": { \"type\": \"double\", \"time_series_metric\": \"gauge\" } } } } }\nPUT _data_stream/measurements","note":"Elasticsearch verwaltet Zeitreihendaten mit Data Streams; neue Backing Indices entstehen im Zeitverlauf automatisch (Rollover), und ILM verschiebt alte Daten durch die Tiers hot/warm/cold und löscht sie. TSDS (index.mode: time_series) bietet Speicheroptimierungen speziell für Metrikdaten; mindestens ein Feld time_series_dimension, ein darauf zeigender index.routing_path und ein Feld @timestamp sind Pflicht (sonst wird das Template abgelehnt).","danger":"safe"}}},{"id":"vector-search","title":"Vektorsuche (KI/Embeddings)","aliases":["vector search","vector search","embedding","ai","semantic search","knn"],"engines":{"oracle":{"command":"-- Oracle 23ai native VECTOR type:\nCREATE TABLE docs (id NUMBER, content CLOB, embedding VECTOR);\n-- Similarity search (cosine distance):\nSELECT id FROM docs\nORDER BY VECTOR_DISTANCE(embedding, :query_vector, COSINE)\nFETCH FIRST 5 ROWS ONLY;","note":"Oracle Database 23ai bietet einen nativen Datentyp VECTOR und die Funktion AI Vector Search. Die Funktion VECTOR_DISTANCE berechnet die Ähnlichkeit mit den Metriken COSINE/EUCLIDEAN/DOT; für approximative Suche werden HNSW- und IVF-Vektorindizes unterstützt. Embeddings werden in der Datenbank oder mit externen Modellen erzeugt.","since":"Oracle 23ai","xnote":"Der native Typ VECTOR und AI Vector Search kamen mit Oracle 23ai.","danger":"safe"},"postgres":{"command":"-- pgvector extension:\nCREATE EXTENSION IF NOT EXISTS vector;\nCREATE TABLE docs (id int, embedding vector(1536));\nCREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);\n-- 5 nearest neighbors (<-> Euclidean, <=> cosine):\nSELECT id FROM docs ORDER BY embedding <=> :query_vec LIMIT 5;","note":"Die Extension pgvector ergänzt PostgreSQL um einen vector-Typ und Ähnlichkeitsoperatoren: <-> (L2/euklidisch), <=> (Kosinus), <#> (inneres Produkt). HNSW- oder IVFFlat-Indizes beschleunigen die approximative Nearest-Neighbor-Suche (ANN). Weit verbreitet in RAG- und semantischen Suchanwendungen.","xnote":"Core-PostgreSQL hat keinen Vektortyp; die Extension pgvector ist erforderlich.","danger":"safe"},"mysql":{"command":"-- MySQL 9.0+ native VECTOR type:\nCREATE TABLE docs (id INT, embedding VECTOR(1536));\n-- Cosine distance (DISTANCE is HeatWave-only):\nSELECT id, DISTANCE(embedding, :query_vec, 'COSINE') AS d\nFROM docs ORDER BY d LIMIT 5;","note":"MySQL 9.0 hat den nativen Datentyp VECTOR sowie die Funktionen STRING_TO_VECTOR/VECTOR_TO_STRING und VECTOR_DIM eingeführt (auch in Community). Die Funktion DISTANCE() und approximative Vektorsuche im großen Maßstab gibt es nur in MySQL HeatWave in Oracle Cloud; in Community/Enterprise berechnest du Distanzen in der Anwendung oder in einem externen Vektorspeicher. Ältere On-Premises-Versionen erfordern einen externen Vektorspeicher.","since":"MySQL 9.0","xnote":"Der native Datentyp VECTOR kam mit MySQL 9.0.","danger":"safe"},"mssql":{"command":"-- SQL Server 2025 native vector type:\nCREATE TABLE docs (id INT, embedding VECTOR(1536));\n-- Nearest neighbors by cosine distance:\nSELECT TOP 5 id,\n  VECTOR_DISTANCE('cosine', embedding, @query_vec) AS distance\nFROM dbo.docs ORDER BY distance;","note":"SQL Server 2025 (und Azure SQL) bieten einen nativen vector-Datentyp und die Funktion VECTOR_DISTANCE (cosine/euclidean/dot); DiskANN-basierte Vektorindizes für approximative Nearest-Neighbor-Suche sind in Entwicklung. Embeddings werden mit externen Modellen wie Azure OpenAI erzeugt und gespeichert.","since":"SQL Server 2025","xnote":"Der native vector-Datentyp kam mit SQL Server 2025.","danger":"safe"},"mongodb":{"command":"// Atlas Vector Search ($vectorSearch aggregation stage):\ndb.docs.aggregate([\n  { $vectorSearch: {\n      index: \"vector_idx\",\n      path: \"embedding\",\n      queryVector: [0.12, 0.87, /* ... */],\n      numCandidates: 100,\n      limit: 5\n  } }\n])","note":"MongoDB Atlas Vector Search fragt Embeddings mit der Aggregation-Stage $vectorSearch ab; intern läuft eine HNSW-basierte approximative Nearest-Neighbor-Suche. Der Vektorindex wird als Atlas-Search-Index definiert. Die Funktion ist spezifisch für den verwalteten Dienst Atlas (in der On-Premises-Community-Edition nicht verfügbar).","danger":"safe"},"redis":{"command":"# Requires the RediSearch (Redis Stack) VECTOR field type.\n# Define an HNSW vector index:\nFT.CREATE docs_idx ON HASH PREFIX 1 docs: SCHEMA\n  embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE\n# KNN search (with a query vector):\nFT.SEARCH docs_idx \"*=>[KNN 5 @embedding $vec]\" PARAMS 2 vec \"...\"","note":"Redis bietet Vektorsuche über das Modul RediSearch (Redis Stack); der Feldtyp VECTOR unterstützt FLAT- (exakt) oder HNSW-Indizes (approximativ) und die Metriken COSINE/L2/IP. Nächste Nachbarn werden mit der KNN-Syntax in FT.SEARCH abgerufen. Genutzt für semantische Suche mit geringer Latenz und RAG.","danger":"safe"},"elasticsearch":{"command":"# dense_vector field and kNN search:\nPUT docs\n{ \"mappings\": { \"properties\": { \"embedding\":\n  { \"type\": \"dense_vector\", \"dims\": 1536, \"similarity\": \"cosine\" } } } }\nGET docs/_search\n{ \"knn\": { \"field\": \"embedding\", \"query_vector\": [0.12, 0.87],\n  \"k\": 5, \"num_candidates\": 100 } }","note":"Elasticsearch unterstützt approximative Nearest-Neighbor-Abfragen (HNSW) mit dem Feldtyp dense_vector und eingebauter knn-Suche; die Ähnlichkeit kann cosine/dot_product/l2_norm sein. Die Relevanz lässt sich verbessern, indem Vektorsuche mit einer klassischen Textabfrage kombiniert wird (Hybrid Search / RRF).","since":"Elasticsearch 8.0","xnote":"Die eingebaute kNN-Suche mit dense_vector kam mit Elasticsearch 8.x.","danger":"safe"}}},{"id":"cdc-change-tracking","title":"Änderungsverfolgung (CDC / Change Tracking)","aliases":["cdc","change data capture","change tracking","change tracking","replication feed","binlog"],"engines":{"oracle":{"command":"-- Read changes from redo logs with LogMiner:\n-- 1) Add the archived/redo log file to analyze:\nEXECUTE DBMS_LOGMNR.ADD_LOGFILE(\n  LOGFILENAME => '/u01/arch/1_1234_1122334455.arc',\n  OPTIONS => DBMS_LOGMNR.NEW);\n-- 2) Start the session:\nEXECUTE DBMS_LOGMNR.START_LOGMNR(\n  OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);\nSELECT sql_redo, sql_undo FROM V$LOGMNR_CONTENTS\nWHERE seg_name = 'ORDERS';\n-- 3) When finished: EXECUTE DBMS_LOGMNR.END_LOGMNR;\n-- For enterprise CDC/replication, Oracle GoldenGate is used.","note":"Oracle verfolgt Änderungen auf Basis des Redo-Logs. LogMiner übersetzt Redo-/Archivlogs in SQL, um vergangene DML zu untersuchen; die zu analysierenden Logdateien werden mit DBMS_LOGMNR.ADD_LOGFILE hinzugefügt. Für aussagekräftige Ergebnisse muss mindestens minimales Supplemental Logging aktiviert sein (ALTER DATABASE ADD SUPPLEMENTAL LOG DATA). Für kontinuierliche CDC mit geringer Latenz und heterogene Replikation ist Oracle GoldenGate (separates Produkt) der Standard; die alten Funktionen Streams/Change Data Capture sind veraltet.","danger":"caution"},"postgres":{"command":"-- Create a logical replication (logical decoding) slot:\nSELECT * FROM pg_create_logical_replication_slot(\n  'cdc_slot', 'wal2json');\n-- Consume changes as JSON:\nSELECT data FROM pg_logical_slot_get_changes('cdc_slot', NULL, NULL);","note":"Logical Decoding in PostgreSQL wandelt WAL-Einträge über ein Output-Plugin (wal2json, pgoutput, test_decoding) in einen lesbaren Änderungsstrom um; der Replication Slot sorgt dafür, dass keine Änderungen verloren gehen. wal_level muss logical sein. Debezium nutzt diesen Mechanismus für Enterprise-CDC.","danger":"caution"},"mysql":{"command":"-- The row-based binlog is the CDC source (binlog_format=ROW):\nSHOW BINARY LOGS;\nSHOW BINLOG EVENTS IN 'binlog.000042';\n-- Usually Debezium/Maxwell reads the binlog and turns it into a stream.","note":"In MySQL ist das Binary Log die CDC-Quelle. Mit binlog_format=ROW wird jede Zeilenänderung aufgezeichnet. Tools wie Debezium oder Maxwell lesen das Binlog wie eine Replica und erzeugen einen Ereignisstrom nach Kafka. Aktiviertes GTID erleichtert die zuverlässige Positionsverfolgung.","danger":"caution"},"mssql":{"command":"-- Enable Change Data Capture on the database and the table:\nEXEC sys.sp_cdc_enable_db;\nEXEC sys.sp_cdc_enable_table\n  @source_schema = N'dbo', @source_name = N'orders',\n  @role_name = NULL;\n-- For lightweight tracking: ALTER DATABASE ... SET CHANGE_TRACKING = ON;","note":"SQL Server bietet zwei Mechanismen: Change Data Capture (CDC) schreibt alle DML (mit alten/neuen Werten) aus dem Transaktionslog in Änderungstabellen und erfordert SQL Server Agent. Change Tracking verfolgt nur schlank, welche Zeilen sich geändert haben (Nettoänderungen), und hält keine vollständige Datenhistorie.","since":"SQL Server 2008","xnote":"CDC und Change Tracking kamen mit SQL Server 2008.","danger":"caution"},"mongodb":{"command":"// Watch changes in real time with Change Streams:\nconst stream = db.orders.watch([\n  { $match: { operationType: { $in: [\"insert\", \"update\"] } } }\n]);\nwhile (stream.hasNext()) { printjson(stream.next()); }","note":"MongoDB Change Streams, aufgebaut auf dem Oplog, liefern insert-, update- und delete-Ereignisse auf Collection-/Datenbank-/Clusterebene als Echtzeitstrom. Erfordert ein Replica Set oder einen Sharded Cluster. Mit einem resumeToken kann ein Stream nach einer Unterbrechung dort fortgesetzt werden, wo er aufgehört hat; er ist die Grundlage von CDC-Integrationen.","since":"MongoDB 3.6","xnote":"Change Streams kamen mit MongoDB 3.6.","danger":"safe"},"redis":{"command":"# Listen for key changes with keyspace notifications:\nCONFIG SET notify-keyspace-events KEA\n# Subscribe to changed keys via Pub/Sub:\nPSUBSCRIBE __keyevent@0__:set\nPSUBSCRIBE __keyevent@0__:expired","note":"Redis Keyspace Notifications veröffentlichen Ereignisse zu Schlüsseln (SET, DEL, EXPIRE usw.) über Pub/Sub-Kanäle; aktiviert werden sie mit der Einstellung notify-keyspace-events (K=keyspace, E=keyevent, A=alle Ereignisse). Für einen zuverlässigen, dauerhaften Änderungsstrom werden Redis Streams (XADD/XREAD) bevorzugt.","danger":"safe"},"elasticsearch":{"command":"-- Not applicable: there is no built-in CDC/change stream.\n# Closest equivalent: incremental query on a timestamp field:\nGET orders/_search\n{ \"query\": { \"range\": { \"updated_ts\": { \"gt\": \"2026-07-05T00:00:00\" } } },\n  \"sort\": [ { \"updated_ts\": \"asc\" } ] }","note":"Elasticsearch bietet keinen Change-Data-Capture-Stream (CDC); es meldet nicht nativ, wann sich ein Dokument geändert hat. Üblich ist, jedem Dokument ein Feld updated_ts hinzuzufügen und inkrementelle Abfragen nach Zeitstempel auszuführen (range + sort). Meist wird die CDC der Quelldatenbank in Elasticsearch eingespeist.","danger":"safe"}}}]},"rosetta/sql-syntax.json":{"category":"SQL-Syntax & Schlüsselwörter","categoryId":"sql-syntax","order":19,"concepts":[{"id":"with-cte","title":"WITH (Common Table Expression / CTE)","aliases":["with","cte","common table expression","temporary query","temporary result set"],"engines":{"oracle":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"WITH definiert innerhalb einer Abfrage eine benannte temporäre Ergebnismenge (CTE); statt eine Unterabfrage mehrfach zu schreiben, definierst du sie einmal und verweist per Name darauf. Komplexe Abfragen werden so in lesbare Teile zerlegt. Unterstützt seit Oracle 9i.","since":"Oracle 9i","danger":"safe"},"postgres":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"Eine CTE zerlegt komplexe Abfragen in benannte Blöcke. Vor PostgreSQL 12 war eine CTE eine Optimierungsbarriere (immer materialisiert); ab 12 kann sie inline aufgelöst werden, das alte Verhalten lässt sich mit WITH ... AS MATERIALIZED erzwingen.","danger":"safe"},"mysql":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"Eine CTE definiert innerhalb einer Abfrage eine temporäre benannte Ergebnismenge und verbessert die Lesbarkeit. Unterstützt ab MySQL 8.0 (und MariaDB 10.2); ältere Versionen erfordern eine Derived Table (Unterabfrage in FROM).","since":"MySQL 8.0","xnote":"Vor MySQL 8.0 gab es keine CTEs; dasselbe wurde mit einer Derived Table (Unterabfrage in FROM) gelöst.","danger":"safe"},"mssql":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"Eine CTE ist eine benannte temporäre Ergebnismenge. In SQL Server muss die unmittelbar vor einer CTE stehende Anweisung mit einem Semikolon enden, daher wird WITH oft als ';WITH' geschrieben. Sie gilt für die Dauer einer einzelnen Anweisung (SELECT/INSERT/UPDATE/DELETE).","danger":"safe"},"mongodb":{"command":"// Closest equivalent: aggregation pipeline stages\ndb.customer.aggregate([\n  { $group: { _id: \"$city\", n: { $sum: 1 } } },\n  { $match: { n: { $gt: 10 } } }\n])","note":"MongoDB hat kein SQL/keine CTE; die Stages der Aggregation Pipeline übernehmen diese Aufgabe. Jede Stage nimmt die Ausgabe der vorherigen als Eingabe, was eine schrittweise Verarbeitung ähnlich CTE-Ketten ergibt. Für Zwischenergebnisse lassen sich auch $facet oder $lookup nutzen.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no concept of SQL or CTEs.","note":"Redis ist ein Key-Value-Datenspeicher ohne Abfragesprache. Zwischenergebnisse kombinierst du clientseitig, oder du nutzt ein Lua-Skript (EVAL) für serverseitige Logik.","danger":"safe"},"elasticsearch":{"command":"# CTEs are not supported in Elasticsearch SQL.\nPOST _sql\n{ \"query\": \"SELECT city, COUNT(*) n FROM customer GROUP BY city HAVING COUNT(*) > 10\" }","note":"Elasticsearch SQL (die _sql API) unterstützt keine WITH-/CTE-Syntax; Gruppierung und Filterung werden in einem einzigen SELECT geschrieben. Nativ erfolgt dasselbe mit einer 'terms'-Aggregation und einer Pipeline-Aggregation 'bucket_selector'.","danger":"safe"}}},{"id":"recursive-cte","title":"Rekursive CTE (WITH RECURSIVE)","aliases":["recursive","recursion","with recursive","connect by","hierarchy","tree query"],"engines":{"oracle":{"command":"-- Hierarchy with CONNECT BY:\nSELECT id, name, manager_id, LEVEL\nFROM employee\nSTART WITH manager_id IS NULL\nCONNECT BY PRIOR id = manager_id;\n\n-- Alternative (11gR2+): standard recursive WITH (the RECURSIVE keyword is not written)\nWITH tree (id, name, manager_id) AS (\n  SELECT id, name, manager_id FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree;","note":"Dient zum Durchlaufen hierarchischer Daten (Baumstrukturen): Organigramme, Stücklisten und Ähnliches. Klassisch nutzt Oracle CONNECT BY / PRIOR und die Pseudospalte LEVEL; START WITH legt die Wurzelzeile fest. Seit 11gR2 wird auch die standardmäßige rekursive CTE unterstützt.","since":"Oracle 11gR2","xnote":"CONNECT BY ist die klassische Methode; das standardmäßige rekursive WITH (ohne das Schlüsselwort RECURSIVE) kam mit Oracle 11gR2.","danger":"safe"},"postgres":{"command":"WITH RECURSIVE tree (id, name, manager_id, level) AS (\n  SELECT id, name, manager_id, 1\n  FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id, a.level + 1\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree ORDER BY level;","note":"WITH RECURSIVE verbindet eine Anker-Abfrage und einen rekursiven Teil mit UNION ALL; der rekursive Teil läuft wiederholt, bis das Ergebnis leer ist. Ideal für Hierarchien und Graphtraversierung. Schütze dich mit UNION (statt ALL) oder Zyklenschutz vor Endlosschleifen.","since":"PostgreSQL 8.4","danger":"safe"},"mysql":{"command":"WITH RECURSIVE tree (id, name, manager_id, level) AS (\n  SELECT id, name, manager_id, 1\n  FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id, a.level + 1\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree ORDER BY level;","note":"MySQL 8.0 (und MariaDB 10.2+) unterstützen standardkonforme WITH RECURSIVE-Syntax. Die Rekursionstiefe wird durch die Systemvariable cte_max_recursion_depth begrenzt (Standard 1000). Ältere Versionen erforderten eine Schleife in einer Stored Procedure.","since":"MySQL 8.0","xnote":"Vor MySQL 8.0 (MariaDB 10.2) gab es keine rekursiven Abfragen; es wurde eine Schleife in einer Stored Procedure geschrieben.","danger":"safe"},"mssql":{"command":"WITH tree (id, name, manager_id, level) AS (\n  SELECT id, name, manager_id, 1\n  FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id, a.level + 1\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree ORDER BY level\nOPTION (MAXRECURSION 100);","note":"In SQL Server wird das Schlüsselwort RECURSIVE bei einer rekursiven CTE nicht geschrieben; eine CTE mit UNION ALL, die auf sich selbst verweist, gilt automatisch als rekursiv. Das Standard-Rekursionslimit ist 100; ändere es mit OPTION (MAXRECURSION n) (0 = unbegrenzt, mit Vorsicht verwenden).","since":"SQL Server 2005","danger":"safe"},"mongodb":{"command":"// Closest equivalent: the $graphLookup stage\ndb.employee.aggregate([\n  { $match: { manager_id: null } },\n  { $graphLookup: {\n      from: \"employee\",\n      startWith: \"$id\",\n      connectFromField: \"id\",\n      connectToField: \"manager_id\",\n      as: \"subordinates\"\n  } }\n])","note":"MongoDB hat keine rekursive CTE; für hierarchische Traversierung wird die Aggregation-Stage $graphLookup genutzt. connectFromField/connectToField legen die Verknüpfungsfelder fest, maxDepth das Tiefenlimit. Baum- und Graphbeziehungen werden so in einer einzigen Abfrage aufgelöst.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no recursive queries.","note":"Redis bietet keine Abfragesprache. Hierarchie-/Graphtraversierung erfolgt im Anwendungscode oder serverseitig mit einem Lua-Skript; in Core Redis wird der Baumdurchlauf clientseitig geschrieben. (Der Support für das früher genutzte Modul RedisGraph endete 2023.)","danger":"safe"},"elasticsearch":{"command":"# Not applicable: recursive queries are not supported.\n# Approach: model with parent-child (join field) or nested,\n# deep hierarchies are traversed on the application side.","note":"Elasticsearch bietet keine rekursive Abfrage (keine Entsprechung zu WITH RECURSIVE / CONNECT BY). Hierarchische Beziehungen werden mit dem Feldtyp 'join' (parent/child) modelliert oder indem der Baumpfad (Materialized Path) in einem Feld gespeichert wird; mehrstufige Traversierung erfolgt meist in der Anwendung.","danger":"safe"}}},{"id":"joins","title":"JOIN-Typen (INNER/LEFT/RIGHT/FULL/CROSS)","aliases":["join","inner join","left join","right join","full join","cross join","combine tables","table join"],"engines":{"oracle":{"command":"-- INNER: matching rows\nSELECT m.name, s.amount\nFROM customer m JOIN orders s ON m.id = s.customer_id;\n-- LEFT: customers without orders are included too\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n-- CROSS: Cartesian product\nSELECT r.name, u.name FROM color r CROSS JOIN product u;","note":"Ein JOIN verbindet zwei Tabellen anhand der angegebenen Bedingung. INNER liefert nur Treffer; LEFT/RIGHT liefern alle Zeilen einer Seite (NULL ohne Treffer); FULL liefert Zeilen beider Seiten; CROSS liefert alle Kombinationen. Neben Standard-ANSI-JOINs unterstützt Oracle auch die alte Outer-Join-Syntax (+).","danger":"safe"},"postgres":{"command":"SELECT m.name, s.amount\nFROM customer m INNER JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m FULL OUTER JOIN orders s ON m.id = s.customer_id;","note":"PostgreSQL unterstützt alle Standard-JOIN-Typen vollständig (INNER, LEFT/RIGHT/FULL OUTER, CROSS). USING(my_column) ist eine Kurzform für gleichnamige Spalten, NATURAL JOIN verknüpft automatisch über alle gleichnamigen Spalten (mit Vorsicht verwenden).","danger":"safe"},"mysql":{"command":"SELECT m.name, s.amount\nFROM customer m INNER JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n\n-- MySQL has no FULL OUTER JOIN:\nSELECT m.name, s.amount FROM customer m LEFT JOIN orders s ON m.id = s.customer_id\nUNION\nSELECT m.name, s.amount FROM customer m RIGHT JOIN orders s ON m.id = s.customer_id;","note":"MySQL/MariaDB unterstützen INNER, LEFT, RIGHT und CROSS JOIN, bieten FULL OUTER JOIN aber nicht direkt; es wird nachgebildet, indem LEFT- und RIGHT-Ergebnisse mit UNION kombiniert werden. Der alte Komma-Join erzeugt ein kartesisches Produkt und muss mit ON/WHERE eingeschränkt werden.","danger":"safe"},"mssql":{"command":"SELECT m.name, s.amount\nFROM customer m INNER JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m FULL OUTER JOIN orders s ON m.id = s.customer_id;","note":"SQL Server unterstützt alle Standard-JOIN-Typen. Die alten Outer-Join-Operatoren *= / =* wurden entfernt; die ANSI-JOIN-Syntax ist zu verwenden. APPLY (CROSS/OUTER APPLY) ist eine zusätzliche Join-Form für zeilenweise korrelierte Unterabfragen.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $lookup (left outer join)\ndb.customer.aggregate([\n  { $lookup: {\n      from: \"orders\",\n      localField: \"id\",\n      foreignField: \"customer_id\",\n      as: \"orders\"\n  } }\n])","note":"MongoDB hat keinen klassischen JOIN; die Aggregation-Stage $lookup holt passende Dokumente aus einer anderen Collection als Array (ähnlich einem Left Outer Join). Für ein INNER-ähnliches Ergebnis filterst du leere Arrays anschließend mit $match heraus und expandierst das Array mit $unwind in Zeilen.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no tables or JOINs.","note":"Redis bietet keine relationalen Joins; Daten werden meist denormalisiert, oder Beziehungen werden clientseitig durch Lesen mehrerer Schlüssel aufgebaut. Für eingeschränkte join-ähnliche Abfragen lässt sich RediSearch (FT.AGGREGATE) nutzen; der Support für das Modul RedisGraph endete 2023.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: there is no general JOIN in a distributed design.\n# Limited options: nested, parent/child (join field),\n# terms lookup. Data is usually denormalized at indexing time.","note":"Aufgrund seiner verteilten Architektur bietet Elasticsearch keine beliebigen tabellenübergreifenden JOINs. Für eingeschränkte Beziehungen gibt es den Feldtyp 'nested' innerhalb eines Index und den Feldtyp 'join' (parent/child) auf demselben Shard. Die häufigste Lösung ist, die Daten beim Indexieren zu denormalisieren.","danger":"safe"}}},{"id":"window-functions","title":"Fensterfunktionen (OVER / PARTITION BY)","aliases":["window","window function","over","partition by","row_number","rank","lag","lead","analytic function"],"engines":{"oracle":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_no,\n  RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rank_no,\n  LAG(salary)    OVER (PARTITION BY department ORDER BY salary)      AS prev_salary\nFROM employee;","note":"Fensterfunktionen rechnen für jede Zeile über ein 'Fenster', ohne Zeilen zu gruppieren (ohne die Zeilenzahl zu verringern). PARTITION BY teilt das Fenster, ORDER BY sortiert es. Oracle war Vorreiter bei diesen analytischen Funktionen und bietet einen umfangreichen Satz mit ROW_NUMBER, RANK, DENSE_RANK, LAG/LEAD und SUM() OVER.","since":"Oracle 8i","danger":"safe"},"postgres":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER w AS row_no,\n  RANK()       OVER w AS rank_no,\n  LEAD(salary)   OVER w AS next_salary\nFROM employee\nWINDOW w AS (PARTITION BY department ORDER BY salary DESC);","note":"PostgreSQL unterstützt alle Standard-Fensterfunktionen und erlaubt, eine wiederholte Fensterdefinition mit WINDOW ... AS zu benennen. Mit Frame-Definitionen ROWS/RANGE/GROUPS berechnest du laufende Summen, gleitende Durchschnitte und Ähnliches.","since":"PostgreSQL 8.4","danger":"safe"},"mysql":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_no,\n  DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_no,\n  LAG(salary)    OVER (PARTITION BY department ORDER BY salary)      AS prev_salary\nFROM employee;","note":"MySQL 8.0 (und MariaDB 10.2+) unterstützen Fensterfunktionen; davor wurden sie mit Benutzervariablen oder Unterabfragen nachgebildet. ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG/LEAD, FIRST_VALUE/LAST_VALUE und die OVER-Form der Aggregatfunktionen sind verfügbar.","since":"MySQL 8.0","xnote":"Vor MySQL 8.0 gab es keine Fensterfunktionen; sie wurden mit Variablen/Self-Joins nachgebildet.","danger":"safe"},"mssql":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_no,\n  RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rank_no,\n  SUM(salary)    OVER (PARTITION BY department) AS dept_total\nFROM employee;","note":"SQL Server unterstützt Ranking-Fensterfunktionen seit 2005 und Aggregate mit Frames sowie Funktionen wie LAG/LEAD seit 2012. ROW_NUMBER wird häufig für Paging (mit OFFSET/FETCH) und zum Ermitteln der Top-N-Zeilen pro Gruppe genutzt.","since":"SQL Server 2005","xnote":"Ranking-Fensterfunktionen kamen 2005, Frames und LAG/LEAD 2012.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $setWindowFields (MongoDB 5.0+)\ndb.employee.aggregate([\n  { $setWindowFields: {\n      partitionBy: \"$department\",\n      sortBy: { salary: -1 },\n      output: { row_no: { $rank: {} },\n                prev_salary: { $shift: { output: \"$salary\", by: -1 } } }\n  } }\n])","note":"Die Stage $setWindowFields in MongoDB 5.0+ entspricht SQL-Fensterfunktionen: Das Fenster wird mit partitionBy und sortBy definiert und liefert Ausgaben wie $rank/$denseRank/$documentNumber, $shift (LAG/LEAD) und laufende Summen. In älteren Versionen wurde dies mit $group + Code gelöst.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no window functions.","note":"Redis bietet keine analytischen Fensterfunktionen. Für sortierte Ergebnisse und Ranglisten nutze die Struktur Sorted Set (ZSET): Scores mit ZADD vergeben, den Rang mit ZRANK/ZREVRANK und Bereiche mit ZRANGE lesen. Komplexe Analytik erfolgt in der Anwendung.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: top_hits and pipeline aggregations\nGET employee/_search\n{\n  \"size\": 0,\n  \"aggs\": { \"dept\": { \"terms\": { \"field\": \"department\" },\n    \"aggs\": { \"top_paid\": { \"top_hits\": {\n      \"sort\": [ { \"salary\": \"desc\" } ], \"size\": 3 } } } } }\n}","note":"Elasticsearch hat keine direkten Fensterfunktionen. Für die Top-N-Datensätze pro Gruppe nutze eine Sub-Aggregation 'top_hits'; für gleitende/kumulative Berechnungen Pipeline-Aggregationen wie 'derivative', 'cumulative_sum' und 'moving_fn'. Sortierung und Partitionierung erfolgen mit einer terms-Aggregation.","danger":"safe"}}},{"id":"group-rollup","title":"GROUP BY, HAVING, ROLLUP/CUBE/GROUPING SETS","aliases":["group by","having","rollup","cube","grouping sets","grouping","subtotal","subtotals"],"engines":{"oracle":{"command":"-- Basic grouping + HAVING\nSELECT city, COUNT(*) AS n\nFROM customer\nGROUP BY city\nHAVING COUNT(*) > 5;\n\n-- Subtotals and grand total with ROLLUP\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY ROLLUP(city, district);","note":"GROUP BY gruppiert Zeilen nach den angegebenen Spalten und wendet Aggregatfunktionen an; HAVING filtert das gruppierte Ergebnis (WHERE filtert Zeilen vor der Gruppierung). ROLLUP erzeugt hierarchische Zwischensummen + Gesamtsumme, CUBE alle Kombinationen und GROUPING SETS die gewählten Gruppierungen. Oracle unterstützt alle drei.","danger":"safe"},"postgres":{"command":"SELECT city, district, SUM(balance)\nFROM customer\nGROUP BY GROUPING SETS ((city, district), (city), ());\n\n-- CUBE produces all subset combinations\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY CUBE(city, district);","note":"PostgreSQL 9.5+ unterstützt ROLLUP, CUBE und GROUPING SETS standardkonform. Die Funktion GROUPING() zeigt, ob eine Spalte in einer Summenzeile steht (NULL). HAVING filtert nach der Aggregation, WHERE davor.","since":"PostgreSQL 9.5","xnote":"ROLLUP, CUBE und GROUPING SETS kamen mit PostgreSQL 9.5.","danger":"safe"},"mysql":{"command":"SELECT city, COUNT(*) AS n\nFROM customer\nGROUP BY city\nHAVING n > 5;\n\n-- ROLLUP in MySQL:\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY city, district WITH ROLLUP;","note":"MySQL/MariaDB erzeugen Zwischen-/Gesamtsummen mit der Syntax GROUP BY ... WITH ROLLUP; CUBE und GROUPING SETS werden nicht unterstützt. SELECT-Aliase lassen sich in HAVING verwenden (in Oracle/SQL Server funktioniert das meist nicht). In ROLLUP-Zeilen erscheint die gruppierte Spalte als NULL.","danger":"safe"},"mssql":{"command":"SELECT city, COUNT(*) AS n\nFROM customer\nGROUP BY city\nHAVING COUNT(*) > 5;\n\n-- ROLLUP / CUBE / GROUPING SETS\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY ROLLUP(city, district);","note":"Neben GROUP BY unterstützt SQL Server ROLLUP(), CUBE() und GROUPING SETS (2008+) mit moderner Syntax; die alte Form WITH ROLLUP/WITH CUBE ist veraltet. GROUPING() und GROUPING_ID() helfen, Summenzeilen zu unterscheiden.","since":"SQL Server 2008","xnote":"Die moderne GROUPING SETS-Syntax kam mit SQL Server 2008.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $group + $sum\ndb.customer.aggregate([\n  { $group: { _id: \"$city\", n: { $sum: 1 } } },\n  { $match: { n: { $gt: 5 } } }\n])\n// $facet is used for ROLLUP-like multi-level totals","note":"In MongoDB entspricht $group dem SQL-GROUP BY; _id legt den Gruppierungsschlüssel fest, Akkumulatoren ($sum, $avg, $max) definieren die Aggregate. Gefiltert wird nach der Gruppierung mit $match (Entsprechung zu HAVING). Mehrstufige Summen wie ROLLUP/CUBE erhältst du, indem du mit $facet mehrere Pipelines ausführst.","danger":"safe"},"redis":{"command":"# Not applicable: core Redis has no GROUP BY.\n# Aggregation with RediSearch:\nFT.AGGREGATE customer_idx \"*\" GROUPBY 1 @city REDUCE COUNT 0 AS n","note":"Core Redis bietet keine Gruppierung; der Befehl FT.AGGREGATE des Moduls RediSearch führt SQL-ähnliche Aggregation mit GROUPBY und REDUCE (COUNT, SUM, AVG) aus. Der Schritt FILTER filtert das gruppierte Ergebnis wie HAVING. Ohne das Modul erfolgt die Aggregation in der Anwendung.","danger":"safe"},"elasticsearch":{"command":"GET customer/_search\n{\n  \"size\": 0,\n  \"aggs\": {\n    \"by_city\": { \"terms\": { \"field\": \"city\" },\n      \"aggs\": { \"total\": { \"sum\": { \"field\": \"balance\" } } } }\n  }\n}","note":"In Elasticsearch erfolgt die Gruppierung mit der Bucket-Aggregation 'terms', Aggregate mit Metrik-Aggregationen (sum, avg, cardinality). Für HAVING-ähnliches Filtern nutze die Pipeline-Aggregation 'bucket_selector'. Mehrstufige Zwischensummen entstehen mit verschachtelten Aggregationen.","danger":"safe"}}},{"id":"case-expr","title":"CASE-Ausdruck (bedingter Wert)","aliases":["case","conditional","when","then","decode","if then","conditional value"],"engines":{"oracle":{"command":"-- Searched CASE\nSELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;\n\n-- Oracle-specific shortcut: DECODE (equality comparison)\nSELECT name, DECODE(status, 'A', 'Active', 'P', 'Passive', 'Unknown') FROM employee;","note":"CASE bildet einen Ausdruck je nach Bedingung auf verschiedene Werte ab; es gibt eine einfache Form (CASE my_column WHEN value) und eine Suchform (CASE WHEN condition). Oracles DECODE erfüllt eine ähnliche Aufgabe, vergleicht aber nur auf Gleichheit und behandelt NULLs als gleich; für Portabilität CASE bevorzugen.","danger":"safe"},"postgres":{"command":"SELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;","note":"PostgreSQL unterstützt den Standard-CASE-Ausdruck vollständig, überall dort, wo ein Wert entsteht (SELECT, WHERE, ORDER BY, GROUP BY). Ein DECODE im Oracle-Stil gibt es nicht; nutze stattdessen CASE oder für kurze Bedingungen COALESCE/NULLIF.","danger":"safe"},"mysql":{"command":"SELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;\n\n-- IF() for a short condition:\nSELECT name, IF(active = 1, 'Yes', 'No') FROM employee;","note":"MySQL/MariaDB unterstützen den Standard-CASE-Ausdruck; für eine schnelle zweiwertige Bedingung gibt es zusätzlich die Funktion IF(condition, if_true, if_false) (ähnlich IIF in SQL Server). CASE ist in jedem Ausdruckskontext verwendbar.","danger":"safe"},"mssql":{"command":"SELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;\n\n-- Shortcut: IIF() (2012+)\nSELECT name, IIF(active = 1, 'Yes', 'No') FROM employee;","note":"SQL Server unterstützt Standard-CASE; seit 2012 gibt es die Kurzform IIF() für zweiwertige Bedingungen und CHOOSE() für mehrere Treffer. IIF wird intern in CASE übersetzt. CASE lässt sich auch in ORDER BY und berechneten Spalten verwenden.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $switch or $cond\ndb.employee.aggregate([\n  { $project: { name: 1, tier: {\n      $switch: { branches: [\n        { case: { $gte: [\"$salary\", 20000] }, then: \"high\" },\n        { case: { $gte: [\"$salary\", 10000] }, then: \"medium\" } ],\n        default: \"low\" } } } }\n])","note":"In der MongoDB-Aggregation entsprechen die Operatoren $switch (mehrere Zweige) und $cond (if-then-else mit drei Argumenten) dem CASE. Sie werden in $project oder $addFields genutzt, um bedingte abgeleitete Felder zu erzeugen.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no in-query conditional expression.","note":"Redis hat keinen Abfrageausdruck wie CASE. Bedingte Logik wird im Anwendungscode oder in einem serverseitigen Lua-Skript (EVAL) geschrieben. Eingeschränkte Ausdrucksauswertung ist mit APPLY in RediSearch FT.AGGREGATE möglich.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: a condition inside a runtime field or script\nGET employee/_search\n{\n  \"runtime_mappings\": { \"tier\": { \"type\": \"keyword\",\n    \"script\": \"if (doc['salary'].value >= 20000) emit('high'); else if (doc['salary'].value >= 10000) emit('medium'); else emit('low');\" } },\n  \"fields\": [\"tier\"]\n}","note":"In Elasticsearch wird CASE-Logik innerhalb einer Abfrage als Painless-Skript geschrieben (Runtime Field, script_fields oder Scripted Metric). Der if/else-Ablauf wird in der Sprache Painless definiert. Auch die Aggregation 'range' kann CASE-ähnliche Buckets zur Kategorisierung erzeugen.","danger":"safe"}}},{"id":"union-set","title":"UNION / UNION ALL / INTERSECT / EXCEPT (MINUS)","aliases":["union","union all","intersect","except","minus","set operations","combine","intersection","difference"],"engines":{"oracle":{"command":"-- Union without duplicates\nSELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\n-- Intersection and difference (MINUS instead of EXCEPT in Oracle)\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;\nSELECT city FROM customer MINUS SELECT city FROM supplier;","note":"Mengenoperatoren kombinieren die Ergebnismengen zweier Abfragen. UNION entfernt Duplikate (langsamer), UNION ALL behält alle (schneller), INTERSECT liefert gemeinsame Zeilen, und der Differenzoperator liefert Zeilen, die nur in der ersten Menge vorkommen. Für die Differenz nutzt Oracle MINUS statt des Standard-EXCEPT (EXCEPT gibt es seit 21c ebenfalls).","danger":"safe"},"postgres":{"command":"SELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;\nSELECT city FROM customer EXCEPT SELECT city FROM supplier;","note":"PostgreSQL unterstützt Standard-UNION, UNION ALL, INTERSECT und EXCEPT vollständig (EXCEPT, nicht MINUS, für die Differenz). Die ALL-Form jedes Operators (INTERSECT ALL, EXCEPT ALL) bewahrt die Anzahl der Duplikate. Spaltenanzahl und -typen müssen kompatibel sein.","danger":"safe"},"mysql":{"command":"SELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\n-- INTERSECT/EXCEPT arrive with MySQL 8.0.31+ and MariaDB 10.3+\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;","note":"Lange unterstützte MySQL nur UNION / UNION ALL; INTERSECT und EXCEPT kamen erst mit MySQL 8.0.31 (MariaDB 10.3). In älteren Versionen nutze INNER JOIN/EXISTS für die Schnittmenge und LEFT JOIN ... IS NULL oder NOT EXISTS für die Differenz.","since":"MySQL 8.0.31","xnote":"INTERSECT/EXCEPT kamen mit MySQL 8.0.31 (MariaDB 10.3); davor wurden sie mit JOIN/EXISTS gelöst.","danger":"safe"},"mssql":{"command":"SELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;\nSELECT city FROM customer EXCEPT SELECT city FROM supplier;","note":"SQL Server unterstützt Standard-UNION, UNION ALL, INTERSECT und EXCEPT (2005+); der Differenzoperator ist EXCEPT (nicht MINUS). INTERSECT/EXCEPT vergleichen Zeilen und behandeln NULL-Werte dabei als gleich.","since":"SQL Server 2005","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $unionWith (MongoDB 4.4+)\ndb.customer.aggregate([\n  { $project: { city: 1, _id: 0 } },\n  { $unionWith: { coll: \"supplier\",\n      pipeline: [ { $project: { city: 1, _id: 0 } } ] } },\n  { $group: { _id: \"$city\" } }  // UNION (distinct) effect\n])","note":"Die Stage $unionWith in MongoDB 4.4+ kombiniert die Ergebnisse zweier Collections (wie UNION ALL). Um Duplikate zu entfernen, dedupliziere anschließend mit $group. INTERSECT/EXCEPT haben keine direkte Entsprechung; sie werden mit $lookup oder Mengenoperatoren ($setIntersection, $setDifference) modelliert.","danger":"safe"},"redis":{"command":"# Set operations with the Set data type:\nSUNION city_customer city_supplier    # union\nSINTER city_customer city_supplier    # intersection\nSDIFF  city_customer city_supplier    # difference","note":"Redis hat kein SQL, aber der Datentyp Set bietet native Mengenoperationen: SUNION (Vereinigung), SINTER (Schnittmenge), SDIFF (Differenz) und deren STORE-Varianten, die das Ergebnis speichern. Sie arbeiten auf vorab aufgebauten Set-Mitgliedern, nicht auf Abfrageergebnissen.","danger":"safe"},"elasticsearch":{"command":"# There is no direct set operator.\n# For a union, search multiple indices in one query:\nGET customer,supplier/_search\n{ \"query\": { \"match_all\": {} } }","note":"Elasticsearch bietet kein UNION/INTERSECT/EXCEPT. Eine Vereinigung erreichst du, indem du mehrere kommagetrennte Indizes abfragst; für Schnittmenge und Differenz nutze die Klauseln must/should/must_not einer bool-Abfrage oder Mengenlogik in der Anwendung.","danger":"safe"}}},{"id":"exists-in","title":"EXISTS / IN / NOT EXISTS (Unterabfrage)","aliases":["exists","in","not exists","not in","subquery","sub-select","correlated subquery"],"engines":{"oracle":{"command":"-- Customers with orders (EXISTS)\nSELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\n-- List membership with IN\nSELECT name FROM customer\nWHERE city IN ('Ankara', 'Izmir');\n\n-- Customers with no orders at all (NOT EXISTS)\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"EXISTS prüft, ob eine korrelierte Unterabfrage mindestens eine Zeile liefert (stoppt beim ersten Treffer und ist daher schnell). IN prüft, ob ein Wert in einer Liste/einem Unterabfrageergebnis enthalten ist. Achtung: Kann die Unterabfrage NULL liefern, ergibt NOT IN unerwartete Ergebnisse; NOT EXISTS ist sicherer. Der Oracle-Optimizer macht aus beiden oft denselben Plan.","danger":"safe"},"postgres":{"command":"SELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\nSELECT name FROM customer\nWHERE city IN (SELECT city FROM target_cities);\n\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"PostgreSQL unterstützt EXISTS/IN/NOT EXISTS vollständig. Enthält bei NOT IN die Unterabfrage NULL, kann das gesamte Ergebnis leer werden; daher werden NOT EXISTS oder LEFT JOIN ... IS NULL empfohlen. Der Planner behandelt IN und EXISTS in den meisten Fällen gleichwertig als Semi-Join.","danger":"safe"},"mysql":{"command":"SELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\nSELECT name FROM customer\nWHERE city IN ('Ankara', 'Izmir');\n\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"MySQL/MariaDB unterstützen EXISTS/IN/NOT EXISTS. Vor MySQL 5.6 konnten korrelierte Unterabfragen langsam sein; ab 5.6 wurden IN-Unterabfragen durch Semi-Join- und Materialisierungsoptimierungen beschleunigt. Die Falle NOT IN + NULL gilt auch hier.","danger":"safe"},"mssql":{"command":"SELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\nSELECT name FROM customer\nWHERE city IN (SELECT city FROM target_cities);\n\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"SQL Server unterstützt EXISTS/IN/NOT EXISTS, und der Optimizer verarbeitet sie meist als Semi-/Anti-Joins. Enthält eine NOT IN-Unterabfrage NULL, kann das Ergebnis unerwartet leer werden; NOT EXISTS ist die sichere Wahl. Für ähnliche Mitgliedschaftslogik lassen sich auch INTERSECT/EXCEPT nutzen.","danger":"safe"},"mongodb":{"command":"// IN equivalent: the $in operator\ndb.customer.find({ city: { $in: [\"Ankara\", \"Izmir\"] } })\n\n// EXISTS equivalent: match check with $lookup + $match\ndb.customer.aggregate([\n  { $lookup: { from: \"orders\", localField: \"id\",\n      foreignField: \"customer_id\", as: \"s\" } },\n  { $match: { \"s.0\": { $exists: true } } }  // at least one order\n])","note":"In MongoDB erfolgt die Mitgliedschaftsprüfung in Wertelisten mit den Operatoren $in / $nin. Die Entsprechung zu EXISTS mit Unterabfrage ist, mit $lookup das Array passender Dokumente zu holen und danach zu filtern, ob das Array leer ist. Die Prüfung auf Feldexistenz ist ein separater Operator { field: { $exists: true } } (das Vorhandensein eines Felds, keine Zeilenbeziehung).","danger":"safe"},"redis":{"command":"# Membership check with Set/Hash:\nSISMEMBER target_cities \"Ankara\"   # is it in the set? (IN-like)\nEXISTS customer:1                 # does the key exist?","note":"Redis hat keine Unterabfragen. Für die Mitgliedschaftsprüfung nutze SISMEMBER auf einem Set (IN-ähnlich). Der Befehl EXISTS ist keine Unterabfrage; er liefert, ob der angegebene Schlüssel existiert. Relationale Existenzprüfungen erfolgen in der Anwendung durch Lesen mehrerer Schlüssel.","danger":"safe"},"elasticsearch":{"command":"# IN equivalent: terms query\nGET customer/_search\n{ \"query\": { \"terms\": { \"city\": [\"Ankara\", \"Izmir\"] } } }\n\n# Field existence: exists query\nGET customer/_search\n{ \"query\": { \"exists\": { \"field\": \"phone\" } } }","note":"In Elasticsearch entspricht die 'terms'-Abfrage IN (Treffer auf einen von mehreren Werten), NOT IN ist terms innerhalb von bool must_not. Die 'exists'-Abfrage entspricht nicht SQL-EXISTS; sie prüft, ob ein Feld im Dokument einen Wert hat. Tabellenübergreifendes korreliertes EXISTS erfordert meist Denormalisierung.","danger":"safe"}}},{"id":"limit-paging","title":"Zeilenbegrenzung / Paging (LIMIT/OFFSET, FETCH, ROWNUM, TOP)","aliases":["limit","offset","fetch","rownum","top","paging","page through results","pagination","row limit"],"engines":{"oracle":{"command":"-- 12c+ standard: OFFSET/FETCH\nSELECT name, salary FROM employee\nORDER BY salary DESC\nOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;\n\n-- Older versions: with ROWNUM\nSELECT * FROM (\n  SELECT name, salary FROM employee ORDER BY salary DESC\n) WHERE ROWNUM <= 10;","note":"Dient zum Begrenzen/seitenweisen Durchlaufen einer Ergebnismenge. Seit Oracle 12c wird das Standard-OFFSET n ROWS FETCH NEXT m ROWS ONLY unterstützt. Davor wurde die Pseudospalte ROWNUM genutzt; da ROWNUM vor der Sortierung vergeben wird, ist eine umschließende Unterabfrage mit ORDER BY Pflicht.","since":"Oracle 12c","xnote":"Vor Oracle 12c erfolgte die Zeilenbegrenzung mit ROWNUM.","danger":"safe"},"postgres":{"command":"-- LIMIT/OFFSET\nSELECT name, salary FROM employee\nORDER BY salary DESC\nLIMIT 10 OFFSET 20;\n\n-- Standard equivalent: FETCH\nSELECT name, salary FROM employee\nORDER BY salary DESC\nOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;","note":"PostgreSQL unterstützt sowohl LIMIT n OFFSET m als auch die Standardform OFFSET ... FETCH. Große OFFSET-Werte sind langsam; für tiefes Paging ist schlüsselbasierte Paginierung (Keyset / 'WHERE id > last_id') effizienter. LIMIT ohne ORDER BY garantiert keine Ergebnisreihenfolge.","danger":"safe"},"mysql":{"command":"-- LIMIT [offset,] row_count\nSELECT name, salary FROM employee\nORDER BY salary DESC\nLIMIT 10 OFFSET 20;\n\n-- Short form: LIMIT offset, row_count\nSELECT name, salary FROM employee ORDER BY salary DESC LIMIT 20, 10;","note":"MySQL/MariaDB nutzen das Schlüsselwort LIMIT in zwei Formen: LIMIT row_count OFFSET offset oder LIMIT offset, row_count. MySQL 8.0.19+ akzeptiert auch die Standard-FETCH-Syntax. Für Performance bei tiefem OFFSET wird Keyset-Paginierung empfohlen.","danger":"safe"},"mssql":{"command":"-- First N with TOP\nSELECT TOP 10 name, salary FROM employee ORDER BY salary DESC;\n\n-- OFFSET/FETCH for paging (2012+), ORDER BY mandatory\nSELECT name, salary FROM employee\nORDER BY salary DESC\nOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;","note":"SQL Server nutzt TOP n (mit den Varianten TOP (n) PERCENT / WITH TIES) für die ersten N Zeilen und seit 2012 OFFSET ... FETCH für Paging. Damit OFFSET/FETCH funktioniert, ist ORDER BY Pflicht. SQL Server hat kein Schlüsselwort LIMIT.","since":"SQL Server 2012","xnote":"Paging mit OFFSET/FETCH kam mit 2012; davor erfolgte es mit TOP oder ROW_NUMBER.","danger":"safe"},"mongodb":{"command":"// Paging with skip/limit\ndb.employee.find()\n  .sort({ salary: -1 })\n  .skip(20)\n  .limit(10)","note":"In MongoDB legt limit() die Anzahl zurückgegebener Dokumente fest und skip() die Anzahl übersprungener; zusammen mit sort() ergibt das Paging. Große skip-Werte sind teuer; für tiefes Paging wird eine Bereichsabfrage (Keyset) auf Basis des zuletzt gesehenen Werts bevorzugt.","danger":"safe"},"redis":{"command":"# Range read on a sorted set (ZSET):\nZREVRANGE salary_sorted 20 29    # members ranked 21-30\n# Range on a list:\nLRANGE queue 0 9              # first 10 elements","note":"Redis hat kein SQL-LIMIT, aber Befehle zum Lesen von Bereichen: ZRANGE/ZREVRANGE start stop (indexbasiert) oder ZRANGEBYSCORE ... LIMIT offset count für Sorted Sets; LRANGE für Lists. Diese Befehle ermöglichen paging-ähnlichen Zugriff.","danger":"safe"},"elasticsearch":{"command":"GET employee/_search\n{\n  \"from\": 20,\n  \"size\": 10,\n  \"sort\": [ { \"salary\": \"desc\" } ]\n}","note":"In Elasticsearch legt 'size' die Anzahl zurückgegebener Treffer fest und 'from' die Anzahl übersprungener (Entsprechung zu LIMIT/OFFSET). from+size darf höchstens index.max_result_window betragen (Standard 10.000); für tiefes Paging nutze 'search_after' (Keyset) oder einen PIT (Point-in-Time).","danger":"safe"}}},{"id":"merge-upsert","title":"MERGE / UPSERT (aktualisieren, falls vorhanden, sonst einfügen)","aliases":["merge","upsert","on conflict","on duplicate key","insert or update","update if exists"],"engines":{"oracle":{"command":"MERGE INTO customer m\nUSING (SELECT 1 AS id, 'Ali' AS name, 500 AS balance FROM dual) k\nON (m.id = k.id)\nWHEN MATCHED THEN\n  UPDATE SET m.name = k.name, m.balance = k.balance\nWHEN NOT MATCHED THEN\n  INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);","note":"MERGE (Upsert) gleicht eine Quelle mit einem Ziel ab, aktualisiert passende Zeilen und fügt nicht passende ein; ideal für die Massensynchronisation von Daten. Oracle bietet starke MERGE-Unterstützung: Auch WHEN MATCHED ... DELETE und bedingte Updates sind möglich. Für eine einzeilige Quelle wird die Tabelle dual genutzt.","since":"Oracle 9i","danger":"caution"},"postgres":{"command":"-- The PostgreSQL way: INSERT ... ON CONFLICT\nINSERT INTO customer (id, name, balance)\nVALUES (1, 'Ali', 500)\nON CONFLICT (id) DO UPDATE\n  SET name = EXCLUDED.name, balance = EXCLUDED.balance;","note":"Der klassische Upsert in PostgreSQL ist INSERT ... ON CONFLICT (unique column) DO UPDATE / DO NOTHING; auf die neuen Werte der kollidierenden Zeile greifst du über den Alias EXCLUDED zu. Seit PostgreSQL 15 wird auch die Standardanweisung MERGE unterstützt. ON CONFLICT ist auf einer einzelnen Tabelle atomar und weit verbreitet.","since":"PostgreSQL 15","xnote":"Die Standardanweisung MERGE kam mit PostgreSQL 15; davor erfolgten Upserts mit INSERT ... ON CONFLICT (9.5).","danger":"caution"},"mysql":{"command":"INSERT INTO customer (id, name, balance)\nVALUES (1, 'Ali', 500)\nON DUPLICATE KEY UPDATE\n  name = VALUES(name), balance = VALUES(balance);","note":"MySQL/MariaDB nutzen für Upserts INSERT ... ON DUPLICATE KEY UPDATE; kollidiert ein Unique- oder Primärschlüssel, wird die Zeile aktualisiert. Auf neue Werte greifst du mit VALUES(my_column) zu (oder ab 8.0.19 mit dem Zeilenalias 'new.my_column'). INSERT IGNORE überspringt die Zeile bei einem Konflikt stillschweigend. MySQL hat kein Standard-MERGE.","danger":"caution"},"mssql":{"command":"MERGE INTO customer AS m\nUSING (VALUES (1, 'Ali', 500)) AS k(id, name, balance)\nON m.id = k.id\nWHEN MATCHED THEN\n  UPDATE SET m.name = k.name, m.balance = k.balance\nWHEN NOT MATCHED THEN\n  INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);","note":"SQL Server unterstützt die Anweisung MERGE seit 2008; sie bietet zusätzliche Zweige wie WHEN NOT MATCHED BY SOURCE zum Löschen überzähliger Zeilen im Ziel. Eine MERGE-Anweisung muss mit einem Semikolon abgeschlossen werden. Hinweis: Wegen einiger Bugs in Randfällen werden für einfache Upserts mitunter separate INSERT-/UPDATE-Anweisungen bevorzugt.","since":"SQL Server 2008","danger":"caution"},"mongodb":{"command":"// Find-and-update or insert with upsert:true\ndb.customer.updateOne(\n  { id: 1 },\n  { $set: { name: \"Ali\", balance: 500 } },\n  { upsert: true }\n)","note":"In MongoDB ist Upsert eine native Option: Wird { upsert: true } zu updateOne/updateMany/findOneAndUpdate hinzugefügt, wird ein passendes Dokument aktualisiert, andernfalls ein neues angelegt. Für Massensynchronisation kommt die Aggregation-Stage $merge (Schreiben/Aktualisieren in eine Ziel-Collection) SQL-MERGE näher.","danger":"caution"},"redis":{"command":"# SET is a natural upsert: it overwrites the key if present, otherwise creates it.\nSET customer:1 '{\"name\":\"Ali\",\"balance\":500}'\n# Update/add a hash field:\nHSET customer:1 name Ali balance 500","note":"In Redis sind Schreibbefehle von Natur aus Upserts: SET/HSET aktualisieren den Schlüssel oder das Feld, falls vorhanden, sonst legen sie es an. Mit der Option SET ... NX schreibst du nur, wenn nicht vorhanden, mit XX nur, wenn vorhanden. Die Unterscheidung 'aktualisieren, falls vorhanden, sonst einfügen' ist bei den meisten Befehlen bereits das Standardverhalten.","danger":"safe"},"elasticsearch":{"command":"# An index call with a document id acts like an upsert (replaces if present):\nPUT customer/_doc/1\n{ \"name\": \"Ali\", \"balance\": 500 }\n# Partial update + insert if absent:\nPOST customer/_update/1\n{ \"doc\": { \"balance\": 500 }, \"doc_as_upsert\": true }","note":"In Elasticsearch ersetzt ein index-Aufruf (PUT) mit derselben _id das Dokument vollständig, falls vorhanden, sonst legt er es an (upsert-ähnlich). Für Teilaktualisierungen mit Einfügen bei Nichtvorhandensein nutze 'doc_as_upsert': true oder einen 'upsert'-Block in der _update API. Auch Scripted Upserts sind möglich.","danger":"caution"}}},{"id":"returning-output","title":"Geänderte Zeile zurückgeben (RETURNING / OUTPUT)","aliases":["returning","output","inserted","deleted","returned value","modified row"],"engines":{"oracle":{"command":"-- Single row: RETURNING ... INTO (PL/SQL bind variable)\nUPDATE employee SET salary = salary * 1.1\nWHERE id = 5\nRETURNING salary INTO :new_salary;\n\n-- Get back the key generated by an INSERT\nINSERT INTO employee (name) VALUES ('Ayse')\nRETURNING id INTO :new_id;","note":"RETURNING liefert die Werte der durch INSERT/UPDATE/DELETE geänderten Zeilen (z. B. eine automatisch erzeugte ID) ohne separates SELECT. In Oracle wird eine einzelne Zeile mit RETURNING ... INTO in eine Bind-Variable zurückgegeben, mehrere Zeilen mit RETURNING ... BULK COLLECT INTO in eine Collection.","danger":"safe"},"postgres":{"command":"INSERT INTO employee (name, salary)\nVALUES ('Ayse', 15000)\nRETURNING id, name;\n\nUPDATE employee SET salary = salary * 1.1\nWHERE department = 'IT'\nRETURNING id, salary;","note":"Die RETURNING-Klausel in PostgreSQL ist sehr flexibel: Sie liefert beliebige Spalten (auch berechnete Ausdrücke) der von INSERT-, UPDATE-, DELETE- und MERGE-Anweisungen betroffenen Zeilen direkt als Ergebnismenge. Sie ist der sauberste Weg, erzeugte serial-/identity-Werte zu erhalten.","since":"PostgreSQL 8.2","danger":"safe"},"mysql":{"command":"-- MySQL has no RETURNING; the generated key is retrieved separately:\nINSERT INTO employee (name, salary) VALUES ('Ayse', 15000);\nSELECT LAST_INSERT_ID();\n\n-- MariaDB 10.5+ supports INSERT ... RETURNING:\nINSERT INTO employee (name) VALUES ('Ayse') RETURNING id;","note":"MySQL unterstützt kein Standard-RETURNING; der von einem INSERT erzeugte Auto-Increment-Schlüssel wird mit LAST_INSERT_ID() abgerufen, aktualisierte Werte werden mit einem separaten SELECT gelesen. MariaDB bietet INSERT ... RETURNING seit 10.5 und DELETE ... RETURNING seit 10.0.","danger":"safe"},"mssql":{"command":"INSERT INTO employee (name, salary)\nOUTPUT INSERTED.id, INSERTED.name\nVALUES ('Ayse', 15000);\n\nUPDATE employee SET salary = salary * 1.1\nOUTPUT DELETED.salary AS old_salary, INSERTED.salary AS new_salary\nWHERE id = 5;","note":"In SQL Server entspricht dem die OUTPUT-Klausel: Sie greift auf die Pseudotabellen INSERTED (neue Werte) und DELETED (alte Werte) zu; in einem UPDATE sind beide nutzbar. Mit OUTPUT ... INTO lassen sich die Ergebnisse in eine Tabelle/Tabellenvariable schreiben. Ideal, um Änderungen in eine Audit-Tabelle zu kopieren.","since":"SQL Server 2005","xnote":"Die OUTPUT-Klausel kam mit SQL Server 2005.","danger":"safe"},"mongodb":{"command":"// findOneAndUpdate returns the modified document\ndb.employee.findOneAndUpdate(\n  { id: 5 },\n  { $mul: { salary: 1.1 } },\n  { returnDocument: \"after\" }   // return the updated version\n)","note":"In MongoDB liefern die Befehle findOneAndUpdate / findOneAndDelete / findOneAndReplace das betroffene Dokument in einer einzigen atomaren Operation. returnDocument: 'before' liefert den Stand vor der Änderung, 'after' den Stand danach. insertOne liefert die _id des eingefügten Dokuments.","danger":"safe"},"redis":{"command":"# Some commands return the old/new value directly:\nSET counter 0 GET       # (6.2+) returns the old value and writes the new one (replaces GETSET)\nINCR visits          # increments and returns the new value","note":"Redis hat kein separates RETURNING, aber viele Schreibbefehle liefern das Ergebnis direkt: INCR/INCRBY liefern den neuen Wert, SET ... GET den alten Wert (GETSET ist seit 6.2 veraltet) und LPOP/RPOP das entfernte Element. Für atomares Lesen-Schreiben ersetzt dieses Verhalten RETURNING.","danger":"safe"},"elasticsearch":{"command":"# The _update response can return the result:\nPOST employee/_update/5\n{\n  \"script\": { \"source\": \"ctx._source.salary *= 1.1\" },\n  \"_source\": true\n}\n# The response returns the updated document in get._source","note":"In Elasticsearch liefern index-/update-Operationen Ergebnis-Metadaten (_id, _version, result). Um den Inhalt des aktualisierten Dokuments zu erhalten, ergänze '_source': true in der _update-Anfrage; das get-Feld der Antwort enthält dann die aktuelle _source. Ein separater GET-Aufruf ist nicht nötig.","danger":"safe"}}},{"id":"lateral-apply","title":"LATERAL / CROSS APPLY (zeilenweise Unterabfrage)","aliases":["lateral","cross apply","outer apply","row-by-row subquery","correlated table function"],"engines":{"oracle":{"command":"-- Each customer's 3 most recent orders (LATERAL, 12c+)\nSELECT m.name, s.created_at, s.amount\nFROM customer m,\n     LATERAL (SELECT created_at, amount FROM orders\n              WHERE customer_id = m.id\n              ORDER BY created_at DESC FETCH FIRST 3 ROWS ONLY) s;\n\n-- Alternative: CROSS APPLY (12c+)\nSELECT m.name, s.amount\nFROM customer m\nCROSS APPLY (SELECT amount FROM orders WHERE customer_id = m.id) s;","note":"LATERAL/APPLY erlaubt einer Unterabfrage in FROM, auf Spalten der links davon stehenden Tabelle zu verweisen, sodass du korrelierte Joins wie 'die ersten N Zeilen für jede Zeile' schreiben kannst. Oracle 12c unterstützt sowohl ANSI-LATERAL als auch die Syntax CROSS/OUTER APPLY.","since":"Oracle 12c","xnote":"LATERAL und CROSS/OUTER APPLY kamen mit Oracle 12c.","danger":"safe"},"postgres":{"command":"SELECT m.name, s.created_at, s.amount\nFROM customer m\nCROSS JOIN LATERAL (\n  SELECT created_at, amount FROM orders\n  WHERE customer_id = m.id\n  ORDER BY created_at DESC LIMIT 3\n) s;","note":"In PostgreSQL werden Unterabfragen, die von der Zeile links davon abhängen, mit dem Schlüsselwort LATERAL geschrieben (JOIN LATERAL / CROSS JOIN LATERAL / LEFT JOIN LATERAL). Sehr praktisch für 'Top N pro Gruppe' und zum Expandieren von Zeilen mit Set-Returning Functions. Eine CROSS APPLY-Syntax gibt es nicht; es wird LATERAL verwendet.","since":"PostgreSQL 9.3","xnote":"LATERAL kam mit PostgreSQL 9.3.","danger":"safe"},"mysql":{"command":"-- MySQL 8.0.14+ supports LATERAL\nSELECT m.name, s.amount\nFROM customer m\nJOIN LATERAL (\n  SELECT amount FROM orders\n  WHERE customer_id = m.id\n  ORDER BY created_at DESC LIMIT 3\n) s ON true;","note":"Seit MySQL 8.0.14 werden LATERAL Derived Tables unterstützt; die Unterabfrage kann auf Spalten der äußeren Tabelle verweisen. Davor und für CROSS APPLY gab es keine Entsprechung; 'Top N pro Gruppe' wurde mit Fensterfunktionen oder korrelierten Unterabfragen gelöst. MariaDB hat noch kein LATERAL.","since":"MySQL 8.0.14","xnote":"LATERAL Derived Tables kamen mit MySQL 8.0.14; MariaDB hat sie nicht.","danger":"safe"},"mssql":{"command":"SELECT m.name, s.created_at, s.amount\nFROM customer m\nCROSS APPLY (\n  SELECT TOP 3 created_at, amount FROM orders\n  WHERE customer_id = m.id\n  ORDER BY created_at DESC\n) s;\n-- Use OUTER APPLY to return non-matching rows too","note":"In SQL Server entspricht der APPLY-Operator LATERAL: CROSS APPLY (wie INNER JOIN LATERAL, verwirft Nicht-Treffer) und OUTER APPLY (wie LEFT JOIN LATERAL, NULL bei Nicht-Treffern). Üblich, um für jede Zeile Table-Valued Functions aufzurufen, und für Top-N-pro-Gruppe-Abfragen.","since":"SQL Server 2005","xnote":"Der Operator CROSS/OUTER APPLY kam mit SQL Server 2005.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $lookup with a sub-pipeline\ndb.customer.aggregate([\n  { $lookup: {\n      from: \"orders\",\n      let: { mid: \"$id\" },\n      pipeline: [\n        { $match: { $expr: { $eq: [\"$customer_id\", \"$$mid\"] } } },\n        { $sort: { created_at: -1 } }, { $limit: 3 }\n      ],\n      as: \"last3\"\n  } }\n])","note":"In MongoDB entspricht LATERAL/APPLY die $lookup-Form mit Sub-Pipeline und let-Variablen: Die Felder des äußeren Dokuments werden über let als $$variable an die Sub-Pipeline übergeben, wo Abgleich/Sortierung/Begrenzung erfolgen. Damit wird das Muster 'zugehörige Top N für jedes Dokument' gelöst.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no subqueries / LATERAL.","note":"Redis bietet keine zeilenweise korrelierten Unterabfragen. Das Muster 'zugehörige Top N für jedes Element' wird mit einer Schleife in der Anwendung gelöst (z. B. ein separates ZRANGE pro Schlüssel) oder mit einem serverseitigen Lua-Skript.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: terms + top_hits sub-aggregation\nGET orders/_search\n{\n  \"size\": 0,\n  \"aggs\": { \"by_customer\": { \"terms\": { \"field\": \"customer_id\" },\n    \"aggs\": { \"last3\": { \"top_hits\": {\n      \"sort\": [ { \"created_at\": \"desc\" } ], \"size\": 3 } } } } }\n}","note":"Elasticsearch hat kein LATERAL/APPLY; das Muster 'Top-N-Datensätze pro Gruppe' wird mit einer in eine terms-Aggregation verschachtelten Sub-Aggregation top_hits erreicht. Statt einer korrelierten Unterabfrage ist dies Gruppierung plus Stichprobe innerhalb der Gruppe auf einem einzelnen Index.","danger":"safe"}}},{"id":"pivot","title":"PIVOT / UNPIVOT (Zeilen↔Spalten)","aliases":["pivot","unpivot","crosstab","cross tab","rows to columns","transpose"],"engines":{"oracle":{"command":"-- Turn sales by month into columns\nSELECT * FROM (\n  SELECT month, amount FROM sale\n)\nPIVOT (SUM(amount) FOR month IN ('Jan' AS jan, 'Feb' AS feb, 'Mar' AS mar));\n\n-- UNPIVOT: turn columns back into rows\nSELECT product, month, amount\nFROM sale_wide\nUNPIVOT (amount FOR month IN (jan AS 'Jan', feb AS 'Feb'));","note":"PIVOT wandelt unterschiedliche Werte in Zeilen in Spalten um (Kreuztabelle), UNPIVOT wandelt Spalten zurück in Zeilen; genutzt in Übersichts-/Berichtstabellen. Oracle bietet seit 11g eingebaute PIVOT/UNPIVOT-Operatoren; die Spaltenliste muss statisch sein (dynamische Spalten erfordern PIVOT XML oder Code).","since":"Oracle 11g","xnote":"Die eingebauten PIVOT/UNPIVOT-Operatoren kamen mit Oracle 11g.","danger":"safe"},"postgres":{"command":"-- The tablefunc extension for crosstab\nCREATE EXTENSION IF NOT EXISTS tablefunc;\nSELECT * FROM crosstab(\n  'SELECT product, month, amount FROM sale ORDER BY 1,2'\n) AS ct(product text, jan numeric, feb numeric, mar numeric);\n\n-- Without an extension: conditional aggregation\nSELECT product,\n  SUM(amount) FILTER (WHERE month = 'Jan') AS jan,\n  SUM(amount) FILTER (WHERE month = 'Feb') AS feb\nFROM sale GROUP BY product;","note":"PostgreSQL hat keinen eingebauten PIVOT-Operator. Es gibt zwei Wege: die Funktion crosstab() der Extension tablefunc oder portable bedingte Aggregation (SUM(...) FILTER (WHERE ...) oder mit CASE). Die FILTER-Klausel ist eine elegante PostgreSQL-spezifische Pivot-Technik.","danger":"safe"},"mysql":{"command":"-- There is no PIVOT; conditional aggregation with manual CASE\nSELECT product,\n  SUM(CASE WHEN month = 'Jan' THEN amount ELSE 0 END) AS jan,\n  SUM(CASE WHEN month = 'Feb' THEN amount ELSE 0 END) AS feb,\n  SUM(CASE WHEN month = 'Mar' THEN amount ELSE 0 END) AS mar\nFROM sale\nGROUP BY product;","note":"MySQL/MariaDB haben keinen PIVOT/UNPIVOT-Operator; eine Kreuztabelle wird manuell mit GROUP BY plus einem SUM(CASE WHEN ... THEN ... END) für jede Zielspalte gebaut. Sind die Spalten dynamisch, wird die Abfrage als String mit einem Prepared Statement erzeugt.","danger":"safe"},"mssql":{"command":"SELECT * FROM (\n  SELECT product, month, amount FROM sale\n) src\nPIVOT (\n  SUM(amount) FOR month IN ([Jan], [Feb], [Mar])\n) pvt;\n\n-- Converting back with UNPIVOT is also supported","note":"SQL Server unterstützt die Operatoren PIVOT und UNPIVOT nativ seit 2005. Die PIVOT-Spaltenliste wird als statische Werte in eckigen Klammern angegeben; für dynamische Spalten wird die Abfrage mit dynamischem SQL gebaut. Für kleine Übersichten ist bedingte Aggregation (CASE) eine Alternative.","since":"SQL Server 2005","xnote":"Die Operatoren PIVOT/UNPIVOT kamen mit SQL Server 2005.","danger":"safe"},"mongodb":{"command":"// PIVOT equivalent: conditional aggregation inside $group\ndb.sale.aggregate([\n  { $group: {\n      _id: \"$product\",\n      jan: { $sum: { $cond: [ { $eq: [\"$month\", \"Jan\"] }, \"$amount\", 0 ] } },\n      feb: { $sum: { $cond: [ { $eq: [\"$month\", \"Feb\"] }, \"$amount\", 0 ] } }\n  } }\n])","note":"MongoDB hat keinen PIVOT-Operator; eine Kreuztabelle wird in der Stage $group mit $sum + $cond (bedingte Aggregation) für jede Zielspalte gebaut. Für die Gegenrichtung (UNPIVOT) werden Felder mit $objectToArray und $unwind in Zeilen expandiert.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no pivot/cross tab.","note":"Redis kennt kein Konzept zum Drehen von Zeilen und Spalten. Übersichten/Kreuztabellen werden in der Anwendung berechnet; häufig angeforderte Übersichten lassen sich vorab berechnen und in Hash-Feldern speichern (ein Feld pro Spalte). RediSearch FT.AGGREGATE kann gruppiert aggregieren, ist aber kein echtes Pivot.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: nested terms aggregations\nGET sale/_search\n{\n  \"size\": 0,\n  \"aggs\": { \"product\": { \"terms\": { \"field\": \"product\" },\n    \"aggs\": { \"month\": { \"terms\": { \"field\": \"month\" },\n      \"aggs\": { \"total\": { \"sum\": { \"field\": \"amount\" } } } } } }\n}","note":"Elasticsearch hat keinen PIVOT-Operator; eine Kreuzübersicht wird mit verschachtelten terms-Aggregationen gebaut (äußerer Schlüssel / innerer Schlüssel), das Ergebnis kommt als verschachtelte Buckets statt als Spalten zurück und wird im Client in eine Tabelle umgewandelt. Ein Transform-Job kann einen gruppierten, pivot-ähnlichen Zielindex erzeugen.","danger":"safe"}}},{"id":"coalesce-null","title":"NULL-Behandlung (COALESCE/NVL/NULLIF/IS NULL)","aliases":["coalesce","nvl","nullif","is null","ifnull","isnull","null handling","empty value"],"engines":{"oracle":{"command":"-- COALESCE: returns the first non-NULL value (standard)\nSELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\n\n-- Oracle-specific NVL / NVL2\nSELECT NVL(balance, 0) FROM customer;\nSELECT NVL2(discount, 'yes', 'no') FROM orders;\n\n-- NULLIF and IS NULL\nSELECT NULLIF(a, b) FROM t;   -- NULL if a=b\nSELECT name FROM customer WHERE phone IS NULL;","note":"Zur Behandlung von NULL (unbekannter Wert): COALESCE liefert den ersten Nicht-NULL-Wert der Liste (Standard, mehrere Argumente). Oracles NVL(a,b) ist die Entsprechung mit zwei Argumenten; NVL2 liefert je nachdem, ob der Wert NULL ist oder nicht, unterschiedliche Werte. NULLIF(a,b) liefert NULL, wenn beide gleich sind. Prüfe NULL immer mit IS NULL / IS NOT NULL (= NULL funktioniert nicht).","danger":"safe"},"postgres":{"command":"SELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\nSELECT NULLIF(divisor, 0) FROM t;   -- prevent division by zero\nSELECT name FROM customer WHERE phone IS NULL;\n-- The equivalent of Oracle NVL is COALESCE","note":"PostgreSQL bietet Standard-COALESCE, NULLIF und IS NULL/IS NOT NULL; nutze COALESCE statt Oracles NVL. Für NULL-Vergleiche IS statt = verwenden; müssen zwei NULLs als gleich gelten, erledigt das der Operator 'IS NOT DISTINCT FROM'.","danger":"safe"},"mysql":{"command":"SELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\n\n-- MySQL shortcut: IFNULL (two arguments)\nSELECT IFNULL(balance, 0) FROM customer;\nSELECT NULLIF(a, b) FROM t;\nSELECT name FROM customer WHERE phone IS NULL;","note":"MySQL/MariaDB unterstützen Standard-COALESCE und NULLIF; IFNULL(a,b) ist eine Kurzform mit zwei Argumenten (ähnlich Oracle NVL). Der Operator <=> bietet NULL-sichere Gleichheit (behandelt zwei NULLs als gleich). NULL wird mit IS NULL / IS NOT NULL geprüft.","danger":"safe"},"mssql":{"command":"SELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\n\n-- SQL Server shortcut: ISNULL (two arguments)\nSELECT ISNULL(balance, 0) FROM customer;\nSELECT NULLIF(a, b) FROM t;\nSELECT name FROM customer WHERE phone IS NULL;","note":"SQL Server bietet Standard-COALESCE (mehrere Argumente, mit Datentyp-Promotion) und ISNULL(a,b) mit zwei Argumenten; beide unterscheiden sich in Feinheiten (COALESCE ist Standard und flexibler bei der Typrangfolge). Auch NULLIF und IS NULL werden unterstützt. ISNULL entspricht Oracle NVL (die Namen sind ähnlich).","danger":"safe"},"mongodb":{"command":"// COALESCE equivalent: $ifNull\ndb.customer.aggregate([\n  { $project: { name: 1,\n      contact: { $ifNull: [\"$phone\", \"$mobile\", \"none\"] } } }\n])\n// NULL / missing field check:\ndb.customer.find({ phone: { $in: [null] } })","note":"In der MongoDB-Aggregation entspricht der Operator $ifNull COALESCE (mehrere Argumente, liefert das erste, das nicht null/fehlend ist). In einer Abfrage trifft { field: null } sowohl Dokumente mit dem Wert null als auch Dokumente, denen das Feld ganz fehlt; mache die Unterscheidung mit { field: { $type: 'null' } } oder $exists explizit.","danger":"safe"},"redis":{"command":"# Instead of NULL, Redis has the concept of 'no such key/field':\nEXISTS customer:1              # does the key exist?\nHGET customer:1 phone        # returns nil if the field is absent\nGET missing_key                  # (nil)","note":"Redis hat kein NULL im SQL-Sinn; die Entsprechung ist ein fehlender Schlüssel oder ein fehlendes Hash-Feld, und Befehle liefern in diesem Fall nil. Standardwertlogik (wie COALESCE) wird in der Anwendung mit einer nil-Prüfung umgesetzt. Die Existenz eines Felds wird mit HEXISTS geprüft.","danger":"safe"},"elasticsearch":{"command":"# Default assignment when a field is missing (missing bucket / null_value)\n# Find documents without the field in a query:\nGET customer/_search\n{ \"query\": { \"bool\": { \"must_not\": { \"exists\": { \"field\": \"phone\" } } } } }\n# Map empty values to a specific value with null_value in the mapping","note":"In Elasticsearch werden null- oder fehlende Felder mit der 'exists'-Abfrage geprüft (must_not exists = Dokumente ohne das Feld). Für einen COALESCE-ähnlichen Standardwert definiere 'null_value' im Mapping oder weise fehlenden Einträgen in Aggregationen mit dem Parameter 'missing' einen Wert zu. Standardwerte lassen sich auch mit Runtime Fields/Skripten erzeugen.","danger":"safe"}}}]},"rosetta/overlay-elasticsearch.json":{"engineId":"elasticsearch","byConcept":{"active-sessions":{"command":"GET _tasks?detailed=true&actions=*search*\n# To see node pressure: GET _nodes/hot_threads","note":"Die Task API liefert aktuell laufende Tasks (Suche, Reindex, Bulk). Für die momentane CPU-Last zeigt hot_threads die aktivsten Threads.","danger":"safe"},"blocking-sessions":{"command":"# Not applicable: Elasticsearch has no concept of row/document locks or blocking.\n# Closest diagnosis: find long-running tasks\nGET _tasks?detailed=true","note":"Statt Blockierungen sind meist lang laufende Tasks (große Suchen, Reindex) die Ursache für Langsamkeit. Bewerte die Dauer anhand des Felds running_time_in_nanos.","danger":"safe"},"kill-session":{"command":"POST _tasks/<task_id>/_cancel\n# task_id format: nodeId:taskNumber (e.g. oTUltX4IQMOUUVeiohTt8A:12345)","note":"Entnimm den Wert task_id der Ausgabe von GET _tasks. Nur abbrechbare Tasks (cancellable:true) lassen sich stoppen.","danger":"danger"},"connection-count":{"command":"GET _nodes/stats/http","note":"current_open liefert die aktuell offenen HTTP-Verbindungen, total_opened die Gesamtzahl der Verbindungen seit dem Start des Knotens. Die Obergrenze für HTTP-Verbindungen wird durch Betriebssystem-/Socket-Limits bestimmt, nicht durch http.max_content_length.","danger":"safe"},"database-size":{"command":"GET _cluster/stats\n# For a simple summary: GET _cat/indices?v&h=index,store.size","note":"_cluster/stats liefert über indices.store.size_in_bytes die gesamte Plattennutzung des Clusters. _cat/indices listet die Größe pro Index in lesbarer Form.","danger":"safe"},"largest-tables":{"command":"GET _cat/indices?v&s=store.size:desc&h=index,docs.count,store.size","note":"In Elasticsearch entspricht ein Index einer \"Tabelle\". s=store.size:desc sortiert die größten Indizes nach oben.","danger":"safe"},"free-space":{"command":"GET _cat/allocation?v","note":"Die Spalten disk.used, disk.avail und disk.percent liefern die Plattennutzung pro Knoten. Werden die Watermark-Schwellen (85%/90%/95%) überschritten, stoppt die Shard-Allokation.","danger":"safe"},"row-count":{"command":"GET <index>/_count\n# Shortcut: GET _cat/count/<index>?v","note":"_count liefert die Dokumentanzahl schnell. Für eine gefilterte Zählung übergib eine Abfrage im Body: GET <index>/_count {\"query\":{\"term\":{\"status\":\"active\"}}}","danger":"safe"},"top-slow-queries":{"command":"PUT <index>/_settings\n{\n  \"index.search.slowlog.threshold.query.warn\": \"1s\",\n  \"index.search.slowlog.threshold.query.info\": \"500ms\",\n  \"index.search.slowlog.threshold.fetch.warn\": \"1s\"\n}\n# Queries exceeding the threshold are written to the <cluster>_index_search_slowlog.json log file","note":"Elasticsearch hat keine zentrale Tabelle für langsame Abfragen; das Search Slowlog schreibt Abfragen über dem Schwellenwert in die Logdateien der Knoten. Lies die Logs dort.","danger":"caution"},"explain-plan":{"command":"GET <index>/_search\n{\n  \"profile\": true,\n  \"query\": { \"match\": { \"title\": \"test\" } }\n}\n# To check query validity/parsing:\n# GET <index>/_validate/query?explain=true","note":"profile:true schlüsselt die Zeit jeder Abfragekomponente und jedes Aggregationsschritts auf. _validate/query?explain zeigt die Lucene-Entsprechung der Abfrage und den Grund einer Ablehnung.","danger":"safe"},"unused-indexes":{"command":"# Not applicable: secondary indexes in ES differ from relational databases.\n# Closest: assess rarely used/unaccessed indices by size and search count\nGET _cat/indices?v&h=index,docs.count,store.size\nGET <index>/_stats/search","note":"Statt ungenutzter Feldindizes identifizierst du über _stats/search Indizes mit geringer Suchanzahl (search.query_total) oder ohne Abfragen mehr und schließt/löschst sie.","danger":"safe"},"cache-hit-ratio":{"command":"GET _nodes/stats/indices/query_cache\n# Ratio = hit_count / (hit_count + miss_count)","note":"query_cache cacht Ergebnisse im Filter-Kontext. Untersuche request_cache separat mit GET _nodes/stats/indices/request_cache.","danger":"safe"},"logical-dump":{"command":"# First define a repository (once): PUT _snapshot/<repo> {\"type\":\"fs\",\"settings\":{\"location\":\"/mnt/backups\"}}\nPUT _snapshot/<repo>/<snapshot_1>?wait_for_completion=true\n{\n  \"indices\": \"*\",\n  \"include_global_state\": true\n}","note":"In Elasticsearch entspricht die Snapshot API einem logischen Dump. wait_for_completion=true wartet, bis der Vorgang abgeschlossen ist; bei großen Snapshots auf false lassen und den Status separat abfragen.","danger":"safe"},"restore-dump":{"command":"POST _snapshot/<repo>/<snapshot_1>/_restore\n{\n  \"indices\": \"*\",\n  \"rename_pattern\": \"(.+)\",\n  \"rename_replacement\": \"restored_$1\"\n}","note":"Um einen vorhandenen Index wiederherzustellen, schließe ihn zuerst (POST <index>/_close) oder stelle ihn mit rename_pattern unter neuem Namen wieder her. Über einen geöffneten Index kann nicht wiederhergestellt werden.","danger":"danger"},"last-backup-status":{"command":"GET _snapshot/<repo>/_all\n# For in-progress/latest snapshot details:\n# GET _snapshot/<repo>/<snapshot_1>/_status","note":"Das Feld state nimmt die Werte SUCCESS / IN_PROGRESS / PARTIAL / FAILED an. _status liefert den detaillierten Fortschritt pro Shard.","danger":"safe"},"list-users":{"command":"GET _security/user","note":"X-Pack Security muss aktiviert sein. Für einen einzelnen Benutzer: GET _security/user/<user>. Auch integrierte Benutzer erscheinen in der Liste.","danger":"safe"},"create-user-grant":{"command":"# 1) Create a role\nPOST _security/role/<role>\n{\n  \"cluster\": [\"monitor\"],\n  \"indices\": [ { \"names\": [\"log-*\"], \"privileges\": [\"read\"] } ]\n}\n# 2) Create the user and assign the role\nPOST _security/user/<user>\n{\n  \"password\": \"StrongPassword123\",\n  \"roles\": [\"<role>\"]\n}","note":"Die Autorisierung ist rollenbasiert: Definiere zuerst die Rolle mit Index-/Cluster-Privilegien und weise sie dann dem Benutzer zu. Das Passwort muss mindestens 6 Zeichen lang sein.","danger":"caution"},"show-privileges":{"command":"GET _security/user/<user>\nGET _security/role/<role>\n# Check whether a specific privilege is held:\n# POST _security/user/<user>/_has_privileges { \"index\": [ { \"names\": [\"log-*\"], \"privileges\": [\"read\"] } ] }","note":"Die Rollen eines Benutzers siehst du mit _security/user, die Privilegien einer Rolle mit _security/role. _has_privileges prüft, ob eine bestimmte Aktion erlaubt ist.","danger":"safe"},"change-password":{"command":"POST _security/user/<user>/_password\n{\n  \"password\": \"NewStrongPassword123\"\n}","note":"Um dein eigenes Passwort zu ändern, genügt POST _security/user/_password (ohne Benutzernamen). Für den integrierten Benutzer elastic lässt sich auch das Tool bin/elasticsearch-reset-password nutzen.","danger":"caution"},"update-statistics":{"command":"# Not applicable: Elasticsearch has no cost-based optimizer or table statistics gathering.\n# Closest: make newly written documents searchable\nPOST <index>/_refresh","note":"Lucene ist segmentbasiert; ein separater Schritt zur Statistikaktualisierung ist nicht nötig. _refresh macht noch nicht durchsuchbare Dokumente sofort durchsuchbar (standardmäßig geschieht das automatisch alle 1s).","danger":"safe"},"rebuild-index":{"command":"POST _reindex\n{\n  \"source\": { \"index\": \"old_index\" },\n  \"dest\":   { \"index\": \"new_index\" }\n}\n# For large data: run in the background with POST _reindex?wait_for_completion=false","note":"Reindex wird für Mapping-Änderungen oder zum Neuaufbau eines defekten Index genutzt. Für eine unterbrechungsfreie Umstellung richte einen Alias auf den neuen Index.","danger":"caution"},"reclaim-space":{"command":"POST <index>/_forcemerge?only_expunge_deletes=true\n# Full merge on read-only/old indices: POST <index>/_forcemerge?max_num_segments=1","note":"Gelöschte/aktualisierte Dokumente bleiben als Tombstones in den Segmenten; forcemerge gibt diesen Platz frei. Auf aktiv beschriebenen Indizes ist das teuer; führe es außerhalb der Spitzenzeiten aus.","danger":"caution"},"version-uptime":{"command":"GET /\n# Uptime (JVM uptime):\n# GET _nodes/stats/jvm\n# Short summary: GET _cat/nodes?v&h=name,uptime,version","note":"Der Root-Endpunkt GET / liefert Version, Lucene-Version und Clusternamen. jvm.uptime_in_millis ist die Laufzeit des Knotens.","danger":"safe"},"show-config-param":{"command":"GET _cluster/settings?include_defaults=true&flat_settings=true","note":"include_defaults=true zeigt auch die Standardwerte. Persistente und transiente Einstellungen werden in separaten Abschnitten geliefert; für Einstellungen auf Knotenebene nutze GET _nodes/settings.","danger":"safe"},"list-tables":{"command":"GET _cat/indices?v","note":"In Elasticsearch entspricht ein Index einer relationalen Tabelle. Liefert eine Übersicht mit den Spalten health, status, docs.count und store.size. Die Standardausgabe listet offene und geschlossene Indizes, aber keine versteckten (z. B. .ds-Backing-Indizes, Systemindizes); ergänze &expand_wildcards=all, um sie einzuschließen, oder &expand_wildcards=open für nur offene Indizes.","danger":"safe"},"describe-table":{"command":"GET <index>/_mapping","note":"Das Mapping liefert Feldtypen und Analyse-Einstellungen; es entspricht DESCRIBE TABLE. Für ein einzelnes Feld: GET <index>/_mapping/field/<field>.","danger":"safe"},"list-indexes":{"command":"GET <index>/_mapping","note":"In ES werden Felder standardmäßig indexiert; eine separate Sekundärindex-Tabelle gibt es nicht. Felder mit \"index\": false im Mapping sind nicht durchsuchbar. Für Indizes (Tabellen) im gesamten Cluster: GET _cat/indices?v.","danger":"safe"},"create-index":{"command":"PUT <index>\n{\n  \"settings\": { \"number_of_shards\": 1, \"number_of_replicas\": 1 },\n  \"mappings\": {\n    \"properties\": {\n      \"title\": { \"type\": \"text\" },\n      \"created_at\": { \"type\": \"date\" }\n    }\n  }\n}\n# To add fields later: PUT <index>/_mapping {\"properties\":{...}}","note":"Nach dem Anlegen eines Index lässt sich die Anzahl der Primary Shards nicht direkt ändern (die Replica-Anzahl ist dynamisch); erzeuge bei Bedarf mit den APIs _split / _shrink oder mit _reindex einen neuen Index. Der Typ eines vorhandenen Felds lässt sich nicht ändern; neue Felder können hinzugefügt werden.","danger":"caution"},"show-ddl":{"command":"GET <index>","note":"Ein einziger Aufruf liefert Settings, Mappings und Aliase; zusammen entsprechen diese drei der \"DDL\" des Index. Dieselbe Ausgabe lässt sich als Body eines neuen PUT <index> wiederverwenden.","danger":"safe"},"replication-status":{"command":"GET _cluster/health\n# For ongoing shard copy/recovery: GET _cat/recovery?v&active_only=true\n# For cross-cluster replication (CCR): GET <index>/_ccr/stats","note":"status fasst den Shard-Zustand als green/yellow/red zusammen; yellow = Replica nicht zugewiesen. active_shards_percent liefert den Fertigstellungsgrad.","danger":"safe"},"list-replicas":{"command":"GET _cat/shards?v&h=index,shard,prirep,state,node\n# For node roles: GET _cat/nodes?v","note":"Die Spalte prirep nimmt den Wert p (primary) oder r (replica) an; sie zeigt, wie die Shards auf die Knoten verteilt sind, und ihren Zustand (STARTED/RELOCATING).","danger":"safe"},"promote-failover":{"command":"# Master election is automatic; no manual promote is needed.\n# To convert a CCR follower into an independent (writable) index:\nPOST <index>/_ccr/pause_follow\nPOST <index>/_close\nPOST <index>/_ccr/unfollow\nPOST <index>/_open","note":"Bei Ausfall eines Knotens wählt Elasticsearch den Master automatisch neu und weist Primary Shards neu zu. Eine manuelle Übernahme ist nur nötig, um einen CCR-Follower-Index von seinem Leader zu lösen und beschreibbar zu machen.","danger":"danger"},"open-transactions":{"command":"# Not applicable: Elasticsearch has no multi-document transactions (BEGIN/COMMIT).\n# Operations are atomic at the document level; the closest diagnosis is running tasks:\nGET _tasks?detailed=true","note":"Ein Schreibvorgang auf ein einzelnes Dokument ist atomar und versioniert (Optimistic Concurrency: if_seq_no / if_primary_term). Ein Konzept zur Verfolgung offener Transaktionen gibt es nicht.","danger":"safe"},"connect-cli":{"command":"curl -u user:password -k https://host:9200\n# Kibana Dev Tools console (recommended): Kibana > Management > Dev Tools\n# With an API key: curl -H \"Authorization: ApiKey <base64key>\" https://host:9200","note":"Es ist REST-basiert; statt eines separaten CLI-Clients werden curl oder Kibana Dev Tools genutzt. -k überspringt die Prüfung selbstsignierter TLS-Zertifikate (in Produktion eine CA verwenden).","danger":"safe"},"list-databases":{"command":"GET _cat/indices?v\n# For aliases (logical views): GET _aliases","note":"Elasticsearch hat keine separate \"Datenbank\"-Ebene; am nächsten kommen Indizes und die Aliase, die sie gruppieren. Für Data Streams: GET _data_stream.","danger":"safe"},"switch-database":{"command":"# Not applicable: the target index is specified directly in the request path; there is no session context to change.\nGET <index>/_search\n# In Kibana, a data view (index pattern) is selected as the default view","note":"Es gibt keinen Befehl im Stil von USE <db>; der Zielindex/-alias wird im Pfad jeder Anfrage angegeben. Um mehrere Indizes zu durchsuchen, trenne sie mit Kommas: GET log-2024,log-2025/_search.","danger":"safe"}}},"errors/oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00001","title":"Unique constraint violated (Unique Constraint verletzt)","severity":"caution","meaning":"Eine einzufügende oder zu aktualisierende Zeile verletzt einen UNIQUE- oder PRIMARY-KEY-Constraint. Der gleiche Wert ist also bereits vorhanden.","cause":"Erneutes Einfügen einer Zeile mit demselben Schlüsselwert, doppelte Daten in einer Spalte, die eindeutig sein muss, oder doppelte Verarbeitung desselben Datensatzes bei Replikation/Wiederholungsläufen.","fix":"Ermittle die verletzte Spalte über den Constraint-Namen in der Fehlermeldung: SELECT column_name FROM all_cons_columns WHERE constraint_name = 'CONSTRAINT_NAME'. Finde doppelte Daten mit GROUP BY ... HAVING COUNT(*) > 1. Verwende in der Anwendung für vorhandene Datensätze UPDATE oder MERGE statt INSERT. Ist der Sequence-Wert zurückgefallen, setze ihn mit ALTER SEQUENCE vor.","tags":["unique","uniqueness","constraint","unique constraint","primary key","duplicate","duplicate key","insert"]},{"code":"ORA-00054","title":"Resource busy and acquire with NOWAIT specified or timeout expired (Ressource belegt; NOWAIT angegeben oder Timeout abgelaufen)","severity":"caution","meaning":"Die zu sperrende Ressource (Zeile oder Tabelle) ist von einer anderen Session gesperrt und konnte nicht sofort angefordert werden, weil NOWAIT angegeben war oder DDL_LOCK_TIMEOUT abgelaufen ist.","cause":"Meist eine DDL (ALTER TABLE, DROP, CREATE INDEX), während die Tabelle von einer anderen Transaktion gesperrt ist; oder eine gesperrte Zeile bei SELECT ... FOR UPDATE NOWAIT.","fix":"Finde die Session, die die Sperre hält: SELECT * FROM v$locked_object. Verknüpfe v$session und v$lock, um die blockierende Session zu ermitteln. Warte, bis die Transaktion endet, oder beende sie bei Bedarf mit ALTER SYSTEM KILL SESSION. Für DDL kannst du eine Wartezeit festlegen: ALTER SESSION SET DDL_LOCK_TIMEOUT = 60; Führe DDL in ruhigen Wartungsfenstern aus.","tags":["lock","locked","ddl","nowait","busy","resource busy","timeout"]},{"code":"ORA-00060","title":"Deadlock detected while waiting for resource (Deadlock beim Warten auf Ressource erkannt)","severity":"danger","meaning":"Zwei oder mehr Sessions blockieren sich gegenseitig; jede wartet auf eine Ressource, die die andere hält. Oracle erkennt den Zyklus, wählt ein Opfer und setzt dessen Statement zurück.","cause":"Zeilensperren in unterschiedlicher Reihenfolge, dieselben Zeilen über Kreuz von verschiedenen Transaktionen aktualisiert, Tabellensperren bei Updates der Elterntabelle durch nicht indizierte Foreign Keys oder parallele DML auf einem Bitmap-Index.","fix":"Untersuche die im Alert Log referenzierte Deadlock-Tracedatei (Graph), um die beteiligten SQL-Statements und Zeilen zu finden. Greife in der Anwendung immer in derselben Reihenfolge auf Tabellen/Zeilen zu. Indiziere Foreign-Key-Spalten (nicht indizierte FKs sind eine klassische Deadlock-Ursache). Halte Transaktionen kurz und prüfe die Commit-Häufigkeit. Fange die Deadlock-Transaktion in der Anwendung ab und wiederhole sie.","tags":["deadlock","deadlock detected","lock","locking","tx","transaction"]},{"code":"ORA-01017","title":"Invalid username/password; logon denied (ungültiger Benutzername/Passwort; Anmeldung verweigert)","severity":"caution","meaning":"Die Anmeldung wurde verweigert, weil der beim Verbinden angegebene Benutzername oder das Passwort falsch ist.","cause":"Falsches Passwort, falscher Benutzername, eine abweichende Einstellung für Groß-/Kleinschreibung bei Passwörtern (SEC_CASE_SENSITIVE_LOGON), ein geändertes Passwort oder veraltete Zugangsdaten im Connection String der Anwendung.","fix":"Prüfe Benutzername und Passwort. Setze das Passwort bei Bedarf als DBA zurück: ALTER USER app_user IDENTIFIED BY new_password. Prüfe, ob der Benutzer existiert und nicht gesperrt ist: SELECT username, account_status FROM dba_users. Mit Passwort-Hash-Versionen ab 12c können 11g-Clients Verbindungsprobleme haben; prüfe die Einstellung SQLNET.ALLOWED_LOGON_VERSION_SERVER.","tags":["auth","credentials","password","password","logon","login","privilege"]},{"code":"ORA-01555","title":"Snapshot too old: rollback segment number too small (Snapshot zu alt: Rollback-Segment-Nummer zu klein)","severity":"danger","meaning":"Eine lang laufende Abfrage konnte nicht auf die alten Undo-Daten zugreifen, die zum Aufbau des lesekonsistenten Abbilds zu ihrem Startzeitpunkt nötig sind, weil diese Undo-Informationen überschrieben wurden.","cause":"Ein kleiner Undo-Tablespace, eine kurze UNDO_RETENTION, lang laufende Abfragen bei sehr hoher paralleler DML-Last oder eine Schleife, die innerhalb einer Abfrage auf derselben Tabelle committet (Fetch across Commit).","fix":"Erhöhe UNDO_RETENTION und vergrößere den Undo-Tablespace; überlasse ihn nach Möglichkeit der automatischen Verwaltung (AUTOEXTEND on). Teile lange Abfragen in kleinere Teile auf oder führe sie außerhalb der Spitzenzeiten aus. Erwäge RETENTION GUARANTEE: ALTER TABLESPACE undotbs1 RETENTION GUARANTEE. Korrigiere Anwendungslogik, die in einer Schleife committet.","tags":["undo","snapshot","rollback","read consistency","consistency","long query"]},{"code":"ORA-01652","title":"Unable to extend temp segment by N in tablespace (Temp-Segment kann im Tablespace nicht um N erweitert werden)","severity":"danger","meaning":"Oracle fand im Tablespace nicht genug freien Platz, um ein temporäres (Temp-)Segment zu erweitern. Tritt meist bei Sortierungen, Hash Joins oder Operationen auf globalen temporären Tabellen auf.","cause":"Der TEMP-Tablespace ist voll; große ORDER-BY-, GROUP-BY-, DISTINCT-, Hash-Join- oder Indexerstellungs-Operationen passen nicht in den PGA-Speicher und werden auf Disk ausgelagert; Datendateien haben AUTOEXTEND off oder der Plattenplatz geht aus.","fix":"Überwache die TEMP-Nutzung: SELECT * FROM v$sort_usage / v$tempseg_usage. Füge dem TEMP-Tablespace eine Datei hinzu oder vergrößere ihn: ALTER TABLESPACE temp ADD TEMPFILE '...' SIZE 2G AUTOEXTEND ON. Ist ein permanenter Tablespace voll, füge eine Datendatei hinzu. Reduziere Disk-Sortierungen durch Optimierung der Abfrage (passender Index, größere PGA_AGGREGATE_TARGET).","tags":["temp","tablespace","free space","space","sort","sorting","extend"]},{"code":"ORA-04031","title":"Unable to allocate N bytes of shared memory (N Bytes Shared Memory können nicht allokiert werden)","severity":"danger","meaning":"Oracle konnte in einem Shared-Memory-Pool (meist Shared Pool oder Large Pool) keinen zusammenhängenden Speicher der angeforderten Größe allokieren. Eine SGA-Komponente steht unter Speicherdruck.","cause":"Fragmentierung des Shared Pools durch viele eindeutige SQL-Statements ohne Bind-Variablen (voller Literale), zu kleine SHARED_POOL_SIZE, zu viele gleichzeitige Verbindungen, große PL/SQL-Packages oder falsche ASMM/AMM-Dimensionierung.","fix":"Prüfe, auf welchen Pool sich die Fehlermeldung bezieht (Shared Pool, Large Pool usw.). Kurzfristig: ALTER SYSTEM FLUSH SHARED_POOL (Vorsicht: in Produktion erzwingt dies ein erneutes Parsen aller SQL-Statements, also einen vorübergehenden Hard-Parse-Sturm, und behebt die Fragmentierung nicht dauerhaft). Dauerhafte Lösung: Nutzung von Bind-Variablen durchsetzen (inklusive CURSOR_SHARING), SGA_TARGET / SHARED_POOL_SIZE / LARGE_POOL_SIZE erhöhen. Pinne häufig genutzte Packages mit DBMS_SHARED_POOL.KEEP im Speicher. Untersuche die Speicherverteilung mit V$SGASTAT.","tags":["memory","shared memory","shared pool","sga","ora-4031","bind"]},{"code":"ORA-12154","title":"TNS: could not resolve the connect identifier specified (angegebener Connect Identifier nicht auflösbar)","severity":"caution","meaning":"Der in der Verbindung verwendete Servicename (Connect Identifier / TNS-Alias) konnte nicht aufgelöst werden. Oracle Net kann den angegebenen Namen keiner Server-/Service-Information zuordnen.","cause":"Der Alias fehlt in tnsnames.ora, die Umgebungsvariable TNS_ADMIN zeigt auf das falsche Verzeichnis, der Aliasname ist falsch geschrieben, die EZConnect/LDAP-Namenskonfiguration fehlt oder die Reihenfolge von NAMES.DIRECTORY_PATH in sqlnet.ora ist falsch.","fix":"Prüfe, ob der verwendete Alias in tnsnames.ora definiert ist. Teste die Verbindung: tnsping service_name. Stelle sicher, dass TNS_ADMIN auf den richtigen Ordner zeigt. Alternativ probiere EZConnect: app_user/password@host:1521/service_name. Prüfe die Zeile NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) in sqlnet.ora.","tags":["tns","connect","connection","network","networking","tnsnames","service"]},{"code":"ORA-12514","title":"TNS: listener does not currently know of service requested in connect descriptor (Listener kennt den angeforderten Service derzeit nicht)","severity":"caution","meaning":"Der Listener läuft, erkennt aber den angeforderten Servicenamen nicht; der Service ist also nicht beim Listener registriert.","cause":"Die Datenbankinstanz ist nicht vollständig geöffnet, der Service hat sich noch nicht beim Listener registriert (LREG ab 12c, PMON-Registrierungsverzögerung in 11g und früher), der falsche SERVICE_NAME wird verwendet oder SID und Servicename werden verwechselt.","fix":"Zeige die dem Listener bekannten Services an: lsnrctl status und lsnrctl services. Prüfe, ob die Datenbank OPEN ist. Löse die Service-Registrierung aus: ALTER SYSTEM REGISTER in SQL. Prüfe den Parameter LOCAL_LISTENER und die korrekte Verwendung von SERVICE_NAME. Stelle bei einer Pluggable Database sicher, dass die PDB OPEN ist.","tags":["tns","listener","service","service_name","connection","network","networking"]},{"code":"ORA-00942","title":"Table or view does not exist (Tabelle oder View existiert nicht)","severity":"caution","meaning":"Die in der Abfrage referenzierte Tabelle oder View existiert nicht oder ist für den zugreifenden Benutzer nicht sichtbar.","cause":"Das Objekt existiert tatsächlich nicht, es liegt in einem anderen Schema und es wurde kein Schemapräfix angegeben, dem Benutzer fehlen Berechtigungen auf dem Objekt (z. B. SELECT), ein Synonym fehlt oder der Objektname ist falsch geschrieben.","fix":"Prüfe, ob das Objekt existiert und wem es gehört: SELECT owner, object_type FROM all_objects WHERE object_name = 'TABLE_NAME'. Liegt es in einem anderen Schema, qualifiziere es vollständig als schema.table oder vergib GRANT SELECT ON app_schema.my_table TO app_user. Lege bei Bedarf ein Public/Private-Synonym an: CREATE SYNONYM my_table FOR app_schema.my_table. Achte auf Groß-/Kleinschreibung und Identifier in Anführungszeichen.","tags":["table","missing table","view","missing view","grant","privilege","synonym","alias"]},{"code":"ORA-01031","title":"Insufficient privileges (unzureichende Berechtigungen)","severity":"caution","meaning":"Dem Benutzer fehlt die für die versuchte Operation erforderliche Berechtigung (System- oder Objektberechtigung).","cause":"Die erforderliche System- oder Objektberechtigung wurde nicht vergeben, die Berechtigung kommt nur über eine Rolle (Rollen wirken nicht in PL/SQL-Blöcken mit Definer Rights), eine administrative Operation wurde ohne SYSDBA/SYSOPER versucht oder für Prozeduren mit Definer Rights fehlt ein direkter Grant.","fix":"Vergib die fehlende Berechtigung, zum Beispiel: GRANT CREATE TABLE TO app_user oder GRANT SELECT ON app_schema.my_table TO app_user. Beachte, dass für benannte PL/SQL-Objekte Berechtigungen direkt und nicht über eine Rolle vergeben werden müssen. Für Operationen, die SYSDBA erfordern, verbinde dich mit den richtigen Zugangsdaten. Prüfe die aktuellen Berechtigungen: SELECT * FROM session_privs / user_sys_privs.","tags":["privilege","privileges","grant","permission","auth","role grant","role"]},{"code":"ORA-00257","title":"Archiver error. Connect internal only, until freed (Archiver-Fehler; bis zur Freigabe nur interne Verbindungen)","severity":"danger","meaning":"Der Archiver-Prozess (ARCH) kann die Redo-Logs nicht archivieren; meist ist das Archivziel (FRA oder Archivlog-Ziel) voll. Die Datenbank hält neue Transaktionen an.","cause":"Die Flash Recovery Area (DB_RECOVERY_FILE_DEST) oder das Archivlog-Ziel ist voll, der Plattenplatz ist erschöpft, RMAN-Backups löschen die Archivlogs nicht oder das Limit DB_RECOVERY_FILE_DEST_SIZE ist erreicht.","fix":"Schaffe Platz: Sichere alte Archivlogs mit RMAN auf ein Ziel außerhalb der FRA und lösche sie: RMAN> BACKUP ARCHIVELOG ALL FORMAT '/backup/arc_%U' DELETE INPUT (ein Backup in die volle FRA schlägt fehl). Lösche bei Data Guard keine Archive, die noch nicht zum Standby übertragen oder dort angewendet wurden; sonst entsteht auf dem Standby eine Lücke (CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY). Zeige die FRA-Nutzung an: SELECT * FROM v$recovery_file_dest / v$flash_recovery_area_usage. Erhöhe DB_RECOVERY_FILE_DEST_SIZE vorübergehend oder verlege das Ziel auf eine größere Platte. Prüfe den freien Platz auf Plattenebene. Führe eine regelmäßige Bereinigungsrichtlinie für Archive ein.","tags":["archive","archivelog","archiver","fra","disk","space","redo"]},{"code":"ORA-01000","title":"Maximum open cursors exceeded (maximale Anzahl offener Cursor überschritten)","severity":"caution","meaning":"Eine Session hat mehr Cursor geöffnet, als der Parameter OPEN_CURSORS zulässt. Es kann kein neuer Cursor geöffnet werden.","cause":"Die Anwendung schließt geöffnete Statement-/ResultSet-Objekte nicht (Cursor-Leak), OPEN_CURSORS ist für die Last zu niedrig gesetzt oder in einer Schleife werden ständig neue Prepared Statements vorbereitet und nie geschlossen.","fix":"Überwache offene Cursor: SELECT * FROM v$open_cursor WHERE sid = ...  und sieh dir die Anzahl pro Session über v$sesstat an. Die eigentliche Lösung ist, jedes Statement/ResultSet in der Anwendung in einem finally-Block zu schließen (Beseitigung des Cursor-Leaks). Als Workaround erhöhe OPEN_CURSORS: ALTER SYSTEM SET OPEN_CURSORS = 1000. Prüfe die Größe des JDBC-Statement-Cache.","tags":["cursor","open cursor","open_cursors","leak","cursor leak","jdbc"]},{"code":"ORA-00018","title":"Maximum number of sessions exceeded (maximale Anzahl von Sessions überschritten)","severity":"danger","meaning":"Die Datenbank hat die durch den Parameter SESSIONS erlaubte maximale Anzahl von Sessions erreicht; es kann keine neue Session geöffnet werden.","cause":"SESSIONS/PROCESSES unter dem Bedarf der Last gesetzt, eine überdimensionierte Connection-Pool-Konfiguration, ein Connection-Leak oder eine Ansammlung nie geschlossener Sessions.","fix":"Zeige die aktuelle Anzahl der Sessions an: SELECT COUNT(*) FROM v$session. Prüfe die Limits: SELECT * FROM v$resource_limit WHERE resource_name IN ('sessions','processes'). Erhöhe PROCESSES und damit indirekt SESSIONS (über SPFILE, Neustart erforderlich): ALTER SYSTEM SET PROCESSES = 500 SCOPE=SPFILE. Prüfe Connection-Pool-Größen und Leaks in der Anwendung.","tags":["session","session limit","sessions","processes","connection","connections","limit"]},{"code":"ORA-28000","title":"The account is locked (das Konto ist gesperrt)","severity":"caution","meaning":"Die Anmeldung schlägt fehl, weil das Datenbankbenutzerkonto, mit dem verbunden wird, gesperrt ist.","cause":"Das Limit FAILED_LOGIN_ATTEMPTS im Profil wurde überschritten (wiederholt falsche Passwörter) oder das Konto wurde von einem DBA manuell gesperrt (ALTER USER ... ACCOUNT LOCK). Eine Sperre durch fehlgeschlagene Anmeldungen wird nach PASSWORD_LOCK_TIME automatisch aufgehoben. Hinweis: Der Ablauf von PASSWORD_LIFE_TIME sperrt das Konto nicht; das Konto wechselt zu EXPIRED / EXPIRED(GRACE) und liefert ORA-28001/ORA-28002.","fix":"Prüfe den Kontostatus: SELECT username, account_status, lock_date FROM dba_users WHERE username = 'APP_USER'. Entsperre es: ALTER USER app_user ACCOUNT UNLOCK. Um eine Wiederholung zu vermeiden, finde die Quelle der fehlgeschlagenen Anmeldungen und prüfe bei Bedarf FAILED_LOGIN_ATTEMPTS im Profil. Ist das Passwort abgelaufen, vergib ein neues Passwort.","tags":["account","user account","locked","account locked","auth","password","profile"]},{"code":"ORA-01578","title":"Oracle data block corrupted (file #, block #) (Oracle-Datenblock beschädigt)","severity":"danger","meaning":"Ein Datenblock in der angegebenen Datendatei ist beschädigt; Oracle meldet, dass der Block die Konsistenzprüfung (Checksumme) nicht bestanden hat. Es handelt sich um eine physische Korruption.","cause":"Platten-/Hardwarefehler, Fehler in der Storage-Schicht, Probleme mit Betriebssystem oder Controller, teilweise geschriebene Blöcke nach einem Stromausfall oder ein beschädigtes Backup/Restore.","fix":"Bestätige die Korruption und ihr Ausmaß: RMAN> VALIDATE DATABASE / VALIDATE CHECK LOGICAL DATAFILE n; führe außerdem DBVERIFY (dbv) aus und prüfe v$database_block_corruption. Ist ein Backup vorhanden, stelle auf Blockebene wieder her: RMAN> RECOVER DATAFILE n BLOCK m; oder für alle gelisteten Blöcke RMAN> RECOVER CORRUPTION LIST; (der ältere Befehl BLOCKRECOVER ist seit 11g veraltet). Handelt es sich um einen Indexblock, baue den Index neu auf. Prüfe immer den Zustand von Hardware/Platten; die Grundursache liegt meist im Storage.","tags":["corruption","block corruption","block","corrupt block","datafile","rman","disk"]},{"code":"ORA-01722","title":"Invalid number (ungültige Zahl)","severity":"safe","meaning":"Eine Zeichenkette, die nicht in eine Zahl umgewandelt werden kann, wurde in einem numerischen Kontext verwendet. Eine implizite oder explizite Konvertierung ist fehlgeschlagen.","cause":"Vergleich einer numerischen Spalte mit Text (z. B. WHERE num_col = 'ABC'), Einfügen eines Werts mit Buchstaben in ein numerisches Feld, ein ungeeignetes Format für TO_NUMBER oder ein falsches Dezimal-/Gruppentrennzeichen (Komma vs. Punkt).","fix":"Finde den Ausdruck mit der fehlschlagenden Konvertierung; meist eine WHERE-Bedingung oder ein INSERT-Wert. Stelle bei Text-/Zahlenvergleichen Typkompatibilität sicher und verwende bei Bedarf explizite Konvertierung: TO_NUMBER(my_column) oder TO_CHAR für die Textseite. Verhindere nicht-numerische Eingaben in numerische Spalten auf Anwendungsebene. Achte auf die Einstellung NLS_NUMERIC_CHARACTERS.","tags":["number","numeric","conversion","type conversion","to_number","datatype","data type"]},{"code":"ORA-02049","title":"Timeout: distributed transaction waiting for lock (Timeout: verteilte Transaktion wartet auf Sperre)","severity":"danger","meaning":"Eine verteilte Transaktion hat beim Warten auf eine Sperre in der entfernten Datenbank DISTRIBUTED_LOCK_TIMEOUT überschritten. Die Transaktion ist in einen Timeout gelaufen.","cause":"Eine über einen Database Link laufende Transaktion wartet auf der entfernten Seite auf eine gesperrte Zeile; lange Transaktionen in der entfernten Session, Netzwerklatenz oder auf beiden Seiten gehaltene, kollidierende Sperren.","fix":"Finde und löse die blockierende Sperre und Session in der entfernten Datenbank (v$lock, v$session). Halte verteilte Transaktionen kurz und fordere die nötigen Sperren so spät wie möglich an. Erhöhe bei Bedarf DISTRIBUTED_LOCK_TIMEOUT für die Last (SPFILE, Neustart erforderlich). Untersuche bei In-Doubt-Transaktionen die View DBA_2PC_PENDING und löse sie bei Bedarf manuell auf.","tags":["distributed","distributed transaction","dblink","lock","lock wait","timeout","2pc"]},{"code":"enq: TX - row lock contention","title":"Wait: Session durch Zeilensperre einer anderen Transaktion blockiert","severity":"caution","meaning":"Eine Session wartet darauf, dass eine von einer anderen Transaktion gehaltene Zeilensperre freigegeben wird. Das ist das klassische Lock-Wait-Event auf Anwendungsebene.","cause":"Parallele Transaktionen, die dieselbe Zeile aktualisieren wollen, lange nicht committete Transaktionen, breite Sperren durch nicht indizierte Foreign Keys oder Konzentration auf einen sequenziellen Schlüssel (Hot Block).","fix":"Finde die blockierenden und blockierten Sessions: SELECT * FROM v$session WHERE blocking_session IS NOT NULL; und gleiche sie mit v$lock ab. Sind lange Transaktionen die Grundursache, überprüfe die Commit-Strategie. Indiziere Foreign-Key-Spalten. Überdenke das Design (Sequence, Hash) für Hot Rows. Beende die blockierende Session, wenn nötig.","tags":["wait","wait event","enq","row lock","row lock wait","tx","contention","lock"]},{"code":"log file sync","title":"Wait: Session wartet beim Commit, bis LGWR Redo auf Disk schreibt","severity":"caution","meaning":"Wenn eine Session COMMIT ausführt, wartet sie darauf, dass LGWR die Redo-Einträge in die Redo-Log-Dateien schreibt (flusht). Ist diese Wartezeit hoch, besteht Commit-Latenz.","cause":"Langsame Redo-Log-Platten (hohe I/O-Latenz), eine Anwendung, die zu häufig committet (Commit pro Zeile), CPU-Druck auf den LGWR-Prozess oder sehr hohes Redo-Volumen.","fix":"Miss die durchschnittliche Wartezeit mit AWR/Statspack. Verlege die Redo-Log-Dateien auf schnelle Platten mit niedriger Latenz (SSD). Reduziere unnötige Commit-Häufigkeit in der Anwendung und bündele Batch-Operationen. Bewerte bei Bedarf die Optionen COMMIT_LOGGING/COMMIT_WAIT (commit_write) gegen die fachlichen Anforderungen; WARNUNG: NOWAIT/BATCH verringern die Dauerhaftigkeit, und scheinbar committete Transaktionen können bei einem Absturz verloren gehen. Verwende sie nur auf Session- oder Statement-Ebene für Arbeiten, bei denen Verlust tolerierbar ist; aktiviere sie nicht systemweit. Untersuche LGWR- und Storage-Latenz gemeinsam.","tags":["wait","wait event","log file sync","commit","lgwr","redo","latency"]},{"code":"db file sequential read","title":"Wait: physischer Single-Block-Read, typisch bei Index- oder Tabellenzugriff","severity":"caution","meaning":"Eine Session wartet darauf, dass ein einzelner Block von Disk gelesen wird. Tritt meist bei Indexzugriffen oder Single-Block-Reads per ROWID auf; normalerweise ein erwartetes Event.","cause":"Blöcke, die nicht im Buffer Cache liegen und von Disk gelesen werden, nicht selektive oder falsche Indexnutzung, ein zu kleiner Buffer Cache oder eine langsame Storage-Schicht.","fix":"Ist der Wert hoch, finde die SQL-Statements mit den meisten Reads (AWR Top SQL, v$sql). Verbessere Abfragen mit passenden Indizes und korrekten Zugriffspfaden und reduziere unnötige Nested Loops. Bewerte die Größe des Buffer Cache (DB_CACHE_SIZE). Prüfe die I/O-Latenz des Storage. Dieses Event ist oft normal; ist es ungewöhnlich hoch, konzentriere dich auf SQL-Tuning.","tags":["wait","wait event","db file sequential read","io","disk io","index","index access","single block"]},{"code":"db file scattered read","title":"Wait: physischer Multi-Block-Read, typisch bei Full Table Scan oder Index Fast Full Scan","severity":"caution","meaning":"Eine Session wartet darauf, dass mehrere Blöcke (Multiblock) von Disk gelesen werden. Tritt meist bei einem Full Table Scan oder Index Fast Full Scan auf.","cause":"Full Table Scans wegen eines fehlenden passenden Index, ein falscher Plan wegen veralteter Statistiken, Scans sehr großer Tabellen oder ein Zugriffspfad, der auf einen Tabellenscan zurückfällt.","fix":"Ermittle die SQL-Statements mit Full Scans und lege die nötigen Indizes an oder sorge dafür, dass der vorhandene Index genutzt wird. Aktualisiere die Statistiken: DBMS_STATS.GATHER_TABLE_STATS. Ist ein großer Scan wirklich nötig, erwäge Parallel Query oder Partitionierung. Prüfe DB_FILE_MULTIBLOCK_READ_COUNT und den Storage-Durchsatz.","tags":["wait","wait event","db file scattered read","full scan","full table scan","io","disk io","multiblock"]},{"code":"buffer busy waits","title":"Wait: Konkurrenz um einen Buffer, den eine andere Session bereits liest oder ändert","severity":"caution","meaning":"Eine Session wartet auf Zugriff auf einen Buffer-Block, den eine andere Session gleichzeitig liest oder ändert. Das ist Blockkonkurrenz im Speicher.","cause":"Konzentration auf Hot Blocks, Konkurrenz am rechten Rand von Indizes mit monoton steigenden Schlüsseln, zu wenige Freelists/Segmente ohne ASSM oder starke parallele DML auf demselben Block.","fix":"Finde heraus, welches Segment und welche Blockklasse betroffen sind (v$session, v$segment_statistics, ASH). Erwäge für Hot-Index-Blöcke einen Reverse-Key- oder Hash-partitionierten Index. Verwende ASSM (Automatic Segment Space Management). Erhöhe bei hoher Parallelität INITRANS/PCTFREE. Verteile das Zugriffsmuster der Anwendung.","tags":["wait","wait event","buffer busy","hot block","hot blocks","contention","block contention"]},{"code":"gc buffer busy","title":"Wait (RAC): Konkurrenz um einen Cache-Fusion-Block, der zwischen Instanzen übertragen wird","severity":"caution","meaning":"In einer RAC-Umgebung wartet eine Session auf Zugriff auf einen globalen Block, der per Cache Fusion von/zu einer anderen Instanz übertragen wird oder von dieser Instanz belegt ist.","cause":"Dieselben Hot Blocks werden zwischen RAC-Knoten geteilt (instanzübergreifender Hot Block), Interconnect-Latenz/-Engpass, knotenübergreifende Konkurrenz auf Indizes mit sequenziellen Schlüsseln oder ungleichmäßige Lastverteilung.","fix":"Finde die betroffenen Segmente mit v$segment_statistics und GV$-Views. Prüfe Zustand und Latenz des Interconnects (privates, schnelles Netzwerk). Verwende Reverse Key oder Hash-Partitionierung für Hot Indexes. Verringere knotenübergreifende Konkurrenz um dieselben Blöcke, indem du die Last per Service auf die Knoten aufteilst.","tags":["wait","wait event","rac","gc buffer busy","cache fusion","interconnect","global"]},{"code":"library cache lock","title":"Wait: Konkurrenz um ein Library-Cache-Objekthandle (Parse-/DDL-Serialisierung)","severity":"caution","meaning":"Eine Session wartet auf eine Sperre auf einem Objekt im Library Cache (SQL, PL/SQL, Tabellendefinition). Meist verursacht durch die Serialisierung von Parse- und DDL-Operationen.","cause":"DDL (ALTER/CREATE) auf stark genutzten Objekten, häufig neu kompilierte PL/SQL-Objekte (Invalidierung), übermäßiges Hard Parsing, Invalidierung abhängiger Objekte oder starke Verbindungsstürme.","fix":"Finde die blockierende Session und das Objekt (v$session, ASH, bei Bedarf Library-Cache-Lock-Diagnose). Führe keine DDL auf stark genutzten Objekten zu Spitzenzeiten aus. Verkürze Abhängigkeitsketten und verhindere unnötige Neukompilierungen. Senke das Hard Parsing durch Bind-Variablen. Glätte Verbindungsstürme mit einem Connection Pool.","tags":["wait","wait event","library cache lock","parse","parsing","ddl","hard parse"]}]},"errors/postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"23505","title":"unique_violation","severity":"caution","meaning":"Die eingefügte oder aktualisierte Zeile verletzt einen UNIQUE-Constraint oder den PRIMARY KEY; der gleiche Wert ist in der Tabelle bereits vorhanden.","cause":"Erneutes Einfügen mit demselben eindeutigen Schlüssel, doppelte Daten in einer eindeutigen Spalte, ein durch manuelle Inserts zurückgefallener Sequence-Wert oder zwei parallele Requests, die denselben Schlüssel einfügen.","fix":"Constraint-Name und Schlüsselwert im Fehlerdetail zeigen, welche Zeile kollidiert. Um die vorhandene Zeile zu aktualisieren, verwende INSERT ... ON CONFLICT (my_column) DO UPDATE oder DO NOTHING. Duplikate finden: SELECT my_column, COUNT(*) FROM my_table GROUP BY my_column HAVING COUNT(*) > 1. Ist die Sequence zurückgefallen: SELECT setval('seq_name', (SELECT MAX(id) FROM my_table)).","tags":["unique","unique","constraint","constraint","duplicate","duplicate","23505","primary key"]},{"code":"23503","title":"foreign_key_violation","severity":"caution","meaning":"Ein FOREIGN-KEY-Constraint wurde verletzt. Die von der eingefügten/aktualisierten Zeile referenzierte Elternzeile existiert nicht, oder die zu löschende Elternzeile hat noch abhängige Kindzeilen.","cause":"Ein INSERT/UPDATE, das einen nicht existierenden Elternschlüssel referenziert, der Versuch, eine noch referenzierte Elternzeile zu löschen/zu ändern, oder Daten in falscher Reihenfolge geladen (zuerst Kind, dann Eltern).","fix":"Füge zuerst die Elternzeile ein, dann die Kindzeile. Wird beim Löschen kaskadierendes Verhalten benötigt, definiere den Constraint mit ON DELETE CASCADE oder ON DELETE SET NULL. Lade bei Massenladungen die Daten in Eltern-Kind-Reihenfolge. Finde die referenzierenden Zeilen: SELECT * FROM child WHERE fk_column = value.","tags":["foreign key","foreign key","fk","constraint","constraint","reference","23503"]},{"code":"23502","title":"not_null_violation","severity":"caution","meaning":"Es wurde versucht, NULL in eine Spalte mit NOT-NULL-Constraint einzufügen oder diese Spalte auf NULL zu setzen.","cause":"Eine Pflichtspalte fehlt im INSERT, NULL wird explizit zugewiesen, die Spalte hat keinen DEFAULT oder aus der Anwendung kommt ein leerer/fehlender Wert.","fix":"Liefere einen gültigen Wert für die Spalte oder definiere einen sinnvollen DEFAULT: ALTER TABLE my_table ALTER COLUMN my_column SET DEFAULT value. Muss die Spalte nicht wirklich Pflicht sein, entferne den Constraint: ALTER TABLE my_table ALTER COLUMN my_column DROP NOT NULL. Ergänze in der Anwendungsschicht eine Prüfung auf fehlende Daten.","tags":["not null","null","constraint","constraint","required","23502","insert"]},{"code":"40P01","title":"deadlock_detected","severity":"danger","meaning":"Zwei oder mehr Transaktionen sind in einen Deadlock geraten, weil jede auf Sperren wartet, die die andere hält. PostgreSQL erkennt den Zyklus und bricht eine der Transaktionen ab.","cause":"Sperren in unterschiedlicher Reihenfolge, Updates derselben Zeilen über Kreuz, lange Transaktionen oder kollidierende Sperren bei Foreign-Key-Prüfungen.","fix":"Das Deadlock-Detail im Serverlog zeigt die beteiligten Abfragen. Greife in allen Transaktionen in derselben Reihenfolge auf Tabellen/Zeilen zu. Halte Transaktionen kurz. Fange die abgebrochene Transaktion in der Anwendung ab und wiederhole sie. Verknüpfe pg_locks mit pg_stat_activity, um Sperren zu untersuchen. Lege die Sperrreihenfolge bei Bedarf mit SELECT ... FOR UPDATE explizit fest.","tags":["deadlock","deadlock","lock","lock","40P01","tx","transaction"]},{"code":"40001","title":"serialization_failure","severity":"caution","meaning":"Unter der Isolationsstufe SERIALIZABLE oder REPEATABLE READ konnte die Transaktion wegen eines Konflikts mit parallelen Änderungen nicht serialisiert werden und wurde abgebrochen. Das ist erwartetes Verhalten, das die Datenkonsistenz schützt.","cause":"Transaktionen, die gleichzeitig auf dieselben Daten zugreifen, lassen sich nicht in eine serialisierbare Reihenfolge bringen, Schreibkonflikte bei hoher Parallelität oder von SSI (Serializable Snapshot Isolation) erkannte gefährliche Abhängigkeiten.","fix":"Das ist kein Bug, sondern ein erwartetes Signal: Wiederhole die Transaktion unverändert. Baue eine automatische Wiederholungslogik in die Anwendung ein. Um Konflikte zu verringern, verkürze Transaktionen und verwende eine niedrigere Isolationsstufe (READ COMMITTED), wenn die höhere nicht wirklich nötig ist. Gestalte das Design so, dass Konkurrenz um Hot Rows sinkt.","tags":["serialization","serialization","isolation","isolation","retry","retry","40001","mvcc"]},{"code":"53300","title":"too_many_connections","severity":"danger","meaning":"Der Server hat die durch max_connections erlaubte maximale Anzahl gleichzeitiger Verbindungen erreicht; neue Verbindungen können nicht angenommen werden.","cause":"Kein oder ein falsch dimensionierter Connection Pool, Connection-Leaks, Anwendungen, die viele kurzlebige Verbindungen öffnen, oder max_connections unter dem Bedarf der Last.","fix":"Ermittle die Verbindungsquellen: SELECT usename, state, COUNT(*) FROM pg_stat_activity GROUP BY 1,2. Beende lange inaktive Client-Verbindungen gezielt (ungefiltertes Massenbeenden trifft jede Anwendung und jeden Pool): SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND backend_type = 'client backend' AND usename = 'app_user' AND state_change < now() - interval '10 minutes' AND pid <> pg_backend_pid(). Die eigentliche Lösung ist ein Connection Pool wie PgBouncer. Eine Erhöhung von max_connections kostet Speicher; bevorzuge Pooling. Für Superuser reservierte Verbindungen halten den administrativen Zugang verfügbar.","tags":["connection","connection","max_connections","too many","53300","pool","pool","pgbouncer"]},{"code":"53100","title":"disk_full","severity":"danger","meaning":"Der Server konnte nicht in eine Datei schreiben, weil die Platte voll ist. Auf der Platte mit dem Datenverzeichnis, dem WAL oder den temporären Dateien ist kein Platz mehr.","cause":"Datenwachstum, unkontrollierte WAL-Ansammlung (zurückfallende Archivierung oder ein zurückfallender Replication Slot), große temporäre Dateien (riesige Sortierungen), aufgeblähte Logdateien oder Tabellen-/Index-Bloat.","fix":"Untersuche die Plattenbelegung und räume unnötige Dateien auf. Prüfe die WAL-Ansammlung: pg_stat_replication und pg_replication_slots (entferne ungenutzte Slots; zurückliegende Slots halten WAL zurück). Führe bei voller Platte kein VACUUM FULL oder REINDEX aus: Da sie die Tabelle neu schreiben, brauchen sie zusätzlichen Platz in Tabellengröße und können die Lage verschlimmern; auch ein einfaches VACUUM gibt (fast) nie Platz an das Betriebssystem zurück. Lösche NIEMALS Dateien im Verzeichnis pg_wal von Hand. Schaffe zuerst Platz, indem du die Ursachen bei WAL/Slots, Logs und Temp-Dateien behebst; überprüfe die Ablage von Logs und Temp-Dateien. Erhöhe als dauerhafte Lösung die Plattenkapazität und richte Monitoring/Alerting ein.","tags":["disk","space","space","full","full","wal","53100","vacuum"]},{"code":"57014","title":"query_canceled","severity":"safe","meaning":"Die Abfrage wurde abgebrochen. Meist verursacht durch ein abgelaufenes statement_timeout oder einen Abbruch durch Benutzer oder Client (pg_cancel_backend, Abbruch durch den Treiber).","cause":"statement_timeout überschritten, ein Abbruch mit pg_cancel_backend oder ein von Anwendung/Client gesendeter Cancel-Request. Ähnliche Situationen haben andere Codes: Ablauf von lock_timeout liefert 55P03, idle_in_transaction_session_timeout 25P03 (FATAL) und ein Abbruch durch einen Replikationskonflikt auf einem Hot Standby 40001 (in manchen Fällen 40P01).","fix":"Dauert die Abfrage tatsächlich lange, optimiere sie (EXPLAIN ANALYZE, Indizes, Plan). Ist der Timeout zu niedrig, passe statement_timeout für die betreffende Session/Rolle an. Bewusste Abbrüche erfordern keine Maßnahme. Für Abbrüche durch Sperren und Standby-Konflikte siehe die Einträge 55P03 und 40001.","tags":["cancel","cancel","timeout","timeout","statement_timeout","57014","standby"]},{"code":"42P01","title":"undefined_table","severity":"caution","meaning":"Die in der Abfrage referenzierte Tabelle (oder View) existiert nicht oder ist im search_path nicht auffindbar.","cause":"Die Tabelle existiert tatsächlich nicht, sie liegt in einem anderen Schema, das nicht im search_path ist, ein Tippfehler/Fehler bei der Groß-/Kleinschreibung im Tabellennamen, eine abweichende Schreibweise bei Identifiern in Anführungszeichen oder das Objekt wurde noch nicht angelegt oder wurde gelöscht.","fix":"Prüfe, ob die Tabelle existiert und in welchem Schema: SELECT schemaname, tablename FROM pg_tables WHERE tablename = 'my_table'. Liegt sie in einem anderen Schema, qualifiziere sie als schema.table oder setze den search_path: SET search_path TO app_schema, public. Beachte, dass Identifier ohne Anführungszeichen in Kleinbuchstaben umgewandelt werden; wurden bei der Erstellung Anführungszeichen verwendet, referenziere sie genauso.","tags":["table","table","undefined","42P01","search_path","schema","schema"]},{"code":"42703","title":"undefined_column","severity":"caution","meaning":"Die in der Abfrage referenzierte Spalte existiert nicht in der Tabelle oder im aktuellen Gültigkeitsbereich.","cause":"Ein Tippfehler im Spaltennamen, eine gelöschte/umbenannte Spalte, ein Verweis auf die falsche Tabelle, eine abweichende Schreibweise bei Identifiern in Anführungszeichen oder eine mehrdeutige/falsche Qualifizierung in einer Abfrage über mehrere Tabellen.","fix":"Liste die Spalten auf: \\d my_table oder SELECT column_name FROM information_schema.columns WHERE table_name = 'my_table'. Korrigiere Name und Tabelle und qualifiziere Spalten in JOINs als table.column. Wurden bei der Erstellung Anführungszeichen verwendet, referenziere die Spalte mit derselben Schreibweise.","tags":["column","column","undefined","42703","typo","typo","schema"]},{"code":"42501","title":"insufficient_privilege","severity":"caution","meaning":"Der Rolle fehlt die für die versuchte Operation erforderliche Berechtigung.","cause":"Der nötige GRANT auf Tabelle/Schema/Datenbank wurde nicht vergeben, USAGE auf dem Schema fehlt, das Objekt gehört einer anderen Rolle oder Row-Level-Security-Policies (RLS) blockieren den Zugriff.","fix":"Vergib die fehlende Berechtigung: GRANT SELECT, INSERT ON my_table TO app_role; für das Schema GRANT USAGE ON SCHEMA app_schema TO app_role. Verwende für Massen-Grants GRANT ... ON ALL TABLES IN SCHEMA und für künftige Objekte ALTER DEFAULT PRIVILEGES. Aktuelle Berechtigungen anzeigen: \\dp my_table. Ist RLS aktiv, prüfe die betreffenden Policies.","tags":["privilege","privilege","permission","grant","42501","permission","rls"]},{"code":"28P01","title":"invalid_password","severity":"caution","meaning":"Das bei der Authentifizierung angegebene Passwort ist falsch; die Anmeldung für die Rolle wurde abgelehnt.","cause":"Ein falsches Passwort, ein geändertes Passwort, veraltete Zugangsdaten im Connection String der Anwendung oder eine Abweichung von der in pg_hba.conf erwarteten Authentifizierungsmethode (scram-sha-256, md5).","fix":"Prüfe das Passwort und setze es bei Bedarf zurück: ALTER ROLE app_role WITH PASSWORD 'new_password'. Stelle sicher, dass die Methode in pg_hba.conf mit dem Client kompatibel ist (alte Clients können mit scram-sha-256 Probleme haben). Lade nach Änderungen die Konfiguration mit SELECT pg_reload_conf() neu.","tags":["auth","identity","password","password","28P01","scram","md5","hba"]},{"code":"08006","title":"connection_failure","severity":"danger","meaning":"Die Verbindung zwischen Client und Server konnte nicht hergestellt werden oder wurde unerwartet getrennt.","cause":"Der Server läuft nicht oder startet neu, Netzwerk-/Firewall-Probleme, falscher Host/Port, eine Einstellung von listen_addresses, die den Zugriff blockiert, eine vom Netzwerk getrennte inaktive Verbindung oder ein überlasteter Server.","fix":"Prüfe, ob der Server läuft und auf dem richtigen Host/Port lauscht (pg_isready host port). Prüfe listen_addresses in postgresql.conf und die Zugriffsregeln in pg_hba.conf. Prüfe Netzwerk-/Firewall- und DNS-Einstellungen. Überprüfe TCP-Keepalive- und Timeout-Einstellungen im Connection Pool. Suche im Serverlog nach Spuren von Abstürzen/Neustarts.","tags":["connection","connection","network","network","08006","failure","disconnect"]},{"code":"55P03","title":"lock_not_available","severity":"caution","meaning":"Die angeforderte Sperre konnte nicht sofort erhalten werden. Meist passiert das, wenn eine Sperre mit NOWAIT angefordert wird, während jemand anderes sie hält, oder wenn lock_timeout abläuft.","cause":"Die Zeile/Tabelle ist gesperrt, während SELECT ... FOR UPDATE NOWAIT oder LOCK TABLE ... NOWAIT verwendet wird, oder lock_timeout läuft ab. Meistens hält eine lange Transaktion die Sperre.","fix":"Finde die blockierende Session: Verknüpfe pg_locks und pg_stat_activity (Warte-/Blockierbeziehung). Beende die lange Transaktion oder terminiere sie bei Bedarf mit pg_terminate_backend(pid). Warte statt NOWAIT mit einem sinnvollen lock_timeout und ergänze eine Wiederholungslogik. Verkürze Transaktionen, um die Haltedauer von Sperren zu reduzieren.","tags":["lock","lock","nowait","lock_timeout","55P03","not available","busy"]},{"code":"54000","title":"program_limit_exceeded","severity":"caution","meaning":"Ein Implementierungs-/Formatlimit von PostgreSQL wurde überschritten; zum Beispiel die maximale Größe einer Zeile oder eines Feldes, die Größe einer Indexzeile oder das Limit für die Anzahl von Parametern/Spalten in einer Abfrage.","cause":"Versuch, einen sehr großen Wert zu speichern (z. B. ein Feld über 1 GB), ein indizierter Wert, der das B-Tree-Seitenlimit (etwa 2704 Bytes) überschreitet, oder zu viele Spalten bzw. Abfrageparameter.","fix":"Ermittle das überschrittene Limit (die Meldung nennt es meist). Überarbeite oder teile die Struktur bei sehr großen Texten/Daten. Indiziere lange Textspalten mit einem Hash-Index oder einem Ausdrucks-/Partial-Index statt eines B-Tree. Reduziere die Anzahl der Parameter/Spalten in der Abfrage oder teile den Batch auf.","tags":["limit","limit","program_limit","54000","index size","size","row size"]},{"code":"22001","title":"string_data_right_truncation","severity":"caution","meaning":"Der Wert wäre abgeschnitten worden, weil die Textlänge die definierte Länge der Zielspalte (z. B. VARCHAR(n)) überschreitet; PostgreSQL lehnt ihn ab und meldet einen Fehler.","cause":"Einfügen oder Aktualisieren von Text länger als n in eine VARCHAR(n)/CHAR(n)-Spalte, unerwartet lange Daten aus der Anwendung oder eine für den Bedarf zu knapp definierte Spaltenlänge.","fix":"Kürze den Wert, sodass er in die Spalte passt, oder erhöhe die Spaltenlänge: ALTER TABLE my_table ALTER COLUMN my_column TYPE VARCHAR(500). Wird unbegrenzter Text benötigt, verwende den Typ TEXT. Prüfe die Länge in der Anwendung. Willst du bewusst abschneiden, wende LEFT(value, n) explizit an.","tags":["truncation","truncation","varchar","string","text","22001","length","length"]},{"code":"25P02","title":"in_failed_sql_transaction","severity":"caution","meaning":"Früher in der Transaktion ist bereits ein Fehler aufgetreten; darin können keine weiteren Befehle ausgeführt werden. Die Transaktion ist im Zustand aborted, und nur ROLLBACK wird akzeptiert.","cause":"Ein vorheriges Statement in einem Transaktionsblock ist fehlgeschlagen (z. B. eine Constraint-Verletzung), und trotzdem werden weiter neue Befehle in derselben Transaktion gesendet. Die Anwendung fängt den Fehler nicht ab und setzt die Transaktion fort.","fix":"Setze die Transaktion zurück: ROLLBACK, und starte sie dann neu. Willst du den Fehler eines Blocks isolieren und weitermachen, verwende SAVEPOINT und kehre mit ROLLBACK TO SAVEPOINT zu diesem Punkt zurück. Fange in der Anwendung den Fehler jedes Statements ab und steuere die Transaktion sauber (Commit-/Rollback-Entscheidung explizit treffen).","tags":["transaction","transaction","aborted","aborted","25P02","rollback","savepoint"]},{"code":"53200","title":"out_of_memory","severity":"danger","meaning":"Der Server konnte den für eine Operation benötigten Speicher nicht allokieren. Ein Backend-Prozess oder der gesamte Server steht unter Speicherdruck.","cause":"Zu hoch gesetztes work_mem/maintenance_work_mem multipliziert mit hoher Parallelität, zu viele Hash-/Sort-Operationen in einer einzelnen Abfrage, eine übermäßige Anzahl von Verbindungen, ein Memory Leak oder Druck durch den OOM Killer des Betriebssystems.","fix":"Prüfe den Memory Context im Serverlog und Spuren des OOM Killers. Setze work_mem sinnvoll unter Berücksichtigung von Abfrageanzahl und Parallelität (es wird für jede Sort-/Hash-Operation separat allokiert). Begrenze die Anzahl der Verbindungen mit Pooling. Überprüfe die Speicher-/Overcommit-Einstellungen des Betriebssystems. Optimiere schwere Abfragen, um ihren Speicherbedarf zu senken.","tags":["memory","memory","out of memory","53200","work_mem","oom"]}]},"errors/mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1045","title":"Access denied for user (using password: YES/NO) (Zugriff für Benutzer verweigert)","severity":"caution","meaning":"Die Authentifizierung für Benutzername, Passwort oder den Host, von dem aus du dich verbindest, ist fehlgeschlagen; die Anmeldung wurde abgelehnt.","cause":"Falsches Passwort, der Benutzer hat keine Berechtigungen von dem Host, von dem er sich verbindet (user@host), das Konto existiert nicht, nicht passendes Authentifizierungs-Plugin (caching_sha2_password vs. mysql_native_password) oder veraltete Zugangsdaten im Connection String.","fix":"Prüfe, ob Benutzer und Host zusammenpassen: SELECT user, host, plugin FROM mysql.user. Vergib Berechtigungen für den richtigen Host (z. B. 'app_user'@'%'). Setze das Passwort zurück: ALTER USER 'app_user'@'host' IDENTIFIED BY 'new_password'; (ALTER USER/GRANT wirken sofort; FLUSH PRIVILEGES ist nur nötig, wenn die Grant-Tabellen direkt per DML geändert wurden). Siehst du 'using password: NO', sendet der Client überhaupt kein Passwort. Bei Plugin-Abweichungen prüfe das Authentifizierungs-Plugin: In MySQL 8.4 ist das Plugin mysql_native_password standardmäßig deaktiviert (wird nur mit --mysql-native-password=ON geladen); alte Konten mit diesem Plugin und alte Clients können sich nicht verbinden. Migriere das Konto mit ALTER USER ... IDENTIFIED WITH caching_sha2_password BY '...'; und aktualisiere den Client-Treiber.","tags":["auth","authentication","access denied","access","password","password","1045","privilege"]},{"code":"1062","title":"Duplicate entry for key (doppelter Eintrag für Schlüssel)","severity":"caution","meaning":"Die eingefügte oder aktualisierte Zeile verletzt einen UNIQUE-Index oder den PRIMARY KEY; der gleiche Schlüsselwert ist bereits vorhanden.","cause":"Ein wiederholter INSERT mit demselben eindeutigen Schlüssel, doppelte Dateneingabe, ein zurückgefallener AUTO_INCREMENT-Zähler oder bereits vorhandene doppelte Zeilen beim Hinzufügen eines Unique Index zu einer bestehenden Tabelle.","fix":"Die Fehlermeldung zeigt den kollidierenden Schlüsselwert und den Indexnamen. Um die vorhandene Zeile zu aktualisieren, erwäge INSERT ... ON DUPLICATE KEY UPDATE oder INSERT IGNORE / REPLACE. Duplikate finden: SELECT my_column, COUNT(*) FROM my_table GROUP BY my_column HAVING COUNT(*) > 1. Kannst du keinen Unique Index anlegen, bereinige zuerst die Duplikate.","tags":["duplicate","duplicate entry","unique","unique key","1062","primary key","insert"]},{"code":"1213","title":"Deadlock found when trying to get lock; try restarting transaction (Deadlock beim Anfordern einer Sperre; Transaktion neu starten)","severity":"danger","meaning":"InnoDB hat einen Deadlock erkannt, bei dem zwei oder mehr Transaktionen gegenseitig auf ihre Sperren warten, und den Zyklus durch Rollback einer der Transaktionen aufgelöst.","cause":"Zeilensperren in unterschiedlicher Reihenfolge, Updates derselben Zeilen über Kreuz, breite gesperrte Zeilenbereiche (Gap Locks) wegen fehlender Indizes oder Konflikte bei Foreign-Key-Prüfungen.","fix":"Zeige die Details des letzten Deadlocks an: SHOW ENGINE INNODB STATUS (Abschnitt LATEST DETECTED DEADLOCK). Greife in allen Transaktionen in derselben Reihenfolge auf Zeilen/Tabellen zu und halte Transaktionen kurz. Verkleinere den gesperrten Bereich durch passende Indizes. Fange die zurückgesetzte Transaktion in der Anwendung ab und wiederhole sie (Retry-Logik).","tags":["deadlock","deadlock detected","lock","row lock","innodb","1213","tx","transaction"]},{"code":"1205","title":"Lock wait timeout exceeded; try restarting transaction (Timeout beim Warten auf Sperre; Transaktion neu starten)","severity":"danger","meaning":"Eine Transaktion hat innodb_lock_wait_timeout Sekunden auf eine benötigte Zeilensperre gewartet, sie nicht erhalten und ist in einen Timeout gelaufen.","cause":"Eine andere Transaktion hält die Sperre lange ohne Commit oder Rollback; inaktive offene Transaktionen, lang laufende Massen-Updates oder umkämpfte Hot Rows.","fix":"Finde die blockierende Transaktion: Verknüpfe performance_schema.data_lock_waits mit data_locks (in älteren Versionen information_schema.innodb_lock_waits/innodb_trx). Committe die blockierende Transaktion, setze sie zurück oder beende sie bei Bedarf mit KILL <trx_mysql_thread_id>. Halte Transaktionen kurz und lass keine Transaktionen inaktiv offen (autocommit/Anwendungslogik). Passe innodb_lock_wait_timeout bei Bedarf an die Last an.","tags":["lock","lock wait","timeout","timed out","innodb","1205","wait","waiting"]},{"code":"1040","title":"Too many connections (zu viele Verbindungen)","severity":"danger","meaning":"Der Server hat die durch max_connections erlaubte maximale Anzahl gleichzeitiger Verbindungen erreicht; neue Verbindungen werden abgelehnt.","cause":"Ein falsch dimensionierter Connection Pool, Connection-Leaks, viele kurzlebige Verbindungen, lange schlafende Verbindungen (Sleep) oder ein für die Last zu niedriger Wert von max_connections.","fix":"Untersuche die Verbindungen: SHOW PROCESSLIST oder SELECT * FROM performance_schema.processlist. Schließe unnötige schlafende Verbindungen mit KILL. Erhöhe max_connections: SET GLOBAL max_connections = 500 (dauerhaft in my.cnf). Die eigentliche Lösung ist ein Connection Pool und das Schließen inaktiver Verbindungen über wait_timeout. Eine zusätzliche Notfall-Admin-Verbindung ist für SUPER/CONNECTION_ADMIN reserviert.","tags":["connection","connections","too many","max_connections","1040","pool","connection pool"]},{"code":"1064","title":"You have an error in your SQL syntax (Fehler in der SQL-Syntax)","severity":"safe","meaning":"Das SQL-Statement enthält einen Syntaxfehler; der Server konnte es nicht parsen. Die Meldung zeigt den Text in der Nähe des Fehlers.","cause":"Ein Tippfehler, ein fehlendes/überzähliges Komma oder eine Klammer, ein reserviertes Wort ohne Quoting als Spalten-/Tabellenname, von der MySQL-Version nicht unterstützte Syntax oder Fehler beim Quoting/Escaping.","fix":"Der Teil 'near ...' der Meldung zeigt, wo der Fehler beginnt; sieh dir die Stelle direkt davor an. Setze reservierte Wörter in Backticks: `order`, `group`. Gleiche Klammern und Kommas aus. Prüfe, ob du Syntax verwendest, die deine Version unterstützt. Erzeugst du dynamisches SQL, protokolliere die vollständige resultierende Abfrage und prüfe sie.","tags":["syntax","syntax error","1064","parse","parsing","reserved word","reserved keyword"]},{"code":"1146","title":"Table doesn't exist (Tabelle existiert nicht)","severity":"caution","meaning":"Die in der Abfrage referenzierte Tabelle existiert nicht oder ist in der aktuellen Datenbank nicht auffindbar.","cause":"Die Tabelle existiert tatsächlich nicht, die falsche Datenbank ist ausgewählt, ein Tippfehler im Tabellennamen, Groß-/Kleinschreibung von Tabellennamen unter Linux (lower_case_table_names) oder eine fehlende/beschädigte InnoDB-Tabellendatei (.ibd) (in 5.7 und früher auch die Definitionsdatei .frm; in 8.0 liegen Definitionen im Data Dictionary).","fix":"Prüfe die Tabelle: SHOW TABLES; und wähle die richtige Datenbank: USE mydb. Achte auf Groß-/Kleinschreibung (besonders unter Linux). Prüfe Tabellenname und Schema mit SELECT ... FROM information_schema.tables. Bei einem Problem mit InnoDB-Dateien kann ein Restore/eine Reparatur nötig sein.","tags":["table","table missing","doesn't exist","not found","1146","schema","database"]},{"code":"1054","title":"Unknown column in field list (unbekannte Spalte in der Feldliste)","severity":"caution","meaning":"Die in der Abfrage referenzierte Spalte existiert nicht in der Tabelle oder im aktuellen Gültigkeitsbereich.","cause":"Ein Tippfehler im Spaltennamen, die Spalte wurde gelöscht/umbenannt, ein Verweis auf die falsche Tabelle, mehrdeutige/falsche Qualifizierung in einem JOIN oder ein ohne Anführungszeichen geschriebener String-Wert, der als Spalte interpretiert wird (WHERE name = value statt WHERE name = 'value').","fix":"Liste die Spalten auf: SHOW COLUMNS FROM my_table oder DESCRIBE my_table. Korrigiere den Spaltennamen und qualifiziere ihn in JOINs als my_table.my_column. Setze String-Literale in einfache Anführungszeichen; Backticks sind nur für Identifier gedacht. Du kannst mit information_schema.columns prüfen.","tags":["column","column name","unknown","unknown column","1054","typo","misspelling"]},{"code":"2002","title":"Can't connect to local MySQL server through socket (keine Verbindung zum lokalen MySQL-Server über Socket)","severity":"danger","meaning":"Der Client konnte sich nicht über den Unix-Socket mit dem lokalen MySQL-Server verbinden. Meist läuft der Server nicht oder der Socket-Pfad ist falsch.","cause":"Der MySQL/MariaDB-Dienst läuft nicht oder ist abgestürzt, der Pfad der Socket-Datei (z. B. /var/run/mysqld/mysqld.sock) unterscheidet sich zwischen Client und Server, Berechtigungsprobleme oder der Server ist nicht gestartet.","fix":"Prüfe den Dienststatus und starte ihn (systemctl status mysql / start mysql). Prüfe den korrekten Socket-Pfad: SHOW VARIABLES LIKE 'socket' und die Socket-Einstellung in my.cnf. Weicht der Pfad ab, gib ihn explizit mit --socket an oder verwende TCP: mysql -h 127.0.0.1 -P 3306. Prüfe die Serverlogs auf Start-/Absturzfehler.","tags":["connection","connect","socket","unix socket","2002","server","local","localhost"]},{"code":"2003","title":"Can't connect to MySQL server (TCP/IP) (keine Verbindung zum MySQL-Server über TCP/IP)","severity":"danger","meaning":"Der Client konnte sich nicht per TCP/IP auf dem angegebenen Host und Port mit dem MySQL-Server verbinden.","cause":"Der Server läuft nicht, falscher Host/Port, eine Firewall blockiert Port 3306, der Server ist nur an localhost gebunden (bind-address = 127.0.0.1) oder skip-networking ist aktiviert.","fix":"Prüfe, ob der Server läuft und auf dem richtigen Port lauscht. Setze für Fernzugriff bind-address = 0.0.0.0 (oder die passende IP) in my.cnf und stelle sicher, dass skip-networking deaktiviert ist. Öffne Port 3306 in der Firewall. Prüfe Host/Port: mysql -h host -P 3306 -u app_user -p. Stelle sicher, dass der Benutzer Berechtigungen vom entfernten Host hat.","tags":["connection","connect","tcp","port","2003","network","firewall","bind-address"]},{"code":"2006","title":"MySQL server has gone away (Verbindung zum MySQL-Server verloren)","severity":"caution","meaning":"Die Verbindung zwischen Client und Server ging während eines Befehls verloren. Der Server hat die Verbindung möglicherweise geschlossen oder das Paket abgelehnt.","cause":"Eine inaktive Verbindung wurde geschlossen, weil wait_timeout/interactive_timeout abgelaufen ist, eine Abfrage/Daten größer als max_allowed_packet, ein Neustart oder Absturz des Servers oder ein Netzwerkausfall.","fix":"Verwende bei Problemen mit inaktiven Verbindungen eine Validierung im Connection Pool und prüfe wait_timeout. Sendest du große Datenmengen, erhöhe max_allowed_packet (z. B. SET GLOBAL max_allowed_packet = 64*1024*1024, dauerhaft in my.cnf). Prüfe die Serverlogs auf Neustarts/Abstürze und OOM-Spuren. Ergänze in der Anwendung eine Reconnect-Logik für getrennte Verbindungen.","tags":["connection","connection lost","gone away","disconnect","2006","timeout","max_allowed_packet"]},{"code":"1114","title":"The table is full (die Tabelle ist voll)","severity":"danger","meaning":"In die Tabelle können keine weiteren Daten geschrieben werden, weil ein Speicherlimit oder das Ende des verfügbaren Platzes erreicht ist.","cause":"Platte voll, Limit max_heap_table_size bei MEMORY-Tabellen erreicht, tmp_table_size bei temporären Tabellen überschritten, ein über innodb_data_file_path definierter Tablespace fester Größe läuft voll oder dateisystem-/tabellenspezifische Größenlimits.","fix":"Prüfe zuerst den Plattenplatz und gib Platz frei. Erhöhe für MEMORY-/Temp-Tabellen max_heap_table_size und tmp_table_size. Stelle bei InnoDB sicher, dass innodb_data_file_path auf autoextend steht, oder füge Platz hinzu. Optimiere Abfragen, die große temporäre Ergebnisse auf Disk auslagern. Erhöhe als dauerhafte Lösung die Speicherkapazität.","tags":["table full","table is full","disk","space","1114","space","memory","innodb"]},{"code":"1215","title":"Cannot add foreign key constraint (Foreign-Key-Constraint kann nicht hinzugefügt werden)","severity":"caution","meaning":"Der zu definierende Foreign-Key-Constraint kann nicht erstellt werden; MySQL kann den Constraint nicht hinzufügen.","cause":"Abweichung bei Datentyp/Länge/Vorzeichen (signed-unsigned) oder Zeichensatz/Collation zwischen Kind- und referenzierter Spalte, kein passender Index/PRIMARY KEY auf der referenzierten Spalte, Tabellen mit unterschiedlichen Storage Engines (eine ist MyISAM) oder die referenzierte Tabelle existiert noch nicht.","fix":"Stelle sicher, dass die Spaltentypen auf beiden Seiten EXAKT gleich sind (z. B. beide INT UNSIGNED). Prüfe, ob die referenzierte Spalte durch einen Index abgedeckt ist (meist PRIMARY KEY oder UNIQUE). Stelle sicher, dass beide Tabellen InnoDB sind. Verwende für String-Schlüssel denselben Charset/dieselbe Collation. Details: SHOW ENGINE INNODB STATUS (LATEST FOREIGN KEY ERROR).","tags":["foreign key","foreign key constraint","fk","1215","constraint","constraint error","innodb","type"]},{"code":"1451","title":"Cannot delete or update a parent row: a foreign key constraint fails (Elternzeile kann nicht gelöscht/geändert werden: Foreign-Key-Constraint schlägt fehl)","severity":"caution","meaning":"Du hast versucht, eine Elternzeile zu löschen oder ihren Schlüssel zu ändern, aber der Foreign-Key-Constraint lässt es nicht zu, weil abhängige Kindzeilen existieren.","cause":"Die referenzierte Elternzeile hat noch Kindzeilen, und der Constraint verwendet RESTRICT/NO ACTION für ON DELETE/UPDATE.","fix":"Lösche zuerst die abhängigen Kindzeilen oder hänge sie an eine andere Elternzeile, dann lösche die Elternzeile. Willst du automatisches Verhalten, definiere den Constraint mit ON DELETE CASCADE oder ON DELETE SET NULL neu. Abhängige Zeilen finden: SELECT * FROM child_table WHERE fk_column = value.","tags":["foreign key","foreign key constraint","fk","1451","parent","parent row","delete","reference"]},{"code":"1452","title":"Cannot add or update a child row: a foreign key constraint fails (Kindzeile kann nicht eingefügt/geändert werden: Foreign-Key-Constraint schlägt fehl)","severity":"caution","meaning":"Du fügst eine Kindzeile ein oder aktualisierst sie, aber der Elternschlüsselwert, auf den sie verweist, existiert in der referenzierten Tabelle nicht; der Foreign-Key-Constraint lehnt sie ab.","cause":"Ein INSERT/UPDATE, das einen nicht existierenden Elternschlüssel referenziert, Daten in falscher Reihenfolge geladen (Kind zuerst) oder eine gelöschte Elternzeile.","fix":"Füge zuerst die Elternzeile ein, dann die Kindzeile. Prüfe, ob der referenzierte Wert existiert: SELECT * FROM parent_table WHERE key_column = value. Halte bei Massenladungen die Eltern-Kind-Reihenfolge ein. Verwende SET FOREIGN_KEY_CHECKS=0 nur bei kontrollierten Wartungsarbeiten und setze es danach wieder auf 1.","tags":["foreign key","foreign key constraint","fk","1452","child","child row","reference","insert"]},{"code":"1366","title":"Incorrect string/integer value for column (falscher String-/Integer-Wert für Spalte)","severity":"caution","meaning":"In eine Spalte wurde ein Wert eingefügt, der mit Typ oder Zeichensatz der Spalte nicht kompatibel ist. Meist verursacht durch eine Abweichung bei Zeichenkodierung oder Typ.","cause":"Schreiben eines 4-Byte-Emojis/-Zeichens (utf8mb4) in eine latin1-Spalte, Eingabe von nicht-numerischem Text in eine numerische Spalte, ein falscher Verbindungs-Charset oder Senden eines Werts mit einem anderen als dem erwarteten Typ.","fix":"Konvertiere bei Zeichenproblemen die Spalte/Tabelle nach utf8mb4: ALTER TABLE my_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci und setze den Verbindungs-Charset auf utf8mb4. Prüfe bei numerischen Feldern die Typen in der Anwendung. Beachte, dass der Server im strikten SQL-Modus (STRICT) läuft; die Grundursache ist Typ/Kodierung der Daten.","tags":["charset","character","incorrect value","1366","utf8mb4","encoding","type","collation"]},{"code":"1044","title":"Access denied for user to database (Zugriff des Benutzers auf die Datenbank verweigert)","severity":"caution","meaning":"Der Benutzer hat sich zwar korrekt authentifiziert, hat aber keine Berechtigung, auf der angegebenen Datenbank zu arbeiten.","cause":"Kein GRANT auf der Datenbank für den Benutzer, ein falscher Datenbankname, die Berechtigung ist für einen anderen Host definiert (user@host) oder die Datenbank existiert nicht.","fix":"Zeige die Berechtigungen des Benutzers an: SHOW GRANTS FOR 'app_user'@'host'. Vergib die nötige Berechtigung: GRANT ALL PRIVILEGES ON mydb.* TO 'app_user'@'host'; (GRANT wirkt sofort, FLUSH PRIVILEGES ist nicht nötig). Prüfe die korrekte Host-Zuordnung und den Datenbanknamen. Vergib nach dem Least-Privilege-Prinzip nur die benötigten Rechte.","tags":["access denied","access","database","database access","1044","grant","privilege","permission"]},{"code":"1264","title":"Out of range value for column (Wert außerhalb des Bereichs für Spalte)","severity":"caution","meaning":"In eine numerische oder Datumsspalte wurde ein Wert außerhalb des vom Typ erlaubten Bereichs eingefügt. Dieser Fehler tritt im strikten SQL-Modus auf.","cause":"Überschreiten des Limits von Typen wie TINYINT/INT (z. B. 200 in TINYINT oder ein negativer Wert in eine UNSIGNED-Spalte), Datums-/Zahlenüberlauf oder Senden eines größeren Werts als erwartet.","fix":"Erweitere den Spaltentyp nach Bedarf (z. B. INT statt TINYINT, BIGINT statt INT) oder korrigiere das Vorzeichen (SIGNED/UNSIGNED). Prüfe Wertebereiche in der Anwendung. Selbst wenn du stilles Abschneiden möchtest (nicht empfohlen), mache die Daten mit dem Typ kompatibel, statt STRICT aus sql_mode zu entfernen.","tags":["out of range","value out of range","1264","value","int","tinyint","unsigned","overflow"]}]},"errors/mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"1205","title":"Transaction (Process ID) was deadlocked on lock resources with another process and has been chosen as the deadlock victim (Transaktion als Deadlock-Opfer gewählt)","severity":"danger","meaning":"Zwei oder mehr Transaktionen warten jeweils auf Sperren, die die andere hält (ein Deadlock). Um den Zyklus aufzulösen, wählt SQL Server eine Transaktion als Opfer und setzt sie zurück.","cause":"Transaktionen, die Tabellen/Zeilen in unterschiedlicher Reihenfolge sperren, lang laufende Transaktionen, breite Scans wegen fehlender Indizes, Lock Escalation und hohe Parallelität sind die typischen Ursachen.","fix":"Fange Fehler 1205 in der Anwendung ab und wiederhole die Transaktion automatisch. Greife in jeder Transaktion in derselben Reihenfolge auf Objekte zu und halte Transaktionen kurz. Untersuche den Deadlock-Graphen: Verwende Extended Events (die Session system_health) oder Trace Flag 1222 für Details. Lege passende Indizes an, um Scans und Sperren zu reduzieren. Bewerte bei Bedarf die Isolationsstufe READ COMMITTED SNAPSHOT (RCSI).","tags":["deadlock","lock","locking","victim","rollback","1205","retry"]},{"code":"2627","title":"Violation of PRIMARY KEY / UNIQUE KEY constraint. Cannot insert duplicate key in object (Verletzung des PRIMARY-KEY-/UNIQUE-KEY-Constraints; doppelter Schlüssel nicht einfügbar)","severity":"caution","meaning":"Du hast versucht, einen Wert einzufügen oder zu aktualisieren, der in den durch einen PRIMARY-KEY- oder UNIQUE-Constraint geschützten Spalte(n) bereits vorhanden ist. Doppelte Schlüssel werden nicht akzeptiert.","cause":"Zweites Einfügen desselben eindeutigen Werts, ein UPDATE, das mit einem vorhandenen Wert kollidiert, eine fehlende Eindeutigkeitsprüfung oder zwei parallele Requests, die dieselbe Zeile erzeugen.","fix":"Prüfe vor dem Einfügen, ob die Zeile existiert, oder verwende MERGE / IF NOT EXISTS. Wende für idempotente Inserts das Muster \"INSERT ... WHERE NOT EXISTS\" an. Fange den Fehler ab und gib eine aussagekräftige Meldung zurück. Brauchst du tatsächlich doppelte Daten, überprüfe den Constraint. Verwende IDENTITY oder SEQUENCE, um Kollisionen beim Erzeugen von Schlüsseln zu vermeiden.","tags":["unique","primary key","uniqueness","duplicate","constraint","constraint violation","2627"]},{"code":"2601","title":"Cannot insert duplicate key row in object with unique index. The duplicate key value is ... (doppelte Schlüsselzeile in Objekt mit Unique Index nicht einfügbar)","severity":"caution","meaning":"Ein UNIQUE INDEX hat eine Zeile wegen eines doppelten Schlüsselwerts abgelehnt. 2627 wird bei Constraints ausgelöst, 2601 bei Unique Indexes.","cause":"Erneutes Schreiben eines vorhandenen Werts in durch einen UNIQUE INDEX geschützte Spalte(n), parallele Inserts oder eine Kollision zwischen Zeilen, die von einem gefilterten Unique Index abgedeckt sind.","fix":"Entferne oder aktualisiere den doppelten Wert. Müssen Duplikate toleriert werden, erwäge die Option IGNORE_DUP_KEY = ON (Duplikate werden stillschweigend übersprungen). Ergänze vor dem Einfügen eine NOT-EXISTS-Prüfung. Gibt es einen gefilterten Index, prüfe dessen WHERE-Klausel und die Kollision.","tags":["unique index","duplicate key","unique idx","duplicate","2601","index"]},{"code":"547","title":"The INSERT/UPDATE/DELETE statement conflicted with the FOREIGN KEY / CHECK constraint (Statement kollidiert mit FOREIGN-KEY-/CHECK-Constraint)","severity":"caution","meaning":"Das Statement hat einen FOREIGN-KEY- oder CHECK-Constraint verletzt. Entweder hat es eine nicht existierende Elternzeile referenziert, einen Wert geschrieben, der eine CHECK-Regel verletzt, oder versucht, eine noch referenzierte Elternzeile zu löschen.","cause":"FK: Die von der eingefügten Kindzeile referenzierte Elternzeile existiert nicht. DELETE/UPDATE: Kindzeilen hängen von der zu löschenden/ändernden Elternzeile ab. CHECK: Der Spaltenwert erfüllt die definierte Bedingung nicht.","fix":"Füge zuerst die Elternzeile ein, dann die Kindzeile. Entferne beim Löschen zuerst die abhängigen Zeilen oder definiere, wo sinnvoll, ON DELETE CASCADE. Bringe bei einer CHECK-Verletzung den Wert in Einklang mit der Regel. Ermittle den verletzten Constraint über den Constraint-Namen im Fehlertext.","tags":["foreign key","fk","check","constraint","constraint violation","referential","547"]},{"code":"18456","title":"Login failed for user (Anmeldung für Benutzer fehlgeschlagen)","severity":"danger","meaning":"Ein Anmeldeversuch wurde bei der Authentifizierung abgelehnt. Benutzername/Passwort können falsch sein, das Konto kann deaktiviert sein oder der Benutzer hat keinen Zugriff auf die Datenbank.","cause":"Falsches Passwort, nicht existierender Login, deaktiviertes/gesperrtes Konto, Verwendung eines SQL-Logins, obwohl nur Windows-Authentifizierung aktiviert ist, oder kein Zugriff auf die Standarddatenbank. Der State-Code im Fehlertext nennt die tatsächliche Ursache (State 5: Login existiert nicht, State 7: Login deaktiviert und Passwort ebenfalls falsch, State 8: falsches Passwort (am häufigsten), State 18: Passwort muss geändert werden, State 38: die angegebene Datenbank kann nicht geöffnet werden, State 58: SQL-Login versucht, während der Server im reinen Windows-Authentifizierungsmodus läuft).","fix":"Ermittle die Grundursache über den State-Wert im SQL Server Error Log (Details werden aus Sicherheitsgründen nicht an den Client gesendet). Prüfe Benutzername/Passwort. Prüfe, ob der Server im Modus 'SQL Server and Windows Authentication Mode' (Mixed Mode) läuft; hast du ihn geändert, starte den Dienst neu. Prüfe die User-Zuordnung des Logins und die Zugriffsberechtigung in der Zieldatenbank.","tags":["login","session","authentication","authentication failed","password","18456","state"]},{"code":"8152","title":"String or binary data would be truncated (String- oder Binärdaten würden abgeschnitten)","severity":"caution","meaning":"Du hast versucht, Daten zu schreiben, die länger als die definierte Länge einer Spalte sind. SQL Server stoppt das Statement, um Datenverlust zu verhindern (2628 ist die neuere Version desselben Fehlers, die Spalte/Wert meldet).","cause":"Zum Beispiel 15 Zeichen in eine VARCHAR(10)-Spalte schreiben, eine Zielspalte schmaler als die Quelle, eine Abweichung bei Datentyp/Länge oder zu kleine Feldgrößen bei ETL/Übertragungen.","fix":"Verbreitere die Spalte passend zu den Daten (ALTER TABLE ... ALTER COLUMN) oder kürze die Daten. Nutze ab SQL Server 2019 den Fehler 2628, der die übergelaufene Tabelle/Spalte/den Wert zeigt; prüfe in älteren Versionen die Spalten einzeln mit LEN(), um die betroffene Zeile zu finden. Gleiche bei Übertragungen die Längen von Quell- und Zielschema ab.","tags":["truncation","truncate","overflow","varchar","length","column length","8152","2628"]},{"code":"2628","title":"String or binary data would be truncated in table, column ...; truncated value (Daten würden in Tabelle/Spalte abgeschnitten; abgeschnittener Wert)","severity":"caution","meaning":"Die erweiterte Version von 8152 (SQL Server 2019 und neuer). Sie meldet explizit, welche Tabelle, welche Spalte und welcher Wert abgeschnitten würde.","cause":"Schreiben eines Text-/Binärwerts, der länger als die definierte Länge der Zielspalte ist. Meist eine Schemaabweichung oder Eingabedaten unerwarteter Länge.","fix":"Sieh dir Tabellen- und Spaltennamen im Fehlertext an; verbreitere entweder die Spalte oder kürze den Quellwert. In SQL Server 2019 wird 2628 standardmäßig nur ab Kompatibilitätsgrad 150 ausgelöst; aktiviere ihn auf niedrigeren Stufen mit ALTER DATABASE SCOPED CONFIGURATION SET VERBOSE_TRUNCATION_WARNINGS = ON oder Trace Flag 460. Ab SQL Server 2016 SP2 CU6 und 2017 CU12 liefert Trace Flag 460 dieselbe detaillierte Meldung. Gleiche die Feldlängen in der Übertragungsschicht an das Ziel an.","tags":["truncation","truncate","overflow","column","column name","length","2628","460"]},{"code":"1222","title":"Lock request time out period exceeded (Timeout der Sperranforderung überschritten)","severity":"caution","meaning":"Der Timeout einer Abfrage ist abgelaufen, während sie auf eine Sperre gewartet hat. Die Anforderung wurde abgebrochen, weil eine andere Transaktion die benötigte Ressource zu lange gesperrt hielt.","cause":"Überschreiten der mit SET LOCK_TIMEOUT festgelegten Wartezeit, lang laufende blockierende Transaktionen, offene nicht committete Transaktionen, lange Scans und Sperrkonflikte.","fix":"Finde den Blocker: Untersuche sys.dm_exec_requests, sys.dm_tran_locks und die Spalte blocking_session_id oder verwende sp_who2 / sp_whoisactive. Verkürze lange Transaktionen und committe sie. Passe LOCK_TIMEOUT bei Bedarf an. Reduziere auf leselastigen Systemen Leser-Schreiber-Konflikte mit RCSI/Snapshot Isolation.","tags":["lock timeout","lock wait timeout","blocking","blocked session","1222","lock_timeout"]},{"code":"9002","title":"The transaction log for database is full due to ... (Transaktionslog der Datenbank ist voll wegen ...)","severity":"danger","meaning":"Die Transaktionslogdatei der Datenbank ist voll, und neue Transaktionen können nicht protokolliert werden. Die Datenbank lehnt Schreibzugriffe ab und kann sich wie schreibgeschützt verhalten.","cause":"Keine Log-Backups im Recovery Model FULL, eine lange offene Transaktion, Replikations-/AlwaysOn-Verzögerung oder das Log kann nicht wachsen (Autogrow deaktiviert oder Platte voll). log_reuse_wait_desc zeigt den Grund.","fix":"Prüfe log_reuse_wait_desc in sys.databases. Lautet er im Modell FULL LOG_BACKUP, erstelle ein Log-Backup (BACKUP LOG). Lautet er ACTIVE_TRANSACTION, finde die offene Transaktion und committe sie bzw. setze sie zurück (DBCC OPENTRAN). Schaffe Plattenplatz oder korrigiere die Autogrow-Einstellung der Logdatei. Ist das Log tatsächlich gewachsen, verkleinere es nach dem Backup mit DBCC SHRINKFILE. Brauchst du die Log-Kette nicht, überprüfe das Recovery Model.","tags":["transaction log","log is full","log full","9002","backup log","recovery model"]},{"code":"1105","title":"Could not allocate space for object in database because the filegroup is full (kein Platz für Objekt, da die Dateigruppe voll ist)","severity":"danger","meaning":"Die Dateigruppe mit der Datendatei ist voll, und für neue Daten kann kein Platz allokiert werden. Inserts/Updates schlagen fehl.","cause":"Die Datendatei kann nicht wachsen (Autogrowth deaktiviert oder MAXSIZE erreicht), kein freier Plattenplatz, die Dateigruppe PRIMARY ist erschöpft oder ein Dateigrößenlimit.","fix":"Schaffe Plattenplatz oder füge eine neue Platte/Datei hinzu. Aktiviere Autogrowth für die Datendatei oder vergrößere sie (ALTER DATABASE ... MODIFY FILE). Füge der Dateigruppe eine neue Datei hinzu. Archiviere und lösche nicht benötigte/alte Daten. Prüfe bei tempdb die tempdb-Dateien und deren Platte.","tags":["filegroup full","file full","allocate space","disk","autogrow","1105"]},{"code":"208","title":"Invalid object name (ungültiger Objektname)","severity":"safe","meaning":"Die in der Abfrage referenzierte Tabelle, View oder das Objekt wurde nicht gefunden. SQL Server konnte den angegebenen Namen nicht auflösen.","cause":"Ein falsch geschriebener Objektname, falscher Datenbank-/Schemakontext (ein anderes Schema als dbo), das Objekt existiert tatsächlich nicht oder wurde noch nicht angelegt oder ein fehlender zweiteiliger Name (schema.table).","fix":"Schreibe Objektname und Schema korrekt; verwende den qualifizierten Namen (z. B. dbo.Customers). Stelle sicher, dass du mit der richtigen Datenbank verbunden bist (USE). Prüfe, ob das Objekt existiert: SELECT * FROM sys.objects WHERE name = 'YourObjectName'. Stelle sicher, dass temporäre Tabellen in derselben Session angelegt werden.","tags":["invalid object","invalid object name","table not found","table does not exist","schema","app schema","208"]},{"code":"229","title":"The SELECT/EXECUTE permission was denied on the object (SELECT-/EXECUTE-Berechtigung für das Objekt verweigert)","severity":"caution","meaning":"Der Benutzer hat keine Berechtigung für die angeforderte Operation (SELECT, INSERT, EXECUTE usw.) auf dem Objekt. Der Zugriff wurde aus Sicherheitsgründen verweigert.","cause":"Die erforderliche Berechtigung wurde dem Benutzer/der Rolle nicht erteilt, falsche Schema-Ownership (unterbrochene Ownership Chain), ein explizites DENY auf dem Objekt oder der Benutzer ist nicht Mitglied einer ausreichenden Rolle.","fix":"Erteile die erforderliche Berechtigung: GRANT SELECT ON dbo.MyTable TO UserName (oder GRANT EXECUTE ON dbo.MyProcedure TO ...). Füge den Benutzer einer passenden Rolle hinzu (z. B. db_datareader). Entferne ein etwaiges explizites DENY. Vergib Berechtigungen nach dem Least-Privilege-Prinzip. Prüfe bestehende Berechtigungen mit sys.database_permissions.","tags":["permission denied","access denied","grant","privilege","security","security permissions","229"]},{"code":"4060","title":"Cannot open database requested by the login. The login failed (vom Login angeforderte Datenbank kann nicht geöffnet werden; Anmeldung fehlgeschlagen)","severity":"danger","meaning":"Die Authentifizierung kann erfolgreich sein, aber die Verbindung wird abgelehnt, weil die angeforderte Datenbank nicht geöffnet werden kann. Tritt meist zusammen mit 18456 auf.","cause":"Die Datenbank existiert nicht, ist OFFLINE/SINGLE_USER/RESTORING/SUSPECT, der Login hat in dieser Datenbank keine User-Zuordnung oder die Standarddatenbank im Connection String ist nicht erreichbar.","fix":"Prüfe, ob die Datenbank existiert und ONLINE ist: SELECT name, state_desc FROM sys.databases. Lege in der Datenbank einen User für den Login an und vergib Berechtigungen. Prüfe den Wert Initial Catalog/Database im Connection String. Ist die Datenbank RESTORING/SUSPECT, folge den Schritten für Reparatur/Restore.","tags":["cannot open database","database cannot be opened","4060","login","state","offline"]},{"code":"233","title":"A connection was successfully established with the server, but then an error occurred (no process is on the other end of the pipe) (Verbindung hergestellt, danach Fehler; kein Prozess am anderen Ende der Pipe)","severity":"danger","meaning":"Der Client hat den Server erreicht, aber die Verbindung brach während des Handshakes ab. Meist verursacht durch Named-Pipes-/TCP-Konfiguration, Protokolleinstellungen oder eine serverseitige Ablehnung. Verwandt mit 10054 (Verbindung zwangsweise geschlossen).","cause":"Protokolle Shared Memory/Named Pipes/TCP deaktiviert, Remoteverbindungen nicht erlaubt, das Konto kann sich nicht anmelden (siehe 18456), ein Firewall-/Netzwerkausfall oder der Server schließt die Verbindung zwangsweise (10054).","fix":"Aktiviere im SQL Server Configuration Manager TCP/IP und bei Bedarf Named Pipes und starte den Dienst neu. Prüfe, ob Remoteverbindungen erlaubt sind. Öffne den SQL-Port (Standard 1433) in der Firewall. Untersuche bei Authentifizierungsproblemen den State-Code von 18456 im Error Log. Prüfe Netzwerkstabilität und TLS-/Verschlüsselungseinstellungen.","tags":["connection","connection error","pipe","10054","tcp","named pipes","233","network"]},{"code":"823","title":"The operating system returned error to SQL Server during a read/write at offset in file (I/O error) (Betriebssystemfehler beim Lesen/Schreiben der Datei)","severity":"danger","meaning":"SQL Server hat beim Lesen oder Schreiben einer Daten-/Logdatei einen I/O-Fehler auf Betriebssystemebene erhalten. Das ist eine ernste Integritätswarnung, die auf ein Problem mit der Plattenhardware oder dem Storage-Subsystem hinweist.","cause":"Defekte Plattensektoren, ein fehlerhafter Controller/Treiber, Storage-/SAN-Probleme, Dateisystemfehler oder eine Seite, die gar nicht gelesen werden kann (harter I/O-Fehler). Meist hardwarebedingt.","fix":"Erstelle sofort ein gutes Backup (falls möglich). Untersuche das Windows-Ereignisprotokoll und die Storage-Logs und teste den Zustand von Hardware/Platten. Führe DBCC CHECKDB aus, um die Integrität zu prüfen. Prüfe die Tabelle suspect_pages (msdb.dbo.suspect_pages). Liegt eine Korruption vor, stelle aus einem guten Backup wieder her oder führe einen Restore auf Seitenebene durch. Ersetze/repariere die Hardware.","tags":["io error","i/o error","disk","corruption","data corruption","823","checkdb"]},{"code":"824","title":"SQL Server detected a logical consistency-based I/O error (page corruption) (logischer Konsistenzfehler bei I/O; Seitenkorruption)","severity":"danger","meaning":"SQL Server hat eine Seite gelesen, deren Inhalt aber logisch inkonsistent ist (falsche Checksumme, Torn Page oder falsche Page-ID). Die Datenseite ist beschädigt.","cause":"Fehler im Storage-Subsystem, plötzlicher Stromausfall/Torn Pages, Controller- oder Treiberprobleme, stille Korruption, die von PAGE_VERIFY CHECKSUM erkannt wurde. Wie 823 meist hardware-/storagebedingt.","fix":"Führe DBCC CHECKDB aus, um das Ausmaß der Korruption zu ermitteln. Stelle aus dem letzten guten Backup wieder her oder repariere die betroffenen Seiten mit einem Restore auf Seitenebene. Untersuche msdb.dbo.suspect_pages. Stelle sicher, dass PAGE_VERIFY = CHECKSUM aktiviert ist. Prüfe Hardware/Storage. REPAIR_ALLOW_DATA_LOSS darf nur das letzte Mittel sein und verursacht Datenverlust.","tags":["page corruption","corrupt page","consistency","824","checkdb","checksum","torn page"]},{"code":"701","title":"There is insufficient system memory in resource pool to run this query (zu wenig Systemspeicher im Resource Pool für diese Abfrage)","severity":"danger","meaning":"SQL Server konnte den für die Ausführung der Abfrage benötigten Speicher nicht aus dem betreffenden Resource Pool allokieren. Die Operation ist wegen Speicherdruck fehlgeschlagen.","cause":"Zu wenig Serverspeicher, eine niedrige Einstellung von max server memory, speicherhungrige Abfragen (große Sortierungen/Hashes), ein Memory Leak, viele parallele Requests oder Pool-Limits des Resource Governor.","fix":"Überprüfe die Einstellung max server memory und stelle ausreichend physischen RAM bereit. Optimiere speicherhungrige Abfragen (passende Indizes, weniger Sortierungen/Hashes). Überwache den Speicher: sys.dm_os_memory_clerks, DBCC MEMORYSTATUS, Perfmon-Speicherzähler. Passe die Pool-Limits des Resource Governor an. Reduziere bei Bedarf die Parallelität.","tags":["memory","memory pressure","insufficient memory","resource pool","701","oom"]},{"code":"845","title":"Time-out occurred while waiting for buffer latch (Timeout beim Warten auf Buffer Latch)","severity":"danger","meaning":"Beim Warten auf einen Buffer Latch auf einer Datenseite ist ein Timeout aufgetreten. SQL Server konnte die kurzlebige interne Sperre (Latch) für den Zugriff auf eine Seite nicht in angemessener Zeit erhalten; meist ein Zeichen für starke I/O-Last oder Speicherdruck.","cause":"Ein langsames/überlastetes Plattensubsystem, Seiten, die wegen Speicherdruck ständig von Disk gelesen werden, Hardwareprobleme, übermäßiges Paging oder ein Storage-Engpass.","fix":"Miss die I/O-Latenz der Platten (sys.dm_io_virtual_file_stats, Perfmon-Plattenlatenz). Prüfe Storage-Performance und Hardwarezustand. Überprüfe, ob der Speicher ausreicht, und die Einstellung max server memory. Untersuche die Wait Types (sys.dm_os_wait_stats, PAGEIOLATCH-Waits). Beseitige den Storage-Engpass; stelle bei Bedarf schnellere Platten/mehr RAM bereit.","tags":["buffer latch","latch timeout","845","io","pageiolatch","memory","memory pressure"]},{"code":"3201","title":"Cannot open backup device. Operating system error (backup device error) (Backup-Device kann nicht geöffnet werden)","severity":"caution","meaning":"Das Ziel-Backup-Device bzw. die Zieldatei konnte bei Backup oder Restore nicht geöffnet werden. SQL Server konnte nicht auf den angegebenen Pfad zugreifen.","cause":"Ein falscher/nicht existierender Ordnerpfad, dem SQL-Server-Dienstkonto fehlt die Schreibberechtigung auf den Zielordner, kein Zugriff auf eine Netzwerkfreigabe (UNC), eine volle Platte oder die Datei ist von einem anderen Prozess gesperrt.","fix":"Prüfe, ob der Backup-Pfad existiert und korrekt geschrieben ist. Erteile dem SQL-Server-Dienstkonto Schreibberechtigung auf den Zielordner. Prüfe bei UNC-Freigaben den Zugriff des Dienstkontos. Stelle sicher, dass genügend freier Plattenplatz vorhanden ist. Prüfe bei einem lokalen Pfad, ob der Laufwerksbuchstabe vom Server aus sichtbar ist (kein Client-Laufwerk).","tags":["backup device","backup file","backup","restore","3201","permission denied","permission","os error"]}]},"errors/mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"E11000","title":"E11000 duplicate key error collection (Fehler wegen doppelten Schlüssels)","severity":"caution","meaning":"Ein Insert oder Update hat versucht, einen in der Collection bereits vorhandenen Wert in durch einen Unique Index geschützte Feld(er) zu schreiben. MongoDB lehnt den doppelten Schlüssel ab.","cause":"Eine _id-Kollision, zweites Schreiben desselben Werts in ein Feld mit Unique Index, eine Race Condition beim Upsert oder kollidierende Null-Werte mehrerer Dokumente in einem Unique Index (nicht sparse).","fix":"Prüfe vor dem Schreiben, ob das Dokument existiert, oder verwende einen Upsert: db.collection.updateOne(filter, {$set:{...}}, {upsert:true}). Sieh dir den Indexnamen und den dup-key-Wert in der Fehlermeldung an. Ist Eindeutigkeit eigentlich nicht nötig, entferne den Index; sind Nulls das Problem, verwende einen Partial/Sparse Index. Fange den Fehler in der Anwendung ab und wiederhole den Vorgang oder melde ihn dem Benutzer.","tags":["duplicate key","duplicate key","unique index","unique","e11000","upsert","_id"]},{"code":"11000","title":"DuplicateKey (Fehlercode 11000)","severity":"caution","meaning":"Der numerische Code des Duplicate-Key-Fehlers (wird zusammen mit dem E11000-Text zurückgegeben). Eine Schreiboperation hat einen Unique-Index-Constraint verletzt.","cause":"Erneutes Schreiben desselben eindeutigen Felds, parallele Upserts, die denselben Schlüssel erzeugen, oder doppelte Dokumente in einem Bulk Write.","fix":"Fange den Fehlercode 11000 im Anwendungscode ab und implementiere idempotentes Verhalten (aktualisieren, wenn vorhanden, sonst einfügen). Verwende bei Bulk-Operationen ordered:false, damit andere Dokumente als die Duplikate geschrieben werden können. Überprüfe die Eindeutigkeitsregel und die Indexdefinition.","tags":["duplicatekey","11000","duplicate","unique","unique","bulk write","write error"]},{"code":"13","title":"Unauthorized (Fehlercode 13)","severity":"caution","meaning":"Der authentifizierte Benutzer hat keine Berechtigung für die versuchte Operation oder Ressource. Der Benutzer ist angemeldet, aber seine Rollen decken diese Aktion nicht ab.","cause":"Die erforderliche Rolle (z. B. readWrite, dbAdmin) ist dem Benutzer nicht zugewiesen, die Operation läuft gegen die falsche Datenbank oder die Berechtigungen reichen für einen administrativen Befehl nicht aus. Unvollständige RBAC-Konfiguration.","fix":"Prüfe die Rollen des Benutzers: db.getUser(\"app_user\"). Weise die erforderliche Rolle zu: db.grantRolesToUser(\"app_user\", [{role:\"readWrite\", db:\"mydb\"}]). Stelle sicher, dass die Operation in der richtigen Datenbank läuft. Weise nach dem Least-Privilege-Prinzip eine passende Built-in- oder benutzerdefinierte Rolle zu. Prüfe den Wert authSource im Connection String.","tags":["unauthorized","unauthorized","authorization","role","role","rbac","13","permission"]},{"code":"18","title":"AuthenticationFailed (Fehlercode 18)","severity":"caution","meaning":"Die Authentifizierung ist fehlgeschlagen. Der angegebene Benutzername, das Passwort oder die Zugangsdaten sind ungültig; der Benutzer konnte sich nicht anmelden.","cause":"Falscher Benutzername/falsches Passwort, falsche authSource-Datenbank, nicht passender Authentifizierungsmechanismus (SCRAM-SHA-1/SCRAM-SHA-256) oder der Benutzer ist in der angegebenen Datenbank nicht definiert.","fix":"Prüfe Benutzername und Passwort. Gib im Connection String die richtige authSource an (die Datenbank, in der der Benutzer angelegt wurde, meist admin): ...?authSource=admin. Prüfe, ob der Authentifizierungsmechanismus mit dem Server kompatibel ist. Stelle sicher, dass der Benutzer in der richtigen Datenbank angelegt wurde. Kodiere das Passwort per URL-Encoding, wenn es Sonderzeichen enthält.","tags":["authentication failed","authentication","password","password","authsource","18","scram"]},{"code":"11600","title":"InterruptedAtShutdown (Fehlercode 11600)","severity":"danger","meaning":"Die Operation wurde unterbrochen, weil der Server mit dem Herunterfahren begonnen hat. Die laufende Operation konnte nicht abgeschlossen werden, da der mongod-Prozess beendet wurde.","cause":"Ein Neustart/Herunterfahren von mongod, Herunterfahren während eines Stepdowns des Replica-Set-Primary, geplante Wartung, ein Absturz oder der Prozess wurde vom OOM Killer beendet.","fix":"Warte, bis der Server wieder läuft, und wiederhole die Operation. Fange in der Anwendung transiente Fehler ab und wiederhole automatisch; aktiviere Retryable Writes. Prüfe bei unerwartetem Herunterfahren die mongod-Logs und Systemlogs (OOM, Absturz). Verwende ein Replica Set für Hochverfügbarkeit.","tags":["interrupted","shutdown","shutdown","downtime","11600","retry","replica set"]},{"code":"50","title":"MaxTimeMSExpired (Fehlercode 50)","severity":"caution","meaning":"Der Server hat die Operation abgebrochen, weil sie das mit maxTimeMS gesetzte Zeitlimit überschritten hat. Die Abfrage wurde nicht in der vorgesehenen Zeit abgeschlossen.","cause":"Lang laufende Abfragen, Collection Scans (COLLSCAN) wegen fehlender Indizes, große Datenmengen, schwere Aggregationen oder hohe Serverlast.","fix":"Optimiere die Abfrage: Untersuche den Plan mit explain() und lege einen passenden Index an. Wird wirklich mehr Zeit benötigt, erhöhe maxTimeMS. Vereinfache schwere Aggregationen oder filtere früh mit $match. Teile Ergebnisse per Pagination auf. Prüfe Serverlast und Ressourcenkapazität.","tags":["maxtimems","timeout","timeout","50","slow query","slow query","index"]},{"code":"251","title":"NoSuchTransaction (Fehlercode 251)","severity":"caution","meaning":"Die referenzierte Multi-Document-Transaktion wurde nicht gefunden. Die Transaktion ist bereits beendet, in einen Timeout gelaufen oder es wurde eine ungültige Transaktionsnummer verwendet.","cause":"Die Transaktion hat transactionLifetimeLimitSeconds überschritten und wurde automatisch abgebrochen, die Session wurde beendet, eine falsche/veraltete txnNumber oder eine bereits nach einem Fehler abgebrochene Transaktion wird fortgesetzt.","fix":"Starte die Transaktion von vorn (mit einer neuen Session). Halte Transaktionen kurz; gestalte sie so, dass sie innerhalb der Standardlebensdauer (60 s) enden. Wiederhole bei Fehlern mit dem Label TransientTransactionError die gesamte Transaktion sicher. Sind lange Transaktionen nötig, überprüfe die Einstellung transactionLifetimeLimitSeconds.","tags":["nosuchtransaction","transaction","transaction","251","session","abort","retry"]},{"code":"112","title":"WriteConflict (Fehlercode 112)","severity":"caution","meaning":"Ein Schreibkonflikt, der entsteht, wenn zwei parallele Operationen dasselbe Dokument bzw. dieselben Dokumente ändern wollen. Wegen der optimistischen Nebenläufigkeitskontrolle setzt WiredTiger eine der Operationen zurück.","cause":"Stark parallele Schreibzugriffe auf dieselben Dokumente, kollidierende Updates in lang laufenden Transaktionen, starke Konkurrenz um ein Hot Document.","fix":"Wiederhole die Operation; WriteConflict ist meist transient und wird durch eine Wiederholung gelöst (er trägt das Label TransientTransactionError). Halte Transaktionen kurz und reduziere die Zahl der berührten Dokumente. Überprüfe das Datenmodell, um Konkurrenz um dasselbe Dokument zu verringern. Implementiere in der Anwendung Wiederholungen mit exponentiellem Backoff.","tags":["writeconflict","write conflict","112","concurrency","concurrency","retry","wiredtiger"]},{"code":"133","title":"FailedToSatisfyReadPreference (Fehlercode 133)","severity":"danger","meaning":"Es wurde kein passendes Replica-Set-Mitglied gefunden, das die angegebene Read Preference (z. B. primary, secondary) erfüllt. Der Treiber konnte keinen Knoten auswählen, an den die Anfrage geleitet wird.","cause":"Replica-Set-Mitglieder sind nicht erreichbar/ausgefallen, während einer laufenden Wahl gibt es keinen Primary, eine Netzwerkpartition, kein Mitglied erfüllt die Tag-Set-Kriterien oder eine Mehrheit (Quorum) ist nicht erreichbar.","fix":"Prüfe den Status des Replica Sets: rs.status(). Prüfe Zustand der Knoten und Netzwerkverbindung. Warte, bis die Wahl des Primary abgeschlossen ist, und wiederhole den Vorgang. Überprüfe deine Read-Preference-Einstellung; verwende bei Bedarf eine flexiblere Preference (z. B. primaryPreferred). Nutzt du Tag Sets, stelle sicher, dass ein passendes Mitglied existiert.","tags":["read preference","readpreference","133","replica set","election","secondary","primary"]},{"code":"6","title":"HostUnreachable (Fehlercode 6)","severity":"danger","meaning":"Treiber/Client konnte den MongoDB-Zielserver (Host) nicht erreichen. Auf Netzwerkebene konnte keine Verbindung hergestellt werden.","cause":"Der Server ist ausgefallen, falscher Host/Port, eine Firewall blockiert, ein DNS-Auflösungsproblem, ein Netzwerkausfall oder eine bindIp-Konfiguration, die den Client ausschließt.","fix":"Prüfe, ob Hostname und Port korrekt sind (Standard 27017). Prüfe, ob der Dienst mongod läuft. Teste Firewall und Netzwerkzugang (ping/telnet). Stelle sicher, dass die Einstellung bindIp in mongod.conf den Client-Zugriff erlaubt. Prüfe DNS-/SRV-Einträge (bei mongodb+srv-Verbindungen).","tags":["hostunreachable","host","connection","network","network","6","firewall","bindip"]},{"code":"89","title":"NetworkTimeout (Fehlercode 89)","severity":"danger","meaning":"Eine Netzwerkoperation in der Kommunikation mit dem Server wurde nicht in der vorgesehenen Zeit abgeschlossen und ist in einen Timeout gelaufen. Die Verbindung war hergestellt, aber die Antwort kam nicht rechtzeitig.","cause":"Ein langsames oder instabiles Netzwerk, ein überlasteter Server, eine lang laufende Operation, die die Antwort verzögert, zu niedrig gesetzte Socket-/Connect-Timeouts oder Netzwerküberlastung.","fix":"Miss Latenz und Stabilität des Netzwerks. Prüfe Serverlast und Ressourcen. Setze connectTimeoutMS und socketTimeoutMS im Treiber auf realistische Werte. Fange transiente Fehler ab und wiederhole. Optimiere langsame Abfragen, um die Antwortzeiten zu verkürzen.","tags":["networktimeout","network timeout","89","timeout","socket","network","retry"]},{"code":"262","title":"ExceededTimeLimit (Fehlercode 262)","severity":"caution","meaning":"Die Operation hat das serverseitig erlaubte Zeitlimit überschritten und wurde beendet. Das Überschreiten von maxTimeMS oder internen Zeitlimits kann diesen Fehler verursachen.","cause":"Eine lang laufende Abfrage bzw. ein lang laufender Befehl, langsame Ausführung wegen eines fehlenden Index, schwere Aggregation, Scan einer großen Datenmenge oder hohe Serverlast.","fix":"Untersuche die Abfrage mit explain() und lege einen passenden Index an. Teile die Operation in kleinere Stücke auf. Setze maxTimeMS entsprechend dem tatsächlichen Bedarf. Reduziere die Daten früh in der Aggregation-Pipeline mit $match/$project. Prüfe, ob die Serverressourcen ausreichen.","tags":["exceededtimelimit","time limit","262","timeout","maxtimems","slow","index"]},{"code":"292","title":"QueryExceededMemoryLimitNoDiskUseAllowed (Fehlercode 292)","severity":"caution","meaning":"Eine Abfrage oder Aggregationsstufe hat das erlaubte Speicherlimit (Standard 100 MB) überschritten und ist fehlgeschlagen, weil Schreiben auf Disk nicht erlaubt war. Tritt meist in Sort- oder Group-Stufen auf.","cause":"Sortierung im Speicher über große nicht indizierte Daten, große Aggregationsstufen $group/$sort, sehr große Zwischenergebnisse, die Einstellung allowDiskUse:false.","fix":"Erlaube in der Aggregation das Auslagern auf Disk mit der Option allowDiskUse:true: db.collection.aggregate(pipeline, {allowDiskUse:true}). Senke den Speicherverbrauch, indem du die sortierten Felder indizierst. Filtere die Pipeline früh mit $match, um die verarbeitete Datenmenge zu verringern. Paginiere Ergebnisse bei Bedarf. Ab MongoDB 6.0 ist die Disk-Nutzung in den meisten Fällen standardmäßig aktiviert.","tags":["memory limit","memory limit","292","allowdiskuse","sort","aggregation","group"]},{"code":"26","title":"NamespaceNotFound (Fehlercode 26)","severity":"safe","meaning":"Der referenzierte Namespace (database.collection) wurde nicht gefunden. Die angegebene Collection oder der Index existiert nicht.","cause":"Ein falsch geschriebener Collection-Name, eine noch nicht angelegte Collection, der falsche Datenbankkontext oder eine Drop-/Index-Operation auf einer nicht existierenden Collection.","fix":"Prüfe Collection-Name und Datenbank: show collections. Beachte, dass Namen zwischen Groß- und Kleinschreibung unterscheiden. MongoDB legt eine Collection beim ersten Schreiben automatisch an; erstelle sie bei Bedarf explizit mit db.createCollection(). Prüfe bei Operationen wie drop vorher, ob die Collection existiert.","tags":["namespacenotfound","namespace","collection missing","26","collection","not found","not found"]},{"code":"43","title":"CursorNotFound (Fehlercode 43)","severity":"caution","meaning":"Der serverseitige Cursor existiert nicht mehr. Wenn der Client den nächsten Batch anfordert (getMore), wird der Fehler zurückgegeben, weil der Cursor nicht gefunden wird.","cause":"Der Cursor ist in einen Timeout gelaufen (Standard 10 Minuten Inaktivität), zu lange Wartezeit zwischen Batches, ein Serverneustart oder der Cursor wurde explizit geschlossen.","fix":"Verarbeite Cursor-Ergebnisse schneller; warte nicht lange zwischen Batches. Verwende bei lang laufenden Operationen die Option noCursorTimeout mit Bedacht (mongosh: cursor.noCursorTimeout(); PyMongo: no_cursor_timeout=True) und schließe den Cursor immer; ein Cursor innerhalb einer Session wird auch mit dieser Option beim Session-Timeout geschlossen (localLogicalSessionTimeoutMinutes, Standard 30 min). Iteriere bei großen Ergebnismengen mit kleinerer batchSize oder verwende bereichsbasierte Pagination. Starte die Operation neu und frage erneut ab.","tags":["cursornotfound","cursor","cursor","43","getmore","timeout","batch","pagination"]},{"code":"211","title":"KeyNotFound (Fehlercode 211)","severity":"caution","meaning":"Ein erwarteter Schlüssel/Wert wurde nicht gefunden. Weist meist auf eine Inkonsistenz bei der internen Schlüsselverwaltung, den Session Keys oder der oplog-/keys-Collection hin.","cause":"Fehlende/gelöschte Signing Keys in der Collection admin.system.keys, Probleme mit der Zeitsynchronisation im Replica Set/Sharded Cluster, Probleme mit Keyfile/Schlüsselrotation oder beschädigter interner Zustand.","fix":"Prüfe die Zeitsynchronisation (NTP) zwischen den Replica-Set-Mitgliedern. Prüfe den Inhalt von admin.system.keys und die Schlüsselerzeugung. Überprüfe den Clusterstatus (rs.status(), sh.status()). Besteht das Problem weiter, analysiere die mongod-Logs und überprüfe die Keyfile-/Identitätskonfiguration. Stelle sicher, dass der Primary fehlerfrei läuft, damit die Schlüssel neu erzeugt werden können.","tags":["keynotfound","key","211","keys","session","ntp","cluster","cluster"]}]},"errors/redis.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"WRONGTYPE","title":"WRONGTYPE Operation against a key holding the wrong kind of value (Operation auf Schlüssel mit falschem Werttyp)","severity":"caution","meaning":"Ein Befehl wurde auf einen Schlüssel angewendet, der nicht zum Datentyp dieses Schlüssels passt. Zum Beispiel ein List- (LPUSH) oder Hash-Befehl (HSET) auf einen Schlüssel, der einen String enthält.","cause":"Der Schlüssel hat einen anderen Typ als erwartet, derselbe Schlüsselname wird mit unterschiedlichen Datentypen verwendet, Typverwechslung in der Anwendungslogik oder ein alter/übrig gebliebener Schlüssel wird überschrieben.","fix":"Prüfe den Schlüsseltyp: TYPE mykey. Wähle den Befehl entsprechend dem tatsächlichen Typ des Schlüssels. Unterscheide Typen in der Schlüsselbenennung (z. B. ein Set für user:1:tags, ein String für user:1:name). Lösche bei Bedarf den alten Schlüssel (DEL) und lege ihn mit dem richtigen Typ neu an. Setze Typkonsistenz in der Anwendungsschicht durch.","tags":["wrongtype","wrong type","type","data type","key","key"]},{"code":"OOM","title":"OOM command not allowed when used memory > 'maxmemory' (Befehl nicht erlaubt, wenn belegter Speicher > maxmemory)","severity":"danger","meaning":"Der von Redis genutzte Speicher hat das Limit maxmemory erreicht, und Schreibbefehle werden abgelehnt, weil die Eviction Policy keinen Platz für neue Daten freigeben kann. Der Speicher ist voll; Schreiben ist nicht möglich.","cause":"Das Limit maxmemory ist überschritten, die Policy noeviction ist gewählt (es wird nie ein Schlüssel entfernt), Daten ohne TTL wachsen weiter, ein Memory Leak oder zu wenig RAM für das Limit.","fix":"Untersuche die Speichernutzung: INFO memory und MEMORY USAGE. Wähle eine passende Eviction Policy (z. B. allkeys-lru oder volatile-lru): CONFIG SET maxmemory-policy allkeys-lru. Setze TTLs auf Schlüssel, damit sie automatisch bereinigt werden. Erhöhe maxmemory/RAM. Finde und verkleinere große Schlüssel (redis-cli --bigkeys). Lösche unnötige Daten.","tags":["oom","out of memory","memory","maxmemory","eviction","memory","danger"]},{"code":"NOAUTH","title":"NOAUTH Authentication required (Authentifizierung erforderlich)","severity":"caution","meaning":"Redis verlangt Authentifizierung, aber der Client hat versucht, einen Befehl vor der Authentifizierung auszuführen. Er muss sich zuerst mit AUTH anmelden.","cause":"Auf dem Server ist ein requirepass-/ACL-Passwort gesetzt, der Client sendet den Befehl AUTH nicht, das Passwort fehlt im Connection String oder AUTH wird nach dem Wiederaufbau der Verbindung nicht wiederholt.","fix":"Authentifiziere dich, bevor du Befehle ausführst: AUTH password (mit ACLs AUTH app_user password). Ergänze das Passwort im Client/Connection String. Nutzt du einen Connection Pool, stelle sicher, dass AUTH bei jeder neuen Verbindung ausgeführt wird. Speichere das Passwort sicher (Umgebungsvariable/Secrets Manager).","tags":["noauth","authentication","authentication","auth","password","requirepass","acl"]},{"code":"WRONGPASS","title":"WRONGPASS invalid username-password pair or user is disabled (ungültiges Benutzername-Passwort-Paar oder Benutzer deaktiviert)","severity":"caution","meaning":"Das im Befehl AUTH angegebene Paar aus Benutzername und Passwort ist ungültig oder der ACL-Benutzer ist deaktiviert. Die Authentifizierung wurde abgelehnt.","cause":"Falsches Passwort, falscher ACL-Benutzername, der Benutzer ist in der ACL deaktiviert, der Wert von requirepass hat sich geändert oder ein Escaping-/Kodierungsproblem im Passwort.","fix":"Gib den richtigen Benutzernamen und das richtige Passwort ein. Untersuche den ACL-Benutzer: ACL LIST und ACL GETUSER app_user. Ist der Benutzer deaktiviert, aktiviere ihn (ACL SETUSER app_user on ...). Prüfe das mit requirepass gesetzte Passwort (CONFIG GET requirepass). Stelle beim Zurücksetzen des Passworts sicher, dass alle Clients aktualisiert werden.","tags":["wrongpass","password","invalid password","auth","acl","user","invalid"]},{"code":"READONLY","title":"READONLY You can't write against a read only replica (Schreiben auf schreibgeschützte Replica nicht möglich)","severity":"caution","meaning":"Ein Schreibbefehl wurde an einen Replica-Knoten (Slave) gesendet. Replicas sind standardmäßig schreibgeschützt; Schreibzugriffe sind nur auf dem Master möglich.","cause":"Der Client verbindet sich mit einer Replica statt mit dem Master, der alte Master wurde nach einem Failover zur Replica, Fehlleitung an den falschen Knoten bei Sentinel/Cluster oder ein Load Balancer leitet Schreibzugriffe an eine Replica weiter.","fix":"Leite Schreibanfragen an den Master-Knoten. Nutzt du Sentinel, frage den aktuellen Master bei Sentinel ab (SENTINEL get-master-addr-by-name). Schreibe im Cluster auf den Master des richtigen Shards. Konfiguriere Read/Write-Splitting in der Client-Bibliothek. Aktualisiere nach einem Failover die Verbindungen, damit sie auf den neuen Master zeigen.","tags":["readonly","read only","replica","slave","write","write","master","failover"]},{"code":"MASTERDOWN","title":"MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no' (Verbindung zum Master unterbrochen; veraltete Daten werden nicht ausgeliefert)","severity":"danger","meaning":"Die Verbindung zwischen Replica und Master ist unterbrochen, und weil replica-serve-stale-data auf 'no' steht, verweigert die Replica die Beantwortung von Anfragen mit veralteten Daten.","cause":"Der Master-Knoten ist abgestürzt oder nicht erreichbar, ein Netzwerkausfall/eine Netzwerkpartition, der Replikationslink zwischen Master und Replica ist abgebrochen oder der Master kann unter hoher Last nicht antworten.","fix":"Prüfe Status und Netzwerkverbindung des Master-Knotens. Untersuche den Replikationsstatus: INFO replication. Bringe den Master wieder hoch oder lass einen Failover über Sentinel/Cluster zu. Ist das Ausliefern veralteter Daten akzeptabel, erwäge replica-serve-stale-data yes (beachte das Konsistenzrisiko). Verwende Sentinel/Cluster für Hochverfügbarkeit.","tags":["masterdown","master down","replication","replication","stale","failover","sentinel","danger"]},{"code":"CLUSTERDOWN","title":"CLUSTERDOWN The cluster is down / Hash slot not served (Cluster ausgefallen / Hash Slot nicht bedient)","severity":"danger","meaning":"Redis Cluster ist nicht voll betriebsbereit. Einige Hash Slots werden von keinem Knoten bedient, daher lehnt der Cluster Anfragen ab.","cause":"Nicht genügend Master-Knoten erreichbar, ein oder mehrere Slots nicht zugewiesen/nicht abgedeckt, Verlust der Mehrheit (Quorum), Knotenabstürze oder einige Slots nicht erreichbar, während cluster-require-full-coverage auf yes steht.","fix":"Prüfe den Clusterzustand: CLUSTER INFO und CLUSTER NODES. Bringe ausgefallene Master wieder hoch oder lass Replicas befördern. Weise nicht zugewiesene Slots neu zu (redis-cli --cluster fix oder CLUSTER ADDSLOTS). Prüfe, ob alle Slots abgedeckt sind. Ist Teilbetrieb akzeptabel, erwäge cluster-require-full-coverage no.","tags":["clusterdown","cluster","cluster","hash slot","slot","quorum","coverage","danger"]},{"code":"LOADING","title":"LOADING Redis is loading the dataset in memory (Redis lädt das Dataset in den Speicher)","severity":"caution","meaning":"Redis startet und lädt das Dataset (RDB/AOF) von Disk in den Speicher. Die meisten Befehle werden bis zum Abschluss des Ladens vorübergehend abgelehnt.","cause":"Der Server wurde gerade gestartet, eine große RDB-/AOF-Datei wird geladen, eine Replica führt einen Full Sync vom Master durch oder ein Recovery-Vorgang nach einem Neustart.","fix":"Warte, bis das Laden abgeschlossen ist; die Dauer hängt von der Datenmenge ab. Überwache den Status: INFO persistence (warte, solange loading:1; bereit bei 0). Fange in der Anwendung diesen transienten Fehler ab und wiederhole in kurzen Abständen. Um die Ladezeit zu verkürzen, überprüfe die Dataset-/Persistenzstrategie (RDB vs. AOF). Stelle ausreichend RAM und schnelle Platten bereit.","tags":["loading","loading","startup","startup","rdb","aof","sync","persistence"]},{"code":"BUSY","title":"BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE (Redis ist mit einem Skript beschäftigt)","severity":"caution","meaning":"Ein lang laufendes Lua-Skript oder eine Function wird ausgeführt und hat busy-reply-threshold überschritten (vor Redis 7 lua-time-limit; Standard 5000 ms). Da Redis single-threaded ist, kann es währenddessen keine anderen Befehle verarbeiten; nur SCRIPT KILL (bei Functions FUNCTION KILL) oder SHUTDOWN NOSAVE wird akzeptiert.","cause":"Eine Endlosschleife oder ein sehr lang laufendes Lua-Skript bzw. eine Function, aufwendige Berechnungen, ein Skript, das über große Datenmengen iteriert, oder ein schlecht geschriebener EVAL-Aufruf.","fix":"Hat das Skript keine Daten geändert, stoppe es: SCRIPT KILL. Hat das Skript bereits geschrieben, funktioniert SCRIPT KILL nicht; als letztes Mittel zur Wahrung der Datenkonsistenz kann SHUTDOWN NOSAVE nötig sein (nicht gespeicherte Daten gehen verloren; Vorsicht). Schreibe Skripte kurz und deterministisch. Teile lange Operationen in Stücke auf. Überprüfe die Einstellung busy-reply-threshold (früher lua-time-limit).","tags":["busy","busy server","lua","script","script kill","eval","time limit"]},{"code":"MISCONF","title":"MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk (RDB-Snapshots konfiguriert, Schreiben auf Disk derzeit nicht möglich)","severity":"danger","meaning":"Redis ist für RDB-Snapshots konfiguriert, kann aber nicht auf Disk schreiben. In diesem Zustand lehnt Redis alle Schreibbefehle ab, wenn stop-writes-on-bgsave-error aktiviert ist.","cause":"Eine volle Platte, Berechtigungsprobleme im Dateisystem, ein nicht beschreibbares Verzeichnis, Fehler beim Hintergrundspeichern (BGSAVE), fehlgeschlagener Fork wegen zu wenig Speicher oder ein Hardwareproblem der Platte.","fix":"Schaffe Plattenplatz und prüfe die Schreibberechtigung auf das RDB-Verzeichnis (dir). Untersuche den letzten BGSAVE-Fehler: INFO persistence und die Redis-Logs. Um Schreibzugriffe bis zur Behebung vorübergehend zuzulassen, kann stop-writes-on-bgsave-error no gesetzt werden (beachte das Risiko für die Dauerhaftigkeit). Sorge für ausreichend Speicher für den Fork und die Einstellung vm.overcommit_memory=1. Prüfe die Persistenzkonfiguration (save/appendonly).","tags":["misconf","persist","durability","rdb","bgsave","disk","snapshot","danger"]},{"code":"CROSSSLOT","title":"CROSSSLOT Keys in request don't hash to the same slot (Schlüssel der Anfrage liegen nicht im selben Slot)","severity":"caution","meaning":"In Redis Cluster wurde ein Multi-Key-Befehl (z. B. MGET, MSET, SUNION) verwendet, aber die Schlüssel fallen in unterschiedliche Hash Slots. Multi-Key-Operationen erfordern, dass alle Schlüssel im selben Slot liegen.","cause":"Im Cluster-Modus werden Befehle, die mehrere Schlüssel gleichzeitig ansprechen, mit über verschiedene Slots verteilten Schlüsseln aufgerufen. Die Schlüssel werden auf verschiedene Knoten verteilt, weil sie keinen Hash Tag verwenden.","fix":"Verwende einen Hash Tag, um zusammengehörige Schlüssel in denselben Slot zu legen: Fixiere einen Teil des Schlüsselnamens mit geschweiften Klammern (z. B. landen {user1}:profile und {user1}:settings im selben Slot). Verwende Multi-Key-Befehle nur mit Schlüsseln, die denselben Hash Tag teilen. Alternativ teile Operationen in Single-Key-Befehle auf.","tags":["crossslot","cross slot","cluster","cluster","hash tag","slot","mget","mset"]},{"code":"MOVED","title":"MOVED slot ip:port (Cluster-Umleitung)","severity":"safe","meaning":"In Redis Cluster gehört der Hash Slot mit dem angeforderten Schlüssel zu einem anderen Knoten. Der Server leitet den Client dauerhaft an den richtigen Knoten um. Das ist eher eine Umleitung als ein Fehler.","cause":"Ein nicht clusterfähiger Client sendet eine Anfrage an den falschen Knoten, Slots wurden zwischen Knoten neu verteilt (Resharding) oder die vom Client zwischengespeicherte Slot-Map ist veraltet.","fix":"Verwende eine clusterfähige Client-Bibliothek; diese folgen der MOVED-Antwort automatisch, leiten an den richtigen Knoten um und aktualisieren die Slot-Map. Verwende beim Verbinden mit redis-cli das Flag -c. Stelle sicher, dass dein Client die Slot-Topologie regelmäßig aktualisiert (CLUSTER SLOTS/SHARDS).","tags":["moved","cluster","cluster","redirect","redirect","slot","resharding"]},{"code":"ASK","title":"ASK slot ip:port (Cluster-Umleitung)","severity":"safe","meaning":"In Redis Cluster wird ein Slot auf einen anderen Knoten migriert, und der angeforderte Schlüssel kann vorübergehend auf dem Zielknoten liegen. Der Server leitet den Client nur für diese Anfrage an den Zielknoten um (temporäre Umleitung).","cause":"Zugriff auf einen Schlüssel, der innerhalb eines während des Reshardings migrierten Slots bereits übertragen wurde. Anders als MOVED ist dies nicht dauerhaft; es gilt nur in diesem Moment.","fix":"Clusterfähige Clients behandeln ASK-Umleitungen automatisch: Sie senden den Befehl ASKING an den Zielknoten und wiederholen die Anfrage, aktualisieren die Slot-Map aber nicht dauerhaft. Verwende eine geeignete Client-Bibliothek und für redis-cli das Flag -c. Ist das Resharding abgeschlossen, werden Umleitungen zu MOVED und dauerhaft.","tags":["ask","asking","cluster","cluster","migration","migration","redirect","redirect"]},{"code":"NOSCRIPT","title":"NOSCRIPT No matching script. Please use EVAL (kein passendes Skript; EVAL verwenden)","severity":"caution","meaning":"Der SHA1-Digest des mit EVALSHA aufgerufenen Lua-Skripts wurde nicht im Skript-Cache des Servers gefunden. Redis kennt kein Skript zu diesem Hash.","cause":"Der Server wurde neu gestartet oder der Skript-Cache mit SCRIPT FLUSH geleert, das Skript wurde nie geladen oder der Cache ist auf dem neuen Knoten nach einem Failover leer.","fix":"Führe das Skript mit vollständigem Inhalt über EVAL aus (Redis cacht es automatisch) oder lade es zuerst mit SCRIPT LOAD und verwende den zurückgegebenen SHA in EVALSHA. Fange in der Anwendung den Fehler NOSCRIPT ab und implementiere eine Fallback-Logik von EVALSHA auf EVAL. Viele Redis-Client-Bibliotheken machen das automatisch.","tags":["noscript","script","evalsha","eval","lua","sha","script load","cache"]},{"code":"EXECABORT","title":"EXECABORT Transaction discarded because of previous errors (Transaktion wegen vorheriger Fehler verworfen)","severity":"caution","meaning":"Eine Transaktion in einem MULTI/EXEC-Block wurde wegen Fehlern in der Queueing-Phase abgebrochen. EXEC lehnt die Transaktion ab, ohne einen Befehl auszuführen.","cause":"Ein Befehl zwischen MULTI und EXEC enthält einen Syntaxfehler, oder beim Queueing erkannte Fehler wie ein nicht existierender Befehlsname oder eine falsche Anzahl von Argumenten. Solche Fehler machen die gesamte Transaktion ungültig.","fix":"Korrigiere Syntax und Argumente der Befehle im MULTI-Block. Prüfe die Antworten beim Einreihen der Befehle; gibt ein Befehl statt QUEUED einen Fehler zurück, wird die Transaktion bei EXEC abgebrochen. Baue die Transaktion mit gültigen Befehlen neu auf. Hinweis: Redis-Transaktionen haben kein Rollback; nur Queueing-Fehler führen zu EXECABORT, und Laufzeitfehler stoppen die anderen Befehle nicht.","tags":["execabort","transaction","transaction","multi","exec","discard","queue","queue"]}]},"errors/elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"cluster_block_exception","title":"ClusterBlockException[blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block]] (vor 7.x: FORBIDDEN/12/index read-only / allow delete (api)) (Index wegen Flood Stage Watermark blockiert)","severity":"danger","meaning":"Weil die Plattenbelegung das Flood Stage Watermark überschritten hat (Standard 95 %; bei großen Platten durch max_headroom bestimmt), hat Elasticsearch auf Indizes mit einem Shard auf dem betroffenen Knoten einen read_only_allow_delete-Block gesetzt, damit die Platte nicht vollständig vollläuft. Wegen dieses Blocks sind Schreibzugriffe auf den Index (Dokument indexieren/aktualisieren/löschen) nicht möglich; nur das Löschen des Index selbst ist erlaubt. ES 8.x gibt diesen Fehler mit HTTP 429 zurück.","cause":"Die Plattenbelegung auf mindestens einem Knoten hat den Schwellenwert cluster.routing.allocation.disk.watermark.flood_stage überschritten. Selbst wenn der Block manuell entfernt wird, setzt Elasticsearch ihn erneut, solange die Platte noch voll ist. Große Indizes ohne Log-Rotation, nicht bereinigte Snapshots oder zu geringe Plattenkapazität sind typische Ursachen.","fix":"Schaffe zuerst Plattenplatz: Lösche alte/nicht benötigte Indizes (DELETE /old-index), erstelle einen Snapshot und entferne Indizes oder vergrößere die Platte. Ab 7.4 wird der Block automatisch entfernt, wenn die Plattenbelegung unter das High Watermark fällt; um ihn sofort ohne Warten aufzuheben, entferne ihn manuell: PUT /_all/_settings {\"index.blocks.read_only_allow_delete\": null}. Lösche/archiviere als dauerhafte Lösung alte Daten automatisch mit ILM (Index Lifecycle Management) und überprüfe die Watermark-Einstellungen (disk.watermark.low/high/flood_stage).","tags":["disk","read-only","watermark","flood-stage","cluster-block","danger"]},{"code":"index_read_only_allow_delete","title":"TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block (legacy: FORBIDDEN/12/index read-only / allow delete (api)) (Index schreibgeschützt wegen Flood Stage Watermark)","severity":"danger","meaning":"Der Index ist schreibgeschützt, weil index.blocks.read_only_allow_delete true ist; neue Dokumente können weder geschrieben noch aktualisiert werden, nur der Index kann gelöscht werden. Elasticsearch setzt dies meist automatisch, wenn das Flood Stage Watermark der Platte überschritten ist; ES 8.x gibt auf Schreibanfragen HTTP 429 zurück.","cause":"Der Block wurde automatisch gesetzt, weil die Plattenbelegung den Flood-Stage-Schwellenwert überschritten hat, oder die Einstellung wurde manuell auf true gesetzt. Wird der Block entfernt, ohne Plattenplatz freizugeben, setzt Elasticsearch ihn erneut.","fix":"Schaffe ausreichend Plattenplatz. Ab 7.4 hebt sich ein automatisch gesetzter Block selbst auf, wenn die Plattenbelegung unter das High Watermark fällt; entferne bei einem manuell gesetzten Block, oder um ihn ohne Warten aufzuheben, den Block: PUT /_all/_settings {\"index.blocks.read_only_allow_delete\": null}. Für einen einzelnen Index: PUT /index-name/_settings {\"index.blocks.read_only_allow_delete\": null}. Tritt es immer wieder auf, erhöhe die Plattenkapazität, richte eine ILM-Policy ein und überwache mit _cat/allocation, welcher Knoten vollläuft.","tags":["read-only","disk","forbidden","watermark","block","danger"]},{"code":"high_disk_watermark_exceeded","title":"high disk watermark [90%] exceeded / flood stage disk watermark [95%] exceeded (Disk Watermark überschritten)","severity":"danger","meaning":"Die Plattenbelegung eines Knotens hat den Schwellenwert High Watermark (Standard 90 %) oder Flood Stage (Standard 95 %) überschritten. Bei Überschreiten des High Watermark versucht Elasticsearch, Shards von diesem Knoten auf andere Knoten zu verschieben; bei Überschreiten der Flood Stage setzt es Indizes auf schreibgeschützt.","cause":"Die Platte läuft voll: kontinuierlich wachsende Log-/Metrik-Indizes, nie gelöschte alte Indizes, ein nicht bereinigtes Snapshot-Repository, eine unterdimensionierte Platte oder ein einzelner Knoten, der wegen ungleicher Shard-Verteilung überläuft.","fix":"Untersuche die Plattenbelegung: GET _cat/allocation?v. Lösche alte Indizes oder sichere sie per Snapshot und entferne sie, und vergrößere die Platte. Du kannst die Schwellenwerte vorübergehend anheben (vorsichtig; setze in 8.x wegen der max_headroom-Validierung alle drei gemeinsam): PUT _cluster/settings {\"persistent\":{\"cluster.routing.allocation.disk.watermark.low\":\"88%\",\"cluster.routing.allocation.disk.watermark.high\":\"93%\",\"cluster.routing.allocation.disk.watermark.flood_stage\":\"96%\"}}. Setze nach der Lösung dieselben Einstellungen auf null, um zu den Standardwerten zurückzukehren (transient-Einstellungen sind veraltet; verwende sie nicht). Dauerhafte Lösung: Lifecycle Management mit ILM, regelmäßige Bereinigung der Indizes und Kapazitätsplanung. Wurden Indizes wegen der Flood Stage schreibgeschützt, entferne den Block separat.","tags":["disk","watermark","flood-stage","allocation","capacity","danger"]},{"code":"circuit_breaking_exception","title":"CircuitBreakingException[[parent] Data too large, data for [<...>] would be [...], which is larger than the limit of [...]] (Daten zu groß; Circuit-Breaker-Limit überschritten)","severity":"danger","meaning":"Eine Anfrage wurde abgelehnt, weil sie das Limit des Speicher-Circuit-Breakers (Heap) überschreiten würde. Elasticsearch stoppt die Anfrage vorab, damit der Knoten nicht mit einem OutOfMemoryError abstürzt. Der Breaker [parent] überwacht die gesamte Speichernutzung, der Breaker [fielddata] den Fielddata-Speicher auf text-Feldern.","cause":"Zu wenig JVM-Heap, sehr große/komplexe Aggregationen, große Ergebnismengen, große Bulk-Requests, auf text-Feldern geladene Fielddata oder viele schwere parallele Abfragen, die den Heap füllen. Es ist meist ein Symptom für Heap-Druck.","fix":"Verkleinere die Abfragen: Reduziere Aggregationsgröße, Bulk-Batchgröße und zurückgegebene Felder. Aggregiere auf dem keyword-Unterfeld (.keyword) statt auf Fielddata von text-Feldern. Erhöhe den JVM-Heap (~50 % des Server-RAM, höchstens ~31GB). Überwache den Heap-Druck: GET _cat/nodes?v&h=name,heap.percent. Behebe die Grundursache (Speicher/Abfrage), statt die Breaker-Limits vorübergehend anzuheben. Prüfe mit GET _nodes/stats/breaker, welcher Breaker ausgelöst hat.","tags":["circuit-breaker","data-too-large","heap","memory","fielddata","oom","danger"]},{"code":"mapper_parsing_exception","title":"mapper_parsing_exception: failed to parse field [...] of type [...] (Feld konnte nicht geparst werden)","severity":"caution","meaning":"Beim Indexieren eines Dokuments passte der für ein Feld gesendete Wert nicht zu dem im Mapping für dieses Feld definierten Typ und konnte nicht geparst werden. Zum Beispiel Text an ein als integer definiertes Feld oder ein ungültiges Datumsformat an ein als date definiertes Feld.","cause":"Abweichender Feldtyp (ein String für ein numerisches Feld, ein fehlerhaftes Format für ein Datumsfeld), ein Datum, das nicht zum erwarteten Datumsformat passt, fehlerhaftes/unvollständiges JSON, dynamisch falsch typisierte Felder oder ein skalarer Wert, wo ein Objekt erwartet wird.","fix":"Prüfe Feldname und erwarteten Typ in der Fehlermeldung: GET /index-name/_mapping. Passe den Wert im Dokument an den Feldtyp an oder definiere das Mapping mit dem richtigen Typ neu. Stimme bei Datumsfeldern den Parameter format auf das Dokumentformat ab. Der Typ eines vorhandenen Felds kann nicht geändert werden; lege bei Bedarf einen neuen Index an und verschiebe die Daten mit _reindex. Validiere Daten in der Anwendungsschicht.","tags":["mapping","parse","mapper","field-type","indexing","caution"]},{"code":"strict_dynamic_mapping_exception","title":"strict_dynamic_mapping_exception: mapping set to strict, dynamic introduction of [...] within [...] is not allowed (striktes Mapping; neues Feld nicht erlaubt)","severity":"caution","meaning":"Weil das Index- oder Objekt-Mapping auf \"dynamic\": \"strict\" gesetzt ist, wurde ein Dokument mit einem neuen, im Mapping nicht definierten Feld abgelehnt. Der Strict-Modus verhindert, dass Felder außerhalb des Schemas stillschweigend hinzugefügt werden.","cause":"Im Dokument wurde ein im Mapping nicht vorhandenes Feld gesendet (Tippfehler, neues Feld, unerwartetes Feld), während das Mapping auf dynamic:strict steht. Meist eine bewusste Schutzmaßnahme für eine strikte Schemakontrolle.","fix":"Korrigiere oder entferne das überzählige/falsch geschriebene Feld im Dokument. Wird das Feld wirklich benötigt, füge es zuerst dem Mapping hinzu: PUT /index-name/_mapping {\"properties\": {\"new_field\": {\"type\": \"...\"}}}. Sollen neue Felder automatisch hinzugefügt werden, setze dynamic auf \"true\", oder auf \"false\", wenn sie gespeichert, aber nicht indexiert werden sollen. Setze den Strict-Modus bewusst ein, um Schemadisziplin zu wahren.","tags":["mapping","strict","dynamic","schema","field","caution"]},{"code":"illegal_argument_exception_fielddata","title":"illegal_argument_exception: Fielddata is disabled on text fields by default. Set fielddata=true on [your_field] in order to load fielddata in memory (Fielddata auf text-Feldern standardmäßig deaktiviert)","severity":"caution","meaning":"Es wurde eine Aggregation, Sortierung oder ein Skript auf einem analysierten text-Feld versucht. Diese Operationen sind auf text-Feldern standardmäßig deaktiviert, weil sie speicherintensive Fielddata erfordern.","cause":"Eine Aggregation/Sortierung wurde direkt auf ein text-Feld angewendet. In Elasticsearch wird text für Volltextsuche und keyword für Aggregation/Sortierung nach exakten Werten verwendet; eine Vermischung der beiden erzeugt diesen Fehler.","fix":"Verwende für Aggregation/Sortierung das keyword-Unterfeld des Felds (z. B. \"my_field.keyword\"). Ist das Feld nur text, füge dem Mapping ein keyword-Unterfeld hinzu (Multi-Field) und führe _reindex aus. Fielddata auf einem text-Feld zu aktivieren (fielddata=true) ist möglich, wegen hohen Speicherverbrauchs und Circuit-Breaker-Risiko aber nicht empfohlen; nutze es nur, wenn es unvermeidbar ist.","tags":["fielddata","text","keyword","aggregation","sorting","mapping","caution"]},{"code":"search_phase_execution_exception","title":"search_phase_execution_exception: all shards failed / Failed to execute phase [query] (Suche auf allen Shards fehlgeschlagen)","severity":"danger","meaning":"Eine Suchanfrage ist auf einem oder allen benötigten Shards fehlgeschlagen. Das ist ein generischer Suchfehler, der die eigentliche Ursache umhüllt; der Abschnitt \"caused by\" nennt das tatsächliche Problem (Parse-Fehler, Breaker, ungeeigneter Feldtyp, Shard nicht erreichbar usw.).","cause":"Ungültige Abfragesyntax, Aggregation auf dem falschen Feldtyp, too_many_clauses, ein ausgelöster Circuit Breaker, ein nicht zugewiesener/nicht erreichbarer Shard, ein Abfrage-Timeout oder ein Knotenabsturz. Die Grundursache steht in der inneren Exception der Antwort.","fix":"Finde die eigentliche Exception in den Details \"caused by\" / \"failed_shards\" der Antwort und behebe sie entsprechend (Abfrage korrigieren, keyword-Feld verwenden, Breaker-/Heap-Einstellungen, Shard-Allokation). Prüfe den Clusterzustand: GET _cluster/health und GET _cat/shards?v. Sind Teilergebnisse erlaubt und einige Shards fehlgeschlagen, überprüfe das Verhalten von allow_partial_search_results.","tags":["search","shard","search","all-shards-failed","query","danger"]},{"code":"too_many_clauses","title":"too_many_clauses: maxClauseCount is set to 1024 (query contains too many clauses) (zu viele Klauseln in der Abfrage)","severity":"caution","meaning":"Eine Abfrage hat die maximal erlaubte Anzahl von Lucene-Klauseln überschritten. In ES 8.x ist dieses Limit nicht fest; es wird automatisch aus Heap-Größe und Anzahl der Such-Threads des Knotens berechnet (auf Knoten mit kleinem Heap kann es auf 1024 sinken), und die Fehlermeldung zeigt den aktuellen Wert. Sehr große bool-Abfragen und Abfragen, die auf viele Terme expandieren, lösen es aus.","cause":"Programmatisch erzeugte bool-Abfragen mit Tausenden should/must-Klauseln; wildcard-/prefix-/fuzzy-/query_string-Abfragen, die auf viele Terme expandieren; tief verschachtelte bool-Strukturen. Hinweis: Die terms-Abfrage (eine Werteliste auf einem einzelnen Feld) unterliegt diesem Limit nicht; ihre Obergrenze ist index.max_terms_count (Standard 65.536).","fix":"Vereinfache die Abfrage: Wandle viele term-Klauseln auf einem einzelnen Feld in eine terms-Abfrage um (oder einen terms lookup bei großen Listen). Verwende spezifischere Abfragetypen oder index_prefixes/keyword-Felder statt wildcard/prefix. In ES 8.x ist die Einstellung indices.query.bool.max_clause_count veraltet und wird ignoriert; das Limit hebst du an, indem du den Heap des Knotens vergrößerst. Überprüfe die Logik zur Abfrageerzeugung.","tags":["too-many-clauses","max-clause-count","bool","terms","query","caution"]},{"code":"version_conflict_engine_exception","title":"version_conflict_engine_exception: [_doc][id]: version conflict, current version [x] is different than the one provided [y] (HTTP 409) (Versionskonflikt)","severity":"caution","meaning":"Eine Update-/Delete-Anfrage für ein Dokument wurde von der optimistischen Nebenläufigkeitskontrolle abgelehnt. Die Dokumentversion (bzw. _seq_no/_primary_term) stimmt nicht mit der in der Anfrage überein; jemand anderes hat das Dokument also nach deinem Lesen geändert. Liefert HTTP 409.","cause":"Parallele Updates desselben Dokuments, ein Schreibversuch mit veralteter version/seq_no, der Versuch, ein bereits existierendes Dokument anzulegen (op_type=create), oder eine Race Condition bei _bulk.","fix":"Lies das Dokument erneut und wiederhole die Anfrage mit den aktuellen Werten _seq_no und _primary_term (bzw. version) (Read-Modify-Write-Schleife). Verwende für automatische Wiederholungen den Parameter retry_on_conflict in der _update-Anfrage; übergib bei _update_by_query/_delete_by_query conflicts=proceed, um kollidierende Dokumente zu überspringen und fortzufahren. Ist nur Anlegen beabsichtigt, ist der Konflikt normal; fange 409 in der Anwendung ab und behandle ihn passend (wiederholen oder überspringen).","tags":["version-conflict","409","concurrency","optimistic","seq-no","caution"]},{"code":"document_missing_exception","title":"document_missing_exception: [_doc][id]: document missing (Dokument fehlt)","severity":"caution","meaning":"Das zu aktualisierende Dokument (_update) wurde im Index nicht gefunden. Ein Update erfordert ein vorhandenes Dokument; existiert es nicht, tritt diese Exception auf.","cause":"Das Dokument wurde zuvor gelöscht, nie angelegt, die falsche _id wurde verwendet, die Anfrage ging an den falschen Index oder auf ein mit benutzerdefiniertem Routing geschriebenes Dokument wurde ohne Routing zugegriffen. (GET und _update sind Echtzeit; eine Refresh-Verzögerung verursacht diesen Fehler nicht.)","fix":"Prüfe, ob _id und Zielindex korrekt sind: GET /index-name/_doc/id. Willst du Verhalten wie aktualisieren, falls vorhanden, sonst anlegen, verwende upsert (oder doc_as_upsert:true) in der _update-Anfrage. Lege das Dokument direkt mit index (PUT) an. Fange diese Exception in der Anwendung ab und weiche auf Upsert-Logik aus.","tags":["document-missing","update","upsert","document","not-found","caution"]},{"code":"index_not_found_exception","title":"index_not_found_exception: no such index [index_name] (HTTP 404) (Index nicht gefunden)","severity":"caution","meaning":"Der angeforderte Index (oder Alias) existiert nicht. Die Operation wurde mit HTTP 404 abgelehnt, weil kein Index mit dem angegebenen Namen gefunden wurde.","cause":"Der Index wurde nie angelegt, wurde gelöscht, der Index-/Aliasname enthält einen Tippfehler, der Client ist mit der falschen Umgebung (Cluster) verbunden oder es wurde in einen nicht existierenden Index geschrieben, während action.auto_create_index deaktiviert ist.","fix":"Liste vorhandene Indizes auf: GET _cat/indices?v. Prüfe Namen und Alias: GET _alias. Lege den Index an: PUT /index-name (mit den nötigen Mappings/Settings) oder konfiguriere die automatische Erstellung mit einem Index Template. Ist automatische Erstellung gewünscht, prüfe die Einstellung action.auto_create_index. Stelle sicher, dass du nicht mit dem falschen Cluster verbunden bist.","tags":["index-not-found","404","index","alias","not-found","caution"]},{"code":"resource_already_exists_exception","title":"resource_already_exists_exception: index [index_name/uuid] already exists (HTTP 400) (Index existiert bereits)","severity":"safe","meaning":"Der anzulegende Index (bzw. die Ressource) existiert bereits. Die Create-Anfrage wurde abgelehnt, weil ein Index mit demselben Namen existiert. In älteren Versionen erscheint er auch als index_already_exists_exception.","cause":"Versuch, denselben Index zweimal anzulegen, nicht idempotente Setup-Skripte, parallele Create-Anfragen oder der Versuch, denselben Namen mit anderen Einstellungen neu anzulegen.","fix":"Existiert der Index bereits, muss er nicht angelegt werden; fahre fort. Muss er neu angelegt werden, lösche ihn zuerst (DELETE /index-name) und lege ihn dann an (Vorsicht: Datenverlust). Mache Setup-Skripte idempotent: Prüfe zuerst mit GET _cat/indices, ob er existiert. Vermeide Konflikte, indem du Index Templates oder ILM/Rollover für die Schemaverwaltung verwendest.","tags":["already-exists","resource-already-exists","index","create","idempotent","safe"]},{"code":"cluster_health_red","title":"cluster health status: RED / unassigned shards (Clusterzustand RED / nicht zugewiesene Shards)","severity":"danger","meaning":"Clusterzustand RED bedeutet, dass mindestens ein Primary Shard nicht zugewiesen und nicht verfügbar ist. Das ist ein kritischer Zustand, in dem einige Daten nicht gelesen/geschrieben werden können, mit dem Risiko teilweisen Datenverlusts. (YELLOW bedeutet, dass nur Replicas nicht zugewiesen sind; Primaries sind intakt.)","cause":"Ein Knoten ist abgestürzt/ausgeschieden und die Primary Shards darauf blieben ohne Kopie, die Allokation ist durch das Disk Watermark blockiert, ein beschädigter Shard, ein Fehler beim Snapshot-Restore oder Allokationseinstellungen verhindern die Shard-Zuweisung.","fix":"Ermittle, warum Shards nicht zugewiesen sind: GET _cluster/allocation/explain oder GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason. Bringe abgestürzte Knoten zurück; bei einem Plattenproblem schaffe Platz (Watermark). War die Allokation vorübergehend blockiert, löse eine erneute Allokation aus: POST _cluster/reroute?retry_failed=true. Ist ein Primary vollständig verloren, stelle aus einem Snapshot wieder her. Konzentriere dich im Zustand RED zuerst auf die Wiederherstellung der Primaries.","tags":["cluster-health","red","unassigned","shard","primary","danger"]},{"code":"no_shard_available_action_exception","title":"no_shard_available_action_exception: No shard available for [...] (kein Shard verfügbar)","severity":"danger","meaning":"Es wurde keine aktive Shard-Kopie gefunden, die eine Anfrage bedienen kann. Die Anfrage kann nicht erfüllt werden, weil der betreffende Shard nicht zugewiesen ist, sich im Recovery befindet oder sein Knoten nicht erreichbar ist.","cause":"Ein Knotenabsturz/eine Netzwerkpartition, nicht zugewiesene Shards, ein laufendes Shard-Recovery, eine Allokationsblockade durch das Disk Watermark oder der Cluster im Zustand RED/YELLOW.","fix":"Untersuche Cluster- und Shard-Zustand: GET _cluster/health und GET _cat/shards?v. Ermittle, warum Shards nicht zugewiesen sind: GET _cluster/allocation/explain. Bringe abgestürzte Knoten zurück, behebe das Plattenproblem und warte, bis das Recovery abgeschlossen ist: GET _cat/recovery?v&active_only=true. Wiederhole bei Bedarf die Allokation mit POST _cluster/reroute?retry_failed=true.","tags":["no-shard-available","shard","unassigned","recovery","cluster","danger"]},{"code":"es_rejected_execution_exception","title":"es_rejected_execution_exception: rejected execution of ... on ... (queue capacity ...) (HTTP 429) (Ausführung abgelehnt; Queue voll)","severity":"danger","meaning":"Eine Operation wurde abgelehnt, weil die Queue-Kapazität des betreffenden Thread Pools (search, write/bulk usw.) erschöpft ist. Bei Überlast schützt sich Elasticsearch, indem es Anfragen abweist (Backpressure), und liefert HTTP 429 (Too Many Requests).","cause":"Sehr hohes paralleles Anfragevolumen, sehr große oder sehr häufige Bulk-Requests, hohe Aggregations-/Suchlast, zu wenig Knoten-/CPU-Ressourcen oder eine für die Last zu kleine Thread-Pool-Queue.","fix":"Wiederhole clientseitig mit exponentiellem Backoff; 429 ist normalerweise transient. Reduziere Bulk-Batchgröße und Senderate und senke die Parallelität. Überwache abgelehnte Operationen: GET _cat/thread_pool?v&h=node_name,name,active,queue,rejected. Füge bei dauerhafter Last Knoten/Ressourcen hinzu (horizontale Skalierung). Die Queue-Größe zu erhöhen ist das letzte Mittel; die Grundursache ist die Kapazität.","tags":["rejected-execution","429","thread-pool","queue","bulk","backpressure","danger"]},{"code":"master_not_discovered_exception","title":"master_not_discovered_exception / MasterNotDiscoveredException (kein Master gefunden)","severity":"danger","meaning":"Der Cluster konnte keinen aktiven Master-Knoten wählen/finden. Ohne Master können Shard-Allokation, Mapping-Änderungen und Aktualisierungen des Cluster State nicht stattfinden; der Cluster kann keine Operationen mehr verarbeiten.","cause":"Eine Mehrheit (Quorum) der master-eligible Knoten ist nicht erreichbar, eine Netzwerkpartition (Split-Brain-Vermeidung), falsche Konfiguration von discovery.seed_hosts / cluster.initial_master_nodes, kein Bootstrap bei einer Neuinstallation oder zu wenige master-eligible Knoten.","fix":"Prüfe, ob die master-eligible Knoten laufen und sich gegenseitig sehen: GET _cat/nodes?v und GET _cluster/state. Prüfe die Einstellungen discovery.seed_hosts und cluster.initial_master_nodes (bei der Ersteinrichtung). Prüfe Netzwerk/Firewall und die Verbindung zwischen den Knoten (Transport 9300). Halte genügend master-eligible Knoten (am besten 3) in Betrieb. Untersuche die Logs auf Meldungen zur Master-Wahl.","tags":["master","discovery","quorum","split-brain","cluster","danger"]},{"code":"security_exception","title":"security_exception: unable to authenticate / action [...] is unauthorized for user [...] (HTTP 401/403) (Authentifizierung/Autorisierung fehlgeschlagen)","severity":"danger","meaning":"Authentifizierung (401 Unauthorized) oder Autorisierung (403 Forbidden) ist fehlgeschlagen. 401 weist auf fehlende/falsche Zugangsdaten hin; 403 bedeutet, dass der Benutzer authentifiziert ist, ihm aber die nötige Rolle/Berechtigung für diese Aktion bzw. diesen Index fehlt.","cause":"Falscher Benutzername/falsches Passwort oder API Key, ein fehlender Authorization-Header, ein abgelaufenes/ungültiges Token, dem Benutzer fehlt die betreffende Index- oder Cluster-Berechtigung oder eine fehlende/falsche Rollenzuweisung.","fix":"Bei 401: Sende die richtigen Zugangsdaten/den richtigen API Key und prüfe den Authorization-Header. Bei 403: Untersuche Rollen und Berechtigungen des Benutzers (GET _security/user/app_user, GET _security/role/app_role) und ergänze die nötigen Index-/Cluster-Berechtigungen. Folge dem Least-Privilege-Prinzip. Nutzt du einen API Key, prüfe dessen Ablaufdatum und Berechtigungen.","tags":["security","401","403","authentication","authorization","security","danger"]},{"code":"validation_exception","title":"validation_exception / illegal_argument_exception: Validation Failed: ... (Validierung fehlgeschlagen)","severity":"caution","meaning":"Die Parameter oder Einstellungen einer Anfrage haben die Validierung nicht bestanden. Elasticsearch hat die Anfrage mit ungültigen oder inkonsistenten Werten abgelehnt, ohne sie auszuführen (z. B. ungültige Shard-Anzahl, inkompatible Kombination von Einstellungen, fehlendes Pflichtfeld).","cause":"Eine ungültige Index-Einstellung (z. B. negatives/null number_of_shards), der Versuch, eine nicht änderbare Einstellung auf einem vorhandenen Index zu aktualisieren, eine inkompatible Parameterkombination, ein fehlendes Pflichtfeld oder ein Wert außerhalb des gültigen Bereichs.","fix":"Lies das Validierungsdetail in der Fehlermeldung; es nennt klar, welcher Parameter ungültig ist und warum. Bringe den Wert in einen gültigen Bereich bzw. ein gültiges Format. Gib nicht änderbare (statische) Einstellungen beim Anlegen eines neuen Index an, nicht auf einem vorhandenen Index (bei Bedarf _reindex). Liefere die Pflichtfelder, die die API erwartet. Prüfe die dokumentierten gültigen Werte der Einstellung.","tags":["validation","validation","settings","illegal-argument","invalid","caution"]},{"code":"task_cancelled_exception","title":"task_cancelled_exception: Task was cancelled / TaskCancelledException (Task abgebrochen)","severity":"caution","meaning":"Ein laufender Task (Suche, _reindex, _update_by_query usw.) wurde abgebrochen. Ein Abbruch kann durch eine Benutzeranfrage an _tasks/_cancel erfolgen, dadurch, dass der Client die HTTP-Verbindung schließt (Client-Timeout/Abbruch; Elasticsearch bricht die zugehörige Suche automatisch ab), oder durch Abbruch eines übergeordneten Tasks bzw. eine abgelaufene asynchrone Suche.","cause":"Der Benutzer hat den Task mit POST _tasks/<task_id>/_cancel abgebrochen (X-Opaque-Id dient nur zur Identifizierung und hilft, den Task zu finden; es bricht ihn nicht ab), ein Client-Timeout hat die Verbindung getrennt, lang laufende teure Abfragen oder das adaptive Ressourcenmanagement hat einen schweren Task gestoppt.","fix":"Wurde der Task unerwartet abgebrochen, überprüfe Client-Timeout und Verbindungseinstellungen; optimiere die schwere Abfrage (weniger Daten, spezifischere Filter). Führe lange Operationen wie _reindex/_update_by_query asynchron aus (wait_for_completion=false) und überwache sie mit GET _tasks. Aktive Tasks anzeigen: GET _tasks?detailed=true. Teile teure Abfragen bei Bedarf auf oder verwende scroll/PIT.","tags":["task-cancelled","task","timeout","reindex","cancel","caution"]}]},"errors/oracle-more.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00604","title":"error occurred at recursive SQL level (Fehler auf rekursiver SQL-Ebene)","severity":"danger","meaning":"Bei rekursivem SQL, das Oracle intern im Hintergrund ausführt, ist ein Fehler aufgetreten. Das ist ein umhüllender (äußerer) Fehler, der den eigentlichen Fehler verdeckt; die tatsächliche Ursache steht im zweiten Fehlercode direkt darunter.","cause":"Zugriff auf das Data Dictionary, ein fehlschlagender DDL-Trigger, ein voller SYSTEM-Tablespace, zu wenig temporärer Platz, ein beschädigtes Data-Dictionary-Objekt oder ein fehlschlagender LOGON-Trigger, der bei der Session-Anmeldung läuft.","fix":"Lies im Error Stack die zweite Zeile nach ORA-00604; das ist die eigentliche Ursache, und behebe sie. Siehst du zum Beispiel ORA-01653, erweitere den Tablespace. Steht ein DDL-/LOGON-Trigger unter Verdacht, deaktiviere ihn vorübergehend: ALTER TRIGGER trigger_name DISABLE; Prüfe die Nutzung der Tablespaces SYSTEM und TEMP mit DBA_FREE_SPACE.","tags":["recursive","recursion","dictionary","data dictionary","trigger","wrapper"]},{"code":"ORA-01438","title":"value larger than specified precision allowed for this column (Wert größer als die für die Spalte erlaubte Precision)","severity":"caution","meaning":"In eine NUMBER-Spalte wurde eine Zahl geschrieben, die die definierten Grenzen für Precision und Scale überschreitet. Zum Beispiel passt ein Wert mit dreistelligem ganzzahligem Anteil nicht in eine NUMBER(4,2)-Spalte.","cause":"Die Definition NUMBER(p,s) der Spalte ist für die Daten zu knapp; ein Rechenergebnis ist größer als erwartet; die Nachkommastellen verringern die für den ganzzahligen Anteil verbleibenden Stellen (NUMBER(5,3) erlaubt nur 2 Vorkommastellen).","fix":"Erweitere die Spaltenkapazität passend zum tatsächlichen Datenbereich: ALTER TABLE my_table MODIFY (my_column NUMBER(12,2)); Runde den Wert in der Anwendung oder passe ihn mit ROUND/TRUNC an die Scale an. Um die überlaufende Zeile zu finden, grenze deine Abfrage beim Testen ein, und ergänze eine Eingabevalidierung.","tags":["number","precision","precision","scale","scale","overflow","numeric"]},{"code":"ORA-01400","title":"cannot insert NULL into (column) (NULL kann nicht in die Spalte eingefügt werden)","severity":"caution","meaning":"In eine Spalte mit NOT-NULL-Constraint wurde ein NULL-Wert eingefügt oder gesetzt. Ein Pflichtfeld darf nicht leer bleiben.","cause":"Kein Wert für eine Pflichtspalte in einem INSERT, ein UPDATE, das die Spalte auf NULL setzt, kein DEFAULT für die Spalte definiert oder ein leerer Wert aus einer Unterabfrage/der Anwendung.","fix":"Liefere einen gültigen Wert für die in der Fehlermeldung genannte Spalte. Definiere einen sinnvollen Standardwert für die Spalte: ALTER TABLE my_table MODIFY (my_column DEFAULT 0); (DEFAULT greift nur, wenn die Spalte im INSERT ganz weggelassen wird; für explizit gesendete NULL-Werte verwende ab 12c DEFAULT ON NULL: ALTER TABLE my_table MODIFY (my_column DEFAULT ON NULL 0);) Ist die Regel nicht wirklich zwingend, entferne den Constraint: ALTER TABLE my_table MODIFY (my_column NULL); Prüfe leere Werte in der Anwendung, bevor du sie sendest.","tags":["not null","null","empty","insert","constraint","constraint","mandatory"]},{"code":"ORA-02291","title":"integrity constraint violated - parent key not found (Integritäts-Constraint verletzt: Elternschlüssel nicht gefunden)","severity":"danger","meaning":"Ein Foreign-Key-Constraint wurde verletzt. Zum in die Kindtabelle eingefügten Wert wurde keine passende Elternzeile gefunden; der referenzierte Datensatz existiert also nicht.","cause":"Ein INSERT/UPDATE, das einen nicht existierenden Elterndatensatz referenziert, die Zeile der Elterntabelle wurde noch nicht eingefügt, falsche Ladereihenfolge (Kind vor Eltern) oder der Abgleich schlägt wegen Unterschieden bei Groß-/Kleinschreibung oder Leerzeichen fehl.","fix":"Füge zuerst den betreffenden Datensatz in die Elterntabelle ein, dann in die Kindtabelle. Prüfe, ob die Referenz existiert: SELECT * FROM parent_table WHERE key_col = value; Verarbeite Tabellen bei Massenladungen in der richtigen Reihenfolge (Eltern zuerst). Um die verletzende Zeile zu finden, vergleiche den Wert in der Kindtabelle per NOT IN / Anti-Join mit der Elterntabelle.","tags":["fk","foreign key","foreign key","constraint","constraint","parent","reference"]},{"code":"ORA-02292","title":"integrity constraint violated - child record found (Integritäts-Constraint verletzt: Kinddatensatz gefunden)","severity":"danger","meaning":"Eine Elternzeile wurde gelöscht oder ihr Schlüssel geändert, aber der Foreign-Key-Constraint ließ es nicht zu, weil abhängige Kinddatensätze existieren.","cause":"Versuch, eine Elternzeile mit Datensätzen in der Kindtabelle per DELETE zu löschen; der FK ist ohne ON DELETE CASCADE definiert; abhängige Datensätze wurden nicht zuerst bereinigt.","fix":"Lösche zuerst die abhängigen Kinddatensätze oder hänge sie an einen anderen Elterndatensatz, dann lösche die Elternzeile. Erlaubt es die Geschäftsregel, lege den Foreign Key mit ON DELETE CASCADE neu an: ALTER TABLE child DROP CONSTRAINT fk_name; ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (my_column) REFERENCES parent(key_col) ON DELETE CASCADE; Um abhängige Zeilen zu finden, frage die Kindtabelle über den Elternschlüssel ab.","tags":["fk","foreign key","foreign key","child","child record","delete","cascade"]},{"code":"ORA-00979","title":"not a GROUP BY expression (kein GROUP-BY-Ausdruck)","severity":"caution","meaning":"In der SELECT-Liste wurde eine nicht aggregierte Spalte zusammen mit einer Aggregatfunktion (SUM, COUNT usw.) verwendet, die Spalte aber nicht in die GROUP-BY-Klausel aufgenommen. Oracle weiß nicht, wie diese Spalte gruppiert werden soll.","cause":"Eine Spalte im SELECT, aber nicht im GROUP BY; Ausdrücke außerhalb von Aggregatfunktionen fehlen in der GROUP-BY-Liste; Inkonsistenz in HAVING oder in Select-Ausdrücken.","fix":"Nimm alle nicht aggregierten Spalten in die GROUP-BY-Liste auf: SELECT department, COUNT(*) FROM employee GROUP BY department; Alternativ umschließe diese Spalte ebenfalls mit einer Aggregatfunktion (MAX, MIN) oder gestalte die Gruppierung mit einer analytischen Funktion (OVER()) neu.","tags":["group by","grouping","aggregate","aggregation","sum","count","query"]},{"code":"ORA-00918","title":"column ambiguously defined (Spalte mehrdeutig definiert)","severity":"caution","meaning":"Eine Spalte mit demselben Namen existiert in mehr als einer Tabelle der Abfrage, und weil sie ohne Tabellen-/Alias-Präfix verwendet wird, kann Oracle nicht bestimmen, zu welcher Tabelle sie gehört.","cause":"Beide verknüpften Tabellen haben eine gleichnamige Spalte (z. B. ID in beiden); die Spalte wird in SELECT, WHERE oder ORDER BY unqualifiziert (ohne Präfix) verwendet.","fix":"Qualifiziere jede mehrdeutige Spalte mit Tabellennamen oder Alias: SELECT a.id, b.id FROM tableA a JOIN tableB b ON a.id = b.a_id; Gib Tabellen kurze Aliase und versieh alle Spalten mit Präfix, um Lesbarkeit und Klarheit zu verbessern.","tags":["ambiguous","ambiguous column","column","column","join","alias","query"]},{"code":"ORA-06550","title":"line/column PLS-... PL/SQL compilation error (PL/SQL-Kompilierfehler)","severity":"caution","meaning":"Beim Kompilieren eines PL/SQL-Blocks, einer Prozedur oder eines anonymen Blocks wurde ein Syntax- oder Semantikfehler gefunden. Der Code ist bereits in der Kompilierphase gescheitert, bevor er laufen konnte.","cause":"Tippfehler, ein Verweis auf eine nicht deklarierte Variable oder ein nicht deklariertes Objekt, ein Aufruf mit falscher Anzahl/falschem Typ von Parametern, ein fehlendes Semikolon, ein Aufruf einer nicht existierenden Prozedur bzw. eines nicht existierenden Packages oder eine fehlende Berechtigung.","fix":"Gehe zur Zeilen- und Spaltennummer in der Fehlermeldung; lies den Untercode PLS-XXXX. Alle Kompilierfehler anzeigen: SHOW ERRORS; oder SELECT * FROM user_errors WHERE name = 'OBJECT_NAME'; Prüfe Objektnamen, Parametersignaturen und die nötigen GRANTs und kompiliere dann neu.","tags":["plsql","compilation","compile","pls","syntax","procedure","code"]},{"code":"ORA-04068","title":"existing state of packages has been discarded (bestehender Package-Zustand wurde verworfen)","severity":"caution","meaning":"Während eine laufende Session den Zustand eines Packages nutzte, wurde das Package neu kompiliert, sodass dieser Zustand ungültig wurde. Die von der Session gehaltene Package-Instanz ist nicht mehr gültig.","cause":"Ein Package Body oder eine Spezifikation wurde mit ALTER/CREATE OR REPLACE neu kompiliert, während davon abhängige Sessions aktiv waren; tritt meist beim Deployment einer Anwendung auf einem Live-System auf. Zusammen mit ORA-04061/04065 zu sehen.","fix":"Ein erneuter Aufruf (Retry) löst es meist; beim zweiten Versuch wird das Package in seiner neuen Form geladen. Fange ORA-04068 in der Anwendung ab und wiederhole automatisch. Spiele Package-Änderungen in Wartungsfenstern mit geringer Benutzeraktivität ein. Ein zustandsloses Package-Design verringert dieses Problem.","tags":["package","package","state","package state","recompile","compile","retry"]},{"code":"ORA-01427","title":"single-row subquery returns more than one row (einzeilige Unterabfrage liefert mehr als eine Zeile)","severity":"caution","meaning":"Eine Unterabfrage, von der eine einzelne Zeile erwartet wurde, hat mehr als eine Zeile zurückgegeben. Zum Beispiel hat die Unterabfrage auf der rechten Seite eines Gleichheitsvergleichs (=) mehrere Werte geliefert.","cause":"Eine Unterabfrage, die mehrere Zeilen liefert, wo = oder eine Zuweisung erwartet wird; eine nicht ausreichend eingegrenzte WHERE-Bedingung; ein nicht eindeutiger Treffer in der Korrelation; eine skalare Unterabfrage in der SELECT-Liste, die mehrere Zeilen liefert.","fix":"Grenze die Unterabfrage so ein, dass sie eine einzelne Zeile liefert (zusätzliches WHERE, eindeutiger Schlüssel). Werden wirklich mehrere Werte erwartet, verwende IN statt =: WHERE id IN (SELECT id FROM ...). Oder ergänze in der Unterabfrage ein Aggregat (MAX, MIN) oder reduziere sie mit ROWNUM = 1 auf eine Zeile. Führe die Unterabfrage zuerst allein aus und prüfe, wie viele Zeilen sie liefert.","tags":["subquery","subquery","single row","single row","in","scalar","query"]},{"code":"ORA-12899","title":"value too large for column (Wert zu groß für Spalte)","severity":"caution","meaning":"In eine Zeichen-/Datenspalte wurde ein Wert geschrieben, der länger als die definierte Maximallänge ist. Zum Beispiel passt ein 15 Zeichen langer Text nicht in eine VARCHAR2(10)-Spalte.","cause":"Die Spaltenbreite reicht für die Daten nicht aus; bei einem Multibyte-Zeichensatz (UTF8) übersteigt die Zeichenanzahl das Byte-Limit (BYTE-Semantik); ein anderer Zeichensatz bei der Datenmigration; ein Verkettungsergebnis wird länger.","fix":"Verbreitere die Spalte: ALTER TABLE my_table MODIFY (my_column VARCHAR2(100)); Verwende für Multibyte-Inhalte CHAR-Semantik: my_column VARCHAR2(100 CHAR). Müssen lange Werte gekürzt werden, schneide sie mit SUBSTR ab. Die Fehlermeldung nennt die tatsächliche und die erlaubte Länge; finde den überlaufenden Datensatz mit LENGTH/LENGTHB.","tags":["varchar2","length","length","too large","column","column","charset"]},{"code":"ORA-30926","title":"unable to get a stable set of rows in the source tables (keine stabile Zeilenmenge in den Quelltabellen)","severity":"danger","meaning":"In einem MERGE-Statement (oder einem aktualisierbaren Join) hat die Quellseite für dieselbe Zielzeile mehr als eine passende Zeile geliefert. Oracle hat die Operation gestoppt, weil es nicht entscheiden kann, mit welchem Quellwert aktualisiert werden soll.","cause":"Die Quelle in MERGE ... USING enthält doppelte Treffer für die ON-Bedingung; die Quellabfrage ist auf dem Zielschlüssel nicht eindeutig; mehrere Quellzeilen werden derselben Zielzeile zugeordnet.","fix":"Mache die Quellabfrage auf dem Zielschlüssel eindeutig. Doppelte Treffer finden: SELECT key_col, COUNT(*) FROM (source_query) GROUP BY key_col HAVING COUNT(*) > 1; Dedupliziere die Quelle mit DISTINCT oder einer Aggregat-/analytischen Funktion (ROW_NUMBER) und führe das MERGE erneut aus. Stelle sicher, dass die ON-Bedingung selektiv genug ist.","tags":["merge","merge","duplicate","duplicate rows","source","source","stable"]},{"code":"ORA-08103","title":"object no longer exists (Objekt existiert nicht mehr)","severity":"danger","meaning":"Ein von einer laufenden Abfrage verwendetes Objekt (Tabelle, Index, LOB-Segment) existiert nicht mehr oder seine physische Struktur hat sich geändert. Meist lief eine DDL/ein TRUNCATE auf dem Objekt, während die Abfrage noch lief.","cause":"Dasselbe Objekt wurde bei offener Abfrage per TRUNCATE/DROP/SHRINK bearbeitet; ein Verschieben (MOVE) oder Partitionswartung; ein beschädigter Index bzw. ein beschädigtes Segment; die zugrunde liegende Struktur verschwindet, während auf ein LOB-Segment zugegriffen wird; ein Konflikt mit paralleler DDL.","fix":"Prüfe, ob das Objekt noch existiert und gültig ist: SELECT status FROM user_objects WHERE object_name = 'NAME'; Baue den betreffenden Index bzw. das Objekt neu auf (REBUILD) oder führe die Abfrage erneut aus. Vermeide TRUNCATE/DROP auf Objekten, während lang laufende Abfragen sie nutzen. Besteht Verdacht auf Korruption, prüfe mit ANALYZE ... VALIDATE STRUCTURE.","tags":["object","object","truncate","drop","ddl","corruption","segment"]},{"code":"ORA-01843","title":"not a valid month (kein gültiger Monat)","severity":"caution","meaning":"Beim Konvertieren von Text in ein DATE konnte der Monatsteil nicht als gültiger Monat interpretiert werden. Meist passt das Datumsformat nicht zum Format der Daten.","cause":"An TO_DATE wurde keine Formatmaske übergeben und das NLS_DATE_FORMAT der Session passt nicht zu den Daten; Tag und Monat vertauscht (Verwechslung DD/MM vs. MM/DD); ein Sprach-/Locale-Unterschied (bei Monatsnamen); beschädigter oder unerwarteter Datumstext.","fix":"Übergib in TO_DATE-Aufrufen immer ein explizites Format: TO_DATE('2026-07-04', 'YYYY-MM-DD'). Gib bei Text mit Monatsnamen die Sprache an: TO_DATE(str, 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH'). Setze bei Bedarf das Session-Format: ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'. Prüfe ungültige Datumstexte vor dem Laden.","tags":["date","date","to_date","nls","format","month","month"]},{"code":"ORA-14400","title":"inserted partition key does not map to any partition (eingefügter Partitionsschlüssel passt zu keiner Partition)","severity":"danger","meaning":"Der Partitionsschlüsselwert der eingefügten Zeile lässt sich keiner auf der Tabelle definierten Partition zuordnen. Es gibt keine Range- oder List-Partition, die den Wert abdeckt.","cause":"Bei RANGE-Partitionierung wurde ein Datum/Wert oberhalb des höchsten Bereichs eingefügt und es gibt keine MAXVALUE-Partition; bei LIST-Partitionierung ein neuer, nicht definierter Wert; die neue Partition wurde an einer Monats-/Jahresgrenze nicht vorab angelegt.","fix":"Füge die fehlende Partition hinzu: ALTER TABLE my_table ADD PARTITION p_2026_08 VALUES LESS THAN (TO_DATE('2026-09-01','YYYY-MM-DD')); Ergänze bei RANGE eine MAXVALUE-Partition als Sicherheitsnetz. Definiere bei LIST eine DEFAULT-Partition, um unerwartete Werte aufzufangen. Verwende automatische Partitionierung (INTERVAL-Partitionierung), damit neue Partitionen automatisch angelegt werden.","tags":["partition","partition","partition key","range","list","maxvalue","insert"]},{"code":"ORA-01536","title":"space quota exceeded for tablespace (Platzkontingent für Tablespace überschritten)","severity":"danger","meaning":"Das Platzkontingent (Quota) des Benutzers im angegebenen Tablespace ist erschöpft. Selbst wenn der Tablespace freien Platz hat, kann kein neuer Platz allokiert werden, weil das dem Benutzer gewährte Limit überschritten ist.","cause":"Der Benutzer hat eine begrenzte Quota (nicht UNLIMITED); die Quota ist durch Datenwachstum vollgelaufen; eine übermäßige Ladung in eine Tabelle/einen Index; Schreiben in den falschen Tablespace; Segmente wachsen schneller als erwartet.","fix":"Erhöhe die Quota des Benutzers: ALTER USER app_user QUOTA UNLIMITED ON tablespace_name; oder gib einen bestimmten Wert an: QUOTA 500M ON users. Aktuelle Nutzung anzeigen: SELECT * FROM dba_ts_quotas WHERE username = 'APP_USER'. Archiviere/lösche unnötig große Segmente oder verschiebe Objekte in einen passenden Tablespace.","tags":["quota","quota","tablespace","space","space","user","user"]},{"code":"direct path read","title":"Wait: Session liest Blöcke unter Umgehung des Buffer Cache direkt in die PGA","severity":"caution","meaning":"Die Session verbringt Zeit in diesem Wait Event, während sie Blöcke direkt in den PGA-Speicher liest statt über den Database Buffer Cache. Meist im Zusammenhang mit großen Full Table Scans und Parallel Queries.","cause":"Viele von Disk gelesene Blöcke wegen Full Scans großer Tabellen, Parallel Queries, Sort-/Hash-Operationen mit Auslagerung in den temporären Tablespace, die Entscheidung für Serial Direct Path Read bei seriellen Full Scans und Abfragen, die keinen Index nutzen können.","fix":"Ermittle mit AWR/ASH die SQL-Statements mit schweren Full Scans und lege passende Indizes an. Reduziere unnötige Scans großer Tabellen durch Query-Tuning. Untersuche die Storage-Latenz (I/O) und wechsle bei Bedarf auf schnellere Platten. Überprüfe den Parallelitätsgrad. Bewerte bei Operationen mit Auslagerung nach TEMP die Größe von PGA_AGGREGATE_TARGET.","tags":["wait","wait","direct path read","full scan","full table scan","io","pga"]},{"code":"latch: cache buffers chains","title":"Wait: Konkurrenz um den Latch cache buffers chains","severity":"danger","meaning":"Es gibt Konkurrenz um den Latch cache buffers chains (CBC), der Blöcke im Buffer Cache schützt. Dieser Wait steigt, wenn viele Sessions dieselben häufig genutzten (heißen) Blöcke erreichen wollen.","cause":"Häufig gelesene Hot Blocks; ineffizientes SQL mit vielen Logical Reads; wiederholter Zugriff auf dieselben Index-Root-Blöcke in Nested Loop Joins; ungleich verteilter starker paralleler Zugriff; kleine, aber stark gelesene Tabellen.","fix":"Finde das SQL mit den meisten Logical Reads (V$SQL, ASH) und verringere den Buffer-Zugriff durch bessere Indizes/einen besseren Zugriffsplan. Ermittle Hot Blocks (X$BH / zugehörige Views). Erwäge, unnötige Nested Loops in Hash Joins umzuwandeln. Verteile bei kleinen, stark gelesenen Referenztabellen das Zugriffsmuster oder cache sie.","tags":["latch","cache buffers chains","cbc","contention","contention","hot block","wait","wait"]}]},"errors/postgres-more.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"42883","title":"undefined_function","severity":"caution","meaning":"Die in der Abfrage aufgerufene Funktion (oder der Operator) wurde mit diesem Namen und diesen Argumenttypen nicht gefunden. PostgreSQL löst Funktionen sowohl über den Namen als auch über die Argumenttypen auf; passt die Signatur nicht, wird dieser Fehler ausgelöst.","cause":"Ein falsch geschriebener Funktionsname; Argumenttypen, die nicht zur definierten Signatur passen (z. B. eine Zahl statt Text); ein fehlender expliziter Cast; das Schema der Funktion ist nicht im search_path; die benötigte Extension ist nicht installiert.","fix":"Prüfe, ob die Funktion existiert, und kontrolliere ihre Signatur: \\df function_name. Caste die Argumente explizit: SELECT fn(value::text). Ergänze das Schemapräfix oder setze den search_path. Wird eine Extension benötigt, installiere sie: CREATE EXTENSION extension_name; Gleiche bei Operatorfehlern die Typen auf beiden Seiten an.","tags":["function","function not found","undefined","operator","cast","type conversion","42883"]},{"code":"42804","title":"datatype_mismatch","severity":"caution","meaning":"Erwarteter und gelieferter Datentyp stimmen nicht überein. Zum Beispiel die Zuweisung eines Werts falschen Typs an eine Spalte, CASE-Zweige mit unterschiedlichen Rückgabetypen oder UNION-Zweige mit nicht übereinstimmenden Typen.","cause":"Eine mit dem Spaltentyp inkompatible Zuweisung; CASE-/COALESCE-Zweige liefern unterschiedliche Typen; einander entsprechende Spalten in einer UNION haben unterschiedliche Typen; ein per RETURN zurückgegebener Wert passt nicht zum Rückgabetyp der Funktion; kein boolean, wo ein boolean erwartet wird.","fix":"Caste den Ausdruck in den richtigen Typ: value::integer oder CAST(value AS numeric). Sorge dafür, dass alle CASE-Zweige denselben Typ liefern. Gleiche in einer UNION die Typen einander entsprechender Spalten auf beiden Seiten an. Stelle sicher, dass die WHERE-Bedingung einen boolean ergibt.","tags":["type","data type","mismatch","type mismatch","cast","union","42804"]},{"code":"22P02","title":"invalid_text_representation","severity":"caution","meaning":"Ein Textwert war bei der Konvertierung keine gültige Darstellung für den Zieldatentyp. Zum Beispiel löst der Versuch, den Text 'abc' in integer zu konvertieren, diesen Fehler aus.","cause":"Nicht-numerischer Text an eine numerische Spalte/einen numerischen Parameter; ein leerer String ('') wird in eine Zahl/ein Datum konvertiert; ungültiger Text für eine UUID oder ein Enum; fehlerhaftes JSON; Eingabedaten nicht im erwarteten Format.","fix":"Prüfe, ob der konvertierte Wert tatsächlich gültig ist. Validiere Eingaben in der Anwendung vor der Typkonvertierung. Wandle leere Strings in NULL um: NULLIF(value, ''). Teste für eine sichere Konvertierung ab PostgreSQL 16 den Wert vorab mit pg_input_is_valid('abc', 'integer') und nutze pg_input_error_info() für Fehlerdetails; erwäge in älteren Versionen eine Validierungsprüfung (Regex). Grenze ein, welche Zeile fehlerhaft ist.","tags":["cast","type conversion","invalid text","invalid input","integer","uuid","22P02"]},{"code":"22003","title":"numeric_value_out_of_range","severity":"caution","meaning":"Ein numerischer Wert liegt außerhalb des vom Zieltyp erlaubten Bereichs. Zum Beispiel ein Wert über 2 Milliarden in einer integer-Spalte oder eine für eine numeric(5,2)-Spalte zu große Zahl.","cause":"Überschreiten der Grenzen von smallint/integer/bigint; Überschreiten von Precision/Scale bei numeric(p,s); arithmetischer Überlauf; eine Summe/ein Produkt übersteigt die Kapazität des Typs; eine Sequence erreicht die Obergrenze ihres Typs.","fix":"Ändere die Spalte bzw. den Parameter auf einen breiteren Typ (bigint statt integer, höhere numeric-Precision): ALTER TABLE my_table ALTER COLUMN my_column TYPE bigint; Runde (ROUND) oder validiere den Wert gemäß der Geschäftsregel. Läuft eine Zwischenberechnung über, berechne den Ausdruck als numeric. Erweitere bei einem Sequence-Überlauf den Typ.","tags":["numeric","number","out of range","overflow","numeric overflow","integer","22003"]},{"code":"23514","title":"check_violation","severity":"caution","meaning":"Ein CHECK-Constraint wurde verletzt. Die eingefügte oder aktualisierte Zeile erfüllt eine auf der Tabelle definierte logische Bedingung nicht (z. B. price > 0).","cause":"Daten, die eine Geschäftsregel verletzen (negativer Preis, ungültiger Statuscode, Verletzung eines Datumsbereichs); keine Validierung auf Anwendungsseite; ein für die Daten zu strikter Constraint; Datensätze, die bei einer Massenladung die Regel verletzen.","fix":"Ermittle über den Constraint-Namen in der Fehlermeldung, welche Bedingung verletzt wurde; Definition anzeigen: \\d table_name. Frage die verletzenden Zeilen ab, indem du die Bedingung umkehrst. Korrigiere die Daten oder, falls sich die Regel tatsächlich geändert hat, aktualisiere den Constraint: ALTER TABLE my_table DROP CONSTRAINT name; ALTER TABLE my_table ADD CONSTRAINT name CHECK (new_condition);","tags":["check","constraint","check constraint","violation","constraint violation","validation","23514"]},{"code":"22012","title":"division_by_zero","severity":"caution","meaning":"Ein arithmetischer Ausdruck hat versucht, durch null zu dividieren (oder Modulo null zu bilden). PostgreSQL erlaubt keine Division durch null und bricht die Abfrage ab.","cause":"Die Divisor-Spalte bzw. der Divisor-Ausdruck ergibt 0; ein Nenner von null in einer Verhältnis-/Prozentberechnung; Division durch ein COUNT oder SUM mit dem Wert null; unerwartete Nullwerte in ungefilterten Daten.","fix":"Verwende NULLIF, um bei einem Nenner von null NULL zurückzugeben: numerator / NULLIF(denominator, 0). Mache das Ergebnis bei Bedarf mit COALESCE zu einem Standardwert: COALESCE(numerator / NULLIF(denominator,0), 0). Oder verwende CASE WHEN denominator = 0 THEN NULL ELSE numerator/denominator END. Filtere Fälle mit null vorab aus den Daten.","tags":["division","divide","zero","divide by zero","nullif","arithmetic","22012"]},{"code":"42601","title":"syntax_error","severity":"caution","meaning":"Im SQL-Statement wurde ein Syntaxfehler gefunden. PostgreSQL konnte die Abfrage nicht parsen und meldet die ungefähre Position (position) des Fehlers.","cause":"Ein fehlendes/überzähliges Komma oder eine Klammer; ein reserviertes Schlüsselwort (wie user, order) als Spaltenname ohne Anführungszeichen; ein fehlendes Semikolon; ein falsch geschriebener Befehl; ein nicht abgeschlossenes String-Literal; Verwendung versionsspezifischer Syntax.","fix":"Lokalisiere das Problem über den Wert 'position' in der Fehlermeldung; untersuche den Teil direkt davor. Setze reservierte Wörter in doppelte Anführungszeichen: \"order\". Prüfe, ob Klammern und Anführungszeichen ausgeglichen sind. Teste eine komplexe Abfrage, indem du sie in Teile zerlegst. Ziehe für die korrekte Syntax die Dokumentation deiner Version heran.","tags":["syntax","syntax error","parse","parser","keyword","position","42601"]},{"code":"42P07","title":"duplicate_table","severity":"caution","meaning":"Die anzulegende Tabelle (oder ein ähnliches Objekt) existiert bereits. CREATE ist fehlgeschlagen, weil ein Objekt mit demselben Namen vorhanden ist.","cause":"Erneutes Anlegen einer vorhandenen Tabelle mit CREATE TABLE; das Skript wurde zweimal ausgeführt; erneutes Anwenden von Migrationen; ein Namenskonflikt mit einer gleichnamigen View/Sequence/einem gleichnamigen Index.","fix":"Verwende IF NOT EXISTS, um das Anlegen zu überspringen, wenn das Objekt bereits existiert: CREATE TABLE IF NOT EXISTS my_table (...). Willst du es wirklich neu anlegen, lösche es zuerst: DROP TABLE IF EXISTS my_table; Mache Migrationen sicher wiederholbar (idempotent). Prüfe das vorhandene Objekt mit \\dt.","tags":["duplicate","duplicate object","table","relation","create","already exists","42P07"]},{"code":"42701","title":"duplicate_column","severity":"caution","meaning":"Derselbe Spaltenname wurde in einer Tabellendefinition oder ALTER-Operation mehr als einmal verwendet, oder eine bereits vorhandene Spalte wurde erneut hinzugefügt.","cause":"Derselbe Spaltenname zweimal in CREATE TABLE; Hinzufügen einer bereits vorhandenen Spalte mit ALTER TABLE ADD COLUMN; ein Copy-Paste-Fehler; eine erneut laufende Migration.","fix":"Entferne den doppelten Spaltennamen in der Tabellendefinition oder benenne ihn um. Prüfe vorher, damit keine vorhandene Spalte erneut hinzugefügt wird: \\d table_name. Füge, wo die Version es unterstützt, sicher hinzu: ALTER TABLE my_table ADD COLUMN IF NOT EXISTS my_column type; Stelle sicher, dass Migrationen idempotent sind.","tags":["duplicate","duplicate column","column","column exists","alter","add column","42701"]},{"code":"3D000","title":"invalid_catalog_name","severity":"danger","meaning":"Die Datenbank, mit der du dich verbinden oder auf die du zugreifen willst, existiert nicht. Es wurde keine Datenbank mit dem angegebenen Namen gefunden.","cause":"Ein falsch geschriebener Datenbankname im Connection String; die Datenbank wurde noch nicht angelegt oder gelöscht; Groß-/Kleinschreibung; Verbindung zum falschen Server; ein falscher Name in der Umgebungskonfiguration (env).","fix":"Vorhandene Datenbanken auflisten: psql -l oder SELECT datname FROM pg_database. Korrigiere den Datenbanknamen im Connection String. Lege sie bei Bedarf an: CREATE DATABASE database_name; Prüfe, ob du dich mit dem richtigen Server/Port verbindest. Prüfe den DB-Namen in der Anwendungskonfiguration.","tags":["database","database not found","catalog","does not exist","missing","connection","3D000"]},{"code":"3F000","title":"invalid_schema_name","severity":"caution","meaning":"Das referenzierte Schema existiert nicht oder ist im search_path nicht auffindbar. Es gibt kein Schema mit dem angegebenen Namen.","cause":"Ein falsch geschriebener Schemaname; das Schema wurde nie angelegt; der search_path enthält das Schema nicht; ein Unterschied bei Groß-/Kleinschreibung; ein falsches Präfix beim Zugriff auf ein Objekt mit Schemapräfix.","fix":"Vorhandene Schemas auflisten: \\dn oder SELECT schema_name FROM information_schema.schemata. Korrigiere den Schemanamen. Lege es bei Bedarf an: CREATE SCHEMA schema_name; Setze den search_path: SET search_path TO schema_name, public; Greife mit dem richtigen Schemapräfix auf Objekte zu.","tags":["schema","schema not found","search_path","does not exist","missing","invalid","3F000"]},{"code":"0A000","title":"feature_not_supported","severity":"caution","meaning":"Das Feature, das du verwenden willst, wird in diesem Kontext oder in dieser PostgreSQL-Version nicht unterstützt. Auch wenn die angeforderte Operation gültig sein mag, kann sie in der aktuellen Situation nicht angewendet werden.","cause":"Syntax/Feature in der Version noch nicht verfügbar; ein für einen bestimmten Spalten-/Tabellentyp nicht unterstütztes ALTER; eine von einem Foreign Data Wrapper (FDW) nicht unterstützte Operation; Einschränkungen bei Kombinationen aus logischer Replikation und Constraints; für bestimmte Datentypen nicht erlaubte Konvertierungen.","fix":"Lies die vollständige Fehlermeldung; sie nennt meist klar, was nicht unterstützt wird. Wähle einen alternativen Ansatz (z. B. neue Spalte + Kopieren + Umbenennen, statt den Spaltentyp direkt zu ändern). Kam das Feature in einer neueren Version, erwäge ein Upgrade. Stößt du an FDW-/Extension-Grenzen, prüfe die Dokumentation.","tags":["not supported","unsupported","feature","unsupported feature","version","upgrade","0A000"]},{"code":"57P03","title":"cannot_connect_now","severity":"danger","meaning":"Der Server kann gerade keine Verbindungen annehmen; meist startet die Datenbank noch oder führt ein Recovery durch. Verbindungen können geöffnet werden, sobald das System bereit ist.","cause":"PostgreSQL wurde gerade gestartet und befindet sich noch in der Start-/Recovery-Phase; Crash Recovery; während eines Restores aus einem Backup; ein Standby, der noch nicht bereit für Abfragen ist; ein laufendes Herunterfahren.","fix":"Warte kurz und verbinde dich erneut; der Server nimmt Verbindungen an, sobald der Start abgeschlossen ist. Überwache den Recovery-Fortschritt im Serverlog. Dauert es lange, kann ein großes WAL-Replay oder ein Recovery-Problem vorliegen. Ergänze die Anwendung um eine Wiederholungslogik.","tags":["connection","connection refused","starting up","startup","recovery","crash recovery","57P03"]},{"code":"2BP01","title":"dependent_objects_still_exist","severity":"caution","meaning":"Das Löschen wurde blockiert, weil andere Objekte von dem zu löschenden Objekt abhängen. DROP schlägt fehl, solange abhängige Objekte existieren.","cause":"Versuch, eine Tabelle zu löschen, von der Views/Foreign Keys/Funktionen abhängen; Objekte, die von einem Typ oder Schema abhängen; DROP ohne CASCADE; eine von anderen Objekten referenzierte Spalte/Sequence.","fix":"Entferne zuerst die abhängigen Objekte oder lösche mit CASCADE: DROP TABLE my_table CASCADE; (beachte, dass CASCADE auch die abhängigen Objekte löscht; Vorsicht). Um die Abhängigkeiten zu sehen, lies das Fehlerdetail (Zeile DETAIL) oder untersuche pg_depend/die betreffenden Kataloge. Plane die Löschreihenfolge korrekt.","tags":["dependency","dependent objects","cascade","drop","objects","object","2BP01"]},{"code":"55006","title":"object_in_use","severity":"danger","meaning":"Die Operation kann auf dem Zielobjekt (meist einer Datenbank) nicht ausgeführt werden, weil es von anderen Sessions verwendet wird. Zum Beispiel kann eine Datenbank mit aktiven Verbindungen nicht gelöscht werden.","cause":"Offene Verbindungen zur Datenbank während DROP DATABASE; eine Operation, die eine exklusive Sperre auf einem verwendeten Objekt erfordert; eine andere Session hält das Objekt.","fix":"Finde und beende offene Verbindungen zur Zieldatenbank: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'db_name' AND pid <> pg_backend_pid(); Blockiere neue Verbindungen (falls nötig). Lösche sie ab PostgreSQL 13 unter zwangsweisem Schließen der Verbindungen: DROP DATABASE db_name WITH (FORCE);","tags":["in use","being accessed","object","object in use","drop database","connection","55006"]},{"code":"22007","title":"invalid_datetime_format","severity":"caution","meaning":"Ein Textwert lag beim Konvertieren in einen Datums-/Zeittyp (date, time, timestamp) nicht in einem gültigen Format vor. Der Wert passt nicht zum erwarteten Datums-/Zeitformat.","cause":"Datumstext in unerwartetem Format (verwechselte Reihenfolge von Tag/Monat/Jahr); ungültige Tages-/Stundenwerte; Konvertieren eines leeren Strings ('') in ein Datum; fehlende oder überzählige Bestandteile; nicht passendes Locale/Format; eine falsche Formatmaske für to_timestamp/to_date.","fix":"Konvertiere den Datumstext mit dem richtigen Format: to_date('2026-07-04', 'YYYY-MM-DD') oder to_timestamp(str, 'YYYY-MM-DD HH24:MI:SS'). Bevorzuge das Format ISO 8601 (YYYY-MM-DD). Wandle leere Werte mit NULLIF(value,'') in NULL um. Validiere Eingabedaten vor dem Laden; grenze die fehlerhafte Zeile ein.","tags":["date","date format","datetime","format","timestamp","to_date","22007"]}]},"errors/mysql-more.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1052","title":"Column 'x' in field list is ambiguous (Spalte in der Feldliste mehrdeutig)","severity":"caution","meaning":"Eine Spalte mit demselben Namen existiert in mehr als einer Tabelle der Abfrage, und weil sie ohne Tabellen-/Alias-Präfix verwendet wird, kann MySQL nicht erkennen, zu welcher Tabelle sie gehört.","cause":"Beide verknüpften Tabellen haben eine gleichnamige Spalte (z. B. id in beiden); die Spalte steht unqualifiziert (ohne Präfix) in SELECT, WHERE, ON, GROUP BY oder ORDER BY.","fix":"Qualifiziere jede mehrdeutige Spalte mit Tabellennamen oder Alias: SELECT a.id, b.id FROM a JOIN b ON a.id = b.a_id; Gib Tabellen kurze Aliase und versieh alle kollidierenden Spalten mit Präfix. Verringere bei Joins über gemeinsame Spalten die Mehrdeutigkeit mit USING(my_column).","tags":["ambiguous","ambiguous column","column","column name","join","alias","1052"]},{"code":"1136","title":"Column count doesn't match value count (Spaltenanzahl passt nicht zur Werteanzahl)","severity":"caution","meaning":"Die Anzahl der Spalten im INSERT-Statement stimmt nicht mit der Anzahl der Werte in der VALUES-Liste überein. Jede Spalte muss genau einen Wert erhalten.","cause":"Spaltenliste und Werteliste in einem INSERT sind unterschiedlich lang; bei einem INSERT ohne Spaltenliste passen die Werte nicht zur Spaltenanzahl der Tabelle; ein fehlendes/überzähliges Komma; die Tabellenstruktur hat sich geändert, aber das Skript wurde nicht angepasst.","fix":"Gleiche Spaltenanzahl und Werteanzahl an. Verwende immer eine explizite Spaltenliste: INSERT INTO my_table (a, b, c) VALUES (1, 2, 3); Prüfe die Tabellenstruktur: DESCRIBE my_table; Ordne die Werte eins zu eins den Spalten zu und korrigiere überzählige/fehlende Kommas.","tags":["column count","column count mismatch","insert","values","mismatch","value count","1136"]},{"code":"1048","title":"Column 'x' cannot be null (Spalte darf nicht NULL sein)","severity":"caution","meaning":"Es wurde versucht, NULL in eine als NOT NULL definierte Spalte einzufügen oder diese Spalte auf NULL zu setzen. Ein Pflichtfeld darf nicht leer bleiben.","cause":"Kein Wert für eine Pflichtspalte im INSERT; ein UPDATE, das die Spalte auf NULL setzt; kein DEFAULT für die Spalte; ein leerer Wert aus der Anwendung/einer Unterabfrage; der strikte SQL-Modus füllt den leeren Wert nicht stillschweigend auf.","fix":"Liefere einen gültigen Wert für die Pflichtspalte. Definiere einen sinnvollen Standardwert für die Spalte: ALTER TABLE my_table MODIFY my_column INT NOT NULL DEFAULT 0; Ist die Regel unnötig, erlaube NULL: ALTER TABLE my_table MODIFY my_column INT NULL; Prüfe leere Werte in der Anwendung, bevor du sie sendest.","tags":["not null","null","empty value","insert","cannot be null","mandatory","1048"]},{"code":"1292","title":"Incorrect datetime value / truncated incorrect value (falscher Datums-/Zeitwert)","severity":"caution","meaning":"Ein Wert war beim Konvertieren in den Zielspaltentyp (insbesondere Datum/Zeit oder numerisch) ungültig oder fehlerhaft. Im strikten Modus lehnt MySQL ihn als Fehler ab.","cause":"Ungültige Datumsangaben oder Werte wie '0000-00-00'; ein falsches Datumsformat; nicht-numerischer Text in einer numerischen Spalte; aktiver strikter SQL-Modus (STRICT_TRANS_TABLES); Konvertierungsprobleme durch einen anderen Zeichensatz/eine andere Collation.","fix":"Bringe den Wert in ein für den Spaltentyp gültiges Format ('YYYY-MM-DD' für Datumsangaben). Verwende NULL statt leerer/ungültiger Datumsangaben. Gib bei Bedarf mit STR_TO_DATE ein explizites Format an: STR_TO_DATE(str, '%Y-%m-%d'). Ist vorübergehende Toleranz nötig, überprüfe sql_mode, korrigiere aber vorzugsweise die Daten. Grenze ein, um die betroffene Zeile zu finden.","tags":["datetime","date","truncated","incorrect value","strict mode","format","1292"]},{"code":"1406","title":"Data too long for column (Daten zu lang für Spalte)","severity":"caution","meaning":"Es wurde ein Wert geschrieben, der länger als die definierte Maximallänge der Spalte ist. Zum Beispiel passt ein 20 Zeichen langer String nicht in eine VARCHAR(10)-Spalte.","cause":"Spaltenbreite zu klein für die Daten; Zeichen in einem Multibyte-Zeichensatz (utf8mb4) belegen mehr Platz als erwartet; ein Verkettungsergebnis wird länger; aktiver strikter SQL-Modus (andernfalls wird der Wert stillschweigend abgeschnitten).","fix":"Verbreitere die Spalte: ALTER TABLE my_table MODIFY my_column VARCHAR(255); Erwäge für sehr lange Inhalte den Typ TEXT. Muss der Wert gekürzt werden, kürze ihn in der Anwendung oder mit SUBSTRING. Finde die überlaufende Zeile: SELECT * FROM my_table WHERE CHAR_LENGTH(my_column) > 10; Ergänze eine Eingabevalidierung.","tags":["data too long","length","varchar","column","column length","truncate","1406"]},{"code":"1071","title":"Specified key was too long; max key length is 767 bytes (Schlüssel zu lang; maximal 767 Bytes)","severity":"caution","meaning":"Der anzulegende Index (oder Schlüssel) hat die maximal erlaubte Länge in Bytes überschritten. Besonders bei Multibyte-Zeichensätzen erreichen VARCHAR-Indizes dieses Limit schnell.","cause":"Ein Index auf einer breiten VARCHAR-Spalte mit utf8mb4 (4 Bytes pro Zeichen); ein altes InnoDB-Row-Format (REDUNDANT/COMPACT; in 5.7 und früher auch ein deaktiviertes innodb_large_prefix); die Gesamtlänge eines zusammengesetzten Index über mehrere Spalten überschreitet das Limit; ein großer Primärschlüssel.","fix":"Verkürze die indizierte Spaltenlänge oder verwende einen Präfix-Index: CREATE INDEX idx ON my_table (my_column(191)); Wechsle mit dem Row-Format DYNAMIC/COMPRESSED auf das Limit von 3072 Bytes (DYNAMIC ist in MySQL 8.0 Standard; innodb_large_prefix wurde in 8.0 entfernt und ist nur in 5.7 und früher nötig). Überprüfe bei Bedarf Zeichensatz/Länge der Spalte.","tags":["index","index length","key too long","key","prefix","utf8mb4","1071"]},{"code":"1050","title":"Table already exists (Tabelle existiert bereits)","severity":"caution","meaning":"Die anzulegende Tabelle existiert bereits. CREATE TABLE ist fehlgeschlagen, weil eine Tabelle mit demselben Namen vorhanden ist.","cause":"Erneutes Anlegen einer vorhandenen Tabelle; ein zweimal ausgeführtes Skript; erneut angewendete Migrationen; ein anderes Objekt (eine View) verwendet denselben Namen.","fix":"Verwende IF NOT EXISTS, um die Tabelle nur anzulegen, wenn sie fehlt: CREATE TABLE IF NOT EXISTS my_table (...); Willst du sie wirklich neu anlegen, lösche sie zuerst: DROP TABLE IF EXISTS my_table; Vorhandene Tabellen auflisten: SHOW TABLES; Mache Migrationen sicher wiederholbar (idempotent).","tags":["table exists","table already exists","duplicate","duplicate table","create table","1050"]},{"code":"1051","title":"Unknown table 'x' (unbekannte Tabelle)","severity":"caution","meaning":"Die referenzierte Tabelle existiert nicht. Beim Löschen, Ändern oder Abfragen wurde keine Tabelle mit dem angegebenen Namen gefunden.","cause":"Ein falsch geschriebener Tabellenname; die Tabelle wurde nie angelegt oder bereits gelöscht; die falsche Datenbank (Schema) ist ausgewählt; Groß-/Kleinschreibung (Tabellennamen unterscheiden unter Linux zwischen Groß- und Kleinschreibung); DROP TABLE auf eine nicht existierende Tabelle.","fix":"Vorhandene Tabellen auflisten: SHOW TABLES; und die richtige Datenbank auswählen: USE mydb; Korrigiere den Tabellennamen und seine Schreibweise. Um den Fehler beim Löschen zu vermeiden: DROP TABLE IF EXISTS my_table; Liegt sie in einem anderen Schema, qualifiziere sie als schema.table.","tags":["unknown table","table not found","does not exist","drop","not exists","1051"]},{"code":"1091","title":"Can't DROP 'x'; check that column/key exists (DROP nicht möglich; Spalte/Schlüssel existiert nicht)","severity":"caution","meaning":"Die mit ALTER TABLE zu löschende Spalte, der Index oder der Foreign Key existiert nicht. Ein nicht existierendes Objekt kann nicht gelöscht werden.","cause":"Angabe eines nicht existierenden Objektnamens in DROP COLUMN/INDEX/FOREIGN KEY; ein falsch geschriebener Name; das Objekt wurde bereits gelöscht; Verwechslung des Foreign-Key-Namens mit dem Indexnamen; eine zum zweiten Mal ausgeführte Migration.","fix":"Prüfe den tatsächlichen Namen des Objekts: SHOW CREATE TABLE my_table; oder SHOW INDEX FROM my_table; Lösche es mit dem richtigen Namen. Verwende für einen Foreign Key seinen Symbolnamen: ALTER TABLE my_table DROP FOREIGN KEY fk_name; MySQL hat keine Syntax DROP COLUMN IF EXISTS (nur MariaDB); prüfe in idempotenten Migrationen zuerst über information_schema.columns, ob die Spalte existiert: SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'my_table' AND column_name = 'my_column';","tags":["cant drop","cannot drop","column","index","foreign key","alter","1091"]},{"code":"1093","title":"You can't specify target table 'x' for update in FROM clause (Zieltabelle des Updates nicht in FROM-Klausel erlaubt)","severity":"caution","meaning":"In einem UPDATE- oder DELETE-Statement kann die zu aktualisierende/zu löschende Tabelle selbst nicht direkt als Quelle in einer Unterabfrage (im FROM) verwendet werden. MySQL erlaubt nicht, dass dieselbe Tabelle auf diese Weise zugleich Ziel und Quelle ist.","cause":"Eine Unterabfrage, die direkt dieselbe Zieltabelle liest, wie in UPDATE my_table ... WHERE id IN (SELECT ... FROM my_table ...); eine Unterabfrage in einem DELETE, die dieselbe Tabelle referenziert.","fix":"Umschließe die Unterabfrage mit einer Derived Table und gib ihr einen neuen Alias, damit MySQL sie als separate Kopie behandelt: UPDATE my_table SET c = 1 WHERE id IN (SELECT id FROM (SELECT id FROM my_table WHERE condition) AS t); Alternativ schreibe sie als Self-Join: UPDATE my_table a JOIN my_table b ON ... SET a.c = ...;","tags":["update","delete","target table","same table","subquery","derived table","1093"]},{"code":"1305","title":"PROCEDURE does not exist (Prozedur existiert nicht)","severity":"caution","meaning":"Die aufgerufene Stored Procedure oder Funktion wurde in der angegebenen Datenbank nicht gefunden. Es ist keine Routine mit diesem Namen definiert.","cause":"Ein falsch geschriebener Prozedurname; die Prozedur liegt in der falschen Datenbank oder wurde nie angelegt; mit USE wurde das falsche Schema ausgewählt; keine EXECUTE-Berechtigung zum Aufruf der Prozedur; Groß-/Kleinschreibung.","fix":"Prüfe, ob die Prozedur existiert und in welchem Schema: SHOW PROCEDURE STATUS WHERE Db = 'mydb'; Wähle die richtige Datenbank: USE mydb; oder rufe sie voll qualifiziert auf: CALL mydb.my_procedure(...); Vergib die nötigen Berechtigungen: GRANT EXECUTE ON PROCEDURE mydb.my_procedure TO app_user;","tags":["procedure","routine","does not exist","not found","stored procedure","call","1305"]},{"code":"1227","title":"Access denied; you need (at least one of) the PRIVILEGE (Zugriff verweigert; Berechtigung erforderlich)","severity":"danger","meaning":"Dem Benutzer fehlt die für die versuchte Operation erforderliche Berechtigung. Die Operation erfordert ein Recht, das über die dem Konto gewährten Berechtigungen hinausgeht.","cause":"Eine fehlende administrative Berechtigung wie SUPER, PROCESS, RELOAD oder CREATE USER; die zum Lesen einer globalen View/Tabelle nötige Berechtigung wurde nicht vergeben; eine bestimmte Operation (SET GLOBAL, Binlog-Zugriff) erfordert erhöhte Berechtigungen.","fix":"Aktuelle Berechtigungen anzeigen: SHOW GRANTS FOR CURRENT_USER(); Vergib die nötige Berechtigung mit einem privilegierten Konto: GRANT PROCESS ON *.* TO 'app_user'@'host'; (GRANT wirkt sofort; FLUSH PRIVILEGES ist nicht nötig). Verwende, wo möglich, dynamische Berechtigungen (z. B. SYSTEM_VARIABLES_ADMIN) statt SUPER. Folge dem Least-Privilege-Prinzip.","tags":["access denied","privilege","missing privilege","permission","grant","access","1227"]},{"code":"3730","title":"Cannot drop table referenced by a foreign key constraint (von Foreign Key referenzierte Tabelle kann nicht gelöscht werden)","severity":"danger","meaning":"Die Tabelle kann nicht gelöscht werden, weil der Foreign Key einer anderen Tabelle auf sie verweist. Solange die Referenz besteht, ist DROP blockiert.","cause":"Versuch, eine Elterntabelle zu löschen, während per FK verknüpfte Kindtabellen noch existieren; die Abhängigkeitsreihenfolge wird nicht eingehalten; die Foreign Keys werden nicht zuerst entfernt.","fix":"Entferne zuerst den verweisenden Foreign Key: ALTER TABLE child_table DROP FOREIGN KEY fk_name; dann lösche die Tabelle. Abhängige Tabellen/Constraints finden: SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'my_table'; Bei Massenoperationen kann vorübergehend SET FOREIGN_KEY_CHECKS = 0; verwendet werden, aber sei vorsichtig und setze es danach wieder auf 1.","tags":["foreign key","fk","drop table","referenced","reference","constraint","3730"]},{"code":"2013","title":"Lost connection to MySQL server during query (Verbindung zum MySQL-Server während der Abfrage verloren)","severity":"danger","meaning":"Die Verbindung zwischen Client und MySQL-Server ist abgebrochen, während die Abfrage lief. Sie wurde getrennt, bevor der Server antworten oder Ergebnisse zurückgeben konnte.","cause":"Eine lang laufende Abfrage überschreitet net_read_timeout/net_write_timeout; der Server beendet die Abfrage/Verbindung wegen Speichermangel (OOM); ein Netzwerkausfall; Überschreiten von max_allowed_packet; der Server stürzt ab oder startet neu; eine sehr große Ergebnismenge.","fix":"Prüfe die Netzwerkstabilität und das Error Log des Servers; sieh nach, ob es einen OOM oder Absturz gab. Erhöhe die Timeouts: SET GLOBAL net_read_timeout = 120; Teile große Abfragen in Batches auf. Erhöhe max_allowed_packet. Ergänze in der Anwendung eine Wiederholungslogik für Verbindungen. Optimiere lange Abfragen.","tags":["lost connection","connection dropped","timeout","network","network outage","oom","2013"]},{"code":"1153","title":"Got a packet bigger than 'max_allowed_packet' bytes (Paket größer als max_allowed_packet)","severity":"danger","meaning":"Client oder Server hat versucht, ein Datenpaket zu senden, das größer als die maximal erlaubte Paketgröße (max_allowed_packet) ist. Das passiert meist bei großen BLOB-/TEXT-Werten oder sehr großen Bulk-INSERTs.","cause":"Einfügen sehr großer BLOB-/TEXT-Daten; ein riesiger einzelner INSERT oder mehrzeiliger Bulk-INSERT; eine Abfrage mit großem Ergebnis; ein kleiner Wert für max_allowed_packet; Import großer Zeilen mit mysqldump.","fix":"Erhöhe max_allowed_packet (auf Server und Client): SET GLOBAL max_allowed_packet = 67108864; (64MB) und verbinde dich neu. Um es dauerhaft zu machen, ergänze max_allowed_packet = 64M unter [mysqld] in my.cnf. Teile große Bulk-Operationen in kleinere Batches auf. Prüfe, ob du sehr große Werte wirklich speichern musst.","tags":["max_allowed_packet","packet","packet size","blob","large data","insert","1153"]},{"code":"1969","title":"MariaDB: Query execution was interrupted (max_statement_time exceeded) (Abfrageausführung unterbrochen)","severity":"caution","meaning":"MariaDB-spezifisch (ER_STATEMENT_TIMEOUT): Die Abfrage wurde unterbrochen, weil sie die durch max_statement_time definierte maximale Ausführungszeit überschritten hat. MySQL hat diesen Code nicht; das MySQL-Gegenstück ist Fehler 3024 (ER_QUERY_TIMEOUT, max_execution_time).","cause":"In MariaDB Überschreiten des Session-/globalen Limits max_statement_time oder eines mit SET STATEMENT max_statement_time=N FOR ... gesetzten Limits; ein ineffizienter Plan, ein fehlender Index, ein großer Datenscan oder Sperrwartezeiten.","fix":"Analysiere die Abfrage mit EXPLAIN und beschleunige sie durch zusätzliche Indizes und einen besseren Plan. Muss die Abfrage wirklich lange laufen, hebe das Limit in MariaDB an: SET SESSION max_statement_time = 60; (Sekunden) oder SET STATEMENT max_statement_time = 60 FOR SELECT ...; Nutzt du MySQL, siehe den Eintrag 3024 (max_execution_time, Millisekunden; Hint MAX_EXECUTION_TIME). Teile große Operationen auf.","tags":["timeout","statement timeout","interrupted","query interrupted","max_execution_time","query","1969"]}]},"errors/mssql-more.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"8134","title":"Divide by zero error encountered (Fehler: Division durch null)","severity":"caution","meaning":"Ein Fehler wegen Division durch null ist aufgetreten, weil der Divisor in einem arithmetischen Ausdruck null ist. SQL Server kann die Division nicht abschließen und stoppt die Abfrage.","cause":"Eine als Divisor verwendete Spalte oder ein Ausdruck ergibt 0, ein Nenner von null in Durchschnitts-/Verhältnisberechnungen, Division ohne NULLIF oder unerwartete Nullwerte in den Daten.","fix":"Sichere den Nenner mit NULLIF ab (z. B. value / NULLIF(denominator, 0)); das Ergebnis wird NULL, und mit ISNULL/COALESCE kann ein Standardwert geliefert werden. Verwende CASE WHEN denominator = 0 THEN NULL ELSE value/denominator END. Filtere Nullwerte vor der Division mit WHERE heraus. Bewerte bei Bedarf das Verhalten von SET ARITHABORT/ANSI_WARNINGS.","tags":["math","arithmetic","divide by zero","division by zero","8134"]},{"code":"245","title":"Conversion failed when converting the value to data type (Konvertierung in den Datentyp fehlgeschlagen)","severity":"caution","meaning":"Ein Wert konnte nicht in den Zieldatentyp konvertiert werden. Zum Beispiel ist eine implizite/explizite Konvertierung wegen ungeeigneten Inhalts beim Umwandeln eines String-Werts in int oder date fehlgeschlagen.","cause":"Konvertierung von nicht-numerischem Text in int/decimal, ein ungültiges Datumsformat, implizite Typkonvertierung bei Spaltenvergleichen, inkompatible Typen in einer WHERE- oder JOIN-Bedingung oder fehlerhafte Eingabedaten.","fix":"Verwende die sicheren Konvertierungsvarianten: TRY_CAST oder TRY_CONVERT (liefern bei Fehlschlag NULL). Ermittle fehlerhafte Zeilen mit ISNUMERIC/TRY_CONVERT. Gleiche Spaltentypen an und beseitige implizite Konvertierung. Validiere Eingaben und verwende für Datumsangaben ein explizites Format (z. B. einen CONVERT-Style).","tags":["conversion","type conversion","cast","convert","data type","245"]},{"code":"515","title":"Cannot insert the value NULL into column (NULL kann nicht in die Spalte eingefügt werden)","severity":"caution","meaning":"Es wurde versucht, einen NULL-Wert in eine als NOT NULL definierte Spalte einzufügen oder zu aktualisieren. Die Operation wurde abgelehnt, weil die Spalte keine Nullwerte zulässt.","cause":"Eine Pflichtspalte fehlt im INSERT-Statement, fehlende/leere Felder in den Quelldaten, eine NOT-NULL-Spalte ohne Standardwert oder von der Anwendung gesendetes NULL.","fix":"Liefere einen gültigen Wert für Pflichtspalten. Ergänze passende Spalten um DEFAULT-Constraints. Prüfe die Quelldaten auf fehlende Felder und weise mit ISNULL/COALESCE Standardwerte zu. Muss die Spalte nicht wirklich Pflicht sein, überprüfe das Schema, um NULL zu erlauben.","tags":["null","not null","insert","constraint","constraint violation","515"]},{"code":"8115","title":"Arithmetic overflow error converting expression to data type (arithmetischer Überlauf bei Konvertierung)","severity":"caution","meaning":"Der durch eine arithmetische Operation oder Konvertierung erzeugte Wert hat den Bereich überschritten, den der Zieldatentyp aufnehmen kann. Es kam zu einem Überlauf, weil die Zahl zu groß ist.","cause":"Eine Summe/ein Produkt (SUM) überschreitet den int-Bereich, ein großer Wert wird einer Spalte mit kleinem Typ zugewiesen, zu geringe decimal-Precision/-Scale oder ein IDENTITY-Wert erreicht das Typlimit.","fix":"Verwende für Zwischenberechnungen einen breiteren Typ (z. B. SUM mit CAST(my_column AS bigint)). Erhöhe decimal-Precision/-Scale. Vergrößere den Spaltentyp passend zu den Daten. Konvertiere überlaufgefährdete Ausdrücke vor der Berechnung und begrenze Eingabebereiche.","tags":["overflow","arithmetic overflow","arithmetic","math","int","bigint","8115"]},{"code":"512","title":"Subquery returned more than 1 value (Unterabfrage lieferte mehr als einen Wert)","severity":"caution","meaning":"Eine Unterabfrage in einem Kontext, der einen einzelnen Wert erwartet (=, <, >, Vergleich oder Zuweisung), hat mehr als eine Zeile zurückgegeben. SQL Server erwartete einen einzelnen skalaren Wert, stieß aber auf mehrere Werte.","cause":"Eine Unterabfrage, von der für den Vergleich ein einzelner Wert angenommen wird, liefert mehrere Zeilen, eine nicht eindeutige Abgleichbedingung, ein fehlender Filter oder eine falsch aufgebaute korrelierte Abfrage.","fix":"Reduziere die Unterabfrage auf eine einzelne Zeile (z. B. TOP 1, MAX/MIN oder ein passendes GROUP BY). Werden mehrere Werte erwartet, verwende IN oder EXISTS statt =. Schreibe sie mit einem JOIN um. Untersuche, warum die Unterabfrage mehrere Zeilen liefert, und korrigiere die Filter-/Schlüsselbedingungen.","tags":["subquery","scalar subquery","more than one","512","in","exists"]},{"code":"207","title":"Invalid column name (ungültiger Spaltenname)","severity":"caution","meaning":"Der in der Abfrage referenzierte Spaltenname wurde in der Tabelle/View nicht gefunden. Es existiert keine Spalte mit diesem Namen, oder sie liegt außerhalb des Gültigkeitsbereichs.","cause":"Ein Schreib-/Groß-/Kleinschreibungsfehler, der falsche Tabellenalias, eine noch nicht hinzugefügte oder umbenannte Spalte, der falsche Schema-/Tabellenkontext oder fehlende eckige Klammern statt Anführungszeichen.","fix":"Prüfe Spaltenname und Tabelle (sys.columns oder Object Explorer). Verwende den richtigen Alias und das richtige Schema. Stelle bei neu hinzugefügten Spalten sicher, dass das Objekt aktuell ist; kompiliere die Stored Procedure bei Bedarf neu. Verwende eckige Klammern für Namen mit Leer-/Sonderzeichen.","tags":["column","column name","invalid column","invalid column name","alias","207"]},{"code":"156","title":"Incorrect syntax near the keyword (falsche Syntax in der Nähe des Schlüsselworts)","severity":"caution","meaning":"In der Nähe eines Schlüsselworts liegt ein Syntaxfehler vor. SQL Server konnte das Statement nicht parsen, weil ein reserviertes Wort an einer unerwarteten Stelle steht.","cause":"Verwendung eines reservierten Worts (z. B. ORDER, USER, TABLE) als Spalten-/Objektname ohne Klammern, ein fehlendes Komma/eine fehlende Klammer, Klauseln in falscher Reihenfolge oder eine falsch platzierte Klausel (WHERE, GROUP BY).","fix":"Untersuche den Bereich um das Schlüsselwort, auf das der Fehler zeigt. Verwendest du reservierte Wörter als Objektnamen, setze sie in eckige Klammern (z. B. [User]). Korrigiere fehlende Kommas, Klammern und die Reihenfolge der Klauseln. Teste die Syntax, indem du die Abfrage in Teile zerlegst.","tags":["syntax","syntax error","keyword","reserved word","156"]},{"code":"2812","title":"Could not find stored procedure (Stored Procedure nicht gefunden)","severity":"caution","meaning":"Die mit EXEC aufgerufene Stored Procedure wurde im aktuellen Datenbank-/Schemabereich nicht gefunden. Es existiert keine Prozedur mit diesem Namen.","cause":"Ein Tippfehler im Prozedurnamen, ein fehlendes oder falsches Schemapräfix (z. B. dbo. weggelassen), die Prozedur liegt in einer anderen Datenbank, wurde noch nicht angelegt oder ist wegen Berechtigungen nicht sichtbar.","fix":"Prüfe Prozedurname und Schema; verwende im Aufruf den voll qualifizierten Namen (z. B. EXEC dbo.ProcedureName). Stelle sicher, dass du in der richtigen Datenbank bist (USE DatabaseName). Prüfe, ob die Prozedur existiert (sys.procedures / OBJECT_ID). Vergib die nötige EXECUTE-Berechtigung und prüfe, ob das Deployment-Skript gelaufen ist.","tags":["stored procedure","procedure","sp","not found","procedure not found","2812"]},{"code":"8623","title":"The query processor ran out of internal resources and could not produce a query plan (Query Processor ohne interne Ressourcen; kein Abfrageplan)","severity":"danger","meaning":"Der Query Processor hat bei einer zu komplexen Abfrage seine internen Ressourcen erschöpft und konnte keinen Ausführungsplan erzeugen. Die Abfrage kann nicht optimiert werden, weil sie zu groß/komplex ist.","cause":"Abfragen mit sehr langen IN-Listen oder vielen OR-/UNION-Klauseln, eine übermäßige Anzahl von JOINs, riesige automatisch generierte Abfragen, Abfragen mit zu vielen Parametern oder zu tiefe Ausdrucksbäume.","fix":"Vereinfache die Abfrage: Verlagere lange IN-Listen in eine Temp-Tabelle/einen Table-Valued Parameter, reduziere OR-Bedingungen mit UNION ALL oder einer Umformulierung und teile die komplexe Abfrage in Teile auf. Reduziere die Anzahl der JOINs. Bündele automatisch generierte Abfragen in Batches. Überprüfe bei Bedarf Indizes und Abfragedesign.","tags":["query processor","internal resources","plan","complex query","8623","danger"]},{"code":"4104","title":"The multi-part identifier could not be bound (mehrteiliger Identifier nicht bindbar)","severity":"caution","meaning":"Ein durch Punkte getrennter mehrteiliger Identifier (z. B. table.column oder alias.column) konnte nicht aufgelöst werden. SQL Server konnte diesen Namen keiner Tabelle/Spalte in der Abfrage zuordnen.","cause":"Ein falscher oder nicht definierter Tabellenalias, ein Verweis auf eine Tabelle, die nicht in FROM/JOIN steht, eine der falschen Tabelle zugeordnete Spalte, Verwendung des echten Tabellennamens nach Definition eines Alias oder ein Tippfehler.","fix":"Prüfe, ob die verwendeten Aliase in der FROM-/JOIN-Klausel definiert sind. Qualifiziere die Spalte mit der richtigen Tabelle bzw. dem richtigen Alias. Hast du einer Tabelle einen Alias gegeben, verwende ihn überall (nicht den echten Namen). Ergänze die fehlende Tabelle im JOIN und prüfe die Namen.","tags":["identifier","multi-part","multi-part identifier","bind","alias","4104"]},{"code":"2714","title":"There is already an object named in the database (Objekt mit diesem Namen existiert bereits)","severity":"caution","meaning":"In der Datenbank existiert bereits ein Objekt mit demselben Namen wie das anzulegende (Tabelle, View, Prozedur usw.). Das CREATE-Statement ist wegen eines Namenskonflikts fehlgeschlagen.","cause":"Zweites Ausführen desselben CREATE-Skripts, das vorhandene Objekt wird nicht zuerst gelöscht, eine abweichende Schema-Annahme, nicht idempotente Deployment-/Migrationsskripte oder ein Namenskonflikt.","fix":"Prüfe zuerst, ob das Objekt existiert: Lege es bedingt mit IF OBJECT_ID('dbo.Name') IS NULL an oder verwende CREATE OR ALTER (für Prozeduren/Views/Funktionen). Lösche das vorhandene Objekt bei Bedarf mit DROP und lege es neu an. Schreibe Migrationsskripte idempotent. Prüfe, ob du im richtigen Schema bzw. in der richtigen Datenbank arbeitest.","tags":["already exists","object exists","create","object","duplicate object","2714"]},{"code":"601","title":"Could not continue scan with NOLOCK due to data movement (Scan mit NOLOCK wegen Datenbewegung abgebrochen)","severity":"caution","meaning":"Bei einem Scan mit NOLOCK (READ UNCOMMITTED) konnte der Scan wegen paralleler Datenbewegung (Page Splits/Verschiebungen) nicht fortgesetzt werden. Das sperrfreie Lesen ist auf einen inkonsistenten Zustand gestoßen.","cause":"Gescannte Seiten werden beim Lesen mit dem Hint NOLOCK oder der Isolationsstufe READ UNCOMMITTED durch parallele Updates/Deletes/Page Splits verschoben.","fix":"Führe die Abfrage erneut aus (meist transient). Vermeide nach Möglichkeit den Hint NOLOCK; verwende die Isolation READ COMMITTED SNAPSHOT (RCSI) für nicht blockierende und dennoch konsistente Lesevorgänge. Bevorzuge auf schreibintensiven Tabellen Snapshot Isolation gegenüber Dirty Reads.","tags":["nolock","scan","data movement","601","isolation","read uncommitted"]},{"code":"50000","title":"Mit RAISERROR ausgelöster benutzerdefinierter Fehler","severity":"caution","meaning":"Das ist die Standardnummer (50000) für benutzerdefinierte Fehler, die mit RAISERROR ohne Angabe einer Message-ID ausgelöst werden. Die Logik der Anwendung/Prozedur hat bewusst einen Fehler ausgelöst.","cause":"Ein mit RAISERROR oder THROW in einer Stored Procedure oder einem Skript ausgelöster benutzerdefinierter Fehler bei Verletzung einer Geschäftsregel, einer fehlgeschlagenen Validierung, bewusster Ablaufsteuerung oder benutzerdefinierten Exception-Meldungen.","fix":"Lies den Meldungstext von RAISERROR/THROW, um die tatsächliche Verletzung der Geschäftsregel zu verstehen. Fange diesen Fehler in der Anwendung mit TRY...CATCH ab und hole Details mit ERROR_MESSAGE()/ERROR_NUMBER(). Definiere Meldungen mit sp_addmessage, um deinen benutzerdefinierten Fehlern aussagekräftige Nummern zu geben. Behebe die auslösende Bedingung in der Geschäftslogik.","tags":["raiserror","custom error","user-defined error","throw","50000","try catch"]},{"code":"3902","title":"The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION (COMMIT ohne zugehöriges BEGIN TRANSACTION)","severity":"caution","meaning":"Ein COMMIT TRANSACTION wurde ausgeführt, aber es gibt kein zugehöriges offenes BEGIN TRANSACTION. Es wurde keine zu committende Transaktion gefunden, weil der Transaktionszähler (@@TRANCOUNT) null ist.","cause":"Unausgeglichene Verwendung von BEGIN/COMMIT/ROLLBACK, die Transaktion wurde nach einem Fehler bereits zurückgesetzt, falsche Verwaltung der Zähler verschachtelter Transaktionen oder erneuter COMMIT-Aufruf nach ROLLBACK in einem CATCH-Block.","fix":"Gleiche die Anzahl der BEGIN- und COMMIT-Statements aus. Prüfe vor dem Commit @@TRANCOUNT (IF @@TRANCOUNT > 0 COMMIT). Führe in TRY...CATCH bei einem Fehler ROLLBACK abhängig von XACT_STATE()/@@TRANCOUNT aus und verhindere ein unnötiges COMMIT. Überprüfe die Logik verschachtelter Transaktionen.","tags":["transaction","commit","begin","trancount","transaction count","3902"]}]},"errors/mongodb-more.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"121","title":"DocumentValidationFailure","severity":"caution","meaning":"Das einzufügende oder zu aktualisierende Dokument wurde abgelehnt, weil es die auf der Collection definierten JSON-Schema-Validierungsregeln (Validator) nicht erfüllt.","cause":"Das Dokument entspricht nicht dem Schema: ein fehlendes Pflichtfeld, ein falscher BSON-Typ, ein Wert außerhalb des erlaubten Bereichs/Musters, verbotene zusätzliche Felder oder eine Regelverletzung, während validationAction auf 'error' steht.","fix":"Untersuche die Fehlerdetails (errInfo), um zu sehen, welche Regel verletzt wurde. Passe das Dokument an das Schema an: Ergänze Pflichtfelder und korrigiere die Typen. Überprüfe den Validator der Collection mit collMod. validationLevel/validationAction können vorübergehend gelockert werden, für die Datenqualität ist es aber besser, die Regeln beizubehalten.","tags":["validation","validation","schema","schema","121","document"]},{"code":"40","title":"ConflictingUpdateOperators","severity":"caution","meaning":"Ein Update hat mehr als einen kollidierenden Update-Operator auf dasselbe Feld (denselben Pfad) angewendet. MongoDB kann denselben Pfad nicht zweimal in einer Operation ändern.","cause":"Kollidierende Operatoren, die in einem Update-Dokument dasselbe Feld ansprechen (z. B. sowohl $set als auch $inc oder gleichzeitige Änderung eines Eltern- und eines Kindpfads), ein schlecht aufgebautes Update oder ein Konflikt bei verschachtelten Pfaden.","fix":"Fasse die kollidierenden Operatoren auf demselben Feld zu einem einzigen Operator zusammen oder teile das Update in separate Operationen auf. Ändere nicht gleichzeitig einen Eltern- und einen Kindpfad (z. B. a und a.b). Überprüfe das Update-Dokument und stelle sicher, dass jedes Feld nur einmal geändert wird.","tags":["update","update","operator","conflict","conflict","40"]},{"code":"66","title":"ImmutableField (cannot update _id) (_id kann nicht aktualisiert werden)","severity":"caution","meaning":"Es wurde versucht, ein unveränderliches Feld zu aktualisieren, typischerweise das Feld _id des Dokuments. _id kann nach dem Anlegen des Dokuments nicht geändert werden.","cause":"Ein Update, das das Feld _id ändern will, eine nicht erlaubte Aktualisierung eines Shard-Key-Felds oder das Setzen eines als unveränderlich behandelten Felds auf einen neuen Wert.","fix":"Aktualisiere den Wert von _id nicht; wird eine andere _id benötigt, füge das Dokument mit der neuen _id ein und lösche das alte (Insert + Delete). Befolge bei Shard-Key-Updates die MongoDB-Regeln. Entferne unveränderliche Felder aus dem Update-Dokument und setze nur änderbare Felder.","tags":["immutable","_id","immutable","update","shard key","66"]},{"code":"2","title":"BadValue","severity":"caution","meaning":"An einen Befehl oder eine Abfrage wurde ein ungültiger oder unerwarteter Wert übergeben. MongoDB konnte das Argument nicht als akzeptablen Wert interpretieren.","cause":"Ein ungültiges Operator-Argument, eine Zahl außerhalb des Bereichs, ein nicht unterstützter Optionswert, eine fehlerhafte Sortierangabe, ein fehlerhafter Abfrageausdruck oder eine unpassende Parameterkombination.","fix":"Korrigiere das Argument anhand des Feld-/Wert-Hinweises in der Fehlermeldung. Prüfe Befehls- und Abfrageoptionen anhand der Dokumentation. Prüfe das Format von Sort-/Projection-/Aggregate-Ausdrücken. Bringe Wertebereiche und Typen in den gültigen Bereich.","tags":["badvalue","invalid value","argument","2","query"]},{"code":"14","title":"TypeMismatch","severity":"caution","meaning":"Der Typ des gelieferten Werts entspricht nicht dem erwarteten BSON-Typ. Für ein Feld oder Argument wurde ein anderer Datentyp erwartet.","cause":"Text, wo eine Zahl erwartet wird, ein String statt eines Datums, ein einzelner Wert, wo ein Array erwartet wird, ein Argument falschen Typs an einen Aggregate-/Abfrageoperator oder Typinkonsistenz im Schema.","fix":"Liefere den richtigen BSON-Typ für das Feld/Argument. Konvertiere Typen in der Anwendungsschicht, bevor du Daten speicherst (z. B. Zahlen als echte Number-Werte schreiben). Prüfe die von Abfrage- und Aggregate-Operatoren erwarteten Typen. Setze Typkonsistenz mit einem Schema-Validator durch.","tags":["typemismatch","type mismatch","bson","type","14"]},{"code":"10334","title":"BSONObjectTooLarge","severity":"danger","meaning":"Ein BSON-Dokument hat die maximal erlaubte Größe (16 MB) überschritten. MongoDB kann kein einzelnes Dokument oberhalb dieses Limits speichern oder zurückgeben.","cause":"Überschreiten des 16-MB-Limits durch fortlaufendes Anhängen von Daten/Array-Elementen an ein Dokument, sehr große eingebettete Arrays, riesige von einer Aggregation erzeugte Dokumente oder große Binärdaten direkt im Dokument.","fix":"Überprüfe das Datenmodell: Teile große Arrays in separate Collections auf und verwende ein Referenzmodell. Verwende GridFS für große Binärdateien. Begrenze unbegrenzt wachsende Arrays (z. B. Capped-/Bucket-Pattern). Halte die Dokumentgröße in Aggregationsstufen unter Kontrolle.","tags":["bson","16mb","too large","large document","gridfs","10334","danger"]},{"code":"46","title":"LockBusy","severity":"caution","meaning":"Die angeforderte Sperre oder Ressource konnte nicht sofort erhalten werden, weil eine andere Operation sie hielt, und die Operation wurde abgelehnt, statt zu warten. Sperrwartezeiten, die in einem Timeout enden, haben einen eigenen Code (24 LockTimeout).","cause":"Parallele Administrations-/DDL-Operationen auf derselben Collection oder Datenbank (z. B. zwei gleichzeitige Index-Builds, collMod, drop oder rename), ein Konflikt mit einer laufenden Wartungsoperation oder eine interne Operation, die nicht einmal kurz auf eine Sperre warten kann und auf eine belegte Ressource trifft.","fix":"Finde die kollidierende Operation: Untersuche mit db.currentOp() DDL- oder Wartungsoperationen auf demselben Namespace. Wiederhole den Befehl, nachdem die kollidierende Operation beendet ist. Serialisiere Administrations-/DDL-Operationen und führe sie nicht parallel auf derselben Collection aus. Besteht das Problem weiter, untersuche in den mongod-Logs die Operation, die die Ressource hält.","tags":["lock","lock","lockbusy","busy","ddl","46"]},{"code":"96","title":"OperationFailed","severity":"danger","meaning":"Ein generischer Fehlschlag einer Operation; während der Ausführung des Befehls ist ein unerwarteter Fehler aufgetreten, und die Operation konnte nicht abgeschlossen werden. Das ist der generische Fehler, wenn kein spezifischerer Code existiert.","cause":"Ein serverseitiger interner Fehler, Ressourcenerschöpfung, ein inkonsistenter Zustand, ein Ausfall eines Subsystems (Storage, Replikation) oder verschiedene befehlsspezifische Fehlerbedingungen.","fix":"Ermittle die Grundursache anhand des vollständigen Fehlermeldungstexts und der Serverlogs (mongod.log). Wiederhole die Operation; der Zustand kann transient sein. Prüfe Ressourcen (Platte, Speicher) und den Zustand des Replica Sets. Tritt das Problem erneut auf, überprüfe den Befehl und seine Eingabedaten.","tags":["operationfailed","operation failed","generic error","96","danger"]},{"code":"72","title":"InvalidOptions","severity":"caution","meaning":"An einen Befehl wurden ungültige oder miteinander inkompatible Optionen übergeben. Die angegebene Optionskombination wird für diesen Befehl nicht unterstützt.","cause":"Ein nicht unterstützter Optionsname, gemeinsam verwendete, sich gegenseitig ausschließende Optionen, eine für die laufende Version ungültige Option, eine ungültige Kombination aus Write Concern/Read Concern oder ein Konflikt bei Index-/Collation-Optionen.","fix":"Prüfe in der Dokumentation die vom Befehl akzeptierten Optionen und entferne inkompatible. Stelle sicher, dass deine Version die Option unterstützt. Überprüfe Write-/Read-Concern- und Collation-Einstellungen. Verwende nur gültige Optionskombinationen.","tags":["invalidoptions","invalid option","options","72","command"]},{"code":"9","title":"FailedToParse","severity":"caution","meaning":"Der Server konnte den übermittelten Befehl, die Abfrage oder den Ausdruck nicht parsen. Die Struktur ist syntaktisch ungültig oder nicht im erwarteten Format.","cause":"Eine fehlerhafte JSON-/BSON-Struktur, fehlende oder überzählige Felder, falsche Operatorverwendung, ungültige Syntax der Aggregation-Pipeline oder ein Befehlsdokument, das nicht dem erwarteten Format entspricht.","fix":"Korrigiere die Struktur von Befehl/Abfrage/Pipeline gemäß der Dokumentation. Prüfe die JSON-Struktur (fehlende Klammern/Kommas, falsche Feldnamen). Prüfe die Syntax der Aggregationsstufen. Stelle sicher, dass Operatoren korrekt verwendet und Argumente im erwarteten Format geliefert werden.","tags":["failedtoparse","parse","parsing","syntax","9","query"]},{"code":"134","title":"ReadConcernMajorityNotAvailableYet","severity":"caution","meaning":"Es wurde ein Lesevorgang mit Read Concern majority angefordert, aber ein von der Mehrheit bestätigter Snapshot ist noch nicht verfügbar. Der Server kann diese Konsistenzstufe derzeit nicht liefern.","cause":"Das Replica Set wurde gerade gestartet oder ist instabil, Mehrheitsschreibvorgänge sind noch nicht bestätigt, Replikationsverzögerung, nicht genügend Mitglieder erreichbar oder der Cluster befindet sich noch in der Startphase.","fix":"Warte kurz und wiederhole; der Lesevorgang gelingt, sobald der Mehrheits-Snapshot verfügbar ist. Prüfe Zustand und Replikationsverzögerung des Replica Sets (rs.status()). Stelle sicher, dass genügend Mitglieder laufen. Ist es nicht erforderlich, erwäge einen niedrigeren Read Concern (z. B. local).","tags":["readconcern","majority","majority","replica","134","consistency"]},{"code":"202","title":"NetworkInterfaceExceededTimeLimit","severity":"danger","meaning":"Eine Netzwerkanfrage (z. B. Kommunikation zwischen Mitgliedern oder eine Client-Verbindung) hat ihr Zeitlimit überschritten. Die Operation konnte wegen eines Netzwerk-Timeouts nicht abgeschlossen werden.","cause":"Netzwerklatenz/-ausfall, ein langsames oder nicht erreichbares Zielmitglied, übermäßige Last, falsch konfigurierte Timeout-Werte, DNS-/Verbindungsprobleme oder geografisch weit entfernte Mitglieder.","fix":"Prüfe Netzwerkverbindung und Latenz; prüfe die Erreichbarkeit zwischen den Mitgliedern. Untersuche Zustand und Last des Zielmitglieds. Konfiguriere die Timeout-Einstellungen (Connect-/Socket-Timeout) passend für deine Umgebung. Ergänze eine Wiederholungslogik. Besteht das Problem weiter, überprüfe die Infrastruktur-/Netzwerkkonfiguration.","tags":["network","network","timeout","timeout","202","danger"]}]},"errors/redis-more.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"NOPERM","title":"NOPERM this user has no permissions to run the command or access one of the keys (keine Berechtigung für Befehl oder Schlüssel)","severity":"caution","meaning":"Der authentifizierte ACL-Benutzer ist für den Befehl, den er ausführen will, oder den Schlüssel, auf den er zugreift, nicht berechtigt. Redis lehnt die Operation gemäß den ACL-Regeln ab.","cause":"Dem ACL-Benutzer fehlt die Befehls-/Kategorieberechtigung, das Schlüsselmuster steht nicht in seiner Allow-Liste, eine Kanal-Einschränkung (Pub/Sub) greift oder dem Benutzer wurden eng gefasste Berechtigungen gegeben.","fix":"Untersuche die Berechtigungen des Benutzers: ACL GETUSER app_user. Ergänze die nötigen Befehle/Kategorien und Schlüsselmuster (z. B. ACL SETUSER app_user +get +set ~app:*). Vergib nach dem Least-Privilege-Prinzip nur die benötigten Berechtigungen. Prüfe, ob du als richtiger Benutzer verbunden bist.","tags":["noperm","acl","privilege","permission denied","permission","user"]},{"code":"ERR value is not an integer or out of range","title":"ERR value is not an integer or out of range (Wert keine Ganzzahl oder außerhalb des Bereichs)","severity":"caution","meaning":"Ein Befehl, der eine Ganzzahl erwartet (z. B. INCR, EXPIRE, LPOP count), hat einen nicht-numerischen Wert oder einen Wert außerhalb des Bereichs erhalten. Redis konnte den Wert nicht als Ganzzahl interpretieren.","cause":"Der von INCR/DECR angesprochene Schlüssel enthält einen Text (keine Ganzzahl), ein Argument als Text statt als Zahl, eine für den Bereich zu große/kleine Zahl oder ein negativer Wert, wo keiner erlaubt ist.","fix":"Prüfe den Wert des Schlüssels: GET mykey und TYPE mykey. Stelle bei numerischen Operationen sicher, dass der Schlüssel einen ganzzahligen Wert enthält. Prüfe, ob die Argumente gültige Ganzzahlen im richtigen Bereich sind. Setze den Wert bei Bedarf zurück (SET mykey 0) oder lege ihn mit dem richtigen Typ neu an.","tags":["err","integer","not an integer","range","incr","value"]},{"code":"ERR value is not a valid float","title":"ERR value is not a valid float (Wert kein gültiger float)","severity":"caution","meaning":"Ein Befehl, der eine Dezimalzahl (float) erwartet (z. B. INCRBYFLOAT, ein ZADD-Score), hat einen Wert erhalten, der keine gültige Gleitkommazahl ist. Redis konnte den Wert nicht als float parsen.","cause":"Der von INCRBYFLOAT angesprochene Schlüssel enthält keinen gültigen float, ein nicht-numerisches Argument, das falsche Dezimaltrennzeichen (ein Punkt ist erforderlich, kein Komma) oder eine fehlerhafte Zahl.","fix":"Prüfe den Wert des Schlüssels mit GET und stelle sicher, dass er einen gültigen float enthält. Verwende einen Punkt als Dezimaltrennzeichen (z. B. 3.14). Prüfe, ob das Argument numerisch und korrekt formatiert ist. Setze den Wert bei Bedarf auf einen gültigen float zurück.","tags":["err","float","decimal","incrbyfloat","zadd","value"]},{"code":"ERR increment or decrement would overflow","title":"ERR increment or decrement would overflow (Inkrement/Dekrement würde überlaufen)","severity":"caution","meaning":"Ein INCR/DECR (oder INCRBY/DECRBY) wurde abgelehnt, weil das Ergebnis den Bereich vorzeichenbehafteter 64-Bit-Ganzzahlen überschreiten würde. Der Wert würde die Ober- oder Untergrenze überschreiten.","cause":"Der Wert des Schlüssels liegt sehr nahe am int64-Limit (etwa 9,2 Trillionen), sodass Inkrement/Dekrement überläuft, ein sehr großes INCRBY-Argument oder ein unkontrolliert wachsender Zähler.","fix":"Gestalte Zählerwerte so, dass sie innerhalb der int64-Grenzen bleiben. Wende bei großen Zählungen Skalierung oder periodisches Zurücksetzen an. Halte das Inkrement (das INCRBY-Argument) in einem vernünftigen Bereich. Sind sehr große Werte nötig, verwende in der Anwendungsschicht eine andere Zählstrategie.","tags":["err","overflow","integer overflow","incr","decr","int64"]},{"code":"ERR max number of clients reached","title":"ERR max number of clients reached (maximale Anzahl von Clients erreicht)","severity":"danger","meaning":"Die Anzahl verbundener Clients hat das Limit maxclients erreicht, und neue Verbindungen können nicht angenommen werden. Redis verweigert weitere Client-Verbindungen.","cause":"Ein Connection-Leak (nie geschlossene Verbindungen), ein zu niedriger Wert für maxclients, ein falsch konfigurierter Connection Pool, ein plötzlicher Anstieg von Clients oder ein niedriges File-Descriptor-Limit des Betriebssystems.","fix":"Untersuche die aktuellen Verbindungen: CLIENT LIST und INFO clients. Behebe Connection-Leaks; dimensioniere den Client-Pool korrekt und verwende Verbindungen wieder. Erhöhe bei Bedarf maxclients (CONFIG SET maxclients) und das ulimit (File Descriptors) des Betriebssystems. Setze einen Timeout für inaktive Verbindungen.","tags":["err","maxclients","connection","client","limit","danger"]},{"code":"BUSYKEY","title":"BUSYKEY Target key name already exists (Zielschlüssel existiert bereits)","severity":"caution","meaning":"RESTORE (oder ein ähnlicher Befehl) ist fehlgeschlagen, weil der Zielschlüssel bereits existiert. Standardmäßig überschreibt Redis keinen vorhandenen Schlüssel.","cause":"Der mit RESTORE wiederherzustellende Schlüsselname existiert bereits in der Datenbank, frühere Daten wurden nicht bereinigt oder mehrere Restore-Versuche auf denselben Schlüssel.","fix":"Willst du den vorhandenen Schlüssel überschreiben, ergänze RESTORE um die Option REPLACE. Alternativ lösche zuerst den Zielschlüssel (DEL mykey) oder verwende einen anderen Schlüsselnamen. Prüfe die Existenz vorab mit EXISTS mykey.","tags":["busykey","restore","already exists","target key","replace","key"]},{"code":"ERR no such key","title":"ERR no such key (Schlüssel existiert nicht)","severity":"caution","meaning":"Der zu bearbeitende Schlüssel wurde in der Datenbank nicht gefunden. Befehle, die einen vorhandenen Schlüssel erfordern, wie RENAME, DUMP und MOVE, funktionieren nicht auf einem nicht existierenden Schlüssel.","cause":"Der Schlüssel wurde nie angelegt, ist abgelaufen und wegen seiner TTL entfernt worden, ein falscher Schlüsselname/eine falsche Schreibweise, der falsche Datenbankindex (SELECT) oder der Schlüssel wurde bereits mit DEL gelöscht.","fix":"Prüfe, ob der Schlüssel existiert: EXISTS mykey. Stelle sicher, dass du den richtigen Schlüsselnamen und die richtige Datenbank (SELECT db) verwendest. RENAME erfordert, dass der Quellschlüssel existiert. Überprüfe die Logik für Schlüssel, die per TTL ablaufen. Lege den Schlüssel bei Bedarf neu an.","tags":["err","no such key","key not found","rename","dump","key"]},{"code":"ERR DUMP payload version or checksum are wrong","title":"ERR DUMP payload version or checksum are wrong (DUMP-Payload: Version oder Prüfsumme falsch)","severity":"caution","meaning":"Die an RESTORE übergebenen serialisierten Daten (DUMP-Ausgabe) sind ungültig; die Version ist inkompatibel oder die Prüfsummenprüfung ist fehlgeschlagen. Redis kann die Daten nicht sicher wiederherstellen.","cause":"Die DUMP-Daten sind beschädigt/verändert, Wechsel zwischen unterschiedlichen, inkompatiblen Redis-Versionen, Beschädigung beim Kodieren/Dekodieren (z. B. falsche Byte-Behandlung) oder eine fehlende/abgeschnittene Payload.","fix":"Stelle sicher, dass die DUMP-Daten unverändert (binärsicher) transportiert werden; übertrage sie als rohe Bytes statt über eine Textkodierung. Prüfe, ob Quell- und Ziel-Redis-Version kompatibel sind. Erstelle einen frischen DUMP und übertrage ihn erneut. Erwäge für Migrationen den Befehl MIGRATE statt RESTORE.","tags":["err","dump","restore","checksum","checksum mismatch","version"]},{"code":"NOREPLICAS","title":"NOREPLICAS Not enough good replicas to write (nicht genügend gesunde Replicas zum Schreiben)","severity":"danger","meaning":"Wegen der Einstellung min-replicas-to-write werden Schreibzugriffe abgelehnt, da nicht genügend gesunde Replicas vorhanden sind. Redis blockiert Schreibzugriffe zur Datensicherheit.","cause":"Nicht genügend verbundene/aktuelle Replicas, Abstürze von Replicas, Replikationsverzögerung über dem Limit min-replicas-max-lag, Netzwerkprobleme oder fehlende Replicas nach einem Failover.","fix":"Prüfe den Replica-Status: INFO replication. Bringe ausgefallene Replicas wieder hoch und verringere die Replikationsverzögerung. Behebe Netzwerkprobleme. Überprüfe die Einstellungen min-replicas-to-write / min-replicas-max-lag für deine Umgebung (Abwägung zwischen Dauerhaftigkeit und Verfügbarkeit). Stelle genügend Replicas bereit.","tags":["noreplicas","replica","write","min-replicas","replication","danger"]},{"code":"ERR This instance has cluster support disabled","title":"ERR This instance has cluster support disabled (Cluster-Unterstützung auf dieser Instanz deaktiviert)","severity":"caution","meaning":"Ein Cluster-Befehl (CLUSTER ...) wurde ausgeführt, aber diese Redis-Instanz wurde nicht im Cluster-Modus gestartet. Der Befehl wurde abgelehnt, weil die Cluster-Unterstützung deaktiviert ist.","cause":"cluster-enabled yes ist nicht gesetzt, die Instanz läuft im Standalone-Modus, Cluster-Befehle werden an einen Standalone-Server gesendet oder Verbindung zum falschen Server.","fix":"Willst du Cluster-Features nutzen, starte die Instanz im Cluster-Modus: Setze cluster-enabled yes in der Konfiguration, starte neu und bilde aus den Knoten den Cluster. Bleibst du im Standalone-Modus, verwende keine Cluster-Befehle. Prüfe, ob du mit dem richtigen (Cluster-)Server verbunden bist.","tags":["err","cluster","cluster mode","disabled","standalone","config"]},{"code":"ERR unknown subcommand","title":"ERR unknown subcommand or wrong number of arguments (unbekannter Unterbefehl oder falsche Argumentanzahl)","severity":"caution","meaning":"Ein Container-Befehl (z. B. CONFIG, CLIENT, ACL, XINFO) hat einen ungültigen Unterbefehl erhalten, oder dem Unterbefehl wurde eine falsche Anzahl von Argumenten übergeben. Redis hat den Unterbefehl nicht erkannt.","cause":"Ein Tippfehler im Namen des Unterbefehls, ein in der verwendeten Redis-Version nicht vorhandener Unterbefehl, eine falsche/fehlende Anzahl von Argumenten oder falsche Syntax des Unterbefehls.","fix":"Verwende den richtigen Namen des Unterbefehls und die richtigen Argumente; ziehe den Unterbefehl HELP des Befehls heran (z. B. CONFIG HELP, CLIENT HELP). Prüfe, ob deine Redis-Version diesen Unterbefehl unterstützt. Korrigiere Argumentanzahl und Syntax gemäß der Dokumentation.","tags":["err","subcommand","unknown subcommand","argument","config","client"]},{"code":"ERR Client sent AUTH, but no password is set","title":"ERR AUTH <password> called without any password configured for the default user (Redis 6+) / ERR Client sent AUTH, but no password is set (Redis <6) (AUTH ohne konfiguriertes Passwort)","severity":"caution","meaning":"Der Client hat versucht, sich mit AUTH zu authentifizieren, aber auf dem Server ist kein Passwort (requirepass) und kein ACL-Passwort definiert. AUTH wurde abgelehnt, weil es keine zu prüfenden Zugangsdaten gibt. Redis 6+ liefert in diesem Fall \"ERR AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct?\"; \"Client sent AUTH, but no password is set\" ist die Meldung vor Redis 6.","cause":"requirepass ist auf dem Server leer, der Client sendet AUTH unnötigerweise, im Connection String ist ein Passwort definiert, während der Server keines hat, oder eine abweichende Konfiguration.","fix":"Verlangt der Server kein Passwort, entferne den Befehl AUTH bzw. die Passworteinstellung aus dem Client. Willst du Passwortschutz, setze requirepass auf dem Server (CONFIG SET requirepass password) oder definiere ein Passwort für den ACL-Benutzer. Bringe Client- und Serverkonfiguration in Einklang.","tags":["err","auth","password","requirepass","authentication","acl"]}]},"errors/elasticsearch-more.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"action_request_validation_exception","title":"action_request_validation_exception","severity":"caution","meaning":"Die Anfrage hat die Validierung vor der Ausführung nicht bestanden. Ein Pflichtfeld fehlt oder Felder sind ungültig, daher lehnt Elasticsearch die Operation ab.","cause":"Fehlende Pflichtparameter (z. B. kein Indexname angegeben), ein leerer Body, eine ungültige Kombination von Feldern oder in einer Bulk-/Index-Anfrage nicht gelieferte Pflichtinformationen.","fix":"Prüfe die Liste der fehlenden/ungültigen Felder in der Fehlermeldung und liefere die Pflichtfelder. Vervollständige Request-Body und Parameter gemäß der API-Dokumentation. Prüfe bei Bulk-Requests, ob jede Action-Zeile korrekte Metadaten und einen korrekten Body hat.","tags":["validation","validation error","request","request error","action"]},{"code":"index_closed_exception","title":"index_closed_exception","severity":"caution","meaning":"Der Zielindex ist geschlossen. Ein geschlossener Index bedient keine Lese-/Schreibanfragen, daher konnte die Operation nicht ausgeführt werden.","cause":"Der Index wurde zuvor mit der Close-API geschlossen, bei Wartungs-/ILM-Operationen geschlossen oder deaktiviert, um Ressourcen zu sparen.","fix":"Öffne den Index wieder: POST /index_name/_open. Warte nach dem Öffnen, bis die Shards zugewiesen sind (green/yellow). Untersuche, warum der Index geschlossen wurde (ILM-Policy, manuelle Aktion). Überprüfe die Strategie, selten genutzte Indizes geschlossen zu halten.","tags":["index closed","closed index","open","index","closed"]},{"code":"unavailable_shards_exception","title":"unavailable_shards_exception","severity":"danger","meaning":"Ein oder mehrere für die Operation benötigte Shards sind nicht verfügbar, daher konnte die Anfrage (meist ein Schreibvorgang) nicht abgeschlossen werden. Es wurden nicht genügend aktive Shard-Kopien gefunden.","cause":"Nicht zugewiesene Shards wegen eines Knotenabsturzes/-ausscheidens, eine Überschreitung des Disk Watermark, fehlende Replicas, eine nicht erfüllte Bedingung wait_for_active_shards oder Clusterzustand red.","fix":"Prüfe Cluster- und Shard-Zustand: GET _cluster/health und GET _cat/shards?v. Untersuche, warum Shards nicht zugewiesen sind: GET _cluster/allocation/explain. Bringe ausgefallene Knoten wieder hoch, schaffe Plattenplatz (Watermark) und überprüfe die Replica-Einstellungen. Setze bei Bedarf wait_for_active_shards passend.","tags":["shard","unavailable","inaccessible","cluster","allocation","danger"]},{"code":"query_shard_exception","title":"query_shard_exception","severity":"caution","meaning":"Eine Abfrage ist bei der Ausführung auf einem bestimmten Shard fehlgeschlagen. Meist passiert das, weil die Abfrage mit dem Mapping inkompatibel ist oder eine ungültige Struktur hat.","cause":"Abfrage eines nicht existierenden Felds oder eines Felds falschen Typs, Versuch einer Aggregation/Sortierung auf einem text-Feld (Fielddata deaktiviert), ungültige Abfragesyntax, falsche Analyse oder ein inkompatibler Feldtyp.","fix":"Prüfe Feld/Grund in den Fehlerdetails. Prüfe das Mapping des abgefragten Felds: GET /index/_mapping. Verwende für Sortierung/Aggregation das keyword-Unterfeld (field.keyword) statt text. Korrigiere die Abfragesyntax und stelle sicher, dass die Feldtypen mit der Abfrage kompatibel sind.","tags":["query","query error","shard","mapping","fielddata","query shard"]},{"code":"script_exception","title":"script_exception","severity":"caution","meaning":"Beim Kompilieren oder Ausführen eines Skripts (meist Painless) ist ein Fehler aufgetreten. Das Skript ist falsch geschrieben oder hat zur Laufzeit eine Exception ausgelöst.","cause":"Painless-Syntaxfehler, Zugriff auf einen Null-Wert, Typabweichung, Verwendung eines nicht existierenden Felds bzw. einer nicht existierenden Variable, Überschreiten des Limits für Skriptkompilierungen oder ein nicht erlaubter API-Aufruf.","fix":"Prüfe die Kompilier-/Laufzeitdetails in der Fehlermeldung. Korrigiere Skriptsyntax und Feldzugriffe; ergänze Null-Prüfungen (z. B. doc['field'].size() > 0). Parametrisiere häufig genutzte Skripte (stored/params), damit das Kompilierungslimit nicht überschritten wird. Verwende korrekte Feldnamen und -typen.","tags":["script","painless","compilation","runtime","script exception"]},{"code":"number_format_exception","title":"number_format_exception","severity":"caution","meaning":"Ein Wert, der numerisch sein sollte, konnte nicht in eine Zahl konvertiert werden. Wo ein Feld oder Parameter eine Zahl erwartet, wurde ein ungültiger/nicht-numerischer String geliefert.","cause":"Nicht-numerischer Text an ein numerisches Feld (integer/long/double), Versuch, einen leeren String in eine Zahl zu konvertieren, ein falsches Dezimalformat oder mit dem Mapping inkompatible Eingabedaten.","fix":"Sende gültige numerische Werte an das Feld. Validiere/transformiere die Quelldaten vor dem Indexieren. Verwende null für leere Werte oder lass das Feld weg. Überprüfe die Einstellung coerce und den Feldtyp im Mapping. Verwende einen Punkt als Dezimaltrennzeichen.","tags":["number format","number","conversion","numeric","format"]},{"code":"connect_transport_exception","title":"connect_transport_exception","severity":"danger","meaning":"Über die Transportschicht (9300) konnte keine Verbindung zu einem Knoten hergestellt werden. Die Kommunikation zwischen Knoten oder zwischen Client und Cluster kam nicht zustande, daher konnte die Operation nicht ausgeführt werden.","cause":"Zielknoten ausgefallen/nicht erreichbar, Netzwerk-/Firewall-Probleme, falsche Transportadresse bzw. falscher Port, abweichende TLS-Konfiguration, ein DNS-Problem oder abweichender Clustername bzw. abweichende Version.","fix":"Prüfe, ob der Zielknoten läuft und der Transport-Port (Standard 9300) über das Netzwerk erreichbar ist. Prüfe Firewall-/Netzwerkregeln und die Discovery-Konfiguration. Stelle sicher, dass die TLS-/Zertifikatseinstellungen auf allen Knoten konsistent sind. Prüfe die Knotenlogs und die Kompatibilität von cluster.name/Version.","tags":["transport","connection","connect","network","node","danger"]},{"code":"repository_missing_exception","title":"repository_missing_exception","severity":"caution","meaning":"Das in einer Snapshot-/Restore-Operation referenzierte Snapshot-Repository wurde nicht gefunden. Unter dem angegebenen Namen ist kein Repository registriert.","cause":"Das Repository wurde nie registriert, ein Tippfehler im Namen, das Repository wurde gelöscht, es ist auf einem anderen Cluster definiert oder es wurde nach dem Zurücksetzen des Cluster State nicht neu registriert.","fix":"Registrierte Repositories auflisten: GET _snapshot/_all. Registriere das Repository mit korrektem Namen und korrekten Einstellungen neu: PUT _snapshot/repo_name (mit type und settings). Stelle sicher, dass der Repository-Name korrekt geschrieben ist. Prüfe, ob der Zugriff auf das gemeinsame Repository (fs/s3 usw.) auf allen Knoten funktioniert.","tags":["repository","snapshot repository","snapshot","missing","backup","restore"]},{"code":"concurrent_snapshot_execution_exception","title":"concurrent_snapshot_execution_exception","severity":"caution","meaning":"Eine neue Snapshot-/Restore-Anfrage wurde abgelehnt, weil gleichzeitig eine kollidierende Snapshot-Operation läuft. Auf dem Repository oder Index ist noch eine andere Snapshot-Operation im Gange.","cause":"Ein vorheriger Snapshot läuft noch, ein Konflikt paralleler Snapshot-/Restore-Operationen auf demselben Index, eine SLM-Policy (Snapshot Lifecycle), die mit einem manuellen Snapshot kollidiert, oder Auslöser in kurzen Abständen.","fix":"Prüfe laufende Snapshots: GET _snapshot/repo/_current und GET _snapshot/_status. Warte, bis die aktuelle Operation beendet ist, oder brich sie bei Bedarf ab: DELETE _snapshot/repo/snapshot_name. Plane Snapshots so, dass sie sich nicht überschneiden. Stimme manuelle Operationen mit SLM ab.","tags":["snapshot","concurrent","conflict","simultaneous","slm","backup"]},{"code":"invalid_index_name_exception","title":"invalid_index_name_exception","severity":"caution","meaning":"Der angegebene Indexname entspricht nicht den Benennungsregeln von Elasticsearch. Der Name enthält ungültige Zeichen oder verletzt Formatregeln.","cause":"Großbuchstaben, nicht erlaubte Zeichen (\\ / * ? \" < > | Leerzeichen , #), ein Name, der mit _, - oder + beginnt, ein zu langer Name (über 255 Bytes) oder reservierte Namen wie . / ... (Mit einem Punkt beginnende Namen sind nicht verboten, aber für versteckte/System-Indizes reserviert und erzeugen eine Warnung.)","fix":"Mache den Indexnamen regelkonform: Verwende nur Kleinbuchstaben; vermeide verbotene Zeichen; beginne den Namen nicht mit _, - oder + und verwende mit einem Punkt beginnende Namen nur für System-/versteckte Indizes. Halte den Namen unter 255 Bytes. Setze diese Regeln in der Anwendung beim Erzeugen von Indexnamen durch.","tags":["index name","index naming","invalid","invalid name","naming"]},{"code":"resource_not_found_exception","title":"resource_not_found_exception","severity":"caution","meaning":"Die angeforderte Ressource (Dokument, Index, Task, Pipeline, Modell usw.) wurde nicht gefunden. Zur angegebenen ID bzw. zum angegebenen Namen existiert keine Ressource.","cause":"Eine nicht existierende Dokument-ID, ein gelöschter oder nie angelegter Index bzw. eine solche Pipeline, ein falsch geschriebener Name, ein noch nicht abgeschlossener oder bereits bereinigter Task oder eine an den falschen Cluster gesendete Anfrage.","fix":"Prüfe Ressourcenname/-ID und ob die Ressource existiert (z. B. GET /index/_doc/id, GET _ingest/pipeline/name). Stelle sicher, dass du die Anfrage an den richtigen Index und Cluster sendest. Überprüfe die Reihenfolge von Lösch-/Anlegeoperationen. Prüfe bei Tasks den Status mit der _tasks-API.","tags":["not found","resource not found","resource","missing resource","404"]},{"code":"process_cluster_event_timeout_exception","title":"process_cluster_event_timeout_exception","severity":"danger","meaning":"Eine Aktualisierung des Cluster State (z. B. Indexerstellung, Mapping-Änderung) wurde nicht in der vorgesehenen Zeit abgeschlossen und ist in einen Timeout gelaufen. Der Master-Knoten konnte die Aktualisierung nicht rechtzeitig verarbeiten.","cause":"Master-Knoten unter hoher Last, ein sehr großer/komplexer Cluster State, häufige und umfangreiche Mapping-/Template-Änderungen, ein langsamer Master oder Probleme bei der Master-Wahl.","fix":"Prüfe Last und Zustand des Master-Knotens: GET _cluster/health, GET _cat/master und GET _cluster/pending_tasks für ausstehende Tasks. Verkleinere den Cluster State (unnötige Indizes/Mappings/Templates reduzieren, Mapping-Explosion durch dynamisches Mapping verhindern). Führe schwere DDL-artige Operationen in Batches zu verkehrsarmen Zeiten aus. Erhöhe bei Bedarf die Master-Ressourcen.","tags":["cluster event","timeout","timed out","master","cluster state","danger"]}]},"errors/oracle-more2.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00955","title":"name is already used by an existing object (Name wird bereits von einem vorhandenen Objekt verwendet)","severity":"caution","meaning":"Der Name des anzulegenden Objekts (Tabelle, Index, View, Sequence, Synonym usw.) wird im selben Schema bereits von einem anderen Objekt verwendet. In Oracle sind Objektnamen innerhalb eines Schemas eindeutig, und selbst unterschiedliche Typen wie Tabellen, Views und Sequences teilen sich denselben Namensraum.","cause":"Der im CREATE-Statement gewählte Name kollidiert mit einem vorhandenen Objekt. Eine häufige Falle: Versuch, eine Sequence oder View mit demselben Namen wie eine Tabelle anzulegen; oder ein automatisch erzeugter Indexname (z. B. der Primary-Key-Index) kollidiert mit dem von dir angegebenen Namen.","fix":"Wähle einen anderen Namen oder entferne das kollidierende Objekt. Um zu finden, welches Objekt den Namen verwendet: SELECT object_type, object_name FROM user_objects WHERE object_name = 'YOUR_NAME'; Willst du es wirklich ersetzen, lösche das alte Objekt mit DROP (sichere vorher seine Daten). CREATE OR REPLACE gibt es für Views, Synonyme, Typen und PL/SQL-Objekte (Prozedur, Funktion, Package, Trigger); für Tabellen, Indizes und Sequences existiert es nicht.","tags":["ddl","create","name","conflict","duplicate","object"]},{"code":"ORA-02449","title":"unique/primary keys in table referenced by foreign keys (Unique-/Primärschlüssel der Tabelle von Foreign Keys referenziert)","severity":"caution","meaning":"Der Primär- oder eindeutige Schlüssel der zu löschenden Tabelle wird von Foreign Keys in anderen Tabellen referenziert. Oracle lässt dich die Tabelle nicht löschen, ohne diese Abhängigkeiten zuerst zu entfernen.","cause":"Du hast DROP TABLE ohne CASCADE CONSTRAINTS ausgeführt, während andere Tabellen über FKs auf diese Tabelle verweisen. Oracle setzt die referenzielle Integrität durch und verhindert verwaiste Foreign Keys.","fix":"Um sie zusammen mit den abhängigen Foreign Keys zu löschen: DROP TABLE table_name CASCADE CONSTRAINTS; Dies entfernt auch die zugehörigen FK-Constraints in den anderen Tabellen (die Daten in diesen Tabellen werden nicht gelöscht). Um zu sehen, welche Constraints davon abhängen: SELECT table_name, constraint_name FROM all_constraints WHERE r_constraint_name IN (SELECT constraint_name FROM user_constraints WHERE table_name = 'TABLE_NAME');","tags":["drop","foreign key","foreign key reference","constraint","cascade","reference"]},{"code":"ORA-01950","title":"no privileges on tablespace 'name' (keine Berechtigungen auf dem Tablespace)","severity":"danger","meaning":"Der Benutzer hat keine Quota auf dem Tablespace, in den er schreiben will. In Oracle muss ein Benutzer eine Quota auf dem Tablespace haben, um Platz für ein Objekt zu allokieren.","cause":"Dem Benutzer wurde keine Quota auf dem Tablespace gewährt; typischerweise versucht ein neuer Benutzer ohne die Systemberechtigung UNLIMITED TABLESPACE, in seinem Default-Tablespace eine Tabelle/einen Index anzulegen oder Daten einzufügen. Tritt bei INSERT, CREATE TABLE oder Indexwachstum auf.","fix":"Gewähre dem Benutzer als DBA eine Quota: ALTER USER app_user QUOTA UNLIMITED ON tablespace_name; oder ein bestimmtes Limit: ALTER USER app_user QUOTA 500M ON users; Alternativ (mit Bedacht verwenden) kann GRANT UNLIMITED TABLESPACE TO app_user; vergeben werden, dies gewährt aber unbegrenzte Rechte auf allen Tablespaces und wird allgemein nicht empfohlen.","tags":["tablespace","quota","tablespace quota","privilege","privilege","insert"]},{"code":"ORA-01110","title":"data file N: 'path'","severity":"danger","meaning":"Eine ergänzende (sekundäre) Meldung, die auf ein Problem mit der Datendatei mit der angegebenen Nummer und dem angegebenen Pfad hinweist. Sie erscheint nie allein; ihr geht ein anderer ORA-Fehler voraus, der das eigentliche Problem meldet, und diese Zeile gibt nur an, welche Datendatei betroffen ist.","cause":"Sie begleitet meist einen primären Fehler, etwa eine offline gesetzte Datendatei (ORA-00376), erforderliche Media Recovery (ORA-01113), Blockkorruption (ORA-01578) oder eine nicht lesbare Datei. Auslöser können ein Plattenausfall, versehentliches Löschen, eine verschobene Datei oder ein unvollständiges Restore aus dem Backup sein.","fix":"Lies den eigentlichen ORA-Fehler vor dieser Zeile und behebe ihn. Prüfe die betroffene Datei: SELECT file#, name, status FROM v$datafile; Falls Recovery nötig ist: RECOVER DATAFILE 'path'; dann ALTER DATABASE DATAFILE 'path' ONLINE; Ist die Datei physisch verloren, stelle sie mit RMAN wieder her: RESTORE DATAFILE N; RECOVER DATAFILE N;","tags":["datafile","data file","recovery","media recovery","offline","rman"]},{"code":"ORA-16038","title":"log N sequence# M cannot be archived","severity":"danger","meaning":"Oracle kann eine Online-Redo-Log-Datei nicht archivieren und diese Log-Gruppe daher nicht wiederverwenden. Hängt die Archivierung im ARCHIVELOG-Modus, nimmt die Datenbank unter Umständen keine neuen Transaktionen mehr an und hängt.","cause":"Meist ist das Archivlog-Ziel oder die Fast Recovery Area (FRA) voll; der Plattenplatz ist erschöpft; das Archivziel ist nicht erreichbar; oder es gibt ein Berechtigungs-/Mount-Problem. Tritt oft zusammen mit ORA-19809 (limit exceeded for recovery files) oder ORA-00312 auf.","fix":"Schaffe Platz im Archivziel/in der FRA: Sichere alte Archivlogs mit RMAN auf ein Ziel außerhalb der FRA und lösche sie (DELETE ARCHIVELOG ALL COMPLETED BEFORE ...; bei Data Guard keine Archivlogs löschen, die noch nicht zum Standby übertragen bzw. dort angewendet wurden), oder erhöhe db_recovery_file_dest_size. Prüfe den Zielstatus: ARCHIVE LOG LIST; und SELECT dest_name, status, error FROM v$archive_dest WHERE status != 'INACTIVE'; Sobald das Problem behoben ist, setzt die Archivierung dort fort, wo sie stehen geblieben ist.","tags":["archivelog","archive","redo","fra","recovery area","archiver stuck"]},{"code":"ORA-19809","title":"limit exceeded for recovery files","severity":"danger","meaning":"Der für die Fast Recovery Area (FRA) zugewiesene Platz (db_recovery_file_dest_size) ist voll. Ohne Platz für Archivlogs, Flashback-Logs und Backups kann Oracle keine neuen Recovery-Dateien anlegen, und die Datenbank hängt unter Umständen.","cause":"Archivlogs haben sich in der FRA angesammelt, weil sie nicht regelmäßig gesichert und bereinigt wurden; der Verbrauch durch Flashback-Logs ist gewachsen; oder die zugewiesene Größe ist für die tatsächliche Last zu klein. Tritt zusammen mit Archivierungsfehlern wie ORA-16038 auf.","fix":"Prüfe die FRA-Belegung: SELECT * FROM v$recovery_file_dest; und SELECT * FROM v$flash_recovery_area_usage; Lösche nicht benötigte Archivlogs mit RMAN: DELETE ARCHIVELOG ALL BACKED UP 1 TIMES TO DISK; (bei Data Guard vorher prüfen, dass sie auf dem Standby angewendet wurden; CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY wird empfohlen) oder erhöhe als dauerhafte Lösung die Größe: ALTER SYSTEM SET db_recovery_file_dest_size = 50G SCOPE=BOTH; Richte einen RMAN-Job ein, der Archivlogs regelmäßig sichert.","tags":["fra","recovery area","db_recovery_file_dest_size","archivelog","space","rman"]},{"code":"ORA-00376","title":"file N cannot be read at this time","severity":"danger","meaning":"Die Datendatei mit der angegebenen Nummer kann gerade nicht gelesen werden, weil sie offline oder nicht zugänglich ist. Abfragen und Operationen auf Daten im betroffenen Tablespace schlagen mit diesem Fehler fehl.","cause":"Die Datendatei wurde nach einem Fehler automatisch oder manuell offline gesetzt; sie wartet auf Media Recovery; die Platte ist nicht mehr erreichbar; oder eine Datei, die im NOARCHIVELOG-Modus nicht wiederherstellbar ist, wurde offline gelassen. Meist begleitet von einer ORA-01110-Zeile, die die Datei benennt.","fix":"Dateistatus anzeigen: SELECT file#, name, status FROM v$datafile WHERE status != 'ONLINE'; Wiederherstellen und online setzen: RECOVER DATAFILE N; dann ALTER DATABASE DATAFILE N ONLINE; Bei physischer Korruption/Verlust mit RMAN wiederherstellen: RESTORE DATAFILE N; RECOVER DATAFILE N; Behebe außerdem das zugrunde liegende Platten-/Hardwareproblem.","tags":["datafile","data file","offline","recovery","media recovery","read"]},{"code":"ORA-12520","title":"TNS:listener could not find available handler for requested type of server","severity":"danger","meaning":"Der Listener hat für den angeforderten Verbindungstyp (dedicated oder shared) keinen passenden Serverprozess (Handler) gefunden. Der Client erreicht den Listener, aber die Verbindung kann nicht an einen Serverprozess übergeben werden.","cause":"Die Instanz ist nicht beim Listener registriert oder die Registrierung ist defekt; das PROCESSES-/SESSIONS-Limit ist erreicht und es kann kein neuer Dedicated-Prozess gestartet werden; in einer Shared-Server-Konfiguration fehlen Dispatcher oder es sind zu wenige; oder die Instanz wurde gerade gestartet und hat sich noch nicht beim Listener registriert.","fix":"Prüfe die Services, die der Listener sieht: lsnrctl services (Anzahl und Status der Handler). Registriere die Instanz erneut beim Listener: ALTER SYSTEM REGISTER; Prüfe das PROCESSES-Limit: SELECT resource_name, current_utilization, max_utilization, limit_value FROM v$resource_limit WHERE resource_name IN ('processes','sessions'); Erhöhe es bei Bedarf mit ALTER SYSTEM SET processes=... SCOPE=SPFILE; und starte die Instanz neu.","tags":["listener","tns","connection","handler","processes","dispatcher"]},{"code":"ORA-28001","title":"the password has expired","severity":"caution","meaning":"Das Passwort des Datenbankbenutzers, mit dem du dich verbindest, ist abgelaufen, weil die PASSWORD_LIFE_TIME seines Profils überschritten ist. Bis zur Änderung des Passworts kann keine Session geöffnet werden.","cause":"Die im Profil des Benutzers festgelegte Passwortlebensdauer ist abgelaufen, und auch die Karenzzeit ist verstrichen. Ab Oracle 11g laufen Passwörter im DEFAULT-Profil alle 180 Tage ab; das führt vor allem bei langlebigen Service-Accounts von Anwendungen zu unerwarteten Ausfällen.","fix":"Ändere das Passwort: ALTER USER app_user IDENTIFIED BY new_password; Um wiederkehrende Probleme mit Anwendungsaccounts zu vermeiden, nutze ein Profil ohne Passwortablauf: CREATE PROFILE app_profile LIMIT PASSWORD_LIFE_TIME UNLIMITED; dann ALTER USER app_user PROFILE app_profile; Sinnvoll ist auch ein Monitoring, das vor dem Ablauf warnt. Prüfe den Status über ACCOUNT_STATUS in DBA_USERS.","tags":["password","password expired","expired","profile","account","login"]},{"code":"ORA-38029","title":"object statistics are locked","severity":"caution","meaning":"Das Sammeln/Löschen von Statistiken wurde abgelehnt, weil die Optimizer-Statistiken einer Tabelle (oder eines Schemas) gesperrt sind. Gesperrte Statistiken sind vor der Neuberechnung mit DBMS_STATS geschützt.","cause":"DBMS_STATS.LOCK_TABLE_STATS oder LOCK_SCHEMA_STATS wurde zuvor aufgerufen (z. B. um Statistiken festzuschreiben), oder ein Data-Pump-Import (insbesondere CONTENT=METADATA_ONLY) hat die Statistiken unbeabsichtigt gesperrt hinterlassen. Ein GATHER_TABLE_STATS bei gesperrten Statistiken löst diesen Fehler aus.","fix":"Entsperre die Statistiken: EXEC DBMS_STATS.UNLOCK_TABLE_STATS('APP_SCHEMA','MY_TABLE'); oder auf Schemaebene DBMS_STATS.UNLOCK_SCHEMA_STATS('APP_SCHEMA'); Willst du die Sperre nicht aufheben, aber trotzdem sammeln, nutze FORCE=>TRUE: EXEC DBMS_STATS.GATHER_TABLE_STATS('APP_SCHEMA','MY_TABLE', force=>TRUE); Sperrstatus anzeigen: SELECT table_name, stattype_locked FROM dba_tab_statistics WHERE stattype_locked IS NOT NULL;","tags":["statistics","optimizer statistics","dbms_stats","locked","lock","optimizer"]}]},"errors/postgres-more2.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"54001","title":"statement_too_complex","severity":"caution","meaning":"Die Anweisung ist komplexer, als PostgreSQL verarbeiten kann; meist lassen sehr tief verschachtelte Ausdrücke oder viele verschachtelte Operatoren den Ausdrucksbaum das Stack-Limit von Parser/Planner überschreiten. Gehört zu Klasse 54 (program_limit_exceeded).","cause":"Übermäßig tiefe Klammerung/verschachtelte Unterabfragen oder OR/AND-Ketten; riesige automatisch generierte WHERE ... IN (...)-Listen; oder sehr große Ausdrucksbäume aus rekursiven Views. Das Limit max_stack_depth wird erreicht.","fix":"Vereinfache die Abfrage: Verschiebe lange IN-Listen in eine temporäre Tabelle oder eine VALUES-Menge und verknüpfe per JOIN; zerlege verschachtelte Ausdrücke; prüfe riesige ORM-generierte Abfragen. Wenn wirklich nötig und das Stack-Limit des Betriebssystems es zulässt, erhöhe max_stack_depth vorsichtig (z. B. SET max_stack_depth = '4MB';), halte es aber unter dem ulimit des Betriebssystems.","tags":["program_limit","complex","stack","nested","query","max_stack_depth"]},{"code":"58030","title":"io_error","severity":"danger","meaning":"PostgreSQL ist beim Lesen oder Schreiben einer Datei auf einen Ein-/Ausgabefehler (I/O) auf Betriebssystemebene gestoßen. Das deutet auf ein physisches oder Dateisystemproblem beim Zugriff auf Datenbankdateien hin. Gehört zu Klasse 58 (system_error).","cause":"Plattenausfall oder defekte Sektoren; eine volle Platte; Dateisystemfehler; ein Ausfall von Netzwerkspeicher (NFS/SAN); Berechtigungsprobleme; oder erschöpfte Limits für Dateideskriptoren/Inodes im Betriebssystem.","fix":"Prüfe Plattenzustand und freien Platz (df -h, dmesg, SMART-Daten). Sieh in den PostgreSQL-Logs nach, welche Datei betroffen ist. Behebe Dateisystemfehler; tausche bei Bedarf die Hardware. Bei Verdacht auf Datenkorruption plane ein Restore aus dem letzten fehlerfreien Backup und lass Prüfsummen künftig aktiviert (initdb --data-checksums).","tags":["system_error","io","disk","hardware","file","corruption"]},{"code":"22004","title":"null_value_not_allowed","severity":"caution","meaning":"Ein NULL wurde übergeben, wo NULL nicht akzeptiert wird. Das unterscheidet sich von einem NOT-NULL-Constraint einer Spalte (23502); es entsteht meist, wenn in einem Funktions- oder Prozedurkontext NULL an einen Parameter oder eine Operation übergeben wird, die NULL nicht akzeptiert. Gehört zu Klasse 22 (data_exception).","cause":"Zuweisung von NULL an eine als NOT NULL deklarierte PL/pgSQL-Variable; Übergabe eines NULL-Abfragestrings an EXECUTE; oder Übergabe von NULL an Argumente eingebauter Funktionen, die es nicht akzeptieren. Hinweis: Eine als STRICT markierte Funktion löst bei NULL-Eingabe keinen Fehler aus — sie gibt ohne Ausführung NULL zurück.","fix":"Finde die fehlerhafte Zeile (CONTEXT) und sichere den dort ankommenden Wert mit einer NULL-Prüfung ab oder liefere mit COALESCE einen Standardwert: COALESCE(value, default_value). Prüfe in PL/pgSQL Werte vor der Zuweisung an NOT-NULL-Variablen; stelle in dynamischem SQL sicher, dass der an EXECUTE übergebene String nicht NULL ist. Verhindere in der Anwendungsschicht, dass Pflichtfelder als NULL gesendet werden.","tags":["data_exception","null","function","strict","parameter","coalesce"]},{"code":"42P18","title":"indeterminate_datatype","severity":"caution","meaning":"PostgreSQL konnte den Datentyp eines Ausdrucks oder Parameters nicht aus dem Kontext ableiten. Am häufigsten, wenn ein untypisierter Platzhalter (etwa $1) oder ein nacktes NULL ohne umgebenden Typhinweis verwendet wird.","cause":"Untypisierte Parameter in Prepared Statements; Vergleiche, bei denen beide Seiten unbestimmt sind, etwa „WHERE $1 = $2“; oder kontextfreie Verwendung von NULL-/Array-Literalen. Der Planner kann nicht entscheiden, welchen Typ er verwenden soll.","fix":"Füge dem unbestimmten Ausdruck einen expliziten Cast hinzu: $1::text, $1::integer, NULL::date usw. Zum Beispiel: SELECT $1::int + 1; Gib Typen für Treiber-/ORM-Parameter an. Stelle bei Spaltenvergleichen sicher, dass mindestens eine Seite einen bekannten Typ hat.","tags":["syntax","type","indeterminate","cast","parameter","prepared"]},{"code":"42P02","title":"undefined_parameter","severity":"caution","meaning":"Die Abfrage referenziert einen Parameter, der nicht existiert. PostgreSQL meldet, dass der referenzierte Positionsparameter (z. B. $2) nicht definiert ist. Gehört zu Klasse 42 (syntax_error_or_access_rule_violation).","cause":"In einer mit PREPARE vorbereiteten Anweisung werden mehr Parameter referenziert als übergeben (z. B. zwei Parameter senden und $3 verwenden); übersprungene Parameternummern; oder falsche $N-Indizes in dynamisch zusammengesetztem SQL.","fix":"Stelle sicher, dass die Parameternummern zur Anzahl der übergebenen Werte passen; prüfe, dass sie bei $1 beginnen und lückenlos fortlaufen. Definiere die Parametertypen in der PREPARE-Anweisung vollständig: PREPARE p(int, text) AS SELECT ... WHERE a=$1 AND b=$2; Wenn du ein ORM/einen Treiber nutzt, vergleiche die Anzahl gebundener Parameter mit den Platzhaltern in der Abfrage.","tags":["syntax","parameter","prepared","undefined","placeholder","sql"]},{"code":"2F003","title":"prohibited_sql_statement_attempted","severity":"caution","meaning":"In einer SQL-Funktion/-Routine wurde eine Anweisung versucht, die in diesem Kontext verboten ist. Gehört zu Klasse 2F (sql_routine_exception). Zum Beispiel versucht eine Funktion, die nur lesen soll, Daten zu ändern.","cause":"Der PostgreSQL-Kern löst diesen Code selten aus; er stammt meist aus Code von Extensions oder prozeduralen Sprachen. Hinweis: Eine als IMMUTABLE/STABLE markierte Funktion, die INSERT/UPDATE/DELETE ausführt, löst nicht 2F003 aus, sondern '... is not allowed in a non-volatile function' mit Code 0A000 (feature_not_supported).","fix":"Lies in der Fehlermeldung, welche Anweisung verboten war, und prüfe die Dokumentation der betroffenen Routine/Extension. Bekommst du den Fehler zur nicht-volatilen Funktion (0A000), definiere die datenändernde Funktion als VOLATILE (das ist der Standard): CREATE FUNCTION ... LANGUAGE plpgsql VOLATILE. Entferne datenändernde Aufrufe aus Stellen, die nur lesen dürfen (Indexausdrücke, Constraint-Prüfungen).","tags":["sql_routine","function","volatile","prohibited","immutable","side effect"]},{"code":"40003","title":"statement_completion_unknown","severity":"danger","meaning":"Der Ausgang der Transaktion ist unbekannt; die Verbindung zum Server brach ab, bevor klar war, ob die Transaktion abgeschlossen wurde (vor allem beim COMMIT). Der Client kann nicht sicher wissen, ob die Änderungen dauerhaft sind. Gehört zu Klasse 40 (transaction_rollback).","cause":"Das Netzwerk fiel aus, der Server stürzte ab oder ein Failover fand statt, nachdem COMMIT gesendet, aber bevor die Antwort eingetroffen war. Die Transaktion kann auf dem Server abgeschlossen oder zurückgerollt worden sein.","fix":"Wiederhole die Transaktion nicht blind; prüfe zuerst, ob sie tatsächlich angewendet wurde (z. B. durch Abfrage der erwarteten Zeile). Gestalte Transaktionen idempotent; verwende eindeutige fachliche Schlüssel/Idempotenzschlüssel. Implementiere im Connection Pool robuste Logik für Reconnect und Validierung.","tags":["transaction","commit","unknown","connection","idempotent","failover"]},{"code":"08003","title":"connection_does_not_exist","severity":"danger","meaning":"Es wurde eine Operation auf einer Datenbankverbindung versucht, die nicht existiert (bereits geschlossen). Die Anwendung wollte eine Abfrage über ein ungültiges Verbindungshandle senden. Gehört zu Klasse 08 (connection_exception).","cause":"Verbindungs-Timeout, Schließen durch den Server, ein Netzwerkausfall oder eine aus dem Pool entnommene Verbindung, die in Wirklichkeit tot ist; oder Wiederverwendung einer Verbindung, die im Anwendungscode per close() geschlossen wurde.","fix":"Aktiviere Lebendigkeitsprüfungen im Connection Pool (Validierungsabfrage / Keepalive); konfiguriere Einstellungen, die tote Verbindungen aus dem Pool entfernen (z. B. connectionTestQuery/maxLifetime in HikariCP). Stelle in der Anwendung sicher, dass die Verbindung vor der Nutzung offen ist, und verbinde dich bei Abbruch automatisch neu. Prüfe die Werte für TCP-Keepalive und idle_in_transaction_session_timeout.","tags":["connection","connection","closed","pool","pool","timeout"]},{"code":"25006","title":"read_only_sql_transaction","severity":"caution","meaning":"Ein datenändernder Befehl (INSERT/UPDATE/DELETE/DDL) wurde in einer Read-only-Transaktion versucht. Die Transaktion ist nur für Lesezugriffe konfiguriert. Gehört zu Klasse 25 (invalid_transaction_state).","cause":"SET TRANSACTION READ ONLY oder die Einstellung default_transaction_read_only=on; oder, am häufigsten, ein Schreibversuch auf einer Lese-Replica (Hot Standby / Streaming-Replica). Standby-Server sind per Definition schreibgeschützt.","fix":"Leite Schreibzugriffe auf den Primary-Server; prüfe dein Verbindungsrouting, das Lese- und Schreibzugriffe aufteilt. Falls versehentlich gesetzt: SET TRANSACTION READ WRITE; oder serverweit default_transaction_read_only=off. Stelle sicher, dass deine Anwendung Lese-/Schreibverkehr an den richtigen Knoten sendet.","tags":["transaction","read only","read only","replica","standby","primary"]},{"code":"21000","title":"cardinality_violation","severity":"caution","meaning":"Es wurde mehr als eine Zeile zurückgegeben, wo eine einzelne Zeile erwartet wurde. Eine skalare Unterabfrage lieferte mehrere Zeilen, oder MERGE/ON CONFLICT DO UPDATE versuchte, dieselbe Zielzeile ein zweites Mal zu ändern. Gehört zu Klasse 21.","cause":"Eine Unterabfrage in der SELECT-Liste oder in der Form WHERE = (subquery), die mehrere Zeilen liefert; derselbe Konfliktschlüssel wird in einem einzigen INSERT ... ON CONFLICT DO UPDATE mehrfach vorgeschlagen ('cannot affect row a second time'); oder mehrere Quellzeilen passen bei MERGE zu einer Zielzeile. UPDATE ... FROM löst bei mehreren Treffern keinen Fehler aus; die Zielzeile wird unvorhersehbar aus nur einer der Quellzeilen aktualisiert.","fix":"Stelle sicher, dass die Unterabfrage eine einzige Zeile liefert: Füge LIMIT 1 hinzu, nutze ein Aggregat (MAX/MIN) oder eine Bedingung, die den Treffer eindeutig macht. Dedupliziere bei MERGE- und ON-CONFLICT-Mustern die Quellseite mit DISTINCT ON/Aggregat; tu dasselbe bei UPDATE ... FROM, um stillschweigend falsche Ergebnisse zu vermeiden. Um unerwartete Mehrfachtreffer zu finden, führe die Unterabfrage allein aus und prüfe die Zeilenzahl.","tags":["cardinality","subquery","subquery","single row","join","scalar"]}]},"errors/mysql-more2.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1927","title":"MariaDB: ER_CONNECTION_KILLED: Connection was killed","severity":"danger","meaning":"MariaDB-spezifisch: Die laufende Verbindung bzw. der Thread wurde vom Server beendet, die laufende Abfrage unterbrochen und die Client-Verbindung getrennt. Diesen Code gibt es in MySQL nicht; dort erscheint dieselbe Situation als 1317 (ER_QUERY_INTERRUPTED), wenn die Abfrage unterbrochen wird, und clientseitig als 2013 (Lost connection), wenn die Verbindung abbricht.","cause":"Ein Administrator oder Monitoring-Tool sendet einen KILL-Befehl; der Server fährt herunter; Limits für inaktive Verbindungen in MariaDB wie idle_transaction_timeout/wait_timeout; oder der Server trennt die Verbindung unter Ressourcendruck. Tritt auch bei Wartungsarbeiten in Cloud-/Proxy-Umgebungen auf.","fix":"Untersuche, warum die Abfrage beendet wurde: SHOW PROCESSLIST und das Slow Query Log für lang laufende Abfragen; Zeitpläne für Serverwartung/-neustarts; Richtlinien zum Beenden von Verbindungen. Fange diesen Fehler in der Anwendung ab und wiederhole sicher (sofern idempotent). Optimiere und zerlege lange Abfragen; nutze automatischen Reconnect im Connection Pool.","tags":["connection","connection killed","kill","thread","shutdown","retry"]},{"code":"1290","title":"ER_OPTION_PREVENTS_STATEMENT: running with the --read-only option","severity":"caution","meaning":"Der MySQL-Server hat eine datenändernde Anweisung (INSERT/UPDATE/DELETE/DDL) abgelehnt, weil er mit der Option --read-only oder --super-read-only läuft. Unter read_only können Benutzer mit CONNECTION_ADMIN/SUPER weiterhin schreiben; ist super_read_only aktiviert, können auch sie es nicht, und die Meldung lautet '--super-read-only option'.","cause":"read_only=ON oder super_read_only=ON; meist ein versehentlicher Schreibversuch auf einer Replica. Replicas und Group-Replication-Secondaries werden für die Datenkonsistenz schreibgeschützt gehalten; Failover-Automatisierung setzt den alten Primary auf super_read_only. Auch Schreiben auf den falschen Knoten während Failover/Switchover löst den Fehler aus.","fix":"Leite Schreibzugriffe auf den Primary-Server (Source). Soll dieser Server tatsächlich der neue Primary werden: SET GLOBAL read_only = OFF; (das Abschalten von read_only schaltet automatisch auch super_read_only ab; das Einschalten von super_read_only schaltet auch read_only ein). Ändere es nicht manuell auf Knoten, die dein HA-Tool verwaltet (Orchestrator/Group Replication/InnoDB Cluster). Zustand prüfen: SELECT @@read_only, @@super_read_only;","tags":["read only","super_read_only","read-only mode","replica","primary","write","failover","1290"]},{"code":"1836","title":"ER_READ_ONLY_MODE: Running in read-only mode","severity":"caution","meaning":"InnoDB läuft im Read-only-Modus (innodb_read_only=ON) oder das Datenverzeichnis/Medium ist schreibgeschützt; Schreibzugriffe und DDL auf InnoDB-Tabellen werden abgelehnt. Hinweis: Ein Schreibversuch bei aktiviertem read_only/super_read_only liefert 1290 (ER_OPTION_PREVENTS_STATEMENT), nicht diesen Code.","cause":"Der Server wurde mit innodb_read_only=ON gestartet (z. B. schreibgeschütztes Medium, gemeinsames Datenverzeichnis, Kopie für Analysen); in diesem Modus können an InnoDB-Tabellen keine Änderungen wie INSERT/UPDATE/DELETE und CREATE/DROP/ALTER TABLE vorgenommen werden.","fix":"Werden Schreibzugriffe benötigt, starte den Server ohne innodb_read_only: Entferne die Zeile innodb_read_only=ON aus my.cnf und starte neu (diese Variable ist nicht dynamisch und erfordert einen Neustart). Zustand prüfen: SELECT @@innodb_read_only; Prüfe, ob Datenverzeichnis und Dateisystem beschreibbar sind. Schreibst du auf eine Replica/ein HA-Secondary (1290), leite die Schreibzugriffe auf den Primary.","tags":["innodb_read_only","read only","read-only mode","innodb","1836"]},{"code":"3819","title":"ER_CHECK_CONSTRAINT_VIOLATED: Check constraint is violated","severity":"caution","meaning":"Die Zeile, die du einfügst oder aktualisierst, verletzt einen auf der Tabelle definierten CHECK-Constraint. Ab MySQL 8.0.16 werden CHECK-Constraints tatsächlich durchgesetzt (frühere Versionen ignorierten sie).","cause":"Der Spaltenwert erfüllt die Bedingung im CHECK-Ausdruck nicht (z. B. Eingabe eines negativen Preises bei CHECK (price >= 0)). Nach einem Upgrade von älteren MySQL-Versionen auf 8.0.16+ kann der Fehler unerwartet auftreten, weil zuvor ignorierte Constraints nun durchgesetzt werden.","fix":"Stelle sicher, dass der Wert die Constraint-Bedingung erfüllt; ergänze eine Validierung in der Anwendung. Constraint-Definition anzeigen: SHOW CREATE TABLE my_table; Ist der Constraint wirklich überflüssig: ALTER TABLE my_table DROP CHECK constraint_name; oder vorübergehend ALTER TABLE my_table ALTER CHECK constraint_name NOT ENFORCED; Behebe außerdem die Quelle der fehlerhaften Daten.","tags":["check","constraint","check constraint","validation","8.0","violation"]},{"code":"1118","title":"ER_TOO_BIG_ROWSIZE: Row size too large","severity":"caution","meaning":"Die Zeilengröße der Tabelle, die du definierst, überschreitet die von der Storage Engine erlaubte maximale Zeilengröße. Die maximale Zeilengröße für Nicht-BLOB/TEXT-Spalten beträgt 65535 Bytes; InnoDB hat zusätzliche, von der Seitengröße abhängige Limits.","cause":"Definition vieler breiter Spalten (vor allem langer VARCHAR/CHAR); bis zu 4 Bytes pro Zeichen in Multibyte-Zeichensätzen wie utf8mb4; oder große Spalten, die im COMPACT/REDUNDANT-Zeilenformat von InnoDB inline gespeichert werden.","fix":"Wandle einige große Spalten in TEXT/BLOB um (diese können off-page gespeichert werden); verkleinere unnötige oder sehr breite Spalten oder teile die Tabelle in zwei auf. In InnoDB hilft ROW_FORMAT=DYNAMIC oder COMPRESSED, weil große Spalten off-page ausgelagert werden: ALTER TABLE my_table ROW_FORMAT=DYNAMIC; Berücksichtige außerdem innodb_page_size.","tags":["row size","row size too large","innodb","varchar","row_format","limit"]},{"code":"1170","title":"ER_BLOB_KEY_WITHOUT_LENGTH: BLOB/TEXT column used in key specification without a key length","severity":"caution","meaning":"Du hast eine BLOB- oder TEXT-Spalte in einer Index-/Schlüsseldefinition ohne Präfixlänge verwendet. Diese Typen können nicht vollständig indiziert werden; nur die ersten N Zeichen/Bytes sind indizierbar.","cause":"Definition eines INDEX/UNIQUE/PRIMARY KEY auf einer TEXT/BLOB-Spalte ohne Längenangabe in CREATE TABLE oder CREATE INDEX. MySQL verlangt eine Präfixlänge, weil es diese potenziell sehr großen Werte nicht vollständig indizieren kann.","fix":"Gib im Index eine Präfixlänge an, etwa INDEX (description(255)). Beispiel: ALTER TABLE my_table ADD INDEX idx_description (description(191)); (191 wird bei utf8mb4 wegen des InnoDB-Limits für Schlüsselpräfixe häufig verwendet). Wird Volltextsuche benötigt, erwäge einen FULLTEXT-Index. Für Felder, die kurz und eindeutig sein sollen, ist die Umstellung der Spalte auf VARCHAR unter Umständen passender.","tags":["index","key","blob","text","prefix","prefix length"]},{"code":"1592","title":"ER_BINLOG_UNSAFE_STATEMENT: Unsafe statement written to the binary log","severity":"caution","meaning":"Bei Verwendung des STATEMENT-basierten Binlog-Formats wurde eine Anweisung mit nichtdeterministischem Ergebnis ausgeführt (die auf der Replica ein anderes Ergebnis liefern könnte). MySQL gibt beim Schreiben ins Binlog eine Warnung aus, weil das Risiko einer Dateninkonsistenz zwischen Source und Replica besteht.","cause":"Mit binlog_format=STATEMENT: nichtdeterministische Funktionen wie UUID(), SYSDATE() oder USER() (NOW() wird mit STATEMENT sicher repliziert), UPDATE/DELETE mit LIMIT, aber ohne ORDER BY, mit AUTO_INCREMENT gemischte INSERTs oder bestimmte benutzerdefinierte/Systemfunktionen. Diese Anweisungen liefern nicht zwingend auf jedem Server dasselbe Ergebnis.","fix":"Stelle das Binlog-Format auf zeilenbasiert um (empfohlen; ROW ist seit MySQL 5.7.7 der Standard): binlog_format=ROW in my.cnf oder SET PERSIST binlog_format = ROW; (die Variable binlog_format ist seit 8.0.34 veraltet; die Änderung gilt für neue Sessions). Das ROW-Format repliziert nichtdeterministische Anweisungen sicher. Musst du bei STATEMENT bleiben, ersetze nichtdeterministische Funktionen/Ausdrücke durch konstante Werte und ergänze DML mit LIMIT um ein deterministisches ORDER BY.","tags":["binlog","replication","unsafe","statement","row","nondeterministic"]},{"code":"3140","title":"ER_INVALID_JSON_TEXT: Invalid JSON text","severity":"caution","meaning":"An eine JSON-Spalte oder JSON-Funktion wurde ungültiger (syntaktisch fehlerhafter) JSON-Text übergeben. MySQL lehnt die Operation ab, weil es den Wert nicht als JSON parsen kann.","cause":"Ein nicht geschlossenes Anführungszeichen/eine nicht geschlossene Klammer, ein abschließendes Komma, einfache Anführungszeichen, Fehler bei Escape-Zeichen oder reiner Text, der an eine JSON-Spalte gesendet wird. Tritt auf, wenn fehlerhafter Text an Funktionen wie JSON_SET, JSON_INSERT oder an eine Spalte vom Typ JSON übergeben wird.","fix":"Korrigiere den JSON-Text zu gültiger Syntax: Verwende doppelte Anführungszeichen, entferne abschließende Kommas, prüfe Escapes. Teste, ob der Text gültig ist: SELECT JSON_VALID('{...}'); (liefert 1, wenn gültig). Serialisiere JSON in der Anwendungsschicht mit einer zuverlässigen Bibliothek und validiere es, bevor du es an den Server sendest.","tags":["json","invalid","syntax","json_valid","parse","data"]},{"code":"1526","title":"ER_NO_PARTITION_FOR_GIVEN_VALUE: Table has no partition for value","severity":"caution","meaning":"Der Partitionierungsschlüssel der einzufügenden Zeile passt zu keiner definierten Partition. Bei RANGE- oder LIST-Partitionierung schlägt das Einfügen fehl, weil keine Partition den eingehenden Wert abdeckt.","cause":"Bei RANGE-Partitionierung wird ein Wert jenseits der höchsten Grenze eingefügt, ohne dass eine MAXVALUE-Partition definiert ist (z. B. ein Datum in der Zukunft); bei LIST-Partitionierung kommt ein nicht aufgeführter Wert an. Häufig bei zeitpartitionierten Tabellen, wenn die Partition für den neuen Zeitraum nicht rechtzeitig angelegt wird.","fix":"Füge eine Partition hinzu, die den Wert abdeckt. Eine neue obere Partition für RANGE: ALTER TABLE my_table ADD PARTITION (PARTITION p2026 VALUES LESS THAN (2027)); Für eine Auffangpartition definiere bei RANGE MAXVALUE und bei LIST die betreffenden Werte. Richte für zeitbasierte Tabellen einen Wartungsjob (Event/Scheduler) ein, der Partitionen automatisch anlegt.","tags":["partition","partitioning","range","list","maxvalue","insert"]}]},"errors/mssql-more2.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"1101","title":"Could not allocate a new page because of insufficient disk space in filegroup","severity":"danger","meaning":"SQL Server wollte für ein Objekt eine neue Seite allokieren, aber die Dateigruppe hat nicht genügend Speicherplatz. Die Datendateien können weder wachsen noch eine freie Seite finden, daher schlägt der Schreibvorgang fehl.","cause":"Die Platte mit der Datendatei ist voll; Autogrow ist für die Datei deaktiviert oder MAXSIZE ist erreicht; oder alle Dateien der Dateigruppe haben ihre Größenobergrenze erreicht. Häufig bei großen INSERTs/Indexaufbauten.","fix":"Schaffe Platz auf der Platte oder erweitere die Dateigruppe. Vergrößere die Datei oder aktiviere Autogrow: ALTER DATABASE db MODIFY FILE (NAME = logical_name, SIZE = 20GB, MAXSIZE = UNLIMITED, FILEGROWTH = 512MB); Alternativ füge der Dateigruppe eine neue Datei hinzu (auf einer anderen Platte). Zur Dateibelegung sieh in sys.database_files nach und prüfe den freien Platz mit DBCC SQLPERF.","tags":["disk","filegroup","file group","autogrow","space","allocation"]},{"code":"3729","title":"Cannot DROP because it is being referenced by object","severity":"caution","meaning":"Das zu löschende Objekt (Tabelle, Typ, Funktion usw.) wird von einem anderen Objekt referenziert, und diese Abhängigkeit verhindert das Löschen. Insbesondere schemagebundene Objekte sind so geschützt.","cause":"Löschversuch für eine Tabelle/ein Objekt, von dem eine WITH SCHEMABINDING erstellte View oder Funktion abhängt; Löschen einer per Foreign Key referenzierten Tabelle; oder Entfernen eines benutzerdefinierten Typs, den eine Tabelle/ein Parameter noch verwendet.","fix":"Lösche zuerst die referenzierenden Objekte oder entferne die Abhängigkeit. Abhängigkeiten finden: SELECT referencing_schema_name, referencing_entity_name FROM sys.dm_sql_referencing_entities('app_schema.object', 'OBJECT'); Entferne bei schemagebundenen Views zuerst SCHEMABINDING mit ALTER VIEW oder lösche die View, und lösche dann die Tabelle.","tags":["drop","reference","schemabinding","view","dependencies","dependency"]},{"code":"3961","title":"Snapshot isolation transaction failed because the object was modified by a DDL statement","severity":"caution","meaning":"Eine Transaktion unter Snapshot-Isolation ist fehlgeschlagen, weil ein Objekt, auf das sie zugriff, in einer anderen, parallelen Transaktion durch eine DDL-Anweisung (Schemaänderung) geändert wurde. Metadaten werden nicht versioniert, daher kann die Snapshot-Transaktion nicht fortfahren.","cause":"Während eine unter SNAPSHOT-Isolation gestartete Transaktion (SET TRANSACTION ISOLATION LEVEL SNAPSHOT) läuft, führt eine andere Session DDL wie ALTER TABLE/CREATE INDEX auf einer Tabelle aus, auf die sie zugreift. Snapshot-Transaktionen sehen Zeilenversionen, aber keine Schemaversionen. Unter READ COMMITTED SNAPSHOT (RCSI) tritt dieser Fehler nicht auf; RCSI erstellt für jede Anweisung einen neuen Snapshot.","fix":"Wiederhole die Transaktion; sie gelingt meist, sobald die DDL abgeschlossen ist. Als dauerhafte Lösung führe Schemaänderungen (DDL) in Wartungsfenstern ohne hohe Snapshot-Last durch. Fange diesen Fehler in der Anwendung ab und implementiere eine automatische Wiederholungslogik. Verkürze lang laufende Snapshot-Transaktionen.","tags":["snapshot","isolation level","ddl","concurrency","retry","isolation"]},{"code":"1785","title":"Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths","severity":"caution","meaning":"Der hinzuzufügende Foreign Key könnte bei kaskadierenden Operationen (ON DELETE/UPDATE CASCADE) einen Zyklus oder mehrere kaskadierende Lösch-/Aktualisierungspfade zur selben Tabelle erzeugen. SQL Server lehnt ihn ab, um mehrdeutiges Kaskadenverhalten zu vermeiden.","cause":"Ein FK mit Kaskadenaktion, der einen Zirkelbezug zwischen Tabellen (A->B->A) oder mehrere Kaskadenpfade zu einer Tabelle erzeugt. SQL Server verbietet das, um mehrdeutige Kaskadenreihenfolgen und Endlosschleifen zu verhindern.","fix":"Definiere den Foreign Key als ON DELETE NO ACTION / ON UPDATE NO ACTION (entferne die Kaskade): ALTER TABLE child ADD CONSTRAINT fk ... FOREIGN KEY (...) REFERENCES parent(...) ON DELETE NO ACTION; Implementiere das benötigte kaskadierende Löschen mit einem INSTEAD OF DELETE-Trigger: Der Trigger löscht zuerst die Kindzeilen, dann die Elternzeile (z. B. DELETE c FROM child c JOIN deleted d ON c.parent_id = d.id; DELETE e FROM parent e JOIN deleted d ON e.id = d.id;). Ein AFTER DELETE-Trigger funktioniert nicht; die FK-Prüfung läuft vor dem Trigger, daher wird das Löschen mit Fehler 547 abgelehnt. Prüfe zirkuläre Abhängigkeiten im Datenmodell.","tags":["foreign key","fk","cascade","circular reference","cycle","constraint"]},{"code":"9421","title":"XML parsing: illegal xml character","severity":"caution","meaning":"SQL Server ist beim Parsen von Text als XML auf ein unzulässiges (ungültiges) Zeichen gestoßen. Die Konvertierung in den XML-Typ oder die Zuweisung an eine XML-Spalte/-Variable ist wegen dieses Zeichens fehlgeschlagen.","cause":"In XML 1.0 verbotene Steuerzeichen (z. B. einige Zeichen im Bereich 0x00-0x1F), falsche Kodierung (Encoding-Konflikt) oder beschädigte/unbereinigte Daten aus einer anderen Quelle. Tritt meist bei CAST(... AS XML) oder einem INSERT in eine XML-Spalte auf.","fix":"Entferne ungültige Zeichen aus dem Text, bevor du ihn in XML konvertierst. Mache die Quellkodierung (UTF-8/UTF-16) mit dem Ziel kompatibel; gib in der XML-Deklaration die richtige Kodierung an. Entferne Steuerzeichen mit REPLACE/NCHAR oder erzeuge und validiere das XML mit einer Standardbibliothek in der Anwendungsschicht. Musst du den Rohtext speichern, erwäge NVARCHAR(MAX).","tags":["xml","parse","character","encoding","invalid","cast"]},{"code":"15151","title":"Cannot find the object/principal because it does not exist or you do not have permission","severity":"caution","meaning":"Das referenzierte Objekt oder der Sicherheitsprinzipal (Benutzer, Rolle, Login) existiert entweder nicht, oder dir fehlt die Berechtigung, darauf zuzugreifen bzw. damit zu arbeiten. Aus Sicherheitsgründen fasst SQL Server beide Fälle in einer Meldung zusammen.","cause":"Verwendung eines nicht existierenden Benutzer-, Rollen-, Schema- oder Objektnamens in einem Befehl wie GRANT/ALTER/DROP; Ausführung im falschen Datenbankkontext (das Objekt liegt in einer anderen DB); oder fehlende Berechtigung für die Operation. Tippfehler, Groß-/Kleinschreibung und Schemapräfixe sind häufige Ursachen.","fix":"Prüfe, ob der Objekt-/Prinzipalname richtig geschrieben ist und du in der richtigen Datenbank bist. Für Benutzer: SELECT name, type_desc FROM sys.database_principals; für Objekte frage sys.objects ab. Stelle sicher, dass das ausführende Konto die nötigen Berechtigungen hat (z. B. ALTER ANY USER, CONTROL). Verwende das richtige Schemapräfix (etwa dbo.object).","tags":["permission","principal","privilege","not found","grant","security"]},{"code":"4834","title":"You do not have permission to use the bulk load statement","severity":"caution","meaning":"Dir fehlt die Berechtigung, um ein Bulk Load mit BULK INSERT (oder OPENROWSET BULK) auszuführen. Bulk Loading greift direkt auf das Dateisystem und Serverressourcen zu und erfordert daher ein eigenes Recht.","cause":"Dem Login fehlt die serverweite Berechtigung ADMINISTER BULK OPERATIONS oder es ist nicht Mitglied der Serverrolle bulkadmin (Azure SQL Database erfordert stattdessen die datenbankweite Berechtigung ADMINISTER DATABASE BULK OPERATIONS). Außerdem sind INSERT-Rechte auf der Zieltabelle und Lesezugriff des SQL Server-Dienstkontos auf die Quelldatei erforderlich.","fix":"Erteile die benötigte Berechtigung: GRANT ADMINISTER BULK OPERATIONS TO login_name; (Serverebene) oder füge den Benutzer der passenden Rolle hinzu. Azure SQL DB verwendet ADMINISTER DATABASE BULK OPERATIONS. Stelle außerdem sicher, dass das SQL Server-Dienstkonto Leserechte auf die Zieldatei/Freigabe hat. Erteile sie nach dem Least-Privilege-Prinzip nur den Konten, die sie benötigen.","tags":["bulk insert","privilege","permission","bulk load","bulkadmin","file"]},{"code":"10928","title":"Resource limit for the database has been reached (Azure SQL)","severity":"danger","meaning":"In Azure SQL Database hat die Datenbank ein Ressourcenlimit erreicht (etwa gleichzeitige Requests/Worker oder Sessions), und die neue Anfrage wurde abgelehnt. Das zeigt an, dass die Obergrenze der gewählten Dienstebene (DTU oder vCore) erreicht ist.","cause":"Überschreitung der von der gewählten Ebene erlaubten maximalen gleichzeitigen Requests (Worker) oder Sessions; ein plötzlicher Lastanstieg; ineffiziente/lang laufende Abfragen, die Worker-Threads binden; oder ein zu großer Connection Pool.","fix":"Reduziere die Parallelität: Optimiere Abfragen, um ihre Laufzeit zu verkürzen, schließe unnötig offene Transaktionen, halte die Größe des Connection Pools angemessen. Skaliere bei dauerhafter Last die Dienstebene hoch (mehr DTU/vCore). Überwache die Ressourcennutzung mit sys.dm_db_resource_stats. Implementiere für wiederholbare Fehler in der Anwendung Retries mit exponentiellem Backoff.","tags":["azure","limit","resource","dtu","vcore","concurrency"]},{"code":"40544","title":"The database has reached its size quota (Azure SQL)","severity":"danger","meaning":"Azure SQL Database hat das für die Dienstebene festgelegte maximale Datengrößenkontingent erreicht. Die Datenbank kann nicht weiter wachsen, daher schlagen neue Schreibvorgänge fehl.","cause":"Daten wachsen bis zum Limit der Ebene; große Indizes/Logs belegen Platz; oder die gewählte Ebene erlaubt nur ein kleines MAXSIZE (z. B. Basic/Standard S-Serie). Nicht archivierte historische Daten füllen das Kontingent ebenfalls schnell.","fix":"Schaffe Platz: Lösche/archiviere nicht benötigte Daten, entferne ungenutzte Indizes, komprimiere Tabellen (Datenkomprimierung). Als dauerhafte Lösung erhöhe MAXSIZE oder wechsle auf eine höhere Dienstebene: ALTER DATABASE db MODIFY (MAXSIZE = 250 GB); Überwache die Größennutzung mit sys.database_files und verwandten DMVs. Plane die Skalierung vorausschauend anhand des Wachstumstrends.","tags":["azure","size","quota","maxsize","size quota","scaling"]},{"code":"40613","title":"Database is not currently available (Azure SQL)","severity":"danger","meaning":"In Azure SQL ist die Zieldatenbank im Moment nicht erreichbar. Meist vorübergehend; tritt kurzzeitig auf, während die Plattform die Datenbank verschiebt, neu konfiguriert oder wiederherstellt.","cause":"Automatische Skalierung/Failover, Patching, Lastverteilung oder Ressourcenverlagerung auf Azure-Seite; selten ein längeres Plattformereignis. Verbindungen werden abgelehnt, bis die Datenbank wieder online ist.","fix":"Warte kurz und versuche es erneut; es handelt sich um einen vorübergehenden Fehler. Implementiere in der Anwendung automatische Retries mit exponentiellem Backoff; Azure-SDK-/Retry-Bibliotheken erkennen diesen Fehlercode als vorübergehend. Hält das Problem an, prüfe Azure Service Health und regionale Störungen. Erwäge für Hochverfügbarkeit Georeplikation/eine Failover-Gruppe.","tags":["azure","temporary","transient","availability","retry","failover"]}]},"errors/mongodb-more2.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"31 RoleNotFound","title":"Role not found","severity":"caution","meaning":"Die referenzierte Rolle ist in MongoDB nicht definiert. Beim Zuweisen einer Rolle an einen Benutzer, beim Aktualisieren einer Rolle oder beim Abfragen der Rechte einer Rolle wurde ein nicht existierender Rollenname verwendet.","cause":"Ein falsch geschriebener oder nie angelegter Rollenname in grantRolesToUser/createUser; Suche der Rolle in der falschen Datenbank (Rollen sind an eine bestimmte DB gebunden); oder eine benutzerdefinierte (nicht eingebaute) Rolle, die noch nicht mit createRole definiert wurde.","fix":"Prüfe den Rollennamen und die Datenbank, zu der er gehört. Vorhandene Rollen auflisten: db.getRoles({showBuiltinRoles: false}) oder die entsprechende Admin-Abfrage über alle DBs. Existiert die benutzerdefinierte Rolle nicht, lege sie zuerst an: db.createRole({role:'reader', privileges:[...], roles:[]}). Gib beim Zuweisen die richtige db an: db.grantRolesToUser('app_user', [{role:'reader', db:'app'}]).","tags":["role","roles","authorization","access control","createrole","user"]},{"code":"263 OperationNotSupportedInTransaction","title":"Operation not supported inside a multi-document transaction","severity":"caution","meaning":"In einer Multi-Document-Transaktion wurde eine Operation versucht, die in diesem Kontext nicht unterstützt wird. Einige Befehle (insbesondere bestimmte DDL- und Metadatenoperationen) können nicht innerhalb einer Transaktion laufen.","cause":"Nicht unterstützte Operationen innerhalb der Transaktion, etwa das Anlegen von Collections/Indizes (abhängig von der Version); bestimmte count-/list-artige Befehle; oder administrative Befehle, die in einer Transaktions-Session nicht gültig sind. Ältere MongoDB-Versionen unterstützen keine DDL in Transaktionen (ein Teil kam mit 4.4+).","fix":"Verlagere die nicht unterstützte Operation aus der Transaktion heraus (z. B. lege die benötigten Collections/Indizes vor Beginn der Transaktion an). Prüfe deine MongoDB-Version; 4.4+ unterstützt einige DDL in Transaktionen. Führe die Operation im Treiber in einer separaten Session aus. Beschränke die Transaktion auf unterstützte CRUD-Operationen.","tags":["transaction","multi-document transaction","ddl","unsupported","session","crud"]},{"code":"20 IllegalOperation","title":"Illegal operation","severity":"caution","meaning":"Die angeforderte Operation ist für den aktuellen Zustand des Servers oder den Typ des Zielobjekts ungültig/verboten. MongoDB liefert diesen allgemeinen Fehler, wenn es eine logisch unzulässige Aktion ablehnt.","cause":"Versuch einer nur für Collections zulässigen Operation auf einer View; ein ungültiges Umbenennen zwischen zwei Datenbanken; eine verbotene Änderung an Systemcollections; oder eine administrative Aktion, die die Replica-Set-/Sharding-Konfiguration nicht erlaubt. Variiert je nach Kontext.","fix":"Lies die Details der Fehlermeldung; meist steht dort ausdrücklich, welche Operation warum verboten war. Passe die Operation an den Typ des Zielobjekts an (z. B. arbeite auf der zugrunde liegenden Collection statt auf der View). Prüfe bei Umbenennen/Verschieben die Einschränkungen von MongoDB (etwa renameCollection innerhalb derselben DB). Prüfe den Clusterzustand (Replica Set/Sharding).","tags":["illegal","invalid","operation","view","rename","system"]},{"code":"48 NamespaceExists","title":"Namespace (collection) already exists","severity":"safe","meaning":"Die Collection (Namespace), die du anlegen willst, existiert bereits. MongoDB meldet, dass sie vorhanden ist; eine neue Collection mit demselben Namen und denselben Optionen muss also nicht angelegt werden.","cause":"Versuch, eine vorhandene Collection mit createCollection erneut anzulegen (eventuell mit anderen Optionen); Schema-/Setup-Skripte, die mehrfach laufen; oder der Versuch, einen vorhandenen Namen als Capped Collection oder mit speziellen Optionen neu zu definieren.","fix":"Kann meist ignoriert werden; die Collection existiert bereits. Mache deine Setup-Skripte idempotent: Prüfe vor dem Anlegen die Existenz (db.getCollectionNames().includes('name')). Um die Optionen zu ändern, musst du die Collection löschen und neu anlegen (sichere vorher die Daten): db.my_collection.drop() dann createCollection.","tags":["namespace","collection","collection exists","createcollection","already exists","idempotent"]},{"code":"11601 Interrupted","title":"Operation was interrupted","severity":"caution","meaning":"Die laufende Operation wurde unterbrochen. MongoDB hat sie wegen eines externen Eingriffs oder einer Änderung des Serverzustands vor dem Abschluss beendet.","cause":"Ein Administrator/Tool beendet die Operation mit db.killOp(); der Server fährt herunter; ein Primary-Wechsel im Replica Set (Stepdown/Failover). (Ein Überschreiten von maxTimeMS liefert 50 MaxTimeMSExpired, nicht diesen Fehler.) Lang laufende Abfragen lassen sich auf diese Weise stoppen.","fix":"Untersuche, warum die Operation unterbrochen wurde: currentOp/killOp-Verlauf, Failover-Ereignisse, Serverwartung. Ist die Operation idempotent, wiederhole sie sicher. Optimiere und verkürze lange Abfragen; lege passende Indizes an. Nutze im Treiber eine Retry-Logik für vorübergehende Fehler; stelle automatischen Reconnect bei Replica-Set-Failovern sicher.","tags":["interrupted","interruption","killop","failover","stepdown","retry"]},{"code":"85 IndexOptionsConflict","title":"Index already exists with different options / different name","severity":"caution","meaning":"Der anzulegende Index kollidiert mit einem vorhandenen: Ein Index mit demselben Namen hat andere Optionen, oder ein gleichwertiger Index mit demselben Schlüssel und denselben Optionen existiert bereits unter einem anderen Namen. MongoDB lässt nicht zu, denselben Index mit einer widersprüchlichen Definition neu anzulegen.","cause":"Anlegen eines Index mit demselben Namen und Schlüssel, aber anderen Optionen (z. B. expireAfterSeconds, partialFilterExpression, collation); oder Neudefinition eines Äquivalents eines vorhandenen Index unter anderem Namen ('Index already exists with a different name'). Typische Ursache sind Indexdefinitionen, die in Anwendungs-/Migrationsskripten mit der Zeit auseinanderlaufen. Derselbe Name mit anderem Schlüssel liefert stattdessen 86 IndexKeySpecsConflict.","fix":"Vorhandenen Index anzeigen: db.my_collection.getIndexes(). Willst du die Optionen wirklich ändern, lösche den alten Index und lege den neuen an: db.my_collection.dropIndex('index_name') dann createIndex(...). Andernfalls bringe die Definition exakt mit dem vorhandenen Index in Übereinstimmung oder gib dem neuen Index einen anderen Namen. Mache Migrationsskripte idempotent.","tags":["index","indexes","options","conflict","unique","createindex"]},{"code":"67 CannotCreateIndex","title":"Cannot create index","severity":"caution","meaning":"Der Indexaufbau ist wegen einer Einschränkung oder eines Limits fehlgeschlagen. MongoDB konnte den angeforderten Index aufgrund vorhandener Daten oder der Konfiguration nicht aufbauen.","cause":"Überschreitung der maximal erlaubten Anzahl von Indizes pro Collection (64); ein zu großer Schlüssel; oder inkompatible Optionskombinationen (z. B. ein ungeeigneter Feldtyp für einen TTL-Index). Hinweis: Der Aufbau eines Unique-Index bei vorhandenen Duplikaten in den Daten löst E11000 DuplicateKey aus, nicht diesen Code.","fix":"Erreichst du das Limit der Indexanzahl, lösche ungenutzte Indizes (analysiere die Nutzung mit $indexStats). Prüfe Schlüsselgröße/Typkompatibilität und die Optionskombination. Baue den Index bei großen Collections bei geringer Last auf (seit 4.2 blockieren Indexaufbauten Schreibzugriffe ohnehin nicht lange; die Option background wird ignoriert).","tags":["index","indexes","unique","duplicate","limit","ttl"]},{"code":"27 IndexNotFound","title":"Index not found","severity":"safe","meaning":"Der referenzierte Index wurde auf der Collection nicht gefunden. Tritt meist beim Versuch auf, einen nicht existierenden Index zu löschen oder umzubenennen.","cause":"Angabe eines falschen Indexnamens oder einer falschen Schlüsselspezifikation in einem Befehl wie dropIndex/hideIndex; der Index wurde bereits früher gelöscht; oder der automatisch erzeugte Indexname weicht vom erwarteten ab (MongoDB erzeugt Namen wie field_1_field2_-1).","fix":"Vorhandene Indizes und ihre exakten Namen auflisten: db.my_collection.getIndexes(). Wiederhole die Operation mit dem richtigen Namen oder Schlüsseldokument: db.my_collection.dropIndex({field: 1}) oder db.my_collection.dropIndex('field_1'). Schreibe Löschskripte so, dass sie nicht fehlschlagen, wenn der Index fehlt (mit Existenzprüfung).","tags":["index","indexes","dropindex","not found","getindexes","name"]},{"code":"79 UnknownReplWriteConcern","title":"Unknown replica set write concern","severity":"caution","meaning":"Der angegebene Write Concern wird nicht erkannt. Der in der Operation verwendete w-Wert (ein benutzerdefinierter/getaggter Write-Concern-Name oder ein numerischer Wert) ist in der Replica-Set-Konfiguration nicht definiert.","cause":"Verwendung eines benutzerdefinierten Write-Concern-Namens, der nicht in getLastErrorModes definiert ist (z. B. ein Tippfehler statt 'majority' oder ein eigener Tag-Name). Konfiguration und Anfrage passen nicht zusammen. Hinweis: Ein numerisches w über der Zahl der stimmberechtigten Mitglieder ist ein anderer Fehler (100 UnsatisfiableWriteConcern).","fix":"Stelle sicher, dass der verwendete Write-Concern-Name in den Replica-Set-Einstellungen definiert ist. Aktuelle Konfiguration anzeigen: rs.conf() (settings.getLastErrorModes). Nutzt du einen benutzerdefinierten Modus, definiere ihn zuerst oder wechsle zu Standardwerten ('majority' oder ein gültiges numerisches w). Prüfe die writeConcern-Einstellung im Treiber/Connection String der Anwendung.","tags":["write concern","write acknowledgement","replica set","majority","getlasterrormodes","config"]}]},"errors/redis-more2.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"ERR DEBUG command not allowed","title":"DEBUG command not allowed","severity":"caution","meaning":"Du wolltest den DEBUG-Befehl ausführen, aber der Server erlaubt ihn nicht. DEBUG ist ein gefährlicher Low-Level-Diagnosebefehl; in neueren Redis-Versionen ist er aus Sicherheitsgründen standardmäßig deaktiviert oder eingeschränkt.","cause":"In Redis 7+ laufen DEBUG-Unterbefehle nicht ohne die Einstellung enable-debug-command; verwaltete Cloud-Redis-Dienste deaktivieren den Befehl meist vollständig; oder der Befehl wird durch eine ACL verweigert. Test-/Diagnoseskripte (z. B. DEBUG SLEEP, DEBUG OBJECT) stoßen oft auf diesen Fehler.","fix":"Vermeide DEBUG in der Produktion. Wird es in einer lokalen/Testumgebung wirklich benötigt, aktiviere es in der Konfiguration: enable-debug-command yes (oder 'local') in redis.conf. Bei Cloud-Diensten lässt sich der Befehl meist gar nicht aktivieren; nutze für gleichwertige Informationen sichere Befehle wie MEMORY USAGE, OBJECT ENCODING und INFO. Greift eine ACL-Einschränkung, prüfe die Berechtigungen.","tags":["debug","command","prohibited","security","acl","config"]},{"code":"ERR Can't save in background: fork failed","title":"Background save failed: fork failed","severity":"danger","meaning":"Redis konnte für einen RDB-Snapshot im Hintergrund oder ein AOF-Rewrite keinen Kindprozess (fork) erzeugen. Da die Persistenz nicht laufen kann, können keine Daten auf die Platte geschrieben werden.","cause":"Das Betriebssystem kann für den Fork nicht genug Speicher bereitstellen; meist ist overcommit_memory deaktiviert und das System verweigert, wenn Redis ungefähr eine Kopie seines belegten Speichers reservieren will. Auch zu wenig physischer/virtueller Speicher oder Limits für die Prozessanzahl können die Ursache sein.","fix":"Aktiviere unter Linux Memory Overcommit: sysctl vm.overcommit_memory=1 (füge es zu /etc/sysctl.conf hinzu, um es dauerhaft zu machen). Erweitere den Speicher des Servers oder senke den Speicherverbrauch von Redis (maxmemory). Stelle ausreichend Swap bereit. Prüfe Limits für Prozesse/Dateideskriptoren (ulimit). Ist Persistenz kritisch, gehe ohne diese Einstellungen nicht in Produktion.","tags":["fork","rdb","aof","persistence","overcommit","memory"]},{"code":"NOPROTO unsupported protocol version","title":"Unsupported RESP protocol version","severity":"caution","meaning":"Der Client hat eine RESP-Protokollversion angefordert, die der Server nicht unterstützt. Wird meist zurückgegeben, wenn mit dem HELLO-Befehl RESP3 (Version 3) angefordert wird und der Server nur RESP2 unterstützt.","cause":"HELLO 3 wurde gesendet, aber der Server läuft mit einer alten Redis-Version (vor 6.0) oder unterstützt RESP3 nicht; die Client-Bibliothek fordert standardmäßig RESP3 an, der Server ist aber alt. Ein Versionskonflikt.","fix":"Konfiguriere deinen Client für RESP2 oder fordere mit HELLO eine gültige Version an (HELLO 2). Aktualisiere den Server auf eine Version mit RESP3-Unterstützung (Redis 6+). Prüfe die Einstellung der Protokollversion in deiner Client-Bibliothek; die meisten Bibliotheken können automatisch auf RESP2 zurückfallen. Serverversion prüfen: INFO server (redis_version).","tags":["resp","protocol","hello","resp3","version","compatibility"]},{"code":"ERR Unknown option or number of arguments for CONFIG SET","title":"Unknown option or number of arguments for CONFIG SET (ältere Versionen: Unsupported CONFIG parameter)","severity":"caution","meaning":"Der Konfigurationsparameter, den du mit CONFIG SET ändern wolltest, ist in dieser Redis-Version nicht bekannt, oder die Anzahl der Argumente ist falsch. In Redis 7 lautet die vollständige Meldung \"ERR Unknown option or number of arguments for CONFIG SET - '<name>'\"; ältere Versionen liefern \"ERR Unsupported CONFIG parameter\". CONFIG GET löst bei einem unbekannten Parameter keinen Fehler aus; es liefert eine leere Liste.","cause":"Ein falsch geschriebener Parametername; Verwendung eines Parameters aus einer anderen Redis-Version (Parameter können zwischen Versionen hinzukommen und wegfallen); oder der verwaltete Cloud-Dienst erlaubt das Ändern bestimmter Parameter nicht. Typische Ursache ist das Ausführen alter Skripte auf neueren/älteren Versionen.","fix":"Prüfe den gültigen Parameternamen. Liste aktuelle Einstellungen mit einem Muster auf, etwa CONFIG GET 'maxmemory*'. Prüfe die Parameterliste in der Dokumentation deiner Redis-Version; die Version zeigt INFO server. Ist es bei einem Cloud-Dienst eingeschränkt, nutze die vom Anbieter erlaubten Parameter. Passe deine Skripte an die Zielversion an.","tags":["config","parameter","unsupported","version","config set","config get"]},{"code":"ERR MULTI calls can not be nested","title":"MULTI calls can not be nested","severity":"caution","meaning":"MULTI wurde erneut gesendet, während bereits ein MULTI-Block (Transaktion) offen war. Redis unterstützt keine verschachtelten Transaktionsblöcke; es kann jeweils nur ein MULTI aktiv sein.","cause":"Anwendungslogik startet ein neues MULTI, ohne das vorherige mit EXEC/DISCARD abzuschließen; Wiederverwendung derselben Verbindung aus einem Connection Pool, ohne den Transaktionszustand korrekt zurückzusetzen; oder ein Bibliotheks-Wrapper ruft unbemerkt zweimal MULTI auf.","fix":"Schließe jeden MULTI-Block mit genau einem EXEC (oder DISCARD zum Abbrechen) und öffne einen neuen Block erst nach Abschluss des vorherigen. Trenne Beginn und Ende der Transaktion im Codeablauf klar. Stelle sicher, dass keine Transaktion offen bleibt, bevor eine Verbindung an den Pool zurückgegeben wird. Ist verschachtelte Logik nötig, erwäge, die atomare Operation mit einem Lua-Skript (EVAL) umzusetzen.","tags":["multi","transaction","nested","nested","exec","discard"]},{"code":"ERR Lua script attempted to access nonexistent global variable","title":"Lua script attempted to access a nonexistent global variable","severity":"caution","meaning":"Ein mit EVAL/EVALSHA ausgeführtes Lua-Skript wollte auf eine nicht definierte globale Variable zugreifen. Zum Schutz der Skriptumgebung schränkt Redis das Anlegen/Lesen globaler Variablen ein und liefert solche Zugriffe als Fehler.","cause":"Versehentliche Verwendung einer globalen statt einer lokalen Variable im Skript (vergessenes Schlüsselwort local); Zugriff auf einen nicht definierten Namen wegen eines Tippfehlers; oder der Versuch, Zustand in einer globalen Variable zu halten. Die gesandboxte Lua-Umgebung von Redis erlaubt das nicht.","fix":"Deklariere alle Variablen im Skript mit local: local x = ... Behebe Tippfehler; stelle sicher, dass jede Variable vor der Verwendung definiert ist. Skripte sollten zustandslos sein; nutze für dauerhaften Zustand Redis-Keys (KEYS/redis.call), keine globalen Lua-Variablen. Halte das Skript kurz und deterministisch.","tags":["lua","eval","script","global","variable","local"]},{"code":"ERR unknown command","title":"Unknown command","severity":"caution","meaning":"Redis erkennt den gesendeten Befehl nicht. Der Befehlsname ist möglicherweise falsch, in dieser Version nicht verfügbar oder deaktiviert/umbenannt.","cause":"Ein falsch geschriebener Befehlsname; der Befehl kam erst in einer neueren Redis-Version hinzu (auf dem alten Server nicht vorhanden); Aufruf eines Befehls, der ein Modul erfordert (z. B. RedisJSON, RediSearch), ohne geladenes Modul; oder der Befehl wurde mit rename-command/ACL deaktiviert. Manchmal lässt auch eine falsche Aufteilung der Argumente durch den Client den Befehlsnamen beschädigt erscheinen.","fix":"Prüfe Befehlsname und Syntax (COMMAND DOCS command oder die Dokumentation). Serverversion prüfen: INFO server. Prüfe bei einem Modulbefehl, ob das Modul geladen ist: MODULE LIST. Wurde der Befehl mit rename-command versteckt, prüfe die Konfiguration. Stelle sicher, dass deine Client-Bibliothek den Befehl korrekt kodiert.","tags":["command","unknown","unknown","version","module","acl"]},{"code":"TRYAGAIN Multiple keys request during rehashing of slot","title":"Multiple keys request during rehashing of slot (cluster)","severity":"caution","meaning":"In Redis Cluster wurde, während ein Hash-Slot zwischen Knoten verschoben wurde (Rehashing/Resharding), eine Anfrage mit mehreren Keys desselben Slots gestellt, von denen einige bereits verschoben waren und andere nicht. Der Server fordert dich auf, die Operation in Kürze zu wiederholen.","cause":"Ausführung eines Multi-Key-Befehls (MGET, MSET, Multi-Key-Lua, Transaktion) während einer laufenden Slot-Migration. Im migrierenden Slot liegen einige Keys auf dem Quell- und einige auf dem Zielknoten, daher kann der Befehl nicht atomar ausgeführt werden.","fix":"Fange diesen Fehler im Client ab und wiederhole nach kurzer Wartezeit (TRYAGAIN ist vorübergehend; es verschwindet, wenn das Resharding abgeschlossen ist). Stelle bei Multi-Key-Operationen mit einem Hash-Tag sicher, dass alle Keys im selben Slot liegen, etwa {app_user123}.profile, {app_user123}.settings. Führe das Rebalancing des Clusters (Resharding) in verkehrsarmen Zeiten durch.","tags":["cluster","slot","rehash","resharding","tryagain","hash tag"]},{"code":"ERR DISCARD without MULTI","title":"DISCARD without MULTI","severity":"caution","meaning":"DISCARD wurde gesendet, ohne dass ein MULTI-Block offen war. DISCARD dient nur zum Abbrechen einer laufenden Transaktion; gibt es nichts abzubrechen, liefert es diesen Fehler.","cause":"Aufruf von DISCARD ohne MULTI oder erneut, nachdem die Transaktion bereits mit EXEC/DISCARD abgeschlossen wurde; ein bedingungsloser DISCARD-Aufruf im Fehlerbehandlungscode; oder der Verbindungszustand weicht vom erwarteten ab (auf der aus dem Pool entnommenen Verbindung ist kein MULTI offen).","fix":"Rufe DISCARD nur auf, wenn tatsächlich ein MULTI offen ist; verfolge den Transaktionszustand im Code. Prüfe in Fehlerbehandlern, ob eine Transaktion offen ist, bevor du DISCARD aufrufst. Nutze den Transaktions-Wrapper deiner Bibliothek, damit er den Zustand automatisch verwaltet. Garantiere einen sauberen Zustand im Connection Pool.","tags":["discard","multi","transaction","cancel","state","error handling"]}]},"errors/elasticsearch-more2.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"search_context_missing_exception","title":"No search context found for id (Scroll abgelaufen)","severity":"caution","meaning":"Der von einer Scroll-Abfrage verwendete Suchkontext existiert auf dem Server nicht mehr. Tritt meist auf, wenn mit einer gespeicherten scroll_id fortgefahren wird, nachdem der Scroll-Kontext abgelaufen ist oder gelöscht wurde.","cause":"Das für den Scroll angegebene Keep-alive ist abgelaufen (zu lange Pause zwischen Anfragen); der Kontext wurde manuell gelöscht (clear scroll); oder der Kontext ging bei einem Neustart eines Knotens verloren. Scroll-Kontexte werden im Speicher des Knotens gehalten, ihre Lebensdauer ist daher begrenzt.","fix":"Übergib bei jeder Scroll-Anfrage ein ausreichendes Keep-alive (z. B. scroll=5m) und verkürze die Pause zwischen den Seiten. Verwende in modernem Elasticsearch für tiefe Paginierung search_after + Point in Time (PIT) statt Scroll; das ist besser skalierbar und die empfohlene Methode. Gib Kontexte nach Abschluss frei (clear scroll), damit kein Speicher verschwendet wird.","tags":["scroll","search_after","pit","context","timeout","pagination"]},{"code":"aliases_not_found_exception","title":"Aliases missing","severity":"safe","meaning":"Der Alias, mit dem du arbeiten willst, existiert nicht. Wird meist beim Entfernen eines nicht existierenden Alias oder bei einer Aktion darauf zurückgegeben.","cause":"Der Aliasname ist falsch geschrieben; der Alias wurde bereits gelöscht; oder eine remove-Aktion in der _aliases-API nennt einen Alias, der nie zugeordnet war. Tritt auch auf, wenn kein Alias zu einem Wildcard-Muster passt.","fix":"Vorhandene Aliase auflisten: GET _alias, oder für einen bestimmten Index GET my-index/_alias. Wiederhole die Operation mit dem richtigen Namen. Schreibe Löschskripte so, dass sie nicht fehlschlagen, wenn der Alias fehlt; behandle Aktionen in _aliases-Massenoperationen bedingt. Um Aliase atomar umzuhängen, nutze add und remove in einer einzigen _aliases-Anfrage.","tags":["alias","alias name","not found","aliases","index","routing"]},{"code":"snapshot_missing_exception","title":"Snapshot is missing","severity":"caution","meaning":"Der referenzierte Snapshot wurde im angegebenen Repository nicht gefunden. Das zeigt an, dass bei einer Restore-, Lösch- oder Statusanfrage ein nicht existierender Snapshot-Name verwendet wurde.","cause":"Der Snapshot-Name ist falsch geschrieben; der Snapshot wurde bereits gelöscht; das falsche Repository wurde abgefragt; oder das Backup wurde nie abgeschlossen. Ein ähnliches Ergebnis tritt auf, wenn sich die Repository-Definition geändert hat oder nicht erreichbar ist.","fix":"Snapshots im Repository auflisten: GET _snapshot/repo_name/_all (oder für einen bestimmten Namen GET _snapshot/repo_name/snapshot_name). Wiederhole mit dem richtigen Repository- und Snapshot-Namen. Prüfe die Repository-Definition: GET _snapshot/repo_name. Prüfe, ob deine Backup-Richtlinie (SLM) erfolgreich läuft und Snapshots erzeugt.","tags":["snapshot","backup","repository","repo","restore","not found"]},{"code":"index_template_missing_exception","title":"Index template is missing","severity":"safe","meaning":"Das referenzierte Index-Template wurde nicht gefunden. Beim Abrufen, Löschen oder bei einer auf einem Template basierenden Operation wurde ein nicht existierender Template-Name verwendet.","cause":"Der Template-Name ist falsch geschrieben; das Template wurde bereits gelöscht; die Legacy-Template-API wurde mit der API für Composable Index Templates verwechselt; oder das Template wurde nie angelegt. Tritt auch auf, wenn ein Setup-Skript nicht vollständig gelaufen ist.","fix":"Vorhandene Templates auflisten. Für Composable Templates: GET _index_template oder GET _index_template/name. Für Legacy-Templates: GET _template. Wiederhole mit der richtigen API und dem richtigen Namen. Fehlt das Template, lege es an: PUT _index_template/name {...}. Mache Lösch-/Aktualisierungsskripte idempotent, damit sie nicht fehlschlagen, wenn das Template fehlt.","tags":["index template","template","composable","legacy","not found","mapping"]},{"code":"settings_exception","title":"Invalid settings","severity":"caution","meaning":"Die Operation wurde abgelehnt, weil die angegebene Einstellung ungültig ist. Beim Anwenden von Index- oder Cluster-Einstellungen gibt es ein Problem mit dem Namen der Einstellung, ihrem Wert oder damit, ob sie in diesem Kontext geändert werden darf.","cause":"Ein unbekannter/falsch geschriebener Einstellungsname; ein Wert vom falschen Typ oder außerhalb des erlaubten Bereichs; Versuch, auf einem aktiven Index eine statische Einstellung zu ändern, die nur beim Anlegen des Index gesetzt werden kann (z. B. number_of_shards); oder Verwendung einer entfernten/umbenannten Einstellung.","fix":"Prüfe Name und Wert der Einstellung anhand der Dokumentation. Dynamische Einstellungen auf dem aktiven Index aktualisieren: PUT my-index/_settings {\"index.number_of_replicas\": 2}. Statische Einstellungen (z. B. number_of_shards) werden nur beim Anlegen gesetzt; ihre Änderung erfordert einen Reindex. Aktuelle Einstellungen anzeigen: GET my-index/_settings.","tags":["settings","setting","invalid","static","dynamic","reindex"]},{"code":"shard_not_found_exception","title":"Shard not found","severity":"danger","meaning":"Der Ziel-Shard wurde nicht gefunden. Elasticsearch konnte die Anfrage nicht abschließen, weil es den betreffenden Shard des angegebenen Index nicht erreichen konnte.","cause":"Der Shard ist noch nicht zugewiesen (unassigned) oder befindet sich in Recovery; der Shard fehlt vorübergehend, weil ein Knoten den Cluster verlassen hat; oder eine direkt an einen Shard geleitete Low-Level-Anfrage trägt Routing-Informationen, die nicht mehr gültig sind. Tritt auf, wenn der Clusterzustand beeinträchtigt ist.","fix":"Prüfe den Zustand von Cluster und Shards: GET _cluster/health und GET _cat/shards?v. Sieh nach, warum Shards nicht zugewiesen sind: GET _cluster/allocation/explain. Ging ein Knoten verloren, bringe ihn zurück; prüfe Platten-/Allokationseinstellungen (Watermark). Läuft eine Recovery, warte ihren Abschluss ab. Halte eine ausreichende Replica-Anzahl vor, damit die Daten beim Verlust eines einzelnen Knotens verfügbar bleiben.","tags":["shard","shard","unassigned","unassigned shard","recovery","cluster health"]},{"code":"not_x_content_exception","title":"Compressor detection error / body is not valid content","severity":"caution","meaning":"Elasticsearch konnte den Request-Body nicht als gültigen strukturierten Inhalt (XContent wie JSON) erkennen. Die gesendeten Daten liegen nicht im erwarteten Format vor oder sind beschädigt.","cause":"Der Request-Body ist ungültiges JSON; ein falscher oder fehlender Content-Type-Header (application/json nicht gesendet); der Body wurde versehentlich komprimiert/binär gesendet; oder an Endpunkte wie _bulk, die zeilengetrenntes JSON (NDJSON) erwarten, wurde normales JSON gesendet. Auch Escape-/Kodierungsfehler lösen den Fehler aus.","fix":"Prüfe, ob der Request-Body gültiges JSON ist, und sende den Header Content-Type: application/json. Verwende für den Endpunkt _bulk NDJSON, bei dem jede Zeile ein eigenes JSON-Objekt ist und die letzte Zeile mit einem Zeilenumbruch endet (Content-Type: application/x-ndjson). Stelle sicher, dass deine Client-Bibliothek den Body korrekt serialisiert. Nutzt du Komprimierung, sende den passenden Content-Encoding-Header.","tags":["json","xcontent","content-type","bulk","ndjson","request body"]},{"code":"document_source_missing_exception","title":"Document source is missing","severity":"caution","meaning":"Die Operation benötigt das Feld _source des Dokuments, es ist aber nicht gespeichert. Auf einem Index mit deaktiviertem Source wurde eine Operation versucht, die _source erfordert (z. B. partielles Update, Abrufen des Source).","cause":"Das Index-Mapping setzt _source: {enabled: false}; da das ursprüngliche JSON des Dokuments nicht gespeichert wird, können partielle Updates, Reindex und Abfragen, die _source zurückgeben, nicht funktionieren. Konfigurationen, die Source zum Platzsparen deaktivieren, verursachen dieses Problem.","fix":"Operationen, die _source benötigen, setzen voraus, dass Source im Mapping aktiviert ist. Lass _source beim Anlegen eines neuen Index aktiviert (es ist standardmäßig aktiviert). Ist _source auf einem vorhandenen Index deaktiviert, indiziere das vollständige Dokument neu, statt es zu aktualisieren (Überschreiben mit der Index-API). Erwäge zum Platzsparen _source-Filterung (includes/excludes) oder synthetisches _source, statt Source ganz zu deaktivieren.","tags":["_source","source","update","mapping","reindex","document source"]}]},"errors/oracle-more3.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00845","title":"MEMORY_TARGET not supported on this system","severity":"danger","meaning":"Die Instanz kann nicht starten, weil die über MEMORY_TARGET / MEMORY_MAX_TARGET angeforderte automatische Speicherverwaltung von der Shared-Memory-Konfiguration des Betriebssystems nicht unterstützt wird. Unter Linux liegt das meist daran, dass /dev/shm kleiner als das angeforderte Speicherziel ist.","cause":"MEMORY_TARGET ist größer gesetzt als /dev/shm (als tmpfs gemountet); oder MEMORY_TARGET (AMM) wird bei aktivierten HugePages verwendet; AMM ist nicht mit HugePages kompatibel, ASMM (SGA_TARGET) hingegen schon. Oracle Automatic Memory Management legt Dateien in /dev/shm an; reicht dieser Platz nicht, startet die Instanz nicht.","fix":"Mache /dev/shm größer als MEMORY_TARGET: Mounte tmpfs neu (mount -o remount,size=8G /dev/shm) und mache es in /etc/fstab dauerhaft. Alternativ nutze statt AMM getrennte Einstellungen: Setze MEMORY_TARGET und MEMORY_MAX_TARGET auf 0 und definiere SGA_TARGET und PGA_AGGREGATE_TARGET. Nutzt du HugePages, deaktiviere AMM und bevorzuge ASMM (SGA_TARGET).","tags":["memory_target","memory","amm","dev/shm","hugepages","startup"]},{"code":"ORA-27102","title":"out of memory","severity":"danger","meaning":"Beim Start konnte die Oracle-Instanz das beim Betriebssystem angeforderte Shared-Memory-Segment (SGA) nicht allokieren. Tritt meist zusammen mit ORA-27102 und einem tieferliegenden OSD-/Linux-Fehlercode auf (z. B. Linux-x86_64 Error: 28: No space left on device).","cause":"Der für die SGA angeforderte Speicher überschreitet die Shared-Memory-Limits des Kernels (shmmax, shmall); der Server hat nicht genügend physischen RAM/Swap; oder die HugePages-Konfiguration fehlt/reicht nicht aus, sodass eine große SGA nicht allokiert werden kann. Häufig nach einer Erhöhung von SGA_MAX_SIZE.","fix":"Erhöhe die Shared-Memory-Parameter des Kernels: Setze kernel.shmmax und kernel.shmall in /etc/sysctl.conf so, dass sie die SGA abdecken, und führe sysctl -p aus. Nutzt du HugePages, berechne vm.nr_hugepages anhand der SGA und setze den Wert. Prüfe RAM und Swap des Servers insgesamt; senke bei Bedarf SGA_MAX_SIZE / SGA_TARGET.","tags":["sga","memory","shmmax","hugepages","startup","osd"]},{"code":"ORA-04030","title":"out of process memory when trying to allocate N bytes","severity":"danger","meaning":"Ein Serverprozess konnte in seiner PGA (Program Global Area), also seinem privaten Prozessspeicher, keinen Platz allokieren. Anders als beim Shared Memory in der SGA ist hier der eigene Speicher einer einzelnen Session bzw. eines einzelnen Prozesses erschöpft.","cause":"Sehr große Sort-/Hash-Operationen, übergroße PL/SQL-Collections, Variablen, die sich in unkontrollierten Schleifen ansammeln, oder Limits des Betriebssystems für Prozessspeicher (ulimit -v/-d, vm.max_map_count, Adressraum) haben den Prozessspeicher erschöpft. Bei 32-Bit-Instanzen kann auch das Adressraumlimit die Ursache sein.","fix":"Finde den Speicherverbraucher, indem du die Allokationskategorie in der zweiten Zeile des Fehlers prüfst (z. B. 'pga heap', 'koh-kghu sessi'). Prüfe PGA_AGGREGATE_LIMIT / PGA_AGGREGATE_TARGET. Prüfe die Limits des Betriebssystems (ulimit -a; memlock, as, stack für den Benutzer oracle). Optimiere die Abfrage bzw. das PL/SQL und verkleinere die Batchgrößen bei Massenverarbeitung. Wende dich bei Bedarf mit der Trace-Datei an Oracle Support.","tags":["pga","process memory","memory","ulimit","sort","plsql"]},{"code":"ORA-12537","title":"TNS:connection closed","severity":"danger","meaning":"Die TNS-Verbindung zwischen Client und Server endete, weil der Kommunikationskanal unerwartet geschlossen wurde. Die Session bricht während des Verbindungsaufbaus oder unmittelbar danach ab.","cause":"Der Listener hat den Client an einen Serverprozess übergeben, aber der Prozess konnte nicht erzeugt werden oder wurde sofort beendet: Absturz der Instanz, erreichtes PROCESSES-Limit, Ressourcenknappheit im Betriebssystem, eine Firewall/Middle-Tier, die die Verbindung kappt, oder eine Regel TCP.VALIDNODE_CHECKING/TCP.INVITED_NODES in sqlnet.ora, die den Client abweist. Manchmal trennt eine Firewall zwischen Client und Server eine inaktive Verbindung.","fix":"Prüfe listener.log und sqlnet.log. Prüfe die PROCESSES-/SESSIONS-Limits: SELECT * FROM v$resource_limit WHERE resource_name IN ('processes','sessions'); Prüfe, dass die Listen TCP.INVITED_NODES/TCP.EXCLUDED_NODES in der serverseitigen sqlnet.ora die Client-IP nicht abweisen (eine nicht passende Logon-Version löst ORA-28040 aus, nicht diesen Fehler). Erhöhe den Idle-Timeout der Firewall oder sende Keepalives mit SQLNET.EXPIRE_TIME. Prüfe das alert.log auf Einträge zu Instanzabstürzen.","tags":["tns","connection","listener","processes","firewall","sqlnet"]},{"code":"ORA-03135","title":"connection lost contact","severity":"danger","meaning":"Eine zuvor aufgebaute, funktionierende Session hat den Kontakt zum Server verloren. Eine lang laufende Abfrage oder eine inaktive Verbindung wurde getrennt, weil sich die beiden Seiten nicht mehr erreichen konnten.","cause":"Der Serverprozess wurde unerwartet beendet (ORA-00600/07445, kill, Ressourcenlimit); ein RESOURCE_LIMIT-Profil hat die Session beendet; eine inaktive Verbindung wurde von einer Firewall/einem Load Balancer getrennt; oder es gab einen Netzwerkausfall/Paketverlust. Tritt typischerweise bei Abfragen auf, die lange nicht antworten, oder bei inaktiven Verbindungen.","fix":"Suche im alert.log und in den zugehörigen Trace-Dateien, warum der Prozess beendet wurde (ORA-00600/ORA-07445?). Erhöhe netzwerkseitig den Idle-Timeout der Firewall und aktiviere die Erkennung toter Verbindungen mit SQLNET.EXPIRE_TIME in sqlnet.ora. Prüfe die Limits CONNECT_TIME/IDLE_TIME im Benutzerprofil. Untersuche den Ressourcendruck auf dem Server (Speicher, CPU, PROCESSES).","tags":["connection","sqlnet","idle","firewall","dead connection","network"]},{"code":"ORA-01033","title":"ORACLE initialization or shutdown in progress","severity":"danger","meaning":"Die Datenbankinstanz, mit der du dich verbindest, befindet sich mitten im Start (Mount/Open) oder Herunterfahren. In diesem Zwischenzustand werden normale Benutzerverbindungen nicht angenommen.","cause":"Die Instanz hängt in NOMOUNT/MOUNT (z. B. wartet sie auf Media Recovery), ein SHUTDOWN läuft, oder die Datenbank hängt wegen eines Fehlers, bevor sie OPEN erreicht. Auch eine Verbindung zu einer frisch gestarteten Instanz vor Abschluss des Starts liefert diesen Fehler.","fix":"Verbinde dich als SYSDBA und prüfe den Zustand: sqlplus / as sysdba; dann SELECT status FROM v$instance; und SELECT open_mode FROM v$database; Versuche, die Instanz zu öffnen: ALTER DATABASE OPEN; Ist Recovery nötig, führe RECOVER DATABASE; aus und öffne sie dann. Finde im alert.log den zugrunde liegenden Fehler, der das Öffnen verhindert (fehlende/beschädigte Datendatei, Redo). Hängt das Herunterfahren, versuche SHUTDOWN ABORT und danach STARTUP.","tags":["startup","shutdown","mount","open","recovery","instance"]},{"code":"ORA-00600","title":"internal error code, arguments: [...]","severity":"danger","meaning":"Eine interne Konsistenzprüfung im Oracle-Kernel ist fehlgeschlagen. Das ist ein generischer interner Fehler, der anzeigt, dass der Kernel-Code auf einen unerwarteten Zustand gestoßen ist; die Argumente in eckigen Klammern bezeichnen den ausgelösten Codepfad.","cause":"Kann durch Block-/Datenkorruption, einen nicht behobenen Bug der Oracle-Software, inkonsistente Patchstände, Speicher-/Hardwareprobleme oder unerwartete Randfälle ausgelöst werden. Das erste Argument (z. B. [ktbdchk1]) zeigt an, welche Schicht betroffen ist, und ist für die Support-Analyse entscheidend.","fix":"Dieser Fehler sollte fast immer mit Oracle Support bearbeitet werden. Bewahre die erzeugte Trace-Datei und den alert.log-Eintrag auf; suche das erste Argument in My Oracle Support mit dem ORA-600 Troubleshooter und dem 'ORA-600 Lookup Tool'. Spiele den passenden Patch/PSU/RU ein. Bei Verdacht auf Korruption prüfe die Blockintegrität mit DBVERIFY/RMAN VALIDATE. Experimentiere in kritischen Umgebungen nicht in der Produktion, ohne einen SR zu eröffnen.","tags":["internal","ora-600","kernel","bug","trace","support"]},{"code":"ORA-07445","title":"exception encountered: core dump","severity":"danger","meaning":"Ein Oracle-Serverprozess ist nach einer Ausnahme auf Betriebssystemebene (z. B. SIGSEGV Segmentation Fault, Access Violation) unerwartet abgestürzt und hat einen Core Dump erzeugt. Anders als bei ORA-00600 wird der Fehler durch ein Signal des Betriebssystems ausgelöst.","cause":"Meist ein Bug der Oracle-Software, Speicherkorruption, beschädigte Daten-/Indexblöcke, inkompatible Bibliotheken oder ein Hardware-/Speicherdefekt. Funktionsname und Signaltyp (z. B. SIGSEGV) in der Meldung bezeichnen den abgestürzten Codepfad.","fix":"Bewahre die erzeugten Trace- und Incident-Dateien sowie das alert.log auf und suche in My Oracle Support nach Funktionsname + Signal; meist ist ein Patch erforderlich. Führe Hardware-/Speichertests durch und gleiche die Patchstände von Betriebssystem und Oracle an. Bei Verdacht auf Korruption führe RMAN VALIDATE / DBVERIFY aus. Tritt der Fehler erneut auf, eröffne einen SR und sende den Core Dump an den Support.","tags":["ora-7445","core dump","sigsegv","kernel","bug","trace"]},{"code":"ORA-01102","title":"cannot mount database in EXCLUSIVE mode","severity":"danger","meaning":"Die Datenbank kann nicht im EXCLUSIVE-Modus gemountet werden, weil eine andere Instanz (oder Dateireste einer alten Instanz) offenbar noch eine Sperre darauf hält. Die Instanz bleibt in der MOUNT-Phase stehen.","cause":"Die vorherige Instanz wurde nicht sauber heruntergefahren und Lock-Dateien (lk<SID> unter $ORACLE_HOME/dbs oder übrig gebliebenes Shared Memory/Semaphoren) sind noch vorhanden; eine andere Instanz ist bereits mit derselben Datenbank verbunden; oder in einer Nicht-RAC-Installation wurde eine zweite Instanz gestartet. Übrig gebliebene Prozesse/Shared Memory einer früheren Instanz oder eine veraltete lk<SID>-Datei sind die häufigsten Ursachen.","fix":"Prüfe, dass tatsächlich keine andere Instanz läuft: ps -ef | grep pmon und ipcs -m (Shared-Memory-Segmente). Gibt es Reste, beende die Instanz mit ABORT und räume auf; entferne bei Bedarf übrig gebliebene Segmente/Semaphoren mit ipcrm. Lösche die Lock-Datei lk<SID> unter $ORACLE_HOME/dbs (nur wenn die Instanz sicher gestoppt ist). Versuche dann STARTUP MOUNT. Prüfe bei RAC CLUSTER_DATABASE und den Knotenstatus.","tags":["mount","exclusive","lock","startup","pmon","instance"]},{"code":"ORA-29913","title":"error in executing ODCIEXTTABLEOPEN callout","severity":"caution","meaning":"Beim Zugriff auf eine External Table ist beim Lesen/Parsen der Quelldatei ein Fehler aufgetreten. Oracle ist in der Routine des Access Drivers (ORACLE_LOADER / ORACLE_DATAPUMP) gescheitert, die es zum Abrufen der Daten der External Table aufruft.","cause":"Meist liegt die Datei nicht im Pfad, auf den das DIRECTORY-Objekt zeigt, dem Oracle-OS-Benutzer fehlt das Leserecht auf Datei/Verzeichnis, es gibt kein Schreibrecht für Log-/Bad-Dateien, oder die Access Parameters (Feldtrenner, Satzformat) sind falsch. Oft begleitet von Unterfehlern wie ORA-29400 und KUP-04xxx.","fix":"Lies die Unterfehler (KUP-xxxx), um die Grundursache zu bestimmen. Prüfe den DIRECTORY-Pfad und ob die Datei existiert: SELECT directory_name, directory_path FROM all_directories; Gib dem Oracle-OS-Benutzer Leserechte auf Datei und Verzeichnis sowie Schreibrechte auf das Log-/Bad-Verzeichnis. Passe den Abschnitt ACCESS PARAMETERS der External-Table-Definition an das Format der Quelldatei an. Prüfe bei Bedarf die READ/WRITE-Grants auf das DIRECTORY.","tags":["external table","external table","directory","oracle_loader","permission","kup"]}]},"errors/postgres-more3.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"08001","title":"sqlclient_unable_to_establish_sqlconnection","severity":"danger","meaning":"Der Client konnte keine Verbindung zum PostgreSQL-Server aufbauen. Die Verbindung ist auf Netzwerk-/Adressebene gescheitert, bevor daraus eine Session wurde.","cause":"Falscher Host/Port; eine Adresse, auf der der Server nicht lauscht (Remote-Verbindung bei listen_addresses = 'localhost'); der Server ist heruntergefahren/nicht erreichbar; eine Firewall blockiert den Port (Standard 5432); oder ein Fehler bei der DNS-Auflösung. Auch ein falsch konfigurierter Connection Pool kann den Fehler auslösen.","fix":"Prüfe, dass der Server läuft und auf der richtigen Adresse lauscht: Kontrolliere listen_addresses = '*' (oder die betreffende IP) und die Port-Einstellung in postgresql.conf und starte den Server dann neu (listen_addresses und port erfordern einen Neustart; ein Reload reicht nicht). Teste den Zugriff vom Client: psql -h host -p 5432 -U app_user. Öffne 5432/tcp in der Firewall. Prüfe die Werte für Host/Port/DNS und den Connection String.","tags":["connection","connection","network","listen_addresses","port","firewall"]},{"code":"08004","title":"sqlserver_rejected_establishment_of_sqlconnection","severity":"danger","meaning":"Der Server wurde erreicht, hat den Verbindungsaufbau aber abgelehnt. Netzwerkzugriff besteht, doch der Server hat diese Verbindung aufgrund einer Richtlinie nicht angenommen.","cause":"Der PostgreSQL-Server erzeugt diesen Code selten; er wird meist vom Client-Treiber oder vom Connection Pool gemeldet. Die tatsächlichen Codes des Servers für ähnliche Ablehnungen sind andere: Eine Ablehnung durch pg_hba.conf ('no pg_hba.conf entry for host', die Methode reject) liefert 28000 (invalid_authorization_specification), erschöpftes max_connections liefert 53300 (too_many_connections), und eine während Start/Herunterfahren abgelehnte Verbindung liefert 57P03.","fix":"Lies im Serverlog den tatsächlichen SQLSTATE und den Grund der Ablehnung. Ergänze bei einer pg_hba-Ablehnung (28000) eine passende Zeile für Client-IP, Benutzer und Datenbank (z. B. host all all 10.0.0.0/24 scram-sha-256) und lade mit SELECT pg_reload_conf(); neu. Vergleiche bei Verbindungssättigung (53300) die Client-Verbindungen mit max_connections über SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';","tags":["connection","pg_hba","rejected","authentication","max_connections","reject"]},{"code":"57P01","title":"admin_shutdown","severity":"danger","meaning":"Deine aktive Verbindung wurde wegen eines Administratorbefehls beendet. Der Client erhält diesen Fehler, wenn der Server herunterfährt oder die Session manuell beendet wird ('terminating connection due to administrator command').","cause":"Ein DBA hat die Session mit pg_terminate_backend() beendet; der Server wird im Modus fast/immediate heruntergefahren (ein Smart Shutdown beendet keine Sessions, sondern wartet auf deren Ende); oder bestehende Verbindungen wurden für eine Konfigurationsänderung/geplante Wartung getrennt. In manchen Failover-Szenarien trennt auch der alte Primary Verbindungen auf diese Weise.","fix":"Das ist meist kein dauerhafter Fehler; die Anwendung muss sich neu verbinden (Retry). Ist es unerwartet, untersuche im Serverlog den Grund für Shutdown/Terminate. Implementiere für geplante Wartungen automatischen Reconnect und Health-Checks des Connection Pools in der Anwendung. Das Serverlog zeigt nicht, wer eine Session beendet hat; um pg_terminate_backend-Aufrufe nachzuverfolgen, führe ein Audit-Protokoll mit log_statement (oder pgaudit).","tags":["shutdown","terminate","connection","maintenance","failover","retry"]},{"code":"53400","title":"configuration_limit_exceeded","severity":"danger","meaning":"Die Operation ist fehlgeschlagen, weil ein Konfigurationslimit überschritten wurde. Der Server hat die Anfrage wegen eines festgelegten Limits abgelehnt (z. B. temp_file_limit, Anzahl der Replication Slots oder WAL Sender).","cause":"Typische Beispiele: Die temporäre Dateinutzung einer Abfrage überschreitet temp_file_limit ('temporary file size exceeds temp_file_limit'); alle Replication Slots sind belegt ('all replication slots are in use', max_replication_slots); das Limit max_wal_senders wird überschritten. Hinweis: Erschöpftes max_prepared_transactions liefert 53200, deaktivierte Prepared Transactions (Wert 0) liefern 55000.","fix":"Die Fehlermeldung nennt das überschrittene Limit. Optimiere bei temp_file_limit die Abfrage (finde mit EXPLAIN ANALYZE Sort-/Hash-Schritte, die auf die Platte auslagern), erhöhe bei Bedarf work_mem auf Session-Ebene oder hebe das Limit bewusst an. Sind die Slots erschöpft, bereinige ungenutzte Slots, nachdem du ihre Eigentümer geprüft hast: SELECT slot_name, slot_type, active FROM pg_replication_slots; oder erhöhe max_replication_slots / max_wal_senders und starte den Server neu.","tags":["limit","temp_file_limit","replication slot","max_wal_senders","configuration","resource"]},{"code":"XX001","title":"data_corrupted","severity":"danger","meaning":"PostgreSQL hat in einer Tabellen-/Heap-Seite Datenkorruption erkannt. Die Abfrage kann nicht sicher abgeschlossen werden, weil der Inhalt des gelesenen Blocks inkonsistent ist ('invalid page in block').","cause":"Platten-/Speicherausfall, Dateisystemkorruption, nicht eingehaltene fsync-Garantien bei plötzlichem Stromausfall, fehlerhafte Hardware (RAM/Controller) oder selten ein Softwarefehler. Mit aktivierten Prüfsummen wird Korruption früher erkannt.","fix":"Prüfe zuerst Hardware/Speicher und plane nach Möglichkeit sofort den Rückgriff auf ein physisches Backup (PITR). Gibt es ein fehlerfreies Backup, stelle es wieder her. Zur Rettung kannst du AUF RISIKO DER DATEN und nur als letztes Mittel beschädigte Seiten mit zero_damaged_pages = on nullen und die lesbaren Daten exportieren (das führt zu Datenverlust). Aktiviere data_checksums (bei einem bestehenden Cluster ab PG12 mit pg_checksums --enable bei gestopptem Server), führe RAM-Tests durch und erstelle regelmäßig PITR-Backups.","tags":["corruption","corruption","checksum","page","hardware","pitr"]},{"code":"XX002","title":"index_corrupted","severity":"danger","meaning":"In einer Indexstruktur wurde Korruption erkannt. Da die Daten, auf die der Index verweist, nicht mit den tatsächlichen Zeilen im Heap übereinstimmen, können Abfragen über den Index falsche Ergebnisse liefern oder fehlschlagen.","cause":"Hardware-/Speicherausfall, durch eine Collation-Änderung (Sortierregel) nach einem glibc-/ICU-Upgrade des Betriebssystems gestörte B-Tree-Sortierung oder ein plötzlicher Ausfall. Insbesondere Collation-Updates des Betriebssystems verursachen stille Korruption in Textindizes.","fix":"Baue den beschädigten Index neu auf: REINDEX INDEX idx_name; oder REINDEX TABLE my_table; Nutze bei großen Tabellen REINDEX INDEX CONCURRENTLY, um Sperren zu reduzieren. Ist die Collation die Ursache, baue alle betroffenen Textindizes neu auf und prüfe sie mit der Extension amcheck (bt_index_check). Ist Hardware die Grundursache, tausche den Speicher.","tags":["index","index","corruption","reindex","collation","amcheck"]},{"code":"25001","title":"active_sql_transaction","severity":"caution","meaning":"Ein Befehl, der nur außerhalb einer Transaktion oder an deren Beginn laufen darf, wurde bei aktiver Transaktion aufgerufen. Manche Befehle laufen nicht innerhalb eines Transaktionsblocks.","cause":"Einstellungen wie SET TRANSACTION ISOLATION LEVEL müssen vor der ersten Abfrage im Block abgesetzt werden ('SET TRANSACTION ISOLATION LEVEL must be called before any query'). Ebenso werden manche Befehle innerhalb eines offenen BEGIN/einer offenen Transaktion abgelehnt. Meist rührt das daher, dass die Anwendung Autocommit deaktiviert und unbemerkt eine Transaktion startet.","fix":"Setze Einstellungen auf Transaktionsebene ganz am Anfang der Transaktion ab, bevor eine Abfrage läuft: BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; ... Prüfe, ob die Client-Bibliothek eine implizite Transaktion öffnet (Autocommit-Einstellung). Schließe für Befehle, die nicht in einem Transaktionsblock laufen können, zuerst die aktuelle Transaktion mit COMMIT/ROLLBACK ab.","tags":["transaction","transaction","isolation","set transaction","autocommit","begin"]},{"code":"42P16","title":"invalid_table_definition","severity":"caution","meaning":"Der DDL-Befehl wurde abgelehnt, weil die Tabellendefinition ungültig ist. Die angeforderte Tabellen-/Spaltenstruktur verstößt gegen PostgreSQL-Regeln.","cause":"Situationen wie die Definition mehrerer Primärschlüssel ('multiple primary keys for table are not allowed'), Definitionen, die mit Vererbung kollidieren, Einschränkungen bei Generated-/Identity-Spalten oder eine Änderung zu einer Struktur, die ALTER TABLE nicht erzeugen kann. Häufig in automatisch generierten Migrationsskripten.","fix":"Lies den konkreten Grund in der Fehlermeldung. Definiere nur einen PRIMARY KEY; wird ein mehrspaltiger Schlüssel benötigt, nutze einen zusammengesetzten Schlüssel der Form PRIMARY KEY (a, b). Prüfe die Regeln für Identity-/Generated-Spalten. Prüfe die aktuelle Tabellendefinition mit \\d my_table und korrigiere das Migrationsskript.","tags":["ddl","table","primary key","definition","alter table","migration"]},{"code":"P0001","title":"raise_exception","severity":"caution","meaning":"Eine PL/pgSQL-Funktion oder ein Trigger hat mit RAISE EXCEPTION absichtlich einen Fehler ausgelöst. Dieser Fehler stammt nicht von der Datenbank-Engine, sondern von Geschäftsregel-Code, den du (oder eine Bibliothek) geschrieben hast.","cause":"Wenn die Geschäftslogik eine Validierung nicht besteht, hat der Entwickler RAISE EXCEPTION 'message' USING ERRCODE = ...; aufgerufen. Zum Beispiel löst ein Trigger einen eigenen Fehler aus, um ein unzulässiges Update zu blockieren oder eine Konsistenzregel durchzusetzen.","fix":"Die Fehlermeldung und eventuelle HINT-/DETAIL-Felder erklären, was die Funktion abgelehnt hat; erfülle die Geschäftsregel entsprechend. Um die Quelle zu finden, prüfe den Code der betreffenden Funktion bzw. des Triggers (die RAISE-Anweisungen). Wird ein bestimmter SQLSTATE benötigt, vergib mit RAISE ... USING ERRCODE statt des generischen P0001 einen aussagekräftigen Code, damit die Anwendung ihn unterscheiden kann.","tags":["plpgsql","raise","trigger","function","business rule","exception"]}]},"errors/mysql-more3.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1049","title":"Unknown database 'name'","severity":"caution","meaning":"Die Datenbank (das Schema), zu der du dich verbinden oder die du verwenden willst, existiert auf dem Server nicht. Die USE-Anweisung oder der Standarddatenbankname im Connection String verweist auf ein nicht existierendes Schema.","cause":"Die Datenbank wurde nie angelegt, ihr Name ist falsch geschrieben (die Groß-/Kleinschreibung kann je nach Betriebssystem relevant sein), sie wurde gelöscht, oder die Anwendung verbindet sich mit der falschen Umgebung (dev/prod). Eine weitere häufige Ursache sind Schemas, die beim Umzug auf einen neuen Server nicht migriert wurden.","fix":"Vorhandene Datenbanken auflisten: SHOW DATABASES; Stelle sicher, dass der Name richtig geschrieben ist und die Groß-/Kleinschreibung passt (die Einstellung lower_case_table_names spielt eine Rolle). Existiert sie nicht, lege sie an: CREATE DATABASE database_name CHARACTER SET utf8mb4; oder stelle sie aus dem Backup wieder her. Prüfe die Zielumgebung in der Verbindungskonfiguration der Anwendung.","tags":["database","database","schema","use","name","connection"]},{"code":"1364","title":"Field 'name' doesn't have a default value","severity":"caution","meaning":"Eine Zeile wurde ohne Wert für eine Spalte eingefügt, die als NOT NULL ohne DEFAULT definiert ist. Im strikten SQL-Modus löst MySQL einen Fehler aus, weil es die fehlende Pflichtspalte nicht automatisch füllen kann.","cause":"Eine Pflichtspalte (NOT NULL, ohne DEFAULT) wurde im INSERT weggelassen, und STRICT_TRANS_TABLES/STRICT_ALL_TABLES ist in sql_mode aktiviert. Eine für den alten, nachsichtigen Modus geschriebene Anwendung stößt auf diesen Fehler, wenn sie auf einen Server mit aktiviertem Strict Mode umzieht.","fix":"Ergänze im INSERT einen Wert für die fehlende Spalte oder definiere ein sinnvolles DEFAULT für die Spalte: ALTER TABLE my_table MODIFY my_column ... NOT NULL DEFAULT 'value'; Ist die Spalte wirklich optional, erlaube NULL. sql_mode lässt sich als vorübergehender Workaround/zur Kompatibilität lockern, für die Datenqualität wird aber empfohlen, den Strict Mode beizubehalten.","tags":["insert","not null","default","strict mode","sql_mode","column"]},{"code":"1206","title":"The total number of locks exceeds the lock table size","severity":"danger","meaning":"InnoDB hat den für seine Sperren reservierten Speicher (die Lock-Strukturen im Buffer Pool) erschöpft. Tritt auf, wenn eine einzelne Transaktion zu viele Zeilen-/Gap-Locks ansammelt; die Transaktion wird zurückgerollt.","cause":"Sehr große Einzeltransaktionen: UPDATE/DELETE von Millionen Zeilen auf einmal, eine WHERE-Klausel ohne Index, die die ganze Tabelle sperrt, oder ein für die Last zu kleines innodb_buffer_pool_size. Große Batch-Jobs sind der klassische Auslöser.","fix":"Teile großes DML in kleine Batches auf (z. B. jeweils 10.000 Zeilen mit LIMIT in einer Schleife löschen/aktualisieren und nach jedem Batch COMMIT). Indiziere die WHERE-Spalten, um die Zahl gesperrter Zeilen zu senken. Erhöhe als dauerhafte Lösung innodb_buffer_pool_size (die Lock-Strukturen werden daraus allokiert). Vermeide unnötig lange Transaktionen.","tags":["innodb","lock","lock","buffer pool","batch","memory"]},{"code":"1236","title":"Got fatal error 1236 from master when reading data from binary log","severity":"danger","meaning":"Ein Replica-Server (Slave) hat beim Lesen von Daten aus dem Binary Log der Source (Master) einen fatalen Fehler erhalten, und die Replikation wurde gestoppt. Die Replica findet die erwartete Position im Binlog der Source nicht.","cause":"Die benötigten Binary-Log-Dateien wurden auf der Source gelöscht (binlog_expire_logs_seconds — vor 8.0 expire_logs_days, in 8.4 entfernt — oder PURGE BINARY LOGS); eine GTID-Lücke/-Abweichung; eine beschädigte Binlog-Position nach einem Absturz der Source; oder die Replica ist so weit zurückgefallen, dass sie ein nicht mehr vorhandenes Log anfordert. 'Could not find first log file name in binary log index file' ist die typische Untermeldung.","fix":"Replikationsstatus prüfen: SHOW REPLICA STATUS\\G (8.0.22+; in älteren Versionen SHOW SLAVE STATUS, die alte Syntax wurde in 8.4 entfernt). Auf der Source verfügbare Binlogs auflisten: SHOW BINARY LOGS; Wurde das fehlende Log bereinigt, musst du die Replica aus einem neuen Backup/einer neuen GTID-Position neu aufbauen (mysqldump --source-data — vor 8.0.26 --master-data, ab 8.0.26 ein veralteter Alias — oder das CLONE-Plugin). Erhöhe die Binlog-Aufbewahrung auf der Source passend zur Last; prüfe bei GTIDs auf GTID-Lücken.","tags":["replication","replication","binlog","master","gtid","slave"]},{"code":"1129","title":"Host 'host' is blocked because of many connection errors","severity":"danger","meaning":"MySQL hat einen Client-Host blockiert, nachdem aufeinanderfolgende Verbindungsfehler von diesem Host den Schwellenwert (max_connect_errors) überschritten haben. Der Host wurde wegen der bis dahin gescheiterten Handshakes gesperrt.","cause":"Ein Load Balancer/Monitoring-Tool sendet unvollständige oder fehlerhafte Handshakes an den TCP-Port; Netzwerkprobleme unterbrechen Verbindungen mittendrin; oder ein falsch konfigurierter Client versucht fortlaufend fehlschlagende Verbindungen. Erreicht der Fehlerzähler max_connect_errors, wird der Host blockiert.","fix":"Hebe die Blockierung sofort auf: in SQL mit TRUNCATE TABLE performance_schema.host_cache; (8.0.23+; FLUSH HOSTS ist seit 8.0.23 veraltet und wurde in 8.4 entfernt) oder auf der Kommandozeile mit mysqladmin flush-hosts. Blockierte Hosts anzeigen: SELECT host, ip, sum_connect_errors FROM performance_schema.host_cache; Behebe die Grundursache: Konfiguriere das Health-Check-/Monitoring-Tool so, dass es einen vollständigen MySQL-Handshake durchführt, und prüfe die Netzwerkstabilität. In gesunden Umgebungen kannst du max_connect_errors erhöhen, um die Toleranz zu steigern, entscheidend ist aber die Behebung der eigentlichen Verbindungsfehler.","tags":["connection","host blocked","max_connect_errors","flush hosts","handshake","network"]},{"code":"1194","title":"Table 'name' is marked as crashed and should be repaired","severity":"danger","meaning":"Eine Tabelle, meist mit der Engine MyISAM gespeichert (in MariaDB auch Aria), ist als beschädigt/abgestürzt markiert und kann bis zur Reparatur nicht verwendet werden. Abfragen auf die Tabelle schlagen mit diesem Fehler fehl.","cause":"Die MyISAM-Tabelle wurde bei einem plötzlichen Serverabsturz/Stromausfall nicht sauber geschlossen, die Platte lief voll, ein Hardwaredefekt trat auf, oder der Prozess mysqld wurde beendet. Da MyISAM weder Transaktionen noch Crash-Sicherheit bietet, passiert das weit häufiger als bei InnoDB.","fix":"Repariere die Tabelle: REPAIR TABLE table_name; oder offline mit myisamchk -r. Sichere vor der Reparatur immer die Datendateien. Als dauerhafte Lösung migriere die Tabellen nach InnoDB: ALTER TABLE my_table ENGINE=InnoDB; das macht sie crash-sicher. Prüfe den Plattenzustand und die Einstellung myisam_recover_options.","tags":["myisam","crashed","repair","corruption","corruption","innodb"]},{"code":"1615","title":"Prepared statement needs to be re-prepared","severity":"caution","meaning":"Die Ausführung eines serverseitigen Prepared Statements wurde abgelehnt, weil es ungültig wurde, nachdem sich die Metadaten geändert haben, von denen es abhängt (Tabellen-/View-Definition). Die Anweisung muss neu vorbereitet werden.","cause":"DDL (ALTER TABLE) auf der zugrunde liegenden Tabelle nach dem Vorbereiten der Anweisung, Neuanlage der Tabelle, ein volllaufender Metadaten-Cache, der den Eintrag verdrängt, oder zu kleine Limits für table_definition_cache/table_open_cache. Häufig auf Systemen mit viel DDL und in manchen ORM-/Pool-Szenarien.","fix":"Die meisten Client-Bibliotheken fangen das ab und bereiten die Anweisung automatisch neu vor; fange diesen Fehler in der Anwendung ab und wiederhole Prepare + Execute. Tritt er häufig auf, erhöhe table_definition_cache und table_open_cache. Vermeide unnötige DDL während des Anwendungsverkehrs.","tags":["prepared statement","re-prepare","ddl","metadata","cache","retry"]},{"code":"1553","title":"Cannot drop index needed in a foreign key constraint","severity":"caution","meaning":"Du willst einen Index löschen, der von einem Foreign-Key-Constraint benötigt wird. InnoDB erlaubt nicht, den Index zu entfernen, auf den sich ein FK stützt.","cause":"In InnoDB benötigt jeder Foreign Key einen Index auf seiner bzw. seinen referenzierenden Spalte(n). Das Löschen dieses Index mit DROP INDEX wird abgelehnt, weil der FK-Constraint sonst ohne Grundlage wäre. Tritt meist bei der Reorganisation einer Tabelle auf (Index-Bereinigung).","fix":"Lösche zuerst den Foreign-Key-Constraint und dann den Index: ALTER TABLE my_table DROP FOREIGN KEY fk_name; dann DROP INDEX idx_name ON my_table; Lege den FK bei Bedarf neu an, sodass er sich auf einen anderen geeigneten Index stützt. Vorhandene FKs auflisten: SELECT constraint_name, table_name FROM information_schema.key_column_usage WHERE referenced_table_name IS NOT NULL;","tags":["foreign key","foreign key","index","drop index","innodb","constraint"]},{"code":"3948","title":"Loading local data is disabled; this must be enabled on both the client and server sides","severity":"caution","meaning":"Du wolltest mit LOAD DATA LOCAL INFILE Daten aus einer clientseitigen Datei laden, aber die Funktion ist auf Client- und/oder Serverseite deaktiviert. Aus Sicherheitsgründen ist LOCAL in MySQL 8 standardmäßig deaktiviert.","cause":"Die Systemvariable local_infile steht auf dem Server auf OFF und/oder der Client-Treiber hat LOCAL INFILE nicht aktiviert. Die Funktion ist standardmäßig eingeschränkt, weil sie einem bösartigen Server das Lesen von Client-Dateien ermöglichen kann.","fix":"Wird sie wirklich benötigt, aktiviere sie auf beiden Seiten: auf dem Server SET GLOBAL local_infile = 1; auf dem Client das LOCAL-INFILE-Flag des Treibers (z. B. die Verbindungsoption allowLoadLocalInfile / --local-infile=1). Aus Sicherheitssicht ist es vorzuziehen, die Datei auf dem Server abzulegen und LOAD DATA INFILE ohne LOCAL zu verwenden oder Bulk-INSERT-/Import-Tools zu nutzen. Deaktiviere local_infile danach wieder.","tags":["load data","local infile","local_infile","security","import","loading"]},{"code":"1075","title":"Incorrect table definition; there can be only one auto column and it must be defined as a key","severity":"caution","meaning":"Die Tabellendefinition ist ungültig, weil die AUTO_INCREMENT-Regeln verletzt wurden: Eine Tabelle kann nur eine Auto-Increment-Spalte haben, und diese muss Teil eines Schlüssels sein (PRIMARY KEY oder UNIQUE/Index).","cause":"Definition mehrerer AUTO_INCREMENT-Spalten oder eine AUTO_INCREMENT-Spalte, die in keinem Schlüssel (Index) enthalten ist. Tritt bei CREATE TABLE oder ALTER TABLE auf.","fix":"Behalte eine einzige AUTO_INCREMENT-Spalte und mache sie zum Teil eines Schlüssels: CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, ...); Um eine vorhandene Tabelle zu korrigieren, nimm die Spalte zuerst in einen Schlüssel auf: ALTER TABLE t ADD PRIMARY KEY (id); und definiere dann AUTO_INCREMENT. Brauchst du mehr als eine Auto-Spalte, überdenke das Design; MySQL unterstützt das nicht.","tags":["auto_increment","table","primary key","key","ddl","definition"]}]},"errors/mssql-more3.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"102","title":"Incorrect syntax near 'token'","severity":"caution","meaning":"Der Transact-SQL-Parser hat in der Nähe des angegebenen Tokens eine ungültige Syntax gefunden und konnte die Anweisung nicht kompilieren. Die Abfrage wird vor der Ausführung abgelehnt.","cause":"Ein Tippfehler, ein fehlendes Komma/eine fehlende Klammer, ein reserviertes Schlüsselwort (z. B. USER, ORDER) ohne eckige Klammern, ein nicht abgeschlossenes String-Literal oder inkompatible Syntax, die aus einem anderen SQL-Dialekt übernommen wurde. Auch Verkettungsfehler in dynamischem SQL sind ein häufiger Auslöser.","fix":"Untersuche den Bereich um das gemeldete Token sorgfältig; der Fehler liegt oft in der vorhergehenden Zeile. Setze reservierte Wörter in eckige Klammern ([Order]) und prüfe, ob Klammern und Kommas stimmig sind. Baust du dynamisches SQL, gib mit PRINT @sql den erzeugten Text aus und führe ihn aus. Prüfe, ob die Syntax von der Version/dem Kompatibilitätsgrad unterstützt wird.","tags":["syntax","syntax error","parse","tsql","dynamic sql","keyword"]},{"code":"8114","title":"Error converting data type X to Y","severity":"caution","meaning":"Ein Wert konnte nicht von einem Datentyp in einen anderen konvertiert werden, weil die Typen inkompatibel sind. Anders als bei Fehler 245 handelt es sich meist um eine gescheiterte Typkonvertierung auf Parameter-/Ausdrucksebene (z. B. varchar nach numeric).","cause":"Nicht numerischer oder fehlerhafter Text wird an eine numerische Spalte/einen numerischen Parameter gesendet; Überlauf von Scale/Precision; Versuch, einen leeren String ('') in numeric zu konvertieren; oder Übergabe eines Arguments falschen Typs in einer parametrisierten Abfrage. Häufig bei impliziten Konvertierungen, die ORMs erzeugen.","fix":"Prüfe, ob die konvertierten Werte zum Zieltyp passen. Nutze TRY_CONVERT / TRY_CAST für eine sichere Konvertierung; sie liefern bei Misserfolg NULL, sodass du diese Zeilen per WHERE filtern kannst. Passe Parametertypen an die Spaltentypen an. Durchsuche die Daten mit ISNUMERIC/TRY_CAST nach fehlerhaften Zeilen und bereinige leere Strings.","tags":["convert","cast","data type","conversion","numeric","try_convert"]},{"code":"1204","title":"The instance of the SQL Server Database Engine cannot obtain a LOCK resource","severity":"danger","meaning":"SQL Server konnte den vom Lock Manager benötigten Speicher nicht allokieren und kann keine weiteren Sperren anlegen. Die Operation schlägt fehl, weil der Speicherpool für Sperren erschöpft ist.","cause":"Zu viele feingranulare Sperren (Zeile/Seite) gleichzeitig: großes, nicht indiziertes oder Bulk-DML; verhinderte Lock Escalation; allgemeiner Speicherdruck auf dem Server; oder lang laufende offene Transaktionen. Gelegentlich ist externer Speicherdruck der Auslöser.","fix":"Teile große Operationen in kleinere Batches auf, um die Zahl gleichzeitiger Sperren zu senken. Nutze passende Indizes, um die Zahl gelesener/gesperrter Zeilen zu verringern. Prüfe Konfigurationen, die Lock Escalation deaktivieren: Trace Flags 1211/1224 und ALTER TABLE ... SET (LOCK_ESCALATION = DISABLE); prüfe mit SELECT name, lock_escalation_desc FROM sys.tables; und DBCC TRACESTATUS(-1); Prüfe den Serverspeicher und die Einstellung max server memory und entlaste den Speicherdruck.","tags":["lock","lock memory","memory","lock escalation","batch","resource"]},{"code":"3906","title":"Failed to update database because the database is read-only","severity":"caution","meaning":"Du wolltest Daten (INSERT/UPDATE/DELETE/DDL) in einer als schreibgeschützt markierten Datenbank ändern. SQL Server hat den Schreibvorgang abgelehnt.","cause":"Die Datenbank wurde auf READ_ONLY gesetzt; die Verbindung ging an eine Secondary Replica einer Availability Group (für Lesezugriffe gedacht); die Datenbank blieb nach einem Snapshot oder Restore schreibgeschützt; oder das Dateisystem ist schreibgeschützt. Typische Ursache ist eine Anwendung, die versehentlich auf eine Secondary Replica geleitet wird.","fix":"Muss sie wirklich beschreibbar sein: ALTER DATABASE mydb SET READ_WRITE; Leite die Anwendung in einer Availability Group auf die Primary Replica (prüfe ReadWrite-Connection-Routing / ApplicationIntent). Ein schreibgeschützter Snapshot kann nicht beschrieben werden; ziele stattdessen auf die Quelldatenbank. Prüfe außerdem Dateiberechtigungen und Dateisystem.","tags":["read only","read-only database","availability group","replica","read_write","write"]},{"code":"15023","title":"User already exists in the current database","severity":"caution","meaning":"Beim Hinzufügen eines neuen Benutzers zur Datenbank wurde festgestellt, dass dort bereits ein Benutzer mit demselben Namen existiert. Tritt auch auf, wenn ein Login einem vorhandenen Benutzer zugeordnet wird (verwaiste Benutzer).","cause":"Typischerweise passen nach dem Restore einer Datenbank aus einem Backup auf einem anderen Server die Login-SIDs nicht zu den Datenbankbenutzern (verwaiste Benutzer). Der Versuch, denselben Namen erneut mit CREATE USER anzulegen oder die Zuordnung Login–Benutzer manuell einzurichten, löst diesen Fehler aus.","fix":"Ordne den verwaisten Benutzer dem vorhandenen Login neu zu: ALTER USER app_user WITH LOGIN = login_name; (empfohlen statt des veralteten sp_change_users_login). Ist der Benutzer tatsächlich überflüssig: DROP USER app_user; Verwaiste Benutzer finden: SELECT dp.name FROM sys.database_principals dp LEFT JOIN sys.server_principals sp ON dp.sid = sp.sid WHERE sp.sid IS NULL AND dp.type IN ('S','U');","tags":["user","database user","login","orphaned","restore","orphaned user"]},{"code":"18452","title":"Login failed. The login is from an untrusted domain and cannot be used with Windows authentication","severity":"danger","meaning":"Ein Anmeldeversuch mit Windows-Authentifizierung wurde abgelehnt, weil er aus einer nicht vertrauenswürdigen Domäne kam (oder Windows-Authentifizierung verwendet wurde, wo SQL-Authentifizierung erforderlich ist). Der Server vertraut dieser Identität nicht.","cause":"Der Client verbindet sich mit einer Windows-Identität aus einer Domäne/Arbeitsgruppe, der SQL Server nicht vertraut; die Rechner liegen in verschiedenen/nicht vertrauenswürdigen Domänen; die Kerberos-/SPN-Konfiguration ist falsch; oder der Connection String verwendet Integrated Security, obwohl SQL-Authentifizierung erwartet wird. Ein SQL-Login-Versuch bei deaktiviertem Mixed Mode zeigt ein ähnliches Symptom.","fix":"Ist SQL-Authentifizierung erforderlich, stelle den Server auf Mixed Mode um und verwende im Connection String Benutzername/Passwort (Integrated Security=false). Ist Windows-Authentifizierung erforderlich, prüfe die Domänenvertrauensstellung und die DNS-/Kerberos-SPN-Konfiguration. Stelle sicher, dass Client und Server in derselben oder einer vertrauenswürdigen Domäne liegen. Grenze die Ursache mit dem Wert 'state' im Fehlerprotokoll ein.","tags":["login","authentication","windows","domain","kerberos","mixed mode"]},{"code":"605","title":"Attempt to fetch logical page belongs to a different allocation unit","severity":"danger","meaning":"Beim Lesen einer Seite hat SQL Server statt des erwarteten Objekts/der erwarteten Allocation Unit eine Seite eines anderen Objekts gefunden. Das ist ein starkes Indiz für eine schwere physische/logische Korruption der Datenbank.","cause":"Defekte Seitenverknüpfung durch Speicher-/Plattenausfall, einen Controller- oder Treiberfehler, Korruption des Schreibcaches bei plötzlichem Stromausfall oder Dateisystemschäden. Ein 'transient 605' kann gelegentlich auf vorübergehende I/O-Probleme zurückgehen, dauerhafte Fälle sind aber echte Korruption.","fix":"Führe sofort DBCC CHECKDB ('mydb') WITH NO_INFOMSGS, ALL_ERRORMSGS aus, um das Ausmaß des Schadens festzustellen. Stelle nach Möglichkeit aus einem bekannt fehlerfreien Backup wieder her (PITR mit minimalem Datenverlust wird bevorzugt). Teste Speicher/Hardware und prüfe die Einstellung PAGE_VERIFY CHECKSUM. REPAIR-Optionen können Datenverlust verursachen; setze sie nur als letztes Mittel und nach einem Backup ein; ziehe in kritischen Umgebungen den Microsoft-Support hinzu.","tags":["corruption","database corruption","page","allocation unit","dbcc checkdb","storage"]},{"code":"4922","title":"ALTER TABLE ALTER COLUMN failed because one or more objects access this column","severity":"caution","meaning":"Du wolltest Typ/Eigenschaft einer Spalte ändern, aber von dieser Spalte abhängige Objekte (Index, Constraint, berechnete Spalte, Statistik, schemagebundene View usw.) haben es verhindert. Die Spalte kann erst geändert werden, wenn die Abhängigkeiten entfernt sind.","cause":"Die Spalte wird in einem Index, einem PRIMARY KEY/UNIQUE/CHECK/DEFAULT-Constraint, einem Ausdruck einer berechneten Spalte, einer WITH SCHEMABINDING definierten View/Funktion oder einem Statistikobjekt verwendet. Eine Typ-/Größenänderung würde diese Objekte ungültig machen, daher wird das ALTER abgelehnt.","fix":"Finde zuerst die abhängigen Objekte und lösche bzw. deaktiviere sie vorübergehend, ändere die Spalte und lege sie dann neu an. Frage sys.sql_expression_dependencies und sys.indexes/sys.default_constraints ab, um die Abhängigkeiten zu sehen. Typische Reihenfolge: DROP CONSTRAINT/INDEX -> ALTER COLUMN -> erneut CREATE. Entferne bei schemagebundenen Views SCHEMABINDING vorübergehend und füge es danach wieder hinzu.","tags":["alter column","dependency","index","constraint","schemabinding","ddl"]},{"code":"7202","title":"Could not find server 'name' in sys.servers","severity":"caution","meaning":"Der in der Abfrage über einen Linked Server oder einen vierteiligen Namen (server.database.schema.object) referenzierte Servername wurde unter den konfigurierten Linked Servern nicht gefunden. SQL Server konnte den Namen nicht auflösen.","cause":"Der Linked Server wurde nie definiert; der Name ist falsch geschrieben; ein in einer anderen Umgebung (dev/prod) vorhandener Linked Server fehlt in der Produktion; oder OPENQUERY/der vierteilige Name verweist auf den falschen Servernamen. Häufig, wenn Skripte zwischen Servern verschoben werden.","fix":"Definierte Linked Server auflisten: SELECT name FROM sys.servers; oder EXEC sp_linkedservers. Bei Bedarf hinzufügen: EXEC sp_addlinkedserver @server='SERVER', ...; und die Login-Zuordnung mit sp_addlinkedsrvlogin einrichten. Prüfe, ob der vierteilige Name in der Abfrage den richtigen Linked-Server-Namen verwendet; greifst du nur auf ein lokales Objekt zu, entferne das Serverpräfix.","tags":["linked server","remote server","sys.servers","openquery","four-part name","configuration"]},{"code":"40501","title":"The service is currently busy. Retry the request after 10 seconds","severity":"danger","meaning":"In Azure SQL Database hat der Dienst die Anfrage wegen Ressourcendrucks vorübergehend abgelehnt (Engine Throttling). Das System ist ausgelastet, und vom Client wird erwartet, dass er es in Kürze erneut versucht.","cause":"Eine Ressource wie CPU, Verbindungen, Schreibrate des Transaktionslogs oder Worker-Anzahl erreicht das Limit der gewählten Dienstebene (DTU/vCore); ein plötzlicher Lastanstieg; oder lang laufende/ineffiziente Abfragen verbrauchen die Ressource. Der 'reason code' zum Fehler zeigt, welche Ressource gedrosselt wird.","fix":"Implementiere in der Anwendung eine Retry-Logik mit exponentiellem Backoff; das ist das erwartete Verhalten bei vorübergehenden Fehlern. Prüfe die Ressourcennutzung: sys.dm_db_resource_stats und die Metriken im Azure-Portal. Optimiere Abfragen/Indizes und reduziere unnötige Parallelität. Ist die Sättigung dauerhaft, skaliere die Dienstebene hoch (DTU/vCore).","tags":["azure","throttling","busy","retry","resource","dtu"]}]},"errors/mongodb-more3.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"10107 NotWritablePrimary","title":"not master / not writable primary","severity":"danger","meaning":"Ein Schreibvorgang (oder eine andere Operation, die den Primary erfordert) wurde an ein Replica-Set-Mitglied gesendet, das gerade nicht Primary ist. Nur der Primary nimmt Schreibvorgänge an, daher wurde die Operation abgelehnt.","cause":"Die Anwendung schreibt auf ein Secondary; während eines Failovers (Election) wurde der alte Primary zum Secondary und der Client ist noch mit ihm verbunden; oder die Read-/Write-Preference und die Topologieinformationen des Treibers sind veraltet. Typische Ursache ist ein fehlender Parameter replicaSet im Connection String.","fix":"Konfiguriere den Treiber mit allen Replica-Set-Mitgliedern und dem Namen des Replica Sets (replicaSet=... und alle Hosts im Connection String), damit der Treiber den Primary automatisch erkennt und bei einem Failover umleitet. Nutze die Standard-Read-/Write-Preference, die Schreibvorgänge an den Primary leitet. Da es ein vorübergehender Fehler ist, aktiviere in der Anwendung Retryable Writes. Prüfe mit rs.status(), welches Mitglied PRIMARY ist.","tags":["replica set","primary","failover","write","election","retryable"]},{"code":"91 ShutdownInProgress","title":"The server is in the process of shutting down","severity":"danger","meaning":"Die Operation konnte nicht abgeschlossen werden, weil der Server (mongod/mongos) herunterfährt. Ein herunterfahrender Knoten lehnt neue Anfragen ab und beendet laufende Operationen.","cause":"Ein geplanter Neustart/eine Wartung, ein Herunterfahren vor einem Primary-Stepdown im Replica Set, ein vom Container/Orchestrator gesendetes Beendigungssignal oder das Stoppen des Prozesses wegen Ressourcenerschöpfung. Bei Rolling Upgrades zu erwarten.","fix":"Das ist meist vorübergehend; nutze in der Anwendung eine Retry-Logik und eine Replica-Set-Verbindung, damit Anfragen an ein anderes gesundes Mitglied geleitet werden. Ist es unerwartet, prüfe in den mongod-Logs den Grund für das Herunterfahren (Signal, OOM Killer, Ressourcen). Führe bei rollierender Wartung vor dem Herunterfahren einen sauberen Stepdown des Primary durch (rs.stepDown()).","tags":["shutdown","shutting down","maintenance","stepdown","replica set","retry"]},{"code":"189 PrimarySteppedDown","title":"Primary stepped down while operation was in progress","severity":"danger","meaning":"Der Primary ist während der laufenden Operation zurückgetreten (Stepdown) und nimmt keine Schreibvorgänge mehr an. Der laufende Befehl wurde daher abgebrochen.","cause":"Im Replica Set wurde eine Election ausgelöst: Der Primary wurde für eine Wartung manuell zurückgestuft, hat den Kontakt zur Mehrheit verloren, eine Prioritätsänderung trat ein, oder es gab eine Netzwerkpartition. Operationen, die zum Zeitpunkt des Failovers laufen, erhalten diesen Fehler.","fix":"Das ist typischerweise ein wiederholbarer Fehler; sind Retryable Writes/Reads im Treiber aktiviert, kann die Operation automatisch an den neuen Primary geleitet werden. Fange diesen Fehler in der Anwendung ab und wiederhole. Tritt er häufig auf, untersuche den Zustand des Replica Sets: Prüfe Mitgliederstatus, Netzwerklatenz und Oplog-Verzögerung mit rs.status() und reduziere unnötige Elections (Heartbeat-/Timeout-Einstellungen).","tags":["stepdown","election","failover","primary","replica set","retry"]},{"code":"24 LockTimeout","title":"Operation exceeded time limit waiting for a lock","severity":"caution","meaning":"Eine Operation hat beim Warten auf eine benötigte Sperre (z. B. auf Datenbank-/Collection-Ebene) ihr Zeitlimit überschritten und wurde abgebrochen. Die Sperre konnte nicht rechtzeitig erlangt werden.","cause":"Eine lang laufende Operation oder ein DDL-Befehl (z. B. konkurrierende administrative Operationen auf einer Collection) hält die benötigte Sperre; hohe parallele Schreiblast; oder eine durch maxTimeMS begrenzte Operation läuft beim Warten auf eine Sperre ab. Tritt bei konkurrierenden Index-/Collection-Operationen auf.","fix":"Finde die Operation, die die Sperre hält: Prüfe aktive und wartende Operationen mit db.currentOp(); beende sie bei Bedarf mit db.killOp(). Verlege konkurrierende administrative Befehle (Indexaufbauten, collMod, compact) in lastarme Zeiten. Halte Operationen kurz und vermeide unnötig lange Transaktionen. Passe bei Bedarf maxTimeMS an die Last an.","tags":["lock","lock wait","timeout","currentop","ddl","concurrency"]},{"code":"7 HostNotFound","title":"Could not find host","severity":"danger","meaning":"MongoDB konnte einen Hostnamen, mit dem es sich verbinden wollte, nicht auflösen/finden. Der Treiber oder ein Replica-Set-Mitglied erreicht den angegebenen Host nicht, daher kann die Verbindung nicht aufgebaut werden.","cause":"Fehler bei der DNS-Auflösung, ein falscher/veralteter Hostname im Connection String, Hostnamen der Replica-Set-Mitglieder (rs.conf), die der Client nicht auflösen kann, oder ein Netzwerk-/Zugriffsproblem. In Container-Umgebungen sind von außen nicht auflösbare interne Hostnamen die typische Ursache.","fix":"Prüfe, ob der Hostname korrekt und auflösbar ist (teste DNS mit ping/nslookup). Aktualisiere die Hosts im Connection String. Stelle sicher, dass die Namen der Replica-Set-Mitglieder sowohl von Clients als auch von Mitgliedern aufgelöst werden können; ändere die Hostnamen in rs.conf() bei Bedarf in erreichbare FQDNs/IPs. Nutzt du SRV (mongodb+srv), prüfe die DNS-SRV-Einträge.","tags":["host","dns","connection","replica set","network","srv"]},{"code":"175 QueryPlanKilled","title":"Query plan killed during execution","severity":"caution","meaning":"Der Plan einer laufenden Abfrage wurde ungültig, weil sich die zugrunde liegenden Daten bzw. die Collection geändert haben, und er wurde beendet. Die Abfrage wurde abgebrochen, weil sie nicht sicher fortgesetzt werden konnte.","cause":"Während die Abfrage lief (vor allem bei offenem Cursor), wurde die Collection gelöscht, ein von ihr genutzter Index gelöscht oder es gab ein Umbenennen/eine Neukonfiguration. Änderungen im Hintergrund während lang laufender getMore-Aufrufe sind der typische Auslöser.","fix":"Starte die Abfrage bzw. den Cursor neu (Retry). Stelle sicher, dass während laufender Abfragen keine destruktive DDL (drop, dropIndex, rename) auf der Collection oder dem Index ausgeführt wird; plane solche Wartungen außerhalb des Abfrageverkehrs. Verkürze lang laufende Lesevorgänge und stelle sicher, dass benötigte Indizes dauerhaft vorhanden sind.","tags":["query","cursor","getmore","drop","index","plan"]},{"code":"11602 InterruptedDueToReplStateChange","title":"Operation interrupted due to a replication state change","severity":"danger","meaning":"Die Operation wurde unterbrochen, weil sich der Zustand des Replica Sets geändert hat (z. B. Primary-Wechsel). Der laufende Befehl wurde beendet, weil er in der neuen Topologie nicht sicher abgeschlossen werden konnte.","cause":"Laufende Operationen werden während einer Election, eines Primary-Stepdowns, bei Rollenwechseln von Mitgliedern oder einer Konfigurationsänderung unterbrochen. Hat dieselbe Ursache wie 189 PrimarySteppedDown und tritt meist zum Zeitpunkt des Failovers auf.","fix":"Das ist ein wiederholbarer Fehler; sind Retryable Writes/Reads im Treiber aktiviert, kann die Operation gegen den neuen Primary wiederholt werden. Fange diesen Fehler in der Anwendung ab und wiederhole. Tritt er häufig auf, untersuche die Ursache der Elections: Netzwerkinstabilität, Heartbeat-Timeouts, Überlast oder Oplog-Verzögerung. Überwache den Zustand des Replica Sets mit rs.status().","tags":["replica set","election","failover","interrupted","retry","state change"]},{"code":"136 CappedPositionLost","title":"Capped collection position lost","severity":"caution","meaning":"Ein Cursor, der eine Capped Collection (feste Größe) liest (typischerweise ein Tailable Cursor), hat seine Position verloren. Das Dokument, auf dem er stand, existiert nicht mehr, weil die Collection es zyklisch überschrieben hat.","cause":"Ist eine Capped Collection voll, werden die ältesten Dokumente automatisch entfernt und überschrieben; ein Tailable Cursor, der einer Collection folgt, die schneller beschrieben wird, als er konsumieren kann, fällt zurück und seine Position wird überschrieben. Tritt bei Consumern auf, die Capped-Strukturen wie dem Oplog folgen.","fix":"Positioniere den Cursor neu (öffne einen neuen Tailable Cursor) und starte das Tailing erneut; fange diesen Fehler in der Anwendung ab und lies neu. Ist die Grundursache ein Consumer, der mit der Schreibrate nicht mithält: Vergrößere die Capped Collection bzw. das Oplog oder beschleunige den Consumer. Nutze für eine zuverlässige Änderungsverfolgung nach Möglichkeit Change Streams statt Tailable Cursors.","tags":["capped","tailable cursor","oplog","cursor","position","change stream"]},{"code":"28 PathNotViable","title":"Cannot use the part of the path to traverse the element","severity":"caution","meaning":"Ein Update wollte einen Feldpfad mit Punktnotation (z. B. a.b.c) durchlaufen, aber ein Teil des Pfads traf auf einen Typ, der keine Unterfelder enthalten kann (einen Skalarwert oder ein Element falschen Typs). Der Pfad konnte daher nicht durchlaufen werden.","cause":"Schreiben auf einen verschachtelten Pfad wie a.b.c, während das Feld 'a' im Dokument ein Skalar (z. B. Zahl/String) statt eines Objekts/Arrays ist; oder Typkonflikte, etwa ein Objekt, wo ein Array-Index erwartet wird. Häufig bei Dokumenten mit inkonsistentem Schema (beim Aktualisieren tiefer Pfade mit $set).","fix":"Stelle vor dem Update sicher, dass der Pfad den erwarteten Typ (Objekt/Array) hat. Korrigiere die inkompatiblen Dokumente oder mache das Update von der Dokumentstruktur abhängig. Konvertiere das Feld bei Bedarf zuerst in den richtigen Typ (z. B. ersetze den Skalarwert durch ein Objekt) und schreibe dann das verschachtelte Feld. Erwäge Schemavalidierung für ein konsistentes Schema.","tags":["update","path","dotted","type","set","schema"]}]},"errors/redis-more3.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"BUSYGROUP Consumer Group name already exists","title":"Consumer group already exists","severity":"caution","meaning":"Du wolltest mit XGROUP CREATE eine Consumer Group auf einem Stream anlegen, aber auf diesem Stream existiert bereits eine Gruppe mit demselben Namen. Redis überschreibt die vorhandene Gruppe nicht.","cause":"Die Anwendung versucht beim Start bedingungslos, die Gruppe anzulegen, und die Gruppe stammt noch aus einem früheren Lauf; oder mehrere Instanzen versuchen parallel, dieselbe Gruppe anzulegen. Typisch für nicht idempotente Startlogik.","fix":"Mache das Anlegen der Gruppe idempotent: Fange den Fehler ab und ignoriere ihn (eine vorhandene Gruppe ist in Ordnung). Nutze XGROUP CREATE stream group $ MKSTREAM, um den Stream automatisch anzulegen, falls er noch nicht existiert. Führe XINFO GROUPS stream aus, um vorhandene Gruppen zu sehen. Willst du sie wirklich zurücksetzen, lösche sie zuerst mit XGROUP DESTROY stream group.","tags":["stream","consumer group","xgroup","busygroup","idempotent","mkstream"]},{"code":"NOGROUP No such key or consumer group","title":"No such key or consumer group in stream","severity":"caution","meaning":"Du hast einen Stream-Konsumbefehl ausgeführt (XREADGROUP, XACK, XCLAIM usw.), aber der angegebene Key (Stream) oder die Consumer Group existiert nicht. Die zu bearbeitende Gruppe bzw. der Stream wurde nicht gefunden.","cause":"Die Gruppe wurde nie angelegt (XGROUP CREATE übersprungen); der Stream-Key wurde gelöscht oder nie beschrieben; Gruppenname/Key ist falsch geschrieben; oder die Gruppe wurde mit XGROUP DESTROY entfernt. Häufig, wenn Consumer starten, bevor die Gruppe angelegt ist.","fix":"Stelle zuerst sicher, dass Stream und Gruppe existieren: XINFO STREAM stream und XINFO GROUPS stream. Fehlt die Gruppe, lege sie an: XGROUP CREATE stream group $ MKSTREAM (legt bei Bedarf auch den Stream an). Ergänze beim Anwendungsstart einen Schritt, der die Gruppe anlegt (BUSYGROUP ignorieren). Prüfe, ob Key- und Gruppennamen richtig geschrieben sind.","tags":["stream","consumer group","nogroup","xreadgroup","xgroup","key"]},{"code":"NOTBUSY No scripts in execution right now","title":"No scripts in execution right now","severity":"safe","meaning":"Du hast SCRIPT KILL oder FUNCTION KILL gesendet, aber derzeit läuft kein (beendbares) Lua-Skript bzw. keine Funktion. Der Befehl wurde abgelehnt, weil es nichts zu beenden gibt.","cause":"Das Skript war bereits beendet, als du es stoppen wolltest; SCRIPT KILL wurde an den falschen Knoten gesendet (während das Skript auf einer anderen Instanz läuft); oder der BUSY-Zustand hat sich vor SCRIPT KILL von selbst aufgelöst. Meist ein Timing-Problem.","fix":"Hängt tatsächlich kein Skript, gibt es nichts zu tun; das ist eine unbedenkliche/informative Antwort. Erhältst du einen BUSY-Fehler (ein Skript hat busy-reply-threshold überschritten, vor Redis 7 lua-time-limit genannt), versuche SCRIPT KILL (bei Funktionen FUNCTION KILL) auf dem richtigen Knoten, auf dem das Skript läuft. Hat das Skript Daten geändert, funktioniert SCRIPT KILL nicht; als letztes Mittel kann SHUTDOWN NOSAVE nötig sein. Zerlege lange Skripte und halte das Zeitlimit ein.","tags":["lua","script kill","notbusy","busy","script","function"]},{"code":"DENIED Redis is running in protected mode","title":"Redis is running in protected mode","severity":"danger","meaning":"Redis läuft im Protected Mode und hat eine passwortlose Verbindung von außen (von einer Nicht-Loopback-Adresse) abgelehnt. Aus Sicherheitsgründen wird nicht authentifizierter Fernzugriff blockiert.","cause":"Redis, an alle Schnittstellen gebunden (oder mit lockerer bind-Einstellung), hat eine Verbindung von einem externen Client erhalten, ohne dass ein Passwort (requirepass) konfiguriert ist. protected-mode ist standardmäßig aktiv und blockiert in dieser Situation Nicht-Loopback-Anfragen. Das deutet auf ein ungesichertes Redis hin, das versehentlich im Internet erreichbar ist.","fix":"Die richtige Lösung ist, es abzusichern: Setze mit requirepass (oder ACL-Benutzern) ein starkes Passwort und lausche mit bind nur auf vertrauenswürdigen Schnittstellen. Ist Fernzugriff nötig, schütze ihn mit Passwort/TLS und beschränke Port 6379 per Firewall. protected-mode no darf nur vorübergehend in einem geschlossenen/sicheren Netz gesetzt werden; für aus dem Internet erreichbare Installationen wird es NIEMALS empfohlen.","tags":["protected mode","security","denied","requirepass","bind","auth"]},{"code":"ERR Can't execute '<cmd>': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context","title":"Command not allowed in subscribe context","severity":"caution","meaning":"Nachdem die Verbindung in den Pub/Sub-Subscribe-Modus gewechselt ist, wolltest du einen normalen Befehl (z. B. GET/SET) ausführen, der in diesem Modus nicht erlaubt ist. Eine Verbindung im Subscribe-Modus akzeptiert nur eine begrenzte Menge an Befehlen. In älteren Versionen lautete die Meldung \"ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context\".","cause":"Senden von Datenbefehlen über eine Verbindung, die mit SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE in den Subscribe-Modus gewechselt ist. Im RESP2-Protokoll erlaubt eine abonnierte Verbindung nur (P|S)SUBSCRIBE, (P|S)UNSUBSCRIBE, PING, QUIT und RESET. Der typische Fehler ist, eine einzige Verbindung für Pub/Sub und normale Arbeit gemeinsam zu nutzen.","fix":"Nutze eine eigene Verbindung für Pub/Sub und öffne eine weitere für normale Befehle (die meisten Client-Bibliotheken tun das automatisch). Sende UNSUBSCRIBE/PUNSUBSCRIBE, um den Subscribe-Modus zu verlassen; danach funktionieren normale Befehle. Im RESP3-Protokoll (HELLO 3) sind auf einer abonnierten Verbindung auch andere Befehle erlaubt; wechsle zu RESP3, wenn dein Treiber es unterstützt.","tags":["pubsub","subscribe","context","resp","connection","hello"]},{"code":"ERR SELECT is not allowed in cluster mode","title":"SELECT is not allowed in cluster mode","severity":"caution","meaning":"Im Redis-Cluster-Modus wolltest du mit SELECT zu einer anderen logischen Datenbank (DB-Index) wechseln. Der Cluster-Modus unterstützt nur Datenbank 0; mehrere DBs können nicht verwendet werden.","cause":"Eine Anwendung, die auf Standalone-Redis mit SELECT zwischen den DBs 0-15 wechselt, sendet nach dem Umzug auf Redis Cluster weiterhin denselben Befehl. Da die Cluster-Architektur Daten nach Slot verteilt, wird das Multi-DB-Konzept nicht unterstützt.","fix":"Entferne SELECT-Aufrufe im Cluster-Modus; es wird nur DB 0 verwendet. Brauchst du eine logische Trennung, nutze Key-Präfixe (Namespaces) (z. B. app1:key). Müssen bestimmte Keys gemeinsam verarbeitet werden, nutze ein Hash-Tag ({user123}), damit sie im selben Slot landen. Mache die Anwendungskonfiguration clusterfähig.","tags":["cluster","select","database","namespace","hash tag","slot"]},{"code":"ERR wrong number of arguments for command","title":"Wrong number of arguments for command","severity":"caution","meaning":"Ein Redis-Befehl wurde mit weniger oder mehr Argumenten als erwartet gesendet. Der Befehl wurde ohne Verarbeitung abgelehnt, weil seine Syntax nicht erfüllt war.","cause":"Fehlende/überzählige Parameter (z. B. nur SET key statt SET key value); als leer/undefined übergebene Variablen; Aufruf des Befehls mit falscher Argumentreihenfolge; oder fehlerhafte Array-/Argumentexpansion (Spread) in der Client-Bibliothek. Häufig bei dynamisch erzeugten Befehlen.","fix":"Prüfe die korrekte Signatur des Befehls (z. B. SET key value [options]); sieh in der offiziellen Befehlsreferenz nach. Prüfe im Client-Code, dass Argumente (vor allem aus Arrays) nicht leer sind oder fehlen. Erzeugst du Befehle aus Benutzereingaben, validiere vorher die Argumentanzahl. Probiere den Befehl manuell in redis-cli aus, um die erwartete Argumentanzahl zu bestätigen.","tags":["syntax","arguments","argument","command","syntax","cli"]},{"code":"ERR Background save already in progress","title":"Background save already in progress","severity":"caution","meaning":"Du wolltest eine neue Hintergrundsicherung (BGSAVE) oder ein AOF-Rewrite starten, aber es läuft bereits eine Hintergrundsicherung. Redis erlaubt keinen zweiten Sicherungsprozess zur selben Zeit.","cause":"Ein neues BGSAVE/BGREWRITEAOF wird ausgelöst, bevor das vorherige abgeschlossen ist; Monitoring-Skripte oder die Anwendung rufen wiederholt BGSAVE auf, während Fork/Sicherung bei einem großen Datenbestand lange dauert. Eine langsame Platte oder eine große RDB-Datei verlängert den Vorgang.","fix":"Warte, bis die laufende Sicherung abgeschlossen ist; prüfe ihren Status über die Felder rdb_bgsave_in_progress und aof_rewrite_in_progress in INFO persistence und den Zeitstempel LASTSAVE. Mache die Anwendungs-/Automatisierungslogik bedingt, damit sie vor Abschluss der vorherigen Sicherung kein neues BGSAVE auslöst. Dauern Sicherungen zu lange, untersuche Plattengeschwindigkeit und RDB-Größe.","tags":["bgsave","persistence","rdb","aof","background save","fork"]},{"code":"ERR bit offset is not an integer or out of range","title":"Bit offset is not an integer or out of range","severity":"caution","meaning":"Der an eine Bitoperation wie SETBIT/GETBIT/BITCOUNT übergebene Bit-Offset ist ungültig: Er ist keine Ganzzahl oder liegt außerhalb des erlaubten Bereichs. Redis kann an diesem Offset keine Bitoperation ausführen.","cause":"Übergabe eines nicht numerischen Werts als Offset; Verwendung eines negativen Offsets; oder Angabe eines sehr großen Offsets, der die erlaubte Obergrenze überschreitet (ein Bitstring kann bis zu 512 MB groß sein, also etwa 2^32 Bits). Entsteht durch Rechenfehler in bitmapbasierten Zähler-/Flag-Strukturen.","fix":"Stelle sicher, dass der Offset eine nicht negative Ganzzahl im erlaubten Bereich ist (0 bis ~2^32-1; Strings sind höchstens 512 MB groß). Prüfe Offset-Berechnungen (z. B. aus Benutzer-IDs abgeleitete) auf Überlauf/negative Werte. Werden sehr große Offsets benötigt, verteile die Daten auf mehrere Keys (Sharding).","tags":["bitmap","setbit","offset","integer","range","bit"]}]},"errors/elasticsearch-more3.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"node_not_connected_exception","title":"Node is not connected","severity":"danger","meaning":"Ein Knoten ist derzeit nicht mit einem anderen Knoten verbunden, mit dem er kommunizieren muss. Die Anfrage konnte nicht an den Zielknoten weitergeleitet werden, weil die clusterinterne Verbindung (Transport-Schicht) verloren ging.","cause":"Netzwerkausfall oder Paketverlust, Absturz/Neustart des Zielknotens, eine Firewall-/Portsperre (Standard-Transport-Port 9300), ein wegen GC-Pausen vorübergehend nicht reagierender Knoten oder ein DNS-/Hostauflösungsproblem. Kann bei Rolling Restarts vorübergehend auftreten.","fix":"Prüfe den Zustand von Cluster und Knoten: Ermittle mit GET _cluster/health und GET _cat/nodes?v, welcher Knoten fehlt. Prüfe den Netzwerkzugang zwischen den Knoten und die Firewall-Regeln für den Transport-Port (9300). Prüfe die Logs des betroffenen Knotens auf Abstürze, GC-Pausen oder OOM. Prüfe die Netzwerkstabilität und die Discovery-/Seed-Host-Konfiguration.","tags":["transport","node","connection","network","9300","cluster"]},{"code":"receive_timeout_transport_exception","title":"Request timed out waiting for a response from another node","severity":"danger","meaning":"Ein Knoten hat eine Anfrage an einen anderen Knoten gesendet, aber die Antwort kam nicht innerhalb der konfigurierten Zeit, und die Anfrage lief in einen Timeout. Der Aufruf zwischen den Knoten (Transport) konnte nicht abgeschlossen werden.","cause":"Der Zielknoten steht unter hoher Last, lange GC-Pausen, langsame Platte/CPU-Druck, Netzwerklatenz/Paketverlust oder der Zielknoten ist zu beschäftigt, um zu antworten. Häufig bei schweren Abfragen, großen Bulk-Operationen und Shard-Rebalancing.","fix":"Prüfe die Ressourcennutzung auf den betroffenen Knoten: Kontrolliere JVM-Heap, GC und Sättigung der Thread Pools mit GET _nodes/stats. Prüfe abgelehnte/wartende Tasks mit GET _cat/thread_pool?v. Miss die Netzwerklatenz. Reduziere die Last (kleinere Abfrage-/Bulk-Größen) und prüfe Heap und Hardwareressourcen. Hält es an, füge Knoten hinzu, um die Last zu verteilen.","tags":["transport","timeout","gc","load","network","thread pool"]},{"code":"routing_missing_exception","title":"Routing value is required for this document","severity":"caution","meaning":"Für den Zugriff auf das Dokument bzw. seine Indizierung ist ein Routing-Wert erforderlich, er wurde aber nicht angegeben. Ohne Routing konnte Elasticsearch nicht bestimmen, zu welchem Shard das Dokument gehört.","cause":"Das Index-Mapping definiert _routing required=true; eine Parent-Join-Beziehung (Parent/Child) wird verwendet und Child-Dokumente erfordern Routing; oder der Parameter routing fehlte in einer index/get/update/delete-Anfrage. Typische Ursache ist das Lesen von mit eigenem Routing geschriebenen Dokumenten ohne Angabe des Routings.","fix":"Gib den Routing-Wert in der Anfrage explizit an (z. B. ?routing=value bei index/get/update/delete-Aufrufen). Übergib bei Parent-Join die id des Parents als Routing für Child-Dokumente. Verwende für Lesen/Aktualisieren/Löschen dasselbe Routing wie beim Schreiben des Dokuments. Prüfe die _routing-Anforderung (required) im Mapping.","tags":["routing","shard","parent-child","join","mapping","index"]},{"code":"too_many_buckets_exception","title":"Trying to create too many buckets","severity":"caution","meaning":"Eine Aggregationsabfrage wollte mehr Buckets erzeugen als die erlaubte Obergrenze (search.max_buckets, Standard 65.536). Die Abfrage wurde zum Schutz des Speichers abgelehnt.","cause":"Aggregationen wie terms/date_histogram laufen auf Feldern hoher Kardinalität mit sehr großem size oder sehr feinem Intervall; verschachtelte Aggregationen vervielfachen die Bucket-Anzahl. Das Limit wird durchgesetzt, weil übermäßiges Erzeugen von Buckets den Speicher eines Knotens erschöpfen kann.","fix":"Reduziere die Zahl der erzeugten Buckets: Senke size bei terms-Aggregationen, vergrößere das date_histogram-Intervall, entferne unnötige verschachtelte Aggregationen. Werden viele Ergebnisse benötigt, blättere mit einer composite-Aggregation durch sie. Das Limit anzuheben sollte das letzte Mittel sein und birgt ein Speicherrisiko: search.max_buckets lässt sich mit PUT _cluster/settings ändern, tu das aber mit Bedacht.","tags":["aggregation","bucket","max_buckets","terms","composite","memory"]},{"code":"shard_lock_obtain_failed_exception","title":"Obtaining shard lock timed out","severity":"danger","meaning":"Elasticsearch konnte die lokale Verzeichnissperre (Shard Lock), die für die Arbeit an einem Shard nötig ist, nicht innerhalb des Timeouts erlangen. Der Shard konnte nicht gestartet oder neu zugewiesen werden.","cause":"Eine frühere Kopie desselben Shards (nach Close/Relocation) hat die Sperre noch nicht freigegeben; langsames/hängendes I/O; Plattenprobleme; oder veralteter Shard-Zustand, der nach einem schnellen Knotenneustart nicht bereinigt wurde. Meist vorübergehend; hält es an, bleibt der Shard UNASSIGNED.","fix":"Prüfe, warum der Shard nicht zugewiesen ist: GET _cluster/allocation/explain. In den meisten Fällen wird die Sperre kurz darauf freigegeben und der Cluster versucht es selbst erneut; löse bei Bedarf eine Neuzuweisung mit POST _cluster/reroute?retry_failed=true aus. Hält es an, prüfe die Logs des betroffenen Knotens auf I/O-/Plattenfehler und kontrolliere den Plattenzustand. Ein sauberer Neustart des problematischen Knotens kann die Sperre aufheben.","tags":["shard","lock","unassigned","allocation","io","reroute"]},{"code":"parsing_exception","title":"Failed to parse the search request / query DSL","severity":"caution","meaning":"Der gesendete Abfrage-Body (Query DSL) konnte nicht geparst werden. Die Abfrage konnte nicht laufen, weil die JSON-Struktur der Anfrage oder die Abfragesyntax nicht dem von Elasticsearch erwarteten Format entspricht.","cause":"Ungültiges JSON, falsch platzierte/unbekannte Abfrageschlüssel, ein Abfragetyp (z. B. bool, range) mit falscher Struktur, ein zwischen Versionen entfernter/geänderter Feldname oder eine überzählige/fehlende Klammer. Häufig bei handgeschriebenen oder dynamisch erzeugten Abfragen.","fix":"Prüfe den Abfrage-Body anhand des Zeilen-/Positionshinweises in der Fehlermeldung; prüfe, ob das JSON gültig ist. Vergleiche die Struktur der Query DSL mit der offiziellen Referenz (Platzierung von bool/must/filter usw.). Aktualisiere Syntax, die bei Versionsupgrades entfernt wurde. Vereinfache die Abfrage schrittweise, um den fehlerhaften Teil einzugrenzen; nutze nach Möglichkeit den Query Builder der Client-Bibliothek.","tags":["query dsl","parse","json","syntax","search","bool"]},{"code":"max_bytes_length_exceeded_exception","title":"Document contains at least one immense term that is too long","severity":"caution","meaning":"In einem zu indizierenden Dokument hat ein Term (insbesondere ein Wert eines keyword-/nicht analysierten Felds) die maximal erlaubte Bytelänge (32766 Bytes) überschritten. Lucene kann einen so langen Term nicht indizieren.","cause":"Ein sehr langer Textwert wird (als Ganzes) in ein keyword-Feld geschrieben; z. B. ein vollständiger Dokumentinhalt, base64-Daten oder eine lange URL/Kennung in einem keyword-Feld. Da keyword-Felder nicht analysiert werden, wird der Wert als einzelner Term indiziert und überschreitet das Limit.","fix":"Verwende für langen Text ein text-Feld (analysiert); keyword ist nur für kurze Werte gedacht, die exakt übereinstimmen müssen. Um den Wert zu begrenzen, nutze ignore_above im Mapping (längere Werte werden nicht indiziert, aber weiterhin gespeichert). Werden sehr lange Felder nicht benötigt, setze sie auf index:false. Bewerte das Felddesign (text vs. keyword) anhand des Datentyps neu.","tags":["keyword","term","ignore_above","lucene","mapping","length"]},{"code":"illegal_argument_exception_total_fields","title":"Limit of total fields has been exceeded","severity":"caution","meaning":"Die Gesamtzahl der Felder in einem Index-Mapping hat die erlaubte Obergrenze überschritten (index.mapping.total_fields.limit, Standard 1000). Die Indizierung schlägt fehl, weil keine neuen Felder hinzugefügt werden können.","cause":"Meist eine Mapping Explosion: Bei aktiviertem Dynamic Mapping fügen unstrukturierte JSON-Dokumente mit variablen Schlüsseln (z. B. Objekte, deren Schlüssel aus Benutzerdaten stammen) jedes neue Feld dem Mapping hinzu. Mit der Zeit wächst die Feldanzahl rasch und erreicht das Limit.","fix":"Um die Grundursache zu beheben, modelliere Daten mit variablen Schlüsseln mit einem flattened-Feld oder als Array von Schlüssel-Wert-Paaren statt als Objekte; schränke Dynamic Mapping ein (dynamic: false oder strict). Brauchst du wirklich mehr Felder, erhöhe das Limit mit Bedacht: PUT idx/_settings {\"index.mapping.total_fields.limit\": 2000}. Überwache Mapping Explosions, indem du die Feldanzahl mit GET idx/_mapping prüfst.","tags":["mapping","total_fields","dynamic mapping","flattened","limit","field"]},{"code":"verification_exception","title":"verification_exception: Found N problem(s) — Unknown column / Unknown index (ES|QL, SQL, EQL)","severity":"caution","meaning":"Die ES|QL- (_query), SQL- (_sql) oder EQL-Abfrage wurde syntaktisch geparst, ist aber an der semantischen Prüfung gescheitert: Der referenzierte Index, das Feld (die Spalte) oder die Funktion wurde nicht gefunden, oder die Typen sind inkompatibel. Der Fehlertext nennt die problematische Zeilen-/Spaltenposition (z. B. \"line 1:18: Unknown column [field]\"). Ein Fehler bei der Verifizierung eines Snapshot-Repositorys ist nicht dieser Code, sondern repository_verification_exception.","cause":"Ein falsch geschriebener oder nicht im Mapping vorhandener Feldname; ein Indexmuster, das nicht existiert oder zu keinem Index passt; eine nicht unterstützte Operation auf einem text-Feld (z. B. Gruppieren auf text statt auf dessen keyword-Unterfeld); ein Vergleich zwischen inkompatiblen Typen oder eine unbekannte Funktion.","fix":"Korrigiere die Abfrage anhand der Angabe Zeile:Spalte in der Fehlermeldung. Prüfe Feldnamen und Typen: GET idx/_mapping (bei SQL DESCRIBE idx / SHOW COLUMNS). Prüfe das Indexmuster mit GET _cat/indices. Nutze zum Gruppieren/Sortieren das keyword-Unterfeld (field.keyword); wende bei Typkonflikten eine explizite Konvertierung an (ES|QL: TO_STRING, TO_INTEGER usw.).","tags":["esql","sql","eql","verification","unknown column","query"]}]},"errors/oracle-more4.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-19804","title":"cannot reclaim N bytes disk space from M bytes limit","severity":"danger","meaning":"Oracle wollte in der Fast Recovery Area (FRA) Platz für eine neue Datei (Archivlog, Backup, Flashback-Log) schaffen, konnte den benötigten Platz aber nicht freigeben, weil das durch DB_RECOVERY_FILE_DEST_SIZE festgelegte logische Limit erreicht ist.","cause":"Die FRA-Größe (DB_RECOVERY_FILE_DEST_SIZE) ist eine von der physischen Platte unabhängige Obergrenze, und Archivlogs, RMAN-Backups oder Flashback-Logs haben sie gefüllt. Grundursache ist meist, dass Archivlogs nicht regelmäßig gesichert und gelöscht oder veraltete Backups nicht bereinigt werden.","fix":"Prüfe die FRA-Belegung: SELECT * FROM v$recovery_area_usage; und SELECT * FROM v$recovery_file_dest; Sichere Archivlogs auf ein Ziel außerhalb der FRA und lösche sie (RMAN: BACKUP ARCHIVELOG ALL FORMAT '/backup/arc_%U' DELETE INPUT;) oder bereinige nicht mehr benötigte Backups (RMAN: DELETE OBSOLETE;). Lösche bei Data Guard keine Archivlogs, die noch nicht auf dem Standby angewendet wurden. Erhöhe als dauerhafte Lösung DB_RECOVERY_FILE_DEST_SIZE, wenn Plattenplatz vorhanden ist. Wird Flashback Database nicht benötigt, erwäge, es zu deaktivieren.","tags":["fra","recovery area","archived log","rman","db_recovery_file_dest_size","disk"]},{"code":"ORA-00494","title":"enqueue held for too long by 'inst N, osid M'","severity":"danger","meaning":"Eine kritische Ressourcen-Enqueue (z. B. CF - Control File Enqueue) wurde von einem Prozess weit länger als erlaubt gehalten, und Oracle hat diesen Prozess beendet, um ein Hängen des Systems zu verhindern. Erscheint im alert.log zusammen mit einem Eintrag 'killing enqueue blocker'.","cause":"Ein Hintergrund- oder Benutzerprozess hat eine wichtige Enqueue wegen eines I/O-Engpasses, hohen CPU-Drucks oder eines Hängers im Betriebssystem (Swapping, langsame Platten) über die erlaubte Zeit hinaus gehalten; für die Control File (CF) Enqueue wird diese Zeit durch _controlfile_enqueue_timeout festgelegt (Standard 900 s). Versteckte Parameter (mit Unterstrich) sollten nur unter Anleitung von Oracle Support geändert werden. Meist ausgelöst durch Speicherlatenz oder langsame Prozesse wie LGWR/CKPT.","fix":"Prüfe das alert.log und die Trace-Datei des beendeten Prozesses, um festzustellen, welche Enqueue (meist das Control File) und welcher Hintergrundprozess verantwortlich ist. Die eigentliche Ursache ist meist Speicher-/I/O-Latenz: Prüfe Antwortzeiten der Platten, Multipath und den Zustand der Controller. Reduziere CPU-/Speicherdruck und Swap-Nutzung. Tritt es erneut auf, teile die Trace-Analyse mit Oracle Support.","tags":["enqueue","control file","i-o","storage","hang","background process"]},{"code":"ORA-04021","title":"timeout occurred while waiting to lock object","severity":"caution","meaning":"Beim Warten auf einen Library Cache Lock/Pin für ein Objekt (Package, Prozedur, Typ usw.) ist ein Timeout aufgetreten. Tritt meist beim Versuch auf, ein PL/SQL-Objekt zu kompilieren oder zu ändern, während es von einer anderen Session ausgeführt oder gesperrt wird.","cause":"Das zu ändernde/kompilierende Objekt wird in einer anderen Session aktiv ausgeführt oder ist durch eine andere DDL gesperrt, daher kann der exklusive Library Cache Lock nicht erlangt werden. Ein lang laufender Prozeduraufruf oder eine hängende Session hält möglicherweise die Sperre.","fix":"Finde die Session, die das Objekt sperrt: SELECT * FROM dba_kgllock WHERE kgllkhdl IN (SELECT p1raw FROM v$session WHERE event LIKE 'library cache%'); oder ermittle mit v$access und v$session die Session, die das Objekt nutzt. Wiederhole DDL/Kompilierung in einem Wartungsfenster mit geringer Last. Beende die Session bei Bedarf auf sichere Weise. Ein kurzes Anhalten der Anwendung erleichtert das Erlangen der Sperre.","tags":["library cache","lock","plsql","ddl","compile","timeout"]},{"code":"ORA-04061","title":"existing state of package/procedure has been invalidated","severity":"caution","meaning":"Der bestehende Zustand eines aufgerufenen PL/SQL-Objekts (Package, Prozedur, Funktion) wurde ungültig, weil sich ein Objekt geändert hat, von dem es abhängt. Es ist eine Variante des von ORA-04068 begleiteten Zustandsverlusts; der von der Session gehaltene Package-Zustand ist nicht mehr gültig.","cause":"Während eine Session ein Package nutzte, wurde DDL auf dieses Package oder seine Abhängigkeiten angewendet (Änderung der Tabellenstruktur, Neukompilierung, Änderung von Grants). Dadurch wurde der bestehende Package-Zustand der Session ungültig. Tritt in Umgebungen mit häufigen Deployments auf, wenn Schemaänderungen bei laufender Anwendung vorgenommen werden.","fix":"Derselbe Aufruf gelingt meist beim zweiten Versuch (da der Zustand zurückgesetzt wurde); fange diesen Fehler in der Anwendung ab und wiederhole sicher. Führe Schemaänderungen bei geringem Anwendungsverkehr bzw. in einem Wartungsfenster durch. Kompiliere ungültige Objekte gesammelt: EXEC DBMS_UTILITY.COMPILE_SCHEMA('APP_SCHEMA'); Designs, die den Package-Zustand (globale Variablen) minimieren, verringern diesen Effekt.","tags":["plsql","package","invalidate","ddl","state","recompile"]},{"code":"ORA-01466","title":"unable to read data - table definition has changed","severity":"caution","meaning":"Eine Abfrage, die Daten eines vergangenen Zeitpunkts liest (Flashback Query, AS OF oder Consistent Read), ist fehlgeschlagen, weil sich die Tabellendefinition (DDL) zwischen dem abgefragten Zeitpunkt und jetzt geändert hat. Oracle kann die Daten dieses Zeitpunkts mit der aktuellen Definition nicht sicher lesen.","cause":"Nach dem Zeitstempel, auf den die Flashback-/AS-OF-Abfrage zielt, wurde eine DDL auf die Tabelle angewendet (Spalte hinzugefügt/entfernt, Typänderung). Auch Uhrzeitabweichungen zwischen Client und Server oder Abfragen der jüngsten Vergangenheit (wenige Sekunden) können wegen der SCN-/Zeitauflösung diesen Fehler auslösen.","fix":"Führe die Flashback-Abfrage gegen einen Zeitpunkt/eine SCN vor der DDL-Änderung der Tabelle aus. Verwende nach Möglichkeit eine exakte SCN statt eines Zeitstempels (AS OF SCN). Gehe bei Abfragen der jüngsten Vergangenheit einige Sekunden zurück. Wird dauerhafter Zugriff auf historische Daten benötigt, erwäge statt DDL ein Design mit Versionierungs- oder Archivtabellen.","tags":["flashback","as of","ddl","consistent read","scn","query"]},{"code":"ORA-08176","title":"consistent read failure; rollback data not available","severity":"danger","meaning":"Beim Aufbau eines Consistent-Read-Abbilds stieß eine Abfrage auf Blöcke, die durch eine Operation ohne Undo-Erzeugung geändert wurden; da für diese Blöcke keine Undo-/Rollback-Daten existieren, konnte das konsistente Abbild nicht aufgebaut werden. Anders als bei ORA-01555 wurde das Undo nie erzeugt, statt überschrieben zu werden. (Der Fehler zur Serializable-Isolation ist ein eigener Code: ORA-08177.)","cause":"Während die Abfrage läuft, wird auf demselben Objekt eine Operation ohne Undo-Erzeugung ausgeführt: Direct-Path-Loads (INSERT /*+ APPEND */, SQL*Loader DIRECT=TRUE), CREATE INDEX / Index-Rebuild oder Discrete Transactions.","fix":"Führe die Abfrage erneut aus, nachdem der Direct-Path-Load oder die Indexoperation abgeschlossen ist (Retry). Plane lange Reports und Direct-Path-Load-/Indexwartungsjobs auf demselben Objekt nicht im selben Zeitfenster. Tritt es erneut auf, ermittle den kollidierenden Job mit dem alert.log und ASH und trenne die Zeitpläne.","tags":["undo","consistent read","direct path","append","create index","retry"]},{"code":"ORA-00020","title":"maximum number of processes (N) exceeded","severity":"danger","meaning":"Die Prozessanzahl der Instanz hat die durch den Initialisierungsparameter PROCESSES festgelegte Obergrenze erreicht, und es kann keine neue Verbindung bzw. kein neuer Prozess erzeugt werden. Neue Sessions können sich nicht verbinden; selbst ein Administrator kann sich unter Umständen nicht verbinden.","cause":"Ein übergroßer Connection Pool der Anwendung, nie geschlossene Verbindungen (Leaks), mehr gleichzeitige Benutzer als erwartet oder ein zu niedrig gesetzter Parameter PROCESSES führen zur Überschreitung des Limits. Manchmal verbrauchen auch hängende Hintergrundjobs oder Parallel-Query-Prozesse Prozesse.","fix":"Du kannst dich mit sqlplus -prelim / as sysdba verbinden, aber diese Verbindung kann kein SQL ausführen (v$session ist nicht abfragbar); sie ist nur für oradebug (z. B. hanganalyze) und SHUTDOWN ABORT nutzbar. Um Plätze freizugeben, beende vorsichtig inaktive Serverprozesse der Anwendung auf Betriebssystemebene (ps -ef | grep 'LOCAL=NO'); finde dann mit einer normalen SYSDBA-Verbindung über v$session/v$process verwaiste Sessions und schließe sie mit ALTER SYSTEM KILL SESSION. Erhöhe als dauerhafte Lösung PROCESSES und starte die Instanz neu (ist SESSIONS explizit im spfile gesetzt, wird es nicht neu abgeleitet; passe es ebenfalls an). Korrigiere auf Anwendungsseite das Maximum des Connection Pools und die Bereinigung inaktiver Verbindungen.","tags":["processes","process","connection","limit","pool","init parameter"]},{"code":"ORA-01113","title":"file N needs media recovery","severity":"danger","meaning":"Eine Datendatei wird online gesetzt oder die Datenbank geöffnet, aber die Datei ist inkonsistent; vor dem Fortfahren muss zuerst Media Recovery angewendet werden. Die Datei steht auf einer älteren/inkonsistenten SCN, als das Control File erwartet.","cause":"Die Datendatei wurde aus einem alten Backup wiederhergestellt, Redo wurde nicht angewendet, während sie offline war, oder die Datei ist nach einem Instanz-/Speicherabsturz nicht aktuell. Typischerweise verursacht durch ein ausgelassenes Recover nach einem Restore oder eine lange offline gebliebene Datei.","fix":"Stelle die Datei oder die Datenbank wieder her: RECOVER DATAFILE N; oder RECOVER DATABASE; dann ALTER DATABASE DATAFILE N ONLINE; oder ALTER DATABASE OPEN; Stelle sicher, dass die benötigten Archivlogs zugänglich sind. Mit RMAN: nach einem RESTORE bei Bedarf RECOVER DATAFILE N; ausführen. Verfolge im alert.log, welche Logs angefordert werden.","tags":["media recovery","datafile","redo","restore","recover","scn"]},{"code":"ORA-02393","title":"exceeded call limit on CPU usage","severity":"caution","meaning":"Die für einen einzelnen Aufruf erlaubte CPU-Zeit (CPU_PER_CALL), festgelegt im der Session zugewiesenen Ressourcenprofil, wurde überschritten, und Oracle hat diesen Aufruf beendet. Es ist eine Schutzmaßnahme zur Ressourcenbegrenzung.","cause":"Im Profil des Benutzers ist ein CPU_PER_CALL-Limit definiert und RESOURCE_LIMIT=TRUE; ein einzelner SQL-/PLSQL-Aufruf hat diese CPU-Schwelle überschritten. Meist ausgelöst durch eine ineffiziente/unkontrollierte Abfrage, die weit mehr CPU verbraucht als erwartet.","fix":"Optimiere die Abfrage (passender Index, richtiger Plan, Vermeidung unnötiger Full Table Scans), sodass der CPU-Verbrauch unter das Limit fällt. Ist die fachliche Anforderung berechtigt, erhöhe CPU_PER_CALL im Profil des betreffenden Benutzers oder setze es auf UNLIMITED: ALTER PROFILE ... LIMIT CPU_PER_CALL ...; Erwäge, Ressourcen statt mit Profilen flexibler mit Database Resource Manager zu verwalten.","tags":["profile","cpu_per_call","resource_limit","resource","query","optimization"]},{"code":"ORA-12801","title":"error signaled in parallel query server Pnnn","severity":"danger","meaning":"Bei einer parallel laufenden Abfrage ist einer der Parallel Execution Server (Parallel Query Slaves) auf einen Fehler gestoßen, und dieser Fehler wurde an den Query Coordinator weitergereicht. ORA-12801 ist eine Hülle; der eigentliche Fehler steht in der darauffolgenden zweiten Zeile.","cause":"Ein Slave-Prozess ist während einer parallelen Operation auf einen echten Fehler gestoßen: unzureichender TEMP-Tablespace (ORA-01652), erschöpfter PGA-Speicher (ORA-04030), Datenkorruption, Limits für parallele Prozesse oder ein anderer zugrunde liegender SQL-Fehler. ORA-12801 meldet nur, dass er in einem parallelen Kontext auftrat.","fix":"Konzentriere dich auf den eigentlichen Fehlercode direkt nach ORA-12801 und behebe ihn (z. B. TEMP vergrößern, PGA-Tuning, beschädigten Block reparieren). Grenze den eigentlichen Fehler bei Bedarf ein, indem du die Abfrage seriell ausführst, PARALLEL-Hints entfernst oder den Parallelitätsgrad senkst. Prüfe PARALLEL_MAX_SERVERS und die Ressourcennutzung.","tags":["parallel","parallel query","slave","temp","pga","wrapper"]}]},"errors/postgres-more4.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"23P01","title":"exclusion_violation","severity":"danger","meaning":"Ein EXCLUDE-Constraint (Exclusion Constraint) wurde verletzt. Dieser Constraint-Typ verhindert, dass zwei Zeilen gemäß bestimmten Operatoren (z. B. Bereichsüberlappung &&) kollidieren; die eingefügte/aktualisierte Zeile wurde abgelehnt, weil sie mit einer vorhandenen Zeile kollidiert.","cause":"Typischerweise ist ein GiST-basierter EXCLUDE-Constraint definiert (z. B. Verbot zeitlicher Überlappungen mit tstzrange && für Termin-/Raumbuchungen), und die neue Zeile hat einen Bereich/Wert, der eine vorhandene Zeile überlappt. Kann auch auftreten, wenn zwei parallele Transaktionen überlappende Bereiche einfügen.","fix":"Prüfe vor dem Einfügen per Abfrage auf Überlappung oder fange den Fehler in der Anwendung ab und zeige dem Benutzer eine aussagekräftige Meldung. Frage den betreffenden Bereich bzw. die Bedingung ab, um die kollidierende vorhandene Zeile zu finden. Erlaubt die Geschäftsregel Überlappungen, lockere den Constraint; andernfalls korrigiere die Eingabe. Wiederhole die Transaktion bei Parallelität sicher, um sie zu serialisieren.","tags":["exclude","constraint violation","gist","range","overlap","constraint"]},{"code":"23001","title":"restrict_violation","severity":"danger","meaning":"Der SQL-Standardcode restrict_violation. Wichtig: PostgreSQL meldet die Verletzung eines mit ON DELETE/ON UPDATE RESTRICT oder NO ACTION definierten Foreign Keys nicht mit diesem Code, sondern mit 23503 (foreign_key_violation); in der Praxis siehst du diesen Code vom PostgreSQL-Kern nicht.","cause":"Wird eine Elternzeile mit abhängigen Kindzeilen gelöscht oder ihr Schlüssel geändert, liefert PostgreSQL 23503. 23001 taucht meist in aus einer anderen Datenbank portiertem Anwendungscode oder als eigener Fehler einer Extension auf. Anders als NO ACTION kann RESTRICT die Prüfung nicht ans Ende der Transaktion verschieben und schlägt sofort fehl.","fix":"Lösche bzw. aktualisiere zuerst die abhängigen Kindzeilen oder verschiebe sie zu einem anderen Elternteil; verarbeite dann die Elternzeile. Erlaubt die Geschäftsregel es, erwäge, den Foreign Key mit ON DELETE CASCADE oder SET NULL neu zu definieren. Halte bei Massenlöschungen die richtige Löschreihenfolge ein (zuerst Kind, dann Eltern).","tags":["foreign key","restrict","fk","delete","reference","constraint"]},{"code":"42P17","title":"invalid_object_definition","severity":"danger","meaning":"Die Definition eines Objekts (Funktion, Collation, Trigger, Rule usw.) ist ungültig. PostgreSQL zeigt an, dass die angegebene Definition in sich inkonsistent oder nicht akzeptabel ist; das Objekt kann so nicht angelegt/geändert werden.","cause":"Häufige Ursachen: widersprüchliche Attributkombinationen einer Funktion (z. B. als IMMUTABLE markiert, verhält sich aber volatil), eine ungültige Collation-/Zeichensatzdefinition oder eine ungeeignete Trigger-/Rule-Definition. Auch inkonsistente Optionen bei Row-Level Security (RLS) und manche DDL können den Fehler auslösen.","fix":"Lies die Fehlerdetails (DETAIL/HINT), um festzustellen, welcher Teil der Definition abgelehnt wurde. Bringe die Volatilitätskennzeichnung von Funktionen mit dem tatsächlichen Verhalten in Einklang (VOLATILE/STABLE/IMMUTABLE). Prüfe, ob der Collation-Name in pg_collation existiert. Korrigiere die Definition gemäß der dokumentierten Syntax/Regeln und führe sie erneut aus.","tags":["function","collation","definition","volatility","ddl","object"]},{"code":"42710","title":"duplicate_object","severity":"caution","meaning":"Das anzulegende Objekt (Rolle, Typ, Constraint, Extension, Schema, Indexname usw.) existiert bereits. PostgreSQL erlaubt kein zweites Objekt mit demselben Namen bzw. derselben Definition.","cause":"Ein Befehl wie CREATE ROLE, CREATE TYPE, CREATE EXTENSION oder ADD CONSTRAINT lief, während bereits ein Objekt mit demselben Namen existierte. Meist verursacht durch ein zweimal ausgeführtes Skript, nicht idempotente Migrationen oder zwei parallele Sessions, die dasselbe Objekt anlegen wollen.","fix":"Prüfe, ob das Objekt existiert (pg_roles für Rollen, pg_type für Typen, pg_extension für Extensions). Schreibe Migrationen idempotent: Nutze, wo verfügbar, CREATE ... IF NOT EXISTS oder führe zuerst DROP ... IF EXISTS aus. Benenne bei einem Constraint-Konflikt den vorhandenen Constraint um oder lösche ihn und lege ihn neu an.","tags":["duplicate","role","type","extension","constraint","migration"]},{"code":"22008","title":"datetime_field_overflow","severity":"caution","meaning":"Beim Berechnen oder Parsen eines Datums-/Zeitwerts lag eines der Felder außerhalb seines gültigen Bereichs. Zum Beispiel entstand eine unmögliche Komponente wie Monat 13 oder Tag 32 oder ein Ergebnis jenseits des unterstützten Zeitbereichs.","cause":"Eine ungültige Datumseingabe (z. B. '2026-02-30'), eine Komponente außerhalb des Bereichs oder eine INTERVAL-Addition, die den unterstützten Datumsbereich überschreitet. Typische Ursache ist das Parsen mit falschem Format/Locale oder das Einfügen nicht validierter Benutzereingaben.","fix":"Validiere Datumseingaben in der Anwendung vor dem Einfügen und sende sie im richtigen Format (vorzugsweise ISO 8601: YYYY-MM-DD). Passe beim Parsen mit TO_DATE/TO_TIMESTAMP die Formatmaske an die Eingabe an. Halte Rechenergebnisse im unterstützten Datumsbereich; unterziehe verdächtige Werte einer Bereichsprüfung.","tags":["date","time","overflow","interval","validation","format"]},{"code":"22026","title":"string_data_length_mismatch","severity":"caution","meaning":"Die Länge eines Bitstrings entspricht nicht der von der Operation geforderten festen Länge. In PostgreSQL tritt das meist auf, wenn ein Bitstring anderer Länge in einen bit(n)-Typ fester Länge geschrieben wird oder bitweises AND/OR/XOR auf Bitstrings unterschiedlicher Länge angewendet wird.","cause":"Schreiben eines Bitstrings, der kürzer/länger als n ist, in eine bit(n)-Spalte (z. B. B'101' in eine bit(4)-Spalte: 'bit string length 3 does not match type bit(4)'), oder Anwenden von &, |, # auf Bitstrings unterschiedlicher Länge ('cannot AND bit strings of different sizes'). Die Ursache liegt nicht in Text oder Zeichenkodierung; ein zu langer Wert für char(n)/varchar(n) löst stattdessen 22001 aus.","fix":"Bringe den Wert auf die Ziellänge: Nutze einen expliziten Cast (B'101'::bit(4) füllt rechts mit Nullen auf und schneidet Überzähliges ab) oder definiere die Spalte als bit varying(n), wenn die Länge variieren darf. Caste die Operanden vor bitweisen Operationen auf dieselbe Länge; prüfe die Länge mit length().","tags":["bit","length","bit string","varbit","conversion","validation"]},{"code":"2201B","title":"invalid_regular_expression","severity":"caution","meaning":"Das an eine Operation mit regulären Ausdrücken (~, ~*, SIMILAR TO, regexp_matches, substring usw.) übergebene Muster ist ungültig. PostgreSQL konnte das Muster nicht kompilieren, weil seine Syntax falsch ist.","cause":"Ein Regex-Muster mit unausgeglichenen Klammern, ungültigen Zeichenklassen, fehlerhaften Escape-Sequenzen oder einem nicht unterstützten Konstrukt. Eine häufige Ursache ist die direkte Verwendung von Benutzereingaben als Muster, ohne ihre Sonderzeichen zu escapen.","fix":"Korrigiere das Regex-Muster gemäß POSIX-/ARE-Syntax; prüfe runde und eckige Klammern sowie Escape-Sequenzen. Suchst du Benutzereingaben als wörtlichen Text statt als Muster: PostgreSQL hat keine eingebaute Funktion regexp_quote, also escape die Sonderzeichen per regexp_replace mit einem vorangestellten Backslash oder verzichte ganz auf Regex und nutze strpos()/position() oder LIKE ... ESCAPE. Prüfe das Muster, indem du es in einem separaten Tool testest.","tags":["regex","regular expression","pattern","similar to","escape","syntax"]},{"code":"44000","title":"with_check_option_violation","severity":"danger","meaning":"Ein INSERT/UPDATE über eine WITH CHECK OPTION definierte View wurde abgelehnt, weil die resultierende Zeile die WHERE-Bedingung der View nicht mehr erfüllt. Die View verhindert das Schreiben von Zeilen, die aus ihrem eigenen Filter fallen würden.","cause":"Eine aktualisierbare View ist WITH CHECK OPTION definiert, und der Schreibvorgang verschiebt die Zeile aus der definierenden Bedingung der View (z. B. eine Zeile wird auf 'inactive' gesetzt, während die View nach status='active' filtert). Eine ähnliche Prüfung kann in Policies der Row-Level Security (RLS) greifen.","fix":"Stelle sicher, dass die geschriebenen Werte die WHERE-Bedingung der View erfüllen, oder führe die Operation direkt auf der Basistabelle statt auf der View aus (sofern die Rechte es erlauben). Erlaubt die Geschäftsregel, dass die Zeile den Filter verlässt, definiere die View ohne WITH CHECK OPTION neu. Prüfe bei RLS den WITH CHECK-Ausdruck der Policy.","tags":["view","check option","updatable view","rls","insert","update"]},{"code":"40002","title":"transaction_integrity_constraint_violation","severity":"danger","meaning":"Der SQL-Standardcode für die Verletzung eines am Transaktionsende geprüften Integritäts-Constraints beim COMMIT. Wichtig: PostgreSQL erzeugt diesen Code in der Praxis nicht; es liefert Verletzungen verzögerter (DEFERRED) Constraints beim COMMIT mit dem eigenen Code des Constraints: 23503 für Foreign Keys, 23505 für Eindeutigkeit.","cause":"Ein DEFERRABLE INITIALLY DEFERRED definierter Foreign-Key- oder Unique-Constraint wurde in Zwischenschritten der Transaktion vorübergehend verletzt und bis zum COMMIT nicht korrigiert; in PostgreSQL wird das als 23503/23505 gemeldet. Zum Beispiel führt das Einfügen sich gegenseitig referenzierender Zeilen in falscher Reihenfolge ohne anschließende Korrektur dazu.","fix":"Korrigiere die Transaktionslogik so, dass alle verzögerten Constraints vor dem COMMIT erfüllt sind (fehlende Referenzen ergänzen, Konflikte auflösen). Prüfe den Constraint bei Bedarf früh innerhalb der Transaktion mit SET CONSTRAINTS ... IMMEDIATE, um den problematischen Schritt eher zu erkennen. Ordne Inserts/Updates bei gegenseitigen Referenzen neu.","tags":["deferred","constraint violation","commit","foreign key","transaction","constraint"]}]},"errors/mysql-more4.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1055","title":"Expression not in GROUP BY (only_full_group_by)","severity":"caution","meaning":"Eine Spalte, die weder in GROUP BY steht noch in eine Aggregatfunktion eingeschlossen ist, wurde in der SELECT-Liste oder in HAVING/ORDER BY verwendet. Da der SQL-Modus ONLY_FULL_GROUP_BY aktiviert ist, lehnt MySQL diesen nichtdeterministischen Ausdruck ab.","cause":"Die Abfrage wählt direkt eine andere als die gruppierten Spalten aus, diese Spalte hat aber möglicherweise keinen einzelnen Wert pro Gruppe. ONLY_FULL_GROUP_BY ist ab MySQL 5.7 standardmäßig aktiviert; Abfragen, die auf dem alten, nachsichtigen GROUP-BY-Verhalten beruhen, lösen diesen Fehler aus.","fix":"Nimm die Spalte entweder in die GROUP-BY-Liste auf oder schließe sie in eine Aggregatfunktion ein (MAX(), MIN(), ANY_VALUE()). Weißt du, dass der Wert innerhalb der Gruppe wirklich eindeutig ist, unterdrückt ANY_VALUE() die Mehrdeutigkeit bewusst. Den Modus zu deaktivieren (ONLY_FULL_GROUP_BY aus sql_mode entfernen), statt die Abfrage zu korrigieren, bringt das Risiko falscher Ergebnisse zurück; vermeide das.","tags":["group by","only_full_group_by","sql_mode","any_value","aggregate","query"]},{"code":"1267","title":"Illegal mix of collations","severity":"caution","meaning":"Bei einer Vergleichs- oder Verknüpfungsoperation (JOIN, WHERE, UNION, CONCAT) wurden zwei Strings mit unterschiedlichen Collations kombiniert, und MySQL konnte nicht bestimmen, welche Collation-Regeln anzuwenden sind.","cause":"Meist der Vergleich von Spalten mit unterschiedlichen Zeichensätzen/Collations (z. B. utf8mb4_general_ci vs. utf8mb4_unicode_ci oder latin1 vs. utf8mb4) oder eine Abweichung zwischen Spalte und Verbindungs-Collation. Häufig, wenn Tabellen/Datenbanken mit unterschiedlichen Standardwerten angelegt wurden.","fix":"Bringe beide Seiten des Vergleichs auf dieselbe Collation: Gib mit COLLATE eine explizite Collation an (z. B. col1 = col2 COLLATE utf8mb4_unicode_ci) oder nutze CONVERT(... USING ...). Konvertiere als dauerhafte Lösung die betroffenen Spalten/Tabellen in einen einheitlichen Zeichensatz und eine einheitliche Collation. Gleiche auch die Verbindungs-Collation (collation_connection) an.","tags":["collation","collation mismatch","character set","join","utf8mb4","convert"]},{"code":"1030","title":"Got error N from storage engine","severity":"danger","meaning":"Die Storage Engine (InnoDB/MyISAM) konnte eine Operation nicht abschließen und hat einen Low-Level-Fehlercode zurückgegeben. Das ist eine generische Meldung eines Platten-, Dateisystem- oder Engine-internen Problems unterhalb der SQL-Schicht. Der Untercode in Klammern/in der Meldung zeigt die eigentliche Ursache.","cause":"Häufige Ursachen: voller Platten-/Temp-Platz (z. B. errno 28 - No space left on device), Probleme mit Dateiberechtigungen, beschädigte Tabellendateien, Probleme mit InnoDB-Tablespaces oder I/O-Fehler auf Betriebssystemebene. Auch Sort-/Gruppierungsabfragen, die große temporäre Tabellen erzeugen, können den Fehler auslösen.","fix":"Entschlüssele den Unterfehlercode (unter Linux zeigt perror N seine Bedeutung, z. B. perror 28). Prüfe die Plattenbelegung und den Platz in tmpdir; schaffe Platz. Prüfe Dateieigentümer/-berechtigungen. Sieh dir bei InnoDB die Details im Fehlerlog an; bei Verdacht auf Korruption führe CHECK TABLE aus und stelle bei Bedarf wieder her. Optimiere Abfragen, die riesige temporäre Ergebnisse erzeugen.","tags":["storage engine","innodb","disk","errno","tmpdir","i-o"]},{"code":"1197","title":"Multi-statement transaction required more than 'max_binlog_cache_size'","severity":"danger","meaning":"Die von einer einzelnen Transaktion erzeugten Binlog-Daten haben das durch max_binlog_cache_size festgelegte Speicher-/Cache-Limit überschritten. Die Transaktion passte nicht ins Binary Log, ist daher fehlgeschlagen und wurde zurückgerollt.","cause":"Eine sehr große Transaktion (ein einzelnes INSERT/UPDATE/DELETE von Millionen Zeilen oder ein riesiger Bulk Load) hat den Binlog-Cache gefüllt. Bei aktiviertem Binary Logging (Replikation/Point-in-Time-Recovery) können große Transaktionen dieses Limit leicht überschreiten.","fix":"Teile die große Transaktion in kleinere Batches auf; führe nach jedem Batch COMMIT aus. Sind wirklich große Transaktionen unvermeidbar, erhöhe max_binlog_cache_size (und bei Bedarf binlog_cache_size). Bei ETL/Bulk Loads verhindert ein Laden in Blöcken mit zwischenzeitlichen COMMITs sowohl diesen Fehler als auch lange gehaltene Sperren.","tags":["binlog","max_binlog_cache_size","transaction","batch","replication","bulk"]},{"code":"1785","title":"Statement violates GTID consistency","severity":"danger","meaning":"Bei aktivierter GTID-basierter Replikation (Global Transaction Identifier) verletzt die ausgeführte Anweisung die GTID-Konsistenzregeln. Der GTID-Modus erlaubt bestimmte Anweisungstypen nicht, die sich nicht sicher einer einzelnen GTID zuordnen lassen.","cause":"1785 (ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE) tritt speziell auf, wenn nicht transaktionale Tabellen (z. B. MyISAM) in derselben Transaktion wie transaktionale Tabellen (InnoDB) oder in einer Multi-Statement-Transaktion ohne Autocommit aktualisiert werden. In älteren Versionen verletzen auch CREATE TABLE ... SELECT (vor 8.0.21; ab 8.0.21 für Engines mit atomarer DDL erlaubt) und CREATE/DROP TEMPORARY TABLE innerhalb einer Transaktion (vor 8.0.13; ab 8.0.13 erlaubt, wenn binlog_format ROW/MIXED ist) die GTID-Konsistenz (diese werden mit eigenen Fehlercodes gemeldet). Bei enforce_gtid_consistency=ON werden sie abgelehnt.","fix":"Mische Tabellen nicht transaktionaler Engines nicht mit transaktionalen Tabellen in derselben Transaktion; aktualisiere MyISAM-Tabellen mit einzelnen Autocommit-Anweisungen oder migriere alle Tabellen nach InnoDB: ALTER TABLE my_table ENGINE=InnoDB; Teile in älteren Versionen (vor 8.0.21) CREATE TABLE ... SELECT in zwei Schritte auf (CREATE TABLE, dann INSERT ... SELECT); lege temporäre Tabellen vor 8.0.13 außerhalb einer expliziten Transaktion an und lösche sie dort.","tags":["gtid","replication","consistency","temporary table","myisam","innodb"]},{"code":"3024","title":"Query execution was interrupted, maximum statement execution time exceeded","severity":"caution","meaning":"Eine SELECT-Abfrage wurde von MySQL unterbrochen, weil sie die für sie festgelegte maximale Ausführungszeit überschritten hat (max_execution_time / Hint MAX_EXECUTION_TIME). Das ist eine Timeout-Schutzmaßnahme, die verhindert, dass lang laufende Abfragen Ressourcen binden.","cause":"Die Abfrage lief länger als der Session-/globale Wert max_execution_time oder der Optimizer-Hint MAX_EXECUTION_TIME(N) beim SELECT. Meist verursacht durch einen ineffizienten Plan, einen fehlenden Index, einen großen Scan oder Verlangsamungen bei hoher Parallelität. (Gilt nur für Read-only-SELECTs.)","fix":"Analysiere die Abfrage mit EXPLAIN und optimiere sie (passende Indizes, selektivere Prädikate, weniger Full Table Scans). Muss die Abfrage berechtigterweise lange laufen, erhöhe den Hint MAX_EXECUTION_TIME für dieses SELECT oder max_execution_time der Session. Führe schwere Analyseabfragen offline/auf einer Replica aus.","tags":["timeout","max_execution_time","query","explain","index","select"]},{"code":"1298","title":"Unknown or incorrect time zone","severity":"caution","meaning":"Der angegebene Zeitzonenname wurde von MySQL nicht erkannt. Ein CONVERT_TZ()-Aufruf, die Einstellung time_zone oder eine Operation, die sich auf eine benannte Zeitzone bezieht (z. B. 'Europe/Istanbul'), ist fehlgeschlagen, weil diese Zeitzone auf dem Server nicht definiert ist.","cause":"Damit MySQL benannte Zeitzonen verwenden kann, müssen die Zeitzonentabellen im Schema mysql (time_zone, time_zone_name usw.) geladen sein. Sind diese Tabellen leer, funktionieren nur numerische Offsets ('+03:00'); benannte Zonen sind unbekannt und dieser Fehler wird ausgelöst.","fix":"Lade die Zeitzonentabellen. Importiere unter Linux die zoneinfo-Daten des Systems mit mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql; starte dann den Server neu oder führe FLUSH TABLES aus. Ist das Laden benannter Zonen nicht möglich, nutze einen numerischen Offset ('+03:00'). Prüfe die Schreibweise des Namens (Groß-/Kleinschreibung, 'Europe/Istanbul').","tags":["time zone","timezone","convert_tz","zoneinfo","mysql_tzinfo_to_sql","offset"]},{"code":"1418","title":"Function has none of DETERMINISTIC, NO SQL, or READS SQL DATA (Binary Logging aktiviert)","severity":"caution","meaning":"Eine Stored Function sollte angelegt werden, aber Binary Logging ist aktiviert und die Funktion ist mit keiner der Eigenschaften für Determinismus/Datenzugriff deklariert. MySQL lehnt eine solche Funktion ab, weil sie aus Sicht der Replikationssicherheit mehrdeutig ist.","cause":"Die Funktion wurde ohne die Eigenschaften DETERMINISTIC, NO SQL oder READS SQL DATA definiert, während log_bin auf ON und log_bin_trust_function_creators=OFF steht. Ohne diese Eigenschaften kann MySQL nicht sicher sein, dass die Funktion auf der Replica dasselbe Ergebnis liefert.","fix":"Deklariere das Verhalten der Funktion mit den richtigen Eigenschaften: Füge DETERMINISTIC hinzu, wenn sie wirklich deterministisch ist, NO SQL, wenn sie nicht auf Daten zugreift, READS SQL DATA, wenn sie nur liest. Bist du sicher, dass die Funktionen unbedenklich sind, kannst du log_bin_trust_function_creators=1 als zentrale Richtlinie setzen; am sichersten ist es jedoch, die Eigenschaft korrekt zu deklarieren. Stelle sicher, dass die Eigenschaft zum tatsächlichen Verhalten der Funktion passt.","tags":["stored function","deterministic","binary log","replication","log_bin_trust_function_creators","reads sql data"]},{"code":"1231","title":"Variable can't be set to the value of ...","severity":"caution","meaning":"Der einer System-/Session-Variable (mit SET) zugewiesene Wert ist ungültig. Der Wert wurde abgelehnt, weil er außerhalb der erlaubten Menge liegt, vom falschen Typ ist oder ein ungeeignetes Format hat.","cause":"Häufige Beispiele: Zuweisung eines ungültigen/falsch geschriebenen Modusnamens an sql_mode, Angabe einer nicht definierten Option für eine Enum-Variable oder Zuweisung von NULL an eine Variable, die NULL nicht akzeptiert. Auch numerische Werte in falscher Einheit/falschem Bereich können den Fehler auslösen.","fix":"Prüfe die gültigen Werte und den Typ, den die Variable akzeptiert; schreibe bei Variablen vom Typ ENUM/SET (z. B. sql_mode, ein SET) Wert-/Modusnamen exakt und trenne mehrere Werte durch Kommas. Prüfe den gültigen Wertebereich in der Dokumentation. Lies bei Listen wie sql_mode den aktuellen Wert (SELECT @@sql_mode) und füge darauf aufbauend sicher Einträge hinzu bzw. entferne sie.","tags":["set","variable","sql_mode","enum","value","invalid"]}]},"errors/mssql-more4.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"8645","title":"A time out occurred while waiting for memory resources to execute the query","severity":"danger","meaning":"Eine Abfrage lief beim Warten am Resource Semaphore auf den Memory Grant, den sie zur Ausführung benötigt, in einen Timeout. Da Speicherdruck die Zuteilung eines Grants verhindert hat, wurde die Operation abgebrochen.","cause":"Gleichzeitige speicherintensive Abfragen (große Sorts, Hash Joins, Hash-Aggregate) haben den gesamten Abfragespeicherpool belegt; neue Abfragen haben sich am Semaphore angestellt und liefen in einen Timeout. Grundursache sind überhöhte Schätzungen für Memory Grants (veraltete Statistiken, schlechte Pläne) oder zu wenig Serverspeicher.","fix":"Ermittle mit sys.dm_exec_query_memory_grants und den Resource-Semaphore-DMVs Abfragen, die auf Speicher warten oder ihn halten. Aktualisiere Statistiken und korrigiere Pläne, die überhöhte Memory Grants verursachen (passende Indizes, bessere Schätzungen). Reduziere die Parallelität speicherintensiver Abfragen; begrenze Memory Grants bei Bedarf mit Resource Governor. Prüfe die Speicherkonfiguration des Servers (max server memory).","tags":["memory grant","resource semaphore","memory","timeout","hash join","statistics"]},{"code":"8651","title":"Could not perform the operation because the requested memory grant was not available","severity":"danger","meaning":"Der von der Abfrage benötigte Memory Grant war nicht verfügbar, und die Operation konnte nicht ausgeführt werden. Anders als bei 8645 handelt es sich nicht um ein Warten, sondern um das schlichte Scheitern, den angeforderten großen Speicherblock bereitzustellen; das deutet meist auf starken Speicherdruck hin.","cause":"Der verfügbare Abfragespeicher des Servers kann den von einem einzelnen Operator angeforderten Grant nicht erfüllen (z. B. ein sehr großer Sort/Hash). Ursache können übermäßig hohe Speicherschätzungen, ein niedriges max server memory, externer Speicherdruck oder ein niedriges Resource-Governor-Limit sein, REQUEST_MAX_MEMORY_GRANT_PERCENT (Workload Group) oder MAX_MEMORY_PERCENT (Resource Pool).","fix":"Optimiere Abfragen, die Memory Grants aufblähen (Statistiken aktualisieren, passende Indizierung, Abfrage aufteilen). Überwache gesamten und verfügbaren Speicher; prüfe die Einstellung max server memory und den Speicherdruck durch externe Anwendungen. Ist Resource Governor im Einsatz, prüfe die Einstellungen REQUEST_MAX_MEMORY_GRANT_PERCENT der Workload Group und MAX_MEMORY_PERCENT des Pools. Verlege speicherintensive Batch-Jobs in lastarme Zeiten.","tags":["memory grant","memory","resource governor","statistics","hash","pressure"]},{"code":"5061","title":"ALTER DATABASE failed because a lock could not be placed on database","severity":"danger","meaning":"Eine ALTER DATABASE-Operation (z. B. Wechsel in den Single-User-Modus, Dateiänderung, Setzen einer Option) konnte die benötigte Sperre auf der Datenbank nicht erlangen, weil andere aktive Verbindungen/Sessions sie nutzen.","cause":"Die Operation wurde versucht, während die Datenbank in Gebrauch war; die für SET SINGLE_USER, RESTRICTED_USER oder ähnliche Optionsänderungen nötige exklusive Sperre konnte wegen aktiver Benutzerverbindungen nicht erlangt werden. Häufig bei häufigen Deployments/Wartungen bei offenen Anwendungsverbindungen.","fix":"Ermittle die anderen mit der Datenbank verbundenen Sessions (sys.dm_exec_sessions, sp_who2) und schließe sie sicher. Ändere für Wartungen den Modus und rolle dabei offene Transaktionen mit der Option ROLLBACK zurück: ALTER DATABASE [db] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; Führe die Operation bei geringem Anwendungsverkehr oder in einem Wartungsfenster durch. Denke daran, danach wieder auf MULTI_USER umzustellen.","tags":["alter database","lock","single_user","connection","maintenance","rollback"]},{"code":"3702","title":"Cannot drop database because it is currently in use","severity":"caution","meaning":"Es wurde versucht, eine Datenbank zu löschen oder umzubenennen, aber das wurde abgelehnt, weil die Datenbank noch von aktiven Verbindungen genutzt wird. SQL Server kann eine Datenbank, die in Gebrauch ist, nicht löschen.","cause":"Andere Sessions (ein Anwendungspool, ein offener SSMS-Tab, Hintergrundjobs) sind mit der zu löschenden Datenbank verbunden. Schon deine eigene Session kann den Fehler auslösen, wenn diese Datenbank ihr Kontext (USE) ist. Auch offene Verbindungen von Monitoring-/Backup-Tools sind eine häufige Ursache.","fix":"Wechsle vor dem Löschen in eine andere Datenbank (USE master;). Finde und schließe die verbundenen Sessions oder setze die Datenbank zuerst in den Single-User-Modus: ALTER DATABASE [db] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; führe dann DROP DATABASE [db]; aus. Stoppe die Connection Pools der Anwendung und stelle sicher, dass Monitoring-Tools keine Verbindungen öffnen.","tags":["drop database","connection","in use","single_user","use","maintenance"]},{"code":"15138","title":"The database principal owns a schema in the database, and cannot be dropped","severity":"caution","meaning":"Es wurde versucht, einen Datenbankbenutzer bzw. eine Rolle (Datenbankprinzipal) zu löschen, aber das war nicht möglich, weil der Prinzipal ein oder mehrere Schemas in der Datenbank besitzt. Er kann erst gelöscht werden, wenn die ihm gehörenden Schemas an einen anderen Prinzipal übertragen sind.","cause":"Der zu entfernende Benutzer ist OWNER mindestens eines Schemas (z. B. eines standardmäßig unter seinem Namen angelegten oder per AUTHORIZATION zugewiesenen Schemas). SQL Server blockiert das Löschen, um keine verwaisten Schemas zu hinterlassen.","fix":"Finde die Schemas, die dem Benutzer gehören: SELECT name FROM sys.schemas WHERE principal_id = USER_ID('app_user'); Übertrage den Besitz dieser Schemas an einen anderen Prinzipal (z. B. dbo): ALTER AUTHORIZATION ON SCHEMA::[app_schema] TO dbo; Wird das Schema nicht mehr benötigt (und ist es leer), führe DROP SCHEMA aus. Nachdem der Besitz übertragen wurde, führe DROP USER erneut aus.","tags":["schema","principal","user","owner","alter authorization","drop user"]},{"code":"1934","title":"INSERT/UPDATE failed because the following SET options have incorrect settings","severity":"danger","meaning":"Eine DML-Operation (INSERT/UPDATE/DELETE) ist fehlgeschlagen, weil erforderliche SET-Optionen falsch gesetzt waren. Tritt typischerweise auf, wenn die zwingenden SET-Einstellungen für indizierte Views, Indizes auf berechneten Spalten, gefilterte Indizes oder Spatial-/XML-Indizes nicht erfüllt sind.","cause":"Die betroffene Tabelle/View hat ein Objekt, das bestimmte SET-Optionen erfordert (insbesondere QUOTED_IDENTIFIER und ANSI_NULLS ON, NUMERIC_ROUNDABORT OFF usw.), aber die aktuellen SET-Optionen der Session erfüllen diese Anforderung nicht. Alte Client-Treiber oder Skripte, die explizit falsche SET-Optionen setzen, lösen den Fehler aus.","fix":"Setze die erforderlichen Optionen für die Verbindung/Session: SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET CONCAT_NULL_YIELDS_NULL ON; SET ARITHABORT ON; SET NUMERIC_ROUNDABORT OFF; Stelle sicher, dass der Anwendungstreiber diese Optionen beim Verbinden korrekt sendet. Die Fehlermeldung nennt die falschen Optionen; korrigiere diese.","tags":["set options","indexed view","computed column","quoted_identifier","ansi_nulls","dml"]},{"code":"468","title":"Cannot resolve the collation conflict","severity":"caution","meaning":"Bei einer Operation (JOIN, UNION, WHERE-Vergleich oder Gleichheit) trafen zwei String-Ausdrücke mit unterschiedlichen Collations aufeinander, und SQL Server konnte nicht auflösen, welche Collation anzuwenden ist. Die Operation wurde wegen der Mehrdeutigkeit abgebrochen.","cause":"Die verglichenen Spalten sind mit unterschiedlichen Collations definiert; am häufigsten, wenn die Datenbank-Collation von der Server-/tempdb-Collation abweicht oder wenn Tabellen in Datenbanken mit unterschiedlichen Collations verknüpft werden. Das Verknüpfen temporärer Tabellen (tempdb-Collation) mit permanenten Tabellen ist ein klassischer Auslöser.","fix":"Gib im Vergleich eine explizite Collation an: ... WHERE a.col = b.col COLLATE DATABASE_DEFAULT (oder einen konkreten Collation-Namen). Verwende COLLATE DATABASE_DEFAULT beim Anlegen von Spalten temporärer Tabellen. Gleiche als dauerhafte Lösung die betroffenen Spalten/Datenbanken an eine einheitliche Collation an. Treten Abweichungen zu tempdb häufig auf, ist DATABASE_DEFAULT die praktischste Lösung.","tags":["collation","collation","join","tempdb","database_default","conflict"]},{"code":"4864","title":"Bulk load data conversion error (Typkonflikt oder ungültiges Zeichen)","severity":"caution","meaning":"Bei einem Bulk Load (BULK INSERT, bcp oder OPENROWSET(BULK...)) konnte ein Feld der Quelldatei nicht in den Datentyp der Zielspalte konvertiert werden. Die Daten in der gemeldeten Zeile/Spalte passen nicht zum erwarteten Typ.","cause":"Ein Wert in der Quelldatendatei passt nicht zum Typ der Zielspalte: Text in einer numerischen Spalte, falsches Datumsformat, unerwartetes Dezimal-/Tausendertrennzeichen, falsche Zeichenkodierung (UTF-8/UTF-16) oder durch falsche Feld-/Zeilenendezeichen verschobene Spalten. Auch das Nichtüberspringen der Kopfzeile ist eine häufige Ursache.","fix":"Gehe zu der vom Fehler gemeldeten Zeile und Spalte und prüfe die Quelldaten. Passe Feld- (FIELDTERMINATOR) und Zeilenendezeichen (ROWTERMINATOR) an die Datei an; nutze FIRSTROW=2, um die Kopfzeile zu überspringen. Setze CODEPAGE oder DATAFILETYPE für die richtige Codepage/Kodierung. Mache Datums-/Zahlenformate mit dem Zieltyp kompatibel. Eine Formatdatei macht die Zuordnung zuverlässig.","tags":["bulk insert","bcp","conversion","fieldterminator","encoding","format file"]},{"code":"916","title":"The server principal is not able to access the database under the current security context","severity":"caution","meaning":"Ein Login wollte auf eine bestimmte Datenbank zugreifen, hat im aktuellen Sicherheitskontext aber keinen Zugriff darauf. Das Login kann sich mit dem Server verbinden, hat in der Zieldatenbank aber keinen zugehörigen Benutzer bzw. keine Berechtigung.","cause":"Das Login hat in der Datenbank keinen zugeordneten Benutzer (verwaist oder nie angelegt), der Datenbankzugriff ist eingeschränkt, oder das Gastkonto ist deaktiviert. Ein häufiger Fall ist der SSMS-Objekt-Explorer, der Metadaten einer Datenbank lesen will, auf die das Login keinen Zugriff hat.","fix":"Lege in der Zieldatenbank einen Benutzer für das Login an und erteile die nötigen Berechtigungen: USE [db]; CREATE USER [login] FOR LOGIN [login]; und weise die passenden Rollen/Berechtigungen zu. Gibt es einen verwaisten Benutzer, ordne ihn mit ALTER USER ... WITH LOGIN = ... zu. Wird der Zugriff wirklich benötigt, füge ihn der passenden Datenbankrolle hinzu; andernfalls ist der Fehler das erwartete Verhalten (kein Zugriff auf diese Datenbank).","tags":["login","user","access","privilege","security context","orphaned user"]}]},"errors/mongodb-more4.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"216 ElectionInProgress","title":"ElectionInProgress","severity":"caution","meaning":"Im Replica Set läuft gerade eine Primary-Election. Bis die Election abgeschlossen ist, steht der Primary nicht fest, daher werden Schreibvorgänge (und manche Lesevorgänge) vorübergehend nicht angenommen.","cause":"Der Cluster wählt einen neuen Primary, nachdem der aktuelle Primary ausgefallen ist, nach einer Netzwerkpartition, einem Stepdown für Wartungsarbeiten oder einem Knotenneustart. Eine Election dauert meist einige Sekunden; Operationen, die in diesem Zeitfenster eintreffen, stoßen auf diesen Zustand.","fix":"Das ist vorübergehend; aktiviere die Retry-Funktion deines Treibers (Retryable Writes/Reads), damit die Operation nach Abschluss der Election automatisch wiederholt wird. Sind Elections häufig oder lang, untersuche die Grundursache: Netzwerkstabilität, Knotenzustand, Stimmenkonfiguration und Election-Timeout-Einstellungen (settings.electionTimeoutMillis). Prüfe den Clusterzustand mit rs.status().","tags":["replica set","election","primary","retry","step-down","high availability"]},{"code":"225 TransactionTooOld","title":"TransactionTooOld","severity":"caution","meaning":"Ein Befehl wurde in einer Multi-Document-Transaktion ausgeführt, aber seine Transaktionsnummer ist älter als die neueste Transaktionsnummer, die der Server für diese Session gesehen hat. Mit anderen Worten: Eine neuere Transaktion wurde gestartet, diese alte ist daher nicht mehr gültig.","cause":"In derselben Session wurde eine neue Transaktion gestartet, bevor die alte abgeschlossen war, oder Transaktionsnummern wurden durch Parallelität in falscher Reihenfolge weitergezählt. Passiert meist, wenn ein Session-Objekt falsch geteilt wird oder mehrere Threads dieselbe Session nutzen.","fix":"Verwalte den Lebenszyklus der Session für jede logische Transaktion korrekt; teile eine Session nicht gleichzeitig über mehrere Threads. Führe Transaktionen mit der vom Treiber empfohlenen withTransaction-/Callback-API aus (sie übernimmt Retries und Nummernverwaltung). Schließe die vorherige Transaktion mit Commit/Abort ab, bevor du eine neue startest.","tags":["transaction","session","session misuse","concurrency","withtransaction","multi-document transaction"]},{"code":"246 SnapshotUnavailable","title":"SnapshotUnavailable","severity":"caution","meaning":"Ein Lesevorgang mit dem angeforderten Snapshot Read Concern (readConcern: snapshot) konnte nicht ausgeführt werden, weil der Server für diesen Zeitpunkt keinen konsistenten Snapshot bereitstellen kann. Hängt meist mit einem Lesezeitstempel zusammen, der noch nicht weit genug fortgeschritten oder zurückgefallen ist.","cause":"readConcern 'snapshot' wird verwendet (meist in Multi-Document-Transaktionen), aber eine konsistente Sicht für den angeforderten atLeast/at-Zeitstempel ist noch nicht verfügbar; z. B. eine neu angelegte Collection/ein neuer Index, eine Katalogänderung oder Replikationsverzögerung. Der Server lehnt den Lesevorgang in diesem Fall ab.","fix":"Das ist meist vorübergehend; wiederhole die Operation nach kurzer Verzögerung (der Retry-Mechanismus des Treibers für TransientTransactionError deckt das ab). Hast du kürzlich DDL ausgeführt (Anlegen von Collection/Index), starte die Transaktion danach. Prüfe Replikationsverzögerung und Knotenzustand. Wird ein Snapshot-Read nicht wirklich benötigt, wähle einen passenderen readConcern.","tags":["snapshot","readconcern","transaction","timestamp","retry","replication"]},{"code":"257 TransactionTooLarge","title":"TransactionTooLarge","severity":"danger","meaning":"Eine Multi-Document-Transaktion hat die erlaubten Limits für Größe/Operationsanzahl überschritten. Die Transaktion wurde abgelehnt, weil die darin angesammelten Änderungen (Oplog-Eintrag oder Speichernutzung) die Obergrenze überschritten haben.","cause":"Das Ändern/Einfügen einer großen Zahl von Dokumenten in einer einzigen Transaktion hat die von der Transaktion erzeugte Datenmenge zu stark anwachsen lassen (z. B. das Volumen der Änderungen, das im WiredTiger-Cache gehalten werden muss, oder Serverlimits wie transactionLimitBytes). Seit 4.2 kann eine Transaktion auf mehrere Oplog-Einträge verteilt werden, daher ist das 16-MB-Limit für Oplog-Einträge allein nicht entscheidend. Transaktionen für Massendatenmigration/ETL zu nutzen, erreicht dieses Limit leicht.","fix":"Teile die Transaktion in kleinere Stücke auf; committe jeden Batch in einer eigenen Transaktion. Multi-Document-Transaktionen sind für kurze, kleine Vorgänge gedacht; führe große Massenupdates bevorzugt ohne Transaktionen durch (idempotent, blockweise) oder teile sie per Bulk Write auf. Vermeide es, Tausende Dokumente in einer einzigen Transaktion zu ändern.","tags":["transaction","size","oplog","batch","bulk","bulk write"]},{"code":"276 IndexBuildAborted","title":"IndexBuildAborted","severity":"danger","meaning":"Ein laufender Indexaufbau wurde vor dem Abschluss abgebrochen/gestoppt. Der Zielindex wurde nicht erfolgreich angelegt.","cause":"Der Indexaufbau wurde möglicherweise abgebrochen wegen zu wenig Platten-/Speicherplatz auf dem Knoten, eines expliziten Abbruchs per dropIndexes, eines Primary-Stepdowns/einer Änderung des Replica-Zustands, einer Eindeutigkeitsverletzung (kollidierende Dokumente beim Aufbau eines Unique-Index) oder eines Knotenneustarts.","fix":"Finde in den mongod-Logs den Abbruchgrund (Plattenplatz, Speicher, Eindeutigkeitskonflikt usw.) und behebe ihn. Baust du einen Unique-Index auf, bereinige zuerst die kollidierenden Dokumente. Stelle ausreichend Platten-/Arbeitsspeicher bereit und sorge für einen stabilen Replica-Zustand. Baue den Index nach der Behebung mit createIndex neu auf; wähle bei großen Collections eine Phase mit geringer Last.","tags":["index","index build","aborted","unique","disk","replica set"]},{"code":"86 IndexKeySpecsConflict","title":"IndexKeySpecsConflict","severity":"caution","meaning":"Der anzulegende Index hat dieselbe Schlüsselspezifikation (gleiche Felder/Reihenfolge) wie ein vorhandener Index, aber andere Optionen oder einen anderen Namen. MongoDB erlaubt keine zwei kollidierenden Indexdefinitionen mit demselben Schlüssel.","cause":"Es wurde versucht, auf demselben Feld bzw. denselben Feldern einen zweiten Index mit anderem Namen oder anderen Optionen anzulegen (z. B. andere collation, partialFilterExpression, unique-Einstellung). Passiert meist, wenn eine Indexdefinition in einer Migration/im Code geändert und erneut ausgeführt wird.","fix":"Prüfe die vorhandenen Indizes: db.collection.getIndexes() Finde den kollidierenden Index mit demselben Schlüssel. Um zu den gewünschten Optionen zu wechseln, lösche zuerst den alten Index (dropIndex) und lege ihn dann mit der neuen Definition an. Unterscheidet sich nur der Name, nutze weiter den vorhandenen Index; lege keine unnötige zweite Definition an.","tags":["index","getindexes","collation","unique","conflict","createindex"]},{"code":"166 CommandNotSupportedOnView","title":"CommandNotSupportedOnView","severity":"caution","meaning":"Der Befehl wird auf einer View nicht unterstützt. MongoDB-Views sind schreibgeschützt und akzeptieren nur bestimmte Leseoperationen; Schreibvorgänge oder nicht unterstützte Befehle werden abgelehnt.","cause":"Schreiboperationen wie insert/update/delete oder bestimmte auf Views nicht unterstützte Befehle (z. B. manche Text-/Geo-Operationen, direktes Anlegen von Indizes) wurden auf einer View versucht. Tritt auf, wenn die Anwendung auf eine View schreiben will, weil sie sie für eine Collection hält.","fix":"Führe Schreibvorgänge auf der zugrunde liegenden (Quell-)Collection aus, auf der die View basiert; Views sind nur zum Lesen da. Prüfe, ob der Befehl auf Views unterstützt wird, und nutze unterstützte Lese-/Aggregationsoperationen. Brauchst du eine beschreibbare Struktur, entwirf statt einer View eine materialisierte Collection ($merge/$out).","tags":["view","read-only view","read only","aggregation","write","collection"]},{"code":"168 InvalidPipelineOperator","title":"InvalidPipelineOperator","severity":"caution","meaning":"In einer Aggregationspipeline wurde ein nicht erkannter oder ungültiger Operator bzw. eine ungültige Stage verwendet. MongoDB konnte den angegebenen Operatornamen nicht auflösen und hat die Pipeline abgelehnt.","cause":"Ein falsch geschriebener Operatorname (z. B. '$groups' statt '$group'), ein neuer Operator, der in der verwendeten MongoDB-Version nicht verfügbar ist, oder ein Ausdruck im falschen Kontext. Auch ein fehlendes $-Präfix oder ein Operator an ungültiger Stelle lösen den Fehler aus.","fix":"Prüfe die Schreibweise der Operator-/Stage-Namen und das $-Präfix. Stelle sicher, dass der Operator von der MongoDB-Version des Servers unterstützt wird (neue Operatoren existieren in älteren Versionen nicht). Vergleiche die Pipeline mit der dokumentierten Syntax; verwende den Ausdruck in der richtigen Stage (z. B. $expr, $project). Isoliere die Stage und teste sie mit einer kleinen Stichprobe.","tags":["aggregation","pipeline","operator","syntax","version","expr"]},{"code":"118 NamespaceNotSharded","title":"NamespaceNotSharded","severity":"caution","meaning":"Eine Operation, die nur bei gesharden Collections sinnvoll ist, wurde gegen eine Collection bzw. einen Namespace ausgeführt, die bzw. der noch nicht geshardet ist. Der Befehl konnte nicht angewendet werden, weil die Ziel-Collection nicht geshardet ist.","cause":"moveChunk, split oder bestimmte Shard-Key-basierte administrative Befehle wurden auf einer Collection ohne aktiviertes Sharding aufgerufen. Passiert meist, weil die Collection nicht mit shardCollection geshardet wurde oder der falsche Namespace angesprochen wurde.","fix":"Prüfe den Sharding-Status der Collection: sh.status() oder config.collections. Erfordert die Operation wirklich Sharding, aktiviere zuerst Sharding für die Datenbank (sh.enableSharding) und sharde die Collection (sh.shardCollection mit einem passenden Shard Key). Andernfalls werden diese administrativen Befehle auf nicht gesharden Collections nicht benötigt; prüfe, ob du den richtigen Namespace ansprichst.","tags":["sharding","shard","namespace","shardcollection","movechunk","cluster"]}]},"errors/redis-more4.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"ERR Protocol error: invalid multibulk length","title":"Protocol error: invalid multibulk length","severity":"danger","meaning":"Der Redis-Server konnte den Bytestrom des Clients nicht gemäß RESP (Redis Serialization Protocol) parsen. Da die von einem Multibulk-Befehl deklarierte Elementanzahl ungültig war, wurde die Verbindung mit einem Protokollfehler geschlossen.","cause":"Meist ausgelöst durch einen Nicht-Redis-Client (z. B. eine HTTP-Anfrage, eine mit dem falschen Port verbundene Anwendung), der Rohdaten an den Redis-Port sendet, eine defekte/unvollständige Client-Bibliothek, fehlerhaftes Framing in einer Pipeline oder riesige Anfragen, die Protokolllimits überschreiten (proto-max-bulk-len).","fix":"Stelle sicher, dass sich nur ein echter Redis-Client mit dem Redis-Port verbindet (kein verirrter HTTP-/sonstiger Protokollverkehr). Aktualisiere die Client-Bibliothek und vermeide eigene/manuelle Befehlserzeugung, die das RESP-Framing bricht. Sendest du sehr große Werte, prüfe das Limit proto-max-bulk-len und die Anfragegröße. Stelle sicher, dass kein zwischengeschalteter Proxy im Netzwerk die Daten beschädigt.","tags":["protocol","resp","multibulk","client","pipeline","connection"]},{"code":"ERR Number of keys can't be greater than number of args","title":"EVAL/EVALSHA: numkeys exceeds argument count","severity":"caution","meaning":"Beim Ausführen eines Lua-Skripts (EVAL/EVALSHA/FCALL) ist die angegebene Anzahl der Keys (numkeys) größer als die Zahl der tatsächlich an den Befehl übergebenen Argumente. Redis lehnt den Befehl ab, weil es die Arrays KEYS und ARGV nicht aufteilen kann.","cause":"Die EVAL-Syntax lautet 'EVAL script numkeys key [key ...] arg [arg ...]'. Der Wert numkeys wurde falsch berechnet (zu hoch gesetzt), oder die erwarteten Keys wurden dem Befehl nie hinzugefügt. Ein häufiger Programmierfehler.","fix":"Setze numkeys exakt auf die Zahl der tatsächlich an den Befehl übergebenen Keys. Nutze den EVAL-Wrapper deiner Client-Bibliothek (der KEYS und ARGV meist als getrennte Listen entgegennimmt), damit numkeys automatisch berechnet wird. Teile beim Aufruf des Skripts Keys korrekt in KEYS und andere Parameter in ARGV auf.","tags":["lua","eval","evalsha","numkeys","keys","script"]},{"code":"ERR WATCH inside MULTI is not allowed","title":"WATCH inside MULTI is not allowed","severity":"caution","meaning":"WATCH wurde innerhalb eines offenen MULTI-Blocks gesendet. Für optimistisches Locking muss WATCH Keys VOR Beginn der Transaktion unter Beobachtung stellen; nach MULTI aufgerufen wird es abgelehnt. Der Fehler bricht die Transaktion nicht ab; nur WATCH schlägt fehl, und nichts wird beobachtet.","cause":"WATCH und MULTI stehen im Code in umgekehrter Reihenfolge (zuerst MULTI, dann WATCH); Aufruf von WATCH innerhalb des Pipeline-/Transaktions-Wrappers der Client-Bibliothek; oder Nutzung einer Pool-Verbindung, die im MULTI-Zustand zurückgelassen wurde. In diesem Fall läuft EXEC auch dann, wenn sich die zu beobachtenden Keys geändert haben; der erwartete Konfliktschutz greift nicht.","fix":"Richtige Reihenfolge: WATCH key -> (Werte lesen) -> MULTI -> Befehle -> EXEC. Liefert EXEC nil, hat sich ein beobachteter Key geändert; wiederhole die Operation von vorn. Nutze die WATCH-/Transaktions-API der Client-Bibliothek (z. B. watch- + pipeline-Helfer). Hinweis: Pub/Sub-Befehle wie SUBSCRIBE werden innerhalb von MULTI nicht abgelehnt; sie werden eingereiht und die Verbindung wechselt nach EXEC in den Subscribe-Modus; auch das wird nicht empfohlen, nutze eine eigene Verbindung für Pub/Sub.","tags":["multi","watch","transaction","optimistic locking","exec","connection"]},{"code":"ERR WAIT cannot be used with replica instances","title":"WAIT cannot be used with replica instances","severity":"caution","meaning":"WAIT wurde auf einer Replica-Instanz (Replica/Slave) ausgeführt. WAIT, das darauf wartet, dass ein Schreibvorgang eine bestimmte Zahl von Replicas erreicht, ist nur auf dem Primary (Master) sinnvoll; auf einer Replica kann es nicht verwendet werden.","cause":"Die Anwendung hat WAIT versehentlich über eine Replica-Verbindung gesendet. Das passiert meist, weil die Verbindung an eine Lese-Replica geleitet wird oder der Client nach einem Rollenwechsel (Failover) noch mit einer Replica verbunden ist.","fix":"Sende WAIT nur an den Primary-Knoten. Stelle sicher, dass die Client-/Proxy-Konfiguration Schreibvorgänge und WAIT-Aufrufe an den Master leitet. Erkenne Rollenwechsel nach einem Failover und leite Verbindungen auf den neuen Primary um (die Rolle lässt sich mit dem Befehl ROLE prüfen).","tags":["wait","replica","master","write","failover","replication"]},{"code":"ERR Background append only file rewriting already in progress","title":"AOF rewrite already in progress","severity":"caution","meaning":"Es wurde versucht, ein Rewrite des AOF (Append Only File) zu starten (BGREWRITEAOF), aber es läuft bereits ein AOF-Rewrite. Zwei AOF-Rewrites können nicht gleichzeitig laufen.","cause":"Eine neue Rewrite-Anfrage kam, während ein vorheriges BGREWRITEAOF (manuell oder automatisch über den Auslöser auto-aof-rewrite-percentage) noch lief. Bei großen Datenbeständen kann ein AOF-Rewrite lange dauern und zu diesem Konflikt führen.","fix":"Warte, bis das laufende Rewrite abgeschlossen ist; beobachte das Feld aof_rewrite_in_progress in der Ausgabe von INFO persistence. Läuft die Operation berechtigterweise, sende keine weitere Anfrage. Sind Konflikte häufig, prüfe die Schwellen für automatische Rewrites (auto-aof-rewrite-percentage/min-size) und die I/O-Leistung der Platte.","tags":["aof","bgrewriteaof","persistence","rewrite","info","disk"]},{"code":"IOERR error or timeout reading from target instance","title":"MIGRATE: I/O error or timeout to target","severity":"danger","meaning":"Ein MIGRATE-Befehl (meist beim Verschieben von Slots/Keys in Redis Cluster) ist beim Verbinden mit der Zielinstanz oder beim Lesen ihrer Antwort auf einen I/O-Fehler oder Timeout gestoßen. Die Migration des Keys konnte nicht sicher abgeschlossen werden.","cause":"Netzwerklatenz/-ausfall zwischen Quell- und Zielknoten, ein überlasteter oder nicht reagierender Zielknoten, ein zu kurzer MIGRATE-Timeout oder ein großer Key, der nicht in der gegebenen Zeit übertragen werden kann. Häufig beim Rebalancing des Clusters (Resharding).","fix":"Prüfe Netzwerkverbindung und Latenz zwischen Quell- und Zielknoten. Gib für MIGRATE einen längeren Timeout an (insbesondere bei großen Keys). Prüfe Zustand/Last und Speicherstatus des Zielknotens. Wähle beim Verschieben großer Keys während des Reshardings lastarme Zeiten; wiederhole die Migration bei Bedarf.","tags":["migrate","cluster","resharding","timeout","network","i-o"]},{"code":"ERR Error compiling script (new function)","title":"Error compiling Lua script","severity":"caution","meaning":"Das mit EVAL gesendete Lua-Skript konnte vom eingebetteten Lua-Interpreter von Redis nicht kompiliert werden. Es wurde vor Beginn der Ausführung abgelehnt, weil das Skript einen Syntaxfehler enthält.","cause":"Syntaxfehler im Lua-Skript: nicht geschlossene Klammern/Anführungszeichen, falsch geschriebene Schlüsselwörter, ein fehlendes 'end', ungültige Operatoren oder ein nicht unterstütztes Lua-Konstrukt. Auch falsches Verketten/Escapen des Skripts auf Client-Seite kann den Fehler auslösen.","fix":"Korrigiere die Lua-Syntax anhand der Zeilen-/Zeichenangabe in der Fehlermeldung (prüfe zusammengehörige Klammern, Anführungszeichen und 'end'). Validiere das Skript, indem du es in einer separaten Lua-Umgebung kompilierst. Stelle sicher, dass der Client das Skript korrekt zusammensetzt (keine Escape-/Verkettungsfehler). Lade validierte Skripte vorab mit SCRIPT LOAD und rufe sie mit EVALSHA auf.","tags":["lua","eval","compilation","syntax","script load","script"]},{"code":"ERR FAILOVER requires connected replicas.","title":"FAILOVER requires connected replicas","severity":"caution","meaning":"FAILOVER wurde ausgeführt, um ein koordiniertes (geordnetes) Failover zu starten, aber es wurde abgelehnt, weil keine geeignete, mit dem Primary verbundene Replica zur Übernahme bereitsteht. Ohne ein Ziel, an das die Rolle übergeben werden kann, ist kein Failover möglich.","cause":"Der Primary hat derzeit keine verbundenen Replicas (Replicas sind ausgefallen, vom Netzwerk getrennt oder nicht synchron), oder die angegebene Ziel-Replica ist nicht geeignet/nicht verbunden. Beim Aufruf von manuellem FAILOVER (Redis 6.2+) erfüllt die Replikationstopologie die erforderliche Bedingung nicht. Hinweis: FAILOVER funktioniert nur bei Standalone-Replikation (Master/Replica); in Redis Cluster wird auf der Replica CLUSTER FAILOVER verwendet.","fix":"Stelle sicher, dass mindestens eine gesunde, verbundene und aktuelle Replica existiert (prüfe connected_slaves und den Synchronisationsstatus mit INFO replication). Behebe Netzwerk-/Zugriffsprobleme auf den Replicas, damit sie sich mit dem Primary verbinden. Für ein Failover auf ein bestimmtes Ziel nutze FAILOVER TO host port und prüfe, ob diese Replica geeignet ist.","tags":["failover","replica","master","replication","info replication","high availability"]},{"code":"ERR invalid expire time in 'set' command","title":"invalid expire time in SET","severity":"caution","meaning":"An SET wurde eine ungültige Ablaufzeit übergeben (mit den Optionen EX/PX/EXAT/PXAT). Redis hat den Befehl abgelehnt, weil es keine sinnlose Lebensdauer wie null oder einen negativen Wert akzeptiert.","cause":"Senden von 0 oder einem negativen Wert mit EX/PX, oder die Anwendung berechnet die Ablaufzeit falsch und erzeugt eine ungültige Zahl. Befehle wie SETEX/GETEX liefern diesen Fehler ebenso bei nicht positiven Dauern. Manche Client-Fehler entstehen durch Verwechseln von Sekunden und Millisekunden.","fix":"Sende die Ablaufzeit immer als streng positive Ganzzahl; EX erwartet Sekunden, PX Millisekunden. Prüfe die Berechnung der Dauer in der Anwendung (sie darf keine negativen Werte oder null ergeben) und verwechsle die Einheiten nicht (Sekunden vs. Millisekunden). Um einen Key sofort zu löschen, nutze DEL/UNLINK, statt eine Ablaufzeit zu setzen. Übergib für absolute Zeitpunkte einen gültigen zukünftigen Unix-Zeitstempel an EXAT/PXAT.","tags":["set","expire","ttl","ex","px","expiration"]}]},"errors/elasticsearch-more4.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"bind_transport_exception","title":"bind_transport_exception (Address already in use)","severity":"danger","meaning":"Beim Start konnte ein Elasticsearch-Knoten keinen Socket an den Transport-Port (Kommunikation zwischen Knoten) oder HTTP-Port binden, um darauf zu lauschen. Meist begleitet vom Unterfehler 'Address already in use'; der Knoten kann nicht starten.","cause":"Der angeforderte Port (Standard Transport 9300, HTTP 9200) wird bereits von einem anderen Prozess verwendet; am häufigsten wurde eine frühere Instanz desselben Knotens nicht sauber beendet, eine andere ES-Instanz/ein anderer Dienst auf demselben Host kollidiert, oder network.host/port ist falsch konfiguriert. Auch Berechtigungs-/Zugriffsbeschränkungen können das Binden verhindern.","fix":"Finde heraus, wer den Port belegt (Linux: ss -ltnp | grep 9300 oder lsof -i:9300), und beende den kollidierenden Prozess oder weise dem Knoten einen anderen Port zu (transport.port / http.port). Prüfe, ob der vorherige ES-Prozess tatsächlich beendet ist. Stelle sicher, dass network.host und die Einstellungen für den Portbereich korrekt und bindbar sind. Starte den Knoten dann neu.","tags":["transport","port","9300","bind","network.host","startup"]},{"code":"translog_corrupted_exception","title":"translog_corrupted_exception","severity":"danger","meaning":"Das Transaktionslog (Translog) eines Shards konnte nicht gelesen werden, weil es beschädigt ist. Während der Recovery hat Elasticsearch festgestellt, dass das Translog die Integritätsprüfung nicht bestanden hat, und der Shard konnte nicht starten.","cause":"Translog-Dateien werden meist durch plötzlichen Stromausfall, eine volle Platte, einen Dateisystemfehler, einen Hardware-/Plattendefekt oder externe Operationen am Datenverzeichnis (Kopieren, manuelle Eingriffe) beschädigt. Auch teilweise abgeschlossene Schreibvorgänge bei einem erzwungenen Beenden (kill -9) können die Ursache sein.","fix":"Versuche zuerst, die Daten aus einer Replica oder einem Snapshot wiederherzustellen; gibt es eine gesunde Kopie, ist der Neuaufbau des beschädigten Shards daraus der sicherste Weg. Gibt es keine andere Kopie und nimmst du Datenverlust in Kauf, stoppe den Knoten und bereinige das beschädigte Translog bzw. die Daten mit bin/elasticsearch-shard remove-corrupted-data --index <index> --shard-id <n> (dabei gehen noch nicht committete Daten dauerhaft verloren; das alte Tool elasticsearch-translog wurde in 7.0 entfernt). Das Tool gibt den Befehl POST _cluster/reroute (allocate_stale_primary, accept_data_loss: true) aus, mit dem der Shard nach dem Neustart des Knotens zugewiesen wird. Sichere vor der Operation das Datenverzeichnis. Prüfe Plattenzustand und freien Platz.","tags":["translog","shard","recovery","corruption","snapshot","disk"]},{"code":"recovery_engine_exception","title":"recovery_engine_exception","severity":"danger","meaning":"Bei der Recovery eines Shards ist ein Fehler auf Engine-Ebene aufgetreten, und der Shard konnte nicht erfolgreich geöffnet werden. Die Recovery ist der Schritt, der bei einem Knotenneustart, einer Replica-Zuweisung oder einem Snapshot-Restore ausgeführt wird.","cause":"Ein zugrunde liegendes Problem während der Recovery: beschädigte Lucene-Segmente oder Translog, zu wenig Plattenplatz, Dateiberechtigungen, eine Netzwerkunterbrechung zwischen Quell- und Zielknoten oder Versions-/Kompatibilitätsprobleme. Umhüllt meist einen spezifischeren Unterfehler (Korruption, I/O).","fix":"Prüfe den zugrunde liegenden Fehler (caused_by), um das eigentliche Problem zu ermitteln (Korruption, Platte, Netzwerk usw.). Prüfe Plattenplatz und Dateiberechtigungen. Schlägt die Replica-Zuweisung fehl, ermittle den Grund mit _cluster/allocation/explain und löse bei Bedarf eine Neuzuweisung aus. Liegt Korruption vor, stelle aus einer gesunden Replica bzw. einem Snapshot wieder her. Ist das Netzwerk die Ursache, prüfe die Verbindung zwischen den Knoten und die Recovery-Einstellungen.","tags":["recovery","shard","engine","allocation","corruption","disk"]},{"code":"snapshot_in_progress_exception","title":"snapshot_in_progress_exception","severity":"caution","meaning":"Die angeforderte Operation wurde abgelehnt, weil sie mit einem gerade laufenden Snapshot kollidiert. Zum Beispiel wird der Versuch blockiert, während eines Snapshots einen Index zu löschen/zu schließen oder das Repository zu ändern.","cause":"Während noch ein Snapshot lief, wurde versucht, in diesem Snapshot enthaltene Indizes zu löschen/zu schließen, das Repository zu entfernen/zu ändern oder eine weitere kollidierende Snapshot-/Restore-Operation zu starten. Elasticsearch erlaubt diese kollidierenden Operationen nicht, um die Konsistenz zu wahren.","fix":"Warte, bis der laufende Snapshot abgeschlossen ist: Verfolge den Fortschritt mit GET _snapshot/_status. Muss die Operation wirklich abgebrochen werden, stoppe sie, indem du den Snapshot löschst: DELETE _snapshot/repo/snapshot_name (das bricht den laufenden Snapshot ab). Führe nach Abschluss oder Abbruch des Snapshots deine ursprüngliche Operation erneut aus (Löschen des Index, Änderung des Repositorys).","tags":["snapshot","repository","restore","index deletion","conflict","backup"]},{"code":"repository_verification_exception","title":"repository_verification_exception","severity":"danger","meaning":"Beim Registrieren oder Verifizieren eines Snapshot-Repositorys ist die Prüfung fehlgeschlagen, ob alle Knoten auf das Repository zugreifen können. Da die Verifizierung des Repositorys nicht bestanden wurde, lassen sich Snapshot-/Restore-Operationen nicht zuverlässig ausführen.","cause":"Ein gemeinsames Dateisystem-Repository (fs) ist nicht auf allen Knoten unter demselben Pfad gemountet/zugänglich, path.repo fehlt in der Konfiguration, die Dateiberechtigungen reichen nicht aus, oder Cloud-/Object-Stores (S3, GCS, Azure) haben falsche Zugangsdaten bzw. keinen Netzwerkzugriff. Typische Ursache ist, dass einige Knoten nicht in das Repository schreiben können.","fix":"Stelle bei einem gemeinsamen fs-Repository sicher, dass path.repo auf allen Knoten in elasticsearch.yml definiert ist und derselbe Pfad auf jedem Knoten lesbar/beschreibbar gemountet ist. Korrigiere die Dateiberechtigungen (Benutzer elasticsearch). Prüfe bei Cloud-Repositorys die Zugangsdaten (Keystore), den Bucket-Namen und die Netzwerk-/Zugriffsrechte. Verifiziere das Repository erneut mit POST _snapshot/repo/_verify.","tags":["repository","snapshot","path.repo","permission","s3","verification"]},{"code":"reduce_search_phase_exception","title":"reduce_search_phase_exception","severity":"danger","meaning":"In der Reduce-Phase einer Suche ist ein Fehler aufgetreten. Der koordinierende Knoten ist beim Zusammenführen (Reduzieren) der Teilergebnisse/Aggregationen verschiedener Shards auf ein Problem gestoßen und konnte das Endergebnis nicht erzeugen.","cause":"Meist wird beim Zusammenführen der Ergebnisse ein Speicher-/Ressourcenlimit überschritten (z. B. sehr große Aggregationen, ein auslösender Circuit Breaker), eine inkompatible/ungültige Aggregationsstruktur liegt vor, oder ein von einem Shard geliefertes Teilergebnis lässt sich nicht zusammenführen. Häufig bei Aggregationen mit sehr hoher Kardinalität.","fix":"Lies das Detail caused_by, um die Grundursache zu ermitteln. Entlaste schwere Aggregationen: Senke size bei terms-Aggregationen hoher Kardinalität, blättere mit einer composite-Aggregation und reduziere unnötige Sub-Aggregationen. Überwache den Speicherdruck und teile die Abfrage bei Bedarf in kleinere Teile auf. Löst ein Circuit Breaker aus, senke die Kosten der Abfrage.","tags":["search","reduce","aggregation","coordinating node","memory","cardinality"]},{"code":"aggregation_execution_exception","title":"aggregation_execution_exception","severity":"caution","meaning":"Bei der Ausführung einer Aggregation ist ein Fehler aufgetreten. Die Aggregationsdefinition wurde zwar geparst, zur Laufzeit trat aber beim Verarbeiten der Zielfelder/-werte ein Problem auf.","cause":"Die Aggregation ist mit dem Feldtyp inkompatibel (z. B. Aggregieren eines text-Felds ohne fielddata), ungültige Werte beim Sortieren/Berechnen, eine Pipeline-Aggregation, die auf einen nicht existierenden Pfad (Bucket Path) verweist, oder nicht unterstützte Kombinationen von Feldtypen. Auch Laufzeitfehler in skriptbasierten Aggregationen lösen den Fehler aus.","fix":"Prüfe den Feld-/Aggregationsnamen im Fehlerdetail. Stelle sicher, dass die aggregierten Felder einen passenden Typ haben (keyword/numerisch statt text; nutze bei Bedarf Felder mit aktivierten doc_values). Prüfe bei Pipeline-Aggregationen, dass buckets_path zu den tatsächlichen Bucket-Namen passt. Ergänze in Skript-Aggregationen Null-/Typprüfungen. Korrigiere das Mapping mit passenden Feldtypen.","tags":["aggregation","execution","fielddata","keyword","pipeline","mapping"]},{"code":"failed_node_exception","title":"failed_node_exception","severity":"danger","meaning":"Bei einer clusterweiten Operation (z. B. Suche, Statistiken, Verwaltungsbefehl) konnte ein bestimmter Knoten im Cluster die Anfrage nicht verarbeiten oder nicht darauf antworten. Der koordinierende Knoten umhüllt den Fehler dieses Knotens mit dieser Exception.","cause":"Der Knoten ist abgestürzt, aus dem Netzwerk gefallen, konnte wegen Überlast/GC-Pausen nicht antworten, hat Platten-/Ressourcenprobleme, oder auf diesem Knoten liegt ein Shard-Fehler vor. Kann auch vorübergehend bei einer Neukonfiguration des Clusters auftreten (Knoten verlassen/treten bei).","fix":"Ermittle mit caused_by, auf welchem Knoten und warum der Fehler auftrat. Prüfe den Knotenzustand: GET _cat/nodes, GET _nodes/stats (CPU, Speicher, GC, Platte). Steht der Knoten unter Ressourcendruck, prüfe die Last-/Heap-Einstellungen; prüfe die Netzwerkverbindung. Hat der Knoten den Cluster verlassen, warte, bis er wieder beitritt und die Shard-Zuweisung sich erholt hat (GET _cluster/health).","tags":["node","cluster","network","gc","shard","resources"]},{"code":"elasticsearch_parse_exception","title":"elasticsearch_parse_exception","severity":"caution","meaning":"Elasticsearch ist beim Parsen des Request-Bodys (JSON-Abfrage, Einstellungen oder Konfiguration) auf einen allgemeinen Parsing-Fehler gestoßen. Die Anfrage entspricht strukturell nicht dem erwarteten Format.","cause":"Fehlerhaftes/ungültiges JSON (fehlendes Komma, nicht geschlossene Klammer), falsch platzierte Felder, unerwartete Werttypen (z. B. Text, wo eine Zahl erwartet wird) oder ein ungültiger Zeit-/Bereichsausdruck. Auch eine falsche Verschachtelung einer DSL-Abfrage erzeugt diese Exception.","fix":"Prüfe den Request-Body mit einem JSON-Validator; kontrolliere Klammern, Anführungszeichen und Kommas. Prüfe anhand der Elasticsearch-DSL-Dokumentation, ob die Felder an der richtigen Stelle stehen und den richtigen Typ haben. Nutze den Positions-/Feldhinweis in der Fehlermeldung, um den problematischen Teil zu korrigieren. Nutzt du eine Client-Bibliothek, stelle sicher, dass die Serialisierung gültiges JSON erzeugt.","tags":["parse","json","dsl","query","parsing","request"]}]},"health/oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"checks":[{"id":"version-uptime","group":"Allgemein","title":"Version und Laufzeit","command":"SELECT i.instance_name, i.version_full, i.status, i.startup_time, ROUND(SYSDATE - i.startup_time, 2) AS uptime_days FROM v$instance i;","expect":"Der Instanzstatus sollte OPEN sein. Ist startup_time unerwartet aktuell (frischer Start), kann ein ungeplanter Neustart stattgefunden haben; untersuche das. Die Spalte version_full gibt es ab 18c; bei 12c und früher die Spalte version verwenden.","severity":"safe"},{"id":"alertlog-errors","group":"Allgemein","title":"Aktuelle kritische Fehler im Alert Log (ORA-)","command":"SELECT originating_timestamp, message_text FROM v$diag_alert_ext WHERE message_text LIKE '%ORA-%' AND originating_timestamp > SYSTIMESTAMP - INTERVAL '1' DAY ORDER BY originating_timestamp DESC FETCH FIRST 50 ROWS ONLY;","expect":"Für die letzten 24 Stunden sollten keine ORA-Fehler zurückgegeben werden. Einträge wie ORA-00600, ORA-07445 und ORA-01578 (Blockkorruption) erfordern sofortiges Handeln.","severity":"danger"},{"id":"invalid-objects","group":"Allgemein","title":"INVALID-Objekte","command":"SELECT owner, object_type, COUNT(*) AS invalid_count FROM dba_objects WHERE status = 'INVALID' GROUP BY owner, object_type ORDER BY invalid_count DESC;","expect":"Idealerweise gibt es keine INVALID-Objekte. Falls doch, mit EXEC DBMS_UTILITY.COMPILE_SCHEMA oder utlrp.sql neu kompilieren; ungültige Objekte unter SYS/SYSTEM haben Vorrang.","severity":"caution"},{"id":"tablespace-usage","group":"Kapazität","title":"Tablespace-Nutzung","command":"SELECT df.tablespace_name, ROUND(df.total_mb, 1) AS total_mb, ROUND(df.total_mb - NVL(fs.free_mb, 0), 1) AS used_mb, ROUND((df.total_mb - NVL(fs.free_mb, 0)) / df.total_mb * 100, 1) AS pct_used FROM (SELECT tablespace_name, SUM(bytes)/1024/1024 total_mb FROM dba_data_files GROUP BY tablespace_name) df LEFT JOIN (SELECT tablespace_name, SUM(bytes)/1024/1024 free_mb FROM dba_free_space GROUP BY tablespace_name) fs ON df.tablespace_name = fs.tablespace_name ORDER BY pct_used DESC;","expect":"pct_used über 85% ist die Warnschwelle, über 90% kritisch. Tablespaces mit deaktiviertem Autoextend laufen eher voll; füge ein Datafile hinzu oder aktiviere Autoextend.","severity":"danger"},{"id":"largest-segments","group":"Kapazität","title":"Größte Segmente (Tabellen/Indizes)","command":"SELECT owner, segment_name, segment_type, ROUND(bytes/1024/1024/1024, 2) AS size_gb FROM dba_segments ORDER BY bytes DESC FETCH FIRST 20 ROWS ONLY;","expect":"Zeigt die Segmente mit dem größten Platzbedarf. Unerwartetes Wachstum kann auf Bedarf an Archivierung/Bereinigung oder Partitionierung hinweisen.","severity":"safe"},{"id":"undo-temp-usage","group":"Kapazität","title":"Nutzung der UNDO- und TEMP-Tablespaces","command":"SELECT ts.tablespace_name, ts.contents, ROUND(SUM(ss.used_blocks) * ts.block_size / 1024 / 1024, 1) AS used_mb FROM v$sort_segment ss JOIN (SELECT tablespace_name, contents, block_size FROM dba_tablespaces WHERE contents = 'TEMPORARY') ts ON ss.tablespace_name = ts.tablespace_name GROUP BY ts.tablespace_name, ts.contents, ts.block_size UNION ALL SELECT tablespace_name, 'UNDO', ROUND(SUM(bytes)/1024/1024, 1) FROM dba_undo_extents WHERE status = 'ACTIVE' GROUP BY tablespace_name;","expect":"Stößt die TEMP-Nutzung ständig an die Obergrenze, gibt es große Sort-/Hash-Operationen. Kann der aktive UNDO-Bereich die Retention nicht erfüllen, droht ORA-01555 (snapshot too old).","severity":"caution"},{"id":"sessions-vs-limit","group":"Kapazität","title":"Session-Anzahl vs. Limit","command":"SELECT resource_name, current_utilization, max_utilization, limit_value FROM v$resource_limit WHERE resource_name IN ('sessions','processes','transactions');","expect":"Nähert sich current_utilization dem limit_value (vor allem über 85%), können neue Verbindungen abgelehnt werden. max_utilization zeigt den historischen Höchstwert; prüfe den Parameter processes.","severity":"caution"},{"id":"fra-usage","group":"Kapazität","title":"Nutzung der Flash Recovery Area (FRA)","command":"SELECT name, ROUND(space_limit/1024/1024/1024, 2) AS limit_gb, ROUND(space_used/1024/1024/1024, 2) AS used_gb, ROUND(space_used/space_limit*100, 1) AS pct_used FROM v$recovery_file_dest;","expect":"Erreicht die FRA 85%, wird eine Warnung ins Alert Log geschrieben; bei 100% stoppt das Schreiben der Archivlogs und die Datenbank hängt (archiver hung, ORA-00257). Bereinige RMAN-Backups/Archivlogs oder erhöhe db_recovery_file_dest_size.","severity":"danger"},{"id":"buffer-cache-hit","group":"Performance","title":"Buffer-Cache-Trefferquote","command":"SELECT ROUND((1 - (phy.value - dir.value) / (con.value + cur.value)) * 100, 2) AS buffer_hit_ratio FROM v$sysstat con, v$sysstat cur, v$sysstat phy, v$sysstat dir WHERE con.name = 'consistent gets' AND cur.name = 'db block gets' AND phy.name = 'physical reads' AND dir.name = 'physical reads direct';","expect":"Auf OLTP-Systemen wird typischerweise ein Wert über 90% erwartet. Dauerhaft niedrige Werte können auf eine zu kleine SGA/einen zu kleinen Buffer Cache oder schlechte Indexierung hinweisen; bewerte den Wert zusammen mit den Wait Events, nicht isoliert.","severity":"safe"},{"id":"top-sql","group":"Performance","title":"Teuerste SQL-Anweisungen (Laufzeit)","command":"SELECT sql_id, ROUND(elapsed_time/1000000, 1) AS elapsed_sec, executions, ROUND(elapsed_time/1000000/GREATEST(executions,1), 3) AS sec_per_exec, SUBSTR(sql_text, 1, 100) AS sql_text FROM v$sqlarea ORDER BY elapsed_time DESC FETCH FIRST 15 ROWS ONLY;","expect":"Abfragen mit der höchsten Gesamtlaufzeit. Solche mit hohem sec_per_exec oder sehr vielen executions sind Tuning-Kandidaten; untersuche sie mit AWR/SQL Tuning Advisor.","severity":"safe"},{"id":"long-active-sessions","group":"Performance","title":"Lang laufende aktive Sessions","command":"SELECT s.sid, s.serial#, s.username, s.status, s.last_call_et AS active_seconds, s.event, SUBSTR(q.sql_text, 1, 80) AS sql_text FROM v$session s LEFT JOIN v$sqlarea q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE' AND s.type = 'USER' AND s.last_call_et > 60 ORDER BY s.last_call_et DESC;","expect":"Aktive Sessions mit sehr hohem last_call_et (Sekunden) können hängen oder eine schwere Abfrage ausführen. Die Spalte event zeigt die Ressource, auf die gewartet wird.","severity":"caution"},{"id":"blocking-sessions","group":"Performance","title":"Blockierende und wartende Sessions (Sperren)","command":"SELECT bs.sid AS blocker_sid, bs.username AS blocker_user, ws.sid AS waiter_sid, ws.username AS waiter_user, ws.seconds_in_wait, ws.event FROM v$session ws JOIN v$session bs ON ws.blocking_session = bs.sid WHERE ws.blocking_session IS NOT NULL ORDER BY ws.seconds_in_wait DESC;","expect":"Es sollten keine Zeilen zurückkommen. Blockierende Sessions verursachen Verzögerungen/Deadlocks in der Anwendung; hält blocker_sid eine Sperre lange (großes seconds_in_wait), stimme dich mit dem Anwendungsteam ab und führe bei Bedarf ALTER SYSTEM KILL SESSION aus.","severity":"danger"},{"id":"rman-last-backup","group":"Backup & Recovery","title":"Status des letzten erfolgreichen RMAN-Backups","command":"SELECT input_type, status, start_time, end_time, ROUND(output_bytes/1024/1024/1024, 2) AS output_gb FROM v$rman_backup_job_details ORDER BY start_time DESC FETCH FIRST 10 ROWS ONLY;","expect":"Das letzte Backup sollte status = COMPLETED und eine für deine RPO-Vorgabe ausreichend aktuelle start_time haben (z. B. innerhalb der letzten 24 Stunden). Einträge mit FAILED oder 'COMPLETED WITH WARNINGS' müssen untersucht werden.","severity":"danger"},{"id":"archivelog-mode","group":"Backup & Recovery","title":"Archivelog-Modus und Archiver-Status","command":"SELECT log_mode, force_logging, (SELECT COUNT(*) FROM v$archived_log WHERE first_time > SYSDATE - 1) AS logs_last_24h FROM v$database;","expect":"Eine Produktionsdatenbank sollte log_mode = ARCHIVELOG haben (sonst ist keine Point-in-Time-Recovery möglich). Wurden in den letzten 24 Stunden keine Archivlogs erzeugt, hängt möglicherweise der Archiver.","severity":"danger"},{"id":"locked-expired-accounts","group":"Sicherheit","title":"Gesperrte oder abgelaufene Konten","command":"SELECT username, account_status, lock_date, expiry_date, profile FROM dba_users WHERE account_status != 'OPEN' ORDER BY username;","expect":"LOCKED/EXPIRED kritische Anwendungskonten verursachen unerwartete Ausfälle. Dass Standardkonten (SCOTT, HR usw.) gesperrt/deaktiviert sind, ist gut. EXPIRED(GRACE) weist auf einen bevorstehenden Passwortablauf hin.","severity":"caution"},{"id":"privileged-users","group":"Sicherheit","title":"Hochprivilegierte Benutzer (Rolle DBA)","command":"SELECT grantee, granted_role, admin_option FROM dba_role_privs WHERE granted_role IN ('DBA','PDB_DBA') AND grantee NOT IN ('SYS','SYSTEM') ORDER BY grantee;","expect":"Die Rolle DBA sollten nur notwendige, bekannte Konten haben. Unerwartete Empfänger oder solche mit admin_option = YES sind ein Risiko für Rechteausweitung; prüfe sie nach dem Least-Privilege-Prinzip. Für Konten mit SYSDBA-/SYSOPER-Privilegien prüfe zusätzlich: SELECT username, sysdba, sysoper FROM v$pwfile_users;","severity":"caution"},{"id":"default-password-users","group":"Sicherheit","title":"Offene Konten mit unveränderten Standardpasswörtern","command":"SELECT du.username, du.account_status, du.profile, CASE WHEN dp.username IS NOT NULL THEN 'YES' ELSE 'NO' END AS has_default_password FROM dba_users du LEFT JOIN (SELECT username FROM dba_users_with_defpwd) dp ON du.username = dp.username WHERE du.account_status = 'OPEN' ORDER BY du.username;","expect":"Bei Konten mit has_default_password = YES muss das Passwort sofort geändert oder das Konto gesperrt werden (ALTER USER ... ACCOUNT LOCK). Prüfe für die Richtlinie zur Passwortlebensdauer auch PASSWORD_LIFE_TIME in DBA_PROFILES.","severity":"caution"},{"id":"dataguard-lag","group":"Replikation","title":"Data Guard-Standby-Verzögerung","command":"SELECT name, value, unit, time_computed FROM v$dataguard_stats WHERE name IN ('apply lag','transport lag');","expect":"apply lag und transport lag sollten möglichst nahe 0 liegen. Verzögerungen im Bereich von Minuten/Stunden deuten auf ein Netzwerk- oder MRP-Problem (Managed Recovery Process) hin; das Failover-Ziel ist gefährdet.","severity":"danger"},{"id":"dataguard-mrp","group":"Replikation","title":"Status des Standby-Apply-Prozesses (MRP)","command":"SELECT name, role, action, thread#, sequence#, block# FROM v$dataguard_process WHERE name LIKE 'MRP%' OR role LIKE 'RFS%' ORDER BY name;","expect":"Auf einem Physical Standby sollte der Prozess MRP0 mit action APPLYING_LOG oder WAIT_FOR_LOG sichtbar sein. Fehlt MRP, ist das Apply gestoppt; fehlt RFS, kommen keine Logs vom Primary an. v$dataguard_process gibt es ab 12.2; bei 12.1 und früher die (inzwischen veraltete) v$managed_standby verwenden (Spalten process, status).","severity":"danger"}]},"health/postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"checks":[{"id":"version-uptime","group":"Allgemein","title":"Version und Laufzeit","command":"SELECT version() AS pg_version, pg_postmaster_start_time() AS started_at, date_trunc('second', now() - pg_postmaster_start_time()) AS uptime;","expect":"Zeigt Version und Startzeit. Ist die Laufzeit unerwartet kurz (kürzlicher Start), kann ein ungeplanter Neustart oder Absturz stattgefunden haben; prüfe die Logs.","severity":"safe"},{"id":"recent-errors","group":"Allgemein","title":"Checkpoint-Statistiken","command":"-- PG17+:\nSELECT num_timed, num_requested, buffers_written, stats_reset FROM pg_stat_checkpointer;\n-- PG16 and earlier:\nSELECT checkpoints_timed, checkpoints_req, buffers_checkpoint, buffers_backend, stats_reset FROM pg_stat_bgwriter;","expect":"Ist die Anzahl angeforderter Checkpoints (num_requested / checkpoints_req) im Verhältnis zu zeitgesteuerten (num_timed / checkpoints_timed) hoch, füllt sich das WAL zu schnell; erhöhe max_wal_size. In PG17 sind die Zähler von pg_stat_bgwriter nach pg_stat_checkpointer gewandert.","severity":"caution"},{"id":"table-bloat","group":"Allgemein","title":"Aufgeblähte Tabellen und Anteil toter Tupel","command":"SELECT schemaname, relname, n_live_tup, n_dead_tup, CASE WHEN n_live_tup > 0 THEN round(n_dead_tup * 100.0 / n_live_tup, 1) ELSE 0 END AS dead_pct, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY n_dead_tup DESC LIMIT 20;","expect":"Tabellen mit hohem dead_pct (z. B. über 20%) sind aufgebläht; das beeinträchtigt die VACUUM-Performance und die Plattennutzung. Ist last_autovacuum sehr alt, prüfe die Autovacuum-Einstellungen oder führe ein manuelles VACUUM (ANALYZE) aus.","severity":"caution"},{"id":"database-size","group":"Kapazität","title":"Datenbankgrößen","command":"SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size, pg_database_size(datname) AS size_bytes FROM pg_database WHERE datistemplate = false ORDER BY pg_database_size(datname) DESC;","expect":"Zeigt die Datenbankgrößen. Bewerte unerwartetes Wachstum zusammen mit der Plattenkapazität (df -h); PostgreSQL stoppt Schreibvorgänge, wenn die Platte voll ist.","severity":"danger"},{"id":"largest-tables","group":"Kapazität","title":"Größte Tabellen (inklusive Indizes)","command":"SELECT schemaname, relname, pg_size_pretty(pg_total_relation_size(relid)) AS total_size, pg_size_pretty(pg_relation_size(relid)) AS table_size, pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_toast_size FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;","expect":"Zeigt die Tabellen mit dem größten Platzbedarf. Ist index_toast_size deutlich größer als die Tabellengröße, gibt es möglicherweise übermäßige/ungenutzte Indizes; erwäge Archivierung oder Partitionierung.","severity":"safe"},{"id":"temp-files","group":"Kapazität","title":"Nutzung temporärer Dateien","command":"SELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_size, stats_reset FROM pg_stat_database WHERE temp_files > 0 ORDER BY temp_bytes DESC;","expect":"Hohe temp_files/temp_bytes zeigen, dass work_mem nicht ausreicht und Sort-/Hash-Operationen auf die Platte ausweichen. Erwäge, work_mem zu erhöhen oder die Abfragen zu optimieren.","severity":"caution"},{"id":"connections-vs-limit","group":"Kapazität","title":"Verbindungsanzahl vs. max_connections","command":"SELECT (SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend') AS current_connections, (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max_connections, round((SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend') * 100.0 / (SELECT setting::int FROM pg_settings WHERE name = 'max_connections'), 1) AS pct_used;","expect":"Gezählt werden nur Client-Verbindungen (der Filter backend_type = 'client backend' ist nötig, da pg_stat_activity auch Hintergrundprozesse enthält). Ab 80% pct_used droht die Ablehnung neuer Verbindungen (vor allem, sobald superuser_reserved_connections erreicht ist). Ein Connection Pooler (PgBouncer) wird empfohlen.","severity":"caution"},{"id":"wal-directory-size","group":"Kapazität","title":"Größe des WAL-Verzeichnisses und Rückhaltung durch Replication Slots","command":"SELECT (SELECT count(*) FROM pg_ls_waldir()) AS wal_file_count, pg_size_pretty((SELECT sum(size) FROM pg_ls_waldir())) AS wal_total_size, slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots ORDER BY slot_name;","expect":"Inaktive (active = false) Replication Slots lassen WAL anwachsen und das Verzeichnis pg_wal volllaufen. Ist retained_wal groß, prüfe den Eigentümer des Slots (slot_type, database, plugin): active = false kann auch auf einen vorübergehend getrennten Standby oder einen Slot eines logischen Subscribers/CDC (z. B. Debezium) hinweisen. Ist er tatsächlich ungenutzt, lösche ihn mit pg_drop_replication_slot; ein Standby, der vom gelöschten Slot abhing, muss neu aufgebaut und die Subscriber-/CDC-Seite neu synchronisiert werden. Für dauerhaften Schutz eine Obergrenze mit max_slot_wal_keep_size setzen (PG13+).","severity":"danger"},{"id":"cache-hit-ratio","group":"Performance","title":"Buffer-Cache-Trefferquote","command":"SELECT sum(heap_blks_read) AS heap_read, sum(heap_blks_hit) AS heap_hit, round(sum(heap_blks_hit) * 100.0 / NULLIF(sum(heap_blks_hit) + sum(heap_blks_read), 0), 2) AS cache_hit_ratio FROM pg_statio_user_tables;","expect":"Für OLTP-Workloads liegt das Ziel typischerweise über 99%. Ein dauerhaft niedriger Wert kann darauf hinweisen, dass shared_buffers zu klein ist oder das Working Set nicht in den Speicher passt.","severity":"safe"},{"id":"top-queries","group":"Performance","title":"Teuerste Abfragen (pg_stat_statements)","command":"SELECT round(total_exec_time::numeric, 1) AS total_ms, calls, round(mean_exec_time::numeric, 2) AS mean_ms, round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 1) AS pct, left(query, 100) AS query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 15;","expect":"pg_stat_statements muss in shared_preload_libraries stehen (erfordert Neustart) und in der Datenbank installiert sein (CREATE EXTENSION pg_stat_statements). Die Namen total_exec_time/mean_exec_time gelten ab PG13; PG12 nutzt total_time/mean_time. Abfragen mit der höchsten Gesamtzeit sind Tuning-Kandidaten; führe für solche mit hohem mean_ms EXPLAIN ANALYZE aus.","severity":"safe"},{"id":"long-running-queries","group":"Performance","title":"Lang laufende aktive Abfragen","command":"SELECT pid, usename, state, now() - query_start AS duration, wait_event_type, wait_event, left(query, 80) AS query FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '1 minute' AND backend_type = 'client backend' ORDER BY duration DESC;","expect":"Aktive Abfragen, die länger als 1 Minute laufen, können hängen oder schwer sein. wait_event zeigt, worauf gewartet wird. Diese Abfrage listet nur aktive Abfragen; prüfe Sessions im Zustand 'idle in transaction' (die verhindern, dass VACUUM tote Tupel entfernt) separat mit state = 'idle in transaction' und xact_start.","severity":"caution"},{"id":"blocking-locks","group":"Performance","title":"Blockierende Sessions und wartende Sperren","command":"SELECT blocked.pid AS blocked_pid, blocked.usename AS blocked_user, blocking.pid AS blocking_pid, blocking.usename AS blocking_user, now() - blocked.query_start AS blocked_duration, left(blocked.query, 60) AS blocked_query FROM pg_stat_activity blocked JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid)) WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;","expect":"Es sollten keine Zeilen zurückkommen. Blockierende Sessions (blocking_pid) verursachen Verzögerungen und Timeouts in der Anwendung; untersuche eine lange blockierende Transaktion und beende sie bei Bedarf mit pg_terminate_backend.","severity":"danger"},{"id":"archiver-status","group":"Backup & Recovery","title":"Zustand der WAL-Archivierung (pg_stat_archiver)","command":"SELECT archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, now() - last_archived_time AS since_last_archive FROM pg_stat_archiver;","expect":"failed_count sollte 0 sein und last_failed_time älter als last_archived_time. Ist since_last_archive sehr groß, ist die Archivierung gestoppt (Fehler in archive_command); die PITR-Backupkette ist unterbrochen.","severity":"danger"},{"id":"last-basebackup","group":"Backup & Recovery","title":"Aktualität des letzten Backups und des WAL-Archivs","command":"-- Base backup time is not stored in the database; read it from the backup tool: pgbackrest info / barman list-backup <server>\nSELECT pg_is_in_recovery() AS in_recovery, last_archived_wal, last_archived_time, now() - last_archived_time AS since_last_archive, failed_count, last_failed_time FROM pg_stat_archiver;","expect":"Prüfe den Zeitpunkt des letzten Base Backups im Backup-Tool (Job-Logs von pgBackRest / Barman / pg_basebackup); er muss für deine RPO-Vorgabe aktuell genug sein. Für PITR muss auch das WAL-Archiv lückenlos sein: Ist since_last_archive groß oder steigt failed_count, ist die Archivkette möglicherweise unterbrochen.","severity":"danger"},{"id":"locked-expired-roles","group":"Sicherheit","title":"Abgelaufene oder bald ablaufende Login-Rollen","command":"SELECT rolname, rolcanlogin, rolvaliduntil, rolconnlimit FROM pg_roles WHERE rolcanlogin = true AND (rolvaliduntil IS NOT NULL AND rolvaliduntil < now() + interval '7 days') ORDER BY rolvaliduntil;","expect":"Anwendungsrollen, deren rolvaliduntil überschritten ist, können sich nicht mehr anmelden, was zu Ausfällen führt. Plane die Erneuerung von Passwörtern, die innerhalb der nächsten 7 Tage ablaufen, rechtzeitig.","severity":"caution"},{"id":"superuser-roles","group":"Sicherheit","title":"Hochprivilegierte Rollen (SUPERUSER / Create-Rechte)","command":"SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolbypassrls FROM pg_roles WHERE rolsuper = true OR rolcreaterole = true OR rolbypassrls = true ORDER BY rolsuper DESC, rolname;","expect":"SUPERUSER sollte es nur bei erforderlichen, bekannten Konten geben. Rollen mit rolbypassrls = true umgehen Row-Level Security (RLS); unerwartet hohe Rechte sollten nach dem Least-Privilege-Prinzip entzogen werden.","severity":"caution"},{"id":"hba-trust-auth","group":"Sicherheit","title":"Schwache Authentifizierungsregeln (trust)","command":"SELECT type, database, user_name, address, auth_method FROM pg_hba_file_rules WHERE auth_method IN ('trust','password') ORDER BY line_number;","expect":"auth_method = trust erlaubt Zugriff ohne Passwort und ist in Produktion gefährlich; 'password' sendet das Passwort im Klartext. Ersetze sie durch scram-sha-256. (Erfordert PG 10+.)","severity":"danger"},{"id":"replication-lag","group":"Replikation","title":"Verzögerung der Streaming-Replikation (Primary-Seite)","command":"SELECT client_addr, state, sync_state, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS sent_lag, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_lag, write_lag, flush_lag, replay_lag AS replay_time_lag FROM pg_stat_replication ORDER BY client_addr;","expect":"Auf dem Primary ausführen. state sollte streaming sein und replay_lag (Bytes/Zeit) klein. Erscheint der Standby gar nicht, ist die Verbindung abgerissen; ist sync_state = async, funktioniert die synchrone Replikation nicht wie erwartet.","severity":"danger"},{"id":"replica-lag-standby","group":"Replikation","title":"Replikationsverzögerung (Standby-Seite)","command":"SELECT pg_is_in_recovery() AS in_recovery, CASE WHEN pg_is_in_recovery() THEN date_trunc('second', now() - pg_last_xact_replay_timestamp()) ELSE NULL END AS replication_delay, pg_last_wal_receive_lsn() AS receive_lsn, pg_last_wal_replay_lsn() AS replay_lsn;","expect":"Auf dem Standby-Server ausführen. in_recovery sollte true sein. Liegt replication_delay im Bereich von Minuten/Stunden, gibt es möglicherweise eine Apply-Verzögerung; der Wert wächst jedoch auch, wenn auf dem Primary nicht geschrieben wird (Fehlalarm), bewerte ihn daher zusammen mit der LSN-Differenz; ist der Abstand zwischen receive_lsn und replay_lsn groß, kommt das WAL-Replay nicht hinterher.","severity":"danger"}]},"health/mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"checks":[{"id":"version-uptime","group":"Allgemein","title":"Version und Laufzeit","command":"SELECT VERSION() AS mysql_version, VARIABLE_VALUE AS uptime_seconds, ROUND(VARIABLE_VALUE/86400, 2) AS uptime_days FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Uptime';","expect":"Zeigt Version und Laufzeit in Sekunden. Ist uptime_seconds unerwartet klein, kann ein ungeplanter Neustart oder Absturz stattgefunden haben; prüfe das Error Log.","severity":"safe"},{"id":"error-log-status","group":"Allgemein","title":"Aktuelle Einträge im Error Log (performance_schema)","command":"SELECT logged, prio, error_code, subsystem, LEFT(data, 120) AS message FROM performance_schema.error_log WHERE prio IN ('Error','Warning') ORDER BY logged DESC LIMIT 30;","expect":"Zeigt aktuelle Fehler und Warnungen (erfordert MySQL 8.0+). Untersuche Einträge mit prio = 'Error' (vor allem InnoDB-, Sperr- und Plattenfehler). In älteren Versionen die Datei log_error des Servers prüfen.","severity":"danger"},{"id":"crashed-tables","group":"Allgemein","title":"Beschädigte / zu prüfende Tabellen","command":"SELECT table_schema, table_name, engine, table_rows, update_time FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') AND engine IS NOT NULL ORDER BY table_schema, table_name;","expect":"Listet Engine und letzten Aktualisierungszeitpunkt jeder Tabelle. MyISAM-Tabellen können abstürzen; prüfe im Zweifel die Integrität mit CHECK TABLE ... oder mysqlcheck. Für kritische Daten wird InnoDB empfohlen.","severity":"caution"},{"id":"schema-sizes","group":"Kapazität","title":"Schema-(Datenbank-)Größen","command":"SELECT table_schema, ROUND(SUM(data_length + index_length)/1024/1024, 1) AS size_mb, ROUND(SUM(data_free)/1024/1024, 1) AS free_mb FROM information_schema.tables GROUP BY table_schema ORDER BY SUM(data_length + index_length) DESC;","expect":"Zeigt die Schemagrößen und den rückgewinnbaren Platz data_free. Bewerte das zusammen mit der Plattenkapazität; ist free_mb hoch, lässt sich Platz mit OPTIMIZE TABLE zurückgewinnen.","severity":"danger"},{"id":"largest-tables","group":"Kapazität","title":"Größte Tabellen (inklusive data_free / Fragmentierung)","command":"SELECT table_schema, table_name, engine, ROUND((data_length + index_length)/1024/1024, 1) AS total_mb, ROUND(data_free/1024/1024, 1) AS data_free_mb, table_rows FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') ORDER BY (data_length + index_length) DESC LIMIT 20;","expect":"Zeigt die größten Tabellen. Tabellen mit hohem data_free_mb sind fragmentiert; Platz lässt sich mit OPTIMIZE TABLE zurückgewinnen. Erwäge für sehr große Tabellen Partitionierung.","severity":"safe"},{"id":"innodb-tablespace","group":"Kapazität","title":"Nutzung des InnoDB-Tablespace und des temporären Bereichs","command":"SELECT FILE_NAME, TABLESPACE_NAME, ROUND(TOTAL_EXTENTS * EXTENT_SIZE / 1024 / 1024, 1) AS total_mb, ROUND((TOTAL_EXTENTS - FREE_EXTENTS) * EXTENT_SIZE / 1024 / 1024, 1) AS used_mb FROM information_schema.files WHERE FILE_TYPE = 'TABLESPACE' ORDER BY TOTAL_EXTENTS DESC LIMIT 20;","expect":"Zeigt, wie voll die InnoDB-Datafiles sind. Ist innodb_file_per_table deaktiviert, wächst ibdata1 ständig und schrumpft nie. Prüfe bei aufgeblähtem Temp-Tablespace auch die Ausgabe von SHOW ENGINE INNODB STATUS.","severity":"caution"},{"id":"connections-vs-limit","group":"Kapazität","title":"Verbindungsanzahl und Limit max_connections","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Threads_connected') AS connected, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'max_connections') AS max_connections, (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Max_used_connections') AS max_used, (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Aborted_connects') AS aborted_connects;","expect":"Nähert sich max_used dem Wert max_connections (über 85%), drohen Fehler 'Too many connections'. Ein hoher Wert aborted_connects weist auf Authentifizierungs-/Netzwerkprobleme hin; nutze einen Connection Pool.","severity":"caution"},{"id":"binlog-size","group":"Kapazität","title":"Binary-Log-Dateien und Gesamtgröße","command":"SHOW BINARY LOGS;","expect":"Listet die Binlog-Dateien und ihre Größen. Belastet die Gesamtgröße den Plattenplatz, prüfe binlog_expire_logs_seconds (vor 8.0 expire_logs_days; diese Variable ist seit 8.0 veraltet und in 8.4 entfernt); wird Replikation genutzt, nicht bereinigen, bevor die Replicas sie gelesen haben.","severity":"caution"},{"id":"buffer-pool-hit","group":"Performance","title":"Trefferquote des InnoDB Buffer Pool","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') AS read_requests, (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') AS disk_reads, ROUND((1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')) * 100, 3) AS hit_ratio_pct;","expect":"Für hit_ratio_pct wird typischerweise ein Wert über 99% angestrebt. Bleibt er niedrig, reicht innodb_buffer_pool_size nicht aus; erwäge auf einem dedizierten Server eine Erhöhung auf ~70-80% des RAM (auf einem geteilten Server weniger).","severity":"safe"},{"id":"top-queries","group":"Performance","title":"Teuerste Abfragen (sys.statement_analysis)","command":"SELECT query, db, exec_count, total_latency, avg_latency, rows_sent_avg, rows_examined_avg FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 15;","expect":"Das sys-Schema und performance_schema müssen aktiviert sein. Abfragen mit dem höchsten total_latency sind Tuning-Kandidaten. Ist rows_examined_avg deutlich größer als rows_sent_avg, fehlt ein Index; untersuche das mit EXPLAIN.","severity":"safe"},{"id":"long-running-queries","group":"Performance","title":"Lang laufende aktive Abfragen","command":"SELECT id, user, host, db, command, time AS seconds, state, LEFT(info, 80) AS query FROM information_schema.processlist WHERE command != 'Sleep' AND time > 60 ORDER BY time DESC;","expect":"Abfragen, die länger als 60 Sekunden laufen, können hängen oder schwer sein. Die Spalte state zeigt, worauf gewartet wird. Auch lange Sessions im Zustand 'Sleep' erschöpfen den Connection Pool; nutze bei Bedarf KILL <id>.","severity":"caution"},{"id":"blocking-locks","group":"Performance","title":"Blockierende Sessions und wartende Sperren","command":"SELECT r.trx_id AS waiting_trx, r.trx_mysql_thread_id AS waiting_thread, TIMESTAMPDIFF(SECOND, r.trx_wait_started, NOW()) AS wait_seconds, LEFT(r.trx_query, 60) AS waiting_query, b.trx_mysql_thread_id AS blocking_thread, LEFT(b.trx_query, 60) AS blocking_query FROM performance_schema.data_lock_waits w JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_engine_transaction_id JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_engine_transaction_id ORDER BY wait_seconds DESC;","expect":"Sollte keine Zeilen liefern. Eine blockierende Transaktion (blocking_thread) verursacht Verzögerungen in der Anwendung und Fehler durch lock wait timeout. Untersuche eine lange blockierende Transaktion und beende sie bei Bedarf mit KILL. (Syntax für MySQL 8.0.)","severity":"danger"},{"id":"binlog-enabled","group":"Backup & Recovery","title":"Einstellungen für Binary Log und Recovery","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'log_bin') AS log_bin, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'binlog_format') AS binlog_format, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'sync_binlog') AS sync_binlog, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'innodb_flush_log_at_trx_commit') AS flush_log_commit;","expect":"Für Point-in-Time-Recovery muss log_bin ON sein. Für Dauerhaftigkeit werden sync_binlog = 1 und innodb_flush_log_at_trx_commit = 1 empfohlen (sonst können bei einem Absturz Transaktionen verloren gehen). binlog_format = ROW ist für Replikation am sichersten.","severity":"danger"},{"id":"last-backup-note","group":"Backup & Recovery","title":"Zeitpunkt des letzten Backups (Binlog-/Tool-Indikator)","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Uptime') AS uptime_seconds, (SELECT MAX(update_time) FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys')) AS last_data_change;","expect":"MySQL verfolgt den Backupstatus nicht selbst; prüfe den Zeitpunkt des letzten erfolgreichen Backups in den Logs von mysqldump / MySQL Shell dump / Percona XtraBackup (mysqlpump ist seit 8.0.34 veraltet und in 8.4 entfernt). Das Backup muss so aktuell sein, wie deine RPO-Vorgabe es verlangt (z. B. innerhalb der letzten 24 Stunden).","severity":"danger"},{"id":"locked-expired-accounts","group":"Sicherheit","title":"Gesperrte Konten oder Konten mit abgelaufenem Passwort","command":"SELECT user, host, account_locked, password_expired, password_lifetime FROM mysql.user ORDER BY user, host;","expect":"Kritische Anwendungskonten mit account_locked = 'Y' oder password_expired = 'Y' verursachen Ausfälle. Ist password_lifetime NULL und der globale Ablauf deaktiviert, läuft das Passwort nie ab; das kann eine Lücke in der Richtlinie sein.","severity":"caution"},{"id":"anonymous-nopassword-users","group":"Sicherheit","title":"Anonyme Benutzer und Benutzer ohne Passwort","command":"SELECT user, host, plugin, IF(authentication_string = '', 'EMPTY_PASSWORD', 'set') AS password_status FROM mysql.user WHERE user = '' OR authentication_string = '' ORDER BY host;","expect":"Sollte keine Zeilen liefern. Anonyme Benutzer (user = '') und Konten ohne Passwort sind eine ernste Sicherheitslücke; entferne sie mit mysql_secure_installation oder vergib starke Passwörter.","severity":"danger"},{"id":"super-privilege-users","group":"Sicherheit","title":"Hochprivilegierte Benutzer (SUPER / GRANT)","command":"SELECT grantee, privilege_type FROM information_schema.user_privileges WHERE privilege_type IN ('SUPER','GRANT OPTION','SHUTDOWN','FILE','CREATE USER') ORDER BY grantee, privilege_type;","expect":"Privilegien wie SUPER, FILE und GRANT OPTION sollten nur notwendige, bekannte Konten haben. Unerwartete Empfänger sind ein Risiko für Rechteausweitung; achte besonders auf hochprivilegierte Konten, die mit % (beliebiger Host) definiert sind.","severity":"caution"},{"id":"replica-status","group":"Replikation","title":"Replica-(Standby-)Status und Verzögerung","command":"SHOW REPLICA STATUS;","expect":"Auf der Replica ausführen (MySQL 8.0.22+). Replica_IO_Running und Replica_SQL_Running müssen beide 'Yes' sein. Seconds_Behind_Source sollte klein sein; ist es NULL, ist die Replikation gestoppt. Prüfe das Feld Last_Error. (In älteren Versionen SHOW SLAVE STATUS.)","severity":"danger"},{"id":"replication-threads","group":"Replikation","title":"Status von Replikationsverbindung und Applier (performance_schema)","command":"SELECT c.channel_name, c.service_state AS io_state, a.service_state AS sql_state, c.last_error_message AS io_error, w.last_error_message AS sql_error FROM performance_schema.replication_connection_status c JOIN performance_schema.replication_applier_status a ON c.channel_name = a.channel_name LEFT JOIN performance_schema.replication_applier_status_by_coordinator w ON w.channel_name = a.channel_name;","expect":"io_state und sql_state müssen 'ON' sein. Ist io_error/sql_error befüllt, ist die Replikation fehlgeschlagen und gestoppt; in Multi-Source-Setups muss jeder channel_name für sich fehlerfrei sein.","severity":"danger"}]},"health/mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"checks":[{"id":"version-uptime","group":"Allgemein","title":"Version und Laufzeit","command":"SELECT @@VERSION AS version, sqlserver_start_time AS start_time, DATEDIFF(HOUR, sqlserver_start_time, GETDATE()) AS uptime_hours FROM sys.dm_os_sys_info;","expect":"Prüfe Version und Patch-Stand. Ist die Laufzeit sehr kurz (weniger als einige Stunden), kann ein unerwarteter Neustart stattgefunden haben; prüfe das SQL Server Error Log.","severity":"safe"},{"id":"database-states","group":"Allgemein","title":"Datenbanken in einem Problemzustand (SUSPECT / RECOVERY_PENDING)","command":"SELECT name AS database_name, state_desc AS state, recovery_model_desc AS recovery_model FROM sys.databases WHERE state_desc <> N'ONLINE' ORDER BY name;","expect":"Alle Produktionsdatenbanken müssen ONLINE sein. SUSPECT, RECOVERY_PENDING oder EMERGENCY erfordert sofortiges Handeln; starte die Wiederherstellung und prüfe das Error Log.","severity":"danger"},{"id":"recent-errors","group":"Allgemein","title":"Aktuelle wesentliche Fehler (Memory-/Error-Ring-Buffer)","command":"SELECT TOP 20 CAST(record AS XML).value('(//Record/@id)[1]', 'int') AS record_id, DATEADD(ms, timestamp - (SELECT ms_ticks FROM sys.dm_os_sys_info), GETDATE()) AS event_time, CAST(record AS XML) AS record_xml FROM sys.dm_os_ring_buffers WHERE ring_buffer_type = N'RING_BUFFER_EXCEPTION' ORDER BY timestamp DESC;","expect":"Prüfe die aktuellen Exception-/Fehlereinträge. Häufig wiederkehrende Fehler 823/824/825 (I/O-Konsistenz) deuten auf Plattenkorruption hin. Für Details lies auch das Error Log auf dem Server mit xp_readerrorlog.","severity":"caution"},{"id":"data-log-file-usage","group":"Kapazität","title":"Nutzung von Daten- und Logdateien","command":"SELECT DB_NAME() AS database_name, name AS logical_name, type_desc AS file_type, size/128.0 AS allocated_mb, CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS used_mb, (size - CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT))/128.0 AS free_mb, CASE WHEN max_size = -1 THEN N'Unlimited' WHEN max_size = 268435456 THEN N'2 TB (log)' ELSE CAST(max_size/128 AS VARCHAR(20)) + N' MB' END AS max_size_limit FROM sys.database_files;","expect":"In jeder Datenbank ausführen. Liegt der belegte Platz sehr nahe am zugewiesenen (85%+) und ist Autogrowth begrenzt, plane Wachstum oder füge Platten hinzu. Ist die Logdatei ständig voll, prüfe die Einstellungen für Backup/Recovery Model.","severity":"caution"},{"id":"largest-tables","group":"Kapazität","title":"Größte Tabellen (Größe und Zeilenzahl)","command":"SELECT TOP 20 s.name AS schema_name, t.name AS table_name, p.rows AS row_count, SUM(a.total_pages)*8/1024 AS total_mb, SUM(a.used_pages)*8/1024 AS used_mb FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id JOIN sys.indexes i ON t.object_id = i.object_id JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id JOIN sys.allocation_units a ON p.partition_id = a.container_id WHERE i.index_id <= 1 GROUP BY s.name, t.name, p.rows ORDER BY total_mb DESC;","expect":"Ermittle die Tabellen mit dem größten Platzbedarf. Unerwartetes Wachstum kann auf Bedarf an Archivierung, Indexwartung oder Partitionierung hinweisen.","severity":"safe"},{"id":"tempdb-usage","group":"Kapazität","title":"Platznutzung von tempdb","command":"SELECT SUM(user_object_reserved_page_count)*8/1024 AS user_objects_mb, SUM(internal_object_reserved_page_count)*8/1024 AS internal_objects_mb, SUM(version_store_reserved_page_count)*8/1024 AS version_store_mb, SUM(unallocated_extent_page_count)*8/1024 AS free_mb FROM sys.dm_db_file_space_usage;","expect":"Geht tempdb der freie Platz aus (niedriges unallocated), können Abfragen fehlschlagen. Wächst der Version Store ständig, gibt es lang laufende offene Transaktionen; untersuche das mit sys.dm_tran_active_snapshot_database_transactions.","severity":"caution"},{"id":"connection-count","group":"Kapazität","title":"Verbindungsanzahl und -verteilung","command":"SELECT DB_NAME(dbid) AS database_name, COUNT(*) AS connection_count, SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) AS active FROM sys.sysprocesses WHERE dbid > 0 GROUP BY dbid ORDER BY connection_count DESC;","expect":"Verfolge die Verbindungsanzahl pro Anwendung/Datenbank. Ungewöhnlich viele Verbindungen von einer einzelnen Anwendung können auf ein Leck im Connection Pool hinweisen. Komm dem Limit max worker threads nicht nahe.","severity":"safe"},{"id":"vlf-count","group":"Kapazität","title":"VLF-Anzahl der Logdatei (Fragmentierung)","command":"SELECT DB_NAME(database_id) AS database_name, COUNT(*) AS vlf_count FROM sys.dm_db_log_info(DB_ID()) GROUP BY database_id;","expect":"Eine hohe VLF-Anzahl (mehr als einige Hundert, z. B. 500+) bedeutet, dass die Logdatei in sehr kleinen Schritten gewachsen ist; das verlangsamt Recovery und Backup. Verkleinere das Log und lass es in einem großen Schritt wieder wachsen.","severity":"caution"},{"id":"buffer-cache-ple","group":"Performance","title":"Buffer Cache Hit Ratio und Page Life Expectancy","command":"SELECT (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name = 'Buffer cache hit ratio') AS hit_ratio_raw, (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name = 'Buffer cache hit ratio base') AS hit_ratio_base, (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name = 'Page life expectancy' AND object_name LIKE '%Buffer Manager%') AS page_life_expectancy_sec;","expect":"Die Buffer Cache Hit Ratio sollte idealerweise 99%+ betragen (hit_ratio_raw / hit_ratio_base). Ist die Page Life Expectancy dauerhaft niedrig (Faustregel: unter 300 s pro 4 GB pro NUMA-Knoten), besteht Speicherdruck; mehr RAM oder Abfrageoptimierung ist nötig.","severity":"caution"},{"id":"expensive-queries","group":"Performance","title":"Teuerste Abfragen (CPU und Reads)","command":"SELECT TOP 15 qs.total_worker_time/1000 AS total_cpu_ms, qs.execution_count AS execution_count, qs.total_logical_reads AS total_logical_reads, qs.total_worker_time/qs.execution_count/1000 AS avg_cpu_ms, SUBSTRING(st.text, (qs.statement_start_offset/2)+1, ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END - qs.statement_start_offset)/2)+1) AS query_text FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st ORDER BY qs.total_worker_time DESC;","expect":"Ermittle die teuersten Abfragen nach CPU und logischen Reads. Abfragen mit vielen Reads sind ein Zeichen fehlender Indizes; prüfe ihre Ausführungspläne und ergänze Indizes oder schreibe die Abfragen um.","severity":"safe"},{"id":"blocking-sessions","group":"Performance","title":"Blockierende Sessions","command":"SELECT r.session_id AS blocked_session, r.blocking_session_id AS blocking_session, r.wait_type AS wait_type, r.wait_time/1000 AS wait_sec, r.status AS status, t.text AS query FROM sys.dm_exec_requests r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t WHERE r.blocking_session_id <> 0 ORDER BY r.wait_time DESC;","expect":"Eine blockierende Session (blocking_session_id <> 0) bedeutet, dass Abfragen aufeinander warten. Lang anhaltende Blockierungen verursachen Timeouts in der Anwendung; finde den Head Blocker und erwäge, die lange Transaktion zu beenden.","severity":"danger"},{"id":"wait-stats","group":"Performance","title":"Häufigste Wait-Statistiken (Wait Stats)","command":"SELECT TOP 15 wait_type AS wait_type, waiting_tasks_count AS waiting_tasks, wait_time_ms/1000 AS total_wait_sec, (wait_time_ms - signal_wait_time_ms)/1000 AS resource_wait_sec, signal_wait_time_ms/1000 AS cpu_wait_sec FROM sys.dm_os_wait_stats WHERE wait_type NOT IN (N'CLR_SEMAPHORE', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'LAZYWRITER_SLEEP', N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'WAITFOR', N'BROKER_TASK_STOP', N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE', N'CHKPT', N'REQUEST_FOR_DEADLOCK_SEARCH', N'XE_TIMER_EVENT', N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'BROKER_TO_FLUSH', N'DISPATCHER_QUEUE_SEMAPHORE', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_WORK_QUEUE', N'HADR_TIMER_TASK', N'HADR_CLUSAPI_CALL', N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE', N'DIRTY_PAGE_POLL', N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SOS_WORK_DISPATCHER', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'PWAIT_EXTENSIBILITY_CLEANUP_TASK', N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'STARTUP_DEPENDENCY_MANAGER', N'PREEMPTIVE_XE_DISPATCHER', N'PVS_PREALLOCATE', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_HOST_WAIT', N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST', N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK') AND wait_type NOT LIKE N'SLEEP[_]%' AND wait_type NOT LIKE N'QDS[_]%' AND waiting_tasks_count > 0 ORDER BY wait_time_ms DESC;","expect":"Interpretiere die dominierenden Wait-Typen: PAGEIOLATCH = Engpass bei Platten-I/O, CXPACKET/CXCONSUMER = Parallelität, LCK_M = Sperren, WRITELOG = Log-Platte, RESOURCE_SEMAPHORE = Speicher. Ein hoher Anteil an Signal Waits weist auf CPU-Druck hin.","severity":"safe"},{"id":"last-backup","group":"Backup & Recovery","title":"Zeitpunkte der letzten Backups (Full / Differential / Log)","command":"SELECT d.name AS database_name, d.recovery_model_desc AS recovery_model, MAX(CASE WHEN b.type = 'D' THEN b.backup_finish_date END) AS last_full_backup, MAX(CASE WHEN b.type = 'I' THEN b.backup_finish_date END) AS last_differential, MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END) AS last_log_backup FROM sys.databases d LEFT JOIN msdb.dbo.backupset b ON d.name = b.database_name WHERE d.database_id > 4 GROUP BY d.name, d.recovery_model_desc ORDER BY last_full_backup;","expect":"Jede Produktionsdatenbank muss ein aktuelles Vollbackup haben. Ist last_full_backup NULL oder sehr alt (jenseits des RPO), schlägt der Backup-Job fehl. Im Recovery Model FULL wächst die Logdatei ohne Log-Backups ständig weiter.","severity":"danger"},{"id":"recovery-model","group":"Backup & Recovery","title":"Konsistenz des Recovery Model","command":"SELECT name AS database_name, recovery_model_desc AS recovery_model, log_reuse_wait_desc AS log_reuse_wait FROM sys.databases WHERE database_id > 4 ORDER BY name;","expect":"Kritische Datenbanken müssen für Point-in-Time-Recovery FULL verwenden. log_reuse_wait_desc = LOG_BACKUP bedeutet, dass ein Log-Backup aussteht; ACTIVE_TRANSACTION weist auf eine lang laufende Transaktion hin.","severity":"caution"},{"id":"sysadmin-members","group":"Sicherheit","title":"Mitglieder der Rolle sysadmin","command":"SELECT p.name AS login_name, p.type_desc AS type, p.is_disabled AS disabled FROM sys.server_role_members rm JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id JOIN sys.server_principals p ON rm.member_principal_id = p.principal_id WHERE r.name = N'sysadmin' ORDER BY p.name;","expect":"Die Mitgliedschaft in sysadmin muss minimiert werden. Unerwartete Logins (vor allem Anwendungskonten oder einzelne Benutzer) sind ein Risiko für Rechteausweitung; wende das Least-Privilege-Prinzip an.","severity":"danger"},{"id":"sa-and-password-policy","group":"Sicherheit","title":"Konto sa und Logins mit deaktivierter Passwortrichtlinie","command":"SELECT name AS login_name, is_disabled AS disabled, is_policy_checked AS password_policy_on, is_expiration_checked AS expiration_check, LOGINPROPERTY(name, 'PasswordLastSetTime') AS last_password_change FROM sys.sql_logins WHERE is_policy_checked = 0 OR name = N'sa' ORDER BY name;","expect":"Das Konto sa sollte deaktiviert oder umbenannt sein. SQL-Logins mit is_policy_checked = 0 bergen das Risiko schwacher/veralteter Passwörter; aktiviere die Passwortrichtlinie (CHECK_POLICY).","severity":"caution"},{"id":"alwayson-sync","group":"Replikation","title":"Always On-Synchronisationsstatus und Warteschlangen","command":"SELECT ag.name AS availability_group, ar.replica_server_name AS replica_server, DB_NAME(drs.database_id) AS database_name, drs.synchronization_state_desc AS sync_state, drs.synchronization_health_desc AS health, drs.log_send_queue_size AS log_send_queue_kb, drs.redo_queue_size AS redo_queue_kb FROM sys.dm_hadr_database_replica_states drs JOIN sys.availability_replicas ar ON drs.replica_id = ar.replica_id JOIN sys.availability_groups ag ON ar.group_id = ag.group_id ORDER BY ag.name, replica_server;","expect":"synchronization_state_desc sollte SYNCHRONIZED (synchron) oder SYNCHRONIZING (asynchron) sein und synchronization_health HEALTHY. Wachsen log_send_queue oder redo_queue, fällt die Replica zurück; das Risiko von Datenverlust bzw. RTO beim Failover steigt.","severity":"caution"}]},"health/mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"checks":[{"id":"version-uptime","group":"Allgemein","title":"Version und Laufzeit","command":"db.serverStatus().version; db.serverStatus().uptime","expect":"Prüfe, ob die Version unterstützt und gepatcht ist. uptime ist in Sekunden; ist der Wert sehr klein (einige Tausend Sekunden), hat ein unerwarteter Neustart stattgefunden; prüfe die mongod-Logs.","severity":"safe"},{"id":"replset-status","group":"Allgemein","title":"Zustände der Replica-Set-Mitglieder (health)","command":"rs.status().members.forEach(function(m) { print(m.name + \" | \" + m.stateStr + \" | health=\" + m.health); })","expect":"Jedes Mitglied sollte health=1 haben, und es muss genau einen PRIMARY geben. health=0 oder (not reachable/healthy), DOWN- und RECOVERING-Zustände weisen auf einen Fehler hin. Gibt es mehr als einen PRIMARY oder keinen, liegt ein Wahlproblem vor.","severity":"danger"},{"id":"dbstats-sizes","group":"Kapazität","title":"Datenbankgrößen (data / storage / index)","command":"db.stats(1024*1024)","expect":"Überwache dataSize, storageSize und indexSize (MB). Ist storageSize deutlich größer als dataSize, liegt möglicherweise Fragmentierung vor. Dass indexSize in den RAM passt (WiredTiger Cache), ist für die Performance entscheidend.","severity":"safe"},{"id":"disk-space","group":"Kapazität","title":"Plattennutzung (fsUsedSize / fsTotalSize)","command":"var st = db.stats(); printjson({fsUsedSize_MB: (st.fsUsedSize/1048576).toFixed(0), fsTotalSize_MB: (st.fsTotalSize/1048576).toFixed(0), usedPct: (100*st.fsUsedSize/st.fsTotalSize).toFixed(1)})","expect":"Übersteigt das Verhältnis von fsUsedSize zu fsTotalSize 85%, läuft die Platte bald voll; MongoDB stoppt Schreibvorgänge bei voller Platte. Plane zusätzliche Platten oder die Archivierung von Daten.","severity":"caution"},{"id":"largest-collections","group":"Kapazität","title":"Größte Collections","command":"db.getCollectionNames().map(function(c) { var s = db.getCollection(c).stats(); return {collection: c, size_MB: (s.size/1048576).toFixed(1), storage_MB: (s.storageSize/1048576).toFixed(1), index_MB: (s.totalIndexSize/1048576).toFixed(1), docs: s.count}; }).sort(function(a,b){return b.size_MB - a.size_MB;})","expect":"Ermittle die Collections mit dem größten Platzbedarf. Unerwartetes Wachstum kann auf Bedarf an Archivierung, einem TTL-Index oder Sharding hinweisen. Prüfe Collections, deren Indexgröße die Datengröße übersteigt.","severity":"safe"},{"id":"connections","group":"Kapazität","title":"Verbindungsanzahl (current / available)","command":"db.serverStatus().connections","expect":"Nähert sich current dem Wert available, ist das Verbindungslimit bald erreicht und neue Verbindungen werden abgelehnt. Prüfe die Connection-Pool-Einstellungen der Anwendung und mögliche Lecks.","severity":"caution"},{"id":"wiredtiger-cache","group":"Performance","title":"Nutzung des WiredTiger Cache und Eviction","command":"var wt = db.serverStatus().wiredTiger.cache; printjson({used_MB: (wt['bytes currently in the cache']/1048576).toFixed(0), max_MB: (wt['maximum bytes configured']/1048576).toFixed(0), dirty_MB: (wt['tracked dirty bytes in the cache']/1048576).toFixed(0), pages_evicted: wt['unmodified pages evicted']})","expect":"Eviction wird ausgelöst, wenn der belegte Speicher 80% des maximalen Cache übersteigt; über 95% werden Anwendungsthreads zur Eviction gezwungen und die Performance sinkt. Ein hoher Anteil an Dirty Pages weist auf Schreibdruck hin.","severity":"caution"},{"id":"slow-queries-profiler","group":"Performance","title":"Langsame Abfragen (Profiler) und COLLSCAN","command":"printjson(db.getProfilingStatus()); db.system.profile.find({millis: {$gt: 100}}).sort({ts: -1}).limit(10).forEach(function(d){ print(d.ts + \" | \" + d.millis + \"ms | \" + (d.planSummary || '') + \" | \" + JSON.stringify(d.command)); })","expect":"Diese Prüfung ist rein lesend: Sie zeigt zuerst den Profiler-Status (was: 0 aus, 1 nur langsame Operationen, 2 alle) und listet dann die aufgezeichneten langsamen Operationen. Ist der Profiler aus (was: 0), bleibt system.profile leer; aktiviere ihn zur Diagnose in einem separaten Schritt mit db.setProfilingLevel(1, {slowms: 100}) und deaktiviere ihn danach mit db.setProfilingLevel(0). Abfragen mit COLLSCAN in planSummary führen einen vollständigen Collection-Scan aus, ihnen fehlt ein Index. Optimiere Abfragen mit hohen millis-Werten per Indexierung. Der Profiler verursacht Overhead, setze ihn in Produktion mit Bedacht ein.","severity":"caution"},{"id":"long-running-ops","group":"Performance","title":"Lang laufende Operationen (currentOp)","command":"db.getSiblingDB('admin').aggregate([{$currentOp: {}}, {$match: {active: true, secs_running: {$gt: 5}}}, {$project: {opid: 1, secs_running: 1, ns: 1, op: 1, planSummary: 1, waitingForLock: 1}}])","expect":"Aktive Operationen mit hohen secs_running-Werten können Blockierungen verursachen. Beende sie bei Bedarf mit db.killOp(opid). Operationen mit waitingForLock=true warten auf eine Sperre; untersuche die auslösende Operation.","severity":"danger"},{"id":"oplog-window","group":"Backup & Recovery","title":"Oplog-Fenster (Replikationshistorie)","command":"rs.printReplicationInfo()","expect":"Das Oplog-Fenster (konfigurierte Oplog-Größe und Loglänge in Stunden) sollte lang genug sein. Ist das Fenster kurz (einige Stunden), kann ein langsamer Secondary in einen vollständigen Resync (Initial Sync) fallen. Backup- und Point-in-Time-Recovery-Strategien hängen vom Oplog ab.","severity":"caution"},{"id":"backup-note","group":"Backup & Recovery","title":"Backupstatus und Hinweis zum letzten Backup","command":"var r = db.getSiblingDB('local').getCollectionNames().includes('oplog.rs') ? db.getReplicationInfo() : null; printjson(r ? {oplogWindowHours: r.timeDiffHours, tFirst: r.tFirst, tLast: r.tLast} : 'standalone: no oplog'); print('Backup verification: check mongodump/mongorestore or Ops Manager / Percona Backup (PBM) status externally.')","expect":"MongoDB hat kein eingebautes geplantes Backup; genutzt werden mongodump, Dateisystem-Snapshots, Ops Manager oder PBM. Prüfe den Zeitpunkt des letzten erfolgreichen Backups und den Restore-Test im externen Tool. Auf einem Replica Set begrenzt das Oplog-Fenster (oplogWindowHours), wie weit oplogbasierte PITR zurückreichen kann; es darf nicht kürzer als das Backupintervall sein. Prüfe, ob die RPO-/RTO-Ziele eingehalten werden.","severity":"danger"},{"id":"users-roles","group":"Sicherheit","title":"Benutzer- und Rollenliste","command":"db.getSiblingDB('admin').system.users.find({}, {user: 1, db: 1, roles: 1, _id: 0}).forEach(printjson)","expect":"Prüfe die definierten Benutzer und ihre Rollen. Unerwartete Konten oder zu weit gefasste Rollen (root, __system, dbOwner) sind ein Risiko für Rechteausweitung. Wende das Least-Privilege-Prinzip an.","severity":"caution"},{"id":"auth-enabled","group":"Sicherheit","title":"Ist die Authentifizierung (authorization) aktiviert","command":"db.getSiblingDB('admin').runCommand({getCmdLineOpts: 1}).parsed.security","expect":"security.authorization muss 'enabled' sein. Ist sie deaktiviert (oder fehlt der security-Block), ist die Datenbank ohne Authentifizierung zugänglich; das ist eine ernste Sicherheitslücke, aktiviere sie sofort.","severity":"danger"},{"id":"secondary-lag","group":"Replikation","title":"Replikationsverzögerung der Secondaries","command":"rs.printSecondaryReplicationInfo()","expect":"Der Wert 'replication lag' jedes Secondary sollte niedrig sein (einige Sekunden). Ist die Verzögerung groß (Minuten), kommt der Secondary mit der Last nicht mit oder es gibt ein Netzwerk-/Plattenproblem; das Risiko von Datenverlust und inkonsistenten Reads beim Failover steigt.","severity":"caution"},{"id":"member-health","group":"Replikation","title":"Zustand der Mitglieder und Wahlstatus","command":"rs.status().members.map(function(m){ return {member: m.name, state: m.stateStr, health: m.health, configVersion: m.configVersion, last_heartbeat: m.lastHeartbeatMessage || 'OK'}; })","expect":"Alle Mitglieder sollten fehlerfrei (health=1) und im erwarteten Zustand (PRIMARY/SECONDARY) sein. Eine Fehlermeldung in lastHeartbeatMessage weist auf eine Netzwerkpartition oder ein nicht erreichbares Mitglied hin. Geht die Mehrheit verloren, stoppen die Schreibvorgänge.","severity":"caution"}]},"health/redis.json":{"engineId":"redis","engine":"Redis","order":6,"checks":[{"id":"server-version-uptime","group":"Allgemein","title":"Version und Laufzeit","command":"redis-cli INFO server | grep -E \"redis_version|uptime_in_days|redis_mode|os\"","expect":"redis_version sollte ein unterstütztes, gepatchtes Release sein. Ist uptime_in_days sehr klein, hat ein unerwarteter Neustart stattgefunden; redis_mode (standalone/sentinel/cluster) bestätigt die Konfiguration.","severity":"safe"},{"id":"persistence-status","group":"Allgemein","title":"Persistenzstatus (RDB / AOF)","command":"redis-cli INFO persistence | grep -E \"rdb_last_bgsave_status|aof_enabled|aof_last_bgrewrite_status|aof_last_write_status|rdb_bgsave_in_progress|loading\"","expect":"rdb_last_bgsave_status und aof_last_write_status müssen 'ok' sein. Ein Wert 'err' weist auf ein Platten-/Berechtigungsproblem oder eine volle Platte hin und birgt das Risiko von Datenverlust. loading:1 bedeutet, dass der Server noch Daten lädt.","severity":"danger"},{"id":"memory-usage","group":"Kapazität","title":"Speichernutzung (used_memory / maxmemory)","command":"redis-cli INFO memory | grep -E \"used_memory_human|used_memory:|maxmemory_human|maxmemory:|maxmemory_policy\"","expect":"Nähert sich used_memory dem Wert maxmemory (85%+), drohen Eviction oder OOM. Bei maxmemory=0 gibt es kein Limit, und Redis kann den gesamten RAM belegen und das System lahmlegen; setze immer ein Limit und eine passende maxmemory_policy.","severity":"caution"},{"id":"memory-fragmentation","group":"Kapazität","title":"Speicherfragmentierungsrate","command":"redis-cli INFO memory | grep -E \"mem_fragmentation_ratio|mem_allocator\"","expect":"mem_fragmentation_ratio liegt idealerweise zwischen 1.0 und 1.5. Über 1.5 bedeutet hohe Fragmentierung (verschwendeter Speicher). Unter 1.0 bedeutet, dass Redis auf die Platte ausgelagert wird (ernstes Performanceproblem); prüfe den System-RAM.","severity":"caution"},{"id":"evicted-keys","group":"Kapazität","title":"Verdrängte Schlüssel (evicted_keys)","command":"redis-cli INFO stats | grep -E \"evicted_keys|expired_keys|keyspace_misses\"","expect":"Steigt evicted_keys ständig, hat Redis sein Speicherlimit erreicht und verwirft Daten; das kann zu ungewolltem Datenverlust führen. Erwäge mehr Speicher, den Einsatz von TTLs oder eine Überprüfung von maxmemory_policy.","severity":"caution"},{"id":"dbsize-keyspace","group":"Kapazität","title":"Schlüsselanzahl und Keyspace-Verteilung","command":"redis-cli DBSIZE; redis-cli INFO keyspace","expect":"Überwache die Gesamtzahl der Schlüssel (DBSIZE) und die Verteilung pro Datenbank (INFO keyspace: keys, expires, avg_ttl). Ein wachsender Keyspace ohne TTLs (niedriges expires) bläht den Speicher auf. Plötzliche Sprünge in der Schlüsselanzahl können ein Zeichen für ungewöhnliches Verhalten sein.","severity":"safe"},{"id":"hit-ratio","group":"Performance","title":"Cache-Trefferquote (Keyspace Hits/Misses)","command":"redis-cli INFO stats | grep -E \"keyspace_hits|keyspace_misses\"","expect":"Trefferquote = hits / (hits + misses). Bei Nutzung als Cache sollte die Quote hoch sein (80%+). Eine niedrige Quote weist auf ineffizientes Caching, falsche TTLs oder ein Cache-Miss-Muster hin; stelle sicher, dass häufig angefragte Daten gecacht werden.","severity":"safe"},{"id":"slowlog","group":"Performance","title":"Log langsamer Befehle (SLOWLOG)","command":"redis-cli SLOWLOG GET 10","expect":"Untersuche langsame Befehle (Standardschwelle 10ms). O(N)-Befehle (KEYS, SMEMBERS, großes HGETALL, SORT) blockieren das single-threaded Redis und verzögern jede Anfrage. Ersetze sie durch Alternativen wie SCAN.","severity":"caution"},{"id":"clients","group":"Performance","title":"Verbundene und blockierte Clients (clients)","command":"redis-cli INFO clients | grep -E \"connected_clients|blocked_clients|maxclients|client_recent_max_input_buffer\"","expect":"Nähert sich connected_clients dem Wert maxclients, werden neue Verbindungen abgelehnt. Eine hohe Anzahl blocked_clients (wartend auf BLPOP/BRPOP) kann gewollt sein, ein unerwarteter Anstieg ist aber ein Zeichen für Überlastung. Prüfe auf Verbindungslecks.","severity":"safe"},{"id":"ops-per-sec","group":"Performance","title":"Momentaner Durchsatz (ops/sec)","command":"redis-cli INFO stats | grep -E \"instantaneous_ops_per_sec|total_commands_processed|rejected_connections|total_net_input_bytes\"","expect":"instantaneous_ops_per_sec zeigt die aktuelle Last. Plötzliche Einbrüche/Spitzen sind ein Zeichen für Anomalien. Ist rejected_connections > 0, wurde das Limit maxclients erreicht. Da Redis single-threaded ist, können sehr hohe ops einen CPU-Engpass erzeugen.","severity":"safe"},{"id":"rdb-last-save","group":"Backup & Recovery","title":"Letzter RDB-Save und ausstehende Änderungen","command":"redis-cli INFO persistence | grep -E \"rdb_last_save_time|rdb_changes_since_last_save|rdb_last_bgsave_status\"; redis-cli LASTSAVE","expect":"Ist rdb_changes_since_last_save sehr hoch, haben sich seit dem letzten Snapshot viele Daten angesammelt; diese Änderungen gehen bei einem Absturz verloren. Ist rdb_last_save_time (LASTSAVE, Unix-Zeit) sehr alt, prüfe das Save-Intervall oder die Backup-Häufigkeit.","severity":"caution"},{"id":"aof-rewrite","group":"Backup & Recovery","title":"Status des AOF-Rewrite","command":"redis-cli INFO persistence | grep -E \"aof_enabled|aof_last_bgrewrite_status|aof_rewrite_in_progress|aof_current_size|aof_base_size\"","expect":"In Szenarien, die Dauerhaftigkeit erfordern, muss aof_enabled=1 gesetzt sein. aof_last_bgrewrite_status muss 'ok' sein; 'err' weist auf ein Platten-/Persistenzproblem hin. Ist aof_current_size deutlich größer als base_size, ist ein Rewrite nötig.","severity":"caution"},{"id":"acl-auth","group":"Sicherheit","title":"Einstellung für ACL / Authentifizierung (requirepass)","command":"redis-cli ACL LIST; redis-cli ACL GETUSER default","expect":"Es muss mindestens einen passwortgeschützten Benutzer geben. Zeigen die Flags in der Ausgabe von ACL GETUSER default 'on' und 'nopass' (requirepass ist auch das Passwort dieses Benutzers), ist Redis ohne Authentifizierung zugänglich; das ist eine ernste Sicherheitslücke. Passwörter werden nur als Hashes angezeigt; nutze für diese Prüfung nicht CONFIG GET requirepass, da es das Passwort im Klartext ausgibt. Definiere Benutzer mit minimalen Rechten über ACLs.","severity":"danger"},{"id":"dangerous-commands","group":"Sicherheit","title":"Gefährliche Befehle und protected-mode","command":"redis-cli CONFIG GET protected-mode; redis-cli CONFIG GET bind; redis-cli COMMAND INFO flushall flushdb config keys","expect":"protected-mode sollte 'yes' sein und bind auf sichere Schnittstellen beschränkt. Gefährliche Befehle wie FLUSHALL, FLUSHDB, CONFIG, KEYS und DEBUG sollten in Produktion mit rename-command deaktiviert oder per ACL eingeschränkt werden.","severity":"caution"},{"id":"replication-status","group":"Replikation","title":"Replikationsstatus (role, master link)","command":"redis-cli INFO replication","expect":"Prüfe role (master/slave), connected_slaves und den Status jeder Replica. Auf einer Replica muss master_link_status 'up' sein; 'down' bedeutet, dass die Verbindung unterbrochen ist. Ist master_last_io_seconds_ago hoch (Verzögerung), steigt das Risiko von Datenverlust und Inkonsistenz.","severity":"danger"}]},"health/elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"checks":[{"id":"cluster-health","group":"Allgemein","title":"Cluster-Zustand (green / yellow / red)","command":"GET _cluster/health","expect":"status sollte 'green' sein: Alle Primary- und Replica-Shards sind zugewiesen. 'yellow' bedeutet, dass die Primaries intakt, aber einige Replicas nicht zugewiesen sind (Redundanzrisiko); 'red' bedeutet, dass mindestens ein Primary Shard verloren/nicht zugewiesen ist (ein Teil der Daten ist nicht verfügbar, sofortiges Handeln nötig). unassigned_shards sollte 0 sein.","severity":"danger"},{"id":"cluster-version","group":"Allgemein","title":"Version und Cluster-Informationen","command":"GET /","expect":"version.number sollte eine unterstützte Version mit eingespielten Sicherheitspatches sein. Alte/EOL-Versionen (End-of-Life) bergen Sicherheits- und Stabilitätsrisiken. Prüfe außerdem mit GET _cat/nodes?v&h=name,version, ob alle Knoten dieselbe Version nutzen (ein vorübergehender Mix während eines Rolling Upgrade ist normal).","severity":"safe"},{"id":"pending-tasks","group":"Allgemein","title":"Ausstehende Cluster-Tasks","command":"GET _cluster/pending_tasks","expect":"Die Liste sollte leer oder sehr kurz sein. Tasks, die sich ständig stauen und lange warten (hohes time_in_queue), weisen darauf hin, dass der Master-Knoten überlastet ist oder Aktualisierungen des Cluster State stocken; das verschlechtert die Antwortzeit des gesamten Clusters.","severity":"caution"},{"id":"disk-allocation-watermark","group":"Kapazität","title":"Plattenbelegung und Watermark-Status","command":"GET _cat/allocation?v","expect":"disk.percent jedes Knotens sollte unter dem High Watermark liegen (Standard 90%). Über 85% ist eine Warnung, 90% (high) löst Shard-Relocation aus und 95% (flood_stage) setzt Indizes auf schreibgeschützt. Prüfe, ob die Spalte shards über die Knoten ausgeglichen ist; eine ungleiche Verteilung lässt einzelne Knoten früh volllaufen.","severity":"danger"},{"id":"largest-indices","group":"Kapazität","title":"Größte Indizes (Store-Größe)","command":"GET _cat/indices?v&s=store.size:desc","expect":"Verfolge die Indizes mit dem größten Platzbedarf und ihre doc.count-Werte. Unkontrolliertes Wachstum von Log-/Metrik-Indizes ist die Hauptursache voller Platten; definiere für sie mit ILM (Index Lifecycle Management) Rollover- und Löschrichtlinien. Achte besonders auf Indizes mit status 'close' oder mit yellow/red health.","severity":"caution"},{"id":"jvm-heap-usage","group":"Kapazität","title":"JVM-Heap- und RAM-Nutzung (pro Knoten)","command":"GET _cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m","expect":"heap.percent sollte dauerhaft unter 75% bleiben; anhaltende Werte von 85%+ bedeuten Speicherdruck, häufige GC-Pausen und Circuit-Breaker-Risiko. Der JVM-Heap sollte ~50% des Server-RAM und höchstens ~31GB betragen (Grenze für Compressed Oops). Bei hohem Heap, der nicht sinkt, Abfragen optimieren oder Knoten hinzufügen.","severity":"caution"},{"id":"shard-count-limits","group":"Kapazität","title":"Shard-Anzahl und Shard-Limit pro Knoten","command":"GET _cluster/health?level=shards","expect":"active_shards und die Anzahl der Shards pro Knoten sollten angemessen sein. Die Standardobergrenze ist cluster.max_shards_per_node = 1000 (pro Datenknoten); sich diesem Limit zu nähern und insgesamt Tausende kleiner Shards zu haben (Oversharding), erzeugt Druck auf den Master und verschwendet Speicher. Die alte Regel \"~20 Shards pro GB Heap\" gilt ab 8.3 nicht mehr; aktuelle Empfehlung sind 10-50GB pro Shard und eine Heap-Planung auf Basis der Anzahl der Felder (Mappings) pro Knoten. initializing_shards / relocating_shards sollten nicht lange hoch bleiben. Bei zu vielen Shards größere/weniger Shards oder Rollover nutzen.","severity":"caution"},{"id":"http-connections","group":"Kapazität","title":"Anzahl der HTTP-Verbindungen","command":"GET _nodes/stats/http","expect":"http.current_open sollte in vernünftigen Grenzen liegen. Wächst http.total_opened sehr schnell, nutzen Clients keinen Connection Pool und öffnen pro Anfrage eine neue Verbindung (ineffizient). Ein ungewöhnlich hohes current_open weist auf ein Verbindungsleck oder eine mögliche Ressourcenerschöpfung hin.","severity":"safe"},{"id":"thread-pool-rejections","group":"Performance","title":"Thread-Pool-Ablehnungen und Queue-Sättigung","command":"GET _cat/thread_pool?v&h=node_name,name,active,queue,rejected","expect":"Die Spalte rejected sollte 0 sein. Ein steigender rejected-Wert in den Pools write/bulk und search zeigt, dass der Cluster überlastet ist und HTTP 429 (es_rejected_execution_exception) liefert. Eine ständig volle Queue ist ein Zeichen für Überlastung. Setze im Client Backoff ein, verringere die Batchgröße oder erweitere die Kapazität.","severity":"danger"},{"id":"query-cache-hit","group":"Performance","title":"Trefferquote des Query Cache","command":"GET _nodes/stats/indices/query_cache","expect":"Das Verhältnis hit_count / (hit_count + miss_count) sollte bei wiederholten Filterabfragen hoch sein. Eine niedrige Trefferquote mit vielen Evictions zeigt, dass der Cache zu klein ist oder die Abfragen zu variabel sind, um gecacht zu werden. Stabile Filter (Filter-Kontext) erhöhen die Trefferquote.","severity":"safe"},{"id":"slowlog-config","group":"Performance","title":"Einstellungen für das Slow Query Log (Slowlog)","command":"GET /_all/_settings?filter_path=**.search.slowlog,**.indexing.slowlog","expect":"Auf wichtigen Indizes sollten Slowlog-Schwellen für Suche und Indexierung definiert sein (z. B. index.search.slowlog.threshold.query.warn: 10s). Mit deaktiviertem Slowlog wird die Diagnose langsamer Abfragen schwieriger. Aktiviere Schwellen und überwache die Logs, um teure Abfragen (breite Aggregationen, Wildcards, tiefe Paginierung) zu ermitteln.","severity":"safe"},{"id":"hot-threads","group":"Performance","title":"Hot Threads","command":"GET _nodes/hot_threads","expect":"Zeigt die Threads mit dem höchsten CPU-Verbrauch. Dient bei hoher CPU-Last/Langsamkeit dazu, festzustellen, welche Operation (schwere Aggregation, Merge, GC, Skript) der Engpass ist. Taucht dieselbe teure Abfrage/derselbe Merge immer wieder auf, optimiere diese Abfrage oder die Indexierungsstrategie.","severity":"safe"},{"id":"fielddata-memory","group":"Performance","title":"Speichernutzung von Fielddata","command":"GET _cat/fielddata?v","expect":"Die Speichernutzung von Fielddata sollte niedrig gehalten werden. Aggregieren/Sortieren auf text-Feldern lädt Fielddata in den Speicher und erhöht das Risiko von Circuit-Breaker-Fehlern (Data too large) und OOM. Wechsle bei Feldern mit viel Fielddata auf ein keyword-Unterfeld (doc_values).","severity":"caution"},{"id":"snapshot-status","group":"Backup & Recovery","title":"Snapshot-Repositorys und Status des letzten Snapshots","command":"GET _snapshot/_all","expect":"Mindestens ein Snapshot-Repository sollte definiert sein. Prüfe nach Bestätigung der Repositorys den Status der letzten Snapshots (zusammen mit der verify-Prüfung unten). Gibt es kein Repository, hat der Cluster kein Backup, und Daten können im Zustand RED dauerhaft verloren gehen. Richte SLM (Snapshot Lifecycle Management) für regelmäßige automatische Snapshots ein.","severity":"danger"},{"id":"snapshot-latest-state","group":"Backup & Recovery","title":"Erfolgsstatus der letzten Snapshots","command":"GET _snapshot/<repo>/_all?verbose=false","expect":"Der state der letzten Snapshots sollte 'SUCCESS' sein. 'PARTIAL' bedeutet, dass einige Shards nicht gesichert werden konnten, 'FAILED', dass das Backup fehlgeschlagen ist; beides bedeutet Datenverlust beim Restore. Das Datum des jüngsten Snapshots (end_time) sollte nicht zu alt sein (sichere in einer Häufigkeit, die dein RPO-Ziel erfüllt).","severity":"danger"},{"id":"snapshot-repo-verify","group":"Backup & Recovery","title":"Prüfung des Snapshot-Repositorys","command":"POST _snapshot/<repo>/_verify","expect":"Die Prüfung bestätigt, dass alle Knoten auf das Repository zugreifen können (gemeinsames Dateisystem, S3, HDFS usw.), und listet die zugreifenden Knoten in der Antwort. Liefert sie einen Fehler, können einige Knoten nicht in das Backup-Repository schreiben; Snapshots sind dann unvollständig/fehlgeschlagen. Behebe Repository-Zugriff und Zugangsdaten.","severity":"caution"},{"id":"security-enabled","group":"Sicherheit","title":"Ist die Sicherheit (X-Pack security) aktiviert","command":"GET _xpack/usage","expect":"security.enabled und security.available sollten true sein. Bei deaktivierter Sicherheit ist der Cluster ohne Authentifizierung zugänglich; ist ein solcher Cluster aus dem Internet oder einem nicht vertrauenswürdigen Netz erreichbar, ist das eine ernste Schwachstelle (Datenleck/-löschung). Authentifizierung, rollenbasierter Zugriff und TLS müssen aktiviert sein.","severity":"danger"},{"id":"users-roles","group":"Sicherheit","title":"Benutzer und Rollen","command":"GET _security/user","expect":"Prüfe die definierten Benutzer und Rollenzuweisungen. Stelle sicher, dass integrierte Konten (elastic, kibana_system usw.) mit starken Passwörtern geschützt sind. Definiere nach dem Least-Privilege-Prinzip für jede Anwendung separate Benutzer mit eingeschränkten Rollen; vermeide die unnötig breite Nutzung der Rolle superuser. Für Rollen GET _security/role verwenden.","severity":"caution"},{"id":"tls-transport-http","group":"Sicherheit","title":"TLS-/HTTPS-Status (Transport und HTTP)","command":"GET _nodes/settings?filter_path=**.xpack.security.transport.ssl.enabled,**.xpack.security.http.ssl.enabled","expect":"xpack.security.transport.ssl.enabled sollte true sein (Verschlüsselung zwischen Knoten, Pflicht). Idealerweise sollte auch http.ssl.enabled true sein (HTTPS vom Client zum Cluster). Bei deaktiviertem TLS gehen Zugangsdaten und Daten im Klartext über das Netzwerk. Prüfe außerdem Zertifikatsablauf und Prüfmodus (verification_mode: certificate/full).","severity":"danger"},{"id":"unassigned-shards","group":"Replikation","title":"Nicht zugewiesene Shards und ihre Ursachen","command":"GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason","expect":"Alle Shards sollten den state 'STARTED' haben. Shards im Zustand UNASSIGNED und ihr unassigned.reason (NODE_LEFT, ALLOCATION_FAILED, DISK_THRESHOLD usw.) sollten untersucht werden. Ein nicht zugewiesener Primary bedeutet health RED, eine nicht zugewiesene Replica YELLOW. Nutze GET _cluster/allocation/explain für die Ursache.","severity":"danger"},{"id":"recovery-active","group":"Replikation","title":"Laufende Shard-Recoverys","command":"GET _cat/recovery?v&active_only=true","expect":"Normalerweise sollte es keine aktive Recovery geben (leere Liste). Eine vorübergehende Recovery nach dem Hinzufügen/Entfernen eines Knotens oder einem Failover ist normal, aber Recoverys, die lange ohne Fortschritt hängen (%progress steigt nicht), weisen auf einen Netzwerk-/Plattenengpass hin. Beeinträchtigt das Rebalancing (Relocation) die Last, prüfe die Einstellungen zur Recovery-Drosselung (indices.recovery.max_bytes_per_sec).","severity":"caution"},{"id":"replica-count","group":"Replikation","title":"Replica-Anzahl pro Index","command":"GET _cat/indices?v&h=index,health,pri,rep,docs.count","expect":"In Produktionsindizes sollte rep (number_of_replicas) mindestens 1 sein; bei einem Index mit 0 Replicas bedeutet der Verlust eines Knotens dauerhaften Datenverlust. In einem Cluster mit mehreren Knoten sollte die Replica-Anzahl jedoch (Anzahl der Datenknoten - 1) nicht übersteigen; sonst lassen sich die zusätzlichen Replicas nicht zuweisen und health bleibt YELLOW. Untersuche Indizes, deren health-Spalte nicht green ist.","severity":"danger"}]},"recipes/oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"recipes":[{"id":"redo-switch","task":"Redo-Log-Switch (aktuelle Logdatei wechseln)","category":"Redo & Archiv","command":"ALTER SYSTEM SWITCH LOGFILE;","what":"Schließt die aktuelle Redo-Log-Gruppe und wechselt zur nächsten; im Archivelog-Modus löst das die Archivierung aus.","when":"Um manuell ein Archivlog zu erzeugen, einen Log Switch zu erzwingen, oder zum Testen.","caution":"Häufige Switches verschlechtern die Performance; sind die Logdateien zu klein, prüfe ihre Dimensionierung.","tags":["redo","switch","logfile","archive","archive","alter system"]},{"id":"redo-add-group","task":"Neue Redo-Log-Gruppe hinzufügen","category":"Redo & Archiv","command":"ALTER DATABASE ADD LOGFILE GROUP 4\n  ('/u01/oradata/ORCL/redo04a.log',\n   '/u02/oradata/ORCL/redo04b.log') SIZE 512M;","what":"Legt eine neue Redo-Log-Gruppe mit der angegebenen Größe und den angegebenen Mitgliedern an (mehrere Mitglieder = Spiegelung).","when":"Wenn der Redo-Durchsatz nicht ausreicht oder du die Anzahl der Gruppen erhöhen willst.","caution":"Stelle sicher, dass die Gruppennummer nicht mit vorhandenen Gruppen kollidiert; prüfe das mit V$LOG.","tags":["redo","logfile","group","add","add","alter database"]},{"id":"redo-resize","task":"Redo-Log-Gruppe in der Größe ändern (drop + add)","category":"Redo & Archiv","command":"-- 1) The group to be resized must be INACTIVE; if not, switch:\nALTER SYSTEM SWITCH LOGFILE;\nALTER SYSTEM CHECKPOINT;\n-- 2) Drop the old group:\nALTER DATABASE DROP LOGFILE GROUP 1;\n-- 3) Add it again with the new size:\nALTER DATABASE ADD LOGFILE GROUP 1\n  ('/u01/oradata/ORCL/redo01a.log',\n   '/u02/oradata/ORCL/redo01b.log') SIZE 1G REUSE;","what":"Ein Redo-Log lässt sich nicht direkt in der Größe ändern; dazu wird die Gruppe gelöscht und mit der neuen Größe wieder hinzugefügt.","when":"Wenn Redo-Dateien zu klein/groß sind, um die Checkpoint-Häufigkeit abzustimmen.","caution":"Nur eine Gruppe im Zustand INACTIVE oder UNUSED lässt sich löschen; lösche nicht die Gruppe CURRENT/ACTIVE. Es müssen immer mindestens zwei Gruppen bestehen bleiben. REUSE verwendet die vorhandene Datei wieder.","tags":["redo","resize","size","drop","add","logfile","reuse"]},{"id":"enable-archivelog","task":"Datenbank in den ARCHIVELOG-Modus versetzen","category":"Redo & Archiv","command":"SHUTDOWN IMMEDIATE;\nSTARTUP MOUNT;\nALTER DATABASE ARCHIVELOG;\nALTER DATABASE OPEN;\n-- Verify:\nARCHIVE LOG LIST;","what":"Versetzt die Datenbank in den Archivelog-Modus; volle Redo-Logs werden archiviert, was Hot Backups und Point-in-Time-Recovery ermöglicht.","when":"Für die Backup- und Recovery-Strategie einer Produktionsdatenbank (Pflicht vor RMAN, Data Guard).","caution":"Erfordert einen Neustart der Instanz; plane die Ausfallzeit. Stelle sicher, dass das Archivziel (FRA oder LOG_ARCHIVE_DEST) genug Platz hat; läuft es voll, stoppt die DB. Bei RAC: alle Instanzen mit srvctl stop database -d DB stoppen, eine einzelne Instanz mit STARTUP MOUNT öffnen, auf ARCHIVELOG umstellen, herunterfahren und mit srvctl start database -d DB starten (ab 11gR2 ist cluster_database=false nicht nötig).","tags":["archivelog","archive","mode","mount","recovery","recovery"]},{"id":"fra-full-fix","task":"Volle FRA / volles Archivziel beheben","category":"Redo & Archiv","command":"-- Check usage:\nSELECT name, space_limit/1024/1024/1024 AS limit_gb,\n       space_used/1024/1024/1024  AS used_gb,\n       ROUND(space_used/space_limit*100,1) AS pct_used\nFROM   v$recovery_file_dest;\n\n-- Fix A) Increase the FRA size:\nALTER SYSTEM SET db_recovery_file_dest_size=100G SCOPE=BOTH;\n\n-- Fix B) Clean up already backed-up archives with RMAN:\n-- RMAN> DELETE ARCHIVELOG ALL BACKED UP 1 TIMES TO DISK COMPLETED BEFORE 'SYSDATE-3';\n-- If there is no backup (CAREFUL): RMAN> DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-1';","what":"Läuft die Fast Recovery Area oder das Archivziel voll, hängt die Datenbank (ORA-00257). Behoben wird das durch Vergrößern des Bereichs oder Bereinigen der Archivlogs.","when":"Archiver-Fehler ORA-00257, wenn die DB keine neuen Transaktionen mehr annimmt.","caution":"Das Löschen nicht gesicherter Archivlogs zerstört die Wiederherstellbarkeit. Erstelle zuerst ein RMAN-Backup und lösche dann. Lösche nicht mit rm auf OS-Ebene; das erfordert ein RMAN CROSSCHECK. Solange die FRA voll ist, sichere Archivlogs an ein Ziel außerhalb der FRA (FORMAT '/backup/arc_%U'). Bei Data Guard keine Archivlogs löschen, die noch nicht zum Standby übertragen/dort angewendet wurden; sonst entsteht auf dem Standby eine Lücke. Nutze CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY.","tags":["fra","recovery area","ora-00257","archiver","archive","full","cleanup"]},{"id":"redo-status","task":"Redo-Log-Status anzeigen","category":"Redo & Archiv","command":"SELECT l.group#, l.thread#, l.sequence#,\n       ROUND(l.bytes/1024/1024) AS mb,\n       l.members, l.archived, l.status,\n       lf.member AS file_path\nFROM   v$log l\n  JOIN v$logfile lf ON lf.group# = l.group#\nORDER BY l.group#, lf.member;","what":"Zeigt den Status der Redo-Log-Gruppen (CURRENT/ACTIVE/INACTIVE), ihre Größen, Mitgliedsdateien und den Archivstatus.","when":"Bei der Prüfung der Redo-Konfiguration oder um vor einer Größenänderung zu sehen, welche Gruppe aktiv ist.","caution":"Nur Abfrage; kein Risiko.","tags":["redo","status","status","v$log","v$logfile","logfile"]},{"id":"asm-add-disk","task":"Platten zu einer ASM-Diskgroup hinzufügen","category":"ASM","command":"-- Connect to the ASM instance (sqlplus / as sysasm)\nALTER DISKGROUP DATA ADD DISK\n  '/dev/oracleasm/disks/DATA05' NAME DATA_0005,\n  '/dev/oracleasm/disks/DATA06' NAME DATA_0006\n  REBALANCE POWER 4;","what":"Fügt der Diskgroup DATA neue Platten hinzu und startet ein automatisches Rebalance; die Kapazität wächst.","when":"Wenn eine ASM-Diskgroup volläuft oder der Speicher erweitert wird.","caution":"Erfordert das Privileg SYSASM. Die Plattengrößen sollten ähnlich sein. Das Rebalance erzeugt I/O-Last; nutze in Spitzenzeiten ein niedriges POWER.","tags":["asm","diskgroup","add disk","add disk","rebalance","alter diskgroup"]},{"id":"asm-drop-disk","task":"Platte aus einer ASM-Diskgroup entfernen","category":"ASM","command":"ALTER DISKGROUP DATA DROP DISK DATA_0005 REBALANCE POWER 4;\n\n-- WAIT until the drop completes (the disk can be physically removed once V$ASM_OPERATION is empty):\nSELECT group_number, operation, state, power,\n       sofar, est_work, est_minutes\nFROM   v$asm_operation;","what":"Entfernt die Platte aus der Diskgroup; ihre Extents werden auf die übrigen Platten verschoben (Rebalance). Danach wird HDR_STATUS der Platte FORMER.","when":"Beim Austausch einer defekten Platte, beim Verringern der Kapazität oder bei der Speichermigration.","caution":"Ziehe die Platte NICHT physisch, BEVOR das Rebalance abgeschlossen ist, sonst gehen Daten verloren. Stelle sicher, dass die übrigen Platten genug freien Platz haben.","tags":["asm","drop disk","remove disk","rebalance","v$asm_operation","alter diskgroup"]},{"id":"asm-rebalance-power","task":"ASM-Rebalance-POWER setzen","category":"ASM","command":"-- Change the power of a running rebalance (1-1024, 0 stops it):\nALTER DISKGROUP DATA REBALANCE POWER 8;\n\n-- Stop the rebalance completely:\nALTER DISKGROUP DATA REBALANCE POWER 0;","what":"Legt Parallelität/Geschwindigkeit eines laufenden Rebalance fest. Ein höheres POWER bedeutet schneller, aber mehr I/O.","when":"Um ein zu langsames Rebalance zu beschleunigen oder eines, das die Produktion beeinträchtigt, zu verlangsamen/anzuhalten.","caution":"POWER 0 setzt das Rebalance aus (ein Disk Drop wird nicht abgeschlossen). Ein hoher Wert beeinträchtigt das Produktions-I/O.","tags":["asm","rebalance","power","power","diskgroup","tuning"]},{"id":"asm-diskgroup-space","task":"Freien Platz der ASM-Diskgroups anzeigen","category":"ASM","command":"SELECT name,\n       state,\n       type,\n       total_mb/1024                 AS total_gb,\n       free_mb/1024                  AS free_gb,\n       ROUND((1-free_mb/total_mb)*100,1) AS pct_used,\n       usable_file_mb/1024           AS usable_gb\nFROM   v$asm_diskgroup\nORDER BY name;","what":"Zeigt den gesamten, freien und (nach Redundanz) nutzbaren Platz jeder Diskgroup.","when":"Für die Kapazitätsplanung, bei knappem Platz, vor der Entscheidung über neue Platten.","caution":"usable_file_mb berücksichtigt die Redundanz; bei NORMAL/HIGH-Redundanz ist diese Spalte der tatsächlich nutzbare Platz. Nur Abfrage; kein Risiko.","tags":["asm","diskgroup","space","space","free","v$asm_diskgroup","capacity"]},{"id":"asm-monitor-rebalance","task":"ASM-Rebalance überwachen","category":"ASM","command":"SELECT group_number, operation, pass, state, power,\n       sofar, est_work,\n       ROUND(sofar/NULLIF(est_work,0)*100,1) AS pct_done,\n       est_minutes\nFROM   v$asm_operation\nORDER BY group_number;","what":"Zeigt Fortschritt, Power und geschätzte Restzeit laufender Rebalance-/Resync-Operationen.","when":"Um nach dem Hinzufügen/Entfernen von Platten zu verfolgen, ob das Rebalance abgeschlossen ist.","caution":"Leeres Ergebnis = keine aktive Operation (abgeschlossen). Nur Abfrage; kein Risiko.","tags":["asm","rebalance","monitor","monitor","v$asm_operation","progress"]},{"id":"add-tablespace","task":"Neuen Tablespace hinzufügen","category":"Speicher","command":"-- File system:\nCREATE TABLESPACE customer_data\n  DATAFILE '/u01/oradata/ORCL/customer_data01.dbf'\n  SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 20G\n  EXTENT MANAGEMENT LOCAL\n  SEGMENT SPACE MANAGEMENT AUTO;\n\n-- If you use ASM:\nCREATE TABLESPACE customer_data\n  DATAFILE '+DATA' SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 20G;","what":"Legt einen neuen permanenten Tablespace an, in dem Tabellen-/Indexsegmente abgelegt werden können.","when":"Wenn für eine neue Anwendung/ein neues Schema separater Speicher bereitgestellt wird.","caution":"Gib MAXSIZE an, sonst kann die Platte volllaufen. In ASM wird der Dateiname automatisch erzeugt.","tags":["tablespace","create","create","datafile","storage","storage","autoextend"]},{"id":"add-datafile","task":"Datafile zu einem vorhandenen Tablespace hinzufügen","category":"Speicher","command":"ALTER TABLESPACE customer_data\n  ADD DATAFILE '/u01/oradata/ORCL/customer_data02.dbf'\n  SIZE 2G AUTOEXTEND ON NEXT 256M MAXSIZE 30G;\n\n-- ASM:\nALTER TABLESPACE customer_data\n  ADD DATAFILE '+DATA' SIZE 2G AUTOEXTEND ON NEXT 256M MAXSIZE 30G;","what":"Erweitert die Kapazität eines vorhandenen Tablespace durch ein neues Datafile.","when":"Wenn der Tablespace voll ist und eine einzelne Datei sich dem Smallfile-Limit (32G/64G) nähert.","caution":"Ein Smallfile-Tablespace hat ein Blocklimit pro Datei; füge bei Bedarf eine neue Datei hinzu. Gib MAXSIZE an.","tags":["datafile","add","add","tablespace","alter tablespace","storage"]},{"id":"resize-datafile","task":"Datafile-Größe ändern / Autoextend setzen","category":"Speicher","command":"-- Grow/shrink to a fixed size:\nALTER DATABASE DATAFILE '/u01/oradata/ORCL/customer_data01.dbf' RESIZE 5G;\n\n-- Enable autoextend:\nALTER DATABASE DATAFILE '/u01/oradata/ORCL/customer_data01.dbf'\n  AUTOEXTEND ON NEXT 256M MAXSIZE 30G;\n\n-- Disable autoextend:\nALTER DATABASE DATAFILE '/u01/oradata/ORCL/customer_data01.dbf' AUTOEXTEND OFF;","what":"Setzt ein Datafile auf eine feste Größe oder konfiguriert sein automatisches Wachstum.","when":"Um Platz zurückzugewinnen (Verkleinern), Platz vorab zu reservieren oder unkontrolliertes Wachstum zu stoppen.","caution":"Ein Verkleinern mit RESIZE kann nicht unter die belegten Blöcke am Dateiende gehen (ORA-03297). Verschiebe zuerst die Segmente.","tags":["datafile","resize","size","autoextend","maxsize","alter database"]},{"id":"move-datafile-online","task":"Datafile online verschieben","category":"Speicher","command":"ALTER DATABASE MOVE DATAFILE\n  '/u01/oradata/ORCL/customer_data01.dbf'\n  TO '/u03/oradata/ORCL/customer_data01.dbf';\n\n-- Move into ASM:\nALTER DATABASE MOVE DATAFILE\n  '/u01/oradata/ORCL/customer_data01.dbf' TO '+DATA';\n\n-- Use KEEP to retain the old file instead of deleting it:\n-- ALTER DATABASE MOVE DATAFILE '...' TO '...' KEEP;","what":"Verschiebt ein Datafile bei geöffneter Datenbank an einen neuen Ort (12c+); ändert den Speicher ohne Ausfallzeit.","when":"Beim Wechsel von Platte/Mount Point, bei der Migration nach ASM oder beim Umzug auf schnellen Speicher.","caution":"Erfordert 12c oder höher und Enterprise Edition. Das Verschieben erzeugt zusätzliches I/O. Ohne KEEP wird die alte Datei gelöscht.","since":"Oracle 12c","note":"Das Online-Verschieben von Datafiles (ALTER DATABASE MOVE DATAFILE) kam mit Oracle 12c (12.1); davor musste man zum Verschieben eines Datafiles den Tablespace/das Datafile OFFLINE setzen, auf OS-Ebene kopieren und mit ALTER DATABASE RENAME FILE neu zuordnen.","tags":["datafile","move","move","online","alter database move datafile","asm"]},{"id":"temp-tablespace","task":"Temp-Tablespace hinzufügen / Standard ändern","category":"Speicher","command":"-- Create a new temp tablespace:\nCREATE TEMPORARY TABLESPACE temp2\n  TEMPFILE '/u01/oradata/ORCL/temp2_01.dbf'\n  SIZE 2G AUTOEXTEND ON NEXT 256M MAXSIZE 20G;\n\n-- Change the database default temp:\nALTER DATABASE DEFAULT TEMPORARY TABLESPACE temp2;\n\n-- Add a tempfile to the old temp:\nALTER TABLESPACE temp ADD TEMPFILE '/u01/oradata/ORCL/temp_02.dbf' SIZE 2G;","what":"Stellt temporären Platz für Sort-/Hash-Operationen und Global Temporary Tables bereit; den Standard kannst du auf den neuen Tablespace umstellen.","when":"Bei ORA-01652 (unable to extend temp segment), einem Temp-Engpass oder zum Ändern der Temp-Größe.","caution":"Nach dem Ändern des Standards den alten Temp-Tablespace nicht sofort per DROP löschen, wenn er noch genutzt wird; lösche ihn, sobald die Sessions beendet sind.","tags":["temp","tablespace","tempfile","ora-01652","default temporary","storage"]},{"id":"tablespace-space","task":"Abfrage des freien Platzes im Tablespace","category":"Speicher","command":"SELECT df.tablespace_name,\n       ROUND(df.total_mb/1024,2)                 AS total_gb,\n       ROUND(NVL(fs.free_mb,0)/1024,2)           AS free_gb,\n       ROUND((df.total_mb-NVL(fs.free_mb,0))/df.total_mb*100,1) AS pct_used\nFROM  (SELECT tablespace_name, SUM(bytes)/1024/1024 total_mb\n         FROM dba_data_files GROUP BY tablespace_name) df\n  LEFT JOIN\n       (SELECT tablespace_name, SUM(bytes)/1024/1024 free_mb\n          FROM dba_free_space GROUP BY tablespace_name) fs\n    ON df.tablespace_name = fs.tablespace_name\nORDER BY pct_used DESC;","what":"Zeigt den gesamten und freien Platz sowie den Nutzungsanteil jedes Tablespace (auf Basis des aktuell zugewiesenen Platzes, ohne Autoextend-Potenzial).","when":"Platzüberwachung, Ermitteln, welcher Tablespace erweitert werden muss.","caution":"Diese Abfrage basiert auf der aktuellen Datafile-Größe; mit Autoextend kann sie weiter wachsen. Für Temp DBA_TEMP_FREE_SPACE verwenden. Nur Abfrage; kein Risiko.","tags":["tablespace","space","space","free","dba_free_space","usage","storage"]},{"id":"alter-parameter","task":"Parameter ändern (mit SCOPE)","category":"Speicher & Parameter","command":"-- Both the running instance and the spfile (default, dynamic parameters):\nALTER SYSTEM SET open_cursors=500 SCOPE=BOTH;\n\n-- Only for the next restart (static parameters):\nALTER SYSTEM SET processes=1000 SCOPE=SPFILE;\n\n-- Immediate only, do not write to the spfile:\nALTER SYSTEM SET optimizer_mode='ALL_ROWS' SCOPE=MEMORY;\n\n-- View the current value:\nSHOW PARAMETER open_cursors;","what":"Ändert einen Init-Parameter in der laufenden Instanz und/oder im spfile. SCOPE bestimmt das Verhalten.","when":"Für Tuning, Kapazitätsanpassungen und Fehlerbehebungen, die eine Parameteränderung erfordern.","caution":"Statische Parameter (processes, sga_max_size...) akzeptieren nur SCOPE=SPFILE und erfordern einen Neustart. Ein falscher Wert kann den Start der Instanz verhindern; sichere bei kritischen Parametern zuerst ein pfile.","tags":["parameter","parameter","alter system","scope","spfile","tuning"]},{"id":"flush-pool","task":"Shared Pool / Buffer Cache leeren (Flush)","category":"Speicher & Parameter","command":"-- Clear the SQL plan/cursor cache:\nALTER SYSTEM FLUSH SHARED_POOL;\n\n-- Clear the buffer cache (usually only for test/benchmark):\nALTER SYSTEM FLUSH BUFFER_CACHE;","what":"Ein Flush des Shared Pool entfernt alle gecachten SQL-/PL-SQL- und Dictionary-Informationen aus dem Speicher; ein Flush des Buffer Cache entfernt alle Datenblöcke.","when":"Um Planprobleme zu testen, nach einer einmaligen großen Operation oder für einen konsistenten Benchmark-Start.","caution":"Vorsicht IN PRODUKTION! Nach einem Flush wird jedes SQL neu geparst (Hard-Parse-Sturm) und Blöcke werden von der Platte gelesen; es folgt ein deutlicher vorübergehender Performanceeinbruch.","tags":["flush","shared pool","buffer cache","memory","alter system","cache"]},{"id":"spfile-pfile","task":"pfile aus spfile erzeugen und umgekehrt","category":"Speicher & Parameter","command":"-- spfile -> pfile (for backup/editing):\nCREATE PFILE='/tmp/init_orcl_backup.ora' FROM SPFILE;\n\n-- pfile -> spfile (make the edited pfile permanent; effective after restart):\nCREATE SPFILE FROM PFILE='/tmp/init_orcl_backup.ora';\n\n-- Create a pfile from the running instance values (11g+):\nCREATE PFILE='/tmp/init_current.ora' FROM MEMORY;","what":"Konvertiert zwischen dem binären spfile und dem Text-pfile; das pfile lässt sich manuell bearbeiten.","when":"Sicherung vor einer Parameteränderung, Reparatur eines beschädigten spfile, manuelle Korrektur eines statischen Parameters.","caution":"Nach CREATE SPFILE FROM PFILE werden Änderungen erst nach einem Neustart wirksam. Prüfe den Pfad, bevor du ein vorhandenes spfile überschreibst.","tags":["spfile","pfile","create pfile","create spfile","parameter","backup"]},{"id":"sga-pga-target","task":"SGA-/PGA-Zielwerte ändern","category":"Speicher & Parameter","command":"-- Automatic memory management (ASMM) targets:\nALTER SYSTEM SET sga_target=8G   SCOPE=BOTH;\nALTER SYSTEM SET pga_aggregate_target=4G SCOPE=BOTH;\n\n-- Upper limit (static, requires restart):\nALTER SYSTEM SET sga_max_size=12G SCOPE=SPFILE;\n\n-- View the current memory distribution:\nSELECT component, current_size/1024/1024 AS mb\nFROM   v$sga_dynamic_components ORDER BY component;","what":"Setzt die Speicherzielwerte für SGA und PGA; Oracle verwaltet die Komponenten automatisch (ASMM/AMM).","when":"Wenn dem Server RAM hinzugefügt wird, bei einem Speicherengpass oder Speicherdruck.","caution":"sga_target darf sga_max_size nicht übersteigen. sga_max_size ist statisch und erfordert einen Neustart. Lass RAM für das Betriebssystem und andere Instanzen übrig; Überbelegung verursacht Swapping/OOM.","tags":["sga","pga","memory","memory","target","sga_target","pga_aggregate_target"]},{"id":"kill-session","task":"Session beenden (kill session)","category":"Sessions & Prozesse","command":"-- First find the session to kill:\nSELECT sid, serial#, username, status, machine, program\nFROM   v$session\nWHERE  username = 'CUSTOMER_APP' AND status = 'ACTIVE';\n\n-- Kill it (starts rollback immediately):\nALTER SYSTEM KILL SESSION '145,12345' IMMEDIATE;\n\n-- If it hangs, the OS process can be terminated:\n-- ALTER SYSTEM DISCONNECT SESSION '145,12345' IMMEDIATE;","what":"Beendet die Session mit der angegebenen SID,SERIAL#; ihre offene Transaktion wird zurückgerollt.","when":"Um eine Session zu stoppen, die Sperren hält, eine außer Kontrolle geratene Abfrage ausführt oder Verbindungen leckt.","caution":"Bei einer offenen Transaktion kann das Rollback lange dauern (bei wenig geleisteter Arbeit ist es schnell erledigt). Prüfe zuerst per Abfrage, damit du nicht die falsche SID beendest. Das Rollback großer DML erzeugt I/O-Last.","tags":["kill session","kill session","v$session","alter system","sid","serial"]},{"id":"long-running-queries","task":"Lang laufende Abfragen / Sessions anzeigen","category":"Sessions & Prozesse","command":"-- Active, long-running SQL:\nSELECT s.sid, s.serial#, s.username, s.status,\n       s.last_call_et AS seconds_active,\n       s.sql_id, q.sql_text,\n       s.event, s.machine, s.program\nFROM   v$session s\n  LEFT JOIN v$sql q ON q.sql_id = s.sql_id\n                   AND q.child_number = s.sql_child_number\nWHERE  s.type = 'USER'\n  AND  s.status = 'ACTIVE'\n  AND  s.last_call_et > 60\nORDER BY s.last_call_et DESC;\n\n-- Long operations with percentage progress:\nSELECT sid, serial#, opname, sofar, totalwork,\n       ROUND(sofar/NULLIF(totalwork,0)*100,1) AS pct, time_remaining\nFROM   v$session_longops\nWHERE  sofar < totalwork ORDER BY start_time DESC;","what":"Listet aktive Sessions, die länger als 60 Sekunden laufen, und den Fortschritt lang laufender Batch-Operationen.","when":"Bei Beschwerden über Langsamkeit, um zu finden, welche Abfrage das System auslastet.","caution":"last_call_et ist die Zeit seit dem letzten Aufruf; sie kann auch bei einer inaktiven Session groß sein, daher ist der Filter status='ACTIVE' wichtig. Nur Abfrage; kein Risiko.","tags":["long running","long query","v$session","v$sql","v$session_longops","active"]},{"id":"find-blocking","task":"Blockierende Session finden","category":"Sessions & Prozesse","command":"SELECT bl.sid       AS blocker_sid,\n       bl.serial#   AS blocker_serial,\n       bl.username  AS blocker_user,\n       w.sid        AS waiter_sid,\n       w.serial#    AS waiter_serial,\n       w.username   AS waiter_user,\n       w.seconds_in_wait AS wait_sec,\n       w.event\nFROM   v$session w\n  JOIN v$session bl ON bl.sid = w.blocking_session\nWHERE  w.blocking_session IS NOT NULL\nORDER BY w.seconds_in_wait DESC;","what":"Listet Paare aus blockierender und wartender Session mit Wartezeit und Event.","when":"Um bei Sperrproblemen wie \"Anwendung hängt / enq: TX wait\" den Verursacher zu finden.","caution":"Stimme dich vor dem Beenden des Blockers mit dem fachlich Verantwortlichen ab; es kann sich um eine kritische Operation handeln. Nur Abfrage; kein Risiko.","tags":["blocking","lock","lock","blocker","waiter","v$session","blocking_session"]},{"id":"gather-stats","task":"Statistiken erheben (DBMS_STATS)","category":"Wartung","command":"-- Single table (including cascaded indexes):\nEXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'CUSTOMER', cascade => TRUE, estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, degree => 4);\n\n-- Whole schema:\nEXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'CUSTOMER_APP', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, cascade => TRUE, degree => 4);\n\n-- Whole database (run only in a maintenance window / off-peak hours):\nEXEC DBMS_STATS.GATHER_DATABASE_STATS(estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, degree => 4);","what":"Erhebt Tabellen-/Indexstatistiken für den Optimizer; sorgt dafür, dass korrekte Ausführungspläne erzeugt werden.","when":"Nach großen DML-Operationen/Ladevorgängen, bei Planregressionen, nach einem neuen Index/einer neuen Tabelle.","caution":"Statistikänderungen können Ausführungspläne ändern; auf kritischen Systemen zuerst mit AWR testen/vergleichen. DEGREE-Parallelität verbraucht CPU. GATHER_DATABASE_STATS ist zu Spitzenzeiten belastend.","tags":["dbms_stats","statistics","statistics","gather","optimizer","maintenance"]},{"id":"rebuild-index-online","task":"Index online neu aufbauen","category":"Wartung","command":"-- Regular index:\nALTER INDEX customer_app.idx_customer_email REBUILD ONLINE PARALLEL 4;\n\n-- Moving it to a different tablespace:\nALTER INDEX customer_app.idx_customer_email\n  REBUILD ONLINE TABLESPACE customer_idx PARALLEL 4;\n\n-- Reset parallelism after the rebuild (so plans are not affected):\nALTER INDEX customer_app.idx_customer_email NOPARALLEL;","what":"Baut den Index neu auf, ohne DML zu blockieren (ONLINE); beseitigt Fragmentierung und kann den Tablespace wechseln.","when":"Index-Bloat/Fragmentierung, Verschieben eines Index in einen anderen Tablespace, Reparieren eines UNUSABLE-Index.","caution":"ONLINE benötigt zusätzlichen temporären Platz und I/O. PARALLEL bleibt bestehen; setze es nach dem Rebuild mit NOPARALLEL zurück, sonst laufen Abfragen unbeabsichtigt parallel. ONLINE wird nicht in jeder Version für Bitmap-Indizes unterstützt. Ein ONLINE-Rebuild ist nur in der Enterprise Edition verfügbar.","tags":["index","rebuild","online","rebuild","alter index","fragmentation","maintenance"]},{"id":"rman-full-backup","task":"Schnelles Vollbackup mit RMAN","category":"Wartung","command":"# From the shell: rman target /\nRUN {\n  ALLOCATE CHANNEL c1 DEVICE TYPE DISK;\n  ALLOCATE CHANNEL c2 DEVICE TYPE DISK;\n  BACKUP AS COMPRESSED BACKUPSET\n    DATABASE\n    PLUS ARCHIVELOG\n    TAG 'FULL_DAILY';\n  BACKUP CURRENT CONTROLFILE;\n  DELETE NOPROMPT OBSOLETE;\n  RELEASE CHANNEL c1;\n  RELEASE CHANNEL c2;\n}","what":"Erstellt parallel über zwei Kanäle ein komprimiertes Vollbackup der gesamten Datenbank und der Archivlogs, sichert die Controlfile und löscht veraltete Backups.","when":"Tägliches/wöchentliches Vollbackup, um einen Wiederherstellungspunkt zu schaffen.","caution":"COMPRESSED verbraucht CPU. DELETE OBSOLETE entfernt alte Backups gemäß der Aufbewahrungsrichtlinie; setze die Richtlinie (CONFIGURE RETENTION POLICY) korrekt. ARCHIVELOG setzt voraus, dass die DB im ARCHIVELOG-Modus läuft. Stelle sicher, dass am Backup-Ziel genug Platz ist.","tags":["rman","backup","backup","full","compressed","archivelog","maintenance"]},{"id":"controlfile-trace","task":"Trace-Backup der Controlfile erstellen","category":"Wartung","command":"-- Generate a trace (CREATE CONTROLFILE script):\nALTER DATABASE BACKUP CONTROLFILE TO TRACE\n  AS '/tmp/create_controlfile.sql' REUSE;\n\n-- Binary controlfile copy:\nALTER DATABASE BACKUP CONTROLFILE TO '/backup/controlfile_orcl.ctl' REUSE;","what":"Erzeugt das SQL-Skript (Trace), das die Controlfile neu anlegt, und/oder eine binäre Kopie davon.","when":"Nach Strukturänderungen (neue Datafiles/Redo), bei der Vorbereitung auf Disaster Recovery oder um die Controlfile neu anzulegen, wenn alle Kopien verloren sind.","caution":"Aktualisiere die Trace-Datei bei jeder Strukturänderung; ein altes Skript erzeugt die falsche Struktur. REUSE überschreibt eine vorhandene Datei. Kein Risiko für das System, aber bewahre die erzeugte Datei sicher auf.","tags":["controlfile","trace","backup","backup","alter database","recovery","maintenance"]}]},"recipes/oracle-ha.json":{"engineId":"oracle","engine":"Oracle","order":1,"recipes":[{"id":"rac-srvctl-db-status","task":"Status von RAC-Datenbank, Instanzen und Services mit srvctl prüfen","category":"RAC & Grid","command":"# Which node each instance runs on, and in what state:\nsrvctl status database -db <db_unique_name> -verbose\n# Configuration registered in Clusterware (Oracle home, spfile, disk group, policy, role):\nsrvctl config database -db <db_unique_name>\n# A single instance:\nsrvctl status instance -db <db_unique_name> -instance <instance>\n# Which instances the services are running on:\nsrvctl status service -db <db_unique_name>","what":"Zeigt den Laufzeitzustand der Datenbank, jeder Instanz und ihrer Services aus Sicht von Clusterware (CRS) sowie die registrierte Konfiguration; -verbose ergänzt den Open Mode der Instanz.","when":"Als erste Prüfung vor und nach jedem RAC-Eingriff; bei einem Ausfall, um zu sehen, welcher Knoten/welche Instanz betroffen ist.","caution":"Nur lesend. Führe es aus dem Oracle Home der Datenbank aus (Benutzer oracle); ein srvctl aus einem Grid Home einer anderen Version kann irreführende Ergebnisse oder Fehler liefern.","since":"12c","note":"Die langen Optionen -db/-instance/-service gibt es ab 12c; die kurzen 11g-Optionen (-d, -i, -s) funktionieren in 19c noch, sind aber veraltet.","tags":["rac","srvctl","status","instance","service","cluster","config"],"untested":true},{"id":"rac-srvctl-start-stop","task":"RAC-Datenbank oder einzelne Instanz mit srvctl stoppen/starten (rolling)","category":"RAC & Grid","command":"# Whole database (all instances):\nsrvctl stop database -db <db_unique_name> -stopoption immediate\nsrvctl start database -db <db_unique_name>\n# Rolling maintenance: stop one instance, moving its services to the other instances\nsrvctl stop instance -db <db_unique_name> -instance <instance> \\\n  -stopoption immediate -drain_timeout 300 -failover\nsrvctl start instance -db <db_unique_name> -instance <instance>\n# By node:\nsrvctl stop instance -db <db_unique_name> -node <node> -stopoption immediate\n# Start in a specific open mode (e.g. a physical standby in mount):\nsrvctl start database -db <db_unique_name> -startoption mount","what":"Stoppt/startet die Datenbank oder die gewählte Instanz kontrolliert über Clusterware; mit -failover wechseln die Services der gestoppten Instanz auf verfügbare Instanzen, und -drain_timeout wartet, bis Sessions abgebaut sind.","when":"Für Rolling-Neustarts (Knoten für Knoten) bei Patching, OS-Wartung oder Parameteränderungen; für ein geplantes vollständiges Herunterfahren.","caution":"Verwende in RAC srvctl statt SQL*Plus SHUTDOWN; sonst kennt Clusterware das TARGET der Ressource nicht und startet die Instanz eventuell neu. stop database fährt alle Instanzen herunter = vollständiger Ausfall. Haben Services keine verfügbaren Instanzen definiert, kann -failover die Verbindungen nicht verschieben.","since":"12c","note":"-drain_timeout und -failover gibt es ab 12.2. Verwende -stopoption abort nur im Notfall; der nächste Start erfordert Instance Recovery.","tags":["rac","srvctl","stop","start","rolling","drain","shutdown"],"untested":true},{"id":"rac-service-add","task":"RAC-Service mit Preferred-/Available-Instanzen hinzufügen (inkl. Application Continuity)","category":"RAC & Grid","command":"srvctl add service -db <db_unique_name> -service <service> \\\n  -preferred <inst1>,<inst2> -available <inst3> \\\n  -pdb <pdb> -role PRIMARY -policy AUTOMATIC \\\n  -failovertype TRANSACTION -commit_outcome TRUE \\\n  -failoverretry 30 -failoverdelay 10 \\\n  -clbgoal SHORT -rlbgoal SERVICE_TIME -notification TRUE \\\n  -drain_timeout 300 -stopoption IMMEDIATE\nsrvctl start service -db <db_unique_name> -service <service>\n# Verify:\nsrvctl config service -db <db_unique_name> -service <service>\nsrvctl status service -db <db_unique_name> -service <service>","what":"Definiert einen Service für Anwendungen, der normalerweise auf den Preferred-Instanzen läuft und bei deren Ausfall auf eine Available-Instanz wechselt; mit -role PRIMARY startet er unter Data Guard nur in der Primary-Rolle.","when":"Um Anwendungen mit einem Service statt mit einer Instanz zu verbinden, Workloads auf Knoten zu verteilen und nahtloses Failover (Drain/Failover) bei geplanter Wartung zu ermöglichen.","caution":"Anwendungen müssen sich mit diesem Servicenamen verbinden (über SCAN), nicht mit einer SID oder einem Instanznamen. -failovertype TRANSACTION (Application Continuity) erfordert Unterstützung durch Anwendung/Treiber; im Zweifel beginne mit -failovertype SELECT (TAF) oder NONE. Verwende für Anwendungen keine SYS$-Services und nicht den Standardservice, der nach db_unique_name benannt ist.","since":"12c","note":"-pdb gibt es ab 12c, -drain_timeout und -stopoption ab 12.2. Ändern mit srvctl modify service; eine dauerhafte Preferred-Änderung erfordert -modifyconfig.","tags":["rac","service","srvctl","preferred","available","taf","application continuity"],"untested":true},{"id":"rac-service-relocate","task":"RAC-Service auf eine andere Instanz verlagern","category":"RAC & Grid","command":"# First check the current placement:\nsrvctl status service -db <db_unique_name> -service <service>\n# Relocate by instance (sessions are drained):\nsrvctl relocate service -db <db_unique_name> -service <service> \\\n  -oldinst <inst1> -newinst <inst3> -drain_timeout 120 -wait YES\n# By node (policy-managed / singleton service):\nsrvctl relocate service -db <db_unique_name> -service <service> \\\n  -currentnode <node1> -targetnode <node2>\n# Session distribution per instance (SQL):\nSELECT inst_id, service_name, COUNT(*) AS sessions\nFROM   gv$session\nWHERE  type = 'USER'\nGROUP  BY inst_id, service_name\nORDER  BY service_name, inst_id;","what":"Verschiebt einen laufenden Service von einer Instanz/einem Knoten auf eine(n) andere(n); neue Verbindungen gehen an die Zielinstanz, bestehende Sessions werden während drain_timeout sich selbst überlassen.","when":"Um einen Knoten vor der Wartung zu leeren, einen Service nach einem Failover auf seine Preferred-Instanz zurückzuholen oder die Last neu zu verteilen.","caution":"Die Verlagerung ist temporär: Sie ändert die Service-Konfiguration (Preferred/Available) nicht; beim Neustart der Instanz kehrt der Service zu seiner Preferred-Instanz zurück. -force beendet bestehende Sessions; kann sich die Anwendung nicht neu verbinden, sehen die Benutzer einen Ausfall.","since":"12c","note":"-drain_timeout und -wait gibt es ab 12.2. Die SQL-Abfrage läuft auch auf einer Einzelinstanz; der srvctl-Teil erfordert RAC.","tags":["rac","service","relocate","move","srvctl","drain","gv$session"],"untested":true},{"id":"rac-crsctl-stat-res","task":"Status der Cluster-Ressourcen prüfen (crsctl stat res -t)","category":"RAC & Grid","command":"# From the Grid home, as the grid user:\ncrsctl stat res -t\n# OHASD (init) layer: ora.cssd, ora.crsd, ora.asm, ora.ctssd...\ncrsctl stat res -t -init\n# Only problem resources (TARGET is ONLINE but STATE is not):\ncrsctl stat res -t -w \"((TARGET = ONLINE) and (STATE != ONLINE))\"\n# Profile and runtime details of a single resource:\ncrsctl stat res ora.<db_unique_name>.db -p\ncrsctl stat res ora.<db_unique_name>.db -v","what":"Listet jede von Clusterware verwaltete Ressource (Datenbank, Instanz, Listener, SCAN, VIP, ASM, Diskgroup, Netzwerk) je Knoten mit TARGET/STATE in Tabellenform.","when":"Nach dem Start eines Knotens, nach dem Patching und bei jeder Ausfallanalyse; um OFFLINE-, INTERMEDIATE- oder UNKNOWN-Ressourcen zu finden.","caution":"Nur lesend. Verwalte ora.*-Ressourcen nicht direkt mit crsctl start/stop/modify; nutze für Datenbankressourcen srvctl (die unterstützte Methode).","note":"Der Ressourcenname enthält den db_unique_name in Kleinbuchstaben (ora.orcl.db). Die Ausgabe von -init ist nur als root/grid vollständig sichtbar.","tags":["rac","grid","crsctl","resource","resource","clusterware","status"],"untested":true},{"id":"rac-crsctl-check-cluster","task":"Zustand des Cluster-Stacks, Knotenliste und Clusterware-Version prüfen","category":"RAC & Grid","command":"# CRS, CSS, EVM health check on all nodes:\ncrsctl check cluster -all\n# Full stack on this node, including OHASD:\ncrsctl check crs\n# Node list: number, status (Active/Inactive), pinned:\nolsnodes -n -s -t\n# Active version, patch level and upgrade state (after a rolling patch):\ncrsctl query crs activeversion -f\ncrsctl query crs softwareversion -all","what":"Zeigt, dass die Daemons Cluster Ready Services, Cluster Synchronization Services und Event Manager auf jedem Knoten laufen, die Cluster-Mitglieder sowie die aktive/Software-Version von Clusterware.","when":"Beim morgendlichen Health-Check, nach einer Node Eviction oder einem Reboot und nach einem Rolling Patch/Upgrade, um zu bestätigen, dass alle Knoten auf demselben Stand sind.","caution":"Nur lesend. Zeigt activeversion -f \"ROLLING PATCH\" oder einen abweichenden Patch-Level, ist eine Rolling-Operation unvollständig; schließe sie ab (crsctl stop rollingpatch / rootcrs -postpatch), bevor du neue Änderungen vornimmst.","since":"12c","note":"activeversion -f und softwareversion -all gibt es ab 12c.","tags":["rac","grid","crsctl","olsnodes","health","cluster","version"],"untested":true},{"id":"rac-ocr-backup","task":"OCR-Integrität prüfen und OCR-Backup erstellen (ocrcheck / ocrconfig)","category":"RAC & Grid","command":"# OCR integrity and capacity check (run as root for the logical check):\nocrcheck\nocrcheck -config\n# Automatic backups (every 4 hours, daily, weekly):\nocrconfig -showbackup\n# Manual backup before a risky change (root):\nocrconfig -manualbackup\nocrconfig -showbackup manual\n# Move the backup location to a disk group (root):\nocrconfig -backuploc +<diskgroup>","what":"Zeigt Integrität, Speicherorte und Backups der Oracle Cluster Registry (des Repositorys der Cluster-Konfiguration); -manualbackup erstellt sofort ein physisches OCR-Backup.","when":"Unmittelbar vor Änderungen an der Cluster-Konfiguration wie Hinzufügen/Entfernen von Knoten, Grid-Patching/-Upgrade, Änderungen an Voting Disks oder am Netzwerk (oifcfg).","caution":"ocrconfig -restore muss bei gestopptem CRS auf allen Knoten und im Exklusivmodus laufen; das Wiederherstellen des falschen Backups kann den Cluster startunfähig machen, daher nur zusammen mit Oracle Support. Automatische Backups liegen standardmäßig eventuell nur auf der lokalen Platte eines einzelnen Knotens; verlege den Speicherort in eine gemeinsame Diskgroup.","note":"Als Benutzer grid überspringt ocrcheck die Prüfung auf logische Korruption (\"Logical corruption check bypassed\"). Backups in eine Diskgroup gibt es ab 12c.","tags":["rac","ocr","ocrcheck","ocrconfig","backup","backup","grid"],"untested":true},{"id":"rac-votedisk-query","task":"Voting Disks und CSS-Timeouts prüfen","category":"RAC & Grid","command":"# Voting disks, their state and disk group:\ncrsctl query css votedisk\n# Node eviction thresholds (seconds):\ncrsctl get css misscount\ncrsctl get css disktimeout\ncrsctl get css reboottime\n# Move the voting disks to another ASM disk group (root/grid):\ncrsctl replace votedisk +<diskgroup>","what":"Zeigt den Zustand der für die Cluster-Mitgliedschaft genutzten Voting Disks (Voting Files) und die Timeouts für Netzwerk-/Disk-Heartbeat; replace votedisk verschiebt alle Voting Files in die Ziel-Diskgroup.","when":"Bei Storage-Änderungen, Neugestaltung von Diskgroups oder der Analyse einer Node Eviction; um zu bestätigen, dass eine Mehrheit (Quorum) der Voting Disks erreichbar ist.","caution":"Ein Knoten, der nicht auf mehr als die Hälfte der Voting Disks zugreifen kann, startet sich selbst neu. Die Ziel-Diskgroup braucht genug Failure Groups (Normal Redundancy: 3, High: 5; bei Bedarf eine Quorum-Failgroup verwenden). Ändere misscount/disktimeout nicht ohne Freigabe durch Oracle Support.","note":"Standardwerte: misscount 30 s, disktimeout 200 s (Linux).","tags":["rac","voting disk","votedisk","css","misscount","quorum","eviction"],"untested":true},{"id":"rac-cluvfy-checks","task":"Vor-/Nachprüfungen mit dem Cluster Verification Utility (cluvfy) ausführen","category":"RAC & Grid","command":"# Full check after a Grid installation/change:\ncluvfy stage -post crsinst -n all -verbose\n# Before adding a node:\ncluvfy stage -pre nodeadd -n <new_node> -verbose\n# Component checks:\ncluvfy comp nodecon -n all -verbose\ncluvfy comp clocksync -n all -verbose\ncluvfy comp ocr -n all -verbose\ncluvfy comp asm -n all -verbose\n# Report only failed checks:\ncluvfy stage -post crsinst -n all -verbose | grep -iE \"failed|warning\"","what":"Prüft auf allen Knoten Netzwerkkonnektivität, Uhrzeitsynchronisation, OCR, ASM, User Equivalence und OS-Voraussetzungen; meldet Probleme mit Korrekturvorschlägen.","when":"Vor und nach der Grid-Installation, dem Hinzufügen von Knoten und Upgrades; als Basisvalidierung bei unerklärlichen Cluster-Problemen.","caution":"Nur lesend, verbindet sich aber per ssh mit allen Knoten; der Benutzer grid braucht ssh-Äquivalenz. Führe die -fixup-Skripte (root) nicht ungeprüft aus; sie ändern OS-Parameter.","note":"Für einen breiteren Best-Practice-Scan nutze orachk/exachk aus AHF. Das neueste cluvfy von My Oracle Support bietet mehr Prüfungen.","tags":["rac","cluvfy","check","verification","precheck","grid","orachk"],"untested":true},{"id":"rac-node-eviction-diag","task":"Ursache einer Node Eviction / eines unerwarteten Knotenneustarts diagnostizieren","category":"RAC & Grid","command":"# 1) When, which node, evicted by whom: CRS alert log (on every node)\ngrep -E \"CRS-1601|CRS-1607|CRS-1608|CRS-1609|CRS-1610|CRS-1611|CRS-1612|CRS-8011\" \\\n  $ORACLE_BASE/diag/crs/$(hostname -s)/crs/trace/alert.log\n# 2) CSS details: was the network heartbeat or the disk heartbeat lost\ngrep -iE \"evict|misscount|heartbeat|disk HB|network HB\" \\\n  $ORACLE_BASE/diag/crs/$(hostname -s)/crs/trace/ocssd.trc | tail -100\n# 3) Who triggered the reboot (cssdagent/cssdmonitor):\nls -lt $ORACLE_BASE/diag/crs/$(hostname -s)/crs/trace/ohasd_cssd*.trc\n# 4) OS resources at the time of the event (Cluster Health Monitor):\noclumon dumpnodeview -allnodes -s \"2026-01-15 03:10:00\" -e \"2026-01-15 03:20:00\"\n# 5) Diagnostic collection from all nodes (AHF/TFA):\ntfactl diagcollect -crs -os -last 4h\n# 6) OS side: /var/log/messages, journalctl -k (OOM, NIC, multipath, I/O errors)","what":"Verfolgt die Kette einer Node Eviction der Reihe nach: CRS-16xx-Meldungen im CRS-Alert-Log, Heartbeat-Verluste in ocssd.trc, OS-Metriken aus CHM und die mit TFA gesammelten Logs aller Knoten.","when":"Wenn ein Knoten unerwartet neu startet oder aus dem Cluster entfernt wird, für die Ursachenanalyse (RCA) und einen Oracle Support SR.","caution":"Sammle Logs von jedem Knoten, die den Zeitpunkt des Ereignisses abdecken; Trace-Dateien rotieren und Beweise können verloren gehen. Prüfe, dass die Zeitzonen auf allen Knoten gleich sind (CTSS/NTP). Wende keine \"Fixes\" wie das Erhöhen von misscount an, bevor die Beweise gesichert sind.","note":"Typische Ursachen: Ausfall/Paketverlust des privaten Interconnects, I/O-Latenz der Voting Disks, CPU-/Speichererschöpfung (OOM, Swap), Zeitsprünge. Der Pfad entspricht dem ADR-Layout ab 12c; in 11g liegt er unter $GRID_HOME/log/<host>/.","tags":["rac","eviction","reboot","ocssd","tfactl","oclumon","rca"],"untested":true},{"id":"rac-gv-cluster-waits","task":"Clusterweite Wait Events (gc-Waits) je Instanz mit GV$ analysieren","category":"RAC & Grid","command":"-- Cluster-class waits per instance (since startup):\nSELECT inst_id, event, total_waits,\n       ROUND(time_waited_micro/1e6) AS time_waited_s,\n       ROUND(time_waited_micro/NULLIF(total_waits,0)/1000, 2) AS avg_ms\nFROM   gv$system_event\nWHERE  wait_class = 'Cluster'\nORDER  BY time_waited_micro DESC\nFETCH FIRST 15 ROWS ONLY;\n\n-- Sessions waiting right now, by instance and event:\nSELECT inst_id, event, COUNT(*) AS sessions\nFROM   gv$session\nWHERE  wait_class <> 'Idle' AND state = 'WAITING'\nGROUP  BY inst_id, event\nORDER  BY sessions DESC;\n\n-- ASH for the last 15 minutes: which instance is waiting on gc\nSELECT inst_id, event, COUNT(*) AS samples\nFROM   gv$active_session_history\nWHERE  sample_time > SYSTIMESTAMP - INTERVAL '15' MINUTE\n  AND  wait_class = 'Cluster'\nGROUP  BY inst_id, event\nORDER  BY samples DESC\nFETCH FIRST 10 ROWS ONLY;","what":"Zeigt über GV$-Views die Waits der Klasse Cluster (gc cr/current block 2-way/3-way, gc buffer busy, gc cr request) und ihre durchschnittliche Latenz über alle Instanzen in einer Abfrage.","when":"Wenn Benutzer Langsamkeit im RAC melden, um zu erkennen, ob das Problem vom Interconnect/Global Cache kommt und auf welche Instanz es sich konzentriert.","caution":"gv$active_session_history erfordert die Diagnostics-Pack-Lizenz. Liegt avg_ms über einigen ms, prüfe zuerst den Interconnect (MTU, Paketverlust, netstat -s) und die LMS-CPU; gc buffer busy deutet meist auf einen Hot Block der Anwendung hin (Sequence, rechtslastiger Index).","note":"Läuft auch auf einer Einzelinstanz (liefert inst_id = 1).","tags":["rac","gv$","gc","wait","wait event","interconnect","global cache"]},{"id":"asm-asmcmd-lsdg-du","task":"Nutzung von Diskgroups, Disks und Verzeichnissen mit asmcmd anzeigen (lsdg / lsdsk / du)","category":"ASM","command":"# As the grid user, with ORACLE_SID=+ASM1 (or +ASM):\nasmcmd lsdg\n# Disks in the disk group: -k sizes, -p state (header/mount/mode):\nasmcmd lsdsk -k -G <diskgroup>\nasmcmd lsdsk -p -G <diskgroup>\n# I/O error and timing statistics:\nasmcmd lsdsk --statistics -G <diskgroup>\n# Disks that can be added (CANDIDATE/PROVISIONED/FORMER):\nasmcmd lsdsk --candidate -p\n# Space used by a database directory (MB):\nasmcmd du +<diskgroup>/<db_unique_name>\nasmcmd ls -l -s +<diskgroup>/<db_unique_name>/DATAFILE","what":"Zeigt auf der Kommandozeile Redundanz, Gesamt-/freien Platz und Usable_file_MB der Diskgroups, den Zustand der Disks und den Platz, den eine Datenbank in ASM belegt.","when":"Für Kapazitätsprüfungen, um vor dem Hinzufügen Kandidaten-Disks zu ermitteln und um bei einem Alarm \"Diskgroup voll\" die Datenbank zu finden, die den Platz verbraucht.","caution":"Nur lesend. Ist Usable_file_MB in lsdg negativ, kann die Diskgroup nach dem Verlust einer Disk/Failgroup die Redundanz nicht wiederherstellen; schaffe sofort Platz oder füge Disks hinzu. du liefert zwei Werte: Used_MB und Mirror_used_MB (gespiegelte Nutzung).","note":"Das SQL-Pendant findest du in den Rezepten asm-diskgroup-space und asm-disk-health.","tags":["asm","asmcmd","lsdg","lsdsk","du","space","disk group"],"untested":true},{"id":"asm-disk-health","task":"ASM-Disk-Zustand, Fehlerzähler und Kandidaten-Disks mit SQL prüfen","category":"ASM","command":"-- On the ASM instance (or from a DB instance; there only the disk groups in use are visible):\nSELECT dg.name AS diskgroup, d.disk_number, d.name AS disk_name, d.path,\n       d.header_status, d.mount_status, d.mode_status, d.state,\n       d.failgroup, d.total_mb, d.free_mb, d.read_errs, d.write_errs\nFROM   v$asm_disk d\nLEFT   JOIN v$asm_diskgroup dg ON dg.group_number = d.group_number\nORDER  BY dg.name, d.disk_number;\n\n-- Disk group summary: AU size, offline disks, voting files:\nSELECT name, type, allocation_unit_size/1048576 AS au_mb,\n       compatibility, database_compatibility, offline_disks, voting_files\nFROM   v$asm_diskgroup;\n\n-- Databases using this ASM:\nSELECT instance_name, db_name, status, software_version, compatible_version\nFROM   v$asm_client;","what":"Listet je Disk header_status (MEMBER, CANDIDATE, PROVISIONED, FORMER), Mount-/Mode-Zustand, Failgroup und Lese-/Schreibfehlerzähler; dazu die Anzahl der Offline-Disks je Diskgroup und die ASM-Clients.","when":"Vor dem Hinzufügen von Disks, um zu prüfen, dass eine neue LUN als CANDIDATE/PROVISIONED erscheint; bei einem Disk-Ausfall, um zu sehen, welche Disk OFFLINE ist und ob I/O-Fehler vorliegen.","caution":"Nur lesend. v$asm_disk führt bei jedem Aufruf eine Disk Discovery durch und ist auf Systemen mit vielen Disks langsam; für häufiges Monitoring nutze v$asm_disk_stat (keine Discovery, zeigt keine neuen Disks). Füge eine Disk mit header_status MEMBER niemals mit FORCE einer anderen Diskgroup hinzu; das zerstört die vorhandenen Daten.","note":"Liefert ohne ASM keine Zeilen; eine aussagekräftige Ausgabe erfordert eine ASM-Instanz.","tags":["asm","v$asm_disk","disk","header_status","error","candidate","failgroup"]},{"id":"asm-diskgroup-attributes","task":"Attribute von ASM-Diskgroups anzeigen und ändern (compatible, disk_repair_time)","category":"ASM","command":"-- Current attributes:\nSELECT dg.name AS diskgroup, a.name, a.value\nFROM   v$asm_attribute a\nJOIN   v$asm_diskgroup dg ON dg.group_number = a.group_number\nWHERE  a.name IN ('compatible.asm','compatible.rdbms','au_size','disk_repair_time',\n                  'failgroup_repair_time','content.type','sector_size')\nORDER  BY dg.name, a.name;\n\n-- Change (ASM instance, as SYSASM):\nALTER DISKGROUP <diskgroup> SET ATTRIBUTE 'disk_repair_time' = '8h';\nALTER DISKGROUP <diskgroup> SET ATTRIBUTE 'compatible.asm'   = '19.0.0.0.0';\nALTER DISKGROUP <diskgroup> SET ATTRIBUTE 'compatible.rdbms' = '19.0.0.0.0';\n\n-- asmcmd equivalent:\n-- asmcmd lsattr -l -G <diskgroup>\n-- asmcmd setattr -G <diskgroup> disk_repair_time '8h'","what":"Zeigt und ändert die Kompatibilitätsstufen der Diskgroup (compatible.asm/rdbms), die AU-Größe und wie lange eine vorübergehend ausgefallene Disk vor dem Entfernen behalten wird (disk_repair_time).","when":"Nach einem Grid-Upgrade, um neue ASM-Funktionen zu aktivieren (Flex Diskgroups, größere Dateilimits), oder um disk_repair_time bei geplanter Storage-Wartung zu verlängern.","caution":"compatible.asm und compatible.rdbms KÖNNEN NACH DEM ERHÖHEN NICHT MEHR GESENKT WERDEN. Ist compatible.rdbms höher als die COMPATIBLE-Einstellung der ältesten Datenbank, die diese Diskgroup nutzt, kann diese Datenbank sie nicht mounten. Erhöhe zuerst compatible.asm, dann compatible.rdbms. au_size lässt sich nur beim Anlegen der Diskgroup festlegen.","note":"Ohne ASM liefert die Abfrage keine Zeilen. Der Standardwert für disk_repair_time ist 12h.","tags":["asm","attribute","compatible","disk_repair_time","disk group","au_size","v$asm_attribute"],"untested":true},{"id":"dg-show-configuration","task":"Data-Guard-Broker-Konfiguration und Datenbankstatus anzeigen (DGMGRL)","category":"Data Guard","command":"# From the shell (connect with a password; also required for switchover/failover):\ndgmgrl sys@<primary_db_unique_name>\nSHOW CONFIGURATION VERBOSE;\nSHOW DATABASE VERBOSE '<standby_db_unique_name>';\n# Monitorable properties:\nSHOW DATABASE '<primary_db_unique_name>' 'LogXptStatus';\nSHOW DATABASE '<standby_db_unique_name>' 'RecvQEntries';\nSHOW DATABASE '<primary_db_unique_name>' 'InconsistentLogXptProps';\nSHOW DATABASE '<standby_db_unique_name>' 'StatusReport';","what":"Zeigt den Gesamtstatus der Broker-Konfiguration (SUCCESS/WARNING/ERROR), den Protection Mode, die Rolle jeder Datenbank, Transport-/Apply-Lag und Inkonsistenzen zwischen Broker und spfile.","when":"Vor und nach jeder Data-Guard-Operation (Switchover, Failover, Parameteränderung); beim täglichen Health-Check.","caution":"Nur lesend. Ändere bei aktivem Broker keine Data-Guard-Parameter wie log_archive_dest_n oder log_archive_config per SQL; der Broker überschreibt sie mit seinen eigenen Werten. Nimm Änderungen mit EDIT DATABASE ... SET PROPERTY vor, ab 19c für Initialisierungsparameter mit EDIT DATABASE ... SET PARAMETER.","note":"StatusReport listet ORA-Fehler je Datenbank; so findest du am schnellsten die Ursache eines WARNING-Status. InconsistentProperties ist in 19c veraltet und liefert immer keinen Wert; nutze InconsistentLogXptProps für Inkonsistenzen beim Redo-Transport.","tags":["data guard","dgmgrl","broker","configuration","standby","status","lag"],"untested":true},{"id":"dg-readiness-sql","task":"Primary-/Standby-Rolle und Switchover-Bereitschaft mit SQL prüfen","category":"Data Guard","command":"SELECT name, db_unique_name, database_role, open_mode, protection_mode,\n       switchover_status, force_logging, flashback_on, dataguard_broker\nFROM   v$database;\n\n-- State of redo destinations and gaps (on the primary):\nSELECT dest_id, dest_name, status, type, database_mode,\n       recovery_mode, gap_status, error\nFROM   v$archive_dest_status\nWHERE  status <> 'INACTIVE';\n\n-- Missing archivelog range on the standby:\nSELECT thread#, low_sequence#, high_sequence# FROM v$archive_gap;","what":"Zeigt Datenbankrolle, Open Mode, Protection Mode, switchover_status (TO STANDBY / TO PRIMARY / SESSIONS ACTIVE / NOT ALLOWED), FORCE LOGGING und Flashback-Zustand sowie Fehler und Lücken der Redo-Ziele.","when":"Als Vorprüfung vor Switchover/Failover, nach dem Aufbau eines neuen Standby und für grundlegendes Monitoring, wo der Broker nicht genutzt wird.","caution":"Nur lesend. Bei FORCE_LOGGING=NO hinterlassen NOLOGGING-Operationen korrupte Blöcke auf dem Standby; auf einer Data-Guard-Primary muss ALTER DATABASE FORCE LOGGING aktiviert sein. Bei FLASHBACK_ON=NO kann die alte Primary nach einem Failover nicht per Reinstate zurückgeholt werden und muss neu aufgebaut werden.","note":"Läuft auch auf der Primary; v$archive_gap ist nur auf einem Physical Standby aussagekräftig.","tags":["data guard","v$database","switchover_status","force logging","gap","role","standby"]},{"id":"dg-lag-apply-rate","task":"Transport-/Apply-Lag, Apply-Rate und Data-Guard-Prozesse auf dem Standby messen","category":"Data Guard","command":"-- Run on the standby:\nSELECT name, value, unit, time_computed, datum_time\nFROM   v$dataguard_stats\nWHERE  name IN ('transport lag','apply lag','apply finish time','estimated startup time');\n\n-- Redo apply rate (KB/s):\nSELECT item, units, sofar, total, TO_CHAR(timestamp,'YYYY-MM-DD HH24:MI:SS') AS ts\nFROM   v$recovery_progress\nWHERE  item IN ('Active Apply Rate','Average Apply Rate','Maximum Apply Rate','Last Applied Redo')\nORDER  BY start_time DESC, item;\n\n-- Transport and apply processes (12.2+; replaces v$managed_standby):\nSELECT name, role, action, thread#, sequence#, block#, delay_mins\nFROM   v$dataguard_process\nORDER  BY role;","what":"Zeigt, wie weit der Standby hinter der Primary liegt (Transport-Lag = noch nicht empfangenes Redo, Apply-Lag = noch nicht angewendetes Redo), wann der Rückstand angewendet sein wird, die Apply-Rate und welcher Prozess an welcher Sequence arbeitet.","when":"Für RPO-/RTO-Monitoring und bei einem Lag-Alarm, um zu erkennen, ob das Problem im Netzwerk (Transport) oder bei I/O/CPU des Standby (Apply) liegt.","caution":"Nur lesend. Ist datum_time alt (weit hinter time_computed), empfängt der Standby keine Daten von der Primary und der Lag-Wert ist unzuverlässig. Prüfe, ob DelayMins gesetzt ist; eine absichtliche Verzögerung sieht wie Apply-Lag aus.","since":"12c","note":"v$dataguard_process gibt es ab 12.2. Ohne Standby liefert v$dataguard_stats keine Zeilen.","tags":["data guard","lag","lag","apply","transport","v$dataguard_stats","rpo"]},{"id":"dg-validate-database","task":"Standby vor Switchover/Failover mit dem Broker validieren (VALIDATE DATABASE)","category":"Data Guard","command":"VALIDATE DATABASE '<standby_db_unique_name>';\nVALIDATE DATABASE VERBOSE '<standby_db_unique_name>';\n# spfile parameter differences between the two sides:\nVALIDATE DATABASE '<standby_db_unique_name>' SPFILE;\n# Network and static listener registrations (18c+):\nVALIDATE NETWORK CONFIGURATION FOR ALL;\nVALIDATE STATIC CONNECT IDENTIFIER FOR ALL;","what":"Prüft, ob der Standby für einen Rollenwechsel bereit ist (\"Ready for Switchover / Failover: Yes\"), Anzahl/Größe der Standby-Redo-Logs, Flashback- und Apply-Zustand, spfile-Unterschiede und die statischen Services, die der Broker zum Neustart der Instanzen braucht.","when":"Als Pflichtschritt vor jedem geplanten Switchover; nach dem Aufbau eines neuen Standby und nach Netzwerk-/Listener-Änderungen.","caution":"Nur lesend. Siehst du \"Ready for Switchover: No\" oder eine Warnung zu Standby-Redo-Logs, führe den Switchover nicht durch. Ein Fehler beim Static Connect Identifier bedeutet, dass der Broker die Instanz während eines Switchovers nicht starten kann, was einen manuellen Eingriff erzwingt.","since":"12c","note":"VALIDATE DATABASE gibt es ab 12.1, die Option SPFILE ab 12.2, NETWORK CONFIGURATION und STATIC CONNECT IDENTIFIER ab 18c.","tags":["data guard","dgmgrl","validate","validate","switchover","standby redo log","broker"],"untested":true},{"id":"dg-switchover","task":"Geplanten Switchover durchführen (DGMGRL, Rollenwechsel ohne Datenverlust)","category":"Data Guard","command":"# Connect with a password (with OS authentication \"/\" the broker cannot restart the remote instance):\ndgmgrl sys@<primary_db_unique_name>\n# 1) Precheck:\nSHOW CONFIGURATION;\nVALIDATE DATABASE '<standby_db_unique_name>';\n# 2) Role change:\nSWITCHOVER TO '<standby_db_unique_name>';\n# 3) Verify: new primary and new standby\nSHOW CONFIGURATION;\nSHOW DATABASE '<primary_db_unique_name>';","what":"Tauscht kontrolliert die Rollen von Primary und Standby: Das gesamte Redo der Primary wird übertragen und auf dem Standby angewendet, die alte Primary startet als Standby neu und Redo Apply beginnt automatisch.","when":"Für geplante Wartung am Primary-Standort (OS, Hardware, Rolling Upgrade), DR-Übungen oder Rechenzentrumsumzüge.","caution":"Die Primary hat einen kurzen Ausfall; Anwendungsverbindungen müssen über mit -role PRIMARY definierte Services und FAN/TAF/AC zur neuen Primary geleitet werden. Lang laufende Transaktionen und hoher Apply-Lag verlängern den Switchover; bring den Lag vorher nahe null. In RAC verwaltet der Broker die Instanzen selbst; greife nicht mit srvctl ein.","since":"12c","note":"Ab 12.1 führt der Broker alle Schritte mit einem Befehl aus; ohne Broker nutzte man in 11g ALTER DATABASE COMMIT TO SWITCHOVER in SQL (ab 12c: ALTER DATABASE SWITCHOVER TO <db> VERIFY).","tags":["data guard","switchover","dgmgrl","role change","planned","standby","primary"],"untested":true},{"id":"dg-failover","task":"Failover nach Verlust der Primary durchführen (DGMGRL)","category":"Data Guard","command":"# Connect to the standby (the primary is unreachable):\ndgmgrl sys@<standby_db_unique_name>\n# 1) Assess the data that will be lost (Transport Lag / Apply Lag):\nSHOW DATABASE '<standby_db_unique_name>';\n# 2) Complete failover: all received redo is applied, then the role changes (recommended)\nFAILOVER TO '<standby_db_unique_name>';\n# 3) Emergency: immediately, without applying redo (more data loss)\n# FAILOVER TO '<standby_db_unique_name>' IMMEDIATE;\nSHOW CONFIGURATION;","what":"Hebt den Standby in die Primary-Rolle, wenn die Primary nicht verfügbar ist; ein vollständiger Failover wendet zuerst alles empfangene Redo an, IMMEDIATE überspringt das Apply.","when":"Wenn Primary-Standort, Storage oder Datenbank unwiederbringlich verloren sind und das RTO kein Warten auf einen Switchover erlaubt.","caution":"Dieser Rollenwechsel ist unumkehrbar. Stelle zuerst sicher, dass die Primary WIRKLICH ausgefallen ist; bleibt die alte Primary offen und schreibt die Anwendung weiter hinein, entsteht Split-Brain mit auseinanderlaufenden Daten (alte Primary herunterfahren/isolieren). Im Modus MaxPerformance verlierst du so viele Daten wie der Transport-Lag. Prüfe danach alle Standbys und die Backup-Strategie für die neue Primary.","note":"Zum Zurückholen der alten Primary siehe das Rezept dg-reinstate-database (erfordert Flashback).","tags":["data guard","failover","dgmgrl","disaster","disaster recovery","data loss","split-brain"],"untested":true},{"id":"dg-reinstate-database","task":"Alte Primary nach einem Failover als Standby zurückholen (REINSTATE)","category":"Data Guard","command":"# 1) Bring the old primary to MOUNT (sqlplus / as sysdba):\n#    SQL> STARTUP MOUNT;\n#    (RAC: srvctl start database -db <old_primary_db_unique_name> -startoption mount)\n# 2) From DGMGRL connected to the new primary:\ndgmgrl sys@<new_primary_db_unique_name>\nREINSTATE DATABASE '<old_primary_db_unique_name>';\n# 3) Verify:\nSHOW CONFIGURATION;\nSHOW DATABASE '<old_primary_db_unique_name>';","what":"Setzt die alte Primary per Flashback Database auf den Stand vor der Failover-SCN zurück, wandelt sie in einen Physical Standby um und beginnt, Redo von der neuen Primary zu empfangen; der Standby muss nicht neu aufgebaut werden.","when":"Nach einem Failover, sobald Server/Storage der alten Primary wieder erreichbar sind; nach einem automatischen FSFO-Failover (der Observer führt das Reinstate meist selbst aus).","caution":"Flashback muss auf der alten Primary VOR dem Failover aktiviert gewesen sein und die Flashback Logs müssen die Failover-SCN abdecken (db_flashback_retention_target, FRA-Größe); sonst schlägt das Reinstate fehl und der Standby muss mit RMAN duplicate neu aufgebaut werden. Nach dem Failover in die alte Primary geschriebene Daten gehen endgültig verloren.","note":"Vorabprüfung: die Spalte flashback_on im Rezept dg-readiness-sql.","tags":["data guard","reinstate","flashback","failover","old primary","standby","dgmgrl"],"untested":true},{"id":"dg-apply-transport-state","task":"Redo Apply oder Redo-Transport mit dem Broker stoppen/starten; verzögertes Apply konfigurieren","category":"Data Guard","command":"# Redo apply on the standby:\nEDIT DATABASE '<standby_db_unique_name>' SET STATE='APPLY-OFF';\nEDIT DATABASE '<standby_db_unique_name>' SET STATE='APPLY-ON';\n# Redo transport on the primary (to all standbys):\nEDIT DATABASE '<primary_db_unique_name>' SET STATE='TRANSPORT-OFF';\nEDIT DATABASE '<primary_db_unique_name>' SET STATE='TRANSPORT-ON';\n# Delayed apply (minutes; protection against logical errors):\nEDIT DATABASE '<standby_db_unique_name>' SET PROPERTY DelayMins=240;\nSHOW DATABASE '<standby_db_unique_name>' DelayMins;","what":"Stoppt/startet über den Broker dauerhaft Redo Apply (MRP) auf dem Standby oder die Redo-Übertragung auf der Primary; DelayMins hält Redo die angegebene Zeit zurück, bevor es angewendet wird.","when":"Für Wartung oder Backups auf dem Standby, geplante Netzwerkausfälle, zur Steuerung des Transports bei großen Massenladungen auf der Primary oder für einen verzögerten Standby gegen versehentliches Löschen.","caution":"Verwende bei aktivem Broker nicht ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL in SQL; der Broker stellt den Zustand wieder her. Während APPLY-OFF wachsen Apply-Lag und Failover-Zeit (RTO); während TRANSPORT-OFF sind auf der Primary erzeugte Daten ungeschützt (RPO). Ist FSFO aktiviert, können diese Operationen einen Failover verhindern.","note":"Mit DelayMins > 0 wird Redo nach der Verzögerung aus Archivlogs angewendet statt per Real-Time Apply; der Standby empfängt während der Verzögerung weiter Redo.","tags":["data guard","apply","transport","mrp","delaymins","stop","dgmgrl"],"untested":true},{"id":"dg-fsfo-observer","task":"Fast-Start Failover (FSFO) aktivieren und den Observer starten","category":"Data Guard","command":"# 1) Targets and threshold (seconds):\nEDIT DATABASE '<primary_db_unique_name>' SET PROPERTY FastStartFailoverTarget='<standby_db_unique_name>';\nEDIT DATABASE '<standby_db_unique_name>' SET PROPERTY FastStartFailoverTarget='<primary_db_unique_name>';\nEDIT CONFIGURATION SET PROPERTY FastStartFailoverThreshold=30;\n# 2) MaxAvailability for zero data loss (SYNC/FASTSYNC transport):\nEDIT DATABASE '<primary_db_unique_name>' SET PROPERTY LogXptMode='FASTSYNC';\nEDIT DATABASE '<standby_db_unique_name>' SET PROPERTY LogXptMode='FASTSYNC';\nEDIT CONFIGURATION SET PROTECTION MODE AS MAXAVAILABILITY;\n# Acceptable loss in MaxPerformance: EDIT CONFIGURATION SET PROPERTY FastStartFailoverLagLimit=30;\nENABLE FAST_START FAILOVER;\n# 3) Observer: on a third server, separate from primary and standby (passwordless connection via wallet)\ndgmgrl /@<primary_tns> \"START OBSERVER obs1 IN BACKGROUND FILE IS '/u01/app/oracle/dg/fsfo_obs1.dat' LOGFILE IS '/u01/app/oracle/dg/obs1.log' CONNECT IDENTIFIER IS <primary_tns>\"\n# 4) Verify:\nSHOW FAST_START FAILOVER;\nSHOW OBSERVER;","what":"Lässt den Observer bei unerreichbarer Primary nach Ablauf des Schwellenwerts automatisch auf den Standby umschalten und die alte Primary automatisch per Reinstate zurückholen, sobald sie wieder da ist.","when":"Auf kritischen Systemen mit einem RTO-Ziel von Minuten, ohne auf menschlichen Eingriff zu warten.","caution":"Der Observer darf nicht auf demselben Server/im selben Rechenzentrum wie Primary oder Standby laufen; sonst versagt FSFO bei einem Standortverlust oder trifft bei einer Netzwerkpartition die falsche Entscheidung. Flashback muss auf beiden Datenbanken aktiviert sein. Ein zu niedriger Schwellenwert macht aus kurzen Netzwerkstörungen unnötige Failovers. Bei geplanter Wartung vor dem Herunterfahren der Primary DISABLE FAST_START FAILOVER ausführen oder den Observer berücksichtigen.","since":"12c","note":"FASTSYNC gibt es ab 12.2, IN BACKGROUND und mehrere Observer (SHOW OBSERVER) ab 12.2. IN BACKGROUND erfordert Zugangsdaten in einem Oracle Wallet (mkstore).","tags":["data guard","fsfo","fast-start failover","observer","automatic failover","maxavailability","dgmgrl"],"untested":true},{"id":"dg-snapshot-standby","task":"Physical Standby in einen Snapshot Standby umwandeln und zurück (Testumgebung)","category":"Data Guard","command":"# Make the standby read-write (redo is still received, apply stops):\nCONVERT DATABASE '<standby_db_unique_name>' TO SNAPSHOT STANDBY;\nSHOW DATABASE '<standby_db_unique_name>';\n# ... testing / application validation ...\n# Convert back: all test changes are discarded, accumulated redo is applied\nCONVERT DATABASE '<standby_db_unique_name>' TO PHYSICAL STANDBY;\nSHOW CONFIGURATION;","what":"Legt auf dem Standby automatisch einen Guaranteed Restore Point an und öffnet ihn read-write; beim Zurückwandeln setzt er per Flashback auf diesen Punkt zurück und wendet das von der Primary angesammelte Redo an.","when":"Um ein Upgrade zu proben, ein Anwendungsrelease zu testen oder Real Application Testing (DB Replay) auf Produktionsdaten auszuführen, ohne eine separate Kopie anzulegen.","caution":"Solange er ein Snapshot Standby ist, wird kein Redo angewendet: Das RPO bleibt erhalten, aber die Failover-Zeit (RTO) wächst mit dem angesammelten Redo, und er kann kein FSFO-Ziel sein. Flashback Logs können die FRA füllen; überwache den FRA-Platz. Im Snapshot vorgenommene Änderungen werden beim Zurückwandeln endgültig verworfen.","note":"SQL-Pendant: ALTER DATABASE CONVERT TO SNAPSHOT STANDBY / CONVERT TO PHYSICAL STANDBY (ohne Broker).","tags":["data guard","snapshot standby","test","restore point","flashback","convert","dgmgrl"],"untested":true},{"id":"rman-catalog-create","task":"RMAN-Recovery-Katalog anlegen und Datenbank registrieren","category":"RMAN","command":"-- 1) In the catalog database (separate DB/PDB), with sqlplus:\nCREATE TABLESPACE rcat_ts DATAFILE '/u02/oradata/RCAT/rcat01.dbf' SIZE 1G AUTOEXTEND ON NEXT 256M;\nCREATE USER rcat IDENTIFIED BY \"<password>\"\n  DEFAULT TABLESPACE rcat_ts QUOTA UNLIMITED ON rcat_ts;\nGRANT RECOVERY_CATALOG_OWNER TO rcat;\n# 2) From the shell: create the catalog schema\nrman catalog rcat@<rcat_tns>\nCREATE CATALOG;\n# 3) Register the target database and verify:\nrman target / catalog rcat@<rcat_tns>\nREGISTER DATABASE;\nREPORT SCHEMA;\n# Resync controlfile records into the catalog (done automatically after backups):\nRESYNC CATALOG;","what":"Baut einen Recovery-Katalog auf, der Backup-Metadaten in einem zentralen Schema speichert, unabhängig von der Aufbewahrung in der Controlfile (control_file_record_keep_time), und registriert die Zieldatenbank.","when":"Um die Backup-Historie vieler Datenbanken an einem Ort zu halten, Backups von Primary und Standby in Data Guard wechselseitig zu nutzen sowie für Langzeit-Backups (KEEP) und gespeicherte Skripte.","caution":"Die Katalogdatenbank muss auf einem anderen Server liegen als die Datenbanken, die sie schützt, und muss selbst gesichert werden. Die Version des Katalogschemas darf nicht niedriger sein als der neueste verwendete RMAN-Client; ein neuerer Client erfordert UPGRADE CATALOG. Registriere in Data Guard nur die Primary; Standbys werden per DB_UNIQUE_NAME mit CONFIGURE DB_UNIQUE_NAME ... CONNECT IDENTIFIER definiert.","note":"Halte das Katalogschema in einer separaten Datenbank; liegt es beim Ziel, nimmt ein Desaster den Katalog mit.","tags":["rman","catalog","catalog","recovery catalog","register","backup","backup"],"untested":true},{"id":"rman-duplicate-standby","task":"Physical Standby mit RMAN DUPLICATE aus der aktiven Datenbank erstellen","category":"RMAN","command":"# Prerequisites: standby instance in NOMOUNT with a pfile containing only db_name,\n# primary password file copied, static listener entries and tnsnames on both sides.\nrman target sys@<primary_tns> auxiliary sys@<standby_tns>\nRUN {\n  ALLOCATE CHANNEL p1 DEVICE TYPE DISK;\n  ALLOCATE CHANNEL p2 DEVICE TYPE DISK;\n  ALLOCATE AUXILIARY CHANNEL s1 DEVICE TYPE DISK;\n  ALLOCATE AUXILIARY CHANNEL s2 DEVICE TYPE DISK;\n  DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE\n    USING COMPRESSED BACKUPSET SECTION SIZE 32G\n    DORECOVER\n    SPFILE\n      SET db_unique_name='<standby_db_unique_name>'\n      SET control_files='+DATA','+FRA'\n      SET db_create_file_dest='+DATA'\n      SET db_recovery_file_dest='+FRA'\n      SET fal_server='<primary_tns>'\n      SET log_archive_dest_2=''\n      SET dg_broker_start='FALSE'\n    NOFILENAMECHECK;\n}","what":"Kopiert die Datafiles der Primary über das Netzwerk auf den Standby-Server (ohne Zwischen-Backupdateien), erstellt das für den Standby angepasste spfile und wendet mit DORECOVER die vorhandenen Archivlogs an.","when":"Beim Aufbau eines neuen Data-Guard-Standby oder beim Neuaufbau eines Standby, der sich nicht per Reinstate zurückholen lässt.","caution":"Erzeugt Netzwerk- und I/O-Last auf der Primary; nicht zu Spitzenzeiten ausführen oder die Anzahl der Kanäle begrenzen. NOFILENAMECHECK ist nur sicher, wenn der Standby auf einem SEPARATEN Server liegt; auf demselben Server kann es die Dateien der Primary überschreiben. Füge davor oder danach Standby-Redo-Logs hinzu (gleiche Größe wie das Online Redo der Primary, eine zusätzliche Gruppe pro Thread) und konfiguriere dann den Broker.","since":"12c","note":"Active Duplicate mit USING [COMPRESSED] BACKUPSET und SECTION SIZE gibt es ab 12c; 11g nutzt Image Copies. Verwendest du statt ASM ein Dateisystem, setze db_file_name_convert/log_file_name_convert.","tags":["rman","duplicate","standby","active database","data guard","setup","clone"],"untested":true},{"id":"rman-crosscheck-cleanup","task":"RMAN-Backup-Einträge validieren und bereinigen (CROSSCHECK, EXPIRED, OBSOLETE)","category":"RMAN","command":"SHOW RETENTION POLICY;\n# 1) Compare records with the physical files (missing ones become EXPIRED):\nCROSSCHECK BACKUP;\nCROSSCHECK COPY;\nCROSSCHECK ARCHIVELOG ALL;\n# 2) Review before deleting:\nLIST EXPIRED BACKUP;\nLIST EXPIRED ARCHIVELOG ALL;\nREPORT OBSOLETE;\n# 3) Clean up:\nDELETE NOPROMPT EXPIRED BACKUP;\nDELETE NOPROMPT EXPIRED COPY;\nDELETE NOPROMPT EXPIRED ARCHIVELOG ALL;\nDELETE NOPROMPT OBSOLETE;","what":"CROSSCHECK prüft, ob die Backups aus den RMAN-Einträgen noch auf Disk/Band existieren; DELETE EXPIRED entfernt die Einträge fehlender Dateien, und DELETE OBSOLETE löscht laut Aufbewahrungsrichtlinie nicht mehr benötigte Backups sowohl aus den Einträgen als auch von der Disk.","when":"Wenn Backupdateien auf OS-Ebene gelöscht/verschoben wurden, wenn die FRA vollläuft, und als Teil des regelmäßigen Backup-Wartungsjobs.","caution":"DELETE OBSOLETE löscht Dateien physisch; prüfe die Liste zuerst mit REPORT OBSOLETE und stelle sicher, dass die Aufbewahrungsrichtlinie (RECOVERY WINDOW / REDUNDANCY) deinen Erwartungen entspricht. Führst du CROSSCHECK aus, während Band/Media Manager vorübergehend nicht erreichbar ist, erscheinen gültige Backups als EXPIRED; führe in diesem Moment kein DELETE EXPIRED aus.","note":"Nutze in Data Guard CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY für das Löschen von Archivlogs.","tags":["rman","crosscheck","expired","obsolete","cleanup","retention","backup"],"untested":true},{"id":"rman-validate","task":"Datenbank und Backups auf Korruption prüfen (VALIDATE)","category":"RMAN","command":"# Physical + logical block check (without producing a backup):\nVALIDATE CHECK LOGICAL DATABASE;\n# Scan large datafiles in parallel (11g+, needs multiple channels):\nVALIDATE CHECK LOGICAL DATABASE SECTION SIZE 16G;\n# A single datafile / tablespace:\nVALIDATE DATAFILE 7;\nVALIDATE TABLESPACE users;\n# Read as if taking a backup and check (including archivelogs):\nBACKUP VALIDATE CHECK LOGICAL DATABASE ARCHIVELOG ALL;\n# Readability of a specific backup set:\nLIST BACKUP SUMMARY;\nVALIDATE BACKUPSET <bs_key>;\n-- Corrupt blocks found and the affected segment (SQL):\nSELECT file#, block#, blocks, corruption_change#, corruption_type\nFROM   v$database_block_corruption ORDER BY file#, block#;\nSELECT DISTINCT e.owner, e.segment_name, e.segment_type\nFROM   dba_extents e\nJOIN   v$database_block_corruption c\n  ON   e.file_id = c.file#\n AND   c.block# BETWEEN e.block_id AND e.block_id + e.blocks - 1;","what":"Liest Datafiles und Backup Sets, um physische und (mit CHECK LOGICAL) logische Blockkorruption zu erkennen, und hält Befunde in v$database_block_corruption fest. Die SQL-Abfrage zeigt, zu welcher Tabelle/welchem Index der korrupte Block gehört.","when":"Nach Korruptionsfehlern wie ORA-01578/ORA-08103, nach Storage-Problemen und als regelmäßige (wöchentliche) Integritätsprüfung; um nachzuweisen, dass Backups wirklich wiederherstellbar sind.","caution":"Führt einen vollständigen Lesevorgang aus; auf großen Datenbanken erzeugt das hohe I/O-Last, daher außerhalb der Spitzenzeiten ausführen. Die Abfrage auf dba_extents kann auf großen Datenbanken langsam sein. Liegt der korrupte Block in temporärem (TEMP) oder freiem Platz, wird kein Objekt zugeordnet.","since":"11g","note":"Die Abfragen liefern keine Zeilen, wenn keine Korruption vorliegt. VALIDATE mit SECTION SIZE gibt es ab 11g.","tags":["rman","validate","corruption","corruption","check logical","block","ora-01578"],"untested":true},{"id":"rman-restore-preview","task":"Ermitteln, welche Backups ein Restore verwenden würde, und sie validieren (PREVIEW / VALIDATE)","category":"RMAN","command":"# Backups and archivelogs a restore needs (without reading the files):\nRESTORE DATABASE PREVIEW SUMMARY;\n# For a return to a point in time:\nRESTORE DATABASE UNTIL TIME \"TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS')\" PREVIEW;\n# Validate the backups by actually reading them (without restoring):\nRESTORE DATABASE VALIDATE;\nRESTORE ARCHIVELOG FROM TIME 'SYSDATE-1' VALIDATE;\nRESTORE CONTROLFILE VALIDATE;\nRESTORE SPFILE VALIDATE;","what":"PREVIEW meldet aus Katalog/Controlfile, welche Backup Sets/Kopien und welchen Archivlog-Bereich ein Restore verwenden würde; VALIDATE liest diese Backups tatsächlich und prüft, dass keine Korruption und keine fehlende Datei vorliegt.","when":"Zur Planung vor einem echten Restore, um nachzuweisen, dass die Backup-Strategie das RPO-Ziel erfüllt, und als regelmäßiger Restore-Test.","caution":"PREVIEW betrachtet nur die Einträge; wurden Dateien gelöscht, ist es irreführend, daher zuerst CROSSCHECK ausführen oder RESTORE ... VALIDATE nutzen. VALIDATE liest das gesamte Backup; über Band/Netzwerk kann das lange dauern. Lass das Datum von der TO_DATE-Maske abhängen, nicht von den NLS-Einstellungen der Session.","note":"PREVIEW RECALL kann auch Backups anfordern, die von Band zurückgeholt werden müssen (erfordert Unterstützung durch den Media Manager).","tags":["rman","restore","preview","validate","restore test","backup","rpo"],"untested":true},{"id":"rman-block-media-recovery","task":"Korrupte Blöcke online reparieren (Block Media Recovery, RECOVER CORRUPTION LIST)","category":"RMAN","command":"# 1) Detect corrupt blocks (populates v$database_block_corruption):\nVALIDATE CHECK LOGICAL DATABASE;\n# 2) Repair all blocks in the list (the datafile stays online):\nRECOVER CORRUPTION LIST;\n# or the file/block from the error message (ORA-01578: file# 7, block# 233):\nRECOVER DATAFILE 7 BLOCK 233;\nRECOVER DATAFILE 7 BLOCK 233 DATAFILE 9 BLOCK 1001;\n# 3) Verify:\nVALIDATE DATAFILE 7;\nSELECT * FROM v$database_block_corruption;","what":"Stellt nur die korrupten Blöcke aus einem Backup (oder aus Flashback Logs / einem Active-Data-Guard-Standby) wieder her und rollt sie mit Redo vorwärts; Datafile und Tablespace bleiben online, und die Anwendung erhält nur beim Zugriff auf diese Blöcke Fehler.","when":"Wenn isolierte Blockkorruption mit ORA-01578 / ORA-01110 auftritt; wenn das Wiederherstellen der gesamten Datafile unnötige Ausfallzeit verursachen würde.","caution":"Erfordert ein sauberes Backup mit dem Block und alle Archivlogs von damals bis jetzt; durch eine NOLOGGING-Operation verursachte Korruption (ORA-26040) lässt sich so nicht reparieren, das Objekt muss neu aufgebaut werden. Dies ist eine Funktion der Enterprise Edition. Findest du die Ursache der Korruption nicht (Storage, HBA, Firmware), tritt sie erneut auf.","note":"Mit Active Data Guard repariert ab 11.2 die automatische Block Media Recovery korrupte Blöcke automatisch vom Standby aus.","tags":["rman","block recovery","corrupt block","corruption","ora-01578","recover","repair"],"untested":true},{"id":"rman-tspitr","task":"Tablespace auf einen früheren Zeitpunkt zurücksetzen (TSPITR)","category":"RMAN","command":"-- 1) Is the tablespace self-contained (SQL, on the target DB):\nEXEC DBMS_TTS.TRANSPORT_SET_CHECK(ts_list => 'USERS', incl_constraints => TRUE, full_check => TRUE);\nSELECT * FROM transport_set_violations;\n-- 2) Objects created AFTER the target time that TSPITR will lose:\nSELECT owner, name, tablespace_name,\n       TO_CHAR(creation_time,'YYYY-MM-DD HH24:MI:SS') AS created\nFROM   ts_pitr_objects_to_be_dropped\nWHERE  tablespace_name = 'USERS'\n  AND  creation_time > TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS');\n# 3) RMAN: with an automatic auxiliary instance\nRECOVER TABLESPACE users\n  UNTIL TIME \"TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS')\"\n  AUXILIARY DESTINATION '/u03/aux';\n# 4) The tablespace is left OFFLINE: take a backup first, then bring it online\nBACKUP TABLESPACE users;\nALTER TABLESPACE users ONLINE;","what":"RMAN stellt SYSTEM/UNDO/SYSAUX und den Ziel-Tablespace in einer temporären Hilfsinstanz bis zum gewünschten Zeitpunkt wieder her und hängt ihn dann per Transportable Tablespace wieder in die Zieldatenbank ein; der Rest der Datenbank bleibt aktuell.","when":"Wenn ein fehlerhafter Batchjob oder ein falsches DROP/TRUNCATE nur Daten in einem Tablespace (oder einer in sich konsistenten Gruppe von Tablespaces) beschädigt hat und Flashback-Optionen nicht ausreichen.","caution":"ALLE in diesem Tablespace vorgenommenen Änderungen und alle nach dem Zielzeitpunkt angelegten Objekte gehen verloren. Die Tablespace-Gruppe muss in sich abgeschlossen sein (kein Index/keine Tabelle, Partition, LOB oder FK in einem anderen Tablespace). Für Tablespaces mit SYSTEM-, UNDO- oder SYS-Objekten ist es nicht nutzbar. Nach TSPITR sind frühere Backups dieses Tablespace unbrauchbar; erstelle sofort ein Backup. AUXILIARY DESTINATION braucht freien Platz für SYSTEM+SYSAUX+UNDO+den Ziel-Tablespace.","note":"Für eine einzelne Tabelle ist rman-recover-table weniger destruktiv.","tags":["rman","tspitr","tablespace","pitr","point-in-time","auxiliary","rollback"],"untested":true},{"id":"rman-recover-table","task":"Einzelne Tabelle aus einem RMAN-Backup auf einen früheren Zeitpunkt wiederherstellen (RECOVER TABLE)","category":"RMAN","command":"# Restore under a new name (the original table is not touched):\nRECOVER TABLE hr.employees\n  UNTIL TIME \"TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS')\"\n  AUXILIARY DESTINATION '/u03/aux'\n  REMAP TABLE hr.employees:employees_rec;\n# Multitenant: a table inside a PDB\nRECOVER TABLE hr.employees OF PLUGGABLE DATABASE <pdb>\n  UNTIL SCN 12345678\n  AUXILIARY DESTINATION '/u03/aux'\n  REMAP TABLE hr.employees:employees_rec;\n# Only produce a Data Pump dump, do not import:\nRECOVER TABLE hr.employees\n  UNTIL SCN 12345678\n  AUXILIARY DESTINATION '/u03/aux'\n  DATAPUMP DESTINATION '/u03/dump'\n  DUMP FILE 'employees_rec.dmp'\n  NOTABLEIMPORT;","what":"RMAN stellt eine automatische Hilfsinstanz bis zum gewünschten Zeitpunkt wieder her, exportiert die Tabelle (oder Partition) mit Data Pump und importiert sie mit REMAP unter anderem Namen in die Zieldatenbank; anschließend wird die Hilfsinstanz entfernt.","when":"Um die Daten einer einzelnen Tabelle wiederherzustellen, die versehentlich gelöscht/geändert oder mit PURGE gedroppt wurde, über das hinaus, was die Undo Retention für Flashback Table/Query erlaubt.","caution":"AUXILIARY DESTINATION braucht freien Platz für SYSTEM, SYSAUX, UNDO und den Tablespace der Tabelle; der Vorgang kann Stunden dauern. Ohne REMAP TABLE schlägt der Vorgang fehl, wenn eine Tabelle gleichen Namens existiert; verwende immer REMAP oder NOTABLEIMPORT, um das Original nicht zu überschreiben. Tabellen in den Schemas SYS/SYSTEM und in den Tablespaces SYSTEM/SYSAUX lassen sich so nicht wiederherstellen. Erfordert die Enterprise Edition.","since":"12c","note":"Erforderlich: ein Vollbackup plus Archivlogs, die den Zielzeitpunkt abdecken. Für eine Partition: RECOVER TABLE hr.sales:p2025 ... REMAP TABLE hr.sales:p2025:sales_p2025_rec.","tags":["rman","recover table","table recovery","pitr","auxiliary","remap","accidental delete"],"untested":true},{"id":"pdb-create-from-seed","task":"Neue PDB aus PDB$SEED anlegen","category":"Multitenant","command":"-- In CDB$ROOT, as SYSDBA:\nCREATE PLUGGABLE DATABASE <pdb>\n  ADMIN USER pdbadmin IDENTIFIED BY \"<password>\" ROLES = (DBA)\n  DEFAULT TABLESPACE users\n    DATAFILE '/u02/oradata/<cdb>/<pdb>/users01.dbf' SIZE 512M AUTOEXTEND ON NEXT 64M\n  FILE_NAME_CONVERT = ('/u02/oradata/<cdb>/pdbseed/', '/u02/oradata/<cdb>/<pdb>/')\n  STORAGE (MAXSIZE 50G);\n-- With OMF/ASM, instead of FILE_NAME_CONVERT: CREATE_FILE_DEST = '+DATA'\nALTER PLUGGABLE DATABASE <pdb> OPEN;\nALTER PLUGGABLE DATABASE <pdb> SAVE STATE;\nSELECT con_id, name, open_mode, restricted FROM v$pdbs ORDER BY con_id;","what":"Legt aus der Seed-Vorlage eine neue, leere Pluggable Database an; definiert im selben Befehl den lokalen Admin-Benutzer, den Standard-Tablespace und das Gesamtgrößenlimit.","when":"Beim Bereitstellen einer isolierten Datenbank für eine neue Anwendung/ein neues Schema; beim Vorbereiten von Test- und Entwicklungsumgebungen.","caution":"Die neue PDB startet im Zustand MOUNTED; sie braucht OPEN und SAVE STATE (um nach einem CDB-Neustart offen zu bleiben). Falsche FILE_NAME_CONVERT-Pfade schreiben die Dateien ins falsche Verzeichnis; ohne OMF ist für DATAFILE ein vollständiger Dateipfad Pflicht (ORA-02236). Lizenzierung: In 19c EE sind je CDB 3 Benutzer-PDBs ohne die Multitenant-Option nutzbar.","since":"12c","note":"Füge in RAC der PDB mit srvctl add service -pdb einen Anwendungsservice hinzu.","tags":["multitenant","pdb","create","create pluggable database","seed","cdb","pdb$seed"]},{"id":"pdb-clone-local","task":"PDB innerhalb derselben CDB klonen (Local / Hot Clone)","category":"Multitenant","command":"-- Hot clone prerequisites: local undo + ARCHIVELOG\nSELECT property_value AS local_undo\nFROM   database_properties WHERE property_name = 'LOCAL_UNDO_ENABLED';\nSELECT log_mode FROM v$database;\n\nCREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb>\n  FILE_NAME_CONVERT = ('/<pdb>/', '/<new_pdb>/');\n-- OMF/ASM: CREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb> CREATE_FILE_DEST = '+DATA';\nALTER PLUGGABLE DATABASE <new_pdb> OPEN;\nSELECT name, open_mode FROM v$pdbs WHERE name = UPPER('<new_pdb>');","what":"Legt in derselben CDB eine neue PDB an, indem die Datafiles der Quell-PDB kopiert werden; mit Local Undo und ARCHIVELOG ist das möglich, während die Quelle read-write geöffnet ist (Hot Clone).","when":"Um eine Test-/Entwicklungskopie einer Produktions-PDB zu erstellen, ein Upgrade oder einen Patch zu proben oder vor einer riskanten Änderung schnell eine Kopie zu ziehen.","caution":"Die Kopie erzeugt zusätzliche I/O-Last auf der Quelle und braucht am Ziel so viel Platz wie die Quelle. Im Shared-Undo-Modus oder bei NOARCHIVELOG muss die Quelle READ ONLY sein (19c-Dokumentation). Der Klon enthält Produktionsdaten: Prüfe vor dem Öffnen Maskierung, DB-Links, Jobs (DBMS_SCHEDULER) und Verbindungen zu externen Systemen.","since":"12c","note":"Ein Hot Clone mit read-write geöffneter Quelle erfordert in 19c Local Undo + ARCHIVELOG. Für eine schnelle Thin Copy nutze SNAPSHOT COPY (erfordert geeigneten Storage).","tags":["multitenant","pdb","clone","clone","hot clone","copy","local undo"]},{"id":"pdb-clone-remote","task":"PDB aus einer anderen CDB über einen DB-Link klonen (Remote Clone)","category":"Multitenant","command":"-- 1) In the SOURCE CDB, a common user:\nCREATE USER c##clone_user IDENTIFIED BY \"<password>\" CONTAINER = ALL;\nGRANT CREATE SESSION, CREATE PLUGGABLE DATABASE TO c##clone_user CONTAINER = ALL;\n\n-- 2) In the TARGET CDB root, DB link and test:\nCREATE DATABASE LINK clone_link\n  CONNECT TO c##clone_user IDENTIFIED BY \"<password>\"\n  USING '//<source_host>:1521/<source_cdb_service>';\nSELECT * FROM dual@clone_link;\n\n-- 3) Clone and open:\nCREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb>@clone_link\n  FILE_NAME_CONVERT = ('/<pdb>/', '/<new_pdb>/');\nALTER PLUGGABLE DATABASE <new_pdb> OPEN;\n\n-- 4) Violations (version, options, parameters, patches):\nSELECT name, cause, type, message\nFROM   pdb_plug_in_violations\nWHERE  name = UPPER('<new_pdb>') AND status <> 'RESOLVED';","what":"Kopiert eine PDB über das Netzwerk per DB-Link von der Quell-CDB in die Ziel-CDB; keine Zwischendateien und kein Unplug nötig.","when":"Um eine PDB auf einen anderen Server/eine andere CDB zu verschieben, eine Testkopie der Produktion auf einem separaten Server zu erstellen oder auf eine CDB mit neuerem Release umzuziehen.","caution":"Quell- und Ziel-CDB müssen bei Endianness, Zeichensatz (oder Ziel AL32UTF8) und Optionen kompatibel sein; eine auf ein neueres Release geklonte PDB öffnet zunächst RESTRICTED und braucht datapatch/Upgrade. Entferne DB-Link und Common User, wenn der Klon fertig ist. Hot Clone erfordert Local Undo + ARCHIVELOG auf der Quelle.","since":"12c","note":"Zum Verschieben (die Quelle wird entfernt) nutze die Option RELOCATE ab 12.2: CREATE PLUGGABLE DATABASE <pdb> FROM <pdb>@clone_link RELOCATE.","tags":["multitenant","pdb","remote clone","db link","clone","migration","relocate"]},{"id":"pdb-unplug-plug","task":"PDB ausstecken (Unplug) und in eine andere (oder dieselbe) CDB einstecken (Plug)","category":"Multitenant","command":"-- SOURCE CDB: close and unplug to an XML manifest\nALTER PLUGGABLE DATABASE <pdb> CLOSE IMMEDIATE;\nALTER PLUGGABLE DATABASE <pdb> UNPLUG INTO '/u01/stage/<pdb>.xml';\n-- (Single-file archive: UNPLUG INTO '/u01/stage/<pdb>.pdb' - includes datafiles, 12.2+)\nDROP PLUGGABLE DATABASE <pdb> KEEP DATAFILES;\n\n-- TARGET CDB: check compatibility first\nSET SERVEROUTPUT ON\nDECLARE\n  l_ok BOOLEAN;\nBEGIN\n  l_ok := DBMS_PDB.CHECK_PLUG_COMPATIBILITY(\n            pdb_descr_file => '/u01/stage/<pdb>.xml',\n            pdb_name       => '<pdb>');\n  DBMS_OUTPUT.PUT_LINE(CASE WHEN l_ok THEN 'COMPATIBLE' ELSE 'NOT COMPATIBLE - see PDB_PLUG_IN_VIOLATIONS' END);\nEND;\n/\nSELECT name, cause, type, status, message\nFROM   pdb_plug_in_violations\nWHERE  name = UPPER('<pdb>') AND status <> 'RESOLVED';\n\n-- NOCOPY if the files stay in place, COPY FILE_NAME_CONVERT if they move:\nCREATE PLUGGABLE DATABASE <pdb> USING '/u01/stage/<pdb>.xml' NOCOPY TEMPFILE REUSE;\n-- From a .pdb archive: CREATE PLUGGABLE DATABASE <pdb> USING '/u01/stage/<pdb>.pdb'\n--                    FILE_NAME_CONVERT = ('/u01/stage/', '/u02/oradata/<cdb>/<pdb>/');\nALTER PLUGGABLE DATABASE <pdb> OPEN;","what":"Löst die PDB von der CDB und schreibt ihre Metadaten in ein XML-Manifest (oder zusammen mit den Datafiles in ein einzelnes .pdb-Archiv); nach einer Kompatibilitätsprüfung steckt sie sie mit denselben Dateien wieder in die Ziel-CDB ein.","when":"Um eine PDB auf demselben Server in eine CDB mit neuerem Release zu verschieben (Unplug/Plug-Upgrade), zur CDB-Konsolidierung oder für Umzüge, bei denen Klonen über das Netzwerk nicht möglich ist.","caution":"Nach dem Unplug ist die PDB in der Quell-CDB nicht verfügbar (Ausfallzeit). Mit NOCOPY eingesteckte Dateien gehören nun der Ziel-CDB; erstelle vorher eine Kopie auf Dateiebene oder ein RMAN-Backup, um bei einem Fehlschlag zurückzukönnen. Bis Verstöße vom Typ ERROR behoben sind, öffnet die PDB RESTRICTED. Beim erneuten Einstecken in dieselbe CDB kann das Weglassen von AS CLONE einen GUID-Konflikt verursachen.","since":"12c","note":"Ein .pdb-Archiv (ab 12.2) enthält die Datafiles und wird daher als einzelne Datei verschoben; bei der XML-Methode werden die Datafiles separat kopiert.","tags":["multitenant","pdb","unplug","plug","migration","xml manifest","check_plug_compatibility"]},{"id":"pdb-open-save-state","task":"PDBs öffnen und über CDB-Neustarts hinweg offen halten (SAVE STATE)","category":"Multitenant","command":"-- Open all PDBs (RAC: INSTANCES = ALL):\nALTER PLUGGABLE DATABASE ALL OPEN;\n-- ALTER PLUGGABLE DATABASE <pdb> OPEN INSTANCES = ALL;\n-- Save the current open state (opens automatically after a CDB restart):\nALTER PLUGGABLE DATABASE <pdb> SAVE STATE;\n-- Saved states:\nSELECT con_name, instance_name, state, restricted FROM dba_pdb_saved_states;\n-- Remove the saved state:\nALTER PLUGGABLE DATABASE <pdb> DISCARD STATE;\n-- If it opens RESTRICTED, find the reason:\nSELECT name, cause, type, message\nFROM   pdb_plug_in_violations\nWHERE  status <> 'RESOLVED';","what":"Öffnet PDBs READ WRITE und speichert ihren aktuellen Open-Zustand, sodass die PDB beim Neustart der CDB automatisch im selben Modus öffnet.","when":"Nach dem Anlegen, Einstecken oder Klonen einer PDB; wenn du bemerkst, dass PDBs nach einem CDB-Neustart MOUNTED bleiben.","caution":"SAVE STATE hält den Zustand zum Zeitpunkt des Befehls fest: Führst du es bei geschlossener PDB aus, wird der Zustand \"geschlossen\" gespeichert (oder der Eintrag entfernt). In RAC gilt der Zustand je Instanz; bei Service-basierter Verwaltung (PDB-Services über srvctl) ist der Start über Clusterware SAVE STATE vorzuziehen. Auf einem Data-Guard-Standby hängt der Open Mode von der Rolle ab.","since":"12c","note":"12.1.0.2+.","tags":["multitenant","pdb","open","save state","auto open","restricted","dba_pdb_saved_states"]},{"id":"pdb-resource-limits","task":"CPU-, Speicher-, I/O- und Storage-Limits je PDB festlegen","category":"Multitenant","command":"ALTER SESSION SET CONTAINER = <pdb>;\n-- CPU (instance caging with Resource Manager; at PDB level in 19c):\nALTER SYSTEM SET cpu_count = 2 SCOPE = BOTH;\n-- Memory (sga_min_size guarantee, sga_target upper limit):\nALTER SYSTEM SET sga_target = 512M SCOPE = BOTH;\nALTER SYSTEM SET sga_min_size = 96M SCOPE = BOTH;\nALTER SYSTEM SET pga_aggregate_limit = 2G SCOPE = BOTH;\n-- I/O (non-Exadata):\nALTER SYSTEM SET max_iops = 5000 SCOPE = BOTH;\nALTER SYSTEM SET max_mbps = 200 SCOPE = BOTH;\n-- Number of sessions (after the PDB is reopened):\nALTER SYSTEM SET sessions = 200 SCOPE = SPFILE;\n-- Storage:\nALTER PLUGGABLE DATABASE STORAGE (MAXSIZE 10G MAX_SHARED_TEMP_SIZE 2G);\n\n-- Settings across all PDBs, from the root:\nALTER SESSION SET CONTAINER = CDB$ROOT;\nSELECT p.name AS pdb, sp.name, sp.value\nFROM   v$system_parameter sp JOIN v$pdbs p ON p.con_id = sp.con_id\nWHERE  sp.name IN ('cpu_count','sga_target','sga_min_size','max_iops','max_mbps','pga_aggregate_limit')\nORDER  BY p.name, sp.name;","what":"Begrenzt über Parameter auf PDB-Ebene, wie viel CPU, SGA/PGA, IOPS/MBPS und Gesamtspeicher eine PDB von der CDB nutzen darf; verhindert, dass \"Noisy Neighbor\"-PDBs die anderen beeinträchtigen.","when":"In konsolidierten CDBs, wenn Anwendungen mit unterschiedlichen SLAs in derselben CDB laufen oder wenn eine PDB den gesamten Server auslastet.","caution":"Die sga_target der PDB muss kleiner sein als die sga_target der CDB; zu niedrige Werte verursachen ORA-04031 und Performanceprobleme. Die Summe von sga_min_size darf nicht die gesamte CDB-SGA reservieren. Damit cpu_count in einer PDB wirkt, muss in der CDB ein Resource-Manager-Plan aktiv sein. Wird MAXSIZE erreicht, erhält die PDB ORA-65114.","since":"12c","note":"23ai verlangt, dass sga_min_size unter 20 % von sga_target liegt (ORA-56746), in 19c liegt das Limit bei 50 %. MAX_IOPS/MAX_MBPS gibt es ab 12.2, PDB-Speicherparameter ab 12.2.","tags":["multitenant","pdb","resource","resource","cpu_count","sga_target","maxsize"]},{"id":"pdb-container-context","task":"Aktuellen Container bestätigen und alle PDBs vom Root aus abfragen (CDB_ / CONTAINERS)","category":"Multitenant","command":"-- Current container:\nSHOW CON_NAME\nSHOW CON_ID\nSHOW PDBS\nSELECT SYS_CONTEXT('USERENV','CON_NAME') AS con_name,\n       SYS_CONTEXT('USERENV','CON_ID')   AS con_id\nFROM   dual;\n\n-- All containers, from the root:\nSELECT con_id, name, open_mode FROM v$containers ORDER BY con_id;\n\n-- CDB_ views combine all open PDBs with CON_ID:\nSELECT c.name AS container, t.tablespace_name, t.status\nFROM   cdb_tablespaces t JOIN v$containers c ON c.con_id = t.con_id\nORDER  BY c.name, t.tablespace_name;\n\n-- Query any table/view across all PDBs with CONTAINERS():\nSELECT con_id, COUNT(*) AS users\nFROM   CONTAINERS(dba_users)\nGROUP  BY con_id ORDER BY con_id;","what":"Zeigt den Container, mit dem die Session verbunden ist; vom Root aus sammeln CDB_-Views und die CONTAINERS()-Klausel Dictionary-Informationen aller geöffneten PDBs in einer einzigen Abfrage.","when":"Um vor einer DDL-/Parameteränderung sicherzugehen, dass du im richtigen Container bist, und um ein Platz-, Benutzer- und Objektinventar über alle PDBs zu erstellen.","caution":"Nur lesend. Lege nicht versehentlich Anwendungsobjekte oder lokale Benutzer im Root (CDB$ROOT) an; ALTER SYSTEM im Root wirkt auf die gesamte CDB. CDB_-Views und CONTAINERS() liefern nur geöffnete PDBs; PDBs im Zustand MOUNTED erscheinen nicht im Ergebnis.","since":"12c","note":"Container wechseln: ALTER SESSION SET CONTAINER = <pdb>; (siehe Rosetta \"Aktive Datenbank wechseln\").","tags":["multitenant","container","con_name","cdb_","containers","pdb","root"]},{"id":"pdb-refreshable-clone","task":"Refreshable PDB anlegen, aktualisieren und in eine unabhängige PDB umwandeln","category":"Multitenant","command":"-- In the target CDB root (DB link to the source ready: see pdb-clone-remote):\nCREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb>@clone_link\n  FILE_NAME_CONVERT = ('/<pdb>/', '/<new_pdb>/')\n  REFRESH MODE EVERY 60 MINUTES;\nSELECT pdb_name, status, refresh_mode, refresh_interval, last_refresh_scn\nFROM   dba_pdbs WHERE pdb_name = UPPER('<new_pdb>');\n\n-- Can only be opened READ ONLY; close it first to refresh manually:\nALTER PLUGGABLE DATABASE <new_pdb> OPEN READ ONLY;\nALTER PLUGGABLE DATABASE <new_pdb> CLOSE IMMEDIATE;\nALTER PLUGGABLE DATABASE <new_pdb> REFRESH;\n\n-- Change the mode (MANUAL) or convert to an independent, writable PDB (irreversible):\nALTER PLUGGABLE DATABASE <new_pdb> REFRESH MODE MANUAL;\nALTER PLUGGABLE DATABASE <new_pdb> REFRESH MODE NONE;\nALTER PLUGGABLE DATABASE <new_pdb> OPEN;","what":"Erstellt eine Kopie, die durch periodisches oder bedarfsgesteuertes Anwenden des Redo der Quell-PDB aktuell gehalten wird; sie öffnet READ ONLY für Reporting und wird mit REFRESH MODE NONE zu einer unabhängigen read-write-PDB.","when":"Für eine regelmäßig aktualisierte Reporting-/Testkopie der Produktion, eine PDB-Migration mit geringer Ausfallzeit (finaler Refresh + REFRESH MODE NONE) oder einen \"günstigen Standby auf PDB-Ebene\".","caution":"Die PDB muss während eines Refresh geschlossen sein; solange sie READ ONLY geöffnet ist, wartet der automatische Refresh. Die für den Refresh nötigen Archivlogs müssen auf der Quelle erreichbar sein. REFRESH MODE NONE ist unumkehrbar; die Kopie lässt sich danach nicht mehr aktualisieren. Erfordert Local Undo + ARCHIVELOG auf der Quelle. Ersetzt Data Guard nicht für echtes DR.","since":"12c","note":"Ab 12.2. Nach REFRESH MODE NONE wird der Klon zu einer regulären PDB und kann READ WRITE geöffnet werden; das lässt sich nicht rückgängig machen.","tags":["multitenant","pdb","refreshable","refresh","refresh","reporting","clone"]}]},"recipes/oracle-ops.json":{"engineId":"oracle","engine":"Oracle","order":2,"recipes":[{"id":"awr-snapshot-report","task":"AWR-Snapshot erstellen und AWR-Report zwischen zwei Snapshots erzeugen","category":"Performance","command":"-- 1) Manual snapshot before and after the event (run in CDB root):\nEXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;\n\n-- 2) List snapshots:\nSELECT snap_id,\n       TO_CHAR(begin_interval_time,'YYYY-MM-DD HH24:MI') AS begin_time,\n       TO_CHAR(end_interval_time,'YYYY-MM-DD HH24:MI')   AS end_time\nFROM   dba_hist_snapshot\nWHERE  dbid = (SELECT dbid FROM v$database)\nORDER  BY snap_id DESC\nFETCH FIRST 24 ROWS ONLY;\n\n-- 3a) Interactive report (prompts for HTML/TEXT and snapshot range):\n-- @?/rdbms/admin/awrrpt.sql      (RAC: awrgrpt.sql, specific instance: awrrpti.sql)\n\n-- 3b) TEXT report from a script:\nSET LINESIZE 8000 PAGESIZE 0 TRIMSPOOL ON HEADING OFF FEEDBACK OFF\nSPOOL /tmp/awr_<begin_snap>_<end_snap>.txt\nSELECT output\nFROM   TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_TEXT(\n         l_dbid     => (SELECT dbid FROM v$database),\n         l_inst_num => (SELECT instance_number FROM v$instance),\n         l_bid      => <begin_snap>,\n         l_eid      => <end_snap>));\nSPOOL OFF\n-- For HTML: DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_HTML (same parameters)","what":"Erzeugt einen Report zwischen zwei AWR-Snapshots, die das Problemfenster abdecken: Load Profile, Top Wait Events, Top-SQL und Instanzstatistiken. Mit der Table Function holst du den Report aus einem Skript oder einem Scheduler-Job.","when":"Bei der Analyse einer Verlangsamung in einem bestimmten Zeitfenster, für Vorher-/Nachher-Vergleiche rund um einen Lasttest oder Batch und für Kapazitätsreports.","caution":"Erfordert die Diagnostics-Pack-Lizenz (CONTROL_MANAGEMENT_PACK_ACCESS='DIAGNOSTIC' oder 'DIAGNOSTIC+TUNING'); ohne sie setze den Parameter auf 'NONE' und nutze Statspack. Ein Report kann keinen Instanzneustart überspannen. Bereiche über einer Stunde mitteln sich aus und verbergen kurze Spitzen; halte das Fenster eng.","note":"Für AWR auf PDB-Ebene (ab 12.2) prüfe AWR_PDB_AUTOFLUSH_ENABLED=TRUE und AWR_SNAPSHOT_TIME_OFFSET/AWR_PDB_MAX_PARALLEL_SLAVES; ein innerhalb einer PDB erstellter Report zeigt nur die Daten dieser PDB. Intervall/Aufbewahrung: DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(interval => 30, retention => 43200).","tags":["awr","awr report","snapshot","awrrpt","dbms_workload_repository","performance report","diagnostics pack"]},{"id":"awr-diff-report","task":"AWR-Diff-Report (Compare Period) zwischen zwei Zeiträumen","category":"Performance","command":"-- Interactive: @?/rdbms/admin/awrddrpt.sql   (RAC: awrgdrpt.sql)\n\n-- From a script: period 1 (good) = <bid1>-<eid1>, period 2 (bad) = <bid2>-<eid2>\nSET LINESIZE 8000 PAGESIZE 0 TRIMSPOOL ON HEADING OFF FEEDBACK OFF\nSPOOL /tmp/awr_diff.txt\nSELECT output\nFROM   TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_DIFF_REPORT_TEXT(\n         dbid1 => (SELECT dbid FROM v$database), inst_num1 => 1,\n         bid1  => <bid1>, eid1 => <eid1>,\n         dbid2 => (SELECT dbid FROM v$database), inst_num2 => 1,\n         bid2  => <bid2>, eid2 => <eid2>));\nSPOOL OFF\n-- HTML: DBMS_WORKLOAD_REPOSITORY.AWR_DIFF_REPORT_HTML","what":"Stellt zwei Snapshot-Bereiche nebeneinander und zeigt prozentuale Unterschiede bei DB Time, Wait Events, Load Profile und Statistiken je SQL. Beantwortet direkt \"gestern lief es, heute ist es langsam\".","when":"Regressionsanalyse nach Upgrade/Patch, Parameteränderung, Deployment oder Statistikerfassung; Vergleich desselben Zeitfensters an zwei Tagen.","caution":"Erfordert die Diagnostics-Pack-Lizenz. Verglichene Zeiträume sollten gleich lang sein und ähnliche Last haben, sonst sind die Deltas irreführend. Um verschiedene Datenbanken zu vergleichen (z. B. Prod vs. Test), übertrage AWR-Daten mit awrextr.sql/awrload.sql und übergib die passende dbid2.","tags":["awr diff","comparison","compare period","awrddrpt","regression","performance","diagnostics pack"]},{"id":"ash-top-activity","task":"Top Wait Events und Top-SQL der letzten Minuten aus ASH","category":"Performance","command":"-- Last 30 min: wait event breakdown (each sample ~1 s of active session time)\nSELECT NVL(event, 'ON CPU') AS event, wait_class,\n       COUNT(*) AS samples,\n       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100, 1) AS pct\nFROM   v$active_session_history\nWHERE  sample_time > SYSTIMESTAMP - INTERVAL '30' MINUTE\nGROUP  BY NVL(event, 'ON CPU'), wait_class\nORDER  BY samples DESC\nFETCH FIRST 10 ROWS ONLY;\n\n-- Last 30 min: top SQL (with CPU / User I/O breakdown)\nSELECT sql_id, sql_opname,\n       COUNT(*) AS samples,\n       SUM(CASE WHEN session_state = 'ON CPU' THEN 1 ELSE 0 END) AS cpu,\n       SUM(CASE WHEN wait_class = 'User I/O' THEN 1 ELSE 0 END)  AS user_io,\n       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100, 1) AS pct\nFROM   v$active_session_history\nWHERE  sample_time > SYSTIMESTAMP - INTERVAL '30' MINUTE\n  AND  sql_id IS NOT NULL\nGROUP  BY sql_id, sql_opname\nORDER  BY samples DESC\nFETCH FIRST 10 ROWS ONLY;\n\n-- Older periods: DBA_HIST_ACTIVE_SESS_HISTORY (one sample every 10 s; COUNT(*)*10 = seconds)\n\n-- Ready-made ASH report (last 30 min):\nSET LINESIZE 8000 PAGESIZE 0 TRIMSPOOL ON HEADING OFF FEEDBACK OFF\nSELECT output\nFROM   TABLE(DBMS_WORKLOAD_REPOSITORY.ASH_REPORT_TEXT(\n         l_dbid     => (SELECT dbid FROM v$database),\n         l_inst_num => (SELECT instance_number FROM v$instance),\n         l_btime    => SYSDATE - 30/1440,\n         l_etime    => SYSDATE));\n-- Interactive: @?/rdbms/admin/ashrpt.sql","what":"Zeigt anhand von Active-Session-History-Samples prozentual, welche Wait Events und welche SQL-Anweisungen in den letzten Minuten DB Time verbraucht haben; diagnostiziert den Vorfall, ohne auf einen AWR-Snapshot zu warten.","when":"Während einer akuten Verlangsamung, für kurze Spitzen, die in AWR-Mittelwerten verschwinden, und um zu sehen, worauf eine bestimmte Session oder SQL gewartet hat.","caution":"Abfragen auf V$ACTIVE_SESSION_HISTORY und DBA_HIST_ACTIVE_SESS_HISTORY erfordern die Diagnostics-Pack-Lizenz. ASH arbeitet mit Stichproben: Sample-Zahlen schätzen die Zeit, sie sind keine exakten Messungen, und sehr kurze SQL erscheinen eventuell nicht. Der ASH-Puffer im Speicher reicht auf ausgelasteten Systemen nur für wenige Stunden.","tags":["ash","active session history","top wait","top sql","wait","ashrpt","real-time analysis","diagnostics pack"]},{"id":"addm-task-report","task":"ADDM-Analyse ausführen und Report abrufen","category":"Performance","command":"-- Analyze the range between two snapshots (in CDB root):\nVAR tname VARCHAR2(60)\nBEGIN\n  :tname := 'ADDM_<begin_snap>_<end_snap>';\n  DBMS_ADDM.ANALYZE_DB(\n    task_name      => :tname,\n    begin_snapshot => <begin_snap>,\n    end_snapshot   => <end_snap>);\nEND;\n/\n\nSET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 200 PAGESIZE 0\nSELECT DBMS_ADDM.GET_REPORT(task_name => :tname) FROM dual;\n\n-- Findings summary (by impact):\nSET PAGESIZE 100\nSELECT finding_name, type, ROUND(impact/1e6) AS impact_sec, message\nFROM   dba_advisor_findings\nWHERE  task_name = :tname\nORDER  BY impact DESC;\n\n-- Interactive alternative: @?/rdbms/admin/addmrpt.sql\n-- Clean up the task when done:\nEXEC DBMS_ADDM.DELETE(task_name => :tname);","what":"Automatic Database Diagnostic Monitor analysiert die DB Time zwischen zwei AWR-Snapshots und meldet Engpässe mit ihrer Auswirkung (Findings) und konkreten Empfehlungen.","when":"Für eine schnelle Zusammenfassung \"wo soll ich hinschauen\" vor dem Lesen des AWR-Reports oder um einen eigenen Bereich zu analysieren, über den automatischen ADDM-Lauf nach jedem Snapshot hinaus.","caution":"Erfordert die Diagnostics-Pack-Lizenz. Wende ADDM-Empfehlungen (Parametererhöhungen, Ausführen des SQL Tuning Advisor usw.) nicht blind an; prüfe Speicherempfehlungen gegen die Advisory-Abschnitte im AWR. Der Bereich kann keinen Neustart überspannen.","tags":["addm","dbms_addm","advisor","finding","finding","recommendation","diagnostics pack","performance"]},{"id":"sql-monitor-report","task":"SQL-Monitor-Report abrufen (laufende oder abgeschlossene lange SQL)","category":"Performance","command":"-- List monitored SQL (longer than 5 s, parallel, or /*+ MONITOR */):\nSELECT sql_id, status, username, sql_exec_id,\n       TO_CHAR(sql_exec_start,'YYYY-MM-DD HH24:MI:SS') AS exec_start,\n       ROUND(elapsed_time/1e6,1) AS elapsed_s,\n       ROUND(cpu_time/1e6,1)     AS cpu_s,\n       buffer_gets, px_servers_allocated\nFROM   v$sql_monitor\nORDER  BY sql_exec_start DESC\nFETCH FIRST 20 ROWS ONLY;\n\n-- TEXT report (actual rows, time and activity per plan line):\nSET LONG 10000000 LONGCHUNKSIZE 10000000 LINESIZE 300 PAGESIZE 0 TRIMSPOOL ON\nSELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(\n         sql_id       => '<sql_id>',\n         type         => 'TEXT',\n         report_level => 'ALL') AS report\nFROM   dual;\n\n-- Shareable HTML (interactive in a browser):\nSPOOL /tmp/sqlmon_<sql_id>.html\nSELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(\n         sql_id => '<sql_id>', type => 'ACTIVE', report_level => 'ALL')\nFROM   dual;\nSPOOL OFF","what":"Real-Time SQL Monitoring zeigt für jeden Schritt des Ausführungsplans einer lang laufenden oder parallelen SQL die tatsächlichen Zeilen, die Laufzeit, Speicher-/Temp-Nutzung und ASH-Aktivität; du kannst den Fortschritt verfolgen, während die SQL noch läuft.","when":"Bei der Frage, ob eine Batch-/Report-Abfrage hängt und wie viel noch fehlt, für die Analyse der Parallel-Query-Verteilung (PX Skew) und um Abweichungen zwischen geschätzten (E-Rows) und tatsächlichen Zeilen zu finden.","caution":"Erfordert die Tuning-Pack-Lizenz (Diagnostics Pack ist Voraussetzung). Standardmäßig wird nur SQL überwacht, die 5 s CPU/I-O verbraucht oder parallel läuft; für andere nutze den Hint /*+ MONITOR */. Sehr große Pläne (_sqlmon_max_planlines, Standard 300) werden nicht überwacht. Einträge bleiben nur begrenzte Zeit im Speicher; ab 12c sind ältere Reports auch über DBA_HIST_REPORTS verfügbar.","tags":["sql monitor","report_sql_monitor","v$sql_monitor","real-time","long running query","parallel","tuning pack"]},{"id":"sql-trace-10046-tkprof","task":"10046-Trace für eine Session aktivieren/deaktivieren und mit TKPROF zusammenfassen","category":"Performance","command":"-- 1) Find the target session and its trace file:\nSELECT s.sid, s.serial#, s.username, s.module, p.tracefile\nFROM   v$session s\n  JOIN v$process p ON p.addr = s.paddr\nWHERE  s.username = 'CUSTOMER_APP';\n\n-- 2) Enable trace (waits + binds = equivalent of level 12):\nBEGIN\n  DBMS_MONITOR.SESSION_TRACE_ENABLE(\n    session_id => <sid>,\n    serial_num => <serial#>,\n    waits      => TRUE,\n    binds      => TRUE,\n    plan_stat  => 'ALL_EXECUTIONS');\nEND;\n/\n-- Verify: SELECT sql_trace, sql_trace_waits, sql_trace_binds FROM v$session WHERE sid = <sid>;\n\n-- 3) Disable once the problem operation completes:\nEXEC DBMS_MONITOR.SESSION_TRACE_DISABLE(session_id => <sid>, serial_num => <serial#>);\n\n-- Alternatives:\n-- Your own session: ALTER SESSION SET EVENTS '10046 trace name context forever, level 12';\n-- Single SQL_ID, all sessions: ALTER SYSTEM SET EVENTS 'sql_trace[SQL: <sql_id>] wait=true, bind=true';\n--   disable: ALTER SYSTEM SET EVENTS 'sql_trace[SQL: <sql_id>] off';\n-- For a connection pool: DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE / SERV_MOD_ACT_TRACE_ENABLE\n\n-- 4) Summarize with TKPROF on the OS (most expensive SQL first):\n-- $ tkprof /u01/app/oracle/diag/rdbms/orcl/ORCL/trace/ORCL_ora_12345.trc /tmp/ora_12345.prf \\\n--     sys=no sort=exeela,fchela,prsela waits=yes aggregate=yes","what":"Extended SQL Trace (10046) schreibt für jede von der Session ausgeführte SQL Parse-/Execute-/Fetch-Zeiten, Wait Events und Bind-Werte in eine Trace-Datei; TKPROF macht daraus einen lesbaren, je SQL aggregierten Report.","when":"Wenn AWR/ASH nicht ausreichen und du Ende-zu-Ende belegen musst, wo eine einzelne Benutzeroperation ihre Zeit verbringt; bei Roundtrip-Problemen zwischen Anwendung und Datenbank (SQL*Net).","caution":"Keine Lizenz erforderlich. Trace-Dateien wachsen schnell und belasten die Ziel-Session zusätzlich; halte es kurz und deaktiviere es immer (prüfe DBA_ENABLED_TRACES auf Überbleibsel). binds=TRUE schreibt sensible Daten (Ausweisnummern, Kartennummern usw.) in die Trace-Datei. ALTER SESSION SET EVENTS erfordert das Privileg ALTER SESSION (sonst ORA-01031). TKPROF zeigt Bind-Werte standardmäßig nicht an; bewahre die rohe .trc-Datei auf.","note":"Tracing auf Ebene von Connection Pool oder Service erzeugt mehrere Dateien; führe sie mit trcsess zusammen (trcsess output=combined.trc clientid=<id> *.trc) und lass dann TKPROF laufen.","tags":["10046","trace","tkprof","dbms_monitor","sql trace","wait","bind","session tracing"]},{"id":"xplan-allstats-last","task":"Tatsächlichen Ausführungsplan mit Row-Source-Statistiken anzeigen (ALLSTATS LAST)","category":"Performance","command":"-- 1) Collect row source statistics (for this query only):\nSET SERVEROUTPUT OFF\nSELECT /*+ gather_plan_statistics */ customer_id, SUM(amount)\nFROM   customer_app.sales\nWHERE  created_at >= DATE '2026-06-01'\nGROUP  BY customer_id;\n-- or for the whole session: ALTER SESSION SET statistics_level = ALL;\n\n-- 2) Plan of the last cursor run in the same session (E-Rows vs A-Rows):\nSELECT *\nFROM   TABLE(DBMS_XPLAN.DISPLAY_CURSOR(\n         sql_id          => NULL,\n         cursor_child_no => NULL,\n         format          => 'ALLSTATS LAST +PEEKED_BINDS +OUTLINE'));\n\n-- 3) A cursor from another session / from earlier:\nSELECT *\nFROM   TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<sql_id>', NULL, 'ALLSTATS LAST +PEEKED_BINDS'));","what":"EXPLAIN PLAN ist eine Schätzung; dies zeigt den tatsächlich ausgeführten Plan mit Starts, E-Rows (Schätzung), A-Rows (tatsächlich), A-Time, Buffers und Speichernutzung je Schritt. So findest du am direktesten den Schritt, an dem die Fehlschätzung der Kardinalität beginnt.","when":"Beim Tuning einer einzelnen langsamen SQL, um den Bedarf an Statistiken/Histogrammen nachzuweisen und um die Wirkung eines Hints oder einer Indexänderung zu messen.","caution":"Keine Lizenz erforderlich. Mit SET SERVEROUTPUT ON zeigt sql_id=>NULL den DBMS_OUTPUT-Aufruf als letzten Cursor; schalte es vorher OFF. Aktiviere statistics_level=ALL nicht systemweit; das verursacht spürbaren CPU-Overhead. Der Hint ändert den SQL-Text, daher erhält sie eine neue sql_id. Der Benutzer braucht Lesezugriff auf V$SQL_PLAN_STATISTICS_ALL (SELECT_CATALOG_ROLE).","tags":["dbms_xplan","display_cursor","allstats last","gather_plan_statistics","actual plan","execution plan","a-rows","cardinality"]},{"id":"sql-tuning-advisor-task","task":"SQL-Tuning-Advisor-Task anlegen, ausführen und Empfehlungen lesen","category":"Performance","command":"-- 1) Create and run a task for a SQL in the cursor cache:\nVAR task VARCHAR2(64)\nBEGIN\n  :task := DBMS_SQLTUNE.CREATE_TUNING_TASK(\n             sql_id      => '<sql_id>',\n             scope       => DBMS_SQLTUNE.SCOPE_COMPREHENSIVE,\n             time_limit  => 300,\n             task_name   => 'STA_<sql_id>',\n             description => 'Slow report query');\n  DBMS_SQLTUNE.EXECUTE_TUNING_TASK(task_name => :task);\nEND;\n/\n-- For a historical SQL in AWR: begin_snap => <b>, end_snap => <e>, sql_id => '<sql_id>'\n\n-- 2) Status and report:\nSELECT task_name, status FROM dba_advisor_log WHERE task_name = :task;\n\nSET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 200 PAGESIZE 0\nSELECT DBMS_SQLTUNE.REPORT_TUNING_TASK(:task) FROM dual;\n\n-- 3) Get the recommendations as an executable script:\nSELECT DBMS_SQLTUNE.SCRIPT_TUNING_TASK(:task) FROM dual;\n\n-- 4) When done:\nEXEC DBMS_SQLTUNE.DROP_TUNING_TASK(task_name => :task);","what":"Führt den Optimizer für die SQL in einem erweiterten \"Tuning-Modus\" aus und erzeugt Empfehlungen zu fehlenden Statistiken, neuen Indizes, einem SQL-Profil, alternativen Plänen und Umstrukturierung der SQL; validiert die Profilempfehlung mit Testausführungen.","when":"Wenn eine einzelne problematische SQL schnell eine zweite Meinung braucht oder wenn für eine Planregression ein SQL-Profil als Lösung ohne Codeänderung gesucht wird.","caution":"Erfordert die Tuning-Pack-Lizenz. Der Umfang COMPREHENSIVE kann die SQL zum Testen ausführen (bei DML nur den Abfrageteil); setze in der Produktion time_limit. Indexempfehlungen ignorieren den Rest der Workload; bewerte die DML-Kosten separat. Der Benutzer braucht das Privileg ADVISOR.","tags":["sql tuning advisor","dbms_sqltune","tuning task","recommendation","sql profile","index recommendation","tuning pack"]},{"id":"sql-profile-accept","task":"SQL-Profil akzeptieren, deaktivieren und löschen","category":"Performance","command":"-- 1) Accept the profile recommended by the tuning task:\nDECLARE\n  l_name VARCHAR2(128);\nBEGIN\n  l_name := DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(\n              task_name   => '<task_name>',\n              name        => 'PROF_<sql_id>',\n              description => 'STA recommendation, CHG-1234',\n              force_match => TRUE,     -- also matches statements differing only in literals\n              replace     => TRUE);\n  DBMS_OUTPUT.PUT_LINE('Profile: ' || l_name);\nEND;\n/\n\n-- 2) Profiles and the cursors using them:\nSELECT name, category, status, force_matching,\n       TO_CHAR(created,'YYYY-MM-DD HH24:MI') AS created\nFROM   dba_sql_profiles\nORDER  BY created DESC;\n\nSELECT sql_id, child_number, plan_hash_value, sql_profile\nFROM   v$sql\nWHERE  sql_profile IS NOT NULL;\n\n-- 3) Temporarily disable / drop:\nEXEC DBMS_SQLTUNE.ALTER_SQL_PROFILE(name => 'PROF_<sql_id>', attribute_name => 'STATUS', value => 'DISABLED');\nEXEC DBMS_SQLTUNE.DROP_SQL_PROFILE(name => 'PROF_<sql_id>', ignore => TRUE);","what":"Ein SQL-Profil gibt dem Optimizer zusätzliche Korrekturen für Kardinalität/Selektivität (OPT_ESTIMATE u. Ä.), damit er ohne Änderung des SQL-Texts einen besseren Plan erzeugt; mit FORCE_MATCH gilt es auch für Anweisungen, die sich nur in Literalen unterscheiden.","when":"Wenn die SQL nicht geändert werden kann (Standardanwendung), wenn der STA eine validierte Verbesserung empfiehlt, oder als schnelle Lösung für eine Planregression.","caution":"Erfordert die Tuning-Pack-Lizenz. Ein Profil \"sperrt\" den Plan nicht; es korrigiert Schätzungen, daher kann sich der Plan bei geänderter Datenverteilung trotzdem ändern. Ist Planstabilität Pflicht, nutze eine SQL Plan Baseline. Existieren beide, wählt der Optimizer unter den akzeptierten Plänen der Baseline. Lies vor dem Akzeptieren immer die Validierungsergebnisse (Abschnitt 'Validation results' im Report).","tags":["sql profile","accept_sql_profile","dbms_sqltune","force_match","plan fix","dba_sql_profiles","tuning pack"]},{"id":"spm-load-from-cursor-cache","task":"Guten Plan aus dem Cursor Cache als SQL Plan Baseline laden","category":"Performance","command":"-- 1) Compare the SQL's plans and their performance:\nSELECT sql_id, child_number, plan_hash_value, executions,\n       ROUND(elapsed_time/1e3/NULLIF(executions,0),1) AS ms_per_exec,\n       sql_plan_baseline\nFROM   v$sql\nWHERE  sql_id = '<sql_id>';\n\n-- 2) Load the chosen plan as a baseline:\nSET SERVEROUTPUT ON\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(\n         sql_id          => '<sql_id>',\n         plan_hash_value => <plan_hash_value>,\n         fixed           => 'NO',\n         enabled         => 'YES');\n  DBMS_OUTPUT.PUT_LINE(n || ' plan(s) loaded');\nEND;\n/\n\n-- 3) Verify the baseline:\nSELECT sql_handle, plan_name, origin, enabled, accepted, fixed, reproduced,\n       TO_CHAR(last_executed,'YYYY-MM-DD HH24:MI') AS last_exec\nFROM   dba_sql_plan_baselines\nWHERE  signature = (SELECT exact_matching_signature FROM v$sql\n                    WHERE sql_id = '<sql_id>' AND ROWNUM = 1);\n-- On subsequent executions v$sql.sql_plan_baseline should be populated.","what":"Macht den gewählten Plan per SQL Plan Management zu einer ACCEPTED-Baseline; der Optimizer verwendet dann nur akzeptierte Baseline-Pläne und stellt neu gefundene Pläne zurück, bis sie evolviert sind.","when":"Um bei einer Planregression zu einem bekannt guten Plan zurückzukehren und um Pläne kritischer SQL vor einem Upgrade oder einer Statistikänderung zu schützen.","caution":"Keine Zusatzlizenz in der Enterprise Edition (in SE2 auf einen Baseline-Plan je SQL beschränkt). Erfordert ADMINISTER SQL MANAGEMENT OBJECT. Eine Baseline gilt für exakt denselben SQL-Text (Signatur); bei SQL mit Literalen hilft sie nicht. Wird ein vom Plan verwendeter Index gelöscht, lässt sich der Plan nicht reproduzieren (REPRODUCED=NO) und der Optimizer fällt auf die kostenbasierte Wahl zurück. OPTIMIZER_USE_SQL_PLAN_BASELINES muss TRUE sein (Standard).","note":"Ist der gute Plan nicht mehr im Cursor Cache, lade ihn aus AWR: ab 12.2 DBMS_SPM.LOAD_PLANS_FROM_AWR(begin_snap, end_snap, basic_filter => q'[sql_id='<sql_id>']'); in älteren Releases über ein SQL Tuning Set mit LOAD_PLANS_FROM_SQLSET.","tags":["spm","sql plan baseline","dbms_spm","load_plans_from_cursor_cache","plan pinning","plan regression","plan_hash_value"]},{"id":"spm-evolve-baseline","task":"Nicht akzeptierte Pläne einer SQL Plan Baseline evolvieren","category":"Performance","command":"-- 1) Plans waiting for acceptance:\nSELECT sql_handle, plan_name, origin, enabled, accepted, fixed,\n       TO_CHAR(created,'YYYY-MM-DD HH24:MI') AS created\nFROM   dba_sql_plan_baselines\nWHERE  accepted = 'NO'\nORDER  BY created DESC;\n\n-- 2) Evolve task: test-executes the candidate plan against the accepted plan\nVAR tname VARCHAR2(128)\nVAR ename VARCHAR2(128)\nBEGIN\n  :tname := DBMS_SPM.CREATE_EVOLVE_TASK(\n              sql_handle => '<sql_handle>',\n              time_limit => 600,\n              task_name  => 'EVOLVE_<sql_handle>');\n  :ename := DBMS_SPM.EXECUTE_EVOLVE_TASK(task_name => :tname);\nEND;\n/\n\nSET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 200 PAGESIZE 0\nSELECT DBMS_SPM.REPORT_EVOLVE_TASK(\n         task_name => :tname, execution_name => :ename,\n         type => 'TEXT', level => 'TYPICAL', section => 'ALL')\nFROM   dual;\n\n-- 3) If the report has recommendations (plans proven better), accept them:\nSET SERVEROUTPUT ON\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  n := DBMS_SPM.IMPLEMENT_EVOLVE_TASK(task_name => :tname);\n  DBMS_OUTPUT.PUT_LINE(n || ' plan(s) accepted');\nEND;\n/\nEXEC DBMS_SPM.DROP_EVOLVE_TASK(task_name => :tname);","what":"Existiert eine Baseline, werden vom Optimizer neu gefundene Pläne mit ACCEPTED=NO gespeichert. Der Evolve-Task führt diese Kandidaten testweise gegen den akzeptierten Plan aus und akzeptiert nur die eindeutig besseren.","when":"Für SQL mit Baseline, wenn ein neuer Index oder neue Statistiken einen besseren Plan ergeben haben könnten; um manuell zu entscheiden, ohne auf den automatischen SYS_AUTO_SPM_EVOLVE_TASK (ab 12c) zu warten.","caution":"Erfordert die Enterprise Edition. Evolve führt die SQL tatsächlich aus (bei DML nur den Abfrageteil); setze für schwere SQL time_limit und meide Spitzenzeiten. Erfordert ADMINISTER SQL MANAGEMENT OBJECT und ADVISOR. Als SYS geparste SQL lässt sich nicht evolvieren (ORA-16963); führe es als Anwendungsbenutzer aus.","since":"Oracle 12c","note":"Die Funktion DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE aus der Zeit vor 12c ist veraltet; ab 12c gibt es die taskbasierte API (CREATE/EXECUTE/IMPLEMENT_EVOLVE_TASK). Verhalten des automatischen Tasks: DBMS_SPM.SET_EVOLVE_TASK_PARAMETER('SYS_AUTO_SPM_EVOLVE_TASK', 'ACCEPT_PLANS', 'FALSE').","tags":["spm","evolve","create_evolve_task","baseline","plan acceptance","dbms_spm","accepted"]},{"id":"spm-alter-drop-baseline","task":"Plan einer SQL Plan Baseline fixieren, deaktivieren oder löschen","category":"Performance","command":"-- Show the plan content:\nSELECT *\nFROM   TABLE(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(\n         sql_handle => '<sql_handle>',\n         plan_name  => '<plan_name>',\n         format     => 'TYPICAL'));\n\nSET SERVEROUTPUT ON\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  -- FIXED: this plan is preferred, new plans are not evolved automatically\n  n := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(\n         sql_handle => '<sql_handle>', plan_name => '<plan_name>',\n         attribute_name => 'fixed', attribute_value => 'YES');\n\n  -- Take the bad plan out of use (without dropping it):\n  n := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(\n         sql_handle => '<sql_handle>', plan_name => '<bad_plan_name>',\n         attribute_name => 'enabled', attribute_value => 'NO');\nEND;\n/\n\n-- Drop one plan or the SQL's entire baseline:\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  n := DBMS_SPM.DROP_SQL_PLAN_BASELINE(sql_handle => '<sql_handle>', plan_name => '<plan_name>');\n  -- without plan_name, all plans of the SQL are dropped:\n  -- n := DBMS_SPM.DROP_SQL_PLAN_BASELINE(sql_handle => '<sql_handle>');\n  DBMS_OUTPUT.PUT_LINE(n || ' plan(s) dropped');\nEND;\n/","what":"Verwaltet Attribute von Baseline-Plänen: Ein FIXED-Plan wird gegenüber anderen akzeptierten Plänen bevorzugt und stoppt die automatische Evolution neuer Pläne; ENABLED=NO deaktiviert einen Plan, ohne ihn zu löschen; DROP entfernt ihn dauerhaft.","when":"Um für eine kritische SQL einen Plan fest zu verankern, einen versehentlich akzeptierten schlechten Plan zu deaktivieren oder nicht mehr benötigte Baselines zu bereinigen.","caution":"Erfordert die Enterprise Edition. FIXED-Pläne können mit wachsenden Daten schlechter werden und verhindern, dass der Optimizer einen besseren Plan nutzt; überprüfe sie regelmäßig. Eine SQL, deren Pläne alle ENABLED=NO sind, wird frei optimiert. DROP ist unumkehrbar; sichere vorher mit DBMS_SPM.PACK_STGTAB_BASELINE in eine Staging-Tabelle.","tags":["spm","baseline","fixed","alter_sql_plan_baseline","drop_sql_plan_baseline","disable plan","display_sql_plan_baseline"]},{"id":"bind-sensitivity-acs-check","task":"Status von Bind Peeking und Adaptive Cursor Sharing prüfen","category":"Performance","command":"-- 1) Child cursors: bind-sensitive / bind-aware / shareable?\nSELECT sql_id, child_number, executions, plan_hash_value,\n       is_bind_sensitive, is_bind_aware, is_shareable,\n       ROUND(buffer_gets/NULLIF(executions,0)) AS gets_per_exec\nFROM   v$sql\nWHERE  sql_id = '<sql_id>'\nORDER  BY child_number;\n\n-- 2) ACS histogram and selectivity ranges:\nSELECT child_number, bucket_id, count\nFROM   v$sql_cs_histogram\nWHERE  sql_id = '<sql_id>'\nORDER  BY child_number, bucket_id;\n\nSELECT child_number, predicate, range_id, low, high\nFROM   v$sql_cs_selectivity\nWHERE  sql_id = '<sql_id>';\n\n-- 3) Peeked / captured bind values:\nSELECT child_number, name, position, datatype_string, value_string,\n       TO_CHAR(last_captured,'YYYY-MM-DD HH24:MI:SS') AS captured\nFROM   v$sql_bind_capture\nWHERE  sql_id = '<sql_id>'\nORDER  BY child_number, position;\n\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<sql_id>', NULL, 'TYPICAL +PEEKED_BINDS'));\n\n-- 4) Why was a new child created?\nSELECT child_number, bind_mismatch, bind_equiv_failure,\n       optimizer_mismatch, load_optimizer_stats\nFROM   v$sql_shared_cursor\nWHERE  sql_id = '<sql_id>';","what":"Der Optimizer wählt beim ersten Hard Parse einen Plan, indem er auf den Bind-Wert schaut (Peeking). Adaptive Cursor Sharing kann bei Spalten mit Histogrammen je Bind-Wert unterschiedliche Child Cursors/Pläne erzeugen; diese Abfragen zeigen, ob der Cursor bind-sensitive/-aware ist, welcher Wert gepeekt wurde und warum Children entstanden sind.","when":"Bei Beschwerden wie \"dieselbe SQL ist mal schnell, mal langsam\", bei schiefer Datenverteilung und wenn die Zahl der Child Cursors explodiert (hoher Version Count).","caution":"Keine Lizenz erforderlich; nur lesend. V$SQL_BIND_CAPTURE wird stichprobenartig erfasst (standardmäßig etwa alle 15 Minuten) und enthält nur WHERE-/HAVING-Binds; es ist nicht der Wert jeder Ausführung. ACS macht einen Cursor erst nach einigen schlechten Ausführungen bind-aware. Für kritische SQL ist das Laden mehrerer Pläne in eine SQL Plan Baseline deterministischer als sich auf ACS zu verlassen.","since":"Oracle 11g","note":"Adaptive Cursor Sharing wurde in 11g eingeführt. Für die Analyse eines hohen Version Count liefert die Spalte REASON (XML) von V$SQL_SHARED_CURSOR die genaue Ursache.","tags":["bind peeking","adaptive cursor sharing","acs","bind variable","v$sql_bind_capture","child cursor","version count"]},{"id":"top-sql-awr-history","task":"Top-SQL nach Laufzeit/CPU in einem vergangenen Zeitraum finden (AWR + V$SQLAREA)","category":"Performance","command":"-- AWR: top 20 by elapsed time within a snapshot range (per plan)\nSELECT *\nFROM  (SELECT st.sql_id,\n              st.plan_hash_value,\n              SUM(st.executions_delta)                       AS execs,\n              ROUND(SUM(st.elapsed_time_delta)/1e6, 1)       AS elapsed_s,\n              ROUND(SUM(st.cpu_time_delta)/1e6, 1)           AS cpu_s,\n              ROUND(SUM(st.iowait_delta)/1e6, 1)             AS io_wait_s,\n              SUM(st.buffer_gets_delta)                      AS buffer_gets,\n              ROUND(SUM(st.elapsed_time_delta)/1e3\n                    / NULLIF(SUM(st.executions_delta),0), 2) AS ms_per_exec,\n              MAX(st.parsing_schema_name)                    AS parsing_schema\n       FROM   dba_hist_sqlstat st\n       WHERE  st.dbid = (SELECT dbid FROM v$database)\n         AND  st.snap_id BETWEEN <begin_snap> AND <end_snap>\n       GROUP  BY st.sql_id, st.plan_hash_value\n       ORDER  BY elapsed_s DESC)\nWHERE ROWNUM <= 20;\n-- For CPU ranking: ORDER BY cpu_s DESC\n-- SQL text: SELECT DBMS_LOB.SUBSTR(sql_text, 4000, 1) FROM dba_hist_sqltext WHERE sql_id = '<sql_id>';\n-- Plan history of a single SQL: @?/rdbms/admin/awrsqrpt.sql\n\n-- Current (since instance startup, still in cache) by CPU:\nSELECT sql_id,\n       ROUND(cpu_time/1e6,1)     AS cpu_s,\n       ROUND(elapsed_time/1e6,1) AS elapsed_s,\n       executions, buffer_gets, disk_reads,\n       ROUND(cpu_time/1e3/NULLIF(executions,0),2) AS cpu_ms_per_exec,\n       parsing_schema_name,\n       SUBSTR(sql_text,1,80) AS sql_text\nFROM   v$sqlarea\nWHERE  parsing_schema_name NOT IN ('SYS','SYSTEM')\nORDER  BY cpu_time DESC\nFETCH FIRST 20 ROWS ONLY;","what":"Summiert die *_DELTA-Spalten in DBA_HIST_SQLSTAT, um die teuersten SQL eines Snapshot-Bereichs aufgeschlüsselt nach plan_hash_value aufzulisten, was auch Planänderungen derselben SQL offenlegt. Die Abfrage auf V$SQLAREA liefert kumulierte Werte für das, was noch im Cache ist.","when":"Für \"was hat das System letzte Nacht zwischen 02:00 und 04:00 belastet?\", um Planänderungen zu erkennen (gleiche sql_id, anderer plan_hash_value) und um eine Liste von Tuning-Kandidaten zu erstellen.","caution":"Abfragen auf DBA_HIST_* erfordern die Diagnostics-Pack-Lizenz, V$SQLAREA nicht. AWR speichert nur Top-SQL (standardmäßig Top-N), daher können kleine, aber sehr häufige Anweisungen fehlen. Werte in V$SQLAREA gehen verloren, wenn der Cursor verdrängt wird. Deltas bleiben über einen Neustart hinweg korrekt, die Cache-basierte Abfrage beginnt aber bei null.","tags":["top sql","dba_hist_sqlstat","v$sqlarea","elapsed","cpu","most expensive query","plan change","diagnostics pack"]},{"id":"stats-stale-prefs","task":"Veraltete Statistiken finden und Statistik-Präferenzen je Tabelle setzen (Prefs)","category":"Statistiken","command":"-- 1) Flush DML counters to the dictionary and list stale objects:\nEXEC DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;\n\nSELECT owner, table_name, partition_name, num_rows,\n       TO_CHAR(last_analyzed,'YYYY-MM-DD HH24:MI') AS last_analyzed,\n       stale_stats, stattype_locked\nFROM   dba_tab_statistics\nWHERE  owner = 'CUSTOMER_APP'\n  AND  (stale_stats = 'YES' OR last_analyzed IS NULL)\nORDER  BY table_name, partition_name;\n\nSELECT table_name, partition_name, inserts, updates, deletes, truncated\nFROM   dba_tab_modifications\nWHERE  table_owner = 'CUSTOMER_APP';\n\n-- 2) Per-table preferences (used by the auto stats job and manual GATHER calls):\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'STALE_PERCENT', '5');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'METHOD_OPT', 'FOR ALL COLUMNS SIZE AUTO FOR COLUMNS SIZE 254 CUSTOMER_ID');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'DEGREE', '4');\n\nSELECT DBMS_STATS.GET_PREFS('STALE_PERCENT', 'CUSTOMER_APP', 'SALES') AS stale_pct FROM dual;\nSELECT table_name, preference_name, preference_value\nFROM   dba_tab_stat_prefs WHERE owner = 'CUSTOMER_APP';\n\n-- 3) Gather only stale objects:\nEXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'CUSTOMER_APP', options => 'GATHER STALE');\n-- Revert a preference: EXEC DBMS_STATS.DELETE_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'STALE_PERCENT');","what":"Statistiken werden veraltet (stale), wenn der Änderungsanteil STALE_PERCENT (Standard 10 %) übersteigt. Präferenzen speichern je Tabelle Sampling, Histogramme, Parallelität und Stale-Schwellenwerte dauerhaft, sodass der nächtliche automatische Statistik-Task dieselben Regeln anwendet.","when":"Wenn die 10-%-Schwelle bei großen Tabellen zu spät greift, wenn bestimmte Spalten immer ein Histogramm bekommen müssen, und um die Erfassungsstrategie im Dictionary statt in Skripten festzulegen.","caution":"Keine Lizenz erforderlich. Präferenzen werden durch explizite Parameter in einem manuellen GATHER-Aufruf überschrieben. Eine Änderung von METHOD_OPT fügt Histogramme hinzu oder entfernt sie und kann Pläne ändern. DBA_TAB_MODIFICATIONS ist erst nach einem Flush aktuell (automatischer Flush etwa alle 15 Minuten).","tags":["stale stats","stale_percent","set_table_prefs","dba_tab_statistics","dba_tab_modifications","statistics preference","gather stale"]},{"id":"stats-lock-unlock","task":"Tabellen-/Schemastatistiken sperren und entsperren","category":"Statistiken","command":"-- Lock (the auto stats job and normal GATHER calls no longer touch them):\nEXEC DBMS_STATS.LOCK_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'ORDERS');\nEXEC DBMS_STATS.LOCK_SCHEMA_STATS(ownname => 'CUSTOMER_APP');\n-- A single partition only: DBMS_STATS.LOCK_PARTITION_STATS('CUSTOMER_APP', 'SALE_PART', 'P2026_H1');\n\n-- Locked objects:\nSELECT owner, table_name, partition_name, stattype_locked\nFROM   dba_tab_statistics\nWHERE  owner = 'CUSTOMER_APP' AND stattype_locked IS NOT NULL;\n\n-- Deliberate gathering while locked (instead of ORA-20005):\nEXEC DBMS_STATS.GATHER_TABLE_STATS('CUSTOMER_APP', 'ORDERS', force => TRUE);\n\n-- Unlock:\nEXEC DBMS_STATS.UNLOCK_SCHEMA_STATS(ownname => 'CUSTOMER_APP');\nEXEC DBMS_STATS.UNLOCK_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'ORDERS');","what":"Sperrt Statistiken gegen Änderungen; der automatische Task überspringt gesperrte Objekte, und ein GATHER-Aufruf auf Tabellenebene löst ORA-20005 aus. force=>TRUE erfasst, ohne die Sperre aufzuheben.","when":"Um repräsentative Statistiken auf Staging-/volatilen Tabellen zu halten, die tagsüber geleert und neu befüllt werden, die Pläne einer kritischen Tabelle vor Statistikänderungen zu schützen oder mit DBMS_STATS.SET_TABLE_STATS gesetzte Werte zu bewahren.","caution":"Keine Lizenz erforderlich. Gesperrte Statistiken entfernen sich mit der Zeit von der Realität und verursachen Kardinalitätsfehler; berichte regelmäßig über gesperrte Objekte. Eine Schemasperre gilt nicht für Tabellen, die später zum Schema hinzukommen. Auf einer gesperrten Tabelle erfassen auch CREATE/REBUILD INDEX keine Indexstatistiken automatisch.","tags":["lock stats","lock statistics","unlock_table_stats","lock_schema_stats","ora-20005","stattype_locked","dbms_stats"]},{"id":"stats-restore-history","task":"Statistiken auf einen früheren Zeitpunkt zurücksetzen","category":"Statistiken","command":"-- 1) Retention and the oldest point you can return to:\nSELECT DBMS_STATS.GET_STATS_HISTORY_RETENTION    AS retention_days,\n       DBMS_STATS.GET_STATS_HISTORY_AVAILABILITY AS oldest_available\nFROM   dual;\n\n-- 2) Statistics history of the table:\nSELECT table_name, partition_name,\n       TO_CHAR(stats_update_time,'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS stats_update_time\nFROM   dba_tab_stats_history\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'SALES'\nORDER  BY stats_update_time DESC;\n\n-- 3) Return to the point before the plan regressed:\nBEGIN\n  DBMS_STATS.RESTORE_TABLE_STATS(\n    ownname         => 'CUSTOMER_APP',\n    tabname         => 'SALES',\n    as_of_timestamp => TIMESTAMP '2026-10-07 22:00:00 +03:00',\n    no_invalidate   => FALSE);\nEND;\n/\n-- Whole schema: DBMS_STATS.RESTORE_SCHEMA_STATS('CUSTOMER_APP', <timestamp>)\n\n-- Change the retention (days):\n-- EXEC DBMS_STATS.ALTER_STATS_HISTORY_RETENTION(31);","what":"DBMS_STATS speichert vor jeder Erfassung die vorherigen Statistiken in SYSAUX (standardmäßig 31 Tage). Die RESTORE_*-Prozeduren setzen Tabellen-/Schemastatistiken auf ihren Stand zum angegebenen Zeitpunkt zurück; mit no_invalidate=>FALSE werden Cursors sofort neu geparst.","when":"Als schnellstes Rollback, wenn auf die nächtliche Statistikerfassung eine Planregression folgt, und um zu testen, ob eine Änderung durch Statistiken verursacht wurde.","caution":"Keine Lizenz erforderlich. Gesperrte Statistiken lassen sich nicht zurücksetzen (zuerst entsperren). Die nächste automatische Erfassung bringt nach dem Restore wieder neue Statistiken; für eine dauerhafte Lösung setze Tabellenpräferenzen, sperre die Statistiken oder nutze eine SQL Plan Baseline. Eine lange Aufbewahrung lässt SYSAUX wachsen (Tabellen WRI$_OPTSTAT_*).","tags":["restore stats","restore statistics","restore_table_stats","dba_tab_stats_history","stats history","plan regression","dbms_stats"]},{"id":"stats-pending-publish","task":"Statistiken als Pending erfassen, testen und veröffentlichen","category":"Statistiken","command":"-- 1) Disable publishing for the table (new statistics go to pending):\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'ORDERS', 'PUBLISH', 'FALSE');\nEXEC DBMS_STATS.GATHER_TABLE_STATS('CUSTOMER_APP', 'ORDERS');\n\n-- 2) Compare pending and published values:\nSELECT owner, table_name, partition_name, num_rows,\n       TO_CHAR(last_analyzed,'YYYY-MM-DD HH24:MI') AS last_analyzed\nFROM   dba_tab_pending_stats\nWHERE  owner = 'CUSTOMER_APP';\nSELECT num_rows, last_analyzed FROM dba_tables\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'ORDERS';\n\n-- 3) Try plans with pending statistics in this session only:\nALTER SESSION SET optimizer_use_pending_statistics = TRUE;\nEXPLAIN PLAN FOR SELECT COUNT(*) FROM customer_app.orders WHERE status = 7;\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'BASIC +ROWS'));\nALTER SESSION SET optimizer_use_pending_statistics = FALSE;\n\n-- 4a) If good, publish:\nEXEC DBMS_STATS.PUBLISH_PENDING_STATS('CUSTOMER_APP', 'ORDERS', no_invalidate => FALSE);\n-- 4b) If bad, discard:\n-- EXEC DBMS_STATS.DELETE_PENDING_STATS('CUSTOMER_APP', 'ORDERS');\n\n-- 5) Return to normal behavior:\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'ORDERS', 'PUBLISH', 'TRUE');","what":"Mit der Präferenz PUBLISH=FALSE werden erfasste Statistiken in einen Pending-Bereich statt ins Dictionary geschrieben, sodass andere Sessions nicht betroffen sind. Pläne werden in einer Test-Session mit optimizer_use_pending_statistics validiert und dann mit einem Befehl veröffentlicht oder verworfen.","when":"Um die Planwirkung aktualisierter Statistiken auf kritischen OLTP-Tabellen in der Produktion risikolos zu testen, und für eine kontrollierte Umstellung nach einer großen Datenladung.","caution":"Keine Lizenz erforderlich. Solange PUBLISH=FALSE gilt, sammeln sich auch vom automatischen Task erfasste Statistiken als Pending an und werden nie veröffentlicht; denk daran, die Präferenz wieder auf TRUE zu setzen. EXPLAIN PLAN ist eine Schätzung; führe die Abfrage nach Möglichkeit aus und prüfe DISPLAY_CURSOR.","since":"Oracle 11g","tags":["pending stats","publish","publish_pending_stats","optimizer_use_pending_statistics","statistics testing","dbms_stats","dba_tab_pending_stats"]},{"id":"stats-incremental-partitioned","task":"Inkrementelle Statistiken für eine partitionierte Tabelle konfigurieren","category":"Statistiken","command":"-- 1) Enable incremental mode (global statistics are derived from partition synopses):\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_PART', 'INCREMENTAL', 'TRUE');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_PART', 'INCREMENTAL_STALENESS', 'USE_STALE_PERCENT');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_PART', 'GRANULARITY', 'AUTO');\n\n-- 2) The first gather scans everything and builds synopses; later ones read only changed partitions:\nEXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'SALE_PART');\n\n-- 3) Verify:\nSELECT DBMS_STATS.GET_PREFS('INCREMENTAL', 'CUSTOMER_APP', 'SALE_PART') AS incremental FROM dual;\nSELECT partition_name, num_rows,\n       TO_CHAR(last_analyzed,'YYYY-MM-DD HH24:MI:SS') AS last_analyzed, stale_stats\nFROM   dba_tab_statistics\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'SALE_PART'\nORDER  BY partition_position NULLS FIRST;\n\n-- 4) Pre-build synopses on a staging table to be loaded via exchange (12c+):\n-- EXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_STAGE', 'INCREMENTAL', 'TRUE');\n-- EXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_STAGE', 'INCREMENTAL_LEVEL', 'TABLE');\n-- EXEC DBMS_STATS.GATHER_TABLE_STATS('CUSTOMER_APP', 'SALE_STAGE');","what":"Im inkrementellen Modus wird je Partition eine Synopsis gespeichert; globale Statistiken (Tabellenebene), insbesondere NDV, werden durch Zusammenführen der Synopsen berechnet, ohne die ganze Tabelle erneut zu scannen. Nur geänderte Partitionen werden erneut gelesen.","when":"Bei großen (TB-Bereich) Range-/Interval-partitionierten Tabellen, bei denen neue Daten nur in den jüngsten Partitionen landen, wenn die globale Statistikerfassung das Wartungsfenster überschreitet.","caution":"Keine eigene Lizenz, aber die Partitioning-Option (kostenpflichtig, EE) ist ohnehin nötig. Synopsen belegen Platz in SYSAUX (das HyperLogLog-Format ab 12.2 ist viel kleiner). Die erste Erfassung ist ein Full Scan; starte sie in einem Wartungsfenster. Ein ESTIMATE_PERCENT ungleich AUTO_SAMPLE_SIZE deaktiviert den inkrementellen Modus.","since":"Oracle 11g","note":"Die Präferenzen INCREMENTAL_STALENESS und INCREMENTAL_LEVEL kamen in 12c hinzu; ab 12.2 wird das Synopsis-Format über APPROXIMATE_NDV_ALGORITHM (HYPERLOGLOG) gesteuert.","tags":["incremental stats","synopsis","partition statistics","global stats","incremental_staleness","dbms_stats","large table"]},{"id":"partition-split-merge-online","task":"Partitionen online teilen und zusammenführen","category":"Partitionierung","command":"-- SPLIT: split a range partition in two, keep indexes usable\nALTER TABLE customer_app.sale_part\n  SPLIT PARTITION p2026_h2 AT (DATE '2026-10-01')\n  INTO (PARTITION p2026_q3, PARTITION p2026_q4)\n  UPDATE INDEXES ONLINE;\n\n-- MERGE: merge two adjacent partitions\nALTER TABLE customer_app.sale_part\n  MERGE PARTITIONS p2026_q3, p2026_q4 INTO PARTITION p2026_h2\n  UPDATE INDEXES ONLINE;\n\n-- ADD: new partition at the end (when there is no MAXVALUE)\nALTER TABLE customer_app.sale_part\n  ADD PARTITION p2027 VALUES LESS THAN (DATE '2028-01-01');\n\n-- Check: partition bounds and UNUSABLE index partitions\nSELECT partition_name, partition_position, high_value, num_rows\nFROM   dba_tab_partitions\nWHERE  table_owner = 'CUSTOMER_APP' AND table_name = 'SALE_PART'\nORDER  BY partition_position;\n\nSELECT index_name, partition_name, status\nFROM   dba_ind_partitions\nWHERE  index_owner = 'CUSTOMER_APP' AND status <> 'USABLE';","what":"SPLIT teilt eine Partition an einem Grenzwert in zwei (ab 12c: in mehrere); MERGE führt benachbarte Partitionen zusammen. Mit ONLINE läuft DML während des Vorgangs weiter; mit UPDATE INDEXES werden lokale und globale Indizes nicht UNUSABLE.","when":"Um eine gewachsene MAXVALUE-/\"Sonstige\"-Partition zu teilen, kleine alte Partitionen vor der Archivierung zusammenzuführen oder das Partitionierungsschema zu ändern (monatlich zu quartalsweise).","caution":"Erfordert die Partitioning-Option (kostenpflichtig, EE); ONLINE erfordert die Enterprise Edition. SPLIT/MERGE schreibt die Daten physisch neu: langsam bei großen Partitionen, erzeugt Redo/Undo und braucht temporären Platz. Ohne ONLINE ist die Tabelle während des Vorgangs für DML gesperrt; ohne UPDATE INDEXES werden globale Indizes UNUSABLE. Das Teilen einer leeren Partition ist schnell (Fast Split).","since":"Oracle 12.2","note":"ONLINE SPLIT kam mit 12.2 und ONLINE MERGE mit 18c; beide sind in 19c verfügbar. In älteren Releases lass das Schlüsselwort ONLINE weg und führe den Vorgang in einem Wartungsfenster aus.","tags":["split partition","merge partition","add partition","split a partition","merge partitions","update indexes","online","partitioning"]},{"id":"partition-exchange","task":"Daten per Exchange Partition massenhaft laden oder archivieren","category":"Partitionierung","command":"-- 1) Staging table that exactly matches the target (12.2+):\nCREATE TABLE customer_app.sale_stage\n  FOR EXCHANGE WITH TABLE customer_app.sale_part;\n\n-- 2) Load the staging table and build indexes matching the local indexes:\nINSERT /*+ APPEND */ INTO customer_app.sale_stage\nSELECT * FROM customer_app.sale_load WHERE created_at >= DATE '2027-01-01';\nCOMMIT;\nCREATE INDEX customer_app.sale_stage_customer_ix ON customer_app.sale_stage (customer_id);\n\n-- 3) Swap (dictionary-only operation, no data movement):\nALTER TABLE customer_app.sale_part\n  EXCHANGE PARTITION p2027 WITH TABLE customer_app.sale_stage\n  INCLUDING INDEXES WITHOUT VALIDATION\n  UPDATE GLOBAL INDEXES;\n\n-- 4) Verify:\nSELECT COUNT(*) FROM customer_app.sale_part PARTITION (p2027);\nSELECT index_name, partition_name, status\nFROM   dba_ind_partitions\nWHERE  index_owner = 'CUSTOMER_APP' AND status <> 'USABLE';","what":"Tauscht im Dictionary das Segment einer Partition mit dem Segment einer regulären Tabelle; Millionen von Zeilen \"kommen\" so in Sekunden in die Tabelle oder \"verlassen\" sie zur Archivierung. FOR EXCHANGE WITH TABLE legt eine Staging-Tabelle an, die Kompatibilität der Spalten und versteckten Spalten garantiert.","when":"Für DWH-/ETL-Massenladungen, das Auslagern einer alten Partition in eine Archivtabelle und um Indizes und Statistiken während des Ladens vorzubereiten, ohne die Live-Tabelle zu beeinträchtigen.","caution":"Erfordert die Partitioning-Option (kostenpflichtig, EE). WITHOUT VALIDATION prüft nicht, ob die Staging-Zeilen in die Partitionsgrenzen passen; falsche Daten bleiben in der falschen Partition und Abfragen können falsche Ergebnisse liefern; im Zweifel WITH VALIDATION verwenden (Full Scan). Ohne UPDATE GLOBAL INDEXES werden globale Indizes UNUSABLE. Der Zustand von Unique-/PK-Constraints muss auf beiden Seiten übereinstimmen.","note":"CREATE TABLE ... FOR EXCHANGE WITH TABLE wurde in 12.2 eingeführt; davor die Staging-Tabelle mit CREATE TABLE ... AS SELECT * FROM <table> WHERE 1=0 anlegen und Unterschiede bei versteckten/virtuellen Spalten manuell prüfen.","tags":["exchange partition","staging","for exchange with table","bulk load","archive","update global indexes","partitioning"]},{"id":"partition-drop-truncate-gidx","task":"Partition leeren/löschen und globale Indizes nutzbar halten","category":"Partitionierung","command":"-- Empty a partition / remove a partition (global indexes stay maintained):\nALTER TABLE customer_app.sale_part TRUNCATE PARTITION p2025 UPDATE INDEXES;\nALTER TABLE customer_app.sale_part DROP PARTITION p2025 UPDATE INDEXES;\n\n-- 12c+ asynchronous global index maintenance: index is VALID but carries orphaned entries\nSELECT index_name, status, orphaned_entries\nFROM   dba_indexes\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'SALE_PART';\n\n-- Clean orphaned entries now (normally SYS.PMO_DEFERRED_GIDX_MAINT_JOB does it at night):\nEXEC DBMS_PART.CLEANUP_GIDX(schema_name_in => 'CUSTOMER_APP', table_name_in => 'SALE_PART');\n-- or a single index: ALTER INDEX customer_app.sale_part_id_gidx COALESCE CLEANUP;\n\n-- Target the partition by value instead of name:\n-- ALTER TABLE customer_app.sale_part DROP PARTITION FOR (DATE '2025-06-15') UPDATE INDEXES;","what":"Entfernt alte Daten sofort auf Partitionsebene statt zeilenweise per DELETE. Ab 12c markiert UPDATE INDEXES globale Indizes nur im Dictionary (asynchrone Wartung), statt jeden Eintrag zu aktualisieren; der Index bleibt sofort nutzbar, und verwaiste Einträge werden später bereinigt.","when":"Beim Löschen alter Zeiträume gemäß einer Datenaufbewahrungsrichtlinie, in ILM-/Purge-Jobs und um die Undo-/Redo-Last von DELETE auf großen Tabellen zu vermeiden.","caution":"Erfordert die Partitioning-Option (kostenpflichtig, EE). DROP/TRUNCATE ist unumkehrbar (Flashback Table holt keine Partition zurück); sichere oder archiviere vorher per Exchange. Wird UPDATE INDEXES vergessen, werden globale Indizes UNUSABLE und Abfragen können auf ORA-01502 stoßen. Der Vorgang schlägt fehl, wenn Child-Tabellen darauf verweisen (FK). Bei Interval-Tabellen lässt sich die letzte Range-Partition (Transition) nicht löschen.","since":"Oracle 12c","tags":["drop partition","truncate partition","update indexes","orphaned_entries","cleanup_gidx","global index","data purge","partitioning"]},{"id":"interval-partitioning","task":"Interval-Partitionierung anlegen und eine bestehende Range-Tabelle auf Interval umstellen","category":"Partitionierung","command":"-- New table: a partition is created automatically for each month\nCREATE TABLE customer_app.measurements (\n  ts        DATE   NOT NULL,\n  sensor_id NUMBER,\n  value     NUMBER)\nPARTITION BY RANGE (ts) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))\n( PARTITION p_first VALUES LESS THAN (DATE '2026-01-01') );\n\n-- Convert an existing range table to interval (there must be no MAXVALUE partition):\nALTER TABLE customer_app.sale_part SET INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'));\n-- Back to range: ALTER TABLE customer_app.sale_part SET INTERVAL ();\n\n-- Rename a system-named partition to a meaningful name:\nALTER TABLE customer_app.measurements RENAME PARTITION FOR (DATE '2026-03-15') TO p2026_03;\n\n-- Check:\nSELECT partition_name, partition_position, interval, high_value\nFROM   dba_tab_partitions\nWHERE  table_owner = 'CUSTOMER_APP' AND table_name = 'MEASUREMENTS'\nORDER  BY partition_position;\n\nSELECT table_name, partitioning_type, interval\nFROM   dba_part_tables\nWHERE  owner = 'CUSTOMER_APP';","what":"Interval-Partitionierung legt automatisch eine neue Partition an, sobald die erste Zeile jenseits der letzten Grenze eintrifft, und beseitigt so ORA-14400-Fehler durch eine vergessene Partition für den nächsten Monat. SET INTERVAL stellt eine bestehende Range-Tabelle um, ohne Daten zu verschieben.","when":"Für Tabellen, die nach Datum wachsen, etwa Zeitreihen, Logs und Bestellungen, und um Skripte zur Partitionswartung zu reduzieren.","caution":"Erfordert die Partitioning-Option (kostenpflichtig, EE). Der Partitionsschlüssel muss eine einzelne NUMBER-/DATE-/TIMESTAMP-Spalte sein; eine MAXVALUE-Partition muss zuerst entfernt werden. Ein fehlerhaftes oder extremes Datum (z. B. Jahr 9999) öffnet eine weit entfernte Partition und verzerrt die Partitionsnummerierung; bei engen Intervallen kannst du dich dem Limit von 1.048.575 Partitionen nähern (ORA-14300), daher Datumswerte in der Anwendung validieren. Automatische Partitionen landen im Standard-Tablespace der Tabelle (mit STORE IN umlenken).","since":"Oracle 11g","tags":["interval partitioning","interval","automatic partition","numtoyminterval","ora-14400","set interval","range partition","partitioning"]},{"id":"partition-wise-join-check","task":"Partitionsschlüssel und Nutzung von Partition-Wise Joins prüfen","category":"Partitionierung","command":"-- 1) Do both tables have the same partitioning type, key and count?\nSELECT p.owner, p.table_name, p.partitioning_type, k.column_name,\n       p.partition_count, p.subpartitioning_type\nFROM   dba_part_tables p\n  JOIN dba_part_key_columns k\n    ON k.owner = p.owner AND k.name = p.table_name AND k.object_type = 'TABLE'\nWHERE  p.owner = 'CUSTOMER_APP'\n  AND  p.table_name IN ('CUSTOMER_H', 'ORDERS_H');\n\n-- 2) Explain the join and inspect the plan shape:\nEXPLAIN PLAN FOR\nSELECT /*+ PARALLEL(4) */ m.city, SUM(s.amount)\nFROM   customer_app.customer_h m\n  JOIN customer_app.orders_h s ON s.customer_id = m.id\nGROUP  BY m.city;\n\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'BASIC +PARALLEL +PARTITION'));\n-- Full PWJ: HASH JOIN sits BELOW the PARTITION HASH ALL line (PX PARTITION HASH ALL in parallel)\n-- Partial PWJ: PX SEND PARTITION (KEY) on one side only\n-- No PWJ: PX SEND HASH / BROADCAST on both sides","what":"Zwei Tabellen, die auf dem Join-Schlüssel gleich partitioniert sind, lassen sich Partitionspaar für Partitionspaar verknüpfen (Full Partition-Wise Join), was Umverteilungsverkehr und Speicher bei parallelen Abfragen deutlich reduziert. Die Planform zeigt, ob PWJ genutzt wird.","when":"Beim Entwurf großer Fact-zu-Fact- oder Fact-zu-Dimension-Joins im DWH, wenn parallele Abfragen starke PX-Umverteilung zeigen (PX Deq Credit: send blkd), und bei der Validierung der Partitionierungsstrategie.","caution":"Lizenz: Partitioning-Option (kostenpflichtig, EE); Parallel Query erfordert die Enterprise Edition. EXPLAIN PLAN ist eine Schätzung; nutze DISPLAY_CURSOR/SQL Monitor für den tatsächlichen Plan. Full PWJ braucht gleiche Partitionsanzahlen (bei Hash wird eine Zweierpotenz empfohlen) und übereinstimmende Datentypen der Schlüssel. PWJ kann auch in seriellen Plänen auftreten, der echte Gewinn liegt aber in der parallelen Ausführung.","tags":["partition-wise join","pwj","equi-partitioning","hash partition","parallel query","dba_part_key_columns","partitioning"]},{"id":"online-redefinition-flow","task":"Tabelle online mit DBMS_REDEFINITION umdefinieren (vollständiger Ablauf)","category":"Online-Operationen","command":"-- 1) Is it eligible? (use CONS_USE_ROWID if there is no PK)\nEXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('CUSTOMER_APP', 'CUSTOMER', DBMS_REDEFINITION.CONS_USE_PK);\n\n-- 2) Interim table with the target structure: hash partitioned in this example\nCREATE TABLE customer_app.customer_int (\n  id NUMBER, name VARCHAR2(100), city VARCHAR2(30))\nPARTITION BY HASH (id) PARTITIONS 8;\n\n-- 3) Start: initial copy + change tracking through an MV log\nBEGIN\n  DBMS_REDEFINITION.START_REDEF_TABLE(\n    uname        => 'CUSTOMER_APP',\n    orig_table   => 'CUSTOMER',\n    int_table    => 'CUSTOMER_INT',\n    col_mapping  => NULL,\n    options_flag => DBMS_REDEFINITION.CONS_USE_PK);\nEND;\n/\n\n-- 4) Copy indexes, constraints, triggers, grants and statistics:\nSET SERVEROUTPUT ON\nDECLARE\n  l_errors PLS_INTEGER;\nBEGIN\n  DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS(\n    uname            => 'CUSTOMER_APP',\n    orig_table       => 'CUSTOMER',\n    int_table        => 'CUSTOMER_INT',\n    copy_indexes     => DBMS_REDEFINITION.CONS_ORIG_PARAMS,\n    copy_triggers    => TRUE,\n    copy_constraints => TRUE,\n    copy_privileges  => TRUE,\n    ignore_errors    => FALSE,\n    num_errors       => l_errors,\n    copy_statistics  => TRUE);\n  DBMS_OUTPUT.PUT_LINE('Error count: ' || l_errors);\nEND;\n/\nSELECT object_name, base_table_name, ddl_txt FROM dba_redefinition_errors;\n\n-- 5) Close the gap before finishing (shortens finish; can be run several times):\nEXEC DBMS_REDEFINITION.SYNC_INTERIM_TABLE('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_INT');\n\n-- 6) Swap under a short lock:\nEXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_INT', dml_lock_timeout => 60);\n\n-- 7) Drop the interim table, which now holds the old structure:\nDROP TABLE customer_app.customer_int PURGE;\n\n-- On problems: EXEC DBMS_REDEFINITION.ABORT_REDEF_TABLE('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_INT');\n-- Status: SELECT * FROM dba_redefinition_status WHERE base_table_owner = 'CUSTOMER_APP';","what":"Ändert die Struktur einer Tabelle (Partitionierung, Spaltentypen/-reihenfolge, Tablespace, Kompression, LOB-Einstellungen), während die Anwendung weiterläuft: Die Daten werden in eine Interimstabelle kopiert, Änderungen über ein MV-Log synchronisiert, und am Ende werden die Namen unter einer sehr kurzen Sperre getauscht.","when":"Für Änderungen, die ALTER TABLE nicht online ausführen kann, etwa eine große nicht partitionierte Tabelle ohne Ausfallzeit partitionieren, einen Spaltentyp ändern oder BasicFile-LOBs in SecureFiles umwandeln.","caution":"Erfordert die Enterprise Edition (plus Partitioning-Option, wenn das Ziel partitioniert ist). Der Vorgang braucht den doppelten Platz der Tabelle und erzeugt zusätzlichen Redo- und MV-Log-Overhead. FINISH nimmt kurz eine exklusive Sperre; lang laufende Transaktionen lassen es warten (dml_lock_timeout). Prüfe immer die COPY_TABLE_DEPENDENTS-Fehler in DBA_REDEFINITION_ERRORS; FKs werden eventuell als NOVALIDATE kopiert. Probe es zuerst in einer Testumgebung.","note":"Der Parameter dml_lock_timeout kam in 12c hinzu. Ab 12.2 den Fortschritt in V$ONLINE_REDEF verfolgen. Für eine einfache Verschiebung/Kompressionsänderung reicht ALTER TABLE ... MOVE ONLINE statt DBMS_REDEFINITION.","tags":["dbms_redefinition","online redefinition","redefinition","zero downtime","convert to partitioned","can_redef_table","finish_redef_table","enterprise edition"]},{"id":"table-move-online","task":"Tabelle oder Partition online verschieben (ALTER TABLE MOVE ONLINE)","category":"Online-Operationen","command":"-- Move the table to another tablespace / reorganize it (DML continues):\nALTER TABLE customer_app.orders MOVE ONLINE TABLESPACE customer_data;\n\n-- Compress while moving (BASIC; for OLTP compression use ROW STORE COMPRESS ADVANCED):\nALTER TABLE customer_app.orders MOVE ONLINE COMPRESS;\n\n-- A single partition:\nALTER TABLE customer_app.sale_part\n  MOVE PARTITION p2026_h1 ONLINE TABLESPACE customer_archive UPDATE INDEXES;\n\n-- Verify: indexes must stay VALID\nSELECT index_name, status FROM dba_indexes\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'ORDERS';\nSELECT table_name, tablespace_name, compression, compress_for\nFROM   dba_tables\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'ORDERS';","what":"Schreibt das Tabellensegment neu: reduziert Fragmentierung und High Water Mark, wechselt den Tablespace, wendet Kompression an. Mit ONLINE wird DML während des Vorgangs nicht blockiert, und Indizes werden automatisch gepflegt und bleiben VALID.","when":"Um nach Massenlöschungen Platz zurückzugewinnen, eine Tabelle in einen neuen Tablespace/Storage zu verschieben und alte Partitionen komprimiert in einen Archiv-Tablespace zu legen.","caution":"Erfordert die Enterprise Edition; ROW STORE COMPRESS ADVANCED erfordert die Advanced-Compression-Option (kostenpflichtig). Der Vorgang braucht zusätzlichen Platz in Höhe der Tabellengröße und erzeugt viel Redo. Am Anfang und Ende werden kurze Sperren genommen; lange offene Transaktionen lassen es warten. Ein MOVE ohne ONLINE oder UPDATE INDEXES macht alle Indizes UNUSABLE.","since":"Oracle 12.2","note":"MOVE ONLINE auf Tabellenebene wurde in 12.2 eingeführt; MOVE PARTITION ONLINE auf Partitionsebene gibt es seit 12.1. In 12.1 und früher für Tabellen DBMS_REDEFINITION verwenden.","tags":["move online","alter table move","move table","reorganization","high water mark","compression","enterprise edition"]},{"id":"tde-keystore-setup","task":"TDE-Keystore anlegen, öffnen, Master Key setzen und sichern (ab 19c)","category":"Sicherheit","command":"-- 1) Keystore location (static parameter, restart required), then keystore type:\nALTER SYSTEM SET WALLET_ROOT = '/u01/app/oracle/admin/ORCL/wallet' SCOPE = SPFILE;\n-- SHUTDOWN IMMEDIATE;  STARTUP;\nALTER SYSTEM SET TDE_CONFIGURATION = 'KEYSTORE_CONFIGURATION=FILE' SCOPE = BOTH;\n\n-- 2) In CDB root: create a software keystore (under WALLET_ROOT/tde), open it, set the master key\nADMINISTER KEY MANAGEMENT CREATE KEYSTORE IDENTIFIED BY \"<keystore_pwd>\";\nADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN\n  IDENTIFIED BY \"<keystore_pwd>\" CONTAINER = ALL;\nADMINISTER KEY MANAGEMENT SET KEY USING TAG 'mk_2026_10'\n  IDENTIFIED BY \"<keystore_pwd>\" WITH BACKUP USING 'mk_before' CONTAINER = ALL;\n\n-- 3) Open automatically after restart (auto-login, cwallet.sso):\nADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE\n  FROM KEYSTORE IDENTIFIED BY \"<keystore_pwd>\";\n\n-- 4) Backup (repeat after every SET KEY / rekey, store SEPARATELY from database backups):\nADMINISTER KEY MANAGEMENT BACKUP KEYSTORE USING 'daily'\n  FORCE KEYSTORE IDENTIFIED BY \"<keystore_pwd>\" TO '/backup/tde/';\n\n-- 5) Status:\nSELECT con_id, wrl_type, wrl_parameter, status, wallet_type, keystore_mode\nFROM   v$encryption_wallet;\nSELECT con_id, key_id, tag, creation_time, activation_time\nFROM   v$encryption_keys ORDER BY creation_time;","what":"Richtet einen Software-Keystore für Transparent Data Encryption im United Mode ein (alle PDBs teilen den Keystore der CDB), erstellt den Master Encryption Key und aktiviert Auto-Login, damit er nach einem Neustart öffnet. Ab 19c ist WALLET_ROOT/TDE_CONFIGURATION statt sqlnet.ora ENCRYPTION_WALLET_LOCATION die empfohlene Methode.","when":"Als erster Schritt vor der Tablespace-/Spaltenverschlüsselung in Compliance-Projekten, die Verschlüsselung ruhender Daten verlangen (DSGVO, PCI-DSS, ISO 27001).","caution":"On-Premises erfordert die Advanced-Security-Option (kostenpflichtig); in der Oracle-Cloud-Lizenzierung (OCI/Autonomous) ist sie enthalten. WALLET_ROOT ist statisch und erfordert einen Instanzneustart (Ausfallzeit). Gehen Keystore und Passwort verloren, sind verschlüsselte Daten DAUERHAFT unlesbar; bewahre Keystore-Backups sicher und getrennt von Datenbank-Backups auf. Wer die Auto-Login-Datei (cwallet.sso) besitzt, kann die Datenbank öffnen; beschränke die Dateirechte und nutze wo sinnvoll LOCAL AUTO_LOGIN. Kopiere den Keystore bei Data Guard auch auf den Standby.","since":"Oracle 18c","note":"WALLET_ROOT und TDE_CONFIGURATION wurden in 18c eingeführt und sind in 19c Standard; die Methode mit sqlnet.ora ENCRYPTION_WALLET_LOCATION aus 12c-18c ist in 19c veraltet und in 23ai nicht mehr unterstützt. In 23ai ist AES256 der Standardalgorithmus; Verschlüsselung mit GOST und SEED wird nicht mehr unterstützt, Entschlüsselung damit ist veraltet. WALLET_ROOT ist ein statischer Parameter und erfordert einen Instanzneustart.","tags":["tde","keystore","wallet","master key","encryption","advanced security","wallet_root","administer key management"],"untested":true},{"id":"tde-tablespace-encryption","task":"Tablespaces mit TDE verschlüsseln (neu und bestehend, online/offline)","category":"Sicherheit","command":"-- Prerequisite: keystore OPEN and master key set (v$encryption_wallet.status = 'OPEN')\n\n-- 1) New encrypted tablespace:\nCREATE TABLESPACE customer_enc\n  DATAFILE '+DATA' SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 20G\n  ENCRYPTION USING 'AES256' ENCRYPT;\n\n-- 2a) Encrypt an existing tablespace ONLINE (12.2+, needs extra space temporarily):\nALTER TABLESPACE customer_data ENCRYPTION ONLINE USING 'AES256' ENCRYPT;\n-- If not OMF: ... ENCRYPT FILE_NAME_CONVERT = ('customer_data01.dbf', 'customer_data01_enc.dbf');\n\n-- 2b) OFFLINE conversion (no extra space, tablespace unavailable meanwhile):\nALTER TABLESPACE customer_data OFFLINE NORMAL;\nALTER TABLESPACE customer_data ENCRYPTION OFFLINE ENCRYPT;\nALTER TABLESPACE customer_data ONLINE;\n\n-- 3) Progress and status:\nSELECT t.name AS tablespace_name, e.encryptionalg, e.status, e.con_id\nFROM   v$encrypted_tablespaces e\n  JOIN v$tablespace t ON t.ts# = e.ts# AND t.con_id = e.con_id;\nSELECT tablespace_name, encrypted FROM dba_tablespaces ORDER BY tablespace_name;","what":"Tablespace-Verschlüsselung verschlüsselt jeden in die Datafiles geschriebenen Block; im Buffer Cache liegen die Daten im Klartext, daher sind keine Änderungen an Anwendung oder SQL nötig. Die Online-Umwandlung baut eine verschlüsselte Kopie der Datafile auf und schaltet darauf um, während DML weiterläuft; die Offline-Umwandlung verschlüsselt die Blöcke an Ort und Stelle.","when":"Um Anwendungs-Tablespaces mit sensiblen ruhenden Daten zu verschlüsseln, Daten in einen neuen verschlüsselten Tablespace zu verschieben oder einen Audit-Befund zu schließen.","caution":"On-Premises erfordert die Advanced-Security-Option (kostenpflichtig). Die Online-Umwandlung braucht zusätzlichen Platz in Höhe der zu verschlüsselnden Datafiles und erzeugt viel I/O und Redo; ein Data-Guard-Standby leistet dieselbe Arbeit. Die Offline-Umwandlung muss auf einem Data-Guard-Standby separat ausgeführt werden. SYSTEM/SYSAUX/UNDO lassen sich ab 12.2 online verschlüsseln, erfordern aber besondere Sorgfalt. Backups verschlüsselter Tablespaces lassen sich ohne Keystore nicht wiederherstellen.","since":"Oracle 12.2","note":"Die Online-/Offline-Umwandlung von Tablespaces wurde in 12.2 eingeführt. Um neue Tablespaces automatisch zu verschlüsseln, nutzt 19c TABLESPACE_ENCRYPTION=AUTO_ENABLE (ab RU 19.16) oder ENCRYPT_NEW_TABLESPACES=ALWAYS. Der Keystore muss zuerst mit gesetztem Master Key geöffnet sein (siehe tde-keystore-setup).","tags":["tde","tablespace encryption","encryption online","aes256","v$encrypted_tablespaces","advanced security","at-rest"],"untested":true},{"id":"unified-audit-policy-lifecycle","task":"Unified-Auditing-Policy anlegen, bedingt aktivieren, Einträge prüfen und entfernen","category":"Sicherheit","command":"-- 1) Object + condition based policy (excluding the batch module):\nCREATE AUDIT POLICY pol_customer_dml\n  ACTIONS SELECT ON customer_app.customer,\n          UPDATE ON customer_app.customer,\n          DELETE ON customer_app.customer\n  WHEN 'SYS_CONTEXT(''USERENV'',''MODULE'') NOT LIKE ''batch%'''\n  EVALUATE PER SESSION;\n\n-- 2) Privileged operations policy:\nCREATE AUDIT POLICY pol_ddl_priv\n  PRIVILEGES DROP ANY TABLE, ALTER SYSTEM\n  ACTIONS DROP USER, ALTER USER, CREATE USER;\n\n-- 3) Enable:\nAUDIT POLICY pol_customer_dml EXCEPT customer_app;   -- everyone except the schema owner\nAUDIT POLICY pol_ddl_priv WHENEVER SUCCESSFUL;\nAUDIT POLICY ORA_LOGON_FAILURES WHENEVER NOT SUCCESSFUL;\n\n-- 4) Which policy is enabled for whom?\nSELECT policy_name, enabled_option, entity_name, success, failure\nFROM   audit_unified_enabled_policies ORDER BY policy_name;\nSELECT policy_name, audit_option, object_schema, object_name, audit_condition\nFROM   audit_unified_policies WHERE policy_name LIKE 'POL_%';\n\n-- 5) Records:\nSELECT event_timestamp, dbusername, action_name, object_schema, object_name,\n       return_code, unified_audit_policies, os_username, userhost\nFROM   unified_audit_trail\nWHERE  event_timestamp > SYSTIMESTAMP - INTERVAL '1' DAY\n  AND  unified_audit_policies LIKE '%POL_%'\nORDER  BY event_timestamp DESC;\n-- 19c (queued-write): EXEC DBMS_AUDIT_MGMT.FLUSH_UNIFIED_AUDIT_TRAIL;  (not in 23ai)\n\n-- 6) Disable and remove:\nNOAUDIT POLICY pol_customer_dml;\nNOAUDIT POLICY pol_ddl_priv;\nDROP AUDIT POLICY pol_customer_dml;\nDROP AUDIT POLICY pol_ddl_priv;","what":"Unified Auditing sammelt alle Audit-Einträge in einem einzigen Trail (Schema AUDSYS, UNIFIED_AUDIT_TRAIL). Policies werden je Objektaktion, Systemprivileg oder Aktion definiert; die WHEN-Bedingung und BY-/EXCEPT-Benutzerlisten reduzieren das Rauschen.","when":"Um Zugriffe auf sensible Tabellen zu protokollieren, privilegierte DDL und Rechteänderungen zu überwachen, fehlgeschlagene Anmeldeversuche zu sehen und Audit-Anforderungen zu erfüllen.","caution":"Keine Zusatzlizenz. Auditing von SELECT auf stark genutzten Tabellen erzeugt viele Einträge und lässt SYSAUX wachsen; schränke es mit Bedingungen und EXCEPT ein und plane Archivierung/Bereinigung mit DBMS_AUDIT_MGMT (CLEAN_AUDIT_TRAIL, SET_LAST_ARCHIVE_TIMESTAMP). Eine aktivierte Policy lässt sich nicht löschen (ORA-46361); führe zuerst NOAUDIT aus. Policy-Änderungen wirken in neuen Sessions. 19c läuft standardmäßig im Mixed Mode, daher können auch klassische AUDIT_TRAIL-Einträge geschrieben werden.","since":"Oracle 12c","note":"In 23ai wird klassisches Auditing nicht mehr unterstützt; es gibt nur noch Unified Auditing, und FLUSH_UNIFIED_AUDIT_TRAIL wurde entfernt, weil Einträge direkt in die Tabelle geschrieben werden. In 19c erfordert der Wechsel in den reinen Unified Mode ein Relinken des oracle-Binarys mit uniaud_on.","tags":["unified auditing","audit policy","auditing","unified_audit_trail","create audit policy","noaudit","ora_logon_failures","security"]},{"id":"vpd-row-level-policy","task":"Zugriffs-Policy auf Zeilenebene mit VPD hinzufügen (DBMS_RLS)","category":"Sicherheit","command":"-- 1) Policy function returning the predicate:\nCREATE OR REPLACE FUNCTION customer_app.customer_vpd_fn (\n  p_schema IN VARCHAR2,\n  p_object IN VARCHAR2\n) RETURN VARCHAR2\nAS\nBEGIN\n  -- The schema owner sees all rows (NULL = no restriction)\n  IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'CUSTOMER_APP' THEN\n    RETURN NULL;\n  END IF;\n  -- Other users see only their own city\n  RETURN 'city = SYS_CONTEXT(''USERENV'', ''CLIENT_IDENTIFIER'')';\nEND;\n/\n\n-- 2) Attach the policy to the table:\nBEGIN\n  DBMS_RLS.ADD_POLICY(\n    object_schema   => 'CUSTOMER_APP',\n    object_name     => 'CUSTOMER',\n    policy_name     => 'CUSTOMER_CITY_VPD',\n    function_schema => 'CUSTOMER_APP',\n    policy_function => 'CUSTOMER_VPD_FN',\n    statement_types => 'SELECT, UPDATE, DELETE',\n    update_check    => TRUE,\n    policy_type     => DBMS_RLS.CONTEXT_SENSITIVE);\nEND;\n/\n\n-- 3) Check:\nSELECT object_owner, object_name, policy_name, function,\n       sel, upd, del, chk_option, enable, policy_type\nFROM   dba_policies WHERE object_owner = 'CUSTOMER_APP';\n-- Test (as the application user): EXEC DBMS_SESSION.SET_IDENTIFIER('Ankara');\n\n-- 4) Temporarily disable / drop:\nEXEC DBMS_RLS.ENABLE_POLICY('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_CITY_VPD', FALSE);\nEXEC DBMS_RLS.DROP_POLICY('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_CITY_VPD');","what":"Virtual Private Database hängt das von der Policy-Funktion gelieferte WHERE-Prädikat automatisch an jede SQL auf die Tabelle an, sodass Benutzer derselben Tabelle nur die Zeilen sehen/ändern, zu denen sie berechtigt sind. update_check=>TRUE verhindert, dass ein Benutzer eine Zeile auf einen Wert ändert, den er nicht sehen dürfte.","when":"In mandantenfähigen Anwendungen, für Datentrennung nach Region/Filiale und um Zeilensicherheit hinzuzufügen, ohne den Anwendungscode anzufassen.","caution":"In der Enterprise Edition enthalten, keine Zusatzlizenz. CLIENT_IDENTIFIER kann der Client frei setzen; nutze in der Produktion einen Application Context, der von einem vertrauenswürdigen Package gesetzt wird (CREATE CONTEXT ... USING). SYS und Benutzer mit EXEMPT ACCESS POLICY umgehen die Policy; Data-Pump-Exporte ohne dieses Privileg exportieren stillschweigend nur einen Teil der Daten. Ein Fehler in der Funktion blockiert jeden Zugriff mit ORA-28112. Ein falscher policy_type (z. B. STATIC) cacht das Prädikat und liefert falsche Ergebnisse.","tags":["vpd","virtual private database","dbms_rls","row security","row level security","add_policy","policy function","security"]},{"id":"password-profile-hardening","task":"Profil für Passwortrichtlinien anlegen und Benutzern zuweisen","category":"Sicherheit","command":"-- Hardened profile for application/human users:\nCREATE PROFILE app_user_prf LIMIT\n  FAILED_LOGIN_ATTEMPTS    5\n  PASSWORD_LOCK_TIME       1/24          -- 1 hour lock\n  PASSWORD_LIFE_TIME       90\n  PASSWORD_GRACE_TIME      7\n  PASSWORD_REUSE_TIME      365\n  PASSWORD_REUSE_MAX       10\n  INACTIVE_ACCOUNT_TIME    60            -- accounts unused for 60 days are locked\n  PASSWORD_VERIFY_FUNCTION ora12c_strong_verify_function;\n\n-- Service accounts: password never expires, gradual password rollover enabled:\nCREATE PROFILE service_account_prf LIMIT\n  FAILED_LOGIN_ATTEMPTS    UNLIMITED\n  PASSWORD_LIFE_TIME       UNLIMITED\n  PASSWORD_ROLLOVER_TIME   1;            -- old and new password both valid for 1 day\n\nALTER USER customer_app PROFILE app_user_prf;\n\n-- Check:\nSELECT username, profile, account_status,\n       TO_CHAR(expiry_date,'YYYY-MM-DD') AS expiry_date, lock_date\nFROM   dba_users\nWHERE  profile IN ('APP_USER_PRF', 'SERVICE_ACCOUNT_PRF');\n\nSELECT profile, resource_name, limit\nFROM   dba_profiles\nWHERE  profile = 'APP_USER_PRF' AND resource_type = 'PASSWORD'\nORDER  BY resource_name;","what":"Ein Profil wendet auf eine Benutzergruppe Regeln für Sperrung nach Fehlanmeldungen, Passwortlaufzeit, Wiederverwendung, Komplexität (Verify-Funktion) und Sperrung inaktiver Konten an. PASSWORD_ROLLOVER_TIME lässt Anwendungen ohne Ausfall umstellen, während das Passwort eines Dienstkontos geändert wird.","when":"Für Anforderungen aus Sicherheitsaudits/CIS-Benchmarks, beim Anlegen neuer Anwendungsbenutzer und um die lockeren Werte des DEFAULT-Profils je Benutzergruppe zu trennen.","caution":"Keine Zusatzlizenz. Mit einem FAILED_LOGIN_ATTEMPTS-Limit auf einem Dienstkonto kann ein einziger App-Server mit falschem Passwort das Konto sperren und einen Produktionsausfall verursachen. PASSWORD_LIFE_TIME auf Anwendungskonten kann einen unerwarteten Status EXPIRED verursachen. Die Verify-Funktion muss unter SYS existieren (sonst @?/rdbms/admin/utlpwdmg.sql; dieses Skript ändert auch das DEFAULT-Profil). Eine Profiländerung validiert bestehende Passwörter nicht neu.","note":"INACTIVE_ACCOUNT_TIME kam in 12.2 hinzu; PASSWORD_ROLLOVER_TIME kam in 21c und wurde mit RU 19.12 nach 19c zurückportiert. Sessions, die sich während der Rollover-Phase mit dem alten Passwort anmelden, lassen sich über Audit-Einträge nachverfolgen.","tags":["profile","password policy","password_verify_function","failed_login_attempts","inactive_account_time","password_rollover_time","account lock","security"]},{"id":"privilege-analysis-capture","task":"Ungenutzte Privilegien mit Privilege Analysis finden","category":"Sicherheit","command":"-- 1) Capture privilege usage for a specific user:\nBEGIN\n  DBMS_PRIVILEGE_CAPTURE.CREATE_CAPTURE(\n    name        => 'CAP_APP_USER',\n    description => 'Actual privilege usage of the application user',\n    type        => DBMS_PRIVILEGE_CAPTURE.G_CONTEXT,\n    condition   => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') = ''CUSTOMER_APP''');\n  DBMS_PRIVILEGE_CAPTURE.ENABLE_CAPTURE(name => 'CAP_APP_USER', run_name => 'RUN_2026_10');\nEND;\n/\n-- Other types: G_DATABASE (whole DB), G_ROLE (roles => role_name_list('DBA')), G_ROLE_AND_CONTEXT\n\n-- 2) After a representative workload (including month end), stop and generate results:\nBEGIN\n  DBMS_PRIVILEGE_CAPTURE.DISABLE_CAPTURE(name => 'CAP_APP_USER');\n  DBMS_PRIVILEGE_CAPTURE.GENERATE_RESULT(name => 'CAP_APP_USER', run_name => 'RUN_2026_10');\nEND;\n/\n\n-- 3) Used and never-used privileges:\nSELECT username, sys_priv, obj_priv, object_owner, object_name, used_role\nFROM   dba_used_privs   WHERE capture = 'CAP_APP_USER';\nSELECT username, sys_priv, obj_priv, object_owner, object_name\nFROM   dba_unused_privs WHERE capture = 'CAP_APP_USER';\n\n-- 4) Through which role chain was the privilege used?\nSELECT username, sys_priv, used_role, path\nFROM   dba_used_sysprivs_path WHERE capture = 'CAP_APP_USER';\n\n-- 5) Cleanup:\nEXEC DBMS_PRIVILEGE_CAPTURE.DROP_CAPTURE(name => 'CAP_APP_USER');","what":"Privilege Analysis erfasst zur Laufzeit, welche System-/Objektprivilegien im gewählten Umfang (gesamte Datenbank, Rolle, Bedingung) tatsächlich genutzt werden; das Ergebnis listet ungenutzte Privilegien, sodass REVOKEs nach dem Least-Privilege-Prinzip geplant werden können.","when":"Beim Bereinigen von Anwendungskonten mit DBA-/ANY-Privilegien, für Rechteprüfungen vor einem Audit und um herauszufinden, was eine Standardanwendung wirklich braucht.","caution":"Seit 2018 in der Enterprise Edition enthalten (vorher war eine Database-Vault-Lizenz nötig); keine Zusatzlizenz. Während der Erfassung entsteht ein geringer Overhead. Das Ergebnis spiegelt nur die Workload im Erfassungsfenster wider: Liefen Monatsend-, Jahresend- oder Notfalljobs nicht, kann ein als \"ungenutzt\" angezeigtes Privileg dennoch nötig sein, daher schrittweise entziehen. Es kann immer nur eine Erfassung aktiv sein; einzige Ausnahme: Eine G_DATABASE-Erfassung kann zusätzlich zu einer Rollen- oder Context-Erfassung aktiv sein.","since":"Oracle 12c","tags":["privilege analysis","dbms_privilege_capture","unused privileges","least privilege","dba_unused_privs","privilege cleanup","security"]},{"id":"opatch-inventory-prereq","task":"Inventar mit OPatch prüfen und Vorprüfungen vor dem Patchen ausführen","category":"Patching & Lifecycle","command":"# OPatch version (must meet the minimum in the patch README; update with patch 6880880 if needed)\n$ORACLE_HOME/OPatch/opatch version\n\n# Installed patches: short list / detailed inventory\n$ORACLE_HOME/OPatch/opatch lspatches\n$ORACLE_HOME/OPatch/opatch lsinventory -detail -oh $ORACLE_HOME\n\n# Pre-apply checks (in the unzipped patch directory):\ncd /u01/stage/<patch_id>\n$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./\n$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph ./\n\n# For Grid Infrastructure / RAC homes (as root), analyze mode:\n# opatchauto apply /u01/stage/<patch_id> -analyze","what":"Listet die im Oracle Home eingespielten Binärpatches (RUs, One-offs) und prüft vor dem Einspielen eines neuen Patches, ob er mit vorhandenen Patches kollidiert und ob genug Plattenplatz vorhanden ist.","when":"Vor einem quartalsweisen RU/Security-Patch, wenn der Support in einem SR eine Inventarausgabe verlangt, und beim Vergleich des Patch-Levels zweier Umgebungen.","caution":"Führe die Befehle als OS-Benutzer aus, dem das Oracle Home gehört (oracle); opatchauto erfordert root. Prereq-Prüfungen sind nur lesend und beeinflussen die Datenbank nicht, wird aber ein Konflikt gemeldet, spiel nichts ein, bevor du von Oracle Support einen Merge-/Overlay-Patch erhalten hast. Sichere vor dem Einspielen das Oracle Home (tar) und das Inventar.","note":"Um das Inventar per SQL anzuzeigen, kann DBMS_QOPATCH.GET_OPATCH_LSINVENTORY verwendet werden (bei Installationen mit OPatch-Inventar).","tags":["opatch","lsinventory","lspatches","prereq","patch","patch conflict","opatchauto","release update"],"untested":true},{"id":"datapatch-apply-verify","task":"SQL-Schicht nach einem Binärpatch mit datapatch -verbose aktualisieren","category":"Patching & Lifecycle","command":"# 1) After the binary patch (opatch apply), start the DB; all PDBs must be open:\nsqlplus / as sysdba <<EOF\nSTARTUP;\nALTER PLUGGABLE DATABASE ALL OPEN;\nEXIT;\nEOF\n\n# 2) Check only first, then apply:\ncd $ORACLE_HOME/OPatch\n./datapatch -prereq\n./datapatch -verbose\n# For specific PDBs: ./datapatch -verbose -pdbs PDB1,PDB2\n\n# 3) Recompile invalid objects:\nsqlplus / as sysdba @?/rdbms/admin/utlrp.sql\n\n# Logs: $ORACLE_BASE/cfgtoollogs/sqlpatch/","what":"datapatch spielt die SQL-/PL-SQL-Komponenten (Dictionary, Packages) der Binärpatches im Oracle Home in CDB-Root, PDB$SEED und alle geöffneten PDBs ein und hält das Ergebnis in DBA_REGISTRY_SQLPATCH fest. Es macht auch den SQL-Teil zurückgerollter Patches rückgängig.","when":"Nach jedem RU-/RUR-/One-off-Binärpatch oder Rollback; für PDBs, die in ein neu gepatchtes Home verschoben oder dort ein-/ausgesteckt wurden; und erneut, wenn DBA_REGISTRY_SQLPATCH WITH ERRORS zeigt.","caution":"Geschlossene oder MOUNTED PDBs werden nicht gepatcht und öffnen eventuell im RESTRICTED-Modus; öffne zuerst alle. Nicht unter hoher Last ausführen (Dictionary-Sperren, Invalidierung von Packages). In RAC nur einmal von einem einzigen Knoten ausführen. Bei Data Guard datapatch nur auf der Primary ausführen; die Änderungen gelangen per Redo zum Standby (zuerst die Standby-Binaries patchen).","note":"Das mit aktuellen 19c-RUs ausgelieferte datapatch bietet -sanity_checks, um vor dem Patchen den Zustand von Dictionary und Umgebung zu prüfen (ungültige Objekte, PDB-Verstöße, Tablespace-/TEMP-Zustand).","tags":["datapatch","sql patch","release update","dba_registry_sqlpatch","post-patch","utlrp","pdb","patching"],"untested":true},{"id":"sqlpatch-registry-check","task":"SQL-Patch-Historie, Komponentenstatus und ungültige Objekte prüfen","category":"Patching & Lifecycle","command":"-- 1) datapatch history (most recent at the bottom):\nSELECT patch_id, patch_type, action, status, source_version, target_version,\n       TO_CHAR(action_time,'YYYY-MM-DD HH24:MI') AS action_time, description\nFROM   dba_registry_sqlpatch\nORDER  BY action_time;\n\n-- Failed entries and their log files:\nSELECT install_id, patch_id, action, status, logfile\nFROM   dba_registry_sqlpatch\nWHERE  status <> 'SUCCESS';\n\n-- In a CDB, for all PDBs (from root):\nSELECT con_id, patch_id, action, status\nFROM   cdb_registry_sqlpatch ORDER BY con_id, action_time;\n\n-- 2) Components must be VALID:\nSELECT comp_id, comp_name, version_full, status\nFROM   dba_registry ORDER BY comp_id;\n\n-- 3) Invalid object summary (compare before/after utlrp):\nSELECT owner, object_type, COUNT(*) AS invalid_cnt\nFROM   dba_objects\nWHERE  status = 'INVALID'\nGROUP  BY owner, object_type\nORDER  BY invalid_cnt DESC;","what":"Zeigt auf einen Blick, ob die SQL-Schicht nach dem Patchen wirklich aktualisiert wurde (Dictionary passend zu den Binaries), welcher Patch in welcher PDB fehlschlug, ob die Dictionary-Komponenten VALID sind und wie viele ungültige Objekte es gibt.","when":"Als Prüfschritt nach jedem datapatch, um vor dem Patchen oder Upgrade eine Baseline festzuhalten, und wenn nach dem Einstecken einer PDB ein Mismatch vermutet wird.","caution":"Nur lesend; keine Lizenz erforderlich. SOURCE_VERSION/TARGET_VERSION sind im RU-Modell ab 18c befüllt. Bei STATUS='WITH ERRORS' den Fehler in LOGFILE beheben und datapatch erneut ausführen. Einige Komponenten (z. B. ungenutztes XDB/JAVA) zeigen eventuell INVALID/REMOVED; mit dem Zustand vor dem Patchen vergleichen.","note":"Um das Binärinventar per SQL zu sehen, nutze SELECT XMLTRANSFORM(DBMS_QOPATCH.GET_OPATCH_LIST, DBMS_QOPATCH.GET_OPATCH_XSLT) FROM dual; bei Installationen ohne OPatch-Inventar löst es ORA-20013 aus.","tags":["dba_registry_sqlpatch","patch history","datapatch","dba_registry","invalid objects","invalid object","patch verification"]},{"id":"scheduler-job-lifecycle","task":"Scheduler-Job anlegen, manuell ausführen, deaktivieren und löschen","category":"Scheduler","command":"-- 1) Create disabled, set attributes, then enable:\nBEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n    job_name        => 'CUSTOMER_APP.NIGHTLY_STATS',\n    job_type        => 'PLSQL_BLOCK',\n    job_action      => q'[BEGIN DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'CUSTOMER_APP', options => 'GATHER STALE'); END;]',\n    start_date      => SYSTIMESTAMP,\n    repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=30; BYSECOND=0',\n    enabled         => FALSE,\n    auto_drop       => FALSE,\n    comments        => 'Nightly stale statistics gathering');\n  DBMS_SCHEDULER.SET_ATTRIBUTE('CUSTOMER_APP.NIGHTLY_STATS', 'max_run_duration', INTERVAL '2' HOUR);\n  DBMS_SCHEDULER.SET_ATTRIBUTE('CUSTOMER_APP.NIGHTLY_STATS', 'logging_level', DBMS_SCHEDULER.LOGGING_FULL);\n  DBMS_SCHEDULER.ENABLE('CUSTOMER_APP.NIGHTLY_STATS');\nEND;\n/\n\n-- 2) Validate the calendar expression (next run time):\nSET SERVEROUTPUT ON\nDECLARE\n  l_next TIMESTAMP WITH TIME ZONE;\nBEGIN\n  DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING(\n    calendar_string   => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=30; BYSECOND=0',\n    start_date        => SYSTIMESTAMP,\n    return_date_after => SYSTIMESTAMP,\n    next_run_date     => l_next);\n  DBMS_OUTPUT.PUT_LINE('Next run: ' || l_next);\nEND;\n/\n\n-- 3) Run now in the background (does not affect the schedule):\nEXEC DBMS_SCHEDULER.RUN_JOB('CUSTOMER_APP.NIGHTLY_STATS', use_current_session => FALSE);\n\n-- 4) Status:\nSELECT owner, job_name, enabled, state, run_count, failure_count,\n       last_start_date, next_run_date\nFROM   dba_scheduler_jobs WHERE owner = 'CUSTOMER_APP';\n\n-- 5) Stop (if running) / disable / drop:\n-- EXEC DBMS_SCHEDULER.STOP_JOB('CUSTOMER_APP.NIGHTLY_STATS', force => FALSE);\nEXEC DBMS_SCHEDULER.DISABLE('CUSTOMER_APP.NIGHTLY_STATS', force => TRUE);\nEXEC DBMS_SCHEDULER.DROP_JOB('CUSTOMER_APP.NIGHTLY_STATS', force => TRUE);","what":"Definiert mit DBMS_SCHEDULER einen geplanten PL/SQL-Job; setzt max_run_duration (löst bei Überschreitung ein Event aus) und eine detaillierte Logging-Stufe, validiert den Kalenderausdruck vorab und erlaubt einen manuellen Lauf, ohne den Zeitplan zu stören.","when":"Um wiederkehrende Datenbankarbeiten wie Wartung, ETL und Bereinigung statt per cron innerhalb der Datenbank auszuführen, mit Berücksichtigung von RAC/Data Guard.","caution":"Keine Zusatzlizenz. Die Zeitzone von start_date ist wichtig: SYSTIMESTAMP nutzt die TZ des Datenbankservers; verwende eine explizite Region (z. B. 'Europe/Istanbul'), um Verschiebungen bei der Zeitumstellung zu vermeiden. Bei JOB_QUEUE_PROCESSES=0 läuft kein Job. DISABLE mit force=>TRUE stoppt eine laufende Ausführung nicht, sondern verhindert nur künftige Läufe; zum Abbrechen STOP_JOB verwenden. Einen Job in einem anderen Schema anzulegen erfordert CREATE ANY JOB.","tags":["dbms_scheduler","job","scheduled job","create_job","run_job","repeat_interval","disable job","scheduler"]},{"id":"scheduler-job-history","task":"Laufhistorie und Fehler von Scheduler-Jobs überwachen","category":"Scheduler","command":"-- 1) Recent runs: duration, CPU, error code and message\nSELECT owner, job_name, status, error#,\n       TO_CHAR(actual_start_date,'YYYY-MM-DD HH24:MI:SS') AS started,\n       run_duration, cpu_used,\n       SUBSTR(additional_info, 1, 200) AS info\nFROM   dba_scheduler_job_run_details\nWHERE  owner = 'CUSTOMER_APP'\nORDER  BY actual_start_date DESC\nFETCH FIRST 20 ROWS ONLY;\n\n-- 2) Failures in the last 24 hours (whole DB):\nSELECT owner, job_name, error#, actual_start_date, SUBSTR(errors, 1, 300) AS errors\nFROM   dba_scheduler_job_run_details\nWHERE  status <> 'SUCCEEDED'\n  AND  log_date > SYSTIMESTAMP - INTERVAL '1' DAY\nORDER  BY log_date DESC;\n\n-- 3) Currently running:\nSELECT owner, job_name, session_id, running_instance, elapsed_time, cpu_used\nFROM   dba_scheduler_running_jobs;\n\n-- 4) Lifecycle events (ENABLE/DISABLE/RUN/STOP...):\nSELECT log_date, owner, job_name, operation, status\nFROM   dba_scheduler_job_log\nWHERE  owner = 'CUSTOMER_APP'\nORDER  BY log_date DESC\nFETCH FIRST 20 ROWS ONLY;\n\n-- Log retention (days): EXEC DBMS_SCHEDULER.SET_SCHEDULER_ATTRIBUTE('log_history', '30');","what":"Listet aus den Scheduler-Log-Views Startzeit, Dauer, CPU-Nutzung, Status und Fehlermeldung jedes Laufs sowie laufende Jobs und Lifecycle-Events der Jobs.","when":"Wenn ein nächtlicher Job nicht oder zu lange lief, für das SLA-Tracking von Jobs und beim Definieren von Fehlerregeln in einem Monitoring-System (OEM/Zabbix/Prometheus).","caution":"Nur lesend; keine Lizenz erforderlich. Die Log-Aufbewahrung beträgt standardmäßig 30 Tage, und Jobs mit logging_level OFF erzeugen keine Einträge. Die Spalte ERRORS gibt es ab 12c; in älteren Releases ADDITIONAL_INFO prüfen. In RAC zeigt RUNNING_INSTANCE, welcher Knoten den Job ausgeführt hat.","tags":["scheduler","job history","dba_scheduler_job_run_details","failed job","dba_scheduler_running_jobs","job log","monitoring"]},{"id":"resource-plan-basic","task":"Einfachen Resource-Manager-Plan anlegen und aktivieren","category":"Scheduler","command":"-- 1) Define groups, plan, directives and mapping inside a pending area (PDB or non-CDB):\nBEGIN\n  DBMS_RESOURCE_MANAGER.CLEAR_PENDING_AREA;\n  DBMS_RESOURCE_MANAGER.CREATE_PENDING_AREA;\n\n  DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP('OLTP_GRP',  'Online transactions');\n  DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP('REPORT_GRP', 'Reporting / batch');\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN('DAYTIME_PLAN', 'Business hours plan');\n\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(\n    plan => 'DAYTIME_PLAN', group_or_subplan => 'OLTP_GRP',\n    comment => 'OLTP first', mgmt_p1 => 70);\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(\n    plan => 'DAYTIME_PLAN', group_or_subplan => 'REPORT_GRP',\n    comment => 'Reports limited', mgmt_p1 => 20,\n    parallel_degree_limit_p1 => 4,\n    switch_group => 'CANCEL_SQL', switch_time => 1800, switch_for_call => TRUE);\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(\n    plan => 'DAYTIME_PLAN', group_or_subplan => 'OTHER_GROUPS',\n    comment => 'Everyone else (mandatory)', mgmt_p1 => 10);\n\n  DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(\n    attribute      => DBMS_RESOURCE_MANAGER.ORACLE_USER,\n    value          => 'REPORT_USER',\n    consumer_group => 'REPORT_GRP');\n\n  DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA;\n  DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA;\nEND;\n/\n-- Switch privilege required for the mapping to take effect:\nEXEC DBMS_RESOURCE_MANAGER_PRIVS.GRANT_SWITCH_CONSUMER_GROUP('REPORT_USER', 'REPORT_GRP', FALSE);\n\n-- 2) Activate (FORCE: prevents maintenance windows from switching the plan):\nALTER SYSTEM SET resource_manager_plan = 'FORCE:DAYTIME_PLAN' SCOPE = BOTH;\n\n-- 3) Monitor:\nSELECT sid, username, resource_consumer_group FROM v$session WHERE username IS NOT NULL;\nSELECT name, active_sessions, execution_waiters, consumed_cpu_time, cpu_wait_time\nFROM   v$rsrc_consumer_group;\n\n-- 4) Roll back: deactivate the plan, then delete it\n-- ALTER SYSTEM SET resource_manager_plan = '' SCOPE = BOTH;\n-- BEGIN DBMS_RESOURCE_MANAGER.CREATE_PENDING_AREA;\n--       DBMS_RESOURCE_MANAGER.DELETE_PLAN_CASCADE('DAYTIME_PLAN');\n--       DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA; END;\n-- /","what":"Legt fest, welche Workload bei CPU-Konkurrenz gewinnt: gibt der OLTP-Gruppe 70 % und der Report-Gruppe 20 % der CPU, begrenzt die Parallelität in der Report-Gruppe auf 4 und bricht Aufrufe über 30 Minuten ab (CANCEL_SQL). Benutzer werden automatisch Consumer Groups zugeordnet.","when":"Für gemischte Workloads, bei denen Reports tagsüber OLTP verlangsamen, um unkontrollierte parallele Abfragen zu deckeln und um ausufernde Abfragen automatisch abzubrechen.","caution":"Resource Manager erfordert die Enterprise Edition (in SE2 stark eingeschränkt); keine Zusatzlizenz. Die mgmt_p1-Prozente gelten nur bei CPU-Konkurrenz und dürfen auf einer Ebene zusammen 100 % nicht überschreiten. CANCEL_SQL beendet die laufende Anweisung mit ORA-00040; eine falsche Zuordnung kann kritische Arbeit abbrechen, daher Schwellenwerte sorgfältig testen. Solange ein Plan aktiv ist, zeigt sich CPU-Drosselung als Wait Event 'resmgr:cpu quantum'. Für die Aufteilung zwischen PDBs auf CDB-Ebene CREATE_CDB_PLAN verwenden.","tags":["resource manager","dbms_resource_manager","resource plan","consumer group","cpu priority","cancel_sql","resmgr:cpu quantum","resource management"]}]},"recipes/postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"recipes":[{"id":"wal-switch","task":"WAL-Segment wechseln","category":"WAL & Checkpoint","command":"SELECT pg_switch_wal();","what":"Schließt das aktuelle WAL-Segment und wechselt zu einem neuen; stößt die Archivierung an.","when":"Um eine Archivdatei zu erzeugen oder WAL nach einem Backup herauszuschreiben.","caution":"Vor PostgreSQL 10 hieß es pg_switch_xlog().","since":"PostgreSQL 10","note":"Diese Funktion wurde in PostgreSQL 10 in pg_switch_wal() umbenannt; davor hieß sie pg_switch_xlog() (WAL hieß früher 'xlog').","tags":["wal","switch","checkpoint","archive"]},{"id":"manual-checkpoint","task":"Manuellen CHECKPOINT ausführen","category":"WAL & Checkpoint","command":"CHECKPOINT;","what":"Schreibt Dirty Buffers auf die Platte und erzeugt einen Checkpoint-Eintrag; verschiebt den Startpunkt der Recovery nach vorn.","when":"Vor einem Backup, in einem Wartungsfenster oder um vor dem Stoppen des Servers auf die Platte zu synchronisieren.","caution":"Kann viel I/O erzeugen; in der Produktion nicht zu Spitzenzeiten ausführen. Nur ein Superuser oder (ab PG15) die Rolle pg_checkpoint darf es ausführen.","tags":["checkpoint","wal","io","flush"]},{"id":"wal-status-size","task":"WAL-Größe und aktuelle LSN anzeigen","category":"WAL & Checkpoint","command":"SELECT pg_current_wal_lsn() AS current_lsn, pg_walfile_name(pg_current_wal_lsn()) AS current_wal_file, pg_size_pretty(sum(size)) AS wal_dir_size FROM pg_ls_waldir();","what":"Liefert die aktuelle WAL-LSN, den Namen der gerade geschriebenen WAL-Datei und die Gesamtgröße des Verzeichnisses pg_wal.","when":"Um WAL-Anhäufung, Plattennutzung und Checkpoint-Abstände zu überwachen.","caution":"pg_ls_waldir und pg_walfile_name gibt es ab PostgreSQL 10; der Zugriff erfordert eventuell die Rolle pg_monitor.","tags":["wal","lsn","size","monitor"]},{"id":"archive-lag","task":"Archiv-Rückstand anzeigen (noch nicht archiviertes WAL)","category":"WAL & Checkpoint","command":"SELECT last_archived_wal, last_failed_wal, archived_count, failed_count, pg_walfile_name(pg_current_wal_lsn()) AS current_wal FROM pg_stat_archiver;","what":"Zeigt den Archiv-Rückstand durch Vergleich der zuletzt archivierten WAL-Datei, des gerade geschriebenen WAL und der Fehlerzähler der Archivierung.","when":"Um zu überwachen, ob archive_command zurückfällt, und um Archivierungsfehler zu erkennen.","caution":"Steigt failed_count weiter, schlägt archive_command fehl und pg_wal kann volllaufen; handle sofort, damit die Platte nicht überläuft.","tags":["archive","wal","lag","monitor"]},{"id":"create-tablespace","task":"Neuen Tablespace hinzufügen","category":"Speicher","command":"CREATE TABLESPACE ssd_ts OWNER postgres LOCATION '/mnt/ssd/pgdata';","what":"Definiert im angegebenen Verzeichnis einen neuen Tablespace; Tabellen und Indizes können auf dieser Platte abgelegt werden.","when":"Um Daten auf eine andere Platte (z. B. schnelle SSD) zu verteilen und I/O zu trennen.","caution":"Das Verzeichnis muss bereits existieren, leer sein und dem Benutzer postgres gehören. Verteile Daten nicht per symbolischen Links auf andere Platten; nutze den eigenen Tablespace-Mechanismus von PostgreSQL.","tags":["tablespace","storage","disk","create"]},{"id":"move-table-tablespace","task":"Tabelle in einen anderen Tablespace verschieben","category":"Speicher","command":"ALTER TABLE customer SET TABLESPACE ssd_ts;","what":"Verschiebt alle Daten der Tabelle physisch in den neuen Tablespace (Platte).","when":"Um eine Tabelle auf eine schnellere/separate Platte zu verschieben.","caution":"Während des Verschiebens wird eine ACCESS-EXCLUSIVE-Sperre auf die Tabelle genommen; die Tabelle ist bis zum Abschluss nicht zugänglich. Bei großen Tabellen in einem Wartungsfenster durchführen.","tags":["tablespace","table","move","storage"]},{"id":"move-index-tablespace","task":"Index in einen anderen Tablespace verschieben","category":"Speicher","command":"ALTER INDEX customer_email_idx SET TABLESPACE ssd_ts;","what":"Verschiebt den Index in den angegebenen Tablespace.","when":"Um Indizes getrennt von den Daten auf einer Platte abzulegen und I/O zu verteilen.","caution":"Während des Verschiebens wird eine ACCESS-EXCLUSIVE-Sperre genommen; der Index ist nicht nutzbar. ALTER TABLE ... SET TABLESPACE umfasst die Indizes der Tabelle nicht und kann sie daher nicht alle auf einmal verschieben; verschiebe jeden Index einzeln.","tags":["tablespace","index","move","storage"]},{"id":"db-table-size","task":"Datenbank- und Tabellengröße abfragen","category":"Speicher","command":"SELECT relname AS table_name, pg_size_pretty(pg_total_relation_size(relid)) AS total_size, pg_size_pretty(pg_relation_size(relid)) AS data_only FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;","what":"Listet die 20 größten Tabellen mit ihrer Gesamtgröße inklusive Indizes und TOAST.","when":"Um die Plattennutzung zu analysieren und herauszufinden, welche Tabellen Platz belegen.","caution":"Für die Größe der gesamten Datenbank: SELECT pg_size_pretty(pg_database_size(current_database()));","tags":["size","table","database","storage"]},{"id":"table-bloat","task":"Tabellen-Bloat schätzen","category":"Speicher","command":"SELECT schemaname, relname AS table_name, n_dead_tup AS dead_rows, n_live_tup AS live_rows, round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup > 0 ORDER BY n_dead_tup DESC LIMIT 20;","what":"Zeigt den Bloat-Anteil anhand der Anzahl toter Tupel und der letzten Autovacuum-Zeit.","when":"Um den VACUUM-Bedarf zu ermitteln und zu sehen, ob Autovacuum hinterherkommt.","caution":"n_dead_tup ist eine laufend aus DML-Zählern aktualisierte Schätzung (sinkt nach VACUUM; ANALYZE korrigiert nur die Schätzung); für exakten physischen Bloat ist die Extension pgstattuple (pgstattuple('customer')) genauer.","tags":["bloat","dead-tuple","vacuum","storage"]},{"id":"vacuum-analyze-table","task":"VACUUM ANALYZE für eine bestimmte Tabelle","category":"Wartung","command":"VACUUM (ANALYZE, VERBOSE) customer;","what":"Bereinigt tote Tupel in der Tabelle, macht den Platz wiederverwendbar und aktualisiert die Planner-Statistiken.","when":"Nach Massen-DELETE/-UPDATE oder wenn sich Abfragepläne verschlechtern.","caution":"VACUUM gibt keinen Plattenplatz an das OS zurück; dafür braucht es VACUUM FULL, das eine ACCESS-EXCLUSIVE-Sperre nimmt. Einfaches VACUUM blockiert die Tabelle nicht.","tags":["vacuum","analyze","maintenance","cleanup"]},{"id":"vacuum-freeze","task":"Aggressives VACUUM FREEZE ausführen","category":"Wartung","command":"VACUUM (FREEZE, VERBOSE, ANALYZE) customer;","what":"Markiert sofort alle infrage kommenden Tupel der Tabelle als 'frozen'; verringert das Risiko eines Transaction-ID-Wraparounds.","when":"Bei einer Wraparound-Warnung oder vorbeugend auf großen Tabellen, die archiviert/schreibgeschützt werden.","caution":"Erzeugt viel I/O, da die gesamte Tabelle gescannt wird. In einem Wraparound-Notfall kann die Datenbank faktisch schreibgeschützt werden; überwache datfrozenxid vorher.","tags":["vacuum","freeze","wraparound","maintenance"]},{"id":"reindex-concurrently","task":"Index ohne Sperre neu aufbauen (REINDEX CONCURRENTLY)","category":"Wartung","command":"REINDEX INDEX CONCURRENTLY customer_email_idx;","what":"Baut den Index neu auf, ohne Schreibzugriffe zu blockieren; beseitigt Index-Bloat.","when":"Wenn ein Index angewachsen/beschädigt ist, auf einem Live-System ohne Wartungsfenster.","caution":"Erfordert PostgreSQL 12+. Schlägt es fehl, kann ein INVALID-Index mit dem Suffix _ccnew zurückbleiben; prüfe pg_index.indisvalid und räume bei Bedarf mit DROP INDEX auf. Um die ganze Tabelle neu aufzubauen: REINDEX TABLE CONCURRENTLY customer;","since":"PostgreSQL 12","note":"REINDEX ... CONCURRENTLY kam mit PostgreSQL 12; davor sperrte REINDEX den Index mit einer ACCESS-EXCLUSIVE-Sperre (ein sperrfreier Neuaufbau erforderte CREATE INDEX CONCURRENTLY + DROP).","tags":["reindex","concurrently","index","maintenance"]},{"id":"autovacuum-per-table","task":"Autovacuum für eine bestimmte Tabelle einstellen","category":"Wartung","command":"ALTER TABLE customer SET (autovacuum_vacuum_scale_factor = 0.02, autovacuum_vacuum_threshold = 5000, autovacuum_analyze_scale_factor = 0.01);","what":"Senkt die Auslöseschwellen von Autovacuum nur für diese Tabelle, sodass große/stark genutzte Tabellen häufiger bereinigt werden.","when":"Wenn Autovacuum bei großen Tabellen mit viel UPDATE-/DELETE-Aktivität hinterherhinkt.","caution":"Zu aggressive Einstellungen können zu ständigen Autovacuum-Läufen und I/O-Last führen. Um die Einstellung zurückzusetzen: ALTER TABLE customer RESET (autovacuum_vacuum_scale_factor);","tags":["autovacuum","tuning","table","maintenance"]},{"id":"statistics-target","task":"Statistikziel einer Spalte erhöhen","category":"Wartung","command":"ALTER TABLE customer ALTER COLUMN city SET STATISTICS 500; ANALYZE customer;","what":"Erhöht den Detailgrad von Histogramm/Stichprobe für eine bestimmte Spalte; verbessert die Schätzungen des Planners.","when":"Wenn bei Spalten mit schiefer Verteilung schlechte Abfragepläne auftreten.","caution":"Ein hoher Wert (Standard 100, max. 10000) erhöht die ANALYZE- und die Planungszeit. Die Änderung wirkt erst nach ANALYZE.","tags":["statistics","planner","analyze","tuning"]},{"id":"alter-system-reload","task":"Parameter ändern und neu laden (ALTER SYSTEM)","category":"Parameter","command":"ALTER SYSTEM SET work_mem = '64MB'; SELECT pg_reload_conf();","what":"Schreibt den Parameter in postgresql.auto.conf und lädt die Konfiguration neu (sofort wirksam für Parameter, die keinen Neustart erfordern).","when":"Um per Reload anwendbare Parameter wie work_mem und Logging-Einstellungen dauerhaft zu ändern.","caution":"Einige Parameter wie shared_buffers und max_connections wirken erst nach einem Neustart, nicht nach einem Reload; solche mit pg_settings.context = 'postmaster' erfordern einen Neustart.","tags":["alter-system","reload","parameter","config"]},{"id":"session-set","task":"Parameter für die Session ändern (SET)","category":"Parameter","command":"SET work_mem = '256MB';","what":"Ändert den Parameter nur für die aktuelle Verbindung/Session; er wird beim Ende der Session zurückgesetzt.","when":"Wenn eine Einstellung für eine einzelne schwere Abfrage oder einen Batchjob vorübergehend erhöht werden muss.","caution":"Mit SET LOCAL auf eine einzelne Transaktion beschränken. Eine globale Änderung erfordert ALTER SYSTEM.","tags":["set","session","parameter","work-mem"]},{"id":"show-setting","task":"Aktuellen Parameterwert anzeigen","category":"Parameter","command":"SELECT name, setting, unit, context, source FROM pg_settings WHERE name = 'shared_buffers';","what":"Zeigt den wirksamen Wert des Parameters, seine Einheit, wie er angewendet werden kann (Context) und seine Quelle.","when":"Um den laufenden Wert eines Parameters zu ermitteln und ob er einen Neustart oder einen Reload erfordert.","caution":"Kurzform: SHOW shared_buffers; oder SELECT current_setting('shared_buffers'); Ist die Spalte context 'postmaster', ist ein Neustart nötig.","tags":["show","pg_settings","parameter","current-setting"]},{"id":"physical-replica-basebackup","task":"Physische Replica aufbauen (pg_basebackup)","category":"Replikation","command":"pg_basebackup -h primary_host -U replicator -D /var/lib/postgresql/16/main -Fp -Xs -P -R --slot=replica_slot_1 -C","what":"Erstellt ein vollständiges physisches Backup von der Primary, streamt WAL (-Xs), schreibt standby.signal und primary_conninfo (-R) und legt den Replication Slot an (-C).","when":"Um einen neuen Streaming-Standby aufzubauen (Read Replica oder Failover-Ziel).","caution":"Das Zielverzeichnis (-D) muss leer sein. Auf der Primary muss pg_hba.conf dem Benutzer replicator 'replication'-Zugriff gewähren, und es muss ausreichend wal_keep_size bzw. ein Slot vorhanden sein. Ab PostgreSQL 12 erzeugt -R standby.signal statt recovery.conf.","tags":["replication","basebackup","standby","physical"]},{"id":"promote-replica","task":"Replica zur Primary hochstufen (Promote)","category":"Replikation","command":"SELECT pg_promote(wait := true, wait_seconds := 60);","what":"Holt den Standby-Server aus dem Recovery-Modus und macht ihn zu einer beschreibbaren Primary.","when":"Um den Standby bei einem geplanten oder Notfall-Failover zur neuen Primary zu machen.","caution":"Das Promote lässt sich nicht rückgängig machen; der Server kann der alten Primary nicht mehr folgen. Stelle sicher, dass die alte Primary wirklich ausgefallen ist, um Split-Brain zu verhindern. Alternative: pg_ctl promote.","since":"PostgreSQL 12","note":"Die SQL-Funktion pg_promote() kam mit PostgreSQL 12; davor wurde ein Standby per Trigger-Datei (promote_trigger_file) oder pg_ctl promote hochgestuft.","tags":["replication","promote","failover","standby"]},{"id":"replication-lag","task":"Replikationsverzögerung anzeigen","category":"Replikation","command":"SELECT client_addr, state, sent_lsn, replay_lsn, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes, write_lag, flush_lag, replay_lag FROM pg_stat_replication;","what":"Zeigt auf der Primary für jeden Standby den Replay-Abstand in Bytes zur aktuellen WAL-Position der Primary sowie Write-/Flush-/Replay-Lag.","when":"Um zu überwachen, wie weit die Streaming-Replikation zurückliegt (auf der Primary ausführen).","caution":"Für die Verzögerung in Sekunden auf dem Standby: SELECT now() - pg_last_xact_replay_timestamp(); dieser Wert wächst auch, wenn auf der Primary nicht geschrieben wird, daher zusammen mit dem LSN-Abstand interpretieren. Ein leeres Ergebnis bedeutet, dass kein Standby verbunden ist.","tags":["replication","lag","lsn","monitor"]},{"id":"replication-slot","task":"Replication Slot anlegen und löschen","category":"Replikation","command":"SELECT pg_create_physical_replication_slot('replica_slot_1'); -- To drop: SELECT pg_drop_replication_slot('replica_slot_1');","what":"Legt einen physischen Slot an, der garantiert, dass die Primary benötigtes WAL erst entfernt, wenn der Standby es bestätigt hat; der zweite Befehl entfernt den Slot.","when":"Um sicherzustellen, dass der Standby bei kurzen Ausfällen kein WAL verliert.","caution":"Ein ungenutzter/verwaister Slot führt zu WAL-Anhäufung und voller Platte. Überwache den Slot-Status: SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained FROM pg_replication_slots; Ein aktiver Slot lässt sich nicht löschen.","tags":["replication","slot","wal","cleanup"]},{"id":"logical-replication","task":"Publication und Subscription für logische Replikation einrichten","category":"Replikation","command":"-- Source (publisher): CREATE PUBLICATION pub_customer FOR TABLE customer; -- Target (subscriber): CREATE SUBSCRIPTION sub_customer CONNECTION 'host=primary_host dbname=appdb user=replicator password=secret' PUBLICATION pub_customer;","what":"Startet logische Replikation auf Tabellenebene, indem auf der Quelle eine Publication für bestimmte Tabellen und auf dem Ziel eine Subscription angelegt wird, die sich damit verbindet.","when":"Für Versionsupgrades, die Replikation ausgewählter Tabellen auf einen anderen Server oder eine Datenverteilung in eine Richtung.","caution":"Erfordert PostgreSQL 10+. Das Schema der Zieltabellen muss bereits existieren; die Subscription führt das initiale COPY automatisch aus. wal_level muss logical sein (erfordert einen Neustart). DDL-Änderungen und Sequence-Werte werden nicht repliziert; Schemaänderungen auf beiden Seiten separat anwenden und Sequences bei der Umstellung mit setval synchronisieren.","since":"PostgreSQL 10","note":"Die integrierte logische Replikation (CREATE PUBLICATION / CREATE SUBSCRIPTION) kam mit PostgreSQL 10; davor waren für dieselbe Aufgabe externe Extensions wie pglogical nötig.","tags":["logical","replication","publication","subscription"]},{"id":"terminate-backend","task":"Backend beenden (pg_terminate_backend)","category":"Sessions","command":"SELECT pg_terminate_backend(12345);","what":"Beendet den Backend-Prozess mit der angegebenen PID zwangsweise; trennt die Verbindung und bricht die laufende Transaktion ab.","when":"Um eine hängende Verbindung oder eine Session, die eine Sperre hält, zu schließen.","caution":"Die laufende Transaktion wird zurückgerollt; nicht committete Arbeit geht verloren. Versuche zuerst das sanftere pg_cancel_backend. Prüfe die PID in pg_stat_activity.","tags":["terminate","backend","session","kill"]},{"id":"cancel-backend","task":"Laufende Abfrage abbrechen (pg_cancel_backend)","category":"Sessions","command":"SELECT pg_cancel_backend(12345);","what":"Bricht die gerade auf der angegebenen PID laufende Abfrage ab; die Verbindung bleibt offen.","when":"Um eine lang laufende Abfrage zu stoppen, ohne die Verbindung zu trennen.","caution":"Bricht nur eine aktive Abfrage ab; eine Session im Zustand idle in transaction wird nicht geschlossen, dafür ist pg_terminate_backend nötig.","tags":["cancel","backend","query","session"]},{"id":"active-blocking-queries","task":"Aktive und blockierende Abfragen anzeigen","category":"Sessions","command":"SELECT pid, usename, state, wait_event_type, wait_event, pg_blocking_pids(pid) AS blocking_pids, now() - query_start AS duration, left(query, 100) AS query FROM pg_stat_activity WHERE state <> 'idle' AND pid <> pg_backend_pid() ORDER BY duration DESC;","what":"Listet alle aktiven (nicht idle) Backends, welche PIDs sie blockieren und wie lange sie schon laufen.","when":"Um Sperr-/Blockierungsprobleme und lang laufende Abfragen zu diagnostizieren.","caution":"pg_blocking_pids gibt es ab PostgreSQL 9.6. Ist das Array blocking_pids nicht leer, halten diese PIDs die Sperre; brich diese PID bei Bedarf ab bzw. beende sie.","since":"PostgreSQL 9.6","note":"Die Funktion pg_blocking_pids() kam mit PostgreSQL 9.6; davor mussten blockierende Prozesse durch manuelles Joinen von pg_locks gefunden werden.","tags":["activity","blocking","lock","session"]},{"id":"create-extension","task":"Extension hinzufügen","category":"Extensions","command":"CREATE EXTENSION IF NOT EXISTS pg_stat_statements;","what":"Installiert die angegebene Extension in der aktuellen Datenbank (fügt Funktionen, Views, Typen usw. hinzu).","when":"Wenn zusätzliche Funktionen wie pg_stat_statements, pgcrypto oder postgis benötigt werden.","caution":"Für Extensions, die shared_preload_libraries erfordern, etwa pg_stat_statements, trage sie zuerst in postgresql.conf ein und starte den Server neu. Die Extension-Dateien (contrib-Paket) müssen auf dem Server installiert sein.","tags":["extension","create","pg_stat_statements","plugin"]},{"id":"list-extensions","task":"Installierte Extensions auflisten","category":"Extensions","command":"SELECT extname AS extension, extversion AS version FROM pg_extension ORDER BY extname;","what":"Listet alle in der aktuellen Datenbank installierten Extensions und ihre Versionen.","when":"Um zu sehen, welche Extensions installiert sind und ob ein Update nötig ist.","caution":"Um installierbare (noch nicht installierte) Extensions zu sehen: SELECT * FROM pg_available_extensions; Upgrade: ALTER EXTENSION pg_stat_statements UPDATE;","tags":["extension","list","pg_extension","plugin"]}]},"recipes/postgres-ha.json":{"engineId":"postgres","engine":"PostgreSQL","order":1,"recipes":[{"id":"patroni-cluster-status","task":"Status, Topologie und Historie des Patroni-Clusters anzeigen","category":"Patroni","command":"# Members, roles, state, timeline and lag (MB):\npatronictl -c /etc/patroni/patroni.yml list <cluster>\n# Extended info (pending restart, tags) + timestamp, refresh every 5 s:\npatronictl -c /etc/patroni/patroni.yml list <cluster> -e -t -w 5\n# Leader -> replica tree (including cascading replicas):\npatronictl -c /etc/patroni/patroni.yml topology <cluster>\n# Past failovers/switchovers (timeline, LSN, reason, time):\npatronictl -c /etc/patroni/patroni.yml history <cluster>","what":"Zeigt die Cluster-Sicht von Patroni aus dem DCS: Leader-/Replica-Rollen, Zustand der Mitglieder (running/streaming), Timeline, Replikationsverzögerung und ausstehende Neustarts; history listet Timeline-Wechsel mit der LSN, bei der sie stattfanden.","when":"Vor und nach jedem Switchover/Failover/jeder Wartung und als erster Diagnoseschritt bei einem Alarm.","caution":"Die Ausgabe spiegelt wider, was im DCS gespeichert ist; ist das DCS nicht erreichbar, schlägt der Befehl fehl oder zeigt eventuell veraltete Daten. Starte keinen Switchover, bevor bestätigt ist, dass alle Mitglieder auf derselben Timeline sind und Lag in MB unter maximum_lag_on_failover liegt.","untested":true,"tags":["patroni","cluster status","topology","history","lag"]},{"id":"patroni-switchover","task":"Geplanten Switchover mit Patroni durchführen","category":"Patroni","command":"# Immediate switchover without confirmation:\npatronictl -c /etc/patroni/patroni.yml switchover <cluster> --leader <leader_member> --candidate <member> --force\n# Scheduled switchover (ISO 8601 timestamp with time zone):\npatronictl -c /etc/patroni/patroni.yml switchover <cluster> --leader <leader_member> --candidate <member> --scheduled '2026-10-10T02:00:00+03:00' --force\n# Cancel a pending scheduled switchover:\npatronictl -c /etc/patroni/patroni.yml flush <cluster> switchover","what":"Stuft den gesunden Leader kontrolliert herab und befördert die gewählte Replica (--candidate); der bisherige Leader tritt automatisch als Replica des neuen Leaders wieder bei. --scheduled verschiebt den Vorgang auf den angegebenen Zeitpunkt.","when":"Um die Primary vor einer Wartung (OS-Patching, Hardwaretausch, Minor-Version-Upgrade) auf einen anderen Knoten zu verlegen oder nach einem Failover die ursprünglichen Rollen wiederherzustellen.","caution":"Beim Leader-Wechsel brechen alle Verbindungen ab; die Anwendung muss sich neu verbinden können. Patroni lehnt den Switchover ab, wenn der Lag des Kandidaten maximum_lag_on_failover übersteigt. Bei aktiviertem synchronous_mode muss der Kandidat ein synchroner Standby sein.","note":"In älteren Patroni-Releases hieß die Option --leader noch --master; Patroni 4 hat --master entfernt. --primary ist gleichbedeutend mit --leader.","untested":true,"tags":["patroni","switchover","planned switchover","maintenance","leader"]},{"id":"patroni-failover","task":"Manuellen Failover mit Patroni durchführen","category":"Patroni","command":"# Manual failover while the leader is unhealthy (candidate is mandatory):\npatronictl -c /etc/patroni/patroni.yml failover <cluster> --candidate <member> --force\n# Verify the result and the new timeline:\npatronictl -c /etc/patroni/patroni.yml list <cluster>\npatronictl -c /etc/patroni/patroni.yml history <cluster>","what":"Befördert die angegebene Replica unabhängig vom Zustand des Leaders; gedacht für Fälle, in denen der automatische Failover nicht stattfand oder den falschen Knoten wählte.","when":"Wenn der Leader nicht erreichbar/fehlerhaft ist und der automatische Failover nicht ausgelöst wurde (z. B. im Pause-Modus), oder wenn du einen Wechsel auf einen bestimmten Knoten erzwingen musst.","caution":"Bei asynchroner Replikation gehen Transaktionen verloren, die der Kandidat noch nicht empfangen hat (RPO > 0). Ist der Leader gesund, nutze Switchover statt Failover. Kehrt der alte Leader zurück, braucht er eventuell pg_rewind (wenn use_pg_rewind aktiviert ist) oder ein Reinit.","note":"Ab Patroni 3.2 ist --candidate für failover Pflicht; die Option --leader/--primary von failover wurde in 3.2 als veraltet markiert und in Patroni 4.0 entfernt. Nutze switchover, wenn du den Leader angeben musst.","untested":true,"tags":["patroni","failover","manual failover","promote","rpo"]},{"id":"patroni-reinit-replica","task":"Defekte Replica mit Patroni neu aufbauen (reinit)","category":"Patroni","command":"# Wipe the replica's PGDATA and rebuild it (create_replica_methods: pgbackrest/basebackup):\npatronictl -c /etc/patroni/patroni.yml reinit <cluster> <member> --wait --force\n# Take the basebackup directly from the leader instead of the backup repository:\npatronictl -c /etc/patroni/patroni.yml reinit <cluster> <member> --from-leader --wait --force","what":"Löscht das Datenverzeichnis der angegebenen Replica und baut es gemäß den create_replica_methods von Patroni (pgBackRest-Restore oder pg_basebackup) komplett neu auf.","when":"Wenn eine Replica wegen eines Timeline-Konflikts, eines fehlgeschlagenen pg_rewind, fehlenden WAL oder Datenkorruption nicht wieder in die Replikation einsteigen kann.","caution":"Alle lokalen Daten des Mitglieds werden gelöscht; prüfe genau, ob der Befehl das richtige Mitglied trifft (Patroni verweigert reinit auf dem Leader). Bei großen Datenbanken kann das Stunden dauern und erzeugt I/O-/Netzwerklast auf dem Leader; der Cluster läuft so lange mit einer Replica weniger. --from-leader gibt es nur in neueren Patroni-4.x-Releases (mit patronictl reinit --help prüfen); es überspringt create_replica_methods und zieht ein pg_basebackup vom Leader.","untested":true,"tags":["patroni","reinit","rebuild replica","rebuild","basebackup"]},{"id":"patroni-edit-config","task":"Clusterweite Parameter in Patroni ändern (edit-config) und ausstehende Neustarts anwenden","category":"Patroni","command":"# Show the current dynamic configuration stored in the DCS:\npatronictl -c /etc/patroni/patroni.yml show-config <cluster>\n# Cluster-wide PostgreSQL parameter (distributed to all members):\npatronictl -c /etc/patroni/patroni.yml edit-config <cluster> -p 'work_mem=64MB' -p 'max_connections=500' --force\n# Patroni behavior parameter:\npatronictl -c /etc/patroni/patroni.yml edit-config <cluster> -s 'maximum_lag_on_failover=1048576' --force\n# Parameters requiring a restart are flagged as 'Pending restart':\npatronictl -c /etc/patroni/patroni.yml list <cluster> -e\n# Restart replicas first, then the leader:\npatronictl -c /etc/patroni/patroni.yml restart <cluster> --role replica --pending --force\npatronictl -c /etc/patroni/patroni.yml restart <cluster> --role leader --pending --force","what":"Schreibt PostgreSQL- und Patroni-Parameter in die dynamische Konfiguration im DCS; Patroni wendet die Änderung auf alle Mitglieder an, aktiviert per Reload änderbare sofort und kennzeichnet solche, die einen Neustart brauchen, als 'Pending restart'.","when":"Um Parameter wie max_connections, shared_buffers oder work_mem auf jedem Knoten eines von Patroni verwalteten Clusters einheitlich zu ändern.","caution":"Bearbeite unter Patroni postgresql.conf nicht manuell und nutze kein ALTER SYSTEM: Patroni schreibt postgresql.conf neu, und postgresql.auto.conf überschreibt die Einstellungen von Patroni, was zu Abweichungen zwischen den Knoten führt. max_connections, max_worker_processes, max_wal_senders, max_prepared_transactions und max_locks_per_transaction dürfen auf einem Standby nicht niedriger sein als auf der Primary; plane die Neustartreihenfolge entsprechend.","untested":true,"tags":["patroni","edit-config","parameter","pending restart","dcs"]},{"id":"patroni-pause-resume","task":"Patroni in den Wartungsmodus versetzen und zurück (pause/resume)","category":"Patroni","command":"# Maintenance mode: automatic failover and Patroni's control over PostgreSQL stop\npatronictl -c /etc/patroni/patroni.yml pause <cluster> --wait\npatronictl -c /etc/patroni/patroni.yml list <cluster>          # should show 'Maintenance mode: on'\n# ... maintenance work (e.g. manual PostgreSQL restart, DCS maintenance) ...\npatronictl -c /etc/patroni/patroni.yml resume <cluster> --wait","what":"Versetzt den Cluster in den Wartungsmodus: Patroni führt keinen automatischen Failover aus und startet ein gestopptes PostgreSQL nicht neu; resume kehrt zur normalen Verwaltung zurück.","when":"Während der Wartung des DCS (etcd/Consul), bei Patroni-Upgrades oder wenn PostgreSQL manuell verwaltet werden muss, ohne dass Patroni eingreift.","caution":"Stürzt die Primary im Pause-Modus ab, gibt es keinen automatischen Failover und der Ausfall dauert an. Vergiss nach der Wartung resume nicht; nimm eine Prüfung auf 'Maintenance mode' in dein Alerting auf. Ein PostgreSQL im Pause-Modus manuell als Primary zu starten, kann Split-Brain verursachen.","untested":true,"tags":["patroni","pause","resume","maintenance mode","maintenance"]},{"id":"patroni-dcs-health","task":"Zustand von Patroni-DCS (etcd/Consul) und REST API prüfen","category":"Patroni","command":"# etcd v3 (default API in etcd 3.4+): member health, leader, raft term and DB size\netcdctl --endpoints=https://<etcd1>:2379,https://<etcd2>:2379,https://<etcd3>:2379 --cacert=/etc/etcd/ca.crt endpoint health --cluster -w table\netcdctl --endpoints=https://<etcd1>:2379 --cacert=/etc/etcd/ca.crt endpoint status --cluster -w table\netcdctl --endpoints=https://<etcd1>:2379 --cacert=/etc/etcd/ca.crt alarm list\n# Consul:\nconsul members\nconsul operator raft list-peers\n# Patroni REST API (on each member):\ncurl -s http://<member>:8008/cluster | python3 -m json.tool\ncurl -s -o /dev/null -w '%{http_code}\\n' http://<member>:8008/primary   # 200 = leader, 503 = not leader","what":"Prüft Quorum, Leader und Kapazität des DCS, in dem Patroni den Leader-Lock hält, sowie über die Patroni REST API die Sicht jedes Mitglieds auf seine eigene Rolle. Der Endpunkt /primary ist die Grundlage für Health-Checks des Load Balancers (HAProxy).","when":"Wenn Patroni 'DCS is not accessible' protokolliert, nach einem unerwarteten Demote/Failover und als routinemäßiger Health-Check.","caution":"Verliert das DCS das Quorum, kann Patroni den Leader-Lock nicht erneuern und stuft die Primary auf read-only herab (außer failsafe_mode, ab Patroni 3.0, ist aktiviert); ein etcd-Ausfall ist daher ein Schreibausfall der Datenbank. Erreicht die Größe der etcd-DB ihre Quota (Standard 2 GB), stoppt ein NOSPACE-Alarm die Schreibvorgänge. Betreibe das DCS auf mindestens 3 Knoten mit getrennten Platten.","untested":true,"tags":["patroni","etcd","consul","dcs health","rest api"]},{"id":"pgbackrest-archive-config","task":"WAL-Archivierung für pgBackRest konfigurieren (archive-push/archive-get)","category":"Backup & PITR","command":"# postgresql.conf (changing archive_mode requires a restart):\nwal_level = replica\narchive_mode = on\narchive_command = 'pgbackrest --stanza=<stanza> archive-push %p'\n# On the restore/standby side (in postgresql.conf on PG12+):\nrestore_command = 'pgbackrest --stanza=<stanza> archive-get %f \"%p\"'\n\n# /etc/pgbackrest/pgbackrest.conf (database host, local repo example):\n[global]\nrepo1-path=/var/lib/pgbackrest\nrepo1-retention-full=2\narchive-async=y\nspool-path=/var/spool/pgbackrest\nprocess-max=4\ncompress-type=zst\nstart-fast=y\n\n[<stanza>]\npg1-path=/var/lib/pgsql/16/data","what":"Lässt PostgreSQL jedes abgeschlossene WAL-Segment an das pgBackRest-Repository senden (archive-push) und es bei der Recovery von dort holen (archive-get); archive-async macht das Senden über ein Spool-Verzeichnis parallel und asynchron.","when":"Als erster Schritt beim Einrichten physischer Backups und PITR mit pgBackRest, vor stanza-create.","caution":"Solange archive_command fehlschlägt, sammelt sich WAL in pg_wal an, und die Primary stoppt, wenn die Platte voll ist; überwache pg_stat_archiver.failed_count. Setze diese Parameter bei Patroni über patroni.yml / edit-config statt in postgresql.conf. Archivieren zwei verschiedene Cluster in dieselbe Stanza, wird das Repository beschädigt.","untested":true,"tags":["pgbackrest","archiving","archive_command","wal archive","configuration"]},{"id":"pgbackrest-stanza-create-check","task":"pgBackRest-Stanza anlegen und Archivierung prüfen (stanza-create/check)","category":"Backup & PITR","command":"# Create the stanza in the repository (once):\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info stanza-create\n# Verify configuration and archiving end to end (switches WAL and waits for the segment to reach the repo):\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info check","what":"stanza-create legt die Metadaten des Clusters (System Identifier, Version) im Repository an; check testet die PostgreSQL-Verbindung, archive_command und das Eintreffen von WAL im Repository mit einem echten WAL-Switch.","when":"Bei der Ersteinrichtung, nach Änderung von archive_command oder des Repositorys und als Health-Check per Monitoring/cron.","caution":"Führe die Befehle als postgres (oder als Eigentümer des Repositorys) aus; als root ausgeführt, werden die Dateieigentümer zerstört. Nach einem Major-Version-Upgrade ist stanza-upgrade nötig, nicht stanza-create. Schlägt check fehl, ist PITR eventuell unmöglich, selbst wenn Backups durchlaufen.","untested":true,"tags":["pgbackrest","stanza","stanza-create","check","archive verification"]},{"id":"pgbackrest-backup-full-diff-incr","task":"Voll-, differenzielle und inkrementelle Backups mit pgBackRest erstellen","category":"Backup & PITR","command":"sudo -u postgres pgbackrest --stanza=<stanza> --type=full backup\nsudo -u postgres pgbackrest --stanza=<stanza> --type=diff backup   # changes since the last full\nsudo -u postgres pgbackrest --stanza=<stanza> --type=incr backup   # changes since the last backup of any type\n# cron example (postgres user): full on Sunday, diff on other days\n# 30 1 * * 0   pgbackrest --stanza=<stanza> --type=full backup\n# 30 1 * * 1-6 pgbackrest --stanza=<stanza> --type=diff backup","what":"Ein Vollbackup (full) kopiert den gesamten Cluster, diff kopiert seit dem letzten full geänderte Dateien, incr seit dem letzten Backup beliebigen Typs geänderte Dateien; am Ende wird geprüft, dass das benötigte WAL archiviert ist, und expire läuft automatisch gemäß Aufbewahrung.","when":"In einem regelmäßigen Backup-Zeitplan: typischerweise wöchentlich full + täglich diff/incr, bei großen Clustern häufiger incr.","caution":"Ohne vorheriges full werden diff-/incr-Anforderungen automatisch zu full hochgestuft. Eine längere incr-Kette erhöht Restore-Zeit und Abhängigkeiten; geht ein Backup der Kette verloren, sind spätere unbrauchbar. Um die Primary zu entlasten, sichere mit backup-standby=y von einem Standby (die Primary wird für die Koordination trotzdem gebraucht).","untested":true,"tags":["pgbackrest","backup","full","differential","incremental"]},{"id":"pgbackrest-info","task":"pgBackRest-Backup-Katalog und WAL-Archivbereich anzeigen (info)","category":"Backup & PITR","command":"sudo -u postgres pgbackrest --stanza=<stanza> info\n# JSON for monitoring/automation:\nsudo -u postgres pgbackrest --stanza=<stanza> --output=json info\n# Details of a single backup (databases, LSN range):\nsudo -u postgres pgbackrest --stanza=<stanza> --set=<backup_label> info","what":"Listet den Status der Stanza, für jedes Backup Label, Typ, Start-/Endzeit, Größe und das Backup, von dem es abhängt, sowie den Min-/Max-Bereich des WAL-Archivs im Repository.","when":"Vor einem Restore/PITR, um zu bestätigen, dass der Zielzeitpunkt abgedeckt ist, um zu überwachen, dass Backups regelmäßig laufen, und um die Aufbewahrung zu prüfen.","caution":"info zeigt, dass ein Backup existiert, nicht dass es wiederherstellbar ist; validiere mit regelmäßigen Test-Restores und pgbackrest verify. Gibt es eine Lücke im WAL-Min-/Max-Bereich, kann PITR sie nicht überbrücken.","untested":true,"tags":["pgbackrest","info","backup list","catalog","monitoring"]},{"id":"pgbackrest-restore-latest","task":"Letztes Backup mit pgBackRest wiederherstellen","category":"Backup & PITR","command":"# PostgreSQL must be stopped and PGDATA empty (otherwise use --delta)\nsudo systemctl stop postgresql-16\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info restore\n# From a specific backup: --set=<backup_label>\n# When restoring to another server for testing, disable archiving:\n# sudo -u postgres pgbackrest --stanza=<stanza> --archive-mode=off restore\nsudo systemctl start postgresql-16","what":"Stellt das letzte Backup wieder her und schreibt recovery.signal und restore_command; beim Start spielt PostgreSQL alles archivierte WAL bis zum jüngsten konsistenten Punkt ein und wird auf einer neuen Timeline zur Primary.","when":"Wenn das Datenverzeichnis der Primary verloren/beschädigt ist oder um regelmäßig zu testen, dass Backups wiederherstellbar sind.","caution":"Archivieren der wiederhergestellte und der alte Cluster gleichzeitig in dieselbe Stanza, wird das Repository beschädigt; nutze für Test-Restores --archive-mode=off. Unter Patroni zuerst den Cluster pausieren oder Patroni stoppen, sonst greift Patroni ein. Gibt es Tablespaces oder Pfade außerhalb von PGDATA, prüfe die Optionen --tablespace-map und link.","untested":true,"tags":["pgbackrest","restore","restore","recovery","disaster recovery"]},{"id":"pgbackrest-pitr-restore","task":"Mit pgBackRest auf einen Zeitpunkt wiederherstellen (PITR, --type=time)","category":"Backup & PITR","command":"sudo systemctl stop postgresql-16\nsudo -u postgres pgbackrest --stanza=<stanza> --delta --type=time \"--target=2026-10-08 14:30:00+03\" --target-action=pause restore\nsudo systemctl start postgresql-16\n# Recovery stops at the target (read-only); validate the data:\npsql -X -c \"SELECT pg_is_in_recovery(), pg_last_xact_replay_timestamp();\"\n# If correct, end recovery and open read-write on a new timeline:\npsql -X -c \"SELECT pg_wal_replay_resume();\"","what":"Stellt ein Backup wieder her, das vor dem Zielzeitpunkt fertig wurde, und spielt WAL nur bis zum Ziel ein; target-action=pause hält am Ziel an, damit du die Daten prüfen kannst, und pg_wal_replay_resume() führt das Promote aus.","when":"Um zum Moment unmittelbar vor einem logischen Fehler wie einem versehentlichen DELETE/DROP/UPDATE zurückzukehren.","caution":"Alle Änderungen nach dem Ziel gehen auf diesem Cluster verloren; führe PITR nach Möglichkeit auf einem separaten Server aus und übertrage nur die betroffenen Daten mit pg_dump zurück. Gib die Zeitzone im Ziel explizit an. pgBackRest ab 2.24 wählt automatisch ein passendes Backup vor dem Ziel; in älteren Releases gib mit --set ein Backup an, das vor dem Ziel fertig wurde. Liegt das Ziel vor der Endzeit des Backups, kann der Restore keine Konsistenz erreichen.","untested":true,"tags":["pgbackrest","pitr","point in time recovery","point in time","recovery"]},{"id":"pgbackrest-delta-restore-standby","task":"Standby mit pgBackRest-Delta-Restore schnell neu aufbauen","category":"Backup & PITR","command":"sudo systemctl stop postgresql-16\n# Keeps the existing PGDATA and fetches only differing files; writes standby.signal:\nsudo -u postgres pgbackrest --stanza=<stanza> --delta --type=standby \"--recovery-option=primary_conninfo=host=<primary_host> port=5432 user=replicator application_name=<standby_name>\" restore\nsudo systemctl start postgresql-16\npsql -X -c \"SELECT status, sender_host FROM pg_stat_wal_receiver;\"","what":"--delta vergleicht die Dateien im vorhandenen PGDATA mit dem Backup (Größe/Zeitstempel und Prüfsumme) und kopiert nur abweichende; --type=standby schreibt standby.signal, und recovery-option ergänzt primary_conninfo. Der Standby holt zuerst aus dem Archiv auf und wechselt dann zu Streaming.","when":"Um einen zurückliegenden oder auf der Timeline abgewichenen Standby oder eine alte Primary, bei der pg_rewind fehlschlug, ohne vollständige Kopie wieder zur Replica zu machen.","caution":"Delta ersetzt/entfernt abweichende oder überzählige Dateien im Zielverzeichnis durch die des Backups; auf dem falschen Server ausgeführt, zerstört es dessen Daten. pgBackRest verweigert einen Restore über ein laufendes PostgreSQL, prüfe aber selbst, dass der Dienst gestoppt ist. Lege das Passwort in .pgpass statt in primary_conninfo ab.","untested":true,"tags":["pgbackrest","delta restore","rebuild standby","rebuild","replica"]},{"id":"pgbackrest-retention-expire","task":"Aufbewahrung in pgBackRest konfigurieren und expire ausführen","category":"Backup & PITR","command":"# /etc/pgbackrest/pgbackrest.conf [global] - count based:\nrepo1-retention-full-type=count\nrepo1-retention-full=2\nrepo1-retention-diff=6\n# Time-based alternative (at least a 14-day PITR window):\n# repo1-retention-full-type=time\n# repo1-retention-full=14\n\n# Expire runs automatically after each backup; to trigger it manually:\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info expire","what":"Legt fest, wie viele Full-/Diff-Backups (count) oder wie viele Tage Full-Backups (time) behalten werden; expire löscht Backups außerhalb der Richtlinie, die davon abhängigen Diff-/Incr-Backups und nicht mehr benötigtes WAL-Archiv.","when":"Um die Größe des Repositorys im Griff zu behalten und die Anforderung an RPO/PITR-Fenster zu garantieren (z. B. 14 Tage zurückgehen können).","caution":"Das PITR-Fenster wird durch das älteste behaltene Vollbackup begrenzt; bei wöchentlichen Fulls und retention-full=2 kannst du höchstens etwa 2 Wochen zurückgehen. repo1-retention-archive folgt standardmäßig der Full-Aufbewahrung; ein niedrigerer Wert lässt ältere Backups nur bis zu ihrem Konsistenzpunkt nutzbar. Expire lässt sich nicht rückgängig machen.","untested":true,"tags":["pgbackrest","retention","retention period","expire","backup cleanup"]},{"id":"standby-manual-config","task":"Standby manuell konfigurieren (standby.signal + primary_conninfo)","category":"Replikation","command":"# On the standby, with PostgreSQL stopped (PGDATA came from a backup/copy):\ntouch $PGDATA/standby.signal\ncat >> $PGDATA/postgresql.auto.conf <<'EOF'\nprimary_conninfo = 'host=<primary_host> port=5432 user=replicator application_name=<standby_name>'\nprimary_slot_name = '<slot>'\nEOF\npg_ctl -D $PGDATA start\n# PG13+: change the connection on a running standby without restart (e.g. point it to a new primary):\npsql -X -c \"ALTER SYSTEM SET primary_conninfo = 'host=<new_primary_host> port=5432 user=replicator application_name=<standby_name>'\" -c \"SELECT pg_reload_conf();\"","what":"Die Datei standby.signal lässt den Server als Standby starten; primary_conninfo definiert die Streaming-Verbindung und primary_slot_name den zu nutzenden Replication Slot. Ab PG13 lassen sich primary_conninfo und primary_slot_name per Reload ändern.","when":"Um ein ohne pg_basebackup -R vorbereitetes Datenverzeichnis (pgBackRest, Snapshot, rsync) zum Standby zu machen oder einen bestehenden Standby auf eine neue Primary zu richten.","caution":"Der Slot muss vorher auf der Primary angelegt werden (pg_create_physical_replication_slot), sonst schlägt der walreceiver ständig fehl. Lege das Passwort in ~/.pgpass statt in conninfo ab. application_name muss exakt dem Namen in synchronous_standby_names entsprechen. Wird standby.signal vergessen, startet der Server als Primary und wechselt auf eine eigene Timeline.","since":"PostgreSQL 12","note":"Vor PostgreSQL 12 standen diese Einstellungen in recovery.conf (standby_mode = on); PG12 verlagerte sie in standby.signal und postgresql.conf. Vor PG13 erforderte eine Änderung von primary_conninfo einen Neustart.","tags":["standby","standby.signal","primary_conninfo","replica setup","streaming"]},{"id":"standby-wal-receiver-lag","task":"WAL-Empfangs- und Replay-Verzögerung auf dem Standby überwachen (pg_stat_wal_receiver)","category":"Replikation","command":"-- Run on the standby:\nSELECT pg_is_in_recovery() AS in_recovery,\n       pg_last_wal_receive_lsn() AS receive_lsn,\n       pg_last_wal_replay_lsn() AS replay_lsn,\n       pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())) AS replay_backlog,\n       now() - pg_last_xact_replay_timestamp() AS last_replay_age,\n       pg_get_wal_replay_pause_state() AS pause_state;\nSELECT pid, status, sender_host, sender_port, slot_name, received_tli, flushed_lsn, latest_end_lsn, now() - last_msg_receipt_time AS since_last_msg FROM pg_stat_wal_receiver;","what":"Zeigt, wie viel WAL der Standby empfangen und wie viel er angewendet hat (Replay-Rückstand), das Alter der zuletzt eingespielten Transaktion, ob das Replay pausiert ist, und den Verbindungszustand des walreceiver (welche Primary, Slot, Timeline, Zeit der letzten Nachricht).","when":"Um Verzögerungen zu diagnostizieren, wenn die Primary nicht erreichbar ist, oder auf kaskadierenden Standbys; bei einem Alarm 'Standby im Rückstand', um ein Netzwerkproblem (Empfang) von einem Replay-Problem (I/O, Konflikt) zu unterscheiden.","caution":"Liefert pg_stat_wal_receiver keine Zeilen, streamt der Standby nicht (wird aus dem Archiv gespeist oder ist getrennt). last_replay_age wächst auch, wenn auf der Primary nicht geschrieben wird; zusammen mit der Differenz receive_lsn/replay_lsn interpretieren. Wächst der Replay-Rückstand, während der Empfang voranschreitet, hat der Standby lange Abfragen (max_standby_streaming_delay) oder einen I/O-Engpass.","since":"PostgreSQL 14","note":"pg_get_wal_replay_pause_state() kam in PostgreSQL 14 hinzu; davor gab es nur pg_is_wal_replay_paused(). Der Rest der Abfrage funktioniert ab PG10.","tags":["standby","replication lag","wal receiver","replay","lag"]},{"id":"pg-rewind-old-primary","task":"Alte Primary nach einem Failover mit pg_rewind als Replica wieder einbinden","category":"Replikation","command":"# Prerequisite (must be set BEFORE the incident): wal_log_hints = on OR data checksums; full_page_writes = on\npg_controldata $PGDATA | grep -E 'wal_log_hints|checksum'\n# 1) The old primary must be stopped:\npg_ctl -D $PGDATA stop -m fast\n# 2) Dry run first:\npg_rewind --target-pgdata=$PGDATA --source-server='host=<new_primary_host> port=5432 user=postgres dbname=postgres' --progress --dry-run\n# 3) Actual rewind + standby configuration (standby.signal + primary_conninfo):\npg_rewind --target-pgdata=$PGDATA --source-server='host=<new_primary_host> port=5432 user=postgres dbname=postgres' --progress --write-recovery-conf\n# 4) Review postgresql.auto.conf copied from the source (port, primary_slot_name), then start:\npg_ctl -D $PGDATA start","what":"Macht das Datenverzeichnis der alten Primary mit der neuen Primary konsistent, indem die nach dem Abzweigpunkt der Timeline geänderten Blöcke von der neuen Primary kopiert werden; macht sie binnen Minuten ohne vollständiges Basebackup zur Replica.","when":"Wenn die alte Primary nach einem Failover/Promote zurückkommt und mit abweichenden Änderungen hinter der neuen Timeline liegt.","caution":"Transaktionen, die nach dem Failover auf der alten Primary verblieben und die neue Primary nie erreicht haben, werden endgültig entfernt. pg_rewind kopiert auch Konfigurationsdateien (postgresql.conf, postgresql.auto.conf, pg_hba.conf) von der Quelle; korrigiere vor dem Start Port- und Slot-Einstellungen. Ist seit dem Abzweigpunkt benötigtes WAL nicht mehr auf der Quelle, hole es mit -c (--restore-target-wal) aus dem Archiv. Die Quellverbindung braucht einen Superuser oder eine Rolle mit den in der Dokumentation genannten Funktionsprivilegien. In Patroni geschieht das automatisch mit use_pg_rewind: true.","since":"PostgreSQL 13","note":"-R/--write-recovery-conf und -c/--restore-target-wal kamen in PostgreSQL 13 hinzu; ab PG13 führt pg_rewind auf einem nicht sauber gestoppten Ziel selbst Crash Recovery aus (abschaltbar mit --no-ensure-shutdown). pg_rewind gibt es seit 9.5.","tags":["pg_rewind","after failover","old primary","timeline","rejoin"]},{"id":"sync-standby-quorum","task":"Synchrone Replikation mit Quorum (ANY) oder Priorität (FIRST) konfigurieren","category":"Replikation","command":"-- Quorum: commit completes once any 1 standby in the list confirms\nALTER SYSTEM SET synchronous_standby_names = 'ANY 1 (stby_a, stby_b)';\n-- Priority based: the first connected standby in the list is sync, the others 'potential'\n-- ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (stby_a, stby_b)';\nSELECT pg_reload_conf();\nSELECT application_name, state, sync_state, sync_priority FROM pg_stat_replication ORDER BY application_name;","what":"Legt fest, welche Standbys bestätigen müssen, bevor ein Commit abgeschlossen ist: ANY N ist quorumbasiert (sync_state = quorum), FIRST N macht die ersten N verbundenen Standbys in Listenreihenfolge synchron (sync) und die übrigen zu wartenden Standbys (potential). Die Bestätigungsstufe setzt synchronous_commit (remote_write, on, remote_apply).","when":"Auf Systemen, die beim Failover keinen Datenverlust erlauben (RPO = 0); bei mehreren Standbys wird ANY bevorzugt, damit der Ausfall eines einzelnen Standby die Schreibvorgänge nicht stoppt.","caution":"Ist die erforderliche Anzahl synchroner Standbys nicht verbunden, hängen Commits ohne Fehlermeldung und die Anwendung friert ein. Namen in der Liste müssen dem application_name der Standbys entsprechen. Im Notfall kann eine einzelne Session dies mit SET synchronous_commit = local umgehen. Unter Patroni über synchronous_mode verwalten, statt es manuell zu setzen.","since":"PostgreSQL 10","note":"Die Syntax FIRST kam in PostgreSQL 9.6 hinzu, ANY (Quorum Commit) in PostgreSQL 10; die Form 'N (a, b)' ohne Schlüsselwort bedeutet FIRST.","tags":["synchronous replication","synchronous_standby_names","quorum","rpo","sync"]},{"id":"slot-retained-wal-monitor","task":"Von Replication Slots zurückgehaltenes WAL überwachen und einen verwaisten Slot sicher löschen","category":"Replikation","command":"SELECT slot_name, slot_type, database, active, active_pid, wal_status,\n       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal,\n       pg_size_pretty(safe_wal_size) AS safe_wal_size,\n       xmin, catalog_xmin\nFROM pg_replication_slots\nORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;\n-- Safe drop: drops only if not active (returns 0 rows if active)\nSELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = '<slot>' AND NOT active;","what":"Zeigt, wie viel WAL jeder Slot auf der Primary zurückhält, wal_status (reserved/extended/unreserved/lost), die verbleibende Sicherheitsreserve unter max_slot_wal_keep_size (safe_wal_size) und die xmin-/catalog_xmin-Werte, die VACUUM aufhalten; die zweite Abfrage löscht den Slot nur, wenn er nicht genutzt wird.","when":"Wenn das Verzeichnis pg_wal wächst, bei einem Plattenalarm und im Routine-Monitoring; zur Bereinigung von Slots, nachdem ein Standby oder Subscriber dauerhaft entfernt wurde.","caution":"Ein inaktiver Slot hält WAL unbegrenzt zurück und kann die Platte füllen und die Primary stoppen; ein Slot, der xmin/catalog_xmin hält, blockiert zudem die Bereinigung toter Tupel und erzeugt Bloat-/Wraparound-Risiko. Nach dem Löschen eines Slots kann sein Standby/Subscriber nicht mehr dort weitermachen, wo er aufgehört hat (Neuaufbau nötig); prüfe vor dem Löschen den Eigentümer. Auf der Primary ausführen (pg_current_wal_lsn() schlägt auf einem Standby fehl).","since":"PostgreSQL 13","note":"wal_status und safe_wal_size kamen in PostgreSQL 13 hinzu. PG16 ergänzte conflicting für logische Slots und PG17 inactive_since; inactive_since ist der direkteste Hinweis auf einen verwaisten Slot.","tags":["replication slot","wal accumulation","disk full","drop slot","retained wal"]},{"id":"slot-max-wal-keep-size","task":"Von Slots zurückhaltbares WAL begrenzen (max_slot_wal_keep_size)","category":"Replikation","command":"ALTER SYSTEM SET max_slot_wal_keep_size = '100GB';\nSELECT pg_reload_conf();\nSELECT slot_name, active, wal_status, pg_size_pretty(safe_wal_size) AS safe_wal_size FROM pg_replication_slots;","what":"Setzt eine Obergrenze für das WAL, das Replication Slots in pg_wal halten dürfen (Standard -1 = unbegrenzt); bei Überschreitung entfernt der Checkpoint altes WAL und der Slot erhält wal_status = 'lost'. Der Parameter wirkt nach einem Reload.","when":"Als Schutz auf jedem Cluster mit Slots gegen das Risiko, dass ein getrennter Standby oder Subscriber die Platte der Primary füllt und das ganze System stoppt.","caution":"Das Limit sichert das Weiterlaufen der Primary auf Kosten des Slot-Eigentümers: Ein Standby mit 'lost'-Slot muss neu aufgebaut und eine logische Subscription neu angelegt werden. Wähle einen Wert unterhalb des freien Platzes der pg_wal-Platte, aber größer als das WAL, das während des längsten erwarteten Ausfalls entsteht. Ist wal_status 'unreserved', steht der Slot kurz vor dem Verlust; löse einen Alarm aus.","since":"PostgreSQL 13","note":"max_slot_wal_keep_size kam in PostgreSQL 13 hinzu; davor ließ sich das von Slots zurückgehaltene WAL nicht begrenzen, und der einzige Schutz war Monitoring plus manuelles Löschen von Slots.","tags":["max_slot_wal_keep_size","slot limit","wal accumulation","disk protection","replication slot"]},{"id":"logical-add-table-refresh","task":"Tabelle zu einer Publication hinzufügen und Subscription aktualisieren (REFRESH PUBLICATION)","category":"Logische Replikation","command":"-- Publisher:\nALTER PUBLICATION pub_app ADD TABLE orders;\n-- PG15+: all tables in a schema: ALTER PUBLICATION pub_app ADD TABLES IN SCHEMA app_schema;\nSELECT pubname, schemaname, tablename FROM pg_publication_tables WHERE pubname = 'pub_app';\n-- Subscriber (the table structure must already exist):\nALTER SUBSCRIPTION sub_app REFRESH PUBLICATION WITH (copy_data = true);\nSELECT srrelid::regclass AS table_name, srsubstate FROM pg_subscription_rel ORDER BY 1;","what":"Fügt auf dem Publisher eine neue Tabelle zur Publication hinzu; auf dem Subscriber nimmt REFRESH PUBLICATION die neuen Tabellen in die Subscription auf und startet mit copy_data = true die initiale Kopie (tablesync). srsubstate: i = init, d = data copy, f = copy finished, s = synchronized, r = ready.","when":"Wenn einer laufenden logischen Replikation eine neue Tabelle hinzugefügt oder eine Tabelle aus der Publication entfernt wird.","caution":"Auch bei Publications mit FOR ALL TABLES oder TABLES IN SCHEMA übernimmt der Subscriber eine neue Tabelle erst nach REFRESH. REFRESH schlägt fehl, wenn die Tabelle auf dem Subscriber nicht existiert; DDL wird nicht repliziert. Hat eine veröffentlichte Tabelle keinen PRIMARY KEY und keine REPLICA IDENTITY, schlagen UPDATE/DELETE auf dem Publisher fehl. Die initiale Kopie einer großen Tabelle hält lange einen Slot und einen Snapshot auf dem Publisher.","since":"PostgreSQL 10","note":"ADD TABLES IN SCHEMA kam in PostgreSQL 15 hinzu (PG15 brachte auch Zeilenfilter mit WHERE und Spaltenlisten).","tags":["logical replication","publication","add table","refresh publication","subscription"]},{"id":"logical-subscription-monitor","task":"Logische Replikation überwachen (pg_stat_subscription, Apply-Fehler, Slot-Lag)","category":"Logische Replikation","command":"-- Subscriber:\nSELECT subname, pid, leader_pid, relid::regclass AS syncing_table, received_lsn, latest_end_lsn, now() - last_msg_receipt_time AS since_last_msg, latest_end_time FROM pg_stat_subscription;\nSELECT subname, apply_error_count, sync_error_count, stats_reset FROM pg_stat_subscription_stats;\n-- Publisher:\nSELECT slot_name, plugin, database, active, wal_status,\n       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS confirmed_lag,\n       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal\nFROM pg_replication_slots WHERE slot_type = 'logical';","what":"Zeigt auf dem Subscriber Apply-/tablesync-Worker, empfangene und bestätigte LSN, Zeit der letzten Nachricht und kumulierte Apply-/Sync-Fehlerzahlen; auf dem Publisher das unbestätigte WAL (confirmed_flush) des logischen Slots und das insgesamt zurückgehaltene WAL.","when":"Beim Einrichten logischer Replikation, um bei der Umstellung eines Upgrades/einer Migration zu bestätigen, dass der Lag gegen null geht, und für das Fehler-Alerting.","caution":"Ist pid NULL, läuft der Apply-Worker nicht (Subscription deaktiviert oder wiederholt fehlschlagend und neu startend); prüfe das Server-Log. Ein steigender apply_error_count weist auf einen Konflikt hin. Eine stockende Subscription sammelt auf dem Publisher WAL und catalog_xmin an; überwache Platte und Katalog-Bloat des Publishers.","since":"PostgreSQL 15","note":"pg_stat_subscription_stats kam in PostgreSQL 15 hinzu und pg_stat_subscription.leader_pid in PG16 (Parallel Apply); PG17 ergänzte die Spalte worker_type. Die Grundspalten von pg_stat_subscription gibt es seit PG10.","tags":["logical replication","subscription monitoring","pg_stat_subscription","apply error","lag"]},{"id":"logical-conflict-skip","task":"Konflikt in der logischen Replikation lösen (disable_on_error, SKIP)","category":"Logische Replikation","command":"-- 1) Prevent a failing apply worker from looping forever:\nALTER SUBSCRIPTION sub_app SET (disable_on_error = true);\n-- 2) Find the finish LSN of the conflicting transaction in the subscriber log:\n--    CONTEXT: ... in transaction 749, finished at 0/5035378\n-- 3a) Preferred: fix/delete the conflicting row on the subscriber, then re-enable:\nALTER SUBSCRIPTION sub_app ENABLE;\n-- 3b) Or skip the whole remote transaction (NONE of its changes are applied on the subscriber):\nALTER SUBSCRIPTION sub_app SKIP (lsn = '0/5035378');\nALTER SUBSCRIPTION sub_app ENABLE;\nSELECT subname, subenabled, subskiplsn FROM pg_subscription;","what":"Stoppt ein Fehler wie ein doppelter Schlüssel, eine fehlende Zeile oder ein Rechteproblem das Apply auf dem Subscriber: disable_on_error deaktiviert die Subscription automatisch; entweder die konfliktverursachenden Daten korrigieren und ENABLE ausführen, oder mit SKIP die gesamte Remote-Transaktion überspringen, die über die LSN 'finished at' im Log identifiziert wird.","when":"Wenn der Apply-Worker wegen direkter Schreibvorgänge auf dem Subscriber, einer unvollständigen initialen Kopie oder bidirektionaler Schreibvorgänge fehlschlägt und ständig neu startet.","caution":"SKIP verwirft auch die nicht konfliktbehafteten Änderungen dieser Transaktion, und beide Seiten laufen dauerhaft auseinander; korrigiere lieber die Daten und vergleiche die Tabelle nach einem SKIP. Bis der Konflikt gelöst ist, sammelt der Slot auf dem Publisher weiter WAL an. Verhindere Anwendungsschreibzugriffe auf Subscriber-Tabellen über Privilegien.","since":"PostgreSQL 15","note":"SKIP und disable_on_error kamen in PostgreSQL 15 hinzu; davor wurde dasselbe durch Vorrücken des Origins auf dem Subscriber mit pg_replication_origin_advance() erreicht (riskanter).","tags":["logical replication","conflict","conflict","skip","duplicate key"]},{"id":"logical-sync-sequences","task":"Sequence-Werte in der logischen Replikation zum Subscriber synchronisieren","category":"Logische Replikation","command":"# Generate the setval script on the publisher and apply it on the subscriber (at cutover, after writes are stopped):\npsql -h <publisher_host> -d <db> -XAtc \"SELECT format('SELECT setval(%L, %s, %s);', quote_ident(schemaname) || '.' || quote_ident(sequencename), COALESCE(last_value, start_value), (last_value IS NOT NULL)::text) FROM pg_sequences;\" | psql -h <subscriber_host> -d <db> -X","what":"Da logische Replikation keine Sequence-Werte (auch nicht für Identity) überträgt, liest dies den letzten Wert jeder Sequence auf dem Publisher und erzeugt setval-Befehle, die auf dem Subscriber denselben Wert setzen, und wendet sie direkt an.","when":"Bei der Umstellung eines Upgrades/einer Migration per logischer Replikation, unmittelbar bevor die Anwendung auf den Subscriber gerichtet wird.","caution":"Wird es ausgeführt, während der Publisher noch Schreibvorgänge annimmt, sind die Werte sofort veraltet und nach der Umstellung folgen Fehler wegen doppelter Schlüssel; führe es nach dem Stoppen der Schreibvorgänge aus oder plane einen Sicherheitsabstand ein. Auch DDL wird nicht repliziert: Schemaänderungen (ALTER TABLE, neue Indizes) auf beiden Seiten separat anwenden, zuerst auf dem Subscriber. pg_sequences liefert last_value NULL für Sequences, die du nicht lesen darfst; führe den Befehl als Eigentümer oder Superuser aus.","since":"PostgreSQL 10","tags":["logical replication","sequence","setval","ddl not replicated","cutover"]},{"id":"logical-orphan-slot-cleanup","task":"Subscription entfernen und verwaisten logischen Slot auf dem Publisher bereinigen","category":"Logische Replikation","command":"-- Subscriber: while the publisher is unreachable DROP SUBSCRIPTION cannot drop the remote slot and fails; detach the slot first:\nALTER SUBSCRIPTION sub_app DISABLE;\nALTER SUBSCRIPTION sub_app SET (slot_name = NONE);\nDROP SUBSCRIPTION sub_app;\n-- Publisher: find and drop orphaned logical slots (including tablesync slots pg_<suboid>_sync_<relid>_<sysid>):\nSELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots WHERE slot_type = 'logical' AND NOT active;\nSELECT pg_drop_replication_slot('sub_app');","what":"Löscht die Subscription, ohne den Remote-Slot anzufassen (slot_name = NONE); sucht und entfernt dann logische Slots auf dem Publisher, die keinen Consumer mehr haben, sowie übrig gebliebene tablesync-Slots.","when":"Wenn eine Subscription entfernt werden muss, während der Publisher nicht erreichbar ist, nach Abschluss einer Migration oder wenn ein logischer Slot mit active = false auf dem Publisher WAL ansammelt.","caution":"Ein verwaister logischer Slot hält sowohl WAL als auch catalog_xmin zurück: Die Platte läuft voll und die Systemkataloge blähen auf. Nach dem Löschen des Slots kann diese Subscription jedoch nicht fortgesetzt werden; prüfe vor dem Löschen, dass kein Subscriber (auch keiner auf einem anderen Server) diesen Slot nutzt. Ein aktiver Slot lässt sich nicht löschen; der verbundene walsender muss zuerst beendet werden.","since":"PostgreSQL 10","tags":["logical replication","slot cleanup","drop subscription","orphaned slot","catalog_xmin"]},{"id":"pg-upgrade-link-runbook","task":"Major-Version-Upgrade mit pg_upgrade --link (End-to-End-Runbook)","category":"Upgrade","command":"# As the postgres user, in a writable directory; example 16 -> 17\nexport OLD_BIN=/usr/pgsql-16/bin NEW_BIN=/usr/pgsql-17/bin\nexport OLD_DATA=/var/lib/pgsql/16/data NEW_DATA=/var/lib/pgsql/17/data\n# 0) initdb the new cluster with the same encoding/locale and checksum setting as the old one (-k if the old cluster has checksums)\n# 1) Compatibility check (can run while the old cluster is up, changes no data):\n$NEW_BIN/pg_upgrade --old-bindir=$OLD_BIN --new-bindir=$NEW_BIN --old-datadir=$OLD_DATA --new-datadir=$NEW_DATA --check\n# 2) Stop the old cluster and upgrade with hard links:\n$OLD_BIN/pg_ctl -D $OLD_DATA stop -m fast\n$NEW_BIN/pg_upgrade --old-bindir=$OLD_BIN --new-bindir=$NEW_BIN --old-datadir=$OLD_DATA --new-datadir=$NEW_DATA --link --jobs=4\n# 3) Carry postgresql.conf/pg_hba.conf customizations over to the new cluster, start it:\n$NEW_BIN/pg_ctl -D $NEW_DATA -l /tmp/pg17_start.log start\n# 4) Gather statistics, run generated scripts if any:\n$NEW_BIN/vacuumdb --all --analyze-in-stages\n# psql -X -f update_extensions.sql   (if pg_upgrade generated it)\n# 5) After validation and a fresh backup, delete the old cluster:\n./delete_old_cluster.sh","what":"Prüft zuerst mit --check die Kompatibilität und führt dann das Major-Upgrade unabhängig von der Datenmenge in Minuten durch, indem die Datendateien des alten Clusters per Hardlink an die neue Version gebunden statt kopiert werden; anschließend werden Statistiken stufenweise erfasst und der alte Cluster entfernt.","when":"Um einen großen Cluster an Ort und Stelle mit akzeptabel kurzem Ausfall (Minuten) auf eine neue Major-Version zu bringen.","caution":"Mit --link lässt sich der alte Cluster nach dem Start des neuen nicht mehr sicher verwenden; der einzige Weg zurück ist ein vor dem Upgrade erstelltes Backup. Altes und neues Datenverzeichnis müssen im selben Dateisystem liegen. Bis analyze fertig ist, können die Pläne im neuen Cluster schlecht sein. Nutzt du pgBackRest, führe stanza-upgrade aus und erstelle sofort ein neues Vollbackup; Backups der alten Version lassen sich nicht in den neuen Cluster zurückspielen.","note":"Physical Standbys werden nicht mit pg_upgrade aktualisiert: Aktualisiere sie nach dem Upgrade der Primary mit der Methode rsync --hard-links --size-only aus der Dokumentation oder baue sie neu auf. Logische Slots werden vor PG17 nicht migriert (ab altem Cluster PG17+ schon); Planner-Statistiken werden vor PG18 nicht migriert.","tags":["pg_upgrade","major upgrade","version upgrade","link","check"]},{"id":"logical-upgrade-near-zero-downtime","task":"Major-Version-Upgrade nahezu ohne Ausfallzeit mit logischer Replikation","category":"Upgrade","command":"# 0) On the old version wal_level = logical (restart); every table needs a PRIMARY KEY or REPLICA IDENTITY\n# 1) Move roles and schema to the new version (a 'role postgres already exists' error is expected):\npg_dumpall -h <old_host> --globals-only | psql -h <new_host> -X -d postgres\npg_dump -h <old_host> --schema-only --create -d <db> | psql -h <new_host> -X -d postgres\n# 2) Publication on the old version, subscription on the new version (initial copy + continuous stream):\npsql -h <old_host> -d <db> -X -c \"CREATE PUBLICATION upg_pub FOR ALL TABLES;\"\npsql -h <new_host> -d <db> -X -c \"CREATE SUBSCRIPTION upg_sub CONNECTION 'host=<old_host> dbname=<db> user=<repl_user>' PUBLICATION upg_pub;\"\n# 3) Wait until all tables are 'r' (ready) and lag is ~0:\npsql -h <new_host> -d <db> -X -c \"SELECT srsubstate, count(*) FROM pg_subscription_rel GROUP BY 1;\"\npsql -h <old_host> -d <db> -X -c \"SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS lag FROM pg_replication_slots WHERE slot_name = 'upg_sub';\"\n# 4) Cutover: stop application writes, confirm lag = 0, synchronize sequences (logical-sync-sequences)\n# 5) Drop the subscription (the remote slot is dropped too), point the application to the new server:\npsql -h <new_host> -d <db> -X -c \"DROP SUBSCRIPTION upg_sub;\"","what":"Baut einen leeren Cluster auf der neuen Version auf, überträgt das Schema und streamt Daten per logischer Replikation fortlaufend aus der alten Version; nur der finale Umstellungsschritt (Schreibvorgänge stoppen, Sequences synchronisieren, umleiten) verursacht einen kurzen Ausfall. Eine zweite Replikation in Gegenrichtung kann einen Rückweg offen halten.","when":"Wenn der von pg_upgrade verlangte Ausfall inakzeptabel ist, wenn gleichzeitig auf neue Hardware/ein neues OS umgezogen wird oder wenn mehrere Major-Versionen übersprungen werden.","caution":"DDL, Sequences, Large Objects und Inhalte von Materialized Views werden nicht repliziert; friere das Schema während der Migration ein. Bei Tabellen ohne Primärschlüssel/Replica Identity schlagen UPDATE/DELETE auf dem Publisher fehl. Bei großen Datenbanken dauert die initiale Kopie lange und hält auf dem alten Server WAL und catalog_xmin zurück. Jede Datenbank braucht eine eigene Publication/Subscription. Vergleiche vor der Umstellung Zeilenzahlen/Prüfsummen.","since":"PostgreSQL 10","note":"pg_createsubscriber, eingeführt in PostgreSQL 17, wandelt einen Physical Standby ohne initiale Kopie in einen logischen Subscriber um und spart so bei großen Datenbanken die Zeit für die initiale Kopie.","tags":["upgrade","logical replication","zero downtime migration","major upgrade","cutover"]},{"id":"pgbouncer-show-pools-stats","task":"PgBouncer-Pools, Statistiken und Verbindungen anzeigen (SHOW POOLS/STATS/CLIENTS/SERVERS)","category":"PgBouncer","command":"# Connect to the PgBouncer admin console (user must be in admin_users/stats_users):\npsql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -X\nSHOW POOLS;    -- if cl_waiting > 0 and maxwait grows, the pool is saturated\nSHOW STATS;    -- per-database total/average query, transaction and wait times\nSHOW CLIENTS;  -- client connections (state, source address, linked server connection)\nSHOW SERVERS;  -- server connections opened to PostgreSQL\nSHOW CONFIG;","what":"Zeigt für jeden Datenbank-/Benutzer-Pool aktive und wartende Clients (cl_active, cl_waiting), aktive/inaktive Serververbindungen (sv_active, sv_idle), die längste Wartezeit (maxwait) und Abfrage-/Transaktionszähler.","when":"Wenn die Anwendung Verbindungsfehler/Timeouts meldet, für die Pool-Dimensionierung und um vor einer Wartung aktive Verbindungen zu sehen.","caution":"Die Admin-Konsole akzeptiert kein SQL; sie führt nur PgBouncer-Befehle aus. Bleibt cl_waiting hoch, prüfe zuerst auf Serverseite lange Transaktionen oder Sperren; ein blindes Erhöhen von default_pool_size treibt PostgreSQL an sein max_connections-Limit und erzeugt Speicherdruck. Die Werte von SHOW STATS sind kumuliert; Durchschnitte beziehen sich auf das Intervall stats_period.","untested":true,"tags":["pgbouncer","show pools","connection pooling","statistics","connection pool"]},{"id":"pgbouncer-pause-resume-reload","task":"Verbindungen während der Wartung mit PgBouncer halten (PAUSE/RESUME) und Konfiguration neu laden (RELOAD)","category":"PgBouncer","command":"psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -X\nRELOAD;            -- apply pgbouncer.ini and auth_file changes without dropping connections\nPAUSE <db>;        -- closes server connections once running queries/transactions finish; new requests are queued\n-- ... maintenance: switchover, PostgreSQL restart, etc. ...\nRESUME <db>;\n-- If the host in [databases] was changed (after RELOAD), gradually refresh server connections:\nRECONNECT <db>;","what":"PAUSE baut die Verbindungen auf PostgreSQL-Seite ab, ohne Client-Verbindungen zu trennen, und stellt neue Abfragen in eine Warteschlange; RESUME lässt die Warteschlange wieder abfließen. Ein kurzer Neustart der Primary oder ein Switchover erscheint der Anwendung so als kurze Verzögerung statt als Fehler.","when":"In kurzen Wartungsfenstern (Switchover, Neustart für Minor-Version, Neustart für Parameter) und beim Anwenden von Änderungen an pgbouncer.ini/userlist.","caution":"PAUSE kehrt erst zurück, wenn alle aktiven Transaktionen beendet sind; eine lang laufende Abfrage hält PAUSE auf (diese Session bei Bedarf in PostgreSQL beenden). Wartende Clients erhalten einen Fehler, wenn query_wait_timeout (Standard 120 s) abläuft; schließe die Wartung innerhalb dieser Zeit ab. RESUME nach PAUSE zu vergessen ist ein vollständiger Ausfall. Einige Einstellungen (listen_addr, listen_port usw.) ändern sich nicht per RELOAD und erfordern einen Neustart.","untested":true,"tags":["pgbouncer","pause","resume","reload","maintenance"]},{"id":"pgbouncer-pool-mode","task":"Wahl des pool_mode in PgBouncer und grundlegende Dimensionierung","category":"PgBouncer","command":"; /etc/pgbouncer/pgbouncer.ini\n[databases]\nappdb = host=<primary_host> port=5432 dbname=appdb pool_mode=transaction\nreportdb = host=<primary_host> port=5432 dbname=appdb pool_mode=session\n\n[pgbouncer]\nlisten_addr = 0.0.0.0\nlisten_port = 6432\nauth_type = scram-sha-256\nauth_file = /etc/pgbouncer/userlist.txt\npool_mode = transaction\nmax_client_conn = 5000\ndefault_pool_size = 20\nreserve_pool_size = 5\nmax_prepared_statements = 200\nquery_wait_timeout = 120","what":"Der Session-Modus bindet einen Client für die Dauer seiner Verbindung an eine Serververbindung; der Transaktionsmodus weist eine Serververbindung nur für die Dauer einer Transaktion zu und bedient Tausende Clients mit Dutzenden PostgreSQL-Verbindungen; der Statement-Modus gibt die Verbindung nach jeder Abfrage zurück und erlaubt keine Transaktionen mit mehreren Anweisungen. pool_mode lässt sich auch je Datenbank überschreiben.","when":"Transaktionsmodus für OLTP-/Microservice-Anwendungen, die viele kurzlebige Verbindungen öffnen; Session-Modus für Arbeiten, die vom Session-Zustand abhängen (Reporting, Migrationen, Dienste mit LISTEN).","caution":"Im Transaktionsmodus bleibt der Session-Zustand zwischen Transaktionen nicht erhalten: SET (außer SET LOCAL), Advisory Locks auf Session-Ebene, LISTEN, WITH-HOLD-Cursors, temporäre Tabellen über Transaktionen hinweg und PREPARE auf SQL-Ebene sind unzuverlässig. Prepared Statements auf Protokollebene werden nur mit PgBouncer 1.21+ und max_prepared_statements > 0 unterstützt (Standard 200 seit 1.24, davor 0). default_pool_size mal Anzahl der Paare (Datenbank, Benutzer) darf PostgreSQL max_connections (abzüglich der Reserve superuser_reserved_connections) nicht übersteigen.","untested":true,"tags":["pgbouncer","pool_mode","transaction mode","connection pooling","sizing"]}]},"recipes/postgres-ops.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"recipes":[{"id":"pgss-top-queries","task":"Teuerste Abfragen finden (pg_stat_statements: Gesamt- und mittlere Zeit)","category":"Performance","command":"-- By total time: queries consuming the most server time\nSELECT queryid,\n       calls,\n       round(total_exec_time::numeric, 1) AS total_ms,\n       round(mean_exec_time::numeric, 2) AS mean_ms,\n       round(stddev_exec_time::numeric, 2) AS stddev_ms,\n       round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 1) AS pct_total,\n       rows / NULLIF(calls, 0) AS rows_per_call,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 120) AS query\nFROM pg_stat_statements\nWHERE dbid = (SELECT oid FROM pg_database WHERE datname = current_database())\nORDER BY total_exec_time DESC\nLIMIT 20;\n\n-- By mean time per call: individually slow queries\nSELECT queryid,\n       calls,\n       round(mean_exec_time::numeric, 2) AS mean_ms,\n       round(max_exec_time::numeric, 2) AS max_ms,\n       round(mean_plan_time::numeric, 2) AS mean_plan_ms,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 120) AS query\nFROM pg_stat_statements\nWHERE calls >= 50\nORDER BY mean_exec_time DESC\nLIMIT 20;","what":"Listet die normalisierten Abfragen, die in der aktuellen Datenbank die meiste Serverzeit verbrauchen, mit ihrem Anteil am Gesamtwert; die zweite Abfrage listet die langsamsten je Aufruf.","when":"Erste Anlaufstelle, wenn CPU-/IO-Last steigt oder Benutzer 'die Datenbank ist langsam' melden; um zu entscheiden, wo das Tuning beginnen soll.","caution":"pg_stat_statements erfasst nichts, bis es in shared_preload_libraries eingetragen, der Server neu gestartet und CREATE EXTENSION in der Datenbank ausgeführt wurde. Die Werte sind seit dem letzten Reset kumuliert. Ein im Verhältnis zu mean_ms hoher stddev_ms zeigt, dass Plan oder Datenverteilung zwischen Aufrufen variieren. mean_plan_time wird nur mit pg_stat_statements.track_planning = on befüllt (Standard off; Aktivieren erzeugt Overhead). Um Abfragetexte anderer Benutzer zu sehen, braucht es Superuser oder die Rolle pg_read_all_stats.","since":"PostgreSQL 13","note":"In PostgreSQL 13 wurden die Spalten total_time/mean_time in total_exec_time/mean_exec_time umbenannt, und Planungszeiten (total_plan_time) erhielten eigene Spalten; verwende in PG12 und früher die alten Namen.","tags":["pg_stat_statements","slow query","most expensive query","top sql","performance","query tuning"]},{"id":"pgss-io-temp-heavy","task":"Abfragen finden, die Platten-I/O, Temp-Dateien und WAL verursachen (pg_stat_statements)","category":"Performance","command":"SELECT queryid,\n       calls,\n       shared_blks_hit,\n       shared_blks_read,\n       round(100.0 * shared_blks_hit / NULLIF(shared_blks_hit + shared_blks_read, 0), 1) AS hit_pct,\n       pg_size_pretty(temp_blks_written * current_setting('block_size')::bigint) AS temp_written,\n       pg_size_pretty(wal_bytes) AS wal,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 120) AS query\nFROM pg_stat_statements\nORDER BY shared_blks_read + temp_blks_read + temp_blks_written DESC\nLIMIT 20;","what":"Zeigt die Abfragen, die die meisten Blöcke außerhalb der Shared Buffers lesen, am meisten in Temp-Dateien schreiben (Sortierungen/Hashes, die nicht in work_mem passen) und das meiste WAL erzeugen, zusammen mit ihrer Cache-Trefferquote.","when":"Um die verantwortliche Abfrage zu finden, wenn die Leselatenz der Platte steigt, Temp-File-Alarme ausgelöst werden (log_temp_files) oder das WAL-Volumen plötzlich wächst.","caution":"shared_blks_read zählt auch Lesevorgänge, die aus dem Page Cache des OS bedient werden; echte Plattenzeit erfordert track_io_timing = on (messe vorher den Overhead mit pg_test_timing). Ist temp_written hoch, besteht die Lösung meist darin, work_mem je Rolle/Session zu erhöhen oder Abfrage/Index zu korrigieren; work_mem global zu erhöhen steigert das Speicherrisiko.","since":"PostgreSQL 13","note":"Die Spalten wal_bytes/wal_records kamen mit PG13. In PG17 wurden blk_read_time/blk_write_time in shared_blk_read_time/shared_blk_write_time umbenannt und local_blk_*_time ergänzt.","tags":["pg_stat_statements","io","temp files","temp_blks_written","work_mem","wal"]},{"id":"pgss-reset","task":"Statistiken von pg_stat_statements zurücksetzen (alle oder eine einzelne Abfrage)","category":"Performance","command":"-- Last reset time and number of entries evicted because the table was full (dealloc)\nSELECT dealloc, stats_reset FROM pg_stat_statements_info;\n\n-- Reset a single query only (userid, dbid, queryid; 0 = all)\nSELECT pg_stat_statements_reset(0, 0, 5927525816675996975);\n\n-- Reset all statistics\nSELECT pg_stat_statements_reset();","what":"Setzt die Zähler von pg_stat_statements vollständig oder je Benutzer/Datenbank/queryid zurück; pg_stat_statements_info zeigt die Zeit des letzten Resets und wie viele Einträge wegen Platzmangels verdrängt wurden.","when":"Um vor einer Tuning-Änderung (Index, Parameter, Deploy) eine saubere Messung zu beginnen oder den neuen Plan einer einzelnen Abfrage isoliert zu messen.","caution":"Ein Reset lässt sich nicht rückgängig machen; Monitoring-/APM-Tools, die auf kumulierte Zähler setzen, zeigen eine Lücke, daher vorher einen Snapshot ziehen (siehe Rezept zu Planregressionen). Wächst dealloc ständig, ist pg_stat_statements.max zu klein (Änderung erfordert einen Neustart). Standardmäßig dürfen es nur Superuser ausführen; mit GRANT EXECUTE ON FUNCTION freigeben.","since":"PostgreSQL 14","note":"Parametrisierter Reset (userid, dbid, queryid) kam mit PG12, die View pg_stat_statements_info mit PG14. PG17 ergänzte einen vierten Parameter minmax_only: pg_stat_statements_reset(0, 0, 0, true) setzt nur Min-/Max-Zeiten zurück; auch die Spalten stats_since und minmax_stats_since gibt es ab PG17.","tags":["pg_stat_statements","reset","clear statistics","queryid","statistics","baseline"]},{"id":"auto-explain-setup","task":"Ausführungspläne langsamer Abfragen mit auto_explain protokollieren","category":"Performance","command":"-- 1) Per session: effective immediately, no restart (superuser)\nLOAD 'auto_explain';\nSET auto_explain.log_min_duration = '500ms';\nSET auto_explain.log_analyze = on;\nSET auto_explain.log_buffers = on;\nSET auto_explain.log_timing = off;\nSET auto_explain.log_nested_statements = on;\n\n-- 2) Persistent: loaded by new sessions, a reload is enough.\n--    The module must be LOADed in this session before ALTER SYSTEM accepts auto_explain.* parameters.\nLOAD 'auto_explain';\nALTER SYSTEM SET session_preload_libraries = 'auto_explain';\nALTER SYSTEM SET auto_explain.log_min_duration = '2s';\nALTER SYSTEM SET auto_explain.log_analyze = on;\nALTER SYSTEM SET auto_explain.log_buffers = on;\nALTER SYSTEM SET auto_explain.log_timing = off;\nALTER SYSTEM SET auto_explain.sample_rate = 0.1;\nSELECT pg_reload_conf();","what":"Schreibt den Ausführungsplan von Abfragen über dem Schwellenwert ins Server-Log (mit tatsächlichen Zeilenzahlen und Buffer-Daten, wenn log_analyze aktiv ist); erfasst den Plan, der mit den echten Parametern der Anwendung entsteht.","when":"Wenn eine Abfrage nur aus der Anwendung oder mit bestimmten Parametern langsam ist und sich mit einem manuellen EXPLAIN nicht reproduzieren lässt; um sporadische Planänderungen zu erwischen.","caution":"log_analyze = on erzeugt Instrumentierungs-Overhead bei jeder erfassten Abfrage, auch unterhalb des Schwellenwerts; log_timing = off und sample_rate < 1 verringern ihn. log_min_duration = 0 flutet in der Produktion das Log. ALTER SYSTEM SET session_preload_libraries überschreibt den vorhandenen Wert; prüfe zuerst SHOW session_preload_libraries. session_preload_libraries wirkt nicht auf bestehende Sessions; zum Laden in jedem Prozess sind shared_preload_libraries plus Neustart nötig.","note":"auto_explain ist ein contrib-Modul und braucht kein CREATE EXTENSION. Die Optionen log_wal (ab PG13) und log_settings (ab PG12) stehen ebenfalls zur Verfügung.","tags":["auto_explain","execution plan","slow query","log","plan capture","explain"]},{"id":"explain-analyze-buffers-wal","task":"Plan im Detail lesen mit EXPLAIN (ANALYZE, BUFFERS, SETTINGS, WAL)","category":"Performance","command":"-- ANALYZE really executes the statement: run DML inside a transaction and roll it back\nBEGIN;\nEXPLAIN (ANALYZE, BUFFERS, SETTINGS, WAL, VERBOSE)\nUPDATE customer SET balance = balance + 1 WHERE city = 'Ankara';\nROLLBACK;\n\n-- For SELECT\nEXPLAIN (ANALYZE, BUFFERS, SETTINGS)\nSELECT name FROM customer ORDER BY name DESC LIMIT 10 OFFSET 1000;","what":"Liefert den Plan mit echten Ausführungsstatistiken: BUFFERS zeigt je Knoten Shared hit/read/dirtied und Temp-Blöcke, SETTINGS zeigt planungsrelevante Parameter mit Nicht-Standardwerten, WAL zeigt die erzeugten WAL-Records/-Bytes.","when":"Um die Abweichung zwischen geschätzten und tatsächlichen Zeilen, auf die Platte ausgelagerte Sortierungen/Hashes (Sort Method: external merge, temp read/written) und die WAL-Kosten von DML zu analysieren.","caution":"ANALYZE führt die Anweisung wirklich aus; DML immer innerhalb von BEGIN/ROLLBACK ausführen (Trigger und Sequence-Inkremente finden trotzdem statt). Lesetipps: Eine Abweichung um Faktor 10 oder mehr zwischen geschätzten 'rows' und 'actual rows' deutet auf ein Statistikproblem hin; bei Knoten in Schleifen gelten Zeit und Zeilen je Schleife, für den Gesamtwert mit loops multiplizieren; hohe 'Heap Fetches' bei einem Index Only Scan bedeuten eine veraltete Visibility Map, und 'Workers Planned' > 'Workers Launched' bedeutet einen erschöpften Worker-Pool. Bei hohem Timing-Overhead TIMING OFF verwenden; COSTS OFF blendet auch den JIT-Abschnitt aus.","since":"PostgreSQL 13","note":"Die Option SETTINGS kam mit PG12, WAL mit PG13. PG16 ergänzte GENERIC_PLAN, PG17 SERIALIZE und MEMORY; in PG18 ist BUFFERS bei ANALYZE standardmäßig aktiv.","tags":["explain analyze","buffers","execution plan","reading plans","wal","performance"]},{"id":"plan-regression-queryid","task":"Planregressionen per queryid erkennen (Snapshot-Vergleich von pg_stat_statements)","category":"Performance","command":"-- 1) Take a reference snapshot (before deploy/upgrade/ANALYZE)\nCREATE TABLE IF NOT EXISTS pgss_snapshot AS\n  SELECT now() AS snap_time, userid, dbid, queryid, calls, total_exec_time,\n         shared_blks_hit, shared_blks_read, left(query, 200) AS query\n  FROM pg_stat_statements WITH NO DATA;\nINSERT INTO pgss_snapshot\n  SELECT now(), userid, dbid, queryid, calls, total_exec_time,\n         shared_blks_hit, shared_blks_read, left(query, 200)\n  FROM pg_stat_statements;\n\n-- 2) Later: queries whose time per call grew the most since the snapshot\nWITH base AS (\n  SELECT DISTINCT ON (userid, dbid, queryid) *\n  FROM pgss_snapshot\n  ORDER BY userid, dbid, queryid, snap_time DESC\n)\nSELECT s.queryid,\n       s.calls - b.calls AS new_calls,\n       round((b.total_exec_time / b.calls)::numeric, 2) AS old_mean_ms,\n       round(((s.total_exec_time - b.total_exec_time) / (s.calls - b.calls))::numeric, 2) AS new_mean_ms,\n       round((b.shared_blks_hit + b.shared_blks_read)::numeric / b.calls, 0) AS old_blks_per_call,\n       round((s.shared_blks_hit + s.shared_blks_read - b.shared_blks_hit - b.shared_blks_read)::numeric\n             / (s.calls - b.calls), 0) AS new_blks_per_call,\n       left(regexp_replace(s.query, '\\s+', ' ', 'g'), 100) AS query\nFROM pg_stat_statements s\nJOIN base b USING (userid, dbid, queryid)\nWHERE s.calls - b.calls >= 10\n  AND b.calls > 0\nORDER BY ((s.total_exec_time - b.total_exec_time) / (s.calls - b.calls))\n         / NULLIF(b.total_exec_time / b.calls, 0) DESC NULLS LAST\nLIMIT 20;\n\n-- 3) Is the suspicious queryid running right now?\nSELECT pid, state, now() - query_start AS runtime, left(query, 100) AS query\nFROM pg_stat_activity\nWHERE query_id = -4094682686355101727;","what":"Speichert die kumulierten Zähler von pg_stat_statements in einer Tabelle und vergleicht sie je queryid mit dem späteren Stand; zeigt mittlere Zeit und Buffers je Aufruf der nach dem Snapshot erfolgten Aufrufe neben den alten Werten.","when":"Um die Abfrage zu finden, deren Plan sich nach einem Deploy, Major-Upgrade, Statistik-Refresh oder einer Parameteränderung geändert hat ('gestern schnell, heute langsam').","caution":"Läuft pg_stat_statements_reset() zwischen Snapshot und Vergleich oder werden Einträge wegen Platzmangels verdrängt (dealloc), sind die Deltas bedeutungslos; prüfe pg_stat_statements_info.stats_reset. Ein Anstieg der Buffers je Aufruf ist ein zuverlässigeres Zeichen für eine Planänderung als ein Anstieg der Zeit, weil er unabhängig vom Caching ist. Um den Plan zu bestätigen, führe EXPLAIN mit echten Parametern aus oder nutze auto_explain; PostgreSQL hat kein eingebautes Plan-Pinning (die Extension pg_hint_plan ist nötig). Lösche die Snapshot-Tabelle, wenn du fertig bist.","since":"PostgreSQL 14","note":"pg_stat_activity.query_id und der Parameter compute_query_id kamen mit PG14 (Standard auto: berechnet, wenn pg_stat_statements geladen ist). Die Zeile 'Query Identifier' in der Ausgabe von EXPLAIN VERBOSE zeigt denselben Wert. Schritt 1 und 2 funktionieren auch in PG13.","tags":["plan regression","queryid","pg_stat_statements","snapshot","plan change","performance"]},{"id":"parallel-query-usage","task":"Kapazität für Parallel Query und aktuelle Worker-Nutzung prüfen","category":"Performance","command":"-- Parallel worker pool settings\nSELECT name, current_setting(name) AS value\nFROM pg_settings\nWHERE name IN ('max_worker_processes', 'max_parallel_workers', 'max_parallel_workers_per_gather',\n               'max_parallel_maintenance_workers', 'parallel_setup_cost', 'parallel_tuple_cost',\n               'min_parallel_table_scan_size', 'min_parallel_index_scan_size')\nORDER BY name;\n\n-- Queries currently running in parallel and their worker counts\nSELECT a.pid AS leader_pid,\n       count(w.pid) AS workers_running,\n       current_setting('max_parallel_workers')::int AS max_parallel_workers,\n       now() - a.query_start AS runtime,\n       left(a.query, 80) AS query\nFROM pg_stat_activity a\nJOIN pg_stat_activity w ON w.leader_pid = a.pid AND w.pid <> a.pid\nGROUP BY a.pid, a.query_start, a.query\nORDER BY workers_running DESC;\n\n-- Pin / reset the planned worker count for a single table\nALTER TABLE orders SET (parallel_workers = 4);\nALTER TABLE orders RESET (parallel_workers);","what":"Zeigt die Limits für Parallel Query und wie viele parallele Worker jeder Leader-Prozess gerade nutzt; demonstriert die Einstellung der Worker-Anzahl je Tabelle.","when":"Wenn analytische Abfragen langsamer sind als erwartet, der Plan 'Workers Planned' > 'Workers Launched' zeigt oder CPU-Kerne ungenutzt sind.","caution":"max_parallel_workers wird durch max_worker_processes begrenzt (Neustart erforderlich), und dieser Pool wird mit Workern der logischen Replikation und anderen Background Workern geteilt. In OLTP sättigt ein hoher max_parallel_workers_per_gather unter Parallelität die CPU; jeder Worker nutzt außerdem sein eigenes work_mem. Abfragen, die Daten schreiben oder Zeilen sperren (außer CREATE TABLE AS / SELECT INTO), Cursors und Abfragen mit PARALLEL-UNSAFE-Funktionen erhalten keine parallelen Pläne. ALTER TABLE ... SET (parallel_workers) nimmt kurz einen ShareUpdateExclusiveLock.","since":"PostgreSQL 13","note":"pg_stat_activity.leader_pid kam mit PG13; davor erscheinen parallele Worker nur als backend_type = 'parallel worker' und lassen sich nicht ihrem Leader zuordnen.","tags":["parallel query","parallel worker","leader_pid","max_parallel_workers","gather","cpu"]},{"id":"jit-impact-check","task":"Auswirkung der JIT-Kompilierung auf die Abfragezeit messen","category":"Performance","command":"-- Share of JIT time in total execution time (PG15+)\nSELECT queryid,\n       calls,\n       jit_functions,\n       round((jit_generation_time + jit_inlining_time + jit_optimization_time + jit_emission_time)::numeric, 1) AS jit_ms,\n       round(total_exec_time::numeric, 1) AS total_ms,\n       round((100 * (jit_generation_time + jit_inlining_time + jit_optimization_time + jit_emission_time)\n             / NULLIF(total_exec_time, 0))::numeric, 1) AS jit_pct,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 100) AS query\nFROM pg_stat_statements\nWHERE jit_functions > 0\nORDER BY jit_pct DESC NULLS LAST\nLIMIT 20;\n\n-- Compare on a single query: check the \"JIT:\" section at the end of the output and Execution Time\nSET jit = on;\nEXPLAIN (ANALYZE) SELECT city, sum(balance) FROM customer GROUP BY city;\nSET jit = off;\nEXPLAIN (ANALYZE) SELECT city, sum(balance) FROM customer GROUP BY city;\nRESET jit;","what":"Zeigt je Abfrage den Anteil der JIT-Kompilierungszeit (Generation, Inlining, Optimization, Emission) an der Ausführungszeit; misst den Nettoeffekt, indem dieselbe Abfrage mit JIT an und aus ausgeführt wird.","when":"Wenn kurze OLTP- oder Reporting-Abfragen unerklärliche Verzögerungen von zehn bis hunderten ms zeigen; besonders bei stark partitionierten Tabellen, bei denen überhöhte Kostenschätzungen JIT auslösen.","caution":"JIT greift nur, wenn die Plankosten jit_above_cost übersteigen (Standard 100000); bei überhöhten Kostenschätzungen kann die Kompilierung bei kurzen Abfragen länger dauern als die Ausführung. Statt es global abzuschalten, erwäge zuerst ALTER ROLE ... SET jit = off für die betroffene Rolle/Datenbank oder ein Anheben von jit_above_cost. EXPLAIN (ANALYZE, COSTS OFF) gibt den JIT-Abschnitt nicht aus. Wurde PostgreSQL ohne LLVM-Unterstützung gebaut, wird JIT nie verwendet; prüfe mit SELECT pg_jit_available();","since":"PostgreSQL 15","note":"Die jit_*-Spalten kamen in PG15 zu pg_stat_statements hinzu; PG17 ergänzte jit_deform_count/jit_deform_time. JIT wurde in PG11 eingeführt und ist seit PG12 standardmäßig aktiv.","tags":["jit","llvm","jit_above_cost","pg_stat_statements","compilation time","performance"]},{"id":"blocking-tree","task":"Blockierungsbaum aufbauen (Root Blocker finden)","category":"Sessions","command":"WITH RECURSIVE tree AS (\n  SELECT a.pid, a.pid AS root_pid, 0 AS level, ARRAY[a.pid] AS path\n  FROM pg_stat_activity a\n  WHERE cardinality(pg_blocking_pids(a.pid)) = 0\n    AND EXISTS (SELECT 1 FROM pg_stat_activity w WHERE a.pid = ANY (pg_blocking_pids(w.pid)))\n  UNION ALL\n  SELECT w.pid, t.root_pid, t.level + 1, t.path || w.pid\n  FROM tree t\n  JOIN pg_stat_activity w ON t.pid = ANY (pg_blocking_pids(w.pid))\n  WHERE w.pid <> ALL (t.path)\n), node AS (\n  SELECT DISTINCT ON (pid) pid, root_pid, level\n  FROM tree\n  ORDER BY pid, level DESC\n)\nSELECT repeat('  ', n.level) || n.pid AS pid_tree,\n       n.root_pid,\n       pg_blocking_pids(n.pid) AS blocked_by,\n       a.usename,\n       a.state,\n       a.wait_event_type || ':' || a.wait_event AS wait,\n       now() - a.xact_start AS xact_age,\n       left(regexp_replace(a.query, '\\s+', ' ', 'g'), 80) AS query\nFROM node n\nJOIN pg_stat_activity a USING (pid)\nORDER BY n.root_pid, n.level, n.pid;","what":"Verfolgt die Wartekette rekursiv mit pg_blocking_pids(); ausgehend vom Root Blocker (der auf niemanden wartet) zeigt es jede wartende Session als eingerückten Baum mit den PIDs, die sie direkt blockieren, und ihrem Transaktionsalter.","when":"Wenn die Anwendung massenhaft Timeouts oder sich stauende Warteschlangen sieht; um unter Dutzenden Wartenden die eine Root-Session zu finden, bei der eingegriffen werden muss.","caution":"Der Root ist oft eine vergessene 'idle in transaction'-Session, die die Sperre hält; die Spalte query zeigt die LETZTE Anweisung dieser Session, nicht die, die die Sperre genommen hat. Eine DDL, die auf ACCESS EXCLUSIVE wartet, blockiert auch einfache SELECTs, die sich dahinter einreihen, und erscheint als Zwischenknoten. pg_blocking_pids() sperrt kurz den Lock Manager; führe diese Abfrage nicht in einer engen Schleife aus. Eine über mehrere Zweige erreichbare Session wird einmal angezeigt, auf ihrer tiefsten Ebene.","since":"PostgreSQL 9.6","tags":["blocking","lock tree","lock chain","pg_blocking_pids","blocking session","lock"]},{"id":"lock-wait-detail","task":"Sperrwartezustände im Detail nach Objekt und Sperrmodus untersuchen (pg_locks)","category":"Sessions","command":"SELECT DISTINCT\n       w.pid AS waiting_pid,\n       w.locktype,\n       coalesce(w.relation::regclass::text, 'xid ' || w.transactionid, w.virtualxid, w.objid::text) AS target,\n       w.mode AS wanted_mode,\n       b.pid AS blocker_pid,\n       b.mode AS blocker_mode,\n       b.granted AS blocker_holds,\n       now() - wa.state_change AS waiting_for,\n       left(wa.query, 60) AS waiting_query,\n       left(ba.query, 60) AS blocker_query\nFROM pg_locks w\nJOIN pg_locks b\n  ON b.pid = ANY (pg_blocking_pids(w.pid))\n AND b.locktype = w.locktype\n AND b.database IS NOT DISTINCT FROM w.database\n AND b.relation IS NOT DISTINCT FROM w.relation\n AND b.page IS NOT DISTINCT FROM w.page\n AND b.tuple IS NOT DISTINCT FROM w.tuple\n AND b.virtualxid IS NOT DISTINCT FROM w.virtualxid\n AND b.transactionid IS NOT DISTINCT FROM w.transactionid\n AND b.classid IS NOT DISTINCT FROM w.classid\n AND b.objid IS NOT DISTINCT FROM w.objid\n AND b.objsubid IS NOT DISTINCT FROM w.objsubid\nJOIN pg_stat_activity wa ON wa.pid = w.pid\nJOIN pg_stat_activity ba ON ba.pid = b.pid\nWHERE NOT w.granted\nORDER BY waiting_for DESC, waiting_pid;","what":"Zeigt für jede nicht gewährte Sperranforderung (granted = false) das Zielobjekt (Tabelle, Transaction ID usw.), den angeforderten Modus, die blockierende PID und deren Modus, ob der Blocker die Sperre tatsächlich hält, und die ungefähre Wartezeit.","when":"Um zu verstehen, welcher Konflikt aus Tabelle und Sperrmodus die Blockierung verursacht; z. B. eine DDL, die auf AccessExclusiveLock wartet, oder eine Zeilensperre, die als Wait auf transactionid erscheint.","caution":"Da der Abgleich pg_blocking_pids() nutzt, werden nur tatsächlich kollidierende Anforderungen gelistet. blocker_holds = false bedeutet, dass der Blocker die Sperre noch nicht hält, aber mit einem kollidierenden Modus weiter vorn in der Warteschlange steht (Lock Queue). locktype = 'transactionid' ist ein Konflikt auf Zeilenebene: Der Wartende wartet, bis die Transaktion endet, die die Zeile geändert hat; die Zeile selbst ist in pg_locks nicht sichtbar. pg_locks kann auf großen Systemen Tausende Zeilen enthalten; nicht häufig ausführen. waiting_for ist ein Näherungswert auf Basis von state_change.","since":"PostgreSQL 9.6","tags":["pg_locks","lock wait","lock waiting","accessexclusivelock","transactionid","lock"]},{"id":"long-idle-transactions","task":"Lang laufende und 'idle in transaction'-Transaktionen finden","category":"Sessions","command":"SELECT pid,\n       usename,\n       datname,\n       application_name,\n       client_addr,\n       state,\n       now() - xact_start AS xact_age,\n       now() - state_change AS in_state_for,\n       backend_xid,\n       age(backend_xmin) AS xmin_age,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 100) AS last_query\nFROM pg_stat_activity\nWHERE xact_start IS NOT NULL\n  AND backend_type = 'client backend'\n  AND pid <> pg_backend_pid()\n  AND (now() - xact_start > interval '5 minutes' OR state LIKE 'idle in transaction%')\nORDER BY xact_start;","what":"Zeigt Sessions, deren offene Transaktion älter als 5 Minuten ist oder die inaktiv in einer Transaktion verharren; mit Transaktionsalter, Zeit im aktuellen Zustand, zugewiesener XID und dem Alter des gehaltenen xmin.","when":"Wenn Bloat wächst, VACUUM 'dead row versions cannot be removed yet' meldet, bei Sperrwartezuständen oder wenn ein Leck im Connection Pool vermutet wird.","caution":"Lange Transaktionen (besonders REPEATABLE READ/SERIALIZABLE oder solche mit offenen Cursors) halten über backend_xmin den Bereinigungshorizont von VACUUM (xmin horizon) zurück und verursachen datenbankweit Bloat und XID-Alterung. 'idle in transaction (aborted)' ist eine Session, die auf einen Fehler gestoßen ist und auf ROLLBACK wartet. Dauerhafte Lösung: idle_in_transaction_session_timeout je Rolle und korrekte Transaktionsgrenzen in der Anwendung.","tags":["long transaction","idle in transaction","xmin","bloat","session","long running"]},{"id":"terminate-idle-in-transaction","task":"Lange 'idle in transaction'-Sessions kontrolliert in großer Zahl beenden","category":"Sessions","command":"-- 1) Dry run first: who will be affected?\nSELECT pid, usename, application_name, client_addr,\n       now() - state_change AS idle_for, left(query, 80) AS last_query\nFROM pg_stat_activity\nWHERE state IN ('idle in transaction', 'idle in transaction (aborted)')\n  AND now() - state_change > interval '15 minutes'\n  AND backend_type = 'client backend'\n  AND pid <> pg_backend_pid();\n\n-- 2) Terminate; wait up to 5 s for each process to exit\nSELECT pid, usename, now() - state_change AS idle_for,\n       pg_terminate_backend(pid, 5000) AS terminated\nFROM pg_stat_activity\nWHERE state IN ('idle in transaction', 'idle in transaction (aborted)')\n  AND now() - state_change > interval '15 minutes'\n  AND backend_type = 'client backend'\n  AND pid <> pg_backend_pid();","what":"Listet zuerst Sessions, die eine Transaktion eine bestimmte Zeit offen und inaktiv gelassen haben, und beendet sie dann; der Parameter timeout wartet, bis der Prozess tatsächlich beendet ist (oder das Timeout abläuft).","when":"Für einen Notfalleingriff, wenn vergessene Sessions mit Sperren Blockierungen verursachen oder den xmin-Horizont zurückhalten und VACUUM ausbremsen.","caution":"pg_cancel_backend bewirkt bei einer 'idle in transaction'-Session nichts (es gibt keine laufende Abfrage zum Abbrechen); pg_terminate_backend ist nötig, und alle nicht committeten Änderungen werden zurückgerollt. Grenze den Filter über usename/application_name ein; schließe Replikations-, Backup- und Monitoring-Sessions aus. Connection Pools öffnen beendete Verbindungen erneut. Läuft das Timeout ab, gibt die Funktion eine Warnung aus und liefert false. Als dauerhafte Lösung idle_in_transaction_session_timeout je Rolle setzen.","since":"PostgreSQL 14","note":"Das zweiargumentige pg_terminate_backend(pid, timeout) kam mit PG14; davor gibt es nur pg_terminate_backend(pid), das direkt nach dem Senden des Signals zurückkehrt.","tags":["pg_terminate_backend","idle in transaction","terminate session","kill session","bulk","lock"]},{"id":"role-timeouts","task":"statement_timeout / lock_timeout je Rolle und Datenbank setzen","category":"Sessions","command":"-- Application role: short queries, short lock waits, a limit on forgotten transactions\nALTER ROLE web_app SET statement_timeout = '30s';\nALTER ROLE web_app SET lock_timeout = '5s';\nALTER ROLE web_app SET idle_in_transaction_session_timeout = '10min';\n\n-- Let the reporting role run longer, only in this database\nALTER ROLE report_app IN DATABASE mydb SET statement_timeout = '15min';\n\n-- Show configured role/database settings\nSELECT coalesce(r.rolname, '(all)') AS role,\n       coalesce(d.datname, '(all)') AS database,\n       s.setconfig\nFROM pg_db_role_setting s\nLEFT JOIN pg_roles r ON r.oid = s.setrole\nLEFT JOIN pg_database d ON d.oid = s.setdatabase\nORDER BY 1, 2;\n\n-- Revert\nALTER ROLE web_app RESET lock_timeout;","what":"Macht Timeout-Parameter auf Ebene der Rolle (und optional der Datenbank) dauerhaft, sodass jede Workload mit eigenen Limits läuft, ohne die globale Einstellung anzufassen.","when":"Damit ausufernde Abfragen keine Ressourcen erschöpfen, DDL-/Sperr-Warteschlangen die Anwendung nicht einfrieren und vergessene Transaktionen keinen Bloat erzeugen.","caution":"Einstellungen gelten nur für NEUE Sessions; bestehende Pool-Verbindungen recyceln. Benutzer können den Wert in ihrer Session mit SET überschreiben; dies ist keine Sicherheitsgrenze. Setze statement_timeout nicht global in postgresql.conf: Wartungsjobs wie pg_dump, VACUUM und CREATE INDEX werden sonst ebenfalls abgebrochen. Vorrang: Rolle+Datenbank > Rolle > Datenbank > postgresql.conf.","note":"idle_in_transaction_session_timeout kam mit PG9.6, idle_session_timeout mit PG14, transaction_timeout mit PG17.","tags":["statement_timeout","lock_timeout","idle_in_transaction_session_timeout","alter role","query timeout","timeout"]},{"id":"ddl-lock-timeout-retry","task":"DDL sicher mit lock_timeout und Wiederholungen anwenden (Lock Queues vermeiden)","category":"Sessions","command":"-- Single attempt: give up if the lock is not acquired within 3 s instead of queueing and blocking traffic\nSET lock_timeout = '3s';\nALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;\nRESET lock_timeout;\n\n-- Automatic retry: up to 5 attempts, 3 s apart\nDO $$\nDECLARE\n  attempt int := 0;\nBEGIN\n  LOOP\n    attempt := attempt + 1;\n    BEGIN\n      SET LOCAL lock_timeout = '2s';\n      ALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;\n      RAISE NOTICE 'OK, attempt %', attempt;\n      EXIT;\n    EXCEPTION WHEN lock_not_available THEN\n      RAISE NOTICE 'lock not acquired, attempt %', attempt;\n      IF attempt >= 5 THEN RAISE; END IF;\n      PERFORM pg_sleep(3);\n    END;\n  END LOOP;\nEND $$;","what":"Verhindert, dass eine DDL, die ACCESS EXCLUSIVE braucht, endlos wartet, wenn sie die Sperre nicht bekommt, und dabei jede dahinter eingereihte Abfrage blockiert (Lock Queue); wiederholt den Versuch in kurzen Abständen.","when":"Beim Anwenden schneller, aber stark sperrender Änderungen wie ALTER TABLE, CREATE/DROP TRIGGER, ATTACH PARTITION oder RENAME auf stark genutzten Tabellen in der Produktion.","caution":"Ohne lock_timeout lässt ein ALTER TABLE, das hinter einem einzigen langen SELECT wartet, auch jeden folgenden Lese-/Schreibzugriff warten und kann binnen Minuten den Connection Pool erschöpfen. lock_timeout begrenzt nur das Warten auf die Sperre, nicht die Laufzeit einer DDL, die die Tabelle neu schreibt (z. B. Änderung eines Spaltentyps). Der DO-Block ist eine einzige Transaktion und hält über alle Versuche hinweg einen Snapshot; für lange Wiederholungsszenarien besser eine externe Shell-Schleife verwenden.","tags":["lock_timeout","ddl","alter table","lock queue","retry","lock"]},{"id":"connection-usage","task":"Verbindungsnutzung im Verhältnis zu max_connections messen","category":"Sessions","command":"-- Capacity: used slots and slots left for regular users\nSELECT max_conn,\n       reserved,\n       used,\n       max_conn - reserved - used AS free_for_normal_users,\n       round(100.0 * used / max_conn, 1) AS used_pct\nFROM (SELECT current_setting('max_connections')::int AS max_conn,\n             current_setting('superuser_reserved_connections')::int\n             + coalesce(current_setting('reserved_connections', true)::int, 0) AS reserved,\n             (SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend') AS used) x;\n\n-- Breakdown by database, user, application and state\nSELECT datname, usename, application_name, state,\n       count(*) AS sessions,\n       max(now() - state_change) AS longest_in_state\nFROM pg_stat_activity\nWHERE backend_type = 'client backend'\nGROUP BY datname, usename, application_name, state\nORDER BY sessions DESC;\n\n-- Role and database connection limits\nSELECT rolname, rolconnlimit FROM pg_roles WHERE rolconnlimit <> -1;\nSELECT datname, datconnlimit FROM pg_database WHERE datconnlimit <> -1;","what":"Zeigt belegte Verbindungsslots und die für normale Benutzer verbleibenden im Verhältnis zu max_connections, die Aufschlüsselung nach Datenbank/Benutzer/Anwendung/Zustand sowie konfigurierte CONNECTION LIMITs.","when":"Bei den Fehlern 'too many clients already' / 'remaining connection slots are reserved', bei der Kapazitätsplanung oder bei der Bewertung, ob ein Pooler nötig ist.","caution":"max_connections begrenzt nur Client-Backends; Autovacuum-Worker, Background Worker und walsender nutzen eigene Slots. Die letzten superuser_reserved_connections-Slots (plus reserved_connections ab PG16) sind für normale Benutzer gesperrt. Statt max_connections auf Hunderte oder Tausende anzuheben, nutze einen Pooler wie PgBouncer: Jede Verbindung ist ein eigener Prozess, und Speicher- und Snapshot-Kosten wachsen mit der Verbindungszahl. Viele 'idle'-Sessions deuten auf einen überdimensionierten Anwendungs-Pool hin. Verhindere mit ALTER ROLE ... CONNECTION LIMIT, dass eine außer Kontrolle geratene Anwendung alle Slots belegt; eine Änderung von max_connections erfordert einen Neustart.","note":"Der Parameter reserved_connections und die Rolle pg_use_reserved_connections kamen mit PG16; die Abfrage funktioniert auch in älteren Versionen (fehlt der Parameter, zählt er als 0).","tags":["max_connections","connection count","connection","pooler","superuser_reserved_connections","session"]},{"id":"autovacuum-progress","task":"Fortschritt laufender Autovacuum-/VACUUM-Vorgänge überwachen","category":"Wartung","command":"SELECT p.pid,\n       a.backend_type,\n       p.datname,\n       p.relid::regclass AS table_name,\n       p.phase,\n       pg_size_pretty(p.heap_blks_total * current_setting('block_size')::bigint) AS table_size,\n       round(100.0 * p.heap_blks_scanned / NULLIF(p.heap_blks_total, 0), 1) AS scanned_pct,\n       round(100.0 * p.heap_blks_vacuumed / NULLIF(p.heap_blks_total, 0), 1) AS vacuumed_pct,\n       p.index_vacuum_count,\n       now() - a.xact_start AS running_for,\n       a.wait_event_type || ':' || a.wait_event AS wait,\n       left(a.query, 80) AS query\nFROM pg_stat_progress_vacuum p\nJOIN pg_stat_activity a USING (pid)\nORDER BY running_for DESC;\n\n-- Busy autovacuum workers / upper limit\nSELECT count(*) FILTER (WHERE backend_type = 'autovacuum worker') AS av_workers_busy,\n       current_setting('autovacuum_max_workers') AS av_max_workers\nFROM pg_stat_activity;","what":"Zeigt für jedes laufende VACUUM (Autovacuum-Worker oder manuell) Tabelle, Phase, gescannten/bereinigten Heap-Anteil in Prozent, Anzahl der Index-Vacuum-Durchläufe und Wait Event; dazu die Anzahl beschäftigter Autovacuum-Worker.","when":"Um abzuschätzen, wann ein VACUUM auf einer großen Tabelle fertig wird, ob alle Autovacuum-Worker beschäftigt sind (Tabellen in der Warteschlange) und ob Vacuum wegen Drosselung wartet.","caution":"index_vacuum_count > 1 bedeutet, dass die Liste toter Tupel nicht in den Speicher passte und Indizes mehrfach gescannt wurden; erhöhe maintenance_work_mem/autovacuum_work_mem (vor PG17 wird dafür höchstens 1GB genutzt). wait = Timeout:VacuumDelay ist kostenbasierte Drosselung (autovacuum_vacuum_cost_delay/cost_limit). Beende kein Autovacuum, dessen Abfragetext 'to prevent wraparound' enthält; es startet sofort neu. VACUUM FULL und CLUSTER erscheinen in pg_stat_progress_cluster, nicht in dieser View.","since":"PostgreSQL 9.6","note":"In PG17 wurden die Spalten max_dead_tuples/num_dead_tuples zu max_dead_tuple_bytes/num_dead_item_ids, und indexes_total/indexes_processed kamen hinzu; die Abfrage nutzt daher versionsunabhängige Spalten.","tags":["autovacuum","vacuum progress","pg_stat_progress_vacuum","vacuum","maintenance","progress"]},{"id":"xid-wraparound-risk","task":"Risiko eines Transaction-ID-Wraparounds messen (XID-Alter je Datenbank und Tabelle)","category":"Wartung","command":"-- Per database: distance to autovacuum_freeze_max_age and the ~2^31 limit\nSELECT datname,\n       age(datfrozenxid) AS xid_age,\n       mxid_age(datminmxid) AS mxid_age,\n       round(100.0 * age(datfrozenxid) / current_setting('autovacuum_freeze_max_age')::int, 1) AS pct_of_freeze_max_age,\n       round(100.0 * age(datfrozenxid) / 2147483647, 1) AS pct_of_wraparound\nFROM pg_database\nORDER BY age(datfrozenxid) DESC;\n\n-- Oldest tables in the current database (including TOAST)\nSELECT c.oid::regclass AS table_name,\n       greatest(age(c.relfrozenxid), age(t.relfrozenxid)) AS xid_age,\n       mxid_age(c.relminmxid) AS mxid_age,\n       pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,\n       s.last_autovacuum,\n       s.last_vacuum\nFROM pg_class c\nLEFT JOIN pg_class t ON t.oid = c.reltoastrelid\nLEFT JOIN pg_stat_all_tables s ON s.relid = c.oid\nWHERE c.relkind IN ('r', 'm')\nORDER BY 2 DESC\nLIMIT 20;","what":"Zeigt für jede Datenbank das Alter der ältesten nicht eingefrorenen XID und Multixact im Verhältnis zu autovacuum_freeze_max_age und zur Wraparound-Grenze; in der aktuellen Datenbank die ältesten Tabellen (inkl. TOAST) mit Größe und letzter Vacuum-Zeit.","when":"Im Routine-Monitoring (typische Schwellen: pct_of_wraparound 50 % Warnung / 75 % kritisch), wenn das Log 'must be vacuumed within N transactions' zeigt, und um Freeze-Arbeiten zu priorisieren.","caution":"Bei Tabellen mit pct_of_freeze_max_age über 100 startet Autovacuum ein verpflichtendes Vacuum (Anti-Wraparound); es ist I/O-intensiv und startet auch nach dem Beenden neu. Sinkt das Alter nicht, ist die Ursache fast immer etwas, das den xmin-Horizont zurückhält (lange Transaktion, Prepared Transaction, Replication Slot, hot_standby_feedback); behebe das zuerst, sonst kann selbst VACUUM FREEZE das Alter nicht senken. Nahe der Grenze warnt der Server zuerst und verweigert dann die Vergabe neuer XIDs (Schreibvorgänge), wenn nur noch wenige Millionen übrig sind. Für ein gezieltes Freeze: VACUUM (FREEZE, VERBOSE) <table>;","note":"PG14 ergänzte vacuum_failsafe_age / vacuum_multixact_failsafe_age (Standard 1,6 Milliarden): Erreicht eine Tabelle dieses Alter, wechselt VACUUM in einen Notfallmodus, der Cost Delay und Index-Bereinigung überspringt.","tags":["wraparound","xid age","datfrozenxid","relfrozenxid","freeze","multixact"]},{"id":"xmin-horizon-holders","task":"xmin-Horizont-Halter finden, die VACUUM am Entfernen toter Tupel hindern","category":"Wartung","command":"SELECT 'session' AS source, pid::text AS id, usename::text AS owner, state AS detail,\n       age(backend_xmin) AS xmin_age\nFROM pg_stat_activity\nWHERE backend_xmin IS NOT NULL AND pid <> pg_backend_pid()\nUNION ALL\nSELECT 'prepared_xact', gid, owner::text, prepared::text, age(transaction)\nFROM pg_prepared_xacts\nUNION ALL\nSELECT 'replication_slot', slot_name::text, slot_type, active::text,\n       greatest(age(xmin), age(catalog_xmin))\nFROM pg_replication_slots\nWHERE xmin IS NOT NULL OR catalog_xmin IS NOT NULL\nUNION ALL\nSELECT 'standby_feedback', application_name, usename::text, state, age(backend_xmin)\nFROM pg_stat_replication\nWHERE backend_xmin IS NOT NULL\nORDER BY xmin_age DESC NULLS LAST;","what":"Listet, sortiert nach xmin-Alter, die vier Quellen, die die Bereinigung toter Tupel blockieren können: Sessions mit Snapshots, Prepared Transactions, Replication Slots und Standbys, die hot_standby_feedback senden.","when":"Wenn VACUUM VERBOSE 'dead row versions cannot be removed yet' meldet, Bloat weiter wächst und das XID-Alter nicht sinkt.","caution":"Das älteste xmin bestimmt den Bereinigungshorizont; Replication Slots und Standby-Feedback wirken auf alle Datenbanken. Prepared Transactions überstehen Neustarts; prüfe den Eigentümer und schließe sie mit COMMIT PREPARED / ROLLBACK PREPARED '<gid>'. Das Löschen eines ungenutzten Slots unterbricht seinen Downstream (Standby, CDC); prüfe zuerst den Eigentümer. Auf einem Standby ausgeführt, zeigt es nur die Sessions dieses Standby.","tags":["xmin horizon","vacuum blocker","dead tuple","prepared transaction","replication slot","bloat"]},{"id":"pgstattuple-bloat","task":"Tabellen- und Index-Bloat mit pgstattuple messen","category":"Wartung","command":"CREATE EXTENSION IF NOT EXISTS pgstattuple;\n\n-- Fast estimate: skips pages marked all-visible in the visibility map\nSELECT pg_size_pretty(table_len) AS table_size,\n       round(approx_tuple_percent::numeric, 1) AS live_pct,\n       round(dead_tuple_percent::numeric, 1) AS dead_pct,\n       round(approx_free_percent::numeric, 1) AS free_pct\nFROM pgstattuple_approx('orders');\n\n-- Exact measurement: reads the whole table\nSELECT pg_size_pretty(table_len) AS table_size,\n       round(dead_tuple_percent::numeric, 1) AS dead_pct,\n       round(free_percent::numeric, 1) AS free_pct,\n       pg_size_pretty(dead_tuple_len + free_space) AS reclaimable\nFROM pgstattuple('orders');\n\n-- B-tree indexes: leaf density (~90 when freshly built; below 50 is bloated)\nWITH idx AS MATERIALIZED (\n  SELECT i.indexrelid\n  FROM pg_index i\n  JOIN pg_class c ON c.oid = i.indexrelid\n  JOIN pg_am am ON am.oid = c.relam\n  WHERE am.amname = 'btree' AND i.indrelid = 'orders'::regclass\n)\nSELECT idx.indexrelid::regclass AS index_name,\n       pg_size_pretty(s.index_size) AS index_size,\n       s.avg_leaf_density,\n       s.leaf_fragmentation\nFROM idx\nCROSS JOIN LATERAL pgstatindex(idx.indexrelid) s;","what":"Zeigt die Anteile lebender/toter Tupel und freien Platzes einer Tabelle (schnelle Schätzung und exakt) sowie den rückgewinnbaren Platz; bei B-Tree-Indizes Leaf-Dichte und Fragmentierung.","when":"Wenn die Schätzung n_dead_tup in pg_stat_user_tables nicht ausreicht; um eine Entscheidung für pg_repack, VACUUM FULL oder REINDEX mit Daten zu untermauern.","caution":"pgstattuple und pgstatindex lesen die gesamte Relation und erzeugen bei großen Tabellen viel I/O (sie nehmen nur AccessShareLock). Nutze zuerst pgstattuple_approx. Standardmäßig ist Superuser oder die Rolle pg_stat_scan_tables nötig. Etwas freier Platz ist normal (fillfactor, Raum für HOT-Updates); VACUUM macht ihn wiederverwendbar, verkleinert aber die Datei nicht; zur Rückgewinnung sind pg_repack oder VACUUM FULL (ACCESS-EXCLUSIVE-Sperre!) nötig. Baue einen Index mit niedriger avg_leaf_density mit REINDEX CONCURRENTLY neu auf.","since":"PostgreSQL 12","note":"pgstattuple ist eine contrib-Extension; pgstattuple_approx kam mit PG9.5. MATERIALIZED in der CTE (ab PG12) verhindert, dass pgstatindex für Nicht-B-Tree-Indizes aufgerufen wird und fehlschlägt.","tags":["bloat","pgstattuple","pgstatindex","index bloat","dead tuple","free space"]},{"id":"pg-repack-online","task":"Aufgeblähte Tabelle ohne lange Sperren reorganisieren (pg_repack)","category":"Wartung","command":"-- Once per target database (the pg_repack package must be installed on the server)\nCREATE EXTENSION pg_repack;\n\n# Dry run: show what would be done\npg_repack -h localhost -U postgres -d mydb --table=public.orders --dry-run\n\n# Rewrite the table and its indexes: 2 parallel index builds, wait at most 60 s for locks\npg_repack -h localhost -U postgres -d mydb --table=public.orders --jobs=2 --wait-timeout=60 --no-kill-backend\n\n# Rebuild indexes only\npg_repack -h localhost -U postgres -d mydb --table=public.orders --only-indexes","what":"Baut im Hintergrund eine verdichtete Kopie der Tabelle auf, erfasst parallele Änderungen per Trigger in einer Log-Tabelle und tauscht die Tabellen am Ende unter einer kurzen ACCESS-EXCLUSIVE-Sperre; anders als VACUUM FULL blockiert es Lesen/Schreiben nicht für die gesamte Dauer.","when":"Wenn starker Bloat, den VACUUM nicht zurückgeben kann (mit pgstattuple geprüft), auf einem Live-System zurückgewonnen werden muss.","caution":"Die Tabelle braucht einen PRIMARY KEY oder einen UNIQUE-Index auf NOT-NULL-Spalten. Sie benötigt vorübergehend zusätzlichen Plattenplatz in Höhe von Tabelle plus Indizes und erzeugt viel WAL (Replica-Lag, Archivvolumen). ACCESS-EXCLUSIVE-Sperren sind am Anfang und am Ende nötig; läuft --wait-timeout ab, werden standardmäßig kollidierende Abfragen abgebrochen und deren Sessions bei Bedarf beendet, während --no-kill-backend stattdessen die Tabelle überspringt. Führe während des Laufs keine DDL auf der Zieltabelle aus. Client- und Extension-Version müssen übereinstimmen. Bei Abbruch Reste bereinigen mit DROP EXTENSION pg_repack CASCADE; CREATE EXTENSION pg_repack;","note":"pg_repack gehört nicht zum Kern von PostgreSQL (separates Paket, z. B. postgresql-17-repack).","tags":["pg_repack","bloat","online","vacuum full alternative","table reorganization","repack"]},{"id":"invalid-indexes","task":"INVALID-Indizes finden und bereinigen","category":"Wartung","command":"SELECT n.nspname AS schema_name,\n       c.relname AS index_name,\n       t.relname AS table_name,\n       pg_size_pretty(pg_relation_size(c.oid)) AS index_size,\n       i.indisvalid,\n       i.indisready,\n       format('DROP INDEX CONCURRENTLY %I.%I;', n.nspname, c.relname) AS fix\nFROM pg_index i\nJOIN pg_class c ON c.oid = i.indexrelid\nJOIN pg_class t ON t.oid = i.indrelid\nJOIN pg_namespace n ON n.oid = c.relnamespace\nWHERE NOT i.indisvalid OR NOT i.indisready;\n\n-- Before dropping: is an index build still in progress?\nSELECT pid, relid::regclass AS table_name, index_relid::regclass AS index_name, command, phase\nFROM pg_stat_progress_create_index;","what":"Listet ungültige Indizes, die fehlgeschlagene Läufe von CREATE INDEX CONCURRENTLY oder REINDEX CONCURRENTLY hinterlassen haben, mit ihrer Größe und erzeugt den Drop-Befehl.","when":"Nach fehlgeschlagenen oder abgebrochenen Online-Indexoperationen, bei routinemäßigen Health-Checks und vor einem Major-Upgrade.","caution":"Ein laufendes CREATE INDEX CONCURRENTLY / REINDEX CONCURRENTLY erscheint ebenfalls als INVALID; prüfe vor dem Löschen pg_stat_progress_create_index. Ein INVALID-Index wird von Abfragen nicht genutzt, wird bei indisready = true aber weiterhin bei jedem INSERT/UPDATE gepflegt, ist also reiner Overhead. Er lässt sich mit REINDEX INDEX CONCURRENTLY neu aufbauen, was aber erneut fehlschlägt, solange die Ursache (z. B. eine Unique-Verletzung) nicht behoben ist. Reste von REINDEX CONCURRENTLY tragen das Suffix _ccnew/_ccold. DROP INDEX CONCURRENTLY kann nicht innerhalb eines Transaktionsblocks laufen.","tags":["invalid index","indisvalid","create index concurrently","broken index","_ccnew","index"]},{"id":"extended-statistics","task":"Erweiterte Statistiken für korrelierte Spalten anlegen (CREATE STATISTICS)","category":"Wartung","command":"-- city and district are correlated: the planner assumes independence and underestimates rows\nCREATE STATISTICS customer_city_district_stx (dependencies, ndistinct, mcv) ON city, district FROM customer;\nANALYZE customer;\n\n-- Collected statistics\nSELECT statistics_name, attnames, kinds, n_distinct, dependencies\nFROM pg_stats_ext\nWHERE tablename = 'customer';\n\n-- Verify: estimated rows should now be close to actual rows\nEXPLAIN (ANALYZE, TIMING OFF)\nSELECT * FROM customer WHERE city = 'Ankara' AND district = 'Cankaya';","what":"Bringt dem Planner spaltenübergreifende Korrelation bei (funktionale Abhängigkeiten, kombinierte Distinct-Anzahlen, gemeinsame häufigste Werte); korrigiert Zeilenschätzungen für WHERE- und GROUP-BY-Bedingungen über mehrere Spalten.","when":"Wenn EXPLAIN ANALYZE bei Filtern über mehrere Spalten weit weniger geschätzte als tatsächliche Zeilen zeigt (z. B. 12 vs. 570) und deshalb eine falsche Join-Methode/ein falscher Index gewählt wird.","caution":"Wirkt erst nach ANALYZE. Liegt die Anzahl der Spaltenkombinationen weit über dem Statistikziel (Standard 100), kann die mcv-Liste die meisten nicht abdecken, und mcv korrigiert dann eventuell eine Schätzung nicht, die dependencies korrigieren würde. Mit EXPLAIN prüfen; bei Bedarf mcv aus der Definition entfernen oder das Ziel anheben mit ALTER STATISTICS customer_city_district_stx SET STATISTICS 1000;. dependencies wird nur für Gleichheits-/IN-Bedingungen genutzt, ndistinct für GROUP-BY-Schätzungen. Jede Definition verlängert ANALYZE; füge sie nur Spaltengruppen mit nachgewiesener Fehlschätzung hinzu.","since":"PostgreSQL 12","note":"CREATE STATISTICS kam mit PG10 (ndistinct, dependencies), die Art mcv mit PG12, ALTER STATISTICS ... SET STATISTICS mit PG13, Statistiken auf Ausdrücken mit PG14.","tags":["extended statistics","create statistics","correlation","row estimate","cardinality","analyze"]},{"id":"analyze-staleness","task":"Tabellen mit veralteten Statistiken finden (ANALYZE-Strategie)","category":"Wartung","command":"SELECT schemaname,\n       relname AS table_name,\n       n_live_tup,\n       n_mod_since_analyze,\n       round(100.0 * n_mod_since_analyze / NULLIF(n_live_tup, 0), 1) AS mod_pct,\n       greatest(last_analyze, last_autoanalyze) AS last_analyzed,\n       round(current_setting('autovacuum_analyze_threshold')::int\n             + current_setting('autovacuum_analyze_scale_factor')::numeric * n_live_tup) AS autoanalyze_at\nFROM pg_stat_user_tables\nWHERE n_mod_since_analyze > 0\nORDER BY mod_pct DESC NULLS LAST\nLIMIT 20;\n\n-- After a bulk load, without waiting for autovacuum\nANALYZE (VERBOSE) orders;","what":"Zeigt die Anzahl der seit dem letzten ANALYZE geänderten Zeilen, ihr Verhältnis zu den lebenden Zeilen und die ungefähre Änderungsschwelle, ab der Autovacuum ANALYZE auslöst.","when":"Nach Massenladungen/-löschungen, wenn Pläne falsche Zeilenschätzungen zeigen oder wenn vermutet wird, dass Autoanalyze bei großen Tabellen hinterherhinkt.","caution":"Die berechnete Schwelle nutzt die globalen Einstellungen; gibt es tabellenspezifische autovacuum_analyze_*-Reloptions (pg_class.reloptions), weicht die echte Schwelle ab. Bei großen Tabellen greift der Standard-scale_factor (0.1) viel zu spät; senke ihn je Tabelle. Autovacuum führt kein ANALYZE auf der Parent-Tabelle einer partitionierten Tabelle aus; Statistiken auf Parent-Ebene brauchen ein manuelles ANALYZE. Temporäre Tabellen sieht Autovacuum nie. ANALYZE liest eine Stichprobe und nimmt einen ShareUpdateExclusiveLock (blockiert kein DML).","tags":["analyze","stale statistics","n_mod_since_analyze","autoanalyze","statistics","planner"]},{"id":"visibility-map-index-only","task":"Abdeckung der Visibility Map und Effizienz von Index Only Scans prüfen","category":"Wartung","command":"-- Tables with a low all-visible page ratio: Index Only Scans must visit the heap\nSELECT c.oid::regclass AS table_name,\n       c.relpages,\n       c.relallvisible,\n       round(100.0 * c.relallvisible / NULLIF(c.relpages, 0), 1) AS all_visible_pct,\n       pg_size_pretty(pg_relation_size(c.oid)) AS heap_size,\n       s.n_ins_since_vacuum,\n       s.last_autovacuum\nFROM pg_class c\nJOIN pg_stat_user_tables s ON s.relid = c.oid\nWHERE c.relpages > 1000\nORDER BY all_visible_pct NULLS FIRST\nLIMIT 20;\n\n-- If \"Heap Fetches\" in the plan is close to the returned rows, the visibility map is stale\nEXPLAIN (ANALYZE, BUFFERS) SELECT id FROM customer WHERE id BETWEEN 1 AND 50000;\n\n-- Exact count (pg_visibility extension)\nCREATE EXTENSION IF NOT EXISTS pg_visibility;\nSELECT * FROM pg_visibility_map_summary('customer');","what":"Zeigt den Anteil all-visible markierter Seiten der Tabellen, seit dem letzten Vacuum eingefügte Zeilen und wie oft ein Index Only Scan auf den Heap zugreifen musste (Heap Fetches).","when":"Wenn ein Index Only Scan zu schwach abschneidet; bei insert-lastigen/Append-only-Tabellen und beim Validieren eines Covering-Index-Designs (INCLUDE).","caution":"relpages/relallvisible sind Schätzungen, die das letzte VACUUM/ANALYZE festgehalten hat. Nur VACUUM setzt Bits der Visibility Map; jedes UPDATE/DELETE löscht das Bit der betroffenen Seite. Bei niedrigem Anteil auf häufig geänderten Tabellen autovacuum_vacuum_scale_factor je Tabelle senken. pg_visibility_map_summary liest den kleinen VM-Fork, nicht den Heap.","since":"PostgreSQL 13","note":"n_ins_since_vacuum und durch Inserts ausgelöstes Autovacuum (autovacuum_vacuum_insert_threshold/scale_factor) kamen mit PG13; davor erhalten Append-only-Tabellen eventuell bis zu einem Anti-Wraparound-Vacuum gar kein Vacuum, sodass die Visibility Map nicht gesetzt wird.","tags":["visibility map","index only scan","heap fetches","relallvisible","pg_visibility","vacuum"]},{"id":"range-partition-monthly","task":"Monatlich Range-partitionierte Tabelle anlegen (Partitionen in einer Schleife erzeugen)","category":"Partitionierung","command":"CREATE TABLE sales (\n  id         bigint GENERATED ALWAYS AS IDENTITY,\n  created_at      date   NOT NULL,\n  customer_id bigint NOT NULL,\n  amount      numeric(12,2),\n  PRIMARY KEY (id, created_at)\n) PARTITION BY RANGE (created_at);\n\n-- An index defined on the parent propagates to all partitions automatically\nCREATE INDEX sales_customer_id_idx ON sales (customer_id);\n\n-- 12 monthly partitions for 2026\nDO $$\nDECLARE\n  m date;\nBEGIN\n  FOR m IN SELECT generate_series(date '2026-01-01', date '2026-12-01', interval '1 month')::date LOOP\n    EXECUTE format(\n      'CREATE TABLE IF NOT EXISTS %I PARTITION OF sales FOR VALUES FROM (%L) TO (%L)',\n      'sales_' || to_char(m, 'YYYY_MM'), m, (m + interval '1 month')::date);\n  END LOOP;\nEND $$;\n\n-- Hierarchy, bounds and sizes\nSELECT t.relid::regclass AS partition_name,\n       t.parentrelid::regclass AS parent,\n       t.isleaf,\n       t.level,\n       pg_get_expr(c.relpartbound, c.oid) AS bounds,\n       pg_size_pretty(pg_total_relation_size(t.relid)) AS total_size\nFROM pg_partition_tree('sales') t\nJOIN pg_class c ON c.oid = t.relid\nORDER BY t.level, bounds;","what":"Legt eine per RANGE auf einer Datumsspalte partitionierte Parent-Tabelle an, einen Index, der sich auf jede Partition überträgt, und in einer Schleife 12 Monatspartitionen; listet dann die Hierarchie mit Grenzen und Größen.","when":"Für große, ständig wachsende Tabellen, die regelmäßig alte Daten entfernen (DETACH/DROP) oder deren Abfragen meist nach Datumsbereich filtern (Bestellungen, Logs, Zeitreihen).","caution":"Auf einer partitionierten Tabelle müssen PRIMARY-KEY-/UNIQUE-Constraints den Partitionsschlüssel enthalten (es gibt keine globalen Unique-Indizes). Die Untergrenze ist inklusiv, die Obergrenze exklusiv (FROM inklusiv, TO exklusiv). Ohne einen Job, der künftige Partitionen vorab anlegt (pg_cron, pg_partman), schlagen INSERTs außerhalb des Bereichs fehl. Tausende Partitionen erhöhen Planungszeit und Sperr-Overhead. CREATE INDEX CONCURRENTLY wird auf der Parent-Tabelle nicht unterstützt: Nutze CREATE INDEX ... ON ONLY auf der Parent-Tabelle, CREATE INDEX CONCURRENTLY auf jeder Partition und dann ALTER INDEX ... ATTACH PARTITION.","since":"PostgreSQL 12","note":"Deklarative Partitionierung kam mit PG10, Indizes/PRIMARY KEY auf der Parent-Tabelle und Default-Partitionen mit PG11, pg_partition_tree() mit PG12.","tags":["partition","range partition","declarative partitioning","monthly partition","pg_partition_tree","partitioning"]},{"id":"attach-partition-safely","task":"Vorbereitete Tabelle mit kurzer Sperre als Partition einhängen (ATTACH PARTITION)","category":"Partitionierung","command":"-- 1) Prepare the data in a separate table without touching the live table\nCREATE TABLE sales_2027_01 (LIKE sales INCLUDING DEFAULTS INCLUDING CONSTRAINTS);\n-- ... bulk load (COPY / INSERT) ...\nALTER TABLE sales_2027_01 ADD PRIMARY KEY (id, created_at);\nCREATE INDEX CONCURRENTLY sales_2027_01_customer_id_idx ON sales_2027_01 (customer_id);\n\n-- 2) A CHECK that exactly matches the bounds: avoids a full scan during ATTACH\nALTER TABLE sales_2027_01 ADD CONSTRAINT sales_2027_01_bound_chk\n  CHECK (created_at IS NOT NULL AND created_at >= DATE '2027-01-01' AND created_at < DATE '2027-02-01') NOT VALID;\nALTER TABLE sales_2027_01 VALIDATE CONSTRAINT sales_2027_01_bound_chk;\n\n-- 3) Attach (SHARE UPDATE EXCLUSIVE on the parent; reads/writes continue)\nSET lock_timeout = '5s';\nALTER TABLE sales ATTACH PARTITION sales_2027_01\n  FOR VALUES FROM ('2027-01-01') TO ('2027-02-01');\nRESET lock_timeout;\n\n-- 4) Drop the now redundant CHECK\nALTER TABLE sales_2027_01 DROP CONSTRAINT sales_2027_01_bound_chk;","what":"Macht in eine separate Tabelle geladene Daten ohne Full Scan und lange Sperren zur Partition, indem ein vorab validierter, zu den Grenzen passender CHECK-Constraint und vorab erstellte Indizes genutzt werden.","when":"Für Massenladungen (ETL) oder Wiederherstellungen aus dem Archiv: Daten vorbereiten, ohne die Live-Tabelle anzufassen, und dann in einem Schritt online bringen.","caution":"ATTACH nimmt SHARE UPDATE EXCLUSIVE auf der Parent-Tabelle und ACCESS EXCLUSIVE auf der einzuhängenden Tabelle. Ohne passenden gültigen CHECK wird die Tabelle unter dieser Sperre vollständig gescannt, um die Grenzen zu validieren; fehlen zu den Parent-Indizes passende Indizes, baut ATTACH sie unter der Sperre auf, also beides vorab vorbereiten. Existiert eine Default-Partition, wird auch sie gescannt, um nachzuweisen, dass sie keine Zeilen im neuen Bereich enthält (siehe Rezept zur Default-Partition). Spaltennamen und -typen müssen exakt der Parent-Tabelle entsprechen; die Tabelle mit LIKE ... INCLUDING anzulegen ist der sicherste Weg.","since":"PostgreSQL 12","note":"Dass ATTACH PARTITION auf der Parent-Tabelle nur SHARE UPDATE EXCLUSIVE nimmt, kam mit PG12; in PG11 und früher wird die Parent-Tabelle mit ACCESS EXCLUSIVE gesperrt.","tags":["attach partition","add partition","check constraint","not valid","etl","partitioning"]},{"id":"detach-partition-concurrently","task":"Alte Partition aushängen, ohne den Live-Betrieb zu blockieren (DETACH CONCURRENTLY)","category":"Partitionierung","command":"-- Must run outside a transaction block\nALTER TABLE sales DETACH PARTITION sales_2026_01 CONCURRENTLY;\n\n-- If interrupted (cancel, lost connection) the partition stays 'detach pending'; complete it:\nALTER TABLE sales DETACH PARTITION sales_2026_01 FINALIZE;\n\n-- Any pending detach?\nSELECT inhrelid::regclass AS partition_name, inhparent::regclass AS parent\nFROM pg_inherits\nWHERE inhdetachpending;\n\n-- The detached table is now standalone: archive or drop it\nDROP TABLE sales_2026_01;","what":"Hängt die Partition in zwei Transaktionen aus; nimmt auf der Parent-Tabelle nur SHARE UPDATE EXCLUSIVE, sodass laufende Abfragen nicht blockiert werden, und fügt der ausgehängten Tabelle einen der Partitionsgrenze entsprechenden CHECK-Constraint hinzu.","when":"Beim Aushängen alter Zeiträume auf einem Live-System für Aufbewahrung/Archivierung (gefolgt von DROP, Verschieben in einen anderen Tablespace oder einem pg_dump-Archiv).","caution":"Kann nicht innerhalb eines BEGIN ... COMMIT-Blocks laufen und nicht verwendet werden, wenn die Parent-Tabelle eine Default-Partition hat. Es wartet auf alle Transaktionen, die die Parent-Tabelle nutzen und vorher begonnen haben; lange Transaktionen verlängern es. Wird es unterbrochen, bleibt die Partition für Abfragen unsichtbar, aber nicht ausgehängt ('detach pending'), und muss mit FINALIZE abgeschlossen werden. Ein einfaches DETACH ohne CONCURRENTLY ist schneller, braucht aber ACCESS EXCLUSIVE auf der Parent-Tabelle; versuche es mit lock_timeout.","since":"PostgreSQL 14","tags":["detach partition","concurrently","finalize","retention","archiving","partitioning"]},{"id":"default-partition-pitfall","task":"Falle Default-Partition: neue Partition scheitert mit 'would be violated by some row'","category":"Partitionierung","command":"-- Which periods have rows in the default partition?\nSELECT date_trunc('month', created_at)::date AS month, count(*) AS row_count\nFROM sales_default\nGROUP BY 1\nORDER BY 1;\n\n-- Move the rows into a new partition and attach it in one transaction\nBEGIN;\nSET LOCAL lock_timeout = '5s';\nLOCK TABLE sales_default IN ACCESS EXCLUSIVE MODE;\nCREATE TABLE sales_2027_02 (LIKE sales INCLUDING DEFAULTS INCLUDING CONSTRAINTS);\nWITH moved AS (\n  DELETE FROM sales_default\n  WHERE created_at >= DATE '2027-02-01' AND created_at < DATE '2027-03-01'\n  RETURNING *\n)\nINSERT INTO sales_2027_02 SELECT * FROM moved;\nALTER TABLE sales ATTACH PARTITION sales_2027_02\n  FOR VALUES FROM ('2027-02-01') TO ('2027-03-01');\nCOMMIT;","what":"Zählt je Zeitraum die in der Default-Partition gelandeten Zeilen; verschiebt Zeilen im Bereich der neuen Partition in eine neue Tabelle und hängt diese ein, alles in einer Transaktion.","when":"Wenn CREATE TABLE ... PARTITION OF oder ATTACH mit 'updated partition constraint for default partition would be violated by some row' fehlschlägt oder wenn festgestellt wird, dass die Default-Partition wächst.","caution":"Mit vorhandener Default-Partition scannt jede neue Partition die Default-Partition vollständig und hält währenddessen ACCESS EXCLUSIVE darauf; eine große Default-Partition bedeutet bei jeder Wartung einen Ausfall. Während Zeilen verschoben werden, ist die Default-Partition für Schreibvorgänge gesperrt; bei geringem Verkehr durchführen. Dauerhafte Lösung: Partitionen vorab anlegen und die Default-Partition per Monitoring leer halten (Alarm bei Zeilenzahl > 0) oder gar keine verwenden, damit INSERTs außerhalb des Bereichs fehlschlagen und das Problem früh sichtbar wird. Eine Default-Partition verhindert außerdem DETACH ... CONCURRENTLY.","since":"PostgreSQL 11","tags":["default partition","partition error","violated by some row","partition constraint","pitfall","partitioning"]},{"id":"partition-pruning-check","task":"Prüfen, ob Partition Pruning funktioniert (Planungs- und Laufzeit)","category":"Partitionierung","command":"SHOW enable_partition_pruning;\n\n-- Plan-time pruning: only the relevant partition should appear in the plan\nEXPLAIN (COSTS OFF)\nSELECT count(*) FROM sales WHERE created_at >= DATE '2026-03-01' AND created_at < DATE '2026-04-01';\n\n-- Run-time pruning: generic plan / parameterized query, \"Subplans Removed: N\"\nPREPARE q_sales(date) AS SELECT count(*) FROM sales WHERE created_at = $1;\nSET plan_cache_mode = force_generic_plan;\nEXPLAIN (ANALYZE, COSTS OFF, TIMING OFF) EXECUTE q_sales('2026-03-15');\nRESET plan_cache_mode;\nDEALLOCATE q_sales;\n\n-- Pruning killer: a function on the key column scans every partition\nEXPLAIN (COSTS OFF)\nSELECT count(*) FROM sales WHERE date_trunc('month', created_at) = DATE '2026-03-01';","what":"Vergleicht Pruning zur Planungszeit, Pruning zur Laufzeit mit einem generischen Plan ('Subplans Removed: N') und wie Pruning verloren geht, wenn eine Funktion auf den Partitionsschlüssel angewendet wird.","when":"Wenn Abfragen auf einer partitionierten Tabelle langsamer sind als erwartet, der Plan jede Partition scannt oder die Anwendung ein ORM/Prepared Statements nutzt.","caution":"Eine Funktion oder ein Cast auf dem Partitionsschlüssel (date_trunc(created_at), created_at::text) deaktiviert Pruning; formuliere die Bedingung als Bereich auf dem Schlüssel. Mit einem generischen Plan erfolgt Pruning beim Start des Executors und zeigt sich nur als 'Subplans Removed'; bei STABLE-Ausdrücken wie now() ist es auch in EXPLAIN ohne ANALYZE sichtbar. Mit enable_partition_pruning = off findet gar kein Pruning statt. Bei vielen Partitionen auch die Planning Time messen.","since":"PostgreSQL 12","note":"Pruning zur Laufzeit kam mit PG11; plan_cache_mode kam mit PG12, das auch die Planungsleistung bei vielen Partitionen stark verbesserte.","tags":["partition pruning","subplans removed","generic plan","plan_cache_mode","explain","partitioning"]},{"id":"pg-partman-setup","task":"Anlegen und Aufbewahrung von Partitionen mit pg_partman automatisieren","category":"Partitionierung","command":"CREATE SCHEMA partman;\nCREATE EXTENSION pg_partman SCHEMA partman;\n\nCREATE TABLE public.measurements (\n  id    bigint GENERATED ALWAYS AS IDENTITY,\n  ts    timestamptz NOT NULL,\n  value numeric,\n  PRIMARY KEY (id, ts)\n) PARTITION BY RANGE (ts);\n\n-- Daily partitions, pre-create 7 days ahead (pg_partman 5.x syntax)\nSELECT partman.create_parent(\n  p_parent_table := 'public.measurements',\n  p_control      := 'ts',\n  p_interval     := '1 day',\n  p_premake      := 7\n);\n\n-- Drop partitions older than 30 days\nUPDATE partman.part_config\n   SET retention = '30 days',\n       retention_keep_table = false\n WHERE parent_table = 'public.measurements';\n\n-- Maintenance: run periodically (schedule with pg_cron or pg_partman_bgw)\nCALL partman.run_maintenance_proc();\n\n-- Any rows in the default partition?\nSELECT * FROM partman.check_default(p_exact_count := true);","what":"Registriert eine Parent-Tabelle bei pg_partman; legt künftige Partitionen vorab an, löscht solche jenseits der Aufbewahrungsdauer und meldet Zeilen, die in der Default-Partition gelandet sind.","when":"Wenn du statt manueller Verwaltung täglicher/monatlicher Partitionen oder eigener Skripte eine standardisierte, beobachtbare Automatisierung brauchst (Log-, Metrik-, Event-Tabellen).","caution":"Die API hat sich zwischen pg_partman 4.x und 5.x geändert: In 5.x akzeptiert p_type nur 'range'/'list' (Standard 'range'), und p_type := 'native' aus 4.x ist ungültig; prüfe die installierte Version mit SELECT extversion FROM pg_extension WHERE extname = 'pg_partman';. create_parent legt standardmäßig auch eine Default-Partition an. retention_keep_table = false löscht Partitionen endgültig per DROP; brauchst du ein Archiv, lass es auf true und nutze retention_schema. Ist run_maintenance_proc() nicht eingeplant, werden keine neuen Partitionen angelegt und Daten landen in der Default-Partition. pg_partman_bgw erfordert shared_preload_libraries plus Neustart.","note":"pg_partman gehört nicht zum Kern von PostgreSQL (separates Paket, z. B. postgresql-17-partman). Die Befehle nutzen die Syntax von pg_partman 5.x.","tags":["pg_partman","partition maintenance","retention","create_parent","automatic partitioning","partitioning"]},{"id":"least-privilege-roles","task":"Rollenhierarchie nach Least Privilege aufbauen (GRANT + ALTER DEFAULT PRIVILEGES)","category":"Sicherheit","command":"-- Group roles (NOLOGIN): object owner, read-only, read-write\nCREATE ROLE app_owner NOLOGIN;\nCREATE ROLE app_readonly NOLOGIN;\nCREATE ROLE app_readwrite NOLOGIN;\nGRANT app_readonly TO app_readwrite;\nCREATE SCHEMA app AUTHORIZATION app_owner;\n\n-- Restrict PUBLIC's default privileges\nREVOKE ALL ON DATABASE mydb FROM PUBLIC;\nREVOKE CREATE ON SCHEMA public FROM PUBLIC;\n\n-- Existing objects\nGRANT CONNECT ON DATABASE mydb TO app_readonly;\nGRANT USAGE ON SCHEMA app TO app_readonly;\nGRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readonly;\nGRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_readwrite;\nGRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_readwrite;\n\n-- Objects app_owner will create IN THE FUTURE\nALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app GRANT SELECT ON TABLES TO app_readonly;\nALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app GRANT INSERT, UPDATE, DELETE ON TABLES TO app_readwrite;\nALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app GRANT USAGE, SELECT ON SEQUENCES TO app_readwrite;\n\n-- Login users get privileges only through group membership\nCREATE ROLE web_app LOGIN PASSWORD 'Change#2026' IN ROLE app_readwrite;\nCREATE ROLE report_app LOGIN PASSWORD 'Change#2026' IN ROLE app_readonly;\n\n-- Configured default privileges (psql: \\ddp)\nSELECT pg_get_userbyid(d.defaclrole) AS owner_role,\n       n.nspname AS schema_name,\n       d.defaclobjtype AS object_type,\n       d.defaclacl\nFROM pg_default_acl d\nLEFT JOIN pg_namespace n ON n.oid = d.defaclnamespace\nORDER BY 1, 2, 3;","what":"Baut eine Hierarchie aus Owner-, Read-only- und Read-write-Gruppenrollen auf; vergibt die passenden Rechte auf bestehende und künftige Tabellen/Sequences und berechtigt Anwendungsbenutzer nur über Gruppenmitgliedschaft.","when":"Beim Einrichten eines neuen Anwendungsschemas, beim Korrigieren von Anwendungen, die sich als Superuser oder Tabelleneigentümer verbinden, und zum Vereinfachen der Rechte vor einem Audit.","caution":"ALTER DEFAULT PRIVILEGES gilt nur für Objekte, die SPÄTER von der in FOR ROLE genannten Rolle angelegt werden; legt das Migrationstool Tabellen als anderer Benutzer an, werden die Rechte nicht angewendet, also DDL mit SET ROLE app_owner ausführen. GRANT ... ON ALL TABLES umfasst nur Objekte, die in diesem Moment existieren. Ab PG15 angelegte Datenbanken gewähren PUBLIC ohnehin kein CREATE auf dem Schema public; von älteren Versionen aktualisierte Datenbanken brauchen das REVOKE. REVOKE ALL ON DATABASE ... FROM PUBLIC entzieht auch CONNECT und TEMP; gewähre sie anderen Rollen, die sie brauchen, separat. Nutze psql \\password, statt Passwörter auf die Kommandozeile zu schreiben (sie bleiben so aus Logs und Historie heraus).","note":"Die Rollen pg_read_all_data / pg_write_all_data kamen mit PG14, die Optionen INHERIT/SET bei der Rollenmitgliedschaft (GRANT ... WITH INHERIT FALSE) mit PG16.","tags":["least privilege","role hierarchy","alter default privileges","grant","privilege","security"]},{"id":"row-level-security","task":"Mandantentrennung mit Row-Level Security einrichten","category":"Sicherheit","command":"ALTER TABLE app.orders ENABLE ROW LEVEL SECURITY;\n-- Make the table owner subject to the policy too\nALTER TABLE app.orders FORCE ROW LEVEL SECURITY;\n\nCREATE POLICY orders_tenant_isolation ON app.orders\n  FOR ALL\n  TO app_readwrite\n  USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::int)\n  WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::int);\n\n-- The application sets the tenant at the start of every transaction (pooler-friendly)\nBEGIN;\nSET LOCAL app.tenant_id = '42';\nSELECT * FROM app.orders;\nCOMMIT;\n\n-- Configured policies\nSELECT schemaname, tablename, policyname, roles, cmd, qual, with_check\nFROM pg_policies\nWHERE tablename = 'orders';","what":"Aktiviert RLS auf der Tabelle und definiert eine Policy, die Zeilen nach der Mandanten-ID in einer Session-Variable filtert (USING) und Schreibvorgänge für einen anderen Mandanten blockiert (WITH CHECK).","when":"Um bei mandantenfähigen Anwendungen die Datentrennung in der Datenbank zu erzwingen, statt sich auf den Anwendungscode zu verlassen.","caution":"Superuser und Rollen mit dem Attribut BYPASSRLS umgehen Policies immer; der Tabelleneigentümer unterliegt ihnen nur mit FORCE ROW LEVEL SECURITY. Ist RLS aktiviert und passt keine Policy, ist das Ergebnis leer (kein Fehler). current_setting(..., true) liefert NULL, wenn die Variable nicht definiert ist; NULLIF verhindert, dass der nach RESET verbleibende leere String als ''::int fehlschlägt. Benutzer können app.tenant_id mit SET ändern; nur eine vertrauenswürdige Anwendungsschicht sollte sie setzen, und Endbenutzer dürfen keinen SQL-Zugriff erhalten. Mit einem Pooler im Transaktionsmodus SET LOCAL statt SET verwenden. Indiziere die in der Policy genutzte Spalte tenant_id. pg_dump schlägt fehl, wenn es als Benutzer läuft, der RLS unterliegt; erstelle Backups mit einer BYPASSRLS-Rolle.","since":"PostgreSQL 9.5","tags":["row level security","rls","create policy","multi-tenant","tenant isolation","security"]},{"id":"pg-hba-validate-reload","task":"Regelreihenfolge in pg_hba.conf prüfen und sicher neu laden","category":"Sicherheit","command":"-- File location\nSHOW hba_file;\n\n# Rules are read top to bottom and the FIRST matching rule wins;\n# specific rules must come BEFORE general ones:\n#   hostssl  appdb  web_app  10.10.0.0/16  scram-sha-256\n#   host     all    all      10.10.0.0/16  reject\n#   host     all    all      0.0.0.0/0     scram-sha-256\n\n-- Validate BEFORE reloading: pg_hba_file_rules reads the file on disk\nSELECT line_number, type, database, user_name, address, netmask, auth_method, error\nFROM pg_hba_file_rules\nORDER BY line_number;\n\n-- Apply if the error column is empty\nSELECT pg_reload_conf();","what":"Zeigt vor dem Reload, wo pg_hba.conf liegt, wie PostgreSQL sie interpretiert (Zeile, Typ, Datenbank, Benutzer, Adresse, Methode) und eventuelle Syntaxfehler; lädt dann die Konfiguration neu.","when":"Beim Hinzufügen eines neuen Client-Netzes/einer neuen Anwendung, bei der Migration von md5 auf scram-sha-256 oder bei der Untersuchung von Fehlern 'no pg_hba.conf entry for host'.","caution":"pg_hba_file_rules zeigt die Datei im aktuellen Zustand, nicht die aktiven Regeln. Hat eine Zeile einen nicht leeren error, lädt ein Reload pg_hba.conf überhaupt NICHT, und die alten Regeln bleiben wirksam (das Log meldet 'pg_hba.conf was not reloaded'). Änderungen betreffen nur neue Verbindungen. Eine weit vorn stehende breite Regel (z. B. host all all 0.0.0.0/0 trust) hebelt jede nachfolgende Einschränkung aus. Bei abgeschaltetem SSL können hostssl-Zeilen nicht greifen und zeigen eine Meldung in der Spalte error. Standardmäßig dürfen nur Superuser die View lesen. Achte darauf, dass Tools, die die Datei als root bearbeiten, den Eigentümer nicht ändern; kann der Benutzer postgres sie nicht lesen, schlägt der Reload fehl.","since":"PostgreSQL 10","note":"pg_hba_file_rules kam mit PG10; neuere Versionen haben auch die Spalten rule_number und file_name. PG16 unterstützt include-Direktiven in pg_hba.conf und Regex-Abgleich in den Feldern für Benutzer/Datenbank.","tags":["pg_hba.conf","authentication","rule order","reload","pg_hba_file_rules","security"]},{"id":"ssl-enable-verify","task":"SSL/TLS aktivieren und verschlüsselte Verbindungen prüfen (pg_stat_ssl)","category":"Sicherheit","command":"# Server certificate (self-signed for testing; use a corporate CA in production)\nopenssl req -new -x509 -days 365 -nodes -subj \"/CN=db.example.com\" -keyout server.key -out server.crt\nchmod 600 server.key    # in the data directory, owned by postgres\n\n-- Enable SSL (a reload is enough)\nALTER SYSTEM SET ssl = on;\nALTER SYSTEM SET ssl_cert_file = 'server.crt';\nALTER SYSTEM SET ssl_key_file = 'server.key';\nALTER SYSTEM SET ssl_min_protocol_version = 'TLSv1.2';\nSELECT pg_reload_conf();\n\n-- Which connections are encrypted?\nSELECT a.pid, a.usename, a.client_addr, a.application_name,\n       s.ssl, s.version, s.cipher, s.bits\nFROM pg_stat_ssl s\nJOIN pg_stat_activity a USING (pid)\nWHERE a.backend_type = 'client backend'\nORDER BY s.ssl, a.usename;\n\n# On the client, verify the server identity as well\npsql \"host=db.example.com dbname=postgres user=postgres sslmode=verify-full sslrootcert=root.crt\"","what":"Aktiviert TLS mit einem Serverzertifikat ohne Neustart, setzt die minimale Protokollversion und listet, ob aktuelle Verbindungen verschlüsselt sind, mit TLS-Version und Cipher.","when":"Für Anforderungen aus Sicherheitsaudits/Compliance (PCI, DSGVO), um Passwörter und Daten bei der Übertragung über das Netzwerk zu schützen.","caution":"ssl = on allein erzwingt keine Verschlüsselung; Clients können sich mit sslmode=prefer/disable weiterhin unverschlüsselt verbinden. Zum Erzwingen in pg_hba.conf hostssl statt host verwenden (und hostnossl ... reject ergänzen). sslmode=require prüft die Identität des Servers nicht (anfällig für MITM); für die Zertifikatsprüfung verify-full verwenden. Die Schlüsseldatei muss dem Benutzer postgres gehören und Modus 0600 haben (oder root gehören mit 0640); sonst wird SSL nicht geladen, also nach dem Reload das Log und SHOW ssl prüfen. Auch Zertifikatserneuerungen werden per Reload übernommen.","since":"PostgreSQL 10","note":"SSL-Einstellungen per Reload statt Neustart zu ändern kam mit PG10, ssl_min_protocol_version mit PG12; der Standardwert ist seit PG13 TLSv1.2.","tags":["ssl","tls","pg_stat_ssl","hostssl","certificate","encryption"]},{"id":"scram-migration","task":"Von md5-Passwörtern auf scram-sha-256 migrieren","category":"Sicherheit","command":"-- 1) Store new passwords as SCRAM\nALTER SYSTEM SET password_encryption = 'scram-sha-256';\nSELECT pg_reload_conf();\n\n-- 2) Find roles still carrying md5 password hashes (requires superuser)\nSELECT rolname,\n       CASE\n         WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram-sha-256'\n         WHEN rolpassword LIKE 'md5%' THEN 'md5'\n         WHEN rolpassword IS NULL THEN 'no password'\n         ELSE 'other'\n       END AS password_type,\n       rolvaliduntil\nFROM pg_authid\nWHERE rolcanlogin\nORDER BY password_type, rolname;\n\n-- 3) Reset the password of each md5 role (preferably in psql: \\password legacy_app)\nALTER ROLE legacy_app PASSWORD 'New#Password2';\n\n# 4) Once all roles use SCRAM, change md5 -> scram-sha-256 in pg_hba.conf and reload","what":"Sorgt dafür, dass neue Passwörter als SCRAM-SHA-256 gespeichert werden, findet Login-Rollen, die noch md5-Hashes tragen, und nennt die Schritte, pg_hba.conf nach der Erneuerung der Passwörter auf scram-sha-256 umzustellen.","when":"Wenn ein Sicherheitsaudit md5 beanstandet, nach einem Upgrade auf PG14+ oder bei der Vorbereitung auf Versionen, in denen md5 veraltet ist.","caution":"SCRAM lässt sich nicht aus vorhandenen md5-Hashes ableiten; jedes Passwort muss im Klartext neu gesetzt werden (erfordert Abstimmung mit den Anwendungsteams). Die Methode 'md5' in pg_hba.conf führt für Rollen mit SCRAM-Passwort automatisch SCRAM-Authentifizierung durch, md5 während der Migration beizubehalten ist also ein sicherer Zwischenschritt; sobald die Methode aber 'scram-sha-256' ist, können sich Rollen mit md5-Passwörtern nicht mehr verbinden. Alte Client-Treiber (libpq < 10, alte JDBC-/.NET-Treiber) unterstützen SCRAM eventuell nicht; Treiber zuerst aktualisieren. ALTER ROLE ... PASSWORD kann das Passwort über log_statement ins Log schreiben; psql \\password hasht es vor dem Senden auf dem Client.","since":"PostgreSQL 10","note":"SCRAM-SHA-256 kam mit PG10, und der Standard von password_encryption wurde in PG14 zu scram-sha-256. PG18 markiert md5-Passwörter als veraltet und gibt eine Warnung aus.","tags":["scram-sha-256","md5","password_encryption","password","authentication","security"]},{"id":"pgaudit-object-audit","task":"Audit-Logs auf Rollen- und Objektebene mit pgaudit führen","category":"Sicherheit","command":"-- Prerequisite: shared_preload_libraries = 'pgaudit' + restart, then:\nCREATE EXTENSION pgaudit;\n\n-- Session auditing: server-wide, only DDL and role/privilege changes\nALTER SYSTEM SET pgaudit.log = 'ddl, role';\nALTER SYSTEM SET pgaudit.log_relation = on;\nALTER SYSTEM SET pgaudit.log_parameter = off;\nSELECT pg_reload_conf();\n\n-- All reads/writes of a specific application role\nALTER ROLE web_app SET pgaudit.log = 'read, write';\n\n-- Object auditing: privileges granted to the auditor role = operations to log\nCREATE ROLE auditor NOLOGIN;\nALTER SYSTEM SET pgaudit.role = 'auditor';\nSELECT pg_reload_conf();\nGRANT SELECT, DELETE ON customer TO auditor;\nGRANT UPDATE (balance) ON customer TO auditor;","what":"Protokolliert serverweit DDL und Rechteänderungen, alle Lese-/Schreibvorgänge einer bestimmten Rolle und Zugriffe auf sensible Tabellen/Spalten, die der Auditor-Rolle gewährt sind, als strukturierte Log-Zeilen mit dem Präfix 'AUDIT:'.","when":"Um bei Compliance-Audits wie DSGVO, PCI-DSS oder SOX nachzuweisen, wer auf sensible Daten zugegriffen hat.","caution":"pgaudit.log = 'all' oder 'read' serverweit zu aktivieren, steigert Log-Volumen und I/O stark; grenze den Umfang mit Rollen- oder Objekt-Auditing ein. Ein Superuser kann das Auditing abschalten (ALTER SYSTEM/ALTER ROLE); leite die Einträge an ein zentrales Log-System (syslog/SIEM) und speichere sie unabhängig vom Datenbankserver. log_parameter = on protokolliert auch Bind-Parameter (personenbezogene Daten). Rolleneinstellungen wirken in neuen Sessions. Die Major-Version von pgaudit muss der Major-Version von PostgreSQL entsprechen (pgaudit 17.x = PG17).","note":"pgaudit gehört nicht zum Kern von PostgreSQL (separates Paket, z. B. postgresql-17-pgaudit).","tags":["pgaudit","audit","auditing","object audit","log","security"]},{"id":"memory-settings-check","task":"Speichereinstellungen prüfen (shared_buffers, work_mem, maintenance_work_mem)","category":"Parameter","command":"SELECT name,\n       current_setting(name) AS value,\n       source,\n       context\nFROM pg_settings\nWHERE name IN ('shared_buffers', 'effective_cache_size', 'work_mem', 'hash_mem_multiplier',\n               'maintenance_work_mem', 'autovacuum_work_mem', 'max_connections',\n               'max_parallel_workers_per_gather', 'huge_pages', 'temp_buffers', 'wal_buffers')\nORDER BY name;\n\n-- Rough ceilings: work_mem x max_connections and the autovacuum memory ceiling\nSELECT pg_size_pretty(pg_size_bytes(current_setting('work_mem'))\n                      * current_setting('max_connections')::int) AS work_mem_x_max_connections,\n       pg_size_pretty(pg_size_bytes(current_setting('maintenance_work_mem'))\n                      * current_setting('autovacuum_max_workers')::int) AS autovacuum_mem_ceiling;\n\n-- Trace of operations that did not fit in work_mem: temp files per database\nSELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_size, stats_reset\nFROM pg_stat_database\nWHERE datname IS NOT NULL\nORDER BY temp_bytes DESC;","what":"Zeigt speicherbezogene Parameter mit Wert, Quelle (Standard/Konfigurationsdatei) und Änderungskontext (postmaster = Neustart); grobe Speicherobergrenzen für work_mem und Autovacuum; und das Temp-File-Volumen je Datenbank.","when":"Beim Aufbau eines neuen Servers, nach einer RAM-Erweiterung, nach einem Eingriff des OOM Killers oder bei starker Nutzung von Temp-Dateien.","caution":"work_mem wird nicht je Verbindung, sondern je Sortier-/Hash-KNOTEN (bei Hashes mal hash_mem_multiplier) und für jeden parallelen Worker separat zugewiesen; eine komplexe Abfrage kann ein Mehrfaches von work_mem nutzen, das berechnete Produkt ist also eine grobe Warnung, keine Obergrenze. Typische Startwerte: shared_buffers ~25 % des RAM (Neustart erforderlich), effective_cache_size ~50-75 % (nur ein Hinweis für den Planner, reserviert nichts), maintenance_work_mem 512MB-2GB. Mit autovacuum_work_mem = -1 darf jeder Autovacuum-Worker maintenance_work_mem nutzen. Ist temp_bytes hoch, work_mem je Rolle oder Abfrage erhöhen, nicht global. Bei großen shared_buffers unter Linux huge_pages in Betracht ziehen.","note":"hash_mem_multiplier kam mit PG13 (Standard 2.0 seit PG15).","tags":["shared_buffers","work_mem","maintenance_work_mem","memory","memory tuning","temp_bytes"]},{"id":"pending-restart-config-errors","task":"Auf Neustart wartende Parameter und Fehler in Konfigurationsdateien anzeigen","category":"Parameter","command":"-- Parameters changed but not effective until a restart\nSELECT name, setting, unit, context, source, sourcefile, pending_restart\nFROM pg_settings\nWHERE pending_restart;\n\n-- Lines in configuration files that were not applied or have errors\nSELECT sourcefile, sourceline, seqno, name, setting, applied, error\nFROM pg_file_settings\nWHERE NOT applied OR error IS NOT NULL\nORDER BY sourcefile, sourceline;\n\n-- Where does a parameter's effective value come from?\nSELECT name, setting, source, sourcefile, sourceline\nFROM pg_settings\nWHERE name = 'work_mem';","what":"Zeigt Parameter, die in den Dateien geändert wurden, aber auf einen Neustart warten, nicht angewendete oder fehlerhafte Zeilen der Konfigurationsdateien und aus welcher Datei/Zeile der wirksame Wert eines Parameters stammt.","when":"Um nach einer Änderung per ALTER SYSTEM oder in postgresql.conf zu prüfen, dass der Reload die erwartete Wirkung hatte, und um vor einem geplanten Neustart ausstehende Änderungen aufzulisten.","caution":"pg_file_settings liest die Dateien im aktuellen Zustand; ist ein Parameter an mehreren Stellen definiert, hat nur die letzte Definition applied = true (postgresql.auto.conf wird zuletzt gelesen, daher gewinnt ALTER SYSTEM immer). Eine Zeile mit applied = false und leerem error wird von einer anderen Definition überdeckt; das ist kein Fehler. Ein Parameter, der einen Neustart braucht, zeigt den error 'setting could not be applied'. Eine fehlerhafte Datei wird beim Reload eventuell teilweise angewendet; nach einer Änderung pg_reload_conf() ausführen und dann diese Abfragen und das Server-Log prüfen. Bearbeite postgresql.auto.conf nicht von Hand; nutze ALTER SYSTEM RESET.","since":"PostgreSQL 9.5","tags":["pending_restart","pg_file_settings","restart","configuration error","alter system","config"]},{"id":"checkpoint-stats","task":"Checkpoint-Verhalten prüfen (ab PG17 pg_stat_checkpointer, PG16 und früher pg_stat_bgwriter)","category":"Parameter","command":"-- PG17+\nSELECT num_timed,\n       num_requested,\n       round(100.0 * num_requested / NULLIF(num_timed + num_requested, 0), 1) AS req_pct,\n       restartpoints_timed,\n       restartpoints_req,\n       round((write_time / 1000)::numeric / NULLIF(num_timed + num_requested, 0), 1) AS avg_write_s,\n       round((sync_time / 1000)::numeric / NULLIF(num_timed + num_requested, 0), 1) AS avg_sync_s,\n       pg_size_pretty(buffers_written * current_setting('block_size')::bigint) AS written_by_checkpoint,\n       stats_reset\nFROM pg_stat_checkpointer;\n\n-- PG16 and earlier\nSELECT checkpoints_timed,\n       checkpoints_req,\n       round(100.0 * checkpoints_req / NULLIF(checkpoints_timed + checkpoints_req, 0), 1) AS req_pct,\n       round((checkpoint_write_time / 1000)::numeric / NULLIF(checkpoints_timed + checkpoints_req, 0), 1) AS avg_write_s,\n       round((checkpoint_sync_time / 1000)::numeric / NULLIF(checkpoints_timed + checkpoints_req, 0), 1) AS avg_sync_s,\n       pg_size_pretty(buffers_checkpoint * current_setting('block_size')::bigint) AS written_by_checkpoint,\n       pg_size_pretty(buffers_clean * current_setting('block_size')::bigint) AS written_by_bgwriter,\n       pg_size_pretty(buffers_backend * current_setting('block_size')::bigint) AS written_by_backends,\n       maxwritten_clean,\n       stats_reset\nFROM pg_stat_bgwriter;\n\n-- Related settings\nSELECT name, current_setting(name) AS value\nFROM pg_settings\nWHERE name IN ('checkpoint_timeout', 'max_wal_size', 'min_wal_size',\n               'checkpoint_completion_target', 'log_checkpoints')\nORDER BY name;","what":"Zeigt das Verhältnis geplanter Checkpoints zu durch WAL-Volumen erzwungenen, die durchschnittliche Schreib-/Sync-Zeit je Checkpoint und das Volumen geschriebener Buffers (in PG16 und früher zusammen mit Schreibvorgängen von bgwriter und Backends).","when":"Wenn schreibintensive Phasen I/O-Spitzen zeigen, die WAL-Erzeugung hoch ist oder das Log 'checkpoints are occurring too frequently' meldet.","caution":"Ein hoher req_pct (typisch über 10-20 %) bedeutet, dass Checkpoints erzwungen werden, weil max_wal_size vollläuft; erhöhe max_wal_size und achte mit log_checkpoints = on auf Zeilen 'checkpoint starting: wal'. Ein längeres checkpoint_timeout verteilt I/O, verlängert aber die Crash Recovery. In PG16 und früher bedeutet ein hoher written_by_backends, dass Backends Dirty Pages selbst schreiben müssen (shared_buffers/bgwriter unzureichend); in PG17 steht das in pg_stat_io. Durchschnitte zählen auch geplante Checkpoints mit, die mangels Arbeit übersprungen wurden, daher als grobe Werte lesen. Die Zähler sind seit stats_reset kumuliert.","since":"PostgreSQL 17","note":"In PG17 wanderten die Checkpointer-Statistiken von pg_stat_bgwriter in die neue View pg_stat_checkpointer (checkpoints_timed → num_timed, checkpoints_req → num_requested, buffers_checkpoint → buffers_written); buffers_backend und buffers_backend_fsync wurden entfernt. PG18 ergänzte die Spalten num_done und slru_written.","tags":["checkpoint","pg_stat_checkpointer","pg_stat_bgwriter","max_wal_size","checkpoint_timeout","wal"]},{"id":"relation-size-breakdown","task":"Tabellengröße aufgeschlüsselt nach Heap / Index / TOAST und die größten Indizes auflisten","category":"Speicher","command":"SELECT n.nspname AS schema_name,\n       c.relname AS table_name,\n       pg_size_pretty(pg_total_relation_size(c.oid)) AS total,\n       pg_size_pretty(pg_relation_size(c.oid)) AS heap,\n       pg_size_pretty(pg_indexes_size(c.oid)) AS indexes,\n       pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid) - pg_indexes_size(c.oid)) AS toast_fsm_vm,\n       round(100.0 * pg_total_relation_size(c.oid) / NULLIF(pg_database_size(current_database()), 0), 1) AS pct_of_db\nFROM pg_class c\nJOIN pg_namespace n ON n.oid = c.relnamespace\nWHERE c.relkind IN ('r', 'm')\n  AND n.nspname NOT IN ('pg_catalog', 'information_schema')\nORDER BY pg_total_relation_size(c.oid) DESC\nLIMIT 20;\n\n-- Largest indexes and their usage counts\nSELECT s.schemaname,\n       s.relname AS table_name,\n       s.indexrelname AS index_name,\n       pg_size_pretty(pg_relation_size(s.indexrelid)) AS index_size,\n       s.idx_scan,\n       i.indisunique,\n       i.indisprimary\nFROM pg_stat_user_indexes s\nJOIN pg_index i ON i.indexrelid = s.indexrelid\nORDER BY pg_relation_size(s.indexrelid) DESC\nLIMIT 20;","what":"Zeigt die größten Tabellen aufgeschlüsselt nach Gesamt, Heap, Index und TOAST (+FSM/VM) mit ihrem Anteil an der Datenbank; und die größten Indizes mit Scan-Zahlen und Unique-/Primary-Kennzeichen.","when":"Um bei steigender Plattennutzung herauszufinden, woher das Wachstum kommt (Daten, Indizes oder TOAST), und um große ungenutzte Indizes zu entdecken.","caution":"Größenfunktionen greifen für jede Relation auf das Dateisystem zu und können in Datenbanken mit Zehntausenden Tabellen langsam sein. Eine partitionierte Parent-Tabelle (relkind 'p') hat selbst 0 Bytes und wird ausgeschlossen; für die Summe einer Hierarchie: SELECT pg_size_pretty(sum(pg_total_relation_size(relid))) FROM pg_partition_tree('sales'); Große Indizes mit idx_scan = 0 sind Kandidaten zum Löschen, aber die Zähler decken nur die Zeit seit dem letzten Statistik-Reset und nur diesen Server ab (Nutzung auf Replicas ist nicht sichtbar); Unique-/Primary-Indizes erzwingen Constraints und werden nicht gelöscht.","tags":["table size","index size","toast","disk usage","pg_total_relation_size","storage"]},{"id":"extension-update-check","task":"Extensions mit ausstehenden Updates finden und aktualisieren","category":"Extensions","command":"SELECT e.extname,\n       e.extversion AS installed,\n       a.default_version AS available,\n       n.nspname AS schema_name,\n       format('ALTER EXTENSION %I UPDATE;', e.extname) AS fix\nFROM pg_extension e\nJOIN pg_available_extensions a ON a.name = e.extname\nJOIN pg_namespace n ON n.oid = e.extnamespace\nWHERE e.extversion IS DISTINCT FROM a.default_version\nORDER BY e.extname;\n\n-- Is there an update path?\nSELECT * FROM pg_extension_update_paths('pgstattuple') WHERE path IS NOT NULL;\n\nALTER EXTENSION pgstattuple UPDATE;","what":"Vergleicht die in der Datenbank installierte Version jeder Extension mit der Standardversion des Pakets auf dem Server und erzeugt den Befehl ALTER EXTENSION für solche mit ausstehenden Updates.","when":"Nach Minor-/Major-Upgrades von PostgreSQL und Updates von Extension-Paketen sowie bei regelmäßiger Wartung.","caution":"Ein Upgrade des OS-Pakets aktualisiert die SQL-Seite nicht automatisch; ALTER EXTENSION ... UPDATE muss in jeder Datenbank separat ausgeführt werden (besonders wichtig nach pg_upgrade). Das Update-Skript kann Sperren nehmen und Objekte ändern (z. B. PostGIS); vorher testen und die Release Notes lesen. Um auf eine bestimmte Version zu wechseln: ALTER EXTENSION pgstattuple UPDATE TO '1.5'; Einige Extensions (z. B. pg_stat_statements) erfordern zusätzlich einen Neustart, um die Shared Library neu zu laden.","tags":["alter extension update","extension version","extension","pg_available_extensions","upgrade","update"]},{"id":"pg-stat-io-analysis","task":"Mit pg_stat_io analysieren, woher I/O kommt (Backend-Typ, Objekt, Kontext)","category":"Extensions","command":"SELECT backend_type,\n       object,\n       context,\n       reads,\n       pg_size_pretty(reads * op_bytes) AS read_size,\n       writes,\n       pg_size_pretty(writes * op_bytes) AS write_size,\n       extends,\n       hits,\n       round(100.0 * hits / NULLIF(hits + reads, 0), 1) AS hit_pct,\n       evictions,\n       reuses,\n       fsyncs,\n       round(read_time::numeric, 1) AS read_ms,\n       round(write_time::numeric, 1) AS write_ms\nFROM pg_stat_io\nWHERE coalesce(reads, 0) + coalesce(writes, 0) + coalesce(extends, 0) + coalesce(evictions, 0) > 0\nORDER BY coalesce(reads, 0) + coalesce(writes, 0) DESC;\n\n-- Who writes? Ideally most writes come from the checkpointer and background writer\nSELECT backend_type,\n       pg_size_pretty(sum(writes * op_bytes)) AS written,\n       round(100.0 * sum(writes) / NULLIF(sum(sum(writes)) OVER (), 0), 1) AS pct_of_writes,\n       sum(fsyncs) AS fsyncs\nFROM pg_stat_io\nWHERE object = 'relation' AND writes > 0\nGROUP BY backend_type\nORDER BY sum(writes) DESC;","what":"Zeigt Reads, Writes, Extends, Evictions und Fsyncs nach Backend-Typ (client backend, autovacuum, checkpointer...), Objekt (relation/temp relation) und Kontext (normal, vacuum, bulkread, bulkwrite); und von welchen Prozesstypen die Schreibvorgänge stammen.","when":"Bei einem I/O-Engpass, um zu erkennen, ob die Last von Abfragen, Autovacuum oder Checkpoints kommt, und um Einstellungen von shared_buffers/bgwriter zu validieren.","caution":"pg_stat_io gilt clusterweit und ist seit dem letzten Reset kumuliert; bilde die Differenz zweier Stichproben, um Veränderungen zu sehen (Reset: SELECT pg_stat_reset_shared('io');). read_time/write_time werden nur mit track_io_timing = on befüllt. reads enthält auch Lesevorgänge, die aus dem Page Cache des OS bedient werden. Hohe Writes oder Evictions durch client backend bedeuten, dass Backends Buffers selbst schreiben, um Platz zu schaffen; prüfe shared_buffers, bgwriter_lru_maxpages und die Checkpoint-Einstellungen. Zeilen mit context = 'vacuum'/'bulkread' spiegeln die Nutzung von Ring Buffers wider; reuses sind dort normal.","since":"PostgreSQL 16","note":"pg_stat_io kam mit PG16. PG18 entfernte die Spalte op_bytes, ergänzte read_bytes/write_bytes/extend_bytes und meldet in dieser View auch WAL-I/O; nutze in PG18 read_bytes statt reads * op_bytes.","tags":["pg_stat_io","io statistics","evictions","fsync","shared_buffers","io"]}]},"recipes/mysql.json":{"engineId":"mysql","engine":"MySQL / MariaDB","order":3,"recipes":[{"id":"rotate-binlog","task":"Binary Log rotieren","category":"Binlog & Log","command":"FLUSH BINARY LOGS;","what":"Schließt die aktuelle Binary-Log-Datei und wechselt zu einer neuen.","when":"Um vor einem Backup einen sauberen Binlog-Startpunkt zu schaffen oder die Dateigröße zu begrenzen.","caution":"Häufiges Rotieren erzeugt viele kleine Binlog-Dateien. Derselbe Befehl funktioniert in MariaDB; FLUSH LOGS rotiert alle Logs.","tags":["binlog","rotate","flush","log"]},{"id":"purge-binlog","task":"Alte Binary Logs löschen (PURGE)","category":"Binlog & Log","command":"PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;","what":"Löscht Binary-Log-Dateien, die älter als das angegebene Datum sind, von der Platte.","when":"Wenn Binlogs die Platte füllen, um alte, nicht mehr benötigte Logs zu bereinigen.","caution":"Braucht eine Replica noch ein zu löschendes Binlog, bricht die Replikation ab; prüfe zuerst mit SHOW REPLICA STATUS, dass alle Replicas darüber hinaus sind. Um bis zu einer bestimmten Datei zu löschen: PURGE BINARY LOGS TO 'mysql-bin.000123'; Für automatische Bereinigung die Einstellung binlog_expire_logs_seconds verwenden.","tags":["binlog","purge","cleanup","log"]},{"id":"show-binlogs","task":"Status der Binary Logs anzeigen","category":"Binlog & Log","command":"SHOW BINARY LOGS;","what":"Listet alle Binary-Log-Dateien auf dem Server und ihre Größen.","when":"Um die Plattennutzung der Binlogs und die vorhandenen Dateien zu sehen.","caution":"Für die gerade geschriebene Binlog-Datei und ihre Position: SHOW BINARY LOG STATUS; (ab MySQL 8.2). Ältere Versionen nutzen SHOW MASTER STATUS (in 8.2–8.3 veraltet, in 8.4 entfernt).","tags":["binlog","status","show","log"]},{"id":"enable-general-slow-log","task":"General Log und Slow Query Log aktivieren","category":"Binlog & Log","command":"SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL general_log = 'ON';","what":"Aktiviert zur Laufzeit das Slow Query Log (Abfragen über 1 Sekunde) und das General Log, das jede Abfrage aufzeichnet.","when":"Um vorübergehend langsame Abfragen oder den gesamten Abfrageverkehr zu analysieren.","caution":"general_log erzeugt erheblichen Performance- und Platten-Overhead; schalte es nach der Diagnose immer ab (SET GLOBAL general_log='OFF'). Diese Einstellungen werden beim Neustart zurückgesetzt; mit SET PERSIST dauerhaft machen.","tags":["slow-log","general-log","log","diagnostics"]},{"id":"innodb-redo-capacity","task":"Größe des InnoDB-Redo-Logs ändern","category":"Speicher","command":"SET GLOBAL innodb_redo_log_capacity = 2147483648;","what":"Setzt zur Laufzeit dynamisch die gesamte Kapazität des InnoDB-Redo-Logs (in Bytes, hier 2 GB).","when":"Um bei schreibintensiver Last die Checkpoint-Häufigkeit zu senken und die Performance zu verbessern.","caution":"innodb_redo_log_capacity kam mit MySQL 8.0.30 und ersetzt die alten Einstellungen innodb_log_file_size + innodb_log_files_in_group. In älteren Versionen/MariaDB wird innodb_log_file_size in my.cnf geändert und erfordert einen Neustart. Dauerhaft machen: SET PERSIST innodb_redo_log_capacity = 2147483648;","since":"MySQL 8.0.30","note":"innodb_redo_log_capacity kam mit MySQL 8.0.30 und ermöglichte eine dynamische Größenänderung des Redo-Logs (ohne Neustart); vorher mussten innodb_log_file_size + innodb_log_files_in_group gesetzt und der Server neu gestartet werden.","tags":["innodb","redo-log","capacity","storage"]},{"id":"innodb-undo-tablespace","task":"InnoDB-Undo-Tablespace hinzufügen / verwalten","category":"Speicher","command":"CREATE UNDO TABLESPACE undo_003 ADD DATAFILE 'undo_003.ibu';","what":"Legt einen neuen InnoDB-Undo-Tablespace an; Undo Logs werden getrennt von den Datendateien verwaltet und lassen sich kürzen (truncate).","when":"Um Platz zu verwalten, wenn lang laufende Transaktionen das Undo Log aufblähen, oder um Undo auf eine separate Platte zu verlegen.","caution":"Der Dateiname muss auf .ibu enden. Ein Undo-Tablespace muss inaktiv gesetzt werden, bevor er gelöscht werden kann: ALTER UNDO TABLESPACE undo_003 SET INACTIVE; dann DROP UNDO TABLESPACE undo_003; Dies ist eine Funktion ab MySQL 8.0.","since":"MySQL 8.0","note":"Undo-Tablespaces per SQL anlegen/löschen (CREATE/DROP UNDO TABLESPACE) kam mit MySQL 8.0; vorher war die Anzahl der Undo-Tablespaces über innodb_undo_tablespaces fest vorgegeben und ließ sich zur Laufzeit nicht verwalten.","tags":["innodb","undo-log","tablespace","storage"]},{"id":"create-tablespace","task":"General Tablespace hinzufügen","category":"Speicher","command":"CREATE TABLESPACE ssd_ts ADD DATAFILE 'ssd_ts.ibd' ENGINE=InnoDB;","what":"Legt einen InnoDB General Tablespace an, den mehrere Tabellen gemeinsam nutzen können.","when":"Um Tabellen in einer bestimmten Datei/auf einer bestimmten Platte zu gruppieren oder die Anzahl der Dateien zu verringern.","caution":"Ist der DATAFILE-Pfad relativ, wird er unter datadir angelegt; für eine andere Platte sind ein absoluter Pfad und die nötigen Rechte erforderlich. Um eine Tabelle hierher zu verschieben: ALTER TABLE customer TABLESPACE ssd_ts; Ein Tablespace lässt sich nur löschen, wenn er leer ist.","tags":["tablespace","innodb","storage","create"]},{"id":"optimize-table","task":"Tabelle mit OPTIMIZE bearbeiten","category":"Speicher","command":"OPTIMIZE TABLE customer;","what":"Baut die Tabelle neu auf, um Fragmentierung zu beseitigen, ungenutzten Platz zurückzugewinnen und Indexstatistiken zu aktualisieren.","when":"Um Platz in einer InnoDB-Tabelle zurückzugewinnen, die durch viele DELETEs/UPDATEs aufgebläht ist.","caution":"Bei InnoDB läuft OPTIMIZE tatsächlich als Online-ALTER, das die Tabelle neu aufbaut, braucht aber dennoch I/O und temporären Plattenplatz; große Tabellen erfordern freien Platz in Höhe der Tabellengröße. Bei geringem Verkehr ausführen, um Sperren zu reduzieren.","tags":["optimize","table","defragment","storage"]},{"id":"table-schema-size","task":"Tabellen- und Schemagrößen abfragen","category":"Speicher","command":"SELECT table_schema AS schema_name, table_name AS table_name, ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') ORDER BY (data_length + index_length) DESC LIMIT 20;","what":"Listet die 20 größten Tabellen nach kombinierter Daten- und Indexgröße (MB).","when":"Um die Plattennutzung zu analysieren und herauszufinden, welche Tabellen Platz belegen.","caution":"Die Größen in information_schema sind bei InnoDB Näherungswerte. Für Summen je Schema nur nach table_schema gruppieren (GROUP BY), statt nach table_name.","tags":["size","table","schema","information-schema"]},{"id":"analyze-table","task":"Tabellenstatistiken aktualisieren (ANALYZE TABLE)","category":"Wartung","command":"ANALYZE TABLE customer;","what":"Berechnet die Verteilungsstatistiken der Indizes/Schlüssel der Tabelle neu, damit der Optimizer bessere Pläne wählen kann.","when":"Wenn nach Massenänderungen an Daten schlechte Abfragepläne auftreten.","caution":"Während der Ausführung wird kurz ein Metadata Lock auf die Tabelle genommen. Bei InnoDB basiert es auf Stichproben; für mehr Genauigkeit innodb_stats_persistent_sample_pages erhöhen.","tags":["analyze","statistics","optimizer","maintenance"]},{"id":"rebuild-table","task":"Tabelle/Indizes neu aufbauen","category":"Wartung","command":"ALTER TABLE customer ENGINE=InnoDB;","what":"Baut die Tabelle mit derselben Engine neu auf; erstellt Indizes und Datenseiten neu, um Fragmentierung und Index-Bloat zu beseitigen.","when":"Um eine Tabelle mit einem Null-ALTER aufzufrischen oder ein beschädigtes Seitenlayout zu reparieren.","caution":"Große Tabellen erfordern temporären Plattenplatz in Höhe der Tabellengröße. Um Sperren zu reduzieren: ALTER TABLE customer ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE;","tags":["rebuild","alter","innodb","maintenance"]},{"id":"check-table","task":"Tabellenintegrität prüfen (CHECK TABLE)","category":"Wartung","command":"CHECK TABLE customer;","what":"Prüft die Tabelle und ihre Indizes auf Korruption und liefert einen Status zurück.","when":"Bei unerwarteten Fehlern oder bei Verdacht auf Korruption.","caution":"Während der Prüfung wird eine Lesesperre auf die Tabelle genommen. Wird Korruption gefunden, wird InnoDB meist per Dump/Restore repariert; REPAIR TABLE funktioniert nur bei Engines wie MyISAM/Archive.","tags":["check","integrity","corruption","maintenance"]},{"id":"set-global-persist","task":"Globale Variable ändern und dauerhaft speichern","category":"Parameter","command":"SET PERSIST innodb_buffer_pool_size = 4294967296;","what":"Setzt die Variable sowohl auf dem laufenden Server als auch dauerhaft über Neustarts hinweg (mysqld-auto.cnf).","when":"Um eine Servervariable ohne Neustart dauerhaft zu ändern.","caution":"SET PERSIST ist eine Funktion ab MySQL 8.0; MariaDB unterstützt sie nicht (dort my.cnf bearbeiten und neu starten). Für eine reine Laufzeitänderung SET GLOBAL verwenden. Variablen, die nur beim Neustart wirken, erfordern SET PERSIST_ONLY.","since":"MySQL 8.0","note":"SET PERSIST kam mit MySQL 8.0 und macht eine Variable dauerhaft, indem es sie in mysqld-auto.cnf schreibt; vorher (und in MariaDB) erforderte das manuelles Bearbeiten von my.cnf und einen Neustart.","tags":["set-persist","global","variable","parameter"]},{"id":"show-variables","task":"Aktuellen Wert einer Variable anzeigen","category":"Parameter","command":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';","what":"Zeigt den wirksamen Wert der angegebenen Servervariable (oder der zum Muster passenden).","when":"Um den laufenden Wert eines Parameters zu prüfen.","caution":"Für den globalen statt des Session-Werts SHOW GLOBAL VARIABLES verwenden. Um zu sehen, woher der Wert stammt (Datei, Kommandozeile, persist): SELECT * FROM performance_schema.variables_info WHERE variable_name = 'innodb_buffer_pool_size';","tags":["show-variables","parameter","current-value","config"]},{"id":"flush-privileges","task":"Grant-Tabellen neu laden (FLUSH PRIVILEGES)","category":"Parameter","command":"FLUSH PRIVILEGES;","what":"Lädt Grant-Tabellen wie mysql.user neu in den Speicher, nachdem sie direkt geändert wurden.","when":"Um Änderungen zu aktivieren, die per manuellem INSERT/UPDATE an den Grant-Tabellen vorgenommen wurden.","caution":"GRANT/REVOKE/CREATE USER wenden Rechte bereits sofort an; FLUSH PRIVILEGES danach ist unnötig. Es wird nur gebraucht, wenn du die Grant-Tabellen direkt änderst.","tags":["flush-privileges","grants","privileges","reload"]},{"id":"setup-replica","task":"Replica einrichten (CHANGE REPLICATION SOURCE)","category":"Replikation","command":"CHANGE REPLICATION SOURCE TO SOURCE_HOST='source_host', SOURCE_PORT=3306, SOURCE_USER='replicator', SOURCE_PASSWORD='secret', SOURCE_AUTO_POSITION=1;","what":"Konfiguriert diesen Server als Replica, die der angegebenen Quelle per GTID-basierter Auto-Positionierung folgt.","when":"Um eine neue Replica einzurichten oder die Replikationsquelle zu ändern.","caution":"Dies ist die Terminologie ab MySQL 8.0.23; ältere Versionen nutzen CHANGE MASTER TO ... MASTER_HOST (in 8.0.23–8.3 ein Alias, in 8.4 entfernt). SOURCE_AUTO_POSITION=1 setzt aktiviertes GTID voraus (gtid_mode=ON); sonst müssen SOURCE_LOG_FILE und SOURCE_LOG_POS angegeben werden. Die Replikation muss vor dem Befehl gestoppt sein. Wurde der Replikationsbenutzer mit dem Standard caching_sha2_password ab 8.0 angelegt, erfordert die Verbindung TLS: SOURCE_SSL=1 ergänzen oder ohne TLS GET_SOURCE_PUBLIC_KEY=1 setzen; sonst kann sich der I/O-Thread mit Fehler 2061 nicht verbinden.","since":"MySQL 8.0.23","note":"CHANGE REPLICATION SOURCE TO (SOURCE_HOST usw.) kam mit MySQL 8.0.23; vorher wurde CHANGE MASTER TO ... MASTER_HOST / MASTER_AUTO_POSITION verwendet.","tags":["replication","change-source","replica","setup"]},{"id":"start-stop-replica","task":"Replica starten / stoppen","category":"Replikation","command":"STOP REPLICA; START REPLICA;","what":"Stoppt die I/O- und SQL-Threads der Replikation und startet sie neu.","when":"Für Wartung, Konfigurationsänderungen oder um einen Replikationsfehler zu beheben.","caution":"Dies ist die Terminologie ab MySQL 8.0.22; ältere Versionen nutzen STOP SLAVE / START SLAVE. Nur für den SQL-Thread: STOP REPLICA SQL_THREAD; Nach STOP REPLICA kannst du CHANGE REPLICATION SOURCE ausführen.","since":"MySQL 8.0.22","note":"START REPLICA / STOP REPLICA kam mit MySQL 8.0.22; vorher wurde START SLAVE / STOP SLAVE verwendet (die alte Syntax funktioniert in 8.0.22–8.3 als Alias und wurde in 8.4 entfernt).","tags":["replication","start","stop","replica"]},{"id":"replica-status","task":"Replica-Status anzeigen (SHOW REPLICA STATUS)","category":"Replikation","command":"SHOW REPLICA STATUS\\G","what":"Zeigt detailliert die Verbindung der Replica zur Quelle, Thread-Zustände, Verzögerung und eventuelle Fehler.","when":"Um zu prüfen, ob die Replikation gesund ist und wie weit sie zurückliegt.","caution":"Dies ist die Terminologie ab MySQL 8.0.22; ältere Versionen nutzen SHOW SLAVE STATUS (in 8.0.22–8.3 ein Alias, in 8.4 entfernt). Wichtige Felder: Replica_IO_Running und Replica_SQL_Running müssen 'Yes' sein, Seconds_Behind_Source zeigt die Verzögerung, Last_Error den Fehler.","since":"MySQL 8.0.22","note":"SHOW REPLICA STATUS kam mit MySQL 8.0.22; vorher wurde SHOW SLAVE STATUS verwendet, und die Felder hießen etwa Slave_IO_Running / Seconds_Behind_Master.","tags":["replication","status","replica","lag"]},{"id":"skip-gtid-transaction","task":"Fehlerhafte Transaktion überspringen (GTID)","category":"Replikation","command":"STOP REPLICA; SET GTID_NEXT='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:15'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START REPLICA;","what":"Committet eine leere Transaktion für die angegebene GTID, sodass diese Transaktion als 'angewendet' gilt, und lässt die Replikation weiterlaufen.","when":"Wenn ein doppeltes/nicht anwendbares Event auf der Replica die Replikation stoppt, um dieses Event zu überspringen.","caution":"Das Überspringen einer Transaktion erzeugt Dateninkonsistenz zwischen Replica und Quelle; nur verwenden, wenn du die Wirkung des übersprungenen Events kennst. Den GTID-Wert aus Last_Error / Retrieved_Gtid_Set in SHOW REPLICA STATUS entnehmen. Ist GTID aus, SET GLOBAL sql_replica_skip_counter = 1; verwenden (ab 8.0.26; vorher sql_slave_skip_counter, ab 8.0.26 ein veralteter Alias).","tags":["replication","gtid","skip","recovery"]},{"id":"kill-query-connection","task":"Abfrage oder Verbindung beenden (KILL)","category":"Session","command":"KILL QUERY 4567;","what":"Beendet die gerade auf der angegebenen Verbindungs-ID laufende Abfrage, ohne die Verbindung zu schließen.","when":"Um eine lang laufende/hängende Abfrage zu stoppen.","caution":"Um die gesamte Verbindung (Session) zu schließen: KILL CONNECTION 4567; (oder einfach KILL 4567). Wird eine laufende Schreibtransaktion beendet, wird sie zurückgerollt, was bei großen Tabellen lange dauern kann.","tags":["kill","query","connection","session"]},{"id":"process-list","task":"Prozessliste anzeigen","category":"Session","command":"SELECT id, user, host, db, command, time, state, LEFT(info, 100) AS query FROM performance_schema.processlist WHERE command <> 'Sleep' ORDER BY time DESC;","what":"Listet alle aktiven (nicht idle) Verbindungen, die laufende Abfrage und die verstrichene Zeit.","when":"Um aktive Sessions und lang laufende Abfragen zu diagnostizieren.","caution":"Kurzform: SHOW FULL PROCESSLIST; performance_schema.processlist liefert alle Spalten und lässt sich mit WHERE filtern. Die Spalte time ist in Sekunden.","tags":["processlist","session","activity","performance-schema"]},{"id":"blocking-locks","task":"Blockierende Sperren anzeigen (data_lock_waits)","category":"Session","command":"SELECT r.trx_id AS waiting_trx, r.trx_mysql_thread_id AS waiting_thread, LEFT(r.trx_query,80) AS waiting_query, b.trx_id AS blocking_trx, b.trx_mysql_thread_id AS blocking_thread, LEFT(b.trx_query,80) AS blocking_query FROM performance_schema.data_lock_waits w JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_engine_transaction_id JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_engine_transaction_id;","what":"Zeigt, welche Transaktion von welcher blockiert wird, mit den zugehörigen Abfragen und Thread-IDs.","when":"Um Sperrwartezustände und durch Deadlocks verursachte Verlangsamungen zu diagnostizieren.","caution":"performance_schema.data_lock_waits kam mit MySQL 8.0; ältere Versionen nutzen information_schema.innodb_lock_waits. Um den blockierenden Thread zu stoppen, KILL blocking_thread; verwenden.","since":"MySQL 8.0","note":"performance_schema.data_lock_waits kam mit MySQL 8.0; in MySQL 5.7 und früher wurde für dieselben Informationen information_schema.innodb_lock_waits verwendet.","tags":["blocking","lock","data-lock-waits","deadlock"]}]},"recipes/mysql-ext.json":{"engineId":"mysql","engine":"MySQL","order":2,"recipes":[{"id":"replica-applier-health","task":"Replica-Lag und Fehler der Applier-Worker überwachen (performance_schema)","category":"Replikation","command":"-- Receiver (I/O) thread: connection state and last error\nSELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER,\n       LEFT(LAST_ERROR_MESSAGE, 100) AS last_error,\n       LAST_QUEUED_TRANSACTION, LAST_HEARTBEAT_TIMESTAMP\nFROM performance_schema.replication_connection_status;\n\n-- Applier workers: error, applied GTID and real lag\nSELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE, LAST_ERROR_NUMBER,\n       LEFT(LAST_ERROR_MESSAGE, 120) AS last_error,\n       LAST_APPLIED_TRANSACTION,\n       APPLYING_TRANSACTION,\n       IF(APPLYING_TRANSACTION = '', 0,\n          TIMESTAMPDIFF(SECOND, APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP, NOW(6))) AS current_lag_sec,\n       TIMESTAMPDIFF(MICROSECOND, LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP,\n                     LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP) / 1000000 AS last_trx_lag_sec\nFROM performance_schema.replication_applier_status_by_worker\nORDER BY CHANNEL_NAME, WORKER_ID;","what":"Schlüsselt auf, was SHOW REPLICA STATUS in einer Zeile zusammenfasst, je Kanal und je Worker: Zustand des Receiver-Threads, letzter Fehler jedes Workers, angewendete / gerade angewendete GTID und die echte Verzögerung, berechnet aus dem ursprünglichen Commit-Zeitstempel.","when":"Wenn Seconds_Behind_Source unzuverlässig wirkt (springt zwischen 0 und großen Werten, NULL), um herauszufinden, welcher Worker auf einer Multi-Threaded Replica hängt, und um eine genaue Lag-Metrik ins Monitoring einzuspeisen.","caution":"current_lag_sec ist nur aussagekräftig, während eine Transaktion angewendet wird; eine inaktive Replica liefert 0. Um zu sehen, ob auch der Receiver zurückliegt, LAST_QUEUED_TRANSACTION mit gtid_executed der Quelle vergleichen. Der ursprüngliche Commit-Zeitstempel stammt von der Uhr der Quelle; Uhrenabweichungen zwischen Servern (NTP) verfälschen das Ergebnis. Seconds_Behind_Source bleibt zu Beginn einer großen Transaktion nahe null und springt am Ende; lange Transaktionen machen diese Metrik irreführend.","note":"Die Spalten ORIGINAL_COMMIT_TIMESTAMP / END_APPLY_TIMESTAMP kamen mit 8.0.2 und decken die gesamte Kette ab (Quelle → Zwischenstufe → Replica); 5.7 hat nur Seconds_Behind_Master.","since":"MySQL 8.0","tags":["replication","lag","applier","worker","performance_schema","delay"]},{"id":"replica-parallel-applier","task":"Multi-Threaded Replica optimieren (replica_parallel_workers)","category":"Replikation","command":"-- Replica: stop the applier, set worker count and commit order\nSTOP REPLICA SQL_THREAD;\nSET PERSIST replica_parallel_workers = 8;\nSET PERSIST replica_preserve_commit_order = ON;\nSTART REPLICA SQL_THREAD;\nSELECT COUNT(*) AS workers FROM performance_schema.replication_applier_status_by_worker;\n\n-- Source (8.0 only): switch dependency tracking to WRITESET (default in 8.4, variable removed)\nSET PERSIST binlog_transaction_dependency_tracking = WRITESET;","what":"Setzt die Anzahl der Worker, die Transaktionen auf der Replica parallel anwenden; replica_preserve_commit_order hält die Commit-Reihenfolge identisch zur Quelle. WRITESET-Abhängigkeitserkennung auf der Quelle erlaubt es, Transaktionen, die verschiedene Zeilen betreffen, parallel anzuwenden, selbst wenn sie auf der Quelle seriell committet wurden.","when":"Wenn die Replica mit der Schreibrate der Quelle nicht mithält und sich Lag aufbaut, während der Applier faktisch single-threaded arbeitet.","caution":"Eine neue Worker-Anzahl wirkt, wenn der Applier neu startet. Mehr Worker als CPU-Kerne erzeugen Koordinations-Overhead; beginne im Bereich 8–16 und beobachte Lag und Worker-Fehler. Eine einzelne riesige Transaktion oder schweres DML auf einer Tabelle ohne PK lässt sich nicht parallelisieren, was meist die eigentliche Ursache dauerhaften Lags ist. replica_preserve_commit_order=OFF bringt mehr Parallelität, erlaubt aber kurzzeitig Commits außer der Reihe auf der Replica; auf Replicas, die Lesezugriffe bedienen, auf ON lassen.","note":"Seit 8.0.27 gelten die Standardwerte replica_parallel_workers=4, replica_parallel_type=LOGICAL_CLOCK und replica_preserve_commit_order=ON. Vor 8.0.26 hießen die Variablen slave_parallel_workers / slave_preserve_commit_order. binlog_transaction_dependency_tracking wurde in 8.0.35 als veraltet markiert und in 8.4 entfernt, wo das WRITESET-Verhalten Standard ist.","since":"MySQL 8.0.26","tags":["replication","parallel","replica_parallel_workers","writeset","lag","mta"]},{"id":"replica-fix-row-conflict","task":"Defekte Replica reparieren: Zeilenkonflikte 1062 / 1032","category":"Replikation","command":"-- 1) Failing worker, error and GTID\nSELECT WORKER_ID, LAST_ERROR_NUMBER, LEFT(LAST_ERROR_MESSAGE, 250) AS last_error, APPLYING_TRANSACTION\nFROM performance_schema.replication_applier_status_by_worker\nWHERE LAST_ERROR_NUMBER <> 0;\n\n-- 2) Shell: decode the row images of that transaction from the relay log\nmysqlbinlog --base64-output=DECODE-ROWS -vv --include-gtids='af6bc361-c353-11f1-8f12-f69e63ad6c0e:11' /var/lib/mysql/db2-relay-bin.0* | grep '^###'\n\n-- 3a) 1062 Duplicate entry: delete the extra row on the replica without binlogging\nSET SESSION sql_log_bin = 0;\nDELETE FROM shop.orders WHERE id = 20001;\n-- 3b) 1032 Can't find record: insert the missing row from the WHERE (before image) values\nINSERT INTO shop.orders (id, cust, amount, note, created) VALUES (6, 6, 9.00, 'n6', '2026-10-08 20:09:44');\nSET SESSION sql_log_bin = 1;\n\n-- 4) Start the applier and verify\nSTART REPLICA SQL_THREAD;\nSELECT SERVICE_STATE, LAST_ERROR_NUMBER FROM performance_schema.replication_applier_status_by_coordinator;","what":"Behebt die zwei häufigsten Abbrüche zeilenbasierter Replikation (1062 duplicate key, 1032 row not found), ohne die Transaktion zu überspringen, indem die Zeile auf der Replica in den Zustand gebracht wird, den die Quelle erwartet; die Datenkonsistenz bleibt erhalten.","when":"Wenn Replica_SQL_Running = No mit Fehler 1062 / 1032 ist; als saubere Reparatur, statt die GTID mit einer leeren Transaktion zu überspringen.","caution":"Die Korrektur auf der Replica muss mit sql_log_bin=0 laufen; sonst entsteht eine Errant GTID, die beim Failover zum Problem wird. Ist super_read_only=ON, vorübergehend deaktivieren und danach wieder aktivieren. Die Before-Image-Werte setzen binlog_row_image=FULL voraus; bei MINIMAL wird nur der PK protokolliert, und der Rest der Zeile muss von der Quelle gelesen werden. Ein Konflikt in einer Tabelle bedeutet meist, dass es weitere gibt; nach der Reparatur die Tabelle mit pt-table-checksum mit der Quelle vergleichen. Pfad des Relay Logs: SELECT @@relay_log_basename;","note":"replica_exec_mode=IDEMPOTENT verschluckt diese Fehler stillschweigend; es ist für NDB und Sonderfälle gedacht und darf nicht dauerhaft aktiv bleiben. Muss die Transaktion wirklich übersprungen werden, siehe Rezept skip-gtid-transaction.","since":"MySQL 8.0.22","tags":["replication","1062","1032","mysqlbinlog","relay log","broken replica"]},{"id":"gtid-errant-transactions","task":"Errant-GTID-Transaktionen finden und neutralisieren","category":"Replikation","command":"-- 1) On the source: get gtid_executed\nSELECT @@GLOBAL.gtid_executed;\n\n-- 2) On the replica: GTIDs missing on the source (empty result = clean)\nSELECT GTID_SUBTRACT(@@GLOBAL.gtid_executed,\n       'af6bc361-c353-11f1-8f12-f69e63ad6c0e:1-13') AS errant_gtids;\n\n-- 3) If the content is harmless: commit an empty transaction per errant GTID on the source\nSET GTID_NEXT = 'b030e5a6-c353-11f1-a887-523817ec642a:1'; BEGIN; COMMIT;\nSET GTID_NEXT = 'b030e5a6-c353-11f1-a887-523817ec642a:2'; BEGIN; COMMIT;\nSET GTID_NEXT = 'AUTOMATIC';","what":"Erkennt per GTID-Mengendifferenz Transaktionen, die direkt auf einer Replica ohne Gegenstück auf der Quelle ausgeführt wurden (errant), und gleicht die GTID-Mengen in der Topologie an, indem sie auf der Quelle mit leeren Transaktionen markiert werden.","when":"Als routinemäßige Prüfung vor Failover / Switchover; wenn Orchestrator oder MySQL Shell sich wegen Errant GTIDs weigert, eine Replica hochzustufen, oder eine Replica beim Anhängen an eine neue Quelle mit 1236 fehlschlägt.","caution":"Finde zuerst aus dem Binlog der Replica heraus, was die Errant-Transaktion getan hat (mysqlbinlog --include-gtids=...). Hat sie Daten geändert, überdeckt die leere Transaktion nur die GTID und der Datenunterschied bleibt; baue die Replica neu auf (CLONE) oder gleiche die Daten manuell an. Auf der Quelle committete leere Transaktionen verbreiten sich auf jede Replica; das ist beabsichtigt. Halte super_read_only=ON auf Replicas, um eine Wiederholung zu verhindern.","note":"Sobald die Replica hochgestuft ist, fordern die anderen Replicas diese GTIDs von ihr an; wurde das Binlog bereits gelöscht, bricht die Replikation mit Fehler 1236 ab. Deshalb sind Errant GTIDs ein Risiko, das bis zum Failover unsichtbar bleibt.","since":"MySQL 5.6","tags":["gtid","errant","gtid_subtract","failover","replication","empty transaction"]},{"id":"replica-rebuild-clone","task":"Replica mit dem CLONE-Plugin komplett neu aufbauen","category":"Replikation","command":"-- Donor (healthy replica or source)\nINSTALL PLUGIN clone SONAME 'mysql_clone.so';\nCREATE USER 'clone_donor'@'%' IDENTIFIED BY 'Cl0ne#Donor';\nGRANT BACKUP_ADMIN ON *.* TO 'clone_donor'@'%';\n\n-- Recipient (replica to rebuild; its data is replaced)\nINSTALL PLUGIN clone SONAME 'mysql_clone.so';\nSET GLOBAL clone_valid_donor_list = 'db1:3306';\nCLONE INSTANCE FROM 'clone_donor'@'db1':3306 IDENTIFIED BY 'Cl0ne#Donor';\n\n-- After the restart: verify and start replication\nSELECT STATE, ERROR_NO, BINLOG_FILE, BINLOG_POSITION, GTID_EXECUTED FROM performance_schema.clone_status;\nSELECT STAGE, STATE, ROUND(DATA / 1024 / 1024) AS data_mb FROM performance_schema.clone_progress;\nCHANGE REPLICATION SOURCE TO SOURCE_HOST = 'db1', SOURCE_PORT = 3306, SOURCE_USER = 'repl', SOURCE_PASSWORD = 'Repl#2024', SOURCE_AUTO_POSITION = 1, SOURCE_SSL = 1;\nSTART REPLICA;","what":"Kopiert die InnoDB-Daten des Donors physisch über das Netzwerk, startet den Empfänger neu und übernimmt den GTID-Zustand; erzeugt eine konsistente Replica ohne separate Schritte für Backup, Übertragung und Restore.","when":"Um eine korrupte Replica, eine mit Errant GTIDs oder eine, die so weit zurückliegt, dass ihre Binlogs gelöscht wurden, neu aufzubauen, oder um eine neue Replica hinzuzufügen.","caution":"Alle Daten auf dem Empfänger (einschließlich Benutzer) werden durch die Kopie des Donors ersetzt; das root-Passwort wird das des Donors. Donor und Empfänger brauchen dieselbe Release-Serie, Plattform und Plugin-Ausstattung (vor 8.0.37 war exakt dasselbe Point Release nötig). Der Benutzer, der den Befehl auf dem Empfänger ausführt, braucht CLONE_ADMIN. Nach Abschluss des Klons startet mysqld sich selbst neu; ohne Supervisor wie systemd / mysqld_safe liefert es 3707 'Restart server failed (mysqld is not managed by supervisor process)'. Das ist kein Fehlschlag: Der Klon ist fertig, starte den Server manuell. Der Donor erfährt hohe IO-/Netzwerklast; begrenze sie mit clone_max_concurrency und clone_max_data_bandwidth. Bei abgeschaltetem GTID BINLOG_FILE / BINLOG_POSITION aus clone_status als SOURCE_LOG_FILE / SOURCE_LOG_POS verwenden.","note":"Nur InnoDB-Daten werden geklont; MyISAM- und CSV-Tabellen kommen leer an. In InnoDB Cluster nutzt addInstance(..., {recoveryMethod: 'clone'}) denselben Mechanismus.","since":"MySQL 8.0.17","tags":["clone","rebuild","replica","provisioning","gtid","reseed"]},{"id":"delayed-replica-recover","task":"Nach einem fehlerhaften DROP / DELETE mit einer verzögerten Replica wiederherstellen (SOURCE_DELAY + UNTIL)","category":"Replikation","command":"-- Setup: the replica applies events one hour behind\nSTOP REPLICA;\nCHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600;\nSTART REPLICA;\n\n-- During the incident: stop the applier (the receiver keeps fetching binlogs)\nSTOP REPLICA SQL_THREAD;\n\n-- Find the bad transaction's GTID in the source binlog, apply up to just before it\nCHANGE REPLICATION SOURCE TO SOURCE_DELAY = 0;\nSTART REPLICA SQL_THREAD UNTIL SQL_BEFORE_GTIDS = 'af6bc361-c353-11f1-8f12-f69e63ad6c0e:17';\nSELECT SERVICE_STATE FROM performance_schema.replication_applier_status_by_coordinator;","what":"Lässt die Replica Events mit festem zeitlichem Abstand anwenden; trifft ein fehlerhaftes DROP / DELETE die Quelle, rollst du die Replica bis kurz vor diese Transaktion vor und holst die verlorenen Daten von dort zurück.","when":"Als schnelle Wiederherstellungsebene gegen menschliche Fehler bei großen Datenbanken, bei denen ein Restore aus dem Backup Stunden dauern würde.","caution":"Ist der Coordinator OFF, sind die Daten im Zustand unmittelbar vor der fehlerhaften Operation; die betroffene Tabelle mit mysqldump sichern und in die Quelle laden. Danach die Replica wieder in den verzögerten Modus versetzen; wendet sie die fehlerhafte Transaktion ebenfalls an, sind die Daten auch hier weg. Eine verzögerte Replica darf kein Failover-Kandidat sein (in Orchestrator / Router / ProxySQL ausschließen). Relay Logs sammeln sich über die gesamte Verzögerung an; Platte entsprechend dimensionieren.","note":"Vor 8.0.23 CHANGE MASTER TO MASTER_DELAY verwenden. Die Verzögerung wird ab dem Commit auf der unmittelbaren Quelle gemessen, verzögerte Zwischen-Replicas in einer Kette addieren sich also. SQL_BEFORE_GTIDS stoppt vor der angegebenen GTID, SQL_AFTER_GTIDS wendet sie mit an; vor 8.2 (einschließlich 8.0) löst SQL_AFTER_GTIDS mit Multi-Threaded Applier eine Warnung aus und die Replica wechselt in den Single-Threaded-Modus.","since":"MySQL 8.0.23","tags":["delayed replica","source_delay","until","sql_before_gtids","recovery","drop table"]},{"id":"mysqldump-consistent","task":"Konsistentes logisches Backup mit GTID-Informationen per mysqldump erstellen","category":"Backup & PITR","command":"mysqldump -h db1 -u backup -p \\\n  --single-transaction --source-data=2 --set-gtid-purged=ON \\\n  --routines --events --triggers --hex-blob \\\n  --all-databases | gzip > /backup/full_$(date +%F_%H%M).sql.gz\n\n# Restore: GTID sets must not overlap on the target\nzcat /backup/full_2026-10-08_0200.sql.gz | mysql -h db2 -u root -p","what":"Sichert InnoDB-Tabellen innerhalb eines einzigen konsistenten Snapshots und schreibt Binlog-Datei / -Position (als Kommentar) und eine Anweisung SET @@GLOBAL.GTID_PURGED an den Anfang, sodass der Dump eine Replica initialisieren oder als Startpunkt für binlogbasiertes PITR dienen kann.","when":"Für Datenbanken bis zu einigen zehn GB, zum Verschieben von Schemas / Tabellen und für logische Migration zwischen Versionen.","caution":"--single-transaction ist nur für InnoDB konsistent; MyISAM-Tabellen kommen inkonsistent heraus. DDL (ALTER / RENAME / TRUNCATE) auf einer Tabelle während des Dumps lässt den Dump fehlschlagen oder die Tabelle auslassen. --source-data / --set-gtid-purged nehmen am Anfang kurz einen globalen Read Lock (Privileg RELOAD nötig); läuft in diesem Moment eine lange Abfrage, stehen alle Schreibvorgänge still, solange die Sperre wartet. Ein Teil-Dump (--databases) schreibt trotzdem alle GTIDs; beim Übertragen einer Teilmenge auf einen anderen Server --set-gtid-purged=OFF verwenden. Der Dump ist single-threaded; für große Datenmengen MySQL Shell Dump oder XtraBackup bevorzugen.","note":"--source-data ersetzte --master-data in 8.0.26. mysqlpump wurde in 8.0.34 als veraltet markiert und in 8.4 entfernt; für parallele logische Backups MySQL Shell util.dumpInstance verwenden.","since":"MySQL 8.0.26","tags":["mysqldump","backup","single-transaction","gtid","logical backup","dump"]},{"id":"mysqlsh-dump-load","task":"Paralleler Dump / Load mit MySQL Shell (util.dumpInstance, loadDump)","category":"Backup & PITR","command":"# Parallel, zstd-compressed, consistent instance dump\nmysqlsh backup@db1:3306 -- util dump-instance /backup/dump_20261008 --threads=8 --compression=zstd --consistent=true\n# Single schema\nmysqlsh backup@db1:3306 -- util dump-schemas shop --outputUrl=/backup/shop_20261008 --threads=8\n\n# Load: local_infile must be temporarily ON on the target\nmysql -h db2 -u root -p -e \"SET GLOBAL local_infile = ON\"\nmysqlsh root@db2:3306 -- util load-dump /backup/dump_20261008 --threads=8 --skipBinlog=false\nmysql -h db2 -u root -p -e \"SET GLOBAL local_infile = OFF\"","what":"Teilt Tabellen in Chunks und sichert / lädt sie mit vielen Threads; Dump und Restore sind um ein Vielfaches schneller als mysqldump. Das Dump-Verzeichnis speichert GTID- und Binlog-Koordinaten in @.json.","when":"Für logische Backups von Hunderten GB, logische Migration bei Upgrades und Umzüge On-Prem → Cloud (RDS, HeatWave).","caution":"--consistent=true nimmt kurz einen globalen Read Lock / Backup Lock; Schreibvorgänge warten, wenn eine lange Abfrage läuft. loadDump nutzt LOAD DATA LOCAL, das Ziel braucht daher local_infile=ON, was danach wieder abgeschaltet werden sollte. Ein unterbrochener Load setzt dort fort, wo er stoppte (Progress-Datei); mit --resetProgress von vorn beginnen. Benutzerkonten werden standardmäßig nicht geladen (--loadUsers=true). --skipBinlog=true deaktiviert das Binlogging während des Ladens; auf false lassen, wenn das Ziel Replicas hat.","note":"Dies ist kein physisches Backup; für PITR Binlogs ab dem Wert gtidExecuted in der @.json des Dumps anwenden. Die Optionen ocimds / compatibility erzeugen Dumps, die die Einschränkungen von HeatWave / RDS einhalten.","since":"MySQL Shell 8.0.21","tags":["mysqlsh","dumpinstance","loaddump","parallel","logical backup","migration"]},{"id":"xtrabackup-full-incremental","task":"Physisches Voll- + inkrementelles Backup und Restore mit XtraBackup","category":"Backup & PITR","command":"# Full backup\nxtrabackup --backup --host=127.0.0.1 --user=bkp --password='Bkp#2024' --target-dir=/backup/full --parallel=4\n# Incremental (based on the previous backup)\nxtrabackup --backup --host=127.0.0.1 --user=bkp --password='Bkp#2024' --target-dir=/backup/inc1 --incremental-basedir=/backup/full\n\n# Prepare: full and intermediate incrementals with --apply-log-only, final step without it\nxtrabackup --prepare --apply-log-only --target-dir=/backup/full\nxtrabackup --prepare --apply-log-only --target-dir=/backup/full --incremental-dir=/backup/inc1\nxtrabackup --prepare --target-dir=/backup/full\n\n# Restore: mysqld stopped and datadir empty\nsystemctl stop mysqld\nxtrabackup --copy-back --target-dir=/backup/full --datadir=/var/lib/mysql\nchown -R mysql:mysql /var/lib/mysql\nsystemctl start mysqld\ncat /backup/full/xtrabackup_binlog_info","what":"Erstellt ein physisches Hot Backup, indem InnoDB-Dateien und das Redo-Log bei laufendem Server kopiert werden; ein inkrementelles Backup enthält nur seit dem vorherigen Backup geänderte Seiten. Der Prepare-Schritt wendet Redo an und macht das Backup konsistent.","when":"Für große Datenbanken, die schnelle Restores brauchen (um ein Vielfaches schneller als logisch), zum Initialisieren von Replicas und für Strategien mit täglichem Voll- + stündlichem inkrementellem Backup.","caution":"Die XtraBackup-Version muss zur Release-Serie des Servers passen (Server 8.0 → XtraBackup 8.0, 8.4 → 8.4); nutzt der Server ein neueres Redo-/Data-Dictionary-Format als der XtraBackup-Build, wird das Backup verweigert. Ein nicht vorbereitetes Backup ist inkonsistent. Wird --apply-log-only bei Zwischenschritten vergessen, sind spätere Inkremente unbrauchbar. Das datadir muss für den Restore leer sein; --move-back spart Platz, verbraucht aber das Backup. Backup-Benutzer: BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT sowie SELECT auf performance_schema.log_status, keyring_component_status, replication_group_members.","note":"xtrabackup_binlog_info enthält Binlog-Datei / -Position und GTID-Menge des Backups; PITR spielt Binlogs ab diesem Punkt ein. Zum Streamen auf einen anderen Host --stream=xbstream | ssh host 'xbstream -x -C /backup' verwenden.","since":"XtraBackup 8.0","tags":["xtrabackup","physical backup","incremental","prepare","restore","percona"]},{"id":"pitr-binlog-replay","task":"Point-in-Time Recovery mit Binlogs: fehlerhafte Transaktion finden und überspringen","category":"Backup & PITR","command":"# 1) Find the bad event's GTID and position (narrow by time range)\nmysqlbinlog --base64-output=DECODE-ROWS -vv --start-datetime='2026-10-08 20:00:00' --stop-datetime='2026-10-08 20:30:00' /var/lib/mysql/binlog.000003 | grep -B12 -iE '^(DROP|TRUNCATE)' | grep -E 'GTID_NEXT|^# at|^#[0-9]{6}|^DROP|^TRUNCATE'\n\n# 2) Restore the backup on a separate server (the dump sets GTID_PURGED)\nzcat /backup/full_2026-10-08_0200.sql.gz | mysql -h restore-db -u root -p\n\n# 3a) Replay all binlogs skipping only the bad transaction\nmysqlbinlog --exclude-gtids='af6bc361-c353-11f1-8f12-f69e63ad6c0e:23' /binlogs/binlog.000003 /binlogs/binlog.000004 | mysql -h restore-db -u root -p\n# 3b) Or stop right before the bad event's GTID event\nmysqlbinlog --stop-position=537205 /binlogs/binlog.000003 | mysql -h restore-db -u root -p","what":"Spielt nach dem Restore des Backups Binlogs der Reihe nach ein, um die Datenbank auf den gewünschten Punkt zu bringen. Mit aktivem GTID werden bereits im Backup enthaltene Transaktionen automatisch übersprungen; --exclude-gtids entfernt nur die fehlerhafte Operation und behält jede Änderung danach.","when":"Nach einem fehlerhaften DROP / DELETE / UPDATE, um wiederherzustellen, ohne Änderungen zwischen Backup und Vorfall zu verlieren.","caution":"Auf einem separaten Server wiederherstellen, nicht in der Produktion; prüfen und dann nur die betroffene Tabelle übertragen. --start/--stop-datetime werden in der Zeitzone des Rechners interpretiert, auf dem mysqlbinlog läuft. --stop-position gilt nur für die zuletzt angegebene Datei; bei mehreren Dateien die früheren in einem separaten Befehl vollständig einspielen. --exclude-gtids ist nur bei einer unabhängigen Operation sicher; schreiben spätere Transaktionen in die gelöschte Tabelle, schlagen auch sie fehl. Die benötigten Binlogs dürfen auf der Quelle nicht gelöscht sein; die Binlog-Aufbewahrung länger als mindestens ein Backup-Intervall halten.","note":"mysqlbinlog und mysql in einer einzigen Pipe mit allen Dateien in einem Befehl ausführen; das Einspielen einzelner Dateien nacheinander zerstört temporäre Tabellen und dateiübergreifende Transaktionen. Für sehr große Binlog-Mengen ist eine Alternative, die Binlogs als Relay Logs auf dem Restore-Server abzulegen und mit dem (parallelen) SQL-Thread anzuwenden.","since":"MySQL 8.0","tags":["pitr","mysqlbinlog","binlog","point in time","exclude-gtids","recovery"]},{"id":"innodb-cluster-status","task":"Zustand von InnoDB Cluster / Group Replication prüfen","category":"InnoDB Cluster","command":"# Shell: topology, primary, fault tolerance, lag\nmysqlsh icadmin@db1:3306 -- cluster status --extended=1\nmysqlsh icadmin@db1:3306 -- cluster list-routers\n\n-- SQL: group members and queues from any member\nSELECT m.MEMBER_HOST, m.MEMBER_PORT, m.MEMBER_STATE, m.MEMBER_ROLE, m.MEMBER_VERSION,\n       s.COUNT_TRANSACTIONS_IN_QUEUE AS certify_queue,\n       s.COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS apply_queue,\n       s.COUNT_CONFLICTS_DETECTED AS conflicts\nFROM performance_schema.replication_group_members m\nJOIN performance_schema.replication_group_member_stats s ON s.MEMBER_ID = m.MEMBER_ID\nORDER BY m.MEMBER_ROLE, m.MEMBER_HOST;","what":"Zeigt den Gesamtzustand des Clusters (OK / OK_NO_TOLERANCE / NO_QUORUM), die Primary, den Zustand jedes Mitglieds und die Certification-/Applier-Queues; die Queues zeigen, welches Mitglied zurückliegt, und das Flow-Control-Risiko.","when":"Vor und nach jedem Eingriff; bei Alarmen, verlangsamten Schreibvorgängen oder wenn ein Mitglied RECOVERING / ERROR / UNREACHABLE zeigt.","caution":"statusText sollte 'can tolerate up to ONE failure' (3 Mitglieder) lauten. Bei NO_QUORUM stoppen Schreibvorgänge; forceQuorumUsingPartitionOf nur verwenden, wenn sicher ist, dass die andere Partition ausgefallen ist, sonst entsteht Split-Brain. Ein Mitglied, dessen apply_queue ständig wächst, löst Flow Control aus und verlangsamt Schreibvorgänge auf der Primary. Ein langes RECOVERING bedeutet, dass Clone-/inkrementelle Recovery läuft; bei ERROR das Error Log prüfen und cluster.rejoinInstance() versuchen. Sonderzeichen in Passwörtern (#, @) müssen in der URI URL-kodiert werden (# → %23).","note":"MEMBER_COMMUNICATION_STACK kam mit 8.0.27; der MYSQL-Stack überträgt GR-Verkehr über 3306 mit normalem TLS und Benutzerauthentifizierung (XCOM nutzt einen separaten Port, meist 33061). In 8.4 ist group_replication_consistency standardmäßig BEFORE_ON_PRIMARY_FAILOVER und exit_state_action standardmäßig OFFLINE_MODE.","since":"MySQL 8.0","tags":["innodb cluster","group replication","mysqlsh","status","flow control","quorum"]},{"id":"innodb-cluster-switch-primary","task":"Geplanter Switchover der Primary in InnoDB Cluster","category":"InnoDB Cluster","command":"# Shell: planned switchover\nmysqlsh icadmin@db1:3306 -- cluster set-primary-instance db2:3306\n\n-- SQL alternative: target member's server_uuid, 30 s timeout for open transactions\nSELECT MEMBER_HOST, MEMBER_ID, MEMBER_ROLE FROM performance_schema.replication_group_members;\nSELECT group_replication_set_as_primary('3f06e651-c356-11f1-8aac-927a9034ef99', 30);","what":"Verlagert im Single-Primary-Modus die Primary-Rolle kontrolliert auf ein anderes Mitglied; die alte Primary wird super_read_only, die neue wendet ihren Rückstand an und öffnet für Schreibvorgänge. MySQL Router erkennt die neue Primary automatisch.","when":"Vor Wartung, OS-Patching oder einem Upgrade der Primary sowie bei Hardwareumzügen.","caution":"Offene Transaktionen auf der alten Primary werden beendet, wenn das Timeout abläuft; ohne Timeout wartet der Wechsel auf sie und dauert länger. Verbindet sich die Anwendung nicht über Router, musst du sie selbst umleiten. Mit BEFORE_ON_PRIMARY_FAILOVER warten Schreibvorgänge, bis die neue Primary ihren Rückstand abgearbeitet hat; bestätige vorher im Cluster-Status, dass apply_queue leer ist. In InnoDB Cluster den Shell-Befehl bevorzugen; er bleibt konsistent mit den Metadaten.","note":"group_replication_set_as_primary kam mit 8.0.13; der Parameter timeout kam in 8.0.29 hinzu. Bei ungeplantem Failover wählt GR die neue Primary selbst (group_replication_member_weight legt die Präferenz fest).","since":"MySQL 8.0.13","tags":["innodb cluster","switchover","primary","group replication","set_as_primary","maintenance"]},{"id":"innodb-cluster-reboot-outage","task":"Cluster nach einem Totalausfall wiederherstellen (rebootClusterFromCompleteOutage)","category":"InnoDB Cluster","command":"# Are all members OFFLINE? (on every node)\nmysql -h db1 -u icadmin -p -e \"SELECT MEMBER_HOST, MEMBER_STATE FROM performance_schema.replication_group_members\"\n# Dry run first: shows the most up-to-date member and the planned steps\nmysqlsh icadmin@db1:3306 --js -e \"dba.rebootClusterFromCompleteOutage('prodCluster', {dryRun: true})\"\nmysqlsh icadmin@db1:3306 --js -e \"dba.rebootClusterFromCompleteOutage('prodCluster')\"\nmysqlsh icadmin@db1:3306 -- cluster status","what":"Bootstrappt die Gruppe nach einem Stromausfall oder gleichzeitigen Neustart aller Knoten erneut, wenn kein Quorum besteht; wählt das Mitglied mit der größten GTID-Menge und bindet die anderen wieder ein.","when":"Wenn der Cluster-Status 'Cluster is not reachable' meldet und jedes Mitglied OFFLINE zeigt.","caution":"Nur verwenden, wenn jedes Mitglied OFFLINE ist; ist ein Mitglied noch ONLINE, lebt die Gruppe und die anderen treten mit rejoinInstance bei. Starte vor der Ausführung jeden Knoten: Ist ein nicht erreichbares Mitglied aktueller, gehen seine Transaktionen verloren. force: true ignoriert fehlende Mitglieder; es ist das letzte Mittel. Die Option primary: 'host:port' erzwingt ein bestimmtes Mitglied als Primary.","note":"Die Optionen dryRun, primary und force kamen mit Shell 8.0.31; frühere Versionen arbeiteten mit interaktiven Abfragen. Selbst mit group_replication_start_on_boot=ON bildet sich die Gruppe nach einem Totalausfall nicht von selbst; das Bootstrapping muss bewusst von einer Stelle aus erfolgen.","since":"MySQL Shell 8.0.31","tags":["innodb cluster","outage","reboot","bootstrap","mysqlsh","disaster"]},{"id":"mysql-router-bootstrap","task":"MySQL Router für ein InnoDB Cluster bootstrappen","category":"Router & Proxy","command":"# Generates config from metadata and creates the router account; --user is required when run as root\nmysqlrouter --bootstrap icadmin@db1:3306 --directory /etc/mysqlrouter/prod --account routeruser --account-create if-not-exists --name router01 --conf-bind-address 0.0.0.0 --user mysqlrouter\n/etc/mysqlrouter/prod/start.sh\n# Ports: 6446 RW, 6447 RO, 6450 RW split (classic); 6448 / 6449 X protocol\nmysql -h app-host -P 6446 -u app -p -e \"SELECT @@hostname\"\nmysql -h app-host -P 6447 -u app -p -e \"SELECT @@hostname\"\nmysqlsh icadmin@db1:3306 -- cluster list-routers","what":"Registriert Router in den Cluster-Metadaten und erzeugt Konto und Konfiguration; die Anwendung erreicht über den RW-Port die aktuelle Primary und über den RO-Port reihum die Secondaries.","when":"Wenn vor einem InnoDB Cluster / ClusterSet / ReplicaSet eine transparente Failover-Schicht gebraucht wird; typischerweise ein Router je Anwendungsserver.","caution":"Router ist zustandslos; neben der Anwendung betrieben, vermeidet er einen Single Point of Failure, ein einzelner zentraler Router nicht. Das Bootstrap-Konto wird nur beim Bootstrap genutzt; Router liest Metadaten mit seinem eigenen Konto (routeruser). Topologieänderungen erfährt er über den Metadata Cache ohne erneutes Bootstrap. Der RW-Split-Port (6450) sendet Lesezugriffe innerhalb einer Transaktion an die Primary und andere an Secondaries; Vorsicht bei Anwendungen, die Read-your-Writes erwarten. Die Router-Version darf nicht älter sein als die MySQL-Server im Cluster.","note":"Der Port für Read/Write-Splitting (6450) kam mit 8.2. Bestehende Verbindungen brechen beim Failover ab; die Anwendung braucht eine Logik für Reconnect (Retry).","since":"MySQL Router 8.0","tags":["mysql router","bootstrap","innodb cluster","failover","read write split","6446"]},{"id":"proxysql-rw-split","task":"Read/Write-Split und Health-Checks für Replicas mit ProxySQL","category":"Router & Proxy","command":"-- On MySQL (source): monitor user\nCREATE USER 'proxysql_mon'@'%' IDENTIFIED BY 'M0n#itor2024';\nGRANT USAGE, REPLICATION CLIENT ON *.* TO 'proxysql_mon'@'%';\n\n-- ProxySQL admin: mysql -u admin -p -h 127.0.0.1 -P 6032\nUPDATE global_variables SET variable_value = 'proxysql_mon' WHERE variable_name = 'mysql-monitor_username';\nUPDATE global_variables SET variable_value = 'M0n#itor2024' WHERE variable_name = 'mysql-monitor_password';\nLOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;\nINSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) VALUES (10, 20, 'read_only', 'shop');\nINSERT INTO mysql_servers (hostgroup_id, hostname, port, max_replication_lag) VALUES (10, 'db1', 3306, 0), (20, 'db2', 3306, 30), (20, 'db3', 3306, 30);\nLOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;\nINSERT INTO mysql_users (username, password, default_hostgroup, transaction_persistent) VALUES ('app', 'App#Pass2024', 10, 1);\nLOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK;\nINSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (100, 1, '^SELECT .* FOR (UPDATE|SHARE)', 10, 1), (200, 1, '^SELECT ', 20, 1);\nLOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;\n\n-- Verify\nSELECT hostgroup_id, hostname, status FROM runtime_mysql_servers;\nSELECT hostgroup, srv_host, status, ConnUsed, Queries, Latency_us FROM stats_mysql_connection_pool;\nSELECT hostgroup, substr(digest_text, 1, 60) AS digest, count_star, sum_time / 1000 AS sum_ms FROM stats_mysql_query_digest ORDER BY sum_time DESC LIMIT 10;","what":"Verwaltet Writer- (10) und Reader-Hostgroups (20) automatisch anhand des Flags read_only; leitet SELECTs an Replicas und sperrende Lesezugriffe sowie Schreibvorgänge an die Quelle; nimmt eine Replica, die max_replication_lag überschreitet, vorübergehend heraus (SHUNNED).","when":"Um in einer asynchronen Replikationstopologie Leselast zu verteilen, ohne die Anwendung anzufassen; für Connection Pooling und Routing / Caching / Firewalling je Abfrage.","caution":"Die Konfiguration hat 3 Ebenen (MEMORY → RUNTIME → DISK): Ohne LOAD ... TO RUNTIME ändert sich nichts, ohne SAVE ... TO DISK geht sie beim Neustart verloren. Die read_only-Prüfung erfordert super_read_only=ON auf Replicas; sonst werden Replicas in die Writer-Hostgroup verschoben. Jedes SELECT an Replicas zu senden, verursacht wegen des Lags Read-after-Write-Fehler; Regeln nur für schwere / Reporting-Abfragen auf Basis von stats_mysql_query_digest zu schreiben ist sicherer. Die Admin-Schnittstelle ist SQLite: MySQL-Funktionen wie LEFT() gibt es nicht, substr() verwenden. Ändere das Standardkonto admin:admin (admin-admin_credentials).","note":"Die Prüfung max_replication_lag erfordert REPLICATION CLIENT für den Monitor-Benutzer. Für Group Replication / InnoDB Cluster die Tabelle mysql_group_replication_hostgroups verwenden.","since":"ProxySQL 2.0","tags":["proxysql","read write split","hostgroup","query rules","connection pool","replication lag"]},{"id":"online-ddl-instant-inplace","task":"Online-DDL: ALGORITHM=INSTANT / INPLACE, LOCK=NONE und Fortschrittsverfolgung","category":"Online-Operationen","command":"-- Always state ALGORITHM/LOCK: unsupported operations fail immediately instead of silently falling back to COPY\nALTER TABLE shop.orders ADD COLUMN status TINYINT NOT NULL DEFAULT 0, ALGORITHM=INSTANT;\nALTER TABLE shop.orders RENAME COLUMN note TO remark, ALGORITHM=INSTANT;\nALTER TABLE shop.orders ADD INDEX ix_status (status), ALGORITHM=INPLACE, LOCK=NONE;\nALTER TABLE shop.orders MODIFY remark VARCHAR(200), ALGORITHM=INPLACE, LOCK=NONE;\n\n-- INSTANT row version counter (INSTANT is refused at 64, a rebuild resets it)\nSELECT NAME, TOTAL_ROW_VERSIONS FROM information_schema.INNODB_TABLES WHERE NAME = 'shop/orders';\n\n-- Progress of a long ALTER (enable the instruments once)\nUPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'stage/innodb/alter%';\nUPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_stages_%';\nSELECT t.PROCESSLIST_ID, s.EVENT_NAME, s.WORK_COMPLETED, s.WORK_ESTIMATED,\n       ROUND(100 * s.WORK_COMPLETED / NULLIF(s.WORK_ESTIMATED, 0), 1) AS pct_done\nFROM performance_schema.events_stages_current s\nJOIN performance_schema.threads t ON t.THREAD_ID = s.THREAD_ID\nWHERE s.EVENT_NAME LIKE 'stage/innodb/alter%';","what":"INSTANT ändert nur das Data Dictionary und ist unabhängig von der Tabellengröße sofort fertig. INPLACE + LOCK=NONE bearbeitet die Tabelle an Ort und Stelle und erlaubt dabei parallele Lese- und Schreibzugriffe. Die letzte Abfrage zeigt, in welcher Phase sich ein laufendes ALTER befindet und mit wie viel Prozent.","when":"Vor Schemaänderungen an Produktionstabellen; um zu wissen, welche Operationen ohne Ausfallzeit laufen können, und um abzuschätzen, wann ein langes ALTER fertig wird.","caution":"INSTANT und INPLACE nehmen am Anfang und Ende trotzdem kurz einen exklusiven Metadata Lock; hält eine lang laufende Transaktion die Tabelle, wartet das ALTER und jede Abfrage dahinter reiht sich ein (siehe Diagnose von Metadata Locks). Senke lock_wait_timeout in der Session (SET SESSION lock_wait_timeout = 5). Während INPLACE eintreffendes DML wird bis innodb_online_alter_log_max_size protokolliert; wird das überschritten, rollt das ALTER am Ende zurück. Änderungen von Spaltentyp, Zeichensatz und PK brauchen COPY und blockieren Schreibvorgänge; bei großen Tabellen gh-ost / pt-online-schema-change verwenden. Auf einer Replica läuft das ALTER genauso lange single-threaded; ein großes INPLACE-ALTER verursacht Replica-Lag, INSTANT nicht.","note":"INSTANT ADD COLUMN kam mit 8.0.12 nur für die letzte Spalte; seit 8.0.29 lassen sich Spalten an beliebiger Position hinzufügen und INSTANT DROP COLUMN wird unterstützt. Das Verlängern eines VARCHAR ist eine reine Metadatenänderung, wenn sich die Anzahl der Längenbytes nicht ändert (die 255-Byte-Grenze wird nicht überschritten); es ist mit INPLACE sofort fertig, nicht mit INSTANT. Eine indizierte Spalte lässt sich nicht mit INSTANT löschen; zuerst den Index löschen.","since":"MySQL 8.0.29","tags":["online ddl","instant","inplace","alter table","lock=none","progress"]},{"id":"pt-online-schema-change","task":"Nicht blockierendes ALTER auf einer großen Tabelle mit pt-online-schema-change","category":"Online-Operationen","command":"# First --dry-run, then the same command with --execute; throttles on replica lag\npt-online-schema-change --alter \"ADD COLUMN region CHAR(2) NULL, ADD INDEX ix_region (region)\" \\\n  D=shop,t=orders,h=db1,u=osc,p='Osc#2024',s=1 \\\n  --max-lag=5 --check-replica-lag h=db2,u=osc,p='Osc#2024',s=1 \\\n  --max-load Threads_running=50 --critical-load Threads_running=200 \\\n  --set-vars lock_wait_timeout=5 --noversion-check --dry-run","what":"Legt eine leere Kopie mit der neuen Struktur an, kopiert Zeilen in Chunks, spiegelt Änderungen während des Kopierens per Trigger und tauscht die Tabellen schließlich mit einem atomaren RENAME. Es verlangsamt / pausiert selbstständig, wenn Last oder Replica-Lag die Schwellenwerte überschreiten.","when":"Für ALTERs, die COPY brauchen (Spaltentyp, Zeichensatz, PK-Änderung) oder lange Replica-Lags verursachen würden.","caution":"Die Tabelle braucht einen PK oder Unique Key. Trigger erzeugen während der Migration Overhead bei jedem Schreibvorgang; bei sehr schreibintensiven Tabellen hat gh-ost weniger Auswirkungen. Verweisen Child-Tabellen per Foreign Key auf diese Tabelle, ist --alter-foreign-keys-method=auto nötig. Bei Konten mit caching_sha2_password s=1 (TLS) zum DSN hinzufügen; sonst schlägt die Replica-Verbindung mit 'Authentication requires secure connection' fehl. Der DSN von --check-replica-lag übernimmt Benutzer / Passwort nicht vom Haupt-DSN; gib sie an. Der Tausch nimmt kurz einen Metadata Lock; halte lock_wait_timeout niedrig, es versucht es erneut, wenn die Sperre nicht zu bekommen ist.","note":"Replicas werden standardmäßig per processlist über die IP erkannt; sind diese IPs nicht erreichbar, eine DSN-Tabelle mit --recursion-method=dsn=D=percona,t=dsns verwenden. In 3.7 wurde --check-slave-lag in --check-replica-lag umbenannt (alter Name veraltet).","since":"Percona Toolkit 3.7","tags":["pt-online-schema-change","pt-osc","percona toolkit","alter","trigger","online schema change"]},{"id":"gh-ost-migration","task":"Triggerlose Online-Schemaänderung mit gh-ost","category":"Online-Operationen","command":"# Connects to a replica and reads its binlog; postpones cut-over until the flag file is removed\ngh-ost --host=db2 --port=3306 --user=ghost --password='Gh0st#2024' \\\n  --database=shop --table=orders \\\n  --alter=\"ADD COLUMN channel VARCHAR(20) NULL, ADD INDEX ix_channel (channel)\" \\\n  --max-load=Threads_running=50 --critical-load=Threads_running=200 --max-lag-millis=1500 \\\n  --exact-rowcount --concurrent-rowcount --default-retries=120 \\\n  --postpone-cut-over-flag-file=/tmp/ghost.postpone --panic-flag-file=/tmp/ghost.panic \\\n  --serve-socket-file=/tmp/gh-ost.shop.orders.sock --ssl --ssl-allow-insecure --execute\n# While running: status / throttle / cut-over\necho status | nc -U /tmp/gh-ost.shop.orders.sock\necho throttle | nc -U /tmp/gh-ost.shop.orders.sock\nrm /tmp/ghost.postpone","what":"Baut eine Ghost-Tabelle auf und kopiert Zeilen; wendet parallele Änderungen an, indem es das Binlog liest statt Trigger zu nutzen. Drosselung, Pausieren und Zeitpunkt des Cut-overs werden zur Laufzeit über den Socket oder Flag-Dateien gesteuert.","when":"Bei großen, schreibintensiven Tabellen, bei denen Trigger-Overhead inakzeptabel ist und du den Zeitpunkt des Cut-overs (z. B. ein Fenster mit wenig Verkehr) selbst wählen willst.","caution":"Erfordert binlog_format=ROW und binlog_row_image=FULL; die Replica, mit der es sich verbindet, braucht log_replica_updates=ON. Der Benutzer braucht SUPER oder REPLICATION CLIENT / REPLICATION SLAVE plus ALL auf der Tabelle. Tabellen mit Foreign Keys (Parent oder Child) werden nicht unterstützt. Nach dem Cut-over bleibt die alte Tabelle als _<table>_del zurück; nach der Prüfung manuell löschen (--ok-to-drop-table löscht sie automatisch). gh-ost-Releases vor 1.1.8 nutzen SHOW SLAVE STATUS und stoppen unter MySQL 8.4 mit Fehler 1064; Unterstützung für 8.4 kam mit 1.1.8. gh-ost legt die in --postpone-cut-over-flag-file angegebene Datei selbst an; der Cut-over erfolgt erst, wenn die Datei entfernt wird.","note":"Mit den Optionen --test-on-replica und --migrate-on-replica kannst du die Änderung zuerst nur auf einer Replica ausprobieren und verifizieren. Drosselungsgrund und ETA erscheinen in der Ausgabe des Befehls 'status'.","tags":["gh-ost","online schema change","binlog","cut-over","alter","triggerless"]},{"id":"ps-top-digests","task":"Die teuersten Abfragen finden (performance_schema digest)","category":"Performance","command":"SELECT SCHEMA_NAME AS db,\n       LEFT(DIGEST_TEXT, 80) AS query,\n       COUNT_STAR AS calls,\n       ROUND(SUM_TIMER_WAIT / 1e12, 2) AS total_sec,\n       ROUND(AVG_TIMER_WAIT / 1e9, 2) AS avg_ms,\n       ROUND(QUANTILE_95 / 1e9, 2) AS p95_ms,\n       ROUND(100 * SUM_TIMER_WAIT / SUM(SUM_TIMER_WAIT) OVER (), 1) AS pct_total,\n       ROUND(SUM_ROWS_EXAMINED / NULLIF(COUNT_STAR, 0)) AS rows_exam_per_call,\n       ROUND(SUM_ROWS_SENT / NULLIF(COUNT_STAR, 0)) AS rows_sent_per_call,\n       SUM_NO_INDEX_USED AS no_index,\n       SUM_CREATED_TMP_DISK_TABLES AS tmp_disk,\n       LAST_SEEN\nFROM performance_schema.events_statements_summary_by_digest\nWHERE SCHEMA_NAME IS NOT NULL\nORDER BY SUM_TIMER_WAIT DESC\nLIMIT 20;\n\n-- Server-wide top wait events\nSELECT events, total, total_latency, avg_latency FROM sys.waits_global_by_latency LIMIT 10;","what":"Ordnet normalisierte Abfragen nach Gesamtzeit mit Aufrufanzahl, durchschnittlicher und p95-Latenz, Anteil an der Gesamtzeit, untersuchten / gesendeten Zeilen pro Aufruf, Ausführungen ohne Index und auf Platte ausgelagerten temporären Tabellen. Die zweite Abfrage zeigt, auf welche Waits (IO, Sperren, Redo) die Zeit entfällt.","when":"Erste Anlaufstelle, wenn CPU- / IO-Last steigt oder Benutzer melden 'die Datenbank ist langsam'; funktioniert auch bei ausgeschaltetem Slow Log.","caution":"Die Werte kumulieren seit dem letzten Neustart oder TRUNCATE TABLE performance_schema.events_statements_summary_by_digest; erstelle zwei Snapshots und vergleiche sie, um die aktuelle Aktivität zu sehen. Die Tabelle ist auf performance_schema_digests_size Zeilen begrenzt; ist sie voll, werden neue Abfragen in einer einzigen Zeile mit NULL-DIGEST zusammengefasst. DIGEST_TEXT wird bei performance_schema_max_digest_length (1024) Bytes abgeschnitten. Zeiten sind in Pikosekunden angegeben. Ein hohes Verhältnis rows_exam_per_call / rows_sent_per_call ist das deutlichste Zeichen für einen fehlenden Index.","note":"Die Spalten QUANTILE_95 / QUANTILE_99 und QUERY_SAMPLE_TEXT kamen mit 8.0. Dieselben Daten in lesbaren Einheiten liefern die Views sys.statement_analysis und sys.statements_with_full_table_scans.","since":"MySQL 8.0","tags":["performance_schema","digest","top sql","slow query","sys","wait"]},{"id":"sys-session-activity","task":"Was läuft gerade: aktive Sessions, Latenz, Sperren und Transaktionen (sys.session)","category":"Performance","command":"SELECT conn_id, user, db, command, state, time,\n       LEFT(current_statement, 100) AS current_statement,\n       statement_latency, progress, lock_latency,\n       rows_examined, full_scan, tmp_disk_tables,\n       trx_state, trx_latency, last_wait\nFROM sys.session\nWHERE command <> 'Sleep' OR trx_state = 'ACTIVE'\nORDER BY time DESC\nLIMIT 25;","what":"Ergänzt SHOW PROCESSLIST um performance_schema-Daten: Latenz der laufenden Anweisung, Fortschritt in Prozent, Sperrwartezeit, untersuchte Zeilen, Full Scan / temporäre Tabelle auf Platte und Status der offenen Transaktion in einer Zeile. Listet auch Sessions im Zustand Sleep, die eine Transaktion offen halten.","when":"In den ersten 30 Sekunden eines Incidents: Welche Abfrage läuft seit wann, wer wartet auf Sperren, welche inaktive Session hält eine Transaktion offen.","caution":"sys.session führt mehrere Joins über performance_schema aus; bei Tausenden Verbindungen kann die Abfrage selbst Sekunden dauern, nutze daher für häufiges Monitoring die x$-Views und LIMIT. progress wird nur mit aktivierten Stage-Instrumenten und für Operationen gefüllt, die Fortschritt melden, etwa ALTER. Eine Session in Sleep mit trx_state=ACTIVE ist eine vergessene Transaktion einer Anwendung / eines Connection-Pools mit autocommit=0, die häufigste Ursache für Metadata-Lock- und Purge-Probleme.","note":"sys.processlist liefert dieselben Daten einschließlich Hintergrund-Threads. Zum Beenden: KILL QUERY conn_id (nur die Anweisung) oder KILL conn_id (Verbindung, die Transaktion wird zurückgerollt).","since":"MySQL 8.0","tags":["sys.session","processlist","active sessions","incident","trx","session"]},{"id":"metadata-lock-diagnose","task":"'Waiting for table metadata lock' diagnostizieren und den eigentlichen Blocker finden","category":"Performance","command":"-- Waiter -> blocker pairs; filter out the ALTER's own SHARED_UPGRADABLE entry\nSELECT object_schema, object_name, waiting_pid, LEFT(waiting_query, 60) AS waiting_query,\n       waiting_lock_type, blocking_pid, blocking_lock_type, sql_kill_blocking_connection\nFROM sys.schema_table_lock_waits\nWHERE blocking_lock_type <> 'SHARED_UPGRADABLE';\n\n-- All MDLs on the table, owner age and open transaction\nSELECT ml.OBJECT_SCHEMA, ml.OBJECT_NAME, ml.LOCK_TYPE, ml.LOCK_DURATION, ml.LOCK_STATUS,\n       t.PROCESSLIST_ID AS conn_id, t.PROCESSLIST_COMMAND AS command, t.PROCESSLIST_TIME AS sec,\n       LEFT(t.PROCESSLIST_INFO, 60) AS query, trx.trx_started\nFROM performance_schema.metadata_locks ml\nJOIN performance_schema.threads t ON t.THREAD_ID = ml.OWNER_THREAD_ID\nLEFT JOIN information_schema.INNODB_TRX trx ON trx.trx_mysql_thread_id = t.PROCESSLIST_ID\nWHERE ml.OBJECT_TYPE = 'TABLE' AND ml.OBJECT_SCHEMA NOT IN ('performance_schema', 'mysql')\nORDER BY ml.OBJECT_NAME, ml.LOCK_STATUS DESC;","what":"Zeigt, auf den Metadata Lock welcher Session eine DDL wie ALTER / TRUNCATE / RENAME wartet, und welche Abfragen sich hinter der DDL anstauen. Der Blocker ist meist eine Session in Sleep, die die Tabelle gelesen und ihre Transaktion nie beendet hat.","when":"Wenn sich nach dem Start eines ALTER alle Abfragen auf dieser Tabelle in 'Waiting for table metadata lock' stauen.","caution":"Eine wartende DDL blockiert auch die danach eintreffenden SELECTs; entscheide im Incident schnell: die blockierende Session per KILL beenden oder das ALTER abbrechen (KILL QUERY). PROCESSLIST_INFO des Blockers kann NULL sein (inaktiv); seine letzte Anweisung siehst du in performance_schema.events_statements_history oder sys.session.last_statement. Vorbeugung: DDL nach SET SESSION lock_wait_timeout = 5 ausführen, damit das ALTER schnell aufgibt, und lange offene Transaktionen in der Anwendung vermeiden.","note":"Das Instrument wait/lock/metadata/sql/mdl ist in 8.0 standardmäßig aktiviert; in 5.7 muss es in setup_instruments aktiviert werden. sys.schema_table_lock_waits listet auch die eigene SHARED_UPGRADABLE-Sperre eines ALTER als Blocker; der eigentliche Verursacher ist die Session mit einer GRANTED SHARED_READ / SHARED_WRITE.","since":"MySQL 8.0","tags":["metadata lock","mdl","alter","waiting for table metadata lock","lock","blocking"]},{"id":"innodb-deadlock-analysis","task":"InnoDB-Deadlocks analysieren und alle protokollieren","category":"Performance","command":"-- Latest deadlock: LATEST DETECTED DEADLOCK section (only the most recent is kept)\nSHOW ENGINE INNODB STATUS\\G\n-- Total deadlock counter\nSELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE NAME = 'lock_deadlocks';\n-- Write every deadlock to the error log (logged at Note level, needs verbosity 3)\nSET PERSIST innodb_print_all_deadlocks = ON;\nSET PERSIST log_error_verbosity = 3;","what":"Zeigt die beiden Transaktionen im Deadlock, die jeweils gehaltene und angeforderte Sperre (Index, lock_mode, Datensatz) und welche InnoDB zurückgerollt hat; der Zähler erfasst die Häufigkeit, das Error Log bewahrt frühere Deadlocks auf.","when":"Wenn die Anwendung den Fehler 1213 'Deadlock found when trying to get lock' erhält oder Deadlock-Alarme zunehmen.","caution":"LATEST DETECTED DEADLOCK enthält nur das letzte Ereignis und geht beim Neustart verloren. Indexname und lock_mode (X, S, gap, insert intention) in den Zeilen 'HOLDS THE LOCK(S)' / 'WAITING FOR THIS LOCK TO BE GRANTED' erklären die Ursache; 'WE ROLL BACK TRANSACTION (n)' zeigt das Opfer. Bei 1213 muss die Anwendung die gesamte Transaktion wiederholen. Dauerhafte Abhilfe: Zeilen in einheitlicher Reihenfolge ansprechen, Transaktionen kurz halten, mit dem richtigen Index Gap Locks reduzieren und wo sinnvoll READ COMMITTED nutzen. log_error_verbosity=3 aktiviert auch alle Note-Meldungen; behalte das Wachstum des Error Logs im Blick. Die Statusvariable Innodb_deadlocks gibt es in MySQL Community nicht; nutze für den Zähler INNODB_METRICS.","note":"Bei sehr hoher Parallelität kann die Deadlock-Erkennung selbst zum CPU-Engpass werden; innodb_deadlock_detect=OFF kommt nur zusammen mit einem niedrigen innodb_lock_wait_timeout in Frage. Ein Lock Wait Timeout (1205) ist kein Deadlock; den Blocker findest du mit dem Rezept für blockierende Sperren.","since":"MySQL 8.0","tags":["deadlock","innodb","1213","innodb status","lock","error log"]},{"id":"long-trx-history-list","task":"Lang laufende Transaktionen und History List Length (Purge-Rückstand)","category":"Performance","command":"-- Undo records waiting for purge (millions = trouble)\nSELECT NAME, COUNT AS history_list_length\nFROM information_schema.INNODB_METRICS WHERE NAME = 'trx_rseg_history_len';\n\n-- Oldest transactions, their owner and last SQL\nSELECT trx.trx_id, trx.trx_mysql_thread_id AS conn_id, trx.trx_state,\n       TIMESTAMPDIFF(SECOND, trx.trx_started, NOW()) AS age_sec,\n       trx.trx_rows_modified, trx.trx_rows_locked, trx.trx_isolation_level,\n       p.USER, p.HOST, p.COMMAND, p.TIME AS state_sec,\n       LEFT(COALESCE(trx.trx_query, esc.SQL_TEXT), 80) AS current_or_last_sql\nFROM information_schema.INNODB_TRX trx\nJOIN performance_schema.processlist p ON p.ID = trx.trx_mysql_thread_id\nLEFT JOIN performance_schema.threads th ON th.PROCESSLIST_ID = trx.trx_mysql_thread_id\nLEFT JOIN performance_schema.events_statements_current esc ON esc.THREAD_ID = th.THREAD_ID\nORDER BY trx.trx_started\nLIMIT 10;","what":"Zeigt, wie weit Purge im Rückstand ist (History List Length), und die ältesten Transaktionen, die ihn aufhalten: Eigentümer, Alter, geänderte Zeilen und die zuletzt ausgeführte SQL-Anweisung.","when":"Wenn der Undo-Tablespace wächst, dieselben Abfragen mit der Zeit langsamer werden (sie lesen alte Zeilenversionen) oder bei Speicherplatzalarmen.","caution":"Häufigste Ursache ist eine Session in Sleep mit offener Transaktion oder ein unter REPEATABLE READ vergessener Consistent Snapshot (z. B. ein abgebrochener mysqldump / Report). Nach KILL dauert das Rollback so lange, wie es den von der Transaktion geänderten Zeilen entspricht; eine Transaktion mit hohem trx_rows_modified zu beenden bedeutet ein langes, IO-intensives Rollback, und ein Neustart verkürzt es nicht. Undo wird automatisch verkleinert, sobald Purge aufgeholt hat (innodb_undo_log_truncate=ON).","note":"performance_schema.processlist kam mit 8.0.22 und ersetzt das veraltete information_schema.PROCESSLIST. 'History list length' in SHOW ENGINE INNODB STATUS liefert denselben Wert.","since":"MySQL 8.0.22","tags":["history list length","purge","undo","long transaction","innodb_trx","idle in transaction"]},{"id":"slow-log-pt-query-digest","task":"Kurzzeitig ein vollständiges Slow Log aufzeichnen und mit pt-query-digest analysieren","category":"Performance","command":"-- Temporary full capture (e.g. 10 minutes); only new connections are affected\nSET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';\nSET GLOBAL log_slow_extra = ON;\nSET GLOBAL long_query_time = 0;\nSET GLOBAL slow_query_log = ON;\n-- ... then return to the normal threshold\nSET GLOBAL long_query_time = 1;\n\n# Shell: top 10 queries by total time\npt-query-digest --limit=10 --order-by=Query_time:sum --noversion-check /var/lib/mysql/slow.log > /tmp/digest.txt\npt-query-digest --since='2026-10-08 09:00:00' --until='2026-10-08 10:00:00' --filter '$event->{db} && $event->{db} eq \"shop\"' --report-format=profile --noversion-check /var/lib/mysql/slow.log","what":"Zeichnet mit long_query_time=0 für ein kurzes Zeitfenster jede Abfrage auf; pt-query-digest gruppiert sie nach Fingerprint und erstellt einen sortierten Bericht mit Gesamt- / Durchschnitts- / p95-Zeit, untersuchten Zeilen und einer Beispielabfrage.","when":"Wenn performance_schema-Digests nicht ausreichen: Analyse mit vollständigem Anweisungstext und echten Parametern, Vorher-Nachher-Vergleich rund um ein Deployment, Offline-Auswertung auf einem anderen Host.","caution":"long_query_time wird beim Start einer Session aus dem globalen Wert übernommen; bestehende Pool-Verbindungen sehen den neuen Schwellenwert erst nach einem Reconnect. long_query_time=0 schreibt auf einem ausgelasteten Server Tausende Einträge pro Sekunde; behalte Speicherplatz und IO im Blick, halte das Fenster kurz und setze den Wert immer zurück. Kopiere die Datei zur Analyse von der Produktion weg. Der Benutzer, der pt-query-digest ausführt, braucht Lesezugriff auf die Datei (Modus 640, mysql:mysql).","note":"log_slow_extra (8.0.14) schreibt zusätzliche Felder wie Bytes_sent, Read_first / Read_next und Created_tmp_disk_tables; pt-query-digest zeigt sie im Bericht. Zum dauerhaften Aktivieren / Deaktivieren siehe das Rezept enable-general-slow-log.","since":"MySQL 8.0.14","tags":["slow log","pt-query-digest","long_query_time","percona toolkit","query analysis","slow query"]},{"id":"explain-analyze-optimizer-trace","task":"Einen schlechten Plan mit EXPLAIN ANALYZE und dem Optimizer Trace untersuchen","category":"Performance","command":"-- Plan + actual time and rows (the query actually runs)\nEXPLAIN ANALYZE\nSELECT region, COUNT(*) FROM shop.big WHERE id < 20000 GROUP BY region;\n\n-- Why the optimizer chose this plan: optimizer trace\nSET SESSION optimizer_trace = 'enabled=on', SESSION optimizer_trace_max_mem_size = 1048576;\nSELECT * FROM shop.big WHERE region = 'TR' AND id > 100 LIMIT 5;\nSELECT JSON_PRETTY(JSON_EXTRACT(TRACE, '$**.considered_execution_plans')) AS plans,\n       MISSING_BYTES_BEYOND_MAX_MEM_SIZE\nFROM information_schema.OPTIMIZER_TRACE;\nSET SESSION optimizer_trace = 'enabled=off';","what":"EXPLAIN ANALYZE zeigt für jeden Planschritt geschätzte (cost, rows) und tatsächliche (actual time, rows, loops) Werte nebeneinander; der Optimizer Trace zeigt die erwogenen Zugriffspfade, ihre Kosten und warum jeder gewählt oder verworfen wurde.","when":"Wenn eine Abfrage plötzlich langsamer wird (Planänderung), der Optimizer den falschen Index wählt oder um die Ursache einer Abweichung zwischen geschätzten und tatsächlichen Zeilen zu finden.","caution":"EXPLAIN ANALYZE führt die Abfrage tatsächlich aus; bei schweren Abfragen auf der Produktion vorsichtig sein. Es funktioniert mit SELECT, TABLE und UPDATE / DELETE über mehrere Tabellen. Eine Abweichung von mehr als 10x zwischen geschätzten und tatsächlichen Zeilen deutet auf ein Statistik- / Histogrammproblem hin (ANALYZE TABLE, Histogramme). Auf der inneren Seite eines Nested Loop gilt actual rows pro Loop; Gesamt = rows × loops. Ist MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0, wurde der Trace abgeschnitten; erhöhe optimizer_trace_max_mem_size. Der Trace enthält nur die letzte Anweisung derselben Session.","note":"EXPLAIN FORMAT=TREE kam mit 8.0.16, EXPLAIN ANALYZE mit 8.0.18. Um einen Plan festzulegen, bieten Optimizer Hints (/*+ INDEX(t ix) */, JOIN_ORDER) feinere Kontrolle als FORCE INDEX.","since":"MySQL 8.0.18","tags":["explain analyze","optimizer trace","execution plan","query tuning","index","plan"]},{"id":"histogram-statistics","task":"Histogramme für nicht indizierte Spalten erstellen und verwalten","category":"Index & Statistiken","command":"ANALYZE TABLE shop.big UPDATE HISTOGRAM ON status WITH 64 BUCKETS;\n-- 8.4+: histogram refreshed automatically as data changes\nANALYZE TABLE shop.big UPDATE HISTOGRAM ON status WITH 64 BUCKETS AUTO UPDATE;\n\nSELECT SCHEMA_NAME, TABLE_NAME, COLUMN_NAME,\n       HISTOGRAM->>'$.\"histogram-type\"' AS type,\n       JSON_LENGTH(HISTOGRAM->'$.buckets') AS buckets,\n       HISTOGRAM->>'$.\"last-updated\"' AS last_updated,\n       HISTOGRAM->>'$.\"sampling-rate\"' AS sampling_rate\nFROM information_schema.COLUMN_STATISTICS;\n\nANALYZE TABLE shop.big DROP HISTOGRAM ON status;","what":"Speichert die Werteverteilung einer Spalte (singleton oder equi-height) im Data Dictionary; der Optimizer schätzt die Selektivität von Filtern auf nicht indizierten Spalten korrekt und verbessert so Join-Reihenfolge und Planqualität.","when":"Wenn EXPLAIN ANALYZE bei ungleich verteilten Spalten (z. B. status, type), auf denen kein Index gewünscht ist, eine große Abweichung zwischen geschätzten und tatsächlichen Zeilen zeigt.","caution":"Auf indizierten Spalten nutzt der Optimizer Index Dives, daher hat ein Histogramm dort meist keine Wirkung (außer eq_range_index_dive_limit wird überschritten). Im Standardmodus MANUAL wird das Histogramm bei Datenänderungen nicht aktualisiert; plane ANALYZE ... UPDATE HISTOGRAM ein. Große Tabellen werden gemäß histogram_generation_max_mem_size per Stichprobe erfasst (sampling-rate < 1). ANALYZE TABLE nimmt kurz einen Metadata Lock; das Aktualisieren eines Histogramms ändert die Tabellenstatistiken (Index-Kardinalität) nicht.","note":"Histogramme kamen mit 8.0; die Option AUTO UPDATE wurde in 8.4 ergänzt und aktualisiert zusammen mit der automatischen Neuberechnung der Statistiken. Maximal sind 1024 Buckets möglich; gibt es weniger eindeutige Werte als Buckets, wird ein Singleton-Histogramm erstellt.","since":"MySQL 8.0","tags":["histogram","statistics","optimizer","analyze table","selectivity","cardinality"]},{"id":"invisible-redundant-index","task":"Ungenutzte / redundante Indizes finden, unsichtbar machen, sicher löschen","category":"Index & Statistiken","command":"-- Candidate 1: indexes unused since the last restart\nSELECT * FROM sys.schema_unused_indexes WHERE object_schema = 'shop';\n-- Candidate 2: redundant indexes that are a left prefix of another\nSELECT table_name, redundant_index_name, redundant_index_columns, dominant_index_name, sql_drop_index\nFROM sys.schema_redundant_indexes WHERE table_schema = 'shop';\n\n-- Make it invisible before dropping; revert instantly if anything regresses\nALTER TABLE shop.big ALTER INDEX ix_region INVISIBLE;\nSELECT INDEX_NAME, IS_VISIBLE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'shop' AND TABLE_NAME = 'big' AND SEQ_IN_INDEX = 1;\n-- Try the invisible index for a single query\nEXPLAIN SELECT /*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */ COUNT(*) FROM shop.big WHERE region = 'TR';\nALTER TABLE shop.big ALTER INDEX ix_region VISIBLE;\n-- After a few business cycles (incl. month-end) without issues\nALTER TABLE shop.big DROP INDEX ix_region, ALGORITHM=INPLACE, LOCK=NONE;","what":"Listet ungenutzte Indizes und Indizes, die von einem anderen abgedeckt sind; einen Index vor dem Löschen unsichtbar zu machen, lässt dich das Produktionsverhalten ohne ihn testen und bei Bedarf sofort zurückschalten (kein Rebuild).","when":"Beim Aufräumen von Indizes, um Schreibkosten, Druck auf den Buffer Pool und Plattenbelegung zu senken.","caution":"schema_unused_indexes stützt sich nur auf Statistiken seit dem letzten Neustart; seltene Abfragen wie Monatsberichte tauchen eventuell nicht auf. Bedienen Replicas unterschiedliche Workloads, prüfe jeden Server einzeln. Ein unsichtbarer Index wird bei DML weiter gepflegt (die Schreibkosten bleiben), aber vom Optimizer ignoriert; ein Unique Index erzwingt die Eindeutigkeit auch unsichtbar. Ein PRIMARY KEY kann nicht unsichtbar gemacht werden. Abfragen, die den Index in FORCE INDEX / USE INDEX nennen, schlagen fehl, sobald er unsichtbar ist.","note":"Das Umschalten der Index-Sichtbarkeit betrifft nur Metadaten und ist sofort abgeschlossen. Die Redundanzliste basiert auf der Spaltenreihenfolge; Index (a) wird von (a,b) abgedeckt, (b) jedoch nicht.","since":"MySQL 8.0","tags":["invisible index","unused index","redundant index","sys","drop index","index cleanup"]},{"id":"buffer-pool-sizing","task":"Ist der Buffer Pool groß genug: Hit Ratio, wait_free und Online-Größenänderung","category":"Speicher","command":"SELECT ROUND(@@innodb_buffer_pool_size / 1024 / 1024) AS bp_mb,\n       @@innodb_buffer_pool_chunk_size / 1024 / 1024 AS chunk_mb,\n       @@innodb_buffer_pool_instances AS instances;\nSELECT\n  ROUND(100 * (1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads')\n                 / NULLIF((SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'), 0)), 3) AS hit_pct,\n  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_free') AS pages_free,\n  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty') AS pages_dirty,\n  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_wait_free') AS wait_free;\n\n-- Online resize and status tracking\nSET PERSIST innodb_buffer_pool_size = 8589934592;\nSELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME LIKE 'Innodb_buffer_pool_resize_status%';","what":"Zeigt Buffer-Pool-Größe, Cache Hit Ratio, Anzahl freier / Dirty Pages und den Zähler für Wartezeiten auf freie Seiten (wait_free); ändert die Größe ohne Neustart und verfolgt den Fortschritt.","when":"Bei hoher Lese-IO, nach dem Hinzufügen / Entfernen von RAM oder bei der Kapazitätsplanung.","caution":"Die Hit Ratio allein täuscht; selbst 99% kann Tausende Plattenlesevorgänge pro Sekunde bedeuten. Betrachte den Anstieg von Innodb_buffer_pool_reads pro Sekunde; wait_free > 0 bedeutet, dass der Buffer Pool zu klein ist oder die Page Cleaner nicht hinterherkommen. Die neue Größe wird auf ein Vielfaches von innodb_buffer_pool_chunk_size × Instanzen gerundet. Während einer Größenänderung sind kurze Hänger möglich, beim Verkleinern eher; meide Spitzenzeiten. Auf einem dedizierten Server mit 60–75% des RAM beginnen; lass Platz für Speicher pro Verbindung, OS-Cache und andere Prozesse, denn Swapping ist immer schlimmer als ein kleinerer Buffer Pool.","note":"Innodb_buffer_pool_resize_status_code / _progress kamen mit 8.0.31. innodb_dedicated_server=ON dimensioniert Buffer Pool und Redo-Kapazität automatisch anhand des RAM. sys.innodb_buffer_stats_by_table zeigt die Buffer-Pool-Nutzung pro Tabelle; es durchsucht INNODB_BUFFER_PAGE und ist bei großen Buffer Pools aufwendig.","since":"MySQL 8.0","tags":["buffer pool","innodb","memory","hit ratio","resize","sizing"]},{"id":"partition-maintenance","task":"Wartung von RANGE-Partitionen: künftige Partitionen hinzufügen, alte archivieren / löschen","category":"Partitionierung","command":"-- Current partitions\nSELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS\nFROM information_schema.PARTITIONS\nWHERE TABLE_SCHEMA = 'shop' AND TABLE_NAME = 'events_log' ORDER BY PARTITION_ORDINAL_POSITION;\n\n-- Add a new month by splitting the empty pmax\nALTER TABLE shop.events_log REORGANIZE PARTITION pmax INTO (\n  PARTITION p2026_11 VALUES LESS THAN ('2026-12-01'),\n  PARTITION pmax VALUES LESS THAN (MAXVALUE));\n\n-- Move the old month into an archive table (instant), then drop the empty partition\nCREATE TABLE shop.events_log_2026_08 LIKE shop.events_log;\nALTER TABLE shop.events_log_2026_08 REMOVE PARTITIONING;\nALTER TABLE shop.events_log EXCHANGE PARTITION p2026_08 WITH TABLE shop.events_log_2026_08;\nALTER TABLE shop.events_log DROP PARTITION p2026_08;\n\n-- Pruning check: the partitions column should list a single partition\nEXPLAIN SELECT * FROM shop.events_log WHERE created >= '2026-10-01' AND created < '2026-11-01';","what":"Fügt bei einer nach Datum RANGE-partitionierten Tabelle die Partition der nächsten Periode hinzu, verschiebt eine alte Periode per EXCHANGE in Millisekunden in eine separate Tabelle und löscht die Partition weit günstiger als mit DELETE.","when":"Für monatliche Aufbewahrung und Archivierung bei wachsenden Tabellen wie Logs, Events und Audit-Trails.","caution":"Die Partitionierungsspalte muss Teil jedes Unique Key sein (PK eingeschlossen). REORGANIZE PARTITION pmax schreibt die Daten in pmax neu; halte pmax leer und lege Partitionen im Voraus an (z. B. 3 Monate). DROP PARTITION ist nicht rückgängig zu machen und wird nicht zeilenweise protokolliert; auf Replicas läuft es als DDL. Das EXCHANGE-Ziel muss dieselbe Struktur haben und darf nicht partitioniert sein; Zeilen außerhalb des Bereichs führen zu einem Fehler (WITHOUT VALIDATION ist schneller, kann aber falsche Daten einlassen). Partitions-DDL nimmt einen Metadata Lock auf Tabellenebene. Für Pruning die Partitionierungsspalte in WHERE verwenden, ohne sie in eine Funktion zu packen.","note":"In 8.0 wird Partitionierung nur von InnoDB (und NDB) unterstützt. Das Anlegen künftiger / Löschen alter Partitionen wird meist mit dem Event Scheduler oder cron automatisiert; ALTER TABLE ... TRUNCATE PARTITION entfernt die Daten, behält aber die Partition.","since":"MySQL 8.0","tags":["partition","range","reorganize","exchange partition","retention","archive"]},{"id":"roles-least-privilege","task":"Least-Privilege-Modell mit Rollen aufbauen (CREATE ROLE, DEFAULT ROLE)","category":"Sicherheit","command":"CREATE ROLE IF NOT EXISTS 'app_read', 'app_write';\nGRANT SELECT ON shop.* TO 'app_read';\nGRANT INSERT, UPDATE, DELETE ON shop.* TO 'app_write';\n\n-- Application account: network range, TLS required, roles active by default\nCREATE USER 'svc_app'@'10.0.%' IDENTIFIED BY 'S3cure#App2024' DEFAULT ROLE 'app_read', 'app_write' REQUIRE SSL;\nCREATE USER 'ana'@'%' IDENTIFIED BY 'An4#Report2024';\nGRANT 'app_read' TO 'ana'@'%';\nSET DEFAULT ROLE ALL TO 'ana'@'%';\n\n-- Show grants with roles expanded\nSHOW GRANTS FOR 'svc_app'@'10.0.%' USING 'app_read', 'app_write';\nSELECT FROM_USER, FROM_HOST, TO_USER, TO_HOST FROM mysql.role_edges;","what":"Vergibt Berechtigungen an Rollen statt an einzelne Benutzer; Konten erhalten Berechtigungen über die Rollenmitgliedschaft. Änderungen erfolgen an einer Stelle, Audits und Offboarding werden einfacher.","when":"Beim Anlegen neuer Anwendungs- / Teamkonten und beim Aufräumen von Altservern mit verworrenen Berechtigungen.","caution":"Eine Rolle, die keine Default-Rolle ist, ist in der Session nicht aktiv (CURRENT_ROLE() NONE), und dem Benutzer scheinen Berechtigungen zu fehlen; nutze SET DEFAULT ROLE ALL oder activate_all_roles_on_login=ON. Eine Rolle ist auch ein Konto (gesperrt, ohne Passwort) und erscheint in mysql.user. Für Anwendungskonten einen Netzbereich statt '%' verwenden. Einem Anwendungskonto niemals ALL, SUPER, FILE, PROCESS oder CONNECTION_ADMIN gewähren. Das Entziehen einer Berechtigung von einer Rolle wirkt auch auf aktive Sessions.","note":"mandatory_roles weist allen Konten automatisch Rollen zu. CREATE USER ... DEFAULT ROLE gewährt die Rollen und macht sie zugleich zu Default-Rollen.","since":"MySQL 8.0","tags":["role","grant","least privilege","default role","security","privilege"]},{"id":"password-policy-lockout","task":"Passwortrichtlinie, Sperre nach Fehlanmeldungen und Passwortrotation ohne Downtime","category":"Sicherheit","command":"-- Password strength policy (component)\nINSTALL COMPONENT 'file://component_validate_password';\nSET PERSIST validate_password.policy = 'MEDIUM';\nSET PERSIST validate_password.length = 12;\nSELECT VALIDATE_PASSWORD_STRENGTH('Summer2024') AS score;\n\n-- Failed-login lockout, password lifetime, history\nALTER USER 'ana'@'%' FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1\n  PASSWORD EXPIRE INTERVAL 90 DAY PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY;\n\n-- Zero-downtime rotation: dual password\nALTER USER 'svc_app'@'10.0.%' IDENTIFIED BY 'N3w#AppSecret2025' RETAIN CURRENT PASSWORD;\n-- Once every application uses the new password\nALTER USER 'svc_app'@'10.0.%' DISCARD OLD PASSWORD;","what":"Aktiviert eine Richtlinie, die schwache Passwörter ablehnt, Konten nach aufeinanderfolgenden Fehlanmeldungen vorübergehend sperrt und Regeln für Passwortlaufzeit und Wiederverwendung festlegt; mit Dual Passwords wird das Passwort eines Dienstkontos ohne Downtime geändert, während die Anwendungen nacheinander umgestellt werden.","when":"Für Sicherheitsaudits / Compliance (PCI, ISO 27001) und die Passwortrotation von Dienstkonten.","caution":"Die Richtlinie gilt nur für neue / geänderte Passwörter; bestehende schwache erkennt sie nicht. Bei Dienstkonten kann mit FAILED_LOGIN_ATTEMPTS ein einziger falsch konfigurierter Client das Konto sperren und einen Ausfall verursachen; nutze es für Konten von Personen. PASSWORD_LOCK_TIME ist in Tagen angegeben (UNBOUNDED: bis zur manuellen Entsperrung); entsperren mit ALTER USER ... ACCOUNT UNLOCK. PASSWORD EXPIRE verursacht bei Dienstkonten unerwartete Ausfälle; bevorzuge dort PASSWORD EXPIRE NEVER plus Rotation mit Dual Passwords.","note":"Dual Passwords kamen mit 8.0.14, FAILED_LOGIN_ATTEMPTS / PASSWORD_LOCK_TIME mit 8.0.19. Das validate_password-Plugin aus 5.7 wurde in 8.0 durch eine Komponente ersetzt; die Variablennamen verwenden einen Punkt (validate_password.policy).","since":"MySQL 8.0.19","tags":["password policy","validate_password","failed_login_attempts","dual password","security","password"]},{"id":"require-tls-transport","task":"TLS erzwingen, Verbindungen ohne TLS finden, Zertifikate ohne Neustart erneuern","category":"Sicherheit","command":"SELECT @@require_secure_transport, @@tls_version, @@ssl_ca, @@ssl_cert;\nSELECT * FROM performance_schema.tls_channel_status\nWHERE PROPERTY IN ('Enabled', 'Current_tls_version', 'Current_tls_cert', 'Ssl_server_not_after');\n\n-- Active connections and their TLS versions (empty = no TLS)\nSELECT t.PROCESSLIST_USER AS user, t.PROCESSLIST_HOST AS host, t.CONNECTION_TYPE, sv.VARIABLE_VALUE AS tls_version\nFROM performance_schema.threads t\nLEFT JOIN performance_schema.status_by_thread sv ON sv.THREAD_ID = t.THREAD_ID AND sv.VARIABLE_NAME = 'Ssl_version'\nWHERE t.TYPE = 'FOREGROUND' AND t.PROCESSLIST_USER IS NOT NULL;\n\nSET PERSIST require_secure_transport = ON;\n-- After copying the new certificate files\nALTER INSTANCE RELOAD TLS;","what":"Zeigt die TLS-Konfiguration des Servers, das Ablaufdatum des Zertifikats und ob jede Verbindung TLS nutzt; verbietet unverschlüsselte TCP-Verbindungen vollständig und erneuert Zertifikate, ohne den Server anzuhalten.","when":"Für Sicherheitsaudits / Compliance, vor dem Ablauf von Zertifikaten und beim Aufspüren alter Clients, die sich unverschlüsselt verbinden.","caution":"require_secure_transport=ON wirkt nicht auf Unix-Socket- und Shared-Memory-Verbindungen; diese gelten als sicher. Finde Clients, die sich ohne TLS verbinden, bevor du es aktivierst, sonst schlagen sie mit Fehler 3159 fehl. ALTER INSTANCE RELOAD TLS wirkt nicht auf bestehende, nur auf neue Verbindungen; ist die Zertifikatsdatei ungültig, bricht es mit einem Fehler ab und behält das alte Zertifikat. Das automatisch erzeugte selbstsignierte Zertifikat können Clients mit VERIFY_CA / VERIFY_IDENTITY nicht verifizieren; nutze in der Produktion ein von deiner CA signiertes Zertifikat.","note":"ALTER INSTANCE RELOAD TLS kam mit 8.0.16, performance_schema.tls_channel_status mit 8.0.21. Zum Erzwingen pro Konto ALTER USER ... REQUIRE SSL (oder REQUIRE X509) verwenden.","since":"MySQL 8.0.21","tags":["tls","ssl","require_secure_transport","certificate","encryption","cert"]},{"id":"risky-accounts-audit","task":"Riskante Konten prüfen: leeres Passwort, Host '%', SUPER, mysql_native_password","category":"Sicherheit","command":"SELECT user, host, plugin,\n       CONCAT_WS(', ',\n         IF(authentication_string = '' AND account_locked = 'N', 'EMPTY PASSWORD', NULL),\n         IF(host = '%', 'ANY HOST', NULL),\n         IF(plugin = 'mysql_native_password', 'NATIVE AUTH', NULL),\n         IF(Super_priv = 'Y', 'SUPER', NULL),\n         IF(Grant_priv = 'Y', 'GRANT OPTION', NULL),\n         IF(File_priv = 'Y', 'FILE', NULL),\n         IF(ssl_type = '', 'NO TLS REQUIRED', NULL),\n         IF(password_expired = 'Y', 'EXPIRED', NULL),\n         IF(account_locked = 'Y', 'LOCKED', NULL)) AS findings,\n       password_last_changed\nFROM mysql.user\nWHERE user NOT IN ('mysql.sys', 'mysql.session', 'mysql.infoschema')\nORDER BY (Super_priv = 'Y') DESC, user;\n\n-- Accounts holding powerful dynamic privileges\nSELECT GRANTEE, PRIVILEGE_TYPE FROM information_schema.USER_PRIVILEGES\nWHERE PRIVILEGE_TYPE IN ('SUPER', 'SYSTEM_VARIABLES_ADMIN', 'CONNECTION_ADMIN', 'BINLOG_ADMIN', 'REPLICATION_SLAVE_ADMIN')\nORDER BY GRANTEE;","what":"Listet Sicherheitsbefunde pro Konto in einer Zeile: kein Passwort, von überall verbindbar, altes Authentifizierungs-Plugin, Berechtigungen SUPER / GRANT / FILE und keine TLS-Pflicht; die zweite Abfrage zeigt gefährliche dynamische Berechtigungen.","when":"Bei regelmäßigen Sicherheitsaudits, auf einem neu übernommenen Server und vor einem Upgrade auf 8.4.","caution":"Die Abfrage liest mysql.user direkt und braucht SELECT darauf. Rollenkonten sind gesperrt und ohne Passwort, daher wird EMPTY PASSWORD nur für nicht gesperrte Konten gemeldet. Super_priv ist die statische SUPER-Berechtigung; da 8.0 sie in dynamische Berechtigungen aufteilt, prüfe auch die zweite Abfrage. Mit NATIVE AUTH markierte Konten müssen vor 8.4 auf caching_sha2_password umgestellt werden: ALTER USER 'u'@'h' IDENTIFIED WITH caching_sha2_password BY '...'; alte Clients / Connectors unterstützen caching_sha2_password eventuell nicht, teste sie vorher.","note":"mysql_native_password wurde in 8.0.34 als veraltet markiert, ist in 8.4 standardmäßig deaktiviert (aktivierbar mit mysql_native_password=ON) und wurde in 9.0 entfernt.","since":"MySQL 8.0","tags":["security audit","mysql.user","mysql_native_password","super","privileges","audit"]},{"id":"upgrade-checker-84","task":"Vorabprüfung für das Upgrade 8.0 → 8.4 LTS (util.checkForServerUpgrade)","category":"Upgrade","command":"# State the target version explicitly; the Shell must be the same version as the target or newer\nmysqlsh root@db1:3306 -- util check-for-server-upgrade --target-version=8.4.10 --output-format=TEXT\n# JSON output for automation, including a my.cnf check\nmysqlsh root@db1:3306 -- util check-for-server-upgrade --target-version=8.4.10 --output-format=JSON --config-path=/etc/my.cnf > /tmp/upgrade_check.json","what":"Prüft den aktuellen Server gegen die Zielversion: entfernte Variablen und Berechtigungen, Parameter mit neuen Standardwerten, alte Authentifizierungsmethoden, ungültige Spaltendefinitionen, Probleme mit Foreign Keys und Partitionen. Befunde der Stufe Error blockieren das Upgrade.","when":"Vor jedem Major- / LTS-Upgrade; nach Behebung der Befunde erneut ausführen.","caution":"Prüfe auch die Warnungen: In 8.4 ändern sich Standardwerte wie innodb_io_capacity (10000), innodb_log_buffer_size (64 MB), innodb_adaptive_hash_index (OFF), innodb_change_buffering (none), innodb_buffer_pool_instances und group_replication_consistency und können die Performance beeinflussen; lege die Werte, deren bisheriges Verhalten du behalten willst, in my.cnf fest. Entfernte Berechtigungen wie SET_USER_ID werden beim Upgrade verworfen. Zuerst eine Replica upgraden, unter Last beobachten, dann umschalten; ein binäres Downgrade von 8.4 auf 8.0 wird nicht unterstützt, der Rückweg ist also ein logisches Backup oder eine auf der alten Version belassene Replica.","note":"In 8.4 entfernt: die alte Replikationssyntax wie START / STOP SLAVE, SHOW SLAVE STATUS und CHANGE MASTER TO, default_authentication_plugin (ersetzt durch authentication_policy), binlog_transaction_dependency_tracking und mysqlpump. mysql_native_password ist standardmäßig deaktiviert.","since":"MySQL Shell 8.0","tags":["upgrade","8.4","checkforserverupgrade","mysqlsh","lts","version upgrade"]},{"id":"charset-utf8mb4-convert","task":"latin1- / utf8mb3-Tabellen nach utf8mb4 konvertieren","category":"Wartung","command":"-- Columns that are not utf8mb4\nSELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_TYPE\nFROM information_schema.COLUMNS\nWHERE CHARACTER_SET_NAME IS NOT NULL AND CHARACTER_SET_NAME <> 'utf8mb4'\n  AND TABLE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema')\nORDER BY TABLE_SCHEMA, TABLE_NAME;\n\n-- Generate conversion DDL for tables whose default charset is not utf8mb4\nSELECT CONCAT('ALTER TABLE `', t.TABLE_SCHEMA, '`.`', t.TABLE_NAME,\n              '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, ALGORITHM=COPY, LOCK=SHARED;') AS ddl\nFROM information_schema.TABLES t\nJOIN information_schema.COLLATIONS c ON c.COLLATION_NAME = t.TABLE_COLLATION\nWHERE c.CHARACTER_SET_NAME <> 'utf8mb4' AND t.TABLE_TYPE = 'BASE TABLE'\n  AND t.TABLE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema');\n\n-- Schema default and a single table\nALTER DATABASE legacy CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;\nALTER TABLE legacy.cust CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, ALGORITHM=COPY, LOCK=SHARED;","what":"Findet Spalten und Tabellen, die nicht utf8mb4 sind, und erzeugt die Konvertierungs-DDL; CONVERT TO ändert sowohl den Tabellen-Standard als auch jede Zeichenspalte und ermöglicht Emojis / 4-Byte-Zeichen.","when":"Bei aus 5.x migrierten Schemas, bei Fehlern 'Incorrect string value' (1366) und wenn Joins zwischen Spalten mit unterschiedlicher Collation keine Indizes nutzen können.","caution":"CONVERT TO schreibt die Tabelle mit COPY neu und blockiert währenddessen Schreibzugriffe (INPLACE wird nicht unterstützt); auf großen Tabellen dasselbe --alter über pt-online-schema-change / gh-ost ausführen. utf8mb4 nutzt 4 Bytes pro Zeichen: Ein indiziertes VARCHAR(255) wird zu 1020 Bytes; das Limit liegt beim Zeilenformat DYNAMIC / COMPRESSED bei 3072 Bytes und bei COMPACT / REDUNDANT bei 767, darüber erhältst du Fehler 1071. VARCHAR- / TEXT-Spalten werden bei Bedarf auf einen größeren Typ hochgestuft (TEXT → MEDIUMTEXT), um dieselbe Zeichenkapazität zu behalten. Eine Änderung der Collation wirkt auf Sortierung und Vergleich: utf8mb4_0900_ai_ci unterscheidet weder Akzente noch Groß-/Kleinschreibung, daher können früher verschiedene Werte auf einem Unique Index kollidieren (1062). Gejointe Tabellen gemeinsam konvertieren. Auch die Client-Verbindung muss utf8mb4 verwenden.","note":"utf8 (utf8mb3) ist in 8.0 veraltet; seit 8.0.30 wird es in SHOW CREATE TABLE als utf8mb3 angezeigt. Für türkische Collation-Regeln (I mit und ohne Punkt) utf8mb4_tr_0900_ai_ci verwenden. Standard in 8.0 ist utf8mb4 / utf8mb4_0900_ai_ci.","since":"MySQL 8.0","tags":["utf8mb4","character set","collation","convert","emoji","charset"]}]},"recipes/mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"recipes":[{"id":"backup-log","task":"Transaktionslog-Backup erstellen","category":"Backup & Log","command":"BACKUP LOG [Customer]\nTO DISK = N'D:\\Backup\\Customer_log_20260706_1200.trn'\nWITH COMPRESSION, CHECKSUM, STATS = 10;","what":"Erstellt ein Transaktionslog-Backup und kürzt (im Recovery Model FULL/BULK_LOGGED) den genutzten Teil des Logs, damit er wiederverwendet werden kann.","when":"Regelmäßig im Recovery Model FULL (z. B. alle 15 Min.), für die Point-in-Time-Recovery-Kette und wenn die Logdatei wächst.","caution":"Nur im Recovery Model FULL/BULK_LOGGED sinnvoll (in SIMPLE wird das Log automatisch gekürzt). Zuerst muss mindestens ein FULL-Backup existieren, damit die Log-Kette nicht unterbrochen ist. Lege eine korrekte Lösch-/Überschreibstrategie für Backup-Dateien fest.","tags":["backup log","log backup","transaction log","trn","recovery","backup"]},{"id":"backup-full-diff","task":"FULL- und DIFFERENTIAL-Backups erstellen","category":"Backup & Log","command":"-- Full backup:\nBACKUP DATABASE [Customer]\nTO DISK = N'D:\\Backup\\Customer_full_20260706.bak'\nWITH COMPRESSION, CHECKSUM, INIT, STATS = 10;\n\n-- Differential backup:\nBACKUP DATABASE [Customer]\nTO DISK = N'D:\\Backup\\Customer_diff_20260706.bak'\nWITH DIFFERENTIAL, COMPRESSION, CHECKSUM, INIT, STATS = 10;","what":"FULL sichert die gesamte Datenbank; DIFFERENTIAL sichert die seit dem letzten FULL geänderten Extents.","when":"FULL: tägliches/wöchentliches Basis-Backup. DIFF: schnelle Zwischen-Backups zwischen FULLs.","caution":"Zum Wiederherstellen eines DIFF ist das zugehörige FULL-Basis-Backup nötig. INIT überschreibt die vorhandene Datei; mit falschem Dateinamen verlierst du ein Backup. CHECKSUM erkennt Korruption frühzeitig.","tags":["backup","full","differential","differential backup","bak","backup"]},{"id":"log-reuse-wait","task":"Warum die Logdatei gewachsen ist / warum sie nicht gekürzt wird","category":"Backup & Log","command":"SELECT name,\n       recovery_model_desc,\n       log_reuse_wait_desc\nFROM   sys.databases\nWHERE  name = N'Customer';\n\n-- Log space usage for all databases:\nSELECT DB_NAME(database_id) AS db, name, type_desc,\n       size*8/1024        AS size_mb,\n       CAST(FILEPROPERTY(name,'SpaceUsed') AS BIGINT)*8/1024 AS used_mb\nFROM   sys.master_files\nWHERE  type_desc = 'LOG';","what":"Die Spalte log_reuse_wait_desc gibt an, warum das Transaktionslog nicht gekürzt werden kann (z. B. LOG_BACKUP, ACTIVE_TRANSACTION, REPLICATION, AVAILABILITY_REPLICA).","when":"Um die Ursache zu finden, wenn die Logdatei ständig wächst oder das Log voll ist (Fehler 9002).","caution":"Wird LOG_BACKUP angezeigt, ist die Lösung ein Log-Backup, nicht das Verkleinern der Datei. Nur Abfrage; kein Risiko.","tags":["log_reuse_wait_desc","log growth","sys.databases","log backup","recovery","diagnostics"]},{"id":"shrink-log","task":"Logdatei verkleinern","category":"Backup & Log","command":"-- First take a log backup so the log can be truncated (FULL model):\nBACKUP LOG [Customer] TO DISK = N'D:\\Backup\\Customer_log_pre_shrink.trn' WITH COMPRESSION;\n\n-- Find the logical name of the log file:\nSELECT name, type_desc FROM sys.database_files WHERE type_desc = 'LOG';\n\n-- Shrink (target 2048 MB):\nUSE [Customer];\nDBCC SHRINKFILE (N'Customer_log', 2048);","what":"Verringert die physische Größe der Transaktionslogdatei und gibt Platz an das Betriebssystem zurück.","when":"Wenn das Log nach einem einmaligen ungewöhnlichen Wachstum (große Transaktion/Index-Rebuild) dauerhaft aufgebläht geblieben ist.","caution":"GEFÄHRLICH/VORSICHT: Routinemäßiges Verkleinern ist SCHLECHTE Praxis; die Performance sinkt, während das Log wieder wächst, und die VLF-Fragmentierung nimmt zu. Erstelle zuerst ein Log-Backup, sonst sinkt die Größe nicht. Verkleinere nur eine unnötig gewachsene Logdatei, nicht die Datendatei. Vergrößere sie danach in einem einzigen Schritt wieder auf eine passende Größe.","tags":["shrink","dbcc shrinkfile","shrink log","log","vlf","caution"]},{"id":"vlf-count","task":"Anzahl der VLFs (Virtual Log Files) anzeigen","category":"Backup & Log","command":"-- SQL Server 2016 SP2 / 2017 and later:\nSELECT DB_NAME(database_id) AS db,\n       COUNT(*)             AS vlf_count,\n       SUM(CASE WHEN vlf_active = 1 THEN 1 ELSE 0 END) AS active_vlf\nFROM   sys.dm_db_log_info(DB_ID(N'Customer'))\nGROUP BY database_id;\n\n-- For older versions: DBCC LOGINFO('Customer'); (row count = VLF count)","what":"Liefert die Anzahl der virtuellen Logdateien (VLFs) im Transaktionslog. Zu viele VLFs verschlechtern die Performance von Start/Recovery/Log-Backup.","when":"Wenn das Log langsam ist oder der Datenbankstart lange dauert; beim Prüfen der Einstellung für das Logwachstum.","caution":"Hunderte/Tausende VLFs sind ein Warnsignal; die Lösung ist, das Log zu verkleinern und in passend großen Schritten (z. B. 8GB-Schritte) wieder zu vergrößern. Nur Abfrage; kein Risiko.","since":"SQL Server 2016 SP2","note":"Die DMV sys.dm_db_log_info kam mit SQL Server 2016 SP2 und 2017; davor wurden VLFs anhand der Zeilenanzahl der undokumentierten DBCC LOGINFO-Ausgabe gezählt.","tags":["vlf","dm_db_log_info","dbcc loginfo","log","virtual log file","performance"]},{"id":"open-transaction","task":"Die älteste offene Transaktion finden (ACTIVE_TRANSACTION)","category":"Backup & Log","command":"USE [Customer];\nDBCC OPENTRAN;\n\n-- Detailed: show long-running open transactions and their owners:\nSELECT s.session_id, s.login_name, s.host_name, s.program_name,\n       t.transaction_id,\n       DATEDIFF(SECOND, t.transaction_begin_time, GETDATE()) AS duration_sec,\n       t.transaction_begin_time\nFROM   sys.dm_tran_active_transactions t\n  JOIN sys.dm_tran_session_transactions st ON st.transaction_id = t.transaction_id\n  JOIN sys.dm_exec_sessions s ON s.session_id = st.session_id\nORDER BY t.transaction_begin_time;","what":"Zeigt die älteste offene (nicht committete/zurückgerollte) Transaktion und die Session, die sie hält. Eine offene Transaktion verhindert das Kürzen des Logs (LOG_REUSE_WAIT = ACTIVE_TRANSACTION).","when":"Um die Ursache zu finden, wenn das Log trotz Log-Backups nicht schrumpft und log_reuse_wait_desc = ACTIVE_TRANSACTION ist.","caution":"Stimme dich mit dem fachlich Verantwortlichen ab, bevor du die Session mit einer lange offenen Transaktion per KILL beendest; das Rollback kann lange dauern. Nur Abfrage; kein Risiko.","tags":["dbcc opentran","open transaction","active_transaction","dm_tran_active_transactions","log","diagnostics"]},{"id":"add-data-file","task":"Einer Datenbank eine Datendatei hinzufügen","category":"Speicher","command":"ALTER DATABASE [Customer]\nADD FILE (\n  NAME = N'Customer_data2',\n  FILENAME = N'E:\\Data\\Customer_data2.ndf',\n  SIZE = 4GB,\n  FILEGROWTH = 512MB,\n  MAXSIZE = 100GB\n) TO FILEGROUP [PRIMARY];","what":"Fügt einer bestehenden Dateigruppe eine neue Datendatei (.ndf) hinzu, um die Kapazität zu erhöhen und I/O zu verteilen.","when":"Wenn die Platte voll ist oder du I/O auf mehrere Platten verteilen willst.","caution":"Schreibvorgänge in die neue Datei werden per Proportional Fill verteilt; die leerste Datei füllt sich zuerst. FILEGROWTH als feste MB angeben, nicht in Prozent. MAXSIZE verhindert unkontrolliertes Wachstum.","tags":["add file","add data file","ndf","alter database","filegroup","storage"]},{"id":"add-filegroup","task":"Dateigruppe hinzufügen","category":"Speicher","command":"-- 1) Create the filegroup:\nALTER DATABASE [Customer] ADD FILEGROUP [FG_Archive];\n\n-- 2) Add a file to it:\nALTER DATABASE [Customer]\nADD FILE (\n  NAME = N'Customer_archive1',\n  FILENAME = N'F:\\Data\\Customer_archive1.ndf',\n  SIZE = 8GB, FILEGROWTH = 1GB\n) TO FILEGROUP [FG_Archive];\n\n-- (Optional) make it the default filegroup:\n-- ALTER DATABASE [Customer] MODIFY FILEGROUP [FG_Archive] DEFAULT;","what":"Legt eine neue Dateigruppe mit einer Datei an; damit lassen sich Tabellen/Indizes auf einer separaten Platte/Dateigruppe ablegen.","when":"Zum Trennen von Archiv-/Cold-Daten, für Backup/Restore auf Dateigruppenebene und zur I/O-Trennung.","caution":"Eine leere Dateigruppe kann weder gesichert noch genutzt werden; füge ihr mindestens eine Datei hinzu. Das Ändern des Standards beeinflusst, wo neue Objekte abgelegt werden.","tags":["filegroup","filegroup","add filegroup","alter database","ndf","storage"]},{"id":"modify-file-growth","task":"Dateigröße / Autogrowth ändern","category":"Speicher","command":"-- Grow (SIZE only grows; shrinking requires shrink, not a resize):\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_data', SIZE = 20GB);\n\n-- Set autogrowth (fixed MB recommended):\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_data', FILEGROWTH = 1GB, MAXSIZE = 200GB);\n\n-- Disable autogrowth:\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_log', FILEGROWTH = 0);","what":"Vergrößert eine Daten-/Logdatei vorab oder legt ihr Autogrowth-Inkrement/ihre Obergrenze fest.","when":"Um unkontrolliert kleine Wachstumsschritte zu beheben, Platz vorab zu reservieren oder eine MAXSIZE festzulegen.","caution":"Prozentuales Autogrowth erzeugt bei großen Dateien sehr große Schritte; nutze feste MB. Ohne Instant File Initialization blockiert das Wachstum von Datendateien. SIZE kann eine Datei nicht verkleinern (dafür ist Shrink nötig).","tags":["modify file","autogrowth","filegrowth","size","alter database","storage"]},{"id":"move-file","task":"Eine Daten-/Logdatei an einen anderen Ort verschieben","category":"Speicher","command":"-- 1) Tell SQL Server the new path (nothing is moved yet):\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_data',\n             FILENAME = N'G:\\Data\\Customer_data.mdf');\n\n-- 2) Take the database offline:\nALTER DATABASE [Customer] SET OFFLINE WITH ROLLBACK IMMEDIATE;\n\n-- 3) Physically copy/move the file to the new path AT THE OPERATING SYSTEM level (G:\\Data\\...).\n\n-- 4) Bring it online:\nALTER DATABASE [Customer] SET ONLINE;","what":"Ändert den physischen Speicherort einer Datenbankdatei: Der neue Pfad wird in den Metadaten festgelegt, die Datei wird physisch verschoben, während die Datenbank offline ist, dann wird die Datenbank online geschaltet.","when":"Beim Austausch einer Platte oder beim Umzug auf ein anderes Laufwerk, um Platz zu gewinnen.","caution":"OFFLINE + ROLLBACK IMMEDIATE trennt alle aktiven Verbindungen; plane eine Downtime ein. Schalte die Datenbank nicht ONLINE, bevor die Datei im Betriebssystem tatsächlich an den neuen Pfad kopiert wurde, sonst wird die Datenbank SUSPECT. tempdb erfordert eine andere Methode (MODIFY FILE + Neustart des SQL-Server-Dienstes).","tags":["move file","move file","modify file","offline","online","mdf","ldf","storage"]},{"id":"add-tempdb-file","task":"tempdb eine Datendatei hinzufügen","category":"Speicher","command":"-- Show current tempdb files:\nSELECT name, physical_name, size*8/1024 AS size_mb, growth\nFROM   sys.master_files WHERE database_id = DB_ID(N'tempdb');\n\n-- Add a new file (up to the number of CPU cores, equally sized):\nALTER DATABASE [tempdb]\nADD FILE (\n  NAME = N'tempdev2',\n  FILENAME = N'T:\\tempdb\\tempdb_mssql_2.ndf',\n  SIZE = 8GB, FILEGROWTH = 1GB\n);","what":"Fügt tempdb eine zusätzliche Datendatei hinzu, um Allocation-Contention (PFS/GAM/SGAM) zu verringern und I/O zu verteilen.","when":"Auf Systemen mit tempdb-Contention (PAGELATCH_UP-Waits) oder starker tempdb-Nutzung.","caution":"Alle tempdb-Datendateien müssen dieselbe Größe und dasselbe Wachstumsinkrement haben (wegen Proportional Fill). Die hinzugefügte Datei wird sofort ohne Neustart genutzt; sind die vorhandenen Dateien unterschiedlich groß, gleiche sie zuerst alle an, sonst bekommt die neue (leere) Datei eine unverhältnismäßige Last. Die Dateianzahl wird meist an die Kernanzahl angepasst (bis 8); das Setup von SQL Server 2016+ macht das standardmäßig.","tags":["tempdb","add file","tempdb file","ndf","contention","pagelatch","storage"]},{"id":"index-rebuild-reorg","task":"Index Rebuild / Reorganize","category":"Wartung","command":"-- Rebuild (high fragmentation > 30%). ONLINE requires Enterprise:\nALTER INDEX IX_Customer_Email ON dbo.Customer\n  REBUILD WITH (ONLINE = ON, MAXDOP = 4, SORT_IN_TEMPDB = ON);\n\n-- All indexes of a table:\nALTER INDEX ALL ON dbo.Customer REBUILD WITH (FILLFACTOR = 90);\n\n-- Reorganize (moderate fragmentation 5-30%, always online, can be stopped):\nALTER INDEX IX_Customer_Email ON dbo.Customer REORGANIZE;","what":"REBUILD erstellt den Index von Grund auf neu (beseitigt Fragmentierung vollständig, aktualisiert Statistiken); REORGANIZE verdichtet die Blattebene an Ort und Stelle (leichtgewichtig, nicht blockierend).","when":"REORGANIZE bei 5-30% Fragmentierung, REBUILD über 30%. Bei großen Tabellen in Wartungsfenstern.","caution":"ONLINE = ON ist nur in Enterprise/Developer verfügbar; in Standard OFF verwenden (die Tabelle wird gesperrt). REBUILD erzeugt viel Log und braucht Platz. MAXDOP und SORT_IN_TEMPDB beeinflussen den Ressourcenverbrauch.","tags":["index","rebuild","reorganize","alter index","fragmentation","maintenance","online"]},{"id":"update-statistics","task":"Statistiken aktualisieren","category":"Wartung","command":"-- Single table, full scan:\nUPDATE STATISTICS dbo.Customer WITH FULLSCAN;\n\n-- Specific statistic, with sampling:\nUPDATE STATISTICS dbo.Customer IX_Customer_Email WITH SAMPLE 30 PERCENT;\n\n-- Bulk for the whole database (user tables), sampled:\nEXEC sys.sp_updatestats;","what":"Aktualisiert die vom Optimizer genutzten Spalten-/Indexstatistiken; sorgt für korrekte Ausführungspläne und Kardinalitätsschätzungen.","when":"Nach großen Datenänderungen/Ladevorgängen, bei Planregressionen; ein Index-Rebuild aktualisiert die Statistiken bereits vollständig (separat nicht nötig).","caution":"FULLSCAN ist genau, aber teuer; es erzeugt auf großen Tabellen I/O-Last. sp_updatestats aktualisiert nur geänderte Statistiken und nutzt das Standard-Sampling. Ein erneutes UPDATE STATISTICS auf demselben Index nach einem Index-REBUILD ist unnötig.","tags":["update statistics","statistics","sp_updatestats","fullscan","optimizer","maintenance"]},{"id":"dbcc-checkdb","task":"Integritätsprüfung (DBCC CHECKDB)","category":"Wartung","command":"-- Full integrity check:\nDBCC CHECKDB (N'Customer') WITH NO_INFOMSGS, ALL_ERRORMSGS;\n\n-- Lighter on large databases (physical consistency):\nDBCC CHECKDB (N'Customer') WITH PHYSICAL_ONLY, NO_INFOMSGS;","what":"Prüft die physische und logische Integrität der Datenbank (Seitenstrukturen, Allokation, Indexkonsistenz) und meldet Korruption.","when":"In der regelmäßigen Wartung (wöchentlich), bei Verdacht auf Hardware-/IO-Probleme, nach Fehlern 823/824.","caution":"Verursacht hohe I/O- und tempdb-Last; führe es in einem Wartungsfenster aus. Werden Fehler gefunden, muss REPAIR_ALLOW_DATA_LOSS das LETZTE Mittel sein und VERURSACHT DATENVERLUST; erwäge zuerst die Wiederherstellung aus einem guten Backup. PHYSICAL_ONLY ist schneller, überspringt aber logische Prüfungen.","tags":["dbcc checkdb","integrity","corruption","corruption","integrity","maintenance"]},{"id":"index-fragmentation","task":"Indexfragmentierung anzeigen","category":"Wartung","command":"SELECT OBJECT_SCHEMA_NAME(ips.object_id) AS schema_name,\n       OBJECT_NAME(ips.object_id)        AS table_name,\n       i.name                            AS index_name,\n       ips.index_type_desc,\n       ROUND(ips.avg_fragmentation_in_percent,1) AS frag_pct,\n       ips.page_count\nFROM   sys.dm_db_index_physical_stats(DB_ID(N'Customer'), NULL, NULL, NULL, 'LIMITED') ips\n  JOIN sys.indexes i\n    ON i.object_id = ips.object_id AND i.index_id = ips.index_id\nWHERE  ips.avg_fragmentation_in_percent > 10\n  AND  ips.page_count > 1000\nORDER BY ips.avg_fragmentation_in_percent DESC;","what":"Listet Fragmentierungsgrad und Seitenanzahl der Indizes; die Grundlage für die Entscheidung zwischen Rebuild und Reorganize.","when":"Vor der Wartung, um zu entscheiden, welche Indizes bearbeitet werden.","caution":"Bei kleinen Indizes (< 1000 Seiten) ist Fragmentierung irrelevant; sie werden herausgefiltert. Der Modus 'DETAILED' ist genauer, aber sehr teuer; 'LIMITED' reicht meist aus. Nur Abfrage; kein Risiko.","tags":["fragmentation","dm_db_index_physical_stats","index","frag","maintenance","diagnostics"]},{"id":"kill-spid","task":"Session beenden (SPID)","category":"Session","command":"-- First find the offending SPID:\nSELECT s.session_id, s.login_name, s.host_name, s.program_name,\n       r.status, r.command, r.wait_type, r.blocking_session_id,\n       DB_NAME(r.database_id) AS db\nFROM   sys.dm_exec_sessions s\n  LEFT JOIN sys.dm_exec_requests r ON r.session_id = s.session_id\nWHERE  s.is_user_process = 1;\n\n-- Kill (example SPID 63):\nKILL 63;\n\n-- Show rollback progress:\nKILL 63 WITH STATUSONLY;","what":"Beendet die angegebene session_id (SPID); ihre offene Transaktion wird zurückgerollt.","when":"Um eine Session zu stoppen, die Sperren hält, eine außer Kontrolle geratene/lange Abfrage ausführt oder ihre Verbindung nicht freigibt.","caution":"Bei einer großen offenen Transaktion kann das Rollback lange dauern und lässt sich nicht abbrechen; verfolge den Fortschritt mit KILL WITH STATUSONLY. Beende keine System-SPIDs (is_user_process=0). Prüfe, ob die SPID stimmt.","tags":["kill","spid","kill session","dm_exec_sessions","dm_exec_requests","session"]},{"id":"blocking-chain","task":"Die Blockierungskette (Sperren) finden","category":"Session","command":"SELECT r.session_id            AS waiting_spid,\n       r.blocking_session_id  AS blocking_spid,\n       r.wait_type,\n       r.wait_time / 1000.0   AS wait_sec,\n       r.status, r.command,\n       DB_NAME(r.database_id) AS db,\n       t.text                 AS waiting_query\nFROM   sys.dm_exec_requests r\n  CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE  r.blocking_session_id <> 0\nORDER BY r.wait_time DESC;\n\n-- To find the head (root) blocker of the chain, look for the SPID whose\n-- own blocking_session_id is not set (it is not blocked itself).","what":"Listet wartende Sessions und die sie blockierenden Sessions mit Wartezeit und ausgeführter Abfrage; macht die Sperrkette sichtbar.","when":"Um bei Beschwerden wie \"die Anwendung hängt / läuft in Timeouts\" die Quelle der Sperrkonflikte zu finden.","caution":"Stimme dich mit dem fachlich Verantwortlichen ab, bevor du den Root Blocker beendest (die SPID, die von niemandem blockiert wird, aber andere blockiert). Nur Abfrage; kein Risiko.","tags":["blocking","lock","lock","dm_exec_requests","blocking_session_id","session","chain"]},{"id":"active-queries","task":"Aktive Abfragen und ihre Kosten anzeigen","category":"Session","command":"SELECT r.session_id,\n       r.status,\n       r.command,\n       DB_NAME(r.database_id) AS db,\n       r.cpu_time,\n       r.total_elapsed_time / 1000 AS elapsed_sec,\n       r.reads, r.writes, r.logical_reads,\n       r.wait_type,\n       SUBSTRING(t.text,\n         (r.statement_start_offset/2)+1,\n         ((CASE r.statement_end_offset WHEN -1 THEN DATALENGTH(t.text)\n            ELSE r.statement_end_offset END - r.statement_start_offset)/2)+1) AS running_statement\nFROM   sys.dm_exec_requests r\n  JOIN sys.dm_exec_sessions s ON s.session_id = r.session_id\n  CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE  s.is_user_process = 1 AND r.session_id <> @@SPID\nORDER BY r.total_elapsed_time DESC;","what":"Zeigt die aktuell laufenden Benutzerabfragen mit CPU, verstrichener Zeit, Lese-/Schreibvorgängen und dem genauen Anweisungstext.","when":"Um bei einer akuten Verlangsamung zu finden, welche Abfrage Ressourcen verbraucht.","caution":"statement_start/end_offset extrahiert genau die laufende Anweisung (nicht den ganzen Batch). Nur Abfrage; kein Risiko.","tags":["active queries","active query","dm_exec_requests","dm_exec_sql_text","cpu","session"]},{"id":"sp-configure-memory","task":"Mit sp_configure konfigurieren (max server memory)","category":"Konfiguration","command":"-- Make advanced options visible:\nEXEC sys.sp_configure 'show advanced options', 1;\nRECONFIGURE;\n\n-- Set max server memory (MB). Example: allow 24 GB:\nEXEC sys.sp_configure 'max server memory (MB)', 24576;\nRECONFIGURE;\n\n-- Verify:\nEXEC sys.sp_configure 'max server memory (MB)';","what":"Begrenzt den maximalen Speicher, den SQL Server für den Buffer Pool nutzen kann; lässt RAM für das Betriebssystem und andere Komponenten frei.","when":"Wenn dem Betriebssystem/anderen Diensten auf dem Server Speicher fehlt oder neuer RAM hinzugefügt wurde.","caution":"Lass genug RAM für das Betriebssystem, SSAS/SSIS, CLR und Thread-Stacks (meist mindestens 4-6 GB für das OS). Ein zu niedriger Wert legt die Performance von SQL Server lahm. Die Änderung wird ohne RECONFIGURE nicht übernommen; diese Einstellung ist dynamisch (kein Neustart nötig).","tags":["sp_configure","max server memory","reconfigure","memory","configuration","memory"]},{"id":"reconfigure-general","task":"Allgemeine sp_configure-Einstellung und RECONFIGURE","category":"Konfiguration","command":"EXEC sys.sp_configure 'show advanced options', 1;\nRECONFIGURE;\n\n-- Example: cost threshold for parallelism and MAXDOP:\nEXEC sys.sp_configure 'cost threshold for parallelism', 50;\nEXEC sys.sp_configure 'max degree of parallelism', 4;\nRECONFIGURE;\n\n-- Show all configured/running values:\nSELECT name, value AS configured, value_in_use AS running, is_dynamic, is_advanced\nFROM   sys.configurations\nORDER BY name;","what":"Setzt Konfigurationswerte auf Serverebene; RECONFIGURE überträgt sie auf den laufenden Wert.","when":"Beim Ändern von Servereinstellungen wie MAXDOP, dem Schwellenwert für Parallelität oder optimize for ad hoc workloads.","caution":"Einstellungen mit is_dynamic = 0 erfordern einen Neustart von SQL Server. RECONFIGURE WITH OVERRIDE überspringt bei manchen Einstellungen die Validierung; vorsichtig verwenden. Notiere value_in_use, bevor du etwas änderst.","tags":["sp_configure","reconfigure","maxdop","cost threshold","sys.configurations","configuration"]},{"id":"ag-failover","task":"Availability Group Failover","category":"Konfiguration","command":"-- Run on the TARGET (secondary) REPLICA - planned, no data loss (synchronous):\nALTER AVAILABILITY GROUP [AG_Customer] FAILOVER;\n\n-- Forced (asynchronous replica / primary unreachable, DATA LOSS risk):\n-- ALTER AVAILABILITY GROUP [AG_Customer] FORCE_FAILOVER_ALLOW_DATA_LOSS;\n\n-- Check status:\nSELECT ag.name, ar.replica_server_name,\n       rs.role_desc, rs.synchronization_health_desc, ar.availability_mode_desc\nFROM   sys.availability_groups ag\n  JOIN sys.availability_replicas ar ON ar.group_id = ag.group_id\n  JOIN sys.dm_hadr_availability_replica_states rs ON rs.replica_id = ar.replica_id;","what":"Verschiebt die Primary-Rolle der Availability Group auf eine andere Replica; ein geplantes Failover auf eine synchrone Replica erfolgt ohne Datenverlust.","when":"Rollenwechsel für Wartung/Patching, Failover bei Ausfall des Primary-Knotens.","caution":"Der normale FAILOVER-Befehl wird auf der SECONDARY-Replica ausgeführt, die übernehmen soll. FORCE_FAILOVER_ALLOW_DATA_LOSS wird nur verwendet, wenn der Primary nicht erreichbar ist oder auf einer asynchronen Replica, und kann DATENVERLUST VERURSACHEN; sei sehr vorsichtig, wenn die Synchronisierung nicht fehlerfrei ist.","tags":["availability group","ag","failover","hadr","failover","alwayson","configuration"]},{"id":"change-recovery-model","task":"Recovery Model der Datenbank ändern","category":"Konfiguration","command":"-- Show the current model:\nSELECT name, recovery_model_desc FROM sys.databases WHERE name = N'Customer';\n\n-- FULL recovery (for point-in-time recovery):\nALTER DATABASE [Customer] SET RECOVERY FULL;\n\n-- SIMPLE recovery (log truncated automatically, no log backups needed):\n-- ALTER DATABASE [Customer] SET RECOVERY SIMPLE;\n\n-- BULK_LOGGED (temporary, for large bulk operations):\n-- ALTER DATABASE [Customer] SET RECOVERY BULK_LOGGED;","what":"Ändert das Recovery Model der Datenbank; bestimmt, wie das Log verwaltet wird und welche Wiederherstellungsoptionen es gibt.","when":"FULL: wenn Point-in-Time-Recovery/Log-Backups/AG nötig sind. SIMPLE: auf Systemen ohne Bedarf an Wiederherstellungspunkten (Dev/Test, DW).","caution":"Erstelle nach dem Wechsel auf FULL ein FULL-Backup, um die Log-Kette zu starten, und richte regelmäßige Log-Backups ein, sonst wächst das Log immer weiter. Der Wechsel von FULL auf SIMPLE unterbricht die Log-Kette; du verlierst die Point-in-Time-Recovery. AG-Mitglieder müssen im Recovery Model FULL sein.","tags":["recovery model","recovery full","simple","bulk_logged","alter database","configuration"]}]},"recipes/mssql-ext.json":{"engineId":"mssql","engine":"SQL Server","order":2,"recipes":[{"id":"ag-health-lag","task":"Zustand und Verzögerung der Availability Group überwachen (Log Send / Redo Queue, RPO)","category":"Always On & HA","command":"-- Replica health (run on the primary to see all replicas):\nSELECT ag.name AS ag_name, ar.replica_server_name, ars.role_desc,\n       ar.availability_mode_desc, ar.failover_mode_desc,\n       ars.connected_state_desc, ars.synchronization_health_desc,\n       ars.operational_state_desc, ars.last_connect_error_description\nFROM   sys.availability_groups ag\n  JOIN sys.availability_replicas ar                ON ar.group_id = ag.group_id\n  JOIN sys.dm_hadr_availability_replica_states ars ON ars.replica_id = ar.replica_id\nORDER BY ag.name, ars.role_desc;\n\n-- Per-database lag: log send queue (RPO), redo queue (RTO):\nSELECT ag.name AS ag_name, ar.replica_server_name, DB_NAME(drs.database_id) AS db,\n       drs.is_primary_replica, drs.synchronization_state_desc, drs.synchronization_health_desc,\n       drs.is_suspended, drs.suspend_reason_desc,\n       drs.log_send_queue_size  AS log_send_queue_kb,\n       drs.log_send_rate        AS log_send_kb_s,\n       drs.redo_queue_size      AS redo_queue_kb,\n       drs.redo_rate            AS redo_kb_s,\n       CAST(drs.redo_queue_size * 1.0 / NULLIF(drs.redo_rate, 0) AS DECIMAL(18,1)) AS est_redo_catchup_s,\n       drs.last_commit_time,\n       DATEDIFF(SECOND, drs.last_commit_time, pri.last_commit_time) AS data_loss_window_s,\n       drs.secondary_lag_seconds\nFROM   sys.dm_hadr_database_replica_states drs\n  JOIN sys.availability_replicas ar ON ar.replica_id = drs.replica_id\n  JOIN sys.availability_groups ag   ON ag.group_id = drs.group_id\n  LEFT JOIN sys.dm_hadr_database_replica_states pri\n         ON pri.group_database_id = drs.group_database_id AND pri.is_primary_replica = 1\nORDER BY ag.name, db, drs.is_primary_replica DESC;\n\n-- WSFC quorum state:\nSELECT cluster_name, quorum_type_desc, quorum_state_desc FROM sys.dm_hadr_cluster;\nSELECT member_name, member_type_desc, member_state_desc, number_of_quorum_votes\nFROM   sys.dm_hadr_cluster_members;","what":"Zeigt Verbindungs- und Synchronisierungszustand der Replicas; pro Datenbank Log Send Queue, Redo Queue, Send-/Redo-Raten, geschätzte Redo-Aufholzeit und den Abstand des letzten Commits zum Primary (mögliches Datenverlustfenster); liefert außerdem den Quorum-Status des WSFC.","when":"Bei einem Alarm NOT_HEALTHY / NOT SYNCHRONIZING im AG-Dashboard, beim Prüfen von RPO/RTO vor einem geplanten Failover oder wenn Benutzer veraltete Daten auf einer lesbaren Secondary melden.","caution":"Eine wachsende log_send_queue ist die Logmenge, die bei Ausfall des Primary verloren geht (RPO); eine wachsende redo_queue bestimmt, wie lange die Datenbank nach dem Failover braucht, um online zu gehen (RTO). Auf einer Secondary ist nur die lokale Replica sichtbar; für das Gesamtbild auf dem Primary ausführen. data_loss_window_s ist nur bei laufendem Schreibverkehr aussagekräftig. Reine Leseabfrage; sicher.","since":"2016","note":"secondary_lag_seconds ist eine Spalte ab 2016. Wächst die Redo Queue weiter, prüfe, ob der Redo-Thread auf der Secondary blockiert ist (lange Leseabfragen, DDL), und achte auf Waits REDO_THREAD_PENDING_WORK / PARALLEL_REDO_*.","tags":["availability group","ag","log send queue","redo queue","synchronization","rpo"],"untested":true},{"id":"ag-forced-failover-dr","task":"Erzwungenes Failover (FORCE_FAILOVER_ALLOW_DATA_LOSS) und Wiederherstellung danach","category":"Always On & HA","command":"-- 1) On each candidate secondary: pick the most current one (highest end_of_log_lsn)\nSELECT ag.name AS ag_name, DB_NAME(drs.database_id) AS db,\n       drs.synchronization_state_desc, drs.end_of_log_lsn, drs.last_commit_time\nFROM   sys.dm_hadr_database_replica_states drs\n  JOIN sys.availability_groups ag ON ag.group_id = drs.group_id\nWHERE  drs.is_local = 1;\n\n-- 2) On the chosen replica (while the primary is unreachable):\nALTER AVAILABILITY GROUP [AG_Musteri] FORCE_FAILOVER_ALLOW_DATA_LOSS;\n\n-- 3) On the other secondaries, resume the suspended data movement:\nALTER DATABASE [Musteri] SET HADR RESUME;\n\n-- 4) When the old primary returns (now a SUSPENDED secondary):\n--    take a snapshot first to inspect the lost transactions, then resume\nCREATE DATABASE [Musteri_lost_snap]\n  ON (NAME = N'Musteri', FILENAME = N'D:\\Data\\Musteri_lost_snap.ss')\n  AS SNAPSHOT OF [Musteri];\nALTER DATABASE [Musteri] SET HADR RESUME;","what":"Macht bei vollständigem Verlust des Primary eine DR-Replica unter Inkaufnahme von Datenverlust zum Primary; setzt danach die angehaltene Datenbewegung auf den anderen Replicas fort und erstellt, wenn der alte Primary zurückkehrt, einen Snapshot, um Transaktionen zu untersuchen, die den neuen Primary nie erreicht haben.","when":"Wenn das primäre Rechenzentrum ausgefallen ist, nur eine asynchrone Replica übrig ist oder eine synchrone Replica nicht SYNCHRONIZED ist und trotzdem ein Failover nötig ist.","caution":"DATENVERLUST: Transaktionen nach der letzten gehärteten LSN gehen verloren. Hol dir zuerst die RPO-Freigabe vom Fachbereich. Nach einem erzwungenen Failover sind alle Secondary-Datenbanken SUSPENDED; das Log wird nicht gekürzt und wächst bis HADR RESUME weiter. Ist das WSFC-Quorum verloren, starte den Cluster zuerst mit erzwungenem Quorum (PowerShell: Start-ClusterNode -FixQuorum), sonst schlägt der Befehl fehl. HADR RESUME auf dem alten Primary verwirft die verlorenen Transaktionen endgültig; nicht fortsetzen, ohne den Snapshot erstellt zu haben.","note":"Für ein geplantes Failover ohne Datenverlust den FAILOVER-Befehl aus dem Rezept ag-failover verwenden. AGs mit CLUSTER_TYPE = NONE / EXTERNAL verwenden ebenfalls FORCE_FAILOVER_ALLOW_DATA_LOSS; bei EXTERNAL (Pacemaker) steuert der Cluster-Manager das Failover. Ein Snapshot braucht jede logische Datendatei der Quelle; bei Datenbanken mit mehreren Dateien einen ON-Eintrag pro Datendatei hinzufügen.","tags":["force failover","allow data loss","dr","availability group","hadr resume","data loss"],"untested":true},{"id":"ag-add-db-autoseed","task":"Datenbank zu einer AG hinzufügen (Automatic Seeding) und den Seeding-Fortschritt überwachen","category":"Always On & HA","command":"-- Prerequisite (primary): FULL recovery and at least one FULL backup\nSELECT name, recovery_model_desc FROM sys.databases WHERE name = N'Musteri';\nBACKUP DATABASE [Musteri] TO DISK = N'D:\\Backup\\Musteri_pre_ag.bak' WITH COMPRESSION, CHECKSUM, INIT;\n\n-- 1) Primary: switch the replica to automatic seeding\nALTER AVAILABILITY GROUP [AG_Musteri]\n  MODIFY REPLICA ON N'SQL02' WITH (SEEDING_MODE = AUTOMATIC);\n\n-- 2) On each secondary: allow the AG to create databases\nALTER AVAILABILITY GROUP [AG_Musteri] GRANT CREATE ANY DATABASE;\n\n-- 3) Primary: add the database\nALTER AVAILABILITY GROUP [AG_Musteri] ADD DATABASE [Musteri];\n\n-- 4) Progress (primary or secondary):\nSELECT local_database_name, role_desc, internal_state_desc,\n       transferred_size_bytes / 1048576         AS transferred_mb,\n       database_size_bytes / 1048576            AS db_mb,\n       transfer_rate_bytes_per_second / 1048576 AS mb_s,\n       estimate_time_complete_utc, failure_message\nFROM   sys.dm_hadr_physical_seeding_stats;\n\n-- 5) Seeding history and failure reason:\nSELECT s.start_time, s.completion_time, adc.database_name, ar.replica_server_name,\n       s.current_state, s.failure_state_desc, s.error_code, s.number_of_attempts\nFROM   sys.dm_hadr_automatic_seeding s\n  JOIN sys.availability_databases_cluster adc ON adc.group_database_id = s.ag_db_id\n  JOIN sys.availability_replicas ar           ON ar.replica_id = s.ag_remote_replica_id\nORDER BY s.start_time DESC;","what":"Fügt die Datenbank der AG hinzu, indem sie ohne manuelles Backup/Restore über den Endpoint (VDI) zu den Secondaries gestreamt wird; zeigt Seeding-Durchsatz, voraussichtliches Ende und Fehlergrund.","when":"Beim Hinzufügen einer neuen Datenbank zu einer AG, beim Hinzufügen einer neuen Replica oder beim Neuaufbau einer defekten Secondary-Datenbank.","caution":"Seeding lastet die Endpoint-Verbindung aus und ist standardmäßig unkomprimiert; bei sehr großen Datenbanken oder schwachem WAN ist ein manuelles Backup/Restore + JOIN besser planbar. Das Primary-Log wird während des Seedings nicht gekürzt (log_reuse_wait_desc = AVAILABILITY_REPLICA). Unter 2016 braucht die Secondary dieselben Daten-/Log-Ordnerpfade wie der Primary; ab 2017 nutzt die Secondary ihre eigenen Standardpfade, wenn die Primary-Dateien in den Standardpfaden der Instanz liegen. Bei einer TDE-Datenbank muss das Zertifikat zuerst auf jeder Secondary wiederhergestellt werden. Ohne GRANT CREATE ANY DATABASE auf der Secondary bleibt das Seeding hängen.","since":"2016","note":"Seeding-Kompression wird mit Trace Flag 9567 aktiviert (kostet CPU). Start, Abschluss und Fehler des Seedings werden in das SQL-Server-Error-Log geschrieben; für eine detaillierte Verfolgung eine eigene Extended-Events-Session mit den Events hadr_automatic_seeding_* und hadr_physical_seeding_* anlegen. Zurückschalten mit SEEDING_MODE = MANUAL.","tags":["automatic seeding","availability group","add database","seeding_mode","dm_hadr_physical_seeding_stats","ag"],"untested":true},{"id":"ag-listener-routing","task":"Listener, seine IPs und die Konfiguration des Read-Only Routing prüfen","category":"Always On & HA","command":"-- Listener, port and IP states:\nSELECT ag.name AS ag_name, l.dns_name, l.port, l.is_conformant,\n       ip.ip_address, ip.ip_subnet_mask, ip.state_desc\nFROM   sys.availability_group_listeners l\n  JOIN sys.availability_groups ag ON ag.group_id = l.group_id\n  LEFT JOIN sys.availability_group_listener_ip_addresses ip ON ip.listener_id = l.listener_id;\n\n-- Read-only routing list: where read intent goes when X is primary:\nSELECT ag.name AS ag_name, pr.replica_server_name AS when_primary_is,\n       rl.routing_priority, sr.replica_server_name AS route_to,\n       sr.read_only_routing_url, sr.secondary_role_allow_connections_desc\nFROM   sys.availability_read_only_routing_lists rl\n  JOIN sys.availability_replicas pr ON pr.replica_id = rl.replica_id\n  JOIN sys.availability_replicas sr ON sr.replica_id = rl.read_only_replica_id\n  JOIN sys.availability_groups ag   ON ag.group_id = pr.group_id\nORDER BY ag.name, when_primary_is, rl.routing_priority;\n\n-- Replica endpoint and connection settings:\nSELECT replica_server_name, endpoint_url, read_only_routing_url,\n       secondary_role_allow_connections_desc, seeding_mode_desc, session_timeout\nFROM   sys.availability_replicas;\n\n-- Routing test from the command line (-d is required with -K ReadOnly):\n-- sqlcmd -S tcp:AGL-Musteri,1433 -d Musteri -E -M -K ReadOnly -Q \"SELECT @@SERVERNAME\"","what":"Zeigt DNS-Namen und Port des Listeners sowie den Cluster-Status jeder Subnetz-IP; für jedes Primary-Szenario in der Read-Only-Routing-Liste, an welche Replica und URL Read-Intent-Verbindungen geleitet werden.","when":"Wenn Anwendungen sich nach einem Failover nicht verbinden können, Verbindungen mit ApplicationIntent=ReadOnly auf dem Primary landen oder ein neues Subnetz/eine neue Replica hinzugefügt wurde.","caution":"Read-Only Routing funktioniert nur für Verbindungen über den Listener mit ApplicationIntent=ReadOnly und expliziter Datenbank; direkte Verbindungen zu einem Instanznamen werden nicht geroutet. Eine Replica mit leerer read_only_routing_url kann kein Routing-Ziel sein. Bei einem Multi-Subnet-Listener erleben Clients ohne MultiSubnetFailover=True nach einem Failover Verbindungs-Timeouts von 20-30 s. Reine Leseabfrage; sicher.","note":"Für Lastverteilung ab 2016 nutzt die Routing-Liste verschachtelte Klammern: READ_ONLY_ROUTING_LIST = (('SQL02','SQL03'),'SQL01'). Zeigen Listener-IPs OFFLINE, prüfe die Ressourcenabhängigkeiten auf Cluster-Seite (Failover Cluster Manager / Get-ClusterResource).","tags":["listener","read-only routing","applicationintent","multisubnetfailover","availability group","connection"],"untested":true},{"id":"logshipping-monitor","task":"Status und Latenz des Log Shipping überwachen","category":"Always On & HA","command":"-- Summary (on the primary, secondary or monitor server):\nEXEC master.sys.sp_help_log_shipping_monitor;\n\n-- PRIMARY: when the last log backup was taken:\nSELECT p.primary_server, p.primary_database,\n       p.last_backup_date, p.last_backup_file,\n       DATEDIFF(MINUTE, p.last_backup_date, GETDATE()) AS backup_age_min,\n       p.backup_threshold                              AS threshold_min\nFROM   msdb.dbo.log_shipping_monitor_primary p;\n\n-- SECONDARY: copy and restore latency:\nSELECT s.secondary_server, s.secondary_database, s.primary_server,\n       s.last_copied_date, s.last_copied_file,\n       s.last_restored_date, s.last_restored_file,\n       s.last_restored_latency                           AS restore_latency_min,\n       DATEDIFF(MINUTE, s.last_restored_date, GETDATE()) AS since_last_restore_min,\n       s.restore_threshold                               AS threshold_min\nFROM   msdb.dbo.log_shipping_monitor_secondary s;\n\n-- Errors in the last 24 hours:\nSELECT e.database_name, e.agent_type, e.log_time, e.message\nFROM   msdb.dbo.log_shipping_monitor_error_detail e\nWHERE  e.log_time >= DATEADD(HOUR, -24, GETDATE())\nORDER BY e.log_time DESC;\n\n-- Secondary DB mode (RESTORING = NORECOVERY, is_in_standby = STANDBY):\nSELECT name, state_desc, is_in_standby FROM sys.databases WHERE name = N'Musteri';","what":"Zeigt das Alter des letzten Log-Backups auf dem Primary, die zuletzt kopierte/wiederhergestellte Datei und die Restore-Latenz auf der Secondary samt Schwellenwerten sowie die Fehler der Backup-/Copy-/Restore-Jobs der letzten 24 Stunden.","when":"Wenn der Log-Shipping-Alert-Job auslöst (Fehler 14420/14421), vor einem DR-Test oder um zu berichten, wie weit die Secondary zurückliegt.","caution":"Monitor-Tabellen enthalten nur die Seite, die der Server sieht: Die Primary-Tabelle wird auf Primary/Monitor gefüllt, die Secondary-Tabelle auf Secondary/Monitor. Ein manuelles Log-Backup (außerhalb des Log Shipping) unterbricht die Kette, und der Restore-Job schlägt fortlaufend fehl; nutze für Ad-hoc-Log-Backups COPY_ONLY. Im STANDBY-Modus werden Benutzerverbindungen vor jedem Restore getrennt. Reine Leseabfrage; sicher.","note":"Datumsspalten sind in lokaler Serverzeit; nutze die *_utc-Spalten, wenn du Server in verschiedenen Zeitzonen vergleichst. Protokolliert der Restore-Job \"skipping log backup file\", wurde diese Datei bereits angewendet; um eine echte Lücke zu finden, prüfe die Abfolge von FirstLSN/LastLSN mit RESTORE HEADERONLY auf der Secondary.","tags":["log shipping","sp_help_log_shipping_monitor","restore latency","dr","secondary","latency"],"untested":true},{"id":"backup-copy-only","task":"Ad-hoc-Backup erstellen, ohne die Kette zu unterbrechen (COPY_ONLY, Striping, Durchsatz)","category":"Backup & Log","command":"-- Ad-hoc FULL that does not reset the differential base:\nBACKUP DATABASE [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_copyonly_20261008.bak'\nWITH COPY_ONLY, COMPRESSION, CHECKSUM, INIT, STATS = 10;\n\n-- Ad-hoc log backup that does not truncate the log:\nBACKUP LOG [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_copyonly_20261008.trn'\nWITH COPY_ONLY, COMPRESSION, CHECKSUM, INIT;\n\n-- Large DB: stripe across files and tune buffers:\nBACKUP DATABASE [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_s1.bak', DISK = N'E:\\Backup\\Musteri_s2.bak'\nWITH COPY_ONLY, COMPRESSION, CHECKSUM, INIT,\n     MAXTRANSFERSIZE = 4194304, BUFFERCOUNT = 64, STATS = 5;","what":"Erstellt ein eigenständiges FULL- oder Log-Backup, das die reguläre Backup-Kette (Differential-Basis und Log-Kette) nicht beeinflusst; bei großen Datenbanken verkürzen Striping über mehrere Dateien und eine größere Transfergröße das Backup.","when":"Bei der Übergabe einer Kopie an Test/Dev, vor einer riskanten Änderung oder wenn für ein Audit ein Backup angefordert wird; immer dann, wenn du ein Backup brauchst, ohne die geplante Backup-Strategie anzutasten.","caution":"Ein Ad-hoc-FULL ohne COPY_ONLY wird zur Basis der folgenden DIFF-Backups; wird diese Datei gelöscht, sind die DIFFs nutzlos. COPY_ONLY hat zusammen mit DIFFERENTIAL keine Wirkung. Das Wiederherstellen eines gestripten Backups braucht jede Datei; fehlt eine, ist das ganze Backup unbrauchbar. BUFFERCOUNT x MAXTRANSFERSIZE an Speicher wird außerhalb des Buffer Pools belegt (256 MB im Beispiel); auf einem Server unter Speicherdruck reduzieren.","note":"Bei einer TDE-Datenbank erfordert die Backup-Kompression eine MAXTRANSFERSIZE größer als 65536 (2016+). Server-Standardwerte: sp_configure 'backup compression default' und 'backup checksum default'. Unter 2022 mit Intel-QAT-Hardware ist WITH COMPRESSION (ALGORITHM = QAT_DEFLATE) verfügbar.","tags":["copy_only","ad-hoc backup","stripe","maxtransfersize","buffercount","backup"]},{"id":"backup-to-url","task":"Backup auf URL (Azure Blob SAS / S3-kompatibler Speicher)","category":"Backup & Log","command":"-- A) Azure Blob: SAS credential (named after the container URL, SECRET without a leading '?')\nCREATE CREDENTIAL [https://dbabackup.blob.core.windows.net/sql]\nWITH IDENTITY = N'SHARED ACCESS SIGNATURE',\n     SECRET   = N'sv=2022-11-02&ss=b&srt=co&sp=rwdlac&se=2027-01-01T00:00:00Z&sig=XXXX';\n\nBACKUP DATABASE [Musteri]\nTO URL = N'https://dbabackup.blob.core.windows.net/sql/Musteri_full_1.bak',\n   URL = N'https://dbabackup.blob.core.windows.net/sql/Musteri_full_2.bak'\nWITH COMPRESSION, CHECKSUM, MAXTRANSFERSIZE = 4194304, BLOCKSIZE = 65536, STATS = 10;\n\n-- B) S3-compatible storage (2022+):\nCREATE CREDENTIAL [s3://s3.eu-central-1.amazonaws.com/dba-backup/sql]\nWITH IDENTITY = N'S3 Access Key',\n     SECRET   = N'<AccessKeyID>:<SecretKeyID>';\n\nBACKUP DATABASE [Musteri]\nTO URL = N's3://s3.eu-central-1.amazonaws.com/dba-backup/sql/Musteri_full.bak'\nWITH COMPRESSION, CHECKSUM, FORMAT, STATS = 10;\n\n-- Verify (uses the same credential):\nRESTORE VERIFYONLY\nFROM URL = N's3://s3.eu-central-1.amazonaws.com/dba-backup/sql/Musteri_full.bak'\nWITH CHECKSUM;","what":"Schreibt das Backup direkt in Object Storage: als Block Blobs mit SAS-Credential nach Azure Blob oder unter 2022 mit Access-Key-Credential in S3-API-kompatiblen Speicher (AWS S3, MinIO usw.). Das Credential wird automatisch über das passende URL-Präfix ausgewählt.","when":"Für Offsite-Backup-Anforderungen, Cloud-Migration/hybride DR-Szenarien oder wenn der lokale Backup-Speicher nicht ausreicht.","caution":"Auf Azure hält ein einzelner Block Blob höchstens 50.000 Blöcke; bei 4 MB MAXTRANSFERSIZE sind das etwa 195 GB pro Datei, verteile große Datenbanken daher per Striping auf mehrere URLs. Auf S3 hat eine Datei höchstens 10.000 Parts; erhöhe bei großen Datenbanken MAXTRANSFERSIZE (bis 20 MB, erfordert COMPRESSION) und verteile auf mehrere URLs (bis 64). S3 unterstützt kein INIT/Anhängen; FORMAT überschreibt ein vorhandenes Backup mit demselben Namen. Läuft die SAS ab, schlagen Backup-Jobs mit einem Fehler fehl; trage das Ablaufdatum in den Kalender ein. Das Credential-Secret wird in master gespeichert; Principals mit CONTROL SERVER können es verwenden.","since":"2022","note":"Block-Blob-Backup mit SAS gibt es ab 2016, S3-Unterstützung ab 2022. Die alte Methode mit Storage-Account-Key (IDENTITY = Kontoname) erzeugt Page Blobs und wird nicht empfohlen. Für S3 muss der Server dem TLS-Zertifikat des Endpoints vertrauen; bei On-Prem-Lösungen wie MinIO gehört der Port in die URL (s3://minio.local:9000/bucket/...).","tags":["backup to url","azure blob","s3","credential","sas","cloud backup"],"untested":true},{"id":"backup-history-msdb","task":"Backup-Historie aus msdb und Prüfung auf fehlende Backups","category":"Backup & Log","command":"-- Last FULL / DIFF / LOG per database (never backed up first):\nSELECT d.name AS db,\n       d.recovery_model_desc,\n       MAX(CASE WHEN b.type = 'D' THEN b.backup_finish_date END) AS last_full,\n       MAX(CASE WHEN b.type = 'I' THEN b.backup_finish_date END) AS last_diff,\n       MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END) AS last_log,\n       DATEDIFF(MINUTE, MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END), GETDATE()) AS log_age_min\nFROM   sys.databases d\n  LEFT JOIN msdb.dbo.backupset b\n         ON b.database_name = d.name AND b.is_copy_only = 0\nWHERE  d.name <> N'tempdb'\nGROUP BY d.name, d.recovery_model_desc\nORDER BY last_full;\n\n-- Chain for one DB over the last 7 days (LSN, size, file):\nSELECT b.backup_start_date, b.backup_finish_date, b.type, b.is_copy_only,\n       b.first_lsn, b.last_lsn, b.database_backup_lsn,\n       CAST(b.backup_size / 1048576.0 AS DECIMAL(12,1))            AS size_mb,\n       CAST(b.compressed_backup_size / 1048576.0 AS DECIMAL(12,1)) AS compressed_mb,\n       b.has_backup_checksums, mf.physical_device_name\nFROM   msdb.dbo.backupset b\n  JOIN msdb.dbo.backupmediafamily mf ON mf.media_set_id = b.media_set_id\nWHERE  b.database_name = N'Musteri'\n  AND  b.backup_start_date >= DATEADD(DAY, -7, GETDATE())\nORDER BY b.backup_start_date;","what":"Listet für jede Datenbank den Zeitpunkt des letzten FULL-/DIFF-/LOG-Backups und das Alter des Log-Backups; für eine Datenbank die Backup-Kette mit LSN-Abfolge, komprimierter Größe, Checksum-Flag und Dateipfad.","when":"Beim morgendlichen Check, um herauszufinden, welche Dateien ein Restore braucht, um in einem Audit \"wann war das letzte Backup\" zu beantworten oder um bei einem Log-voll-Alarm zu sehen, ob Log-Backups laufen.","caution":"msdb kennt nur Backups dieser Instanz; Backups auf einem anderen Server oder einer anderen AG-Replica sind nicht sichtbar (in einer AG jede Replica prüfen). Die first_lsn eines Log-Backups muss der last_lsn des vorherigen entsprechen; eine Lücke bedeutet, dass die Kette unterbrochen ist. Bei Recovery Model FULL bedeutet ein NULL in last_log, dass das Log nie gesichert wird und weiter wächst. Reine Leseabfrage; sicher.","note":"Die msdb-Historie wächst unbegrenzt und verlangsamt Backup-Jobs; bereinige sie regelmäßig mit EXEC msdb.dbo.sp_delete_backuphistory @oldest_date = '2026-01-01';. Werte von type: D = FULL, I = DIFF, L = LOG, F = Datei/Dateigruppe.","tags":["backupset","msdb","backup history","last backup","lsn","backup chain"]},{"id":"backup-restore-progress","task":"Fortschritt und voraussichtliches Ende von Backup / Restore / CHECKDB anzeigen","category":"Backup & Log","command":"SELECT r.session_id, r.command, DB_NAME(r.database_id) AS db,\n       CAST(r.percent_complete AS DECIMAL(5,1))                     AS pct,\n       DATEADD(SECOND, r.estimated_completion_time / 1000, GETDATE()) AS eta,\n       r.total_elapsed_time / 60000                                 AS elapsed_min,\n       r.wait_type, r.wait_time, r.blocking_session_id\nFROM   sys.dm_exec_requests r\nWHERE  r.percent_complete > 0;\n\n-- Restore phases and throughput are in the error log:\nEXEC sys.xp_readerrorlog 0, 1, N'Restore';","what":"Zeigt für lange Operationen, die percent_complete melden (BACKUP, RESTORE, DBCC CHECKDB/SHRINK, ALTER INDEX REORGANIZE, Rollback nach KILL), den prozentualen Fortschritt, die voraussichtliche Endzeit und den aktuellen Wait.","when":"Wenn das nächtliche Backup in die Geschäftszeit hineinläuft, um bei einem großen Restore \"wann ist es fertig\" zu beantworten oder um das Rollback einer beendeten Session zu verfolgen.","caution":"Die Schätzung geht von linearem Fortschritt aus; in den Redo-/Undo-Phasen eines Restores und während der Null-Initialisierung des Logs kann der Prozentwert lange stehen bleiben. Logdateien profitieren nicht von Instant File Initialization (außer Vergrößerungen bis 64 MB unter 2022); Restores mit großen Logs verbringen lange Zeit in der ersten Phase. Reine Leseabfrage; sicher.","note":"Ist wait_type überwiegend BACKUPIO / ASYNC_IO_COMPLETION, ist die Ziel-/Quellplatte der Engpass; hohe BACKUPTHREAD-Werte oder CPU deuten auf Kompressionskosten hin. Um die lange Nullungsphase eines Restores im Error Log detailliert zu sehen, die Trace Flags 3004 + 3605 verwenden.","tags":["percent_complete","backup progress","restore progress","eta","dm_exec_requests","progress"]},{"id":"tail-log-backup","task":"Vor einem Restore ein Tail-Log-Backup erstellen (NORECOVERY / NO_TRUNCATE)","category":"Restore & Recovery","command":"-- A) Healthy DB: back up the tail and put the DB into RESTORING (no new writes):\nBACKUP LOG [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_tail.trn'\nWITH NORECOVERY, CHECKSUM, INIT;\n\n-- B) Damaged DB / data file lost, log file intact:\n-- BACKUP LOG [Musteri]\n-- TO DISK = N'D:\\Backup\\Musteri_tail_damaged.trn'\n-- WITH NO_TRUNCATE, CONTINUE_AFTER_ERROR, INIT;\n\nSELECT name, state_desc FROM sys.databases WHERE name = N'Musteri';","what":"Sichert den seit dem letzten Log-Backup geschriebenen Teil des Logs (den Tail), damit die Restore-Kette bis zum Zeitpunkt des Ausfalls reicht und die Transaktionen der letzten Minuten nicht verloren gehen.","when":"Vor einem Restore über dieselbe Datenbank, wenn die Platte der Datendatei ausfällt, oder beim finalen Cutover einer Migration.","caution":"NORECOVERY versetzt die Datenbank in RESTORING und beendet den Benutzerzugriff; nicht auf der falschen Datenbank ausführen. Ein Restore WITH REPLACE ohne Tail-Log-Backup verwirft die Transaktionen nach dem letzten Log-Backup endgültig. Ein mit NO_TRUNCATE erstelltes Backup kann unvollständige Metadaten enthalten; führe nach dem Restore DBCC CHECKDB aus. Im Recovery Model SIMPLE ist kein Tail-Log-Backup möglich.","note":"Um die Datenbank ohne Restore aus RESTORING zurückzuholen: RESTORE DATABASE [Musteri] WITH RECOVERY;. NO_TRUNCATE entspricht COPY_ONLY + CONTINUE_AFTER_ERROR und versucht das Log auch dann zu lesen, wenn die Datenbank nicht geöffnet werden kann.","tags":["tail-log","backup log","norecovery","no_truncate","restore","recovery"]},{"id":"restore-pitr-stopat","task":"Auf einen Zeitpunkt wiederherstellen (Point-in-Time-Restore, STOPAT)","category":"Restore & Recovery","command":"-- Restore under a side name, keep the original intact:\nRESTORE DATABASE [Musteri_PITR]\nFROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak'\nWITH MOVE N'Musteri'     TO N'D:\\Data\\Musteri_PITR.mdf',\n     MOVE N'Musteri_log' TO N'D:\\Log\\Musteri_PITR_log.ldf',\n     NORECOVERY, STATS = 10;\n\nRESTORE DATABASE [Musteri_PITR]\nFROM DISK = N'D:\\Backup\\Musteri_diff_20261008.bak' WITH NORECOVERY;\n\n-- All log backups in order, each with the same STOPAT:\nRESTORE LOG [Musteri_PITR] FROM DISK = N'D:\\Backup\\Musteri_log_1200.trn'\nWITH NORECOVERY, STOPAT = '2026-10-08T14:29:00';\nRESTORE LOG [Musteri_PITR] FROM DISK = N'D:\\Backup\\Musteri_log_1500.trn'\nWITH NORECOVERY, STOPAT = '2026-10-08T14:29:00';\n\nRESTORE DATABASE [Musteri_PITR] WITH RECOVERY;\n\n-- Alternative: step forward with STANDBY and read data at each point:\n-- RESTORE LOG [Musteri_PITR] FROM DISK = N'...' WITH STANDBY = N'D:\\Backup\\Musteri_PITR_undo.dat', STOPAT = '...';","what":"Wendet FULL + (optional) DIFF + Log-Backups WITH NORECOVERY an und stoppt das Einspielen des Logs zum angegebenen Zeitpunkt; versetzt die Datenbank auf den Stand unmittelbar vor der fehlerhaften Operation (falsches DELETE/UPDATE, DROP).","when":"Um versehentlich gelöschte/geänderte Daten zurückzuholen, die Daten zu einem bestimmten Zeitpunkt zu untersuchen oder für ein Audit.","caution":"Unter einem anderen Namen wiederherstellen statt über das Original, dann die benötigten Zeilen zurückkopieren; musst du darüber wiederherstellen, erstelle zuerst ein Tail-Log-Backup. STOPAT ist in lokaler Serverzeit. Das Recovery Model muss FULL sein; enthält das Log-Backup mit dem STOPAT-Zeitpunkt minimal protokollierte Operationen unter BULK_LOGGED, kannst du nicht innerhalb dieses Backups stoppen. Nach RESTORE ... WITH RECOVERY lassen sich keine weiteren Logs mehr anwenden.","note":"STOPAT bei jedem Log-Restore anzugeben ist das empfohlene Muster; die Info-Meldung \"contains records that were logged before the designated point in time\" bei früheren Dateien ist normal. Um an einer benannten Markierung (BEGIN TRAN ... WITH MARK) zu stoppen, STOPATMARK / STOPBEFOREMARK verwenden. Den genauen Zeitpunkt findest du mit fn_dump_dblog oder Anwendungslogs.","tags":["stopat","point-in-time","restore log","norecovery","standby","rollback to time"]},{"id":"restore-verify-header","task":"Backup-Datei untersuchen und verifizieren (VERIFYONLY, HEADERONLY, FILELISTONLY)","category":"Restore & Recovery","command":"-- Readability and page checksum verification:\nRESTORE VERIFYONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak' WITH CHECKSUM;\n\n-- Backup sets in the file (type, LSN, date, version, compression):\nRESTORE HEADERONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak';\n\n-- Logical file names and sizes (needed for MOVE):\nRESTORE FILELISTONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak';\n\n-- Media info (stripe count: FamilyCount):\nRESTORE LABELONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak';","what":"Prüft ohne Restore, ob das Backup lesbar ist und seine Seiten-Checksums gültig sind; zeigt die enthaltenen Backup-Sets (Position, BackupType, FirstLSN/LastLSN, DatabaseVersion), die logischen Dateinamen und Stripe-Informationen.","when":"Vor dem Restore eines von einem anderen Team erhaltenen Backups, um Dateinamen für MOVE zu ermitteln, als schnelle Prüfung nach einem Backup-Job oder um das richtige FILE = n zu wählen, wenn eine Datei mehrere Sets enthält.","caution":"VERIFYONLY garantiert nicht, dass die Datenbank wiederherstellbar und konsistent ist; es prüft nur die Backup-Struktur und (falls WITH CHECKSUM erstellt) die Seiten-Checksums. Echte Sicherheit geben nur regelmäßige Test-Restores + DBCC CHECKDB. Ein Backup einer neueren Version lässt sich auf einer älteren nicht wiederherstellen (DatabaseVersion prüfen). Leseoperationen; sicher.","note":"Zeigt HEADERONLY HasBackupChecksums = 0, wurde das Backup ohne CHECKSUM erstellt, und VERIFYONLY WITH CHECKSUM kann die Seiten nicht validieren. In ohne INIT geschriebenen Dateien sammeln sich mehrere Sets; beim Restore WITH FILE = <Position> angeben.","tags":["restore verifyonly","headeronly","filelistonly","labelonly","backup verification","checksum"]},{"id":"page-restore-suspect","task":"Beschädigte Seiten finden (suspect_pages) und einzelne Seiten wiederherstellen","category":"Restore & Recovery","command":"-- Pages that hit 823/824 or checksum errors:\nSELECT DB_NAME(sp.database_id) AS db, sp.file_id, sp.page_id,\n       CASE sp.event_type WHEN 1 THEN '823/824 error' WHEN 2 THEN 'bad checksum'\n            WHEN 3 THEN 'torn page'     WHEN 4 THEN 'restored'\n            WHEN 5 THEN 'repaired'      WHEN 7 THEN 'deallocated by DBCC' END AS event_desc,\n       sp.error_count, sp.last_update_date\nFROM   msdb.dbo.suspect_pages sp\nORDER BY sp.last_update_date DESC;\n\n-- Page restore (example 1:336): FULL + later logs + a new log backup\nRESTORE DATABASE [Musteri] PAGE = '1:336'\nFROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak' WITH NORECOVERY;\nRESTORE LOG [Musteri] FROM DISK = N'D:\\Backup\\Musteri_log_1200.trn' WITH NORECOVERY;\nBACKUP LOG [Musteri] TO DISK = N'D:\\Backup\\Musteri_log_pagetail.trn' WITH INIT;\nRESTORE LOG [Musteri] FROM DISK = N'D:\\Backup\\Musteri_log_pagetail.trn' WITH RECOVERY;\n\n-- Verify:\nDBCC CHECKDB (N'Musteri') WITH NO_INFOMSGS, ALL_ERRORMSGS;","what":"Listet aus msdb.dbo.suspect_pages Seiten mit I/O-Fehlern (823/824), Checksum- oder Torn-Page-Fehlern; repariert einzelne beschädigte Seiten, indem sie aus dem FULL-Backup geholt und mit Logs vorwärtsgerollt werden, ohne die ganze Datenbank wiederherzustellen.","when":"Bei Fehlern 823/824 im Error Log, bei auf wenige Seiten begrenzten Konsistenzfehlern in CHECKDB oder wenn eine Benutzerabfrage mit \"incorrect checksum\" fehlschlägt.","caution":"Erfordert Recovery Model FULL oder BULK_LOGGED und eine ununterbrochene Log-Kette. Online Page Restore gibt es nur in Enterprise/Developer; in anderen Editionen ist die Datenbank während des Restores offline. Allokationsseiten (GAM/SGAM/PFS), Dateiheader und die Boot Page (1:9) lassen sich so nicht wiederherstellen. 823/824 bedeutet meist ein Speicherproblem; schließe den Fall nicht, ohne Platte/Controller/SAN zu prüfen, sonst tritt er erneut auf. Bei vielen defekten Seiten ist ein vollständiger Datenbank-Restore schneller.","note":"suspect_pages hält höchstens 1.000 Zeilen; ist die Tabelle voll, werden neue Fehler nicht erfasst, lösche daher alte Zeilen (event_type 4/5/7). Mehrere Seiten werden durch Kommas getrennt: PAGE = '1:336, 1:337'. In einer AG wird eine beschädigte Seite auf dem Primary per Automatic Page Repair (sys.dm_hadr_auto_page_repair) automatisch von einer Secondary repariert.","tags":["suspect_pages","page restore","823","824","corruption","bad page"]},{"id":"top-queries-cpu","task":"Die teuersten Abfragen im Plan Cache finden (CPU, Reads, Dauer)","category":"Performance","command":"SELECT TOP (20)\n       qs.query_hash,\n       SUM(qs.execution_count)                                       AS execs,\n       SUM(qs.total_worker_time) / 1000                              AS total_cpu_ms,\n       SUM(qs.total_worker_time) / SUM(qs.execution_count) / 1000    AS avg_cpu_ms,\n       SUM(qs.total_elapsed_time) / SUM(qs.execution_count) / 1000   AS avg_ms,\n       SUM(qs.total_logical_reads) / SUM(qs.execution_count)         AS avg_reads,\n       SUM(qs.total_grant_kb) / SUM(qs.execution_count)              AS avg_grant_kb,\n       COUNT(DISTINCT qs.query_plan_hash)                            AS plan_count,\n       MAX(qs.last_execution_time)                                   AS last_exec,\n       MIN(SUBSTRING(st.text, qs.statement_start_offset / 2 + 1,\n           ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text)\n             ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1)) AS statement_text,\n       MIN(DB_NAME(pa.dbid))                                         AS db\nFROM   sys.dm_exec_query_stats qs\n  CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st\n  OUTER APPLY (SELECT CONVERT(int, value) AS dbid FROM sys.dm_exec_plan_attributes(qs.plan_handle)\n               WHERE attribute = N'dbid') pa\nGROUP BY qs.query_hash\nORDER BY total_cpu_ms DESC;\n\n-- To rank by reads: ORDER BY SUM(qs.total_logical_reads) DESC\n-- Plan: SELECT query_plan FROM sys.dm_exec_query_plan(<plan_handle>);","what":"Gruppiert Anweisungen im Plan Cache nach query_hash (Ad-hoc-Abfragen, die sich nur in Literalen unterscheiden, fallen in eine Zeile zusammen) und liefert Gesamt- und Durchschnittswerte für CPU, Dauer, logische Reads, Memory Grant und Planzahl.","when":"Wenn die CPU am Anschlag ist, bei der Beschwerde \"der Server ist langsam\", für Datenbanken ohne Query Store oder wenn du eine serverweite Sicht über alle Datenbanken brauchst.","caution":"Sieht nur Pläne, die noch im Cache sind: Abfragen, deren Pläne durch Neustart, DBCC FREEPROCCACHE, Speicherdruck oder RECOMPILE verdrängt wurden, fehlen. Für dauerhafte Analyse mit Zeitfenstern Query Store verwenden. plan_count > 1 deutet auf Parameter-Sensitivität oder unterschiedliche SET-Optionen hin. Bei einem sehr großen Plan Cache kann die Abfrage einige Sekunden dauern; sicher.","since":"2016","note":"total_grant_kb ist eine Spalte ab 2016. Für Summen pro Stored Procedure sys.dm_exec_procedure_stats verwenden.","tags":["dm_exec_query_stats","top queries","cpu","query_hash","plan cache","expensive query"]},{"id":"wait-stats-top","task":"Wait-Statistiken: Worauf der Server wartet","category":"Performance","command":"WITH w AS (\n    SELECT wait_type, waiting_tasks_count,\n           wait_time_ms / 1000.0                         AS wait_s,\n           (wait_time_ms - signal_wait_time_ms) / 1000.0 AS resource_s,\n           signal_wait_time_ms / 1000.0                  AS signal_s\n    FROM   sys.dm_os_wait_stats\n    WHERE  waiting_tasks_count > 0\n      AND  wait_type NOT LIKE N'SLEEP[_]%'\n      AND  wait_type NOT IN (\n           N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH',\n           N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT',\n           N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE', N'FT_IFTS_SCHEDULER_IDLE_WAIT',\n           N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_WORK_QUEUE', N'LAZYWRITER_SLEEP',\n           N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE', N'PVS_PREALLOCATE', N'REQUEST_FOR_DEADLOCK_SEARCH',\n           N'SNI_HTTP_ACCEPT', N'SOS_WORK_DISPATCHER', N'SP_SERVER_DIAGNOSTICS_SLEEP',\n           N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'WAITFOR',\n           N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT', N'XE_LIVE_TARGET_TVF', N'PREEMPTIVE_XE_DISPATCHER',\n           N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_ASYNC_QUEUE', N'QDS_SHUTDOWN_QUEUE',\n           N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'PWAIT_ALL_COMPONENTS_INITIALIZED',\n           N'PWAIT_EXTENSIBILITY_CLEANUP_TASK', N'STARTUP_DEPENDENCY_MANAGER', N'WAIT_XTP_CKPT_CLOSE')\n)\nSELECT TOP (20) wait_type, waiting_tasks_count,\n       CAST(wait_s AS DECIMAL(18,1))                                AS wait_s,\n       CAST(100.0 * wait_s / SUM(wait_s) OVER () AS DECIMAL(5,1))   AS pct,\n       CAST(resource_s AS DECIMAL(18,1))                            AS resource_s,\n       CAST(signal_s AS DECIMAL(18,1))                              AS signal_s,\n       CAST(1000.0 * wait_s / waiting_tasks_count AS DECIMAL(18,2)) AS avg_wait_ms\nFROM   w\nORDER BY wait_s DESC;\n\n-- Values accumulate since:\nSELECT sqlserver_start_time FROM sys.dm_os_sys_info;\n\n-- User requests waiting right now:\nSELECT wt.session_id, wt.wait_type, wt.wait_duration_ms, wt.resource_description, wt.blocking_session_id\nFROM   sys.dm_os_waiting_tasks wt\n  JOIN sys.dm_exec_sessions s ON s.session_id = wt.session_id\nWHERE  s.is_user_process = 1;","what":"Filtert harmlose Hintergrund-Waits heraus und schlüsselt die gesamte Wartezeit des Servers nach Wait-Typ mit Prozentanteil und dem Anteil Resource vs. Signal (CPU-Warteschlange) auf; zeigt außerdem Benutzeranfragen, die gerade warten.","when":"Als erster Schritt einer Performance-Analyse, um den Engpass einzuordnen (I/O, CPU, Sperren, Log, Speicher, Parallelität); für Baseline-Vergleiche vorher/nachher.","caution":"Die Werte kumulieren seit dem Neustart; für ein aktuelles Problem zwei Snapshots erstellen und das Delta vergleichen. DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR) setzt die Zähler für den ganzen Server zurück und stört Monitoring-Tools; vermeide es. Interpretation: PAGEIOLATCH_* = Plattenlesevorgänge, WRITELOG = Log-Platte, LCK_M_* = Blockierung, CXPACKET/CXCONSUMER = Parallelität, RESOURCE_SEMAPHORE = Memory Grants, SOS_SCHEDULER_YIELD + hoher Signal-Anteil = CPU-Druck. Reine Leseabfrage; sicher.","note":"Für Waits pro Session sys.dm_exec_session_wait_stats (2016+) verwenden; für Wait-Kategorien pro Abfrage in Query Store sys.query_store_wait_stats (2017+).","tags":["wait stats","dm_os_wait_stats","waits","pageiolatch","writelog","performance"]},{"id":"deadlock-system-health","task":"Vergangene Deadlocks aus der system_health-Session lesen (Deadlock-Graph)","category":"Performance","command":"SET QUOTED_IDENTIFIER ON;  -- required for XML methods (sqlcmd/Agent default is OFF)\nWITH dl AS (\n    SELECT CAST(event_data AS XML) AS x\n    FROM   sys.fn_xe_file_target_read_file(N'system_health*.xel', NULL, NULL, NULL)\n    WHERE  object_name = N'xml_deadlock_report'\n)\nSELECT TOP (20)\n       x.value('(event/@timestamp)[1]', 'datetime2')                                          AS utc_time,\n       x.value('(event/data/value/deadlock/victim-list/victimProcess/@id)[1]', 'varchar(50)')  AS victim,\n       x.value('count(event/data/value/deadlock/process-list/process)', 'int')                 AS process_count,\n       x.value('(event/data/value/deadlock/process-list/process/@currentdbname)[1]', 'sysname') AS db,\n       x.query('event/data/value/deadlock')                                                    AS deadlock_graph\nFROM   dl\nORDER BY utc_time DESC;","what":"Liest xml_deadlock_report-Events aus dem event_file-Target der Extended-Events-Session system_health (standardmäßig aktiv); liefert für jeden Deadlock Zeitpunkt (UTC), Opferprozess, Anzahl der Beteiligten, Datenbank und das vollständige Deadlock-Graph-XML.","when":"Wenn die Anwendung Fehler 1205 meldet, beim Analysieren der Deadlock-Häufigkeit und der beteiligten Objekte; auf Servern ohne Trace Flag 1222.","caution":"system_health-Dateien rotieren; auf einem ausgelasteten Server sind ältere Deadlocks eventuell weg, richte daher für ein dauerhaftes Archiv eine eigene XE-Session ein. Das Lesen vieler Dateien kostet CPU; behalte TOP während der Geschäftszeit bei. Events werden verzögert geschrieben, ein neuer Deadlock erscheint also nach einigen Sekunden. Nur Lesen; sicher.","note":"Klicke auf den Wert deadlock_graph und speichere ihn als .xdl, um ihn in SSMS grafisch zu öffnen. Standard für das event_file von system_health ist 10 x 100 MB ab 2016 SP2 CU12 / 2017 CU20 / 2019 CU3 (Standard/Enterprise); 4 x 5 MB bei älteren Builds und anderen Editionen. Im Graph sind inputbuf, waitresource (KEY/PAGE/OBJECT) und lockMode die wichtigsten Felder; die meisten Deadlocks lassen sich mit einem fehlenden Index oder einer einheitlichen Zugriffsreihenfolge beheben.","tags":["deadlock","system_health","xml_deadlock_report","extended events","1205","deadlock graph"]},{"id":"xe-long-query-session","task":"Extended-Events-Session anlegen: langsame Abfragen in eine Datei schreiben und auslesen","category":"Performance","command":"SET QUOTED_IDENTIFIER ON;\nCREATE EVENT SESSION [dba_long_queries] ON SERVER\nADD EVENT sqlserver.sql_statement_completed (\n    ACTION (sqlserver.client_app_name, sqlserver.client_hostname, sqlserver.database_name,\n            sqlserver.session_id, sqlserver.sql_text, sqlserver.username, sqlserver.query_hash)\n    WHERE (duration >= 1000000 AND sqlserver.is_system = 0)),   -- 1 s (microseconds)\nADD EVENT sqlserver.rpc_completed (\n    ACTION (sqlserver.client_app_name, sqlserver.client_hostname, sqlserver.database_name,\n            sqlserver.session_id, sqlserver.sql_text, sqlserver.username, sqlserver.query_hash)\n    WHERE (duration >= 1000000 AND sqlserver.is_system = 0))\nADD TARGET package0.event_file (SET filename = N'D:\\XE\\dba_long_queries.xel',\n                                    max_file_size = 256, max_rollover_files = 4)\nWITH (MAX_MEMORY = 8 MB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,\n      MAX_DISPATCH_LATENCY = 15 SECONDS, STARTUP_STATE = OFF);\n\nALTER EVENT SESSION [dba_long_queries] ON SERVER STATE = START;\n\n-- Read:\nSELECT x.value('(event/@name)[1]', 'sysname')                                AS event_name,\n       x.value('(event/@timestamp)[1]', 'datetime2')                         AS utc_time,\n       x.value('(event/data[@name=\"duration\"]/value)[1]', 'bigint') / 1000   AS duration_ms,\n       x.value('(event/data[@name=\"cpu_time\"]/value)[1]', 'bigint') / 1000   AS cpu_ms,\n       x.value('(event/data[@name=\"logical_reads\"]/value)[1]', 'bigint')     AS logical_reads,\n       x.value('(event/action[@name=\"database_name\"]/value)[1]', 'sysname')  AS db,\n       x.value('(event/action[@name=\"client_app_name\"]/value)[1]', 'nvarchar(256)') AS app,\n       x.value('(event/action[@name=\"sql_text\"]/value)[1]', 'nvarchar(max)') AS sql_text\nFROM  (SELECT CAST(event_data AS XML) AS x\n       FROM sys.fn_xe_file_target_read_file(N'D:\\XE\\dba_long_queries*.xel', NULL, NULL, NULL)) t\nORDER BY duration_ms DESC;\n\n-- When done:\n-- ALTER EVENT SESSION [dba_long_queries] ON SERVER STATE = STOP;\n-- DROP EVENT SESSION [dba_long_queries] ON SERVER;","what":"Legt eine leichtgewichtige Extended-Events-Session an, die Anweisungen und RPC-Aufrufe über 1 Sekunde mit Anwendung, Host, Benutzer, Datenbank und SQL-Text auf Platte schreibt; wandelt die .xel-Dateien per T-SQL in Zeilen um.","when":"Bei Beschwerden über sporadische Langsamkeit, wenn du Client-Details (App/Host) brauchst, die Query Store nicht zeigt, und als Ersatz für SQL Trace/Profiler.","caution":"Ein sehr niedriger Schwellenwert (z. B. 0) füllt die Dateien schnell und erzeugt auf einem ausgelasteten Server Overhead; im WHERE-Prädikat filtern, nicht nachträglich. Die Action sql_text ist bei hohem Volumen teuer. Der Ordner muss für das Dienstkonto von SQL Server beschreibbar sein. duration und cpu_time sind in Mikrosekunden angegeben. Session nach Abschluss stoppen/löschen; mit STARTUP_STATE = OFF startet sie nach einem Neustart nicht.","note":"Um auf eine Datenbank einzugrenzen, sqlserver.database_name = N'Musteri' in die WHERE-Klausel aufnehmen. Für einen schnellen Blick SSMS > Management > Extended Events > Watch Live Data verwenden.","tags":["extended events","xe","event session","slow query","sql_statement_completed","event_file"]},{"id":"tempdb-contention","task":"tempdb-Contention und Platzverbrauch diagnostizieren","category":"Performance","command":"-- 1) Current tempdb page latch waits (2:1:1 PFS, 2:1:2 GAM, 2:1:3 SGAM):\nSELECT wt.session_id, wt.wait_type, wt.wait_duration_ms, wt.resource_description\nFROM   sys.dm_os_waiting_tasks wt\nWHERE  wt.wait_type LIKE N'PAGELATCH[_]%'\n  AND  wt.resource_description LIKE N'2:%';\n\n-- 2) Sessions using the most tempdb (MB):\nSELECT TOP (20) su.session_id, s.login_name, s.program_name,\n       (su.user_objects_alloc_page_count - su.user_objects_dealloc_page_count) * 8 / 1024         AS user_obj_mb,\n       (su.internal_objects_alloc_page_count - su.internal_objects_dealloc_page_count) * 8 / 1024 AS internal_obj_mb\nFROM   sys.dm_db_session_space_usage su\n  JOIN sys.dm_exec_sessions s ON s.session_id = su.session_id\nWHERE  s.is_user_process = 1\nORDER BY su.user_objects_alloc_page_count + su.internal_objects_alloc_page_count\n       - su.user_objects_dealloc_page_count - su.internal_objects_dealloc_page_count DESC;\n\n-- 3) Overall breakdown and version store:\nSELECT SUM(unallocated_extent_page_count) * 8 / 1024       AS free_mb,\n       SUM(user_object_reserved_page_count) * 8 / 1024     AS user_obj_mb,\n       SUM(internal_object_reserved_page_count) * 8 / 1024 AS internal_obj_mb,\n       SUM(version_store_reserved_page_count) * 8 / 1024   AS version_store_mb\nFROM   tempdb.sys.dm_db_file_space_usage;\n\nSELECT DB_NAME(database_id) AS db, reserved_page_count * 8 / 1024 AS version_store_mb\nFROM   sys.dm_tran_version_store_space_usage\nORDER BY reserved_page_count DESC;\n\n-- 4) File count / equal sizing and memory-optimized metadata:\nSELECT name, type_desc, size * 8 / 1024 AS size_mb, growth * 8 / 1024 AS growth_mb, is_percent_growth\nFROM   tempdb.sys.database_files;\nSELECT SERVERPROPERTY('IsTempdbMetadataMemoryOptimized') AS tempdb_metadata_memopt;","what":"Zeigt PAGELATCH-Waits auf tempdb-Allokationsseiten (PFS/GAM/SGAM) und Systemtabellen, die Sessions mit dem höchsten tempdb-Verbrauch (temporäre Tabellen vs. Sort-/Hash-Spills), welche Datenbank den Version Store aufbläht, sowie die Dateikonfiguration.","when":"Wenn PAGELATCH_UP/EX-Waits bei starker Nutzung temporärer Tabellen steigen, tempdb vollläuft (1105/9002) oder der Version Store wächst.","caution":"Die Seiten 2:1:1, 2:1:2, 2:1:3 und PFS-Seiten bei Vielfachen von 8088 bedeuten Allocation-Contention: Abhilfe schaffen mehrere gleich große Datendateien mit gleichem Wachstum (eine pro Kern bis 8, danach jeweils 4 hinzufügen). Bei Contention auf Seiten von Systemtabellen bietet 2019+ speicheroptimierte tempdb-Metadaten (Neustart erforderlich). Wächst der Version Store, die älteste offene Snapshot-Transaktion suchen; tempdb zu verkleinern ist keine Lösung. Reine Leseabfrage; sicher.","note":"Speicheroptimierte Metadaten: ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA = ON; + Neustart (2019+). 2022 brachte latchfreie GAM/SGAM-Updates, was die Contention stark verringert. Die Transaktion, die den Version Store hält: sys.dm_tran_active_snapshot_database_transactions ORDER BY elapsed_time_seconds DESC.","tags":["tempdb","pagelatch","pfs","gam","version store","contention"]},{"id":"qs-enable-config","task":"Query Store aktivieren, empfohlene Einstellungen anwenden und Zustand prüfen","category":"Query Store","command":"ALTER DATABASE [Musteri] SET QUERY_STORE = ON;\nALTER DATABASE [Musteri] SET QUERY_STORE (\n    OPERATION_MODE = READ_WRITE,\n    QUERY_CAPTURE_MODE = AUTO,\n    MAX_STORAGE_SIZE_MB = 2048,\n    INTERVAL_LENGTH_MINUTES = 15,\n    DATA_FLUSH_INTERVAL_SECONDS = 900,\n    CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),\n    SIZE_BASED_CLEANUP_MODE = AUTO,\n    MAX_PLANS_PER_QUERY = 200,\n    WAIT_STATS_CAPTURE_MODE = ON);\n\n-- State: if actual_state dropped to READ_ONLY, check readonly_reason\nUSE [Musteri];\nSELECT actual_state_desc, desired_state_desc, readonly_reason,\n       current_storage_size_mb, max_storage_size_mb,\n       query_capture_mode_desc, size_based_cleanup_mode_desc, wait_stats_capture_mode_desc\nFROM   sys.database_query_store_options;","what":"Aktiviert Query Store in der Datenbank; beginnt, Abfragetexte, Pläne, Laufzeit- und Wait-Statistiken in Zeitintervallen dauerhaft zu speichern. Die Optionen legen Speicherlimit, Bereinigung und Capture-Modus fest.","when":"Wenn eine neue Datenbank live geht, vor einem Versions-/Compatibility-Level-Upgrade (als Baseline) oder bei Verdacht auf eine Planregression.","caution":"Ist MAX_STORAGE_SIZE_MB erreicht, schaltet Query Store stillschweigend auf READ_ONLY (readonly_reason = 65536) und sammelt nicht mehr; überwache current_storage_size_mb. QUERY_CAPTURE_MODE = ALL füllt den Speicher schnell und erzeugt bei starker Ad-hoc-Last Overhead. Sehr ausgelastete OLTP-Systeme können QDS_*-Waits zeigen. OFF behält die gesammelten Daten; CLEAR löscht sie.","since":"2016","note":"Unter 2022 haben neu angelegte Datenbanken Query Store standardmäßig in READ_WRITE aktiviert. Ab 2019 akzeptiert QUERY_CAPTURE_MODE = CUSTOM Schwellenwerte (EXECUTION_COUNT, TOTAL_COMPILE_CPU_TIME_MS, TOTAL_EXECUTION_CPU_TIME_MS). Query Store für lesbare Secondaries (ALTER DATABASE ... FOR SECONDARY SET QUERY_STORE = ON) ist unter 2022 eine eingeschränkte Preview, nur mit Trace Flag 12606 aktivierbar und in der Produktion nicht unterstützt; 2025 braucht kein Trace Flag (weiterhin Preview).","tags":["query store","enable","max_storage_size_mb","query_capture_mode","read_only","qds"]},{"id":"qs-regressed-queries","task":"Planregressionen mit Query Store finden (gleiche Abfrage, unterschiedliche Pläne)","category":"Query Store","command":"USE [Musteri];\n-- Queries with more than one plan in the last 24 hours, per-plan performance:\nWITH plan_stats AS (\n    SELECT p.query_id, p.plan_id, p.is_forced_plan,\n           SUM(rs.count_executions) AS execs,\n           SUM(rs.avg_duration * rs.count_executions) / SUM(rs.count_executions) / 1000.0     AS avg_ms,\n           SUM(rs.avg_cpu_time * rs.count_executions) / SUM(rs.count_executions) / 1000.0     AS avg_cpu_ms,\n           SUM(rs.avg_logical_io_reads * rs.count_executions) / SUM(rs.count_executions)      AS avg_reads,\n           MAX(rs.last_execution_time) AS last_exec\n    FROM   sys.query_store_plan p\n      JOIN sys.query_store_runtime_stats rs ON rs.plan_id = p.plan_id\n      JOIN sys.query_store_runtime_stats_interval i\n        ON i.runtime_stats_interval_id = rs.runtime_stats_interval_id\n    WHERE  i.start_time >= DATEADD(HOUR, -24, SYSUTCDATETIME())\n    GROUP BY p.query_id, p.plan_id, p.is_forced_plan\n)\nSELECT ps.query_id, ps.plan_id, ps.is_forced_plan, ps.execs,\n       CAST(ps.avg_ms AS DECIMAL(18,2))     AS avg_ms,\n       CAST(ps.avg_cpu_ms AS DECIMAL(18,2)) AS avg_cpu_ms,\n       CAST(ps.avg_reads AS BIGINT)         AS avg_reads,\n       CAST(ps.avg_ms / NULLIF(MIN(ps.avg_ms) OVER (PARTITION BY ps.query_id), 0) AS DECIMAL(18,1)) AS x_slower_than_best,\n       ps.last_exec,\n       OBJECT_NAME(q.object_id)     AS module,\n       LEFT(qt.query_sql_text, 120) AS query_text\nFROM   plan_stats ps\n  JOIN sys.query_store_query q       ON q.query_id = ps.query_id\n  JOIN sys.query_store_query_text qt ON qt.query_text_id = q.query_text_id\nWHERE  ps.query_id IN (SELECT query_id FROM plan_stats GROUP BY query_id HAVING COUNT(*) > 1)\nORDER BY ps.query_id, ps.avg_ms;","what":"Findet Abfragen, die in den letzten 24 Stunden mit mehr als einem Plan liefen; stellt für jeden Plan Ausführungsanzahl, gewichtete durchschnittliche Dauer/CPU/Reads und den Faktor, um den er langsamer als der beste Plan ist, nebeneinander.","when":"Wenn eine Abfrage \"gestern schnell und heute langsam war\", nach einem Statistik-Update, einer Indexänderung, einem Compatibility-Level-Upgrade oder Patch.","caution":"Die durchschnittliche Dauer hängt auch von der Datenverteilung ab; beim Vergleich von Plänen, die mit unterschiedlichen Parameterwerten liefen, auch avg_reads betrachten (Parameter Sniffing). Zeitfenster sind in UTC. Bei Plänen mit wenigen Ausführungen sind die Ergebnisse statistisch schwach. Reine Leseabfrage; sicher.","since":"2016","note":"SSMS > Query Store > Regressed Queries macht dasselbe grafisch. Unter 2022 mit Compatibility Level 160 erzeugt die Parameter Sensitive Plan (PSP) Optimization für eine Anweisung mehrere Varianten-query_ids; ordne sie mit sys.query_store_query_variant (parent_query_id) der übergeordneten zu. Ab 2017 kann Automatic Tuning (FORCE_LAST_GOOD_PLAN) solche Regressionen automatisch beheben.","tags":["query store","plan regression","regressed queries","parameter sniffing","query_store_runtime_stats","plan"]},{"id":"qs-force-plan-hints","task":"Mit Query Store einen guten Plan erzwingen oder einen Hint anhängen","category":"Query Store","command":"USE [Musteri];\n-- Force the good plan (query_id / plan_id from the regression query):\nEXEC sys.sp_query_store_force_plan @query_id = 42, @plan_id = 7;\n\n-- Forced plans and failure reason:\nSELECT p.query_id, p.plan_id, p.is_forced_plan, p.plan_forcing_type_desc,\n       p.force_failure_count, p.last_force_failure_reason_desc\nFROM   sys.query_store_plan p\nWHERE  p.is_forced_plan = 1;\n\n-- Undo:\nEXEC sys.sp_query_store_unforce_plan @query_id = 42, @plan_id = 7;\n\n-- 2022+: attach / remove a hint without changing code:\nEXEC sys.sp_query_store_set_hints @query_id = 42, @query_hints = N'OPTION (RECOMPILE)';\nSELECT query_id, query_hint_text, last_query_hint_failure_reason_desc\nFROM   sys.query_store_query_hints;\nEXEC sys.sp_query_store_clear_hints @query_id = 42;","what":"Erzwingt für die gewählte Abfrage einen bestimmten Query-Store-Plan; der Optimizer verwendet diesen Plan bei jeder Kompilierung. Unter 2022 kannst du alternativ Hints wie RECOMPILE, MAXDOP oder OPTIMIZE FOR anhängen, ohne den Anwendungscode zu ändern.","when":"Wenn eine Planregression in der Produktion dringend behoben werden muss, bis eine dauerhafte Lösung (Index, Umschreiben der Abfrage, Statistiken) bereitsteht.","caution":"Ein erzwungener Plan kann mit wachsenden Daten schlechter werden und ist keine dauerhafte Lösung; überprüfe ihn regelmäßig. Wird ein vom Plan genutzter Index gelöscht/umbenannt, schlägt das Erzwingen fehl (last_force_failure_reason_desc = NO_INDEX usw.) und die Abfrage wird normal kompiliert. Nach dem Erzwingen kann ein \"morally equivalent\"-Plan unter einer neuen plan_id erscheinen; das ist erwartet. Erzwingen funktioniert auch, während Query Store READ_ONLY ist, prüfe es aber nach einem Restore oder AG-Failover.","since":"2016","note":"Query Store Hints gibt es ab 2022 (in Azure SQL früher). Hints ersetzen Plan Guides; lässt sich ein Hint nicht anwenden, läuft die Abfrage ohne ihn und der Grund erscheint in last_query_hint_failure_reason_desc. Plan Forcing auf lesbaren Secondaries (@replica_group_id) erfordert Query Store für Secondary-Replicas, was unter 2022 eine eingeschränkte Preview ist, aktiviert mit Trace Flag 12606.","tags":["force plan","sp_query_store_force_plan","query store hints","plan forcing","regression","recompile"]},{"id":"missing-index-dmv","task":"Missing-Index-Vorschläge aus DMVs lesen (mit Vorbehalten)","category":"Wartung","command":"USE [Musteri];\nSELECT TOP (25)\n       mid.statement                                   AS table_name,\n       mid.equality_columns, mid.inequality_columns, mid.included_columns,\n       migs.user_seeks, migs.user_scans,\n       CAST(migs.avg_total_user_cost AS DECIMAL(18,2)) AS avg_query_cost,\n       migs.avg_user_impact                            AS est_impact_pct,\n       CAST(migs.user_seeks * migs.avg_total_user_cost * migs.avg_user_impact / 100.0 AS DECIMAL(18,0)) AS score,\n       migs.last_user_seek,\n       N'CREATE INDEX [IX_' + OBJECT_NAME(mid.object_id, mid.database_id) + N'_missing_'\n         + CAST(mid.index_handle AS NVARCHAR(10)) + N'] ON ' + mid.statement + N' ('\n         + ISNULL(mid.equality_columns, N'')\n         + CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN N', ' ELSE N'' END\n         + ISNULL(mid.inequality_columns, N'') + N')'\n         + ISNULL(N' INCLUDE (' + mid.included_columns + N')', N'') AS create_stmt_draft\nFROM   sys.dm_db_missing_index_details mid\n  JOIN sys.dm_db_missing_index_groups mig       ON mig.index_handle = mid.index_handle\n  JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle\nWHERE  mid.database_id = DB_ID()\nORDER BY score DESC;\n\n-- 2019+: the query text behind each suggestion:\nSELECT TOP (25) mid.statement AS table_name, misq.group_handle, misq.user_seeks,\n       SUBSTRING(st.text, misq.last_statement_start_offset / 2 + 1, 300) AS statement_text\nFROM   sys.dm_db_missing_index_group_stats_query misq\n  JOIN sys.dm_db_missing_index_groups mig  ON mig.index_group_handle = misq.group_handle\n  JOIN sys.dm_db_missing_index_details mid ON mid.index_handle = mig.index_handle\n  CROSS APPLY sys.dm_exec_sql_text(misq.last_sql_handle) st\nWHERE  mid.database_id = DB_ID()\nORDER BY misq.user_seeks DESC;","what":"Bewertet die vom Optimizer bei der Kompilierung erfassten Missing-Index-Vorschläge nach Nutzung, geschätzten Kosten und Wirkung und erzeugt eine Entwurfsanweisung CREATE INDEX; ab 2019 zeigt es auch die Abfrage hinter jedem Vorschlag.","when":"Nach einem neuen Anwendungsrelease, für einen schnellen Überblick über den Indexbedarf leselastiger Abfragen oder als Ausgangsliste für ein Index-Design-Review.","caution":"Übernimm Vorschläge nicht blind: Der Optimizer ignoriert vorhandene Indizes, erzeugt oft nahezu doppelte (überlappende) Vorschläge und zu breite INCLUDE-Listen und ordnet Spalten nach Gleichheit/Ungleichheit statt nach Selektivität. Jeder neue Index kostet Schreibleistung und Speicher; erwäge zuerst, einen vorhandenen Index zu erweitern. Die DMVs werden bei Neustart, Offline-Schalten der Datenbank und Indexänderungen an der Tabelle zurückgesetzt. Reine Leseabfrage; sicher.","note":"Vergleiche einen vorgeschlagenen Index vor dem Anlegen mit den vorhandenen Indizes der Tabelle (sp_helpindex oder sys.index_columns) und prüfe ihn zusammen mit dem Rezept für ungenutzte Indizes. Das MissingIndex-Element im Plan-XML liefert dieselbe Information pro Abfrage.","tags":["missing index","missing index dmv","dm_db_missing_index_details","index suggestion","create index","tuning"]},{"id":"unused-indexes","task":"Ungenutzte / nur beschriebene Indizes finden","category":"Wartung","command":"USE [Musteri];\nSELECT OBJECT_SCHEMA_NAME(i.object_id) AS schema_name,\n       OBJECT_NAME(i.object_id)        AS table_name,\n       i.name                          AS index_name,\n       ISNULL(us.user_seeks, 0) + ISNULL(us.user_scans, 0) + ISNULL(us.user_lookups, 0) AS reads,\n       ISNULL(us.user_updates, 0)      AS writes,\n       ps.used_mb,\n       us.last_user_seek, us.last_user_scan,\n       N'DROP INDEX ' + QUOTENAME(i.name) + N' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id))\n         + N'.' + QUOTENAME(OBJECT_NAME(i.object_id)) + N';' AS drop_stmt_draft\nFROM   sys.indexes i\n  LEFT JOIN sys.dm_db_index_usage_stats us\n         ON us.database_id = DB_ID() AND us.object_id = i.object_id AND us.index_id = i.index_id\n  CROSS APPLY (SELECT CAST(SUM(p.used_page_count) * 8 / 1024.0 AS DECIMAL(18,1)) AS used_mb\n               FROM sys.dm_db_partition_stats p\n               WHERE p.object_id = i.object_id AND p.index_id = i.index_id) ps\nWHERE  OBJECTPROPERTY(i.object_id, 'IsUserTable') = 1\n  AND  i.type_desc = N'NONCLUSTERED'\n  AND  i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0\n  AND  ISNULL(us.user_seeks, 0) + ISNULL(us.user_scans, 0) + ISNULL(us.user_lookups, 0) = 0\nORDER BY writes DESC, ps.used_mb DESC;\n\n-- Usage counters since:\nSELECT sqlserver_start_time FROM sys.dm_os_sys_info;","what":"Listet Nonclustered-Indizes, die seit dem Neustart nicht gelesen wurden (seek/scan/lookup = 0), aber weiterhin Schreibkosten verursachen, mit ihrer Größe und erzeugt eine Entwurfsanweisung DROP INDEX.","when":"Bei langsamen INSERT/UPDATE auf schreiblastigen Tabellen, um Speicher- und Wartungszeit (Rebuild, CHECKDB) zu senken, oder vor einer Index-Konsolidierung.","caution":"Die Zähler werden bei Neustart, Offline-Schalten/Detach der Datenbank und (bei einigen älteren 2012/2014-Builds) bei Index-Rebuild zurückgesetzt; nur von Monats-/Jahresabschlussberichten genutzte Indizes wirken in einem kurzen Zeitfenster \"ungenutzt\". Beobachte vor dem Löschen mindestens einen vollständigen Geschäftszyklus und prüfe in einer AG die Nutzung auf lesbaren Secondaries separat (die Zähler gelten pro Replica). Unique Indizes erzwingen Constraints, auch wenn sie nicht gelesen werden; sie sind ausgeschlossen. Reine Leseabfrage; sicher.","note":"Deaktivieren mit ALTER INDEX ... DISABLE vor dem Löschen macht das Zurücknehmen einfach (die Definition bleibt; REBUILD stellt ihn wieder her). Eine Suche nach dem Indexnamen in Query Store oder im Plan Cache zeigt, ob er in einem Hint referenziert wird.","tags":["unused index","write-only index","dm_db_index_usage_stats","drop index","write overhead","index"]},{"id":"stats-health","task":"Zustand der Statistiken: wann aktualisiert, wie viele Zeilen geändert","category":"Wartung","command":"USE [Musteri];\nSELECT OBJECT_SCHEMA_NAME(s.object_id) AS schema_name,\n       OBJECT_NAME(s.object_id)        AS table_name,\n       s.name                          AS stats_name,\n       s.auto_created, s.has_filter, s.no_recompute,\n       sp.last_updated,\n       sp.rows, sp.rows_sampled,\n       CAST(100.0 * sp.rows_sampled / NULLIF(sp.rows, 0) AS DECIMAL(5,1))         AS sample_pct,\n       sp.modification_counter,\n       CAST(100.0 * sp.modification_counter / NULLIF(sp.rows, 0) AS DECIMAL(9,1)) AS modified_pct,\n       CAST(CASE WHEN 500 + 0.20 * sp.rows < SQRT(1000.0 * sp.rows) THEN 500 + 0.20 * sp.rows\n                 ELSE SQRT(1000.0 * sp.rows) END AS BIGINT)                       AS auto_update_threshold,\n       sp.persisted_sample_percent\nFROM   sys.stats s\n  CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp\nWHERE  OBJECTPROPERTY(s.object_id, 'IsUserTable') = 1\nORDER BY sp.modification_counter DESC;\n\nSELECT name, is_auto_create_stats_on, is_auto_update_stats_on, is_auto_update_stats_async_on\nFROM   sys.databases\nWHERE  name = DB_NAME();","what":"Zeigt für jede Statistik den Zeitpunkt der letzten Aktualisierung, die Sampling-Rate, die seit der letzten Aktualisierung geänderten Zeilen und den Schwellenwert, bei dem Auto Update auslöst (dynamischer Schwellenwert ab Compat 130).","when":"Bei Plänen mit großer Abweichung zwischen geschätzten und tatsächlichen Zeilen, nach einem großen Datenladevorgang oder um zu prüfen, ob der Job zur Statistikpflege wirkt.","caution":"Die Schwellenwertformel gilt ab Compatibility Level 130 (MIN(500 + 20%, SQRT(1000 x rows))); niedrigere Compat Levels nutzen die Regel 20% + 500. Nie aktualisierte Statistiken (leere Tabellen) liefern NULL. Ungleich verteilte Spalten mit niedrigem sample_pct brauchen eventuell FULLSCAN oder PERSIST_SAMPLE_PERCENT. Reine Leseabfrage; sicher.","since":"2016","note":"persisted_sample_percent spiegelt UPDATE STATISTICS ... WITH PERSIST_SAMPLE_PERCENT = ON wider (2016 SP1 CU4 / 2017 CU1+). Die Befehle zum Aktualisieren findest du im Rezept update-statistics.","tags":["statistics","stats health","dm_db_stats_properties","modification_counter","auto update","cardinality"]},{"id":"checkdb-strategy","task":"DBCC CHECKDB-Strategie: letzte erfolgreiche Prüfung, Aufteilen der Arbeit, Ergebnisse deuten","category":"Wartung","command":"-- Last successful CHECKDB (1900-01-01 = never):\nSELECT name, DATABASEPROPERTYEX(name, 'LastGoodCheckDbTime') AS last_good_checkdb\nFROM   sys.databases\nWHERE  state_desc = N'ONLINE' AND name <> N'tempdb'\nORDER BY last_good_checkdb;\n\n-- Estimated tempdb requirement:\nDBCC CHECKDB (N'Musteri') WITH ESTIMATEONLY;\n\n-- Split a large DB across days: allocation + catalog + table by table CHECKTABLE\nUSE [Musteri];\nDBCC CHECKALLOC (N'Musteri') WITH NO_INFOMSGS;\nDBCC CHECKCATALOG (N'Musteri') WITH NO_INFOMSGS;\nDBCC CHECKTABLE (N'dbo.Siparis') WITH NO_INFOMSGS, ALL_ERRORMSGS;\n\n-- Get results as rows (error rows with object and page):\nDBCC CHECKDB (N'Musteri') WITH TABLERESULTS, NO_INFOMSGS, ALL_ERRORMSGS, MAXDOP = 4;","what":"Zeigt den Zeitpunkt des letzten erfolgreichen CHECKDB für jede Datenbank, schätzt den benötigten tempdb-Platz, lässt dich die Prüfung großer Datenbanken in CHECKALLOC + CHECKCATALOG + CHECKTABLE aufteilen und liefert die Ergebnisse als Zeilen (Objekt, Index, Seite).","when":"Bei der Überprüfung der Wartung, für sehr große Datenbanken, die nicht ins Wartungsfenster passen, oder um nach 823/824 das Ausmaß der Korruption zu bestimmen.","caution":"Die Formulierung \"repair_allow_data_loss is the minimum repair level\" im Ergebnis bedeutet, dass die Reparatur Daten löscht; erwäge zuerst einen Restore aus einem guten Backup (oder einen Page Restore). Sind nur Nonclustered-Indizes (index_id > 1) betroffen, behebt Löschen und Neuanlegen des Index das Problem ohne Datenverlust. Untersuche nach dem Fund einer Korruption das Plattensubsystem. PHYSICAL_ONLY überspringt logische Prüfungen; ein vollständiges CHECKDB ist trotzdem regelmäßig nötig. Beim Aufteilen mit CHECKTABLE ist die Abdeckung unvollständig, wenn nicht auch CHECKALLOC/CHECKCATALOG laufen.","note":"DATABASEPROPERTYEX(..., 'LastGoodCheckDbTime') ist ab 2016 SP2, 2017 CU9 und 2019+ verfügbar; bei älteren Builds dbi_dbccLastKnownGood aus DBCC DBINFO WITH TABLERESULTS lesen. Zum Auslagern validiert ein CHECKDB auf einer aus dem Backup wiederhergestellten Kopie (z. B. auf einem Testserver) auch die Produktionsdatenbank. Die Option MAXDOP gibt es ab 2014 SP2 / 2016.","tags":["dbcc checkdb","lastgoodcheckdbtime","checktable","corruption","repair_allow_data_loss","integrity"]},{"id":"resource-governor","task":"Reporting-Last mit Resource Governor begrenzen (Pool, Workload Group, Classifier)","category":"Konfiguration","command":"USE master;\n-- 1) Pool and workload group:\nCREATE RESOURCE POOL [pool_report]\nWITH (MIN_CPU_PERCENT = 0, MAX_CPU_PERCENT = 30, CAP_CPU_PERCENT = 40,\n      MIN_MEMORY_PERCENT = 0, MAX_MEMORY_PERCENT = 25);\nCREATE WORKLOAD GROUP [wg_report]\nWITH (IMPORTANCE = LOW, REQUEST_MAX_MEMORY_GRANT_PERCENT = 15,\n      MAX_DOP = 2, REQUEST_MAX_CPU_TIME_SEC = 600)\nUSING [pool_report];\nGO\n-- 2) Classifier function (in master, SCHEMABINDING required):\nCREATE FUNCTION dbo.fn_rg_classifier()\nRETURNS sysname\nWITH SCHEMABINDING\nAS\nBEGIN\n    DECLARE @grp sysname = N'default';\n    IF SUSER_SNAME() = N'rapor_user' OR APP_NAME() LIKE N'Power BI%'\n        SET @grp = N'wg_report';\n    RETURN @grp;\nEND;\nGO\n-- 3) Enable:\nALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = dbo.fn_rg_classifier);\nALTER RESOURCE GOVERNOR RECONFIGURE;\n\n-- 4) Which session landed in which group:\nSELECT s.session_id, s.login_name, s.program_name, wg.name AS workload_group, rp.name AS pool\nFROM   sys.dm_exec_sessions s\n  JOIN sys.dm_resource_governor_workload_groups wg ON wg.group_id = s.group_id\n  JOIN sys.dm_resource_governor_resource_pools rp  ON rp.pool_id = wg.pool_id\nWHERE  s.is_user_process = 1;\n\n-- Rollback:\n-- ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL); ALTER RESOURCE GOVERNOR DISABLE;","what":"Leitet Sessions bestimmter Logins oder Anwendungen beim Verbindungsaufbau in eine separate Workload Group; schützt die OLTP-Last, indem CPU, Memory Grants, DOP und CPU-Zeit pro Anfrage dieser Gruppe begrenzt werden.","when":"Wenn Reporting-/BI-Abfragen OLTP ausbremsen, ETL sich mit der Last der Geschäftszeit überschneidet oder zur Ressourcenaufteilung auf einem Multi-Tenant-Server.","caution":"Die Classifier-Funktion läuft bei jeder neuen Verbindung; eine langsame oder fehlerhafte Funktion verlangsamt oder verhindert jeden Login (keine Abfragen oder Tabellenzugriffe darin). Verhält sie sich falsch, über die DAC verbinden und CLASSIFIER_FUNCTION = NULL setzen. Die Klassifizierung erfolgt nur beim Verbindungsaufbau; bestehende Sessions sind nicht betroffen. CAP_CPU_PERCENT ist eine harte Grenze, die auch bei freier CPU gilt. MAX_CPU_PERCENT greift nur bei CPU-Konkurrenz. Resource Governor ist ein Enterprise-/Developer-Feature.","note":"Das Überschreiten von REQUEST_MAX_CPU_TIME_SEC löst standardmäßig nur ein Event aus (cpu_threshold_exceeded); mit Trace Flag 2422 (2016 SP2 / 2017 CU3+) wird die Anfrage abgebrochen. Für I/O-Limits MAX_IOPS_PER_VOLUME auf dem Pool verwenden (2014+). Statistiken: sys.dm_resource_governor_resource_pools / _workload_groups.","tags":["resource governor","resource pool","workload group","classifier","cpu limit","resource management"]},{"id":"tde-enable","task":"TDE einrichten: Master Key, Zertifikat, Zertifikatsbackup und Verschlüsselungsfortschritt","category":"Sicherheit","command":"USE master;\nIF NOT EXISTS (SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')\n    CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'<GucluParola_1>';\nCREATE CERTIFICATE [TDE_Cert_2026]\n    WITH SUBJECT = N'TDE certificate', EXPIRY_DATE = '20361231';\n\n-- Back it up NOW: without cert + private key the backups cannot be restored anywhere\nBACKUP CERTIFICATE [TDE_Cert_2026]\n    TO FILE = N'D:\\Keys\\TDE_Cert_2026.cer'\n    WITH PRIVATE KEY (FILE = N'D:\\Keys\\TDE_Cert_2026.pvk',\n                      ENCRYPTION BY PASSWORD = N'<GucluParola_2>');\n\nUSE [Musteri];\nCREATE DATABASE ENCRYPTION KEY\n    WITH ALGORITHM = AES_256\n    ENCRYPTION BY SERVER CERTIFICATE [TDE_Cert_2026];\nALTER DATABASE [Musteri] SET ENCRYPTION ON;\n\n-- Progress and certificate backup date:\nSELECT DB_NAME(dek.database_id) AS db, dek.encryption_state_desc, dek.percent_complete,\n       dek.encryption_scan_state_desc, c.name AS cert_name, c.expiry_date, c.pvt_key_last_backup_date\nFROM   sys.dm_database_encryption_keys dek\n  LEFT JOIN master.sys.certificates c ON c.thumbprint = dek.encryptor_thumbprint;\n\n-- On the target server (before restore / AG secondary):\n-- CREATE CERTIFICATE [TDE_Cert_2026] FROM FILE = N'D:\\Keys\\TDE_Cert_2026.cer'\n--   WITH PRIVATE KEY (FILE = N'D:\\Keys\\TDE_Cert_2026.pvk', DECRYPTION BY PASSWORD = N'<GucluParola_2>');","what":"Legt in master einen Database Master Key und ein Zertifikat an, sichert das Zertifikat mit seinem Private Key, legt einen AES-256-Database-Encryption-Key an und verschlüsselt die Daten- und Logdateien at rest; zeigt den Fortschritt des Verschlüsselungsscans im Hintergrund.","when":"Für Anforderungen an Data-at-Rest-Verschlüsselung wie DSGVO/PCI und gegen den Diebstahl von Backup- sowie MDF/LDF-Dateien.","caution":"Gehen das Backup von Zertifikat + Private Key oder dessen Passwort verloren, sind die TDE-Datenbank und alle ihre Backups DAUERHAFT unlesbar; bewahre das Backup in einem sicheren Tresor getrennt vom Server auf. Das Aktivieren von TDE auf irgendeiner Datenbank verschlüsselt auch tempdb (geringe serverweite CPU-Kosten). Der Verschlüsselungsscan ist I/O-intensiv; ab 2019 lässt er sich mit ALTER DATABASE ... SET ENCRYPTION SUSPEND/RESUME pausieren. Für AG/Log Shipping muss das Zertifikat zuerst auf jedem Zielserver installiert werden. TDE verschlüsselt keine Daten im Speicher oder im Netzwerk.","note":"Ein abgelaufenes Zertifikat stoppt bestehendes TDE nicht, aber für eine Rotation ein neues Zertifikat anlegen und mit ALTER DATABASE ENCRYPTION KEY ENCRYPTION BY SERVER CERTIFICATE [new] neu verschlüsseln (nur der DEK wird neu verschlüsselt, nicht die Daten). encryption_state_desc und encryption_scan_state_desc sind Spalten ab 2019; in älteren Versionen encryption_state verwenden (3 = verschlüsselt, 2 = in Arbeit). TDE ist ab 2019 in der Standard Edition verfügbar.","tags":["tde","transparent data encryption","certificate","backup certificate","encryption","dek"]},{"id":"sql-server-audit","task":"SQL Server Audit einrichten: Logins, Berechtigungsänderungen und Tabellenzugriffe","category":"Sicherheit","command":"USE master;\nCREATE SERVER AUDIT [Audit_DBA]\nTO FILE (FILEPATH = N'D:\\Audit\\', MAXSIZE = 256 MB, MAX_ROLLOVER_FILES = 20, RESERVE_DISK_SPACE = OFF)\nWITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE);\nALTER SERVER AUDIT [Audit_DBA] WITH (STATE = ON);\n\n-- Server-wide (covers all databases):\nCREATE SERVER AUDIT SPECIFICATION [AuditSpec_Server]\nFOR SERVER AUDIT [Audit_DBA]\n    ADD (FAILED_LOGIN_GROUP),\n    ADD (SERVER_ROLE_MEMBER_CHANGE_GROUP),\n    ADD (DATABASE_ROLE_MEMBER_CHANGE_GROUP),\n    ADD (SERVER_PRINCIPAL_CHANGE_GROUP),\n    ADD (DATABASE_PRINCIPAL_CHANGE_GROUP),\n    ADD (SERVER_PERMISSION_CHANGE_GROUP),\n    ADD (DATABASE_PERMISSION_CHANGE_GROUP),\n    ADD (AUDIT_CHANGE_GROUP)\nWITH (STATE = ON);\n\n-- Database: access to a sensitive table:\nUSE [Musteri];\nCREATE DATABASE AUDIT SPECIFICATION [AuditSpec_Musteri]\nFOR SERVER AUDIT [Audit_DBA]\n    ADD (SELECT, INSERT, UPDATE, DELETE ON OBJECT::[dbo].[Siparis] BY [public])\nWITH (STATE = ON);\n\n-- Read:\nSELECT event_time, action_id, succeeded, server_principal_name, database_name,\n       schema_name, object_name, LEFT(statement, 200) AS stmt, client_ip, application_name\nFROM   sys.fn_get_audit_file(N'D:\\Audit\\Audit_DBA_*.sqlaudit', DEFAULT, DEFAULT)\nWHERE  event_time >= DATEADD(HOUR, -24, SYSUTCDATETIME())\nORDER BY event_time DESC;","what":"Legt ein Server Audit an, das in eine Datei schreibt; protokolliert serverweit fehlgeschlagene Logins, Rollenmitgliedschaften, Principal- und Berechtigungsänderungen sowie auf Datenbankebene SELECT-/DML-Zugriffe auf eine gewählte Tabelle und liest die Einträge per T-SQL aus.","when":"Für Audit-/Compliance-Anforderungen (DSGVO, PCI, SOX), für Fragen wie \"wer hat diese Tabelle gelesen/geändert\" oder bei Verdacht auf Privilege Escalation.","caution":"SELECT-Auditing auf einer stark genutzten Tabelle erzeugt sehr schnell Einträge und kostet Performance; grenze es nach Objekt und Principal ein. ON_FAILURE = SHUTDOWN stoppt den Server, wenn das Audit nicht geschrieben werden kann; nur verwenden, wenn vorgeschrieben. Der Audit-Ordner muss für das Dienstkonto von SQL Server beschreibbar und für Nicht-DBAs nicht löschbar sein. event_time ist in UTC. Eine Database Audit Specification wird mit der Datenbank wiederhergestellt, bleibt aber verwaist, wenn die GUID des Server Audits nicht übereinstimmt.","note":"Database Audit Specifications sind ab 2016 SP1 in der Standard Edition verfügbar. Immer AUDIT_CHANGE_GROUP einschließen, damit Änderungen am Audit selbst, auch das Ausschalten, protokolliert werden.","tags":["sql server audit","server audit","audit specification","fn_get_audit_file","auditing","compliance"]},{"id":"orphaned-users-fix","task":"Verwaiste Benutzer finden und Logins neu zuordnen (nach Restore / AG)","category":"Sicherheit","command":"USE [Musteri];\n-- Database users without a matching login:\nSELECT dp.name AS user_name, dp.type_desc, dp.authentication_type_desc, dp.sid, dp.create_date\nFROM   sys.database_principals dp\n  LEFT JOIN sys.server_principals sp ON sp.sid = dp.sid\nWHERE  sp.sid IS NULL\n  AND  dp.type IN ('S', 'U', 'G')\n  AND  dp.authentication_type_desc IN (N'INSTANCE', N'WINDOWS')\n  AND  dp.principal_id > 4;\n\n-- If a login with the same name exists, remap the SID:\nALTER USER [app_user] WITH LOGIN = [app_user];\n\n-- Permanent fix: generate CREATE LOGIN with SID + password hash on the source, run it on the target:\nSELECT N'CREATE LOGIN ' + QUOTENAME(sp.name)\n     + N' WITH PASSWORD = ' + CONVERT(NVARCHAR(MAX), LOGINPROPERTY(sp.name, 'PasswordHash'), 1)\n     + N' HASHED, SID = ' + CONVERT(NVARCHAR(MAX), sp.sid, 1)\n     + N', CHECK_POLICY = OFF;' AS create_login_stmt\nFROM   sys.server_principals sp\nWHERE  sp.type = 'S' AND sp.name = N'app_user';","what":"Findet Datenbankbenutzer, deren SID zu keinem Server-Login passt (verwaist); ordnet einen Benutzer einem vorhandenen Login neu zu und erzeugt ein Skript, um Logins auf einem anderen Server mit derselben SID und demselben Passwort-Hash neu anzulegen.","when":"Nach dem Wiederherstellen/Anfügen einer Datenbank auf einem anderen Server, wenn die Anwendung nach einem AG-/Log-Shipping-Failover \"Login failed for user\" erhält, oder bei einer Servermigration.","caution":"ALTER USER ... WITH LOGIN ändert die SID des Benutzers auf die des Logins; in einer AG überträgt sich diese Änderung vom Primary auf die Secondaries, aber haben die Logins auf den Secondary-Servern andere SIDs, kehrt das Problem beim nächsten Failover zurück. Die dauerhafte Lösung ist, Logins auf jeder Replica mit derselben SID anzulegen. Der Passwort-Hash ist sensibel; bewahre das Skript sicher auf. Benutzer in Contained Databases (authentication_type = DATABASE) brauchen kein Login und werden nicht aufgeführt.","note":"sp_change_users_login ist veraltet; ALTER USER WITH LOGIN verwenden. Für einen verwaisten dbo (Login des Datenbankbesitzers fehlt) ALTER AUTHORIZATION ON DATABASE::[Musteri] TO [sa]; verwenden. Für die Massenmigration von Logins PowerShell-dbatools verwenden: Copy-DbaLogin -Source SQL01 -Destination SQL02.","tags":["orphaned user","alter user with login","sid","login failed","after restore","sp_change_users_login"]},{"id":"permission-audit","task":"Audit von Logins und Berechtigungen: sysadmin-Mitglieder, riskante Berechtigungen, schwache Passwörter","category":"Sicherheit","command":"-- 1) Server role memberships:\nSELECT r.name AS server_role, m.name AS member, m.type_desc, m.is_disabled, m.create_date\nFROM   sys.server_role_members rm\n  JOIN sys.server_principals r ON r.principal_id = rm.role_principal_id\n  JOIN sys.server_principals m ON m.principal_id = rm.member_principal_id\nORDER BY r.name, m.name;\n\n-- 2) Risky server permissions:\nSELECT pr.name AS grantee, pr.type_desc, pe.permission_name, pe.state_desc\nFROM   sys.server_permissions pe\n  JOIN sys.server_principals pr ON pr.principal_id = pe.grantee_principal_id\nWHERE  pe.permission_name IN (N'CONTROL SERVER', N'IMPERSONATE ANY LOGIN', N'ALTER ANY LOGIN',\n                              N'ALTER ANY SERVER ROLE', N'ALTER SETTINGS', N'IMPERSONATE')\n   OR  pe.state_desc = N'GRANT_WITH_GRANT_OPTION';\n\n-- 3) SQL login health (policy, lockout, password = login name):\nSELECT sl.name, sl.is_disabled, sl.is_policy_checked, sl.is_expiration_checked,\n       LOGINPROPERTY(sl.name, 'PasswordLastSetTime') AS pwd_last_set,\n       LOGINPROPERTY(sl.name, 'IsLocked')            AS is_locked,\n       LOGINPROPERTY(sl.name, 'BadPasswordCount')    AS bad_pwd_count,\n       PWDCOMPARE(sl.name, sl.password_hash)         AS pwd_equals_name,\n       PWDCOMPARE(N'', sl.password_hash)             AS pwd_empty\nFROM   sys.sql_logins sl;\n\n-- 4) Database role memberships and explicit object permissions:\nUSE [Musteri];\nSELECT r.name AS db_role, m.name AS member, m.type_desc\nFROM   sys.database_role_members rm\n  JOIN sys.database_principals r ON r.principal_id = rm.role_principal_id\n  JOIN sys.database_principals m ON m.principal_id = rm.member_principal_id\nWHERE  m.name <> N'dbo'\nORDER BY r.name, m.name;\n\nSELECT pr.name AS grantee, pe.class_desc, pe.permission_name, pe.state_desc,\n       OBJECT_SCHEMA_NAME(pe.major_id) AS schema_name,\n       CASE pe.class WHEN 1 THEN OBJECT_NAME(pe.major_id) WHEN 3 THEN SCHEMA_NAME(pe.major_id) END AS object_name\nFROM   sys.database_permissions pe\n  JOIN sys.database_principals pr ON pr.principal_id = pe.grantee_principal_id\nWHERE  (pr.name NOT IN (N'public', N'dbo') AND NOT (pe.class = 0 AND pe.permission_name = N'CONNECT'))\n   OR  (pr.name = N'public' AND OBJECT_SCHEMA_NAME(pe.major_id) <> N'sys')\nORDER BY pr.name, pe.class_desc;","what":"Listet Mitgliedschaften in Serverrollen, riskante Berechtigungen wie CONTROL SERVER/IMPERSONATE, SQL-Logins mit deaktivierter Passwortrichtlinie, gesperrt oder mit einem Passwort, das dem Loginnamen entspricht oder leer ist; in der Datenbank Rollenmitgliedschaften und explizite Berechtigungen auf Nicht-System-Objekte.","when":"Für regelmäßige Zugriffsüberprüfungen, vor einem Audit, nachdem jemand das Team verlassen hat, oder bei Verdacht auf einen Sicherheitsvorfall.","caution":"Über Windows-Gruppen gewährte Berechtigungen erscheinen unter dem Gruppennamen; um die Personen zu sehen, EXEC xp_logininfo N'DOMAIN\\Group', 'members'; verwenden. Prüfe Abhängigkeiten der Anwendungen, bevor du etwas entziehst. PWDCOMPARE funktioniert nur für SQL-Logins und erfordert CONTROL SERVER. Reine Leseabfrage; sicher.","note":"Für die effektiven Berechtigungen eines Principals: EXECUTE AS USER = N'app_user'; SELECT * FROM fn_my_permissions(NULL, 'DATABASE'); REVERT;. Unter 2022 vergeben die neuen granularen Rollen (##MS_ServerStateReader##, ##MS_DefinitionReader## usw.) Monitoring-Rechte ohne sysadmin.","tags":["permission audit","sysadmin","server_role_members","database_permissions","pwdcompare","security"]},{"id":"ledger-tables","task":"Ledger-Tabelle anlegen, ihre Änderungshistorie lesen und die Integrität verifizieren","category":"Sicherheit","command":"USE [Musteri];\n-- Ledger verification requires snapshot isolation:\nALTER DATABASE [Musteri] SET ALLOW_SNAPSHOT_ISOLATION ON;\n\n-- Updatable ledger table (history table maintained automatically):\nCREATE TABLE dbo.Hesap (\n    HesapId int           NOT NULL PRIMARY KEY,\n    Bakiye  decimal(18,2) NOT NULL\n)\nWITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Hesap_History), LEDGER = ON);\n\n-- Append-only ledger (UPDATE/DELETE are blocked):\nCREATE TABLE dbo.IslemLog (Id bigint IDENTITY, Mesaj nvarchar(400))\nWITH (LEDGER = ON (APPEND_ONLY = ON));\n\n-- Who changed what, and when:\nSELECT t.commit_time, t.principal_name, l.HesapId, l.Bakiye, l.ledger_operation_type_desc\nFROM   dbo.Hesap_Ledger l\n  JOIN sys.database_ledger_transactions t ON t.transaction_id = l.ledger_transaction_id\nORDER BY t.commit_time DESC;\n\n-- Take a digest (store it somewhere trusted outside) and verify:\nEXEC sys.sp_generate_database_ledger_digest;\nEXEC sys.sp_verify_database_ledger\n     N'{\"database_name\":\"Musteri\",\"block_id\":0,\"hash\":\"0x...\",\"last_transaction_commit_time\":\"...\",\"digest_time\":\"...\"}';","what":"Legt eine Ledger-Tabelle an, die Zeilenänderungen in einer kryptografischen Hash-Kette protokolliert; die _Ledger-View zeigt, wer jedes INSERT/UPDATE/DELETE wann ausgeführt hat, und die Verifizierung gegen einen gespeicherten Digest beweist, dass die Daten nachträglich nicht verändert wurden (auch nicht von DBAs).","when":"Für Daten, deren Unveränderlichkeit nachweisbar sein muss: Finanzbuchungen, Audit-Trails, Vertrags-/Genehmigungsdatensätze.","caution":"Ledger lässt sich nach dem Anlegen der Tabelle nicht ausschalten; eine Ledger-Tabelle kann nicht in eine normale Tabelle umgewandelt werden, und eine gelöschte Ledger-Tabelle bleibt als gelöschte Ledger-Tabelle erhalten. Updatable Ledger schreibt bei jeder Änderung eine Historienzeile, der Speicherbedarf wächst. Werden Digests nicht außerhalb der Datenbank in unveränderlichem Speicher (WORM, Azure immutable blob) abgelegt, hat die Verifizierung keine Beweiskraft. sp_verify_database_ledger schlägt ohne Snapshot Isolation mit Fehler 37498 fehl.","since":"2022","note":"sp_generate_database_ledger_digest liefert JSON; sp_verify_database_ledger akzeptiert einen einzelnen Digest oder ein JSON-Array und kann mit @table_name eine einzelne Tabelle verifizieren. Für automatische Digest-Ablage: ALTER DATABASE SCOPED CONFIGURATION SET LEDGER_DIGEST_STORAGE_ENDPOINT = '<blob url>'. CREATE DATABASE ... WITH LEDGER = ON macht standardmäßig jede Tabelle zu einer Ledger-Tabelle.","tags":["ledger","append-only","sp_verify_database_ledger","digest","immutable","tamper-evident"]},{"id":"agent-job-failures","task":"Fehlerhistorie der SQL-Agent-Jobs und Status des letzten Laufs","category":"SQL Agent","command":"-- Job steps that failed / were cancelled in the last 24 hours:\nSELECT j.name AS job_name, h.step_id, h.step_name,\n       msdb.dbo.agent_datetime(h.run_date, h.run_time) AS run_at,\n       STUFF(STUFF(RIGHT('000000' + CAST(h.run_duration AS VARCHAR(6)), 6), 3, 0, ':'), 6, 0, ':') AS duration_hhmmss,\n       CASE h.run_status WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' WHEN 2 THEN 'Retry'\n                         WHEN 3 THEN 'Canceled' WHEN 4 THEN 'In progress' END AS status,\n       h.sql_message_id, h.sql_severity,\n       LEFT(h.message, 500) AS message\nFROM   msdb.dbo.sysjobhistory h\n  JOIN msdb.dbo.sysjobs j ON j.job_id = h.job_id\nWHERE  h.run_status IN (0, 3)\n  AND  msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(HOUR, -24, GETDATE())\nORDER BY run_at DESC;\n\n-- Each job's last outcome, running now, next run:\nSELECT j.name, j.enabled,\n       CASE lr.run_status WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' WHEN 3 THEN 'Canceled' END AS last_outcome,\n       lr.run_at AS last_run_at,\n       CASE WHEN a.start_execution_date IS NOT NULL AND a.stop_execution_date IS NULL THEN 'RUNNING' END AS running_now,\n       a.next_scheduled_run_date\nFROM   msdb.dbo.sysjobs j\n  LEFT JOIN msdb.dbo.sysjobactivity a\n         ON a.job_id = j.job_id AND a.session_id = (SELECT MAX(session_id) FROM msdb.dbo.syssessions)\n  OUTER APPLY (SELECT TOP (1) h.run_status, msdb.dbo.agent_datetime(h.run_date, h.run_time) AS run_at\n               FROM msdb.dbo.sysjobhistory h\n               WHERE h.job_id = j.job_id AND h.step_id = 0\n               ORDER BY h.instance_id DESC) lr\nORDER BY j.name;\n\n-- Is the Agent service running:\nSELECT servicename, status_desc, startup_type_desc FROM sys.dm_server_services;","what":"Zeigt die in den letzten 24 Stunden fehlgeschlagenen oder abgebrochenen Job-Schritte mit ihrer Fehlermeldung; das letzte Ergebnis jedes Jobs, ob er gerade läuft, und seinen nächsten geplanten Lauf; sowie den Zustand des Agent-Dienstes.","when":"Beim morgendlichen Check, bei einem Alarm eines Backup-/Wartungsjobs, für \"ist der nächtliche Job gelaufen\" oder nach einem Failover, um zu prüfen, ob die Jobs auf der richtigen Replica laufen.","caution":"Die Job-Historie in msdb ist begrenzt (standardmäßig insgesamt 1.000 Zeilen, 100 pro Job); häufig laufende Jobs verdrängen alte Fehler schnell, erhöhe das Limit daher in den Agent-Eigenschaften. Die Spalte message wird bei 4.000 Zeichen abgeschnitten; für lange Ausgaben eine Ausgabedatei am Job-Schritt festlegen. Ist der Agent-Dienst gestoppt, läuft kein geplanter Job und keiner meldet einen Fehler. Reine Leseabfrage; sicher.","note":"msdb.dbo.agent_datetime ist undokumentiert, aber weit verbreitet. In einer AG müssen Jobs auf jeder Replica existieren und im ersten Schritt die Rolle mit sys.fn_hadr_is_primary_replica(N'Musteri') prüfen. dbatools: Get-DbaAgentJobHistory -SqlInstance SQL01 -StartDate (Get-Date).AddDays(-1) -OutcomeType Failed.","tags":["sql agent","job history","sysjobhistory","job failure","msdb","scheduled job"]}]},"recipes/mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"recipes":[{"id":"rs-add-member","task":"Mitglied zum Replica Set hinzufügen","category":"Replica Set","command":"rs.add({ host: \"mongo2:27017\", priority: 1, votes: 1 })","what":"Fügt dem bestehenden Replica Set ein neues Mitglied (Secondary) hinzu und startet eine Initial Sync.","when":"Beim Hinzufügen eines neuen Knotens für mehr Lesekapazität oder Redundanz.","caution":"Das neue Mitglied zieht während der Initial Sync Daten vom Primary; bei großen Datenmengen erzeugt das Netzwerk- und Plattenlast.","tags":["replica","rs.add","member","member","add"]},{"id":"rs-remove-member","task":"Mitglied aus dem Replica Set entfernen","category":"Replica Set","command":"rs.remove(\"mongo2:27017\")","what":"Entfernt den angegebenen Host aus der Konfiguration des Replica Sets.","when":"Beim Außerbetriebnehmen eines Knotens oder beim Austausch von Hardware.","caution":"Entferne nicht den Primary; Mitglieder so zu entfernen, dass die Mehrheit verloren geht, kann das Set für Lese- und Schreibzugriffe unverfügbar machen.","tags":["replica","rs.remove","member","member","remove"]},{"id":"rs-step-down","task":"Primary herabstufen (stepDown)","category":"Replica Set","command":"rs.stepDown(60)","what":"Stuft den aktuellen Primary für die angegebene Anzahl Sekunden zum Secondary herab und löst eine neue Wahl aus.","when":"Um die Primary-Rolle vor einer geplanten Wartung an einen anderen Knoten zu übergeben.","caution":"Während stepDown kommt es zu einer kurzen Schreibunterbrechung; die Anwendung muss sich neu verbinden.","tags":["replica","stepdown","primary","failover","step down"]},{"id":"rs-set-priority","task":"Priorität eines Mitglieds ändern","category":"Replica Set","command":"cfg = rs.conf(); cfg.members[1].priority = 2; rs.reconfig(cfg)","what":"Ändert die Priorität des angegebenen Mitglieds, bei Wahlen Primary zu werden.","when":"Wenn ein bestimmter Knoten bevorzugt Primary sein soll.","caution":"reconfig kann eine Wahl auslösen und einen kurzen Primary-Wechsel/eine Schreibunterbrechung verursachen.","tags":["replica","priority","reconfig","priority","election"]},{"id":"rs-status","task":"Status des Replica Sets anzeigen","category":"Replica Set","command":"rs.status()","what":"Zeigt Zustand, Rollen, Replikationsverzögerung und Health der Set-Mitglieder.","when":"Beim Prüfen von Replikationszustand und Verzögerung.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["replica","status","status","health","rs.status"]},{"id":"rs-hidden-member","task":"Verstecktes / nicht stimmberechtigtes Mitglied konfigurieren","category":"Replica Set","command":"cfg = rs.conf(); cfg.members[2].hidden = true; cfg.members[2].priority = 0; cfg.members[2].votes = 0; rs.reconfig(cfg)","what":"Macht ein Mitglied versteckt (für die Anwendung unsichtbar), nicht stimmberechtigt und mit Priorität 0, sodass es nur für Backup/Analytics genutzt wird.","when":"Wenn du Backups oder analytische Abfragen isolieren willst.","caution":"Ein verstecktes Mitglied muss Priorität 0 haben; sonst schlägt reconfig fehl.","tags":["replica","hidden","votes","hidden","non-voting"]},{"id":"rs-resync","task":"Mitglied von Grund auf neu synchronisieren","category":"Replica Set","command":"// On the SECONDARY to be resynced (NEVER on the PRIMARY):\ndb.getSiblingDB(\"admin\").shutdownServer()\n// At the OS level: empty the dbPath contents (or move them elsewhere as a backup)\n// and restart mongod with the same configuration; the member starts an initial sync automatically.\n// Monitor progress (stateStr: STARTUP2 -> SECONDARY):\nrs.status()","what":"MongoDB hat keinen separaten resync-Befehl; das Stoppen des Secondary, Leeren seines Datenverzeichnisses und Neustarten startet eine neue Initial Sync vom Primary (oder einer Sync Source).","when":"Wenn ein Mitglied so weit zurückfällt, dass es aus dem Oplog-Fenster herausfällt (RECOVERING / too stale), oder seine Daten inkonsistent werden.","caution":"Alle lokalen Daten des Mitglieds werden gelöscht und neu gezogen; nur auf einem Secondary durchführen und sicherstellen, dass die Mehrheit verfügbar bleibt. Sync-Dauer und Netzwerk-/Plattenlast sind hoch; bei sehr großen Datenmengen ist das Seeding aus einem Dateisystem-Snapshot eines gesunden Mitglieds schneller.","tags":["replica","resync","initialsync","sync","resync"]},{"id":"sh-add-shard","task":"Shard zum Cluster hinzufügen","category":"Sharding","command":"sh.addShard(\"shardRS/mongoShard1:27018\")","what":"Fügt dem Sharded Cluster einen neuen Shard (Replica Set) hinzu.","when":"Beim horizontalen Skalieren von Speicher- oder Durchsatzkapazität.","caution":"Nach dem Hinzufügen eines Shards beginnt der Balancer, Chunks zu migrieren; das erzeugt zusätzliche IO-Last.","tags":["sharding","addshard","shard","add","cluster"]},{"id":"sh-shard-collection","task":"Collection sharden","category":"Sharding","command":"sh.shardCollection(\"shop.customer\", { customerId: \"hashed\" })","what":"Shardet die angegebene Collection über den gegebenen Shard Key.","when":"Wenn du eine wachsende Collection auf mehrere Shards verteilen willst.","caution":"Der Shard Key lässt sich ab 5.0 mit reshardCollection ändern (und ab 4.4 mit refineCollectionShardKey erweitern), das ist aber teuer, weil alle Daten neu verteilt werden; eine schlechte Schlüsselwahl führt zu ungleicher Verteilung.","tags":["sharding","shardcollection","key","customer","shard key"]},{"id":"sh-start-balancer","task":"Balancer starten","category":"Sharding","command":"sh.startBalancer()","what":"Aktiviert den Balancer-Prozess, der Chunks verteilt.","when":"Wenn du das Balancing nach einer Wartung wieder aktivieren willst.","caution":"Der Balancer erzeugt beim Migrieren von Chunks IO- und Netzwerklast; lege ein Balancing-Fenster außerhalb der Spitzenzeiten fest.","tags":["sharding","balancer","start","start","rebalance"]},{"id":"sh-stop-balancer","task":"Balancer stoppen","category":"Sharding","command":"sh.stopBalancer()","what":"Deaktiviert den Balancer, der Chunk-Migrationen durchführt.","when":"Um Migrationen vor einem Backup, einer Wartung oder einem Massenladevorgang zu stoppen.","caution":"Bleibt er lange aus, werden die Shards ungleich; vergiss nicht, ihn danach wieder zu aktivieren.","tags":["sharding","balancer","stop","stop","maintenance"]},{"id":"sh-status","task":"Sharding-Status anzeigen","category":"Sharding","command":"sh.status()","what":"Fasst Shards, Datenbanken, geshardete Collections und Chunk-Verteilung zusammen.","when":"Beim Untersuchen des Verteilungszustands des Clusters und der Balancer-Health.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["sharding","status","status","chunk","sh.status"]},{"id":"sh-add-zone","task":"Zone (Tag) hinzufügen und einen Bereich zuweisen","category":"Sharding","command":"sh.addShardToZone(\"shard0000\", \"TR\"); sh.updateZoneKeyRange(\"shop.customer\", { region: \"TR\" }, { region: \"TR~\" }, \"TR\")","what":"Weist einen Shard einer Zone zu und bindet einen bestimmten Schlüsselbereich an diese Zone (Zone Sharding).","when":"Wenn du Daten nach Geografie oder Hardware auf bestimmten Shards halten willst.","caution":"Zonenbereiche müssen zum Shard Key passen; ein falscher Bereich verhindert die Migration von Chunks.","tags":["sharding","zone","tag","region","range"]},{"id":"sh-move-chunk","task":"Chunk manuell verschieben","category":"Sharding","command":"sh.moveChunk(\"shop.customer\", { customerId: 1000 }, \"shard0001\")","what":"Verschiebt den Chunk mit dem angegebenen Schlüssel manuell auf den Ziel-Shard.","when":"Um eine ungleiche Verteilung zu korrigieren oder bestimmte Daten zu platzieren.","caution":"Eine manuelle Verschiebung kann mit dem Balancer kollidieren; große Chunk-Migrationen erzeugen IO-Last und kurze Sperren.","tags":["sharding","movechunk","chunk","move","manual"]},{"id":"compact-collection","task":"Collection kompaktieren","category":"Wartung","command":"db.runCommand({ compact: \"customer\" })","what":"Schreibt die Collection und ihre Indizes auf der Platte neu und gewinnt freien Platz zurück.","when":"Wenn du nach vielen Deletes/Updates Plattenplatz zurückgewinnen willst.","caution":"Ab 4.4 blockiert compact keine Lese-/Schreibzugriffe; es blockiert nur bestimmte DDL-Operationen auf derselben Collection (drop, createIndex, dropIndex usw.); es erzeugt dennoch hohe IO-Last und gibt Platz nicht immer an das Betriebssystem zurück. Wenn möglich auf einem Secondary und in einem Wartungsfenster ausführen.","tags":["compact","storage","maintenance","disk","storage"]},{"id":"create-index-background","task":"Index anlegen (nicht blockierend)","category":"Wartung","command":"db.customer.createIndex({ email: 1 }, { name: \"idx_email\" })","what":"Legt einen Index auf dem angegebenen Feld an; seit MongoDB 4.2 blockieren Index-Builds standardmäßig keine Schreibzugriffe.","when":"Beim Hinzufügen eines neuen Index zur Verbesserung der Abfrage-Performance.","caution":"Auf großen Collections verbrauchen Index-Builds CPU und IO; die Last auf dem Primary kann steigen.","since":"MongoDB 4.2","note":"MongoDB 4.2 führte einen neuen Index-Build-Prozess ein, und das Anlegen von Indizes blockiert Schreibzugriffe standardmäßig nicht mehr; die alte Option { background: true } ist daher unnötig und wird ab 4.2 ignoriert.","tags":["index","createindex","background","create","performance"]},{"id":"collection-stats","task":"Index- und Collection-Größen anzeigen","category":"Wartung","command":"db.customer.stats({ indexDetails: true })","what":"Meldet Collection-Größe, Speichergröße und die Größe jedes Index.","when":"Beim Analysieren von Speicherverbrauch und Indexkosten.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["stats","index","size","collection","storage"]},{"id":"log-rotate","task":"Logdatei rotieren","category":"Wartung","command":"db.adminCommand({ logRotate: 1 })","what":"Schließt die aktuelle Logdatei und öffnet eine neue (Log-Rotation).","when":"Wenn die Logdatei gewachsen ist oder vor der Archivierung.","caution":"Bei gleichzeitiger Nutzung der Log-Rotation des Betriebssystems die Konfiguration prüfen, um Konflikte zu vermeiden.","tags":["log","logrotate","rotate","maintenance","archive"]},{"id":"current-op-long","task":"Lang laufende Operationen finden","category":"Wartung","command":"db.currentOp({ \"active\": true, \"secs_running\": { $gt: 5 } })","what":"Listet aktive Operationen, die seit mehr als 5 Sekunden laufen.","when":"Beim Ermitteln problematischer Abfragen bei Langsamkeit oder Hängern.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["currentop","long","long-running","operation","diagnostics"]},{"id":"kill-op","task":"Operation beenden","category":"Wartung","command":"db.killOp(12345)","what":"Beendet die laufende Operation mit der angegebenen opid.","when":"Wenn eine Abfrage das System blockiert oder viel zu lange läuft.","caution":"Das Beenden einer Schreiboperation kann Teiländerungen hinterlassen; vermeide es, System-/Replikationsoperationen zu beenden.","tags":["killop","kill","operation","kill","diagnostics"]},{"id":"set-parameter","task":"Laufzeitparameter ändern","category":"Parameter","command":"db.adminCommand({ setParameter: 1, cursorTimeoutMillis: 600000 })","what":"Ändert einen Laufzeitparameter ohne Neustart des Servers.","when":"Wenn du ein Serververhalten vorübergehend anpassen willst.","caution":"Die Änderung ist nicht dauerhaft; beim Neustart gilt wieder der Wert aus der Konfigurationsdatei. Ein falscher Wert kann die Performance verschlechtern.","tags":["setparameter","parameter","runtime","setting","admin"]},{"id":"set-profiling","task":"Profiling-Level festlegen","category":"Parameter","command":"db.setProfilingLevel(1, { slowms: 100 })","what":"Protokolliert langsame Abfragen (über 100 ms) in der Collection system.profile (Level 1).","when":"Wenn du langsame Abfragen diagnostizieren willst.","caution":"Level 2 protokolliert alle Operationen und erzeugt erheblichen Overhead; Profiling-Daten belegen Plattenplatz.","tags":["profiling","slowms","profiler","slow","diagnostics"]}]},"recipes/mongodb-ext.json":{"engineId":"mongodb","engine":"MongoDB","order":2,"recipes":[{"id":"rs-oplog-window","task":"Oplog-Fenster und Replikationsverzögerung messen","category":"Replica Set","command":"// Oplog size and the time span it covers (oplog window):\nrs.printReplicationInfo()\n// Lag of each secondary relative to the primary:\nrs.printSecondaryReplicationInfo()\n// Lag in seconds and sync source, for monitoring/scripts:\nconst s = rs.status(); const p = s.members.find(m => m.stateStr === \"PRIMARY\");\ns.members.filter(m => m.stateStr === \"SECONDARY\").map(m => ({ host: m.name, lagSec: (p.optimeDate - m.optimeDate) / 1000, syncSource: m.syncSourceHost }))\n// Window (hours):\ndb.getReplicationInfo().timeDiffHours","what":"Zeigt, wie viele Stunden an Schreibvorgängen das Oplog fasst (Oplog-Fenster) und wie viele Sekunden jeder Secondary hinter dem Primary liegt, zusammen mit seiner Sync Source.","when":"Bei einem Alarm wegen Replikationsverzögerung, vor einer geplanten Wartung, um zu berechnen, wie lange ein Mitglied ausfallen darf, und nach Massenschreibjobs.","caution":"Bleibt ein Mitglied länger als das Oplog-Fenster ausgefallen, wird es 'too stale' und braucht eine Initial Sync. Massen-Update-/Delete-Jobs können das Fenster innerhalb von Stunden verkleinern; beurteile das Fenster nach seinem Wert unter Spitzenlast. Die Verzögerung wird aus der Differenz von optimeDate berechnet; erhält der Primary keine Schreibzugriffe, wirkt die Verzögerung nicht hoch, aber auch das Fenster schrumpft nicht.","since":"MongoDB 4.4","note":"rs.printSlaveReplicationInfo() wurde in 4.4 in rs.printSecondaryReplicationInfo() umbenannt. Ist das Fenster zu klein, das Oplog mit replSetResizeOplog vergrößern (siehe rs-oplog-resize). Verzögerte Mitglieder liegen um secondaryDelaySecs zurück (slaveDelay in 4.4); nimm sie von Verzögerungsalarmen aus.","tags":["oplog","oplog window","replication lag","delay","secondary","printReplicationInfo"],"untested":true},{"id":"rs-oplog-resize","task":"Oplog online in der Größe ändern (replSetResizeOplog)","category":"Replica Set","command":"// Current oplog size (MB):\ndb.getSiblingDB(\"local\").oplog.rs.stats().maxSize / 1024 / 1024\n// Resize the oplog to 50 GB (in MB; run on each member separately, secondaries first):\ndb.adminCommand({ replSetResizeOplog: 1, size: 51200 })\n// Minimum retention period (hours), independent of size:\ndb.adminCommand({ replSetResizeOplog: 1, minRetentionHours: 48 })\n// Verify the result:\nrs.printReplicationInfo()","what":"Vergrößert oder verkleinert das Oplog eines laufenden Mitglieds ohne Neustart; minRetentionHours bewahrt Oplog-Einträge für eine Mindestdauer auf, auch wenn das Größenlimit überschritten wird.","when":"Wenn das Oplog-Fenster die Dauer der Wartung oder der Initial Sync nicht abdeckt oder Massenschreibjobs das Fenster verkleinern.","caution":"Der Befehl wirkt nur auf das Mitglied, auf dem er läuft, und wird nicht repliziert; auf jedem Mitglied anwenden. Auch auf Mitgliedern, die als Sync Source dienen, muss das Fenster ausreichen. Verkleinern gibt Plattenplatz nicht sofort an das OS zurück; dafür ist compact auf oplog.rs nötig. Mit minRetentionHours kann das Oplog je nach Schreibvolumen über size hinaus wachsen; plane den Plattenplatz entsprechend.","since":"MongoDB 4.4","note":"Die neue Größe bleibt über Neustarts erhalten; per Befehl gesetztes minRetentionHours nicht, setze daher zusätzlich storage.oplogMinRetentionHours. size muss mindestens 990 MB betragen. Eine Änderung von replication.oplogSizeMB in der Konfiguration wirkt nur beim erstmaligen Anlegen des Oplogs.","tags":["oplog","resize","replSetResizeOplog","minRetentionHours","size","oplog window"],"untested":true},{"id":"rs-force-reconfig","task":"Rekonfiguration des Replica Sets nach Verlust der Mehrheit erzwingen (force reconfig)","category":"Replica Set","command":"// On a surviving member with the most recent optime (runs as a secondary):\nrs.status().members.map(m => ({ host: m.name, state: m.stateStr, health: m.health, optime: m.optimeDate }))\ncfg = rs.conf()\ncfg.members.map((m, i) => ({ i: i, host: m.host }))\n// Keep only the reachable members (example: members 0 and 2):\ncfg.members = [cfg.members[0], cfg.members[2]]\nrs.reconfig(cfg, { force: true })\n// Verify the new primary election:\nrs.status().members.map(m => ({ host: m.name, state: m.stateStr }))","what":"Baut bei dauerhaftem Verlust der Mehrheit der stimmberechtigten Mitglieder (z. B. ein ganzes Rechenzentrum ist weg) aus den verbliebenen Mitgliedern eine neue Konfiguration, damit das Set wieder einen Primary wählen kann.","when":"Nur im Katastrophenfall: Die Mehrheit kommt nicht zurück und das Set hängt im Nur-Lese-Zustand fest.","caution":"Dies ist das letzte Mittel. Kehren die verlorenen Mitglieder später mit der alten Konfiguration zurück, können Split-Brain und Rollback auftreten; isoliere sie vom Netzwerk und lösche beim Zurückholen ihre Daten und füge sie mit einer Initial Sync hinzu. Wähle das Mitglied mit der neuesten optime; sonst fallen Schreibvorgänge, die auf anderen Mitgliedern nicht majority-committed sind, ins Rollback. Verwende force nicht für routinemäßige Änderungen der Mitgliedschaft.","since":"MongoDB 4.4","note":"force reconfig erhöht die Konfigurationsversion um einen großen zufälligen Wert und ist die einzige reconfig-Form, die auf einem Secondary laufen kann. Ab 4.4 können normale reconfigs höchstens ein stimmberechtigtes Mitglied auf einmal hinzufügen oder entfernen; force unterliegt diesem Limit nicht.","tags":["force reconfig","majority","disaster","recovery","split-brain","rs.reconfig"],"untested":true},{"id":"rs-rolling-maintenance","task":"Rollierende Wartung eines Replica Sets (Neustart, OS-Patch, Minor-Upgrade)","category":"Replica Set","command":"// 1) Before starting: all members healthy, low lag, and an oplog window longer than the maintenance\nrs.printSecondaryReplicationInfo()\n// 2) Clean shutdown on the SECONDARY under maintenance (then OS/binary work, restart with the same config):\ndb.getSiblingDB(\"admin\").shutdownServer({ timeoutSecs: 60 })\n// 3) Wait until the member is SECONDARY and lag has closed, then move to the next secondary:\nrs.status().members.map(m => ({ host: m.name, state: m.stateStr, health: m.health }))\n// 4) When all secondaries are done, on the PRIMARY: hand over to a secondary that catches up within 30 s\nrs.stepDown(120, 30)\n// 5) The old primary is now a SECONDARY: apply steps 2 and 3 to it","what":"Gibt die Reihenfolge vor, in der Mitglieder einzeln in Wartung gehen, Secondaries zuerst und der Primary zuletzt, sodass Neustarts, OS-Patches oder Binär-Updates erfolgen, ohne das Set außer Betrieb zu nehmen.","when":"Bei geplanten OS-/Kernel-Updates, Minor-Version-Upgrades, Änderungen von mongod-Parametern oder Platten-/Hardwarewartung.","caution":"Fahre nie mehr als ein Mitglied gleichzeitig herunter; in einem Set mit 3 Mitgliedern geht beim Verlust von zwei die Mehrheit verloren. Bestätige nach jedem Mitglied den Zustand SECONDARY und dass die Verzögerung aufgeholt ist. stepDown verursacht eine kurze Schreibunterbrechung; stelle sicher, dass sich die Treiber mit Retryable Writes neu verbinden. Gibt es einen Arbiter, behandle ihn in derselben Abfolge.","since":"MongoDB 4.4","note":"rs.stepDown(stepDownSecs, secondaryCatchUpPeriodSecs): Der zweite Parameter gibt an, wie lange der Primary darauf wartet, dass ein Secondary aufholt; holt keiner auf, schlägt stepDown fehl und der Primary bleibt. Um zu verhindern, dass ein bestimmtes Mitglied während der Wartung Primary wird, rs.freeze(seconds) darauf ausführen.","tags":["rolling","maintenance","restart","stepDown","upgrade","patching"],"untested":true},{"id":"sh-balancer-window","task":"Balancer-Zustand, aktives Zeitfenster und Balancing pro Collection","category":"Sharding","command":"// Is the balancer enabled, is it running a round right now:\nsh.getBalancerState()\ndb.adminCommand({ balancerStatus: 1 })\n// Run the balancer only between 01:00 and 05:00 (on mongos):\ndb.getSiblingDB(\"config\").settings.updateOne({ _id: \"balancer\" }, { $set: { activeWindow: { start: \"01:00\", stop: \"05:00\" } } }, { upsert: true })\n// Remove the window:\ndb.getSiblingDB(\"config\").settings.updateOne({ _id: \"balancer\" }, { $unset: { activeWindow: true } })\n// Disable / enable balancing for a single collection:\nsh.disableBalancing(\"shop.musteri\")\nsh.enableBalancing(\"shop.musteri\")\n// Whether the collection is balanced (4.4+):\ndb.adminCommand({ balancerCollectionStatus: \"shop.musteri\" })","what":"Zeigt, ob der Balancer aktiviert ist und gerade eine Runde ausführt, beschränkt Chunk-Migrationen auf ein Zeitfenster und deaktiviert oder aktiviert das Balancing für eine einzelne Collection.","when":"Wenn Chunk-Migrationen tagsüber die Performance beeinträchtigen, während des Backup-Fensters oder beim Eingrenzen eines Migrationsproblems auf einer Collection.","caution":"Ein zu enges Fenster kommt mit dem Datenwachstum nicht mit, und die Shards werden ungleich; prüfe die Balance regelmäßig mit balancerCollectionStatus. Eine vor Schließen des Fensters gestartete Migration läuft bis zum Ende. Bleibt das Balancing pro Collection deaktiviert, wächst diese Collection weiter auf einem einzigen Shard.","since":"MongoDB 6.0","note":"Die Zeiten von activeWindow werden in der lokalen Zeitzone des Primary des Config-Server-Replica-Sets ausgewertet. Ab 6.0.3 entscheidet der Balancer anhand des Unterschieds der Datengröße zwischen Shards, nicht anhand der Chunk-Anzahl; die Chunk-Anzahlen in sh.status() können ungleich aussehen, obwohl die Daten ausgeglichen sind.","tags":["balancer","activeWindow","window","migration","sharding","balancerCollectionStatus"],"untested":true},{"id":"sh-jumbo-chunks","task":"Jumbo-Chunks finden, aufteilen und das Flag löschen","category":"Sharding","command":"// Chunks flagged as jumbo (5.0+ config.chunks links to the collection by uuid):\nconst uuid = db.getSiblingDB(\"config\").collections.findOne({ _id: \"shop.musteri\" }).uuid;\ndb.getSiblingDB(\"config\").chunks.find({ uuid: uuid, jumbo: true }, { min: 1, max: 1, shard: 1 })\n// If the chunk can be split, split it at the median:\nsh.splitFind(\"shop.musteri\", { musteriId: 1000 })\n// Last resort for an indivisible chunk, clear the flag manually (a successful split already clears it):\ndb.adminCommand({ clearJumboFlag: \"shop.musteri\", find: { musteriId: 1000 } })\n// If a single key value is too large, refine the key (4.4+):\ndb.adminCommand({ refineCollectionShardKey: \"shop.musteri\", key: { musteriId: 1, siparisId: 1 } })","what":"Listet Chunks, die als jumbo markiert sind, weil der Balancer sie nicht verschieben konnte, teilt die aufteilbaren und löscht das Jumbo-Flag; zeigt, wie der Shard Key für riesige Chunks mit einem einzigen Wert verfeinert wird.","when":"Wenn ein Shard deutlich größer bleibt als die anderen, jumbo in der Ausgabe von sh.status() erscheint oder der Balancer Migrationsfehler meldet.","caution":"Ein Chunk, dessen Dokumente alle denselben Shard-Key-Wert haben, lässt sich nicht aufteilen; die einzige dauerhafte Lösung ist, den Shard Key mit refineCollectionShardKey oder reshardCollection zu ändern. refineCollectionShardKey erfordert, dass vorher ein unterstützender Index für den neuen Schlüssel existiert. Das Jumbo-Flag ohne Aufteilen zu löschen führt dazu, dass der Balancer denselben Chunk erneut versucht und wieder scheitert.","since":"MongoDB 5.0","note":"Shard Keys mit niedriger Kardinalität oder auf einen Wert konzentrierte Schlüssel (z. B. status, country) sind die häufigste Ursache für Jumbo-Chunks. Vor 5.0 werden Dokumente in config.chunks über das Feld ns abgefragt. clearJumboFlag kam mit 4.2.3/4.0.15.","tags":["jumbo","chunk","split","clearJumboFlag","shard key","refineCollectionShardKey"],"untested":true},{"id":"sh-shard-key-check","task":"Shard-Key-Wahl und Datenverteilung prüfen (getShardDistribution, analyzeShardKey)","category":"Sharding","command":"// Data and document distribution of the collection across shards:\ndb.getSiblingDB(\"shop\").musteri.getShardDistribution()\n// Cardinality, frequency and monotonicity analysis of the current or candidate shard key:\ndb.adminCommand({ analyzeShardKey: \"shop.musteri\", key: { musteriId: 1 }, keyCharacteristics: true, readWriteDistribution: false })\n// For read/write distribution, enable query sampling first (10 samples per second):\ndb.adminCommand({ configureQueryAnalyzer: \"shop.musteri\", mode: \"full\", samplesPerSecond: 10 })\ndb.adminCommand({ analyzeShardKey: \"shop.musteri\", key: { musteriId: 1 }, keyCharacteristics: false, readWriteDistribution: true })\n// Turn sampling off when done:\ndb.adminCommand({ configureQueryAnalyzer: \"shop.musteri\", mode: \"off\" })","what":"Zeigt den prozentualen Anteil der Daten einer Collection auf jedem Shard; analyzeShardKey meldet Kardinalität, häufigste Werte und Monotonie des aktuellen oder eines Kandidaten-Shard-Keys sowie, ob die gesampelten Abfragen einen einzelnen Shard oder alle Shards (Scatter-Gather) ansprechen.","when":"Vor dem Sharden einer neuen Collection, wenn ein Hot Shard oder ungleiches Wachstum auffällt, und vor der Entscheidung für reshardCollection.","caution":"analyzeShardKey braucht einen Index, der den Schlüssel unterstützt, und nimmt bei großen Collections Stichproben von Dokumenten; das erzeugt in Spitzenzeiten zusätzliche Leselast. Solange Query Sampling aktiv ist, werden Stichproben in config.sampledQueries geschrieben; schalte es nach Abschluss der Analyse aus. Ein monoton steigender Schlüssel (ObjectId, Zeitstempel) häuft alle Inserts auf einem Shard an.","since":"MongoDB 7.0","note":"analyzeShardKey und configureQueryAnalyzer kamen mit 7.0 und funktionieren auch auf Replica Sets. Ein guter Shard Key hat hohe Kardinalität, geringe Häufigkeit, ist nicht monoton und kommt im Filter häufiger Abfragen vor. Ein Hashed Key behebt die Verteilung, macht aber Bereichsabfragen zu Scatter-Gather.","tags":["shard key","analyzeShardKey","getShardDistribution","cardinality","distribution","hot shard"],"untested":true},{"id":"explain-execstats","task":"Abfrageplan mit echten Ausführungsstatistiken untersuchen (explain executionStats)","category":"Performance","command":"db.musteri.find({ sehir: \"Ankara\", durum: \"aktif\" }).sort({ kayitTarihi: -1 }).explain(\"executionStats\")\n// Summary: winning plan, returned documents, examined keys/documents and time\nconst e = db.musteri.find({ sehir: \"Ankara\", durum: \"aktif\" }).sort({ kayitTarihi: -1 }).explain(\"executionStats\");\n({ plan: e.queryPlanner.winningPlan, n: e.executionStats.nReturned, keys: e.executionStats.totalKeysExamined, docs: e.executionStats.totalDocsExamined, ms: e.executionStats.executionTimeMillis })\n// For an aggregation:\ndb.musteri.explain(\"executionStats\").aggregate([{ $match: { sehir: \"Ankara\" } }, { $group: { _id: \"$durum\", n: { $sum: 1 } } }])","what":"Führt die Abfrage aus und zeigt den gewählten Plan (IXSCAN/COLLSCAN, SORT-Stage), die Anzahl der zurückgegebenen Dokumente, der untersuchten Indexschlüssel und Dokumente sowie die benötigte Zeit.","when":"Beim Analysieren einer Abfrage, die im Profiler oder Log langsam erscheint, und um zu prüfen, ob ein neuer Index tatsächlich genutzt wird.","caution":"executionStats führt die Abfrage wirklich aus; auch ein explain einer schweren Abfrage zur Spitzenzeit erzeugt Last. Bei Schreibbefehlen (update/delete/findAndModify) ändert explain keine Daten, führt aber den Plan aus. Ein hohes Verhältnis totalDocsExamined/nReturned oder eine In-Memory-SORT-Stage im Plan bedeutet, dass der Index nicht passt.","since":"MongoDB 4.4","note":"Wende beim Entwurf zusammengesetzter Indizes die ESR-Regel an: zuerst Equality-Felder, dann Sort, dann Range. Mit explain(\"allPlansExecution\") siehst du auch verworfene Pläne. Ab 7.0 enthält winningPlan die Felder queryPlan und slotBasedPlan, wenn eine Abfrage auf der Slot-Based Engine läuft.","tags":["explain","executionStats","IXSCAN","COLLSCAN","plan","index"]},{"id":"profiler-analyze","task":"Die langsamsten Abfragen und Query Shapes aus Profiler-Daten ermitteln","category":"Performance","command":"// Recent slow operations (in the db where profiling is on; see set-profiling):\ndb.system.profile.find({ millis: { $gte: 100 } }, { ts: 1, ns: 1, op: 1, millis: 1, planSummary: 1, keysExamined: 1, docsExamined: 1, nreturned: 1, appName: 1 }).sort({ ts: -1 }).limit(20)\n// Total time per query shape (those consuming the most time):\ndb.system.profile.aggregate([\n  { $group: { _id: { ns: \"$ns\", plan: \"$planSummary\", shape: \"$queryHash\" }, n: { $sum: 1 }, totalMs: { $sum: \"$millis\" }, maxMs: { $max: \"$millis\" }, avgDocs: { $avg: \"$docsExamined\" } } },\n  { $sort: { totalMs: -1 } },\n  { $limit: 10 }\n])\n// Queries not using an index (COLLSCAN):\ndb.system.profile.find({ planSummary: \"COLLSCAN\" }, { ns: 1, millis: 1, command: 1 }).sort({ millis: -1 }).limit(10)","what":"Listet aus den Einträgen der Collection system.profile die letzten langsamen Operationen, die Gesamtzeit pro Query Shape (queryHash) und COLLSCAN-Abfragen ohne Indexnutzung.","when":"Bei einer Beschwerde über Langsamkeit, um herauszufinden, welche Abfrage Serverzeit verbraucht, und um den Indexbedarf zu bestimmen.","caution":"system.profile ist standardmäßig eine Capped Collection mit 1 MB und gilt pro Datenbank; auf einem ausgelasteten System rotieren die Einträge innerhalb von Minuten, analysiere daher bei aktivem Profiling oder direkt danach. Profiling-Level 2 oder slowms 0 schreibt jede Operation und erzeugt erheblichen Overhead. Einträge werden nur auf dem Mitglied gehalten, auf dem sie liefen, und nicht repliziert.","since":"MongoDB 4.4","note":"Auch bei ausgeschaltetem Profiling werden Operationen über slowms (Standard 100 ms) als 'Slow query' in das mongod-Log geschrieben; attr.durationMillis und attr.planSummary im Log liefern dieselbe Diagnose. Ab 8.0 wird für den Query Shape zusätzlich ein Feld planCacheShapeHash geschrieben.","tags":["profiler","system.profile","slow query","top queries","COLLSCAN","queryHash"]},{"id":"index-usage-hide","task":"Ungenutzte Indizes finden und vor dem Löschen verstecken (hideIndex)","category":"Performance","command":"// Usage counter per index (on this member, since the last restart):\ndb.musteri.aggregate([{ $indexStats: {} }, { $project: { name: 1, ops: \"$accesses.ops\", since: \"$accesses.since\" } }, { $sort: { ops: 1 } }])\n// Hide it from the planner before dropping (the index keeps being maintained):\ndb.musteri.hideIndex(\"idx_email\")\n// Revert immediately if problems appear:\ndb.musteri.unhideIndex(\"idx_email\")\n// If the observation period passes without issues:\ndb.musteri.dropIndex(\"idx_email\")","what":"$indexStats zeigt, wie oft jeder Index genutzt wurde; hideIndex verbirgt einen Index vor dem Query Planner, ohne ihn zu löschen, sodass sich die Wirkung des Entfernens umkehrbar testen lässt.","when":"Beim Aufräumen unnötiger Indizes, die Schreib-Overhead und Speicherbedarf verursachen.","caution":"Die Zähler gelten pro Mitglied und werden beim Neustart zurückgesetzt; gehen Lesezugriffe an Secondaries, alle Mitglieder prüfen und einen ausreichend langen Zeitraum beobachten (inklusive Monatsabschlussberichten). Unique- oder TTL-Indizes mit ops 0 können trotzdem nötig sein: Unique Constraint und TTL-Löschungen tauchen im Zähler nicht auf. Ein versteckter Index wird bei Schreibzugriffen weiter gepflegt, und ein versteckter Unique Index erzwingt den Constraint weiterhin.","since":"MongoDB 4.4","note":"hideIndex erfordert featureCompatibilityVersion 4.4 oder höher. Der _id-Index kann nicht versteckt werden. Das Aufheben mit unhideIndex braucht keinen Rebuild und wirkt sofort.","tags":["indexStats","hideIndex","unused index","index cleanup","dropIndex","index"]},{"id":"connections-by-app","task":"Verbindungen nach Anwendung und Client zählen (Diagnose eines Connection Storms)","category":"Performance","command":"// Total, available and rejected connection counts:\ndb.serverStatus().connections\n// Count connections by application name and client IP:\ndb.getSiblingDB(\"admin\").aggregate([\n  { $currentOp: { allUsers: true, idleConnections: true } },\n  { $group: { _id: { app: \"$appName\", client: { $arrayElemAt: [{ $split: [\"$client\", \":\"] }, 0] } }, conns: { $sum: 1 }, active: { $sum: { $cond: [\"$active\", 1, 0] } } } },\n  { $sort: { conns: -1 } },\n  { $limit: 20 }\n])","what":"Zeigt die gesamte und verfügbare Verbindungsanzahl auf dem Server und gruppiert dann offene Verbindungen nach appName und Client-IP samt der Anzahl aktiver Verbindungen.","when":"Wenn die Verbindungsanzahl plötzlich sprunghaft steigt, bei Fehlern 'connection refused' oder Pool-Timeouts, um herauszufinden, welcher Anwendungsserver Verbindungen leckt.","caution":"idleConnections: true liefert auch alle inaktiven Verbindungen, auf Systemen mit Tausenden Verbindungen ist die Ausgabe daher groß; deshalb ist das Zusammenfassen mit $group wichtig. Zeilen mit null-Client sind interne Threads. Jede Verbindung verbraucht etwa 1 MB Speicher; ein Connection Storm lässt auch den Speicher außerhalb des WiredTiger-Caches wachsen.","since":"MongoDB 4.4","note":"Die dauerhafte Lösung liegt meist auf Treiberseite: maxPoolSize, maxIdleTimeMS und die Anzahl der Anwendungsinstanzen. Setzen Anwendungen kein appName, sind sie schwer zu unterscheiden; fordere appName=<service> im Connection String an. net.maxIncomingConnections und der Wert ulimit -n des OS legen die harte Obergrenze fest.","tags":["connections","connection count","currentOp","appName","connection storm","pool"]},{"id":"index-build-monitor","task":"Laufenden Index-Build überwachen und abbrechen","category":"Wartung","command":"// Index builds in progress and their completion percentage:\ndb.getSiblingDB(\"admin\").aggregate([\n  { $currentOp: { allUsers: true } },\n  { $match: { $or: [{ \"command.createIndexes\": { $exists: true } }, { desc: /^IndexBuildsCoordinator/ }] } },\n  { $project: { opid: 1, desc: 1, ns: 1, secs_running: 1, msg: 1, progress: 1, \"command.indexes.name\": 1 } }\n])\n// Abort the build (on the primary; in a replica set it stops on all members):\ndb.musteri.dropIndex(\"idx_email\")","what":"Listet laufende Index-Builds mit der Anzahl gescannter Dokumente und dem prozentualen Fortschritt (progress.done / progress.total); bricht den Build bei Bedarf mit dropIndex ab.","when":"Um abzuschätzen, wann ein auf einer großen Collection gestartetes createIndex fertig wird, oder um einen Build zu stoppen, der zur Spitzenzeit Last erzeugt.","caution":"Einen Index-Build mit dropIndex/dropIndexes abbrechen, nicht mit killOp; in einem Replica Set auf dem Primary ausführen, damit sich der Abbruch auf alle Mitglieder überträgt. Manche DDL-Operationen auf der Collection warten während des Builds. Am Ende des Builds wird kurz eine exklusive Sperre genommen; bei sehr hoher Schreiblast kann dieser Moment Latenz verursachen.","since":"MongoDB 4.4","note":"Ab 4.4 läuft ein Index-Build in Replica Sets gleichzeitig auf allen datentragenden Mitgliedern und wird committet, sobald commitQuorum (Standard votingMembers) erfüllt ist; hängt ein Secondary hinterher, wird der Build auch auf dem Primary nicht fertig. Ab 5.0 setzt ein durch ein sauberes Herunterfahren unterbrochener Build nach dem Neustart dort fort, wo er aufgehört hat.","tags":["index build","createIndex","progress","monitor","dropIndex","commitQuorum"]},{"id":"rolling-index-build","task":"Rollierender Index-Build auf einem Replica Set (Mitglied für Mitglied)","category":"Wartung","command":"// 1) Shut down a SECONDARY; comment out replication.replSetName and start it standalone on another port:\n//    mongod --config /etc/mongod.conf --port 27217 --setParameter disableLogicalSessionCacheRefresh=true\n// 2) Build the index on the standalone member:\ndb.getSiblingDB(\"shop\").musteri.createIndex({ email: 1 }, { name: \"idx_email\" })\n// 3) Restart with the normal configuration; wait until it is SECONDARY and lag has closed:\nrs.printSecondaryReplicationInfo()\n// 4) When all secondaries are done, step down the primary and apply steps 1-3 to the old primary:\nrs.stepDown(120, 30)","what":"Baut den Index auf jedem Mitglied separat, indem das Mitglied vorübergehend standalone außerhalb des Sets betrieben wird; der Primary trägt nie die Build-Last.","when":"Wenn die Auswirkung des standardmäßigen verteilten Builds auf den Primary und die Replikationsverzögerung bei einer sehr großen Collection nicht akzeptabel ist.","caution":"Die Wartungszeit jedes Mitglieds muss kürzer als das Oplog-Fenster sein; sonst ist das Mitglied bei der Rückkehr too stale. Beim rollierenden Aufbau eines Unique Index die Schreibzugriffe auf die Collection für den gesamten Build stoppen; sonst können einige Mitglieder verletzende Dokumente enthalten. Starte das Standalone-Mitglied auf einem anderen Port, damit sich keine Anwendungen damit verbinden.","since":"MongoDB 4.4","note":"Ab 4.4 ist der Standard-Build bereits nicht blockierend und läuft gleichzeitig auf allen Mitgliedern; die rollierende Methode ist nur nötig, wenn diese Last nicht akzeptabel ist. In einem Sharded Cluster wird das Verfahren für jedes Shard-Replica-Set separat durchgeführt, mit vorher gestopptem Balancer.","tags":["rolling index","index build","standalone","replica","createIndex","maintenance"],"untested":true},{"id":"ttl-index","task":"TTL-Index anlegen, Ablaufzeit ändern und TTL-Löschungen überwachen","category":"Wartung","command":"// Automatically delete documents lastSeen + 3600 s later:\ndb.oturum.createIndex({ lastSeen: 1 }, { expireAfterSeconds: 3600, name: \"ttl_lastSeen\" })\n// Change the expiry without rebuilding the index:\ndb.runCommand({ collMod: \"oturum\", index: { name: \"ttl_lastSeen\", expireAfterSeconds: 7200 } })\n// TTL indexes on the collection and their expiry:\ndb.oturum.getIndexes().filter(i => i.expireAfterSeconds !== undefined)\n// TTL monitor statistics (deleted documents, passes):\ndb.serverStatus().metrics.ttl","what":"Legt einen TTL-Index an, der Dokumente eine festgelegte Zeit nach einem Datumsfeld automatisch löscht, ändert die Ablaufzeit mit collMod ohne Rebuild und zeigt, wie viele Dokumente der TTL-Monitor gelöscht hat.","when":"Um zeitgebundene Daten wie Sessions, Logs oder temporäre Tokens ohne Lösch-Job in der Anwendung zu bereinigen und um zu untersuchen, warum eine wachsende Collection nicht schrumpft.","caution":"Der TTL-Monitor läuft standardmäßig alle 60 Sekunden; das Löschen erfolgt nicht sofort. Das Feld muss vom Typ Date (oder ein Array von Dates) sein; Datumsangaben als String oder Dokumente ohne das Feld werden nie gelöscht. Eine verkürzte Ablaufzeit führt dazu, dass viele Dokumente auf einmal gelöscht werden, mit Schreiblast und Replikationsverzögerung. Löschungen erfolgen nur auf dem Primary und werden ins Oplog geschrieben.","since":"MongoDB 5.1","note":"Ein TTL-Index muss ein Einzelfeld-Index sein (zusammengesetztes TTL wird nicht unterstützt). expireAfterSeconds mit collMod zu einem bestehenden Einzelfeld-Index ohne TTL hinzuzufügen ist ab 5.1 möglich; das Ändern einer bestehenden TTL-Ablaufzeit funktioniert auch in älteren Versionen. Um zu einem bestimmten Zeitpunkt zu löschen, expireAfterSeconds: 0 verwenden und den Löschzeitpunkt im Feld speichern.","tags":["ttl","expireAfterSeconds","collMod","auto delete","index","metrics.ttl"]},{"id":"wt-cache-pressure","task":"Druck auf den WiredTiger-Cache messen (Füllgrad, Dirty-Anteil, Eviction durch Anwendungs-Threads)","category":"Performance","command":"const c = db.serverStatus().wiredTiger.cache;\nconst max = c[\"maximum bytes configured\"];\n({\n  maxGB: (max / 1024 ** 3).toFixed(1),\n  usedPct: (100 * c[\"bytes currently in the cache\"] / max).toFixed(1),\n  dirtyPct: (100 * c[\"tracked dirty bytes in the cache\"] / max).toFixed(1),\n  appThreadEvictedPages: c[\"pages evicted by application threads\"],\n  pagesReadIntoCache: c[\"pages read into cache\"]\n})\n// Read/write ticket usage and queue:\ndb.serverStatus().wiredTiger.concurrentTransactions","what":"Zeigt Größe des WiredTiger-Caches, Füllgrad und Dirty-Anteil in Prozent, wie viele Seiten Anwendungs-Threads zwangsweise verdrängen mussten, und den Status der Lese-/Schreib-Tickets.","when":"Wenn Plattenlesevorgänge zunehmen, die Latenz plötzlich ansteigt oder der Schreibdurchsatz sinkt; beim Dimensionieren des Caches.","caution":"Überschreitet der Füllgrad 80% (eviction_target), beginnt die Eviction im Hintergrund; bei 95% (eviction_trigger) beteiligen sich auch Anwendungs-Threads an der Eviction und Abfragen werden langsamer. Für Dirty-Daten sind 5% das Ziel und 20% der Auslöser; ständig nahe 20% bedeutet, dass die Schreibkapazität der Platte nicht ausreicht. Ein wachsender Zähler pages evicted by application threads ist Latenz, die Benutzer direkt spüren. Die Werte sind kumulative Zähler; betrachte die Differenz zwischen zwei Messungen.","since":"MongoDB 7.0","note":"Die Standard-Cachegröße ist der größere Wert von (RAM - 1 GB) / 2 oder 256 MB; in Containern wird sie aus dem cgroup-Limit berechnet. Eine Erhöhung von storage.wiredTiger.engineConfig.cacheSizeGB verkleinert den Page Cache des OS; über die Hälfte des RAM hinaus hilft es selten. In 7.0 wird die Ticket-Anzahl dynamisch angepasst; ab 8.0 erscheinen Ticket-Metriken auch unter serverStatus().queues.execution.","tags":["wiredtiger","cache","eviction","dirty","tickets","memory"]},{"id":"dump-restore-archive","task":"Archiv-Backup mit mongodump/mongorestore erstellen und eine einzelne Collection wiederherstellen","category":"Backup","command":"# Single database, one compressed archive file (password is prompted):\nmongodump --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --db=shop --gzip --archive=/backup/shop_$(date +%F).archive.gz\n# Dry run before restoring the archive under a different name:\nmongorestore --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --gzip --archive=/backup/shop_2026-10-08.archive.gz --nsFrom=\"shop.*\" --nsTo=\"shop_restore.*\" --dryRun\n# Restore a single collection from the archive under a new name:\nmongorestore --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --gzip --archive=/backup/shop_2026-10-08.archive.gz --nsInclude=\"shop.musteri\" --nsFrom=\"shop.*\" --nsTo=\"shop_restore.*\" --numInsertionWorkersPerCollection=4\n# Restore under the same name, dropping the existing collection:\nmongorestore --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --gzip --archive=/backup/shop_2026-10-08.archive.gz --nsInclude=\"shop.musteri\" --drop","what":"Sichert eine Datenbank in eine einzige gzip-Archivdatei; stellt die ganze Datenbank oder eine einzelne Collection aus dem Archiv wieder her, optional unter einem anderen Namen (nsFrom/nsTo).","when":"Für logische Backups, das Übertragen von Daten zwischen Umgebungen oder das Wiederherstellen einer einzelnen versehentlich gelöschten oder beschädigten Collection.","caution":"mongodump ist ein logisches Backup: Es ist bei großen Datenmengen langsam, und Indizes werden beim Restore neu aufgebaut. Ein ohne --oplog erstellter Dump ist auf einem laufenden System nicht zeitpunktkonsistent. --drop löscht die Ziel-Collection, bevor der Restore beginnt; zuerst mit nsTo unter anderem Namen wiederherstellen und prüfen. Der Restore schreibt auf den Primary und kann Replikationsverzögerung verursachen.","since":"MongoDB 4.4","note":"Seit 4.4 werden die Database Tools (mongodump/mongorestore) getrennt vom Server versioniert (100.x). Füge der URI readPreference=secondary hinzu, um den Primary während des Backups von Leselast zu entlasten. Um auch Benutzer und Rollen zu übertragen, einen vollständigen Instanz-Dump mit --restoreDbUsersAndRoles verwenden oder die admin-Datenbank separat sichern.","tags":["mongodump","mongorestore","backup","archive","nsFrom","restore"]},{"id":"pitr-oplog-replay","task":"Point-in-Time-Restore mit Oplog-Replay (Rückkehr vor ein fehlerhaftes drop/delete)","category":"Backup","command":"# 1) Full backup + oplog written during the backup (whole instance, from a replica set member):\nmongodump --uri=\"mongodb://admin@mongo2:27017/?authSource=admin&readPreference=secondary\" --oplog --gzip --out=/backup/full\n# 2) Find the time of the bad operation (mongosh): db.getSiblingDB(\"local\").oplog.rs.find({ \"o.drop\": \"musteri\" }).sort({ $natural: -1 }).limit(1)\n# 3) Dump the oplog after the backup (t: start time of the full backup, epoch seconds):\nmongodump --uri=\"mongodb://admin@mongo2:27017/?authSource=admin\" --db=local --collection=oplog.rs --query='{\"ts\":{\"$gte\":{\"$timestamp\":{\"t\":1791400000,\"i\":1}}}}' --out=/backup/oplog\n# 4) Restore the full backup together with its own oplog:\nmongorestore --uri=\"mongodb://admin@target:27017/?authSource=admin\" --gzip --oplogReplay /backup/full\n# 5) Replay the oplog up to the bad operation (oplogLimit is exclusive):\nmkdir /backup/pitr && mv /backup/oplog/local/oplog.rs.bson /backup/pitr/oplog.bson\nmongorestore --uri=\"mongodb://admin@target:27017/?authSource=admin\" --oplogReplay --oplogLimit=1791403600:1 /backup/pitr","what":"Macht das Vollbackup mit --oplog konsistent und spielt dann das nach dem Backup angesammelte Oplog bis zum Zeitstempel der fehlerhaften Operation (drop, Massenlöschung) ein, wodurch die Daten auf diesen Zeitpunkt zurückgesetzt werden.","when":"Wenn eine Collection versehentlich gelöscht wurde oder ein fehlerhaftes Massen-Update/-Delete lief und du den letzten Stand vor dem Schaden brauchst.","caution":"In ein separates Ziel wiederherstellen, nicht in das Live-System, und die benötigten Daten nach der Prüfung übertragen. Das Oplog vom Beginn des Backups bis zur fehlerhaften Operation muss noch im Oplog-Fenster liegen; aus dem Fenster gefallene Operationen lassen sich nicht wiederherstellen. --oplog funktioniert nur mit einem vollständigen Instanz-Dump (nicht kombinierbar mit --db). Konsistente PITR mit mongodump ist auf einem Sharded Cluster nicht möglich; dafür ist ein Dateisystem-Snapshot oder ein Backup-Produkt nötig.","since":"MongoDB 4.4","note":"--oplogLimit=<seconds>:<ordinal> wendet keine Einträge ab dem angegebenen Zeitstempel an; die direkte Angabe des ts der fehlerhaften Operation lässt diese aus. ts-Werte erscheinen im Oplog als Timestamp(t, i). Dumpe das Oplog direkt nach der fehlerhaften Operation, bevor sich das Fenster schließt.","tags":["pitr","point in time","oplogReplay","oplogLimit","rollback","mongorestore"],"untested":true},{"id":"user-role-admin","task":"Eigene Rolle definieren, Benutzer anlegen, Rollen gewähren/entziehen und Passwort ändern","category":"Sicherheit","command":"use admin\ndb.createRole({\n  role: \"opsRead\",\n  privileges: [\n    { resource: { db: \"shop\", collection: \"\" }, actions: [\"find\", \"listCollections\", \"listIndexes\", \"collStats\", \"dbStats\"] },\n    { resource: { cluster: true }, actions: [\"serverStatus\", \"inprog\"] }\n  ],\n  roles: []\n})\ndb.createUser({ user: \"shop_app\", pwd: passwordPrompt(), roles: [{ role: \"readWrite\", db: \"shop\" }, { role: \"opsRead\", db: \"admin\" }] })\ndb.grantRolesToUser(\"shop_app\", [{ role: \"clusterMonitor\", db: \"admin\" }])\ndb.revokeRolesFromUser(\"shop_app\", [{ role: \"clusterMonitor\", db: \"admin\" }])\ndb.changeUserPassword(\"shop_app\", passwordPrompt())\ndb.getUser(\"shop_app\", { showPrivileges: true })\ndb.runCommand({ usersInfo: { forAllDBs: true } })","what":"Definiert eine eigene Rolle mit bestimmten Datenbank- und Cluster-Actions, legt einen Benutzer mit dieser Rolle und eingebauten Rollen an, gewährt und entzieht Rollen, ändert das Passwort und listet Benutzer über alle Datenbanken.","when":"Beim Anlegen von Anwendungs- und Monitoring-Konten nach Least Privilege, bei Zugriffsüberprüfungen oder Passwortrotation.","caution":"Die Datenbank, in der der Benutzer definiert ist (hier admin), wird als authSource verwendet; sie muss zum Connection String der Anwendung passen. Gewähre Anwendungskonten keine weitreichenden Rollen wie root/dbOwner. Rollenänderungen wirken sofort auf bestehende Sessions; eine Passwortänderung trennt offene Verbindungen nicht und gilt für neue.","since":"MongoDB 4.4","note":"passwordPrompt() fragt das Passwort ab, ohne es in der Shell-Historie oder der Prozessliste zu hinterlassen. In Replica Sets und Sharded Clusters werden Benutzer über den Primary/mongos verwaltet und repliziert. Mit showPrivileges: true in getUser siehst du die effektiven Berechtigungen eines Benutzers.","tags":["createUser","createRole","grantRolesToUser","privileges","rbac","password"]},{"id":"fcv-upgrade","task":"featureCompatibilityVersion (fCV) und die Reihenfolge von Major-Upgrades prüfen","category":"Upgrade","command":"// 1) Current fCV (must be the same on all members; targetVersion appears if a transition is incomplete):\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })\n// 2) Upgrade binaries in a rolling fashion (secondaries -> stepDown -> old primary); fCV stays old\ndb.version()\n// 3) After the observation period, raise fCV on the primary (on mongos in a sharded cluster):\ndb.adminCommand({ setFeatureCompatibilityVersion: \"7.0\", confirm: true })\n// 4) If a rollback is needed, fCV is lowered BEFORE the binaries:\ndb.adminCommand({ setFeatureCompatibilityVersion: \"6.0\", confirm: true })","what":"Zeigt die laufende fCV; gibt die Reihenfolge vor, in der Binär-Upgrade und fCV-Upgrade, die getrennte Schritte sind, durchgeführt werden.","when":"Bei der Planung eines Major-Version-Upgrades (z. B. 6.0 -> 7.0), wenn neue Features nach einem Upgrade nicht verfügbar sind, oder vor dem Wechsel zur nächsten Major-Version.","caution":"Major-Versionen lassen sich nicht überspringen: Für 5.0 -> 7.0 zuerst auf 6.0 wechseln und fCV auf 6.0 setzen. Nach Anheben der fCV können Datendateien ins neue Format wechseln, und ein Binär-Downgrade wird schwieriger; hebe die fCV erst an, nachdem die neue Version einige Tage fehlerfrei gelaufen ist. Ein Binär-Downgrade wird bei Community ab 7.0 offiziell nicht unterstützt; stütze den Rückfallplan auf ein Backup. Prüfe vor dem Upgrade die Treiberkompatibilität.","since":"MongoDB 7.0","note":"Seit 7.0 erfordert setFeatureCompatibilityVersion confirm: true. Schlägt ein fCV-Übergang fehl, erscheint targetVersion/previousVersion in der Ausgabe von getParameter; denselben Befehl erneut ausführen, um ihn abzuschließen. Der fCV-Wert wird innerhalb des Replica Sets repliziert.","tags":["fcv","featureCompatibilityVersion","upgrade","major upgrade","major","downgrade"]}]},"recipes/redis.json":{"engineId":"redis","engine":"Redis","order":6,"recipes":[{"id":"bgrewriteaof","task":"AOF-Datei neu schreiben (kompaktieren)","category":"Persistenz","command":"BGREWRITEAOF","what":"Schreibt die AOF-Datei im Hintergrund in kompakterer Form neu.","when":"Wenn die AOF-Datei aufgebläht ist, um die Neustartzeit zu verkürzen.","caution":"Speicher- und IO-Verbrauch steigen während des Forks; zu Spitzenzeiten vorsichtig sein.","tags":["aof","rewrite","persistence","persistence"]},{"id":"bgsave","task":"RDB-Snapshot im Hintergrund erstellen","category":"Persistenz","command":"BGSAVE","what":"Schreibt über einen geforkten Kindprozess im Hintergrund einen RDB-Snapshot auf die Platte.","when":"Wenn du ein Backup oder einen Snapshot erstellen willst.","caution":"Beim Fork kann der Speicherverbrauch wegen Copy-on-Write vorübergehend steigen.","tags":["rdb","bgsave","snapshot","persistence","backup"]},{"id":"save","task":"Synchronen RDB-Snapshot erstellen","category":"Persistenz","command":"SAVE","what":"Schreibt einen RDB-Snapshot synchron im Hauptthread auf die Platte.","when":"Nur vor Wartung/Herunterfahren, wenn keine Clients bedient werden.","caution":"SAVE blockiert den gesamten Server, und bis zum Abschluss werden keine Befehle verarbeitet; bevorzuge in der Produktion BGSAVE.","tags":["rdb","save","snapshot","blocking","persistence"]},{"id":"lastsave","task":"Zeitpunkt des letzten erfolgreichen Snapshots anzeigen","category":"Persistenz","command":"LASTSAVE","what":"Liefert den Unix-Zeitstempel des letzten erfolgreichen RDB-Speichervorgangs.","when":"Um zu prüfen, ob ein BGSAVE abgeschlossen ist.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["lastsave","rdb","timestamp","check","persistence"]},{"id":"config-rewrite","task":"Laufende Konfiguration auf die Platte schreiben","category":"Persistenz","command":"CONFIG REWRITE","what":"Schreibt zur Laufzeit mit CONFIG SET vorgenommene Änderungen dauerhaft in die Datei redis.conf.","when":"Wenn Laufzeitänderungen einen Neustart überdauern sollen.","caution":"Überschreibt die vorhandene Datei redis.conf; die Datei muss definiert und beschreibbar sein.","tags":["config","rewrite","persistent","conf","setting"]},{"id":"config-appendonly","task":"AOF aktivieren oder deaktivieren","category":"Persistenz","command":"CONFIG SET appendonly yes","what":"Aktiviert (yes) oder deaktiviert (no) die AOF-Persistenz zur Laufzeit.","when":"Wenn du die AOF-Persistenz ohne Neustart ein- oder ausschalten willst.","caution":"Das Deaktivieren von AOF schwächt die Dauerhaftigkeitsgarantie; beim Aktivieren erzeugt das initiale AOF-Rewrite IO-Last.","tags":["aof","appendonly","config","persistence","setting"]},{"id":"config-save-rdb","task":"Regeln für RDB-Snapshots festlegen","category":"Persistenz","command":"CONFIG SET save \"900 1 300 10 60 10000\"","what":"Legt die Regeln (Sekunden/Anzahl Änderungen) fest, die automatische RDB-Snapshots auslösen.","when":"Wenn du die Häufigkeit automatischer Snapshots ändern willst.","caution":"Übergibst du einen leeren Wert (\"\"), wird das automatische RDB-Speichern vollständig deaktiviert, und die Persistenz kann verloren gehen.","tags":["rdb","save","config","snapshot","setting"]},{"id":"replicaof","task":"Knoten zur Replica machen","category":"Replikation","command":"REPLICAOF redis-master 6379","what":"Macht diesen Knoten zur Replica des Masters unter dem angegebenen Host und Port und startet die Synchronisierung.","when":"Beim Hinzufügen einer neuen Lese-Replica oder beim Zuweisen von Rollen.","caution":"Die vorhandenen Daten auf dem Knoten, der zur Replica wird, werden verworfen, und vom Master wird eine Full Sync gezogen.","since":"Redis 5.0","note":"Der Befehl REPLICAOF ersetzte in Redis 5.0 SLAVEOF; der alte Befehl SLAVEOF funktioniert weiterhin als Alias.","tags":["replicaof","replica","replication","master","sync"]},{"id":"replicaof-no-one","task":"Replica zum Master hochstufen","category":"Replikation","command":"REPLICAOF NO ONE","what":"Trennt die Replica-Verbindung des Knotens und macht ihn zu einem eigenständigen Master.","when":"Wenn du in einem Failover-Szenario eine Replica hochstufen willst.","caution":"Verwalte die Topologie nach der Hochstufung sorgfältig, damit es keine Datendivergenz (Split-Brain) mit dem alten Master gibt.","since":"Redis 5.0","note":"REPLICAOF NO ONE ersetzte in Redis 5.0 SLAVEOF NO ONE; das alte SLAVEOF NO ONE funktioniert weiterhin als Alias.","tags":["replicaof","promote","master","failover","replication"]},{"id":"cluster-failover","task":"Manuelles Failover durchführen","category":"Cluster","command":"CLUSTER FAILOVER","what":"Startet, auf einer Replica ausgeführt, ein kontrolliertes Failover, indem diese Replica zum Master hochgestuft wird.","when":"Wenn du die Master-Rolle vor einer Wartung an eine Replica übergeben willst.","caution":"Dieser Befehl muss auf der Ziel-Replica ausgeführt werden, nicht auf dem Master selbst.","tags":["cluster","failover","promote","maintenance","replication"]},{"id":"cluster-meet","task":"Knoten zum Cluster hinzufügen","category":"Cluster","command":"CLUSTER MEET 10.0.0.12 6379","what":"Nimmt den Knoten unter der angegebenen IP und dem Port in das Gossip-Netz des Clusters auf.","when":"Beim Hinzufügen eines neuen Knotens zum Cluster (Skalierung).","caution":"Erfolgt nach dem Hinzufügen des Knotens keine Slot-Zuweisung (ADDSLOTS) oder kein Reshard, erhält er keine Daten.","tags":["cluster","meet","node","add","node"]},{"id":"cluster-addslots","task":"Cluster-Slots zuweisen","category":"Cluster","command":"CLUSTER ADDSLOTS 0 1 2 3 4 5","what":"Weist diesem Master-Knoten die angegebenen Hash-Slots zu.","when":"Beim manuellen Konfigurieren eines neuen Master-Knotens oder beim Korrigieren der Slot-Verteilung.","caution":"Derselbe Slot kann nicht mehr als einem Knoten zugewiesen werden; sind nicht alle 16384 Slots vollständig zugewiesen, geht der Cluster in den Zustand down.","tags":["cluster","addslots","slot","assign","node"]},{"id":"info-replication","task":"Replikationsstatus anzeigen","category":"Cluster","command":"INFO replication","what":"Zeigt Rolle (master/replica), verbundene Replicas und Informationen zum Replikations-Offset.","when":"Beim Prüfen von Replikationsverzögerung und Verbindungsstatus.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["info","replication","status","replication","lag"]},{"id":"cluster-info","task":"Cluster-Status anzeigen","category":"Cluster","command":"CLUSTER INFO","what":"Zeigt den Gesamtzustand des Clusters (ok/fail), die Anzahl zugewiesener Slots und die Anzahl bekannter Knoten.","when":"Beim Prüfen von Cluster-Health und Slot-Integrität.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["cluster","info","status","slot","health"]},{"id":"config-maxmemory","task":"maxmemory und die Policy festlegen","category":"Speicher","command":"CONFIG SET maxmemory 4gb maxmemory-policy allkeys-lru","what":"Legt das Speicherlimit und die Eviction Policy fest, die bei Überschreiten des Limits angewendet wird. Redis 7.0+ akzeptiert mehrere Parameter in einem einzigen CONFIG SET; in älteren Versionen zwei getrennte CONFIG SET-Befehle ausführen.","when":"Wenn du den Speicherverbrauch begrenzen und das Eviction-Verhalten festlegen willst.","caution":"Mit der Policy noeviction schlagen Schreibzugriffe fehl, sobald das Limit erreicht ist; ein falsches Limit kann zu Datenverlust/Ausfall führen.","tags":["maxmemory","policy","memory","eviction","config"]},{"id":"memory-doctor","task":"Speicher analysieren","category":"Speicher","command":"MEMORY DOCTOR","what":"Analysiert den Speicherverbrauch und erstellt einen lesbaren Bericht über mögliche Probleme.","when":"Zur Diagnose bei Verdacht auf hohen Speicherverbrauch oder Fragmentierung.","caution":"Reine Leseanalyse; für Details kannst du auch den Befehl MEMORY STATS verwenden.","tags":["memory","doctor","memory","analysis","diagnostics"]},{"id":"slowlog-get","task":"Log langsamer Befehle anzeigen","category":"Wartung","command":"SLOWLOG GET 10","what":"Liefert die 10 neuesten Einträge langsamer Befehle (Dauer, Zeitpunkt, Argumente) aus dem Slowlog.","when":"Beim Ermitteln langsamer Befehle und Untersuchen von Performance-Engpässen.","caution":"Reiner Lesebefehl; mit SLOWLOG RESET werden die Einträge gelöscht.","tags":["slowlog","get","slow","diagnostics","performance"]},{"id":"slowlog-config","task":"Schwellenwert für langsame Befehle festlegen","category":"Wartung","command":"CONFIG SET slowlog-log-slower-than 10000","what":"Legt den Schwellenwert in Mikrosekunden fest, ab dem ein Befehl im Slowlog protokolliert wird (10000 = 10 ms).","when":"Wenn du die Empfindlichkeit des Slowlogs anpassen willst.","caution":"Der Wert 0 protokolliert jeden Befehl und erzeugt Overhead; -1 deaktiviert das Slowlog.","tags":["slowlog","threshold","threshold","config","slow"]},{"id":"slowlog-reset","task":"Log langsamer Befehle zurücksetzen","category":"Wartung","command":"SLOWLOG RESET","what":"Löscht alle Einträge im Slowlog.","when":"Wenn du die Einträge löschst, um ein neues Beobachtungsfenster zu beginnen.","caution":"Die vorhandene Historie langsamer Befehle wird endgültig gelöscht.","tags":["slowlog","reset","reset","clear","maintenance"]},{"id":"client-kill","task":"Client-Verbindung beenden","category":"Wartung","command":"CLIENT KILL ADDR 10.0.0.5:53210","what":"Beendet die Client-Verbindung unter der angegebenen Adresse.","when":"Wenn du einen hängenden oder fehlerhaften Client beenden willst.","caution":"Die Verbindung wird sofort getrennt; laufende Operationen können unvollständig bleiben. Achte darauf, die richtige Adresse zu wählen.","tags":["client","kill","client","kill","maintenance"]},{"id":"client-list","task":"Verbundene Clients auflisten","category":"Wartung","command":"CLIENT LIST","what":"Listet alle verbundenen Clients mit Details wie Adresse, Leerlaufzeit und letztem Befehl.","when":"Beim Untersuchen von Verbindungen oder beim Finden eines problematischen Clients.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["client","list","client","list","connection"]},{"id":"config-get","task":"Parameterwert anzeigen","category":"Laufzeit","command":"CONFIG GET maxmemory-policy","what":"Liefert den aktuellen Wert eines oder mehrerer Konfigurationsparameter (Glob-Muster werden unterstützt).","when":"Beim Prüfen des Laufzeitwerts einer Einstellung.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["config","get","parameter","view","runtime"]},{"id":"config-set","task":"Parameterwert ändern","category":"Laufzeit","command":"CONFIG SET appendfsync everysec","what":"Ändert einen Konfigurationsparameter zur Laufzeit ohne Neustart.","when":"Wenn du ein Serververhalten sofort anpassen willst.","caution":"Die Änderung ist vorübergehend; mit CONFIG REWRITE dauerhaft machen. Ein falscher Wert kann Performance/Dauerhaftigkeit verschlechtern.","tags":["config","set","parameter","runtime","setting"]}]},"recipes/redis-ext.json":{"engineId":"redis","engine":"Redis","order":2,"recipes":[{"id":"mem-fragmentation","task":"Speicherverbrauch und Fragmentierung analysieren, Active Defrag aktivieren","category":"Speicher","command":"INFO memory\n# Fields to check: used_memory_human, used_memory_rss_human, mem_fragmentation_ratio, mem_fragmentation_bytes, allocator_frag_ratio\nMEMORY STATS\n# If fragmentation is high and persistent, enable active defrag (requires jemalloc):\nCONFIG SET activedefrag yes\nCONFIG SET active-defrag-ignore-bytes 100mb\nCONFIG SET active-defrag-threshold-lower 10\n# Ask the allocator to return free pages to the OS:\nMEMORY PURGE","what":"Zeigt den Unterschied zwischen dem Speicher, den Redis für Daten belegt (used_memory), und dem vom OS gesehenen RSS, die Fragmentierungsrate und die Speicheraufschlüsselung; aktiviert bei Bedarf Active Defrag zur Laufzeit.","when":"Wenn RSS deutlich größer als der Datenbestand ist, der Speicher nach Massenlöschungen/Ablauf nicht sinkt oder ein OOM-Risiko entsteht.","caution":"mem_fragmentation_ratio täuscht bei kleinen Datenbeständen (z. B. 4.0 bei 2 MB Daten); für den absoluten Wert mem_fragmentation_bytes betrachten. Ein Wert unter 1 bedeutet Swapping, nicht Fragmentierung, und verursacht Latenz. Active Defrag verbraucht CPU; begrenze es mit active-defrag-cycle-max. Für eine dauerhafte Einstellung ist CONFIG REWRITE oder redis.conf nötig.","since":"Redis 4.0","note":"Active Defrag funktioniert nur mit Builds, die mit dem mit Redis ausgelieferten jemalloc kompiliert wurden. allocator_frag_ratio zeigt interne Fragmentierung, die Defrag beheben kann; allocator_rss_ratio zeigt Platz, den der Allocator nicht an das OS zurückgegeben hat. Fragmentierung wächst schneller bei Workloads, die häufig Werte unterschiedlicher Größe aktualisieren.","tags":["memory","ram","fragmentation","activedefrag","rss","info memory"]},{"id":"bigkeys-memkeys","task":"Große Keys finden (--bigkeys, --memkeys, MEMORY USAGE)","category":"Speicher","command":"# Largest keys per type by element count:\nredis-cli -h redis1 -p 6379 --bigkeys -i 0.01\n# By memory size:\nredis-cli -h redis1 -p 6379 --memkeys -i 0.01\n# Size and length together, top 20 keys (redis-cli 7.4+):\nredis-cli -h redis1 -p 6379 --keystats --top 20 -i 0.01\n# Memory usage (SAMPLES 0: all elements) and encoding of a single suspicious key:\nredis-cli -h redis1 -p 6379 MEMORY USAGE kullanici:1001:sepet SAMPLES 0\nredis-cli -h redis1 -p 6379 OBJECT ENCODING kullanici:1001:sepet","what":"Durchsucht den Keyspace mit SCAN und meldet pro Typ die Keys mit den meisten Elementen und dem höchsten Speicherverbrauch; zeigt den tatsächlichen Speicherverbrauch und das Encoding eines verdächtigen Keys.","when":"Wenn der Speicher unerwartet wächst, ein einzelner Befehl (HGETALL, SMEMBERS, DEL) Latenz verursacht oder ein Knoten in einem Cluster mehr Speicher nutzt als die anderen.","caution":"Der Scan verwendet SCAN und blockiert den Server nicht, durchläuft aber den gesamten Keyspace; -i 0.01 pausiert alle 100 SCAN-Aufrufe, um die Last zu senken. Das Löschen eines großen Keys mit DEL blockiert den Hauptthread; UNLINK verwenden. MEMORY USAGE mit SAMPLES 0 ist bei großen Collections O(N). Die Ausführung auf einer Replica belastet den Master nicht.","since":"Redis 6.0","note":"--bigkeys betrachtet Elementanzahlen, --memkeys MEMORY USAGE; bei Typen wie list/hash können sie unterschiedliche Keys hervorheben. Kleine Hashes/Zsets/Lists nutzen das Encoding listpack und belegen viel weniger Speicher; werden Schwellenwerte wie hash-max-listpack-entries überschritten, werden sie in hashtable umgewandelt. In einem Cluster auf jedem Master separat ausführen.","tags":["bigkeys","memkeys","big key","memory usage","keystats","large key"]},{"id":"hotkeys-lfu","task":"Hot Keys finden (--hotkeys, OBJECT FREQ)","category":"Performance","command":"# Requires an LFU-based policy; note the current policy:\nCONFIG GET maxmemory-policy\nCONFIG SET maxmemory-policy allkeys-lfu\n# Hottest keys by access frequency:\nredis-cli -h redis1 -p 6379 --hotkeys -i 0.01\n# LFU counter of a single key:\nOBJECT FREQ urun:populer:liste\n# Revert to the previous policy when the analysis is done:\nCONFIG SET maxmemory-policy noeviction","what":"Listet anhand von LFU-Zählern die am häufigsten angesprochenen Keys; zeigt den Zugriffshäufigkeitszähler eines einzelnen Keys.","when":"Wenn die CPU eines Knotens gegen 100% geht, ein Slot/Knoten in einem Cluster weit mehr Verkehr erhält als die anderen oder die Netzwerkbandbreite durch einen einzelnen Key ausgelastet ist.","caution":"--hotkeys funktioniert nur, wenn maxmemory-policy *-lfu ist. Eine Änderung der Policy ändert auch das Eviction-Verhalten: Ist maxmemory gesetzt und war die vorherige Policy volatile-* oder noeviction, kann allkeys-lfu auch Keys ohne TTL verdrängen. Halte die Analyse kurz und setze die Policy zurück. LFU-Zähler sind logarithmisch und nehmen mit der Zeit ab; sie sind keine absoluten Zugriffszahlen.","since":"Redis 4.0","note":"Nach der Policy-Änderung ist etwas Verkehr nötig, bevor die LFU-Zähler aussagekräftig werden. Lässt sich die Policy nicht ändern, ist eine kurze MONITOR-Stichprobe eine Alternative, aber MONITOR senkt den Durchsatz auf ausgelasteten Systemen erheblich. Für heiße Lese-Keys sind clientseitiges Caching (CLIENT TRACKING) oder das Aufteilen des Keys die dauerhafte Lösung.","tags":["hotkeys","hot keys","lfu","object freq","hot key","cpu"]},{"id":"scan-unlink-pattern","task":"Keys nach Muster ohne KEYS finden und sicher löschen (SCAN + UNLINK)","category":"Wartung","command":"# Cursor-based scan instead of KEYS (call again with the returned cursor until it returns 0):\nSCAN 0 MATCH oturum:* COUNT 1000 TYPE string\n# Number of keys matching the pattern:\nredis-cli -h redis1 -p 6379 --scan --pattern 'oturum:*' --count 1000 | wc -l\n# Bulk delete in batches of 500, freeing memory in the background:\nredis-cli -h redis1 -p 6379 --scan --pattern 'oturum:*' --count 1000 | xargs -r -n 500 redis-cli -h redis1 -p 6379 UNLINK","what":"Durchsucht den Keyspace mit einem Cursor, ohne den Server zu blockieren, zählt Keys, die zu Muster und Typ passen, und löscht sie massenhaft mit UNLINK, wobei der Speicher im Hintergrund freigegeben wird.","when":"Beim Aufräumen von Keys einer alten Anwendungsversion, um herauszufinden, wie viele Keys unter einem Präfix existieren, oder wenn sonst KEYS in der Produktion nötig wäre.","caution":"KEYS liefert den gesamten Keyspace auf einmal und blockiert den Server bei großen Datenmengen für Sekunden; nicht in der Produktion verwenden. SCAN gibt für während des Scans hinzugefügte/entfernte Keys keine vollständige Garantie und kann denselben Key mehrfach liefern. Keynamen mit Leerzeichen oder Anführungszeichen brechen mit xargs. Prüfe vor dem Löschen die Anzahl und einige Stichproben; es gibt kein Rückgängig.","since":"Redis 4.0","note":"Die Option TYPE von SCAN kam mit 6.0. COUNT ist ein Hinweis und garantiert nicht die Anzahl der zurückgegebenen Elemente. In einem Cluster durchsucht --scan nur den verbundenen Knoten; auf jedem Master ausführen. UNLINK gibt große Werte über lazyfree in einem separaten Thread frei; für kleine Keys kostet es so viel wie DEL.","tags":["scan","unlink","keys","bulk delete","pattern","match"]},{"id":"latency-diagnosis","task":"Ursache von Latenz diagnostizieren (LATENCY DOCTOR, Histogramm, commandstats)","category":"Performance","command":"# Enable the latency monitor (record events above 100 ms):\nCONFIG SET latency-monitor-threshold 100\nLATENCY LATEST\nLATENCY HISTORY command\nLATENCY DOCTOR\n# Per-command latency distribution and percentiles:\nLATENCY HISTOGRAM GET SET HGETALL\nINFO latencystats\n# Most expensive commands by total CPU time:\nredis-cli -h redis1 -p 6379 INFO commandstats | sort -t= -k3 -rn | head\n# On the server host, baseline latency outside Redis (VM/steal detection, 30 s):\nredis-cli --intrinsic-latency 30\nLATENCY RESET","what":"Der Latency Monitor erfasst Latenzspitzen durch Ereignisse wie Fork, AOF-fsync, den Expire-Zyklus und langsame Befehle; zeigt Latenz-Perzentile pro Befehl und die teuersten Befehle nach gesamter CPU-Zeit; misst die Basislatenz auf Host-Ebene.","when":"Wenn die Anwendung p99-Latenzspitzen sieht und Beschwerden über Langsamkeit anhalten, obwohl SLOWLOG sauber ist.","caution":"latency-monitor-threshold ist standardmäßig 0 (aus); stelle nach der Diagnose den alten Wert wieder her. SLOWLOG misst nur die Ausführungszeit von Befehlen und sieht keine Netzwerk- oder Warteschlangenzeiten; LATENCY-Events erfassen auch serverinterne Hänger wie Fork und fsync. --intrinsic-latency verbraucht CPU und muss auf der Maschine laufen, auf der der Redis-Server läuft.","since":"Redis 7.0","note":"LATENCY HISTOGRAM und INFO latencystats kamen mit 7.0 (latency-tracking ist standardmäßig aktiv). Dominieren Fork-Events in LATENCY HISTORY, Datenbestandsgröße und THP (transparent huge pages) prüfen; sind aof-fsync-always/aof-write-Events hoch, die Plattenlatenz prüfen. Befehle mit hohem usec_per_call in commandstats deuten auf O(N)-Nutzung hin.","tags":["latency","slowness","latency doctor","histogram","commandstats","intrinsic-latency"]},{"id":"eviction-oom-diag","task":"Eviction, Ablauf und OOM-Fehler diagnostizieren","category":"Speicher","command":"INFO stats\n# Fields to check: evicted_keys, expired_keys, expired_stale_perc, keyspace_hits, keyspace_misses, total_eviction_exceeded_time (7.0+)\nINFO keyspace\nCONFIG GET maxmemory\nCONFIG GET maxmemory-policy\nINFO memory\n# The value that triggers eviction: used_memory - mem_not_counted_for_evict, compared with maxmemory","what":"Zeigt die Anzahl verdrängter und abgelaufener Keys, die Cache Hit Ratio, den Anteil der Keys mit TTL und den Speicherwert, der die Eviction auslöst, im Verhältnis zu maxmemory.","when":"Wenn die Anwendung 'OOM command not allowed when used memory > maxmemory' erhält, die Cache Hit Ratio sinkt oder Keys unerwartet zu verschwinden scheinen.","caution":"Mit der Policy noeviction erhalten Schreibbefehle bei Erreichen von maxmemory einen OOM-Fehler, während Lesezugriffe weiter funktionieren. Mit volatile-*-Policies werden Keys ohne TTL nie verdrängt; gibt es keine Keys mit TTL, verhält es sich wie noeviction (expires in INFO keyspace prüfen). Setze maxmemory nicht zu nah an den physischen RAM: Lass Platz für Replikationspuffer und Copy-on-Write beim Fork.","since":"Redis 6.0","note":"Hit Ratio = keyspace_hits / (keyspace_hits + keyspace_misses). Wächst evicted_keys stetig, während die Hit Ratio sinkt, reicht der Speicher nicht aus. Ein hohes expired_stale_perc bedeutet einen hohen Anteil abgelaufener, noch nicht entfernter Keys; der Expire-Zyklus lässt sich mit active-expire-effort beschleunigen. Replica-Puffer und der AOF-Puffer sind in mem_not_counted_for_evict enthalten.","tags":["eviction","oom","maxmemory","expire","hit ratio","evicted_keys"]},{"id":"client-memory","task":"Verbindungen finden, die den Speicher aufblähen (Output Buffer, Query Buffer, maxmemory-clients)","category":"Speicher","command":"INFO clients\n# Top 10 connections by tot-mem (tot-mem, omem, qbuf, id, addr, user, cmd, age):\nredis-cli -h redis1 -p 6379 CLIENT LIST | awk '{for(i=1;i<=NF;i++){split($i,a,\"=\"); f[a[1]]=a[2]} print f[\"tot-mem\"], f[\"omem\"], f[\"qbuf\"], f[\"id\"], f[\"addr\"], f[\"user\"], f[\"cmd\"], f[\"age\"]}' | sort -rn | head -10\nCLIENT LIST TYPE pubsub\nCONFIG GET client-output-buffer-limit\n# Upper limit for the total memory of all clients (largest clients are closed when exceeded):\nCONFIG SET maxmemory-clients 5%","what":"Sortiert Verbindungen nach Gesamtspeicher (tot-mem), Output Buffer (omem) und Query Buffer (qbuf); zeigt Pub/Sub-Verbindungen und Pufferlimits; legt eine Gesamtobergrenze für Client-Speicher fest.","when":"Wenn used_memory ohne Datenwachstum steigt, mem_clients_normal hoch aussieht oder ein langsam konsumierender Pub/Sub-/MONITOR-Client den Speicher füllt.","caution":"Ein großes omem deutet meist auf einen Client hin, der Ergebnisse langsam liest (große KEYS-/HGETALL-Antwort, langsamer Pub/Sub-Subscriber, ein offen gelassenes MONITOR). Wird maxmemory-clients überschritten, werden die Clients mit dem meisten Speicher ohne Warnung geschlossen; Replica- und Master-Verbindungen sind ausgenommen, Anwendungsverbindungen können aber abbrechen. Für kritische Verbindungen CLIENT NO-EVICT on verwenden.","since":"Redis 7.0","note":"maxmemory-clients kam mit 7.0 und ist standardmäßig 0 (unbegrenzt). client-output-buffer-limit wendet harte/weiche Limits für die Klassen pubsub und replica an; normale Clients sind standardmäßig unbegrenzt. Eine problematische Verbindung mit CLIENT KILL ID <id> schließen (siehe client-kill).","tags":["client list","omem","output buffer","maxmemory-clients","connections","pubsub"]},{"id":"persistence-health","task":"RDB-/AOF-Fehlerzustand, Fork-Dauer und die Ursache von MISCONF diagnostizieren","category":"Persistenz","command":"INFO persistence\n# Critical fields: rdb_last_bgsave_status, aof_last_bgrewrite_status, aof_last_write_status, rdb_changes_since_last_save, aof_delayed_fsync\nINFO stats\n# latest_fork_usec: duration of the last fork (microseconds), total_forks\nCONFIG GET stop-writes-on-bgsave-error\nCONFIG GET dir\nCONFIG GET appendfsync","what":"Zeigt, ob der letzte RDB-Snapshot und das letzte AOF-Rewrite/-Schreiben erfolgreich waren, wie viele Keys sich seit dem letzten Speichern geändert haben, die Anzahl verzögerter fsyncs und die Fork-Dauer.","when":"Wenn die Anwendung 'MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk' erhält, BGSAVE/BGREWRITEAOF fehlschlägt oder forkbedingte Latenz auftritt.","caution":"Mit stop-writes-on-bgsave-error yes (Standard) werden nach einem fehlgeschlagenen BGSAVE alle Schreibzugriffe abgelehnt. Den Fehler durch Setzen auf 'no' zu unterdrücken verdeckt das Risiko eines Datenverlusts; behebe zuerst die Ursache: Platte voll, keine Schreibberechtigung auf dir oder der Fork bekommt keinen Speicher (vm.overcommit_memory=0). Die Fork-Dauer kann bei einem großen Datenbestand Hunderte Millisekunden erreichen, und Redis antwortet in dieser Zeit nicht.","since":"Redis 7.0","note":"vm.overcommit_memory=1 und deaktivierte transparent huge pages unter Linux sind die offiziellen Empfehlungen von Redis. Ein wachsendes aof_delayed_fsync bedeutet, dass die Platte mit fsync nicht mithält (appendfsync everysec). Fehlerdetails stehen im Redis-Log ('Background saving error', 'Can't save in background: fork').","tags":["misconf","bgsave","aof","fork","persistence","durability"]},{"id":"aof-repair","task":"Redis reparieren, das wegen eines beschädigten AOF nicht startet (redis-check-aof, redis-check-rdb)","category":"Persistenz","command":"# With Redis stopped, copy the files first:\ncp -a /var/lib/redis/appendonlydir /var/lib/redis/appendonlydir.bak\n# Check (7.0+ multi-part AOF: pass the manifest file):\nredis-check-aof /var/lib/redis/appendonlydir/appendonly.aof.manifest\n# Repair by truncating everything after the first corrupt point (asks for confirmation):\nredis-check-aof --fix /var/lib/redis/appendonlydir/appendonly.aof.manifest\n# Verify the RDB file:\nredis-check-rdb /var/lib/redis/dump.rdb","what":"Validiert die AOF-Dateien (base + incr + manifest) und die RDB-Datei; schneidet das unvollständige oder beschädigte Ende des AOF ab, damit Redis wieder starten kann.","when":"Wenn Redis nach einem Stromausfall, einer vollen Platte oder einem abrupten Herunterfahren mit 'Bad file format reading the append only file' oder 'Unexpected EOF' nicht startet.","caution":"--fix löscht alles von der ersten beschädigten Stelle bis zum Dateiende; liegt die Beschädigung in der Mitte der Datei, gehen alle späteren Schreibvorgänge verloren. Prüfe vor dem Bestätigen in der Ausgabe (ok_up_to, diff), wie viele Bytes abgeschnitten werden. Ein unvollständiger letzter Befehl (z. B. ein großes HSET) wird vollständig verworfen. Erstelle immer zuerst eine Kopie.","since":"Redis 7.0","note":"Mit aof-load-truncated yes (Standard) wird ein nur am Ende abgeschnittenes AOF beim Start automatisch gekürzt; redis-check-aof ist meist nötig, wenn die Beschädigung in der Mitte der Datei liegt. Beim Multi-Part-AOF ab 7.0 liegen die Dateien im Verzeichnis appenddirname, und die base-Datei kann im RDB-Format sein. Bei einem beschädigten RDB diagnostiziert redis-check-rdb nur und repariert nicht.","tags":["aof","redis-check-aof","corrupted","corrupt","repair","redis-check-rdb"]},{"id":"replication-backlog","task":"Replikationsabbrüche und eine endlose Full-Resync-Schleife beheben (Backlog, Replica-Puffer)","category":"Replikation","command":"INFO replication\n# Master: connected_slaves, slaveN:...,state=online,offset=,lag=  Replica: master_link_status, master_last_io_seconds_ago, master_sync_in_progress\nINFO stats\n# sync_full / sync_partial_ok / sync_partial_err: repeated full syncs = backlog too small\nCONFIG GET repl-backlog-size\nCONFIG SET repl-backlog-size 256mb\n# If the replica buffer exceeds its limit during a full sync, the link drops and sync starts over:\nCONFIG GET client-output-buffer-limit\nCONFIG SET client-output-buffer-limit \"replica 1gb 512mb 120\"","what":"Zeigt auf Master und Replica Verbindungsstatus, Offset-Differenz und Zähler für Partial/Full Sync; vergrößert den Replikations-Backlog, damit kurze Verbindungsabbrüche nicht zu Full Syncs werden, und das Output-Buffer-Limit der Replica, damit eine Full Sync nicht mittendrin abbricht.","when":"Wenn eine Replica ständig in 'MASTER <-> REPLICA sync started' kreist, der Zähler sync_full wächst oder das Master-Log 'scheduled to be closed ASAP for overcoming of output buffer limits' zeigt.","caution":"Jede Full Sync bedeutet einen Fork + RDB-Erzeugung auf dem Master; die Schleife belastet Speicher und Platte des Masters. repl-backlog-size wird aus dem Speicher des Masters belegt. Ein höheres Replica-Pufferlimit bedeutet, dass der Master während einer Full Sync entsprechend mehr Speicher nutzen kann; beziehe das in die maxmemory-Berechnung ein. Für dauerhafte Änderungen ist CONFIG REWRITE oder redis.conf nötig.","since":"Redis 4.0","note":"Grobe Dimensionierung des Backlogs: Schreibvolumen des Masters pro Sekunde (Anstieg von master_repl_offset) x die zu tolerierende Dauer eines Verbindungsabbruchs. Der Klassenname 'replica' in client-output-buffer-limit wird seit 5.0 anstelle von 'slave' akzeptiert; die Ausgabe von CONFIG GET kann weiterhin slave zeigen. Ist die Platte langsam, streamt repl-diskless-sync yes (Standard ab 7.0) das RDB über den Socket, ohne es auf die Platte zu schreiben.","tags":["replication","backlog","full sync","psync","replica","output buffer"]},{"id":"sentinel-status","task":"Sentinel-Zustand, aktuellen Master und Quorum prüfen","category":"Sentinel","command":"redis-cli -p 26379 SENTINEL MASTERS\nredis-cli -p 26379 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster\nredis-cli -p 26379 SENTINEL REPLICAS mymaster\nredis-cli -p 26379 SENTINEL SENTINELS mymaster\n# Are there enough sentinels (quorum and majority) for a failover:\nredis-cli -p 26379 SENTINEL CKQUORUM mymaster\nredis-cli -p 26379 INFO sentinel","what":"Listet die von Sentinel überwachten Master, die aktuelle Master-Adresse, die bekannten Replicas und andere Sentinels; prüft, ob Quorum und Mehrheit für ein Failover verfügbar sind.","when":"Wenn sich die Anwendung mit dem falschen Knoten verbindet, beim Prüfen der Topologie nach einem Failover und vor jeder Wartung.","caution":"Jeder Sentinel meldet seine eigene Sicht; GET-MASTER-ADDR-BY-NAME muss auf allen Sentinels dieselbe Adresse liefern, sonst liegt eine Netzwerkpartition oder eine Konfigurationsabweichung vor. s_down/o_down im Feld flags oder disconnected bei einer Replica erfordert dringende Untersuchung. Der Sentinel-Port (26379) sollte in der Firewall nur zwischen Sentinels und Clients offen sein.","since":"Redis 5.0","note":"SENTINEL REPLICAS ersetzte in 5.0 SENTINEL SLAVES (der alte Name funktioniert weiterhin). Die Anzahl der Sentinels sollte ungerade und mindestens 3 sein, verteilt auf verschiedene Fehlerdomänen (Host/Rack/AZ). Nutzt Redis ACL/Passwörter, müssen sentinel auth-user/auth-pass in sentinel.conf gesetzt sein.","tags":["sentinel","master","quorum","ckquorum","topology","failover"],"untested":true},{"id":"sentinel-failover","task":"Manuelles Failover mit Sentinel durchführen und die Topologie bereinigen","category":"Sentinel","command":"# First check quorum and an eligible replica:\nredis-cli -p 26379 SENTINEL CKQUORUM mymaster\nredis-cli -p 26379 SENTINEL REPLICAS mymaster\n# Manual failover without waiting for agreement from other sentinels:\nredis-cli -p 26379 SENTINEL FAILOVER mymaster\n# Verify the new master (must be the same on all sentinels):\nredis-cli -p 26379 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster\n# If old/removed replicas remain listed, reset sentinels one at a time, 30 s apart:\nredis-cli -p 26379 SENTINEL RESET mymaster","what":"Übergibt die Master-Rolle auch bei gesundem Master an die am besten geeignete Replica; prüft anschließend den neuen Master und entfernt nicht mehr existierende Replicas aus dem Gedächtnis der Sentinels.","when":"Vor einer geplanten Wartung am Host des Masters, bei einer Failover-Übung oder beim Verlegen des Masters in ein anderes Rechenzentrum.","caution":"Während des Failovers gibt es eine Schreibunterbrechung von einigen Sekunden, und auf den Master geschriebene, noch nicht replizierte Daten können verloren gehen. Replicas mit replica-priority 0 werden nie gewählt; gibt es keine geeignete Replica, liefert der Befehl einen Fehler. Läuft SENTINEL RESET auf allen Sentinels gleichzeitig, gehen Topologieinformationen vorübergehend verloren; auf jedem einzeln und zeitversetzt ausführen. Stelle sicher, dass Clients einen Treiber verwenden, der Sentinel nach der Master-Adresse fragt.","since":"Redis 5.0","note":"SENTINEL FAILOVER fragt andere Sentinels nicht nach Zustimmung, verbreitet aber die neue Config Epoch. Auswahlreihenfolge: replica-priority (niedrigere zuerst), Replikations-Offset (größerer zuerst), Run ID. Kehrt der alte Master nach dem Failover zurück, konfiguriert Sentinel ihn automatisch als Replica des neuen Masters.","tags":["sentinel","failover","manual failover","master","reset","switchover"],"untested":true},{"id":"cluster-check-fix","task":"Cluster-Health prüfen und Slot-Inkonsistenzen reparieren (--cluster check/fix)","category":"Cluster","command":"redis-cli -h node1 -p 6379 CLUSTER INFO\nredis-cli -h node1 -p 6379 CLUSTER NODES\nredis-cli -h node1 -p 6379 CLUSTER SHARDS\n# Slot coverage, open (migrating/importing) slots and node consistency:\nredis-cli --cluster check node1:6379\n# Repair if there are open or unowned slots:\nredis-cli --cluster fix node1:6379\n# Which slot and node a key lives on (-c: follow redirects):\nredis-cli -h node1 -p 6379 CLUSTER KEYSLOT kullanici:1001\nredis-cli -c -h node1 -p 6379 GET kullanici:1001","what":"Prüft, ob alle Knoten dieselbe Slot-Map sehen, alle 16384 Slots zugewiesen sind und keine unvollständigen (migrating/importing) Slots existieren; behebt gefundene Probleme und zeigt, auf welchem Slot/Knoten ein Key liegt.","when":"Wenn cluster_state:fail erscheint, Clients CLUSTERDOWN oder ständig MOVED/ASK-Fehler erhalten, und nach einem unterbrochenen Reshard.","caution":"--cluster fix kann Slots ohne Eigentümer beliebigen Knoten zuweisen und Keys in offenen Slots verschieben; prüfe zuerst die Ausgabe von check. Gehen ein Master und alle seine Replicas verloren, sind die Daten in diesen Slots weg; fix weist den Slot nur leer einem anderen Knoten zu. Mit cluster-require-full-coverage yes (Standard) stoppt ein einziger Slot ohne Eigentümer die Schreibzugriffe auf dem ganzen Cluster.","since":"Redis 7.0","note":"CLUSTER SHARDS kam mit 7.0 und ersetzt CLUSTER SLOTS. Ist Authentifizierung aktiviert, den --cluster-Befehlen -a <password> oder --user/--pass hinzufügen. fail/pfail-Flags in der Ausgabe von CLUSTER NODES bedeuten, dass der Knoten von den anderen als nicht erreichbar gesehen wird.","tags":["cluster","check","fix","slot","clusterdown","cluster nodes"],"untested":true},{"id":"cluster-reshard","task":"Knoten hinzufügen, Slots verschieben und Knoten aus einem Cluster entfernen (add-node, reshard, rebalance)","category":"Cluster","command":"# Add the new master to the cluster (joins empty, with no slots):\nredis-cli --cluster add-node node7:6379 node1:6379\n# Attach a new replica to a specific master:\nredis-cli --cluster add-node node8:6379 node1:6379 --cluster-slave --cluster-master-id <master-node-id>\n# Move 1000 slots from all masters to the new node:\nredis-cli --cluster reshard node1:6379 --cluster-from all --cluster-to <node7-id> --cluster-slots 1000 --cluster-yes\n# Or rebalance automatically (give slots to empty masters too):\nredis-cli --cluster rebalance node1:6379 --cluster-use-empty-masters\n# Remove a node whose slots have been emptied:\nredis-cli --cluster del-node node1:6379 <node-id>","what":"Fügt dem Cluster einen neuen Master oder eine Replica hinzu, verschiebt Slots samt ihren Keys online zwischen Knoten, gleicht die Slot-Verteilung automatisch aus und entfernt einen geleerten Knoten aus dem Cluster.","when":"Beim horizontalen Skalieren von Speicher/Durchsatz, beim Verteilen der Last eines heißen Knotens oder beim Außerbetriebnehmen eines Knotens.","caution":"Die Slot-Migration überträgt Keys einzeln mit MIGRATE; während große Keys verschoben werden, können beide Knoten kurz blockieren. Während der Migration erhalten Clients ASK-Redirects; ein clusterfähiger Client ist erforderlich. Wird ein Reshard unterbrochen, bleiben Slots im Zustand migrating/importing; mit --cluster fix abschließen. Entferne einen Master, der noch Slots besitzt, nicht mit del-node; verschiebe zuerst seine Slots.","since":"Redis 5.0","note":"Knoten-IDs stammen aus CLUSTER NODES oder CLUSTER MYID. --cluster-pipeline legt fest, wie viele Keys jeder MIGRATE-Aufruf verschiebt (Standard 10). --cluster rebalance mit --cluster-weight kann Knoten unterschiedlich gewichten. Die Befehle redis-cli --cluster ersetzten in 5.0 redis-trib.rb.","tags":["cluster","reshard","rebalance","add-node","slot","del-node"],"untested":true},{"id":"acl-users","task":"ACL-Benutzer anlegen, Berechtigungen testen und dauerhaft speichern","category":"Sicherheit","command":"# Application user: only app:* keys and channels, dangerous commands excluded:\nACL SETUSER app_rw on >S3cret-Change ~app:* resetchannels &app:* +@read +@write -@dangerous\n# Monitoring user: no key access, only specific commands:\nACL SETUSER monitor on >M0n-Secret resetkeys resetchannels -@all +info +ping +client|list +slowlog|get +latency +config|get\n# Test permissions without running the command:\nACL DRYRUN app_rw GET app:1001\nACL GETUSER app_rw\nACL LIST\n# Make it persistent: ACL SAVE if aclfile is set, otherwise CONFIG REWRITE into redis.conf\nACL SAVE","what":"Legt Anwendungs- und Monitoring-Benutzer an, die nach Key-Präfix, Pub/Sub-Kanal und Befehlskategorie berechtigt sind; testet mit DRYRUN, ob ein Befehl erlaubt ist, ohne ihn auszuführen, und schreibt die ACLs auf die Platte.","when":"Beim Umstieg von einem gemeinsamen Passwort (requirepass) auf Zugriff pro Benutzer, beim Isolieren von Anwendungen von den Keys der anderen oder beim Anlegen eines Nur-Lese-Kontos für ein Monitoring-Tool.","caution":"Die Reihenfolge der Regeln zählt: resetchannels entfernt alle vorher angegebenen &channel-Regeln. -@dangerous blockiert auch Befehle wie KEYS, FLUSHALL, CONFIG und DEBUG; prüfe die von der Anwendung genutzten Befehle mit DRYRUN. Bleibt der Benutzer default mit nopass und +@all aktiv, bieten ACLs keinen Schutz; nach dem Anlegen eines Admin-Benutzers ACL SETUSER default off ausführen. Schlägt in redis-cli AUTH mit --user/--pass fehl, läuft die Verbindung als Benutzer default weiter und der Befehl wird trotzdem ausgeführt; nutze deshalb DRYRUN für Berechtigungstests.","since":"Redis 7.0","note":"ACLs kamen mit 6.0; ACL DRYRUN, Selektoren und Regeln für Unterbefehle (client|list) wurden in 7.0 ergänzt. aclfile und user-Zeilen in redis.conf können nicht zusammen verwendet werden. Passwörter werden als SHA-256-Hashes gespeichert; ACL GETUSER zeigt das Klartextpasswort nicht.","tags":["acl","setuser","users","permissions","dryrun","security"]},{"id":"acl-log-denied","task":"NOPERM- und WRONGPASS-Fehler untersuchen (ACL LOG)","category":"Sicherheit","command":"# Last 10 permission denials and failed logins (reason: auth/command/key/channel):\nACL LOG 10\n# User of this connection:\nACL WHOAMI\n# Cumulative denial counters:\nINFO stats\n# acl_access_denied_auth, acl_access_denied_cmd, acl_access_denied_key, acl_access_denied_channel\nACL LOG RESET","what":"Listet abgelehnte Befehls-, Key- und Kanalzugriffe sowie fehlgeschlagene AUTH-Versuche mit Benutzername, Objekt, Wiederholungsanzahl und Client-Informationen (IP, letzter Befehl); zeigt kumulative Ablehnungszähler.","when":"Wenn die Anwendung nach einem Deployment NOPERM oder WRONGPASS erhält, um den durch eine verschärfte ACL-Regel gebrochenen Befehl zu finden oder um Passwort-Rateversuche zu erkennen.","caution":"ACL LOG liegt im Speicher, wird beim Neustart geleert und ist auf acllog-max-len (Standard 128) Einträge begrenzt; wiederholt sich derselbe Fehler, steigt count statt einer neuen Zeile. Das Feld client-info enthält die Client-IP und den verwendeten Benutzer; notiere, was du brauchst, bevor du zurücksetzt.","since":"Redis 7.0","note":"Die Zähler acl_access_denied_* wurden in 7.2 zu INFO stats hinzugefügt; binde sie ins Monitoring ein, um bei Anstiegen zu alarmieren. Die Felder entry-id und timestamp-created/last-updated kamen mit 7.2. Falsche Passwortversuche erscheinen als reason=auth, fehlende Befehlsberechtigung als reason=command.","tags":["acl log","noperm","wrongpass","access denied","auth","security"]},{"id":"keyspace-notifications","task":"Löschungen und Ablauf von Keys mit Keyspace Notifications überwachen","category":"Wartung","command":"CONFIG GET notify-keyspace-events\n# E: keyevent channel, x: expire events, g: generic commands (DEL, EXPIRE, RENAME):\nCONFIG SET notify-keyspace-events Exg\n# Listen to expire and del events in db 0:\nredis-cli -h redis1 -p 6379 --csv PSUBSCRIBE '__keyevent@0__:expired' '__keyevent@0__:del'\n# Turn off:\nCONFIG SET notify-keyspace-events \"\"","what":"Veröffentlicht Ereignisse zu Keys (expire, del, set usw.) in Pub/Sub-Kanälen, sodass du live verfolgen kannst, welcher Key wann gelöscht wurde oder abgelaufen ist.","when":"Um nachzuweisen, welcher Befehl oder Ablauf Keys entfernt, wenn sie 'von selbst verschwinden', oder um Abläufe zu versorgen, die von Ablauf-Events abhängen (Session-Ende).","caution":"Events erzeugen CPU- und Netzwerklast; 'A' (alle Klassen) ist auf ausgelasteten Systemen besonders teuer, aktiviere daher nur die benötigten Klassen und schalte sie danach ab. Pub/Sub ist Fire-and-Forget: Ein nicht verbundener Subscriber verpasst das Event, und nichts wird gespeichert. Das Event expired wird erzeugt, wenn der Key tatsächlich gelöscht wird (beim Zugriff oder durch den Expire-Zyklus im Hintergrund), nicht zum TTL-Zeitpunkt.","since":"Redis 2.8","note":"Füge die Klasse e (evicted) für durch Eviction entfernte Keys hinzu. In einem Cluster werden Events nur auf dem Knoten veröffentlicht, der den Key hält; abonniere jeden Master separat. Für eine dauerhafte Einstellung notify-keyspace-events in redis.conf setzen.","tags":["keyspace notifications","notify-keyspace-events","expired","pubsub","events","psubscribe"]}]},"recipes/elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"recipes":[{"id":"flush","task":"Index flushen","category":"Index-Lebenszyklus","command":"POST /customer/_flush","what":"Führt einen Lucene-Commit durch: schreibt Indexierungsdaten aus dem Speicher dauerhaft als Segmente auf die Platte und kürzt nicht mehr benötigte Translog-Einträge.","when":"Um das Translog vor einer Wartung oder einem Neustart zu verkleinern.","caution":"Erfolgt meist automatisch; ein manueller Flush erhöht die IO leicht.","tags":["flush","translog","index","customer","maintenance"]},{"id":"force-merge","task":"Index-Segmente zusammenführen (Force Merge)","category":"Index-Lebenszyklus","command":"POST /customer/_forcemerge?max_num_segments=1","what":"Führt Index-Segmente zusammen, um die Segmentanzahl zu verringern und die Lese-Performance zu verbessern.","when":"Nur bei Indizes, in die nicht mehr geschrieben wird (read-only), um die Abfrage-Performance zu verbessern.","caution":"Verbraucht viel IO und CPU; nie auf aktiv beschriebenen Indizes ausführen. Es kann lange dauern und lässt sich nicht abbrechen.","tags":["forcemerge","segment","merge","merge segments","performance"]},{"id":"rollover","task":"Rollover über einen Alias","category":"Index-Lebenszyklus","command":"POST /customer-alias/_rollover\n{\n  \"conditions\": {\n    \"max_age\": \"7d\",\n    \"max_docs\": 100000000,\n    \"max_primary_shard_size\": \"50gb\"\n  }\n}","what":"Erstellt einen neuen Index und richtet den Alias darauf aus, wenn der Write Index, auf den der Alias zeigt, die Bedingungen erfüllt.","when":"Um die Indexgröße bei Zeitreihendaten zu steuern (Alternative/Ergänzung zu ILM).","caution":"Der Alias muss über is_write_index einen einzigen Write Index haben; eine falsche Konfiguration blockiert den Rollover.","tags":["rollover","alias","index","time series","ilm"]},{"id":"set-refresh-interval","task":"Refresh-Intervall ändern","category":"Index-Lebenszyklus","command":"PUT /customer/_settings\n{\n  \"index\": { \"refresh_interval\": \"30s\" }\n}","what":"Legt fest, wie oft der Index neue Dokumente durchsuchbar macht (Refresh).","when":"Beim Erhöhen des Intervalls, um die Indexierung bei Massenladevorgängen zu beschleunigen.","caution":"-1 deaktiviert den Refresh vollständig; Dokumente werden nicht durchsuchbar. Nach dem Laden wieder auf einen sinnvollen Wert setzen.","tags":["refresh","interval","settings","bulk","refresh interval"]},{"id":"set-replicas","task":"Replica-Anzahl ändern","category":"Index-Lebenszyklus","command":"PUT /customer/_settings\n{\n  \"index\": { \"number_of_replicas\": 2 }\n}","what":"Ändert die Anzahl der Replica-Shards des Index dynamisch.","when":"Um Redundanz oder Lesekapazität zu erhöhen/verringern.","caution":"Zusätzliche Replicas erzeugen weitere Plattenbelegung und Netzwerkverkehr (Recovery); eine Reduzierung auf 0 entfernt die Redundanz.","tags":["replica","settings","number_of_replicas","redundancy","capacity"]},{"id":"open-close-index","task":"Index öffnen oder schließen","category":"Index-Lebenszyklus","command":"POST /customer/_close\nPOST /customer/_open","what":"Schließt den Index für Lese-/Schreibzugriffe (gibt Ressourcen frei) oder öffnet ihn wieder.","when":"Um den Ressourcenverbrauch selten genutzter Indizes zu senken oder bestimmte Einstellungen zu ändern.","caution":"Ein geschlossener Index kann weder abgefragt noch beschrieben werden; solange er geschlossen ist, ist er außer über Snapshots nicht zugänglich.","tags":["open","close","index","open index","close index"]},{"id":"node-join-note","task":"Knoten zum Cluster hinzufügen","category":"Cluster/Knoten","command":"GET /_cat/nodes?v","what":"Es genügt, den neuen Knoten mit demselben cluster.name und korrekten discovery.seed_hosts in elasticsearch.yml zu starten; dieser Befehl prüft, ob der Knoten beigetreten ist.","when":"Um die Mitgliedschaft beim Hinzufügen eines neuen Knotens zum Cluster zu prüfen.","caution":"Ab 7.0 gibt es kein minimum_master_nodes; die Voting-Konfiguration wird automatisch verwaltet. Füge master-fähige Knoten einzeln hinzu und entferne sie einzeln, und entferne nie die Hälfte oder mehr auf einmal; beim dauerhaften Entfernen von mehr als einem master-fähigen Knoten zuerst POST _cluster/voting_config_exclusions?node_names=.... verwenden.","tags":["node","join","cluster","cat","add node"]},{"id":"node-decommission","task":"Knoten leeren (Außerbetriebnahme)","category":"Cluster/Knoten","command":"PUT /_cluster/settings\n{\n  \"persistent\": {\n    \"cluster.routing.allocation.exclude._name\": \"node-3\"\n  }\n}","what":"Leert den angegebenen Knoten sicher, indem seine Shards auf andere Knoten verschoben werden.","when":"Um die Daten eines Knotens vor dessen Außerbetriebnahme wegzuverlagern.","caution":"Die Shard-Verlagerung erzeugt hohe Netzwerk-/IO-Last; stelle sicher, dass die verbleibenden Knoten genug Platte und Kapazität haben. Entferne die Einstellung nach dem Entfernen des Knotens mit null. transient-Einstellungen sind seit 7.16 veraltet; persistent verwenden.","tags":["decommission","exclude","allocation","drain","node"]},{"id":"reroute-shard","task":"Shard manuell verschieben (reroute)","category":"Cluster/Knoten","command":"POST /_cluster/reroute\n{\n  \"commands\": [\n    {\n      \"move\": {\n        \"index\": \"customer\",\n        \"shard\": 0,\n        \"from_node\": \"node-1\",\n        \"to_node\": \"node-2\"\n      }\n    }\n  ]\n}","what":"Verschiebt einen bestimmten Shard manuell von einem Knoten auf einen anderen.","when":"Um eine unausgewogene Shard-Verteilung zu korrigieren oder einen bestimmten Knoten zu leeren.","caution":"Es kann mit dem automatischen Rebalancing kollidieren; das Verschieben großer Shards erzeugt Netzwerk- und IO-Last.","tags":["reroute","shard","move","relocate","cluster"]},{"id":"allocation-enable","task":"Shard-Allokation aktivieren oder deaktivieren","category":"Cluster/Knoten","command":"PUT /_cluster/settings\n{\n  \"persistent\": {\n    \"cluster.routing.allocation.enable\": \"primaries\"\n  }\n}","what":"Schränkt die Shard-Allokation clusterweit ein (primaries: nur Primaries werden zugewiesen, none: nichts) oder setzt sie auf den Standard zurück (all / null).","when":"Vor einem Rolling Restart, um unnötige Verlagerungen von Replicas zu verhindern (der offiziell empfohlene Wert ist \"primaries\").","caution":"Denke daran, die Einstellung danach mit null (oder \"all\") zurückzusetzen; bleibt sie eingeschränkt, bleiben Replicas unassigned. transient-Einstellungen sind veraltet; persistent verwenden.","tags":["allocation","enable","restart","shard","cluster"]},{"id":"cluster-settings","task":"Cluster-Einstellung ändern","category":"Cluster/Knoten","command":"PUT /_cluster/settings\n{\n  \"persistent\": {\n    \"indices.recovery.max_bytes_per_sec\": \"100mb\"\n  }\n}","what":"Ändert eine persistente oder transiente clusterweite Einstellung.","when":"Beim Abstimmen des Cluster-Verhaltens, etwa der Recovery-Geschwindigkeit.","caution":"persistent-Einstellungen überdauern Neustarts; transient-Einstellungen gehen verloren und sind seit 7.16 veraltet, verwende sie daher nicht. Um eine Einstellung auf ihren Standard zurückzusetzen, ihren Wert auf null setzen. Ein falscher Wert beeinträchtigt die Stabilität des Clusters.","tags":["cluster","settings","persistent","transient","setting"]},{"id":"snapshot-repo","task":"Snapshot-Repository registrieren","category":"Snapshot","command":"PUT /_snapshot/backup_repo\n{\n  \"type\": \"fs\",\n  \"settings\": {\n    \"location\": \"/mnt/es_backups\"\n  }\n}","what":"Registriert ein Repository, in dem Snapshots gespeichert werden.","when":"Beim erstmaligen Einrichten der Backup-Infrastruktur.","caution":"Bei einem fs-Repository muss der Pfad in path.repo definiert und auf allen Master- und Datenknoten zugänglich sein.","tags":["snapshot","repository","repo","backup","register"]},{"id":"snapshot-create","task":"Snapshot erstellen","category":"Snapshot","command":"PUT /_snapshot/backup_repo/snap1?wait_for_completion=false\n{\n  \"indices\": \"customer\",\n  \"include_global_state\": false\n}","what":"Erstellt ein zeitpunktbezogenes Backup (Snapshot) der angegebenen Indizes im Repository.","when":"Für regelmäßige Backups oder als Sicherheitsbackup vor einer größeren Änderung.","caution":"wait_for_completion=true lässt die HTTP-Verbindung bei großen Indizes lange warten; für große Snapshots false verwenden.","tags":["snapshot","create","backup","backup snapshot","take"]},{"id":"snapshot-restore","task":"Aus einem Snapshot wiederherstellen","category":"Snapshot","command":"POST /_snapshot/backup_repo/snap1/_restore\n{\n  \"indices\": \"customer\",\n  \"rename_pattern\": \"customer\",\n  \"rename_replacement\": \"customer_restored\"\n}","what":"Stellt Indizes aus einem Snapshot im Cluster wieder her (optional umbenannt).","when":"Zur Wiederherstellung nach Datenverlust oder beim Übertragen eines Backups in eine Testumgebung.","caution":"Um unter demselben Namen wiederherzustellen, muss der Zielindex geschlossen oder gelöscht sein; über einen offenen Index lässt sich nicht wiederherstellen.","tags":["snapshot","restore","restore snapshot","recovery","backup"]},{"id":"cache-clear","task":"Cache leeren","category":"Wartung","command":"POST /customer/_cache/clear","what":"Leert Query-, Request- und Fielddata-Cache des Index.","when":"Um den Speicherdruck zu verringern oder Fielddata-Probleme zu beheben.","caution":"Nach dem Leeren des Caches laufen die ersten Abfragen langsamer, bis er wieder aufgewärmt ist.","tags":["cache","clear","clear cache","fielddata","maintenance"]},{"id":"pending-tasks","task":"Ausstehende Cluster-Tasks anzeigen","category":"Wartung","command":"GET /_cluster/pending_tasks","what":"Listet Tasks auf Cluster-Ebene, die auf dem Master-Knoten auf ihre Verarbeitung warten.","when":"Um die Master-Last zu diagnostizieren, wenn der Cluster langsamer wird oder Updates hängen bleiben.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["pending","tasks","master","diagnostics","pending tasks"]},{"id":"hot-threads","task":"Hot-Threads-Analyse abrufen","category":"Wartung","command":"GET /_nodes/hot_threads","what":"Zeigt die Stack Traces der Threads mit dem höchsten CPU-Verbrauch auf den Knoten.","when":"Beim Diagnostizieren der Ursache hoher CPU-Auslastung.","caution":"Reiner Lesebefehl; die Ausgabe kann lang sein, grenze sie mit dem Parameter thread_type ein.","tags":["hotthreads","cpu","diagnostics","thread","performance"]},{"id":"cat-shards","task":"Shard-Verteilung anzeigen","category":"Wartung","command":"GET /_cat/shards?v&s=state","what":"Listet alle Shards mit Index, Typ (primary/replica), Zustand und dem Knoten, auf dem sie liegen.","when":"Beim Überprüfen der Shard-Platzierung und nicht zugewiesener Shards.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["cat","shards","distribution","state","shard"]},{"id":"allocation-explain","task":"Herausfinden, warum ein Shard nicht zugewiesen ist","category":"Wartung","command":"GET /_cluster/allocation/explain\n{\n  \"index\": \"customer\",\n  \"shard\": 0,\n  \"primary\": true\n}","what":"Erklärt im Detail, warum ein bestimmter Shard nicht zugewiesen werden konnte (unassigned).","when":"Bei der Suche nach dem Grund für nicht zugewiesene Shards in einem gelben/roten Cluster.","caution":"Reiner Diagnosebefehl; kein Risiko.","tags":["allocation","explain","unassigned","diagnostics","shard"]},{"id":"cluster-health","task":"Cluster-Health anzeigen","category":"Wartung","command":"GET /_cluster/health","what":"Zeigt die Gesamt-Health des Clusters (green/yellow/red) und die Anzahl aktiver und nicht zugewiesener Shards.","when":"Beim schnellen Prüfen des Cluster-Status.","caution":"Reiner Lesebefehl; kein Risiko.","tags":["cluster","health","health check","status","green"]},{"id":"refresh-index","task":"Index manuell refreshen","category":"Index-Lebenszyklus","command":"POST /customer/_refresh","what":"Macht ausstehende Änderungen im Index sofort durchsuchbar.","when":"Wenn Daten direkt nach einem Massenladevorgang abfragbar sein sollen.","caution":"Häufige manuelle Refreshes können viele kleine Segmente erzeugen und die Performance verschlechtern.","tags":["refresh","index","search","segment","customer"]}]},"recipes/elasticsearch-ext.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":2,"recipes":[{"id":"red-yellow-triage","task":"Triage-Ablauf für roten/gelben Cluster (welcher Index, welcher Shard, warum)","category":"Cluster/Knoten","command":"GET _cluster/health?filter_path=status,unassigned_shards,initializing_shards,delayed_unassigned_shards,number_of_pending_tasks\nGET _health_report?verbose=false\nGET _cat/indices?v&health=red&h=health,index,pri,rep,docs.count,store.size\nGET _cat/indices?v&health=yellow&h=health,index,pri,rep\nGET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason,unassigned.for,node&s=state\n# Without a body, the call explains the first unassigned shard:\nGET _cluster/allocation/explain","what":"Gibt einen geordneten Triage-Ablauf vor: vom Cluster-Status zu den problematischen Indizes, dann zu den nicht zugewiesenen Shards und dem Grund, warum sie nicht zugewiesen werden (unassigned.reason, allocation explain); _health_report fasst zusammen, welcher Indikator (Platte, Shard-Verfügbarkeit, Master) nicht gesund ist.","when":"Als erste Reaktion, wenn der Cluster rot oder gelb wird.","caution":"Rot: Mindestens ein Primary Shard ist nicht zugewiesen; Schreibzugriffe auf diesen Index und Suchen, die diesen Shard treffen, schlagen fehl. Gelb: Alle Primaries sind vorhanden, aber Replicas fehlen; die Daten sind erreichbar, aber es gibt keine Redundanz. Auf einem Single-Node-Cluster bleiben Indizes mit replicas > 0 dauerhaft gelb (Entscheidung same_shard). Hat ein Knoten den Cluster kürzlich verlassen, werden delayed_unassigned_shards absichtlich zurückgehalten, bis index.unassigned.node_left.delayed_timeout (Standard 1m) abläuft.","since":"Elasticsearch 8.7","note":"_health_report kam mit 8.7; in älteren Versionen die ersten vier Schritte verwenden. Werte von unassigned.reason: NODE_LEFT (Knoten hat den Cluster verlassen), ALLOCATION_FAILED (Retry-Limit erreicht, siehe retry-failed-allocation), INDEX_CREATED, CLUSTER_RECOVERED. Für einen bestimmten Shard das Rezept allocation-explain mit einem Body aus index/shard/primary verwenden.","tags":["red","yellow","cluster health","unassigned","health report","triage"]},{"id":"retry-failed-allocation","task":"Shards neu zuweisen, die das Retry-Limit erreicht haben (reroute retry_failed)","category":"Cluster/Knoten","command":"# Attempt count and last failure details:\nGET _cluster/allocation/explain?filter_path=index,shard,unassigned_info.reason,unassigned_info.failed_allocation_attempts,unassigned_info.details,allocate_explanation\n# After fixing the cause, retry shards that hit the limit:\nPOST _cluster/reroute?retry_failed=true&metric=none\n# Recovery progress:\nGET _cat/recovery?v&active_only=true&h=index,shard,stage,source_node,target_node,bytes_percent,translog_ops_percent","what":"Zeigt Fehlerdetails zu Shards, deren Allokation nach aufeinanderfolgenden Fehlschlägen gestoppt wurde (ALLOCATION_FAILED, max_retry), und löst die Allokation erneut aus; überwacht den Recovery-Fortschritt.","when":"Wenn allocation explain 'shard has exceeded the maximum number of retries' zeigt; nachdem eine volle Platte, ein beschädigtes Translog oder ein vorübergehendes Netzwerkproblem behoben wurde.","caution":"retry_failed auszuführen, ohne die Ursache zu beheben, lässt den Shard 5 weitere Male versuchen und erneut scheitern; behebe zuerst den Fehler in unassigned_info.details. Gehen viele Shards gleichzeitig in Recovery, steigt die Netzwerk- und Plattenlast; begrenzt wird sie durch cluster.routing.allocation.node_concurrent_recoveries und indices.recovery.max_bytes_per_sec.","since":"Elasticsearch 7.0","note":"Das Retry-Limit ist index.allocation.max_retries (Standard 5). Elasticsearch versucht es auch regelmäßig selbst erneut, aber retry_failed hebt die Wartezeit auf. metric=none unterdrückt den großen Cluster State in der Antwort.","tags":["reroute","retry_failed","allocation failed","max_retries","unassigned","recovery"]},{"id":"allocate-stale-primary","task":"Verlorenen Primary Shard als letztes Mittel zuweisen (allocate_stale_primary)","category":"Cluster/Knoten","command":"# Which nodes hold copies of the primary (allocation id, store errors):\nGET _shard_stores/musteri?status=red\n# Last resort: promote a stale copy to primary; data loss is accepted:\nPOST _cluster/reroute\n{\n  \"commands\": [\n    {\n      \"allocate_stale_primary\": {\n        \"index\": \"musteri\",\n        \"shard\": 0,\n        \"node\": \"node-2\",\n        \"accept_data_loss\": true\n      }\n    }\n  ]\n}","what":"Weist bei dauerhaftem Verlust des Knotens mit der aktuellen Primary-Kopie eine ältere (nicht in-sync) Kopie auf einem anderen Knoten als Primary zu, um den Index aus dem roten Zustand zu holen.","when":"Nur wenn der Knoten mit dem Primary nicht wiederherstellbar ist und ein Restore aus einem Snapshot nicht in Frage kommt oder länger dauern würde.","caution":"Die veraltete Kopie enthält nicht die neuesten Schreibvorgänge; alle nach dieser Kopie erfolgten Schreibvorgänge gehen endgültig verloren. Kehrt der verlorene Knoten später zurück, wird die aktuellere Kopie darauf gelöscht. Erwäge zuerst, den Knoten wiederherzustellen oder aus einem Snapshot wiederherzustellen. allocate_empty_primary legt den Shard völlig leer an und löscht alle Daten in diesem Shard; nur verwenden, wenn keine Kopie mehr existiert.","since":"Elasticsearch 7.0","note":"In der Ausgabe von _shard_stores erscheint der Allokationstyp als 'primary', 'replica' oder 'unused'; wähle keine Kopien mit store_exception. Du kannst den Befehl zuerst mit ?dry_run=true&explain=true ausführen, um die Entscheidungen zu sehen. Schließe nach der Operation die Datenlücke durch Reindexierung von der Anwendungsseite.","tags":["allocate_stale_primary","red","primary","data loss","shard_stores","reroute"],"untested":true},{"id":"disk-watermark","task":"Disk Watermarks verwalten und die Schreibsperre der Flood Stage aufheben","category":"Cluster/Knoten","command":"GET _cat/allocation?v&h=node,shards,disk.indices,disk.used,disk.avail,disk.percent&s=disk.percent:desc\nGET _cluster/settings?include_defaults=true&filter_path=*.cluster.routing.allocation.disk\n# Adjust thresholds temporarily (null returns to the default):\nPUT _cluster/settings\n{\n  \"persistent\": {\n    \"cluster.routing.allocation.disk.watermark.low\": \"85%\",\n    \"cluster.routing.allocation.disk.watermark.high\": \"90%\",\n    \"cluster.routing.allocation.disk.watermark.flood_stage\": \"95%\"\n  }\n}\n# After freeing space, remove the flood-stage write block:\nPUT _all/_settings\n{\n  \"index.blocks.read_only_allow_delete\": null\n}","what":"Zeigt die Plattennutzung pro Knoten und die wirksamen Watermark-Werte; passt die Schwellenwerte low/high/flood_stage an und hebt die read_only_allow_delete-Sperre auf, die bei Flood Stage auf Indizes gesetzt wird.","when":"Wenn Schreibzugriffe mit 'TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block' abgelehnt werden, neue Shards nicht zugewiesen werden oder Shards beginnen, von einem vollen Knoten wegzuwandern.","caution":"Über low werden dem Knoten keine neuen Shards zugewiesen; über high werden Shards vom Knoten wegverschoben; über flood_stage werden Indizes mit einem Shard auf diesem Knoten schreibgeschützt. Das Anheben der Schwellenwerte verschafft nur Zeit; die dauerhafte Lösung ist das Löschen alter Indizes, das Reduzieren von Replicas oder das Hinzufügen von Platten/Knoten. Läuft die Platte komplett voll, können Shards beschädigt werden.","since":"Elasticsearch 8.5","note":"Ab 7.4 wird die Sperre automatisch aufgehoben, sobald die Plattennutzung unter das High Watermark fällt; das manuelle Aufheben verkürzt die Wartezeit. Ab 8.5 nutzen große Platten max_headroom (low 200GB, high 150GB, flood 100GB) als absoluten freien Platz statt des Prozentwerts; diese Standard-Headrooms gelten nur, solange die Watermarks auf ihren Standardwerten belassen sind, ein explizites Setzen von Prozentwerten wie oben deaktiviert sie also. Watermarks gelten auch auf Clustern mit nur einem Datenknoten.","tags":["disk watermark","flood_stage","read_only_allow_delete","disk full","allocation","cat allocation"]},{"id":"rolling-restart","task":"Rolling Restart / Rolling Upgrade (Knoten für Knoten, ohne unnötige Shard-Verschiebungen)","category":"Cluster/Knoten","command":"# 1) Disable replica allocation (only primaries are allocated):\nPUT _cluster/settings\n{\n  \"persistent\": { \"cluster.routing.allocation.enable\": \"primaries\" }\n}\n# 2) Optional: stop indexing and flush to speed up recovery:\nPOST _flush\n# 3) Stop one node, do the maintenance, start it; verify it joined:\nGET _cat/nodes?v&h=name,version,node.role,master,heap.percent\n# 4) Re-enable allocation:\nPUT _cluster/settings\n{\n  \"persistent\": { \"cluster.routing.allocation.enable\": null }\n}\n# 5) Wait for green before moving to the next node:\nGET _cluster/health?wait_for_status=green&timeout=10m","what":"Gibt die Reihenfolge vor, in der Knoten einzeln neu gestartet oder aktualisiert werden, und verhindert dabei, dass Replicas bei einem ausgefallenen Knoten auf andere Knoten neu kopiert werden; kehrt der Knoten zurück, erholen sich die Shards schnell aus der lokalen Kopie.","when":"Beim aufeinanderfolgenden Neustart von Knoten für einen OS-Patch, eine JVM-/Heap-Änderung, eine Änderung an elasticsearch.yml oder ein Minor-/Major-Upgrade.","caution":"Gehe nicht zum nächsten Knoten über, ohne auf green zu warten; sind zwei Knoten gleichzeitig ausgefallen, können alle Kopien eines Shards weg sein. Starte master-fähige Knoten zuletzt und einzeln neu; geht die Mehrheit verloren, kann der Cluster keinen Master wählen. Vergisst du, allocation.enable wieder zu aktivieren, bleiben Replicas unassigned. Bei einem Upgrade werden zuerst die Datenknoten aktualisiert (frozen/cold zuerst, hot zuletzt), dann die Master.","since":"Elasticsearch 7.16","note":"Gibt es ML-Jobs, zu Beginn POST _ml/set_upgrade_mode?enabled=true und am Ende enabled=false ausführen. Synced Flush wurde in 8.0 entfernt; ein normales _flush genügt. persistent verwenden, da transient-Einstellungen seit 7.16 veraltet sind. Bei kurzen Neustarts funktioniert statt des Deaktivierens der Allokation auch das Anheben von index.unassigned.node_left.delayed_timeout.","tags":["rolling restart","rolling upgrade","allocation","primaries","maintenance","upgrade"],"untested":true},{"id":"shard-sizing","task":"Shard-Größen und Shard-Anzahl prüfen (Erkennung von Oversharding / übergroßen Shards)","category":"Wartung","command":"# Largest shards (GB):\nGET _cat/shards?v&h=index,shard,prirep,state,docs,store,node&s=store:desc&bytes=gb\n# Indices by primary size:\nGET _cat/indices?v&h=index,pri,rep,docs.count,pri.store.size,store.size&s=pri.store.size:desc&bytes=gb\n# Shard count and disk per node:\nGET _cat/allocation?v&h=node,shards,disk.indices,disk.percent&s=shards:desc\nGET _cluster/stats?filter_path=indices.shards.total,indices.shards.primaries,nodes.count.data\nGET _cluster/settings?include_defaults=true&filter_path=*.cluster.max_shards_per_node","what":"Listet die größten Shards, Primary-Größen der Indizes, Shard-Anzahl pro Knoten, Gesamtzahl der Shards und das Limit cluster.max_shards_per_node.","when":"Bei der Kapazitätsplanung, wenn Recovery-Zeiten wachsen oder der Heap-Druck steigt, und beim Fehler 'this action would add [x] shards, but this cluster currently has [y]/[z] maximum normal shards open'.","caution":"Viele sehr kleine Shards (Oversharding) belasten Master und Heap; sehr große Shards dehnen Recovery und Rebalancing auf Stunden aus. Die Anzahl der Primary Shards lässt sich nach dem Anlegen des Index nicht ändern; zur Korrektur sind Shrink, Split oder Reindex nötig. Das Anheben von max_shards_per_node hebt das Limit auf, löst aber das Problem nicht.","since":"Elasticsearch 8.3","note":"Allgemeine Empfehlung von Elastic: 10-50 GB pro Shard und höchstens etwa 200 Millionen Dokumente. Ab 8.3 wurde die alte Regel '20 Shards pro GB Heap' durch eine Heap-Schätzung auf Basis der Anzahl gemappter Felder ersetzt. Bei Zeitreihen die Shard-Größe mit max_primary_shard_size im Rollover festlegen.","tags":["shard size","oversharding","cat shards","max_shards_per_node","capacity","sizing"]},{"id":"shrink-index","task":"Index mit zu vielen Shards verkleinern (Shrink)","category":"Index-Lebenszyklus","command":"# 1) Block writes on the source and gather all shard copies on one node:\nPUT log-2026.09/_settings\n{\n  \"index.number_of_replicas\": 0,\n  \"index.routing.allocation.require._name\": \"node-1\",\n  \"index.blocks.write\": true\n}\nGET _cluster/health/log-2026.09?wait_for_no_relocating_shards=true&timeout=30m\n# 2) Create a new index with fewer shards (the target must be a factor of the source):\nPOST log-2026.09/_shrink/log-2026.09-shrunk\n{\n  \"settings\": {\n    \"index.number_of_shards\": 1,\n    \"index.number_of_replicas\": 1,\n    \"index.routing.allocation.require._name\": null,\n    \"index.blocks.write\": null,\n    \"index.codec\": \"best_compression\"\n  }\n}","what":"Verringert die Anzahl der Primary Shards eines nicht mehr beschriebenen Index, indem seine Segmente per Hardlink in einen neuen Index übernommen werden; senkt Heap- und Shard-Overhead.","when":"Wenn tägliche Indizes mit mehr Shards als nötig angelegt wurden, das Shard-Limit nahe ist oder bevor alte Indizes in den Warm Tier verschoben werden.","caution":"Der Quellindex wird während des Shrink für Schreibzugriffe gesperrt; nur auf Indizes anwenden, in die nicht mehr geschrieben wird. Die Ziel-Shard-Anzahl muss ein Teiler der Quellanzahl sein (8 -> 4, 2 oder 1). Der Knoten, auf dem alle Kopien zusammengeführt werden, braucht so viel freien Plattenplatz wie der Quellindex. Mit replicas 0 hat der Quell-Shard eine einzige Kopie; lösche die Quelle erst, wenn die Operation abgeschlossen und der neue Index green ist.","since":"Elasticsearch 7.0","note":"Nach dem Shrink die Quelle löschen oder den Alias auf den neuen Index umstellen. Die Operation ist meist schnell, weil derselbe Knoten Dateien im selben Dateisystem per Hardlink verknüpfen kann; andernfalls werden Dateien kopiert. Zum Erhöhen der Shard-Anzahl _split verwenden, zur Automatisierung mit ILM die Action shrink in der Warm-Phase.","tags":["shrink","shard count","oversharding","index","blocks.write","warm"]},{"id":"ilm-policy-bootstrap","task":"ILM-Policy definieren, über ein Template zuweisen und die Rollover-Kette initialisieren","category":"Index-Lebenszyklus","command":"PUT _ilm/policy/log-policy\n{\n  \"policy\": {\n    \"phases\": {\n      \"hot\": { \"actions\": { \"rollover\": { \"max_primary_shard_size\": \"50gb\", \"max_age\": \"1d\" }, \"set_priority\": { \"priority\": 100 } } },\n      \"warm\": { \"min_age\": \"7d\", \"actions\": { \"shrink\": { \"number_of_shards\": 1 }, \"forcemerge\": { \"max_num_segments\": 1 }, \"set_priority\": { \"priority\": 50 } } },\n      \"delete\": { \"min_age\": \"30d\", \"actions\": { \"delete\": {} } }\n    }\n  }\n}\nPUT _index_template/log\n{\n  \"index_patterns\": [\"log-*\"],\n  \"priority\": 500,\n  \"template\": {\n    \"settings\": {\n      \"number_of_shards\": 1,\n      \"number_of_replicas\": 1,\n      \"index.lifecycle.name\": \"log-policy\",\n      \"index.lifecycle.rollover_alias\": \"log\"\n    }\n  }\n}\n# Create the first index with the write alias (its name must end with a number such as -000001):\nPUT log-000001\n{\n  \"aliases\": { \"log\": { \"is_write_index\": true } }\n}","what":"Legt eine ILM-Policy mit den Phasen hot (Rollover), warm (Shrink + Force Merge) und delete an, wendet Policy und Rollover-Alias über ein Template auf neue Indizes an und legt den ersten Index der Kette mit dem Write-Alias an.","when":"Beim Automatisieren von Indexgröße, Aufbewahrung und Löschung für Zeitreihendaten wie Logs oder Metriken.","caution":"min_age in den Phasen warm und delete wird ab dem Rollover-Zeitpunkt gezählt, nicht ab dem Anlegen des Index. Policy-Änderungen wirken auf bestehende Indizes erst nach Abschluss ihrer aktuellen Phase. Shrink und Forcemerge in der Warm-Phase erzeugen hohe IO-Last; in den Index darf nicht geschrieben werden, bis der Force Merge abgeschlossen ist. ILM läuft standardmäßig alle 10 Minuten, Bedingungen werden also nicht sofort ausgewertet.","since":"Elasticsearch 7.8","note":"Für neue Projekte erreicht ein Data Stream dasselbe einfacher: \"data_stream\": {} zum Index Template hinzufügen, dann sind weder Rollover-Alias noch Bootstrap-Index nötig. Rollover-Bedingungen werden anhand refreshter Statistiken ausgewertet. Composable Index Templates (_index_template) kamen mit 7.8; die alte API _template ist veraltet.","tags":["ilm","lifecycle","rollover","policy","index template","retention"]},{"id":"ilm-explain-retry","task":"Hängenden ILM-Schritt diagnostizieren und erneut versuchen (_ilm/explain, _ilm/retry)","category":"Index-Lebenszyklus","command":"# Indices waiting in an error step:\nGET log-*/_ilm/explain?only_errors=true\n# Phase/action/step state and failure reason for one index:\nGET log-000003/_ilm/explain?filter_path=indices.*.phase,indices.*.action,indices.*.step,indices.*.failed_step,indices.*.step_info,indices.*.is_auto_retryable_error\n# After fixing the cause (example: missing rollover alias setting):\nPUT log-000003/_settings\n{\n  \"index.lifecycle.rollover_alias\": \"log\"\n}\nPOST log-000003/_ilm/retry\n# Is ILM running (if STOPPED, POST _ilm/start):\nGET _ilm/status","what":"Zeigt Indizes, die im Schritt ERROR warten, in welcher Phase/Action/welchem Schritt sie hängen und den Fehlergrund (step_info); führt den fehlgeschlagenen Schritt nach Behebung der Ursache erneut aus.","when":"Wenn kein Rollover erfolgt, alte Indizes nicht gelöscht werden oder sich die Platte mit Indizes füllt, die ILM hätte entfernen sollen.","caution":"_ilm/retry funktioniert nur für Indizes im Schritt ERROR; sonst liefert es 'has not encountered an error'. Häufige Ursachen: Einstellung rollover_alias fehlt oder der Alias hat is_write_index auf einem anderen Index, nicht genug Platte auf einem einzelnen Knoten für Shrink, eine Allokationsregel (_tier_preference) lässt sich nicht erfüllen. Das Löschen der Policy entfernt die ILM-Verwaltung nicht von den Indizes; entferne zuerst die Policy-Einstellung von den Indizes.","since":"Elasticsearch 7.0","note":"Fehler mit is_auto_retryable_error true wiederholt ILM selbstständig (failed_step_retry_count wächst). Das ILM-Prüfintervall wird mit indices.lifecycle.poll_interval festgelegt (Standard 10m). Mit POST <index>/_ilm/remove nimmst du einen Index aus ILM heraus.","tags":["ilm","explain","retry","error step","rollover","lifecycle"]},{"id":"index-template-component","task":"Index Template aus Component Templates aufbauen und das Ergebnis simulieren","category":"Index-Lebenszyklus","command":"PUT _component_template/app-mappings\n{\n  \"template\": {\n    \"mappings\": {\n      \"dynamic\": \"strict\",\n      \"properties\": {\n        \"@timestamp\": { \"type\": \"date\" },\n        \"level\": { \"type\": \"keyword\" },\n        \"msg\": { \"type\": \"text\" }\n      }\n    }\n  }\n}\nPUT _index_template/app-events\n{\n  \"index_patterns\": [\"app-events-*\"],\n  \"priority\": 500,\n  \"composed_of\": [\"app-mappings\"],\n  \"template\": { \"settings\": { \"number_of_shards\": 1, \"refresh_interval\": \"5s\" } }\n}\n# See the final settings and mapping before the index is created:\nPOST _index_template/_simulate_index/app-events-000001\nGET _cat/templates?v&s=name","what":"Definiert ein wiederverwendbares Mapping-Component-Template, setzt es zu einem Index Template zusammen und zeigt die endgültigen Settings und das Mapping, die für einen gegebenen Indexnamen gelten würden, ohne den Index anzulegen.","when":"Wenn neue Indizes mit falschem Mapping oder falscher Shard-Anzahl angelegt werden (Dynamic-Mapping-Explosion, text statt keyword), und vor dem Ausrollen einer Template-Änderung.","caution":"Ein Template gilt nur für neu angelegte Indizes; das Mapping bestehender Indizes ändert sich nicht (siehe reindex-alias-swap). Passen zwei Templates auf dasselbe Muster, wird nur das mit der höchsten Priorität verwendet; sie werden nicht zusammengeführt. dynamic: strict lehnt Dokumente mit undefinierten Feldern ab; sendet die Anwendung ein neues Feld, schlägt die Indexierung fehl.","since":"Elasticsearch 7.8","note":"Das Feld overlapping in der Ausgabe von _simulate_index zeigt andere Templates, die auf dasselbe Muster passen und überschrieben werden. Halte die Priorität über 200, um Kollisionen mit eingebauten Templates (logs-*-*, metrics-*-*) zu vermeiden. Composable Templates haben Vorrang vor der alten API _template.","tags":["index template","component template","simulate","mapping","priority","dynamic strict"]},{"id":"reindex-alias-swap","task":"Mapping ohne Downtime ändern (Reindex + atomarer Alias-Wechsel)","category":"Index-Lebenszyklus","command":"# 1) Target index with the new mapping (refresh off and replicas 0 for loading):\nPUT musteri-v2\n{\n  \"settings\": { \"number_of_shards\": 1, \"number_of_replicas\": 0, \"refresh_interval\": \"-1\" },\n  \"mappings\": { \"properties\": { \"kod\": { \"type\": \"keyword\" }, \"adet\": { \"type\": \"long\" } } }\n}\n# 2) Reindex in the background (tracked with the returned task id):\nPOST _reindex?wait_for_completion=false&slices=auto\n{\n  \"source\": { \"index\": \"musteri-v1\", \"size\": 1000 },\n  \"dest\": { \"index\": \"musteri-v2\", \"op_type\": \"create\" },\n  \"conflicts\": \"proceed\"\n}\nGET _tasks/<task_id>\n# 3) Restore settings and compare document counts:\nPUT musteri-v2/_settings\n{\n  \"index\": { \"refresh_interval\": null, \"number_of_replicas\": 1 }\n}\nPOST musteri-v2/_refresh\nGET _cat/indices/musteri-v1,musteri-v2?v&h=index,docs.count,pri.store.size\n# 4) Switch the alias to the new index in one atomic operation:\nPOST _aliases\n{\n  \"actions\": [\n    { \"remove\": { \"index\": \"musteri-v1\", \"alias\": \"musteri\" } },\n    { \"add\": { \"index\": \"musteri-v2\", \"alias\": \"musteri\", \"is_write_index\": true } }\n  ]\n}","what":"Legt einen Index mit dem neuen Mapping an, kopiert die Daten im Hintergrund per Reindex, vergleicht die Dokumentanzahlen und stellt den von der Anwendung genutzten Alias in einer atomaren Operation auf den neuen Index um.","when":"Wenn sich der Typ eines bestehenden Felds ändern muss (text -> keyword, integer -> long), ein Analyzer geändert oder die Shard-Anzahl neu festgelegt werden muss; das ist am bestehenden Index nicht direkt möglich.","caution":"Die Anwendung muss sich mit dem Alias verbinden, nicht mit dem Indexnamen; sonst erfordert der Wechsel ein Deployment. Neue Schreibvorgänge in die Quelle während des Reindex erreichen das Ziel nicht: Schreibzugriffe stoppen oder den Reindex kurz vor dem Wechsel mit op_type create + conflicts proceed erneut ausführen, um die Lücke zu schließen (Updates und Deletes werden so nicht übertragen). Reindex erzeugt Leselast auf der Quelle und Schreiblast auf dem Ziel; mit requests_per_second drosseln.","since":"Elasticsearch 7.0","note":"Dokumente, die bei refresh_interval -1 geladen wurden, erscheinen bis zu einem Refresh nicht in den Zählungen; vor dem Vergleich _refresh ausführen. Erhält _cat/count mehrere kommagetrennte Indizes, liefert es deren Summe; für einen Vergleich pro Index _cat/indices verwenden. Die Geschwindigkeit eines laufenden Reindex lässt sich mit POST _reindex/<task_id>/_rethrottle?requests_per_second=500 ändern. wait_for_completion=false schreibt das Ergebnis in den Index .tasks; prüfe response.failures mit GET _tasks/<task_id>.","tags":["reindex","alias","mapping change","zero downtime","_aliases","no downtime"]},{"id":"tasks-cancel","task":"Lang laufende Such- und Reindex-Tasks finden, abbrechen oder drosseln","category":"Wartung","command":"# Longest running tasks:\nGET _cat/tasks?v&detailed&h=action,task_id,parent_task_id,running_time,node,description&s=running_time:desc\n# Search, reindex and by-query tasks, grouped by parent:\nGET _tasks?actions=*search*,*reindex*,*byquery*&detailed=true&group_by=parents\n# Cancel a single task:\nPOST _tasks/<task_id>/_cancel\n# Cancel all search tasks (with care):\nPOST _tasks/_cancel?actions=*search*\n# Slow down a running reindex/update_by_query:\nPOST _reindex/<task_id>/_rethrottle?requests_per_second=500","what":"Sortiert im Cluster laufende Tasks nach Dauer, zeigt Suchen und Bulk-Operationen mit ihren Abfragebeschreibungen; bricht einen ausgewählten Task oder alle Suchen ab und drosselt Reindex-/by-query-Jobs.","when":"Wenn sich der Search Thread Pool füllt, die CPU plötzlich steigt, ein Dashboard den Cluster mit einer schweren Aggregation belastet oder ein update_by_query die Produktion beeinträchtigt.","caution":"Das Abbrechen ist kooperativ: Der Task stoppt an seinem nächsten Checkpoint; manche Phasen (z. B. Force Merge) lassen sich nicht abbrechen. Das Abbrechen aller Suchen liefert Benutzern Fehler. Ein abgebrochener Reindex/by-query macht bisherige Änderungen nicht rückgängig; das Ziel enthält Teildaten. Wird ein Parent Task abgebrochen, werden auch seine Child Tasks abgebrochen.","since":"Elasticsearch 7.0","note":"Der Parameter detailed schreibt den Query Body von Such-Tasks in das Feld description. Für dauerhaften Schutz search.default_search_timeout, ein timeout in der Suchanfrage und search.max_buckets für große Aggregationen verwenden. Ab 7.x werden Suchen, deren HTTP-Verbindung geschlossen wird, automatisch abgebrochen.","tags":["tasks","cancel","kill query","rethrottle","long query","search"]},{"id":"search-slowlog","task":"Slow Log für Suche und Indexierung aktivieren (Schwellenwerte pro Index)","category":"Performance","command":"PUT musteri/_settings\n{\n  \"index.search.slowlog.threshold.query.warn\": \"2s\",\n  \"index.search.slowlog.threshold.query.info\": \"500ms\",\n  \"index.search.slowlog.threshold.fetch.warn\": \"1s\",\n  \"index.indexing.slowlog.threshold.index.warn\": \"1s\",\n  \"index.indexing.slowlog.source\": \"200\"\n}\nGET musteri/_settings/index.*slowlog*?flat_settings=true\n# Set thresholds to null to turn it off:\nPUT musteri/_settings\n{\n  \"index.search.slowlog.threshold.query.warn\": null,\n  \"index.search.slowlog.threshold.query.info\": null,\n  \"index.search.slowlog.threshold.fetch.warn\": null,\n  \"index.indexing.slowlog.threshold.index.warn\": null,\n  \"index.indexing.slowlog.source\": null\n}","what":"Schreibt für einen gegebenen Index Anfragen, die den Schwellenwert in der Query- und Fetch-Phase sowie bei der Indexierung überschreiten, zusammen mit dem Query Body ins Slow Log.","when":"Um herauszufinden, welche Abfrage langsame Suchen verursacht, oder um schwere Abfragen eines bestimmten Dashboards oder Clients zu erfassen.","caution":"Der Schwellenwert wird pro Shard gemessen: Bei einem Index mit vielen Shards kann jeder Shard eine eigene Zeile schreiben. Ein sehr niedriger Schwellenwert (0ms) erhöht Logvolumen und IO erheblich; nach der Diagnose mit null ausschalten. indexing.slowlog.source schreibt die Dokumentquelle ins Log; auf false setzen, wenn sie sensible Daten enthält. Die Einstellung gilt nur für den angegebenen Index; für neue Indizes ins Template aufnehmen.","since":"Elasticsearch 8.0","note":"Slow-Log-Dateien werden im Verzeichnis logs als <cluster>_index_search_slowlog.json und <cluster>_index_indexing_slowlog.json geschrieben; wird log4j2 auf die Konsole geleitet, gehen sie nach stdout. Die Einstellung index.search.slowlog.level wurde in 8.0 entfernt; das Level ergibt sich aus den Schwellenwerten (warn/info/debug/trace). Ab 8.14 schreibt index.search.slowlog.include.user: true auch den anfragenden Benutzer ins Log. Um eine einzelne Abfrage im Detail zu untersuchen, \"profile\": true zur Suchanfrage hinzufügen.","tags":["slowlog","slow log","slow search","search","indexing","threshold"]},{"id":"rejections-heap-breaker","task":"429-Ablehnungen, Heap- und Circuit-Breaker-Druck diagnostizieren","category":"Performance","command":"# Thread pool queue and rejection counts (source of 429):\nGET _cat/thread_pool/write,search,get?v&h=node_name,name,active,queue,rejected,completed,size,queue_size&s=rejected:desc\nGET _cat/nodes?v&h=name,node.role,heap.percent,ram.percent,cpu,load_1m,disk.used_percent,master&s=heap.percent:desc\n# Circuit breaker trips and parent breaker usage:\nGET _nodes/stats/breaker?filter_path=nodes.*.name,nodes.*.breakers.*.tripped,nodes.*.breakers.parent\nGET _nodes/stats/jvm?filter_path=nodes.*.name,nodes.*.jvm.mem.heap_used_percent,nodes.*.jvm.gc.collectors.old\n# Fielddata memory usage (per field):\nGET _nodes/stats/indices/fielddata?fields=*&filter_path=nodes.*.indices.fielddata","what":"Zeigt Anfragen, die wegen voller Warteschlangen der Write-/Search-Thread-Pools abgelehnt wurden, Heap-/CPU-Zustand pro Knoten, ausgelöste Circuit Breaker, Anzahl der Old-GCs und Fielddata-Speicherverbrauch.","when":"Wenn Clients 429 Too Many Requests oder es_rejected_execution_exception erhalten, CircuitBreakingException ([parent] Data too large) auftritt oder Knoten unter langen GC-Pausen leiden.","caution":"rejected-Zähler kumulieren seit dem Start des Knotens; betrachte den Anstieg zwischen zwei Messungen. Ein höheres queue_size verdeckt Ablehnungen, erhöht aber Heap und Latenz; die eigentliche Lösung ist, Bulk-Größe/Parallelität zu verringern oder Kapazität hinzuzufügen. Bleibt der Heap über 85% und läuft Old GC häufig, nähert sich der Knoten einem OOM. Fielddata auf einem text-Feld zu aktivieren verbraucht schnell Heap.","since":"Elasticsearch 7.0","note":"Der Parent Breaker liegt standardmäßig bei 95% des Heaps (Real Memory Circuit Breaker, 7.0+). Schreibablehnungen konzentrieren sich meist auf einen einzelnen heißen Knoten: Shard-Verteilung und Routing (_routing) prüfen. Um Fielddata vorübergehend freizugeben, POST _cache/clear?fielddata=true verwenden (siehe cache-clear).","tags":["429","rejected","thread pool","circuit breaker","heap","fielddata"]},{"id":"slm-snapshot-monitor","task":"Snapshots und Aufbewahrung mit SLM automatisieren, Snapshot-Fortschritt überwachen","category":"Snapshot","command":"# Take a snapshot every night at 01:30, keep for 30 days (at least 7, at most 60 snapshots):\nPUT _slm/policy/gece-yedek\n{\n  \"schedule\": \"0 30 1 * * ?\",\n  \"name\": \"<gece-{now/d}>\",\n  \"repository\": \"yedek_repo\",\n  \"config\": { \"indices\": [\"*\"], \"include_global_state\": true },\n  \"retention\": { \"expire_after\": \"30d\", \"min_count\": 7, \"max_count\": 60 }\n}\n# Run the policy now and see success/failure statistics:\nPOST _slm/policy/gece-yedek/_execute\nGET _slm/stats\n# Snapshot in progress and per-shard progress:\nGET _snapshot/yedek_repo/_current\nGET _snapshot/yedek_repo/_status\nGET _cat/snapshots/yedek_repo?v&s=end_epoch:desc\n# Verify that the repository is writable from all nodes:\nPOST _snapshot/yedek_repo/_verify","what":"Definiert eine SLM-Policy, die nach einem cron-Zeitplan Snapshots erstellt und alte gemäß einer Aufbewahrungsregel löscht; zeigt den Fortschritt eines laufenden Snapshots pro Shard, frühere Snapshots und die Erreichbarkeit des Repositorys.","when":"Beim Einrichten regelmäßiger Backups, bei einem Alarm über einen fehlgeschlagenen nächtlichen Snapshot oder beim Abschätzen, wann ein lang laufender Snapshot fertig wird.","caution":"Der Zeitplan wird in UTC ausgewertet. Schreibt mehr als ein Cluster in dasselbe Repository oder ist das Repository auf einem anderen Cluster als beschreibbar registriert, kann es beschädigt werden; auf den anderen Clustern readonly: true verwenden. Die Aufbewahrung löscht nur von SLM erstellte Snapshots. include_global_state true kann bei einem Restore auch Templates und persistente Cluster-Einstellungen wiederherstellen.","since":"Elasticsearch 7.5","note":"Snapshots sind inkrementell: Nur im vorherigen Snapshot fehlende Segmente werden kopiert, häufige Snapshots belegen im Repository also nicht proportional Platz. SLM kam mit 7.4, die Aufbewahrung mit 7.5. Ohne Repository-Namen liefert _status alle laufenden Snapshots im Cluster; mit dem Namen eines früheren Snapshots liest es aus dem Repository und kann langsam sein.","tags":["slm","snapshot","retention","backup","schedule","snapshot status"],"untested":true},{"id":"security-user-role","task":"Rolle und Native-Benutzer anlegen, Passwort ändern, API-Key ausstellen","category":"Sicherheit","command":"POST _security/role/log_okuma\n{\n  \"cluster\": [\"monitor\"],\n  \"indices\": [\n    { \"names\": [\"log-*\"], \"privileges\": [\"read\", \"view_index_metadata\"] }\n  ]\n}\nPOST _security/user/rapor_kullanici\n{\n  \"password\": \"Degistir-123!\",\n  \"roles\": [\"log_okuma\"],\n  \"full_name\": \"Rapor Servisi\"\n}\nGET _security/user/rapor_kullanici\n# User making this request and its roles:\nGET _security/_authenticate\nPOST _security/user/rapor_kullanici/_password\n{\n  \"password\": \"Yeni-Sifre-456!\"\n}\n# Time-limited API key with restricted privileges:\nPOST _security/api_key\n{\n  \"name\": \"rapor-key\",\n  \"expiration\": \"30d\",\n  \"role_descriptors\": { \"log_okuma\": { \"indices\": [ { \"names\": [\"log-*\"], \"privileges\": [\"read\"] } ] } }\n}\n# If the elastic user's password is lost (shell on a node):\n# bin/elasticsearch-reset-password -u elastic -i","what":"Legt eine Rolle mit Lesezugriff auf ein Indexmuster und einen Benutzer im Native Realm mit dieser Rolle an, ändert das Passwort, prüft die Identität des Aufrufers und stellt einen zeitlich begrenzten API-Key mit eingeschränkten Berechtigungen aus.","when":"Beim Anlegen von Reporting-, Anwendungs- oder Monitoring-Konten mit Least Privilege, bei der Passwortrotation und beim Umstellen von Diensten von Benutzern auf API-Keys.","caution":"Verwende den Superuser elastic nicht in Anwendungen. Die Berechtigungen eines API-Keys können die des erstellenden Benutzers nicht übersteigen; role_descriptors schränken sie nur ein. Ein API-Key ist auf einen Snapshot der Berechtigungen seines Eigentümers zum Erstellungszeitpunkt begrenzt; spätere Rollenänderungen wirken nicht auf bestehende Keys. Nach einer Passwortänderung erhalten Dienste mit dem alten Passwort 401; plane die Umstellung. Der Wert encoded in der API-Key-Antwort wird nur einmal geliefert; speichere ihn.","since":"Elasticsearch 8.0","note":"Security ist ab 8.0 standardmäßig aktiviert, und das Passwort für elastic wird beim ersten Start erzeugt. Das Tool elasticsearch-reset-password kam mit 8.0. API-Keys mit GET _security/api_key?name=rapor-key auflisten und mit DELETE _security/api_key widerrufen. Das Role Mapping für Benutzer in LDAP-/AD-/SAML-Realms erfolgt mit _security/role_mapping.","tags":["security","users","roles","api key","password","rbac"],"untested":true}]},"solver/slow-query.json":{"id":"slow-query","title":"Langsame Abfrage: tiefgehende Diagnose","icon":"🐢","category":"Performance","intro":"Eine Abfrage oder das ganze System ist langsam. Grenze es Schritt für Schritt ein: Führe an jedem Knoten eine Prüfung aus und wähle den Zweig anhand des Ergebnisses. Am Ende stehen Ursache + Lösung.","start":"scope","nodes":{"scope":{"kind":"step","title":"Was ist langsam?","detail":"Sind eine oder wenige bestimmte Abfragen langsam oder das ganze System? Das teilt die Untersuchung in einen von zwei Pfaden: 'abfragebezogen' oder 'ressourcen-/engpassbezogen'.","choices":[{"label":"Eine oder wenige bestimmte Abfragen sind langsam","next":"running"},{"label":"Das ganze System ist langsam","next":"system"}]},"running":{"kind":"step","title":"Läuft die Abfrage gerade?","detail":"Läuft/hängt die Abfrage gerade, oder ist sie 'generell langsam'? Sieh dir die aktiven Sessions an.","command":{"oracle":"SELECT sid, serial#, sql_id, last_call_et AS running_sec, event, blocking_session\nFROM v$session WHERE status='ACTIVE' AND type='USER' ORDER BY last_call_et DESC;","postgres":"SELECT pid, now()-query_start AS dur, state, wait_event, query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start;","mysql":"SELECT id, time, state, info FROM information_schema.processlist WHERE command='Query' ORDER BY time DESC;","mssql":"SELECT session_id, total_elapsed_time/1000 AS sec, status, wait_type, blocking_session_id\nFROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1) ORDER BY total_elapsed_time DESC;"},"choices":[{"label":"Läuft / hängt gerade","next":"blocked"},{"label":"Generell langsam; ich will den Plan ansehen","next":"plan"}]},"blocked":{"kind":"step","title":"Wartet die Abfrage auf etwas?","detail":"Ist sie aktiv, kommt aber nicht voran, wartet sie höchstwahrscheinlich. Ist blocking_session / wait_event gefüllt?","command":{"oracle":"SELECT sid, blocking_session, event, seconds_in_wait\nFROM v$session WHERE sid=&target_sid;","postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by, wait_event_type, wait_event\nFROM pg_stat_activity WHERE pid=&target_pid;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_resource\nFROM sys.dm_exec_requests WHERE session_id=&target;"},"choices":[{"label":"Von einer anderen Session blockiert","next":"sol_blocking"},{"label":"Wartet auf eine Ressource (IO / Log / Latch)","next":"wait"},{"label":"Kein Wait; verbraucht aktiv CPU","next":"plan"}]},"wait":{"kind":"step","title":"Welcher Wait dominiert?","detail":"Das dominierende Wait Event zeigt die Art des Engpasses.","command":{"oracle":"SELECT event, total_waits, time_waited FROM v$session_event WHERE sid=&target_sid ORDER BY time_waited DESC FETCH FIRST 5 ROWS ONLY;","postgres":"SELECT wait_event_type, wait_event, count(*) FROM pg_stat_activity WHERE state='active' GROUP BY 1,2 ORDER BY 3 DESC;","mssql":"SELECT TOP 5 wait_type, waiting_tasks_count, wait_time_ms FROM sys.dm_os_wait_stats ORDER BY wait_time_ms DESC;"},"choices":[{"label":"IO-Wait (db file read / PAGEIOLATCH)","next":"sol_io"},{"label":"Log-/Commit-Wait (log file sync / WRITELOG)","next":"sol_log"},{"label":"Sperre / Enqueue (enq: TX / LCK)","next":"sol_blocking"}]},"plan":{"kind":"step","title":"Ausführungsplan abrufen (EXPLAIN)","detail":"Hol den Plan. Gibt es einen Full Table Scan, eine unerwartete Join-Reihenfolge oder eine Abweichung zwischen geschätzten und tatsächlichen Zeilen?","command":{"oracle":"SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));","postgres":"EXPLAIN (ANALYZE, BUFFERS) <query>;","mysql":"EXPLAIN ANALYZE <query>;","mssql":"SET STATISTICS PROFILE ON; <query>; SET STATISTICS PROFILE OFF;","mongodb":"db.collection.find({...}).explain('executionStats')  // COLLSCAN or IXSCAN?"},"choices":[{"label":"Full Scan / fehlender Index","next":"sol_index"},{"label":"Gestern war sie schnell; der Plan hat sich plötzlich geändert","next":"sol_planchange"},{"label":"Verarbeitet zu viele Zeilen / N+1","next":"sol_rewrite"},{"label":"Der Plan sieht gut aus, aber sie ist langsam","next":"stats"}]},"stats":{"kind":"step","title":"Sind die Statistiken aktuell?","detail":"Eine große Abweichung zwischen geschätzten und tatsächlichen Zeilen (E-rows vs. A-rows) ist ein Zeichen veralteter Statistiken.","command":{"oracle":"SELECT table_name, num_rows, last_analyzed FROM dba_tables WHERE table_name='&TABLE_NAME';","postgres":"SELECT relname, last_analyze, last_autoanalyze, n_live_tup FROM pg_stat_user_tables WHERE relname='&table_name';","mysql":"SELECT table_name, table_rows, update_time FROM information_schema.tables WHERE table_name='&table_name';","mssql":"SELECT name, STATS_DATE(object_id, stats_id) AS last_updated FROM sys.stats WHERE object_id=OBJECT_ID('&table_name');"},"choices":[{"label":"Statistiken sind veraltet / alt","next":"sol_stats"},{"label":"Statistiken sind aktuell, aber sie ist trotzdem langsam","next":"sol_deep"}]},"system":{"kind":"step","title":"Systemweit: Wo ist der Engpass?","detail":"Ist alles langsam statt nur einer Abfrage, nach einem Ressourcenengpass suchen: CPU, IO, Speicher oder verbreitete Blockierung?","command":{"oracle":"SELECT metric_name, value FROM v$sysmetric WHERE metric_name IN ('Host CPU Utilization (%)','Average Active Sessions') AND intsize_csec=(SELECT MAX(intsize_csec) FROM v$sysmetric);","postgres":"SELECT state, wait_event_type, count(*) FROM pg_stat_activity GROUP BY 1,2 ORDER BY 3 DESC;","mssql":"SELECT TOP 5 wait_type, wait_time_ms FROM sys.dm_os_wait_stats ORDER BY wait_time_ms DESC;"},"choices":[{"label":"CPU nahe 100%","next":"sol_cpu"},{"label":"Hoher IO-Wait","next":"sol_io"},{"label":"Speicherdruck / niedrige Cache Hit Ratio","next":"sol_mem"},{"label":"Viel Blockierung","next":"sol_blocking"}]},"sol_blocking":{"kind":"solution","title":"Ursache: Blockierung","detail":"Die Abfrage wartet auf eine Sperre, die eine andere Session hält. Finde die Session am Anfang der Kette (Root Blocker) und löse sie auf.","steps":["Finde den Anfang der Blockierungskette (die Session, die selbst nicht wartet, aber andere warten lässt).","Verstehe, was der Root Blocker tut: eine lange nicht committete Transaktion oder 'idle in transaction'?","Kann die Anwendung committen/zurückrollen, löse das aus; wenn nicht und die Arbeit steht, beende den Root Blocker.","Behebe die Ursache: Ein fehlender Index kann breite Sperren verursachen; verkürze Transaktionen und überprüfe das Isolation Level."],"related":["blocking-sessions","kill-session"],"playbook":"blocking"},"sol_io":{"kind":"solution","title":"Ursache: IO-Engpass","detail":"Die Abfrage liest viel von der Platte. Meist wegen Full Scans (fehlender Index) oder zu wenig Speicher (Buffer Cache).","steps":["Lege für den Filter, der den Full Scan verursacht, einen passenden Index an (der Plan sollte auf einen Index Scan, IXSCAN, wechseln).","Prüfe die Hit Ratio von Buffer Cache / Buffer Pool; ist sie niedrig, Speicher erhöhen.","Unnötige Spalten-/Zeilenlesevorgänge reduzieren (benötigte Spalten statt SELECT *, Paginierung).","Ist der Speicher wirklich langsam (hohe IO-Latenz), die Plattenebene verbessern."],"related":["cache-hit-ratio","create-index"]},"sol_log":{"kind":"solution","title":"Ursache: Commit-/Log-Engpass","detail":"Dominiert 'log file sync' / 'WRITELOG', warten Commits auf Redo-/WAL-Schreibvorgänge.","steps":["Häufige einzeilige Commits in gebündelte Commits umwandeln; unnötige Commits reduzieren.","Die Redo-/WAL-Platte beschleunigen (separate, schnelle Platte); Größe/Anzahl der Logdateien überprüfen.","Einstellungen synchronous_commit / commit_write gegen Workload und Abwägung bei der Dauerhaftigkeit bewerten."],"related":["show-config-param"]},"sol_index":{"kind":"solution","title":"Ursache: Fehlender Index","detail":"Der Plan macht einen Full Scan. Ein passender Index auf Filter-/Join-Spalten bringt eine große Beschleunigung.","steps":["Einen passenden Index (bei Bedarf zusammengesetzt) auf den WHERE-/JOIN-Spalten online anlegen.","Nach dem Anlegen des Index den Plan erneut abrufen und prüfen, ob er auf einen Index Scan (IXSCAN) gewechselt ist.","Bei hoher Schreiblast die Indexkosten abwägen; unnötige Indizes löschen.","Auf Spalten mit geringer Selektivität hilft ein Index nicht; prüfe die Selektivität."],"related":["create-index","explain-plan"]},"sol_planchange":{"kind":"solution","title":"Ursache: Planregression","detail":"Die Abfrage war gestern schnell, heute nicht; der Plan hat sich geändert. Meist veraltete Statistiken oder eine Parameteränderung.","steps":["Zuerst frische Statistiken sammeln (veraltete Statistiken sind die häufigste Ursache).","Den guten Plan festlegen: Oracle SQL Plan Baseline / Hint, MSSQL Query Store 'force plan', PG-Einstellungen.","Gab es kürzlich ein Upgrade/einen Patch/eine Parameteränderung, diese überprüfen (Optimizer-Verhalten)."],"related":["update-statistics"]},"sol_rewrite":{"kind":"solution","title":"Ursache: Abfragedesign","detail":"Der Plan ist vernünftig, aber die Abfrage leistet von Natur aus zu viel Arbeit (zu viele Zeilen, N+1, unnötige Joins).","steps":["Ergebnismenge verkleinern: Paginierung (LIMIT/OFFSET oder Keyset), frühes Filtern.","Das N+1-Muster in eine einzige Batch-Abfrage umwandeln; unnötige Joins/Unterabfragen vereinfachen.","Für wiederkehrende schwere Reports eine Materialized View / Summentabelle erwägen."],"related":["explain-plan","materialized-view"]},"sol_stats":{"kind":"solution","title":"Ursache: Veraltete Statistiken","detail":"Der Optimizer schätzt falsch, weil die Statistiken nicht aktuell sind.","steps":["Statistiken für die betroffene(n) Tabelle(n) manuell sammeln.","Prüfen, ob der automatische Sammeljob (autovacuum/auto stats/gather job) läuft.","Nach plötzlichen großen Datenladevorgängen das Sammeln von Statistiken in den Ablauf aufnehmen."],"related":["update-statistics"]},"sol_deep":{"kind":"solution","title":"Tiefgehende Analyse","detail":"Sind Plan und Statistiken in Ordnung, aber es ist trotzdem langsam, tiefer graben.","steps":["Trace auf Session-Ebene aktivieren: Oracle 10046, PG auto_explain, MySQL profiling, MSSQL Extended Events.","Parallelität, Memory Grants und Sort-/Hash-Spills (tempdb/temp) untersuchen.","Hardware-/OS-Ebene ausschließen (CPU Steal, IO-Latenz, Netzwerk).","Durch Verbindungsanzahl und Parallelität verursachte Contention bewerten."],"related":["session-trace","top-wait-events"]},"sol_cpu":{"kind":"solution","title":"Ursache: CPU-Engpass","detail":"Das ganze System ist CPU-gebunden. Meist verbrennen einige schlecht geplante Abfragen die CPU.","steps":["Die 10 Abfragen mit dem höchsten CPU-Verbrauch finden und ihre Pläne korrigieren (Indizes/Statistiken).","Außer Kontrolle geratene Abfragen drosseln (Resource Manager / Resource Governor).","Bleibt die Auslastung hoch, skalieren (CPU hinzufügen, Lesezugriffe auf Read Replicas verteilen)."],"related":["top-slow-queries"],"playbook":"high-cpu"},"sol_mem":{"kind":"solution","title":"Ursache: Speicherdruck","detail":"Niedrige Cache Hit Ratio und/oder Swapping: Der Speicher reicht nicht aus oder ist falsch verteilt.","steps":["Buffer Pool / SGA und Session-Speicher (work_mem/PGA) korrekt dimensionieren (Tools > Memory calculator).","Die außer Kontrolle geratene Abfrage finden und beheben, die Speicher verbraucht.","Swappt das System, hat das Beenden des Swappings Vorrang; bei Bedarf RAM hinzufügen."],"related":["cache-hit-ratio","show-config-param"],"playbook":"memory-pressure"}}},"solver/deadlock.json":{"id":"deadlock","title":"Deadlock / Sperrkonflikt (ORA-00060, 1213, 40P01)","icon":"♻️","category":"Sperren","intro":"Du hast einen Deadlock erhalten (Oracle ORA-00060, MySQL 1213, PG 40P01, MSSQL 1205). Erfasse den Graph, ermittle das Muster, verhindere die Wiederholung. Deadlocks werden durch Design gelöst — Ziel ist, ihre Häufigkeit zu senken.","start":"capture","nodes":{"capture":{"kind":"step","title":"Zuerst den Deadlock-Graph erfassen","detail":"Welche zwei (oder mehr) Sessions, welche Ressourcen, welche Sperrmodi sind kollidiert? Ohne das gesehen zu haben keine blinden Korrekturen anwenden.","command":{"oracle":"-- Check the trace file referenced in the alert log (deadlock graph). For history:\nSELECT * FROM dba_hist_active_sess_history WHERE event LIKE 'enq: TX%' ORDER BY sample_time DESC FETCH FIRST 20 ROWS ONLY;","mysql":"SHOW ENGINE INNODB STATUS\\G  -- 'LATEST DETECTED DEADLOCK' section","postgres":"-- Deadlock errors are always written to postgresql.log as ERROR (log_lock_waits not required); log_lock_waits=on also logs lock waits longer than deadlock_timeout.","mssql":"SELECT XEvent.query('.') FROM (SELECT CAST(target_data AS XML) td FROM sys.dm_xe_session_targets t JOIN sys.dm_xe_sessions s ON s.address=t.event_session_address WHERE s.name='system_health') x CROSS APPLY td.nodes('//event[@name=\"xml_deadlock_report\"]') AS q(XEvent);"},"choices":[{"label":"Graph liegt vor — zwei Transaktionen sperren Ressourcen in umgekehrter Reihenfolge","next":"sol_order"},{"label":"Viele Transaktionen stauen sich auf einer einzigen Zeile/Tabelle (Hotspot)","next":"sol_hotspot"},{"label":"Es gibt breite Bereichssperren (viele Zeilen werden gesperrt)","next":"index_check"}]},"index_check":{"kind":"step","title":"Nutzt die sperrende Abfrage einen Index?","detail":"Ohne Index sperrt ein UPDATE/DELETE viele Zeilen (sogar die ganze Tabelle), um die WHERE-Klausel zu erfüllen → eine Quelle von Deadlocks. Prüfe den Plan.","command":{"mysql":"EXPLAIN <the UPDATE/DELETE involved in the deadlock>;  -- type=ALL means a full scan, no index","postgres":"EXPLAIN <query>;  -- Seq Scan or Index Scan?","oracle":"SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id'));"},"choices":[{"label":"Kein Index / macht einen Full Scan","next":"sol_index"},{"label":"Es gibt einen Index, aber es kollidiert trotzdem","next":"sol_order"}]},"sol_order":{"kind":"solution","title":"Ursache: umgekehrte Sperrreihenfolge","detail":"Die häufigste Deadlock-Ursache: Zwei Transaktionen greifen in unterschiedlicher Reihenfolge auf Ressourcen zu. Laufen A→B und B→A gleichzeitig, entsteht ein Deadlock.","steps":["Auf jedem Codepfad in DERSELBEN Reihenfolge auf Ressourcen zugreifen (z. B. immer niedrigste id → höchste id).","Transaktionen kurz halten; Benutzerinteraktion/lange Arbeit aus der Transaktion herausnehmen.","Bei Bedarf Sperren früh in deterministischer Reihenfolge nehmen (geordnetes SELECT ... FOR UPDATE).","In der Anwendung automatische Wiederholung (Retry + Backoff) für das Deadlock-Opfer einbauen — das ist Pflicht."],"related":["blocking-sessions","open-transactions"]},"sol_index":{"kind":"solution","title":"Ursache: fehlender Index → breite Sperren","detail":"Ein UPDATE/DELETE ohne Index sperrt mehr Zeilen als nötig und macht Deadlocks weit wahrscheinlicher.","steps":["Einen Index auf der Spalte in der WHERE-Klausel anlegen; die Sperre beschränkt sich dann auf die betroffenen Zeilen.","Den Plan erneut abrufen und prüfen, ob aus dem Full Scan (Seq Scan / type=ALL) ein Indexzugriff geworden ist.","Massen-Updates in kleinen Batches (Chunks) ausführen."],"related":["create-index","list-indexes"]},"sol_hotspot":{"kind":"solution","title":"Ursache: Hotspot","detail":"Viele Transaktionen stauen sich gleichzeitig auf einer einzigen Ressource wie einer Zählerzeile/einem Key.","steps":["Die heiße Zeile verteilen: Sequence/Identity verwenden, Zähler aufteilen (Sharded Counter).","Transaktionsdauer verkürzen, damit die Sperre kürzer gehalten wird.","Wenn möglich Optimistic Locking (Version/Zeitstempel) oder ein Queue-Muster verwenden."],"related":["sequence","open-transactions"]}}},"solver/disk-full.json":{"id":"disk-full","title":"Platte / Tablespace voll","icon":"💽","category":"Kapazität","intro":"Du hast 'unable to extend', 'disk full', 'could not extend' oder 'log full' erhalten. Keine Panik: Finde zuerst heraus, WAS voll ist, gewinne sicher Platz zurück und behebe es dann dauerhaft.","start":"what","nodes":{"what":{"kind":"step","title":"Was ist voll?","detail":"An der falschen Stelle aufzuräumen hilft nicht. Miss zuerst die Belegung: Ist es eine Datendatei, das Transaktionslog, Archive/Redo, Temp oder die OS-Platte?","command":{"oracle":"SELECT d.tablespace_name, ROUND((1-(d.alloc-NVL(f.free,0))/d.maxb)*100,1) AS pct_free\nFROM (SELECT tablespace_name, SUM(bytes) alloc, SUM(GREATEST(bytes,maxbytes)) maxb FROM dba_data_files GROUP BY tablespace_name) d LEFT JOIN (SELECT tablespace_name, SUM(bytes) free FROM dba_free_space GROUP BY tablespace_name) f ON f.tablespace_name = d.tablespace_name ORDER BY pct_free;","postgres":"SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY 2 DESC;  -- OS disk: df -h","mssql":"SELECT name, size/128.0 AS size_mb, size/128.0 - CAST(FILEPROPERTY(name,'SpaceUsed') AS int)/128.0 AS free_mb, type_desc\nFROM sys.database_files;","mysql":"SELECT table_schema, ROUND(SUM(data_length+index_length)/1048576,1) AS mb FROM information_schema.tables GROUP BY table_schema ORDER BY mb DESC;"},"choices":[{"label":"Datendatei / Tablespace ist voll","next":"data"},{"label":"Transaktionslog / Archive-Redo ist voll","next":"log"},{"label":"Temp / Undo ist voll","next":"sol_temp"},{"label":"OS-Platte ist voll (nicht die DB)","next":"sol_os"}]},"data":{"kind":"step","title":"Was ist der größte Verbraucher?","detail":"Meist ist eine einzelne ausufernde Tabelle/ein Segment der Verursacher. Finde die größten.","command":{"oracle":"SELECT segment_name, segment_type, ROUND(bytes/1048576,1) AS mb FROM dba_segments ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;","mssql":"SELECT TOP 10 OBJECT_NAME(object_id) AS table_name, SUM(reserved_page_count)*8/1024 AS mb FROM sys.dm_db_partition_stats GROUP BY object_id ORDER BY mb DESC;"},"choices":[{"label":"Es gibt eine unerwartet große/ausufernde Tabelle","next":"sol_runaway"},{"label":"Normales Wachstum, der Platz ist wirklich aufgebraucht","next":"sol_extend"},{"label":"Zu viele tote Zeilen / Bloat","next":"sol_reclaim"}]},"log":{"kind":"step","title":"Warum wächst das Log?","detail":"Log/Archive lässt sich erst bereinigen, wenn die älteste aktive Transaktion/das älteste Backup abgeschlossen ist. Warum hat es sich angesammelt?","command":{"mssql":"SELECT name, log_reuse_wait_desc FROM sys.databases WHERE name=DB_NAME();  -- why can it not be truncated?","postgres":"SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained FROM pg_replication_slots ORDER BY 3 DESC;","oracle":"SELECT * FROM v$flash_recovery_area_usage;  -- FRA (archive) usage"},"choices":[{"label":"MSSQL: Es werden keine Log-Backups erstellt (LOG_BACKUP)","next":"sol_logbackup"},{"label":"PG: Ein hängender Replication Slot hält WAL zurück","next":"sol_slot"},{"label":"Oracle: Archivlog-Bereich (FRA) ist voll","next":"sol_archive"},{"label":"Eine lange offene Transaktion hält das Log fest","next":"sol_longtxn"}]},"sol_runaway":{"kind":"solution","title":"Ursache: Ausufernde Tabelle/ausuferndes Segment","detail":"Ein einzelnes Objekt ist unerwartet gewachsen (unkontrollierte Inserts, eine nie bereinigte Queue-/Log-Tabelle).","steps":["Prüfen, ob das Objekt tatsächlich gebraucht wird; ist es eine Log-/Queue-Tabelle, eine Aufbewahrungsregel anwenden.","Nicht benötigte alte Daten per Partition Drop / TRUNCATE bereinigen (nach Backup/Freigabe).","Das unkontrollierte Wachstum in der Anwendung stoppen; einen Wachstumsalarm einrichten."],"related":["largest-tables","create-partition"]},"sol_extend":{"kind":"solution","title":"Ursache: Echter Kapazitätsbedarf","detail":"Normales Wachstum; es muss Platz hinzugefügt werden.","steps":["Der Datendatei Platz hinzufügen oder AUTOEXTEND aktivieren (Oracle: ALTER DATABASE DATAFILE ... AUTOEXTEND ON MAXSIZE).","Bei Bedarf eine neue Datendatei/einen neuen Tablespace hinzufügen oder die Platte vergrößern.","Alarme bei 80%/90% Belegung einrichten, damit es keine Überraschungen mehr gibt."],"related":["free-space"]},"sol_reclaim":{"kind":"solution","title":"Ursache: Bloat / tote Zeilen","detail":"Der Platz gelöschter/geänderter Zeilen wurde nicht zurückgewonnen.","steps":["Platz zurückgewinnen: PG VACUUM (bei Bedarf pg_repack), Oracle SHRINK SPACE, MySQL OPTIMIZE TABLE, Mongo compact.","Autovacuum-/automatische Wartungseinstellungen aggressiver einstellen.","VACUUM FULL / OPTIMIZE sperren die Tabelle — außerhalb der Spitzenzeiten ausführen."],"related":["reclaim-space","free-space"]},"sol_temp":{"kind":"solution","title":"Ursache: Temp / Undo voll","detail":"Ein großer Sort/Hash oder eine lange offene Transaktion hat den temporären Platz gefüllt.","steps":["Die Abfrage finden, die Temp gesprengt hat (großer Sort-/Hash-Spill), und optimieren.","Die lange offene Transaktion finden; sie ist diejenige, die das Undo festhält (Oracle).","Bei Bedarf Temp-/Undo-Platz vergrößern; die Retention-Einstellung überprüfen."],"related":["open-transactions","reclaim-space"]},"sol_os":{"kind":"solution","title":"Ursache: OS-Platte voll","detail":"Das Problem liegt auf der Betriebssystemplatte, nicht in der DB. Logs, Core Dumps, alte Backups oder Trace-Dateien können Platz belegen.","steps":["Das volle Dateisystem mit df -h finden; nach den größten Verzeichnissen suchen (du -sh *).","Alte Log-/Trace-/Core-Dump-/Backup-Dateien bereinigen (mit Freigabe).","Die Diagnoseverzeichnisse der DB (Oracle ADR, PG-Logs) unter Rotation stellen."]},"sol_logbackup":{"kind":"solution","title":"Ursache: Es werden keine Log-Backups erstellt (SQL Server)","detail":"Im Recovery Model FULL wächst das Log unbegrenzt, wenn keine Log-Backups erstellt werden.","steps":["Ein Log-Backup erstellen (BACKUP LOG) — damit wird das Log wiederverwendbar.","Sollte das Recovery Model wirklich SIMPLE sein, ändere es.","Die Logdatei nur bei Bedarf verkleinern (DBCC SHRINKFILE) und einen regelmäßigen Log-Backup-Job einrichten."],"related":["last-backup-status"],"playbook":"log-growth"},"sol_slot":{"kind":"solution","title":"Ursache: Verwaister Replication Slot (PG)","detail":"Ein hängender (inaktiver) Slot verhindert das Entfernen von WAL und bläht pg_wal auf.","steps":["Prüfen, ob der Slot tatsächlich genutzt wird.","Wird er nicht gebraucht, löschen: SELECT pg_drop_replication_slot('slot_name');","Die aktive Replica reparieren, damit der Slot vorankommt; einen Alarm für Slot-Verzögerung einrichten."],"related":["free-space"],"playbook":"log-growth"},"sol_archive":{"kind":"solution","title":"Ursache: Archivlog-Bereich (FRA) voll (Oracle)","detail":"Archivlogs haben sich angesammelt und die FRA ist voll; bleibt der Archiver stehen, kann die Datenbank keine Schreibzugriffe mehr annehmen.","steps":["Bereits gesicherte alte Archivlogs mit RMAN löschen (NIEMALS ohne Backup löschen).","Bei Bedarf die FRA-Größe erhöhen (db_recovery_file_dest_size).","Den Job zum Bereinigen/Sichern der Archivlogs so einrichten, dass er regelmäßig läuft."],"related":["last-backup-status"]},"sol_longtxn":{"kind":"solution","title":"Ursache: Lange offene Transaktion","detail":"Die älteste aktive Transaktion verhindert, dass Log/Undo zurückgewonnen werden.","steps":["Die älteste offene Transaktion finden und committen/zurückrollen lassen (oder mit ihrem Eigentümer sprechen).","Das Anwendungsverhalten beheben, das Sessions 'idle in transaction' hinterlässt; ein Timeout setzen.","Lange Reports auf eine separate Replica/in ein separates Zeitfenster verlagern."],"related":["open-transactions"],"playbook":"log-growth"}}},"solver/connection-storm.json":{"id":"connection-storm","title":"Connection Storm (zu viele Verbindungen)","icon":"🌊","category":"Kapazität","intro":"Du hast 'too many connections', 'remaining connection slots reserved' oder ORA-00018 erhalten; neue Verbindungen werden abgelehnt. Finde die Quelle, entlaste sofort, behebe es dauerhaft.","start":"measure","nodes":{"measure":{"kind":"step","title":"Lage messen: Wie viele Verbindungen, und wie viele sind aktiv?","detail":"Zuerst Gesamtverbindungen, Limit und Aufteilung aktiv/inaktiv ansehen.","command":{"oracle":"SELECT status, COUNT(*) FROM v$session WHERE type='USER' GROUP BY status;\nSELECT name, value FROM v$parameter WHERE name IN ('processes','sessions');","postgres":"SELECT state, count(*) FROM pg_stat_activity GROUP BY state;\nSHOW max_connections;","mysql":"SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';","mssql":"SELECT status, COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY status;","redis":"INFO clients"},"choices":[{"label":"Die meisten Verbindungen sind idle / idle in transaction","next":"sol_idle"},{"label":"Die meisten sind aktiv; ich muss die Quelle finden","next":"who"}]},"who":{"kind":"step","title":"Wer/woher? Verbindungen nach Quelle gruppieren","detail":"Meist ist der Pool eines einzelnen Anwendungsservers explodiert oder es gibt ein Connection Leak. Betrachte die Verteilung nach Host/Benutzer/Anwendung.","command":{"postgres":"SELECT client_addr, usename, application_name, count(*) FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;","mysql":"SELECT host, user, count(*) FROM information_schema.processlist GROUP BY host, user ORDER BY 3 DESC;","mssql":"SELECT host_name, program_name, login_name, COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY host_name, program_name, login_name ORDER BY 4 DESC;","oracle":"SELECT machine, program, COUNT(*) FROM v$session WHERE type='USER' GROUP BY machine, program ORDER BY 3 DESC;"},"choices":[{"label":"Eine einzelne Anwendung/ein Host hat die Verbindungen explodieren lassen (Pool)","next":"sol_pool"},{"label":"Die Last ist wirklich gestiegen; alle sind legitim und aktiv","next":"sol_scale"}]},"sol_idle":{"kind":"solution","title":"Ursache: Anhäufung inaktiver Verbindungen / Leak","detail":"Verbindungen werden geöffnet und nie geschlossen oder 'idle in transaction' gelassen; sie belegen Slots umsonst.","steps":["Lange inaktive / idle-in-transaction-Verbindungen bereinigen; den für Admins reservierten Slot verwenden.","Serverseitige Timeouts setzen: PG idle_in_transaction_session_timeout, MySQL wait_timeout, Oracle-Profil idle_time.","Sicherstellen, dass die Anwendung Verbindungen (und Transaktionen) schließt; das Leak an der Quelle beheben."],"related":["open-transactions","kill-session","connection-count"],"playbook":"connection-storm"},"sol_pool":{"kind":"solution","title":"Ursache: Anwendungs-Pool ist explodiert","detail":"Der Connection Pool eines Anwendungsservers ist unbegrenzt/zu groß oder hat ein Leak. Er erzeugt die Verbindungen.","steps":["Sofort: inaktive Verbindungen der verursachenden Anwendung beenden und ihren Pool neu starten.","Auf Anwendungsseite eine sinnvolle Pool-Größe festlegen (Tools > Connection Pool calculator).","Einen Pooler zwischen Server und DB schalten (PgBouncer / ProxySQL / Oracle DRCP / Connection Manager (CMAN)).","max_connections nicht blind erhöhen; das verbraucht Speicher und verschiebt das Problem nur."],"related":["connection-count","kill-session"],"playbook":"connection-storm"},"sol_scale":{"kind":"solution","title":"Ursache: Echter Lastanstieg","detail":"Die Verbindungen sind legitim und aktiv; die Kapazität reicht tatsächlich nicht aus.","steps":["Die Anzahl der Verbindungen zur DB verringern, indem sie über einen Connection Pooler gemultiplext werden.","Leselast auf Read Replicas verteilen.","Bei Bedarf max_connections kontrolliert gemäß dem Speicherbudget erhöhen; auch den Serverspeicher skalieren."],"related":["connection-count"],"playbook":"connection-storm"}}},"solver/replication-lag.json":{"id":"replication-lag","title":"Replikationsverzögerung / gestoppt","icon":"🔁","category":"Replikation","intro":"Die Standby/Replica fällt hinter den Primary zurück, oder die Replikation ist ganz gestoppt. Dieser Baum grenzt die Art der Verzögerung Schritt für Schritt ein (wachsend, konstant oder gestoppt), um zur Ursache zu gelangen, etwa einem Apply-Fehler, einem Single-Thread-Engpass, einem Recovery-Konflikt oder Korruption.","start":"root","nodes":{"root":{"kind":"step","title":"Wie verhält sich die Verzögerung?","detail":"Miss zuerst die Lage. Betrachte den Replica-Stream und die Verzögerungsmetrik: Wächst die Verzögerung mit der Zeit, bleibt sie auf konstantem Niveau, oder ist das Apply ganz gestoppt? Das teilt die Untersuchung in drei Hauptzweige.","command":{"postgres":"SELECT client_addr, state, write_lag, flush_lag, replay_lag,\n       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS bytes_behind\nFROM pg_stat_replication;","mysql":"SHOW REPLICA STATUS\\G","oracle":"SELECT name, value, unit FROM v$dataguard_stats WHERE name IN ('apply lag','transport lag');","mssql":"SELECT database_id, is_local, synchronization_state_desc, redo_queue_size, log_send_queue_size\nFROM sys.dm_hadr_database_replica_states;","redis":"INFO replication","elasticsearch":"GET _cluster/health"},"choices":[{"label":"Apply ganz gestoppt / es gibt einen Fehler","next":"stopped"},{"label":"Verzögerung wächst weiter (kann nicht aufholen)","next":"growing"},{"label":"Verzögerung ist konstant, aber eine lange Abfrage steht im Verdacht","next":"standby_query"},{"label":"Weit zurück / wirkt inkonsistent","next":"sol_rebuild"}]},"stopped":{"kind":"step","title":"Was sagt der Apply-Fehler?","detail":"Ist die Replikation gestoppt, gibt es meist einen Fehler in einem angewendeten Event. Sieh dir die Fehlermeldung an: Fehlt eine Zeile/Tabelle auf dem Ziel, gibt es einen Primary-Key-Konflikt oder einen Verbindungs-/Netzwerkabbruch?","command":{"mysql":"SHOW REPLICA STATUS\\G  -- Last_Error, Last_SQL_Error, Replica_IO_Running, Replica_SQL_Running","postgres":"SELECT status, last_msg_receipt_time, latest_end_lsn, slot_name\nFROM pg_stat_wal_receiver;","oracle":"SELECT process, status, sequence#, block# FROM v$managed_standby WHERE process LIKE 'MRP%';","elasticsearch":"GET _cat/recovery?v&active_only=true"},"choices":[{"label":"Fehler wegen kollidierender/fehlender Zeile oder Tabelle","next":"sol_apply_error"},{"label":"Netzwerk/Verbindung abgebrochen, IO-Thread gestoppt","next":"sol_connection"}]},"growing":{"kind":"step","title":"Warum kann sie nicht aufholen?","detail":"Wächst die Verzögerung weiter, kommt die Replica mit der Schreibrate des Primary nicht mit. Ist das Single-Thread-Apply der Engpass, oder ist die Platte/IO der Replica langsam? Betrachte Zustand des Apply Workers und IO-Waits.","command":{"mysql":"SHOW REPLICA STATUS\\G  -- Seconds_Behind_Source and apply thread state\nSELECT * FROM performance_schema.replication_applier_status_by_worker;","postgres":"SELECT pid, wait_event_type, wait_event, state\nFROM pg_stat_activity WHERE backend_type='startup';","mssql":"SELECT database_id, redo_queue_size, redo_rate, log_send_queue_size\nFROM sys.dm_hadr_database_replica_states;"},"choices":[{"label":"Single-Thread-Apply + hohe Schreiblast","next":"sol_parallel"},{"label":"Die Platte/IO der Replica ist langsam","next":"sol_io"}]},"standby_query":{"kind":"step","title":"Gibt es eine lange Abfrage auf der Standby?","detail":"Eine lange Abfrage auf einer schreibgeschützten Standby kann das Apply (Recovery) blockieren. In PostgreSQL entsteht so ein Recovery-Konflikt. Betrachte aktive Abfragen und Konfliktzähler auf der Standby.","command":{"postgres":"SELECT now()-query_start AS dur, pid, state, query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start;\nSELECT confl_snapshot, confl_lock, confl_bufferpin FROM pg_stat_database_conflicts WHERE datname=current_database();"},"choices":[{"label":"Ja, eine lange Abfrage blockiert das Apply","next":"sol_recovery_conflict"},{"label":"Nein, keine Abfrage, aber sie liegt trotzdem zurück","next":"sol_parallel"}]},"sol_apply_error":{"kind":"solution","title":"Ursache: Apply-Fehler (Datenkonflikt)","detail":"Die Replica hängt an einem Event, das sie nicht anwenden kann: eine auf dem Ziel fehlende Zeile, ein Primary-Key-Konflikt oder eine fehlende Tabelle. Behebe die Ursache der Inkonsistenz, statt blind zu überspringen.","steps":["Fehler und hängendes Event untersuchen: Welche Tabelle und welche Operation (delete/update/insert) ist fehlgeschlagen?","Die Ursache finden: manuell auf das Ziel geschriebene Daten, fehlende DDL (Tabelle/Spalte) oder ein beschädigter Binlog-/WAL-Eintrag.","Das Schema angleichen: fehlende Tabelle/Spalte hinzufügen; die kollidierende Zeile mit dem Primary vergleichen und korrigieren.","Musst du das Event überspringen (z. B. ein einzelnes Event in MySQL), dokumentiere es und prüfe danach die Datenkonsistenz der Replica.","Wiederholung verhindern: direkte Schreibzugriffe auf das Ziel unterbinden und DDL über den Primary ausführen."],"related":["replication-status"],"playbook":"replication-lag"},"sol_connection":{"kind":"solution","title":"Ursache: Verbindungs- / Netzwerkabbruch","detail":"Der Empfänger-Thread (IO/receiver) kann sich nicht mit dem Primary verbinden. Das Problem können Netzwerk, Authentifizierung oder angesammelte/bereinigte Log-Segmente sein.","steps":["Netzwerkzugriff zwischen Primary und Replica sowie die Berechtigungen des Replikationsbenutzers prüfen.","Prüfen, ob die benötigten WAL-/Binlog-Segmente auf dem Primary noch existieren (Aufbewahrung/expire).","In PostgreSQL prüfen, ob der Replication Slot existiert und aktiv ist; ohne Slot wurde WAL eventuell zu früh entfernt.","Verbindung neu starten; sobald die Logs wieder fließen, sollte sich die Verzögerung schließen."],"related":["replication-status","list-replicas"],"playbook":"replication-lag"},"sol_parallel":{"kind":"solution","title":"Ursache: Single-Thread-Apply-Engpass","detail":"Während der Primary parallel schreibt, wendet die Replica Änderungen in einer einzigen Abfolge an und kommt nicht mit. Das Apply parallelisieren.","steps":["Paralleles Apply aktivieren: in MySQL replica_parallel_workers + replica_parallel_type=LOGICAL_CLOCK.","Bei physischer Replikation in PostgreSQL ist das Apply ein einzelner Prozess; die Erzeugungsrate auf dem Primary durch Aufteilen großer Transaktionen senken.","Schwere Batch-Jobs in verkehrsarme Zeiten verlegen oder aufteilen.","Nach der Änderung die Verzögerungsmetrik überwachen; sie sollte auf null sinken."],"related":["replication-status"],"playbook":"replication-lag"},"sol_io":{"kind":"solution","title":"Ursache: IO-Engpass der Replica","detail":"Der Apply-Thread wartet auf die Platte, nicht auf die CPU. Der Speicher der Replica kommt mit der Schreibrate des Primary nicht mit.","steps":["IO-Latenz/-Auslastung der Replica-Platte messen; mit dem Primary vergleichen.","Ist die Hardware der Replica schwächer (langsame Platte, weniger RAM), sie an den Primary angleichen; asymmetrische Hardware ist eine häufige Ursache.","Die Platte für das Redo-/WAL-Apply beschleunigen; Puffer/Cache ausreichend dimensionieren.","Verbessert es sich nicht, die Schreiblast verringern oder den Leseverkehr auf eine andere Replica verteilen."],"related":["replication-status"],"playbook":"replication-lag"},"sol_recovery_conflict":{"kind":"solution","title":"Ursache: Recovery-Konflikt auf der Standby","detail":"Eine lange Leseabfrage auf der Standby blockiert das Anwenden von Änderungen des Primary (Vacuum-/Sperrkonflikt). PostgreSQL löst das, indem es das Apply verzögert oder die Abfrage abbricht.","steps":["Die lange blockierende Abfrage finden; wird sie wirklich gebraucht, auf ihr Ende warten, sonst beenden.","Mit hot_standby_feedback=on den Primary Standby-Abfragen berücksichtigen lassen (auf Kosten von Bloat auf dem Primary).","Alternativ max_standby_streaming_delay an die Workload anpassen (Balance zwischen Verzögerungstoleranz und Abfrageabbruch).","Lange Reports auf eine separate Replica verlagern, wo sie das Apply nicht blockieren, oder auf eine andere Zeit."],"related":["replication-status","open-transactions"],"playbook":"replication-lag"},"sol_rebuild":{"kind":"solution","title":"Ursache: Replica zu weit zurück / beschädigt - neu aufbauen","detail":"Die Replica liegt zu weit zurück, um sich zu erholen, die benötigten Logs wurden entfernt oder die Daten sind inkonsistent. Der sicherste Weg ist ein Neuaufbau aus einem frischen Backup.","steps":["Ein aktuelles Base Backup vom Primary erstellen: PG pg_basebackup, MySQL clone/xtrabackup, Oracle RMAN duplicate.","Das Backup auf der Replica wiederherstellen und die Replikationskonfiguration (Slot/Benutzer/Position) neu einrichten.","Replikation starten und den Fortschritt der initialen Synchronisierung (Catch-up) überwachen.","Nachdem die Verzögerung null erreicht hat, Datenkonsistenz und Leseverkehr prüfen, dann in Betrieb nehmen."],"related":["replication-status","list-replicas","last-backup-status"],"playbook":"replication-lag"}}},"solver/ora-01555.json":{"id":"ora-01555","title":"ORA-01555: Snapshot too old (Oracle)","icon":"⏳","category":"Oracle","intro":"Der Oracle-Fehler 'ORA-01555: snapshot too old' bedeutet, dass die alten (Undo-)Daten, die eine Abfrage für ein konsistentes Lesen braucht, bereits überschrieben wurden. Dieser Baum grenzt die Quelle des Fehlers ein (lange Abfrage + zu wenig UNDO, hoher Undo-Umschlag oder LOB) und führt zur richtigen Lösung.","start":"root","nodes":{"root":{"kind":"step","title":"Wo tritt der Fehler auf?","detail":"Bestimme zuerst den Kontext des Fehlers. Tritt das klassische ORA-01555 bei einem lang laufenden SELECT/Report auf oder beim Zugriff auf eine LOB-Spalte (CLOB/BLOB)? LOB-bezogenes ORA-01555 ist ein anderer Mechanismus (PCTVERSION/RETENTION) und wird separat gelöst.","command":{"oracle":"SELECT sql_id, elapsed_time/1e6 AS sec, sql_text\nFROM v$sql ORDER BY elapsed_time DESC FETCH FIRST 10 ROWS ONLY;"},"choices":[{"label":"Bei einer lang laufenden Abfrage/einem Report","next":"undo_size"},{"label":"Beim Zugriff auf eine LOB-Spalte","next":"sol_lob"}]},"undo_size":{"kind":"step","title":"Reicht UNDO, ist die Retention angemessen?","detail":"Eine lange Abfrage liest das alte Abbild von Blöcken, die seit ihrem Start geändert wurden, aus UNDO. Ist der UNDO-Tablespace klein oder undo_retention kürzer als die Abfragedauer, wird altes Undo überschrieben. UNDO-Nutzung und Retention messen.","command":{"oracle":"SHOW PARAMETER undo_retention;\nSELECT tablespace_name, SUM(bytes)/1024/1024 AS mb, status\nFROM dba_undo_extents GROUP BY tablespace_name, status;\nSELECT MAX(maxquerylen) AS longest_query_sec, MAX(tuned_undoretention) AS tuned_retention\nFROM v$undostat;"},"choices":[{"label":"UNDO ist klein / Retention kürzer als die Abfrage","next":"sol_undo"},{"label":"UNDO reicht, aber Commits sind sehr häufig","next":"sol_commits"},{"label":"Die Abfrage läuft übermäßig lange","next":"sol_query"}]},"sol_undo":{"kind":"solution","title":"Ursache: zu wenig UNDO / Retention","detail":"Das von der langen Abfrage benötigte alte Undo wurde überschrieben, weil die Retention-Dauer abgelaufen oder der Tablespace voll war. UNDO vergrößern und die Retention so setzen, dass sie die Abfragedauer abdeckt.","steps":["Ausgehend von der längsten Abfragedauer (v$undostat.maxquerylen) undo_retention darüber anheben: ALTER SYSTEM SET undo_retention=10800;","Den UNDO-Tablespace so weit vergrößern, dass er die Retention fasst, oder Autoextend aktivieren: ALTER DATABASE DATAFILE '...' AUTOEXTEND ON MAXSIZE ...;","Für kritische lange Abfragen garantieren: ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;  (Vorsicht: Läuft UNDO voll, kann DML fehlschlagen).","Nach der Änderung denselben Report erneut ausführen und bestätigen, dass ORA-01555 verschwunden ist."],"related":["show-config-param"],"playbook":"ora-01555"},"sol_commits":{"kind":"solution","title":"Ursache: hoher Undo-Umschlag (häufige Commits)","detail":"Starke DML mit sehr häufigen Commits recycelt Undo-Blöcke schnell und löscht das alte Abbild, das die lange Abfrage braucht. Der Effekt 'commit cleanout' verschärft das.","steps":["Unnötig häufige Commits reduzieren: Batch-Commits statt eines Commits pro Zeile in einer Schleife verwenden.","Um den Undo-Umschlag zu verlangsamen, schwere DML-Workloads nicht zeitgleich mit dem langen Report einplanen.","undo_retention erhöhen und bei Bedarf altes Undo mit RETENTION GUARANTEE schützen.","Den Report aus dem Fenster hoher Schreiblast verlegen oder an einen konsistenten Lesezeitpunkt binden."],"related":["open-transactions","show-config-param"],"playbook":"ora-01555"},"sol_query":{"kind":"solution","title":"Ursache: übermäßig lange Abfragedauer","detail":"Die Abfrage läuft so lange, dass selbst eine vernünftige undo_retention nicht ausreicht. Die dauerhafte Lösung ist, die Abfrage zu beschleunigen oder ihre Last auszulagern.","steps":["Die Abfrage optimieren: fehlende Indizes, schlechte Pläne oder unnötige Full Scans beheben; ihre Dauer verkürzen.","Den großen Job aufteilen (nach Datum/Bereich), sodass jeder Teil ins Undo-Fenster passt.","Den schweren Report auf eine Standby/Replica oder in eine separate Lesezeit verlegen — damit er nicht mit aktiver DML kollidiert.","Sobald die Dauer akzeptabel ist, undo_retention entsprechend setzen."],"related":["explain-plan"],"playbook":"ora-01555"},"sol_lob":{"kind":"solution","title":"Ursache: LOB-Retention / PCTVERSION","detail":"ORA-01555 beim Lesen eines LOB bedeutet, dass der Platz für alte LOB-Versionen (RETENTION oder PCTVERSION) nicht ausreicht. LOBs haben einen eigenen Versionierungsmechanismus.","steps":["Aktuelle Einstellung der LOB-Spalte anzeigen: SELECT retention, pctversion FROM dba_lobs WHERE table_name='...' AND column_name='...';","Je nach AUTO-/MANUAL-Undo-Modus die passende Einstellung anheben: ALTER TABLE t MODIFY LOB (my_column) (RETENTION);  oder im MANUAL-Modus PCTVERSION erhöhen.","LOBs mit RETENTION basieren auf dem undo_retention-Wert der Datenbank — halte auch diesen ausreichend hoch.","Lange Operationen mit LOB-Zugriff verkürzen; den Fehler nach der Änderung erneut testen."],"related":["show-config-param"],"playbook":"ora-01555"}}},"solver/backup-restore.json":{"id":"backup-restore","title":"Backup- & Restore-Entscheidung","icon":"💾","category":"Backup & Recovery","intro":"Ein Entscheidungsbaum, der dich Schritt für Schritt durch die nötigen Backup-Aufgaben führt (Backup erstellen, wiederherstellen, auf einen bestimmten Zeitpunkt zurückkehren - PITR, oder ein Backup verifizieren) und dich für jede Datenbank zu den richtigen Befehlen und Schritten bringt.","start":"root","nodes":{"root":{"kind":"step","title":"Was willst du tun?","detail":"Wähle dein Ziel. Erstellst du ein neues Backup, stellst du ein vorhandenes wieder her, kehrst du zu einem bestimmten Zeitpunkt zurück (kurz vor einem Ausfall) oder prüfst du, ob dein vorhandenes Backup in Ordnung ist?","choices":[{"label":"Ich will ein Backup ERSTELLEN","next":"take"},{"label":"Ich will ein Backup WIEDERHERSTELLEN","next":"restore"},{"label":"Auf einen bestimmten Zeitpunkt zurückkehren (PITR)","next":"pitr"},{"label":"Ich will ein Backup VERIFIZIEREN","next":"verify"}]},"take":{"kind":"step","title":"Welche Art von Backup?","detail":"Ein logisches Backup (Dump) ist portabel und eignet sich für selektive Restores, ist aber bei großen Datenmengen langsam. Ein physisches Backup (Datei-/Blockebene) eignet sich für große Datenbanken und schnelle Wiederherstellung. Wähle nach deinem Bedarf.","choices":[{"label":"Logisches Backup (Dump / Export)","next":"sol_logical_backup"},{"label":"Physisches / vollständiges Backup","next":"sol_physical_backup"}]},"sol_logical_backup":{"kind":"solution","title":"Logisches Backup (Dump) erstellen","detail":"Exportiert Daten als SQL-/BSON-Anweisungen. Ideal für den Umzug über Versionen/Plattformen hinweg und das Wiederherstellen einer einzelnen Tabelle/Collection.","steps":["PostgreSQL: pg_dump -Fc -d mydb -f backup.dump  (pg_dumpall für den ganzen Cluster).","Oracle: expdp app_user/password directory=DP_DIR dumpfile=backup.dmp schemas=APP_SCHEMA logfile=exp.log","MySQL/MariaDB: mysqldump --single-transaction --routines --triggers mydb > backup.sql","MongoDB: mongodump --db mydb --out /backup/dir","Redis: mit BGSAVE im Hintergrund einen RDB-Snapshot erstellen (dump.rdb).","Elasticsearch: zuerst ein Snapshot-Repository registrieren, dann PUT _snapshot/my_repo/snap1?wait_for_completion=true","Das Backup an einen sicheren Ort verschieben und Größe/Abschlussstatus festhalten."],"related":["logical-dump","last-backup-status"],"playbook":"backup-restore"},"sol_physical_backup":{"kind":"solution","title":"Physisches / vollständiges Backup erstellen","detail":"Eine Kopie der Datendateien auf Block-/Dateiebene. Schnell bei großen Datenbanken und die Grundlage für PITR und vollständige Wiederherstellung.","steps":["Oracle: RMAN> BACKUP DATABASE PLUS ARCHIVELOG;  (zusammen mit den Archivlogs).","PostgreSQL: pg_basebackup -D /backup/base -Fp -Xs -P  (mit aktivierter WAL-Archivierung).","SQL Server: BACKUP DATABASE [mydb] TO DISK='D:\\backup\\db.bak' WITH CHECKSUM, COMPRESSION;","MySQL/MariaDB: xtrabackup --backup --target-dir=/backup/base  (konsistente physische Kopie).","MongoDB: einen Dateisystem-Snapshot oder einen konsistenten Snapshot von einem Replica-Set-Mitglied erstellen.","Das Backup an einen Offline-/entfernten Ort kopieren und den Status des letzten Backups festhalten."],"related":["last-backup-status"],"playbook":"backup-restore"},"restore":{"kind":"solution","title":"Backup wiederherstellen","detail":"Das am besten geeignete vorhandene Backup auf dem Ziel wiederherstellen. Zuerst Integrität und Zeitstempel des Backups prüfen, dann mit dem passenden Befehl der Engine laden.","steps":["Datum und Zustand des wiederherzustellenden Backups prüfen (kenne dein Datenverlustfenster).","PostgreSQL: pg_restore -d new_db backup.dump   (für einen SQL-Dump: psql -d new_db -f backup.sql).","Oracle: RMAN> RESTORE DATABASE; RECOVER DATABASE; ALTER DATABASE OPEN;  (logischer Import mit impdp).","MySQL/MariaDB: mysql mydb < backup.sql   (physisch: xtrabackup --copy-back).","SQL Server: RESTORE DATABASE [mydb] FROM DISK='D:\\backup\\db.bak' WITH RECOVERY, REPLACE;","MongoDB: mongorestore --db mydb /backup/dir","Nach dem Restore die Daten mit Zeilenzählungen/Anwendungsprüfungen validieren."],"related":["restore-dump","last-backup-status"],"playbook":"backup-restore"},"pitr":{"kind":"solution","title":"Auf einen bestimmten Zeitpunkt zurückkehren (PITR)","detail":"Du willst sekundengenau auf den Zeitpunkt kurz vor einem versehentlichen Löschen/einer Korruption zurück. Dafür brauchst du ein Vollbackup + die Transaktionslogs bis zu diesem Zeitpunkt (Archivlogs / WAL / Binlog).","steps":["Den Zielzeitpunkt genau bestimmen (kurz vor dem Vorfall) und prüfen, ob die benötigten Logs verfügbar sind.","Oracle: RMAN> RESTORE DATABASE; RECOVER DATABASE UNTIL TIME \"TO_DATE('2026-07-04 10:00:00','YYYY-MM-DD HH24:MI:SS')\"; ALTER DATABASE OPEN RESETLOGS;","PostgreSQL: das Base Backup wiederherstellen, dann recovery_target_time='2026-07-04 10:00:00' setzen und WAL bis zu diesem Zeitpunkt anwenden.","SQL Server: RESTORE ... WITH NORECOVERY; dann RESTORE LOG ... WITH STOPAT='2026-07-04 10:00:00', RECOVERY;","MySQL/MariaDB: das Vollbackup wiederherstellen, dann das Binlog mit mysqlbinlog --stop-datetime='2026-07-04 10:00:00' bis zu diesem Zeitpunkt anwenden.","Nach dem Öffnen prüfen, ob die Daten auf dem erwarteten Zeitpunkt stehen."],"related":["restore-dump","last-backup-status"],"playbook":"backup-restore"},"verify":{"kind":"solution","title":"Backup verifizieren","detail":"Ein Backup ist erst dann ein Backup, wenn seine Wiederherstellbarkeit nachgewiesen ist. Integrität und tatsächliche Wiederherstellbarkeit regelmäßig testen.","steps":["Oracle: RMAN> RESTORE DATABASE VALIDATE;  und für Blockkonsistenz VALIDATE CHECK LOGICAL DATABASE;","SQL Server: Backups WITH CHECKSUM erstellen und RESTORE VERIFYONLY ... WITH CHECKSUM ausführen (prüft nur die Backup-Datei); für einen echten Nachweis das Backup auf einem Testserver wiederherstellen und dort DBCC CHECKDB([mydb]) WITH NO_INFOMSGS; ausführen (CHECKDB prüft eine laufende/wiederhergestellte Datenbank, nicht die Backup-Datei).","PostgreSQL: das Backup tatsächlich auf einem separaten/temporären Server wiederherstellen und bestätigen, dass pg_restore ohne Fehler abschließt.","MySQL/MongoDB: den Dump in einer Testumgebung wiederherstellen und Zeilen-/Collection-Anzahlen sowie Stichprobendaten vergleichen.","Regelmäßige 'Restore-Übungen' einplanen — Backups zu erstellen reicht nicht, weise auch den Restore nach.","Die Ergebnisse und das Datum der letzten erfolgreichen Verifizierung festhalten."],"related":["last-backup-status","restore-dump"],"playbook":"backup-restore"}}},"solver/upgrade-wizard.json":{"id":"upgrade-wizard","title":"Assistent für Versions-Upgrades","icon":"⬆️","category":"Lebenszyklus","intro":"Patch oder Major, in-place oder per Migration? Plane das Upgrade Schritt für Schritt; jeder Zweig umfasst Vorabprüfungen, Methode und Nacharbeiten. Probe es hier, bevor du die Produktion anfasst.","start":"kind","nodes":{"kind":{"kind":"step","title":"Welche Art von Upgrade führst du durch?","detail":"Ein Patch/eine Minor-Version (innerhalb derselben Major-Version, z. B. 16.2 -> 16.4, oder ein SQL Server CU) ist risikoarm und wird meist in-place durchgeführt. Eine Major-Version (z. B. 15 -> 16) kann Änderungen am Datenformat mit sich bringen und erfordert sorgfältigere Planung.","command":{"oracle":"SELECT * FROM v$version;\nSELECT patch_id, action, status FROM dba_registry_sqlpatch ORDER BY action_time DESC;","postgres":"SELECT version();","mysql":"SELECT VERSION();","mssql":"SELECT SERVERPROPERTY('ProductVersion') AS v, SERVERPROPERTY('ProductUpdateLevel') AS cu;","elasticsearch":"GET /"},"choices":[{"label":"Minor-Version / Patch (gleiche Major-Version, CU)","next":"patch_prep"},{"label":"Major-Version (die Major-Version ändert sich)","next":"major_method"},{"label":"Ich kenne den Unterschied nicht; erkläre ihn","next":"explain"}]},"explain":{"kind":"step","title":"Patch oder Major?","detail":"Patch: Die Major-Version bleibt gleich (16.2 -> 16.4); das Format der Datendateien ändert sich nicht, meist Binärdateien austauschen + Neustart. Major: Die Major-Version ändert sich (15 -> 16); Katalog/Format können sich ändern, pg_upgrade / AutoUpgrade / Dump-Restore ist nötig, und die Kompatibilität der Anwendung muss getestet werden.","choices":[{"label":"Bei mir ist es ein Patch (gleiche Major-Version)","next":"patch_prep"},{"label":"Bei mir ist es ein Major (die Major-Version ändert sich)","next":"major_method"}]},"patch_prep":{"kind":"step","title":"Sind Backup und Rollback-Plan bereit?","detail":"Auch wenn ein Patch wenig riskant ist, gilt für jedes Upgrade die goldene Regel: zuerst ein Vollbackup (und wenn möglich ein Snapshot). Du brauchst einen Punkt, zu dem du zurückkehren kannst, falls etwas schiefgeht.","choices":[{"label":"Ja, Backup + Rollback sind bereit","next":"sol_patch"},{"label":"Nein, noch nicht","next":"sol_backupfirst"}]},"sol_backupfirst":{"kind":"solution","title":"Zuerst Backup + Rollback-Plan","detail":"Richte das Sicherheitsnetz ein, bevor du mit dem Upgrade beginnst.","steps":["Ein Vollbackup erstellen (und wenn möglich einen Storage-Snapshot / Oracle Guaranteed Restore Point).","Die Rollback-Schritte aufschreiben: Wie komme ich zur alten Version zurück, wenn etwas schiefgeht?","Wartungsfenster und Kommunikationsplan festlegen, dann zum Patch-Schritt übergehen."],"related":["logical-dump","last-backup-status"]},"sol_patch":{"kind":"solution","title":"Patch einspielen","detail":"Ein Patch innerhalb derselben Major-Version. Das rollierende Einspielen im Cluster (beginnend mit den Replicas) verringert die Downtime.","steps":["Oracle: das neue RU mit opatch installieren, dann die SQL-Änderungen mit 'datapatch -verbose' anwenden.","PostgreSQL: Minor-Upgrade = neue Binärdateien installieren und den Dienst neu starten (kein initdb/Dump nötig).","MySQL: das Paket aktualisieren; in 8.0 führt der Server das Upgrade beim Start automatisch durch.","SQL Server: den passenden CU-/GDR-Installer ausführen, dann neu starten.","MongoDB/Redis/Elasticsearch: Rolling Restart; Secondary-Knoten zuerst, der Primary zuletzt.","Dann Version und Anwendung prüfen; siehst du Fehler, den Rollback-Plan ausführen."],"related":["apply-patch","check-version"],"playbook":"upgrade-rollback"},"major_method":{"kind":"step","title":"Methode für Major-Upgrades","detail":"Zwei Hauptwege: (1) In-place: auf dem bestehenden Server konvertieren (pg_upgrade, Oracle AutoUpgrade/DBUA, MySQL in-place). Schnell, aber schwer zurückzunehmen. (2) Per Migration: einen frischen Server mit der neuen Version aufbauen, die Daten übertragen (per Dump/Restore oder Replikation), dann umschalten. Sicherer, mit minimaler Downtime.","choices":[{"label":"Zuerst eine Vorbereitungs-/Kompatibilitätsprüfung ausführen","next":"precheck"},{"label":"In-place","next":"sol_inplace"},{"label":"Per Migration auf einen neuen Server (minimale Downtime)","next":"sol_migrate"}]},"precheck":{"kind":"step","title":"Kompatibilitäts- / Bereitschaftsprüfung","detail":"Vor dem Upgrade die Warnungen der Zielversion sammeln: entfernte (veraltete) Features, ungültige Objekte, Compatibility Level.","command":{"oracle":"-- AutoUpgrade analyze mode: java -jar autoupgrade.jar -config cfg -mode analyze","postgres":"-- pg_upgrade --check   (with the old and new binary paths)","mysql":"-- MySQL Shell: util.checkForServerUpgrade('root@localhost:3306', {targetVersion: '8.4.0'})","mssql":"-- Compatibility report with Data Migration Assistant (DMA)","elasticsearch":"GET _migration/deprecations"},"choices":[{"label":"Saubere Ausgabe, weiter","next":"major_method"},{"label":"Es gibt Warnungen/Inkompatibilitäten","next":"sol_fixwarn"}]},"sol_fixwarn":{"kind":"solution","title":"Zuerst die Warnungen beheben","detail":"Behebe jede Warnung im Bericht VOR dem Upgrade; sonst kann das Upgrade mittendrin scheitern oder die Anwendung kaputtgehen.","steps":["Objekte und Abfragen aktualisieren, die entfernte (veraltete) Features/Syntax verwenden.","Ungültige Objekte kompilieren/reparieren; fehlende Abhängigkeiten auflösen.","Die Schritte für Compatibility Level / Feature-Version der Zielversion planen.","Sind die Warnungen beseitigt, die Vorabprüfung erneut ausführen und zur Methodenwahl zurückkehren."],"related":["check-upgrade-readiness","set-compatibility"]},"sol_inplace":{"kind":"solution","title":"In-place-Major-Upgrade","detail":"Auf dem bestehenden Server konvertieren. Schnell, aber ein Rollback bedeutet meist einen Restore aus dem Backup, ein Snapshot ist daher Pflicht.","steps":["Ein Vollbackup + Snapshot/Guaranteed Restore Point erstellen (der einzige Weg zurück).","Oracle: AutoUpgrade (Deploy-Modus) oder DBUA. PostgreSQL: pg_upgrade (--link ist schnell, aber ohne Weg zurück). MySQL: neue Binärdateien + automatisches In-place-Upgrade. SQL Server: setup /ACTION=upgrade.","Die Aufgaben nach dem Upgrade ausführen (siehe unten unter verwandt).","Die Anwendung unter realer Last validieren; tritt eine Planregression auf, das Optimizer-Verhalten festlegen."],"related":["upgrade-major","post-upgrade-tasks"],"playbook":"upgrade-rollback"},"sol_migrate":{"kind":"solution","title":"Upgrade per Migration (minimale Downtime)","detail":"Einen frischen Server mit der neuen Version aufbauen, die Daten übertragen und dann mit einem kurzen Cutover umschalten. Die sicherste Methode; das alte System bleibt unangetastet.","steps":["Den neuen Server mit der Zielversion aufbauen und das Schema vorbereiten.","Den initialen Datenbestand per Dump/Restore oder Replikation übertragen; danach mit kontinuierlicher Replikation (CDC / logische Replikation) synchron halten.","In einem kurzen Wartungsfenster: Schreibzugriffe stoppen, auf das verbleibende Delta warten und die Anwendung auf den neuen Server umstellen (Cutover).","Die Migration prüfen (Zeilenanzahlen/Checksums); bei einem Problem sofort auf den alten Server zurückschalten."],"related":["setup-cdc-replication","verify-migration","export-for-migration"]}}},"solver/migration-wizard.json":{"id":"migration-wizard","title":"Migrationsassistent (Daten-/Schemamigration)","icon":"🚚","category":"Lebenszyklus","intro":"Du migrierst Daten/Schema. Ist es homogen oder heterogen, und wie hoch ist deine Downtime-Toleranz? Plane Schritt für Schritt: bewerten → migrieren → verifizieren. Probe den Cutover zuerst hier.","start":"kind","nodes":{"kind":{"kind":"step","title":"Von wo nach wo migrierst du?","detail":"Homogen: dieselbe Datenbank-Engine (z. B. Oracle 19 → Oracle 23 oder PostgreSQL auf einen anderen Server). Heterogen: eine andere Engine (z. B. Oracle → PostgreSQL, MySQL → SQL Server). Heterogene Migrationen erfordern deutlich mehr Kompatibilitätsarbeit (Datentypen, Funktionen, PL/SQL).","choices":[{"label":"Gleiche Engine, neuer Server/neue Version (homogen)","next":"downtime"},{"label":"Zu einer anderen Engine (heterogen)","next":"hetero"},{"label":"Ich bin in der Planungsphase; wie gehe ich vor?","next":"downtime"}]},"downtime":{"kind":"step","title":"Wie hoch ist deine Downtime-Toleranz?","detail":"Das bestimmt die Migrationsmethode. Gibt es ein Wartungsfenster, ist Dump/Restore der einfachste Weg. Ist nahezu keine Downtime erlaubt, lädst du die initialen Daten, hältst sie mit kontinuierlicher Replikation (CDC) synchron und führst einen kurzen Cutover durch.","choices":[{"label":"Es gibt ein Wartungsfenster (Downtime ist akzeptabel)","next":"sol_offline"},{"label":"Nahezu keine Downtime erlaubt","next":"sol_online"}]},"hetero":{"kind":"step","title":"Hast du die Schema- und Typkompatibilität bewertet?","detail":"Bei einer heterogenen Migration liegt die eigentliche Arbeit nicht im Kopieren der Daten, sondern im Auflösen der Unterschiede: Datentypen (VARCHAR2↔TEXT), Funktionen (NVL↔COALESCE), Auto-Increment-Werte, PL/SQL ↔ PL/pgSQL, entfernte/abweichende Features.","choices":[{"label":"Ich habe die Schema-/Typunterschiede zugeordnet","next":"sol_hetero"},{"label":"Ich habe sie noch nicht bewertet","next":"sol_assess"}]},"sol_assess":{"kind":"solution","title":"Zuerst die Kompatibilität bewerten","detail":"Ordne vor der Migration die Unterschiede zwischen Quelle und Ziel zu; wird dieser Schritt übersprungen, bleibt die Migration auf halbem Weg stecken.","steps":["Den Schema-Diff erstellen: Tabellen, Constraints, Indizes, Views, Prozeduren.","Datentypen und Funktionen zuordnen (das Modul DBAtlas > Types & Functions ist genau dafür gebaut).","Festlegen, wie enginespezifische Features (Identity/Sequence, Partitionierung, PL/SQL, Trigger) auf dem Ziel behandelt werden.","Ein Konvertierungstool wählen (ora2pg, pgloader, AWS SCT/DMS) und mit einer kleinen Stichprobe proben."],"related":["compare-schema","heterogeneous-migration"]},"sol_offline":{"kind":"solution","title":"Offline-Migration (Dump / Restore)","detail":"Die einfachste und zuverlässigste homogene Methode: ein konsistentes Backup erstellen, ins Ziel laden, prüfen, umschalten.","steps":["Einen KONSISTENTEN Export aus der Quelle erstellen (pg_dump -Fc, expdp FLASHBACK_SCN, mysqldump --single-transaction, BACKUP, mongodump --oplog).","Ins Ziel laden (pg_restore -j, impdp, mysql <, RESTORE, mongorestore).","Die Migration prüfen: Stimmen Zeilenanzahlen und kritische Checksums zwischen Quelle und Ziel überein?","Im Wartungsfenster die Anwendung auf das Ziel umstellen (Cutover); geht etwas schief, auf die Quelle zurückfallen."],"related":["export-for-migration","import-for-migration","verify-migration"]},"sol_online":{"kind":"solution","title":"Minimale Downtime (CDC / Replikation)","detail":"Die initialen Daten laden und das Ziel dann über kontinuierliche Replikation mit der Quelle synchron halten; Downtime entsteht nur während des kurzen Cutovers.","steps":["Die initialen (Baseline-)Daten per Dump/Restore oder Snapshot laden.","Kontinuierliche Replikation einrichten: PG logische Replikation (PUBLICATION/SUBSCRIPTION), MySQL Binlog-Replikation, Oracle GoldenGate, MSSQL Transaktionsreplikation/CDC, Mongo Change Streams/mongosync.","Warten und überwachen, bis das Ziel die Quelle eingeholt hat und die Verzögerung ~0 ist.","Kurzer Cutover: Schreibzugriffe auf die Quelle stoppen, auf das verbleibende Delta warten, die Anwendung auf das Ziel umstellen, prüfen."],"related":["setup-cdc-replication","verify-migration"]},"sol_hetero":{"kind":"solution","title":"Heterogene Migration (auf eine andere Engine)","detail":"Schema konvertieren, Daten übertragen, Logik anpassen, prüfen. In Phasen vorgehen; nicht alles auf einmal migrieren.","steps":["Schema konvertieren: mit einem Tool (ora2pg, pgloader, AWS SCT) oder von Hand; Typen und Constraints an das Ziel anpassen.","Daten übertragen: Bulk Load oder ein Tool; große Tabellen partitionieren/in Chunks aufteilen.","Serverseitige Logik anpassen: PL/SQL → PL/pgSQL, Entsprechungen für Funktionen/Trigger.","Gründlich prüfen: Zeilenanzahlen, Stichprobendaten, Ergebnisse kritischer Abfragen; die Anwendung auf dem Ziel testen.","Muss die Downtime verringert werden, das Ziel nach der Konvertierung über eine CDC-Schicht synchron halten und dann umschalten."],"related":["heterogeneous-migration","verify-migration","setup-cdc-replication"]}}},"solver/high-cpu.json":{"id":"high-cpu","title":"Hohe CPU","icon":"🔥","category":"Performance","intro":"Die Server-CPU ist am Anschlag. Prüfe zuerst, ob wirklich die Datenbank sie verbraucht, und folge dann den Zweigen zur Ursache: schlecht geplante Abfragen, eine Planregression, übermäßige Parallelität oder ein Prozess außerhalb der DB.","start":"root","nodes":{"root":{"kind":"step","title":"Verbraucht wirklich die Datenbank die CPU?","detail":"Prüfe auf Betriebssystemseite, welcher Prozess CPU verbrennt. Liegt der Großteil der Last in DB-Prozessen oder in einem benachbarten Prozess (Backup, Antivirus, eine andere Anwendung)? Das teilt die Untersuchung in zwei Zweige.","command":{"oracle":"-- OS side: top -o %CPU  (or: top -c)\n-- DB-side verification:\nSELECT ROUND(value,1) AS host_cpu_util_pct FROM v$sysmetric WHERE metric_name='Host CPU Utilization (%)' AND rownum=1;","postgres":"-- OS: top -c  /  ps -eo pcpu,pid,cmd --sort=-pcpu | head\n-- DB active session count:\nSELECT count(*) FROM pg_stat_activity WHERE state='active';","mysql":"-- OS: top -c  /  mysqladmin processlist\nSHOW GLOBAL STATUS LIKE 'Threads_running';","mssql":"-- OS: Task Manager / typeperf\nSELECT record_id, SQLProcessUtilization AS sql_cpu_pct, (100 - SystemIdle - SQLProcessUtilization) AS other_cpu_pct FROM (SELECT record.value('(./Record/@id)[1]','int') AS record_id, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]','int') AS SystemIdle, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]','int') AS SQLProcessUtilization FROM (SELECT CONVERT(xml, record) AS record FROM sys.dm_os_ring_buffers WHERE ring_buffer_type='RING_BUFFER_SCHEDULER_MONITOR') AS x) AS y ORDER BY record_id DESC;","mongodb":"// OS: top -c  |  db.serverStatus().metrics\ndb.serverStatus().opcounters","redis":"INFO cpu","elasticsearch":"GET _nodes/stats/os,process"},"choices":[{"label":"Ja, DB-Prozesse verbrauchen die CPU","next":"db_confirm"},{"label":"Nein, ein benachbarter Prozess / Backup-Job verbraucht sie","next":"sol_os"}]},"db_confirm":{"kind":"step","title":"Wie ist die Last aufgetreten?","detail":"Verbraucht die DB die CPU, das Muster bestimmen: Dominieren einige schwere Abfragen, begann das Problem plötzlich nach einer Änderung, oder gibt es hohe Parallelität/Verbindungs-Contention?","choices":[{"label":"Einige schwere Abfragen verbrennen die CPU","next":"topcpu"},{"label":"Plötzlich begonnen; es gab kürzlich ein Deployment / eine Parameteränderung","next":"sol_regression"},{"label":"Viele Verbindungen / Contention durch Parallelität","next":"sol_concurrency"}]},"topcpu":{"kind":"step","title":"Die Abfragen mit dem höchsten CPU-Verbrauch finden","detail":"Die Top-Abfragen nach gesamtem CPU-Verbrauch abrufen. Dann entscheiden, ob jede pro Aufruf schwer ist (schlechter Plan) oder pro Aufruf günstig, aber sehr häufig aufgerufen.","command":{"oracle":"SELECT sql_id, executions, ROUND(cpu_time/1e6,1) AS cpu_sec, ROUND(cpu_time/GREATEST(executions,1)/1000,2) AS cpu_ms_per_exec, sql_text\nFROM v$sql ORDER BY cpu_time DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT queryid, calls, ROUND(total_exec_time::numeric,1) AS total_ms, ROUND(mean_exec_time::numeric,2) AS mean_ms, query\nFROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;","mysql":"SELECT DIGEST_TEXT, COUNT_STAR AS calls, ROUND(SUM_CPU_TIME/1e12,2) AS cpu_sec, ROUND(SUM_TIMER_WAIT/1e12,2) AS total_sec\nFROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;","mssql":"SELECT TOP 10 qs.execution_count, qs.total_worker_time/1000 AS cpu_ms, qs.total_worker_time/qs.execution_count/1000 AS cpu_ms_per_exec, SUBSTRING(st.text,(qs.statement_start_offset/2)+1,50) AS stmt\nFROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st ORDER BY qs.total_worker_time DESC;","mongodb":"db.setProfilingLevel(1, { slowms: 100 });  // then: db.system.profile.find().sort({ millis: -1 }).limit(10)"},"choices":[{"label":"Schwer pro Aufruf; schlechter Plan / Full Scan","next":"sol_query"},{"label":"Günstig pro Aufruf, aber sehr häufig aufgerufen","next":"sol_frequency"}]},"sol_query":{"kind":"solution","title":"Ursache: Schlecht geplante Abfragen verbrennen CPU","detail":"Einige Abfragen verbrauchen pro Aufruf übermäßig CPU wegen Full Scans oder einer schlechten Join-Reihenfolge. Das Korrigieren ihrer Pläne ist die Kernlösung.","steps":["Die Pläne (EXPLAIN) der Abfragen mit dem höchsten CPU-Verbrauch abrufen; nach Full Scans, schlechter Join-Reihenfolge oder Abweichungen zwischen geschätzten und tatsächlichen Zeilen suchen.","Passende Indizes auf Filter-/Join-Spalten anlegen; prüfen, ob der Plan auf einen Index Scan (IXSCAN) wechselt.","Schätzt der Optimizer falsch, frische Statistiken sammeln.","Außer Kontrolle geratene Abfragen drosseln (Oracle Resource Manager / MSSQL Resource Governor) und die Abfrage bei Bedarf umschreiben."],"related":["top-slow-queries","explain-plan","create-index","update-statistics"],"playbook":"high-cpu"},"sol_frequency":{"kind":"solution","title":"Ursache: Übermäßige Aufrufhäufigkeit","detail":"Die Abfragen sind einzeln günstig, werden aber tausendfach pro Sekunde aufgerufen; daher kommen die gesamten CPU-Kosten. Meist ein N+1-Muster oder ein nicht gecachter Hot Path.","steps":["Die am häufigsten aufgerufenen Anweisungen (calls / execution_count) ermitteln und den aufrufenden Anwendungspfad finden.","Das N+1-Muster in eine Batch-Abfrage umwandeln; wiederholte Lesezugriffe mit einem Anwendungs-/Ergebnis-Cache verringern.","Prepared Statements verwenden, um die Kosten wiederholten Parsens (Hard Parse) zu senken.","Für häufige, unveränderliche schwere Abfragen eine Materialized View / Summentabelle erwägen."],"related":["top-slow-queries","explain-plan"],"playbook":"high-cpu"},"sol_regression":{"kind":"solution","title":"Ursache: Planregression / veraltete Statistiken","detail":"Gestern war das System normal; nach einem Deployment, einer Parameteränderung oder einem Datenladevorgang ist die CPU sprunghaft gestiegen. Der Optimizer hat eventuell auf einen schlechteren Plan gewechselt.","steps":["Die Änderung ermitteln: kürzliches Deployment, Upgrade/Patch, Parameteränderung oder großer Datenladevorgang.","Frische Statistiken für die betroffenen Tabellen sammeln (veraltete Statistiken sind die häufigste Ursache).","Den guten Plan festlegen: Oracle SQL Plan Baseline / Hint, MSSQL Query Store 'force plan'.","Kam die Änderung von einem Parameter, ihn zurücksetzen und kontrolliert neu bewerten."],"related":["update-statistics","explain-plan","show-config-param"],"playbook":"high-cpu"},"sol_concurrency":{"kind":"solution","title":"Ursache: Hohe Parallelität / Verbindungs-Contention","detail":"Die einzelnen Abfragen sind akzeptabel, aber zu viele Sessions laufen gleichzeitig; mehr aktive Sessions als CPU-Kerne verursachen Kontextwechsel und Latch-/Spin-Contention.","steps":["Die Anzahl aktiver Sessions mit der Anzahl der CPU-Kerne vergleichen; bei mehr aktiven Sessions als Kernen bildet sich eine Run Queue.","Einen Connection Pooler zwischen Anwendung und DB schalten (PgBouncer / ProxySQL) und eine sinnvolle Pool-Größe festlegen.","Gibt es übermäßige Parallelität, den Parallelitätsgrad begrenzen (Oracle parallel_degree_limit / parallel_max_servers, MSSQL MAXDOP).","Die CPU des Primary-Knotens entlasten, indem Leselast auf Read Replicas verteilt wird."],"related":["connection-count","active-sessions"],"playbook":"connection-storm"},"sol_os":{"kind":"solution","title":"Ursache: Ein Prozess außerhalb der DB verbraucht CPU","detail":"Nicht die Datenbank, sondern ein benachbarter Prozess verbraucht die CPU: ein Backup-/Kompressionsjob, ein Antivirus-Scan, ein Log Shipper oder eine andere Anwendung auf derselben Maschine.","steps":["Den verursachenden Prozess mit einem OS-Tool (top / Task Manager) ermitteln.","Ist es ein Backup-, Statistiksammel- oder Wartungsjob, ihn außerhalb der Spitzenzeiten neu planen.","Die Datenverzeichnisse auf die Ausschlussliste von Antivirus-/Scan-Prozessen setzen.","Ist der Konflikt dauerhaft, die DB auf einen separaten Server mit eigenen Ressourcen verlegen."],"related":["os-resource"],"playbook":"high-cpu"}}},"solver/memory-pressure.json":{"id":"memory-pressure","title":"Speicherdruck / OOM","icon":"🧠","category":"Performance","intro":"Der Serverspeicher geht aus, das System swappt oder der OOM Killer ist aktiv. Bestimme zuerst, wer den Speicher verbraucht: belegter Speicher (Buffer Pool/SGA), Session-Speicher (work_mem/PGA), das OS insgesamt oder eine Redis-spezifische Situation?","start":"root","nodes":{"root":{"kind":"step","title":"Wer verbraucht den Speicher?","detail":"Trenne die Quelle des Speicherdrucks nach Ebene. Ist der belegte (feste) Speicher zu groß, explodiert der Speicher pro Session, frisst ein Prozess außerhalb der DB ihn, oder spielt die Speicherverwaltung von Redis selbst eine Rolle?","command":{"oracle":"SELECT component, ROUND(current_size/1024/1024) AS mb FROM v$memory_dynamic_components WHERE current_size>0 ORDER BY current_size DESC;","postgres":"SHOW shared_buffers; SHOW work_mem; SHOW effective_cache_size;\nSELECT count(*) FROM pg_stat_activity WHERE state='active';","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';\nSHOW VARIABLES LIKE 'sort_buffer_size'; SHOW VARIABLES LIKE 'join_buffer_size';\nSHOW STATUS LIKE 'Threads_connected';","mssql":"SELECT physical_memory_in_use_kb/1024 AS used_mb, large_page_allocations_kb/1024 AS large_mb FROM sys.dm_os_process_memory;\nSELECT cntr_value/1024 AS target_mb FROM sys.dm_os_performance_counters WHERE counter_name='Target Server Memory (KB)';","mongodb":"db.serverStatus().wiredTiger.cache","redis":"INFO memory","elasticsearch":"GET _nodes/stats/jvm,os"},"choices":[{"label":"Belegter Speicher (Buffer Pool / SGA) dominiert","next":"allocated"},{"label":"Session-Speicher (work_mem / PGA) explodiert","next":"sol_session"},{"label":"Ein Prozess außerhalb der DB / das OS insgesamt","next":"sol_os"},{"label":"Redis-spezifisch (maxmemory / Eviction / Fragmentierung)","next":"sol_redis"}]},"allocated":{"kind":"step","title":"Übersteigt der konfigurierte Gesamtspeicher den physischen RAM?","detail":"Ist der fest belegte Speicher groß, gibt es zwei Möglichkeiten: Entweder übersteigt die konfigurierte Summe (Buffer Pool + Sessions + OS) den physischen RAM (Over-Commit), oder die Größe ist vernünftig, aber die Cache Hit Ratio trotzdem niedrig. Addiere die Einstellungen und vergleiche sie mit dem RAM.","command":{"oracle":"SHOW PARAMETER sga_target;  SHOW PARAMETER pga_aggregate_target;\nSELECT ROUND(value/1024/1024/1024,1) AS host_ram_gb FROM v$osstat WHERE stat_name='PHYSICAL_MEMORY_BYTES';","postgres":"SELECT name, setting, unit FROM pg_settings WHERE name IN ('shared_buffers','work_mem','maintenance_work_mem','max_connections');","mysql":"SELECT ROUND(@@innodb_buffer_pool_size/1024/1024/1024,1) AS pool_gb, ROUND((@@sort_buffer_size+@@join_buffer_size)/1024/1024,1) AS per_conn_mb, @@max_connections AS max_conn;","mssql":"SELECT value_in_use AS max_server_mem_mb FROM sys.configurations WHERE name='max server memory (MB)';\nSELECT total_physical_memory_kb/1024 AS ram_mb FROM sys.dm_os_sys_memory;"},"choices":[{"label":"Ja, die konfigurierte Summe übersteigt den RAM (Over-Commit)","next":"sol_overcommit"},{"label":"Nein, die Größe ist vernünftig, aber die Cache Hit Ratio ist niedrig","next":"sol_cache"}]},"sol_overcommit":{"kind":"solution","title":"Ursache: Speicher-Over-Commit","detail":"Die Summe aus Buffer Pool/SGA + Session-Speicher + OS-Bedarf übersteigt den physischen RAM. Das System swappt, oder der OOM Killer beendet den Prozess.","steps":["Das Speicherbudget neu aufstellen: einen Anteil für das OS reservieren, dann den belegten Speicher korrekt dimensionieren (Tools > Memory calculator).","Das Ziel für Buffer Pool/SGA und die Obergrenze des Session-Speichers senken, damit sie ins RAM-Limit passen.","Die Änderungen kontrolliert anwenden und prüfen, ob Swap-Nutzung und OOM-Ereignisse aufgehört haben.","Reicht die Kapazität dauerhaft nicht aus, RAM hinzufügen oder die Last aufteilen."],"related":["show-config-param","cache-hit-ratio"],"playbook":"memory-pressure"},"sol_cache":{"kind":"solution","title":"Ursache: Zu kleiner Cache (niedrige Cache Hit Ratio)","detail":"Der belegte Speicher übersteigt den RAM nicht, ist aber im Verhältnis zum Working Set klein; die Hit Ratio des Buffer Cache ist niedrig, und die DB liest ständig von der Platte.","steps":["Die Hit Ratio von Buffer Cache / Buffer Pool messen; ist sie dauerhaft niedrig (z. B. unter 90%), ist der Cache zu klein.","Ist RAM verfügbar, Buffer Pool/SGA kontrolliert vergrößern und die Hit Ratio erneut messen.","Sind die heißen Daten wirklich groß, unnötige Lesezugriffe verringern: Indizes hinzufügen, statt SELECT * nur benötigte Spalten auswählen, paginieren.","Belastet das Vergrößern den RAM, Session-Speicher und belegten Speicher neu austarieren."],"related":["cache-hit-ratio","show-config-param"],"playbook":"memory-pressure"},"sol_session":{"kind":"solution","title":"Ursache: Explosion des Session-Speichers","detail":"Speicher pro Session wie work_mem/PGA, multipliziert mit hoher Parallelität, erschöpft den RAM. Jede Sort-/Hash-Operation kann diesen Speicher separat belegen.","steps":["Den ungünstigsten Fall abschätzen: Session-Speicher × gleichzeitige Sessions × Anzahl der Operationen; mit dem RAM vergleichen.","Die globale Einstellung für Session-Speicher senken (work_mem / sort_buffer_size / PGA target).","Für schwere Abfragen, die wirklich große Sorts/Hashes brauchen, die Einstellung nur auf Session-Ebene anheben.","Begrenze die Parallelität mit einem Connection Pooler, um den gesamten Speicherbedarf zu senken."],"related":["show-config-param","connection-count"],"playbook":"memory-pressure"},"sol_redis":{"kind":"solution","title":"Ursache: Redis-Speicherverwaltung","detail":"In Redis hat used_memory maxmemory erreicht, Eviction ist aktiv oder die Fragmentierung ist hoch (used_memory_rss >> used_memory). Policy und Fragmentierung müssen behandelt werden.","steps":["Lies used_memory, maxmemory und mem_fragmentation_ratio mit INFO memory aus.","Setze eine zum Zweck passende Eviction-Policy: CONFIG SET maxmemory-policy allkeys-lru (Cache) oder noeviction (persistente Daten).","Nutze TTLs, damit abgelaufene Schlüssel bereinigt werden, und verkleinere große Schlüssel (BIG KEYS).","Bei hoher Fragmentierung aktiviere Active Defrag (CONFIG SET activedefrag yes) oder starte in einem passenden Wartungsfenster neu."],"related":["show-config-param"],"playbook":"memory-pressure"},"sol_os":{"kind":"solution","title":"Ursache: Speicherverbrauch außerhalb der DB","detail":"Der Speicher wird nicht von der Datenbank verbraucht, sondern vom Betriebssystem insgesamt oder einem benachbarten Prozess: einer anderen Anwendung, Druck durch den Dateisystem-Cache oder einem Hilfsprozess mit Speicherleck.","steps":["Ermittle den Prozess mit dem höchsten Speicherverbrauch mit einem OS-Tool (top / free / Task-Manager).","Ist der Verursacher ein Nicht-DB-Prozess, verlagere oder begrenze ihn (cgroup / Job-Objekt) oder behebe das Speicherleck.","Beendet der OOM-Killer den DB-Prozess, überprüfe die OOM-Einstellungen und garantiere der DB Speicher.","Ist der Konflikt dauerhaft, verlagere die DB auf einen separaten Server mit eigenem Speicher."],"related":["os-resource"],"playbook":"memory-pressure"}}},"solver/install-setup.json":{"id":"install-setup","title":"Neuinstallation & sichere Basis","icon":"🛠️","category":"Lebenszyklus","intro":"Übersprungene Schritte bei der Vorbereitung einer neu installierten Datenbank für die Produktion führen zu den teuersten Ausfällen. Wähle deine aktuelle Phase und erhalte eine geordnete Checkliste für Erstkonfiguration, Sicherheitshärtung, Dauerhaftigkeit und Monitoring.","start":"root","nodes":{"root":{"kind":"step","title":"In welcher Phase bist du?","detail":"Die Vorbereitung einer Neuinstallation für die Produktion hat vier Phasen: Erstkonfiguration, Sicherheitshärtung, Dauerhaftigkeit (Backup/Replikation) und Monitoring. An welcher arbeitest du gerade? Empfohlen ist, alle der Reihe nach zu durchlaufen.","command":{"oracle":"SELECT banner_full FROM v$version; SHOW PARAMETER compatible;","postgres":"SELECT version(); SHOW server_encoding; SHOW data_directory;","mysql":"SELECT VERSION(); SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'datadir';","mssql":"SELECT @@VERSION; SELECT SERVERPROPERTY('Collation') AS collation;","mongodb":"db.version(); db.serverStatus().host","redis":"INFO server","elasticsearch":"GET /"},"choices":[{"label":"Erstkonfiguration nach der Installation","next":"firstconfig"},{"label":"Sicherheitshärtung","next":"sol_security"},{"label":"Backup & Dauerhaftigkeit einrichten","next":"durability"},{"label":"Monitoring & Alarmierung einrichten","next":"sol_monitoring"}]},"firstconfig":{"kind":"step","title":"Was hat bei der Erstkonfiguration Priorität?","detail":"Die Erstkonfiguration gliedert sich in zwei Hauptthemen: Ressourcen-/Parameterdimensionierung (Speicher, Verbindungen) und dauerhafte Grundlagen (Zeichensatz/Collation, Datenverzeichnis/Dateilayout). Diese Entscheidungen lassen sich später am schwersten ändern.","command":{"postgres":"SELECT name, setting, unit FROM pg_settings WHERE name IN ('shared_buffers','work_mem','max_connections','wal_level');","oracle":"SHOW PARAMETER sga_target; SHOW PARAMETER pga_aggregate_target; SHOW PARAMETER processes;","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections';","mssql":"SELECT name, value_in_use FROM sys.configurations WHERE name IN ('max server memory (MB)','max degree of parallelism');"},"choices":[{"label":"Speicher-/Parameterdimensionierung","next":"sol_sizing"},{"label":"Zeichensatz, Collation und Dateilayout","next":"sol_layout"}]},"durability":{"kind":"step","title":"Welchen Teil der Dauerhaftigkeit richtest du ein?","detail":"Dauerhaftigkeit steht auf zwei Beinen: einer verifizierten Backup-Strategie (erstes Backup + Restore-Test) und Kontinuität (Archiv-/Log-Modus, Replikation). Zu sagen „Ich habe ein Backup“, ohne es getestet zu haben, ist der häufigste Irrtum.","command":{"postgres":"SHOW archive_mode; SHOW wal_level;","oracle":"ARCHIVE LOG LIST; SELECT log_mode FROM v$database;","mysql":"SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'server_id';","mssql":"SELECT name, recovery_model_desc FROM sys.databases;"},"choices":[{"label":"Backup-Strategie + erstes Backup + Restore-Test","next":"sol_backup"},{"label":"Archiv-/Log-Modus + Replikation","next":"sol_replication"}]},"sol_sizing":{"kind":"solution","title":"Erstkonfiguration: Speicher- und Parameterdimensionierung","detail":"Die Standardwerte sind bei den meisten Engines konservativ; dimensioniere sie nach RAM und CPU des Servers, vermeide aber Overcommit.","steps":["Teile den gesamten RAM auf: Lass einen Anteil für das OS übrig und setze dann Buffer Pool/SGA als Hauptcache.","Setze den Session-Speicher (work_mem/PGA) konservativ anhand der erwarteten Parallelität; überschreite nicht den gesamten RAM.","Setze max_connections auf den tatsächlichen Bedarf; plane bei hoher Parallelität einen Connection Pooler ein.","Prüfe die zentralen Engine-spezifischen Parameter (WAL/Redo, Checkpoint, Parallelität) und dokumentiere die Änderungen."],"related":["show-config-param","connect-cli"]},"sol_layout":{"kind":"solution","title":"Erstkonfiguration: Zeichensatz, Collation und Dateilayout","detail":"Zeichensatz/Collation und Layout des Datenverzeichnisses werden bei der Installation festgelegt; eine spätere Änderung erfordert meist eine Neuinstallation oder Migration.","steps":["Wähle auf Serverebene einen Unicode-Zeichensatz (z. B. UTF-8 / AL32UTF8) und setze eine einheitliche Collation.","Lege Daten-, Log-/WAL- und Backup-Verzeichnisse auf getrennte Disks/Volumes; so verringerst du IO-Konkurrenz und Single Points of Failure.","Härte Dateisystem und Berechtigungen: Nur der DB-Benutzer darf das Datenverzeichnis lesen können.","Dokumentiere Layout- und Zeichensatzentscheidungen, damit später hinzugefügte Knoten dazu passen."],"related":["show-config-param","connect-cli"]},"sol_security":{"kind":"solution","title":"Sicherheitshärtung","detail":"Eine Neuinstallation bringt Standardkonten, lockeren Netzwerkzugriff und offene Ports mit. Die Angriffsfläche vor dem Produktivgang zu verkleinern ist Pflicht.","steps":["Entferne oder sperre Standard-/Beispielkonten; deaktiviere ungenutzte Administratorkonten.","Setze eine starke Passwortrichtlinie durch und ändere alle Standardpasswörter.","Least Privilege: Gib jeder Anwendung nur die benötigten Berechtigungen und vermeide weit gefasste Rollen.","Aktiviere TLS, beschränke Listener/Port auf vertrauenswürdige Netze und aktiviere Audit-Logging."],"related":["create-user-grant","change-password","enable-audit"]},"sol_backup":{"kind":"solution","title":"Dauerhaftigkeit: Backup-Strategie und Restore-Test","detail":"Ein Backup ist nur so gut wie seine Wiederherstellbarkeit. Erstelle das erste Backup, richte Kontinuität ein und führe immer einen Restore-Test durch.","steps":["Definiere die Wiederherstellungsziele (RPO/RTO) und richte einen Zeitplan aus Voll- und inkrementellen Backups ein.","Erstelle das erste Vollbackup; lagere Backups an einem vom DB-Server getrennten Ort.","Teste das Backup, indem du es in einer separaten Umgebung wiederherstellst; ein Backup ohne nachgewiesene Integrität zählt nicht.","Plane den Backup-Job ein, leite seine Ausgabe ins Monitoring und prüfe regelmäßig den Status des letzten Backups."],"related":["logical-dump","restore-dump","last-backup-status"]},"sol_replication":{"kind":"solution","title":"Dauerhaftigkeit: Archiv-/Log-Modus und Replikation","detail":"Für Point-in-Time-Recovery und Hochverfügbarkeit muss der Archiv-/Log-Modus aktiviert sein und es muss mindestens eine Replica geben.","steps":["Aktiviere den Archiv-/Log-Modus (PG archive_mode/wal_level, Oracle ARCHIVELOG, MySQL binlog, MSSQL FULL recovery).","Richte mindestens eine Replica (Standby/Replica) ein und schließe die initiale Synchronisierung ab.","Überwache die Replikationsverzögerung und definiere einen Alarm dafür.","Validiere das Failover mit einer kontrollierten Failover-/Switchover-Übung."],"related":["replication-status","list-replicas"],"playbook":"replication-lag"},"sol_monitoring":{"kind":"solution","title":"Monitoring & Alarmierung einrichten","detail":"Der letzte Schritt vor der Produktion: Probleme sehen, bevor die Benutzer sie sehen. Richte Health-Checks und Alarme mit den richtigen Schwellenwerten ein.","steps":["Erfasse grundlegende Health-Checks: Cache-Trefferquote, aktive/wartende Sessions, langsamste Abfragen.","Definiere Kapazitätsalarme: Plattenbelegung, Verbindungsanzahl nahe am Limit, Replikationsverzögerung.","Leite Alarme an einen Kanal (E-Mail/Chat) und kalibriere die Schwellenwerte an der realen Last.","Nimm auch Backup-Erfolg und Fehlerlogs ins Monitoring auf, um stille Fehler zu erkennen."],"related":["cache-hit-ratio","connection-count","free-space"]}}},"solver/corruption-recovery.json":{"id":"corruption-recovery","title":"Wiederherstellung nach Datenkorruption","icon":"🧯","category":"Wiederherstellung","intro":"Ein schrittweiser Entscheidungsbaum für Datenkorruption (Block-/Page-Checksummenfehler, nicht lesbare Tabellen, beschädigte RDB/AOF): Bestätige zuerst das Ausmaß der Korruption und wähle dann richtig zwischen Recovery auf Blockebene, vollständigem Restore/PITR aus dem Backup und Reparaturtools als letztem Mittel.","start":"root","nodes":{"root":{"kind":"step","title":"Wie groß ist das Ausmaß der Korruption?","detail":"Führe zuerst eine Integritätsprüfung durch, um festzustellen, wie weit sich die Korruption ausgebreitet hat. Sind nur einige Blöcke/Pages beschädigt, ist ein einzelnes Objekt (Tabelle/Index) betroffen oder ist die Korruption weit verbreitet? Prüfe mit den folgenden Befehlen das Ausmaß und wähle dann den passenden Zweig.","command":{"oracle":"RMAN> BACKUP VALIDATE CHECK LOGICAL DATABASE;  -- then: SELECT file#, block#, blocks, corruption_type FROM v$database_block_corruption;","postgres":"pg_amcheck --all --heapallindexed --progress   # single table: pg_amcheck -d mydb -t app_schema.my_table","mysql":"CHECK TABLE app_schema.my_table EXTENDED;   -- for InnoDB also: SHOW ENGINE INNODB STATUS\\G","mssql":"DBCC CHECKDB (N'mydb') WITH NO_INFOMSGS, ALL_ERRORMSGS;","mongodb":"db.my_table.validate({ full: true })","redis":"redis-check-rdb /var/lib/redis/dump.rdb   # AOF (7+, read-only check): redis-check-aof /var/lib/redis/appendonlydir/appendonly.aof.manifest  (--fix modifies the files; only after taking a copy)","elasticsearch":"GET _cluster/health?level=shards   # for a corrupted shard: GET _cat/shards?v&h=index,shard,state,unassigned.reason"},"choices":[{"label":"Nur einige Blöcke / Pages sind beschädigt","next":"block_level"},{"label":"Ein einzelnes Objekt (Tabelle/Index) ist betroffen","next":"restore"},{"label":"Korruption ist weit verbreitet / viele Objekte","next":"restore"},{"label":"Prüftools funktionieren nicht, die DB öffnet nicht","next":"last_resort"},{"label":"Korruption tritt immer wieder auf (Ursache vermutet)","next":"hardware"}]},"block_level":{"kind":"solution","title":"Ursache: begrenzte Block-/Page-Korruption → gezielte Recovery","detail":"Sind nur einige Blöcke/Pages beschädigt, musst du nicht die ganze Datenbank wiederherstellen. Minimiere den Datenverlust, indem du mit der Recovery-Funktion der Engine auf Block-/Page-Ebene nur die beschädigten Teile aus Backup/Logs reparierst.","steps":["Oracle: Nachdem du die Liste beschädigter Blöcke in v$database_block_corruption geprüft hast (ist die Liste veraltet, führe zuerst RMAN> VALIDATE DATABASE; aus), stelle einen einzelnen Block mit RMAN> RECOVER DATAFILE 5 BLOCK 1234; oder alle gelisteten Blöcke mit RMAN> RECOVER CORRUPTION LIST; wieder her.","SQL Server: Restore einer einzelnen Page — RESTORE DATABASE [db] PAGE='5:1234' FROM DISK='D:\\backup\\db.bak' WITH NORECOVERY;  wende dann die nachfolgenden Log-Backups WITH NORECOVERY und das letzte WITH RECOVERY an.","PostgreSQL: Es gibt keine automatische Reparatur auf Blockebene; stelle die betroffene Tabelle teilweise aus dem Backup wieder her oder aktiviere zero_damaged_pages=on vorübergehend NUR zum Extrahieren von Daten (beschädigte Zeilen gehen verloren).","MySQL/InnoDB: Es gibt keine Reparatur einzelner Pages; stelle die betroffene Tabelle aus dem letzten guten Backup wieder her und rolle bei Bedarf mit PITR vorwärts.","Führe nach der Reparatur den Prüfbefehl (VALIDATE / DBCC CHECKDB / pg_amcheck) erneut aus, um zu bestätigen, dass keine Korruption mehr vorliegt.","Verifiziere die Geschäftsdaten in den wiederhergestellten Blöcken stichprobenartig auf Anwendungsseite."],"related":["restore-dump","last-backup-status"],"playbook":"corruption"},"restore":{"kind":"solution","title":"Ursache: Korruption auf Objektebene/weit verbreitet → vollständiger Restore oder PITR aus dem Backup","detail":"Hat sich die Korruption auf ein oder mehrere Objekte ausgebreitet, ist der sicherste Weg ein Restore aus einem guten Backup und, wenn möglich, ein Vorwärtsrollen bis zum Zeitpunkt der Korruption (PITR). So vermeidest du den Datenverlust, den Reparaturtools verursachen würden.","steps":["Ermittle den ungefähren Beginn der Korruption (Alert-Log / Error-Log), damit du das PITR-Ziel direkt davor setzen kannst.","Verifiziere das letzte GUTE Vollbackup und die Transaktionslogs bis zu diesem Zeitpunkt (Archivlogs / WAL / binlog).","Oracle: RMAN> RESTORE DATABASE; RECOVER DATABASE UNTIL TIME \"TO_DATE('2026-07-04 09:59:00','YYYY-MM-DD HH24:MI:SS')\"; ALTER DATABASE OPEN RESETLOGS;","SQL Server: RESTORE DATABASE [db] FROM DISK='...' WITH NORECOVERY; RESTORE LOG [db] FROM DISK='...' WITH STOPAT='2026-07-04 09:59:00', RECOVERY;","PostgreSQL: Stelle das Basis-Backup wieder her, setze recovery_target_time='2026-07-04 09:59:00' und wende WAL bis zu diesem Zeitpunkt an.","MySQL/MariaDB: Stelle das Vollbackup wieder her und wende dann das binlog mit mysqlbinlog --stop-datetime='2026-07-04 09:59:00' bis zu diesem Zeitpunkt an.","MongoDB: Stelle mit mongorestore aus einer guten Replica/einem guten Snapshot wieder her; Elasticsearch: Stelle die beschädigten Indizes mit POST _snapshot/my_repo/snap1/_restore wieder her.","Führe nach dem Öffnen die Integritätsprüfung erneut aus und validiere die Daten mit Zeilenzahlen/Anwendungsprüfungen."],"related":["restore-dump","logical-dump","last-backup-status"],"playbook":"corruption"},"last_resort":{"kind":"step","title":"Kein Backup / DB öffnet nicht — zum letzten Mittel greifen?","detail":"Reparaturtools (REPAIR_ALLOW_DATA_LOSS, innodb_force_recovery, --fix) KÖNNEN DATENVERLUST VERURSACHEN und Integritätsbedingungen verletzen. Sie sind nur dann das letzte Mittel, wenn kein brauchbares Backup existiert und die Datenbank sich nicht öffnen lässt. Stelle zuerst sicher, dass es wirklich kein brauchbares Backup gibt.","choices":[{"label":"Ich habe ein gutes Backup gefunden, ich stelle wieder her","next":"restore"},{"label":"Es gibt gar kein Backup, ein Reparaturtool ist die einzige Option","next":"repair"}]},"repair":{"kind":"solution","title":"Letztes Mittel: Reparaturtools mit Datenverlustrisiko","detail":"Diese Tools verwerfen beschädigte Zeilen/Pages, damit sich die Datenbank öffnen lässt. Verwende sie nur, wenn es kein Backup gibt und du zuvor eine Kopie der vorhandenen Dateien erstellt hast. Ziel: gerade so weit öffnen, dass sich die Daten exportieren lassen, und sie dann in eine saubere Kopie übertragen.","steps":["Erstelle ZUERST eine Rohkopie der vorhandenen Datafiles (Versuche können die Lage verschlimmern).","SQL Server: DBCC CHECKDB (N'db', REPAIR_ALLOW_DATA_LOSS) WITH ALL_ERRORMSGS;  (die Datenbank muss im SINGLE_USER-Modus sein; dieser Befehl kann Datenverlust verursachen).","MySQL/InnoDB: Starte mit innodb_force_recovery=1 unter [mysqld] in my.cnf; öffnet sie nicht, erhöhe auf 2, 3... (bis 6); sobald sie öffnet, exportiere die Daten sofort mit mysqldump, importiere sie dann in eine saubere Installation und entferne die Einstellung.","PostgreSQL: Als letztes Mittel pg_resetwal (hohes Datenverlustrisiko) nur, wenn das WAL beschädigt ist; extrahiere dann die Daten mit pg_dump und lade sie in einen neuen Cluster.","Oracle: Ohne Backup wird DBMS_REPAIR / eventbasierte Recovery mit dem Oracle Support bewertet; Blöcke lassen sich mit 'skip corrupt' überspringen (Datenverlust).","MongoDB: Öffne mit mongod --repair oder WiredTiger-Salvage und übertrage die Daten dann in ein neues Replica Set.","Wechsle nach der Reparatur IMMER zu einer sauberen Installation, führe die Integritätsprüfung erneut aus und melde die verlorenen Daten."],"related":["logical-dump","restore-dump"],"playbook":"corruption"},"hardware":{"kind":"solution","title":"Ursache: Hardware/Infrastruktur → Quelle beheben und Checksummen aktivieren","detail":"Tritt Korruption immer wieder auf, liegt die eigentliche Ursache höchstwahrscheinlich bei Disk, Controller, Arbeitsspeicher (RAM) oder einer instabilen Speicherschicht. Nur die Daten wiederherzustellen reicht nicht; behebst du die Quelle nicht, kommt die Korruption zurück. Aktiviere zur Früherkennung außerdem Checksummen-/Integritätsprüfung.","steps":["Prüfe den Disk-Zustand: SMART-Werte (smartctl -a /dev/sdX), RAID-Controller-Logs und Storage-Alarme.","Führe einen Speichertest durch (memtest86+ oder ECC-Fehlerzähler); Einzelbitfehler sind eine klassische Ursache für Korruption.","Prüfe OS-/Dateisystem- und Hardware-Fehlerlogs (dmesg, Ereignisanzeige, Alarme des Storage-Arrays).","PostgreSQL: Lege mit initdb --data-checksums (oder pg_checksums --enable) einen Cluster mit Checksummen an, damit Korruption beim Schreiben auf die Disk erkannt wird.","Oracle: Aktiviere die Blockprüfung mit DB_BLOCK_CHECKSUM=FULL und DB_BLOCK_CHECKING=FULL; ergänze DB_LOST_WRITE_PROTECT=TYPICAL.","SQL Server: Stelle sicher, dass die Datenbank PAGE_VERIFY CHECKSUM verwendet (ALTER DATABASE [db] SET PAGE_VERIFY CHECKSUM).","MySQL/InnoDB: Schütze dich mit innodb_checksum_algorithm=crc32 und bei Bedarf innodb_doublewrite=ON vor unvollständigen Schreibvorgängen.","Stelle nach dem Beheben der Quelle aus dem letzten guten Backup in eine saubere Kopie wieder her und verschärfe das Monitoring."],"related":["restore-dump","last-backup-status"],"playbook":"corruption"}}},"solver/failover-drill.json":{"id":"failover-drill","title":"Failover-Übung / geplanter Rollenwechsel","icon":"🔀","category":"Replikation","intro":"Ein Entscheidungsbaum, der die Art des Rollenwechsels bestimmt (geplanter Switchover, Notfall-Failover oder eine Übung ohne Eingriff in die Produktion) und für jedes Szenario die richtige Befehlsfolge und Prüfschritte liefert. Ziel: bereit sein für einen Wechsel ohne Datenverlust im Planfall, mit minimalem Verlust im Notfall und ganz ohne Ausfallzeit bei einer Übung.","start":"root","nodes":{"root":{"kind":"step","title":"Was willst du tun?","detail":"Wähle den Kontext des Rollenwechsels. Führst du einen geplanten Wechsel bei gesundem Primary durch (Wartung/Migration), ist der Primary wirklich ausgefallen und ein Notfallwechsel nötig, oder führst du eine Übung/Bereitschaftsprüfung ohne Eingriff in die Produktion durch?","choices":[{"label":"Geplanter Switchover (Primary ist gesund)","next":"planned"},{"label":"Notfall-Failover (Primary ist ausgefallen)","next":"emergency_check"},{"label":"Übung / Bereitschaftstest (Produktion nicht anfassen)","next":"drill"}]},"planned":{"kind":"step","title":"Geplanter Wechsel — bist du zuerst bereit?","detail":"Ziel eines geplanten Switchovers ist NULL Datenverlust. Dafür musst du vor dem Wechsel sicherstellen, dass die Replica den Primary eingeholt hat (Verzug ~0) und gesund ist. Prüfe den Verzug mit den folgenden Befehlen; liegt er nahe null, fahre mit den Wechselschritten fort.","command":{"oracle":"SELECT thread#, MAX(sequence#) FROM v$archived_log GROUP BY thread#;   -- DG: SELECT name, value FROM v$dataguard_stats WHERE name LIKE '%lag%';","postgres":"SELECT client_addr, state, replay_lag, write_lag FROM pg_stat_replication;","mysql":"SHOW REPLICA STATUS\\G   -- check Seconds_Behind_Source and Replica_SQL_Running","mssql":"SELECT ag.name, drs.synchronization_state_desc, drs.log_send_queue_size, drs.redo_queue_size FROM sys.dm_hadr_database_replica_states drs JOIN sys.availability_groups ag ON ag.group_id = drs.group_id;","mongodb":"rs.printSecondaryReplicationInfo()","redis":"redis-cli INFO replication   # check the difference between master_repl_offset and the slave offset","elasticsearch":"GET _cat/recovery?v&active_only=true"},"choices":[{"label":"Verzug nahe null, bereit zum Wechsel","next":"sol_planned"},{"label":"Verzug ist hoch, zuerst auf Synchronisierung warten","next":"drill"}]},"sol_planned":{"kind":"solution","title":"Geplanter Switchover (kontrollierter Wechsel ohne Datenverlust)","detail":"Solange Primary und Standby gesund und synchron sind, tausche die Rollen kontrolliert. Die Reihenfolge zählt: Schreibvorgänge stoppen, sicherstellen, dass die restlichen Änderungen den Standby erreicht haben, dann die Rolle übergeben und die Anwendung auf den neuen Primary umleiten.","steps":["Kündige das Wartungsfenster an und halte die Anwendung für Lese- und Schreibzugriffe kontrolliert an.","Oracle Data Guard (12c+): SQL> ALTER DATABASE SWITCHOVER TO standby_db VERIFY; dann SQL> ALTER DATABASE SWITCHOVER TO standby_db;  (mit dem Broker: DGMGRL> SWITCHOVER TO 'standby_db'; 11g und älter: COMMIT TO SWITCHOVER TO PHYSICAL STANDBY).","PostgreSQL: Fahre den Primary sauber herunter (pg_ctl stop -m fast), promote den Standby mit SELECT pg_promote(); und hänge den alten Primary dann mit pg_rewind als neuen Standby wieder an.","SQL Server AG: auf einer synchronen Replica ALTER AVAILABILITY GROUP [ag] FAILOVER;  (geplantes Failover ohne Datenverlust; SYNCHRONOUS_COMMIT ist erforderlich).","MongoDB: Zwinge den aktuellen Primary mit rs.stepDown(60) für 60 s zum Zurücktreten; der aktuellste Secondary wird automatisch gewählt und wird Primary.","Redis: auf dem neuen Master-Kandidaten REPLICAOF NO ONE; dann auf den anderen Knoten REPLICAOF new_master port; (mit Sentinel: SENTINEL FAILOVER master_name).","Richte Connection String / Routing der Anwendung auf den neuen Primary aus (VIP, Service-Name oder Connection String).","Verifiziere: Der neue Primary nimmt Schreibvorgänge an, der alte Primary streamt als Standby mit Verzug ~0 und die Anwendung zeigt keine Fehler."],"related":["promote-failover","replication-status","list-replicas"],"playbook":"replication-lag"},"emergency_check":{"kind":"step","title":"Notfallwechsel — ist der Primary wirklich ausgefallen?","detail":"Bevor du ein Failover erzwingst, verifiziere, dass der Primary wirklich nicht erreichbar ist, und bewerte das Split-Brain-Risiko. Lebt der Primary tatsächlich noch (z. B. kurzer Netzwerkausfall), führt ein erzwungener Wechsel zu zwei getrennten beschreibbaren Kopien (Split-Brain). Vergleiche außerdem den Verzug, um den aktuellsten Standby zu wählen.","command":{"oracle":"-- try to reach the primary: tnsping primary_db; and on the standby: SELECT thread#, MAX(sequence#) FROM v$archived_log GROUP BY thread#;","postgres":"SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();   -- choose the highest LSN among the standbys","mysql":"SHOW REPLICA STATUS\\G   -- choose the candidate with the largest Exec_Source_Log_Pos","mssql":"SELECT drs.replica_id, drs.last_hardened_lsn FROM sys.dm_hadr_database_replica_states drs;","mongodb":"rs.status()   -- the member with the highest optime is the primary candidate","redis":"redis-cli INFO replication   # choose the one with the highest slave_repl_offset","elasticsearch":"GET _cat/nodes?v   # verify whether the master node is reachable"},"choices":[{"label":"Ja, der Primary ist ausgefallen — Wechsel erzwingen","next":"emergency"},{"label":"Nicht sicher / der Primary kommt evtl. zurück","next":"planned"}]},"emergency":{"kind":"solution","title":"Notfall-Failover (Primary ausgefallen, Wechsel mit Datenverlustrisiko)","detail":"Der Primary ist nicht erreichbar und du kannst nicht auf seine Rückkehr warten. Du musst den Standby per Force promoten; bei asynchroner Replikation können einige der letzten Transaktionen verloren gehen. Priorität hat die Wiederherstellung des Dienstes; ermittle danach das Verlustfenster und bringe den alten Primary sicher zurück.","steps":["Verifiziere, dass der Primary wirklich ausgefallen ist (isoliere den alten Primary vom Netzwerk / fahre ihn herunter, um Split-Brain zu verhindern — Fencing/STONITH).","Wähle den aktuellsten Standby: Vergleiche vor dem Handeln Verzug/Offset, damit du den mit dem geringsten Datenverlust promotest.","Oracle Data Guard: DGMGRL> FAILOVER TO 'standby_db';  (oder SQL> ALTER DATABASE ACTIVATE STANDBY DATABASE;).","PostgreSQL: Promote den Standby sofort mit SELECT pg_promote();; wenn der alte Primary zurückkommt, öffne ihn NICHT ohne pg_rewind als Primary (Split-Brain-Risiko).","SQL Server AG: ALTER AVAILABILITY GROUP [ag] FORCE_FAILOVER_ALLOW_DATA_LOSS;  (bei einer asynchronen Replica ist Datenverlust möglich).","MongoDB: Bleibt keine Mehrheit übrig, konfiguriere die verbliebenen Mitglieder mit rs.reconfig(cfg, {force:true}) neu.","Redis: Ohne Sentinel führe REPLICAOF NO ONE auf dem neuen Master-Kandidaten aus; hänge die anderen daran; lass den alten Master niemals automatisch wieder beitreten.","Leite die Anwendung auf den neuen Primary um; ermittle und melde dann das Fenster verlorener Daten (verlorene Transaktionen) und baue den alten Primary sauber als neuen Standby auf."],"related":["promote-failover","replication-status","list-replicas"],"playbook":"db-down"},"drill":{"kind":"solution","title":"Übung / Bereitschaftstest (validieren, ohne die Produktion anzufassen)","detail":"Weise nach, dass du für ein Failover bereit bist, ohne einen echten Wechsel durchzuführen. Verifiziere den Zustand der Replica, einen akzeptablen Verzug und die Umleitbarkeit der Anwendung, ohne die Produktion zu beeinträchtigen. Optional probst du einen echten Switchover auf einer isolierten Kopie.","steps":["Miss den Verzug und verifiziere, dass er unter dem Schwellenwert liegt (PG: pg_stat_replication.replay_lag; MySQL: Seconds_Behind_Source; Oracle: v$dataguard_stats; Mongo: rs.printSecondaryReplicationInfo()).","Verifiziere den Zustand der Replicas: Alle Standbys streamen und der synchrone Modus ist auf den erwarteten Mitgliedern aktiv (SQL Server: synchronization_state_desc='SYNCHRONIZED').","Lese-/Rollentest: Führe auf dem Standby eine Read-only-Abfrage aus und bestätige, dass die Antwort ohne Verzögerung kommt (PG Hot Standby, Oracle Active Data Guard, Read Replica).","Probe des Anwendungs-Routings: Richte in einer Testumgebung Connection String/Service-Name auf den neuen Primary aus und verifiziere, dass die Anwendung problemlos verbindet (Produktionstraffic nicht anfassen).","Probe einen echten Switchover durchgängig auf einer isolierten Kopie und miss die Wechselzeit (RTO).","Probe auch die Failback-Schritte: Wiederaufbau des alten Primary mit pg_rewind / DG reinstate / AG resume.","Halte Erkenntnisse, Zeiten und Lücken fest; aktualisiere das Runbook und plane die Übung regelmäßig ein."],"related":["replication-status","list-replicas","promote-failover"],"playbook":"replication-lag"}}},"solver/security-incident.json":{"id":"security-incident","title":"Sicherheitsvorfall / verdächtiger Zugriff","icon":"🚨","category":"Sicherheit","intro":"Ein Entscheidungsbaum für verdächtigen Zugriff, eine unerwartete Berechtigungsänderung oder ein mögliches Datenleck, der je nach Symptom zu den richtigen Reaktionsschritten (Konto sperren, überzählige Berechtigungen entziehen, Verbindungen trennen, Ausmaß bestimmen) und zur dauerhaften Härtung nach dem Vorfall führt.","start":"root","nodes":{"root":{"kind":"step","title":"Was ist dir aufgefallen?","detail":"Wähle die Art des Vorfalls. Ist das Symptom ein verdächtiger Login oder eine Häufung fehlgeschlagener Logins, eine unerwartete Berechtigungsänderung (GRANT/Rolle), ein mögliches Datenleck / ein auffälliger Export, oder ist der Vorfall abgeschlossen und du härtest dauerhaft? Wähle anhand deiner ersten Beobachtung den passendsten Zweig. Notiere zuerst den Zeitpunkt des Vorfalls und das betroffene Konto/die betroffene Ressource.","choices":[{"label":"Verdächtiger Login / Häufung fehlgeschlagener Logins","next":"identity"},{"label":"Unerwartete Berechtigungsänderung (GRANT/Rolle)","next":"privilege"},{"label":"Datenleck / auffälliger Export","next":"data"},{"label":"Dauerhafte Maßnahmen nach dem Vorfall (Härtung)","next":"harden"}]},"identity":{"kind":"step","title":"Identitätsvorfall: welches Symptom?","detail":"Viele fehlgeschlagene Logins deuten auf einen Brute-Force-/Credential-Stuffing-Angriff hin. Ein erfolgreicher, aber unerwarteter Login (von ungewöhnlicher IP/zu ungewöhnlicher Zeit) zeigt dagegen, dass das Konto kompromittiert sein könnte. Sammle in beiden Fällen zuerst Belege und reagiere dann.","command":{"oracle":"SELECT username, os_username, userhost, timestamp, returncode FROM dba_audit_session WHERE returncode <> 0 ORDER BY timestamp DESC;","postgres":"-- failed logins in the log file: grep 'password authentication failed' postgresql.log | awk '{print $NF}' | sort | uniq -c | sort -rn","mysql":"SELECT * FROM performance_schema.host_cache ORDER BY COUNT_AUTHENTICATION_ERRORS DESC;   -- also 'Access denied' in the error log","mssql":"-- failed logins in the error log: EXEC xp_readerrorlog 0,1,N'Login failed';  also SELECT login_name, COUNT(*) FROM sys.dm_exec_sessions GROUP BY login_name;","mongodb":"-- audit log: grep 'authenticate' audit.json | grep '\"result\":18'  (18 = AuthenticationFailed)","redis":"ACL LOG   # shows failed authentication/command attempts","elasticsearch":"GET .security-audit-*/_search { \"query\": { \"match\": { \"event.action\": \"authentication_failed\" } } }"},"choices":[{"label":"Häufung fehlgeschlagener Logins (Brute Force)","next":"id_bruteforce"},{"label":"Erfolgreicher, aber unerwarteter Login (Kompromittierung)","next":"id_compromised"}]},"id_bruteforce":{"kind":"solution","title":"Brute Force → Quelle blockieren und Konto schützen","detail":"Bei einer Häufung fehlgeschlagener Logins wurde möglicherweise noch kein Zugriff erlangt; Ziel ist, die Quelle zu blockieren, das Konto per Sperre/Verzögerung zu schützen und ein erfolgreiches Eindringen auszuschließen.","steps":["Ermittle die angreifenden IPs (Abfragen oben) und blockiere sie auf Netzwerkebene: Firewall / pg_hba.conf reject / Listener-ACL / Security Group.","Sperre das angegriffene Konto vorübergehend: Oracle: ALTER USER target_user ACCOUNT LOCK;  PostgreSQL: ALTER ROLE target_user NOLOGIN;  SQL Server: ALTER LOGIN [target_user] DISABLE;  MySQL: ALTER USER 'target_user'@'%' ACCOUNT LOCK;  Redis: ACL SETUSER target_user off.","Wende ein Limit für Login-Versuche / eine Sperrrichtlinie an: Oracle: FAILED_LOGIN_ATTEMPTS im Profil; PostgreSQL: das Modul auth_delay (shared_preload_libraries; es verzögert fehlgeschlagene Versuche, statt zu sperren); MySQL: FAILED_LOGIN_ATTEMPTS ... PASSWORD_LOCK_TIME.","Setze das Passwort des angegriffenen Kontos trotzdem auf einen starken Wert zurück (ein Versuch könnte erfolgreich gewesen sein).","Verifiziere, ob ein Versuch erfolgreich war: Prüfe auf einen erfolgreichen Login von derselben IP; gibt es einen, folge auch dem Kompromittierungszweig.","Dokumentiere den Vorfall mit einer Zeitleiste und folge für dauerhafte Maßnahmen dem Härtungsschritt („Dauerhafte Maßnahmen nach dem Vorfall“ ab dem Start)."],"related":["change-password","list-users"],"playbook":"harden-sql"},"id_compromised":{"kind":"solution","title":"Kompromittiertes Konto → stoppen, untersuchen, Zugangsdaten rotieren","detail":"Ein unerwarteter erfolgreicher Login zeigt, dass das Konto kompromittiert wurde. Stoppe zuerst das Konto und seine Sessions, untersuche, was es getan hat, setze dann das Passwort zurück und schränke die Zugriffsquelle ein.","steps":["Sperre das Konto sofort (LOCK/DISABLE-Befehle oben) und beende seine aktiven Sessions: Oracle: ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;  PostgreSQL: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename='app_user';  SQL Server: KILL <spid>;  MySQL: KILL <id>;  Redis: CLIENT KILL.","Untersuche, was das Konto in seinen Sessions getan hat: ausgeführte Abfragen, zugegriffene Tabellen und etwaige GRANT/CREATE-Operationen im Audit-Trail/in den Logs.","Setze das Passwort auf einen starken Wert zurück und rotiere alle zugehörigen Service-/Anwendungszugangsdaten.","Suche nach neuen Konten oder Berechtigungsänderungen, die der Angreifer angelegt haben könnte (folge auch dem Berechtigungszweig).","Schränke die Zugriffsquelle ein: Erlaube dem Konto Verbindungen nur von bekannten IPs (pg_hba.conf / Listener-ACL / Firewall).","Folge für dauerhafte Maßnahmen dem Härtungsschritt („Dauerhafte Maßnahmen nach dem Vorfall“ ab dem Start)."],"related":["change-password","list-users","show-privileges"],"playbook":"harden-sql"},"privilege":{"kind":"solution","title":"Rechteausweitung → überzählige Berechtigungen prüfen und entziehen","detail":"Ein unerwarteter GRANT, eine Rollenzuweisung oder ein neues hochprivilegiertes Konto kann die zweite Stufe einer Kompromittierung sein (Persistenz/Rechteausweitung). Prüfe die jüngsten Berechtigungsänderungen, entziehe die nicht legitimen und kehre zum Least-Privilege-Prinzip zurück.","steps":["Prüfe die jüngsten Berechtigungsänderungen: Oracle: SELECT * FROM dba_audit_trail WHERE action_name IN ('GRANT','REVOKE','CREATE USER') ORDER BY timestamp DESC;  SQL Server: GRANT-Ereignisse im Default Trace / Server Audit und SELECT * FROM sys.server_permissions;  MySQL: GRANT-Einträge im Audit-Log und SELECT * FROM mysql.user;  MongoDB: db.getRoles({showPrivileges:true}).","Liste die aktuellen Berechtigungen auf: Oracle: SELECT grantee, privilege FROM dba_sys_privs WHERE grantee='APP_USER';  PostgreSQL: SELECT * FROM information_schema.role_table_grants WHERE grantee='app_user';  MySQL: SHOW GRANTS FOR 'app_user'@'%';","Entziehe nicht legitime überzählige Berechtigungen: Oracle: REVOKE DBA FROM app_user;  PostgreSQL: REVOKE ALL ON ... FROM app_user; ALTER ROLE app_user NOSUPERUSER;  SQL Server: ALTER SERVER ROLE [sysadmin] DROP MEMBER [app_user];  MySQL: REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'app_user'@'%';","Suche und entferne versteckte/Reservekonten, die der Angreifer angelegt haben könnte (unerwartete CREATE USER-Einträge).","Prüfe überzogene Berechtigungen für PUBLIC oder weit gefasste Rollen und schränke sie ein.","Verifiziere, dass die Änderungen zu einem legitimen Change Request passen; behandle alles Unpassende als Teil des Vorfalls und folge dem Härtungsschritt."],"related":["show-privileges","list-users","create-user-grant"],"playbook":"harden-sql"},"data":{"kind":"solution","title":"Datenleck → Zugriff trennen und Ausmaß bestimmen","detail":"Auffällig große Abfragen/Exporte oder unerwartete externe Verbindungen deuten auf ein Datenleck hin. Trenne zuerst die aktiven gefährlichen Verbindungen und bestimme dann mit Audit-Trail/Logs das Ausmaß: welche Daten, von wem und wann abgerufen wurden.","steps":["Prüfe die Quellen aktiver Verbindungen: PostgreSQL: SELECT pid, usename, client_addr, state, query FROM pg_stat_activity WHERE client_addr IS NOT NULL;  Oracle: SELECT sid, serial#, username, machine, program FROM v$session;  SQL Server: SELECT * FROM sys.dm_exec_connections;  MongoDB: db.currentOp(true);  Redis: CLIENT LIST.","Trenne verdächtige/externe Verbindungen sofort und sperre das Konto (KILL/LOCK-Befehle).","Blockiere die Quelle auf Netzwerkebene: Sperre den verdächtigen IP-Bereich per Firewall / pg_hba.conf / Listener-ACL.","Prüfe das Audit-Log: Bestimme, welche Tabellen per SELECT/EXPORT abgerufen wurden, die Zeilenzahlen und das Zeitfenster (Oracle unified_audit_trail; SQL Server sys.fn_get_audit_file; PostgreSQL pgaudit-Logs; MongoDB auditLog).","Suche nach Exportspuren: große Ergebnismengen, COPY / SELECT ... INTO OUTFILE, mongoexport, Muster von Massenlesezugriffen.","Dokumentiere den betroffenen Datenumfang (welche Tabellen, welche personenbezogenen/vertraulichen Felder) und die mögliche Datenmenge; bewerte rechtliche Pflichten/Meldepflichten und folge dem Härtungsschritt."],"related":["show-privileges","list-users"],"playbook":"harden-sql"},"harden":{"kind":"solution","title":"Dauerhafte Härtung nach dem Vorfall","detail":"Wende nach der Notfallreaktion dauerhafte Kontrollen an, um eine Wiederholung zu verhindern: Auditing aktivieren, Least Privilege durchsetzen, Netzwerkzugriff einschränken und den Vorfall vollständig dokumentieren.","steps":["Auditing aktivieren: Erfasse kritische Ereignisse (Logins, GRANT/REVOKE, Zugriff auf sensible Tabellen) im Audit-Trail; Details findest du im Baum „Audit einrichten“.","Least Privilege: Gib jedem Konto nur die benötigten Berechtigungen, trenne gemeinsam genutzte/Service-Konten, deaktiviere ungenutzte Konten.","Passwort-/Identitätsrichtlinie: starke Passwörter, regelmäßige Rotation, wo möglich Multi-Faktor-Authentifizierung und zentrale Identitätsverwaltung (LDAP/Kerberos).","Netzwerkbeschränkung: Öffne die Datenbank nur für die Anwendungsserver (Firewall, Security Group, pg_hba.conf, Listener-ACL); blockiere direkten Internetzugriff.","Verschlüsselung: Aktiviere TLS für die Übertragung und, wo nötig, Verschlüsselung ruhender Daten.","Monitoring/Alarmierung: Richte Alarme für Spitzen bei fehlgeschlagenen Logins, neue GRANTs und auffällige Datenmengen ein.","Vorfallbericht: Dokumentiere Zeitleiste, betroffene Assets, ergriffene Maßnahmen und Lessons Learned; aktualisiere das Runbook."],"related":["list-users","show-privileges","change-password"]}}},"solver/audit-setup.json":{"id":"audit-setup","title":"Audit einrichten","icon":"🧾","category":"Sicherheit","intro":"Ein Entscheidungsbaum, der je nach Prüfgegenstand (Logins/Sessions, DDL/Schemaänderungen, Datenzugriff oder Berechtigungsänderungen) Auditing für diesen Umfang auf der richtigen Datenbank-Engine aktiviert und dann die Schritte zum Lesen der Einträge sowie zu Aufbewahrung/Rotation liefert.","start":"root","nodes":{"root":{"kind":"step","title":"Was willst du auditieren?","detail":"Wähle den Audit-Umfang. Alles zu auditieren erzeugt Rauschen und Kosten; beschränke dich auf deinen Bedarf. Willst du Login-/Session-Ereignisse, DDL/Schemaänderungen, Datenzugriff (DML/SELECT) oder Berechtigungsänderungen (GRANT/REVOKE) nachverfolgen?","choices":[{"label":"Logins / Sessions (Login, Connect)","next":"login"},{"label":"DDL / Schemaänderungen (CREATE/ALTER/DROP)","next":"ddl"},{"label":"Datenzugriff (DML / SELECT)","next":"dml"},{"label":"Berechtigungsänderungen (GRANT / REVOKE)","next":"grants"},{"label":"Audit-Einträge lesen und Aufbewahrung / Rotation","next":"retention"}]},"login":{"kind":"solution","title":"Login-/Session-Auditing","detail":"Erfasst erfolgreiche und fehlgeschlagene Logins: wer sich wann von wo verbunden hat. Das ist die Grundlage, um unbefugten Zugriff und Brute-Force-Versuche zu erkennen. Aktiviere die passende Policy je Engine und lies und bewahre die Einträge dann auf.","steps":["Oracle (Unified Auditing): CREATE AUDIT POLICY login_pol ACTIONS LOGON, LOGOFF;  AUDIT POLICY login_pol;  (für Fehlschläge: AUDIT POLICY login_pol WHENEVER NOT SUCCESSFUL;).","PostgreSQL: Setze log_connections=on und log_disconnections=on in postgresql.conf und lade neu; Verbindungen werden ohne pgaudit protokolliert.","MySQL/MariaDB: Installiere das Plugin audit_log (INSTALL PLUGIN audit_log ...) und führe SET GLOBAL audit_log_policy='LOGINS'; aus (MariaDB: server_audit_events='CONNECT').","SQL Server: Lege ein Server Audit an (CREATE SERVER AUDIT ...), füge FAILED_LOGIN_GROUP und SUCCESSFUL_LOGIN_GROUP zu einer Server Audit Specification hinzu und aktiviere beide mit ENABLE.","MongoDB: Starte mit --auditDestination file --auditFilter '{ atype: { $in: [\"authenticate\", \"logout\"] } }' (Enterprise/Percona).","Redis: Verfolge fehlgeschlagene Authentifizierungs-/Befehlsversuche mit ACL LOG (für Details das ACL-Log nutzen, nicht 'notify').","Elasticsearch: Setze xpack.security.audit.enabled: true in elasticsearch.yml und füge authentication_success, authentication_failed zur include-Liste hinzu.","Um die Einträge zu lesen und aufzubewahren/zu rotieren, folge ab dem Start dem Schritt „Audit-Einträge lesen und Aufbewahrung / Rotation“."],"related":["list-users","change-password"]},"ddl":{"kind":"solution","title":"DDL-/Schemaänderungs-Auditing","detail":"Erfasst Schemaänderungen wie CREATE/ALTER/DROP. Dient zum Erkennen unbefugter Objektänderungen, versteckter Tabellen/Hintertüren und Schema-Drift.","steps":["Oracle: CREATE AUDIT POLICY ddl_pol ACTIONS CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE USER, ALTER USER;  AUDIT POLICY ddl_pol;","PostgreSQL (pgaudit): Lade es mit shared_preload_libraries='pgaudit', dann ALTER SYSTEM SET pgaudit.log='ddl'; SELECT pg_reload_conf();","MySQL/MariaDB: Mit audit_log SET GLOBAL audit_log_policy='ALL'; und dann DDL-Anweisungen im Log filtern (MariaDB: server_audit_events='QUERY_DDL').","SQL Server: Füge SCHEMA_OBJECT_CHANGE_GROUP (und DATABASE_OBJECT_CHANGE_GROUP) zu einer Database Audit Specification hinzu und aktiviere sie mit ENABLE.","MongoDB: --auditFilter '{ atype: { $in: [\"createCollection\",\"dropCollection\",\"createIndex\",\"dropIndex\",\"createDatabase\",\"dropDatabase\"] } }'.","Elasticsearch: Füge Ereignisse zu index_creation/index_deletion zur Audit-include-Liste hinzu; protokolliere für Schemaänderungen (Mapping) _mapping-Aufrufe als Requests.","Um die Einträge zu lesen und aufzubewahren/zu rotieren, folge ab dem Start dem Schritt „Audit-Einträge lesen und Aufbewahrung / Rotation“."],"related":["show-privileges"]},"dml":{"kind":"solution","title":"Datenzugriffs-Auditing (DML / SELECT)","detail":"Erfasst Lese-/Schreibzugriffe (SELECT/INSERT/UPDATE/DELETE) auf sensible Tabellen. Da hohes Volumen entstehen kann, beschränke das Auditing auf sensible Objekte. Es ist entscheidend für Leckerkennung und Compliance (KVKK/DSGVO).","steps":["Schränke den Umfang ein: Auditiere nur sensible Tabellen/Spalten, nicht alle Daten (um Rauschen und Kosten zu kontrollieren).","Oracle: CREATE AUDIT POLICY dml_pol ACTIONS SELECT, INSERT, UPDATE, DELETE ON HR.SALARIES;  AUDIT POLICY dml_pol;","PostgreSQL (pgaudit): Für breites Logging ALTER SYSTEM SET pgaudit.log='read,write'; oder für gezieltes Auditing auf Objektebene der Audit-Rolle nur auf den relevanten Tabellen Berechtigungen geben und pgaudit.role setzen.","MySQL/MariaDB: Protokolliere mit einem audit_log-Filter nur die relevanten Tabellen/Benutzer (audit_log_filter / MariaDB server_audit_events='QUERY_DML').","SQL Server: Füge SELECT-, INSERT-, UPDATE-, DELETE-Aktionen für bestimmte Tabellen und einen Principal (z. B. public) zu einer Database Audit Specification hinzu und aktiviere sie mit ENABLE.","MongoDB: --auditFilter '{ atype: \"authCheck\", \"param.command\": { $in: [\"find\",\"insert\",\"update\",\"delete\"] }, \"param.ns\": \"db.sensitive\" }' (Volumen im Blick behalten).","Elasticsearch: Füge access_granted/access_denied zur Audit-include-Liste hinzu und nutze emit_request_body, um zu sehen, auf welchen Index eine Abfrage zielt (Vorsicht beim Protokollieren von Bodys mit sensiblen Daten).","Um die Einträge zu lesen und aufzubewahren/zu rotieren, folge ab dem Start dem Schritt „Audit-Einträge lesen und Aufbewahrung / Rotation“."],"related":["show-privileges","list-users"]},"grants":{"kind":"solution","title":"Auditing von Berechtigungsänderungen (GRANT / REVOKE)","detail":"Erfasst Berechtigungs- und Rollenänderungen (GRANT/REVOKE, Rollenzuweisung, Benutzeranlage). Das ist die Grundlage, um Rechteausweitung und Hintertür-Konten zu erkennen; meist geringes Volumen, aber hoher Wert.","steps":["Oracle: CREATE AUDIT POLICY priv_pol ACTIONS GRANT, REVOKE, CREATE USER, ALTER USER, CREATE ROLE;  AUDIT POLICY priv_pol;","PostgreSQL (pgaudit): ALTER SYSTEM SET pgaudit.log='role'; SELECT pg_reload_conf();  (erfasst GRANT/REVOKE/CREATE ROLE-Ereignisse).","MySQL/MariaDB: Protokolliere GRANT/REVOKE/CREATE USER-Anweisungen mit audit_log (Policy 'ALL' oder DDL) und behalte ACL-Anweisungen im Filter.","SQL Server: Füge SERVER_PERMISSION_CHANGE_GROUP, SERVER_ROLE_MEMBER_CHANGE_GROUP und DATABASE_PERMISSION_CHANGE_GROUP zu einer Server Audit Specification hinzu und aktiviere sie mit ENABLE.","MongoDB: --auditFilter '{ atype: { $in: [\"grantRolesToUser\",\"revokeRolesFromUser\",\"createUser\",\"dropUser\",\"createRole\"] } }'.","Elasticsearch: Protokolliere für Rollen-/Benutzeränderungen sicherheitsrelevante Request-Ereignisse (Realm-/Rollen-API-Aufrufe) im Audit-Trail.","Um die Einträge zu lesen und aufzubewahren/zu rotieren, folge ab dem Start dem Schritt „Audit-Einträge lesen und Aufbewahrung / Rotation“."],"related":["show-privileges","create-user-grant","list-users"]},"retention":{"kind":"solution","title":"Audit-Einträge lesen und Aufbewahrung / Rotation","detail":"Auditing ist nur nützlich, wenn die Einträge gelesen und aufbewahrt werden. Lege hier fest, wie die Einträge abgefragt werden, wo sie liegen und welche Aufbewahrungs-/Rotationsrichtlinie ihr Wachstum im Griff behält.","steps":["Einträge lesen: Oracle: SELECT event_timestamp, dbusername, action_name, object_name, sql_text FROM unified_audit_trail ORDER BY event_timestamp DESC;  PostgreSQL: Die pgaudit-Ausgabe landet in der Server-Logdatei (log_directory); filtere die 'AUDIT:'-Zeilen mit grep.","SQL Server: SELECT * FROM sys.fn_get_audit_file('D:\\audit\\*.sqlaudit', DEFAULT, DEFAULT);  MySQL: Lies die audit_log-Datei (JSON/XML) oder MariaDBs server_audit.log; MongoDB: Parse audit.json; Elasticsearch: Frage die Indizes .security-audit-* ab.","Lege die Aufbewahrungsdauer fest (gemäß Compliance-Anforderungen, z. B. 90 Tage / 1 Jahr) und halte sie in der Richtlinie fest.","Rotation/Bereinigung: Oracle: Archiviere/lösche alte Einträge mit DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP und CLEAN_AUDIT_TRAIL; rotiere die Dateilogs von PostgreSQL/MySQL/MongoDB mit logrotate nach Größe/Zeit.","Elasticsearch: Definiere eine ILM-Policy (Rollover + Delete) für die Audit-Indizes.","Sende Audit-Einträge an einen unveränderlichen Ort (WORM), dessen Zugriff von den DBAs getrennt ist (Funktionstrennung); beschränke den Zugriff.","Überwache das Speicherwachstum: Prüfe regelmäßig, dass Auditing die Disk nicht füllt und die Performance nicht übermäßig beeinträchtigt."],"related":["list-users","show-privileges"]}}},"solver/decide-ha.json":{"id":"decide-ha","title":"Entscheidung: welche HA/DR-Lösung?","icon":"🧭","category":"Entscheidungshilfe","intro":"Die Wahl einer Lösung für Hochverfügbarkeit und Disaster Recovery beginnt nicht bei der Technik, sondern beim Gleichgewicht zwischen Geschäftsanforderungen (RTO/RPO), Bedarf an geografischer Verteilung und Kosten. Ausgehend von deiner Toleranz für Ausfallzeit und Datenverlust führt dich dieser Baum zum passenden Ansatz zwischen synchronen Replicas, asynchronen Standbys, Clustern mit automatischem Failover und Backup+PITR.","start":"root","nodes":{"root":{"kind":"step","title":"Wie hoch ist deine Toleranz für Datenverlust (RPO)?","detail":"Beginne mit der härtesten Bedingung: Wie viele Sekunden an Daten kannst du dir im Katastrophenfall leisten zu verlieren? Ist null Datenverlust (RPO=0) gefordert, ist synchrone Replikation Pflicht; sind einige Sekunden/Minuten akzeptabel, kommen asynchrone Lösungen und günstigere Ansätze infrage. Das RPO-Ziel bestimmt meist alle weiteren Entscheidungen.","choices":[{"label":"Null Datenverlust ist Pflicht (RPO = 0)","next":"rpo_zero"},{"label":"Einige Sekunden/Minuten Datenverlust sind akzeptabel","next":"rpo_soft"}]},"rpo_zero":{"kind":"step","title":"Ist auch eine automatische, schnelle Übernahme (niedrige RTO) gefordert?","detail":"Du willst null Datenverlust; aber wie schnell musst du wieder laufen, wenn der Primary ausfällt? Automatisches Failover innerhalb von Sekunden, oder ist ein kurzes Fenster für manuelles Eingreifen akzeptabel? Das entscheidet zwischen einer einfachen synchronen Replica und einem voll automatisierten Cluster.","choices":[{"label":"Automatisches Failover innerhalb von Sekunden ist gefordert","next":"sol_cluster"},{"label":"Manuelle/geplante Übernahme reicht; null Verlust hat Vorrang","next":"sol_sync"}]},"rpo_soft":{"kind":"step","title":"Ist eine andere Region/Geografie erforderlich (DR)?","detail":"Ist etwas Datenverlust akzeptabel, ist die eigentliche Frage der Umfang der Ausfallsicherheit: Musst du gegen den Verlust eines ganzen Rechenzentrums/einer Region geschützt sein, oder reicht Schutz gegen den Ausfall eines einzelnen Knotens in derselben Region? Ist geografische Verteilung gefordert, rückt ein asynchroner Standby in den Vordergrund; wenn nicht, werden kostenorientierte Optionen bewertet.","choices":[{"label":"Ja, eine separate Region gegen geografische Katastrophen ist Pflicht","next":"sol_async"},{"label":"Nein, Budget und Einfachheit haben Vorrang","next":"budget"}]},"budget":{"kind":"step","title":"Kannst du eine lange Wiederherstellungszeit (hohe RTO) akzeptieren?","detail":"Du bleibst in einer Region und tolerierst etwas Datenverlust. Strebst du die niedrigsten Kosten an, oder willst du einen Standby-Knoten, der bei einem Ausfall schnell übernimmt? Ist eine Wiederherstellung über Stunden akzeptabel, ist Backup+PITR allein der wirtschaftlichste Weg; andernfalls brauchst du einen Warm Standby.","choices":[{"label":"Ja, eine Wiederherstellung über Stunden ist akzeptabel (am günstigsten)","next":"sol_backup"},{"label":"Nein, ein Warm Standby, der innerhalb von Minuten übernimmt, ist gefordert","next":"sol_async"}]},"sol_sync":{"kind":"solution","title":"Empfehlung: synchrone Replica (null Datenverlust)","detail":"Der Primary schließt einen Commit erst ab, wenn mindestens ein Standby ihn bestätigt; selbst bei Verlust des Primary geht also keine bestätigte Transaktion verloren (RPO=0). Die ideale Lösung innerhalb einer Region mit latenzarmem Netzwerk.","steps":["Warum: Ein Commit wird erst abgeschlossen, wenn bestätigte Transaktionen auf den Standby geschrieben sind; der Datenverlust ist also mathematisch null.","Wann: Finanz-/kritisches OLTP in derselben Region (gleiche/benachbarte Availability Zone) mit verfügbarem Netzwerk niedriger RTT.","Beispiel: PostgreSQL synchronous_commit + synchronous_standby_names, MySQL Group Replication / semisync, Oracle Data Guard SYNC (Maximum Protection/Availability).","Achtung: Die synchrone Bestätigung fügt jedem Commit Netzwerklatenz hinzu; wird der Standby langsam/trennt sich und gibt es nur eine synchrone Kopie, kann auch der Primary keine Schreibvorgänge mehr annehmen. Plane mit mindestens zwei Standbys oder einem passenden Fallback.","Achtung: Repliziere nicht synchron in eine entfernte Region mit hoher Latenz; die Schreibperformance bricht ein. Nutze für geografische DR asynchrone Replikation."],"related":["replication-status","promote-failover"]},"sol_async":{"kind":"solution","title":"Empfehlung: asynchroner Standby (geografische DR)","detail":"Der Standby wendet Änderungen als Stream an, ohne die Commits des Primary zu verzögern. Das ist der Standardweg, eine Kopie in einer anderen Region zu halten, ohne die Schreibperformance zu beeinträchtigen; im Gegenzug wird ein kleines Datenverlustfenster (in Höhe der Replikationsverzögerung) akzeptiert.","steps":["Warum: Der Primary wartet nicht auf die Bestätigung des Standby; die Replikation in eine entfernte Region fügt daher keine Schreiblatenz hinzu.","Wann: wenn Disaster Recovery auf Regions-/Rechenzentrumsebene nötig ist und Datenverlust im Sekundenbereich akzeptabel ist.","Beispiel: PostgreSQL Streaming Replication (async), asynchrone MySQL-Replikation, Oracle Data Guard ASYNC (Maximum Performance).","Achtung: Beim Failover kannst du so viele Daten verlieren, wie der Verzug beträgt; überwache die Replikationsverzögerung kontinuierlich und richte Schwellenwertalarme ein.","Achtung: Die Übernahme erfolgt meist manuell oder durch externe Orchestrierung; ein klares Failover-Verfahren und regelmäßige Übungen sind unerlässlich."],"related":["replication-status","promote-failover"]},"sol_cluster":{"kind":"solution","title":"Empfehlung: Cluster mit automatischem Failover","detail":"Eine Cluster-Lösung, die Health Monitoring, Leader-Wahl und automatische Übernahme kombiniert; sie zielt sowohl auf niedrige RTO (automatisches Failover innerhalb von Sekunden) als auch auf niedrige/null RPO mit einer synchronen Kopie. Der Betriebsaufwand ist höher, menschliches Eingreifen wird aber minimiert.","steps":["Warum: Der Cluster-Manager erkennt einen Ausfall des Primary automatisch, promotet eine gesunde Kopie und leitet den Traffic um; kein manuelles Eingreifen um Mitternacht.","Wann: wenn sowohl niedrige RTO als auch niedrige RPO Pflicht sind und das Betriebsteam die zusätzliche Komplexität bewältigen kann.","Beispiel: Patroni/etcd für PostgreSQL, MySQL InnoDB Cluster (Group Replication + MySQL Router), SQL Server Always On Availability Groups, Oracle RAC + Data Guard mit Fast-Start Failover (automatisches Failover erfordert FSFO und einen Observer).","Achtung: Das Split-Brain-Risiko ist real; setze nichts ohne ordentliches Fencing/Quorum ein (ungerade Knotenzahl, zuverlässiger verteilter Store).","Achtung: Der Cluster selbst ist eine neue Fehlerquelle; probe das Failover regelmäßig und validiere die Automatisierung unter realen Bedingungen."],"related":["promote-failover","replication-status"]},"sol_backup":{"kind":"solution","title":"Empfehlung: Backup + PITR (am wirtschaftlichsten)","detail":"Statt eines laufenden Standby werden nur regelmäßige Backups und Point-in-Time-Recovery (PITR) über WAL/Archivlogs vorgehalten. Das ist die kostengünstigste Lösung; der Preis ist, dass die Wiederherstellung Stunden dauern kann (hohe RTO).","steps":["Warum: Es fallen keine Kosten für einen dauerhaft laufenden zweiten Knoten an; nur Backup-Speicher und Archivlogs werden vorgehalten.","Wann: nicht kritische Systeme mit hoher Toleranz für Ausfallzeit; Dev/Test-Umgebungen; wenn Budget und Einfachheit Vorrang haben.","Beispiel: PITR mit physischem Backup + Archivlogs (PG Base Backup + WAL, Oracle RMAN + Archivlog, MySQL xtrabackup + binlog).","Achtung: Die RTO ist hoch — die Zeit für Restore + Log-Anwendung wächst mit der Datenmenge; miss sie, um zu bestätigen, dass sie in die akzeptierte Zeit passt.","Achtung: Ein ungetestetes Backup ist kein Backup; führe regelmäßig Restore-Übungen und Backup-Verifizierungen durch."],"related":["last-backup-status","logical-dump"]}}},"solver/decide-isolation.json":{"id":"decide-isolation","title":"Entscheidung: Welche Isolationsstufe?","icon":"🧭","category":"Entscheidungshilfe","intro":"Die Isolationsstufe einer Transaktion bestimmt das Gleichgewicht zwischen Parallelität und Konsistenz. Höhere Isolation bedeutet weniger Anomalien, aber mehr Sperren/Konflikte und Wiederholungen. Dieser Baum hilft dir, zwischen Read Committed, Repeatable Read/Snapshot und Serializable zu wählen, je nachdem, welche Leseanomalien (Dirty, Non-repeatable, Phantom) du tolerieren kannst.","start":"root","nodes":{"root":{"kind":"step","title":"Kannst du Dirty Reads tolerieren?","detail":"Beginne mit der grundlegendsten Anomalie: Soll eine Transaktion Daten lesen können, die noch nicht committet sind (und zurückgerollt werden könnten)? Fast keine ernsthafte Anwendung will das; in den meisten modernen Engines verhindert der Standard Dirty Reads bereits. Gelockert wird das nur für ungefähre/analytische Grobzählungen und maximale Geschwindigkeit.","choices":[{"label":"Nein, ich darf nur committete Daten lesen","next":"committed"},{"label":"Ja, ein grobes/ungefähres Lesen reicht (selten)","next":"sol_read_uncommitted"}]},"committed":{"kind":"step","title":"Musst du beim erneuten Lesen innerhalb derselben Transaktion dasselbe Ergebnis sehen?","detail":"Nachdem Dirty Reads ausgeschlossen sind, die nächste Frage: Liest du dieselbe Zeile zweimal innerhalb einer Transaktion, ist es für dich ein Problem, einen anderen Wert zu sehen, weil jemand zwischendurch committet hat (Non-repeatable Read), oder dass neue Zeilen auftauchen (Phantom)? Logik, die Konsistenz bei Berichten/Berechnungen erfordert, braucht meist einen konsistenten Snapshot.","choices":[{"label":"Nein, jede Anweisung darf die neuesten committeten Daten sehen","next":"sol_read_committed"},{"label":"Ja, für die gesamte Transaktion ist ein konsistenter Snapshot nötig","next":"consistent"}]},"consistent":{"kind":"step","title":"Müssen parallele Transaktionen logisch vollständig geordnet sein?","detail":"Du willst konsistente Lesevorgänge; reicht das, oder müssen parallele Transaktionen ein Ergebnis liefern, als wären sie nacheinander gelaufen (Serializable)? Kannst du subtile Anomalien wie Write Skew tolerieren (z. B. zwei Transaktionen, die verschiedene Zeilen lesen und so schreiben, dass sie sich gegenseitig ungültig machen), reicht Snapshot; andernfalls ist volle Serialisierung erforderlich.","choices":[{"label":"Nein, ein konsistenter Snapshot reicht","next":"sol_repeatable_read"},{"label":"Ja, keine Parallelitätsanomalie ist akzeptabel","next":"sol_serializable"}]},"sol_read_uncommitted":{"kind":"solution","title":"Empfehlung: Read Uncommitted (nur Sonderfälle)","detail":"Die niedrigste Isolation; sie erlaubt Dirty Reads. Sie liest fast ohne Warten auf Sperren, du siehst aber möglicherweise Daten, die zurückgerollt werden können. Für allgemeine Anwendungen nicht empfohlen.","steps":["Warum: Der Leser wartet nicht auf die Sperre des Schreibers; maximale Parallelität, aber keine Konsistenzgarantie.","Wann: Nur in Fällen wie groben Metriken/Fortschrittszählungen, bei denen Genauigkeit keine Rolle spielt.","Achtung: In PostgreSQL verhält sich diese Stufe wie Read Committed, und Oracle bietet Read Uncommitted gar nicht an; echte Dirty Reads treten vor allem im sperrbasierten SQL Server (NOLOCK) auf.","Achtung: „NOLOCK für Geschwindigkeit“ sollte nicht zur dauerhaften Gewohnheit werden; es birgt das Risiko, falsche/doppelte/fehlende Zeilen zu lesen."]},"sol_read_committed":{"kind":"solution","title":"Empfehlung: Read Committed (Standard-OLTP)","detail":"Jede Anweisung sieht die zu diesem Zeitpunkt zuletzt committeten Daten; Dirty Reads werden verhindert. Das ist in den meisten Engines der Standard und bietet für typische OLTP-Workloads das beste Gleichgewicht aus Parallelität und Konsistenz.","steps":["Warum: Es verhindert Dirty Reads, ohne die Transaktion zu zwingen, Sperren unnötig lange zu halten; es bietet hohe Parallelität.","Wann: Kurze, gezielte OLTP-Transaktionen; der richtige Standard für die große Mehrheit der Webanwendungen.","Beispiel: Read Committed ist der Standard in PostgreSQL, SQL Server und Oracle (in Oracle sorgt ein Snapshot auf Anweisungsebene für Konsistenz).","Achtung: Zwei Lesevorgänge in derselben Transaktion können unterschiedliche Ergebnisse liefern (Non-repeatable/Phantom); stütze Logik, die konsistente Berichte braucht, nicht darauf.","Achtung: Der Standard bei MySQL/InnoDB ist Repeatable Read; beachte den Engine-Unterschied bei einer Migration."]},"sol_repeatable_read":{"kind":"solution","title":"Empfehlung: Repeatable Read / Snapshot (konsistente Lesevorgänge)","detail":"Die Transaktion arbeitet auf einem konsistenten Snapshot, der zu ihrem Beginn (oder beim ersten Lesen) festgelegt wird; Lesevorgänge ändern sich während der Transaktion nicht. Mit MVCC blockieren Leser keine Schreiber und Schreiber keine Leser.","steps":["Warum: Es bietet eine feste Sicht für die gesamte Transaktion; es verhindert Non-repeatable Reads (und in MVCC-Engines praktisch auch Phantome) und macht leseintensive Berichts-/Berechnungslogik konsistent.","Wann: Berichte, Abstimmungen und Batch-Berechnungen, die dieselben Daten in einer Transaktion mehrfach lesen und konsistente Ergebnisse brauchen.","Beispiel: MySQL/InnoDB-Standard Repeatable Read; SQL Server SNAPSHOT (Row Versioning); PostgreSQL REPEATABLE READ; Oracle SERIALIZABLE hat tatsächlich Snapshot-Semantik.","Achtung: Es verhindert keine Serialisierungsanomalien wie Write Skew; wechsle zu Serializable, wenn die Geschäftsregel es erfordert.","Achtung: Snapshot/Row Versioning kann tempdb oder den Undo-/Version Store anwachsen lassen; lange Transaktionen blähen alte Versionen auf."]},"sol_serializable":{"kind":"solution","title":"Empfehlung: Serializable (volle Isolation)","detail":"Die höchste Isolation: Parallele Transaktionen liefern ein Ergebnis, als wären sie seriell (nacheinander) ausgeführt worden. Sie verhindert alle Leseanomalien und Write Skew; der Preis sind mehr Konflikte und die Notwendigkeit von Wiederholungen.","steps":["Warum: Es garantiert, dass das Ergebnis immer einer gültigen seriellen Ausführung entspricht; es beseitigt alle Parallelitätsanomalien.","Wann: Logik mit korrektheitskritischen Invarianten: Saldo-/Bestandsprüfungen, Fälle, in denen eine Doppelbuchung nie passieren darf.","Beispiel: PostgreSQL SSI (Serializable Snapshot Isolation), SQL Server SERIALIZABLE (Range Locks); in Oracle wird echte Serialisierung auf Anwendungsebene gelöst.","Achtung: Serialisierungsfehler (z. B. 40001 in PG) sind erwartetes Verhalten; Retry-Logik, die die Transaktion in der Anwendung automatisch erneut ausführt, ist PFLICHT.","Achtung: Unter stark konfliktbehafteter Last kann der Durchsatz sinken; setze es nicht überall ein, sondern nur bei Transaktionen, bei denen Korrektheit kritisch ist."]}}},"solver/decide-sqlnosql.json":{"id":"decide-sqlnosql","title":"Entscheidung: SQL oder NoSQL (welche Datenbankfamilie)?","icon":"🧭","category":"Entscheidungshilfe","intro":"Die Wahl der richtigen Datenbankfamilie hängt von der Form des Datenmodells, deinen Prioritäten bei Konsistenz und Skalierung und der Komplexität deiner Abfragen ab. Verallgemeinerungen wie „NoSQL ist moderner“ führen in die Irre; jede Familie ist für bestimmte Zugriffsmuster gebaut. Ausgehend von der Struktur der Daten führt dich dieser Baum zum richtigen Werkzeug zwischen relationalen, Dokument-, Key-Value- und Suchengines und erinnert bei Bedarf an den polyglotten Ansatz.","start":"root","nodes":{"root":{"kind":"step","title":"Welcher Art sind die Daten und welches Zugriffsmuster dominiert?","detail":"Beginne mit der entscheidendsten Frage: Sind die Daten strukturierte Datensätze mit starken Beziehungen, Dokumente mit flexiblem/variablem Schema, einfache Key-Value-Lookups oder ein Sonderbedarf wie Textsuche? Die Form der Daten und die Art der Abfrage bestimmen die Familie weitgehend.","choices":[{"label":"Relationale, strukturierte Datensätze und komplexe Abfragen","next":"relational_check"},{"label":"Verschachtelte Dokumente mit flexiblem/variablem Schema","next":"sol_document"},{"label":"Einfaches Key-Value, sehr niedrige Latenz/Caching","next":"sol_keyvalue"},{"label":"Volltext-/facettierte Suche, Log-Analyse","next":"sol_search"}]},"relational_check":{"kind":"step","title":"Hat starke Konsistenz (ACID) oder horizontale Skalierung Vorrang?","detail":"Die Daten wirken relational; aber was ist deine Priorität? Sind Joins über mehrere Tabellen, transaktionale Integrität und starke Konsistenz (ACID) unverzichtbar, oder geht es darum, ein riesiges Volumen horizontal zu skalieren, das nicht auf einen Knoten passt? Das relationale Modell ist für die meisten Workloads der richtige Standard; an Grenzen stößt es erst, wenn die Skalierung einen Knoten wirklich übersteigt.","choices":[{"label":"ACID, Joins und Integrität komplexer Abfragen haben Vorrang","next":"sol_relational"},{"label":"Skalierung vor allem anderen, schwache Beziehungen/Joins","next":"sol_document"}]},"sol_relational":{"kind":"solution","title":"Empfehlung: relationale Datenbank (RDBMS)","detail":"Der richtige Standard für Workloads mit strukturierten, stark verknüpften Daten, die transaktionale Integrität erfordern. ACID-Garantien, eine ausgereifte SQL-Engine und Joins beantworten komplexe Fragen zuverlässig.","steps":["Warum: Sie bietet ACID-Transaktionen, Fremdschlüsselintegrität und starke Konsistenz sowie einen ausgereiften Planer, der komplexe Abfragen über mehrere Tabellen optimiert.","Wann: OLTP- und Reporting-Workloads, bei denen Beziehungen und Konsistenz kritisch sind, etwa Finanzen, Bestellungen/Lager, ERP/CRM.","Beispiel: PostgreSQL, MySQL, Oracle, SQL Server; für Zeitreihen Erweiterungen wie PostgreSQL + TimescaleDB.","Achtung: Schemaänderungen (Migrationen) erfordern Disziplin; sind Schemaänderungen häufig und unvorhersehbar, prüfe das Dokumentmodell.","Achtung: Erreichst du die Schreib-/Datengrenze eines einzelnen Knotens, skaliere mit Read Replicas, Partitionierung und bei Bedarf Sharding."],"related":["create-partition"]},"sol_document":{"kind":"solution","title":"Empfehlung: Dokumentdatenbank","detail":"Speichert flexible, verschachtelte Daten mit häufig wechselndem Schema in ihrer natürlichen Form (JSON-ähnliche Dokumente). Geeignet für Anwendungen, die meist über ein einzelnes Dokument lesen/schreiben, wenige Joins brauchen und horizontale Skalierung benötigen.","steps":["Warum: Das Schema ist flexibel; Dokumente können Felder hinzufügen oder entfernen, verschachtelte Strukturen mit einem einzigen Lesevorgang holen und lassen sich gut horizontal skalieren (Sharding).","Wann: Dokumentzentrierte, variabel strukturierte, schnell wachsende Datenmodelle wie Kataloge, Benutzerprofile, Content/CMS, Ereignisdatensätze.","Beispiel: MongoDB, Couchbase; in der relationalen Welt bietet JSONB (PostgreSQL) in begrenztem Maß ähnliche Flexibilität.","Achtung: Komplexe Joins über mehrere Dokumente und Reporting sind schwach; dominieren Beziehungen, ist das relationale Modell besser geeignet.","Achtung: Die Verantwortung für Konsistenz und Schemavalidierung verlagert sich weitgehend in die Anwendung; schütze die Datenqualität im Code."]},"sol_keyvalue":{"kind":"solution","title":"Empfehlung: Key-Value-Store / Cache","detail":"Gebaut für Workloads, die sehr schnelles Lesen/Schreiben per Schlüssel und keine komplexen Abfragen brauchen. Läuft meist im Arbeitsspeicher und bietet Latenzen im Mikro-/Millisekundenbereich; ideal für Aufgaben wie Caching, Sessions und Zähler.","steps":["Warum: Extrem niedrige Latenz und hoher Durchsatz für einfachen Schlüsselzugriff; In-Memory-Strukturen entlasten die relationale Datenbank.","Wann: Cache-Schicht, Session Store, Rate-Limit-Zähler, Bestenlisten, temporäre Queues/Pub-Sub.","Beispiel: Redis, Memcached; Redis bietet zudem reichhaltige Strukturen wie Lists/Sets/Sorted Sets und Persistenzoptionen.","Achtung: Er ist meist nicht die alleinige Quelle der Wahrheit; halte die Quelldaten in einer dauerhaften Datenbank und speichere hier abgeleitete/gecachte Daten.","Achtung: Arbeitsspeicher ist begrenzt und teuer; plane Eviction-Policy, TTLs und Persistenz-/Dauerhaftigkeitseinstellungen nach Geschäftsbedarf."]},"sol_search":{"kind":"solution","title":"Empfehlung: Such-/Analyse-Engine","detail":"Eine auf invertiertem Index basierende Engine, spezialisiert auf Volltextsuche, Relevanz-Ranking, facettierte Filterung und Log-/Textanalyse mit hohem Volumen. Sie liefert das Sucherlebnis, das relationale LIKE-Abfragen nicht bieten können.","steps":["Warum: Mit invertiertem Index und Scoring führt sie Volltextsuche, Autovervollständigung, Facetten und Aggregationen mit hoher Performance aus.","Wann: Produkt-/Seitensuche, Log- und Observability-Analyse, Anwendungen, die ein nach Relevanz sortiertes Sucherlebnis brauchen.","Beispiel: Elasticsearch/OpenSearch, Apache Solr; für Volltextsuche mittleren Umfangs kann PostgreSQL tsvector genügen.","Achtung: Sie ist meist nicht das primäre System of Record; halte die Hauptdaten in einer relationalen/Dokument-DB und befülle den Suchindex von dort (CDC/Sync).","Achtung: Garantien für starke Konsistenz in Echtzeit sind schwach (Near-Real-Time-Indexierung); berücksichtige die Indexierungslatenz im Design."],"related":["setup-cdc-replication"]}}},"solver/decide-shard-replica.json":{"id":"decide-shard-replica","title":"Entscheidung: Sharding oder Replikation (Skalierung)?","icon":"🧭","category":"Entscheidungshilfe","intro":"Die Skalierungsentscheidung hängt davon ab, ob der Engpass bei Lese- oder Schreibvorgängen liegt und ob die Daten auf einen einzelnen Knoten passen. Die falsche Strategie bedeutet unnötige Komplexität oder einen ungelösten Engpass. Dieser Baum erinnert zuerst an die günstigsten und einfachsten Optionen (vertikale Skalierung, Caching) und hilft dann bei der richtigen Wahl zwischen Read Replicas und Sharding.","start":"root","nodes":{"root":{"kind":"step","title":"Hast du zuerst den einfachsten Weg versucht: vertikale Skalierung und Query-Tuning?","detail":"Bevor du auf eine verteilte Architektur umsteigst, nimm den günstigsten Gewinn mit: Mehr Hardware (CPU/RAM/IOPS), fehlende Indizes ergänzen und teure Abfragen beheben löst die meisten Engpässe. Füge Komplexität erst hinzu, wenn das ausgeschöpft ist.","choices":[{"label":"Nein / ich habe das Limit noch nicht erreicht","next":"sol_vertical"},{"label":"Ja, vertikale Skalierung und Tuning reichen nicht","next":"bottleneck"}]},"bottleneck":{"kind":"step","title":"Liegt der Engpass bei Lesevorgängen oder bei Schreibvorgängen/Datenvolumen?","detail":"Grenze die Ursache des Skalierungsproblems ein: Ersticken viele Leseabfragen einen einzelnen Knoten (leseintensiv), oder übersteigen Schreibrate oder Gesamtdatenmenge die Kapazität eines Knotens (schreib-/volumenintensiv)? Diese Unterscheidung ist der grundlegende Unterschied zwischen Replicas und Sharding.","choices":[{"label":"Lesetraffic dominiert, Schreibvorgänge passen auf einen Knoten","next":"read_path"},{"label":"Schreibrate oder Datenvolumen übersteigt einen Knoten","next":"sol_sharding"}]},"read_path":{"kind":"step","title":"Sind die Lesevorgänge wiederholt/cachebar oder frisch und vielfältig?","detail":"Teile den Leseengpass noch einen Schritt weiter auf: Werden immer wieder dieselben Ergebnisse gelesen (ein Cache ist sehr wirksam), oder fordern Lesevorgänge vielfältige, breite und aktuelle Daten an (zusätzliche Lesekapazität nötig)? Das entscheidet zwischen einer Cache-Schicht und Read Replicas.","choices":[{"label":"Dieselben Daten werden wiederholt gelesen, leichte Veraltung ist akzeptabel","next":"sol_cache"},{"label":"Vielfältige/frische Lesevorgänge, reine Lesekapazität ist nötig","next":"sol_read_replica"}]},"sol_vertical":{"kind":"solution","title":"Empfehlung: zuerst vertikale Skalierung + Query-Tuning","detail":"Der erste und günstigste Weg vor einer verteilten Architektur. Mehr Hardware und korrigierte Abfragen/Indizes lösen die meisten Skalierungsprobleme, ohne Betriebskomplexität hinzuzufügen.","steps":["Warum: geringste Komplexität und geringstes Risiko; bleibst du auf einem Knoten, ändert sich dein Transaktions-/Konsistenzmodell nicht und du musst den Anwendungscode nicht aufteilen.","Wann: wenn du die Hardwaregrenze eines einzelnen Knotens noch nicht erreicht hast oder der Engpass eigentlich von schlechten Abfragen/fehlenden Indizes kommt.","Schritt: Profiliere langsame Abfragen, ergänze fehlende Indizes, korrigiere Pläne/Statistiken; erhöhe dann CPU/RAM/IOPS.","Achtung: Vertikale Skalierung ist endlich und ihre Kosten steigen exponentiell; ein einzelner Knoten bleibt ein Single Point of Failure (richte für HA separat eine Replica ein).","Achtung: Plane den nächsten Schritt (Replicas/Sharding) rechtzeitig, wenn die Grenze näher rückt; überstürzte Migrationen sind riskant."]},"sol_read_replica":{"kind":"solution","title":"Empfehlung: Read Replicas (Leseskalierung)","detail":"Schreibvorgänge bleiben auf einem Knoten (dem Primary), während der Lesetraffic auf eine oder mehrere Replicas verteilt wird. Das ist der Standardweg, reine Lesekapazität und Ausfallsicherheit für leseintensive Workloads zu erhöhen.","steps":["Warum: Leseabfragen verteilen sich auf mehrere Kopien; der Primary verarbeitet nur Schreibvorgänge, und der Lesedurchsatz wächst nahezu linear.","Wann: Workloads mit hohem Lese-/Schreibverhältnis; Trennung von Reporting- und Analyseabfragen vom Primary; gleichzeitig einen HA-Standby gewinnen.","Beispiel: PostgreSQL-Streaming-Replicas, MySQL-Replicas + Proxy-Routing, Lese-/Schreibaufteilung in der Anwendung (Read-Write Split).","Achtung: Bei asynchronen Replicas erzeugt die Replikationsverzögerung ein Read-your-Writes-Problem (du kannst gerade Geschriebenes nicht sofort lesen); leite kritische Lesevorgänge an den Primary.","Achtung: Replicas lösen keinen Schreibengpass; der einzelne schreibende Knoten bleibt das Limit — sind auch Schreibvorgänge ein Engpass, ist Sharding erforderlich."],"related":["replication-status"]},"sol_cache":{"kind":"solution","title":"Empfehlung: Cache-Schicht","detail":"Häufig erneut gelesene Ergebnisse in einer schnellen In-Memory-Schicht zu halten, senkt die Datenbanklast drastisch. Mit dem richtigen Muster antwortet sie, ohne zur Datenbank zu gehen, und senkt so Latenz und Last.","steps":["Warum: Wiederholte Lesevorgänge werden mit Speichergeschwindigkeit beantwortet, ohne die Datenbank überhaupt zu erreichen; das ist die Leseskalierung mit dem besten Nutzen-Kosten-Verhältnis.","Wann: Dieselben Daten werden oft gelesen und leichte Veraltung ist akzeptabel; z. B. Produktdetails, Einstellungen, Referenzdaten.","Beispiel: das Cache-Aside-Muster mit Redis/Memcached; die Anwendung geht zuerst zum Cache und bei einem Miss zur Datenbank.","Achtung: Cache-Invalidierung ist schwierig; lege TTL und die Strategie zum Aktualisieren/Löschen beim Schreiben explizit fest.","Achtung: Verlass dich bei Lesevorgängen, die starke Konsistenz erfordern (Geld/Bestand), nicht auf einen veralteten Cache; bediene diese Lesevorgänge aus der Quelle."]},"sol_sharding":{"kind":"solution","title":"Empfehlung: Sharding (Schreib-/Datenskalierung)","detail":"Daten werden nach einem Shard Key aufgeteilt und auf mehrere unabhängige Knoten verteilt; jeder Knoten besitzt einen Teil der Daten und dessen Schreibvorgänge. Das ist der Weg, Schreibrate und Gesamtdatenvolumen zu skalieren, bringt aber erhebliche Komplexität.","steps":["Warum: Schreibvorgänge und Speicher verteilen sich auf mehrere Knoten; die Grenzen eines einzelnen Schreibers und eines einzelnen Knotens entfallen und horizontale Skalierung wird möglich.","Wann: wenn Schreibdurchsatz oder Datengröße nicht mehr auf einen einzelnen Knoten (und Read Replicas) passen; wenn keine andere Option bleibt.","Beispiel: Shard-Routing in der Anwendungsschicht, Vitess (MySQL), Citus (PostgreSQL), integriertes Sharding in Dokument-DBs.","Achtung: Die Wahl des Shard Keys ist kritisch und schwer rückgängig zu machen; ein schlechter Schlüssel erzeugt Hotspots und ungleiche Verteilung. Wähle einen Schlüssel mit hoher Kardinalität, der zum Zugriffsmuster passt.","Achtung: Shard-übergreifende Joins, Transaktionen und Eindeutigkeit werden schwierig; Anwendungskomplexität, Rebalancing (Resharding) und Betriebslast steigen erheblich."],"related":["create-partition"]}}},"solver/decide-backup.json":{"id":"decide-backup","title":"Entscheidung: Welche Backup-Strategie?","icon":"🧭","category":"Entscheidungshilfe","intro":"Eine Backup-Strategie wird durch Abwägen von drei Faktoren gewählt: wie viel Datenverlust du tolerieren kannst (RPO), deine Datengröße und wie schnell du wiederherstellen musst (RTO). Keine Methode passt für jedes Szenario. Ausgehend vom RPO-Ziel führt dich dieser Baum zwischen logischen Dumps, physischen Backups + PITR und Snapshots und erinnert daran, dass 3-2-1 und Verifizierung nicht verhandelbar sind.","start":"root","nodes":{"root":{"kind":"step","title":"Was ist dein Ziel für Datenverlust (RPO)?","detail":"Beginne mit der entscheidendsten Bedingung: Wie viele Minuten/Stunden an Daten kannst du dir im Katastrophenfall leisten zu verlieren? Ist nahezu null Datenverlust gefordert, ist kontinuierliche Log-Archivierung (PITR) Pflicht; ist eine tägliche/wöchentliche Backup-Toleranz akzeptabel, reichen einfachere Methoden. Das RPO gibt den Rahmen der Strategie vor.","choices":[{"label":"Nahezu null — ich muss zu jedem beliebigen Zeitpunkt zurückkehren können","next":"sol_physical_pitr"},{"label":"Tägliche/periodische Backups (einige Stunden Verlust) sind akzeptabel","next":"size"},{"label":"Erinnere mich zuerst an die allgemeine Best Practice (3-2-1 / Verifizierung)","next":"sol_321"}]},"size":{"kind":"step","title":"Welche Anforderungen gibt es an Datengröße und Portabilität?","detail":"Reichen periodische Backups, ist die nächste Frage Umfang und Zweck: Ist die Datenbank klein/mittelgroß und soll das Backup in eine andere Umgebung/Version portierbar sein, oder sind die Daten groß und dein Hauptanliegen ist, innerhalb von Minuten wieder zu laufen? Das entscheidet zwischen logischem Dump und Snapshot.","choices":[{"label":"Klein/mittel, portabel und versionsübergreifend kompatibel","next":"sol_logical"},{"label":"Große Daten, Wiederherstellung muss sehr schnell sein (niedrige RTO)","next":"sol_snapshot"}]},"sol_logical":{"kind":"solution","title":"Empfehlung: logischer Dump","detail":"Exportiert Daten auf logischer Ebene (Schemadefinitionen + Zeilen); die Ausgabe ist entweder SQL/Text (pg_dump im Plain-Format, mysqldump) oder ein werkzeugspezifischer Binär-Dump (pg_dump -Fc, Oracle Data Pump). Eine flexible und portable Methode für kleine und mittlere Datenbanken, Migrationen, Versionsupgrades und die Wiederherstellung ausgewählter Tabellen/Objekte.","steps":["Warum: Die Ausgabe ist portabel (und in Textform lesbar); geeignet für den Wechsel zwischen Versionen/Engines und für die Wiederherstellung einer einzelnen Tabelle/eines einzelnen Objekts.","Wann: Kleine/mittlere Daten, Versionsupgrades/Migrationen, Kopien für Entwicklungsumgebungen, Bedarf an logischer Wiederherstellung (auf Zeilenebene).","Beispiel: PostgreSQL pg_dump/pg_dumpall, MySQL mysqldump, Oracle Data Pump (expdp/impdp; erzeugt eine binäre .dmp, nutze impdp SQLFILE=, um die DDL als Text zu sehen).","Achtung: Da ein Restore die Daten neu verarbeitet, sind Backup und Restore bei großen Datenbanken sehr langsam (hohe RTO).","Achtung: Allein bietet er kein niedriges RPO; Point-in-Time-Recovery erfordert ein physisches Backup + Log-Archiv. Nutze für einen konsistenten Snapshot die passenden Konsistenzoptionen."],"related":["logical-dump","last-backup-status"]},"sol_physical_pitr":{"kind":"solution","title":"Empfehlung: physisches Backup + WAL/Archivlogs (PITR)","detail":"Du erstellst regelmäßig physische Basis-Backups und archivierst die Transaktionslogs (WAL/Redo/binlog) kontinuierlich. So kannst du Point-in-Time-Recovery zu jedem Zeitpunkt durchführen und den Datenverlust auf nahezu null senken.","steps":["Warum: Dank kontinuierlicher Log-Archivierung kannst du nach dem letzten Basis-Backup bis zu jeder Transaktion vorwärtsrollen und zum gewünschten Zeitpunkt zurückkehren (z. B. direkt vor einem fehlerhaften Befehl) — das niedrigste RPO.","Wann: Große und kritische Produktionsdatenbanken; Systeme, die ein niedriges RPO und eine vertretbare RTO brauchen.","Beispiel: PostgreSQL Base Backup + WAL-Archiv (pgBackRest/barman), Oracle RMAN + Archivlog, MySQL physisches Backup + binlog (xtrabackup).","Achtung: Einrichtung und Betrieb sind komplexer; überwache kontinuierlich, dass Archivlogs ohne Unterbrechung oder Lücken fließen — eine einzige Lücke unterbricht die PITR-Kette.","Achtung: Der Log-Archivspeicher wächst ständig; ohne Aufbewahrungsrichtlinie und Speicherplanung läuft die Disk voll."],"related":["last-backup-status","logical-dump"]},"sol_snapshot":{"kind":"solution","title":"Empfehlung: Snapshot","detail":"Erstellt einen Snapshot der Daten auf Blockebene in der Storage- oder Virtualisierungsschicht. Backup und Restore sind selbst bei sehr großen Datenbanken sehr schnell; es hängt aber von der Infrastruktur ab und erfordert sorgfältigen Einsatz.","steps":["Warum: Eine Kopie auf Blockebene entsteht fast sofort und Restores sind sehr schnell; das ergibt die niedrigste RTO für große Daten.","Wann: Terabytes an Daten, schnelle Wiederherstellung hat Priorität; wenn die Storage-/Cloud-Schicht Snapshots unterstützt.","Beispiel: SAN-/LVM-Snapshot, Cloud-Disk-Snapshot (EBS usw.), ZFS-/Dateisystem-Snapshot; konsistent mit der Datenbank-Engine ausgelöst.","Achtung: Ein Snapshot hängt vom Storage-System ab und liegt meist auf derselben Infrastruktur; allein erfüllt er 3-2-1 nicht — kopiere ihn offsite.","Achtung: Für Anwendungskonsistenz muss die Datenbank zum Snapshot-Zeitpunkt im passenden Modus sein (fsfreeze/Backup-Modus/Hot Backup); andernfalls erhältst du ein „crash-konsistentes“ Backup."],"related":["last-backup-status"]},"sol_321":{"kind":"solution","title":"Empfehlung: 3-2-1-Regel + Offsite + verpflichtende Verifizierung","detail":"Welche Methode du auch wählst, diese Prinzipien gehören über die Strategie. Die bloße Existenz eines Backups reicht nicht; es muss auf verschiedenen Medien, an einem anderen Ort liegen und regelmäßig getestet werden. Das ist ein Rahmen, der alle obigen Optionen ergänzt.","steps":["3-2-1: Halte mindestens 3 Kopien auf 2 verschiedenen Medien/Typen vor, davon 1 Kopie offsite.","Warum offsite: Feuer, Überschwemmung, Ransomware oder der Verlust einer Region können alle Kopien an einem Ort auf einmal vernichten; mindestens eine Kopie muss isoliert/entfernt sein.","Verifizierung ist Pflicht: Ein ungetestetes Backup ist kein Backup; führe regelmäßig echte Restore-Übungen durch und miss die Wiederherstellungszeit (RTO).","Unveränderlichkeit: Halte gegen Ransomware eine nicht lösch-/änderbare (immutable/WORM) oder Air-Gap-Kopie vor.","Achtung: Überwache Backup-Integrität (Checksumme) und Wiederherstellbarkeit automatisch; richte Alarme für fehlgeschlagene/veraltete Backups ein. Verschlüssele auch die Backups selbst."],"related":["last-backup-status"]}}},"solver/decide-index.json":{"id":"decide-index","title":"Entscheidung: Welcher Indextyp?","icon":"🧭","category":"Entscheidungshilfe","intro":"Der richtige Indextyp hängt vom Zugriffsmuster der Abfrage (Gleichheit, Bereich oder Text-/JSON-Suche), der Kardinalität der Spalte und dem Lese-/Schreibverhältnis ab. Der falsche Index verschwendet Platz und verlangsamt Schreibvorgänge. Dieser Baum geht von der Form der Abfrage aus, führt dich zwischen B-Tree, Hash, Bitmap, GIN/GiST, partiellem und Covering Index und erinnert daran, dass die Spaltenreihenfolge in einem zusammengesetzten Index kritisch ist.","start":"root","nodes":{"root":{"kind":"step","title":"Wie nutzt deine Abfrage diese Spalte?","detail":"Das Zugriffsmuster ist der entscheidendste Faktor bei der Indexwahl. Vergleichst du die Spalte nur mit '=', nutzt du Bereiche/Sortierung (>, <, BETWEEN, ORDER BY) oder durchsuchst du Volltext, ein JSON-Feld oder geometrische Daten/Arrays? Fang hier an; wähle keinen Indextyp, bevor der Bedarf klar ist.","choices":[{"label":"Gemischt Gleichheit und Bereich/Sortierung (=, >, <, BETWEEN, ORDER BY)","next":"cardinality"},{"label":"Nur Gleichheit (=), kein Bereich und keine Sortierung","next":"sol_hash"},{"label":"Volltext-, JSON/JSONB-Feld-, Array- oder geometrische Suche","next":"sol_gin_gist"},{"label":"Die Abfrage zielt immer auf dieselbe kleine Teilmenge von Zeilen (z. B. WHERE status='active')","next":"sol_partial"}]},"cardinality":{"kind":"step","title":"Wie hoch ist die Kardinalität der Spalte (Anzahl unterschiedlicher Werte)?","detail":"Werden Bereiche/Sortierung benötigt, ist B-Tree der Standard; bei Spalten mit sehr wenigen unterschiedlichen Werten (niedrige Kardinalität: Geschlecht, Status, Ja/Nein) in einem leseintensiven Data Warehouse kann jedoch ein Bitmap-Index besser sein. Hat die Spalte viele eindeutige Werte oder wenige sich wiederholende?","choices":[{"label":"Hohe Kardinalität (viele eindeutige Werte) oder OLTP-Workload","next":"coverage"},{"label":"Niedrige Kardinalität und leseintensiv / Data Warehouse (DWH)","next":"sol_bitmap"}]},"coverage":{"kind":"step","title":"Liest die Abfrage nur die Spalten im Index?","detail":"Das Zugriffsmuster passt zu einem B-Tree. Eine letzte Optimierung: Sind alle Spalten, die die Abfrage zurückgibt und filtert, im Index, kann die Engine allein aus dem Index antworten, ohne die Tabelle zu besuchen (Index-only / Covering Scan). Liest deine Abfrage wenige Spalten oder braucht sie den Großteil der Zeile?","choices":[{"label":"Ja, wenige Spalten; Antwort ohne Tabellenzugriff möglich","next":"sol_covering"},{"label":"Nein, Standardzugriff reicht","next":"sol_btree"}]},"sol_btree":{"kind":"solution","title":"Empfehlung: B-Tree (Standard)","detail":"Ein B-Tree ist eine balancierte Baumstruktur, die viele Zugriffsmuster unterstützt: Gleichheit, Bereich, Sortierung (ORDER BY), Präfixsuche mit LIKE 'abc%' und MIN/MAX. Er ist in den meisten relationalen Engines der Standardindextyp und im Zweifel der richtige Ausgangspunkt.","steps":["Warum: Eine einzige Struktur bedient Gleichheit wie Bereich/Sortierung effizient; es ist der universellste Index.","Wann: OLTP-Abfragen, WHERE + Bereich + ORDER BY zusammen, Primär-/Unique-Schlüssel.","Die Spalten-REIHENFOLGE ist in einem zusammengesetzten Index kritisch: Stelle die selektivste per Gleichheit genutzte Spalte an den Anfang und die Bereichsspalte ans Ende. Ein Index (a,b) hilft bei einem Filter auf 'a' oder 'a,b'; bei einem Filter nur auf 'b' hilft er in der Regel nicht (Leftmost-Prefix-Regel).","Beispiel: der Standard-B-Tree/B+Tree-Index in PostgreSQL/Oracle/MySQL(InnoDB)/MSSQL gleichermaßen.","Achtung: Jeder Index erhöht die Schreibkosten (INSERT/UPDATE/DELETE) und den Plattenplatz; behalte keine unnötigen oder doppelten Indizes."],"related":["slow-query"]},"sol_hash":{"kind":"solution","title":"Empfehlung: Hash-Index (nur Gleichheit)","detail":"Ein Hash-Index speichert den Hash des Spaltenwerts und ist nur für exakte Gleichheitssuchen ('=') sehr schnell. Er unterstützt keine Bereichs-, Sortier- oder Präfixsuchen, daher ist sein Einsatzbereich eng.","steps":["Warum: Er bietet für Gleichheitssuchen im Mittel Zugriff in konstanter Zeit (O(1)); bei sehr großen Gleichheits-Workloads, die in den Speicher passen, kann er vorteilhaft sein.","Wann: Die Spalte wird nur mit '=' genutzt und Bereich/Sortierung wird nie benötigt.","Engine-Unterschiede: PostgreSQL hat Hash-Indizes, in der Praxis reicht aber meist ein B-Tree und ist flexibler; MySQL(InnoDB) verwaltet Hash-Indizierung nur intern als Adaptive Hash Index, während die MEMORY-Engine Hash explizit nutzt; in Oracle tritt er im Kontext von Clustern/Hash-Clustern auf.","Achtung: Bereich, BETWEEN, ORDER BY und LIKE 'abc%' werden nicht unterstützt; wähle einen B-Tree, sobald solcher Bedarf entsteht.","Achtung: Da der Standard-B-Tree meist ausreicht, wechsle nicht ohne Messung zu Hash."],"related":["slow-query"]},"sol_bitmap":{"kind":"solution","title":"Empfehlung: Bitmap-Index (niedrige Kardinalität / DWH)","detail":"Ein Bitmap-Index hält für jeden unterschiedlichen Wert ein Bit-Array und kombiniert mehrere Bedingungen niedriger Kardinalität sehr schnell per bitweisem AND/OR. Er ist stark bei leseintensiven Workloads wie Data Warehousing und Reporting.","steps":["Warum: Er braucht bei Spalten mit wenigen unterschiedlichen Werten sehr wenig Platz und kombiniert mehrere Bedingungen (WHERE gender=.. AND region=..) effizient mit Bitmap-Operationen.","Wann: Spalten mit niedriger Kardinalität, Star-Schema-Abfragen, Data Warehouse/Reporting, leseintensive Workloads.","Engine-Unterschiede: Eine klassische Stärke von Oracle; PostgreSQL hat keinen persistenten Bitmap-Index, der Planer kann aber zur Laufzeit einen 'bitmap heap scan' erzeugen. MySQL/MSSQL haben keinen persistenten Bitmap-Index als solchen.","Achtung: Nicht auf häufig aktualisierten (OLTP-)Tabellen einsetzen; Bitmap-Sperren senken die Parallelität auf Zeilenebene stark.","Achtung: Bei Spalten mit hoher Kardinalität verschwindet der Vorteil; bevorzuge dort einen B-Tree."],"related":["slow-query"]},"sol_gin_gist":{"kind":"solution","title":"Empfehlung: GIN / GiST (Text, JSON, Array, Geometrie)","detail":"PostgreSQL hat spezialisierte Indexfamilien für Zugriffsarten, die ein Standard-B-Tree nicht abdeckt. GIN verweist auf viele Bestandteile innerhalb eines Werts (Wörter, JSONB-Schlüssel, Array-Elemente); GiST eignet sich für Abfragen wie geometrische/Bereichs-/Nearest-Neighbor-Suchen.","steps":["Warum: Für diese Datentypen können Abfragen wie „enthält/überlappt/nahe bei“ von einem B-Tree nicht effizient bedient werden; GIN/GiST beschleunigen sie.","Wann GIN: Volltextsuche (tsvector), JSONB-Feld-/Schlüsselsuchen (@>, ?), Array-Containment-Abfragen; leseintensiv, schreibarm.","Wann GiST: Geometrisch/raumbezogen (PostGIS), Range-Typen, Nearest-Neighbor-Abfragen (KNN).","Engine-Unterschiede: GIN/GiST sind PostgreSQL-spezifisch. FULLTEXT- und SPATIAL-Indizes (R-Tree) in MySQL, Oracle Text und Spatial in Oracle sowie der Full-Text Index in MSSQL erfüllen einen ähnlichen Zweck.","Achtung: GIN ist beim Schreiben teuer und langsam im Aufbau (mit fastupdate abmilderbar); miss die Kosten auf schreibintensiven Tabellen."],"related":["slow-query"]},"sol_partial":{"kind":"solution","title":"Empfehlung: partieller / gefilterter Index","detail":"Ein partieller Index indiziert statt der ganzen Tabelle nur die Zeilen, die eine Bedingung (WHERE ...) erfüllen. Zielen Abfragen immer auf dieselbe kleine Teilmenge, wird der Index viel kleiner und schneller, und die Schreibkosten sinken.","steps":["Warum: Da er nur die relevanten Zeilen indiziert, sinken Platz- und Wartungskosten; die häufig abgefragte „heiße“ Teilmenge wird deutlich schneller.","Wann: Stabile, enge Bedingungen wie WHERE status='active', deleted_at IS NULL, priority='high'.","Engine-Unterschiede: PostgreSQL unterstützt das direkt per 'partial index' (CREATE INDEX ... WHERE ...), MSSQL per 'filtered index'. In Oracle werden funktionsbasierte/bedingte Indexmuster als Entsprechung genutzt; MySQL(InnoDB) unterstützt es nicht direkt.","Achtung: Die WHERE-Bedingung des Index muss zur Bedingung der Abfrage passen; sonst kann der Planer den Index nicht nutzen.","Achtung: Wächst die Teilmenge, schrumpft der Vorteil; stelle sicher, dass die Bedingung stabil ist."],"related":["slow-query"]},"sol_covering":{"kind":"solution","title":"Empfehlung: Covering Index (INCLUDE)","detail":"Ein Covering Index enthält alle Spalten, die die Abfrage braucht, sodass die Engine allein aus dem Index antworten kann, ohne die Tabelle (Heap) überhaupt zu besuchen (Index-only Scan). Bei häufig ausgeführten Abfragen mit wenigen Ergebnisspalten bringt das eine deutliche Beschleunigung.","steps":["Warum: Der Tabellenzugriff (Heap Fetch) entfällt; Disk-I/O und Latenz sinken spürbar.","Wann: Häufig ausgeführte Abfragen mit klar definierten WHERE- + SELECT-Spalten und einer kleinen Ergebnismenge.","Engine-Unterschiede: MSSQL und PostgreSQL fügen Nicht-Schlüsselspalten mit 'INCLUDE' zum Index-Leaf hinzu; in MySQL(InnoDB) enthalten Sekundärindizes bereits den Primärschlüssel, Covering wird also mit einem zusammengesetzten Index erreicht; in Oracle erfolgt Index-only-Zugriff, wenn alle benötigten Spalten im Index sind.","Achtung: Zusätzliche/breite INCLUDE-Spalten blähen den Index auf und verlangsamen Schreibvorgänge; füge nur wirklich benötigte Spalten hinzu.","Achtung: In PostgreSQL erfordert ein Index-only Scan eine aktuelle Visibility Map; regelmäßiges VACUUM ist unerlässlich."],"related":["slow-query"]}}},"solver/decide-partition.json":{"id":"decide-partition","title":"Entscheidung: Welche Partitionierungsstrategie?","icon":"🧭","category":"Entscheidungshilfe","intro":"Partitionierung teilt eine große Tabelle unter einem logischen Namen in mehrere physische Teile auf, erleichtert das Entfernen alter Daten und lässt Abfragen nur die relevanten Teile lesen (Partition Pruning). Bei kleinen Tabellen fügt sie jedoch nur Komplexität hinzu. Dieser Baum hinterfragt zuerst, ob Partitionierung wirklich nötig ist, und führt dich dann je nach Zugriffs- und Verteilungsmuster zwischen Range, List, Hash und Composite.","start":"root","nodes":{"root":{"kind":"step","title":"Ist die Tabelle wirklich groß genug, um Partitionierung zu brauchen?","detail":"Partitionierung ist keine Performance-Magie, sondern ein Werkzeug für Skalierung und Wartung (Lebenszyklus). Ist die Tabelle nicht groß genug oder laufen Abfragen mit Indizes bereits gut, bringt Partitionierung nur Plankomplexität und Verwaltungsaufwand. Erreicht deine Tabelle zig Millionen+ Zeilen/Hunderte GB, oder ist sie noch klein/mittelgroß?","choices":[{"label":"Ja, sie ist sehr groß und/oder alte Daten müssen in großen Mengen gelöscht/archiviert werden","next":"access"},{"label":"Nein, kleine/mittlere Tabelle, oder ich bin nicht sicher","next":"sol_no_partition"}]},"access":{"kind":"step","title":"Wie greifst du auf die Daten zu und wie sollen sie aufgeteilt werden?","detail":"Der Partitionsschlüssel sollte die Spalte sein, nach der die meisten Abfragen filtern; nur dann greift Pruning. Welches Zugriffsmuster dominiert? Nach Datum/fortlaufenden Bereichen (letzte 30 Tage), nach bestimmten Kategorie-/Regionswerten, oder gibt es keinen klaren Filter und du willst die Last gleichmäßig verteilen?","choices":[{"label":"Fortlaufende Bereiche wie Datum/Sequenznummer (letzte X Tage, nach Monat)","next":"sol_range"},{"label":"Diskrete Wertemengen wie Kategorie/Region/Mandant","next":"sol_list"},{"label":"Kein klarer Bereich/keine Kategorie; Ziel ist gleichmäßige Lastverteilung","next":"sol_hash"},{"label":"Sowohl Datum als auch hohes Volumen; Aufteilung in zwei Dimensionen nötig","next":"sol_composite"}]},"sol_no_partition":{"kind":"solution","title":"Empfehlung: (noch) nicht partitionieren","detail":"Bei kleinen/mittleren Tabellen bringt Partitionierung keinen Nutzen; gut gewählte Indizes und normale Wartung reichen. Verfrühte Partitionierung verkompliziert Abfragepläne, verfehlt mit dem falschen Schlüssel das Pruning und erhöht den Betriebsaufwand.","steps":["Warum: Bei einer kleinen Tabelle ist ein Full Scan oder Indexzugriff bereits schnell; Partitionierung bringt nur zusätzliche Verwaltung und Plankomplexität.","Wann es so bleiben kann: Unter einigen Millionen Zeilen, kein Bedarf, alte Daten massenhaft zu entfernen, Abfragen laufen mit Indizes gut.","Stattdessen: Die richtigen Indizes, bei Bedarf eine Archivtabelle, regelmäßige Statistiken/VACUUM/ANALYZE.","Achtung: Richte Partitionierung nicht früh unter der Annahme „sie wird wachsen“ ein; stelle um, sobald das Wachstum nachgewiesen und der richtige Schlüssel klar ist.","Engine-Unterschiede: Ein allgemeines Prinzip für alle Engines; deklarative Partitionierung in PostgreSQL, MySQL PARTITION BY sowie Partitionierung in Oracle und MSSQL lassen sich problemlos später hinzufügen."],"related":[]},"sol_range":{"kind":"solution","title":"Empfehlung: Range-Partitionierung","detail":"Range-Partitionierung teilt Zeilen nach fortlaufenden Bereichen einer Spalte auf (z. B. Datumsbereiche: eine Partition pro Monat/Quartal). Sie ist die häufigste und wirkungsvollste Strategie für Zeitreihen und stetig wachsende Tabellen.","steps":["Warum: Filtern Abfragen nach Datum/Bereich, liest die Engine nur die relevanten Partitionen (Partition Pruning); alte Daten werden mit einem einzigen Befehl sofort entfernt (Partition DROP).","Wann: Zeitreihen, Logs, Bestell-/Transaktionshistorie; Abfragen der Art „letzte N Tage“ und zeitbasierte Archivierung.","Partitionsschlüssel: Wähle die Datums-/fortlaufende Spalte, die in der WHERE-Klausel der überwältigenden Mehrheit der Abfragen vorkommt; sonst funktioniert Pruning nicht.","Beispiel: PostgreSQL RANGE-Partitionierung, Oracle Range-/Interval-Partitionierung (automatische neue Partitionen), MySQL PARTITION BY RANGE (für Datumswerte meist RANGE COLUMNS), MSSQL Partition Function/Scheme.","Achtung: Automatisiere das Anlegen neuer Partitionen für künftige Bereiche (Oracle Interval oder ein geplanter Job); eine volllaufende 'default/maxvalue'-Partition wird zum Hotspot."],"related":[]},"sol_list":{"kind":"solution","title":"Empfehlung: List-Partitionierung","detail":"List-Partitionierung gruppiert Zeilen nach diskreten, bekannten Werten einer Spalte (z. B. region = 'TR','EU','US' oder Mandanten-ID). Gibt es natürliche, endliche Kategorien, macht sie Zugriff und Verwaltung übersichtlich.","steps":["Warum: Filtern Abfragen nach bestimmten Kategoriewerten, wird nur diese Partition gelesen; Wartung/Löschung pro Kategorie wird einfacher.","Wann: Diskrete und relativ stabile Wertemengen wie Region/Land, Produkttyp, Status, Mandantentrennung.","Partitionsschlüssel: Die Wertemenge sollte endlich und vorhersehbar sein; kommen häufig neue Werte hinzu, erwäge Hash statt List.","Beispiel: PostgreSQL LIST-Partitionierung, Oracle List-Partitionierung, MySQL PARTITION BY LIST, MSSQL Partition Scheme mit passenden Grenzen.","Achtung: Halte eine 'default'-Partition für undefinierte/neue Werte vor; sonst lässt sich die Zeile nicht einfügen und du erhältst einen Fehler."],"related":[]},"sol_hash":{"kind":"solution","title":"Empfehlung: Hash-Partitionierung (gleichmäßige Verteilung)","detail":"Hash-Partitionierung verteilt Zeilen anhand des Hashs der Schlüsselspalte relativ gleichmäßig auf eine festgelegte Anzahl von Partitionen. Sie wird genutzt, um Hotspots zu vermeiden und Last auszugleichen, wenn es keinen natürlichen Bereich/keine Kategorie gibt.","steps":["Warum: Zeilen werden gleichmäßig verteilt; der Schreib-Hotspot, der sich auf einer Partition oder am Ende des Primärschlüssels staut, wird verringert.","Wann: Hohes Schreibvolumen, kein natürlicher Bereich/keine Kategorie, Ziel ist der Ausgleich von I/O und Größe (z. B. eine id mit hoher Kardinalität).","Partitionsschlüssel: Wähle eine gleichmäßig verteilte Spalte mit hoher Kardinalität (meist den Primär-/Unique-Schlüssel).","Beispiel: PostgreSQL HASH-Partitionierung, Oracle Hash-Partitionierung, MySQL PARTITION BY HASH/KEY.","Achtung: Für Bereichsabfragen gibt es kein Pruning (Hashing zerstört die Reihenfolge); Abfragen wie „letzte N Tage“ scannen alle Partitionen. Eine spätere Änderung der Partitionsanzahl erfordert eine Neuverteilung."],"related":[]},"sol_composite":{"kind":"solution","title":"Empfehlung: Composite-Partitionierung (mehrstufig)","detail":"Composite-Partitionierung kombiniert zwei Strategien: Sie teilt zuerst auf oberster Ebene auf und unterteilt dann jede Partition in Subpartitionen (z. B. Range-by-Hash oder Range-by-List). Sie wird genutzt, wenn zeitbasierter Lebenszyklus und gleichmäßige Verteilung/Kategorietrennung gleichzeitig nötig sind.","steps":["Warum: Reicht eine Strategie nicht, erfüllt sie beide Anforderungen zusammen; z. B. nach Monat aufteilen (Archivierung) und innerhalb des Monats per Hash verteilen (Hotspot-Vermeidung).","Wann: Sehr große Zeitreihen + viele parallele Schreibvorgänge, oder ein doppeltes Zugriffsmuster aus Datum + Region/Mandant.","Beispiel: Oracle Composite (Range-Hash, Range-List, List-Hash usw.); in PostgreSQL manuell durch erneutes Partitionieren der Partitionen (Partition of Partition); MySQL-Subpartitionen (RANGE/LIST + HASH/KEY).","Achtung: Das ist die Option mit der höchsten Verwaltungs- und Schemakomplexität; wähle sie nur, wenn beide Dimensionen nachweislich nötig sind.","Achtung: Eine falsche Wahl des Ober-/Unterschlüssels verliert das Pruning; verifiziere, dass beide Ebenen zum Abfragemuster passen."],"related":[]}}},"solver/decide-pooling.json":{"id":"decide-pooling","title":"Entscheidung: Wie richte ich Connection Pooling ein?","icon":"🧭","category":"Entscheidungshilfe","intro":"Jede Datenbankverbindung kostet Speicher und Scheduling; eine unkontrolliert wachsende Verbindungsanzahl verlangsamt den Server oder legt ihn sogar lahm. Ein Connection Pool löst das, indem er wenige persistente Verbindungen wiederverwendet. Dieser Baum hilft bei der Entscheidung, wo der Pool sitzt, welcher Modus (Session/Transaction/Statement) gilt und welche Größe richtig ist, und betont, warum in Serverless-Umgebungen ein externer Pooler Pflicht ist.","start":"root","nodes":{"root":{"kind":"step","title":"Wie verbindet sich deine Anwendungsarchitektur?","detail":"Die Pooling-Entscheidung beginnt bei der Architektur. Ist es ein einzelner langlebiger Anwendungsprozess (klassischer Server), sind es viele unabhängige Prozesse/Instanzen (Microservices, viele Pods) oder Serverless-/Lambda-Funktionen, die mit jedem Request entstehen und wieder verschwinden? Jede Variante erfordert eine andere Platzierung des Pools.","choices":[{"label":"Ein/wenige langlebige Anwendungsprozesse","next":"sol_app_pool"},{"label":"Viele Prozesse/Instanzen/Pods verbinden sich mit derselben DB","next":"middleware"},{"label":"Serverless / Lambda / kurzlebige Funktionen","next":"sol_serverless"},{"label":"Zeig mir zuerst, wie ich die richtige Poolgröße wähle","next":"sol_size"}]},"middleware":{"kind":"step","title":"Wie wähle ich den Modus des Middleware-Poolers?","detail":"Bei vielen Clients hält ein Middleware-Pooler (PgBouncer, ProxySQL) die Verbindungsanzahl auf der DB niedrig. Der Pool-Modus hängt aber von den Funktionen ab, die die Anwendung nutzt: Verlässt du dich auf sessiongebundenen Zustand (Prepared Statements, temporäre Tabellen, SET, Advisory Locks, Funktionen auf Session-Ebene), oder wird alles innerhalb einer einzigen Transaktion abgeschlossen?","choices":[{"label":"Ich hänge von Session-Zustand ab (SET, temporäre Tabellen, Session Prepared Statements)","next":"sol_session_mode"},{"label":"Transaktionen sind kurz und unabhängig; ich halte keinen Session-Zustand","next":"sol_txn_mode"}]},"sol_app_pool":{"kind":"solution","title":"Empfehlung: Connection Pool in der Anwendung","detail":"Bei einem/wenigen langlebigen Prozessen deckt eine Pool-Bibliothek in der Anwendung (HikariCP, der eingebaute Pool von Gos database/sql, .NET Connection Pooling) den meisten Bedarf ab. Verbindungen werden einmal geöffnet und immer wieder verwendet.","steps":["Warum: Die Kosten für das Öffnen/Schließen von Verbindungen entfallen; eine begrenzte und vorhersehbare Verbindungsanzahl schützt die DB.","Wann: Ein klassischer Monolith oder wenige Anwendungsinstanzen; der gesamte Verbindungsbedarf liegt unter der DB-Kapazität.","Beispiel: Java HikariCP, .NET SqlConnection/Npgsql-Pooling, Node pg-pool, Python SQLAlchemy-Pool.","Zur Dimensionierung: Bestimme die Poolgröße anhand von Kernen/Disks (nächste Entscheidung); „je mehr, desto besser“ gilt nicht.","Achtung: Jede Anwendungsinstanz öffnet ihren eigenen Pool; Instanzanzahl × Poolgröße darf max_connections der DB nicht überschreiten. Ist das der Fall, wechsle zu einem Middleware-Pooler."],"related":["connection-storm"]},"sol_size":{"kind":"solution","title":"Empfehlung: Pool richtig dimensionieren","detail":"Der Pool sollte weder zu klein (Requests warten) noch zu groß sein. Zu viele Verbindungen erzeugen CPU-Kontextwechsel und Lock-Konkurrenz und senken den Durchsatz. Die Größe ist keine Zahl, die man beliebig erhöht, sondern eine kleine Zahl auf Basis von Kernen und I/O.","steps":["Startformel: Ein gängiger Ausgangspunkt ist Poolgröße ≈ (Kernanzahl × 2) + effektive Spindelanzahl; bei SSD/vielen Kernen ist etwa „Kerne × 2-4“ ein guter Start.","Warum klein halten: Die echte Parallelität der DB ist durch die Kernanzahl begrenzt; zusätzliche Verbindungen stehen nur Schlange und bremsen alles durch Kontextwechsel/Lock-Konkurrenz.","Regel: Bei Hunderten paralleler Clients ist die Lösung nicht, den Pool zu vergrößern, sondern einen kleinen Pool schnell umzuschlagen (kurze Transaktionen + Warteschlange).","Gesamtlimit: (Anzahl Anwendungsinstanzen × Poolgröße) < max_connections der DB; lass Reserve für Superuser- und Wartungsverbindungen.","Achtung: Starte mit der Formel, aber miss immer; justiere anhand der Metriken Wartezeit, aktive Verbindungen und Queue-Tiefe."],"related":["connection-storm"]},"sol_session_mode":{"kind":"solution","title":"Empfehlung: Session-Pool-Modus","detail":"Im Session-Modus ist eine Client-Verbindung bis zum Ende ihrer Session an eine Backend-Serververbindung gebunden. Zustand auf Session-Ebene (SET, temporäre Tabellen, Advisory Locks, Session Prepared Statements) bleibt erhalten, die Verbindungsteilung ist aber minimal.","steps":["Warum: Es ist der einzige sichere Modus für Anwendungen, die Zustand über die ganze Session brauchen; das Verhalten entspricht einer direkten Verbindung zur DB.","Wann: Anwendungen mit SET-Session-Variablen, temporären Tabellen, LISTEN/NOTIFY, Prepared Statements auf Session-Ebene oder Advisory Locks.","Beispiel: PgBouncer pool_mode=session; in ProxySQL sessionsensitiven Traffic separat routen.","Achtung: Die Verbindungsteilung ist gering, daher schrumpft der Nutzen bei vielen Clients; strebe wo möglich an, die Anwendung frei von Session-Zustand zu machen und in den Transaction-Modus zu wechseln.","Engine-Unterschiede: PgBouncer für PostgreSQL; auf MySQL-Seite handhabt ProxySQL ähnliches Verhalten anders, und Session-Variablen erfordern Sorgfalt."],"related":["connection-storm"]},"sol_txn_mode":{"kind":"solution","title":"Empfehlung: Transaction-Pool-Modus","detail":"Im Transaction-Modus wird dem Client eine Serververbindung nur für die Dauer einer Transaktion geliehen und kehrt danach in den Pool zurück. So können Tausende Clients mit sehr wenigen echten Verbindungen auf der DB arbeiten; es ist der gängigste Modus für hohe Skalierung.","steps":["Warum: Da Verbindungen pro Transaktion übergeben werden, ist die Teilung am höchsten; du bedienst viele Clients mit wenigen DB-Verbindungen.","Wann: Kurze, unabhängige Transaktionen; Microservice-/Multi-Pod-Architekturen, die keinen Session-Zustand halten (oder so umgebaut werden).","Beispiel: PgBouncer pool_mode=transaction (das gängigste Setup), das standardmäßige Multiplexing-Verhalten von ProxySQL.","Achtung: Sessionübergreifende Funktionen brechen: Prepared Statements auf Session-Ebene, temporäre Tabellen, SET und Advisory Locks sind nicht sicher. Verifiziere bei benannten Prepared Statements auf Protokollebene die Kompatibilität von Anwendung/Pooler.","Statement-Modus: Der noch aggressivere (Statement-)Modus übergibt bei jeder Anweisung; er kommt nur für sehr spezielle, transaktionslose Read-only-Szenarien infrage und ist sehr restriktiv."],"related":["connection-storm"]},"sol_serverless":{"kind":"solution","title":"Empfehlung: Für Serverless ist ein externer Pooler Pflicht","detail":"Serverless-/Lambda-Funktionen skalieren schnell und jede Instanz öffnet ihre eigene Verbindung; ein Pool in der Anwendung hilft hier nicht, weil Instanzen kurzlebig sind und nichts teilen. Entstehen plötzlich Hunderte/Tausende Instanzen, wird das Verbindungslimit der DB gesprengt. Die Lösung ist ein externer Connection Pool vor der DB.","steps":["Warum: In kurzlebigen, nicht geteilten Funktionsinstanzen kann ein Pool in der Anwendung nicht bestehen bleiben; beim Scale-out entsteht ein Verbindungssturm.","Wann: Umgebungen, die pro Request eine Instanz starten, etwa AWS Lambda, Cloud Functions, Azure Functions.","Beispiel: PgBouncer/ProxySQL als eigene Schicht oder ein verwalteter Pooler (z. B. AWS RDS Proxy, Data API/externe Pooler-Dienste für PostgreSQL).","Modus: Meist wird der Transaction-Pool-Modus bevorzugt; mach den Funktionscode frei von Session-Zustand.","Achtung: Setze außerdem ein Parallelitätslimit pro Funktion (Reserved Concurrency); dimensioniere Pooler und max_connections der DB gemeinsam, sonst wird der Pooler selbst zum Engpass."],"related":["connection-storm"]}}},"solver/decide-cache.json":{"id":"decide-cache","title":"Entscheidung: welche Caching-Strategie?","icon":"🧭","category":"Entscheidungshilfe","intro":"Ein Cache hält häufig gelesene Daten in einer schnellen Schicht vor der Datenbank (z. B. Redis) und senkt so Latenz und DB-Last. Aber nicht alle Daten eignen sich zum Cachen, und falsche Invalidierung birgt das Risiko veralteter/falscher Daten. Dieser Baum prüft zuerst, ob du wirklich einen Cache brauchst, hilft dann je nach Lese-/Schreibmuster bei der Wahl zwischen Cache-Aside, Read-Through, Write-Through und Write-Behind und behandelt die Vermeidung von Stampedes.","start":"root","nodes":{"root":{"kind":"step","title":"Wie sieht deine Workload aus: leseintensiv oder schreibintensiv?","detail":"Ein Cache lohnt sich am meisten, wenn dieselben Daten oft gelesen werden (leseintensiv) und sich selten ändern. Bestimme zuerst das Gesamtmuster: Werden die Daten viel gelesen und selten geändert, werden sie ständig aktualisiert, oder bist du unsicher, ob sich ein Cache lohnt?","choices":[{"label":"Sehr leseintensiv, Daten ändern sich relativ selten","next":"read_pattern"},{"label":"Auch viele Schreibvorgänge; geschriebene Daten werden sofort/oft gelesen","next":"write_pattern"},{"label":"Nicht sicher; brauche ich wirklich einen Cache?","next":"sol_no_cache"}]},"read_pattern":{"kind":"step","title":"Wer soll für das Befüllen des Cache zuständig sein?","detail":"In einem leseintensiven Szenario ist die zentrale Wahl, wer den Cache befüllt. Soll deine Anwendung den Cache explizit verwalten und selbst auf die DB zurückgreifen (hohe Flexibilität), oder soll die Cache-Schicht bei einem Miss die Daten aus der zugrunde liegenden DB holen und transparent zurückgeben?","choices":[{"label":"Die Anwendung soll ihn explizit verwalten; ich will Flexibilität und Kontrolle","next":"sol_cache_aside"},{"label":"Die Cache-Schicht soll bei einem Miss aus der DB laden (transparent)","next":"sol_read_through"}]},"write_pattern":{"kind":"step","title":"Hat beim Schreiben Konsistenz oder Schreibgeschwindigkeit Vorrang?","detail":"Bei vielen Schreibvorgängen musst du entscheiden, wie konsistent geschriebene Daten zwischen Cache und DB sein sollen. Sollen Cache und DB bei jedem Schreiben gemeinsam aktualisiert werden, um Konsistenz zu garantieren (etwas langsameres Schreiben), oder sollen Schreibvorgänge zuerst in den Cache gehen und asynchron im Hintergrund auf die DB angewendet werden (sehr schnelles Schreiben, aber Verlustrisiko)?","choices":[{"label":"Konsistenz zählt; Lesevorgänge müssen immer aktuell sein","next":"sol_write_through"},{"label":"Schreibgeschwindigkeit ist kritisch; leicht verzögerte Konsistenz und Verlustrisiko sind akzeptabel","next":"sol_write_behind"}]},"sol_no_cache":{"kind":"solution","title":"Empfehlung: vorerst keinen Cache hinzufügen","detail":"Ein Cache ist nicht kostenlos: Er bringt ein Konsistenzproblem (veraltete Daten), zusätzliche Infrastruktur und Invalidierungskomplexität mit. Ist die Abfrage bereits schnell oder ändern sich die Daten bei jedem Request, bringt ein Cache keinen Nutzen, nur zusätzliche Fehlerquellen.","steps":["Warum: Ein Cache bringt Konsistenz-, Invalidierungs- und Betriebsaufwand. Ohne Nutzen ist er ein Nettoverlust.","Wann er nicht nötig ist: Die Abfrage ist dank Index bereits schnell, die Daten unterscheiden sich bei jedem Lesen/sind benutzerspezifisch und werden nicht wiederverwendet, oder Schreibvorgänge überwiegen die Lesevorgänge deutlich.","Stattdessen: zuerst Index-/Abfrageoptimierung, eine Materialized View oder der eigene Buffer-/Plan-Cache der DB.","Achtung: Füge keinen Cache „für alle Fälle“ hinzu; nicht ohne gemessenen Leseengpass und wiederverwendete Daten.","Als Nächstes: Sobald ein echter Lese-Hotspot nachgewiesen ist, beginne mit Cache-Aside."],"related":["slow-query","high-cpu"]},"sol_cache_aside":{"kind":"solution","title":"Empfehlung: Cache-Aside (Lazy Loading)","detail":"Bei Cache-Aside prüft die Anwendung zuerst den Cache; fehlen die Daten (Miss), liest sie aus der DB, schreibt sie in den Cache und gibt sie zurück. Das ist das gängigste und flexibelste Muster; der Cache füllt sich nur mit tatsächlich angefragten Daten.","steps":["Warum: einfach, flexibel und robust; selbst wenn der Cache ausfällt, läuft die Anwendung weiter, indem sie auf die DB zurückgreift. Nur angefragte Daten werden gecacht.","Wann: allgemeine Lesebeschleunigung; viele Lese-, moderate Schreibvorgänge; der klassische Einsatz mit Redis/Memcached.","Invalidierung: Aktualisiere oder lösche beim Schreiben den zugehörigen Schlüssel (Write-Invalidate); begrenze die Veraltung, indem du jedem Schlüssel eine sinnvolle TTL gibst.","Beispiel: Redis GET → falls nicht vorhanden, DB-Abfrage → SET key value EX <ttl>.","Achtung: Der erste Request ist immer ein Miss (Kaltstart). Fragen viele Clients denselben Schlüssel gleichzeitig an, kann eine Cache Stampede auftreten — siehe Stampede-Vermeidung (nächster Punkt).","Stampede: Läuft ein beliebter Schlüssel ab, treffen Tausende Requests gleichzeitig die DB; verhindere das mit Lock/Single-Flight, frühzeitiger Aktualisierung oder TTLs mit Jitter."],"related":["high-cpu","slow-query"]},"sol_read_through":{"kind":"solution","title":"Empfehlung: Read-Through-Cache","detail":"Bei Read-Through spricht die Anwendung nur mit dem Cache; bei einem Miss holt die Cache-Schicht selbst die Daten aus der DB und befüllt sich (über einen Provider/Loader). Der Anwendungscode wird einfacher und die Befüllungslogik liegt an einer Stelle.","steps":["Warum: Die Befüllungslogik ist in der Cache-Schicht zentralisiert; der Anwendungscode muss den Miss nicht mehr überall behandeln.","Wann: leseintensive Workloads, bei denen du eine einheitliche Cache-Zugriffsschicht (Bibliothek/Dienst) nutzen kannst.","Beispiel: Eine Cache-Bibliothek/ein Client (z. B. ein lokaler Cache mit definiertem Loader oder eine Zugriffsschicht vor Redis) lädt bei einem Miss aus der DB.","Invalidierung: TTLs und Invalidierung beim Schreiben sind weiterhin nötig; Read-Through allein garantiert keine Aktualität.","Achtung: Sehr ähnlich zu Cache-Aside; der Unterschied ist, „wer befüllt“. Das Stampede-Risiko besteht auch hier; wende dieselben Gegenmaßnahmen an.","Engine-Hinweis: Redis macht Read-Through nicht von selbst; die Anwendungs-/Bibliotheksschicht stellt dieses Muster bereit."],"related":["slow-query"]},"sol_write_through":{"kind":"solution","title":"Empfehlung: Write-Through-Cache","detail":"Bei Write-Through geht jeder Schreibvorgang (synchron) sowohl in den Cache als auch in die DB; beide werden gemeinsam aktualisiert. Der Cache hinkt der DB daher nie hinterher, und Lesevorgänge sehen immer aktuelle Daten.","steps":["Warum: Cache und DB bleiben konsistent; werden geschriebene Daten sofort gelesen, besteht kein Risiko veralteter Lesevorgänge.","Wann: Szenarien, in denen geschriebene Daten sofort und oft gelesen werden und starke Lesekonsistenz gefordert ist.","Beispiel: Beim Schreiben aktualisiert die Anwendung zuerst die DB und setzt dann sofort den Cache-Schlüssel auf den neuen Wert (oder löscht ihn).","Achtung: Jeder Schreibvorgang geht an zwei Ziele, daher steigt die Schreiblatenz; auch Daten, die nie gelesen werden, landen eventuell unnötig im Cache. Die beiden Schreibvorgänge sind nicht atomar (einer gelingt, der andere scheitert), was Inkonsistenz verursachen kann — lösche bei einem Fehler den Cache-Eintrag.","Dual-Write-Falle: Statt DB und Cache getrennt zu schreiben, ist der Ansatz „DB schreiben, Cache löschen (invalidieren)“ meist sicherer."],"related":[]},"sol_write_behind":{"kind":"solution","title":"Empfehlung: Write-Behind-Cache (Write-Back)","detail":"Bei Write-Behind geht der Schreibvorgang zuerst in den Cache und kehrt sofort zum Benutzer zurück; das Schreiben in die DB erfolgt im Hintergrund asynchron (gebündelt/verzögert). Die Schreibgeschwindigkeit ist am höchsten, es bestehen aber Risiken für Dauerhaftigkeit und Konsistenz.","steps":["Warum: Die Schreiblatenz sinkt stark, und Schreibvorgänge lassen sich bündeln und effizient in die DB übertragen; die DB wird bei schreibintensiver Last entlastet.","Wann: sehr hohe Schreibvolumen wie Zähler, Metriken oder Aktivitätsfeeds, bei denen leicht verzögerte Konsistenz akzeptabel ist.","Beispiel: In Redis schreiben → eine Queue/ein Hintergrund-Worker schreibt in Intervallen gebündelt in die DB.","Achtung: Stürzt der Cache-Knoten vor dem Schreiben in die DB ab, gehen die Daten VERLOREN; nutze es nicht für kritische Daten, die Dauerhaftigkeit erfordern, oder sichere es mit einer dauerhaften Queue/AOF ab.","Achtung: Die DB bleibt eine Zeit lang veraltet; andere Systeme, die die DB direkt lesen, sehen alte Daten. Akzeptiere die Konsistenzverzögerung und das Verlustfenster bewusst."],"related":[]}}},"solver/decide-datatype.json":{"id":"decide-datatype","title":"Entscheidung: den richtigen Datentyp wählen","icon":"🧭","category":"Entscheidungshilfe","intro":"Die Wahl des Datentyps gehört zu den Entscheidungen, deren spätere Korrektur am teuersten ist: Der falsche Typ erzeugt falsche Ergebnisse (z. B. Float für Geld), verschwendeten Platz und langsame Abfragen. Dieser Baum führt dich zum richtigen Typ für numerische, Text-, Datums-/Zeit-, Schlüssel- (PK), semistrukturierte (JSON) und Kategoriefelder (ENUM) und weist auf klassische Fallstricke wie Geld und Zeichensätze hin.","start":"root","nodes":{"root":{"kind":"step","title":"Welche Art von Daten speicherst du?","detail":"Der richtige Typ ergibt sich aus der Art der Daten. In welche Kategorie fällt das Feld: eine Zahl, Text, ein Datum/eine Uhrzeit, der Primärschlüssel der Tabelle, flexible/semistrukturierte Daten (JSON) oder eine begrenzte Menge von Kategorien?","choices":[{"label":"Numerischer Wert (Menge, Geld, Messwert, Zähler)","next":"numeric"},{"label":"Text (Name, Beschreibung, Code)","next":"sol_text"},{"label":"Datum und/oder Uhrzeit","next":"sol_datetime"},{"label":"Ich entwerfe einen Primärschlüssel (PK)","next":"sol_pk"},{"label":"Flexible/verschachtelte/variable Felder (semistrukturierte Daten)","next":"sol_json"},{"label":"Eine begrenzte, bekannte Menge von Kategorien (Status, Typ, Priorität)","next":"sol_enum"}]},"numeric":{"kind":"step","title":"Ist die Zahl ganzzahlig oder dezimal — und ist es Geld?","detail":"Bei Zahlen ist die wichtigste Unterscheidung die Exaktheit. Ist der Wert ganzzahlig (Menge, ID, Zähler) oder dezimal? Wenn dezimal: Ist es GELD/ein Finanzbetrag? Binäre Gleitkommazahlen (FLOAT/DOUBLE) werden nie für Geld oder überall dort verwendet, wo Exaktheit gefordert ist, weil sie Rundungsfehler erzeugen.","choices":[{"label":"Ganzzahl (Menge, Zähler, id)","next":"sol_integer"},{"label":"Geld oder exakte Dezimalzahl (Preis, Saldo, Satz)","next":"sol_decimal"}]},"sol_integer":{"kind":"solution","title":"Empfehlung: Ganzzahl (SMALLINT / INT / BIGINT)","detail":"Wähle bei Ganzzahlen den kleinsten Typ, der den benötigten Bereich abdeckt. Ein unnötig großer Typ verschwendet Platz und Indexgröße; bei Feldern, die wachsen können, wie Zählern/PKs, ist ausreichende Breite aber unerlässlich, um Überläufe zu verhindern.","steps":["Warum: Die richtige Breite optimiert sowohl den Speicher als auch den Index-/Speicherbedarf; Arithmetik ist schnell.","Wann SMALLINT/INT: Für einen begrenzten Bereich (Alter, Menge, kleiner Zähler) reicht SMALLINT/INT.","Wann BIGINT: Wähle BIGINT für stetig steigende IDs/Zähler und PKs, deren Zeilenzahl Milliarden erreichen kann — ein INT-Überlauf ist eine ernste Ursache für Produktionsausfälle.","Engine-Unterschiede: In MySQL verdoppelt INT UNSIGNED den Bereich; PostgreSQL hat kein UNSIGNED, wechsle zu BIGINT. Oracle NUMBER(p), MSSQL INT/BIGINT.","Achtung: Setze nicht überall BIGINT ein, „falls es wächst“; für einen steigenden PK ist BIGINT von Anfang an aber eine günstige Versicherung."],"related":[]},"sol_decimal":{"kind":"solution","title":"Empfehlung: NUMERIC / DECIMAL — NIEMALS FLOAT für Geld","detail":"Nutze für Geld und exakte Dezimalzahlen NUMERIC/DECIMAL mit Festkomma (exakt). FLOAT/DOUBLE sind binäre Gleitkommazahlen und können Werte wie 0.1 nicht exakt speichern; Addition/Multiplikation erzeugt Cent-Abweichungen, falsche Salden und Audit-Probleme.","steps":["Warum: NUMERIC/DECIMAL speichern Werte exakt dezimal; Geldarithmetik liefert korrekte Ergebnisse ohne Rundungsfehler.","Wann: Jedes Feld, das Exaktheit erfordert, etwa Preis, Saldo, Steuer, Satz oder Zins. Gib eine passende Skala an: DECIMAL(precision, scale), z. B. DECIMAL(19,4) für einen Betrag.","Alternative: Manche Systeme speichern Geld in der kleinsten Einheit (Cent) als BIGINT; auch das ergibt volle Exaktheit.","Engine-Unterschiede: PostgreSQL NUMERIC, MySQL DECIMAL, Oracle NUMBER(p,s), MSSQL DECIMAL/MONEY (beachte die Einschränkungen von MONEY).","Achtung: FLOAT/DOUBLE nur für wissenschaftliche/gemessene Näherungswerte und Fälle, in denen Performance wichtiger als Exaktheit ist — niemals für Geld."],"related":[]},"sol_text":{"kind":"solution","title":"Empfehlung: Text — VARCHAR vs. TEXT vs. CHAR","detail":"Für Text wird VARCHAR/TEXT bei variabler Länge und CHAR bei wirklich fester Länge verwendet. In modernen Engines ist der Speicherunterschied zwischen VARCHAR und TEXT meist vernachlässigbar; die eigentliche Entscheidung ist, ob ein semantisches Limit (Längenbeschränkung) gesetzt werden soll.","steps":["VARCHAR(n): Ist die Länge variabel und gibt es eine sinnvolle Obergrenze (z. B. E-Mail, Name), nutze VARCHAR(n) — das Limit ist ein Werkzeug zur Datenvalidierung.","TEXT: Für langen Freitext unvorhersehbarer Länge (Beschreibung, Inhalt) nutze TEXT.","CHAR(n): Nur für wirklich feste Längen verwenden (z. B. Ländercode 'TR', immer 2 Zeichen); bei kurzem Text füllt CHAR mit Leerzeichen auf und ist meist unnötig.","Engine-Unterschiede: In PostgreSQL verhalten sich VARCHAR und TEXT gleich, das Limit ist nur eine Constraint. In MySQL wird VARCHAR in der Zeile gespeichert, TEXT ggf. separat, und die Indizierung hat ein Präfixlimit. In Oracle VARCHAR2 verwenden.","Zeichensatz/Collation: Für Emoji und volles Unicode in MySQL immer utf8mb4 verwenden (das alte 'utf8' hat 3 Bytes und zerstört Emoji); wähle die Collation (z. B. utf8mb4_0900_ai_ci) nach Sortier-/Vergleichsbedarf. In PostgreSQL typischerweise eine UTF-8-Datenbank + passende Collation."],"related":[]},"sol_datetime":{"kind":"solution","title":"Empfehlung: Datum/Zeit — TIMESTAMP mit Zeitzone","detail":"Speicherst du einen Zeitpunkt (Ereigniszeit), wähle einen zeitzonenbewussten Typ. Speichern ohne Zeitzone führt über Regionen/Sommerzeitwechsel hinweg zu falschen Berechnungen und Vergleichen.","steps":["Warum: Ein zeitzonenbewusster Typ normalisiert den Zeitpunkt auf UTC und sorgt für konsistente Vergleiche und Umrechnungen; unerlässlich für globale Systeme.","Wann TIMESTAMPTZ: Für Ereignis-/Transaktionszeitpunkte in PostgreSQL TIMESTAMP WITH TIME ZONE (timestamptz) verwenden; UTC speichern und zur Anzeige in die Zeitzone des Benutzers umrechnen.","Nur Datum oder Uhrzeit: DATE für einen Kalendertag, TIME für eine Uhrzeit ohne Zeitzonenbezug; INTERVAL für Dauern (in Engines, die es unterstützen).","Engine-Unterschiede: PostgreSQL timestamptz (speichert tatsächlich UTC). In MySQL wird TIMESTAMP gemäß Zeitzone umgerechnet, hat aber einen begrenzten Bereich (2038); DATETIME wird nicht umgerechnet — setze in der Anwendung die Disziplin „in UTC speichern“ durch. Oracle TIMESTAMP WITH TIME ZONE, MSSQL DATETIMEOFFSET.","Achtung: Zeit als Text (VARCHAR) oder als Epoch-INT zu speichern, zerstört Sortierung, Bereichs- und Zeitzonenoperationen; nutze einen echten Datums-/Zeittyp."],"related":[]},"sol_pk":{"kind":"solution","title":"Empfehlung: Primärschlüssel — BIGINT vs. UUID","detail":"Für einen PK gibt es zwei Hauptoptionen: eine steigende Zahl (BIGINT Identity/Sequence) oder eine UUID. Ein numerischer PK ist klein und indexfreundlich, aber vorhersehbar und erfordert zentrale Erzeugung; eine UUID lässt sich verteilt erzeugen, ist aber größer und ihre zufällige Reihenfolge kann dem Index schaden.","steps":["BIGINT (steigend): der kleinste, schnellste Index; sequenzielle Inserts sind ideal für einen B-Tree. Die Standardwahl bei einer einzelnen DB/zentraler Erzeugung; BIGINT statt INT von Anfang an schützt vor Überlauf.","UUID: Lässt sich auf Clients/in verteilten Systemen kollisionsfrei erzeugen und verrät keine Sequenznummer, wenn die ID offengelegt wird. Sie belegt aber 16 Bytes (2×BIGINT).","Die UUID-Reihenfolge zählt: Vollständig zufällige UUIDs (v4) sind nicht sequenziell und verursachen daher verstreute Inserts in den Index (Page Splits, niedrige Cache-Trefferquote); zeitlich geordnete UUIDs (v7/ULID) beseitigen das weitgehend.","Engine-Unterschiede: PostgreSQL hat einen nativen uuid-Typ; in MySQL die UUID als BINARY(16) speichern (statt als Text CHAR(36)) und möglichst geordnet erzeugen. MSSQL uniqueidentifier + NEWSEQUENTIALID, Oracle RAW(16).","Achtung: Eine UUID als Text VARCHAR/CHAR(36) zu speichern, verdoppelt bis verdreifacht Platz und Indexgröße; speichere sie binär."],"related":[]},"sol_json":{"kind":"solution","title":"Empfehlung: JSON / JSONB — maßvoll einsetzen","detail":"Der JSON-Typ ist stark für Felder mit flexiblem Schema, verschachtelte oder dünn besetzte Felder; die relationale Disziplin (Constraints, Indizes, Typsicherheit) kann aber verloren gehen. Regel: Feste und häufig abgefragte Felder in normale Spalten verlagern, nur den wirklich variablen Teil in JSON halten.","steps":["Warum: Speichert variable/optionale Felder flexibel in einer Spalte; Felder lassen sich ohne Schemamigration hinzufügen.","Wann: Unvorhersehbare Strukturen wie Payloads externer APIs, benutzerdefinierte Felder oder Produktattribute.","Engine-Unterschiede: In PostgreSQL JSONB verwenden (binär, indizierbar, @>/?-Abfragen mit GIN) — gegenüber einfachem JSON bevorzugen. Der JSON-Typ von MySQL ist ebenfalls binär und wird über Generated Column + Index abgefragt. Oracle 21c+ hat einen JSON-Typ/OSON; MSSQL vor 2025 hält JSON in NVARCHAR und verarbeitet es mit Funktionen; SQL Server 2025 und Azure SQL haben einen nativen (binären) json-Typ.","Achtung: Indiziere häufig gefilterte JSON-Felder (PostgreSQL GIN, MySQL Generated-Column-Index); sonst scannt jede Abfrage das ganze Dokument.","Achtung: Stopf nicht alles in JSON — relationale Integrität, Fremdschlüssel und Typisierung gehen verloren. Felder, die Abfragen/Constraints brauchen, sollten echte Spalten sein."],"related":["decide-sqlnosql"]},"sol_enum":{"kind":"solution","title":"Empfehlung: ENUM vs. Lookup-Tabelle (Referenztabelle)","detail":"Für begrenzte Kategoriemengen gibt es zwei Optionen: den ENUM-Typ der Engine oder eine Lookup-Tabelle + Fremdschlüssel. ENUM ist kompakt und einfach, das Hinzufügen eines Werts erfordert aber eine Schemaänderung; eine Lookup-Tabelle ist flexibel und kann zusätzliche Attribute tragen (Beschreibung, Aktiv-Flag, Reihenfolge).","steps":["Wann ENUM: Wenn die Werte sehr stabil sind und sich selten ändern (z. B. Geschlecht, Wochentag) und keine zusätzlichen Attribute nötig sind; kompakt und lesbar.","Wann eine Lookup-Tabelle: Wenn sich die Werte im Lauf der Zeit ändern können, zusätzliche Felder wie Übersetzungen/Beschreibung/Reihenfolge/Aktiv-Flag nötig sind oder Berichte darauf joinen — sie ist flexibler.","Engine-Unterschiede: PostgreSQL und MySQL bieten einen nativen ENUM-Typ (in PostgreSQL erfordert das Hinzufügen eines ENUM-Werts ALTER TYPE, und die Sortierung folgt der Definitionsreihenfolge). Oracle und MSSQL haben kein eingebautes ENUM; nutze eine CHECK-Constraint oder eine Lookup-Tabelle.","Achtung: Das Hinzufügen/Entfernen eines ENUM-Werts ist eine Schemamigration und kann auf einer großen Tabelle Sperren/Kosten verursachen; bevorzuge für häufig wechselnde Mengen eine Lookup-Tabelle.","Alternative: Freitext + eine CHECK-Constraint ist für kleine, stabile Mengen ebenfalls ein praktikabler Mittelweg."],"related":[]}}},"solver/decide-capacity.json":{"id":"decide-capacity","title":"Entscheidung: Serverdimensionierung","icon":"🧭","category":"Entscheidungshilfe","intro":"Korrekte Dimensionierung beginnt beim Workload-Profil (OLTP, Data Warehouse, gemischt) und erfordert, den echten Engpass zu finden (CPU, I/O oder RAM). Goldene Regel: Der häufig genutzte Datenbestand (Working Set) muss in den RAM passen; erst messen, dann skalieren, und Reserve für Lastspitzen lassen. Dieser Baum hilft dir, die Workload zu bestimmen und je nach Engpass Ressource und Skalierungsrichtung (vertikal/horizontal) zu wählen.","start":"root","nodes":{"root":{"kind":"step","title":"Wie sieht dein Workload-Profil aus?","detail":"Die Dimensionierung beginnt bei der Workload, denn OLTP- und Analyselasten erfordern völlig unterschiedliche Ressourcenprofile. Was ist die überwiegende Arbeit des Systems: viele kleine, schnelle Transaktionen (OLTP), Reporting/Analysen mit großen Scans und Aggregationen (DWH) oder eine Mischung aus beidem? Bist du nicht sicher, musst du zuerst messen.","choices":[{"label":"OLTP — viele kleine, kurze Transaktionen (Web/Anwendung)","next":"bottleneck"},{"label":"Analyse / Data Warehouse (DWH) — große Scans und Aggregationen","next":"sol_dwh"},{"label":"Gemischte Workload, oder ich bin nicht sicher","next":"sol_measure"},{"label":"Ich kenne den Engpass; soll ich vertikal oder horizontal skalieren?","next":"sol_scale"}]},"bottleneck":{"kind":"step","title":"Wo liegt der Engpass: RAM, I/O oder CPU?","detail":"Finde bei OLTP den echten Engpass, statt blind Ressourcen hinzuzufügen. Sieh dir deine Metriken an: Passen häufig genutzte Daten nicht in den RAM und gibt es viele Disk-Lesevorgänge (RAM/I/O), reichen Disk-Latenz/IOPS nicht aus (I/O), oder liegen die Daten bereits im Speicher, aber die CPU ist ausgelastet (CPU)? Die falsche Ressource zu skalieren verschwendet Geld und Zeit.","choices":[{"label":"Häufig genutzte Daten passen nicht in den RAM; viele Disk-Lesevorgänge","next":"sol_ram"},{"label":"Daten liegen im Speicher, aber Disk-Latenz/IOPS reichen nicht (schreibintensiv)","next":"sol_io"},{"label":"Daten liegen im Speicher und die Disk ist in Ordnung, aber die CPU ist ständig ausgelastet","next":"sol_cpu"}]},"sol_measure":{"kind":"solution","title":"Empfehlung: erst messen, dann dimensionieren","detail":"Hardware nach Schätzungen zu kaufen, führt fast immer zur falschen Größe. Workload-Profil und Engpass ergeben sich aus echten Metriken. Dimensionierung ist keine Schätzung, sondern eine messungsgetriebene Iteration.","steps":["Warum: Ob OLTP oder DWH und ob der Engpass bei CPU, I/O oder RAM liegt, zeigt sich erst bei Messung unter realer Last; Schätzen ist ein teurer Fehler.","Messen: CPU-Auslastung und Run Queue, Speicher-/Cache-Trefferquote (Buffer Cache Hit), Disk-IOPS und Latenz, aktive Verbindungen und Wait Events.","Engine-Tools: PostgreSQL pg_stat_* und Wait Events, Oracle AWR/ASH, MySQL Performance Schema, MSSQL DMVs und Wait Stats.","Vorgehen: Teste mit einer repräsentativen Last, finde die knappste Ressource, skaliere nur diese und miss erneut. Ändere jeweils nur eine Ressource, damit du die Wirkung isolieren kannst.","Als Nächstes: Sobald das Profil klar ist, gehe zur passenden Entscheidung (OLTP-Engpass, DWH oder horizontale/vertikale Skalierung)."],"related":["high-cpu","memory-pressure","slow-query"]},"sol_ram":{"kind":"solution","title":"Empfehlung: RAM ergänzen — das Working Set muss in den Speicher passen","detail":"Die wichtigste OLTP-Regel: Häufig genutzte Daten und Indizes (das Working Set) müssen in den Speicher (Buffer/Cache) passen. Tun sie das nicht, geht jede Abfrage auf die Disk, die Latenz explodiert und I/O wird zum Engpass. RAM ist meist die Investition mit der höchsten Rendite.","steps":["Warum: Lesen aus dem Speicher ist hundert- bis tausendmal schneller als von der Disk; passt das Working Set in den RAM, steigt die Cache-Trefferquote und Disk-I/O bricht ein.","Wann: Niedrige Cache-Trefferquote, häufige 'disk read'-Wait-Events, ein aktiver Datenbestand größer als der verfügbare RAM.","Wie: Dimensioniere den DB-Speicherbereich richtig — PostgreSQL shared_buffers + OS-Page-Cache, MySQL innodb_buffer_pool_size (meist der Großteil des RAM), Oracle SGA/Buffer Cache, MSSQL max server memory.","Ziel: Idealerweise passen Working Set + Indizes in den Speicher; passt nicht alles, dann zumindest der am häufigsten genutzte „heiße“ Teil.","Achtung: Gib nicht den gesamten physischen RAM an den Buffer Pool; lass Platz für OS, Speicher pro Verbindung und andere Prozesse, sonst bremst Swapping alles aus."],"related":["memory-pressure","slow-query"]},"sol_io":{"kind":"solution","title":"Empfehlung: I/O-Schicht stärken (IOPS/Latenz)","detail":"Selbst wenn die Daten im Speicher liegen, ist unter schreibintensiver Last (Commit, WAL/Redo/binlog, Checkpoint) die Disk-Geschwindigkeit entscheidend. Das Problem ist hier nicht die Kapazität, sondern IOPS und Latenz. Die falsche Disk-Klasse zwingt das System trotz schneller CPU/RAM in die Knie.","steps":["Warum: Für die Dauerhaftigkeit schreibt jeder Commit das Log auf die Disk; eine Disk mit wenig IOPS/hoher Latenz begrenzt den Transaktionsdurchsatz direkt.","Wann: Hohe Disk-Latenz, lange 'log/WAL write'-Wartezeiten, ein Engpass während Checkpoints, schreibintensives OLTP.","Wie: Nutze NVMe/SSD; verlege das Transaktionslog auf eine schnelle, von den Datendateien getrennte Disk; wähle in der Cloud eine Disk-Klasse mit ausreichend bereitgestellten IOPS/Durchsatz (z. B. Provisioned IOPS).","Ziel: Lege Ziele für IOPS und Latenz fest — nicht nur IOPS, auch die p99-Latenz zählt. RAID/Schreibcache (BBU) kann die Schreiblatenz senken.","Achtung: In der Cloud hängen Disk-Durchsatz und IOPS oft auch von der Instanzgröße ab; die Latenz netzwerkgebundener Disks ist höher als bei lokalem NVMe."],"related":["disk-full","slow-query"]},"sol_cpu":{"kind":"solution","title":"Empfehlung: CPU-Engpass beheben (zuerst Abfragen, dann Kerne)","detail":"Ist die CPU ausgelastet, während die Daten im Speicher liegen und die Disk in Ordnung ist, liegt die Ursache meist in ineffizienten Abfragen (schlechte Pläne, fehlende Indizes, viele kleine Abfragen) oder übermäßiger Parallelität. Die Last zu senken, bevor man Kerne hinzufügt, ist meist günstiger und wirksamer.","steps":["Warum: CPU wird am meisten durch Full Scans, Sorts/Hashes, Plankompilierung und die Kontextwechsel zu vieler Verbindungen verbraucht. Diese zu beheben ist günstiger als Kerne.","Zuerst optimieren: Finde die teuersten Abfragen (pg_stat_statements / AWR / Performance Schema), ergänze fehlende Indizes, reduziere N+1-Abfragen, verkleinere den Connection Pool.","Dann skalieren: Sind die Abfragen gesund und die CPU trotzdem ausgelastet, erhöhe die Kernanzahl (vertikal) — besonders bei OLTP mit hoher Parallelität.","Engine-Hinweis: Mehr Verbindungen = mehr Kontextwechsel; ein kleiner Pool mit schnellem Umschlag schlägt oft mehr Kerne (siehe Entscheidung zum Connection Pool).","Achtung: Wird pro Kern lizenziert (Oracle/MSSQL), sind zusätzliche Kerne teuer; Query-Optimierung zuerst senkt die Kosten."],"related":["high-cpu","slow-query"]},"sol_dwh":{"kind":"solution","title":"Empfehlung: Analyse/DWH — Durchsatz, Parallelität und horizontale Skalierung","detail":"Eine Data-Warehouse-Workload besteht aus wenigen schweren Abfragen, die große Tabellen scannen und aggregieren. Priorität hat hier nicht niedrige Latenz, sondern hoher Scan-Durchsatz, Parallelität und meist eine horizontal skalierbare Architektur.","steps":["Warum: Abfragen scannen Terabytes; entscheidend sind Disk-/Speicherbandbreite und parallel arbeitende Kerne, nicht die Latenz einzelner Transaktionen.","Wie: Storage mit hohem I/O-Durchsatz, viel RAM (für Aggregation/Hash Joins), viele Kerne und Parallel Query; ein spaltenorientierter Store oder eine MPP-Architektur ist bei großem Umfang sehr effizient.","Horizontale Skalierung: Für ein großes DWH skaliert eine verteilte/MPP-Architektur (z. B. geshardete Analyse-Engines) meist besser als eine größere Einzelmaschine; Partitionierung + Partition Pruning verkleinern Scans.","Engine-Hinweis: Übernimm keine OLTP-Einstellungen — für DWH werden größere work_mem/Sortierbereiche, ein höherer Parallelitätsgrad und große Blöcke/Readahead bevorzugt.","Achtung: Mische OLTP und DWH nicht auf demselben Server; schwere Berichte ersticken OLTP. Verlagere Analysen nach Möglichkeit auf eine separate Replica/ein Data Warehouse."],"related":["decide-shard-replica","decide-partition"]},"sol_scale":{"kind":"solution","title":"Allgemeines Prinzip: vertikal vs. horizontal + Reserve + erst messen","detail":"Wie du skalierst, zählt ebenso wie welche Ressource du skalierst. Vertikal (größere Maschine) ist einfach, hat aber eine Obergrenze und einen Single Point of Failure; horizontal (mehr Maschinen) skaliert, bringt aber Komplexität. Lass in jedem Fall Reserve für Lastspitzen und validiere die Entscheidung mit Messungen.","steps":["Vertikal (Scale-up): Ressourcen (CPU/RAM/IO) einer einzelnen Maschine erhöhen — die einfachste Option und der erste Schritt für die meisten OLTP-Systeme; es gibt aber eine physische/Kostengrenze und einen Single Point of Failure.","Horizontal (Scale-out): Read Replicas (für Leselast) oder Sharding (für Schreib-/Datenvolumen) hinzufügen — nahezu unbegrenzte Skalierung, bringt aber Konsistenz- und Betriebskomplexität.","Reserve: Plane Ressourcen nicht auf 100 %; lass bei normaler Spitzenlast ~30 % Reserve, damit plötzliche Spitzen, Wartung und Wachstum hineinpassen.","Erst messen: Bestätige vor dem Skalieren den Engpass per Messung, ziele auf die knappste Ressource und miss nach der Änderung erneut.","Achtung: Horizontale Skalierung löst nicht jedes Problem — verteilte Transaktionen und Joins sind teuer; stelle erst um, wenn vertikale Skalierung und Optimierung ausgeschöpft sind."],"related":["decide-shard-replica","decide-ha"]}}},"solver/slow-write.json":{"id":"slow-write","title":"Langsame Schreibvorgänge (INSERT/UPDATE-Latenz)","icon":"🐢","category":"Performance","intro":"INSERT/UPDATE/DELETE-Operationen sind langsamer als erwartet. Zuerst bestimmen wir, worauf der Schreibvorgang wartet (Commit/Redo, Sperren, Disk oder Overhead pro Zeile), und arbeiten uns dann Schritt für Schritt zur Ursache vor.","start":"root","nodes":{"root":{"kind":"step","title":"Worauf wartet der Schreibvorgang?","detail":"Klassifiziere den Engpass anhand der Wait Events: Dominiert die Commit-/Redo-Synchronisierung (WAL), Sperren, die Disk-Schreiblatenz oder Index-/Trigger-Overhead pro Zeile?","command":{"oracle":"SELECT event, total_waits, ROUND(time_waited/100,1) AS sec_waited\nFROM v$system_event\nWHERE event IN ('log file sync','log file parallel write','enq: TX - row lock contention','db file sequential read')\nORDER BY time_waited DESC;","postgres":"SELECT wait_event_type, wait_event, count(*) AS sessions\nFROM pg_stat_activity\nWHERE state='active'\nGROUP BY 1,2 ORDER BY 3 DESC;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_waits';\nSHOW GLOBAL STATUS LIKE 'Innodb_os_log_pending_fsyncs';\nSHOW ENGINE INNODB STATUS\\G","mssql":"SELECT TOP 10 wait_type, waiting_tasks_count, wait_time_ms\nFROM sys.dm_os_wait_stats\nWHERE wait_type IN ('WRITELOG','PAGEIOLATCH_EX','LCK_M_X','LCK_M_U')\nORDER BY wait_time_ms DESC;"},"choices":[{"label":"Wartet auf Commit / Redo (WAL-Sync)","next":"commit"},{"label":"Wartet auf eine Sperre","next":"lock"},{"label":"Vermuteter Overhead pro Zeile (Indizes/Trigger)","next":"overhead"},{"label":"Disk-Schreiblatenz ist hoch","next":"sol_disk"}]},"commit":{"kind":"step","title":"Commit-Muster oder fsync?","detail":"Ein eigener Commit pro Zeile (Autocommit) löst die Redo-/Log-Synchronisierung so oft aus, wie es Zeilen gibt. Ist umgekehrt die Commit-Anzahl vernünftig, jeder Commit wartet aber auf fsync, liegt das Problem bei der Latenz der Log-Disk.","command":{"oracle":"SELECT name, value FROM v$sysstat WHERE name IN ('user commits','redo writes','redo size');\nSELECT ROUND(time_waited_micro/GREATEST(total_waits,1)/1000,2) AS ms_per_sync FROM v$system_event WHERE event='log file sync';","postgres":"SHOW synchronous_commit;\nSELECT wal_records, wal_fpi, wal_bytes, wal_buffers_full FROM pg_stat_wal; -- wal_sync/wal_sync_time PG14-17; PG18+: pg_stat_io (object = 'wal')","mysql":"SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';\nSHOW VARIABLES LIKE 'sync_binlog';\nSHOW GLOBAL STATUS LIKE 'Com_commit';","mssql":"SELECT io_stall_write_ms, num_of_writes, io_stall_write_ms/NULLIF(num_of_writes,0) AS ms_per_write\nFROM sys.dm_io_virtual_file_stats(DB_ID(), 2);"},"choices":[{"label":"Autocommit / ein Commit pro Zeile","next":"sol_batch"},{"label":"Commit wartet auf Log-Synchronisierung (fsync)","next":"sol_disk"}]},"lock":{"kind":"step","title":"Wer hält die Sperre?","detail":"Wartet der Schreibvorgang auf eine Sperre: Eine andere Session hält vielleicht dieselbe Zeile/denselben Index, oder viele Sessions versuchen, in dieselbe „heiße“ Zeile zu schreiben.","command":{"oracle":"SELECT s.sid, s.serial#, s.username, s.blocking_session, s.event, s.seconds_in_wait\nFROM v$session s WHERE s.blocking_session IS NOT NULL;","postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by, wait_event, left(query,60) AS query\nFROM pg_stat_activity\nWHERE cardinality(pg_blocking_pids(pid)) > 0;","mysql":"SELECT requesting_engine_transaction_id AS waiter, blocking_engine_transaction_id AS blocker,\n       object_name, lock_type, lock_mode\nFROM performance_schema.data_lock_waits w\nJOIN performance_schema.data_locks l ON l.engine_lock_id = w.blocking_engine_lock_id;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_time, wait_resource\nFROM sys.dm_exec_requests\nWHERE blocking_session_id <> 0;"},"choices":[{"label":"Eine andere Session hält die Sperre","next":"sol_lock"},{"label":"Viele Sessions schreiben in dieselbe heiße Zeile","next":"sol_hotrow"}]},"overhead":{"kind":"step","title":"Woher kommt der Overhead pro Zeile?","detail":"Jedes INSERT/UPDATE aktualisiert auch alle Indizes der Tabelle und löst etwaige Trigger/Constraints aus. Eine übermäßige Anzahl von Indizes oder ein schwerer Trigger verlangsamt Schreibvorgänge erheblich.","command":{"oracle":"SELECT index_name, uniqueness FROM user_indexes WHERE table_name='T';\nSELECT trigger_name, triggering_event FROM user_triggers WHERE table_name='T';","postgres":"SELECT indexname, indexdef FROM pg_indexes WHERE tablename='t';\nSELECT tgname FROM pg_trigger WHERE tgrelid='t'::regclass AND NOT tgisinternal;","mysql":"SHOW INDEX FROM t;\nSHOW TRIGGERS LIKE 't';","mssql":"SELECT name, type_desc FROM sys.indexes WHERE object_id=OBJECT_ID('dbo.t');\nSELECT name, is_disabled FROM sys.triggers WHERE parent_id=OBJECT_ID('dbo.t');"},"choices":[{"label":"Die Tabelle hat zu viele / unnötige Indizes","next":"sol_index"},{"label":"Es gibt hohe Kosten durch Trigger oder Constraints","next":"sol_trigger"}]},"sol_batch":{"kind":"solution","title":"Ursache: Autocommit / Commit pro Zeile","detail":"Ein eigener Commit pro Zeile wiederholt die Redo-/Log-Synchronisierung so oft, wie es Zeilen gibt. Durch Bündeln der Operationen kannst du die Anzahl der Commits (und damit der fsyncs) drastisch senken.","steps":["Deaktiviere Autocommit in der Anwendung und setze Batch-Commits ein, mit einem Commit alle N Zeilen (z. B. 500-5000).","Nutze mehrzeilige INSERTs / Bulk-Load-Wege statt einzeiliger INSERTs (Postgres COPY, Oracle Bulk Insert, MySQL LOAD DATA, MSSQL BULK INSERT).","Nutze Prepared Statements, um die Kosten wiederholten Parsens (Hard Parse) zu senken.","Verifiziere nach der Änderung, dass die Wartezeit 'log file sync' / WRITELOG gesunken ist."],"related":["active-sessions"]},"sol_disk":{"kind":"solution","title":"Ursache: langsame Redo-/Log-Disk (fsync-Latenz)","detail":"Jeder Commit wartet, bis der Redo-/WAL-Eintrag auf dauerhaften Speicher geschrieben ist (fsync). Ist die Log-Disk langsam (hohe Schreiblatenz, unzureichende IOPS), zahlt jeder Commit diese Latenz.","steps":["Verlege die Log-/Redo-Dateien auf eine dedizierte Disk mit niedriger Latenz (vorzugsweise NVMe-SSD); trenne sie physisch von den Datafiles.","Miss die Storage-Latenz (Datei-I/O-Statistiken); sind die ms pro Schreibvorgang hoch, prüfe Hardware/RAID-Schreibcache.","Erhöhe in virtuellen/Cloud-Umgebungen die Limits für bereitgestellte IOPS und Durchsatz.","Wenn die Anforderungen an die Dauerhaftigkeit es zulassen, erwäge Group-Commit-/Commit-Delay-Einstellungen (z. B. Postgres commit_delay); wäge aber das Risiko von Datenverlust ab."],"related":[]},"sol_lock":{"kind":"solution","title":"Ursache: Eine andere Session hält die Sperre","detail":"Der Schreibvorgang wartet auf eine Zeilen-/Indexsperre, die eine andere Session hält. Der Blocker ist meist eine lang laufende Transaktion oder eine, deren Commit vergessen wurde.","steps":["Ermittle die blockierende Session und untersuche, was sie tut (ein langer Batch oder idle in transaction?).","Bleibt der Blocker grundlos offen/lange, korrigiere das Commit-/Rollback-Muster in der Anwendung.","Beende den Blocker im Notfall kontrolliert (kill session / pg_terminate_backend / KILL).","Halte Transaktionen kurz; warte innerhalb einer Transaktion nicht auf Benutzerinteraktion."],"related":["blocking-sessions","current-locks"]},"sol_hotrow":{"kind":"solution","title":"Ursache: Hot-Row-Konkurrenz","detail":"Viele Sessions versuchen, in dieselbe Zeile zu schreiben (z. B. Zähler, Sequenznummer, Saldo); alle stehen für dieselbe Sperre an und Schreibvorgänge werden serialisiert.","steps":["Teile beim Hot-Counter-Muster den Wert auf parallele Buckets auf (Sharded Counter) und summiere sie beim Lesen.","Verlagere die Sequenz-/ID-Erzeugung von einer zentralen Einzelzeile auf einen Sequence-/Identity-Mechanismus.","Senke die Aktualisierungsfrequenz: Puffere laufende Summen in der Anwendung und schreibe sie periodisch.","Verringere die Konkurrenz durch eine einheitliche Operationsreihenfolge und kürzere Sperrdauer."],"related":["current-locks"]},"sol_index":{"kind":"solution","title":"Ursache: übermäßiger Index-Overhead","detail":"Jeder Index der Tabelle wird bei jedem Schreibvorgang aktualisiert. Viele oder ungenutzte Indizes verlangsamen INSERT/UPDATE direkt.","steps":["Ermittle ungenutzte/überlappende Indizes (Nutzungsstatistiken) und entferne sie sicher.","Vereinfache breite zusammengesetzte Indizes; führe Indizes mit denselben führenden Spalten zusammen.","Erwäge bei großen Bulk-Loads, Indizes vorübergehend zu deaktivieren und nach dem Laden neu aufzubauen.","Miss nach der Änderung die Schreiblatenz erneut und bestätige die Verbesserung."],"related":["slow-queries"]},"sol_trigger":{"kind":"solution","title":"Ursache: schwerer Trigger / schwere Constraint","detail":"Ein Trigger, der bei jedem Schreibvorgang feuert, oder eine zu prüfende Constraint (insbesondere eine Fremdschlüsselprüfung ohne Index) verursacht bei jeder Zeile zusätzliche Kosten.","steps":["Mach schwere Arbeit im Trigger-Body (z. B. synchrone Log-Schreibvorgänge) asynchron/queue-basiert.","Indiziere die referenzierenden (Child-)Fremdschlüsselspalten; sonst scannt jedes DELETE/UPDATE auf dem Parent die Child-Tabelle vollständig.","Stelle Trigger auf Zeilenebene wo möglich auf Anweisungsebene um.","Verlagere unnötige Trigger-Logik oder Logik, die zu Geschäftsregeln geworden ist, in die Anwendungsschicht."],"related":[]}}},"solver/io-bottleneck.json":{"id":"io-bottleneck","title":"Hoher IO-Wait / Disk-Latenz","icon":"💽","category":"Performance","intro":"Das System wartet auf Disk-IO. Zuerst klären wir, ob die Last lese- oder schreibintensiv ist, und arbeiten uns dann zu den Ursachen vor: zu kleiner Buffer Cache, Full Table Scans, Checkpoint-Stürme, IOPS-Limits oder Auslagerung in temporären Speicher.","start":"root","nodes":{"root":{"kind":"step","title":"Ist das IO leseintensiv oder schreibintensiv?","detail":"Bestimme mit Datei-IO-Statistiken, ob die Wartezeit auf der Leseseite (Daten werden von der Disk gelesen) oder auf der Schreibseite (Redo-/Checkpoint-/Datenschreibvorgänge) liegt. Das teilt den Baum in zwei Zweige.","command":{"oracle":"SELECT function_name, small_read_reqs+large_read_reqs AS reads, small_write_reqs+large_write_reqs AS writes,\n       small_read_megabytes+large_read_megabytes+small_write_megabytes+large_write_megabytes AS mb\nFROM v$iostat_function ORDER BY mb DESC;","postgres":"SELECT sum(blks_read) AS reads, sum(blks_hit) AS hits FROM pg_stat_database;\nSELECT buffers_checkpoint, buffers_clean, buffers_backend FROM pg_stat_bgwriter;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_data_read';\nSHOW GLOBAL STATUS LIKE 'Innodb_data_written';\nSHOW GLOBAL STATUS LIKE 'Innodb_data_reads';","mssql":"SELECT DB_NAME(database_id) AS db, io_stall_read_ms, io_stall_write_ms, num_of_reads, num_of_writes\nFROM sys.dm_io_virtual_file_stats(NULL, NULL)\nORDER BY io_stall_read_ms + io_stall_write_ms DESC;"},"choices":[{"label":"Überwiegend Lese-IO","next":"read"},{"label":"Überwiegend Schreib-IO","next":"write"}]},"read":{"kind":"step","title":"Warum ist das Lese-IO hoch?","detail":"Übermäßige physische Lesevorgänge haben drei Hauptursachen: Der Buffer Cache ist zu klein (das Working Set passt nicht in den RAM), Abfragen führen Full Table Scans aus, oder große Sort-/Hash-Operationen werden in temporären Speicher ausgelagert.","command":{"oracle":"SELECT ROUND((1-(pr.value/(dbg.value+cg.value)))*100,2) AS buffer_hit_pct\nFROM v$sysstat pr, v$sysstat dbg, v$sysstat cg\nWHERE pr.name='physical reads' AND dbg.name='db block gets' AND cg.name='consistent gets';\nSELECT sql_id, disk_reads, executions FROM v$sql ORDER BY disk_reads DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT ROUND(sum(blks_hit)*100.0/nullif(sum(blks_hit)+sum(blks_read),0),2) AS cache_hit_pct FROM pg_stat_database;\nSELECT query, shared_blks_read, temp_blks_read FROM pg_stat_statements ORDER BY shared_blks_read DESC LIMIT 10;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';\nSHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';\nSHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';","mssql":"SELECT CAST(100.0 * a.cntr_value / NULLIF(b.cntr_value, 0) AS decimal(5,2)) AS buffer_cache_hit_ratio\nFROM sys.dm_os_performance_counters a\nJOIN sys.dm_os_performance_counters b ON b.object_name = a.object_name AND b.counter_name = 'Buffer cache hit ratio base'\nWHERE a.counter_name = 'Buffer cache hit ratio' AND a.object_name LIKE '%Buffer Manager%';"},"choices":[{"label":"Buffer-Cache-Trefferquote ist niedrig, das Working Set passt nicht","next":"sol_cache"},{"label":"Abfragen führen Full Table Scans aus","next":"sol_scan"},{"label":"Große Sorts/Hashes werden in temporären Speicher ausgelagert","next":"sol_spill"}]},"write":{"kind":"step","title":"Warum ist das Schreib-IO hoch?","detail":"Ein Engpass auf der Schreibseite entsteht entweder dadurch, dass periodische Checkpoints/Flushes die Disk kurzzeitig auslasten (Checkpoint-Sturm), oder dadurch, dass dem Storage IOPS/Durchsatz für die Dauerlast fehlen.","command":{"oracle":"SELECT event, total_waits, ROUND(time_waited/100,1) AS sec\nFROM v$system_event\nWHERE event IN ('log file parallel write','db file parallel write','checkpoint completed')\nORDER BY time_waited DESC;","postgres":"-- PG17+:\nSELECT num_timed, num_requested, buffers_written FROM pg_stat_checkpointer;\n-- PG16 and earlier:\nSELECT checkpoints_timed, checkpoints_req, buffers_checkpoint, maxwritten_clean\nFROM pg_stat_bgwriter;\nSHOW checkpoint_completion_target;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';\nSHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';\nSHOW ENGINE INNODB STATUS\\G","mssql":"SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Checkpoint pages/sec';\nSELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Page writes/sec';"},"choices":[{"label":"Plötzliche Checkpoint-/Flush-Wellen (Checkpoint-Sturm)","next":"sol_checkpoint"},{"label":"Storage hält der Dauerlast nicht stand (IOPS-Limit)","next":"sol_iops"}]},"sol_cache":{"kind":"solution","title":"Ursache: zu kleiner Buffer Cache","detail":"Der häufig genutzte Datenbestand (Working Set) passt nicht in den Buffer Cache, daher werden Blöcke ständig erneut von der Disk gelesen. Die Trefferquote ist niedrig und die physischen Lesevorgänge sind hoch.","steps":["Vergrößere den Buffer/Cache: Oracle db_cache_size/SGA, Postgres shared_buffers, MySQL innodb_buffer_pool_size, MSSQL max server memory.","Vergleiche Trefferquote und physische Lesevorgänge vor und nach der Änderung.","Reicht der RAM physisch nicht, erweitere den Serverspeicher oder verkleinere das Working Set durch Archivierung/Partitionierung.","Kommt der eigentliche Engpass von einem einzelnen Scan-Muster, wechsle in den Scan-Zweig des Cache-Hit-Baums."],"related":["cache-hit-ratio"]},"sol_scan":{"kind":"solution","title":"Ursache: Full Table Scans","detail":"Abfragen scannen große Tabellen komplett, weil kein passender Index existiert; das verursacht viele physische Lesevorgänge und füllt den Cache mit nutzlosen Blöcken.","steps":["Hole die Pläne (EXPLAIN) der Abfragen mit den meisten physischen Lesevorgängen und finde die Full Scans.","Lege passende Indizes auf Filter- und Join-Spalten an; verifiziere, dass der Plan auf Indexzugriff wechselt.","Verringere bei sehr großen Tabellen die gescannte Datenmenge mit datums-/bereichsbasierter Partitionierung.","Wähle nur die benötigten Spalten statt eines unnötigen 'SELECT *', um die Zahl gelesener Blöcke zu senken."],"related":["slow-queries"]},"sol_spill":{"kind":"solution","title":"Ursache: Sorts/Hashes werden in temporären Speicher ausgelagert","detail":"Große Sort-, Hash-Join- oder Group-by-Operationen passen nicht in den Arbeitsspeicher und schreiben in temporären Speicher (temp/tempdb), was hohes Lese-/Schreib-IO erzeugt.","steps":["Erhöhe die Arbeitsspeichereinstellung: Oracle pga_aggregate_target, Postgres work_mem, MySQL sort_buffer_size/join_buffer_size, MSSQL Memory Grant.","Verringere das Operationsvolumen: Filter früh anwenden, unnötige Sortierungen entfernen, Join-Reihenfolge verbessern.","Verhindere Auslagerungen auf die Disk durch passende Indizes für häufig genutzte Sortierung/Gruppierung.","Verlege den temporären Speicher auf eine schnelle Disk; besteht das Problem weiter, geh mit dem Baum zu Undo-/Temp-Druck tiefer."],"related":["slow-queries"]},"sol_checkpoint":{"kind":"solution","title":"Ursache: Checkpoint-Sturm","detail":"Dirty Pages sammeln sich lange an und werden in kurzen Abständen massenhaft auf die Disk geschrieben; diese plötzlichen Schreibwellen lasten das IO periodisch aus.","steps":["Verteile Checkpoints zeitlich: Erhöhe in Postgres checkpoint_completion_target und gleiche checkpoint_timeout aus.","Setze in MySQL innodb_io_capacity / innodb_io_capacity_max passend zum Storage und optimiere Adaptive Flushing.","Glätte in MSSQL plötzliche Wellen mit Indirect Checkpoints (target_recovery_time).","Gleiche in Oracle die Checkpoint-Frequenz mit fast_start_mttr_target aus und vergrößere die Redo-Log-Dateien."],"related":[]},"sol_iops":{"kind":"solution","title":"Ursache: unzureichende IOPS / unzureichender Durchsatz","detail":"Dem Storage fehlen IOPS oder Bandbreite, um die von der Workload benötigten Lese- und Schreibanfragen zu bedienen; Queue-Tiefe und Latenz sind dauerhaft hoch.","steps":["Miss die Disk-Latenz (ms pro Anfrage) und vergleiche sie mit dem IOPS-Limit von Hardware/Cloud.","Wechsle auf schnelleren Storage (HDD→SSD/NVMe) oder erhöhe in der Cloud die Stufe für bereitgestellte IOPS/Durchsatz.","Verringere die Konkurrenz, indem du Daten, Redo/Log und temporären Speicher auf verschiedene Disks verteilst.","Senke als dauerhafte Lösung den gesamten IO-Bedarf (Indizierung, Archivierung, Query-Optimierung)."],"related":[]}}},"solver/undo-temp-pressure.json":{"id":"undo-temp-pressure","title":"Druck auf Undo / TempDB / temporären Speicher","icon":"🧯","category":"Kapazität","intro":"Undo-/Rollback-Bereich, temporärer Speicher (temp) oder TempDB stehen unter Druck. Wir bestimmen, wo der Druck liegt, und arbeiten uns zur Ursache vor, etwa ein großer Sort/Hash, eine lange Abfrage/Transaktion oder ein Speicherleck.","start":"root","nodes":{"root":{"kind":"step","title":"Wo liegt der Druck: Undo, Temp oder TempDB-Konkurrenz?","detail":"Klassifiziere die Quelle des Drucks anhand der Speichernutzung: Läuft der Undo-/Rollback-Tablespace voll, der allgemeine temporäre Speicher (temp), oder ist es in MSSQL eine Latch-Konkurrenz bei der Seitenzuweisung in TempDB?","command":{"oracle":"SELECT tablespace_name, ROUND(used_percent,1) AS used_pct\nFROM dba_tablespace_usage_metrics\nWHERE tablespace_name LIKE '%UNDO%' OR tablespace_name LIKE 'TEMP%';","postgres":"SELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_written\nFROM pg_stat_database ORDER BY temp_bytes DESC;","mysql":"SELECT * FROM information_schema.INNODB_TEMP_TABLE_INFO;\nSHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';","mssql":"SELECT SUM(user_object_reserved_page_count)*8/1024 AS user_obj_mb,\n       SUM(internal_object_reserved_page_count)*8/1024 AS internal_obj_mb,\n       SUM(version_store_reserved_page_count)*8/1024 AS version_store_mb\nFROM sys.dm_db_file_space_usage;"},"choices":[{"label":"Undo-/Rollback-Bereich läuft voll (ORA-01555 / ORA-30036)","next":"undo"},{"label":"Temporärer Speicher (temp) läuft voll (Sort-/Hash-Auslagerung)","next":"temp"},{"label":"Latch-Konkurrenz bei der TempDB-Zuweisung (PFS/GAM/SGAM)","next":"tempdb"}]},"undo":{"kind":"step","title":"Was verbraucht Undo?","detail":"Undo-Druck ist typischerweise eine von zwei Arten: Eine lange ABFRAGE braucht alten Undo für das konsistente Lesen zu ihrem Startzeitpunkt (ORA-01555), oder eine lange/offene TRANSAKTION erzeugt so viel Undo, dass der Bereich vollläuft und es zu ORA-30036 kommt.","command":{"oracle":"SELECT s.sid, s.username, t.used_ublk*8/1024 AS undo_mb, s.status, t.start_time\nFROM v$transaction t JOIN v$session s ON s.taddr=t.addr\nORDER BY undo_mb DESC;\nSELECT MAX(maxquerylen) AS longest_query_sec, MAX(tuned_undoretention) AS tuned_retention FROM v$undostat;","postgres":"SELECT pid, now()-xact_start AS xact_age, state, left(query,60) AS query\nFROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 10;"},"choices":[{"label":"Eine lange Abfrage braucht alten Undo (ORA-01555)","next":"sol_undo_query"},{"label":"Eine lange/offene Transaktion häuft Undo an (ORA-30036)","next":"sol_undo_tx"}]},"temp":{"kind":"step","title":"Was füllt den temporären Speicher?","detail":"Temp-Druck entsteht entweder durch eine große Sort-/Hash-Operation, die auf die Disk ausgelagert wird (normal, aber riesig), oder durch eine einzelne Session, die dauerhaft Temp belegt (offener Cursor, Leck, endlose Abfrage).","command":{"oracle":"SELECT s.sid, s.username, u.tablespace, u.blocks*8/1024 AS temp_mb, u.sqladdr\nFROM v$tempseg_usage u JOIN v$session s ON s.saddr=u.session_addr\nORDER BY temp_mb DESC;","postgres":"SELECT query, temp_blks_written, temp_blks_read\nFROM pg_stat_statements ORDER BY temp_blks_written DESC LIMIT 10;","mysql":"SELECT event_name, current_number_of_bytes_used\nFROM performance_schema.memory_summary_global_by_event_name\nWHERE event_name LIKE '%temptable%' OR event_name LIKE '%sort%';","mssql":"SELECT session_id, request_id, SUM(internal_objects_alloc_page_count)*8/1024 AS internal_mb\nFROM sys.dm_db_task_space_usage GROUP BY session_id, request_id ORDER BY internal_mb DESC;"},"choices":[{"label":"Eine große Sort-/Hash-Operation wird auf die Disk ausgelagert","next":"sol_temp_sort"},{"label":"Eine einzelne Session belegt Temp (Leck/offener Cursor)","next":"sol_temp_leak"}]},"tempdb":{"kind":"step","title":"Geht es bei der TempDB-Konkurrenz um Zuweisung oder Volumen?","detail":"In MSSQL TempDB treten zwei verschiedene Probleme auf: PAGELATCH-Konkurrenz auf PFS-/GAM-/SGAM-Seiten (viele kleine temporäre Objekte) oder ein großer Sort/Version Store, der TempDB volumenmäßig füllt.","command":{"mssql":"SELECT session_id, wait_type, wait_duration_ms, resource_description\nFROM sys.dm_os_waiting_tasks\nWHERE wait_type LIKE 'PAGELATCH%' AND resource_description LIKE '2:%';\nSELECT COUNT(*) AS tempdb_data_files FROM sys.master_files WHERE database_id=2 AND type=0;"},"choices":[{"label":"PFS-/GAM-/SGAM-Latch-Konkurrenz","next":"sol_tempdb_latch"},{"label":"Ein großer Sort/Version Store füllt TempDB","next":"sol_temp_sort"}]},"sol_undo_query":{"kind":"solution","title":"Ursache: lange Abfrage + zu wenig Undo (ORA-01555)","detail":"Der alte Undo, den die lange Abfrage für ein konsistentes Lesen braucht, wurde überschrieben, weil die Retention abgelaufen war oder der Platz nicht reichte.","steps":["Erhöhe undo_retention auf einen Wert über der längsten Abfragedauer (v$undostat.maxquerylen).","Vergrößere den UNDO-Tablespace oder aktiviere Autoextend, damit die Retention-Dauer durch Platz gedeckt ist.","Schütze für kritische Berichte alten Undo mit ALTER TABLESPACE undotbs RETENTION GUARANTEE (DML kann scheitern, wenn der Platz vollläuft; Vorsicht).","Optimiere für eine dauerhafte Lösung die lange Abfrage oder verlagere sie auf einen Standby/eine Replica."],"related":["slow-queries"]},"sol_undo_tx":{"kind":"solution","title":"Ursache: Eine lange/offene Transaktion hat den Undo gefüllt (ORA-30036)","detail":"Eine einzelne riesige DML oder eine offene Transaktion mit verzögertem Commit hat zu viel Undo erzeugt; der UNDO-Tablespace konnte nicht wachsen und ORA-30036 wurde ausgelöst.","steps":["Ermittle die aktive Transaktion mit dem meisten Undo (v$transaction.used_ublk) und finde ihren Eigentümer.","Führe die riesige DML in Datums-/Bereichsblöcken mit Zwischen-Commits aus.","Korrigiere Sessions mit vergessenem Commit / idle in transaction auf Anwendungsseite; beende sie bei Bedarf kontrolliert.","Füge dem UNDO-Tablespace vorübergehend Platz hinzu; plane dauerhaft Retention und Platzgröße passend zur Workload."],"related":["active-sessions"]},"sol_temp_sort":{"kind":"solution","title":"Ursache: Ein großer Sort/Hash füllt den temporären Speicher","detail":"Große Sort-/Hash-/Group-by-Operationen, die nicht in den Speicher passen, werden in den temporären Speicher ausgelagert und können ihn füllen.","steps":["Erhöhe den Arbeitsspeicher: Oracle pga_aggregate_target, Postgres work_mem, MySQL sort_buffer_size, MSSQL Memory Grant.","Verbessere die Abfrage: unnötige Sortierungen entfernen, Filter früh anwenden, Indizes auf Sortier-/Gruppierungsspalten anlegen.","Teile sehr große Jobs in Blöcke auf; begrenze die Parallelität, damit sie nicht alle gleichzeitig Temp belegen.","Vergrößere den temporären Speicher und verlege ihn auf eine schnelle Disk; konfiguriere MSSQL TempDB mit mehreren Dateien passend zur Kernanzahl."],"related":["slow-queries"]},"sol_temp_leak":{"kind":"solution","title":"Ursache: Eine Session belegt temporären Speicher (Leck)","detail":"Eine einzelne Session gibt temporären Speicher wegen nicht geschlossener Cursor, einer endlosen Abfrage oder offener temporärer Tabellen nicht frei.","steps":["Ermittle die Session mit dem meisten Temp und das SQL, das sie ausführt.","Stelle sicher, dass Cursor/Ergebnismengen und temporäre Tabellen in der Anwendung ordnungsgemäß geschlossen werden.","Beende die hängende/übermäßig lange Session kontrolliert und verifiziere, dass der Platz freigegeben wird.","Definiere globale temporäre Tabellen so, dass sie am Session-/Transaktionsende automatisch bereinigt werden."],"related":["active-sessions"]},"sol_tempdb_latch":{"kind":"solution","title":"Ursache: TempDB-PFS-/GAM-/SGAM-Latch-Konkurrenz","detail":"Wenn viele Sessions gleichzeitig kleine temporäre Objekte anlegen, entsteht PAGELATCH-Konkurrenz auf den Allocation-Map-Seiten von TempDB (PFS/GAM/SGAM).","steps":["Konfiguriere die Anzahl der TempDB-Datendateien proportional zur CPU-Kernanzahl und mit gleicher Größe (z. B. 4-8 Dateien, alle mit gleicher Größe/Autogrowth).","Aktiviere in modernem SQL Server Optionen, die die Zuweisungskonkurrenz senken (memory-optimized tempdb metadata).","Lege weniger temporäre Tabellen an: Ersetze unnötige #temp-Tabellen durch Tabellenvariablen / eine einzelne Abfrage.","Verifiziere nach der Änderung, dass die PAGELATCH-Wartezeiten gesunken sind."],"related":["current-locks"]}}},"solver/long-transaction.json":{"id":"long-transaction","title":"Lang laufende / offene Transaktion","icon":"⏱️","category":"Sperren","intro":"Eine lang laufende oder offene Transaktion häuft Sperren, Undo und Bloat an. Wir finden die verantwortliche Transaktion, bestimmen ihren Typ (idle in transaction, langer Batch, Auswirkung) und lösen sie sicher auf.","start":"root","nodes":{"root":{"kind":"step","title":"Welche ist die älteste offene Transaktion?","detail":"Liste offene Transaktionen nach Startzeit auf. Die älteste ist meist die Ursache des Problems. Prüfe ihren Zustand: Läuft sie aktiv, oder ist sie 'idle in transaction' und wartet auf einen Commit?","command":{"oracle":"SELECT s.sid, s.serial#, s.username, s.status, t.start_time, t.used_ublk*8/1024 AS undo_mb\nFROM v$transaction t JOIN v$session s ON s.taddr=t.addr\nORDER BY t.start_time;","postgres":"SELECT pid, state, now()-xact_start AS xact_age, now()-state_change AS since_change, left(query,60) AS query\nFROM pg_stat_activity\nWHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 20;","mysql":"SELECT trx_id, trx_state, trx_started, TIMESTAMPDIFF(SECOND,trx_started,NOW()) AS secs, trx_mysql_thread_id, trx_rows_locked\nFROM information_schema.INNODB_TRX ORDER BY trx_started;","mssql":"SELECT s.session_id, s.login_name, t.transaction_begin_time,\n       DATEDIFF(second,t.transaction_begin_time,GETDATE()) AS secs\nFROM sys.dm_tran_active_transactions t\nJOIN sys.dm_tran_session_transactions st ON st.transaction_id=t.transaction_id\nJOIN sys.dm_exec_sessions s ON s.session_id=st.session_id\nORDER BY t.transaction_begin_time;"},"choices":[{"label":"idle in transaction (Commit vergessen, inaktiv)","next":"idle"},{"label":"Aktiver, aber sehr lang laufender Batch/DML","next":"batch"},{"label":"Sperren/Bloat/Undo häufen sich an, während die Transaktion läuft","next":"impact"}]},"idle":{"kind":"step","title":"Warum tritt idle in transaction auf?","detail":"Die Session hat eine Transaktion geöffnet, ist aber inaktiv, ohne eine Abfrage auszuführen; Commit/Rollback wurde also vergessen. Das hält Sperren und Undo unnötig fest. Reicht eine Korrektur in der Anwendung, oder muss sie dringend beendet werden?","command":{"postgres":"SELECT pid, now()-state_change AS idle_age, left(query,80) AS last_query\nFROM pg_stat_activity WHERE state='idle in transaction' ORDER BY state_change;","mysql":"SELECT trx.trx_mysql_thread_id, trx.trx_started, p.time AS idle_secs, p.command\nFROM information_schema.INNODB_TRX trx\nJOIN information_schema.PROCESSLIST p ON p.id=trx.trx_mysql_thread_id\nWHERE p.command='Sleep' ORDER BY trx.trx_started;","oracle":"SELECT sid, serial#, status, last_call_et AS idle_secs\nFROM v$session WHERE status='INACTIVE' AND type='USER' AND last_call_et>300;","mssql":"SELECT s.session_id, s.status, DATEDIFF(second,s.last_request_end_time,GETDATE()) AS idle_secs\nFROM sys.dm_exec_sessions s\nWHERE s.session_id IN (SELECT session_id FROM sys.dm_tran_session_transactions)\n  AND s.status='sleeping';"},"choices":[{"label":"Anwendung/Connection Pool committet nicht (dauerhafte Lösung)","next":"sol_idle"},{"label":"Ich muss sie sofort beenden","next":"sol_kill"}]},"batch":{"kind":"step","title":"Was macht die aktive lange Transaktion?","detail":"Die Session läuft tatsächlich (aktiv). Ist es eine einzelne riesige DML/ein Bericht, oder ein Blocker, der andere Sessions warten lässt und dringend getrennt werden muss?","command":{"oracle":"SELECT s.sid, s.serial#, sq.sql_text, s.last_call_et AS running_secs\nFROM v$session s JOIN v$sql sq ON sq.sql_id=s.sql_id\nWHERE s.status='ACTIVE' AND s.type='USER' ORDER BY s.last_call_et DESC;","postgres":"SELECT pid, now()-query_start AS run_time, left(query,80) AS query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start LIMIT 10;","mysql":"SELECT id, time AS run_secs, state, LEFT(info,80) AS query\nFROM information_schema.PROCESSLIST WHERE command<>'Sleep' ORDER BY time DESC;","mssql":"SELECT r.session_id, r.status, r.total_elapsed_time/1000 AS secs, SUBSTRING(t.text,1,80) AS query\nFROM sys.dm_exec_requests r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nORDER BY r.total_elapsed_time DESC;"},"choices":[{"label":"Eine einzelne riesige DML/ein Bericht; muss aufgeteilt werden","next":"sol_batch"},{"label":"Sie lässt andere warten; dringend beenden","next":"sol_kill"}]},"impact":{"kind":"step","title":"Was häuft die lange Transaktion an?","detail":"Bestimme die Nebenwirkung der langen Transaktion: Ist es vor allem ein Sperrstau, der andere Sessions warten lässt, oder Bloat, weil alte Zeilenversionen gehalten werden und die Bereinigung (VACUUM/Undo) blockiert ist?","command":{"postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;\nSELECT max(age(backend_xmin)) AS max_xmin_age FROM pg_stat_activity WHERE backend_xmin IS NOT NULL;","oracle":"SELECT blocking_session, sid, serial#, seconds_in_wait, event\nFROM v$session WHERE blocking_session IS NOT NULL;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_time\nFROM sys.dm_exec_requests WHERE blocking_session_id<>0;"},"choices":[{"label":"Ein Sperrstau lässt andere Sessions warten","next":"sol_lock_backlog"},{"label":"Angehäufte alte Versionen blockieren die Bereinigung (Undo/Bloat)","next":"sol_bloat"}]},"sol_idle":{"kind":"solution","title":"Ursache: Die Anwendung committet nicht (idle in transaction)","detail":"Connection Pool oder Anwendung öffnet eine Transaktion und hält die Verbindung dann inaktiv, ohne Commit/Rollback. Das hält Sperren und Undo unnötig fest.","steps":["Garantiere im Anwendungscode auf jedem Pfad, auf dem eine Transaktion geöffnet wird, einen Commit/Rollback; warte innerhalb einer Transaktion nicht auf Benutzereingaben oder langes IO.","Trenne inaktive Transaktionen automatisch: Postgres idle_in_transaction_session_timeout, MySQL-Verbindungs-/Timeout-Einstellungen, maxLifetime im Anwendungspool.","Konfiguriere den Connection Pool nach dem Prinzip „Autocommit an, kurze Transaktionen“.","Richte Monitoring ein: Löse einen Alarm für lange idle-in-transaction-Sessions aus."],"related":["active-sessions","blocking-sessions"]},"sol_kill":{"kind":"solution","title":"Lösung: verantwortliche Session sicher beenden","detail":"Lässt eine blockierende/lange Session andere warten, muss sie kontrolliert beendet werden. Das Beenden löst ein Rollback aus, das Platz/Zeit brauchen kann; Vorsicht.","steps":["Verifiziere zuerst, was die Session ist (welcher Benutzer/welche Anwendung, welcher Job), damit du keinen kritischen Job abbrichst.","Beenden: Oracle ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE; Postgres SELECT pg_terminate_backend(pid); MySQL KILL thread_id; MSSQL KILL session_id;","Beendest du eine große Schreibtransaktion, warte, bis das Rollback (Undo-/Log-Rückabwicklung) abgeschlossen ist.","Verifiziere nach dem Beenden, dass Sperren und wartende Sessions aufgelöst sind; behebe die Ursache (idle/Batch) dauerhaft."],"related":["blocking-sessions"]},"sol_batch":{"kind":"solution","title":"Ursache: eine einzelne riesige DML/ein Bericht","detail":"Eine DML, die Millionen Zeilen in einer Transaktion verarbeitet, oder ein sehr langer Bericht häuft über die gesamte Laufzeit Sperren und Undo/Bloat an.","steps":["Teile die große DML nach Datum/Bereich/Schlüssel in Blöcke auf und committe nach jedem Block.","Verlege den schweren Bericht außerhalb der Spitzenzeiten oder auf einen Standby/eine Read Replica.","Optimiere die lange Abfrage, um ihre Gesamtdauer (und damit die Zeit, in der sie Sperren/Undo hält) zu verkürzen.","Mach den Batch-Job beobachtbar: Protokolliere den Fortschritt und mach ihn dort fortsetzbar, wo er aufgehört hat."],"related":["slow-queries"]},"sol_lock_backlog":{"kind":"solution","title":"Ursache: Warteschlange durch Sperrstau","detail":"Durch die gehaltenen Sperren hat die lange Transaktion eine Warteschlange hinter sich erzeugt; viele Sessions warten auf dieselbe Ressource.","steps":["Ermittle die Session am Anfang der Sperrkette (Head Blocker); nur sie löst die Warteschlange auf.","Verkürze den Head Blocker (Commit/Rollback) oder beende ihn kontrolliert; beobachte, wie sich die Warteschlange leert.","Mach Transaktionen kleiner und definiere eine einheitliche Zugriffsreihenfolge, um die Sperrdauer zu verkürzen.","Wechsle wo sinnvoll zu Ansätzen mit schwächeren Sperren (Zeilenebene, passende Isolationsstufe)."],"related":["blocking-sessions","current-locks"]},"sol_bloat":{"kind":"solution","title":"Ursache: angehäufte alte Versionen (Undo/Bloat)","detail":"Die lang laufende Transaktion verhindert die Bereinigung alter Zeilenversionen, die älter sind als sie. In Postgres zeigt sich das als Tabellen-/Index-Bloat, in Oracle als Undo-Druck.","steps":["Finde die Session mit dem ältesten xmin/der ältesten Transaktion (Postgres backend_xmin, in Oracle die älteste aktive Transaktion).","Verkürze/beende diese Transaktion, damit die Bereinigung (VACUUM / Undo-Recycling) wieder vorankommt.","Führe in Postgres nach Ende der Transaktion VACUUM auf den aufgeblähten Tabellen aus (bei Bedarf VACUUM FULL/pg_repack).","Führe dauerhaft ein Anwendungsmuster mit kurzen Transaktionen ein und mach Autovacuum aggressiver."],"related":[]}}},"solver/plan-regression.json":{"id":"plan-regression","title":"Abfrageplan hat sich verschlechtert (plötzlich langsam)","icon":"📉","category":"Performance","intro":"Eine Abfrage, die gestern schnell war, ist heute plötzlich langsam. Meist ist der Optimizer auf einen schlechteren Plan umgestiegen. Wir finden die Ursache zwischen veralteten Statistiken, Parameterempfindlichkeit, Datenwachstum oder einem plötzlichen Planwechsel und stabilisieren den Plan.","start":"root","nodes":{"root":{"kind":"step","title":"Hat sich der Plan wirklich geändert, und wann?","detail":"Gibt es für dieselbe Abfrage mehrere Pläne (plan_hash/plan_generation), und wann begann die Verlangsamung? Fällt sie mit einem kürzlichen Deploy, Upgrade, einer Parameteränderung oder einem großen Datenladevorgang zusammen?","command":{"oracle":"SELECT sql_id, plan_hash_value, executions, ROUND(elapsed_time/GREATEST(executions,1)/1e6,3) AS sec_per_exec, first_load_time\nFROM v$sql WHERE sql_id='&sqlid' ORDER BY first_load_time;","postgres":"EXPLAIN (ANALYZE, BUFFERS) SELECT /* your query */ 1;\n-- Trend: SELECT query, calls, mean_exec_time FROM pg_stat_statements WHERE query LIKE '%...%';","mysql":"EXPLAIN FORMAT=JSON SELECT /* your query */ 1;\nSELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT/1e9 AS avg_ms\nFROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT LIKE '%...%';","mssql":"SELECT qs.plan_generation_num, qs.execution_count, qs.total_worker_time/qs.execution_count AS avg_cpu_us, qs.creation_time\nFROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t\nWHERE t.text LIKE '%...%';"},"choices":[{"label":"Statistiken sind möglicherweise veraltet","next":"stats"},{"label":"Dieselbe Abfrage verhält sich je nach Parameter anders (Sniffing/Peeking)","next":"sniff"},{"label":"Das Datenvolumen ist deutlich gewachsen","next":"sol_datagrowth"},{"label":"Der Plan hat sich nach einem Upgrade/Flush plötzlich geändert","next":"sol_planflip"}]},"stats":{"kind":"step","title":"Sind die Statistiken aktuell?","detail":"Der Optimizer stützt seine Entscheidungen auf Statistiken. Ist das letzte Analyze-Datum alt oder der Änderungszähler hoch, können die Schätzungen tatsächlich abgedriftet sein, was zu einem schlechten Plan führt.","command":{"oracle":"SELECT table_name, num_rows, last_analyzed, stale_stats\nFROM dba_tab_statistics WHERE table_name='T';","postgres":"SELECT relname, n_live_tup, n_mod_since_analyze, last_analyze, last_autoanalyze\nFROM pg_stat_user_tables WHERE relname='t';","mysql":"SELECT table_name, update_time, table_rows FROM information_schema.tables WHERE table_name='t';\nSHOW INDEX FROM t;","mssql":"SELECT s.name, STATS_DATE(s.object_id,s.stats_id) AS last_updated, p.modification_counter, p.rows\nFROM sys.stats s CROSS APPLY sys.dm_db_stats_properties(s.object_id,s.stats_id) p\nWHERE s.object_id=OBJECT_ID('dbo.t');"},"choices":[{"label":"Statistiken sind veraltet/fehlen","next":"sol_stats"},{"label":"Statistiken sind aktuell, aber der Plan ist weiterhin parameterempfindlich","next":"sniff"}]},"sniff":{"kind":"step","title":"Gibt es eine Parameterempfindlichkeit?","detail":"Die Abfrage wählt und cacht einen Plan anhand des Parameterwerts ihrer ersten Ausführung (MSSQL Parameter Sniffing / Oracle Bind Peeking). Dieser Plan kann für andere Werte bei schiefer Verteilung schlecht sein.","command":{"oracle":"SELECT sql_id, child_number, plan_hash_value, is_bind_sensitive, is_bind_aware\nFROM v$sql WHERE sql_id='&sqlid';","mssql":"SELECT qs.query_hash, qs.execution_count, qs.total_worker_time/qs.execution_count AS avg_cpu_us\nFROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t\nWHERE t.text LIKE '%...%';","postgres":"-- For generic vs custom plan behavior:\nPREPARE p AS SELECT /* your query */ 1;\nEXPLAIN EXECUTE p;"},"choices":[{"label":"Ja, der Plan wechselt je nach Parameter zwischen gut und schlecht","next":"sol_sniff"},{"label":"Der richtige Plan ist bekannt, ich will ihn dauerhaft fixieren","next":"sol_forceplan"}]},"sol_stats":{"kind":"solution","title":"Ursache: veraltete / fehlende Statistiken","detail":"Auf Basis alter, nicht der Realität entsprechender Statistiken hat der Optimizer die Zeilenzahl falsch geschätzt und einen schlechten Plan gewählt. Das ist die häufigste Ursache für Planregressionen.","steps":["Erhebe frische Statistiken für die betroffenen Tabellen und Indizes: Oracle DBMS_STATS.GATHER_TABLE_STATS, Postgres ANALYZE, MySQL ANALYZE TABLE, MSSQL UPDATE STATISTICS ... WITH FULLSCAN.","Lege Histogramme/erweiterte Statistiken für Spalten mit schiefer Verteilung an.","Verifiziere den Plan nach dem Erheben der Statistiken mit EXPLAIN und bestätige, dass er zum guten Plan zurückgekehrt ist.","Halte die automatische Statistikerhebung (Autovacuum/Auto Update Stats) mit passenden Schwellenwerten aktiv, um eine Wiederholung zu verhindern."],"related":["slow-queries"]},"sol_sniff":{"kind":"solution","title":"Ursache: Parameterempfindlichkeit (Sniffing / Bind Peeking)","detail":"Der gecachte Plan wurde für den zuerst eingetroffenen Parameterwert gewählt und ist für andere Werte bei schiefer Verteilung schlecht. Ziel ist sicherzustellen, dass ein zum Wert passender Plan gewählt wird.","steps":["Lege in MSSQL für die kritische Abfrage gezieltes Verhalten mit OPTION (RECOMPILE) oder OPTIMIZE FOR fest; bevorzuge Recompile bei häufig wechselnden Werten.","Nutze in Oracle Adaptive Cursor Sharing / bind-bewusstes Verhalten; teile die Abfrage bei Bedarf nach Wertebereich auf.","Beobachte in Postgres die Wahl zwischen generischem und individuellem Plan; steuere sie bei Bedarf mit plan_cache_mode.","Wird ein einzelner dauerhaft korrekter Plan benötigt, wechsle zur Lösung mit Plan-Fixierung."],"related":["slow-queries"]},"sol_forceplan":{"kind":"solution","title":"Lösung: den richtigen Plan fixieren (Baseline / Force Plan)","detail":"Gibt es einen bekannten guten Plan, stoppt seine Fixierung — damit der Optimizer nicht davon abweicht — die Regression dauerhaft.","steps":["Oracle: Lege für den guten Plan eine SQL Plan Baseline an (DBMS_SPM) oder akzeptiere ihn über SQL Plan Management.","MSSQL: Wähle den guten Plan im Query Store aus und erzwinge ihn mit sp_query_store_force_plan.","MySQL/Postgres: Steuere den Plan mit Optimizer-Hints (z. B. Index-Hints) oder pg_hint_plan.","Überwache die Performance nach der Fixierung; ändert sich das Datenmuster grundlegend, bewerte die Baseline neu."],"related":[]},"sol_datagrowth":{"kind":"solution","title":"Ursache: Das Datenvolumen ist gewachsen, der Plan passt nicht mehr","detail":"Mit wachsender Tabelle skaliert ein früher idealer Plan (z. B. Nested Loop) nun schlecht; der Optimizer braucht Statistiken und Zugriffspfade, die das Wachstum korrekt abbilden.","steps":["Erhebe aktuelle Statistiken, damit der Optimizer die neue Zeilenzahl sieht.","Lege passende Indizes für die wachsenden Tabellen an; korrigiere den Zugriffspfad, der nun schlecht skaliert (z. B. Full Scan/Nested Loop).","Begrenze bei sehr großen Tabellen die gescannte Datenmenge mit datums-/bereichsbasierter Partitionierung.","Schreibe die Abfrage passend zum Wachstum um (frühes Filtern, Paginierung, Zusammenfassungen/Materialized Views)."],"related":["slow-queries"]},"sol_planflip":{"kind":"solution","title":"Ursache: plötzlicher Planwechsel (Upgrade / Cache-Flush)","detail":"Der Plan hat sich nach einem Versionsupgrade, einer Parameteränderung oder dem Leeren des Plan-Cache plötzlich geändert. Das Verhalten des Optimizers oder die Umgebungsbedingungen haben sich verschoben.","steps":["Ermittle den Auslöser der Änderung: Upgrade/Patch, Optimizer-Parameter, ein Datenladevorgang oder ein Neustart/Cache-Flush.","Ist ein Parameter die Ursache, nimm die Änderung zurück und bewerte sie kontrolliert neu.","Fixiere den bekannten guten Plan (Baseline / Force Plan), damit der Optimizer nicht erneut abweicht.","Prüfe bei Regressionen nach einem Upgrade die Kompatibilitäts-/Readiness-Einstellungen des Optimizers."],"related":[]}}},"solver/replica-resync.json":{"id":"replica-resync","title":"Replica defekt / Resync nötig","icon":"🔗","category":"Replikation","intro":"Die Synchronisierung zwischen Replica/Standby und Primary ist unterbrochen. Bestimme, ob die Replikation gestoppt ist, wie weit sie zurückliegt und ob die Positions-/GTID-Kette gebrochen ist; wähle dann den richtigen Weg zwischen Überspringen eines Fehlers, Abwarten oder Neuaufbau (Reseed). Mit Split-Brain-Warnung.","start":"root","nodes":{"root":{"kind":"step","title":"Wie ist der Gesamtzustand der Replikation?","detail":"Lies zuerst den Replikationszustand: Laufen die Apply-Threads, wie groß ist der Verzug, gibt es einen Fehler? Verifiziere auch, dass nicht beide Knoten Schreibvorgänge annehmen (Split-Brain).","command":{"mysql":"SHOW REPLICA STATUS\\G\n-- Check: Replica_IO_Running, Replica_SQL_Running, Seconds_Behind_Source, Last_Error, Retrieved/Executed_Gtid_Set","postgres":"-- On the primary:\nSELECT client_addr, state, sent_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn,replay_lsn) AS lag_bytes FROM pg_stat_replication;\n-- On the replica:\nSELECT status, last_msg_receipt_time, latest_end_lsn FROM pg_stat_wal_receiver;","oracle":"SELECT name, value, unit, time_computed FROM v$dataguard_stats WHERE name IN ('transport lag','apply lag');\nSELECT dest_id, status, error, gap_status FROM v$archive_dest_status WHERE status<>'INACTIVE';","mssql":"SELECT ag.name AS ag, drs.synchronization_state_desc, drs.synchronization_health_desc,\n       drs.log_send_queue_size, drs.redo_queue_size\nFROM sys.dm_hadr_database_replica_states drs\nJOIN sys.availability_groups ag ON ag.group_id=drs.group_id;"},"choices":[{"label":"Replikation ist gestoppt / schlägt fehl","next":"error"},{"label":"Läuft, liegt aber weit zurück (großer Verzug)","next":"lag"},{"label":"GTID/Position gebrochen, inkonsistent — neu aufbauen","next":"sol_reseed"},{"label":"Beide Knoten schreiben als Primary (Verdacht auf Split-Brain)","next":"sol_splitbrain"}]},"error":{"kind":"step","title":"Lässt sich der Fehler reparieren, oder ist ein Neuaufbau nötig?","detail":"Untersuche den Apply-Fehler. Manche Fehler (Duplicate Key, fehlende Zeile) lassen sich durch sicheres Überspringen eines einzelnen Ereignisses beheben; bei Dateninkonsistenz/Korruption ist der Neuaufbau der Replica aber der einzig richtige Weg.","command":{"mysql":"SHOW REPLICA STATUS\\G\n-- Check: Last_SQL_Errno, Last_SQL_Error, Last_IO_Errno, Last_IO_Error","postgres":"-- Examine the replica logs (PostgreSQL log): recovery / WAL apply error messages.\nSELECT status, last_msg_receipt_time FROM pg_stat_wal_receiver;","mssql":"SELECT * FROM sys.dm_hadr_automatic_seeding;\nSELECT database_id, synchronization_state_desc, is_suspended, suspend_reason_desc\nFROM sys.dm_hadr_database_replica_states WHERE is_local=1;"},"choices":[{"label":"Ein/wenige Apply-Fehler (überspringbar)","next":"sol_skiperror"},{"label":"Nicht reparierbare Inkonsistenz / Korruption","next":"sol_reseed"}]},"lag":{"kind":"step","title":"Ist der Verzug vorübergehend oder dauerhaft?","detail":"Sieh dir den Verlauf des Verzugs über die Zeit an. Schließt er sich nach einer vorübergehenden Lastspitze (Aufholen), oder wächst er weiter, sodass die Replica den Primary nie einholt?","command":{"mysql":"SHOW REPLICA STATUS\\G\n-- Measure Seconds_Behind_Source several times: is it decreasing or increasing?","postgres":"-- Measure repeatedly on the replica:\nSELECT now()-pg_last_xact_replay_timestamp() AS replay_lag;","oracle":"SELECT name, value, time_computed FROM v$dataguard_stats WHERE name='apply lag';","mssql":"SELECT drs.redo_queue_size, drs.redo_rate, drs.log_send_queue_size\nFROM sys.dm_hadr_database_replica_states drs WHERE drs.is_local=0;"},"choices":[{"label":"Vorübergehende Lastspitze; Verzug schließt sich","next":"sol_lag_wait"},{"label":"Wächst weiter; kann nicht aufholen","next":"sol_lag_perm"}]},"sol_skiperror":{"kind":"solution","title":"Lösung: Apply-Fehler vorsichtig überspringen","detail":"Ist die Replikation an einem einzelnen Apply-Fehler (z. B. Duplicate Key) gestoppt und bist du sicher, dass die Daten tatsächlich konsistent sind, kannst du dieses Ereignis überspringen und den Stream fortsetzen. Das ist ein letztes Mittel; blindes Überspringen erzeugt stillen Daten-Drift.","steps":["Verstehe zuerst, warum der Fehler aufgetreten ist, und verifiziere die Datenkonsistenz, indem du die betroffene Zeile auf beiden Seiten vergleichst.","Bei MySQL ohne GTID ein einzelnes Ereignis überspringen: STOP REPLICA; SET GLOBAL sql_replica_skip_counter=1; START REPLICA; (vor 8.0.26: sql_slave_skip_counter; mit Vorsicht).","Mit GTID: Um die fehlschlagende Transaktion zu überspringen, markiere diese GTID mit einer leeren Transaktion und starte dann die Replica.","Führe nach dem Überspringen eine Konsistenzprüfung auf Zeilenebene (Checksumme) zwischen beiden Seiten durch; bei Drift wechsle zu einem Reseed."],"related":["replication-status"]},"sol_reseed":{"kind":"solution","title":"Lösung: Replica neu aufbauen (Reseed / Rebuild)","detail":"Ist die Positions-/GTID-Kette gebrochen, sind die Daten abgedriftet oder gibt es einen nicht reparierbaren Fehler, ist der einzig verlässliche Weg, die Replica aus einer frischen Kopie des Primary neu aufzubauen.","steps":["Erstelle ein konsistentes Basis-Backup vom Primary: MySQL xtrabackup/mysqldump oder CLONE, Postgres pg_basebackup, Oracle RMAN duplicate, MSSQL Voll- + Log-Backup.","Bereinige den alten Zustand auf der Replica und stelle das Backup wieder her (Postgres pg_basebackup -R, MSSQL RESTORE ... WITH NORECOVERY).","Starte die Replikation ab der richtigen Position/GTID: MySQL CHANGE REPLICATION SOURCE TO ...; START REPLICA; bei MSSQL die Datenbank erneut zur AG hinzufügen (Automatic Seeding).","Überwache das Aufholen; sobald der Verzug null erreicht, verifiziere die Konsistenz beider Seiten mit einer Checksumme."],"related":["replication-status"]},"sol_lag_wait":{"kind":"solution","title":"Ursache: vorübergehende Lastspitze (Verzug schließt sich)","detail":"Die Replikation ist gesund; der Verzug ist durch eine vorübergehende Schreibspitze auf dem Primary oder Netzwerkschwankungen entstanden, die Replica holt aber auf.","steps":["Bestätige mit mehreren Messungen, dass der Verzug tatsächlich abnimmt.","Leite bis zum Ende des Lastfensters bei Bedarf nur verzugsempfindliche Lesevorgänge an den Primary.","Tritt es wiederholt auf, verteile Schreibspitzen (Batch-Jobs, ETL) zeitlich oder verlege sie außerhalb der Spitzenzeiten.","Nimm Netzwerk-/Apply-Latenz ins Monitoring auf, damit vorübergehender Verzug erkannt wird, bevor er dauerhaft wird."],"related":["replication-status"]},"sol_lag_perm":{"kind":"solution","title":"Ursache: Die Replica hält mit dem Primary nicht Schritt","detail":"Die Apply-Seite (Apply/Redo) kommt mit der Änderungsrate des Primary nicht mit; der Verzug wächst ständig. Meist liegt es an Single-Thread-Apply, langsamer Replica-Hardware oder einem Netzwerkengpass.","steps":["Finde den Apply-Engpass: Begrenzt Single-Thread-Apply, Disk/CPU der Replica oder die Netzwerkbandbreite?","Aktiviere/erhöhe paralleles Apply: mehrere Replica Worker in MySQL (replica_parallel_workers); prüfe die Apply-Ressourcen in Postgres/Oracle.","Bringe die Replica-Hardware mindestens auf das Niveau des Primary (insbesondere Disk-IOPS).","Reduziere unnötige schwere Leseabfragen auf der Replica, um Ressourcen für Apply freizuhalten; hält sie dennoch nicht mit, bewerte nach einem Reseed neu."],"related":["replication-status"]},"sol_splitbrain":{"kind":"solution","title":"WARNUNG: Split-Brain (zwei Primaries) — Datenverlustrisiko","detail":"Nehmen beide Knoten in der Primary-Rolle Schreibvorgänge an, häufen sich auf beiden Seiten widersprüchliche Änderungen; das ist die gefährlichste Lage und kann zu Datenverlust führen. Vorrang hat, Schreibvorgänge auf einen einzigen Knoten zu beschränken.","steps":["Bündle Schreibvorgänge sofort auf einem Knoten: Leite den Anwendungstraffic strikt an einen Primary, setze den anderen auf read-only/isoliere ihn.","Bestimme den maßgeblichen (korrekten) Knoten: Entscheide, wessen Daten aus Geschäftssicht als gültig gelten.","Vergleiche die widersprüchlichen (abweichenden) Änderungen auf beiden Seiten; gleiche die kritischen Änderungen der unterlegenen Seite manuell ab.","Baue den anderen Knoten per Reseed mit dem maßgeblichen Knoten als Quelle neu auf; richte dann einen Fencing-/Quorum-Mechanismus ein, der doppelte Primaries verhindert."],"related":["replication-status"]}}},"solver/cache-hit-low.json":{"id":"cache-hit-low","title":"Niedrige Cache-Trefferquote (Buffer-/Cache-Hit)","icon":"🎯","category":"Performance","intro":"Die Cache-Trefferquote ist niedrig; Daten werden ständig von der Disk (oder aus der Quelle) gelesen. Bestimme, ob das Problem im Buffer/Shared Cache der DB oder im Redis-/Anwendungscache liegt, miss es und behebe es.","start":"root","nodes":{"root":{"kind":"step","title":"Welcher Cache hat eine niedrige Trefferquote?","detail":"Miss die Trefferquote und grenze das Problem ein: Ist der Buffer/Shared Cache der Datenbank niedrig (viele physische Lesevorgänge), oder hat der Redis-/Anwendungscache viele Misses und Evictions?","command":{"oracle":"SELECT ROUND((1-(pr.value/(dbg.value+cg.value)))*100,2) AS buffer_hit_pct\nFROM v$sysstat pr, v$sysstat dbg, v$sysstat cg\nWHERE pr.name='physical reads' AND dbg.name='db block gets' AND cg.name='consistent gets';","postgres":"SELECT ROUND(sum(blks_hit)*100.0/nullif(sum(blks_hit)+sum(blks_read),0),2) AS cache_hit_pct\nFROM pg_stat_database;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';\nSHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';","mssql":"SELECT cntr_value AS buffer_cache_hit_ratio\nFROM sys.dm_os_performance_counters\nWHERE counter_name='Buffer cache hit ratio' AND object_name LIKE '%Buffer Manager%';","redis":"INFO stats\n# Check the keyspace_hits / keyspace_misses ratio and evicted_keys in INFO memory"},"choices":[{"label":"Trefferquote des DB-Buffer/Shared Cache ist niedrig","next":"dbcache"},{"label":"Viele Misses / Evictions im Redis-/Anwendungscache","next":"rediscache"}]},"dbcache":{"kind":"step","title":"Warum wird von der Disk gelesen: Größe oder Scan-Muster?","detail":"Eine niedrige Buffer-Trefferquote hat zwei typische Ursachen: Die häufig genutzten Daten (Working Set) passen nicht in den Speicher (Cache zu klein), oder große Full Table Scans verschmutzen den Cache mit Einmal-Blöcken und verdrängen nützliche Pages.","command":{"oracle":"SELECT component, current_size/1024/1024 AS mb FROM v$sga_dynamic_components WHERE component LIKE '%buffer cache%';\nSELECT sql_id, disk_reads, executions FROM v$sql ORDER BY disk_reads DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SHOW shared_buffers;\nSELECT pg_size_pretty(pg_database_size(current_database())) AS db_size;\nSELECT query, shared_blks_read FROM pg_stat_statements ORDER BY shared_blks_read DESC LIMIT 10;","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';\nSELECT ROUND(SUM(data_length+index_length)/1024/1024) AS data_mb FROM information_schema.tables;","mssql":"SELECT physical_memory_kb/1024 AS server_ram_mb FROM sys.dm_os_sys_info;\nSELECT COUNT(*)*8/1024 AS buffer_pool_mb FROM sys.dm_os_buffer_descriptors;"},"choices":[{"label":"Working Set passt nicht in den RAM (Cache zu klein)","next":"sol_sizing"},{"label":"Full Table Scans verschmutzen den Cache","next":"sol_scan"}]},"rediscache":{"kind":"step","title":"Warum die Redis-Misses: maxmemory-Eviction oder TTL-/Schlüsselverwaltung?","detail":"Ist die Redis-Trefferquote niedrig, hat entweder der Speicher maxmemory erreicht und Schlüssel werden verdrängt, oder die TTL-Verwaltung/das Caching-Muster der Schlüssel ist schwach (kalte Schlüssel, geringe Wiederverwendung).","command":{"redis":"INFO memory\n# used_memory, maxmemory, maxmemory_policy, evicted_keys\nCONFIG GET maxmemory-policy\nINFO keyspace\n# Key count and expires (keys with TTL) ratio per db"},"choices":[{"label":"maxmemory voll, hohe Eviction","next":"sol_maxmem"},{"label":"Schwaches TTL-/Schlüsselmuster (geringe Wiederverwendung)","next":"sol_ttl"}]},"sol_sizing":{"kind":"solution","title":"Ursache: Der Cache ist zu klein für das Working Set","detail":"Die häufig genutzten Daten sind größer als der zugewiesene Buffer/Cache, daher werden sie ständig verdrängt und erneut von der Disk gelesen. Die Trefferquote ist niedrig, die physischen Lesevorgänge sind hoch.","steps":["Vergrößere den Cache, bis er das Working Set abdeckt: Oracle db_cache_size/SGA, Postgres shared_buffers, MySQL innodb_buffer_pool_size, MSSQL max server memory.","Reicht der physische RAM nicht, erweitere den Serverspeicher; das ist die Hauptressource, mit der die DB skaliert.","Verkleinere das Working Set: Alte Daten archivieren/partitionieren, unnötige Spalten/Indizes entfernen.","Vergleiche nach der Änderung Trefferquote und physische Lesevorgänge, um die Verbesserung zu bestätigen."],"related":["cache-hit-ratio"]},"sol_scan":{"kind":"solution","title":"Ursache: Das Scan-Muster verschmutzt den Cache","detail":"Große Full Table Scans füllen den Cache mit Blöcken, die einmal genutzt und verworfen werden, und verdrängen heiße Pages; das senkt die Trefferquote.","steps":["Hole die Ausführungspläne der Abfragen mit den meisten physischen Lesevorgängen und wandle Full Scans in Indexzugriffe um (passende Indizes anlegen).","Wähle nur die benötigten Spalten statt 'SELECT *'; verringere die Menge gescannter Blöcke.","Grenze Scans bei großen Tabellen per Partitionierung auf den relevanten Bereich ein.","Trenne schwere Analyse-/Scan-Jobs vom OLTP-Cache (eine Read Replica oder ein separates Zeitfenster)."],"related":["slow-queries","cache-hit-ratio"]},"sol_maxmem":{"kind":"solution","title":"Ursache: Redis-maxmemory-Eviction","detail":"Der Redis-Speicher hat das Limit maxmemory erreicht; alte Schlüssel werden verdrängt, um Platz für neue zu schaffen (evicted_keys steigt ständig). Dadurch gehen noch benötigte Einträge verloren und die Trefferquote sinkt.","steps":["Erhöhe maxmemory passend zum echten Working Set oder verkleinere den Datenbestand.","Wähle die Eviction-Policy passend zum Zugriffsmuster (z. B. LRU/LFU: allkeys-lfu trennt heiß und kalt oft wirksam).","Sortiere große/selten genutzte Strukturen aus; verkleinere Werte, entferne unnötige Felder, nutze passende Datentypen.","Ist der Bedarf dauerhaft, skaliere Redis (mehr RAM oder Cluster/Sharding) und überwache evicted_keys."],"related":[]},"sol_ttl":{"kind":"solution","title":"Ursache: schwaches TTL-/Caching-Muster","detail":"Entweder bekommen Schlüssel gar keine TTL, sodass sich der Speicher mit kalten Daten füllt, oder das Caching-Muster hat eine geringe Wiederverwendung, sodass die meisten Requests als Miss enden.","steps":["Weise gecachten Schlüsseln passende TTLs zu, damit abkühlende Daten von selbst ablaufen.","Cache häufig gelesene (heiße) Daten; cache keine selten genutzten Daten (vermeide, den Cache mit Einträgen zu füllen, die nie getroffen werden).","Implementiere im Cache-Aside-Muster das Befüllen nach einem Miss korrekt und nutze Locking/Request-Deduplizierung gegen die „Thundering Herd“.","Überwache das Verhältnis keyspace_hits/misses und verfeinere TTL- und Schlüsseldesign anhand der Daten."],"related":[]}}},"solver/arch-oltp.json":{"id":"arch-oltp","title":"Referenzarchitektur: dreistufige OLTP-Anwendung","icon":"🏛️","category":"Referenzarchitektur","intro":"Klassische Geschäftsanwendungen (Bestellungen, Zahlungen, Lager) führen viele kurze, einzeilige, konsistenzkritische Transaktionen aus (OLTP). Dieser Baum zeigt Schritt für Schritt, ausgehend vom Lastprofil, wann die primäre relationale Datenbank, ein Connection Pooler, Read Replicas, eine Cache-Schicht, Backup + PITR und Monitoring ins Spiel kommen. Ziel ist nicht, „alles von Anfang an hinzuzufügen“, sondern die richtige Komponente dann zu ergänzen, wenn der jeweilige Engpass nachgewiesen ist.","start":"root","nodes":{"root":{"kind":"step","title":"Wie sieht dein Lastprofil aus: Transaktionsmix und Konsistenzanforderungen?","detail":"Die erste Entscheidung in einer OLTP-Architektur ist, ob die Workload wirklich transaktional ist (ein kurzer, ACID-pflichtiger Mix aus Schreib- und Lesevorgängen). Sind die Daten naturgemäß relational und ist Konsistenz kritisch (Geld, Bestand, Bestellungen), besteht die Workload überwiegend aus Lesevorgängen, oder ist die Last noch sehr gering?","choices":[{"label":"Kurze Transaktionen, starke Konsistenz (Geld/Bestand/Bestellungen)","next":"primary_db"},{"label":"Lesevorgänge dominieren stark, wenige Schreibvorgänge, und der Traffic wächst","next":"scale_reads"},{"label":"Die Last ist sehr gering; reicht das einfachste Setup?","next":"sol_single_node"}]},"primary_db":{"kind":"step","title":"Hast du die primäre Datenbank-Engine gewählt, und sorgst du dich um die Skalierung?","detail":"Eine relationale Engine ist die richtige Grundlage für den transaktionalen Kern (PostgreSQL, MySQL/InnoDB, Oracle, SQL Server). Steht die Engine fest, ist die nächste Frage die Skalierung: Reicht vorerst eine einzelne Instanz, oder steigen Verbindungsanzahl und Leselast?","choices":[{"label":"Engine steht fest; eine einzelne Instanz reicht vorerst","next":"sol_single_node"},{"label":"Die Verbindungsanzahl ist sprunghaft gestiegen, die Ressourcen gehen zur Neige","next":"sol_pooler"},{"label":"Die Leselast wächst und belastet die primäre Schreibinstanz","next":"scale_reads"}]},"scale_reads":{"kind":"step","title":"Wie skalierst du Lesevorgänge: Replica oder Cache?","detail":"Belastet die Leselast die primäre Instanz, gibt es zwei sich ergänzende Optionen. Lesevorgänge, die leicht verzögerte Daten tolerieren, etwa Berichte/Listen, lassen sich an eine Read Replica leiten; werden dieselben „heißen“ Daten immer wieder angefragt, entlastet ein Cache mit Mikrosekundenlatenz (Redis) die DB.","choices":[{"label":"Viele Berichts-/Listen-Lesevorgänge; leichter Verzug ist akzeptabel","next":"sol_read_replica"},{"label":"Dieselben beliebten Daten werden immer wieder gelesen","next":"sol_cache"}]},"sol_single_node":{"kind":"solution","title":"Empfehlung: Mit einem einzelnen Primary + Pool + Backup beginnen","detail":"Halte die Architektur anfangs einfach: Eine einzelne relationale Primary-Instanz, ein Connection Pool davor und regelmäßige Backups reichen. Füge Komplexität erst hinzu, wenn ein gemessener Engpass es erzwingt.","steps":["Primäre DB: eine ACID-konforme relationale Engine wie PostgreSQL oder MySQL/InnoDB; richte Schema, Constraints und Indizes korrekt ein.","Connection Pool: Setze von Anfang an einen anwendungsseitigen Pool (z. B. HikariCP) oder ein serverseitiges PgBouncer ein; das verhindert Verbindungsstürme.","Backup + PITR: tägliche Vollbackups + WAL-/binlog-Archivierung für Point-in-Time-Recovery; lagere Backups in separatem Storage.","Achtung: Füge Replicas/Sharding/Cache nicht früh hinzu; ein nicht gemessenes Problem lösen zu wollen, vergrößert nur die Fehlerfläche.","Monitoring: Erfasse von Anfang an Metriken zu Latenz, Fehlerrate und Verbindungsanzahl; verpasse das Wachstumssignal nicht."],"related":["decide-pooling","decide-backup"]},"sol_pooler":{"kind":"solution","title":"Empfehlung: Connection Pooler hinzufügen","detail":"Öffnet bei OLTP jede Anwendungsinstanz eigene Verbindungen, erstickt die primäre DB bei Tausenden Verbindungen an Speicher- und Scheduler-Overhead. Ein Pooler dazwischen teilt wenige echte DB-Verbindungen auf viele Anwendungsrequests auf.","steps":["PostgreSQL: PgBouncer (Transaction-Pooling-Modus) reduziert viele Clients auf wenige Backend-Verbindungen.","MySQL: ProxySQL bietet auf ähnliche Weise Connection Multiplexing und Routing.","Poolgröße: in der Regel proportional zur CPU-Kernanzahl; einige Dutzend Backend-Verbindungen reichen. „Je mehr, desto besser“ gilt nicht.","Achtung: Beim Transaction Pooling sind Funktionen auf Session-Ebene (Prepared Statements, temporäre Tabellen, Session-Variablen) eingeschränkt; schreibe die Anwendung entsprechend.","Als Nächstes: Ist auch der Pool ausgelastet, liegt das Problem nicht an den Verbindungen, sondern an der Abfrage-/Transaktionsdauer; spüre langsame Abfragen und lange Transaktionen auf."],"related":["decide-pooling","connection-storm"]},"sol_read_replica":{"kind":"solution","title":"Empfehlung: Read Replica hinzufügen","detail":"Schreibvorgänge bleiben auf dem Primary, während Read-only-Traffic (Berichte, Listen, Suche) an eine oder mehrere vom Primary gespeiste asynchrone Replicas geleitet wird. Das macht den Primary frei für Schreibvorgänge und konsistenzkritische Lesevorgänge.","steps":["Topologie: Primary -> eine oder mehrere asynchrone Replicas; die Anwendung trennt Lese- und Schreibvorgänge und schickt Lesevorgänge an die Replica.","Engine: PostgreSQL Streaming Replication, MySQL-Replicas oder verwaltete Read Replicas.","Wann: für Lesevorgänge, die etwas Verzug tolerieren, etwa Berichte/Analysen/Listen; wenn du CPU/IO des Primary entlasten willst.","Achtung: Replikationsverzögerung erzeugt veraltete Lesevorgänge; bediene Abläufe, die „das eigene Schreiben sofort lesen“ müssen, vom Primary (Read-your-Writes).","Als Nächstes: Reichen auch Replicas nicht, erwäge einen Cache für heiße Daten oder Sharding auf der Schreibseite."],"related":["replication-lag","decide-shard-replica"]},"sol_cache":{"kind":"solution","title":"Empfehlung: Cache-Schicht hinzufügen (Redis)","detail":"Werden dieselben beliebten Daten (Sessions, Katalog, Profile, Seitenfragmente) wiederholt gelesen, setze einen In-Memory-Cache vor die DB und bediene die meisten Lesevorgänge, ohne die DB überhaupt zu erreichen. Die Latenz sinkt und die primäre DB wird entlastet.","steps":["Engine: das Cache-Aside-Muster mit Redis (oder Memcached); die Anwendung prüft zuerst den Cache und holt bei einem Miss aus der DB und schreibt in den Cache.","TTL + Invalidierung: Gib jedem Schlüssel eine sinnvolle TTL; lösche/aktualisiere beim Schreiben den zugehörigen Schlüssel, damit keine veralteten Daten bleiben.","Wann: viele Lesevorgänge, Daten ändern sich relativ selten, und dieselben Schlüssel werden wiederholt angefragt.","Achtung: Cache Stampede; läuft ein beliebter Schlüssel ab, können Tausende Requests gleichzeitig die DB treffen. Nutze Locking/Single-Flight und TTLs mit Jitter.","Achtung: Behandle den Cache nicht als „Source of Truth“; die dauerhafte Wahrheit liegt immer in der DB, und die Anwendung muss auch bei einem Cache-Ausfall weiterlaufen, indem sie auf die DB zurückgreift."],"related":["decide-cache","cache-hit-low"]}}},"solver/arch-dwh.json":{"id":"arch-dwh","title":"Referenzarchitektur: Data Warehouse / Analyse","icon":"🏛️","category":"Referenzarchitektur","intro":"Analytische Abfragen (Reporting, Aggregation, breite Scans) unterscheiden sich grundlegend von OLTP-Transaktionen: wenige, aber sehr breite, lang laufende Abfragen. Ausgehend von der Entscheidung, Analysen von OLTP zu trennen, geht dieser Baum Schritt für Schritt Komponenten wie Lademethode (ETL vs. ELT), spaltenorientierten Speicher/Columnstore, Star-Schema-Modellierung, Partitionierung + Komprimierung und Vorberechnung mit Materialized Views durch und wägt dabei zwischen Parallelität und Durchsatz ab.","start":"root","nodes":{"root":{"kind":"step","title":"Läuft deine Analyse-Workload auf derselben Datenbank wie OLTP?","detail":"Der häufigste Fehler ist, schwere Berichte auf der produktiven Transaktions-DB laufen zu lassen und so die OLTP-Latenz zu verschlechtern. Die erste Entscheidung ist die Trennung: Laufen Analysen noch auf der OLTP-Instanz, baust du eine separate Analyseumgebung auf, oder entscheidest du gerade, wie die Daten dorthin gelangen?","choices":[{"label":"Berichte laufen noch auf dem produktiven OLTP; sie bremsen OLTP","next":"sol_separate"},{"label":"Ein separates Warehouse existiert; wie soll ich Daten laden?","next":"load_method"},{"label":"Ein Warehouse existiert; Abfragen sind weiterhin langsam, wie soll ich modellieren?","next":"model"}]},"load_method":{"kind":"step","title":"Wo transformierst du: vor oder nach dem Laden?","detail":"Wirst du beim Übertragen von Daten aus den Quellen ins Warehouse Bereinigung/Transformation vor dem Laden in einer separaten Verarbeitungsschicht erledigen (ETL), oder die Rohdaten zuerst ins Warehouse laden und mit SQL in der leistungsstarken Warehouse-Engine transformieren (ELT)? Die Entscheidung hängt von der Rechenleistung des Warehouse und vom Bedarf an Schemaflexibilität ab.","choices":[{"label":"Transformation ist komplex/extern; vor dem Laden erledigen","next":"sol_etl"},{"label":"Das Warehouse ist leistungsstark; roh laden, intern mit SQL transformieren","next":"sol_elt"}]},"model":{"kind":"step","title":"Liegt der Abfrageengpass im Speicherlayout oder im Datenmodell?","detail":"Sind Analysen langsam, gibt es zwei Hauptstellschrauben. Breite Scans lesen bei zeilenbasiertem Speicher die ganze Zeile; spaltenorientierter Speicher liest nur die benötigten Spalten und komprimiert gut. Auf Modellseite bedeutet übermäßige Normalisierung viele Joins, die Abfragen bremsen; ein Star-Schema (Fakten + Dimensionen) vereinfacht die Analyse.","choices":[{"label":"Breite Aggregations-Scans sind langsam; wenige Spalten werden gelesen","next":"sol_columnar"},{"label":"Zu viele Joins, komplexes normalisiertes Schema","next":"sol_star"},{"label":"Derselbe schwere Bericht liefert wiederholt dasselbe Ergebnis","next":"sol_matview"}]},"sol_separate":{"kind":"solution","title":"Empfehlung: Analysen von OLTP trennen","detail":"Transaktionale und analytische Workloads ersticken einander auf derselben Engine: Lange Berichte halten Sperren/IO und verzögern kurze Transaktionen. Analysen auf ein separates Ziel zu verlagern (eine Read Replica oder ein separates Analyse-Warehouse), entlastet beide Seiten.","steps":["Erster Schritt: Leite leichte Berichte an eine OLTP-Read-Replica und schwere Analysen an eine separate Analyse-Engine.","Analyse-Engine: Eine spaltenorientierte/analytische Engine (z. B. SQL Server mit Columnstore, ClickHouse oder ein spaltenorientiertes Warehouse) ist bei breiten Scans um ein Vielfaches schneller als zeilenbasierter Speicher.","Parallelität vs. Durchsatz: OLTP ist auf viele kleine Transaktionen (hohe Parallelität) ausgelegt, das Warehouse auf wenige riesige Abfragen (hoher Durchsatz); beides lässt sich nicht auf derselben Maschine optimieren.","Achtung: Das Warehouse ist keine „Echtzeitkopie der Produktion“; es enthält in Intervallen geladene Daten, verzögert, aber für die Analyse organisiert.","Als Nächstes: Entscheide nach der Trennung über Lademethode (ETL/ELT) und Modell (Star-Schema)."],"related":["decide-sqlnosql","slow-query"]},"sol_etl":{"kind":"solution","title":"Empfehlung: ETL (vor dem Laden transformieren)","detail":"Bei ETL werden Daten aus den Quellen geholt (Extract), in einer separaten Verarbeitungsschicht bereinigt und transformiert (Transform) und erst dann ins Warehouse geladen (Load). Nur saubere, modellkonforme Daten gelangen ins Warehouse.","steps":["Wann: Die Transformation ist komplex, du musst mehrere Quellen kombinieren, oder Roh-/sensible Daten sollen nie ins Warehouse gelangen.","Ablauf: Quelle (OLTP/Datei/API) → Transformationsschicht → Batch-Load ins Warehouse.","Pro: Hält den Warehouse-Speicher sauber; sensible Daten lassen sich vor dem Laden maskieren.","Contra: Die Transformation hängt von der Leistung eines separaten Systems ab; sie kann die SQL-Flexibilität des Warehouse nicht nutzen.","Achtung: Lade in einem Batch-Fenster (z. B. nachts); plane so, dass sich das Laden nicht mit schweren Abfragen überschneidet."],"related":["decide-capacity"]},"sol_elt":{"kind":"solution","title":"Empfehlung: ELT (im Warehouse transformieren)","detail":"Bei ELT werden Rohdaten zuerst ins Warehouse geladen und die Transformation erfolgt in der leistungsstarken SQL-Engine des Warehouse selbst. Da moderne spaltenorientierte Warehouses hohe Rechenleistung haben, bewegst du die Daten einmal und verarbeitest sie intern, statt die Transformation zu den Daten zu bringen.","steps":["Wann: Die Warehouse-Engine ist leistungsstark und skalierbar; du willst Rohdaten auch aufbewahren (Audit/Neuverarbeitung).","Ablauf: Quelle → Rohschicht (Staging) → SQL-/Transformations-Views → modellierte Fakten-/Dimensionsschicht.","Pro: Eine Engine, flexibel; da Rohdaten erhalten bleiben, kannst du die Transformation später ändern und neu erzeugen.","Contra: Rohe (eventuell sensible) Daten gelangen ins Warehouse; Zugriffskontrolle und Maskierung sind Pflicht.","Achtung: Modelliere in Schichten (Raw → Staging → Mart); schreibe Transformationen idempotent, damit erneute Läufe sicher sind."],"related":["decide-capacity"]},"sol_columnar":{"kind":"solution","title":"Empfehlung: spaltenorientierter Speicher / Columnstore + Komprimierung","detail":"Analytische Abfragen scannen typischerweise wenige Spalten über alle Zeilen. Spaltenorientierter Speicher hält Daten Spalte für Spalte; er liest nur die benötigten Spalten, komprimiert sehr gut, weil ähnliche Werte nebeneinander liegen, und das IO sinkt drastisch.","steps":["Engine/Funktion: SQL Server Columnstore Index, ClickHouse oder spaltenorientierte Warehouses; spaltenorientierte Erweiterungen auf PostgreSQL-Seite.","Komprimierung: Das spaltenorientierte Layout ergibt hohe Komprimierung; weniger Disk, weniger gelesene Bytes, schnellere Scans.","Partitionierung: Partitioniere große Faktentabellen nach Datum/Bereich, damit Partition Pruning irrelevante Partitionen komplett überspringt.","Achtung: Spaltenorientierung ist schlecht für viele einzeilige Updates/Punktabfragen; verlagere OLTP nicht dorthin.","Parallelität vs. Durchsatz: Spaltenorientierung glänzt bei wenigen riesigen Scan-Abfragen, nicht bei Tausenden kleiner paralleler Abfragen."],"related":["decide-partition","io-bottleneck"]},"sol_star":{"kind":"solution","title":"Empfehlung: Star-Schema (Fakten + Dimensionen)","detail":"In einem Analysemodell steht im Zentrum eine Faktentabelle mit den Kennzahlen, umgeben von beschreibenden Dimensionstabellen. Verglichen mit einem übernormalisierten Schema sinkt die Zahl der Joins, Abfragen werden einfacher und die Engine erzeugt bessere Pläne.","steps":["Struktur: Die Fakten (Kennzahlen + Dimensionsschlüssel) im Zentrum; Dimensionen (Zeit, Produkt, Kunde) bilden einen Stern darum.","Wann: Berichte joinen viele Tabellen und das normalisierte Schema ertrinkt in Joins.","Denormalisierung: Gezielte Denormalisierung ist in der Analyse normal; die Konsistenz wird im Ladeprozess (ETL/ELT) sichergestellt.","Als Nächstes: Berechne häufig ausgeführte schwere Aggregationen mit Materialized Views vor.","Achtung: Stelle OLTP nicht auf ein Star-Schema um; dieses Modell ist für Analysen, nicht für transaktionale Schreibvorgänge."],"related":["decide-index"]},"sol_matview":{"kind":"solution","title":"Empfehlung: Vorberechnung mit Materialized Views","detail":"Läuft dieselbe schwere Aggregation (Tagesumsatz, Monatszusammenfassung) wiederholt und liefert dasselbe Ergebnis, speichere das Ergebnis physisch in einer Materialized View und lass Berichte das vorberechnete Ergebnis lesen. Die schwere Berechnung läuft einmal und Abfragen antworten sofort.","steps":["Wann: Bestimmte Zusammenfassungsabfragen scannen häufig und wiederholt dieselbe große Tabelle.","Aktualisierung: Aktualisiere die Materialized View nach Zeitplan (z. B. nächtlicher Batch oder inkrementell); lege die Frequenz nach Aktualitätsbedarf fest.","Engine: PostgreSQL Materialized View (REFRESH), Oracle Materialized View (sogar Query Rewrite), SQL Server Indexed View.","Achtung: Die View bringt ein Aktualitätsfenster mit sich; wo Echtzeitdaten gefordert sind, ist eine Live-Abfrage Pflicht.","Als Nächstes: Sind die Aktualisierungskosten hoch, erwäge inkrementelle oder partitionsbasierte Aktualisierung."],"related":["decide-partition"]}}},"solver/arch-microservice.json":{"id":"arch-microservice","title":"Referenzarchitektur: Datenschicht für Microservices","icon":"🏛️","category":"Referenzarchitektur","intro":"Bei Microservices ist die wichtigste Entscheidung die Datenhoheit: Jeder Service muss seine Daten besitzen (Database-per-Service) und darf nicht direkt auf die Tabellen eines anderen Service zugreifen. Dieser Baum geht Schritt für Schritt das Shared-DB-Anti-Pattern, serviceübergreifende Konsistenz ohne verteilte Transaktionen (Saga + Outbox), Datensynchronisierung per CDC und serviceübergreifende Abfragen (API Composition vs. CQRS-Lesemodell) durch. Ziel ist, Services wirklich unabhängig und separat deploybar zu halten.","start":"root","nodes":{"root":{"kind":"step","title":"Wie besitzen Services Daten: eine gemeinsame DB oder eine pro Service?","detail":"Datenisolation ist das Rückgrat einer Microservice-Architektur. Nutzen Services eine einzige gemeinsame Datenbank (eine versteckte Abhängigkeit), hat jeder Service eine eigene Datenbank (das richtige Muster), oder liegt die eigentliche Herausforderung bei serviceübergreifender Konsistenz/Abfragen? Kläre zuerst den aktuellen Stand.","choices":[{"label":"Alle Services nutzen eine einzige gemeinsame DB","next":"sol_db_per_service"},{"label":"Jeder Service hat eine eigene DB, aber Abläufe brauchen serviceübergreifende Konsistenz","next":"consistency"},{"label":"Jeder Service hat eine eigene DB, aber kombinierte Abfragen/Berichte sind nötig","next":"query"}]},"consistency":{"kind":"step","title":"Wie hältst du einen serviceübergreifenden Ablauf konsistent?","detail":"Für eine Operation über mehrere Services (Bestellung anlegen → Zahlung einziehen → Bestand verringern) ist eine klassische verteilte Transaktion (2PC) fragil und skaliert nicht. Teile den Ablauf stattdessen in Schritte aus lokaler Transaktion + Ereignis auf (Saga) und veröffentliche Ereignisse zuverlässig (Outbox). Mit welchem Problem hast du es zu tun?","choices":[{"label":"Ich versuche, einen Ablauf über mehrere Services atomar zu halten (2PC?)","next":"sol_saga"},{"label":"Ich bekomme lokales Schreiben und Ereignisveröffentlichung nicht konsistent (Dual Write)","next":"sol_outbox"}]},"query":{"kind":"step","title":"Wie bedienst du eine Abfrage, die Daten aus mehreren Services anzeigt?","detail":"Sobald Daten auf Services verteilt sind, kannst du keine kombinierte Ansicht mit einem einzigen Join aufbauen. Es gibt zwei Hauptansätze: zur Anfragezeit die APIs der betreffenden Services aufrufen und die Ergebnisse zusammenführen (API Composition) oder ein vorab verknüpftes, über Events gespeistes Lesemodell pflegen (CQRS). Häufigkeit und Komplexität der Abfrage bestimmen die Wahl.","choices":[{"label":"Einfacher, seltener Join; einige Services aufzurufen und zusammenzuführen reicht","next":"sol_api_composition"},{"label":"Komplexe/häufige kombinierte Abfrage; ein fertiges Lesemodell wird benötigt","next":"sol_cqrs"}]},"sol_db_per_service":{"kind":"solution","title":"Empfehlung: Database-per-Service (gemeinsame DB aufteilen)","detail":"Services, die sich eine Datenbank teilen, sind eine versteckte und gefährliche Abhängigkeit: Die Schemaänderung eines Service bricht die anderen, die Zuständigkeit für Tabellen wird unklar und unabhängiges Deployment wird unmöglich. Jeder Service muss seine Daten besitzen.","steps":["Prinzip: Jeder Service liest/schreibt nur seine eigene Datenbank; er kann nicht direkt auf die Tabellen eines anderen Service zugreifen, sondern Daten nur über dessen API anfordern.","Engine-Unabhängigkeit: Jeder Service kann eine Engine nach seinem Bedarf wählen (z. B. PostgreSQL für Bestellungen, MongoDB für den Katalog, Elasticsearch für die Suche, Redis für den Warenkorb).","Anti-Pattern: Die „gemeinsame Datenbank“: gemeinsame Tabellen, Cross-Joins und ein einziges Schema; das ist kein Microservice, sondern ein verteilter Monolith.","Zerlegung: Zuerst logische Trennung (separates Schema/separater Benutzer), dann physische Trennung; tabellenübergreifende Joins entfernen.","Achtung: Nach der Aufteilung entstehen Probleme mit Konsistenz (Saga/Outbox) und kombinierten Abfragen (Composition/CQRS); das sind die natürlichen Kosten dieses Übergangs."],"related":["decide-isolation","decide-sqlnosql"]},"sol_saga":{"kind":"solution","title":"Empfehlung: Saga-Muster (verteilte Transaktionen vermeiden)","detail":"Versuche nicht, eine Operation über mehrere Services mit einer einzigen verteilten Transaktion (2PC) atomar zu machen; 2PC hält Sperren und bringt Fragilität an einem einzelnen Punkt und geringe Verfügbarkeit. Teile den Ablauf stattdessen in Schritte, die jeweils eine lokale Transaktion im eigenen Service sind, und mach sie bei einem Fehler mit Kompensationsschritten rückgängig.","steps":["Prinzip: Modelliere den Ablauf als Kette lokaler Transaktionen + Ereignisse; jeder Schritt löst den nächsten aus.","Kompensation: Schlägt ein Schritt fehl, führe kompensierende Aktionen aus, die die vorherigen Schritte rückgängig machen (z. B. Bestellung stornieren, wenn die Zahlung scheitert).","Choreografie vs. Orchestrierung: In einfachen Abläufen lösen Services einander per Ereignis aus (Choreografie); in komplexen Abläufen steuert ein zentraler Orchestrator die Schritte.","Achtung: Eine Saga ist nicht atomar; sie bietet Eventual Consistency. Zwischenzustände können von außen sichtbar sein, gestalte Geschäftsregeln entsprechend.","Achtung: Nutze kein 2PC/XA in hochskalierten Microservices; Blockierung und Sperrdauer bringen das System zum Stillstand."],"related":["decide-isolation","long-transaction"]},"sol_outbox":{"kind":"solution","title":"Empfehlung: Transactional Outbox (die Dual-Write-Falle)","detail":"Will ein Service sowohl in seine eigene DB schreiben als auch ein Ereignis veröffentlichen, ist es die „Dual Write“-Falle, beides getrennt zu tun: Gelingt das eine und scheitert das andere, bleibt das System inkonsistent. Die Lösung ist, das Ereignis in DERSELBEN lokalen Transaktion wie die Geschäftsdaten in eine Outbox-Tabelle zu schreiben und es dann mit einem separaten Prozess zu veröffentlichen.","steps":["Prinzip: Geschäftsdatensatz + Ereignisdatensatz werden in einer lokalen Transaktion in die Outbox-Tabelle geschrieben; entweder geschieht beides oder nichts (atomar).","Veröffentlichung: Ein separater Relay-/CDC-Prozess liest Outbox-Zeilen, veröffentlicht sie an einen Message Broker (z. B. Kafka) und markiert sie.","Mit CDC: Ein CDC-Tool wie Debezium liest das DB-Log (WAL/binlog) und überträgt Outbox-Änderungen zuverlässig nach Kafka.","Idempotenz: Consumer können dasselbe Ereignis mehrfach erhalten; verarbeite idempotent anhand der Ereignis-ID (gehe von At-least-once-Zustellung aus).","Achtung: Nutze nicht direkt den Ansatz „erst in die DB schreiben, dann nach Kafka veröffentlichen“; ein Absturz beim Veröffentlichen führt zu Ereignisverlust."],"related":["replication-lag"]},"sol_api_composition":{"kind":"solution","title":"Empfehlung: API Composition (zum Abfragezeitpunkt zusammenführen)","detail":"Wo kombinierte Daten nötig sind, ruft der Aufrufer (z. B. ein Gateway/BFF) die APIs der relevanten Services auf und führt die Ergebnisse im Speicher zusammen. Das ist die einfachste Lösung für einfache Joins über wenige Services; es wird keine zusätzliche Datenkopie vorgehalten.","steps":["Prinzip: Ein Composer (API Gateway/BFF) ruft service-A und service-B auf, führt das Ergebnis zusammen und gibt es zurück.","Wann: Der Join ist einfach, umfasst wenige Services und die Abfragefrequenz ist moderat; du willst nicht den Wartungsaufwand eines zusätzlichen Lesemodells.","Pro: Kein zusätzlicher Datenspeicher und keine Synchronisierung; jeder Service liefert seine eigenen aktuellen Daten.","Achtung: Bei Joins über viele Services oder große Datenmengen summiert sich die Latenz und In-Memory-Joins sind teuer.","Als Nächstes: Wird der Join komplex oder sehr häufig, wechsle zu einem CQRS-Lesemodell."],"related":[]},"sol_cqrs":{"kind":"solution","title":"Empfehlung: CQRS-Lesemodell (durch Ereignisse gespeist)","detail":"Halte für komplexe und häufige kombinierte Abfragen ein vorab gejointes, abfrageoptimiertes Lesemodell (Materialized View/denormalisierte Kopie) vor, das von den Ereignissen der Services gespeist wird. Die Schreibseite bleibt in den Services; die Leseseite liefert fertige Daten schnell von einer Stelle.","steps":["Prinzip: Services veröffentlichen Zustandsänderungen als Ereignisse; eine Projektion hört auf diese Ereignisse und aktualisiert das Lesemodell.","Speicherwahl: Wähle den Speicher des Lesemodells nach Abfragetyp: Elasticsearch für Suche, MongoDB für flexible Dokumente, Redis für schnellen Schlüsselzugriff, PostgreSQL für relationale Berichte.","Wann: Die kombinierte Abfrage ist komplex/häufig, niedrige Latenz ist gefordert und die Kosten der API Composition sind gestiegen.","Achtung: Das Lesemodell ist eventually consistent; bis Ereignisse verarbeitet sind, hinkt es leicht hinterher.","Idempotenz: Die Projektion kann dasselbe Ereignis zweimal sehen; aktualisiere idempotent mit der Ereignis-ID und verfolge bei Bedarf Reihenfolge/Version."],"related":["decide-cache"]}}},"solver/arch-eventdriven.json":{"id":"arch-eventdriven","title":"Referenzarchitektur: ereignisgesteuert / CQRS","icon":"🏛️","category":"Referenzarchitektur","intro":"In einer ereignisgesteuerten Architektur rufen sich Services nicht direkt auf, sondern veröffentlichen Ereignisse in ein Event-Log (z. B. Kafka), und Consumer verarbeiten sie asynchron. Ausgehend vom Aufbau des Event-Backbones geht dieser Baum Schritt für Schritt Event Sourcing (Zustand aus Ereignissen ableiten), Command-/Query-Trennung mit CQRS, Projektion des Lesemodells, Idempotenz, Toleranz für Eventual Consistency und den Trugschluss der „Exactly-once-Zustellung“ durch. Ziel ist ein lose gekoppeltes, skalierbares System, dessen Konsistenz-Kompromisse gut verstanden sind.","start":"root","nodes":{"root":{"kind":"step","title":"Vor welcher Entscheidung stehst du in deiner ereignisgesteuerten Architektur?","detail":"Die Grundlage eines ereignisgesteuerten Systems ist ein dauerhaftes Event-Log, das Services über asynchrone Ereignisse verbindet. Baust du noch das Backbone auf, willst du Zustand aus Ereignissen ableiten (Event Sourcing), oder trennst du Lese- und Schreibpfad (CQRS)?","choices":[{"label":"Ich baue das Event-Backbone auf, das Services verbindet","next":"sol_event_log"},{"label":"Ich will Zustand aus der Ereignishistorie ableiten","next":"sol_event_sourcing"},{"label":"Ich will Lese- und Schreibpfad trennen (CQRS)","next":"cqrs_split"}]},"cqrs_split":{"kind":"step","title":"Wie baust du die Leseseite in CQRS auf, und wie hoch ist deine Konsistenztoleranz?","detail":"In CQRS sind Command- (Schreib-) und Query-Seite (Lese-) getrennt; Schreibereignisse aktualisieren die Projektion, die das Lesemodell speist. Die Kernfrage: Kannst du tolerieren, dass das Lesemodell den Schreibvorgängen hinterherhinkt (Eventual Consistency), oder erwartest du, dass jedes Ereignis genau einmal verarbeitet wird?","choices":[{"label":"Ich speise das Lesemodell per Projektion aus Ereignissen","next":"sol_read_model"},{"label":"Lesevorgänge müssen aktuell sein; Verzug ist inakzeptabel","next":"sol_consistency"},{"label":"Ich will Ereignisse „genau einmal“ verarbeiten","next":"sol_idempotency"}]},"sol_event_log":{"kind":"solution","title":"Empfehlung: ein dauerhaftes Event-Log als Backbone","detail":"Im Zentrum einer ereignisgesteuerten Architektur steht ein dauerhaftes, geordnetes Event-Log, in das Producer schreiben und das Consumer in ihrem eigenen Tempo lesen. Dieses Backbone entkoppelt Services zeitlich und in der Skalierung.","steps":["Engine: Apache Kafka (oder eine ähnliche logbasierte Plattform): ein partitioniertes, persistentes, wiederabspielbares Event-Log.","Partitionen & Reihenfolge: Die Reihenfolge ist nur innerhalb einer Partition garantiert; erhalte sie, indem du Ereignisse derselben Entität auf denselben Schlüssel leitest.","Unabhängigkeit der Consumer: Jede Consumer Group hält ihren eigenen Offset; sie kann erneut lesen/zurückspulen, ohne den Producer zu beeinflussen.","Aufbewahrung: Bewahre das Log lange genug auf, damit neue Consumer und Neuverarbeitungen die Historie lesen können.","Achtung: Kafka ist keine Queue, sondern ein wiederabspielbares Log; gehe von At-least-once-Zustellung aus und schreibe idempotente Consumer."],"related":[]},"sol_event_sourcing":{"kind":"solution","title":"Empfehlung: Event Sourcing (Zustand aus Ereignissen ableiten)","detail":"Beim Event Sourcing speicherst du nicht den aktuellen Zustand einer Entität, sondern alle (unveränderlichen) Änderungsereignisse, die auf sie angewendet wurden; der aktuelle Zustand ergibt sich durch Anwenden dieser Ereignisse in Reihenfolge. Das liefert eine vollständige Audit-Historie und die Möglichkeit, zu jedem Zeitpunkt der Vergangenheit zurückzukehren.","steps":["Prinzip: Der Ereignisstrom ist die Quelle der Wahrheit; der Zustand wird durch erneutes Abspielen der Ereignisse von Anfang an (oder Snapshot + nachfolgende Ereignisse) wiederhergestellt.","Snapshots: Erstelle bei langen Strömen periodische Snapshots, um nicht jedes Mal von vorn abzuspielen, und wende spätere Ereignisse darauf an.","Pro: Ein vollständiger Audit-Trail, eine Antwort auf „wie sind wir zu diesem Zustand gekommen“ über die Zeit und die Möglichkeit, verschiedene Lesemodelle abzuleiten.","Achtung: Ereignisschemas entwickeln sich weiter; das Lesen alter Ereignisse erfordert Versionierung/Upcasting, denn Ereignisse leben ewig.","Achtung: Nicht für jedes Szenario nötig; reicht der aktuelle Zustand, ist klassisches CRUD einfacher. Wird meist zusammen mit CQRS eingesetzt."],"related":[]},"sol_read_model":{"kind":"solution","title":"Empfehlung: Projektion des Lesemodells","detail":"In CQRS erzeugt die Schreibseite Ereignisse; eine Projektion hört auf diese Ereignisse und aktualisiert ein abfrageoptimiertes Lesemodell (denormalisierte Sicht). Lesevorgänge werden aus diesem fertigen Modell bedient, vollständig getrennt vom Schreibpfad, und beide Seiten skalieren unabhängig.","steps":["Prinzip: Ereignisse → Projektion → Lesespeicher; Leseabfragen treffen nur dieses denormalisierte Modell.","Speicherwahl: Nach Abfragetyp: Elasticsearch für Suche, MongoDB für flexible Dokumente, Redis für schnellen Zugriff, PostgreSQL für relationale Daten.","Neuaufbaubarkeit: Da das Lesemodell aus Ereignissen abgeleitet ist, lässt es sich verwerfen und durch erneutes Lesen des Logs von Grund auf neu aufbauen.","Idempotenz: Die Projektion kann dasselbe Ereignis mehrfach sehen; aktualisiere idempotent anhand von Ereignis-ID/Version.","Achtung: Das Lesemodell ist eventually consistent; es hinkt den Schreibvorgängen hinterher, bis das Ereignis verarbeitet ist, gestalte die UI entsprechend."],"related":["decide-cache"]},"sol_consistency":{"kind":"solution","title":"Empfehlung: auf Toleranz für Eventual Consistency auslegen","detail":"In ereignisgesteuerten/CQRS-Systemen hinkt das Lesemodell den Schreibvorgängen leicht hinterher (Eventual Consistency). Statt das „beseitigen“ zu wollen, lege das Design darauf aus, es zu tolerieren; halte die wenigen Abläufe, die strikte sofortige Konsistenz erfordern, getrennt.","steps":["Prinzip: Die meisten Geschäftsabläufe tolerieren Konsistenz mit kurzer Verzögerung; gib dem Benutzer die Rückmeldung „Anfrage erhalten, wird verarbeitet“.","Read-your-Writes: Müssen Benutzer ihre eigenen Schreibvorgänge sofort sehen, bediene diesen Lesevorgang von der Schreibseite/synchron.","Kern mit strikter Konsistenz: Halte Arbeit, die absolute sofortige Konsistenz braucht, etwa einen Geldsaldo, mit einer ACID-Transaktion in einem einzelnen Service/einer DB; verteile sie nicht über Ereignisse.","Achtung: Verstecke Eventual Consistency nicht; behandle Zwischen-/Übergangszustände explizit in Oberflächen und Geschäftsregeln.","Achtung: Das Ziel, „das ganze System stark konsistent zu machen“, zerstört die Vorteile ereignisgesteuerter Architektur (Skalierung, lose Kopplung)."],"related":["decide-ha","replication-lag"]},"sol_idempotency":{"kind":"solution","title":"Empfehlung: Idempotenz (der Exactly-once-Trugschluss)","detail":"In verteiltem Messaging ist durchgängige „Exactly-once-Zustellung“ in der Praxis ein Trugschluss; Nachrichtenduplikate sind wegen Netzwerk und Wiederholungen unvermeidlich. Das richtige Ziel ist idempotente Verarbeitung: Dasselbe Ereignis zweimal zu verarbeiten muss dasselbe Ergebnis liefern wie einmal.","steps":["Prinzip: Akzeptiere At-least-once-Zustellung; mach Duplikate im Consumer harmlos.","Methode: Gib jedem Ereignis eine eindeutige ID; speichere verarbeitete IDs und überspringe sie bei erneutem Eintreffen (Dedup), oder nutze naturgemäß idempotente Operationen (Upsert, Set).","Exactly-once-Semantik: Die „Exactly-once“-Funktion von Kafka gilt nur innerhalb von Kafkas eigener Transaktionsgrenze; Seiteneffekte in einer externen DB sind ohne Idempotenz nicht garantiert.","Reihenfolge: Vermeide Umsortierung, indem du Ereignisse derselben Entität auf denselben Partitionsschlüssel leitest.","Achtung: Die Annahme „Ich habe Exactly-once-Zustellung konfiguriert, kein Dedup nötig“ erzeugt in der Produktion Inkonsistenzen; verarbeite immer idempotent."],"related":[]}}},"solver/arch-multiregion.json":{"id":"arch-multiregion","title":"Referenzarchitektur: Multi-Region / global","icon":"🏛️","category":"Referenzarchitektur","intro":"Sind Benutzer geografisch verteilt oder ist Ausfallsicherheit gegen eine regionale Katastrophe gefordert, verteilen sich Daten über mehrere Regionen. Dieser Baum geht Schritt für Schritt Leselokalität (eine Read Replica pro Region), Schreibtopologie (Single-Leader vs. Multi-Leader vs. Quorum), Datenresidenz/Compliance (DSGVO), Konfliktauflösung und die Kompromisse zwischen RPO/RTO und Latenz durch. Die Grundwahrheit: Wegen der Gesetze der Physik lassen sich niedrige Latenz, starke Konsistenz und Schreiben in mehreren Regionen nicht gleichzeitig vollständig erreichen; ein bewusster Kompromiss ist Pflicht.","start":"root","nodes":{"root":{"kind":"step","title":"Warum Multi-Region: Leselokalität, Schreibvorgänge/Ausfallsicherheit oder Compliance?","detail":"Eine Multi-Region-Architektur ist teuer und komplex; kläre die Motivation. Willst du niedrige Leselatenz für entfernte Benutzer, regionsübergreifende Schreibvorgänge/Katastrophenresistenz, oder ist die eigentliche Anforderung, dass Daten in einem bestimmten Land bleiben (rechtliche Residenz)?","choices":[{"label":"Schnelle Lesevorgänge für entfernte Benutzer; Schreibvorgänge können in einer Region bleiben","next":"sol_read_locality"},{"label":"Regionsübergreifende Schreibvorgänge / Ausfallsicherheit gegen Verlust einer Region sind gefordert","next":"write_topology"},{"label":"Daten müssen rechtlich in einem bestimmten Land/einer bestimmten Region bleiben","next":"sol_data_residency"}]},"write_topology":{"kind":"step","title":"Wie soll die Schreibtopologie aussehen: Single-Leader, Multi-Leader oder Quorum?","detail":"Bei regionsübergreifenden Schreibvorgängen ist die zentrale Entscheidung das Leader-Modell. Gehen alle Schreibvorgänge an eine Region (einfach, konsistent, aber entfernte Schreibvorgänge sind langsam), kann jede Region schreiben (niedrige Schreiblatenz, aber Konflikte), oder wird ein Schreibvorgang mit Bestätigung einer Mehrheit der Regionen angenommen (Quorum: konsistent, aber mit zusätzlicher Latenz)?","choices":[{"label":"Eine Region schreibt, die anderen lesen (einfach und konsistent)","next":"sol_single_leader"},{"label":"Jede Region kann lokal schreiben (niedrige Schreiblatenz)","next":"sol_multi_leader"},{"label":"Schreiben mit Mehrheitsbestätigung; Konsistenz hat Vorrang","next":"sol_quorum"}]},"sol_read_locality":{"kind":"solution","title":"Empfehlung: eine Read Replica pro Region (Leselokalität)","detail":"Schreibvorgänge bleiben in einer einzigen Primary-Region, während du in jede Benutzerregion eine von dort gespeiste Read Replica stellst. Benutzer lesen von der nächstgelegenen Replica und die Leselatenz sinkt; da Schreibvorgänge weiterhin an ein Zentrum gehen, bleibt das Konsistenzmodell einfach.","steps":["Topologie: Eine Primary-Region (Schreibvorgänge) → eine asynchrone Read Replica in jeder Benutzerregion.","Engines: PostgreSQL Streaming Replication, regionsübergreifende MySQL-Replicas oder verwaltete globale Read Replicas.","Routing: Die Anwendung leitet Lesevorgänge an die nächstgelegene Replica und Schreibvorgänge an die Primary-Region (Geo-DNS / Smart Router).","Achtung: Regionsübergreifendie Replikationsverzögerung verursacht veraltete Lesevorgänge; lass Abläufe, die Read-your-Writes brauchen, vom Primary lesen.","RPO/RTO: Fällt die Primary-Region aus, kann eine asynchrone Replica mit etwas Datenverlust promotet werden (RPO > 0); bereite den Failover-Plan im Voraus vor."],"related":["replication-lag","decide-shard-replica"]},"sol_single_leader":{"kind":"solution","title":"Empfehlung: Single-Leader-Schreibvorgänge","detail":"Alle Schreibvorgänge gehen an eine einzige Leader-Region; die anderen Regionen sind Read Replicas, die asynchron von diesem Leader gespeist werden. Das ist das einfachste und am stärksten konsistente Modell; es gibt keine Konflikte, da es nur einen Schreibpunkt gibt. Der Preis ist hohe Schreiblatenz aus entfernten Regionen.","steps":["Prinzip: Eine einzige Schreibinstanz → keine Konflikte, einfaches Konsistenzmodell.","Wann: Das Schreibvolumen lässt sich in einer Region bewältigen und die Latenz entfernter Schreibvorgänge ist tolerierbar.","Failover: Fällt die Leader-Region aus, wird eine Replica zum Leader promotet; automatisches/kontrolliertes Failover und Split-Brain-Schutz sind Pflicht.","RPO/RTO: Bei asynchroner Replikation kann beim Failover eine kleine Datenmenge verloren gehen (RPO); synchrone Replikation ergibt RPO=0, erhöht aber die Schreiblatenz.","Achtung: Schreibvorgänge entfernter Benutzer sind langsam; ist Schreiblokalität kritisch, sieh dir Multi-Leader oder Quorum an."],"related":["decide-ha","failover-drill"]},"sol_multi_leader":{"kind":"solution","title":"Empfehlung: Multi-Leader-Schreibvorgänge + Konfliktauflösung","detail":"Da jede Region lokal schreiben kann, ist die Schreiblatenz niedrig, und selbst wenn eine Region abgeschnitten ist, schreiben die anderen weiter. Der Preis ist hoch: Zwei Regionen können denselben Datensatz unterschiedlich aktualisieren, und du musst eine Konfliktauflösung entwerfen.","steps":["Prinzip: Jede Region ist ein Leader; Schreibvorgänge werden asynchron an die anderen Regionen weitergegeben und zusammengeführt.","Konfliktauflösung: Last-Write-Wins (einfach, aber mit Datenverlustrisiko), anwendungsspezifisches Zusammenführen oder konfliktfreie Datentypen wie CRDTs.","Engine-Beispiele: Multi-Master-/Multi-Leader-Konfigurationen (z. B. EDB Postgres Distributed (BDR)/pgEdge für PostgreSQL, Redis Enterprise Active-Active (CRDT), CouchDB). Cassandra ist eher leaderless als Multi-Leader; mit LOCAL_ONE/LOCAL_QUORUM erlaubt es lokale Schreibvorgänge in jeder Region mit ähnlichem Ergebnis und löst Konflikte per Last-Write-Wins (Zeitstempel).","Wann: Lokale Schreiblatenz und regionale Unabhängigkeit sind wichtiger als starke Konsistenz.","Achtung: Entwirf die Konfliktauflösung von Anfang an; die Annahme „Konflikte werden nicht vorkommen“ erzeugt stillen Datenverlust. Achte auf regionsübergreifende Kollisionen bei der Erzeugung eindeutiger IDs."],"related":["decide-ha"]},"sol_quorum":{"kind":"solution","title":"Empfehlung: quorumbasierte Schreibvorgänge (Mehrheitsbestätigung)","detail":"Ein Schreibvorgang wird angenommen, wenn eine Mehrheit (Quorum) der Regionen/Replicas ihn bestätigt; gehen auch Lesevorgänge an eine Mehrheit, ist der neueste Wert garantiert. Das ist robust gegen Regionsverlust und stark konsistent; der Preis ist die Latenz, bis jeder Schreibvorgang eine Mehrheit erreicht.","steps":["Prinzip: Mehrheitsbestätigung nach der Regel W + R > N; selbst wenn eine Minderheitsregion ausfällt, schreibt das System konsistent weiter.","Engine-Beispiele: Konsensbasierte verteilte DBs (z. B. Systeme mit Raft/Paxos), die Konsistenzstufe QUORUM in Cassandra.","Wann: Sowohl Resistenz gegen regionale Katastrophen als auch starke Konsistenz sind gefordert, und höhere Schreiblatenz ist akzeptabel.","RPO/RTO: Da eine Mehrheit dauerhaft schreibt, gilt RPO≈0; mit automatischer Neubildung der Mehrheit nach einem Regionsverlust kann die RTO niedrig sein.","Achtung: Ein Quorum zwischen geografisch weit entfernten Regionen fügt durch die Lichtgeschwindigkeitslatenz jedem Schreibvorgang Dutzende ms hinzu; wähle Anzahl und Platzierung der Regionen sorgfältig."],"related":["decide-ha","decide-shard-replica"]},"sol_data_residency":{"kind":"solution","title":"Empfehlung: Datenresidenz / Compliance (DSGVO)","detail":"Manche Daten (personenbezogene, Gesundheits-, Finanzdaten) müssen rechtlich innerhalb der Grenzen eines bestimmten Landes/einer Region bleiben (z. B. DSGVO). In diesem Fall entwirfst du die Architektur nicht nach „überall kopieren“, sondern so, dass Daten geografisch fixiert sind.","steps":["Prinzip: Halte personenbezogene/regulierte Daten in der Region, in der sie entstehen; beschränke oder verbiete Replikation außerhalb der Region.","Regionale Partitionierung: Leite Benutzer in die Region, in der sie registriert sind (Geo-Partitionierung); jede Region hält die Daten ihrer eigenen Einwohner.","Globale vs. lokale Daten: Globale Daten wie ein Katalog lassen sich überall replizieren; personenbezogene Daten bleiben in der Region.","Achtung: Auch Backups und Read Replicas bewegen Daten; die Region des Backup-Speichers und die DR-Region fallen ebenfalls unter die Compliance.","Achtung: Eine Entscheidung wie „kopieren wir es überall hin, nur für niedrige Latenz“ kann bei regulierten Daten ein Rechtsverstoß sein; baue Residenzvorgaben von Anfang an in die Architektur ein."],"related":["security-incident","audit-setup"]}}},"solver/arch-highscale.json":{"id":"arch-highscale","title":"Referenzarchitektur: hohe Skalierung / hoher Traffic","icon":"🏛️","category":"Referenzarchitektur","intro":"Wächst der Traffic, gibt es eine richtige Reihenfolge zum Schutz der Datenbank: zuerst die einfachen, günstigen Hebel, dann die komplexen. Dieser Baum geht Schritt für Schritt die Reihenfolge vertikale Skalierung → Read Replica → Sharding, Caching-Muster (Cache-Aside, Write-Back), Pufferung mit Queue + Backpressure, Rate Limiting, Isolation der Datenbank mit Bulkheads/Circuit Breakern und die Vermeidung von Hotspots durch. Grundprinzip: Schwer umkehrbare Entscheidungen wie Sharding kommen zuletzt, die günstigen Gewinne zuerst ausschöpfen.","start":"root","nodes":{"root":{"kind":"step","title":"Wo liegt der Engpass: Ist eine einzelne Instanz ausgelastet, sind Lese- oder Schreibvorgänge hoch, oder ist der Traffic stoßweise?","detail":"Um ein Skalierungsproblem an der richtigen Stelle zu lösen, bestimme zuerst den Engpass. Ist eine einzelne Instanz bei CPU/Speicher ausgelastet, dominieren Lesevorgänge, stoßen Schreibvorgänge an das Limit, oder erdrücken plötzliche Trafficspitzen die Datenbank? Jedes deutet auf eine andere Komponente.","choices":[{"label":"Eine einzelne Instanz ist ausgelastet; die einfachen Hebel habe ich noch nicht versucht","next":"scale_path"},{"label":"Lesevorgänge sind sehr hoch und wachsen","next":"sol_cache"},{"label":"Plötzliche Trafficspitzen / stoßweise Last erdrücken die DB","next":"protect"}]},"scale_path":{"kind":"step","title":"An welchem Skalierungsschritt bist du: vertikal, Read Replicas oder Sharding?","detail":"Skalierung hat eine richtige Reihenfolge. Vertikale Skalierung (stärkere Hardware) ist der einfachste erste Schritt; dann verteilst du Lesevorgänge auf Replicas; erst wenn ein einzelner Primary die Schreibvorgänge nicht mehr bewältigt, wechselst du zu horizontaler Partitionierung (Sharding). Wo stehst du?","choices":[{"label":"Ich habe die Hardware noch nicht aufgerüstet / das Basis-Tuning noch nicht ausgeschöpft","next":"sol_vertical"},{"label":"Schreibvorgänge passen auf eine Instanz, aber ich muss die Leselast verteilen","next":"sol_read_replica"},{"label":"Ein einzelner Primary bewältigt die Schreiblast nicht mehr","next":"sol_sharding"}]},"protect":{"kind":"step","title":"Wie schützt du die Datenbank vor Last: Pufferung, Begrenzung oder Isolation?","detail":"Skalierung bedeutet nicht immer, die DB größer zu machen; oft geht es darum, die DB vor Überlast zu schützen. Glättest du plötzliche Schreibspitzen mit einer Queue, begrenzt du die eingehende Request-Rate (Rate Limiting), oder verhinderst du, dass eine einzelne Abhängigkeit das ganze System lahmlegt (Bulkhead/Circuit Breaker)?","choices":[{"label":"Ich will plötzliche Schreibspitzen glätten","next":"sol_queue"},{"label":"Ich will die eingehende Request-Rate begrenzen/fair aufteilen","next":"sol_ratelimit"},{"label":"Ich will nicht, dass eine ausfallende Abhängigkeit das ganze System mitreißt","next":"sol_bulkhead"}]},"sol_vertical":{"kind":"solution","title":"Empfehlung: zuerst vertikale Skalierung und Basis-Tuning","detail":"Der günstigste und risikoärmste Schritt ist vertikale Skalierung: mehr CPU/RAM/schnellere Disks hinzufügen und die Grundlagen korrigieren (Indizes, Connection Pooling, Speicherparameter). Schöpfe diese Gewinne aus, bevor du komplexe Entscheidungen wie Sharding triffst.","steps":["Erster Schritt: Rüste die Hardware auf (CPU/RAM/NVMe) und stimme die Buffer-/Cache-Speicherparameter auf die Workload ab.","Günstige Gewinne: Fehlende Indizes ergänzen, schwere Abfragen korrigieren, einen Connection Pooler (PgBouncer/ProxySQL) vorschalten; die meisten „Skalierungsprobleme“ sind eigentlich Tuning-Probleme.","Wann es reicht: Erfüllt die aufgerüstete Einzelinstanz die Ziele für Latenz/Durchsatz, hör auf; füge keine weitere Komplexität hinzu.","Achtung: Vertikale Skalierung hat eine physische und eine Kostengrenze; nähert du dich ihr, wechsle zu Read Replicas und Caching.","Als Nächstes: Dominieren Lesevorgänge, Replicas + Cache; dominieren Schreibvorgänge und ist die Grenze überschritten, Sharding."],"related":["decide-capacity","high-cpu"]},"sol_read_replica":{"kind":"solution","title":"Empfehlung: Lesevorgänge mit Read Replicas verteilen","detail":"Schreibvorgänge bleiben auf einem einzelnen Primary, während Read-only-Traffic auf eine oder mehrere Replicas verteilt wird. Das ist viel einfacher als Sharding und löst die meisten leseintensiven Workloads; der Primary bleibt nur für Schreibvorgänge und konsistenzkritische Lesevorgänge.","steps":["Topologie: Primary (Schreibvorgänge) → mehrere asynchrone Read Replicas; Lesevorgänge werden über einen Load Balancer auf die Replicas verteilt.","Engines: PostgreSQL Streaming Replication, MySQL-Replicas, verwaltete Read Replicas.","Wann: Das Lese-/Schreibverhältnis ist stark zu Lesevorgängen verschoben und Schreibvorgänge lassen sich noch von einer Instanz bewältigen.","Achtung: Replikationsverzögerung verursacht veraltete Lesevorgänge; bediene Lesevorgänge, die Read-your-Writes brauchen, vom Primary.","Als Nächstes: Reichen selbst Replicas nicht oder liegt der echte Engpass bei Schreibvorgängen, füge einen Cache hinzu und sieh dir dann Sharding an."],"related":["replication-lag","decide-shard-replica"]},"sol_sharding":{"kind":"solution","title":"Empfehlung: Sharding (horizontale Partitionierung) als letztes Mittel","detail":"Bewältigt ein einzelner Primary die Schreiblast nicht, teilst du die Daten nach einem Schlüssel in mehrere unabhängige Teile (Shards) auf; jeder Shard übernimmt einen Teil der Schreibvorgänge. Das ist mächtig, aber schwer umkehrbar; es verkompliziert Shard-übergreifende Abfragen und Transaktionen und kommt daher zuletzt.","steps":["Shard Key: Wähle einen Schlüssel, der Last und Daten gleichmäßig verteilt und die meisten Abfragen auf einem einzelnen Shard landen lässt (z. B. customer_id).","Methode: Hash-basierte (gleichmäßige Verteilung) oder bereichsbasierte Partitionierung (einfache Bereichsabfragen, aber Hotspot-Risiko).","Engine-Beispiele: MongoDB Sharding, Citus (PostgreSQL), Vitess (MySQL) oder Sharding auf Anwendungsebene.","Achtung: Shard-übergreifende Joins/Transaktionen sind teuer und komplex; entwirf das Schema so, dass die meisten Transaktionen innerhalb eines Shards abgeschlossen werden.","Achtung: Ein falscher Schlüssel erzeugt Hotspots (Last staut sich auf einem Shard); vermeide monotone Schlüssel wie steigende IDs/Datumswerte und bedenke, dass Resharding schwierig ist."],"related":["decide-shard-replica","decide-partition"]},"sol_cache":{"kind":"solution","title":"Empfehlung: Caching-Muster (Cache-Aside + Write-Back)","detail":"Bei leseintensivem hohem Traffic setzt du einen In-Memory-Cache (Redis) vor die DB und bedienst die meisten Lesevorgänge, ohne dass sie die DB je erreichen. Bei hohem Schreibvolumen puffert Write-Back Schreibvorgänge und schreibt sie gebündelt in die DB, was den Primary entlastet.","steps":["Cache-Aside: Die Anwendung prüft zuerst Redis; bei einem Miss holt sie aus der DB und schreibt in den Cache. Das nimmt der DB den Großteil der Leselast ab.","Write-Back (Write-Behind): Bei sehr hohen Schreibvolumen wie Zählern/Metriken zuerst in den Cache schreiben und im Hintergrund gebündelt in die DB übertragen.","TTL + Invalidierung: Gib Schlüsseln eine sinnvolle TTL; aktualisiere/lösche beim Schreiben den zugehörigen Schlüssel, damit keine veralteten Daten bleiben.","Achtung: Cache Stampede: Läuft ein beliebter Schlüssel ab, treffen Tausende Requests gleichzeitig die DB; nutze Locking/Single-Flight, frühzeitige Aktualisierung und TTLs mit Jitter.","Achtung: Stürzt bei Write-Back der Cache-Knoten vor dem Schreiben in die DB ab, gehen Daten verloren; schütze kritische Daten mit einer dauerhaften Queue/AOF."],"related":["decide-cache","cache-hit-low"]},"sol_queue":{"kind":"solution","title":"Empfehlung: Pufferung mit einer Queue (Backpressure)","detail":"Damit plötzliche Schreibspitzen die DB nicht direkt treffen, schaltest du eine dauerhafte Queue (z. B. Kafka/eine Message Queue) dazwischen und verarbeitest Schreibvorgänge in dem Tempo, das die DB verkraftet. Die Queue fängt die Spitze ab; statt synchron zu scheitern, läuft das System mit kontrollierter Latenz (Backpressure).","steps":["Prinzip: Der Producer schreibt das Ereignis in die Queue; der Consumer schreibt in seinem verkraftbaren Tempo in die DB (ggf. gebündelt).","Engine: Kafka oder eine Message Queue; sie wirkt als Puffer, der die DB von Spitzentraffic isoliert.","Backpressure: Wächst die Queue ständig, bremse den Producer/signalisiere ihm; erlaube keine unbegrenzte Anhäufung, sonst explodiert die Latenz.","Wann: Schreibtraffic ist sehr stoßweise und sofortige synchrone Verarbeitung ist nicht zwingend (asynchrone Verarbeitung ist akzeptabel).","Achtung: Eine Queue bringt Asynchronität; das Ergebnis ist eventually consistent. Mach den Consumer idempotent (Nachrichten können erneut zugestellt werden)."],"related":["connection-storm"]},"sol_ratelimit":{"kind":"solution","title":"Empfehlung: Rate Limiting (Request-Rate begrenzen)","detail":"Durch Begrenzung eingehender Requests pro Quelle (Rate Limiting) verhinderst du sowohl Missbrauch als auch, dass ein einzelner Client alle Ressourcen verbraucht. Eine Obergrenze für die Last, die die DB erreicht, hält das System vorhersehbar und fair.","steps":["Platzierung: Setze die Begrenzung vor der DB an (API Gateway / Anwendungsrand); nur angenommene Requests sollen die DB erreichen.","Methode: Rate pro Client oder pro Benutzer mit Token Bucket / Sliding Window; bei Überschreitung 429 zurückgeben.","Faire Aufteilung: Verhindere mit Quoten, dass ein „Noisy Neighbor“ die gesamte Kapazität verschluckt.","Wann: Der Traffic kann plötzlich ansteigen, oder es besteht ein Risiko durch Missbrauch/automatisierte Clients.","Achtung: Rate Limiting ist eine Schutzmaßnahme, keine Skalierung; besteht echter Kapazitätsbedarf, behebe auch den eigentlichen Engpass (vertikal/Replica/Shard)."],"related":["connection-storm"]},"sol_bulkhead":{"kind":"solution","title":"Empfehlung: Bulkhead + Circuit Breaker (Isolation)","detail":"Um zu verhindern, dass eine Abhängigkeit (z. B. eine langsamer werdende DB oder ein externer Dienst) das ganze System lahmlegt, teilst du Ressourcen in Abteilungen auf (Bulkheads) und unterbrichst vorübergehend Aufrufe an eine ständig fehlschlagende Abhängigkeit (Circuit Breaker). So breitet sich ein Teilausfall nicht auf das ganze System aus.","steps":["Bulkhead: Weise verschiedenen Arten von Arbeit getrennte Connection Pools/Threads zu; ein langsamer Pfad darf nicht den ganzen Pool erschöpfen und die anderen aushungern.","Circuit Breaker: Überschreitet eine Abhängigkeit eine Fehler-/Latenzschwelle, „öffne“ den Stromkreis und scheitere eine Zeit lang schnell; prüfe dann mit Testaufrufen die Erholung (Half-Open).","Timeouts: Setze für jeden DB-/Dienstaufruf ein sinnvolles Timeout; hängende Aufrufe dürfen den Pool nicht blockieren.","Wann: In einem System mit vielen Abhängigkeiten, in dem die Verlangsamung einer Komponente das Risiko eines Kaskadenausfalls birgt.","Achtung: Entwirf eine kontrollierte Degradierung für den offenen Stromkreis: Gib veraltete Daten aus dem Cache oder ein Teilergebnis zurück, statt den ganzen Request abzulehnen."],"related":["connection-storm","high-cpu"]}}},"types/types-functions.json":{"schemaVersion":1,"sections":[{"id":"datatypes","title":"Datentypen","icon":"🔤","rows":[{"id":"var-text","concept":"Text variabler Länge","engines":{"oracle":"VARCHAR2(n)","postgres":"VARCHAR(n) / TEXT","mysql":"VARCHAR(n)","mssql":"NVARCHAR(n)","mongodb":"string","redis":"string (value)","elasticsearch":"text / keyword"},"note":"In ES wird text für Volltextsuche und keyword für exakte Übereinstimmung/Aggregationen verwendet."},{"id":"fixed-text","concept":"Text fester Länge","engines":{"oracle":"CHAR(n)","postgres":"CHAR(n)","mysql":"CHAR(n)","mssql":"NCHAR(n)","mongodb":"string","redis":"string","elasticsearch":"keyword"},"note":"Dokument-/Key-Value-Systeme kennen kein Konzept fester Länge; Werte werden als Strings gespeichert."},{"id":"large-text","concept":"Großer Text (CLOB/TEXT)","engines":{"oracle":"CLOB","postgres":"TEXT","mysql":"LONGTEXT / TEXT","mssql":"NVARCHAR(MAX)","mongodb":"string","redis":"string (up to 512 MB)","elasticsearch":"text"}},{"id":"int32","concept":"Ganzzahl (32 Bit)","engines":{"oracle":"NUMBER(10)","postgres":"INTEGER / INT4","mysql":"INT","mssql":"INT","mongodb":"int (NumberInt / int32)","redis":"string (interpreted as an integer)","elasticsearch":"integer"},"note":"Oracle hat keinen separaten INT-Typ; es wird NUMBER verwendet."},{"id":"int64","concept":"Große Ganzzahl (64 Bit)","engines":{"oracle":"NUMBER(19)","postgres":"BIGINT / INT8","mysql":"BIGINT","mssql":"BIGINT","mongodb":"long (NumberLong / int64)","redis":"string (INCR 64-bit integer)","elasticsearch":"long"}},{"id":"decimal","concept":"Dezimal-/exakte Zahl (NUMERIC)","engines":{"oracle":"NUMBER(p,s)","postgres":"NUMERIC(p,s) / DECIMAL","mysql":"DECIMAL(p,s)","mssql":"DECIMAL(p,s) / NUMERIC","mongodb":"decimal (Decimal128)","redis":"none, stored as a string","elasticsearch":"scaled_float"},"note":"Für exakte Werte wie Geld werden diese Typen gegenüber Float bevorzugt."},{"id":"float","concept":"Gleitkommazahl (FLOAT/DOUBLE)","engines":{"oracle":"BINARY_DOUBLE / FLOAT","postgres":"REAL / DOUBLE PRECISION","mysql":"FLOAT / DOUBLE","mssql":"FLOAT / REAL","mongodb":"double","redis":"none, string (INCRBYFLOAT)","elasticsearch":"float / double"}},{"id":"boolean","concept":"Logisch (BOOLEAN)","engines":{"oracle":"NUMBER(1) / CHAR(1) (BOOLEAN in 23c)","postgres":"BOOLEAN","mysql":"TINYINT(1) / BOOL","mssql":"BIT","mongodb":"bool","redis":"none, 0/1 string or bit","elasticsearch":"boolean"},"note":"Ein BOOLEAN-Spaltentyp kam in Oracle SQL erst mit 23c; davor wird er mit NUMBER/CHAR nachgebildet."},{"id":"date","concept":"Datum","engines":{"oracle":"DATE (includes time)","postgres":"DATE","mysql":"DATE","mssql":"DATE","mongodb":"Date (ISODate)","redis":"none (epoch string/number)","elasticsearch":"date (with format)"},"note":"Der Oracle-Typ DATE enthält Datum und Uhrzeit; er ist nicht nur ein Datum."},{"id":"datetime","concept":"Datum-Uhrzeit (Timestamp/Datetime)","engines":{"oracle":"TIMESTAMP","postgres":"TIMESTAMP","mysql":"DATETIME / TIMESTAMP","mssql":"DATETIME2","mongodb":"Date (ISODate)","redis":"none (epoch string/number)","elasticsearch":"date"}},{"id":"datetime-tz","concept":"Datum-Uhrzeit mit Zeitzone","engines":{"oracle":"TIMESTAMP WITH TIME ZONE","postgres":"TIMESTAMPTZ","mysql":"TIMESTAMP (stored as UTC)","mssql":"DATETIMEOFFSET","mongodb":"Date (stored as UTC)","redis":"none (epoch UTC)","elasticsearch":"date (UTC, time zone via format)"},"note":"Mongo und ES speichern Datumswerte in UTC; die Zeitzone wird bei der Anzeige angewendet."},{"id":"binary","concept":"Binärdaten (BLOB/Binary)","engines":{"oracle":"BLOB / RAW","postgres":"BYTEA","mysql":"BLOB / VARBINARY","mssql":"VARBINARY(MAX)","mongodb":"BinData (binary)","redis":"string (binary-safe)","elasticsearch":"binary (base64, not searchable)"},"note":"Redis-Strings sind binärsicher und können jede Bytefolge aufnehmen."},{"id":"uuid","concept":"UUID/GUID","engines":{"oracle":"RAW(16) / SYS_GUID()","postgres":"UUID","mysql":"BINARY(16) / CHAR(36)","mssql":"UNIQUEIDENTIFIER","mongodb":"UUID (BinData subtype 4) / ObjectId","redis":"none, stored as a string","elasticsearch":"keyword"},"note":"Die native _id von Mongo ist ObjectId; eine UUID lässt sich auch als BinData speichern."},{"id":"json","concept":"JSON","engines":{"oracle":"JSON (21c+) / CLOB","postgres":"JSON / JSONB","mysql":"JSON","mssql":"JSON (2025) / NVARCHAR","mongodb":"object (native BSON)","redis":"not native / RedisJSON module","elasticsearch":"object / nested / flattened"},"note":"In Postgres wird JSONB für Abfragen/Indizierung bevorzugt; MongoDB ist von vornherein dokumentbasiert."},{"id":"auto-increment","concept":"Autoinkrement (Identity/Serial)","engines":{"oracle":"GENERATED AS IDENTITY / SEQUENCE","postgres":"SERIAL / GENERATED ... IDENTITY","mysql":"AUTO_INCREMENT","mssql":"IDENTITY(1,1)","mongodb":"none (ObjectId or a counter pattern)","redis":"counter with INCR","elasticsearch":"none (automatic _id)"}},{"id":"enum","concept":"Enum/feste Liste","engines":{"oracle":"via a CHECK constraint","postgres":"ENUM (CREATE TYPE)","mysql":"ENUM(...)","mssql":"via a CHECK constraint","mongodb":"none (validation with JSON Schema enum)","redis":"none, stored as a string","elasticsearch":"keyword"}},{"id":"array","concept":"Array","engines":{"oracle":"VARRAY / nested table / collection","postgres":"ARRAY (e.g. INTEGER[])","mysql":"none (via JSON array)","mssql":"none (JSON / table variable)","mongodb":"array (native)","redis":"List / Set (native)","elasticsearch":"any field can be an array (native)"},"note":"In ES kann jedes Feld implizit ein Array enthalten; es gibt keinen separaten Array-Typ."},{"id":"ip-address","concept":"IP-Adresse","engines":{"oracle":"VARCHAR2 / UTL_INADDR","postgres":"INET / CIDR","mysql":"VARBINARY(16) / INET6_ATON","mssql":"none, via VARCHAR","mongodb":"none, stored as a string","redis":"none, stored as a string","elasticsearch":"ip"},"note":"Die nativen IP-Typen in Postgres und ES unterstützen Bereichs-/Subnetzabfragen."},{"id":"money","concept":"Geld","engines":{"oracle":"NUMBER(p,2)","postgres":"MONEY / NUMERIC(p,2)","mysql":"DECIMAL(p,2)","mssql":"MONEY / DECIMAL(p,2)","mongodb":"decimal (Decimal128)","redis":"none, an integer in minor units (cents) is recommended","elasticsearch":"scaled_float"},"note":"Um Rundungsfehler zu vermeiden, wird Geld mit DECIMAL/Decimal128 statt Float gespeichert."},{"id":"spatial","concept":"Geografisch/räumlich","engines":{"oracle":"SDO_GEOMETRY (Oracle Spatial)","postgres":"GEOMETRY / GEOGRAPHY (PostGIS)","mysql":"GEOMETRY / POINT","mssql":"GEOGRAPHY / GEOMETRY","mongodb":"GeoJSON object (2dsphere index)","redis":"Geo (GEOADD, sorted set based)","elasticsearch":"geo_point / geo_shape"}}]},{"id":"functions","title":"Gängige Funktionen","icon":"🧩","rows":[{"id":"coalesce","concept":"Standardwert bei NULL (COALESCE/NVL)","engines":{"oracle":"NVL(x, y) / COALESCE","postgres":"COALESCE(x, y)","mysql":"COALESCE(x, y) / IFNULL","mssql":"COALESCE(x, y) / ISNULL","mongodb":"$ifNull: [x, y]","redis":"none (application side)","elasticsearch":"?.field / painless ternary"}},{"id":"nullif-replace","concept":"Ersetzen bei NULL (ISNULL/IFNULL)","engines":{"oracle":"NVL(x, y)","postgres":"COALESCE(x, y)","mysql":"IFNULL(x, y)","mssql":"ISNULL(x, y)","mongodb":"$ifNull: [x, y]","redis":"none","elasticsearch":"painless: doc[f].size()==0 ? y : x"}},{"id":"now","concept":"Aktuelles Datum mit Uhrzeit (NOW/SYSDATE)","engines":{"oracle":"SYSDATE / SYSTIMESTAMP","postgres":"NOW() / CURRENT_TIMESTAMP","mysql":"NOW()","mssql":"GETDATE() / SYSDATETIME()","mongodb":"$$NOW / new Date()","redis":"TIME command","elasticsearch":"now (date math)"}},{"id":"current-date","concept":"Aktuelles Datum","engines":{"oracle":"TRUNC(SYSDATE) / CURRENT_DATE","postgres":"CURRENT_DATE","mysql":"CURDATE()","mssql":"CAST(GETDATE() AS DATE)","mongodb":"$dateTrunc / new Date()","redis":"TIME (epoch)","elasticsearch":"now/d"}},{"id":"concat","concept":"Text verketten (concat)","engines":{"oracle":"a || b / CONCAT","postgres":"a || b / CONCAT","mysql":"CONCAT(a, b)","mssql":"CONCAT(a, b) / a + b","mongodb":"$concat: [a, b]","redis":"APPEND","elasticsearch":"painless: a + b"},"note":"In MySQL bedeutet || standardmäßig OR; nutze CONCAT zum Verketten."},{"id":"substring","concept":"Teilstring","engines":{"oracle":"SUBSTR(s, p, n)","postgres":"SUBSTRING(s, p, n)","mysql":"SUBSTRING(s, p, n)","mssql":"SUBSTRING(s, p, n)","mongodb":"$substrCP: [s, p, n]","redis":"GETRANGE key start end","elasticsearch":"painless: s.substring(p, e)"}},{"id":"length","concept":"Stringlänge","engines":{"oracle":"LENGTH(s)","postgres":"LENGTH(s) / CHAR_LENGTH","mysql":"CHAR_LENGTH(s)","mssql":"LEN(s)","mongodb":"$strLenCP: s","redis":"STRLEN key","elasticsearch":"painless: s.length()"}},{"id":"upper-lower","concept":"Groß-/Kleinschreibung","engines":{"oracle":"UPPER(s) / LOWER(s)","postgres":"UPPER(s) / LOWER(s)","mysql":"UPPER(s) / LOWER(s)","mssql":"UPPER(s) / LOWER(s)","mongodb":"$toUpper / $toLower","redis":"none (application side)","elasticsearch":"painless: s.toUpperCase() / lowercase analyzer"}},{"id":"trim","concept":"Leerzeichen entfernen (TRIM)","engines":{"oracle":"TRIM(s) / LTRIM / RTRIM","postgres":"TRIM(s)","mysql":"TRIM(s)","mssql":"TRIM(s)","mongodb":"$trim: { input: s }","redis":"none","elasticsearch":"painless: s.trim() / trim token filter"}},{"id":"replace","concept":"Text ersetzen (REPLACE)","engines":{"oracle":"REPLACE(s, a, b)","postgres":"REPLACE(s, a, b)","mysql":"REPLACE(s, a, b)","mssql":"REPLACE(s, a, b)","mongodb":"$replaceAll / $replaceOne","redis":"SETRANGE (positional) / none","elasticsearch":"painless: s.replace(a, b)"}},{"id":"cast","concept":"Typumwandlung (CAST/CONVERT)","engines":{"oracle":"CAST(x AS t) / TO_NUMBER","postgres":"CAST(x AS t) / x::t","mysql":"CAST(x AS t) / CONVERT","mssql":"CAST(x AS t) / CONVERT","mongodb":"$convert / $toInt, $toString","redis":"none (everything is a string)","elasticsearch":"painless: Integer.parseInt(...) / runtime field"}},{"id":"date-to-char","concept":"Datum als Text formatieren (TO_CHAR/FORMAT)","engines":{"oracle":"TO_CHAR(d, 'YYYY-MM-DD')","postgres":"TO_CHAR(d, 'YYYY-MM-DD')","mysql":"DATE_FORMAT(d, '%Y-%m-%d')","mssql":"FORMAT(d, 'yyyy-MM-dd') / CONVERT","mongodb":"$dateToString: { format, date }","redis":"none","elasticsearch":"date field format / painless SimpleDateFormat"}},{"id":"parse-date","concept":"Text in Datum umwandeln (TO_DATE/PARSE)","engines":{"oracle":"TO_DATE(s, 'YYYY-MM-DD')","postgres":"TO_DATE(s, 'YYYY-MM-DD')","mysql":"STR_TO_DATE(s, '%Y-%m-%d')","mssql":"CONVERT(date, s) / PARSE","mongodb":"$dateFromString: { dateString: s }","redis":"none","elasticsearch":"automatic parsing via date mapping"}},{"id":"date-add","concept":"Zu einem Datum addieren (Interval/DATEADD)","engines":{"oracle":"d + n / ADD_MONTHS / INTERVAL","postgres":"d + INTERVAL '1 day'","mysql":"DATE_ADD(d, INTERVAL 1 DAY)","mssql":"DATEADD(day, 1, d)","mongodb":"$dateAdd: { startDate, unit, amount }","redis":"none (add to the epoch)","elasticsearch":"date math: now+1d"}},{"id":"date-diff","concept":"Differenz zwischen zwei Datumswerten (DATEDIFF)","engines":{"oracle":"d1 - d2 (days) / MONTHS_BETWEEN","postgres":"d1 - d2 / AGE(d1, d2)","mysql":"DATEDIFF(d1, d2)","mssql":"DATEDIFF(day, d2, d1)","mongodb":"$dateDiff: { startDate, endDate, unit }","redis":"none (epoch difference)","elasticsearch":"painless: ChronoUnit.DAYS.between(...)"}},{"id":"extract","concept":"Datumsteil extrahieren (EXTRACT)","engines":{"oracle":"EXTRACT(YEAR FROM d)","postgres":"EXTRACT(YEAR FROM d) / DATE_PART","mysql":"EXTRACT(YEAR FROM d) / YEAR(d)","mssql":"DATEPART(year, d) / YEAR(d)","mongodb":"$year / $month / $dayOfMonth","redis":"none","elasticsearch":"painless: doc[f].value.year"}},{"id":"round","concept":"Runden (ROUND)","engines":{"oracle":"ROUND(x, n)","postgres":"ROUND(x, n)","mysql":"ROUND(x, n)","mssql":"ROUND(x, n)","mongodb":"$round: [x, n]","redis":"none","elasticsearch":"painless: Math.round(x)"}},{"id":"mod","concept":"Modulo (Rest)","engines":{"oracle":"MOD(a, b)","postgres":"MOD(a, b) / a % b","mysql":"MOD(a, b) / a % b","mssql":"a % b","mongodb":"$mod: [a, b]","redis":"none","elasticsearch":"painless: a % b"}},{"id":"case","concept":"Bedingung (CASE/DECODE)","engines":{"oracle":"CASE ... / DECODE(x, ...)","postgres":"CASE WHEN ... END","mysql":"CASE WHEN ... END / IF()","mssql":"CASE WHEN ... END / IIF()","mongodb":"$cond / $switch","redis":"none","elasticsearch":"painless: if/else"}},{"id":"greatest-least","concept":"Größter/kleinster Wert (GREATEST/LEAST)","engines":{"oracle":"GREATEST(...) / LEAST(...)","postgres":"GREATEST(...) / LEAST(...)","mysql":"GREATEST(...) / LEAST(...)","mssql":"GREATEST(...) / LEAST(...) (2022+)","mongodb":"$max / $min (expression)","redis":"none","elasticsearch":"painless: Math.max / Math.min"}},{"id":"string-agg","concept":"Innerhalb einer Gruppe verketten (STRING_AGG/GROUP_CONCAT/LISTAGG)","engines":{"oracle":"LISTAGG(x, ',')","postgres":"STRING_AGG(x, ',')","mysql":"GROUP_CONCAT(x)","mssql":"STRING_AGG(x, ',')","mongodb":"$push + $reduce / $concatArrays","redis":"none","elasticsearch":"terms agg / scripted_metric"}},{"id":"row-number","concept":"Zeilennummer (ROW_NUMBER)","engines":{"oracle":"ROW_NUMBER() OVER (...)","postgres":"ROW_NUMBER() OVER (...)","mysql":"ROW_NUMBER() OVER (...)","mssql":"ROW_NUMBER() OVER (...)","mongodb":"$setWindowFields: { $documentNumber }","redis":"none","elasticsearch":"none (top_hits / bucket_sort)"}},{"id":"limit","concept":"Erste N Zeilen (LIMIT/TOP/ROWNUM)","engines":{"oracle":"FETCH FIRST n ROWS ONLY / ROWNUM","postgres":"LIMIT n","mysql":"LIMIT n","mssql":"TOP n / OFFSET-FETCH","mongodb":"$limit: n / .limit(n)","redis":"LRANGE key 0 n-1","elasticsearch":"size: n"}},{"id":"current-user","concept":"Aktueller Benutzer","engines":{"oracle":"USER / SYS_CONTEXT","postgres":"CURRENT_USER","mysql":"CURRENT_USER() / USER()","mssql":"CURRENT_USER / SUSER_SNAME()","mongodb":"connectionStatus / $$USER (none, application side)","redis":"ACL WHOAMI","elasticsearch":"_security/_authenticate API"}},{"id":"gen-uuid","concept":"UUID erzeugen","engines":{"oracle":"SYS_GUID()","postgres":"gen_random_uuid()","mysql":"UUID()","mssql":"NEWID()","mongodb":"UUID() / ObjectId()","redis":"none (application side)","elasticsearch":"automatic _id / application side"}},{"id":"json-extract","concept":"JSON-Feld extrahieren","engines":{"oracle":"JSON_VALUE(j, '$.a')","postgres":"j->>'a' / j #>> '{a}'","mysql":"JSON_EXTRACT(j, '$.a') / j->>'$.a'","mssql":"JSON_VALUE(j, '$.a')","mongodb":"doc.a (native access)","redis":"JSON.GET key $.a (RedisJSON)","elasticsearch":"doc['a'] / field access (native)"}},{"id":"regexp","concept":"Abgleich mit regulärem Ausdruck (REGEXP)","engines":{"oracle":"REGEXP_LIKE(s, p)","postgres":"s ~ p / REGEXP_MATCHES","mysql":"s REGEXP p / REGEXP_LIKE","mssql":"REGEXP_LIKE (2025+) / before: LIKE / CLR","mongodb":"$regex / $regexMatch","redis":"none (SCAN MATCH glob pattern)","elasticsearch":"regexp query"},"note":"SQL Server 2025 (und Azure SQL) haben native Regex-Funktionen wie REGEXP_LIKE, REGEXP_REPLACE und REGEXP_SUBSTR eingeführt (REGEXP_LIKE erfordert Kompatibilitätsgrad 170); ältere Versionen erfordern LIKE oder eine CLR-Funktion."}]}]},"reference/params-oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"params":[{"name":"sga_target","default":"0","recommended":"~60 % des RAM auf einem dedizierten Server","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Legt die Gesamtgröße der SGA für Automatic Shared Memory Management (ASMM) fest; verteilt die Unterpools automatisch.","tags":["memory","memory","sga"]},{"name":"sga_max_size","default":"Same as sga_target","recommended":"Gleich oder etwas über sga_target","unit":"bytes (K/M/G)","dynamic":"restart","desc":"Legt die Obergrenze fest, bis zu der die SGA zur Laufzeit wachsen kann; eine Erhöhung erfordert einen Neustart der Instanz.","tags":["memory","memory","sga"]},{"name":"pga_aggregate_target","default":"10M or 20% of the SGA","recommended":"~20 % des RAM","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Legt den gesamten Ziel-PGA-Speicher aller Serverprozesse fest; beeinflusst Sort- und Hash-Operationen.","tags":["memory","memory","pga","sort"]},{"name":"pga_aggregate_limit","default":"Greatest of: 2G, 200% of pga_aggregate_target, 3M x processes (not exceeding 120% of physical RAM minus SGA)","recommended":"Der Standardwert reicht meist aus","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Harte Obergrenze für den instanzweiten PGA-Speicher; bei Überschreitung werden die Aufrufe der Sessions mit dem höchsten Verbrauch abgebrochen (SYS- und Hintergrundprozesse ausgenommen). Eingeführt in Oracle 12c.","tags":["memory","memory","pga","limit"]},{"name":"memory_target","default":"0","recommended":"0 (getrennte SGA-/PGA-Verwaltung wird bevorzugt)","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Fasst SGA und PGA mit Automatic Memory Management (AMM) unter einem gemeinsamen Ziel zusammen; auf großen Systemen nicht empfohlen.","tags":["memory","memory","amm"]},{"name":"db_cache_size","default":"0 (automatic)","recommended":"Bei Verwendung von sga_target auf 0 lassen","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Legt die Größe des Buffer Cache für die Standard-Blockgröße fest; hält Datenblöcke im Speicher.","tags":["memory","memory","cache","buffer"]},{"name":"shared_pool_size","default":"0 (automatic)","recommended":"Bei Verwendung von sga_target auf 0 lassen","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Größe des Shared Pool, der SQL/PLSQL-Parse-Ergebnisse, den Dictionary Cache und den Library Cache enthält.","tags":["memory","memory","shared","pool"]},{"name":"processes","default":"100 / 300 (depending on version)","recommended":"Je nach erwarteten gleichzeitigen Verbindungen erhöhen","unit":"integer","dynamic":"restart","desc":"Maximale Anzahl an Betriebssystemprozessen, die sich mit der Instanz verbinden können; eine Änderung erfordert einen Neustart.","tags":["connection","connection","process","process"]},{"name":"sessions","default":"Derived from processes (1.5x+22)","recommended":"Der automatisch abgeleitete Standardwert reicht meist aus","unit":"integer","dynamic":"restart","desc":"Maximale Anzahl gleichzeitig existierender Sessions; wird automatisch aus dem Wert von processes abgeleitet.","tags":["connection","connection","session","session"]},{"name":"open_cursors","default":"50","recommended":"300-1000 je nach Anforderungen der Anwendung","unit":"integer","dynamic":"dynamic","desc":"Legt fest, wie viele Cursor eine Session maximal gleichzeitig offen halten kann.","tags":["cursor","cursor","session","session"]},{"name":"cursor_sharing","default":"EXACT","recommended":"EXACT (Bind-Variablen verwenden)","unit":"EXACT | FORCE","dynamic":"dynamic","desc":"Bestimmt, ob SQL-Anweisungen mit Literalen über Bind-Variablen gemeinsam genutzt werden; FORCE ersetzt Literale.","tags":["cursor","cursor","sql","parse"]},{"name":"optimizer_mode","default":"ALL_ROWS","recommended":"ALL_ROWS","unit":"ALL_ROWS | FIRST_ROWS_n","dynamic":"dynamic","desc":"Legt das Ziel des kostenbasierten Optimizers fest: Priorität auf Gesamtdurchsatz oder auf Antwortzeit der ersten Zeile.","tags":["optimizer","optimize","plan","sql"]},{"name":"undo_retention","default":"900","recommended":"Nach der längsten Abfrage-/Flashback-Dauer","unit":"seconds","dynamic":"dynamic","desc":"Legt fest, wie lange committete Undo-Daten nach Möglichkeit aufbewahrt werden; beeinflusst das Risiko von ORA-01555.","tags":["undo","rollback","retention","flashback"]},{"name":"undo_tablespace","default":"UNDOTBS1","recommended":"Ein ausreichend großer, dedizierter Undo-Tablespace","unit":"string (tablespace name)","dynamic":"dynamic","desc":"Legt den aktiven Undo-Tablespace für das automatische Undo-Management fest; online änderbar.","tags":["undo","rollback","tablespace"]},{"name":"db_recovery_file_dest_size","default":"0","recommended":"Groß genug für Archivlogs + Backups + Flashback-Logs","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Legt die maximale Plattengröße der Fast Recovery Area (FRA) fest; enthält Archivlogs und Backups.","tags":["recovery","recovery","fra","archive"]},{"name":"db_writer_processes","default":"1 or based on CPU count","recommended":"Etwa CPU/8 (bei hoher Schreiblast erhöhen)","unit":"integer","dynamic":"restart","desc":"Legt die Anzahl der Database-Writer-Prozesse (DBWn) fest, die Dirty Blocks auf die Platte schreiben; parallelisiert die Schreiblast.","tags":["io","write","dbwr","writer"]},{"name":"parallel_max_servers","default":"parallel_threads_per_cpu x cpu_count x concurrent_parallel_users (1/2/4) x 5; capped at processes - 15","recommended":"Nach Workload und CPU-Anzahl festlegen","unit":"integer","dynamic":"dynamic","desc":"Begrenzt die maximale Anzahl gleichzeitig laufender Parallel Execution Server (PX) in der Instanz.","tags":["parallel","parallel","px","query"]},{"name":"statistics_level","default":"TYPICAL","recommended":"TYPICAL","unit":"BASIC | TYPICAL | ALL","dynamic":"dynamic","desc":"Legt die Stufe von AWR/ADDM und der automatischen Statistikerfassung fest; BASIC deaktiviert viele Diagnosen.","tags":["statistics","statistics","awr","monitoring"]},{"name":"deferred_segment_creation","default":"TRUE","recommended":"TRUE (spart Platz bei leeren Tabellen)","unit":"TRUE | FALSE","dynamic":"dynamic","desc":"Verzögert das Anlegen des ersten Segments einer leeren Tabelle/eines leeren Index bis zum Einfügen der ersten Zeile; spart Platz.","tags":["segment","deferred-segment","space","storage"]},{"name":"audit_trail","default":"NONE (DB in databases created with DBCA)","recommended":"DB oder Unified Auditing verwenden","unit":"NONE | DB | DB,EXTENDED | OS | XML","dynamic":"restart","desc":"Bestimmt, wo und wie klassische Audit-Datensätze geschrieben werden; eine Änderung erfordert einen Neustart.","tags":["audit","audit","security","security"]},{"name":"compatible","default":"Installed version (e.g., 19.0.0)","recommended":"Erst anheben, wenn die Features der neuen Version stabil laufen","unit":"version string","dynamic":"restart","desc":"Bestimmt, welche Versions-Features aktiviert sind; kann nach dem Anheben nicht gesenkt werden und erfordert einen Neustart.","tags":["compatible","compatibility","version","version"]},{"name":"db_block_size","default":"8192","recommended":"8K (OLTP); nur beim Anlegen festlegbar","unit":"bytes","dynamic":"restart","desc":"Legt die Standard-Blockgröße der Datenbank fest; wird nur beim Anlegen der Datenbank gesetzt und kann danach nicht geändert werden.","tags":["block","block","storage","io"]},{"name":"filesystemio_options","default":"none (platform-dependent)","recommended":"SETALL (Direct + Async I/O)","unit":"none | asynch | directio | setall","dynamic":"restart","desc":"Steuert die Nutzung von asynchronem und direktem I/O für Datendateien auf Dateisystemen.","tags":["io","io","async","directio"]},{"name":"log_archive_max_processes","default":"4","recommended":"Je nach Anzahl der Archivierungsziele erhöhen","unit":"integer","dynamic":"dynamic","desc":"Legt die maximale Anzahl der ARCn-Prozesse fest, die Redo-Logs archivieren; wird erhöht, um Archivierungsverzug zu verringern.","tags":["archive","archive","redo","log"]}]},"reference/params-postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"params":[{"name":"shared_buffers","default":"128MB","recommended":"~25% des RAM","unit":"bytes (kB/MB/GB)","dynamic":"restart","desc":"Menge an Shared Memory, die PostgreSQL zum Cachen von Datenblöcken reserviert; eine Änderung erfordert einen Neustart.","tags":["memory","memory","cache","buffer"]},{"name":"effective_cache_size","default":"4GB","recommended":"~50-75% des RAM","unit":"bytes (kB/MB/GB)","dynamic":"dynamic","desc":"Teilt dem Planner die geschätzte Gesamtgröße von OS- und PostgreSQL-Cache mit; beeinflusst die Indexnutzung.","tags":["memory","memory","planner","cache"]},{"name":"work_mem","default":"4MB","recommended":"16-64MB (Verbindungsanzahl beachten)","unit":"bytes (kB/MB/GB)","dynamic":"dynamic","desc":"Speicher, den Sortier- und Hash-Operationen nutzen können, bevor sie auf die Platte auslagern; wird pro Knoten des Abfrageplans zugeteilt.","tags":["memory","memory","sort","query"]},{"name":"maintenance_work_mem","default":"64MB","recommended":"256MB-1GB","unit":"bytes (kB/MB/GB)","dynamic":"dynamic","desc":"Legt den maximalen Speicher für Wartungsoperationen wie VACUUM, CREATE INDEX und ALTER TABLE fest.","tags":["memory","memory","vacuum","maintenance"]},{"name":"max_connections","default":"100","recommended":"100-200 (darüber hinaus einen Pooler verwenden)","unit":"integer","dynamic":"restart","desc":"Maximale Anzahl akzeptierter gleichzeitiger Client-Verbindungen; eine Änderung erfordert einen Neustart.","tags":["connection","connection","limit"]},{"name":"wal_buffers","default":"-1 (1/32 of shared_buffers)","recommended":"16MB","unit":"bytes (kB/MB/GB)","dynamic":"restart","desc":"Legt die Größe des Shared-Memory-Puffers fest, der WAL-Records hält, bevor sie auf die Platte geschrieben werden. Der Wert -1 (automatisch) ist auf mindestens 64kB und höchstens ein WAL-Segment (meist 16MB) begrenzt.","tags":["wal","memory","memory","buffer"]},{"name":"max_wal_size","default":"1GB","recommended":"4-16GB je nach Workload","unit":"bytes (MB/GB)","dynamic":"dynamic","desc":"Legt die weiche Obergrenze fest, bis zu der WAL wachsen kann, bevor ein automatischer Checkpoint ausgelöst wird.","tags":["wal","checkpoint","disk"]},{"name":"min_wal_size","default":"80MB","recommended":"1-2GB","unit":"bytes (MB/GB)","dynamic":"dynamic","desc":"Untergrenze, unterhalb der WAL-Dateien zur Wiederverwendung behalten werden; verringert häufiges Neuanlegen.","tags":["wal","checkpoint","disk"]},{"name":"checkpoint_completion_target","default":"0.9","recommended":"0.9","unit":"ratio (0-1)","dynamic":"dynamic","desc":"Legt den Anteil des Checkpoint-Intervalls fest, über den die Checkpoint-Schreibvorgänge verteilt werden; glättet I/O-Spitzen. Der Standardwert ist seit PG14 0.9; davor war er 0.5.","tags":["checkpoint","io","wal"]},{"name":"checkpoint_timeout","default":"5min","recommended":"15-30min","unit":"time (s/min)","dynamic":"dynamic","desc":"Legt die maximale Zeit zwischen automatischen Checkpoints fest; eine Erhöhung senkt die Schreiblast, verlängert aber die Recovery-Zeit.","tags":["checkpoint","wal","recovery"]},{"name":"random_page_cost","default":"4.0","recommended":"1.1 für SSD","unit":"decimal","dynamic":"dynamic","desc":"Legt die relativen Kosten des Planners für zufälligen Plattenzugriff fest; ein niedrigerer Wert auf SSD begünstigt Index Scans.","tags":["planner","optimize","io","index"]},{"name":"effective_io_concurrency","default":"1","recommended":"200 für SSD","unit":"integer","dynamic":"dynamic","desc":"Geschätzte Anzahl gleichzeitiger I/O-Operationen, die die Platte bedienen kann; beeinflusst das Prefetching bei Bitmap Scans. Der Standardwert ist 1 in PG17 und früher, 16 in PG18.","tags":["io","gc","ssd","prefetch"]},{"name":"default_statistics_target","default":"100","recommended":"100 (bei großen Tabellen spaltenweise erhöhen)","unit":"integer","dynamic":"dynamic","desc":"Legt den Detailgrad der von ANALYZE gesammelten Statistiken fest; ein höherer Wert verbessert die Plangenauigkeit und verlängert die Analyze-Dauer.","tags":["statistics","statistics","analyze","planner"]},{"name":"autovacuum","default":"on","recommended":"on","unit":"on | off","dynamic":"dynamic","desc":"Aktiviert den automatischen Vacuum-Prozess, der Dead Tuples entfernt und Statistiken sammelt; das Deaktivieren führt zu Bloat.","tags":["autovacuum","vacuum","maintenance","bloat"]},{"name":"autovacuum_vacuum_scale_factor","default":"0.2","recommended":"0.05-0.1 bei großen Tabellen","unit":"ratio (0-1)","dynamic":"dynamic","desc":"Legt den Anteil der Tabellenzeilen fest, der sich ändern muss, bevor die Tabelle gevacuumt wird; ein niedrigerer Wert bereinigt große Tabellen häufiger.","tags":["autovacuum","vacuum","bloat","maintenance"]},{"name":"autovacuum_max_workers","default":"3","recommended":"3-6 (bei Systemen mit vielen Tabellen erhöhen)","unit":"integer","dynamic":"restart","desc":"Legt die maximale Anzahl gleichzeitig laufender Autovacuum-Worker-Prozesse fest; in PG17 und früher erfordert eine Änderung einen Neustart. In PG18 kann sie per Reload bis zum Limit autovacuum_worker_slots geändert werden.","tags":["autovacuum","vacuum","worker","maintenance"]},{"name":"synchronous_commit","default":"on","recommended":"on (kritische Daten); off (Performance zuerst)","unit":"on | off | local | remote_write | remote_apply","dynamic":"dynamic","desc":"Bestimmt, ob ein Commit wartet, bis WAL auf die Platte geschrieben ist; off birgt bei einem Absturz das Risiko eines geringen Datenverlusts.","tags":["wal","commit","durability","performance"]},{"name":"wal_level","default":"replica","recommended":"replica (für Replikation/PITR)","unit":"minimal | replica | logical","dynamic":"restart","desc":"Bestimmt, wie viele Informationen in WAL geschrieben werden; erforderlich für Replikation und logische Replikation, eine Änderung erfordert einen Neustart.","tags":["wal","replication","replication","pitr"]},{"name":"max_wal_senders","default":"10","recommended":"Nach Anzahl der Standbys + Backups","unit":"integer","dynamic":"restart","desc":"Legt die maximale Anzahl der Prozesse fest, die gleichzeitig WAL streamen können (für Standbys und Backups).","tags":["wal","replication","replication","standby"]},{"name":"wal_keep_size","default":"0","recommended":"Genug, um den Standby-Verzug abzudecken","unit":"bytes (MB/GB)","dynamic":"dynamic","desc":"Mindestmenge an WAL, die in pg_wal für Standbys behalten wird; schützt einen verzögerten Standby, wenn kein Replication Slot existiert. Eingeführt in PG13; davor wurde wal_keep_segments (Anzahl der Segmente) verwendet.","tags":["wal","replication","replication","standby"]},{"name":"log_min_duration_statement","default":"-1 (disabled)","recommended":"1000ms, um langsame Abfragen zu erfassen","unit":"milliseconds","dynamic":"dynamic","desc":"Protokolliert Anweisungen, die länger als die angegebene Zeit laufen; dient zur Erkennung langsamer Abfragen, -1 deaktiviert es.","tags":["logging","log","slow-query","slow-query"]},{"name":"idle_in_transaction_session_timeout","default":"0 (disabled)","recommended":"60000ms gegen gehaltene Sperren","unit":"milliseconds","dynamic":"dynamic","desc":"Beendet Sessions, die innerhalb einer offenen Transaktion für die angegebene Zeit inaktiv bleiben; verringert Sperr- und Bloat-Risiko.","tags":["timeout","transaction","transaction","lock"]},{"name":"statement_timeout","default":"0 (disabled)","recommended":"Anwendungsabhängig (niedrig für OLTP)","unit":"milliseconds","dynamic":"dynamic","desc":"Bricht Anweisungen, die die angegebene Zeit überschreiten, automatisch ab; verhindert, dass außer Kontrolle geratene Abfragen Ressourcen verbrauchen.","tags":["timeout","query","query","limit"]},{"name":"shared_preload_libraries","default":"'' (empty)","recommended":"pg_stat_statements (für das Monitoring)","unit":"string (comma-separated list)","dynamic":"restart","desc":"Gibt die beim Serverstart geladenen Shared Libraries an; Extensions wie pg_stat_statements werden hier konfiguriert.","tags":["extension","extension","preload","monitoring"]},{"name":"max_parallel_workers_per_gather","default":"2","recommended":"2-4 (je nach CPU-Anzahl)","unit":"integer","dynamic":"dynamic","desc":"Legt die maximale Anzahl paralleler Worker fest, die ein einzelner Gather-/GatherMerge-Knoten starten kann; 0 deaktiviert die Parallelität.","tags":["parallel","parallel","worker","query"]}]},"reference/params-mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"params":[{"name":"innodb_buffer_pool_size","default":"128MB","recommended":"~70-80% des RAM auf einem dedizierten Server","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Der Hauptspeicherpool, in dem InnoDB Daten und Indizes cacht; in 8.0 online änderbar, große Änderungen erfordern jedoch Sorgfalt.","tags":["memory","memory sizing","innodb","buffer"]},{"name":"innodb_buffer_pool_instances","default":"1 (or 8 if size>1GB)","recommended":"8 bei großen Pools","unit":"integer","dynamic":"restart","desc":"Legt die Anzahl der Instanzen fest, in die der Buffer Pool aufgeteilt wird; verringert Mutex-Konkurrenz bei Parallelität, eine Änderung erfordert einen Neustart.","tags":["memory","memory sizing","innodb","concurrency"]},{"name":"innodb_redo_log_capacity","default":"100MB (8.0.30+)","recommended":"1-8GB (je nach Schreiblast; ~1 Stunde Redo in der Spitze)","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Legt den gesamten Plattenplatz des InnoDB-Redo-Logs fest; eine Erhöhung glättet Checkpoint-Druck und Schreibspitzen. Eingeführt in MySQL 8.0.30 und ohne Neustart per SET GLOBAL/SET PERSIST änderbar. Die alten innodb_log_file_size (Standard 48MB) und innodb_log_files_in_group sind seit 8.0.30 veraltet; vor 8.0.30 wird die Redo-Größe mit diesen beiden festgelegt und erfordert einen Neustart.","tags":["innodb","redo","log","io"]},{"name":"innodb_flush_log_at_trx_commit","default":"1","recommended":"1 (volles ACID); 2 (Performance)","unit":"0 | 1 | 2","dynamic":"dynamic","desc":"Steuert, ob das Redo-Log bei jedem Commit geschrieben und auf die Platte synchronisiert wird; 1 bietet volle Dauerhaftigkeit, 2/0 bergen ein Verlustrisiko.","tags":["innodb","commit","durability","acid"]},{"name":"innodb_flush_method","default":"8.0: fsync (Linux) / 8.4: O_DIRECT (if supported); Windows: unbuffered","recommended":"O_DIRECT (Linux)","unit":"fsync | O_DIRECT | O_DIRECT_NO_FSYNC","dynamic":"restart","desc":"Legt fest, wie InnoDB Daten- und Logdateien auf die Platte flusht; O_DIRECT vermeidet doppeltes Puffern, eine Änderung erfordert einen Neustart.","tags":["innodb","io","flush","gc"]},{"name":"innodb_io_capacity","default":"8.0: 200 / 8.4: 10000","recommended":"Nach den realen IOPS des Storage (1000-2000 für SSD unter 8.0)","unit":"integer (IOPS)","dynamic":"dynamic","desc":"Legt die geschätzte IOPS-Kapazität der Platte fest, die InnoDB-Hintergrundaufgaben (Flush, Merge) nutzen. In MySQL 8.4 wurden die Standardwerte auf innodb_io_capacity=10000 und innodb_io_capacity_max=20000 angehoben (200/2000 unter 8.0).","tags":["innodb","io","iops","ssd"]},{"name":"innodb_read_io_threads","default":"8.0: 4 / 8.4: half the number of logical CPUs (minimum 4)","recommended":"4-8 (je nach Leselast)","unit":"integer","dynamic":"restart","desc":"Legt die Anzahl der InnoDB-Hintergrund-Threads für Lese-I/O-Anfragen fest; eine Änderung erfordert einen Neustart.","tags":["innodb","io","thread","read"]},{"name":"innodb_write_io_threads","default":"4","recommended":"4-8 (je nach Schreiblast)","unit":"integer","dynamic":"restart","desc":"Legt die Anzahl der InnoDB-Hintergrund-Threads für Schreib-I/O-Anfragen fest; eine Änderung erfordert einen Neustart.","tags":["innodb","io","thread","write"]},{"name":"max_connections","default":"151","recommended":"200-500 je nach Workload","unit":"integer","dynamic":"dynamic","desc":"Legt die maximale Anzahl erlaubter gleichzeitiger Client-Verbindungen fest; online änderbar.","tags":["connection","connections","limit"]},{"name":"thread_cache_size","default":"-1 (autosized)","recommended":"Automatisch; bei vielen kurzlebigen Verbindungen erhöhen","unit":"integer","dynamic":"dynamic","desc":"Legt fest, wie viele Threads zur Wiederverwendung im Cache gehalten werden; verringert das Erzeugen von Threads für neue Verbindungen.","tags":["connection","connections","thread","cache"]},{"name":"table_open_cache","default":"4000","recommended":"4000-8000 je nach Anzahl offener Tabellen","unit":"integer","dynamic":"dynamic","desc":"Legt die Cache-Größe für offene Tabellen-Handles über alle Threads fest; verringert den Aufwand für das erneute Öffnen von Tabellen.","tags":["cache","table","table cache","open"]},{"name":"tmp_table_size","default":"16MB","recommended":"64-256MB","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Legt die maximale Größe einer temporären In-Memory-Tabelle fest, bevor sie in eine Tabelle auf der Platte umgewandelt wird; wirkt zusammen mit max_heap_table_size.","tags":["memory","memory sizing","temp","temporary table"]},{"name":"max_heap_table_size","default":"16MB","recommended":"Gleich wie tmp_table_size","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Legt die maximale Größe fest, bis zu der Tabellen der MEMORY-Engine und temporäre In-Memory-Tabellen wachsen können.","tags":["memory","memory sizing","temp","heap"]},{"name":"sort_buffer_size","default":"256KB","recommended":"256KB-2MB (Kosten pro Verbindung beachten)","unit":"bytes (K/M)","dynamic":"dynamic","desc":"Pro Session zugeteilter Puffer für ORDER BY- und GROUP BY-Sortierungen; ein zu großer Wert verbraucht Speicher.","tags":["memory","memory sizing","sort","buffer"]},{"name":"join_buffer_size","default":"256KB","recommended":"256KB-1MB (besser per Index lösen)","unit":"bytes (K/M)","dynamic":"dynamic","desc":"Pro Session zugeteilter Puffer für Joins, die keinen Index nutzen können; die dauerhafte Lösung ist ein passender Index.","tags":["memory","memory sizing","join","buffer"]},{"name":"sync_binlog","default":"1","recommended":"1 (volle Sicherheit); 100-1000 bei hoher Last","unit":"integer","dynamic":"dynamic","desc":"Legt fest, nach wie vielen Commits das Binary Log auf die Platte synchronisiert wird; 1 ist am sichersten, höhere Werte tauschen Dauerhaftigkeit gegen Performance.","tags":["binlog","replication","durability","commit"]},{"name":"binlog_expire_logs_seconds","default":"2592000 (30 days)","recommended":"Nach dem Backup-/Replikationsfenster","unit":"seconds","dynamic":"dynamic","desc":"Legt fest, wie lange Binary-Log-Dateien vor dem automatischen Löschen aufbewahrt werden; steuert die Plattennutzung.","tags":["binlog","log","retention","replication"]},{"name":"long_query_time","default":"10","recommended":"1 (langsame Abfragen erfassen)","unit":"seconds","dynamic":"dynamic","desc":"Legt die Dauer fest, die eine Abfrage überschreiten muss, um im Slow Query Log erfasst zu werden; akzeptiert Bruchwerte.","tags":["slow-query","slow-query-log","log","performance"]},{"name":"slow_query_log","default":"OFF","recommended":"ON (für die Diagnose)","unit":"ON | OFF","dynamic":"dynamic","desc":"Steuert, ob Abfragen, die long_query_time überschreiten, in das Slow Query Log geschrieben werden; für Performance-Analysen aktivieren.","tags":["slow-query","slow-query-log","log","monitoring"]},{"name":"wait_timeout","default":"28800 (8 hours)","recommended":"Nach dem Anwendungspool (z. B. 600)","unit":"seconds","dynamic":"dynamic","desc":"Legt die maximale Zeit fest, die eine nicht interaktive Verbindung inaktiv bleiben darf, bevor sie geschlossen wird; räumt inaktive Verbindungen auf.","tags":["timeout","connection","connections","idle"]},{"name":"max_allowed_packet","default":"64MB","recommended":"64-256MB für große BLOBs/Zeilen","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Legt die maximale Größe eines einzelnen Pakets oder einer erzeugten/Zwischen-Zeile fest; für große BLOB-Übertragungen erhöhen.","tags":["packet","packet size","blob","network"]},{"name":"innodb_lock_wait_timeout","default":"50","recommended":"10-50 für OLTP","unit":"seconds","dynamic":"dynamic","desc":"Legt fest, wie lange eine InnoDB-Transaktion auf eine Zeilensperre wartet, bevor ein Timeout eintritt.","tags":["innodb","lock","lock wait","timeout"]},{"name":"character_set_server","default":"utf8mb4","recommended":"utf8mb4","unit":"text (character set)","dynamic":"dynamic","desc":"Legt den Standard-Zeichensatz des Servers fest; utf8mb4 bietet volle Unicode-Unterstützung (einschließlich Emoji).","tags":["charset","character-set","unicode","utf8mb4"]}]},"reference/params-mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"params":[{"name":"max server memory (MB)","default":"2147483647","recommended":"Gesamt-RAM abzüglich des Anteils für OS und andere Dienste (~10-25%)","unit":"MB","dynamic":"dynamic","desc":"Legt die maximale Speichermenge fest, die der SQL Server Buffer Pool und andere Speicherverbraucher nutzen können. Der Standardwert ist praktisch unbegrenzt; laufen weitere Dienste auf dem Server, muss er gesenkt werden, damit dem Betriebssystem genug Speicher bleibt. Wird mit sp_configure gesetzt und mit RECONFIGURE sofort wirksam.","tags":["memory","memory limit","buffer pool"]},{"name":"min server memory (MB)","default":"0","recommended":"Auf kritischen Instanzen eine sinnvolle Untergrenze relativ zu max","unit":"MB","dynamic":"dynamic","desc":"Die minimale Speichermenge, die SQL Server nach der Zuteilung nicht mehr an das Betriebssystem zurückgibt. Verhindert, dass SQL Server unter Speicherdruck zu viel Speicher freigibt. Wird mit sp_configure gesetzt und mit RECONFIGURE wirksam.","tags":["memory","memory limit"]},{"name":"max degree of parallelism","default":"0","recommended":"Meist 8 oder die Anzahl physischer Kerne pro NUMA-Knoten","unit":"count","dynamic":"dynamic","desc":"Begrenzt die maximale Anzahl an Prozessoren (Kernen), die eine einzelne Abfrage bei paralleler Ausführung nutzen kann. 0 bedeutet, dass alle Kerne genutzt werden dürfen; bei OLTP-Workloads wird der Wert begrenzt, um übermäßige Parallelität zu vermeiden. Mit sp_configure + RECONFIGURE sofort wirksam.","tags":["parallelism","parallel query","maxdop","cpu"]},{"name":"cost threshold for parallelism","default":"5","recommended":"25-50 auf moderner Hardware","unit":"cost","dynamic":"dynamic","desc":"Die minimale geschätzte Kostenschwelle, die ein Abfrageplan erreichen muss, um parallel ausgeführt zu werden. Der Standardwert 5 ist sehr niedrig und führt dazu, dass kleine Abfragen unnötig parallelisiert werden; eine Erhöhung verbessert das serverweite Parallelitätsverhalten. Mit sp_configure + RECONFIGURE sofort wirksam.","tags":["parallelism","parallel query","cpu"]},{"name":"max worker threads","default":"0","recommended":"0 (von SQL Server berechnen lassen)","unit":"count","dynamic":"dynamic","desc":"Legt die maximale Anzahl an Worker Threads fest, die SQL Server zur Verarbeitung von Client-Anfragen erzeugen kann. Bei 0 wird der Wert automatisch anhand von Prozessoranzahl und Architektur berechnet. Erweiterte Option; wird mit sp_configure + RECONFIGURE ohne Neustart angewendet (sys.configurations.is_dynamic = 1).","tags":["threads","worker","cpu","concurrency"]},{"name":"optimize for ad hoc workloads","default":"0","recommended":"1 (ein)","unit":"0/1","dynamic":"dynamic","desc":"Cacht bei Ad-hoc-Abfragen, die nur einmal laufen, bei der ersten Ausführung statt des vollständigen Plans nur einen kleinen kompilierten Plan-Stub. Verhindert, dass sich der Plan Cache mit Einmalplänen füllt, und verbessert die Speichernutzung. Mit sp_configure + RECONFIGURE sofort wirksam.","tags":["plan cache","memory","memory usage"]},{"name":"backup compression default","default":"0","recommended":"1 (ein)","unit":"0/1","dynamic":"dynamic","desc":"Sorgt dafür, dass neue Backups standardmäßig komprimiert werden. Komprimierung verringert Backup-Größe und Backup-Dauer deutlich, kostet aber etwas CPU. Mit sp_configure + RECONFIGURE sofort wirksam.","tags":["backup","backup size","compression","backup compression"]},{"name":"fill factor (%)","default":"0","recommended":"0 oder 90-100 (je nach Workload)","unit":"%","dynamic":"restart","desc":"Legt in Prozent fest, wie voll Seiten der Blattebene beim Anlegen oder Neuaufbau (REBUILD) eines Index gefüllt werden. 0 und 100 füllen die Seiten vollständig; niedrigere Werte verringern Page Splits, erhöhen aber Platten- und Speichernutzung. Der serverweite Standardwert wird mit sp_configure geändert, wird aber erst nach einem Neustart des Dienstes wirksam (is_dynamic = 0); die FILLFACTOR-Option pro Index ist sofort nutzbar.","tags":["index","page split","storage","disk usage"]},{"name":"recovery interval (min)","default":"0","recommended":"0 (automatisch)","unit":"minutes","dynamic":"dynamic","desc":"Legt die Häufigkeit automatischer Checkpoints über die angestrebte maximale Recovery-Zeit fest. Bei 0 verwaltet SQL Server die Ziel-Recovery-Zeit automatisch pro Datenbank. Mit sp_configure + RECONFIGURE sofort wirksam.","tags":["checkpoint","recovery","crash recovery"]},{"name":"remote query timeout (s)","default":"600","recommended":"600 oder 0 (unbegrenzt), je nach Workload","unit":"seconds","dynamic":"dynamic","desc":"Die Zeit, die der Server auf eine Antwort auf Remote-Abfragen (über einen Linked Server) wartet, bevor ein Timeout eintritt. 0 bedeutet unbegrenztes Warten. Mit sp_configure + RECONFIGURE sofort wirksam.","tags":["timeout","remote","linked server"]},{"name":"network packet size (B)","default":"4096","recommended":"4096 (außer in Sonderfällen nicht ändern)","unit":"bytes","dynamic":"dynamic","desc":"Die Paketgröße für die Datenübertragung über das Netzwerk. Außer bei speziellen Massenoperationen mit sehr großen Datenmengen wird der Standardwert empfohlen; eine falsche Einstellung kann die Performance verschlechtern. Mit sp_configure + RECONFIGURE wirksam.","tags":["network","network packet","packet"]},{"name":"database compatibility level (ALTER DATABASE)","default":"Depends on server version","recommended":"Die neueste getestete Stufe","unit":"level","dynamic":"dynamic","desc":"Bestimmt, mit welchem Verhalten und welchem Query-Optimizer-Modell welcher SQL Server-Version eine Datenbank läuft. Wird pro Datenbank mit ALTER DATABASE ... SET COMPATIBILITY_LEVEL gesetzt und ist sofort wirksam; beeinflusst Optimizer-Features wie die Kardinalitätsschätzung.","tags":["database","compatibility level","optimizer","compatibility"]},{"name":"MAXDOP (database scoped configuration)","default":"0 (inherits the server setting)","recommended":"Ein zum Datenbank-Workload passender Wert","unit":"count","dynamic":"dynamic","desc":"Überschreibt die serverweite Einstellung max degree of parallelism auf Datenbankebene. Wird mit ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP gesetzt und ist sofort wirksam; ermöglicht unterschiedliches Parallelitätsverhalten für verschiedene Datenbanken.","tags":["parallelism","parallel query","maxdop","database scoped"]},{"name":"Anzahl der tempdb-Dateien (Empfehlung)","default":"SQL Server 2016+ setup: equal to the number of logical cores, at most 8 (1 before)","recommended":"Gleich der Anzahl logischer Kerne, höchstens 8 (bei Bedarf in Viererschritten erhöhen)","unit":"count","dynamic":"dynamic","desc":"Die Anzahl der tempdb-Datendateien ist wichtig, um Konkurrenz um Allocation Pages zu verringern. Die allgemeine Empfehlung lautet, gleich große Dateien bis zur Anzahl der Kerne (max. 8) zu verwenden. Eine mit ALTER DATABASE tempdb ADD FILE hinzugefügte Datei wird ohne Neustart sofort genutzt; sind die vorhandenen Dateien jedoch voll, bringe alle Dateien für ein ausgewogenes Proportional Fill auf gleiche Größe. Das Entfernen einer tempdb-Datei (REMOVE FILE) ist oft erst nach einem Neustart möglich, weil sich die Datei nicht leert.","tags":["tempdb","storage","data files","contention"]},{"name":"Trace Flag 1117 / 1118 (Hinweis)","default":"Default behavior for tempdb in SQL 2016+","recommended":"Ab SQL 2016+ meist unnötig; in älteren Versionen aktiv","unit":"flag","dynamic":"restart","desc":"TF 1117 erzwingt, dass alle Dateien einer Dateigruppe gemeinsam wachsen; TF 1118 erzwingt Uniform-Extent-Zuteilung statt Mixed Extents. Seit SQL Server 2016 sind diese Verhaltensweisen für tempdb Standard, daher sind die Flags meist unnötig. Als Startup-Flag hinzugefügt, ist ein Neustart erforderlich.","tags":["trace flag","tempdb","note"]},{"name":"Trace Flag 3226","default":"Off","recommended":"Aktiv (unterdrückt Meldungen über erfolgreiche Backups)","unit":"flag","dynamic":"dynamic","desc":"Verhindert, dass erfolgreiche Backup-Vorgänge in das SQL Server-Fehlerprotokoll und das Windows-Ereignisprotokoll geschrieben werden. Auf Systemen mit häufigen Log-Backups verhindert es, dass sich die Logs mit unnötigen Meldungen füllen (Log-Spam). Kann zur Laufzeit sofort mit DBCC TRACEON(3226, -1) aktiviert werden; damit es Neustarts überdauert, zusätzlich -T3226 als Startparameter hinzufügen.","tags":["trace flag","backup","backup messages","log"]},{"name":"lock pages in memory (Hinweis zur Windows-Richtlinie)","default":"Off","recommended":"Wird dem Dienstkonto in bestimmten Szenarien gewährt","unit":"policy","dynamic":"restart","desc":"Eine Windows-Richtlinie für Benutzerrechte, die dem SQL Server-Dienstkonto erlaubt zu verhindern, dass das Betriebssystem seine Speicherseiten auf die Platte auslagert. Kann Performance-Schwankungen unter Speicherdruck verringern, muss aber sorgfältig konfiguriert werden. Eine Änderung der Richtlinie erfordert einen Neustart des Dienstes.","tags":["memory","memory paging","windows","policy","note"]}]},"reference/params-mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"params":[{"name":"storage.wiredTiger.engineConfig.cacheSizeGB","default":"(RAM - 1GB) * 0.5","recommended":"Standard beibehalten oder ~50% des verfügbaren RAM","unit":"GB","dynamic":"restart","desc":"Legt in Gigabyte die Speichermenge fest, die dem internen Cache der Storage Engine WiredTiger zugeteilt wird. Standardmäßig wird die Hälfte von (Gesamt-RAM - 1GB) verwendet. Ein manuelles Setzen des Werts ist nur in Sonderfällen nötig und erfordert einen Neustart von mongod.","tags":["memory","memory","cache","wiredtiger"]},{"name":"net.maxIncomingConnections","default":"1000000 (effectively limited by OS limits)","recommended":"Nach Workload und ulimit des Betriebssystems","unit":"count","dynamic":"restart","desc":"Begrenzt die maximale Anzahl gleichzeitiger eingehender Verbindungen, die eine mongod- oder mongos-Instanz akzeptiert. Liegt der Wert über dem Dateideskriptor-/Verbindungslimit des Betriebssystems (ulimit), hat er keine Wirkung; die tatsächliche Obergrenze bestimmt das OS (sichtbar als current + available in db.serverStatus().connections). Die Änderung erfolgt in der Konfigurationsdatei und erfordert einen Neustart.","tags":["network","network","connections","connection"]},{"name":"operationProfiling.slowOpThresholdMs","default":"100","recommended":"100 oder niedriger je nach Workload","unit":"ms","dynamic":"dynamic","desc":"Legt in Millisekunden die Dauerschwelle fest, ab der eine Operation als langsam gilt und vom Profiler sowie im Log erfasst wird. Operationen über dieser Schwelle werden erfasst, um die Diagnose zu erleichtern. Zur Laufzeit sofort mit db.setProfilingLevel() änderbar.","tags":["profiling","slow query","slow query","monitoring"]},{"name":"replication.oplogSizeMB","default":"~5% of disk size (max 50GB)","recommended":"Groß genug, um das Wartungsfenster abzudecken","unit":"MB","dynamic":"dynamic","desc":"Legt in Megabyte die maximale Größe der Oplog-Collection fest, die Operationen auf Replica-Set-Mitgliedern speichert. Ein ausreichend großes Oplog lässt verzögerte Mitglieder aufholen, ohne dass ein vollständiger Resync nötig ist. Auf einem laufenden Replica Set sofort mit dem Befehl replSetResizeOplog änderbar.","tags":["replication","oplog","replica set"]},{"name":"security.authorization","default":"disabled","recommended":"enabled (rollenbasierte Zugriffskontrolle)","unit":"enabled/disabled","dynamic":"restart","desc":"Aktiviert die rollenbasierte Zugriffskontrolle (RBAC); wenn aktiviert, müssen sich Clients authentifizieren und autorisiert sein. In Produktionsumgebungen muss sie immer aktiviert sein. Wird in der Konfigurationsdatei gesetzt und erfordert einen Neustart.","tags":["security","security","authorization","authorization","rbac"]},{"name":"net.compression.compressors","default":"snappy,zstd,zlib","recommended":"snappy,zstd (der Standard passt)","unit":"list","dynamic":"restart","desc":"Legt die Kompressionsalgorithmen und ihre Präferenzreihenfolge für Client- und clusterinternen Netzwerkverkehr fest. Verringert die Netzwerkbandbreite auf Kosten etwas CPU. Wird in der Konfigurationsdatei gesetzt und erfordert einen Neustart.","tags":["network","network","compression","compression"]},{"name":"storage.wiredTiger.collectionConfig.blockCompressor","default":"snappy","recommended":"snappy (Geschwindigkeit) oder zstd (hohe Kompression)","unit":"algorithm","dynamic":"restart","desc":"Legt den Blockkompressionsalgorithmus fest, den WiredTiger beim Schreiben von Collection-Daten auf die Platte nutzt. snappy bevorzugt Geschwindigkeit, zlib und zstd bieten höhere Kompressionsraten. Gilt nur für Collections, die nach der Einstellung angelegt werden, und erfordert einen Neustart.","tags":["storage","storage","compression","compression","wiredtiger"]},{"name":"storage.directoryPerDB","default":"false","recommended":"true, wenn du Platten trennen willst","unit":"true/false","dynamic":"restart","desc":"Speichert die Daten jeder Datenbank in einem eigenen Unterverzeichnis, sodass verschiedene Datenbanken auf verschiedenen Plattenvolumes liegen können. Nur bei leerem Datenverzeichnis aktivierbar. Die Änderung erfolgt in der Konfigurationsdatei und erfordert einen Neustart.","tags":["storage","storage","directory"]},{"name":"setParameter.cursorTimeoutMillis","default":"600000","recommended":"Der Standard (10 Minuten), je nach Workload","unit":"ms","dynamic":"dynamic","desc":"Legt in Millisekunden fest, wie lange inaktive Cursor warten, bevor der Server sie automatisch schließt. Verhindert, dass verwaiste Cursor Ressourcen verbrauchen. Zur Laufzeit sofort mit db.adminCommand({setParameter:1}) änderbar.","tags":["cursor","timeout","setparameter"]},{"name":"setParameter.transactionLifetimeLimitSeconds","default":"60","recommended":"Der Standard, sofern keine langen Transaktionen nötig sind","unit":"seconds","dynamic":"dynamic","desc":"Legt in Sekunden die maximale Zeit fest, die eine Multi-Document-Transaktion offen bleiben kann, bevor sie automatisch abgebrochen wird. Verhindert, dass lang laufende Transaktionen Ressourcen und den WiredTiger-Cache blockieren. Sofort mit setParameter änderbar.","tags":["transaction","transaction","setparameter"]},{"name":"setParameter.maxTransactionLockRequestTimeoutMillis","default":"5","recommended":"Der Standard, je nach Lock-Konkurrenz","unit":"ms","dynamic":"dynamic","desc":"Legt in Millisekunden die maximale Zeit fest, die eine Transaktion auf eine benötigte Sperre wartet, bevor ein Timeout eintritt. Durch Anpassen des Sperrwarteverhaltens werden Transaktionen nicht zu lange blockiert. Sofort mit setParameter änderbar.","tags":["transaction","lock","lock","setparameter"]},{"name":"net.tls.mode","default":"disabled","recommended":"requireTLS (in Produktion)","unit":"mode","dynamic":"restart","desc":"Bestimmt die Nutzung TLS/SSL-verschlüsselter Verbindungen durch den Server. requireTLS erzwingt die Verschlüsselung aller Verbindungen; preferTLS und allowTLS bieten Zwischenmodi für Übergangsphasen. Wird zusammen mit der Zertifikatskonfiguration verwendet und erfordert einen Neustart.","tags":["security","security","tls","ssl","encryption"]},{"name":"storage.syncPeriodSecs","default":"60","recommended":"60 (Standard nicht ändern)","unit":"seconds","dynamic":"dynamic","desc":"Legt in Sekunden fest, wie oft WiredTiger Checkpoints erstellt, also das Intervall zwischen den Flushes der In-Memory-Daten auf die Platte. Der Standard passt für die meisten Workloads, eine Änderung wird meist nicht empfohlen. Wird in der Konfigurationsdatei gesetzt; zur Laufzeit auch ohne Neustart mit db.adminCommand({ setParameter: 1, syncdelay: <seconds> }) änderbar.","tags":["storage","storage","checkpoint","sync"]},{"name":"replication.enableMajorityReadConcern","default":"true","recommended":"true (ab 5.0+ nicht änderbar)","unit":"true/false","dynamic":"restart","desc":"Erlaubt die Nutzung der Read-Concern-Stufe majority; diese Stufe garantiert das Lesen von Daten, die auf eine Mehrheit der Mitglieder geschrieben wurden und nicht zurückgerollt werden. Seit MongoDB 5.0 ist diese Einstellung nicht änderbar und immer true; in älteren Versionen (z. B. in einer PSA-Architektur) konnte sie auf false gesetzt werden. Wird in der Konfigurationsdatei gesetzt und erfordert einen Neustart.","tags":["replication","read concern","replica set"]},{"name":"processManagement.fork","default":"false","recommended":"false mit systemd, true für einen manuellen Daemon","unit":"true/false","dynamic":"restart","desc":"Lässt den mongod-Prozess forken und im Hintergrund als Daemon laufen. Mit systemd oder einem ähnlichen Service Manager bleibt dies meist false, da der Service Manager die Prozessverwaltung übernimmt. Wird in der Konfigurationsdatei gesetzt und beeinflusst das Startverhalten.","tags":["process","process","daemon","fork"]}]},"reference/params-redis.json":{"engineId":"redis","engine":"Redis","order":6,"params":[{"name":"maxmemory","default":"0 (unlimited)","recommended":"~70-75% des physischen RAM","unit":"bytes","dynamic":"dynamic","desc":"Legt die maximale Speichermenge fest, die Redis für Daten nutzen kann. Der Wert 0 bedeutet unbegrenzte Nutzung und kann zu Out-of-Memory-Zuständen führen; ein Limit wird dringend empfohlen. Sofort mit CONFIG SET maxmemory änderbar.","tags":["memory","memory"]},{"name":"maxmemory-policy","default":"noeviction","recommended":"allkeys-lru für einen Cache, sonst noeviction","unit":"policy","dynamic":"dynamic","desc":"Die Richtlinie, die bestimmt, welche Keys Redis bei Erreichen des maxmemory-Limits wie verdrängt. noeviction gibt Schreibfehler zurück, allkeys-lru entfernt die am längsten nicht genutzten Keys. Sofort mit CONFIG SET änderbar.","tags":["memory","memory","eviction","cache"]},{"name":"maxclients","default":"10000","recommended":"Nach Workload und ulimit des OS","unit":"count","dynamic":"dynamic","desc":"Legt die maximale Anzahl akzeptierter gleichzeitiger Client-Verbindungen fest. Das Dateideskriptorlimit des Betriebssystems kann diesen Wert begrenzen. Sofort mit CONFIG SET maxclients änderbar.","tags":["connections","connection","clients"]},{"name":"appendonly","default":"no","recommended":"yes, wenn Persistenz erforderlich ist","unit":"yes/no","dynamic":"dynamic","desc":"Aktiviert den Persistenzmechanismus AOF (Append Only File); wenn aktiviert, wird jede Schreiboperation an eine Logdatei angehängt. Bietet ein geringeres Datenverlustrisiko als RDB-Snapshots. Sofort mit CONFIG SET appendonly yes aktivierbar.","tags":["persistence","persistence","aof"]},{"name":"appendfsync","default":"everysec","recommended":"everysec (ausgewogen)","unit":"policy","dynamic":"dynamic","desc":"Legt fest, wie oft die AOF-Datei auf die Platte synchronisiert wird (fsync). always synchronisiert bei jedem Schreibvorgang und ist am sichersten, aber langsam; everysec synchronisiert einmal pro Sekunde und balanciert Performance und Dauerhaftigkeit; no überlässt die Synchronisierung dem Betriebssystem. Sofort mit CONFIG SET änderbar.","tags":["persistence","persistence","aof","fsync"]},{"name":"save","default":"3600 1 300 100 60 10000","recommended":"Workload-abhängig oder deaktiviert (nur AOF)","unit":"rule","dynamic":"dynamic","desc":"Definiert die Bedingungen (eine bestimmte Anzahl an Änderungen innerhalb einer bestimmten Zeit), unter denen ein RDB-Snapshot auf die Platte gespeichert wird. Ein leerer Wert deaktiviert RDB-Snapshots vollständig. Sofort mit CONFIG SET save änderbar.","tags":["persistence","persistence","rdb","snapshot"]},{"name":"tcp-keepalive","default":"300","recommended":"300 (der Standard passt)","unit":"seconds","dynamic":"dynamic","desc":"Legt in Sekunden das Intervall fest, in dem Redis TCP-Keepalive-ACKs an Client-Verbindungen sendet. Sorgt dafür, dass tote Verbindungen erkannt und geschlossen und halboffene Verbindungen bereinigt werden. Sofort mit CONFIG SET änderbar.","tags":["network","network","tcp","keepalive"]},{"name":"timeout","default":"0","recommended":"0 (Verbindungen nie schließen) oder eine sinnvolle Leerlaufzeit","unit":"seconds","dynamic":"dynamic","desc":"Legt in Sekunden die maximale Leerlaufzeit einer Client-Verbindung fest; bei Überschreitung wird die Verbindung geschlossen. Der Wert 0 bedeutet, dass Clients unbegrenzt inaktiv bleiben können. Sofort mit CONFIG SET timeout änderbar.","tags":["connections","connection","timeout","idle"]},{"name":"hz","default":"10","recommended":"10 (auf ausgelasteten Systemen bis 100)","unit":"Hz","dynamic":"dynamic","desc":"Legt fest, wie oft pro Sekunde Redis seine Hintergrundaufgaben ausführt (Ablauf von Keys, Prüfen von Client-Timeouts usw.). Höhere Werte ermöglichen eine genauere Steuerung, erhöhen aber die CPU-Last. Sofort mit CONFIG SET hz änderbar.","tags":["performance","cpu","background"]},{"name":"activedefrag","default":"no","recommended":"yes bei hoher Speicherfragmentierung","unit":"yes/no","dynamic":"dynamic","desc":"Aktiviert die aktive Speicherdefragmentierung; reorganisiert Daten im laufenden Betrieb, um die Speicherfragmentierung zu verringern. Nützlich auf lang laufenden Instanzen mit hoher Fragmentierung. Sofort mit CONFIG SET activedefrag yes aktivierbar.","tags":["memory","memory","defragmentation"]},{"name":"maxmemory-samples","default":"5","recommended":"5 (für genauere Verdrängung erhöhbar)","unit":"count","dynamic":"dynamic","desc":"Legt fest, wie viele Keys die LRU- und LFU-Verdrängungsalgorithmen bei der Auswahl eines Keys stichprobenartig prüfen. Höhere Werte kommen echtem LRU/LFU-Verhalten näher, verbrauchen aber etwas mehr CPU. Sofort mit CONFIG SET änderbar.","tags":["memory","memory","eviction","lru"]},{"name":"io-threads","default":"1","recommended":"2-4 auf Mehrkernservern (je nach Kernanzahl)","unit":"count","dynamic":"restart","desc":"Legt die Anzahl der I/O-Threads für das Schreiben auf und Lesen von Client-Sockets fest. Kann den Durchsatz auf Mehrkernsystemen mit hohem Netzwerkverkehr erhöhen. Diese Einstellung ist nur in der Konfigurationsdatei änderbar und erfordert einen Neustart.","tags":["performance","threads","io","cpu"]},{"name":"repl-backlog-size","default":"1mb","recommended":"Größer, je nach Schreibrate und Ausfalldauer","unit":"bytes","dynamic":"dynamic","desc":"Legt die Größe des auf dem Primary gehaltenen Replikationspuffers für eine partielle Resynchronisierung fest, wenn die Verbindung einer Replica abbricht. Ein ausreichend großer Puffer lässt die Replica nach kurzen Ausfällen ohne vollständige Synchronisierung aufholen. Sofort mit CONFIG SET änderbar.","tags":["replication","replication","backlog"]},{"name":"repl-diskless-sync","default":"yes","recommended":"yes (in schnellen Netzwerken)","unit":"yes/no","dynamic":"dynamic","desc":"Sendet bei einer vollständigen Synchronisierung RDB-Daten direkt über den Socket an Replicas, ohne sie auf die Platte zu schreiben. Beschleunigt die Synchronisierung auf Systemen mit Platten-I/O-Engpass. Sofort mit CONFIG SET repl-diskless-sync änderbar.","tags":["replication","replication","diskless","sync"]},{"name":"requirepass","default":"(empty)","recommended":"Ein starkes Passwort setzen","unit":"password","dynamic":"dynamic","desc":"Legt das Passwort fest, mit dem sich Clients per AUTH authentifizieren müssen, bevor sie Befehle ausführen. Ein starkes Passwort ist nötig, um unbefugten Zugriff auf Redis zu verhindern. Sofort mit CONFIG SET requirepass änderbar.","tags":["security","security","authentication","password"]},{"name":"cluster-enabled","default":"no","recommended":"yes, wenn horizontale Skalierung erforderlich ist","unit":"yes/no","dynamic":"restart","desc":"Aktiviert den Redis-Cluster-Modus; wenn aktiviert, werden Daten über Hash Slots auf mehrere Knoten verteilt. Dient der horizontalen Skalierung und automatischen Datenpartitionierung. Diese Einstellung ist nur in der Konfigurationsdatei änderbar und erfordert einen Neustart.","tags":["cluster","cluster","scaling","scaling"]},{"name":"cluster-node-timeout","default":"15000","recommended":"15000 oder ein an die Netzwerklatenz angepasster Wert","unit":"ms","dynamic":"dynamic","desc":"Legt im Cluster-Modus in Millisekunden die maximale Zeit fest, die ein Knoten ohne Antwort bleiben darf, bevor er als nicht erreichbar (fail) gilt. Beeinflusst direkt das Failover-Verhalten und die Cluster-Stabilität. Sofort mit CONFIG SET änderbar.","tags":["cluster","cluster","timeout","failover"]},{"name":"aof-use-rdb-preamble","default":"yes","recommended":"yes (schnelles Neuladen)","unit":"yes/no","dynamic":"dynamic","desc":"Aktiviert das hybride Persistenzformat, bei dem ein AOF-Rewrite einen kompakten RDB-Snapshot an den Dateianfang schreibt, gefolgt von inkrementellen AOF-Befehlen. Das verkürzt die AOF-Ladezeit beim Neustart. Sofort mit CONFIG SET änderbar.","tags":["persistence","persistence","aof","rdb"]}]},"reference/params-elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"params":[{"name":"-Xms / -Xmx (JVM-Heap)","default":"Automatic depending on version (half of RAM)","recommended":"50% des physischen RAM, höchstens ~30-31GB (Xms=Xmx)","unit":"GB","dynamic":"restart","desc":"Legt die Anfangsgröße (-Xms) und die maximale Größe (-Xmx) des JVM-Heaps fest; beide sollten gleich gesetzt werden. Empfohlen wird, die Hälfte des Gesamt-RAM bzw. das ~32GB-Limit, ab dem komprimierte Objektzeiger nicht mehr greifen, nicht zu überschreiten. Wird in jvm.options gesetzt und erfordert einen Neustart des Knotens.","tags":["memory","memory","jvm","heap"]},{"name":"indices.memory.index_buffer_size","default":"10%","recommended":"10% (bei hoher Indexierungslast erhöhbar)","unit":"% / bytes","dynamic":"restart","desc":"Legt den Heap-Anteil für den Speicherpuffer fest, der neue Dokumente während der Indexierung hält. Ein größerer Puffer kann den Indexierungsdurchsatz bei schreiblastigen Workloads verbessern. Wird pro Knoten in elasticsearch.yml gesetzt und erfordert einen Neustart.","tags":["memory","memory","indexing","buffer"]},{"name":"thread_pool.write.queue_size","default":"10000","recommended":"10000 (bei Queue-Ablehnungen vorsichtig erhöhen)","unit":"count","dynamic":"restart","desc":"Legt die Queue-Länge des Thread Pools für Index-, Delete- und Bulk-Schreiboperationen fest. Ist die Queue voll, werden Anfragen abgelehnt. Ein zu hoher Wert kann Speicherdruck verursachen. Wird in elasticsearch.yml gesetzt und erfordert einen Neustart.","tags":["thread pool","write","queue","queue"]},{"name":"thread_pool.search.queue_size","default":"1000","recommended":"1000 (bei Such-Ablehnungen vorsichtig erhöhen)","unit":"count","dynamic":"restart","desc":"Legt die Queue-Länge des Thread Pools für Suchoperationen fest. Übersteigen gleichzeitige Suchen die Pool-Kapazität, warten Anfragen in dieser Queue und werden abgelehnt, wenn sie voll ist. Wird in elasticsearch.yml gesetzt und erfordert einen Neustart.","tags":["thread pool","search","search","queue"]},{"name":"indices.fielddata.cache.size","default":"unbounded","recommended":"Ein Limit wie 20-40% (zum Speicherschutz)","unit":"% / bytes","dynamic":"restart","desc":"Legt die maximale Größe des Fielddata-Cache fest, der für Sortierungen und Aggregationen auf Textfeldern verwendet wird. Da der Standard unbegrenzt ist, wird ein Limit empfohlen, um Speichererschöpfung zu verhindern. Wird in elasticsearch.yml gesetzt und erfordert einen Neustart.","tags":["memory","memory","fielddata","cache"]},{"name":"index.number_of_shards","default":"1","recommended":"Nach Datenvolumen; Ziel 10-50GB pro Shard","unit":"count","dynamic":"restart","desc":"Legt die Anzahl der Primary Shards fest, in die ein Index aufgeteilt wird. Es ist eine statische Index-Einstellung (kein Neustart, aber nur beim Anlegen des Index angebbar): Auf einem bestehenden Index kann sie nicht direkt geändert werden; ändere sie, indem du per neuem Index + _reindex einen neuen Index erzeugst oder die APIs _split (vervielfachen) / _shrink (auf einen Teiler verringern) nutzt. Zu viele Shards erhöhen den Cluster-Overhead, zu wenige begrenzen die Skalierung.","tags":["index","index","shard","scaling"]},{"name":"index.number_of_replicas","default":"1","recommended":"In Produktion mindestens 1 (für Hochverfügbarkeit)","unit":"count","dynamic":"dynamic","desc":"Legt die Anzahl der Replica Shards fest, die für jeden Primary Shard gehalten werden. Replicas sorgen sowohl für Hochverfügbarkeit als auch für Leseperformance. Diese Einstellung ist auf einem Live-Index sofort mit der _settings-API änderbar.","tags":["index","index","replica","availability"]},{"name":"index.refresh_interval","default":"1s","recommended":"30s oder -1 (vorübergehend) bei hoher Indexierungslast","unit":"duration","dynamic":"dynamic","desc":"Legt fest, wie oft der Refresh läuft, der neu indexierte Dokumente für die Suche sichtbar macht. Ein höherer Wert oder -1 während Bulk-Loads verbessert den Indexierungsdurchsatz deutlich. Sofort mit der _settings-API änderbar.","tags":["index","index","refresh","indexing"]},{"name":"indices.breaker.total.limit","default":"95%","recommended":"95% (Standard; vorsichtig ändern)","unit":"%","dynamic":"dynamic","desc":"Verhindert, dass der Knoten mit OutOfMemory abstürzt, indem der maximale Heap-Anteil festgelegt wird, den alle Circuit Breaker zusammen nutzen dürfen. Clusterweit sofort mit der _cluster/settings-API änderbar.","tags":["memory","memory","circuit breaker","stability"]},{"name":"cluster.routing.allocation.disk.watermark.low","default":"85%","recommended":"85% (der Standard passt)","unit":"% / bytes","dynamic":"dynamic","desc":"Überschreitet die Plattennutzung eines Knotens diese Schwelle, weist Elasticsearch diesem Knoten keine neuen Shards mehr zu (bestehende Shards bleiben). Es ist die erste Warnstufe, um eine volle Platte zu verhindern. Sofort mit der _cluster/settings-API änderbar.","tags":["disk","allocation","watermark","storage"]},{"name":"cluster.routing.allocation.disk.watermark.high","default":"90%","recommended":"90% (der Standard passt)","unit":"% / bytes","dynamic":"dynamic","desc":"Überschreitet die Plattennutzung eines Knotens diese Schwelle, beginnt Elasticsearch, Shards von diesem Knoten auf andere Knoten zu verlagern. Entlastet Knoten, deren Platten volllaufen. Sofort mit der _cluster/settings-API änderbar.","tags":["disk","allocation","watermark","storage"]},{"name":"cluster.routing.allocation.disk.watermark.flood_stage","default":"95%","recommended":"95% (der Standard passt)","unit":"% / bytes","dynamic":"dynamic","desc":"Überschreitet die Plattennutzung diese kritische Schwelle, werden Indizes mit einem Shard auf dem betroffenen Knoten in den Nur-Lese-Modus (read-only-allow-delete) versetzt und Schreibvorgänge blockiert. Verhindert Datenbeschädigung, bevor die Platte ganz voll ist; ab 7.4+ wird die Sperre automatisch aufgehoben, sobald die Plattennutzung unter das High Watermark fällt. In 8.x ist bei großen Platten statt des Prozentwerts max_headroom (Standard 100GB frei) maßgeblich. Sofort mit der _cluster/settings-API änderbar.","tags":["disk","allocation","watermark","read-only"]},{"name":"index.translog.durability","default":"request","recommended":"request (Dauerhaftigkeit) oder async (Performance)","unit":"mode","dynamic":"dynamic","desc":"Legt fest, wann das Transaktionslog (Translog) auf die Platte synchronisiert wird (fsync). request synchronisiert nach jeder Anfrage und minimiert Datenverlust; async synchronisiert periodisch und verbessert die Performance, birgt aber bei einem Absturz ein Datenverlustrisiko. Sofort mit der _settings-API änderbar.","tags":["translog","durability","durability","indexing"]},{"name":"indices.recovery.max_bytes_per_sec","default":"40mb","recommended":"Je nach Netzwerk- und Plattenkapazität erhöhbar","unit":"bytes/s","dynamic":"dynamic","desc":"Legt die maximale Datenübertragungsrate pro Knoten während der Shard Recovery fest. Ein höherer Wert beschleunigt die Recovery, kann aber Netzwerk- und Plattenressourcen des normalen Workloads beanspruchen. Sofort mit der _cluster/settings-API änderbar.","tags":["recovery","recovery","throttle","network"]},{"name":"action.destructive_requires_name","default":"true","recommended":"true (blockiert destruktive Wildcard-Operationen)","unit":"true/false","dynamic":"dynamic","desc":"Blockiert destruktive Operationen wie das Löschen von Indizes per Wildcard (*) oder _all und verlangt, dass der Indexname explizit angegeben wird. Wichtige Sicherheitseinstellung gegen das versehentliche Löschen aller Indizes. Sofort mit der _cluster/settings-API änderbar.","tags":["security","security","destructive","safety"]},{"name":"discovery.seed_hosts","default":"(empty / 127.0.0.1)","recommended":"Adressen der master-eligible Knoten im Cluster","unit":"list","dynamic":"restart","desc":"Legt die Liste der Seed-Host-Adressen (master-eligible Knoten) fest, die ein Knoten kontaktiert, um dem Cluster beizutreten. Erforderlich für die Cluster-Discovery und damit sich die Knoten gegenseitig finden. Wird in elasticsearch.yml gesetzt und erfordert einen Neustart.","tags":["discovery","cluster","cluster","network"]}]},"reference/waits-oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"waits":[{"name":"log file sync","kind":"wait","meaning":"Die Session hat ein COMMIT (oder ROLLBACK) abgesetzt und wartet darauf, dass der LGWR-Prozess die Redo-Records in die Online-Redo-Log-Dateien schreibt und dies bestätigt.","causes":"Langsames Redo-Log-I/O, sehr häufige kleine Commits, ein überlasteter LGWR-Prozess, durch CPU-Mangel verzögerter LGWR, Redo-Logs auf langsamen Platten.","action":"Commit-Häufigkeit senken (Batch-Commits), Redo-Log-Dateien auf schnelle Platten mit niedriger Latenz (SSD) verlagern, unnötige Commits entfernen, COMMIT NOWAIT/BATCH nur für Arbeit erwägen, bei der Verlust tolerierbar ist (WARNUNG: verringert die Dauerhaftigkeit; scheinbar committete Transaktionen können bei einem Absturz verloren gehen), und die I/O-Latenz des Log Writer messen (log file parallel write).","tags":["commit","redo","io","lgwr"]},{"name":"db file sequential read","kind":"wait","meaning":"Die Session führt einen physischen Single-Block-Read durch; sie wartet darauf, dass der Block von der Platte in den Buffer Cache geholt wird, meist bei Indexzugriff oder beim Lesen eines Tabellenblocks per ROWID.","causes":"Index Scans, ineffiziente Zugriffspläne, ein kleiner Buffer Cache, ein langsames Plattensubsystem, wegen vieler physischer Reads nicht im Cache gefundene Blöcke.","action":"SQL tunen, um unnötige Reads zu verringern, passende Indizes hinzufügen, den Buffer Cache vergrößern, das I/O-Subsystem verbessern und die Abfragen mit den meisten Reads per AWR/ASH ermitteln.","tags":["io","index","single-block","read"]},{"name":"db file scattered read","kind":"wait","meaning":"Die Session führt einen physischen Multi-Block-Read durch; sie wartet darauf, dass mehrere Blöcke von der Platte gelesen werden, meist bei einem Full Table Scan oder einem Fast Full Index Scan.","causes":"Full Table Scans, fehlende oder ungenutzte Indizes, ineffiziente Abfragepläne, Scans großer Tabellen, falsche Optimizer-Statistiken.","action":"Unnötige Full Scans durch Indizes verhindern, Abfragepläne prüfen, Statistiken aktualisieren, die Einstellung db_file_multiblock_read_count untersuchen und tatsächlich nötige Full Scans ggf. parallelisieren.","tags":["io","full-scan","multi-block","read"]},{"name":"direct path read","kind":"wait","meaning":"Die Session liest Blöcke unter Umgehung des Buffer Cache direkt in die PGA; meist bei parallelen Abfragen, Scans großer Tabellen oder LOB-Reads.","causes":"Parallele Full Table Scans, eine serielle Direct-Path-Read-Entscheidung (Adaptive Direct Read), Scans großer Tabellen, Reads aus dem temporären Tablespace (temp), LOB-Zugriff.","action":"Den Bedarf an großen Scans durch Query Tuning verringern, die PGA-Größe bewerten, bei hoher Temp-Nutzung Sortier-/Hash-Operationen optimieren und die I/O-Bandbreite erhöhen.","tags":["io","direct-path","parallel","read"]},{"name":"direct path write","kind":"wait","meaning":"Die Session schreibt Blöcke direkt auf die Platte, ohne den Buffer Cache zu durchlaufen; meist bei Direct-Path-Inserts, Sort-Spills oder LOB-Writes.","causes":"Direct-Path-INSERT-Operationen (APPEND), Sortier-/Hash-Spills in den temporären Tablespace, paralleles DML, LOB-Writes, langsame Temp- oder Datenplatten.","action":"PGA für Sortier-/Hash-Operationen vergrößern, um Temp-Spills zu verringern, den Temp-Tablespace auf schnelle Platten legen, unnötige Direct-Path-Loads prüfen und die I/O-Performance verbessern.","tags":["io","direct-path","temp","write"]},{"name":"buffer busy waits","kind":"wait","meaning":"Die Session will auf einen Block im Buffer Cache zugreifen, der jedoch von einer anderen Session in einem inkompatiblen Modus genutzt wird; sie wartet auf dessen Freigabe.","causes":"Starker gleichzeitiger Zugriff auf denselben Block (Hot Block), unzureichende Freelists/ASSM, Right-Hand-Block-Konkurrenz bei einem sequenziellen Index (Right-Hand Index Growth), Segmente mit hoher DML-Intensität.","action":"Hot Blocks verteilen (Hash-Partitionierung, Reverse Key Index), ASSM verwenden, Freelists/Freelist Groups erhöhen und bei Tabellen mit sequenziellen Schlüsseln Sequence-Caching und -Verteilung verbessern.","tags":["concurrency","buffer","hot-block","contention"]},{"name":"read by other session","kind":"wait","meaning":"Eine andere Session liest den von dieser Session benötigten Block bereits von der Platte; diese Session wartet, bis der Read desselben Blocks abgeschlossen ist.","causes":"Gleichzeitige physische Read-Anfragen für dieselben Hot Blocks, ein kleiner Buffer Cache, langsames I/O, viele Sessions, die gleichzeitig auf beliebte Index- oder Tabellenblöcke zugreifen.","action":"Buffer Cache vergrößern, um physische Reads zu verringern, Hot Segments ermitteln und ihre Zugriffspläne verbessern, I/O-Latenz senken und unnötige Full Scans beseitigen.","tags":["io","concurrency","read","hot-block"]},{"name":"latch: cache buffers chains","kind":"wait","meaning":"Die Session wartet auf den Latch, der eine Hash Chain des Buffer Cache schützt; beim Auffinden von Blöcken im Buffer Cache tritt Serialisierung auf.","causes":"Ineffizientes SQL mit übermäßigem logischem I/O, starker gleichzeitiger Zugriff auf Hot Blocks, wiederholter Zugriff auf dieselben Blöcke über Nested Loops, sehr beliebte Index-Root-/Branch-Blöcke.","action":"SQL tunen, um logische Reads zu verringern, unnötigen Nested-Loop-Zugriff beseitigen, Hot Blocks verteilen und Abfragen mit übermäßigen Buffer Gets per AWR/ASH finden und beheben.","tags":["latch","concurrency","logical-io","buffer"]},{"name":"cursor: pin S wait on X","kind":"wait","meaning":"Die Session will einen Cursor per Mutex im Shared-Modus (S) pinnen, wartet aber, weil eine andere Session denselben Cursor im Exclusive-Modus (X) hält.","causes":"Eine hohe Hard-/Soft-Parse-Rate, sehr häufig geparstes beliebtes SQL, Literal-Abfragen ohne Bind-Variablen, Wildwuchs von Child Cursors, viele Sessions, die dasselbe SQL gleichzeitig parsen.","action":"Parse-Last durch Bind-Variablen verringern, Cursor in der Anwendung cachen (session_cached_cursors), unnötiges Re-Parsing reduzieren und SQL mit hohem Version Count beheben.","tags":["mutex","parse","cursor","concurrency"]},{"name":"library cache lock","kind":"wait","meaning":"Die Session wartet auf eine Sperre auf dem Handle eines Objekts im Library Cache (Package, View, Tabellendefinition); meist bei gleichzeitigem Zugriff auf bzw. Änderungen an der Objektdefinition.","causes":"DDL auf genutzten Objekten, ständig neu kompilierte Packages/Views, ungültige Objekte, viele gleichzeitige Verbindungen und Parsing, Änderungen an Berechtigungen (Grant).","action":"DDL außerhalb der Spitzenzeiten ausführen, unnötige Kompilierungen verhindern, ungültige Objekte beheben und übermäßige Connect-/Parse-Schleifen auf Anwendungsseite verringern.","tags":["library-cache","concurrency","ddl","parse"]},{"name":"library cache: mutex X","kind":"wait","meaning":"Die Session wartet auf einen Mutex, um ein Objekt im Library Cache im Exclusive-Modus (X) zu schützen; meist bei hohem Parsing und Konkurrenz um Objekt-Handles.","causes":"Übermäßiges Parsing (Hard/Soft), viele verschiedene Literal-SQLs ohne Binds, eine hohe Anzahl an Child Cursors, häufige Objektinvalidierung, viele Sessions mit gleichzeitigem Zugriff auf dasselbe Objekt.","action":"Bind-Variablen und Cursor-Caching nutzen, um Parsing zu verringern, SQL-Literale standardisieren, Probleme mit hohem Version Count beheben und das Verbindungsverhalten der Anwendung verbessern.","tags":["mutex","library-cache","parse","concurrency"]},{"name":"enq: TX - row lock contention","kind":"wait","meaning":"Die Session will eine von einer anderen Transaktion gesperrte Zeile ändern und wartet, bis diese Transaktion committet/zurückrollt und die Zeilensperre freigibt.","causes":"Gleichzeitiges UPDATE/DELETE derselben Zeilen, lang laufende Transaktionen ohne Commit, ein schlecht gestalteter Anwendungsablauf, indirekte Zeilensperren über Unique-/Foreign Keys, hängende Sessions.","action":"Transaktionen kurz halten und zügig committen, das umkämpfte Zugriffsmuster umgestalten, bei Bedarf geordneten Zugriff mit SELECT FOR UPDATE erzwingen und die blockierende Session per ASH finden und auflösen.","tags":["lock","transaction","row-lock","contention"]},{"name":"enq: TX - allocate ITL entry","kind":"wait","meaning":"Die Session braucht einen ITL-Slot (Interested Transaction List) in einem Block, um darauf zu arbeiten, muss aber warten, weil im Block kein freier ITL-Slot mehr übrig ist.","causes":"Eine niedrige INITRANS-Einstellung, eine kleine Blockgröße, viele gleichzeitige Transaktionen im selben Block, dicht gepackte Blöcke mit starkem DML.","action":"INITRANS für Tabelle/Index erhöhen, PCTFREE anheben, um weniger Zeilen pro Block abzulegen, Hot Blocks verteilen und das Objekt bei Bedarf neu aufbauen.","tags":["lock","transaction","itl","block"]},{"name":"gc buffer busy acquire","kind":"wait","meaning":"In einer RAC-Umgebung will die Session einen Block über den Global Cache beziehen, wartet aber, weil eine andere Session derselben Instanz bereits eine globale Anfrage für denselben Block gestartet hat.","causes":"Gemeinsame Nutzung von Hot Blocks zwischen Instanzen im RAC, unzureichende Interconnect-Performance, Right-Hand-Konkurrenz bei einem sequenziellen Index, ein schlecht partitionierter Workload, starker Global-Cache-Verkehr.","action":"Workload sinnvoll auf Instanzen aufteilen (Application Affinity/Services), Hot Segments verteilen (Reverse Key/Partition), Interconnect-Latenz verbessern und gc-Konkurrenz mit dem RAC-Abschnitt des AWR und GV$-Views analysieren.","tags":["rac","global-cache","concurrency","interconnect"]},{"name":"gc cr block busy","kind":"wait","meaning":"In einer RAC-Umgebung hat die Session einen Block für Consistent Read (CR) von einer anderen Instanz angefordert und wartet, bis diese Instanz den Block vorbereitet und über den Interconnect sendet.","causes":"Gemeinsame Nutzung von Hot Blocks zwischen Instanzen, ein langsamer oder ausgelasteter Interconnect, starker Redo-/Commit-Verkehr, ein schlecht partitionierter Workload, übermäßige Global-Cache-CR-Anfragen.","action":"Workload-Affinität verbessern, Interconnect-Bandbreite und -Latenz optimieren, Hot Blocks reduzieren, Commit- und DML-Muster prüfen und RAC tunen.","tags":["rac","global-cache","cr","interconnect"]},{"name":"log file switch (checkpoint incomplete)","kind":"wait","meaning":"Die Session will auf ein neues Redo-Log wechseln, aber der Checkpoint für die nächste Logdatei ist noch nicht abgeschlossen; LGWR kann dieses Log daher nicht wiederverwenden, und die gesamte Redo-Erzeugung stoppt.","causes":"Zu kleine oder zu wenige Redo-Log-Dateien, langsames DBWR-/Checkpoint-I/O, eine hohe Redo-Erzeugungsrate, eine unzureichende Anzahl an Log-Gruppen.","action":"Redo-Log-Dateien vergrößern und Gruppen hinzufügen, DBWR-/Checkpoint-I/O-Performance verbessern, die Redo-Erzeugung durch Anwendungsoptimierungen verringern und die Log-Switch-Häufigkeit überwachen.","tags":["redo","checkpoint","log-switch","io"]},{"name":"free buffer waits","kind":"wait","meaning":"Die Session sucht einen sauberen/freien Buffer im Buffer Cache, um einen neuen Block zu lesen, wartet aber auf einen freien Buffer, weil DBWR Dirty Buffers nicht schnell genug auf die Platte schreiben kann.","causes":"Langsames DBWR-Schreib-I/O, ein kleiner Buffer Cache, schnelle Erzeugung von Dirty Buffers durch starkes DML, unzureichende Checkpoint-Geschwindigkeit, ein ausgelastetes Plattensubsystem.","action":"Schreib-I/O der Datendateien beschleunigen, Buffer Cache vergrößern, die Anzahl der DBWR-Prozesse (db_writer_processes) bewerten und DML-Intensität sowie Checkpoint-Einstellungen prüfen.","tags":["buffer","dbwr","io","checkpoint"]},{"name":"Buffer Cache Hit Ratio","kind":"metric","meaning":"Der Anteil angeforderter Blöcke, die ohne Plattenzugriff aus dem Buffer Cache bedient werden; zeigt, wie viel Prozent der logischen Reads keinen physischen Read erforderten.","causes":"Eine niedrige Ratio liegt meist an einem kleinen Buffer Cache, ineffizienten Full Scans, einem kalten Cache oder vielen physischen Reads. Allein betrachtet kann sie irreführend sein; auch eine hohe Ratio kann schlechtes SQL verbergen.","action":"Die Ratio nicht für sich allein als Ziel nehmen; zusammen mit Wait Events und Trends physischer Reads bewerten. Ineffizientes SQL tunen, unnötige logische Reads verringern und den Cache passend zum Workload dimensionieren.","tags":["metric","buffer-cache","ratio","memory"]},{"name":"Average Active Sessions","kind":"metric","meaning":"Die durchschnittliche Anzahl gleichzeitig aktiver Sessions (auf CPU laufend oder auf ein Event wartend) über ein Intervall; fasst Gesamtlast und Engpassintensität der Datenbank zusammen.","causes":"Ein hoher AAS kann an CPU-Sättigung, I/O-Waits, Lock-/Latch-Konkurrenz oder gestiegener Parallelität liegen. Werte nahe an oder über der Anzahl der CPU-Kerne deuten auf einen Ressourcenengpass hin.","action":"AAS mit der Anzahl der CPU-Kerne vergleichen, die dominante Wait Class (CPU/IO/Concurrency) per ASH ermitteln, SQL und Sessions mit der höchsten Last tunen und das Ressourcen- oder Parallelitätsproblem beheben.","tags":["metric","load","ash","sessions"]}]},"reference/waits-postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"waits":[{"name":"LWLock:WALWrite","kind":"wait","meaning":"Die Session wartet auf den Lightweight Lock, der zum Schreiben der WAL-Puffer auf die Platte nötig ist; meist verursacht durch Serialisierung der WAL-Schreibvorgänge beim Commit.","causes":"Langsames WAL-Platten-I/O, sehr häufige/kleine Commits, hohe Schreiblast, unzureichende WAL-Schreibbandbreite, viele Backends, die gleichzeitig WAL schreiben.","action":"WAL auf eine schnelle Platte mit niedriger Latenz verlagern, Commits bündeln (synchronous_commit, commit_delay bewerten), wal_buffers erhöhen, unnötige Schreiboperationen verringern.","tags":["wal","lwlock","commit","io"]},{"name":"LWLock:BufferContent","kind":"wait","meaning":"Die Session wartet auf den Lightweight Lock eines Buffers im Shared Buffer Pool, der den Seiteninhalt schützt; deutet auf Konkurrenz durch gleichzeitigen Zugriff auf die Seite hin.","causes":"Starke gleichzeitige Reads/Writes auf derselben Seite (Hot Page), Right-Edge-Konkurrenz bei sequenziellen Indizes, kleine Tabellen mit vielen Updates, hohe Parallelität.","action":"Hot Pages verteilen (fillfactor-Einstellung, Partitionierung), Konkurrenz bei sequenziellen Schlüsseln verringern, häufig aktualisierte Zeilen umstrukturieren, das gleichzeitige Zugriffsmuster prüfen.","tags":["lwlock","buffer","concurrency","contention"]},{"name":"Lock:transactionid","kind":"wait","meaning":"Die Session wartet darauf, dass eine andere Transaktion, die die zu ändernde Zeile gesperrt hat, abschließt (Commit/Rollback); die Sperrwartezeit bezieht sich auf die ID der Zieltransaktion.","causes":"Gleichzeitiges UPDATE/DELETE auf denselben Zeilen, lang laufende Transaktionen ohne Commit, Idle-in-Transaction-Sessions, ein schlecht gestalteter umkämpfter Ablauf.","action":"Transaktionen kurz halten und zügig committen, Idle-in-Transaction-Sessions begrenzen (idle_in_transaction_session_timeout), das umkämpfte Zugriffsmuster umgestalten, die blockierende Abfrage per pg_stat_activity finden.","tags":["lock","transaction","row-lock","contention"]},{"name":"Lock:tuple","kind":"wait","meaning":"Die Session wartet auf eine Sperre auf ein bestimmtes Tupel (Zeile); tritt auf, wenn sich mehrere Sessions anstellen, um dieselbe Zeile zu aktualisieren/sperren.","causes":"Gleichzeitiges SELECT FOR UPDATE/UPDATE auf derselben Zeile, Konkurrenz um Hot Rows (z. B. Zählerzeilen), lange gehaltene Zeilensperren, hohe Parallelität.","action":"Updates von Hot Rows verteilen oder in eine Queue stellen, Transaktionsdauer verkürzen, umkämpfte Muster wie Zähler umgestalten (z. B. gebündelte Updates), FOR UPDATE SKIP LOCKED erwägen.","tags":["lock","tuple","row-lock","contention"]},{"name":"Lock:relation","kind":"wait","meaning":"Die Session wartet auf eine Sperre auf Tabellenebene für eine Tabelle/einen Index (Relation); meist bei Konkurrenz zwischen inkompatiblen Sperrmodi von DDL und DML.","causes":"DDL wie ALTER/DROP/TRUNCATE während laufender Abfragen, VACUUM FULL mit aggressiver Sperre, gleichzeitiger Zugriff auf dieselbe Tabelle in inkompatiblen Modi, lang laufende Abfragen.","action":"DDL in ruhigen Zeiten mit kurzem lock_timeout ausführen, regulären Autovacuum statt VACUUM FULL nutzen, lange Abfragen überwachen, Operationen prüfen, die den Zugriff auf die Tabelle serialisieren.","tags":["lock","relation","ddl","table-lock"]},{"name":"IO:DataFileRead","kind":"wait","meaning":"Die Session wartet darauf, dass eine Datendateiseite von der Platte in shared buffers gelesen wird; das ist der physische Read-Wait, der auftritt, wenn die angeforderte Seite nicht im Cache ist.","causes":"Kleine shared_buffers, ineffiziente Abfragepläne und große Scans, ein kalter Cache, ein langsames Plattensubsystem, unzureichende Indexnutzung.","action":"shared_buffers und den Page Cache des OS auf den Workload abstimmen, ineffiziente Abfragen und fehlende Indizes beheben, die I/O-Performance verbessern, effective_cache_size korrekt setzen.","tags":["io","read","buffer","disk"]},{"name":"IO:WALSync","kind":"wait","meaning":"Die Session wartet darauf, dass WAL dauerhaft auf die Platte geschrieben wird (fsync/Synchronisierung); für die Commit-Dauerhaftigkeit wartet sie auf das physische Flushen von WAL.","causes":"Hohe fsync-Latenz des Storage, sehr häufige Commits, eine leistungsschwache WAL-Platte, eine Platte ohne Write-Back-Cache, hohe Schreiblast.","action":"WAL-Platte auf schnellen Storage mit niedriger fsync-Latenz verlagern, Commits bündeln (commit_delay/commit_siblings), synchronous_commit gegen die fachlichen Anforderungen abwägen, die Storage-Schicht verbessern.","tags":["wal","io","fsync","commit"]},{"name":"IO:DataFileWrite","kind":"wait","meaning":"Die Session wartet darauf, dass eine Dirty Page auf die Platte geschrieben wird; meist, wenn das Backend selbst schreibt, um eine saubere Seite im Buffer Pool zu finden.","causes":"Langsame Schreib-I/O auf den Datenplatten, Checkpoint-Stau, ein unzureichender Background Writer, ein kleiner Buffer Pool, das Backend schreibt wegen hoher Schreiblast selbst.","action":"Checkpoints glätten (checkpoint_completion_target, max_wal_size), den Background Writer tunen (bgwriter_lru_maxpages), die Schreibleistung der Platten verbessern, shared_buffers prüfen.","tags":["io","write","checkpoint","bgwriter"]},{"name":"Client:ClientRead","kind":"wait","meaning":"Das Backend wartet auf den nächsten Befehl bzw. die nächsten Daten vom Client; tritt auf, wenn der Server inaktiv ist und darauf wartet, dass der Client Daten sendet.","causes":"Ein langsames Netzwerk, Sessions im Zustand idle in transaction, eine Clientanwendung, die Daten langsam sendet/empfängt, langsam abgearbeitete große Ergebnismengen, Probleme mit dem Connection Pool.","action":"Latenz auf Anwendungs- und Netzwerkseite untersuchen, Sessions im Zustand idle in transaction begrenzen, Ergebnisse effizient abarbeiten (z. B. Cursor/Batch), den Connection Pool verbessern. Dieser Wait ist meist kein Engpass der Datenbank.","tags":["client","network","idle","application"]},{"name":"IPC:ProcArrayGroupUpdate","kind":"wait","meaning":"Die Session wartet darauf, dass das Group-Leader-Backend das Ende ihrer Transaktion gruppenweise im ProcArray markiert; dies ist die Serialisierung von ProcArray-Aktualisierungen beim Commit.","causes":"Sehr hohe Commit-/Transaktionsrate, übermäßig viele gleichzeitige kurze Transaktionen, hohe Anzahl an Verbindungen, intensiver Snapshot-/ProcArray-Zugriff.","action":"Transaktionsrate und Anzahl gleichzeitiger Verbindungen steuern (Connection Pool), unnötige kurze Transaktionen reduzieren, die Anzahl der Verbindungen bei sehr hoher Parallelität begrenzen, die Last ausbalancieren.","tags":["ipc","commit","procarray","concurrency"]},{"name":"LWLock:LockManager","kind":"wait","meaning":"Die Session wartet auf den Lightweight Lock des Lock Managers, der die Heavyweight-Lock-Tabelle schützt; verursacht durch die Serialisierung von Lock-Anforderungen und -Freigaben.","causes":"Abfragen, die viele Tabellen/Partitionen berühren (Überschreitung des Fast-Path-Lock-Limits), hohe Parallelität, übermäßig partitionierte Tabellen, eine Last, die viele Locks anfordert/freigibt.","action":"Die Anzahl der pro Abfrage berührten Partitionen/Tabellen reduzieren (Partition Pruning), übermäßige Partitionierung prüfen, Parallelität mit einem Connection Pool steuern, Vorgänge mit unnötigen Locks vereinfachen.","tags":["lwlock","lock-manager","concurrency","partitioning"]},{"name":"Timeout:VacuumDelay","kind":"wait","meaning":"Der Vacuum-/Autovacuum-Prozess pausiert bewusst wegen des kostenbasierten Vacuum Delay; ein beabsichtigter Wait zur Begrenzung der I/O-Last.","causes":"Drosselung durch die Einstellungen vacuum_cost_delay/autovacuum_vacuum_cost_delay, ein niedriges cost_limit, Vacuum auf vielen Tabellen, wenig aggressive Autovacuum-Einstellungen.","action":"Wenn Vacuum zu langsam ist und sich Bloat/Dead Tuples ansammeln, cost_limit erhöhen und cost_delay senken, Anzahl der Autovacuum-Worker und Schwellenwerte tunen, gegen die I/O-Kapazität abwägen.","tags":["vacuum","timeout","throttle","maintenance"]},{"name":"cache hit ratio","kind":"metric","meaning":"Anteil der Blockanforderungen, die ohne Plattenzugriff aus den Shared Buffers bedient werden; berechnet aus blks_hit und blks_read in pg_stat_database.","causes":"Eine niedrige Quote deutet meist auf kleine shared_buffers, ineffiziente große Scans, einen kalten Cache oder zu wenig Speicher hin. Da auch der Page Cache des Betriebssystems Lesezugriffe bedient, kann die Quote allein irreführend sein.","action":"Die Quote zusammen mit Wait Events und I/O-Trends interpretieren, shared_buffers und effective_cache_size tunen, ineffiziente Abfragen beheben; bei dauerhaft niedrigem Wert Speicher und Ausführungspläne untersuchen.","tags":["metric","cache","ratio","memory"]},{"name":"replication lag","kind":"metric","meaning":"Wie weit ein Standby-/Replikat-Server beim Anwenden des vom Primary erzeugten WAL zurückliegt; gemessen in Bytes (LSN-Differenz) oder Sekunden Verzögerung.","causes":"Ein langsames Netzwerk, hohe Abfragelast auf dem Standby (vor allem Replay-Konflikte), langsame Platten-I/O auf dem Standby, eine hohe Schreibrate auf dem Primary, lang laufende Leseabfragen, die die Recovery verzögern.","action":"Write-/Flush-/Replay-Lag mit pg_stat_replication überwachen, Netzwerk- und I/O-Leistung des Standby verbessern, konfliktträchtige lange Abfragen steuern (hot_standby_feedback/max_standby_streaming_delay), den Bedarf an synchroner Replikation prüfen.","tags":["metric","replication","lag","standby"]}]},"reference/waits-mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"waits":[{"name":"wait/synch/mutex/innodb/buf_pool_LRU_list_mutex","kind":"wait","meaning":"Der Thread wartet auf den Mutex, der die LRU-Liste des InnoDB Buffer Pool schützt; deutet auf Serialisierung beim Lesen/Verdrängen von Seiten hin. In 8.0 wurde der frühere einzelne buf_pool_mutex aufgeteilt; die anderen zugehörigen Instrumente sind buf_pool_free_list_mutex, buf_pool_flush_state_mutex und buf_pool_zip_mutex.","causes":"Intensiver Buffer-Pool-Zugriff bei hoher Parallelität, eine einzige Buffer-Pool-Instanz, häufiges Lesen/Verdrängen von Seiten, ein zu kleiner Buffer Pool.","action":"Den Mutex durch Erhöhen von innodb_buffer_pool_instances aufteilen, innodb_buffer_pool_size am Working Set ausrichten, Abfragen tunen, um physische Lesezugriffe zu reduzieren, und die Parallelität steuern.","tags":["innodb","mutex","buffer-pool","concurrency"]},{"name":"wait/io/file/innodb/innodb_data_file","kind":"wait","meaning":"Der Thread wartet auf den Abschluss physischer Datei-I/O (Lesen/Schreiben) auf InnoDB-Datendateien (ibd/ibdata).","causes":"Ein langsames Plattensubsystem, viele physische Lesezugriffe wegen eines kleinen Buffer Pool, große Table Scans, Checkpoint-/Flush-Druck, unzureichende I/O-Kapazität.","action":"Den Buffer Pool vergrößern, ineffiziente Abfragen und fehlende Indizes beheben, auf schnellen Speicher umziehen (SSD/NVMe), innodb_io_capacity passend zum Speicher setzen und das Flush-Verhalten prüfen.","tags":["innodb","io","datafile","disk"]},{"name":"wait/lock/table/sql/handler","kind":"wait","meaning":"Der Thread wartet darauf, den Handler-Lock auf Tabellenebene (Table Lock) in der SQL-Schicht zu erhalten; tritt bei gleichzeitigem Zugriff auf eine Tabelle in inkompatiblen Modi auf.","causes":"Vorgänge, die einen Table Lock erfordern, Storage Engines mit Sperren auf Tabellenebene wie MyISAM, LOCK TABLES-Anweisungen, Konkurrenz zwischen DDL und DML, lang laufende Abfragen.","action":"InnoDB mit Sperren auf Zeilenebene verwenden, LOCK TABLES seltener nutzen, DDL in Zeiten geringer Aktivität ausführen, lange Abfragen optimieren und Muster prüfen, die den Zugriff auf die Tabelle serialisieren.","tags":["lock","table-lock","sql-layer","contention"]},{"name":"wait/synch/mutex/sql/LOCK_open","kind":"wait","meaning":"Der Thread wartet auf den Mutex LOCK_open, der offene Tabellendefinitionen bzw. das Objekt-Dictionary schützt; dies ist die Serialisierung von Öffnungs-/Schließvorgängen für Tabellen.","causes":"Ständiger Zugriff auf viele verschiedene Tabellen, ein kleiner table_open_cache/table_definition_cache, häufiges Öffnen und Schließen von Tabellen, hohe Parallelität, zu viele Tabellen/Partitionen.","action":"table_open_cache und table_open_cache_instances erhöhen, table_definition_cache tunen, eine übermäßige Anzahl an Tabellen/Partitionen prüfen und unnötig häufiges Öffnen von Tabellen reduzieren.","tags":["mutex","table-cache","sql-layer","concurrency"]},{"name":"row lock waits","kind":"wait","meaning":"Eine Transaktion wartet auf die Freigabe eines InnoDB-Zeilen-Locks, den eine andere Transaktion hält; deutet auf Lock-Konkurrenz auf Zeilenebene hin.","causes":"Gleichzeitige UPDATE/DELETE auf denselben Zeilen, lang laufende Transaktionen ohne Commit, breite Lock-Bereiche (Gap Locks) wegen fehlender Indizes, Konkurrenz um Hot Rows, Deadlock-Zyklen.","action":"Transaktionen kurz halten und zügig committen, den gesperrten Zeilenbereich mit einem passenden Index eingrenzen, konkurrierende Muster umgestalten, Deadlocks überwachen (SHOW ENGINE INNODB STATUS) und den Isolation Level prüfen.","tags":["innodb","row-lock","transaction","contention"]},{"name":"log file waits","kind":"wait","meaning":"Der Thread wartet darauf, dass im InnoDB-Redo-Log Buffer Platz frei wird oder das Redo-Log auf die Platte geschrieben wird; tritt meist bei Druck durch Log-Buffer-Flush/Checkpoint auf.","causes":"Ein kleines Redo-Log (ab 8.0.30 innodb_redo_log_capacity; davor innodb_log_file_size × innodb_log_files_in_group), eine langsame Log-Platte, eine hohe Schreibrate, häufige Flushes (innodb_flush_log_at_trx_commit=1 bei häufigen Commits), ein unzureichender Log Buffer.","action":"Die Redo-Kapazität (ab 8.0.30 innodb_redo_log_capacity, dynamisch; davor innodb_log_file_size, erfordert Neustart) und innodb_log_buffer_size erhöhen, das Redo-Log auf eine schnelle Platte verlegen, das Commit-Muster prüfen und die Flush-Einstellung gegen die Anforderungen an die Dauerhaftigkeit abwägen.","tags":["innodb","redo","log","io"]},{"name":"wait/io/file/innodb/innodb_log_file","kind":"wait","meaning":"Der Thread wartet auf den Abschluss physischer I/O (Schreiben und fsync) auf den InnoDB-Redo-Log-Dateien; er wartet, bis das Redo für die Dauerhaftigkeit des Commits auf der Platte ist.","causes":"Hohe fsync-Latenz des Speichers, sehr häufige Commits mit innodb_flush_log_at_trx_commit=1, eine leistungsschwache Log-Platte, eine hohe Schreiblast.","action":"Die Redo-Log-Platte auf schnellen Speicher mit niedriger Latenz verlegen, Commits nach Möglichkeit bündeln, innodb_flush_log_at_trx_commit gegen die fachlichen Anforderungen abwägen und die Speicherschicht verbessern.","tags":["innodb","redo","fsync","commit"]},{"name":"wait/synch/mutex/innodb/log_writer_mutex","kind":"wait","meaning":"Der Thread wartet auf den Mutex, der die Strukturen des InnoDB-Redo-Log-Writer-Threads schützt. Das Redo-Subsystem wurde in 8.0 neu gestaltet; statt des früheren einzelnen log_sys_mutex gibt es getrennte Instrumente wie log_writer_mutex, log_flusher_mutex und log_checkpointer_mutex.","causes":"Eine sehr hohe Schreib-/Commit-Rate, umfangreiche Redo-Erzeugung, Konkurrenz um die Log-Writer-/Flusher-Threads, eine langsame Log-Platte, ein kleiner Log Buffer.","action":"Schreiblast und Commit-Häufigkeit ausbalancieren, Batch-Vorgänge erwägen, die weniger Redo erzeugen, den Log Buffer tunen und die Verbesserungen an der Skalierbarkeit des Redo-Log in deiner MySQL-Version nutzen.","tags":["innodb","mutex","redo","concurrency"]},{"name":"Innodb_buffer_pool_wait_free","kind":"metric","meaning":"Anzahl der Fälle, in denen ein Thread keine freie Seite im Buffer Pool fand und warten musste, bis Dirty Pages geflusht wurden, um eine freie Seite zu erhalten; ein stetiger Anstieg deutet auf Druck im Buffer Pool hin.","causes":"Ein kleiner Buffer Pool, langsame Flush-/Schreib-I/O, ein niedriger innodb_io_capacity, schnelle Entstehung von Dirty Pages bei hoher Schreiblast, Checkpoint-Rückstand.","action":"innodb_buffer_pool_size erhöhen, innodb_io_capacity/io_capacity_max passend zum Speicher setzen, die Schreib-I/O-Leistung verbessern und den Anteil an Dirty Pages prüfen (innodb_max_dirty_pages_pct).","tags":["metric","innodb","buffer-pool","flush"]},{"name":"Threads_running","kind":"metric","meaning":"Anzahl der Threads, die nicht inaktiv sind und gerade aktiv Abfragen ausführen; zeigt die momentane gleichzeitige Ausführung auf dem Server und die wahrscheinliche Stärke der Konkurrenz.","causes":"Ein plötzlicher Anstieg der Abfragen, sich stauende langsame Abfragen, durch Lock-/Mutex-Konkurrenz blockierte Threads, CPU-Sättigung, ein Thundering-Herd-Effekt.","action":"Bei Spitzen von Threads_running langsame/blockierte Abfragen finden (SHOW PROCESSLIST), Abfragen tunen, die Parallelität mit Connection Pool und Thread Pool begrenzen und die Ursache der Konkurrenz beseitigen.","tags":["metric","concurrency","threads","load"]},{"name":"Innodb_row_lock_time_avg","kind":"metric","meaning":"Die durchschnittliche Zeit (Millisekunden), die mit dem Warten auf einen Zeilen-Lock verbracht wird; zeigt die Schwere der Lock-Konkurrenz auf Zeilenebene.","causes":"Intensiver gleichzeitiger Zugriff auf dieselben Zeilen, lange Transaktionen, breite Lock-Bereiche wegen fehlender Indizes, Waits nahe an Deadlock/Lock Wait Timeout.","action":"Transaktionsdauer verkürzen, den Lock-Bereich mit passenden Indizes eingrenzen, das konkurrierende Zugriffsmuster umgestalten sowie innodb_lock_wait_timeout und den Isolation Level prüfen.","tags":["metric","innodb","row-lock","contention"]},{"name":"history list length","kind":"metric","meaning":"Anzahl der Transaktionen im InnoDB Undo Log, die noch nicht gepurgt wurden (und alte Zeilenversionen halten); zeigt die Ansammlung alter Versionen, die für MVCC aufbewahrt werden.","causes":"Lang laufende offene Transaktionen (vor allem lange Lesetransaktionen), ein langsamer Purge-Thread, eine hohe DML-Rate, nicht committete/inaktive Transaktionen.","action":"Lang laufende und inaktive Transaktionen identifizieren und beenden, die Anzahl der Purge-Threads tunen (innodb_purge_threads) und Transaktionen in der Anwendung kurz halten; eine hohe HLL führt zu Performance- und Bloat-Problemen.","tags":["metric","innodb","undo","mvcc"]},{"name":"Innodb_data_reads","kind":"metric","meaning":"Gesamtzahl der physischen Leseanforderungen, die InnoDB an die Platte gestellt hat; zeigt die Leselast, die der Buffer Pool nicht bedienen konnte und die auf die Platte ging.","causes":"Ein kleiner Buffer Pool, ein kalter Cache, ineffiziente Abfragen und große Scans, fehlende Indizes, ein Working Set, das größer als der Speicher ist.","action":"Den Buffer Pool vergrößern, ineffiziente Abfragen und fehlende Indizes beheben, den Trend der physischen Lesezugriffe zusammen mit der Buffer Pool Hit Ratio überwachen und die I/O-Leistung verbessern.","tags":["metric","innodb","io","read"]},{"name":"Innodb_buffer_pool_read_requests","kind":"metric","meaning":"Gesamtzahl der logischen Leseanforderungen, die aus dem InnoDB Buffer Pool bedient wurden; dient zusammen mit Innodb_buffer_pool_reads (Anzahl der Seiten, die nicht im Buffer Pool gefunden und von der Platte gelesen wurden) zur Berechnung der Buffer Pool Hit Ratio.","causes":"Ein hoher Wert deutet auf intensive Abfrageaktivität hin. Ist das Verhältnis von Innodb_buffer_pool_reads zu diesem Wert hoch (niedrige Hit Ratio), liegt Speichermangel oder ineffizienter Zugriff vor.","action":"Die Buffer Pool Hit Ratio überwachen (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests); ist sie niedrig, den Buffer Pool vergrößern, Abfragen optimieren und ineffiziente Abfragen mit übermäßig vielen logischen Lesezugriffen tunen.","tags":["metric","innodb","buffer-pool","logical-io"]}]},"reference/waits-mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"waits":[{"name":"CXPACKET","kind":"wait","meaning":"Wait, der auftritt, während sich Threads bei paralleler Abfrageausführung synchronisieren; tritt auf, solange die Arbeitsverteilung/Zuordnung zwischen parallelen Planoperatoren aussteht.","causes":"Ein hoher Parallelitätsgrad, ungleichmäßige Arbeitsverteilung (Skew), unbegrenzter/hoher MAXDOP (0) und ein niedriger cost threshold for parallelism (Standard 5), große parallele Scans wegen fehlender Indizes, ineffiziente Pläne.","action":"cost threshold for parallelism und MAXDOP an die Last anpassen, fehlende Indizes und Statistiken beheben und CXPACKET nicht für sich allein als Problem betrachten; den echten Wait mithilfe von CXCONSUMER abgrenzen und ineffiziente parallele Pläne angehen.","tags":["parallelism","cxpacket","query","cpu"]},{"name":"CXCONSUMER","kind":"wait","meaning":"In einem parallelen Plan wartet der Consumer-Thread auf Daten von den Producer-Threads; meist ein harmloser Parallelitäts-Wait.","causes":"Der Consumer-Thread wartet bei normaler paralleler Ausführung, ungleichmäßiger Datenfluss, Verzögerungen auf der Producer-Seite, ineffiziente parallele Pläne.","action":"CXCONSUMER ist meist unbedenklich und erfordert für sich allein keine Maßnahme; um den echten Engpass zu finden, auf die zugrunde liegenden Wait-Typen auf der Producer-Seite (IO, LCK usw.) und die Effizienz des Plans achten.","tags":["parallelism","cxconsumer","query","benign"]},{"name":"PAGEIOLATCH_SH","kind":"wait","meaning":"Der Thread wartet auf einen Latch, der eine Datenseite im Shared-Modus (SH) schützt, während sie von der Platte in den Buffer Pool gelesen wird; ein physischer I/O-Wait für Lesezugriffe.","causes":"Langsame Platten-I/O, unzureichender Speicher (kleiner Buffer Pool), große Scans und fehlende Indizes, ein kalter Cache, hohe physische Leselast.","action":"Speicher (max server memory) und Buffer Pool bewerten, ineffiziente Abfragen und fehlende Indizes beheben, die I/O-Latenz des Speichers verbessern sowie die Abfragen mit den meisten Lesezugriffen identifizieren und tunen.","tags":["io","pageiolatch","read","buffer-pool"]},{"name":"PAGEIOLATCH_EX","kind":"wait","meaning":"Der Thread wartet auf einen Latch, der eine von der Platte gelesene Datenseite im Exclusive-Modus (EX) schützt; meist wird auf die physische I/O einer Seite gewartet, die zur Änderung gelesen wird.","causes":"Langsame Platten-I/O, eine hohe Schreiblast, unzureichender Speicher, Druck durch Checkpoint/Lazy Writer, Vorgänge mit großen Datenänderungen.","action":"Schreib-/Leseleistung des Speichers verbessern, Buffer-Pool-Speicher erhöhen, schreibintensive Vorgänge in Batches und effizient gestalten sowie I/O von tempdb und Datenplatten getrennt messen.","tags":["io","pageiolatch","write","buffer-pool"]},{"name":"PAGELATCH_UP","kind":"wait","meaning":"Der Thread wartet auf einen Latch, der eine bereits im Speicher befindliche Seite im Update-Modus (UP) schützt; dies ist keine physische I/O, sondern Konkurrenz um den gleichzeitigen Zugriff auf eine Seite im Speicher.","causes":"Konkurrenz um Hot Pages im Speicher, vor allem Konkurrenz um tempdb-Systemseiten (PFS/GAM/SGAM), intensives gleichzeitiges Anlegen von tempdb-Objekten, Insert-Konkurrenz auf der letzten Seite (Hot Page).","action":"tempdb auf gleich große Datendateien passend zur Anzahl der Kerne aufteilen, die tempdb-Konkurrenz reduzieren, Hot-Page-Muster verteilen und unnötige tempdb-Nutzung (Spills usw.) beseitigen.","tags":["pagelatch","tempdb","concurrency","contention"]},{"name":"PAGELATCH_EX","kind":"wait","meaning":"Der Thread wartet auf einen Latch, der eine Seite im Speicher im Exclusive-Modus (EX) schützt; tritt meist bei Insert-Konkurrenz auf der letzten Seite von Tabellen mit aufsteigenden (sequenziellen) Schlüsseln auf.","causes":"Insert-Konkurrenz auf der letzten Seite bei Indizes mit sequenziellen/aufsteigenden Schlüsseln, Konkurrenz um tempdb-Systemseiten, intensive gleichzeitige Schreibzugriffe auf Hot Pages, eine hohe Insert-Rate.","action":"Bei Konkurrenz um die letzte Seite OPTIMIZE_FOR_SEQUENTIAL_KEY = ON (SQL Server 2019+) oder eine Schlüsselverteilung (Hash/GUID/Reverse Index) verwenden, tempdb auf mehrere Dateien aufteilen und das intensive Insert-Muster umgestalten.","tags":["pagelatch","last-page-insert","concurrency","contention"]},{"name":"LCK_M_X","kind":"wait","meaning":"Der Thread wartet darauf, einen exklusiven Lock auf einer Ressource (Zeile/Seite/Tabelle) zu erhalten; meist wird auf die Freigabe anderer Locks gewartet, um Daten zu ändern (UPDATE/DELETE/INSERT).","causes":"Lang laufende Transaktionen, gleichzeitige Änderungen an denselben Ressourcen, Lock Escalation, breiter Lock-Bereich wegen fehlender Indizes, blockierende Sessions.","action":"Transaktionen kurz halten und zügig committen, den Lock-Bereich mit passenden Indizes eingrenzen, die blockierende Abfrage finden (sys.dm_exec_requests/Blocking), Lock Escalation und Isolation Level prüfen und bei Bedarf RCSI erwägen.","tags":["lock","exclusive","blocking","transaction"]},{"name":"LCK_M_U","kind":"wait","meaning":"Der Thread wartet darauf, einen Update-Lock auf einer Ressource zu erhalten; während ein UPDATE scannt, eine Zeile liest und deren Änderung vorbereitet, wartet es auf die Freigabe anderer Locks.","causes":"Gleichzeitige Updates derselben Zeilen, lange Transaktionen, viele gescannte und gesperrte Zeilen wegen fehlender Indizes, blockierende Sessions, Lock-Konkurrenz.","action":"Passende Indizes für UPDATE-Anweisungen anlegen, um die Anzahl gescannter/gesperrter Zeilen zu reduzieren, die Transaktionsdauer verkürzen, die Blocking-Kette analysieren sowie Isolation Level und RCSI-Optionen bewerten.","tags":["lock","update-lock","blocking","transaction"]},{"name":"WRITELOG","kind":"wait","meaning":"Der Thread wartet darauf, dass Transaktionslog-Einträge auf die Platte geschrieben werden (Log Flush); meist wird beim Commit gewartet, bis das Log dauerhaft gespeichert ist.","causes":"Langsame I/O der Log-Platte, sehr häufige/kleine Commits, hohe Schreiblast, Konkurrenz zwischen Log- und Datenplatte, Auswirkung synchroner Always On-Replikation.","action":"Das Transaktionslog auf eine schnelle Platte mit niedriger Latenz verlegen, Commits bündeln, unnötig häufige Commits reduzieren, Delayed Durability erwägen (falls geeignet) und die Log-I/O-Latenz messen.","tags":["log","writelog","commit","io"]},{"name":"ASYNC_NETWORK_IO","kind":"wait","meaning":"SQL Server ist bereit, Ergebnisse zu senden, wartet aber auf Netzwerk/Client, weil der Client die Daten nicht schnell genug abnimmt; meist ein Engpass auf Anwendungsseite.","causes":"Die Clientanwendung verarbeitet Ergebnisse langsam (zeilenweise Abarbeitung), große Ergebnismengen, ein langsames Netzwerk, RBAR-Verarbeitung in der Anwendung, unzureichende Clientressourcen.","action":"Ergebnisse in der Anwendung schnell und in großen Mengen abarbeiten, unnötig große Ergebnismengen reduzieren (nur benötigte Spalten/Zeilen) und die Netzwerklatenz prüfen. Meist ein Client-/Netzwerkproblem, kein Problem von SQL Server.","tags":["network","client","application","async"]},{"name":"SOS_SCHEDULER_YIELD","kind":"wait","meaning":"Wait, der auftritt, wenn ein Thread seine CPU-Zeitscheibe (Quantum) aufgebraucht hat und den Scheduler freiwillig abgibt; deutet meist auf CPU-Druck oder CPU-intensive Verarbeitung hin.","causes":"Intensive CPU-gebundene Abfragen, hohe Parallelität und CPU-Sättigung, Spinlock-Konkurrenz, ineffiziente Abfragen mit vielen logischen Lesezugriffen.","action":"CPU-intensive, ineffiziente Abfragen finden und tunen (fehlender Index, schlechter Plan), logische Lesezugriffe reduzieren, CPU-Kapazität und Parallelität bewerten und bei anhaltend hohem Wert den Hardware-/Plan-Engpass untersuchen.","tags":["cpu","scheduler","cpu-bound","yield"]},{"name":"RESOURCE_SEMAPHORE","kind":"wait","meaning":"Die Abfrage wartet auf den Memory Grant, den sie zur Ausführung braucht; die Ausführung kann erst starten, wenn genug Abfragespeicher frei ist.","causes":"Abfragen mit übermäßigem Speicherbedarf (große Sorts/Hashes), falsche (überhöhte) Kardinalitätsschätzungen, unzureichender Serverspeicher, viele gleichzeitige speicherintensive Abfragen, veraltete Statistiken.","action":"Statistiken aktualisieren, um überhöhte Memory-Grant-Schätzungen zu korrigieren, Abfragen mit großen Sorts/Hashes optimieren (Index, Filter), Resource Governor und max server memory prüfen und gleichzeitige speicherintensive Last begrenzen.","tags":["memory","memory-grant","query","resource"]},{"name":"THREADPOOL","kind":"wait","meaning":"Tasks warten auf einen freien Thread aus dem Thread Pool, weil kein Worker Thread für einen neuen Task verfügbar ist; ein kritischer Wait-Typ, der auf eine ernsthafte Erschöpfung der Worker Threads hinweist.","causes":"Durch langes Blocking festgehaltene Threads, sehr viele gleichzeitige Verbindungen, zu niedriger max worker threads, starke Parallelität, Deadlocks/lange Lock-Ketten.","action":"Die Blocking-Kette dringend auflösen, gleichzeitige Verbindungen mit einem Connection Pool steuern, lange gehaltene Locks beseitigen, die Einstellung max worker threads prüfen; über die DAC verbinden, um die Ursache zu finden.","tags":["threadpool","worker-thread","blocking","critical"]},{"name":"HADR_SYNC_COMMIT","kind":"wait","meaning":"Auf einem synchronen Always On-Replikat wartet der Primary darauf, dass der Log-Eintrag auf dem sekundären Replikat gehärtet wird, bevor die Transaktion committet wird; Latenz des synchronen Commits.","causes":"Ein langsames Netzwerk bzw. hohe Latenz zum sekundären Replikat, langsame Log-Platten-I/O auf dem Sekundärreplikat, ein überlastetes Sekundärreplikat, ein geografisch entferntes Replikat, eine hohe Schreibrate.","action":"Netzwerklatenz und Bandbreite zwischen den Replikaten verbessern, die Leistung der Log-Platte auf dem Sekundärreplikat steigern, dessen Last ausbalancieren und bei kritischer Latenz die synchrone/asynchrone Replikat-Topologie prüfen.","tags":["alwayson","replication","sync-commit","hadr"]},{"name":"Page Life Expectancy","kind":"metric","meaning":"Metrik für die durchschnittliche Anzahl Sekunden, die eine Datenseite im Buffer Pool bleibt (ohne aus dem Speicher verdrängt zu werden); ein wichtiger Indikator für Speicherdruck.","causes":"Eine niedrige oder schnell fallende PLE bedeutet Speicherdruck, ineffiziente große Scans, ein Working Set größer als der Speicher oder zu wenig Speicher. NUMA-Knoten sollten auch einzeln bewertet werden.","action":"Statt eines festen Schwellenwerts auf den PLE-Trend und plötzliche Einbrüche achten, den Buffer-Pool-Speicher bewerten (max server memory), ineffiziente speicherintensive Abfragen tunen und die PLE je NUMA-Knoten überwachen.","tags":["metric","memory","buffer-pool","ple"]},{"name":"Buffer-Cache-Trefferquote","kind":"metric","meaning":"Anteil der Datenseitenanforderungen, die ohne Plattenzugriff aus dem Buffer Cache bedient werden; zeigt den Prozentsatz der aus dem Speicher gelesenen Seiten.","causes":"Auf modernen Systemen ist diese Quote meist sehr hoch (dank Read-Ahead), und ein niedriger Wert weist auf erheblichen Speicherdruck hin. Eine hohe Quote allein garantiert keine gute Performance.","action":"Diese Metrik nicht allein, sondern zusammen mit PLE, PAGEIOLATCH-Waits und Trends der physischen Lesezugriffe interpretieren; bei Anzeichen von Speicherdruck den Buffer-Pool-Speicher erhöhen und ineffiziente Abfragen beheben.","tags":["metric","memory","buffer-pool","ratio"]}]},"reference/waits-mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"waits":[{"name":"WiredTiger cache used %","kind":"metric","meaning":"Wie viel des zugewiesenen internen Cache der Storage Engine WiredTiger belegt ist; zeigt den Druck, Daten im Speicher zu halten (der Standard-Zielschwellenwert liegt bei etwa 80%).","causes":"Ein Working Set größer als der Cache, ein zu kleiner Cache, große Scans, intensive Lese-/Schreibzugriffe, viele offene Transaktionen.","action":"Die Größe des WiredTiger-Cache (storage.wiredTiger.engineConfig.cacheSizeGB) und den RAM bewerten, Indizes anlegen, die das Working Set verkleinern, und Eviction-Druck und Latenz überwachen, wenn die Belegung sich dem Ziel von 80% und der Eviction-Schwelle von 95% nähert.","tags":["metric","wiredtiger","cache","memory"]},{"name":"WiredTiger cache dirty %","kind":"metric","meaning":"Prozentsatz der Daten im WiredTiger-Cache, die noch nicht auf die Platte geschrieben wurden (dirty); zeigt Schreibdruck und Checkpoint-/Eviction-Last (die Dirty-Eviction-Schwelle liegt meist bei etwa 20%).","causes":"Hohe Schreiblast, langsame Platten-I/O, große/lang laufende Schreibvorgänge, Checkpoint-Druck, ein Cache, der mit der Schreibrate nicht mithält.","action":"Nähert sich der Dirty-Anteil der Eviction-Schwelle (20%), die Schreibleistung der Platte verbessern, die Schreiblast ausbalancieren, die Cachegröße bewerten sowie lange Schreibvorgänge und das Checkpoint-Intervall prüfen.","tags":["metric","wiredtiger","cache","write"]},{"name":"tickets available (read/write)","kind":"metric","meaning":"Verbleibende Anzahl der Concurrency Tickets, die WiredTiger für gleichzeitige Lese- und Schreibvorgänge vergibt; fällt sie auf null, reihen sich neue Vorgänge für ein Ticket in die Warteschlange ein.","causes":"Langsame Vorgänge, die Tickets lange halten, ein Platten-I/O-Engpass, Locking/Konkurrenz, plötzliche Lastspitzen, durch lang laufende Abfragen erschöpfte Tickets.","action":"Nähern sich die verfügbaren Tickets null und bildet sich eine Warteschlange, langsame Abfragen optimieren (Indizes anlegen), Platten-I/O verbessern, die Last ausbalancieren; erschöpfte Tickets sind meist ein Symptom eines tieferliegenden I/O-/Konkurrenz-Engpasses.","tags":["metric","wiredtiger","concurrency","tickets"]},{"name":"global lock queue","kind":"metric","meaning":"Länge der Warteschlange von Lese- (readers) und Schreibvorgängen (writers), die auf einen globalen Lock bzw. Datenbank- oder Collection-Lock warten; zeigt die Anzahl der Vorgänge, die bei Lock-Konkurrenz warten.","causes":"Lange gehaltene Locks, starke Schreibkonkurrenz, lang laufende Vorgänge, DDL-Vorgänge sowie der kurze exklusive Collection-Lock zu Beginn/am Ende eines Index-Builds (ab 4.2 hybride Builds; Foreground Builds gibt es nicht mehr), wegen langsamer I/O lange gehaltene Locks.","action":"Ist die Warteschlange dauerhaft voll, lang laufende Vorgänge finden und optimieren (currentOp), Index-Builds und andere DDL außerhalb der Spitzenzeiten planen, I/O verbessern und konkurrierende Schreibmuster prüfen.","tags":["metric","lock","queue","concurrency"]},{"name":"replication lag (secondary)","kind":"metric","meaning":"Wie weit (in Sekunden) ein Secondary beim Anwenden des Oplog des Primary zurückliegt; zeigt die Datenverzögerung zwischen den Mitgliedern des Replica Set.","causes":"Langsames Netzwerk, langsame Platten-I/O auf dem Secondary, hohe Leselast auf dem Secondary, eine hohe Schreibrate auf dem Primary, unzureichende Hardware des Secondary, lang laufende Vorgänge.","action":"Lag mit rs.printSecondaryReplicationInfo überwachen, Netzwerk- und I/O-Leistung des Secondary verbessern, dessen Last ausbalancieren, die Oplog-Größe ausreichend halten und die Anforderung an den Write Concern prüfen.","tags":["metric","replication","lag","secondary"]},{"name":"oplog window","kind":"metric","meaning":"Zeitspanne (Stunden) zwischen dem ältesten und dem neuesten Vorgang im Oplog; zeigt, wie weit ein Secondary zurückfallen kann, ohne die Synchronisation zu verlieren.","causes":"Eine kleine Oplog-Größe, eine plötzlich hohe Schreibrate, lange offline befindliche Secondaries, gestiegene Replikationslast.","action":"Ist das Oplog-Fenster geschrumpft, die Oplog-Größe erhöhen, den Lag der Secondaries unter dem Fenster halten sowie Schreiblast und Anforderungen an Backup/Synchronisation prüfen; wird das Fenster verpasst, ist ein vollständiger Resync nötig.","tags":["metric","replication","oplog","window"]},{"name":"page faults","kind":"metric","meaning":"Anzahl der Page Faults (vor allem Hard Faults), bei denen das Betriebssystem nicht im Speicher befindliche Datenseiten von der Platte holen muss; zeigt, dass Daten aus dem Speicher auf die Platte ausweichen.","causes":"Ein Working Set größer als der RAM, unzureichender Speicher, große Scans, Cache-Druck, Speicherverbrauch durch andere Prozesse.","action":"Serverspeicher/Working Set bewerten, gescannte Datenmengen durch Indizes reduzieren, WiredTiger-Cache und Speicher des Betriebssystems ausbalancieren und einen Anstieg der Hard Page Faults zusammen mit der I/O-Latenz überwachen.","tags":["metric","memory","page-fault","io"]},{"name":"connections current","kind":"metric","meaning":"Anzahl der aktuell zum Server geöffneten Client-Verbindungen; zeigt die Nutzung des Connection Pool und den Abstand zum Maximum.","causes":"Falsch konfigurierte Connection Pools, Connection Leaks, ein plötzlicher Anstieg der Clients, lange gehaltene Verbindungen, übermäßig viele Verbindungen aus Microservices.","action":"Die Anzahl der Verbindungen mit dem Serverlimit (maxIncomingConnections) vergleichen, den Connection Pool auf Anwendungsseite richtig dimensionieren, Connection Leaks beheben und plötzliche Spitzen überwachen.","tags":["metric","connections","pool","capacity"]},{"name":"scanAndOrder / COLLSCAN ratio","kind":"metric","meaning":"Anteil der Abfragen, die eine Collection ohne Index vollständig scannen (COLLSCAN) oder im Speicher sortieren (scanAndOrder); ein Indikator für ineffiziente Indizierung.","causes":"Fehlende oder ungeeignete Indizes, Sortierschlüssel, die nicht zur Abfrage passen, breite ungefilterte Abfragen, schlechte Abfragemuster.","action":"Langsame Abfragen und ihre Pläne prüfen (explain), Compound Indexes passend zu Abfrage- und Sortiermustern anlegen, Abfragen mit COLLSCAN und scanAndOrder beheben und das Verhältnis untersuchter zu zurückgegebenen Dokumenten überwachen.","tags":["metric","index","collscan","query"]},{"name":"queued operations","kind":"metric","meaning":"Anzahl der Lese- und Schreibvorgänge in der Warteschlange (noch nicht aktiv ausführbar), weil sie auf einen Lock oder eine Ressource warten; zeigt, dass das System die Nachfrage nicht bewältigt.","causes":"Lock-Konkurrenz, erschöpfte Tickets, ein I/O-Engpass, lang laufende Abfragen, plötzliche Lastspitzen, langsame Platten.","action":"Wächst die Warteschlange, aktive und lang laufende Vorgänge untersuchen und optimieren (currentOp), Indizes und I/O verbessern, die Last ausbalancieren und die Ursache zusammen mit Metriken zu Tickets und Lock-Konkurrenz finden.","tags":["metric","queue","concurrency","load"]}]},"reference/waits-redis.json":{"engineId":"redis","engine":"Redis","order":6,"waits":[{"name":"keyspace hit ratio","kind":"metric","meaning":"Anteil der Key-Lookups, die im Speicher gefunden werden, berechnet aus keyspace_hits und keyspace_misses; zeigt, wie effektiv Redis als Cache arbeitet.","causes":"Eine niedrige Quote weist auf Eviction wegen zu wenig Speicher, kurze TTLs, einen kalten Cache, viele Lookups eindeutiger Keys, die nie gecacht wurden, oder ein schlechtes Key-Design hin.","action":"Die Hit Ratio als hits/(hits+misses) überwachen; ist sie niedrig, Speicher/Eviction Policy und TTLs prüfen, häufig genutzte Daten vorab laden sowie Key-Design und Caching-Strategie verbessern.","tags":["metric","cache","hit-ratio","keyspace"]},{"name":"evicted_keys","kind":"metric","meaning":"Anzahl der Keys, die bei Erreichen des maxmemory-Limits durch die Eviction Policy aus dem Speicher entfernt (gelöscht) wurden; ein direkter Indikator für Speicherdruck.","causes":"Überschreitung des maxmemory-Limits, unzureichender Speicher, Ansammlung von Keys ohne TTL, ein Datenvolumen größer als der Speicher, eine ungeeignete Eviction Policy.","action":"Bei stetig steigender Eviction Speicher hinzufügen oder das Datenvolumen reduzieren, maxmemory-policy passend zur Last wählen (z. B. allkeys-lru/volatile-lru) sowie TTL-Nutzung und Big Keys prüfen.","tags":["metric","memory","eviction","maxmemory"]},{"name":"blocked_clients","kind":"metric","meaning":"Anzahl der Clients, die mit blockierenden Befehlen wie BLPOP, BRPOP, WAIT und XREAD BLOCK auf ein Ereignis/Daten warten; bei Queue-Consumer-Mustern normal, ein ungewöhnlicher Anstieg ist jedoch ein Warnsignal.","causes":"Consumer mit blockierenden List-/Stream-Befehlen, ausbleibende Daten auf Producer-Seite, Warten auf Replica-Bestätigung mit WAIT, Ungleichgewicht zwischen Consumer und Producer.","action":"Die Anzahl blockierter Clients mit dem erwarteten Consumer-Modell vergleichen; bei ungewöhnlichem Anstieg die Producer-Seite und Replikationsbestätigungen untersuchen und die Verbrauchsrate der Queue ausbalancieren.","tags":["metric","clients","blocking","queue"]},{"name":"mem_fragmentation_ratio","kind":"metric","meaning":"Verhältnis des vom Betriebssystem an Redis zugewiesenen physischen Speichers (RSS) zum tatsächlich von Redis genutzten Speicher; zeigt Speicherfragmentierung oder Swapping.","causes":"Werte deutlich über 1 weisen auf Speicherfragmentierung hin, Werte unter 1 darauf, dass Redis ausgelagert (auf die Platte geswappt) wurde. Keys variabler Größe und Lösch-/Einfügezyklen erhöhen die Fragmentierung.","action":"Bei hoher Fragmentierung activedefrag aktivieren oder einen Neustart erwägen; liegt der Wert unter 1, Swapping untersuchen und Speicher hinzufügen; Verhalten des Allocators (jemalloc) und Big-Key-Muster prüfen.","tags":["metric","memory","fragmentation","swap"]},{"name":"instantaneous_ops_per_sec","kind":"metric","meaning":"Anzahl der Befehle, die Redis gerade pro Sekunde verarbeitet (momentaner Durchsatz); zeigt die Lastintensität und plötzliche Verkehrsänderungen.","causes":"Plötzliche Verkehrsspitzen, Last durch Batch-Jobs, ineffiziente Zugriffsmuster der Clients, Einzelbefehle statt Pipelining, intensive Lese-/Schreibschübe.","action":"Den Durchsatz zusammen mit Latenz und CPU überwachen, plötzliche Einbrüche/Spitzen mit Verkehr und langsamen Befehlen korrelieren, die Effizienz durch Pipelining/Batching verbessern und Kapazität planen.","tags":["metric","throughput","ops","load"]},{"name":"rdb_last_bgsave_status","kind":"metric","meaning":"Erfolgsstatus der letzten RDB-Hintergrundsicherung (BGSAVE); zeigt, ob der Persistenz-Snapshot erfolgreich erstellt wurde.","causes":"Ein fehlgeschlagener Status (err) kann durch eine volle Platte/zu wenig Platz, zu wenig Speicher für den Fork, einen Platten-I/O-Fehler, Berechtigungsprobleme oder übermäßige Last verursacht werden.","action":"Lautet der Status 'err', Plattenplatz, den für den Fork nötigen Speicher (overcommit-Einstellung) und den Zustand der Platte prüfen und die Logs ansehen; ein fehlgeschlagener BGSAVE birgt das Risiko von Datenverlust, daher die Persistenzkonfiguration prüfen.","tags":["metric","persistence","rdb","bgsave"]},{"name":"master_link_status","kind":"metric","meaning":"Status (up/down) der Replikationsverbindung eines Replikats zum Master; zeigt, ob die Verbindung zwischen Replikat und Master intakt ist.","causes":"Ein Status 'down' kann aus einem Netzwerkausfall, einem nicht erreichbaren Master, einem Fehler bei der Replikationssynchronisation, einem Authentifizierungsproblem oder einer Überlastung des Masters resultieren.","action":"Lautet der Status 'down', Netzwerkverbindung, Erreichbarkeit des Masters und Auth-Einstellungen prüfen, die Logs des Replikats ansehen, den Lag von master_repl_offset überwachen und prüfen, ob die Synchronisation abgeschlossen ist.","tags":["metric","replication","master-link","availability"]},{"name":"latency (slowlog)","kind":"metric","meaning":"Latenz langsamer Befehle oberhalb eines festgelegten Schwellenwerts, erfasst über slowlog und Latency Monitoring; im Single-Threaded-Redis zeigt sie das Risiko, dass ein Befehl die anderen blockiert.","causes":"Befehle mit hoher Zeitkomplexität (KEYS, großes SORT, SMEMBERS/HGETALL auf großen Collections), Big Keys, langsame Lua-Skripte, Pausen durch Fork/Persistenz, Swapping.","action":"Langsame Befehle mit den Befehlen SLOWLOG und LATENCY finden, O(N)-Befehle (SCAN statt KEYS) und Big Keys beseitigen, Skripte optimieren und durch Fork/AOF/RDB verursachte Pausen überwachen.","tags":["metric","latency","slowlog","performance"]},{"name":"expired_keys","kind":"metric","meaning":"Gesamtzahl der Keys, die wegen abgelaufener TTL gelöscht wurden; ein Indikator für TTL-basiertes Ablaufverhalten und die Rückgewinnung von Speicher.","causes":"Dass Keys mit TTL ablaufen, ist normal. Plötzliche Änderungen können auf eine geänderte TTL-Policy, massenhaftes Ablaufen von Keys oder unerwartete Löschmuster hinweisen.","action":"expired von evicted unterscheiden (expired ist gesundes TTL-Verhalten, evicted ist Speicherdruck), die Auswirkung plötzlicher Ablaufwellen auf Cache Misses und Last überwachen und die TTL-Verteilung streuen.","tags":["metric","ttl","expiration","keyspace"]},{"name":"connected_clients vs maxclients","kind":"metric","meaning":"Wie nah die aktuelle Anzahl verbundener Clients am konfigurierten maxclients-Limit liegt; zeigt das Risiko, die Verbindungskapazität zu erschöpfen.","causes":"Connection Leaks, ein falsch konfigurierter Connection Pool, ein plötzlicher Anstieg der Clients, lange gehaltene inaktive Verbindungen, viele Verbindungen von Microservices.","action":"Verbundene Clients mit maxclients vergleichen; bei Annäherung an das Limit den Connection Pool richtig dimensionieren, Connection Leaks beheben, ein Timeout für inaktive Verbindungen setzen und maxclients bei Bedarf erhöhen.","tags":["metric","connections","maxclients","capacity"]}]},"reference/waits-elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"waits":[{"name":"JVM heap used %","kind":"metric","meaning":"Prozentsatz des belegten JVM-Heap eines Knotens; ein hoher, dauerhaft voller Heap bedeutet Speicherdruck und das Risiko intensiver GC.","causes":"Übermäßiger Speicher für Fielddata/Aggregationen, zu viele Shards, hohe Bulk-/Abfragelast, große Mappings, ein zu kleiner Heap, Muster von Memory Leaks.","action":"Die Heap-Nutzung überwachen (vor allem eingreifen, wenn sie nach GC über 75% bleibt), die Shard-Anzahl reduzieren, schwere Aggregationen und Fielddata-Nutzung begrenzen und den Heap bei höchstens der Hälfte des RAM und unter 32GB halten.","tags":["metric","jvm","heap","memory"]},{"name":"GC duration/frequency","kind":"metric","meaning":"Dauer und Häufigkeit der JVM Garbage Collection; lange/häufige GC-Pausen machen den Knoten unresponsiv und verschlechtern die Performance.","causes":"Hoher Heap-Druck, große Ansammlung in der Old Generation (Old Gen), schwere Aggregationen/Fielddata, ein zu kleiner Heap, zu viele Shards, lang laufende große Abfragen.","action":"Den Heap-Druck als Ursache langer Pausen (vor allem Old GC) beseitigen, speicherhungrige Abfragen optimieren, die Shard-Anzahl reduzieren; häufige und lange GC sind eine Frühwarnung vor instabilen Knoten.","tags":["metric","jvm","gc","pause"]},{"name":"thread_pool write rejected","kind":"metric","meaning":"Anzahl der abgelehnten Indexierungsanfragen, weil die Warteschlange des Write-Thread-Pools (Indexing/Bulk) voll war; zeigt, dass der Knoten die Schreiblast nicht bewältigt.","causes":"Übermäßige Bulk-/Indexierungsrate, langsame Platten-I/O, viele kleine Anfragen statt Bulk, unzureichende Knotenkapazität, Refresh-Druck.","action":"Indexierungsanfragen als passend dimensionierte Bulks senden, die Schreibrate an die Knotenkapazität anpassen (Backoff), Platten-I/O verbessern, refresh_interval erhöhen, Kapazität skalieren.","tags":["metric","thread-pool","indexing","rejection"]},{"name":"thread_pool search rejected","kind":"metric","meaning":"Anzahl der abgelehnten Suchanfragen, weil die Warteschlange des Search-Thread-Pools voll war; zeigt, dass der Knoten die Abfragelast nicht bewältigt.","causes":"Übermäßig viele gleichzeitige Abfragen, schwere/langsame Aggregationen, Zugriff auf zu viele Shards, ineffiziente Abfragen, unzureichende Knoten-/CPU-Kapazität.","action":"Abfrageparallelität auf Clientseite begrenzen, schwere Abfragen und Aggregationen optimieren, unnötigen Shard-Fan-out reduzieren (Routing/Filterung), Knoten hinzufügen und die Anzahl der Replica Shards erhöhen (Suchen werden auch von Replica Shards bedient), Kapazität skalieren.","tags":["metric","thread-pool","search","rejection"]},{"name":"indexing/search latency","kind":"metric","meaning":"Durchschnittliche Latenz von Indexierungs- und Suchvorgängen; abgeleitet aus den Metriken index_time_in_millis und query_time_in_millis, spiegelt sie die für Nutzer sichtbare Antwortzeit wider.","causes":"Langsame Platten-I/O, schwere Abfragen/Aggregationen, GC-Pausen, eine hohe Shard-Anzahl, Refresh-/Merge-Druck, unzureichende Ressourcen.","action":"Langsame Abfragen mit dem Slow Log finden und optimieren, Bulk und refresh_interval für die Indexierung tunen, Platten- und Heap-Leistung verbessern und die Latenz zusammen mit Thread-Pool-Ablehnungen und GC interpretieren.","tags":["metric","latency","indexing","search"]},{"name":"refresh/flush time","kind":"metric","meaning":"Zeit für Refresh, der neue Dokumente durchsuchbar macht, und Flush, der das Translog in Lucene persistiert; zeigt den Indexierungs-Overhead.","causes":"Zu häufige Refreshes (niedriges refresh_interval), langsame Platten-I/O, intensive Indexierung, ein großes Translog, viele kleine Segmente.","action":"Bei intensiver Indexierung refresh_interval erhöhen (z. B. 30s) oder vorübergehend deaktivieren, Platten-I/O verbessern, Bulk-Größe tunen, das Segment-Merge-Verhalten prüfen.","tags":["metric","refresh","flush","indexing"]},{"name":"unassigned shards","kind":"metric","meaning":"Anzahl der Shards, die keinem Knoten zugewiesen sind; ein nicht zugewiesener Primary bedeutet, dass Daten nicht erreichbar sind (Status red), ein nicht zugewiesenes Replica bedeutet ein Risiko für Dauerhaftigkeit/Kapazität.","causes":"Ausfall/Weggang eines Knotens, überschrittenes Disk Watermark, eine für die Knotenanzahl zu hohe Replica-Einstellung, Allocation-Regeln/-Filter, Verzögerungen bei der Shard Recovery.","action":"Den Grund mit der Cluster Allocation Explain API finden, ausgefallene Knoten wiederherstellen, Probleme mit Plattenplatz/Watermark beheben, Replica-Anzahl und Allocation-Regeln prüfen; Status red priorisieren.","tags":["metric","shards","allocation","cluster-health"]},{"name":"disk watermark %","kind":"metric","meaning":"Plattennutzung des Knotens relativ zu den Schwellen low/high/flood-stage (Standard 85%/90%/95%), die die Shard-Platzierung in Elasticsearch beeinflussen.","causes":"Volllaufende Platte, Datenwachstum, unzureichender Speicher, unausgewogene Shard-Verteilung, nicht bereinigte alte Indizes.","action":"Ab dem Low Watermark werden dem Knoten keine neuen Shards zugewiesen; ab dem High Watermark werden Shards auf andere Knoten verlagert; ab flood-stage werden Indizes schreibgeschützt. Plattenplatz freigeben (alte Indizes löschen/ILM nutzen), Speicher vergrößern, Shard-Verteilung ausbalancieren und die Watermark-Schwellen überwachen.","tags":["metric","disk","watermark","storage"]},{"name":"fielddata/query cache evictions","kind":"metric","meaning":"Anzahl der Einträge, die wegen Speicherlimits aus dem Fielddata Cache und dem Query (Filter) Cache verdrängt wurden; deutet auf ineffizientes Caching und Speicherdruck hin.","causes":"Hohe Fielddata-Nutzung (Sortierung/Aggregationen auf Textfeldern), kleine Cache-Limits, stark variierende Abfragefilter, Speicherdruck, nie wiederverwendete Filter.","action":"Auf Textfeldern keyword/doc_values statt Fielddata verwenden, schwere Fielddata-Aggregationen begrenzen, Cachegrößen prüfen; häufige Evictions zusammen mit dem Heap-Druck bewerten.","tags":["metric","cache","fielddata","eviction"]},{"name":"circuit breaker tripped","kind":"metric","meaning":"Anzahl der Fälle, in denen Circuit Breaker (parent/fielddata/request breaker) vorhergesagt haben, dass eine Anfrage den Speicher überschreiten würde, und sie abgelehnt haben; ein Schutzmechanismus gegen OOM des Knotens.","causes":"Aggregationen/Abfragen mit übermäßigem Speicherbedarf, hohe Fielddata-Nutzung, große Bulk-Anfragen, allgemeiner Heap-Druck, sehr große Ergebnismengen.","action":"Ausgelöste Circuit Breaker als Zeichen von Heap-Druck werten, speicherhungrige Abfragen optimieren, Fielddata-/Aggregationsnutzung reduzieren, die Bulk-Größe senken sowie Heap und Shard-Anzahl prüfen.","tags":["metric","circuit-breaker","memory","oom"]},{"name":"pending tasks","kind":"metric","meaning":"Warteschlange der Aufgaben zur Aktualisierung des Cluster State, die vom Master-Knoten verarbeitet werden müssen; zeigt, dass der Master mit der Last der Clusterverwaltung nicht mithält.","causes":"Eine große Anzahl von Indizes/Shards, häufige Änderungen an Mappings/Settings, unzureichende Ressourcen des Master-Knotens, ein großer Cluster State, Probleme mit Netzwerk/Master-Wahl.","action":"Ist die Warteschlange dauerhaft voll, die Anzahl der Shards/Indizes reduzieren, häufige dynamische Mapping-Updates begrenzen, separate dedizierte Master-Knoten verwenden sowie Größe des Cluster State und Ressourcen des Masters prüfen.","tags":["metric","master","cluster-state","queue"]}]},"reference/alerts-oracle.json":{"engineId":"oracle","engine":"Oracle Database","order":1,"alerts":[{"metric":"Buffer-Cache-Trefferquote","warn":"< 95%","crit":"< 90%","tool":"V$SYSSTAT / V$BUFFER_POOL_STATISTICS","note":"Die Buffer Cache Hit Ratio zeigt den Prozentsatz der Datenblöcke, die ohne Plattenzugriff aus dem Speicher gelesen werden. Niedrige Werte deuten auf zunehmende physische Lesezugriffe und eine zu kleine SGA hin. Für sich allein kann sie irreführend sein; immer zusammen mit den physischen Lesezugriffen im AWR-Bericht bewerten.","tags":["memory","cache","sga"]},{"metric":"Tablespace-Belegung (%used)","warn":"> 85%","crit":"> 95%","tool":"DBA_TABLESPACE_USAGE_METRICS","note":"Erreicht die Tablespace-Belegung 100%, können keine neuen Extents zugewiesen werden und Vorgänge brechen mit Fehlern wie ORA-01653 ab. Auch bei aktiviertem Autoextend das Plattenlimit im Blick behalten. Die View DBA_TABLESPACE_USAGE_METRICS liefert den korrekten Prozentsatz sowohl für autoextensible als auch für Dateien fester Größe.","tags":["disk","tablespace","capacity"]},{"metric":"PGA-Speichernutzung","warn":"> 90% pga_aggregate_target","crit":"> 100% (over-alloc)","tool":"V$PGASTAT / V$PGA_TARGET_ADVICE","note":"Wird das PGA-Ziel überschritten, weichen Sort- und Hash-Vorgänge auf die Platte (Temp) aus und Abfragen werden langsamer. Ein stetig steigender over allocation count zeigt, dass das Ziel nicht ausreicht. Die Einstellung pga_aggregate_target im Verhältnis zum Serverspeicher prüfen.","tags":["memory","pga","temp"]},{"metric":"Belegung des TEMP-Tablespace","warn":"> 80%","crit":"> 95%","tool":"V$TEMPSEG_USAGE / V$SORT_SEGMENT / DBA_TEMP_FREE_SPACE","note":"TEMP-Speicher wird für große Sorts, Hash Joins und Vorgänge mit globalen temporären Tabellen genutzt. Läuft er voll, wird ORA-01652 ausgelöst und Abfragen schlagen fehl. Plötzliches Volllaufen geht meist auf eine einzelne Abfrage mit schlechtem Plan oder mit zu großer Sortiermenge zurück.","tags":["temp","disk","sort"]},{"metric":"Average Active Sessions (AAS)","warn":"> Anzahl CPU-Kerne","crit":"> 2x Anzahl CPU-Kerne","tool":"V$ACTIVE_SESSION_HISTORY / OEM","note":"Average Active Sessions fasst die aktuelle Last der Datenbank in einer Zahl zusammen. Übersteigt der Wert die Anzahl der CPU-Kerne, stehen Sessions für CPU oder Wait Events an. Bleibt er bei einem Vielfachen der Kernanzahl, ist eine Engpassanalyse nötig.","tags":["cpu","load","session"]},{"metric":"Data Guard Apply Lag","warn":"> 60 s","crit":"> 300 s","tool":"V$DATAGUARD_STATS (apply lag)","note":"Apply Lag gibt an, wie weit die Standby-Datenbank hinter dem Primary liegt. Hoher Apply Lag erhöht die beim Failover anzuwendende Redo-Menge und verlängert damit die Wiederherstellungszeit (RTO); ist das Redo bereits auf dem Standby angekommen, gehen keine Daten verloren. Der mögliche Datenverlust (RPO) wird durch den Transport Lag bestimmt. Zusammen mit dem Transport Lag prüfen, um festzustellen, ob das Netzwerk oder das Redo Apply langsam ist.","tags":["replication","dataguard","lag"]},{"metric":"Belegung der Fast Recovery Area (FRA)","warn":"> 85%","crit":"> 95%","tool":"V$RECOVERY_FILE_DEST / V$RECOVERY_AREA_USAGE","note":"Läuft die FRA voll, stoppt die Erzeugung von Archivlogs und die gesamte Datenbank hängt. Dies ist eine der häufigsten Ursachen für Ausfälle in Produktivsystemen. RMAN-Löschrichtlinien und den Wert von db_recovery_file_dest_size regelmäßig prüfen.","tags":["disk","fra","archivelog"]},{"metric":"Anzahl blockierender Sessions","warn":">= 1 (> 30 s)","crit":">= 1 (> 120 s)","tool":"V$LOCK / V$SESSION (blocking_session)","note":"Lässt ein von einer Session gehaltener Lock andere Sessions warten, entsteht eine kaskadierende Verlangsamung in der gesamten Anwendung. Lang anhaltendes Blocking geht meist auf nicht committete Transaktionen oder Anwendungsfehler zurück. Die Root Session der Blocking-Kette finden und bei Bedarf beenden.","tags":["lock","blocking","concurrency"]},{"metric":"Anzahl ungültiger Objekte","warn":"> 0","crit":"> 50","tool":"DBA_OBJECTS (status='INVALID')","note":"Ungültige Packages, Views oder Trigger können zur Laufzeit unerwartete Fehler verursachen. Sie treten meist nach einer DDL-Änderung auf. Durch Neukompilieren mit UTL_RECOMP.RECOMP_PARALLEL oder utlrp.sql beheben und die Ursache dauerhaft ungültiger Objekte untersuchen.","tags":["object","compile","maintenance"]},{"metric":"Am längsten laufende Abfrage","warn":"> 5 min","crit":"> 30 min","tool":"V$SQL / V$SESSION_LONGOPS","note":"Eine einzelne lang laufende Abfrage kann Ressourcen monopolisieren und auch andere Vorgänge verlangsamen. Abgesehen von Batch-Jobs gehen minutenlang laufende Abfragen in OLTP-Systemen meist auf einen fehlenden Index oder einen schlechten Plan zurück. Plan und tatsächliche Zeilenzahlen mit einem SQL Monitor-Bericht untersuchen.","tags":["query","performance","sql"]},{"metric":"ORA-Fehler im Alert Log","warn":">= 1 (nicht kritisch)","crit":"ORA-00600 / ORA-07445 / ORA-04031","tool":"alert log / V$DIAG_ALERT_EXT","note":"Das Alert Log ist das Gesundheitsprotokoll der Datenbank. ORA-00600 und ORA-07445 sind interne Fehler, die auf Datenkorruption hindeuten können, während ORA-04031 auf erschöpften Shared-Pool-Speicher hinweist. Treten diese Fehler auf, sofort untersuchen und bei Bedarf an Oracle Support weitergeben.","tags":["error","alertlog","monitoring"]},{"metric":"Häufigkeit der Redo-Log-Switches","warn":"> 6 / Stunde","crit":"> 12 / Stunde","tool":"V$LOG_HISTORY","note":"Sehr häufige Redo-Log-Switches deuten auf zu kleine Logdateien und Checkpoint-Druck hin. Idealerweise wird in Spitzenzeiten alle 15-20 Minuten ein Switch erwartet. Übermäßig häufige Switches erhöhen die IO- und Archivierungslast; eine Vergrößerung der Redo-Log-Gruppen erwägen.","tags":["redo","io","checkpoint"]},{"metric":"Alter des letzten erfolgreichen RMAN-Backups","warn":"> 24 Stunden","crit":"> 48 Stunden","tool":"V$RMAN_BACKUP_JOB_DETAILS","note":"Das Backup-Alter bestimmt den jüngsten Zeitpunkt, bis zu dem du wiederherstellen kannst, und spiegelt direkt das Risiko von Datenverlust wider. Bleiben fehlgeschlagene oder übersprungene Backups unbemerkt, gibt es im Katastrophenfall keinen Weg zurück. Auch Archivlog-Backups separat überwachen, um die Fähigkeit zur Point-in-Time-Recovery sicherzustellen.","tags":["backup","rman","recovery"]},{"metric":"Platten-IO-Latenz (db file sequential read)","warn":"> 10 ms","crit":"> 20 ms","tool":"V$SYSTEM_EVENT / AWR","note":"Die durchschnittliche Wartezeit von Single-Block-Lesezugriffen zeigt die Leistung der Speicherschicht. Auf modernen Flash-/SSD-Systemen sollte dieser Wert wenige Millisekunden betragen. Dauerhaft hohe Latenz deutet auf einen Speicherengpass oder übermäßige physische Lesezugriffe hin; SGA und Speicherschicht gemeinsam bewerten.","tags":["io","disk","latency"]}]},"reference/alerts-postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"alerts":[{"metric":"Cache Hit Ratio","warn":"< 99%","crit":"< 95%","tool":"pg_stat_database / postgres_exporter","note":"Die Cache Hit Ratio ist der Prozentsatz der Blöcke, die allein aus shared_buffers bedient werden; vom Cache des Betriebssystems bediente Lesezugriffe zählen ebenfalls als blks_read, sodass die Quote die tatsächlichen Plattenzugriffe schlechter aussehen lassen kann, als sie sind. In gesunden PostgreSQL-OLTP-Systemen sollte die Quote über 99% liegen. Niedrige Werte deuten auf zu wenig Speicher oder einen kalten Cache hin; berechnet wird sie aus blks_hit und blks_read.","tags":["memory","cache"]},{"metric":"Anzahl der Verbindungen (connections vs max_connections)","warn":"> 80%","crit":"> 90%","tool":"pg_stat_activity / max_connections","note":"Nähern sich die aktiven Verbindungen dem Limit max_connections, schlagen neue Verbindungen fehl. Da jede Verbindung Speicher verbraucht, erzeugt eine hohe Anzahl auch Speicherdruck. Empfohlen wird, die Anzahl der Backends mit einem Connection Pooler wie PgBouncer niedrig zu halten.","tags":["connection","limit","pool"]},{"metric":"Replication Lag","warn":"> 10 MB","crit":"> 100 MB","tool":"pg_stat_replication (replay_lag / LSN-Differenz)","note":"Replication Lag ist die WAL-Menge in Bytes, um die der Standby hinter dem Primary liegt. Hoher Lag bedeutet veraltete Daten auf Lesereplikaten und ein Risiko von Datenverlust beim Failover. Die LSN-Differenz in Bytes zusammen mit der Dauer replay_lag überwachen.","tags":["replication","lag","standby"]},{"metric":"Alter der ältesten Transaktion","warn":"> 5 min","crit":"> 30 min","tool":"pg_stat_activity (xact_start)","note":"Lang laufende offene Transaktionen hindern VACUUM daran, Dead Tuples zu entfernen, und führen zu Table Bloat. Sessions im Zustand 'idle in transaction' halten zudem unnötig Locks. Solche Sessions identifizieren und mit idle_in_transaction_session_timeout begrenzen.","tags":["query","vacuum","transaction"]},{"metric":"Deadlocks","warn":"> 1 / min","crit":"> 5 / min","tool":"pg_stat_database (deadlocks)","note":"Ein Deadlock entsteht, wenn zwei Transaktionen gegenseitig auf ihre Locks warten; PostgreSQL löst ihn durch Abbruch einer der beiden auf. Gelegentliche Deadlocks kommen vor, häufige deuten jedoch auf eine inkonsistente Lock-Reihenfolge in der Anwendung hin. log_lock_waits aktivieren, um zu sehen, welche Abfragen kollidieren.","tags":["lock","deadlock","concurrency"]},{"metric":"Nutzung temporärer Dateien","warn":"> 1 GB / Stunde","crit":"> 10 GB / Stunde","tool":"pg_stat_database (temp_bytes)","note":"Temporäre Dateien entstehen, wenn Sort- und Hash-Vorgänge, die nicht in work_mem passen, auf die Platte ausweichen. Hohe Temp-Nutzung verursacht sowohl Langsamkeit als auch Plattendruck. Für häufig auslagernde Abfragen eine Erhöhung von work_mem oder eine Optimierung der Abfrage erwägen.","tags":["temp","disk","work_mem"]},{"metric":"Autovacuum-Rückstand (Dead Tuples)","warn":"> 10% Dead Tuples","crit":"> 20% Dead Tuples","tool":"pg_stat_user_tables (n_dead_tup)","note":"Ein steigender Anteil an Dead Tuples zeigt, dass Autovacuum nicht mithält und der Table Bloat wächst. Aufgeblähte Tabellen belegen Platz und verlangsamen Scans. Auf stark genutzten Tabellen autovacuum_vacuum_scale_factor senken, damit die Bereinigung häufiger läuft.","tags":["vacuum","bloat","maintenance"]},{"metric":"Plattennutzung (Datenverzeichnis)","warn":"> 85%","crit":"> 95%","tool":"df / node_exporter","note":"PostgreSQL bietet keinen Selbstschutzmechanismus, bevor die Platte voll ist. Läuft die Datenplatte voll, schlagen Schreibvorgänge mit 'could not extend file' / 53100 fehl; läuft die WAL-Platte (pg_wal) voll, fährt der Server mit PANIC herunter. WAL und temporäre Dateien können plötzliches Wachstum verursachen; frühes Eingreifen ist entscheidend.","tags":["disk","capacity"]},{"metric":"WAL-Aufbewahrung (Replication Slot Lag)","warn":"> 1 GB","crit":"> 10 GB","tool":"pg_replication_slots (restart_lsn)","note":"Ein ungenutzter oder zurückliegender Replication Slot verhindert das Entfernen von WAL-Dateien und bläht das Verzeichnis pg_wal auf. Das kann mit einer vollen Platte enden. Tote Slots identifizieren und löschen und mit max_slot_wal_keep_size eine Obergrenze setzen.","tags":["wal","slot","disk"]},{"metric":"Checkpoint-Häufigkeit (angeforderte Checkpoints)","warn":"> 10% Anteil angefordert","crit":"> 30% Anteil angefordert","tool":"PG16 und früher: pg_stat_bgwriter (checkpoints_req) / PG17+: pg_stat_checkpointer (num_requested)","note":"Steigt der Anteil angeforderter Checkpoints gegenüber zeitgesteuerten, ist max_wal_size zu klein. Häufige Checkpoints verursachen IO-Spitzen und Performance-Einbußen. Die Einstellungen max_wal_size und checkpoint_completion_target prüfen, um die Last zu verteilen.","tags":["checkpoint","wal","io"]},{"metric":"Alter des letzten erfolgreichen Backups","warn":"> 24 Stunden","crit":"> 48 Stunden","tool":"pgBackRest / barman info","note":"Das Backup-Alter bestimmt den jüngsten Punkt, zu dem du zurückkehren kannst, und drückt direkt dein RPO aus. Für eine Point-in-Time-Recovery sind eine lückenlose Backup-Kette und ein WAL-Archiv nötig. Backups müssen nicht nur erstellt, sondern ihre Gültigkeit auch mit regelmäßigen Restore-Tests geprüft werden.","tags":["backup","recovery","rpo"]},{"metric":"Platten-IO-Latenz","warn":"> 10 ms","crit":"> 20 ms","tool":"node_exporter / iostat","note":"Die Lese-/Schreiblatenz des Speichers wirkt sich direkt auf die Abfragezeiten aus. Auf modernen SSD-Systemen sollte dieser Wert wenige Millisekunden betragen. Anhaltend hohe Latenz deutet auf einen Plattenengpass oder übermäßige physische Lesezugriffe hin; shared_buffers und Speicherschicht gemeinsam bewerten.","tags":["io","disk","latency"]},{"metric":"Transaction ID Wraparound (Alter)","warn":"> 1 Milliarde","crit":"> 1,5 Milliarden","tool":"pg_database (age(datfrozenxid))","note":"Nähert sich das Alter der Transaction ID 2 Milliarden, stoppt PostgreSQL alle Schreibvorgänge, um Datenverlust zu verhindern (Wraparound-Schutz). Dies ist einer der gefährlichsten PostgreSQL-Ausfälle. Das Alter von datfrozenxid überwachen und sicherstellen, dass Autovacuum bei der Freeze-Arbeit nicht zurückfällt.","tags":["wraparound","vacuum","critical"]}]},"reference/alerts-mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"alerts":[{"metric":"Trefferquote des InnoDB Buffer Pool","warn":"< 99%","crit":"< 95%","tool":"SHOW ENGINE INNODB STATUS / mysqld_exporter","note":"Die Buffer Pool Hit Ratio ist der Prozentsatz der Daten- und Indexseiten, die ohne Plattenzugriff aus dem Speicher gelesen werden. Niedrige Werte zeigen, dass innodb_buffer_pool_size für das Working Set zu klein ist. Auf einem dedizierten Server werden typischerweise ~70-80% des Speichers dem Buffer Pool zugewiesen (auf einem geteilten Server weniger).","tags":["memory","cache","innodb"]},{"metric":"Anzahl der Verbindungen (Threads_connected vs max_connections)","warn":"> 80%","crit":"> 90%","tool":"SHOW STATUS / max_connections","note":"Nähert sich die Anzahl der Verbindungen dem Limit max_connections, werden neue Verbindungen mit 'Too many connections' abgelehnt. Max_used_connections zeigt den historischen Höchstwert. Gleichzeitige Verbindungen mit einem Connection Pool auf Anwendungsseite unter Kontrolle halten.","tags":["connections","limit","thread"]},{"metric":"Replica Lag (Seconds_Behind_Source)","warn":"> 30 s","crit":"> 300 s","tool":"SHOW REPLICA STATUS","note":"Seconds_Behind_Source gibt in Sekunden an, wie weit das Replikat hinter der Quelle liegt. Hoher Lag bedeutet veraltete Daten auf Lesereplikaten und ein Failover-Risiko. Bei Single-Threaded Apply kann der Wert bei großen Transaktionen irreführend sein; zusammen mit der GTID-Lücke bestätigen.","tags":["replication","lag","replica"]},{"metric":"Wartezeit auf InnoDB-Zeilen-Locks","warn":"Ø > 1 s","crit":"Ø > 5 s","tool":"SHOW STATUS (Innodb_row_lock_time_avg)","note":"Die durchschnittliche Wartezeit auf Zeilen-Locks zeigt, wie stark sich Transaktionen gegenseitig warten lassen. Hohe Werte deuten auf stark kollidierende Updates oder lang laufende Transaktionen hin. Zusammen mit Innodb_row_lock_waits prüfen und die kollidierenden Abfragen in performance_schema finden.","tags":["lock","blocking","innodb"]},{"metric":"History List Length (Undo)","warn":"> 100000","crit":"> 1000000","tool":"SHOW ENGINE INNODB STATUS","note":"Die History List Length ist die Anzahl nicht gepurgter Undo-Einträge und wächst durch lang laufende offene Transaktionen. Ein hoher Wert zeigt, dass Purge zurückfällt und die Leseleistung wegen MVCC nachlässt. Lang laufende Transaktionen finden und beenden, damit die Liste schrumpfen kann.","tags":["undo","transaction","mvcc"]},{"metric":"Fehlgeschlagene Verbindungen (Aborted_connects)","warn":"> 5 / min","crit":"> 20 / min","tool":"SHOW STATUS (Aborted_connects)","note":"Aborted_connects zählt Verbindungen, die bei der Authentifizierung oder beim Handshake scheitern. Plötzliche Spitzen können auf falsche Passwörter, Netzwerkprobleme oder einen möglichen Angriffsversuch hindeuten. Zusammen mit Aborted_clients prüfen, um festzustellen, wo die Verbindung abbricht.","tags":["connections","error","security"]},{"metric":"Auf Platte geschriebene temporäre Tabellen","warn":"> 25% Anteil","crit":"> 50% Anteil","tool":"SHOW STATUS (Created_tmp_disk_tables)","note":"Der Anteil temporärer In-Memory-Tabellen, die auf die Platte ausweichen, zeigt, dass Abfragen das Limit tmp_table_size überschreiten. Temporäre Tabellen auf der Platte verlangsamen Abfragen spürbar. Abfragen mit GROUP BY und BLOB/TEXT-Spalten optimieren und eine Erhöhung von tmp_table_size und max_heap_table_size erwägen.","tags":["temp","disk","query"]},{"metric":"Langsame Abfragen","warn":"> 5 / min","crit":"> 20 / min","tool":"Slow Query Log / Status Slow_queries","note":"Abfragen über dem Schwellenwert long_query_time werden ins Slow Query Log geschrieben und sind die Hauptquelle von Performance-Problemen. Häufige langsame Abfragen deuten auf fehlende Indizes oder schlecht geschriebene Abfragen hin. Das Log mit pt-query-digest analysieren und die teuersten Abfragen priorisieren.","tags":["query","performance","slowlog"]},{"metric":"CPU-Auslastung","warn":"> 80%","crit":"> 90%","tool":"node_exporter / top","note":"Anhaltend hohe CPU-Auslastung kann von schweren Abfragen, fehlenden Indizes oder zu vielen Verbindungen herrühren. Kurze Spitzen sind normal, anhaltende Sättigung führt jedoch zu Warteschlangen bei Abfragen. Die CPU-intensivsten Abfragen mit performance_schema oder dem Slow Log identifizieren.","tags":["cpu","load"]},{"metric":"Plattennutzung (Datenverzeichnis)","warn":"> 85%","crit":"> 95%","tool":"df / node_exporter","note":"Ein volles Datenverzeichnis verhindert, dass MySQL schreibt, und verursacht Tabellenfehler. Wachsende Binlog-Dateien, Undo-Tablespaces und der temporäre Tablespace (ibtmp1) können die Platte plötzlich füllen; das Redo-Log hat eine feste Größe (ab 8.0.30 #innodb_redo, innodb_redo_log_capacity). Sicherstellen, dass alte Binlogs über binlog_expire_logs_seconds gelöscht werden.","tags":["disk","capacity","binlog"]},{"metric":"Am längsten laufende Abfrage","warn":"> 60 s","crit":"> 300 s","tool":"SHOW PROCESSLIST / information_schema.PROCESSLIST","note":"Eine einzelne lang laufende Abfrage kann durch gehaltene Locks und Ressourcenverbrauch andere Transaktionen beeinträchtigen. In OLTP-Systemen gehen minutenlang laufende Abfragen meist auf einen schlechten Plan zurück. Den Plan mit EXPLAIN untersuchen und die Abfrage bei Bedarf beenden und optimieren.","tags":["query","performance"]},{"metric":"Alter des letzten erfolgreichen Backups","warn":"> 24 Stunden","crit":"> 48 Stunden","tool":"xtrabackup / mysqldump Job-Log","note":"Das Backup-Alter bestimmt den jüngsten Punkt, zu dem du zurückkehren kannst, und damit das RPO. Zusammen mit Binlog-Backups wird eine Point-in-Time-Recovery möglich. Backups nur zu erstellen reicht nicht; ihre Gültigkeit mit regelmäßigen Restore-Tests prüfen.","tags":["backup","recovery","rpo"]},{"metric":"Platten-IO-Latenz","warn":"> 10 ms","crit":"> 20 ms","tool":"iostat / node_exporter","note":"Die Speicherlatenz schlägt direkt auf die Abfragezeiten durch und ist vor allem bei Workloads kritisch, die nicht in den Buffer Pool passen. Auf modernen SSD-Systemen sind wenige Millisekunden zu erwarten. Hohe Latenz deutet auf einen Plattenengpass oder übermäßige physische Lesezugriffe hin; auch die Größe des Buffer Pool prüfen.","tags":["io","disk","latency"]}]},"reference/alerts-mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"alerts":[{"metric":"Page Life Expectancy (PLE)","warn":"< 900 s","crit":"< 300 s","tool":"sys.dm_os_performance_counters","note":"Page Life Expectancy zeigt in Sekunden die geschätzte Zeit, die eine Datenseite im Speicher bleiben kann, bevor sie aus dem Buffer Pool verdrängt wird. Niedrige Werte deuten auf Speicherdruck und schnell aus dem Cache verdrängte Seiten hin. Getrennt je NUMA-Knoten überwachen und plötzliche Einbrüche als Speichermangel werten.","tags":["memory","cache","ple"]},{"metric":"Buffer-Cache-Trefferquote","warn":"< 98%","crit":"< 95%","tool":"sys.dm_os_performance_counters","note":"Die Buffer Cache Hit Ratio ist der Prozentsatz der Seiten, die ohne Plattenzugriff aus dem Speicher gelesen werden. In SQL Server ist dieser Wert meist sehr hoch; zusammen mit PLE bewerten. Da er für sich allein irreführend sein kann, bei der Analyse eines Speicherengpasses PLE als primäre Metrik verwenden.","tags":["memory","cache"]},{"metric":"Blockierte Prozesse","warn":">= 1 (> 5 s)","crit":">= 1 (> 30 s)","tool":"sys.dm_exec_requests (blocking_session_id)","note":"Lässt ein von einer Session gehaltener Lock andere warten, bildet sich eine Blocking-Kette und es folgt eine anwendungsweite Verlangsamung. Lang anhaltendes Blocking geht meist auf nicht committete Transaktionen oder einen ungünstigen Isolation Level zurück. Den Head Blocker mit dem Blocked Process Report identifizieren.","tags":["lock","blocking","concurrency"]},{"metric":"Belegung des Transaktionslogs (%used)","warn":"> 75%","crit":"> 90%","tool":"sys.dm_db_log_space_usage / DBCC SQLPERF(LOGSPACE)","note":"Läuft das Transaktionslog voll, kann die Datenbank nicht schreiben und stoppt mit Fehler 9002. Im Wiederherstellungsmodell FULL wächst das Log weiter, wenn keine Log-Backups erstellt werden. Häufigkeit der Log-Backups und VLF-Anzahl überwachen, um sicherzustellen, dass das Log abgeschnitten und wiederverwendet wird.","tags":["log","disk","recovery"]},{"metric":"tempdb-Belegung (%used)","warn":"> 80%","crit":"> 95%","tool":"sys.dm_db_file_space_usage","note":"tempdb wird gemeinsam für temporäre Tabellen, Sorts, den Version Store und Hash-Vorgänge genutzt. Läuft sie voll, schlagen Abfragen auf dem gesamten Server fehl. Plötzliches Wachstum stammt meist von einer großen Abfrage oder einer lang laufenden Snapshot-Transaktion; die Größe des Version Store separat überwachen.","tags":["tempdb","disk","sort"]},{"metric":"Am längsten laufende Abfrage","warn":"> 60 s","crit":"> 300 s","tool":"sys.dm_exec_requests / Query Store","note":"Eine einzelne lang laufende Abfrage kann durch gehaltene Locks und Ressourcenverbrauch andere Arbeit beeinträchtigen. In OLTP-Systemen gehen minutenlang laufende Abfragen meist auf einen fehlenden Index, Parameter Sniffing oder einen schlechten Plan zurück. Plan-Regressionen mit Query Store untersuchen.","tags":["query","performance"]},{"metric":"Redo Queue der Availability Group","warn":"> 1 MB","crit":"> 100 MB","tool":"sys.dm_hadr_database_replica_states","note":"Die Redo Queue zeigt die Log-Menge auf dem sekundären Replikat, die noch auf ihre Anwendung wartet. Eine hohe Redo Queue verlängert die Wiederherstellungszeit nach einem Failover (RTO) und bedeutet veraltete Daten auf lesbaren Replikaten. Zusammen mit redo_rate untersuchen, um vorherzusagen, wann sich der Rückstand schließt.","tags":["replication","ag","lag"]},{"metric":"Deadlock-Rate","warn":"> 1 / min","crit":"> 5 / min","tool":"system_health XEvent / Leistungsindikatoren","note":"Ein Deadlock entsteht, wenn zwei Transaktionen gegenseitig auf ihre Locks warten; SQL Server löst ihn, indem es eine als Opfer wählt. Gelegentliche Deadlocks kommen vor, häufige deuten jedoch auf eine inkonsistente Lock-Reihenfolge hin. Den Deadlock-Graphen aus der Extended-Event-Session system_health extrahieren und die kollidierenden Abfragen beheben.","tags":["lock","deadlock","concurrency"]},{"metric":"Nutzung der Worker Threads","warn":"> 80%","crit":"> 90%","tool":"sys.dm_os_schedulers / max worker threads","note":"Ist der Worker Thread Pool erschöpft, reihen sich neue Anfragen im Wait THREADPOOL ein und der Server kann unresponsiv werden. Ursache ist meist lang anhaltendes Blocking oder zu viele gleichzeitige Verbindungen. Den Thread-Druck durch Auflösen von Blocking-Ketten verringern.","tags":["cpu","thread","load"]},{"metric":"CPU-Auslastung","warn":"> 80%","crit":"> 90%","tool":"sys.dm_os_ring_buffers / Performance Monitor","note":"Anhaltend hohe CPU-Last kann von schweren Abfragen, fehlenden Indizes oder schlechten Plänen herrühren. Kurze Spitzen sind normal, anhaltende Sättigung führt jedoch zu Warteschlangen bei Abfragen. Die CPU-intensivsten Abfragen mit sys.dm_exec_query_stats identifizieren.","tags":["cpu","load"]},{"metric":"Alter des letzten erfolgreichen Backups","warn":"> 24 Stunden (Full)","crit":"> 48 Stunden (Full)","tool":"msdb.dbo.backupset","note":"Das Backup-Alter bestimmt den jüngsten Punkt, zu dem du zurückkehren kannst, und dein RPO. Im Wiederherstellungsmodell FULL ist mit Log-Backups eine Point-in-Time-Recovery möglich. Die Kette aus Full-, Differential- und Log-Backups überwachen, um die Wiederherstellbarkeit sicherzustellen, und regelmäßige Restore-Tests durchführen.","tags":["backup","recovery","rpo"]},{"metric":"Platten-IO-Latenz (Ø Lesen/Schreiben)","warn":"> 15 ms","crit":"> 30 ms","tool":"sys.dm_io_virtual_file_stats","note":"Die durchschnittliche Lese-/Schreiblatenz je Datei zeigt die Speicherleistung. Für Datendateien sind etwa 15 ms zu erwarten, für Logdateien niedrigere Werte. Anhaltend hohe Latenz deutet auf einen Speicherengpass hin; die langsamsten Dateien mit sys.dm_io_virtual_file_stats identifizieren.","tags":["io","disk","latency"]}]},"reference/alerts-mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"alerts":[{"metric":"WiredTiger Cache Dirty Ratio","warn":"> 5%","crit":"> 20%","tool":"db.serverStatus().wiredTiger.cache","note":"Die Dirty Cache Ratio ist der Prozentsatz geänderter Seiten im WiredTiger-Cache, die noch nicht auf die Platte geschrieben wurden. Über 20% bedeutet Eviction-Druck und einen Schreibengpass; MongoDB zwingt dann Anwendungsthreads, die Eviction selbst durchzuführen. Cachegröße und Schreibdurchsatz der Platte gemeinsam bewerten.","tags":["memory","cache","wiredtiger"]},{"metric":"WiredTiger-Cache-Belegung (used %)","warn":"> 80%","crit":"> 95%","tool":"db.serverStatus().wiredTiger.cache","note":"Übersteigt die Cache-Belegung das Eviction-Ziel von 80%, beginnt WiredTiger, Seiten aggressiv zu verdrängen, und die Performance sinkt. Passt das Working Set nicht in den Cache, steigen die physischen Lesezugriffe. Die Einstellung wiredTigerCacheSizeGB und den Serverspeicher prüfen.","tags":["memory","cache","wiredtiger"]},{"metric":"Verfügbare Tickets (Read/Write Tickets)","warn":"available/totalTickets < 20% oder queueLength > 0 (anhaltend)","crit":"available = 0 und queueLength wächst weiter","tool":"db.serverStatus().wiredTiger.concurrentTransactions","note":"WiredTiger begrenzt gleichzeitige Lese- und Schreibvorgänge mit einem Ticket-System. Vor 7.0 lag der Standard fest bei 128 Tickets; ab 7.0 wird die Ticketanzahl dynamisch angepasst (storageEngineConcurrencyAdjustmentAlgorithm: throughputProbing), daher statt der absoluten Zahl das Verhältnis available/totalTickets und queueLength überwachen. Gehen die Tickets aus und reihen sich Vorgänge ein, ist das ein Zeichen für einen ernsten Engpass; meist verursacht durch langsame Abfragen oder gesättigte Platten-I/O.","tags":["concurrency","ticket","bottleneck"]},{"metric":"Replication Lag","warn":"> 10 s","crit":"> 60 s","tool":"rs.printSecondaryReplicationInfo()","note":"Replication Lag gibt in Sekunden an, wie weit ein Secondary hinter dem Primary liegt. Hoher Lag bedeutet je nach Read Preference veraltete Daten und ein Risiko von Datenverlust beim Failover. Ursache sind meist Engpässe bei Platten-I/O oder Netzwerk auf dem Secondary oder lang laufende Vorgänge.","tags":["replication","lag","replicaset"]},{"metric":"Verbindungsauslastung (connections %)","warn":"> 80%","crit":"> 90%","tool":"db.serverStatus().connections","note":"Nähern sich die aktiven Verbindungen der konfigurierten Obergrenze, werden neue Verbindungen abgelehnt. Da jede Verbindung Speicher und einen Thread verbraucht, erzeugt eine hohe Anzahl auch Ressourcendruck. Verbindungsstürme verhindern, indem der Connection Pool auf Anwendungsseite richtig dimensioniert wird.","tags":["connection","limit","pool"]},{"metric":"Page-Fault-Rate","warn":"> 100 / s","crit":"> 1000 / s","tool":"db.serverStatus() / extra_info page_faults","note":"Ein Page Fault tritt auf, wenn Daten nicht im Speicher liegen und von der Platte geholt werden müssen. Eine hohe Fault-Rate zeigt, dass das Working Set nicht in den Speicher passt und das System plattengebunden läuft. Anhaltend hohe Werte erfordern mehr Speicher oder eine Optimierung von Abfragen/Indizes.","tags":["memory","disk","pagefault"]},{"metric":"Vorgänge in der Warteschlange","warn":"> 10","crit":"> 50","tool":"db.serverStatus().globalLock.currentQueue","note":"globalLock currentQueue zeigt die Anzahl der Lese-/Schreibvorgänge, die beim Warten auf einen Lock oder eine Ressource in der Warteschlange stehen. Eine dauerhaft volle Warteschlange zeigt, dass der Server mit der eingehenden Last nicht mithält. Zusammen mit erschöpften Tickets und Platten-I/O untersuchen, um die Quelle des Engpasses zu bestimmen.","tags":["concurrency","queue","lock"]},{"metric":"Oplog-Fenster","warn":"< 24 Stunden","crit":"< 6 Stunden","tool":"rs.printReplicationInfo()","note":"Das Oplog-Fenster ist die vom Oplog abgedeckte Zeitspanne und bestimmt, wie lange ein Secondary getrennt bleiben und sich dennoch neu synchronisieren kann. Wird das Fenster schmaler, braucht ein zurückfallendes Mitglied einen vollständigen Resync. Das Fenster verkürzt sich mit steigender Schreiblast; die Oplog-Größe entsprechend vergrößern.","tags":["oplog","replication","recovery"]},{"metric":"Am längsten laufende Abfrage","warn":"> 30 s","crit":"> 120 s","tool":"db.currentOp() / slow query log","note":"Lang laufende Abfragen beeinträchtigen andere Vorgänge durch Ressourcenverbrauch und gehaltene Locks. Meist stammen sie von Abfragen mit Collection Scan (COLLSCAN) wegen eines fehlenden Index. Langsame Vorgänge mit Profiler und currentOp finden und passende Indizes anlegen.","tags":["query","performance","index"]},{"metric":"CPU-Auslastung","warn":"> 80%","crit":"> 90%","tool":"node_exporter / mongostat","note":"Anhaltend hohe CPU-Last kann von Abfragen ohne Index, schweren Aggregationen oder zu vielen Verbindungen herrühren. Kurze Spitzen sind normal, anhaltende Sättigung führt jedoch zu Warteschlangen. Mit dem Profiler die CPU-intensivsten Abfragen identifizieren und optimieren.","tags":["cpu","load"]},{"metric":"Plattennutzung (dbPath)","warn":"> 85%","crit":"> 95%","tool":"df / node_exporter","note":"Ein volles Datenverzeichnis macht MongoDB schreibunfähig und kann zu Korruption führen. Die WiredTiger-Komprimierung hilft zwar, doch Journal und wachsende Collections können die Platte schnell füllen. Kapazität planen und nicht benötigte Daten mit TTL-Indizes automatisch löschen.","tags":["disk","capacity"]},{"metric":"Alter des letzten erfolgreichen Backups","warn":"> 24 Stunden","crit":"> 48 Stunden","tool":"mongodump / Ops Manager / Snapshot-Eintrag","note":"Das Backup-Alter bestimmt den jüngsten Punkt, zu dem du zurückkehren kannst, und dein RPO. In Kombination mit dem Oplog wird eine Point-in-Time-Recovery möglich. Backups zu erstellen reicht nicht; sie mit regelmäßigen Restore-Tests validieren und sicherstellen, dass in Sharded Clusters konsistente Snapshots erstellt werden.","tags":["backup","recovery","rpo"]}]},"reference/alerts-redis.json":{"engineId":"redis","engine":"Redis","order":6,"alerts":[{"metric":"Speichernutzung (used_memory vs maxmemory)","warn":"> 80%","crit":"> 95%","tool":"INFO memory / redis_exporter","note":"Das Verhältnis von used_memory zu maxmemory zeigt, wie viel vom Speicher von Redis belegt ist. Wird das Limit erreicht, werden Keys gemäß maxmemory-policy verdrängt oder Schreibvorgänge abgelehnt. Das Speicherwachstum verfolgen, um unerwartete OOM und Eviction-Wellen zu verhindern.","tags":["memory","maxmemory","capacity"]},{"metric":"Keyspace Hit Ratio","warn":"< 90%","crit":"< 80%","tool":"INFO stats (keyspace_hits/misses)","note":"Die Hit Ratio ist der Prozentsatz angefragter Keys, die im Cache gefunden werden, und wird aus keyspace_hits und keyspace_misses berechnet. Eine niedrige Quote deutet auf ineffektive Cache-Nutzung, falsche TTLs oder einen kalten Cache hin. Bei Cache-Workloads sollte diese Quote möglichst hoch sein.","tags":["cache","hit","performance"]},{"metric":"Verdrängte Schlüssel (evicted_keys)","warn":"> 1 / s","crit":"> 100 / s","tool":"INFO stats (evicted_keys)","note":"evicted_keys ist die Anzahl der Keys, die unter maxmemory-policy entfernt wurden, weil der Speicher voll ist. Kontinuierliche Eviction zeigt, dass der Speicher nicht reicht und wichtige Daten verloren gehen können. Eviction durch mehr Speicher oder eine Überarbeitung von TTL-Strategie bzw. Datenmodell reduzieren.","tags":["memory","eviction","maxmemory"]},{"metric":"Blockierte Clients (blocked_clients)","warn":"> 50","crit":"> 200","tool":"INFO clients (blocked_clients)","note":"blocked_clients ist die Anzahl der Clients, die auf blockierende Befehle wie BLPOP/BRPOP/WAIT warten. Höhere Werte als erwartet deuten auf Consumer hin, die nicht mithalten, oder auf einen wachsenden Rückstau in der Queue. Bei Queue-basierten Workloads das Gleichgewicht zwischen Producer und Consumer prüfen.","tags":["client","blocking","queue"]},{"metric":"Speicherfragmentierung (mem_fragmentation_ratio)","warn":"> 1.5","crit":"> 2.0","tool":"INFO memory","note":"Die Fragmentierungsquote ist der vom Betriebssystem bezogene Speicher geteilt durch den von Redis genutzten Speicher. Über 1.5 deutet auf übermäßige Fragmentierung hin; unter 1.0 auf Speicher, der in den Swap ausweicht, was sehr gefährlich ist. Fragmentierung lässt sich durch Aktivieren von activedefrag oder durch einen Neustart verringern.","tags":["memory","fragmentation"]},{"metric":"Status der Master-Verbindung (master_link_status)","warn":"down (< 10 s)","crit":"down (> 30 s)","tool":"INFO replication (master_link_status)","note":"master_link_status zeigt den Zustand (up/down) der Verbindung des Replikats zum Master. 'down' bedeutet, dass die Replikation unterbrochen ist und das Replikat veraltete Daten ausliefert. Lange Unterbrechungen erfordern einen vollständigen Resync; Netzwerk und Zustand des Masters sofort prüfen.","tags":["replication","master","connection"]},{"metric":"Replication Lag (Differenz von master_repl_offset)","warn":"> 1 MB","crit":"> 10 MB","tool":"INFO replication (offset diff)","note":"Die Differenz zwischen den Offsets von Master und Replikat zeigt in Bytes, wie weit das Replikat zurückliegt. Eine große Differenz bedeutet ein Risiko von Datenverlust beim Failover und Inkonsistenz auf Lesereplikaten. Netzwerklatenz und Last des Replikats untersuchen, um sicherzustellen, dass sich die Lücke schließt.","tags":["replication","lag","offset"]},{"metric":"Status des letzten RDB-bgsave (rdb_last_bgsave_status)","warn":"err (letzter Versuch)","crit":"err (> 24 Stunden)","tool":"INFO persistence","note":"rdb_last_bgsave_status zeigt, ob der letzte Hintergrund-Snapshot erfolgreich war (ok). Ein fehlgeschlagener bgsave bedeutet, dass die Persistenz nicht funktioniert und beim Neustart Datenverlust droht. Meist liegt es an einer vollen Platte oder zu wenig Speicher für den Fork.","tags":["backup","persistence","rdb"]},{"metric":"Verbindungsauslastung (connected_clients)","warn":"> 80% maxclients","crit":"> 90% maxclients","tool":"INFO clients / maxclients","note":"Nähert sich connected_clients dem Limit maxclients, werden neue Verbindungen abgelehnt. Connection Leaks oder ein falsch konfigurierter Pool können plötzliche Spitzen verursachen. Den Connection Pool auf Anwendungsseite richtig konfigurieren und inaktive Verbindungen schließen.","tags":["connection","limit","client"]},{"metric":"Momentane Befehlslatenz","warn":"> 1 ms","crit":"> 10 ms","tool":"redis-cli --latency / LATENCY DOCTOR","note":"Da Redis Single-Threaded ist, liegt die Befehlslatenz meist im Mikrosekundenbereich. Steigende Latenz kann von langsamen Befehlen (KEYS, großes SORT), Blockierung beim Fork oder Swap-Nutzung herrühren. Die Ursache mit slowlog und den LATENCY-Befehlen finden.","tags":["latency","performance","slowlog"]},{"metric":"CPU-Auslastung","warn":"> 80% (ein Kern)","crit":"> 90% (ein Kern)","tool":"top / redis_exporter","note":"Da der Main Thread von Redis einen einzigen Kern nutzt, verlangsamt die Sättigung eines Kerns den gesamten Server. Hohe CPU-Last kann von teuren Befehlen, zu vielen Verbindungen oder Lua-Skripten herrühren. Teure Befehle mit SLOWLOG finden und die Last bei Bedarf auf mehrere Instanzen verteilen.","tags":["cpu","load"]},{"metric":"Abgelehnte Verbindungen (rejected_connections)","warn":"> 0","crit":"> 10 / min","tool":"INFO stats (rejected_connections)","note":"rejected_connections ist die Anzahl der Verbindungen, die abgelehnt wurden, weil das Limit maxclients erreicht war. Ein Wert über null bedeutet, dass Clients nicht bedient werden und in der Anwendung Fehler auftreten. Den Wert von maxclients und Connection Leaks sofort untersuchen.","tags":["connection","error","limit"]}]},"reference/alerts-elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"alerts":[{"metric":"JVM-Heap-Nutzung","warn":"> 75%","crit":"> 85%","tool":"_nodes/stats/jvm / elasticsearch_exporter","note":"Die JVM-Heap-Nutzung zeigt den Speicherdruck des Knotens; anhaltende Werte über 75% bedeuten häufige GC und mögliches OutOfMemory. Übersteigt der Heap 85%, kann der Knoten unresponsiv werden oder aus dem Cluster fallen. Den Heap auf etwa die Hälfte des Serverspeichers und höchstens 30-31 GB dimensionieren.","tags":["memory","jvm","heap"]},{"metric":"Anzahl nicht zugewiesener Shards (unassigned shards)","warn":">= 1 (Replica)","crit":">= 1 (Primary)","tool":"_cluster/health / _cat/shards","note":"Nicht zugewiesene Shards bedeuten, dass ein Teil der Daten nicht erreichbar oder nicht repliziert ist. Ein nicht zugewiesener Primary Shard bedeutet ein Risiko von Datenverlust, ein nicht zugewiesenes Replica den Verlust von Redundanz, und der Cluster wird yellow/red. Den Grund mit _cluster/allocation/explain finden.","tags":["shard","cluster","health"]},{"metric":"Belegung relativ zum Disk Watermark","warn":"> 85% (low)","crit":"> 90% (high) / > 95% (flood_stage)","tool":"_nodes/stats/fs / _cat/allocation","note":"Elasticsearch reagiert abgestuft auf die Plattennutzung: Beim Low Watermark von 85% werden dem Knoten keine neuen Shards mehr zugewiesen, beim High Watermark von 90% werden vorhandene Shards auf andere Knoten verlagert, und bei 95% flood_stage wird auf Indizes ein read_only_allow_delete-Block gesetzt. In 8.x haben auf großen Platten die max_headroom-Werte (low 200GB / high 150GB / flood 100GB frei) Vorrang vor Prozentwerten. Ab 7.4 wird der Block automatisch aufgehoben, sobald die Plattennutzung unter das High Watermark fällt; dennoch Plattenplatz freigeben, bevor die Watermarks überschritten werden.","tags":["disk","watermark","capacity"]},{"metric":"Abgelehnte Thread-Pool-Vorgänge (rejected)","warn":"> 0 (write/search)","crit":"> 100 / min","tool":"_cat/thread_pool / _nodes/stats/thread_pool","note":"Ist eine Thread-Pool-Warteschlange voll, werden eingehende Anfragen abgelehnt; eine Ablehnung im Write-Pool bedeutet verlorene Indexierung, im Search-Pool eine fehlgeschlagene Abfrage. Anhaltende Ablehnungen zeigen, dass der Knoten mit der eingehenden Last nicht mithält. Die Last durch geringere Bulk-Größe und Parallelität ausbalancieren.","tags":["threadpool","rejected","load"]},{"metric":"GC-Pausenzeit (old gen GC pause)","warn":"> 1 s","crit":"> 5 s","tool":"GC logs / _nodes/stats/jvm","note":"Während langer GC-Pausen kann der Knoten nicht antworten und die Verbindung zum Master verlieren, was eine Neuverteilung der Shards auslöst. Häufige und lange GC der Old Generation sind ein Symptom von Heap-Druck. Heap-Größe sowie speicherhungrige Aggregationen und Fielddata-Nutzung prüfen.","tags":["jvm","gc","pause"]},{"metric":"Indexierungslatenz","warn":"> 50 ms","crit":"> 200 ms","tool":"_nodes/stats/indices (index_time/index_total)","note":"Die durchschnittliche Indexierungszeit zeigt die Schreibleistung für Dokumente und wird berechnet, indem index_time_in_millis durch index_total geteilt wird. Steigende Werte deuten auf einen Platten-IO-Engpass, eine schwere Analysekette oder Refresh-Druck hin. Bulk-Größe und die Einstellung refresh_interval optimieren.","tags":["indexing","latency","performance"]},{"metric":"Suchlatenz","warn":"> 100 ms","crit":"> 500 ms","tool":"_nodes/stats/indices (query_time/query_total)","note":"Die durchschnittliche Abfragezeit spiegelt direkt die Suchleistung wider, die Nutzer erleben, und wird berechnet, indem query_time_in_millis durch query_total geteilt wird. Steigende Werte können von schweren Aggregationen, zu vielen Shards oder einem kalten Cache herrühren. Das Slow Log aktivieren, um teure Abfragen zu finden.","tags":["search","latency","performance"]},{"metric":"Pending Tasks","warn":"> 10","crit":"> 100","tool":"_cluster/pending_tasks","note":"Pending Tasks sind Änderungen am Cluster State, die der Master-Knoten noch nicht angewendet hat (Mapping, Shard-Zuweisung usw.). Ständige Ansammlung zeigt, dass der Master überlastet oder der Cluster State zu groß geworden ist. Den Druck auf den Master durch Reduzieren einer übermäßigen Shard-Anzahl verringern.","tags":["cluster","master","queue"]},{"metric":"Ausgelöste Circuit Breaker","warn":"> 0 (tripped)","crit":"> 10 / min","tool":"_nodes/stats/breakers","note":"Circuit Breaker brechen eine Anfrage ab, damit ein Vorgang nicht übermäßig Speicher verbraucht und den Knoten in ein OutOfMemory treibt. Häufiges Auslösen deutet auf Speicherdruck durch schwere Aggregationen oder große Anfragen hin. Den tripped count überwachen und die problematischen Abfragen entschlacken.","tags":["memory","breaker","jvm"]},{"metric":"Cluster-Status (cluster status)","warn":"yellow","crit":"red","tool":"_cluster/health","note":"Der Cluster-Status kann green (alle Shards zugewiesen), yellow (Replicas fehlen) oder red (Primaries fehlen, Daten nicht erreichbar) sein. Red erfordert sofortiges Handeln, weil Daten verloren oder nicht erreichbar sind. Yellow bedeutet Verlust von Redundanz und darf nicht ignoriert werden.","tags":["cluster","health","shard"]},{"metric":"CPU-Auslastung","warn":"> 80%","crit":"> 90%","tool":"_nodes/stats/os / node_exporter","note":"Anhaltend hohe CPU-Last kann von schweren Abfragen, übermäßiger Indexierung oder Merge-Vorgängen herrühren. Kurze Spitzen sind normal, anhaltende Sättigung führt jedoch zu Warteschlangen bei Abfragen und Indexierung. Die CPU-intensivsten Vorgänge mit der Hot Threads API (_nodes/hot_threads) identifizieren.","tags":["cpu","load"]},{"metric":"Alter des letzten erfolgreichen Snapshots","warn":"> 24 Stunden","crit":"> 48 Stunden","tool":"_snapshot/_status / SLM","note":"Das Snapshot-Alter bestimmt den jüngsten Punkt, zu dem du zurückkehren kannst, und dein RPO. Automatische, regelmäßige Backups sollten mit Snapshot Lifecycle Management (SLM) sichergestellt werden. Bleiben fehlgeschlagene oder übersprungene Snapshots unbemerkt, lassen sich Daten im Katastrophenfall nicht wiederherstellen; den Snapshot-Status regelmäßig prüfen.","tags":["backup","snapshot","rpo"]}]},"cloud/cloud-ops-1.json":{"category":"Verbindung & Backup & Replikat","categoryId":"cloud-core","order":1,"providers":[{"id":"aws-rds","name":"AWS RDS","color":"#B85C00"},{"id":"aws-aurora","name":"Aurora","color":"#7B3FE4"},{"id":"gcp-cloudsql","name":"Cloud SQL","color":"#C5372C"},{"id":"azure-sql","name":"Azure SQL","color":"#0067B8"},{"id":"mongodb-atlas","name":"Atlas","color":"#00843D"}],"concepts":[{"id":"list-instances","title":"Instanzen / Datenbanken auflisten","aliases":["list","instances","list all"],"providers":{"aws-rds":{"command":"aws rds describe-db-instances --query 'DBInstances[].DBInstanceIdentifier'","note":"Listet die Bezeichner aller RDS-Instanzen auf. Mit --region eine Region ansteuern.","danger":"safe"},"aws-aurora":{"command":"aws rds describe-db-clusters --query 'DBClusters[].DBClusterIdentifier'","note":"Listet die Bezeichner der Aurora-Cluster auf; die Clustermitglieder stehen im Feld DBClusterMembers.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud sql instances list","note":"Listet alle Cloud SQL-Instanzen im Projekt auf. Mit --project ein Projekt ansteuern.","danger":"safe"},"azure-sql":{"command":"az sql db list -g <rg> -s <server>","note":"Listet die Datenbanken in der angegebenen Ressourcengruppe (-g) und dem logischen Server (-s) auf. Server mit 'az sql server list' auflisten.","danger":"safe"},"mongodb-atlas":{"command":"atlas clusters list","note":"Listet alle Cluster im Atlas-Projekt auf. Mit --projectId ein Projekt ansteuern.","danger":"safe"}}},{"id":"connect","title":"Mit einer Instanz verbinden (Endpoint ermitteln + verbinden)","aliases":["connect","endpoint","connection string","connect to"],"providers":{"aws-rds":{"command":"aws rds describe-db-instances --db-instance-identifier <id> --query 'DBInstances[0].Endpoint.Address'","note":"Gibt die Verbindungsadresse (Host) der Instanz zurück. Den Port aus Endpoint.Port entnehmen und mit dem psql/mysql-Client verbinden.","danger":"safe"},"aws-aurora":{"command":"aws rds describe-db-clusters --db-cluster-identifier <id> --query 'DBClusters[0].Endpoint'","note":"Gibt den Writer-Endpoint des Clusters zurück. Für reinen Lesezugriff das Feld ReaderEndpoint verwenden.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud sql instances describe <instance> --format='value(ipAddresses[].ipAddress)'","note":"Gibt die IP-Adresse(n) der Instanz zurück. Für sichere Verbindungen wird der Cloud SQL Auth Proxy empfohlen.","danger":"safe"},"azure-sql":{"command":"az sql db show-connection-string -s <server> -n <db> -c ado.net","note":"Erzeugt den Connection String für den angegebenen Clienttyp (-c: ado.net, jdbc, odbc, php usw.); Benutzer/Passwort trägst du selbst ein.","danger":"safe"},"mongodb-atlas":{"command":"atlas clusters connectionStrings describe <clusterName>","note":"Gibt die Standard- und SRV-Connection-Strings des Clusters zurück. Ein Eintrag in der IP Access List und ein Datenbankbenutzer sind erforderlich.","danger":"safe"}}},{"id":"take-snapshot","title":"Manuelles Backup / Snapshot erstellen","aliases":["snapshot","backup","backup"],"providers":{"aws-rds":{"command":"aws rds create-db-snapshot --db-instance-identifier <id> --db-snapshot-identifier <snap-id>","note":"Erstellt einen manuellen Snapshot der Instanz (bleibt bis zum manuellen Löschen erhalten). Eine kurze I/O-Unterbrechung ist möglich.","danger":"caution"},"aws-aurora":{"command":"aws rds create-db-cluster-snapshot --db-cluster-identifier <id> --db-cluster-snapshot-identifier <snap-id>","note":"Erstellt einen manuellen Snapshot des Aurora-Clusters. Da Aurora-Backups kontinuierlich laufen, ist dies eine feste Kopie.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql backups create --instance=<instance>","note":"Erstellt ein On-Demand-Backup der Instanz. Während des Vorgangs ist eine kurze Performance-Beeinträchtigung möglich.","danger":"caution"},"azure-sql":{"command":"az sql db export -s <server> -n <db> -u <user> -p <pwd> --admin-user <adm> --admin-password <pwd> --storage-key <key> --storage-key-type StorageAccessKey --storage-uri https://<acct>.blob.core.windows.net/<container>/<file>.bacpac","note":"Azure SQL wird automatisch gesichert; für ein portables manuelles Backup wird die Datenbank als BACPAC exportiert. Die Point-in-Time-Wiederherstellung ist eine separate Funktion.","danger":"caution"},"mongodb-atlas":{"command":"atlas backups snapshots create <clusterName>","note":"Startet einen On-Demand-Backup-Snapshot des Clusters. Cloud Backup muss aktiviert sein.","danger":"caution"}}},{"id":"restore-pitr","title":"Auf einen Zeitpunkt wiederherstellen (Point-in-Time-Restore)","aliases":["pitr","restore","point-in-time","roll back"],"providers":{"aws-rds":{"command":"aws rds restore-db-instance-to-point-in-time --source-db-instance-identifier <src> --target-db-instance-identifier <new> --restore-time 2026-07-03T10:00:00Z","note":"Erstellt aus der Quellinstanz zum angegebenen Zeitpunkt eine NEUE Instanz; die vorhandene Instanz wird nicht überschrieben. Mit --use-latest-restorable-time auf den jüngsten Punkt wiederherstellen.","danger":"caution"},"aws-aurora":{"command":"aws rds restore-db-cluster-to-point-in-time --source-db-cluster-identifier <src> --db-cluster-identifier <new> --restore-to-time 2026-07-03T10:00:00Z","note":"Stellt den Aurora-Cluster zum angegebenen Zeitpunkt als neuen Cluster wieder her. Anschließend müssen Instanzen mit create-db-instance hinzugefügt werden.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances clone <src> <new> --point-in-time '2026-07-03T10:00:00Z'","note":"Erstellt einen Klon der Quellinstanz zum angegebenen Zeitpunkt als neue Instanz. Auf der Quelle muss Binary-/WAL-Logging aktiviert sein.","danger":"caution"},"azure-sql":{"command":"az sql db restore -g <rg> -s <server> -n <db> --dest-name <new-db> --time '2026-07-03T10:00:00'","note":"Stellt die Datenbank zum angegebenen Zeitpunkt als NEUE Datenbank wieder her; die Quelldatenbank bleibt erhalten. Die Zeit ist in UTC.","danger":"caution"},"mongodb-atlas":{"command":"atlas backups restores start pointInTime --clusterName <cluster> --pointInTimeUTCMillis <ms> --targetClusterName <target> --targetProjectId <projId>","note":"Startet mit Continuous Cloud Backup eine Wiederherstellung auf die angegebene UTC-Zeit. Der Zielcluster wird überschrieben; vorsichtig vorgehen.","danger":"danger"}}},{"id":"read-replica","title":"Lesereplikat erstellen","aliases":["replica","read replica","read replica"],"providers":{"aws-rds":{"command":"aws rds create-db-instance-read-replica --db-instance-identifier <replica-id> --source-db-instance-identifier <src>","note":"Erstellt aus der Quellinstanz ein asynchrones Lesereplikat. Für eine regionsübergreifende Kopie --source-region verwenden.","danger":"caution"},"aws-aurora":{"command":"aws rds create-db-instance --db-instance-identifier <reader-id> --db-cluster-identifier <cluster> --engine aurora-postgresql --db-instance-class db.r6g.large","note":"In Aurora ist ein Reader eine dem Cluster hinzugefügte Instanz; er teilt den Cluster-Speicher. Den Wert für engine passend zum Cluster setzen (aurora-mysql / aurora-postgresql).","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances create <replica> --master-instance-name=<src> --region=<region>","note":"Erstellt ein Lesereplikat aus der primären (Master-)Instanz. Auf der Quelle muss Binary Logging aktiviert sein.","danger":"caution"},"azure-sql":{"command":"az sql db replica create -g <rg> -s <server> -n <db> --partner-server <target-server>","note":"Erstellt auf einem anderen Server ein aktiv georepliziertes, lesbares Secondary der Datenbank. Der Zielserver muss existieren.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters update <clusterName> --file cluster.json","note":"Atlas-Cluster sind bereits replizierte Replica Sets. Für zusätzliche Lesekapazität readOnly- oder analytics-Knoten unter replicationSpecs hinzufügen und aktualisieren.","danger":"caution"}}},{"id":"failover","title":"Failover","aliases":["failover","fail over","switch to standby"],"providers":{"aws-rds":{"command":"aws rds reboot-db-instance --db-instance-identifier <id> --force-failover","note":"Erzwingt auf einer Multi-AZ-Instanz einen Neustart, der ein Failover auf den Standby auslöst. Kurze Ausfallzeit einplanen.","danger":"danger"},"aws-aurora":{"command":"aws rds failover-db-cluster --db-cluster-identifier <cluster> --target-db-instance-identifier <reader>","note":"Befördert einen Reader im Aurora-Cluster zum neuen Writer. Ohne Angabe eines Ziels wird ein passendes Replikat gewählt; kurze Ausfallzeit einplanen.","danger":"danger"},"gcp-cloudsql":{"command":"gcloud sql instances failover <instance>","note":"Führt ein Failover einer HA-Instanz (Hochverfügbarkeit) in die Standby-Zone durch. Funktioniert nur bei Instanzen mit aktivierter HA; es entsteht Ausfallzeit.","danger":"danger"},"azure-sql":{"command":"az sql failover-group set-primary -g <rg> -s <secondary-server> -n <fog-name>","note":"Verschiebt die Primary-Rolle in einer Auto-Failover-Gruppe auf den sekundären Server (geplantes Failover). Mit --allow-data-loss erzwingbar, was Datenverlust riskiert.","danger":"danger"},"mongodb-atlas":{"command":"atlas clusters failover <clusterName>","note":"Atlas führt Failover automatisch durch; dieser Befehl löst zu Testzwecken ein manuelles Failover aus. Der Primary-Knoten wechselt und die Anwendung muss sich neu verbinden.","danger":"danger"}}}]},"cloud/cloud-ops-2.json":{"category":"Konfiguration & Skalierung & Monitoring","categoryId":"cloud-config","order":2,"providers":[{"id":"aws-rds","name":"AWS RDS","color":"#B85C00"},{"id":"aws-aurora","name":"Aurora","color":"#7B3FE4"},{"id":"gcp-cloudsql","name":"Cloud SQL","color":"#C5372C"},{"id":"azure-sql","name":"Azure SQL","color":"#0067B8"},{"id":"mongodb-atlas","name":"Atlas","color":"#00843D"}],"concepts":[{"id":"scale-instance","title":"Instanzgröße ändern (vertikal skalieren)","aliases":["scale","size","resize","scale up","vertical"],"providers":{"aws-rds":{"command":"aws rds modify-db-instance --db-instance-identifier <id> --db-instance-class db.r6g.xlarge --apply-immediately","note":"--apply-immediately wendet die Änderung sofort an und startet die Instanz neu (kurze Ausfallzeit). Ohne dieses Flag wird die Änderung im nächsten Wartungsfenster angewendet. Bei Multi-AZ-Instanzen verkürzt sich die Ausfallzeit auf etwa die Failover-Dauer.","danger":"caution"},"aws-aurora":{"command":"aws rds modify-db-instance --db-instance-identifier <writer-or-reader-id> --db-instance-class db.r6g.2xlarge --apply-immediately","note":"In Aurora wird jede Instanz (Writer und Reader) separat skaliert; geändert wird die Instanzgröße, nicht der Cluster. Zuerst die Reader skalieren, dann per Failover den Writer ersetzen, um die Ausfallzeit zu minimieren. Bei Serverless v2 wird über min/max ACU skaliert.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --tier db-custom-4-16384","note":"Ein benutzerdefinierter Maschinentyp wird als db-custom-CPU-MEMORY angegeben (Beispiel: 4 vCPU, 16384 MB RAM). Die Änderung startet die Instanz neu und verursacht kurze Ausfallzeit. Auch vordefinierte Tiers (z. B. db-n1-standard-4) sind möglich.","danger":"caution"},"azure-sql":{"command":"az sql db update -g <rg> -s <server> -n <db> --service-objective S3","note":"Im DTU-Modell ein Service Objective wie S3/P2 verwenden; im vCore-Modell --edition GeneralPurpose --family Gen5 --capacity 4. Die Skalierung erfolgt online; gegen Ende des Vorgangs kann die Verbindung für einige Sekunden abbrechen.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters update <name> --tier M30","note":"Atlas ändert die Größe der Replica-Set-Knoten rollierend, daher gibt es keine Ausfallzeit. Erfordert das Flag --projectId oder ein Standardprojekt. Beim Verkleinern auf Platten- und Speicherlimits achten.","danger":"caution"}}},{"id":"parameter-group","title":"Parameter ändern (Parameter Group / Flags / Serverkonfiguration)","aliases":["parameter","parameter group","flags","config","setting"],"providers":{"aws-rds":{"command":"aws rds modify-db-parameter-group --db-parameter-group-name <pg> --parameters \"ParameterName=max_connections,ParameterValue=500,ApplyMethod=pending-reboot\"","note":"Für dynamische Parameter ApplyMethod=immediate, für statische pending-reboot verwenden. Die Parameter Group muss an die Instanz angehängt sein; Änderungen an statischen Parametern greifen nach einem Neustart. Eine Gruppe mit create-db-parameter-group anlegen.","danger":"caution"},"aws-aurora":{"command":"aws rds modify-db-cluster-parameter-group --db-cluster-parameter-group-name <cpg> --parameters \"ParameterName=innodb_lock_wait_timeout,ParameterValue=100,ApplyMethod=immediate\"","note":"In Aurora werden clusterweite Parameter über die DB Cluster Parameter Group und instanzspezifische über die DB Parameter Group (modify-db-parameter-group) verwaltet. Statische Parameter erfordern einen Neustart im gesamten Cluster.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --database-flags max_connections=500,log_min_duration_statement=1000","note":"--database-flags setzt ALLE Flags auf einmal; zuvor gesetzte Flags, die nicht in der Liste stehen, werden zurückgesetzt, daher alle gemeinsam angeben. Manche Flags erfordern einen Neustart der Instanz. Aktuelle Flags mit 'gcloud sql instances describe <id>' anzeigen.","danger":"caution"},"azure-sql":{"command":"az sql db configure -g <rg> -s <server> -n <db> ...   # DB-level settings via T-SQL: ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 4;","note":"In Azure SQL werden Einstellungen statt über eine klassische Serverkonfiguration überwiegend mit T-SQL vorgenommen: ALTER DATABASE SCOPED CONFIGURATION ...; sp_configure auf Serverebene ist eingeschränkt. Für Optionen von Elastic Pool/Datenbank die Flags von 'az sql db update' verwenden.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters advancedSettings update <name> --oplogMinRetentionHours 24 --oplogSizeMB 20480","note":"In Atlas lassen sich Low-Level-Prozessargumente von MongoDB nur eingeschränkt über 'advancedSettings' setzen (Oplog, Volltextsuche, Indextiefe usw.). Die meisten Serverparameter verwaltet Atlas und sie sind nicht direkt änderbar.","danger":"caution"}}},{"id":"monitoring","title":"Metriken und Performance anzeigen","aliases":["monitoring","metric","observability","performance","insights"],"providers":{"aws-rds":{"command":"aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name CPUUtilization --dimensions Name=DBInstanceIdentifier,Value=<id> --start-time 2026-07-05T00:00:00Z --end-time 2026-07-05T01:00:00Z --period 300 --statistics Average","note":"Grundlegende Metriken (CPU, Verbindungen, IOPS, Latenz) liegen im CloudWatch-Namespace AWS/RDS. Für die Analyse auf Abfrageebene Performance Insights aktivieren: 'aws pi get-resource-metrics --service-type RDS ...' oder das Performance Insights-Dashboard in der Konsole nutzen.","danger":"safe"},"aws-aurora":{"command":"aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name AuroraReplicaLag --dimensions Name=DBInstanceIdentifier,Value=<reader-id> --start-time 2026-07-05T00:00:00Z --end-time 2026-07-05T01:00:00Z --period 60 --statistics Maximum","note":"Aurora-spezifische Metriken (AuroraReplicaLag, VolumeReadIOPs, CommitLatency, buffer cache hit) liegen ebenfalls im Namespace AWS/RDS. Performance Insights wird für Aurora empfohlen und bietet Analysen zu Top-SQL und Wait Events.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud monitoring time-series list --filter='metric.type=\"cloudsql.googleapis.com/database/cpu/utilization\" AND resource.labels.database_id=\"<project>:<id>\"' --interval-start-time=2026-07-05T00:00:00Z --interval-end-time=2026-07-05T01:00:00Z","note":"Cloud SQL-Metriken liegen in Cloud Monitoring unter cloudsql.googleapis.com/... Für die Analyse auf Abfrageebene Cloud SQL Query Insights aktivieren (Konsole oder API); es zeigt die langsamsten Abfragen und Wait Events.","danger":"safe"},"azure-sql":{"command":"az monitor metrics list --resource <db-resource-id> --metric \"cpu_percent\" --interval PT5M --start-time 2026-07-05T00:00:00Z --end-time 2026-07-05T01:00:00Z","note":"Metriken wie cpu_percent, dtu_consumption_percent, storage_percent und connection_successful kommen aus Azure Monitor. Für die Abfrageperformance die Dashboards Query Performance Insight und Intelligent Insights (Azure SQL Analytics) nutzen.","danger":"safe"},"mongodb-atlas":{"command":"atlas metrics processes <host>:<port> --granularity PT5M --period P1D --type SYSTEM_CPU_USER,QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED","note":"Messwerte auf Prozessebene werden mit 'atlas metrics processes' abgerufen, Plattenmesswerte mit 'atlas metrics disks'. Für Empfehlungen zu langsamen Abfragen Performance Advisor nutzen: 'atlas performanceAdvisor slowQueryLogs list' und 'atlas performanceAdvisor suggestedIndexes list'.","danger":"safe"}}},{"id":"maintenance-window","title":"Wartungsfenster und Update-Verwaltung","aliases":["maintenance","maintenance window","window","update","patch"],"providers":{"aws-rds":{"command":"aws rds modify-db-instance --db-instance-identifier <id> --preferred-maintenance-window Sun:03:00-Sun:04:00   # apply pending maintenance now: aws rds apply-pending-maintenance-action --resource-identifier <arn> --apply-action system-update --opt-in-type immediate","note":"Das Fenster ist in UTC im Format ddd:hh24:mi-ddd:hh24:mi angegeben und muss mindestens 30 Minuten lang sein. Ausstehende Wartung mit 'aws rds describe-pending-maintenance-actions' auflisten und mit apply-pending-maintenance-action sofort oder im nächsten Fenster anwenden.","danger":"caution"},"aws-aurora":{"command":"aws rds modify-db-cluster --db-cluster-identifier <cluster> --preferred-maintenance-window Sun:03:00-Sun:04:00","note":"In Aurora wird das Wartungsfenster auf Clusterebene mit modify-db-cluster und auf Instanzebene mit modify-db-instance gesetzt. Ausstehende Engine-/OS-Updates werden mit apply-pending-maintenance-action verwaltet; Upgrades der Engine-Version können Ausfallzeit erfordern.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --maintenance-window-day SUN --maintenance-window-hour 3 --maintenance-release-channel production","note":"Legt Tag (MON..SUN), Stunde (0-23, Ortszeit der Instanz/UTC) und Release Channel (production oder preview) fest. Mit den Flags --deny-maintenance-period-* lässt sich zudem ein Zeitraum definieren, in dem keine Wartung erfolgt.","danger":"caution"},"azure-sql":{"command":"az sql db update -g <rg> -s <server> -n <db> --maint-config-id SQL_Default   # or custom: /subscriptions/<sub>/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/SQL_<Region>_DB_1","note":"Das Wartungsfenster von Azure SQL wird verwaltet, indem eine vordefinierte Maintenance Configuration zugewiesen wird (Abendfenster an Wochentagen/am Wochenende statt des Standards). Konfigurationen mit 'az maintenance public-configuration list' auflisten.","danger":"caution"},"mongodb-atlas":{"command":"atlas maintenanceWindows update --dayOfWeek 1 --hourOfDay 3 --projectId <projectId>   # defer: atlas maintenanceWindows defer","note":"dayOfWeek 1=Sonntag ... 7=Samstag, hourOfDay 0-23 (UTC). Mit 'atlas maintenanceWindows defer' ausstehende Wartung um eine Woche verschieben, mit 'atlas maintenanceWindows start' sofort starten.","danger":"caution"}}},{"id":"encryption","title":"Verschlüsselungsstatus (at rest) anzeigen/konfigurieren","aliases":["encryption","encryption at rest","at-rest","kms","tde","cmek"],"providers":{"aws-rds":{"command":"aws rds describe-db-instances --db-instance-identifier <id> --query 'DBInstances[0].[StorageEncrypted,KmsKeyId]'   # at creation: aws rds create-db-instance ... --storage-encrypted --kms-key-id <kms-arn>","note":"Die At-Rest-Verschlüsselung von RDS lässt sich nur BEIM ERSTELLEN der Instanz aktivieren; für eine vorhandene unverschlüsselte Instanz eine verschlüsselte Kopie eines Snapshots erstellen und daraus wiederherstellen. Der Schlüssel wird von AWS KMS verwaltet.","danger":"caution"},"aws-aurora":{"command":"aws rds describe-db-clusters --db-cluster-identifier <cluster> --query 'DBClusters[0].[StorageEncrypted,KmsKeyId]'   # at creation: aws rds create-db-cluster ... --storage-encrypted --kms-key-id <kms-arn>","note":"In Aurora wird die Verschlüsselung auf Clusterebene und nur beim Erstellen festgelegt. Um einen vorhandenen unverschlüsselten Cluster zu verschlüsseln, einen Snapshot verschlüsselt kopieren und wiederherstellen. Alle Instanzen und Backups nutzen denselben KMS-Schlüssel.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances create <id> --disk-encryption-key projects/<project>/locations/<region>/keyRings/<ring>/cryptoKeys/<key>   # to view: gcloud sql instances describe <id> --format='value(diskEncryptionConfiguration.kmsKeyName)'","note":"Cloud SQL ist standardmäßig mit einem von Google verwalteten Schlüssel verschlüsselt; ein vom Kunden verwalteter Schlüssel (CMEK) kann nur beim Erstellen der Instanz mit --disk-encryption-key zugewiesen werden. Das Dienstkonto von Cloud SQL braucht auf dem Schlüssel die Berechtigung zum Ver-/Entschlüsseln.","danger":"caution"},"azure-sql":{"command":"az sql db tde set -g <rg> -s <server> -d <db> --status Enabled   # or T-SQL: ALTER DATABASE <db> SET ENCRYPTION ON;   # status: az sql db tde show -g <rg> -s <server> -d <db>","note":"In Azure SQL wird die At-Rest-Verschlüsselung durch Transparent Data Encryption (TDE) bereitgestellt und ist bei neuen Datenbanken standardmäßig aktiviert. Für einen eigenen Schlüssel 'az sql server tde-key set' (Azure Key Vault / BYOK) auf Serverebene konfigurieren.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters describe <name> --output json | jq '.encryptionAtRestProvider'   # enable BYOK: atlas encryptionAtRest enable AWS --awsKmsMasterKeyID <keyId> --awsKmsRegion <region> --awsKmsRoleID <roleId>","note":"In Atlas sind Daten standardmäßig at rest verschlüsselt. Zusätzlich wird Envelope Encryption mit einem Kundenschlüssel (BYOK) über AWS KMS / Azure Key Vault / GCP KMS projektweit mit 'atlas encryptionAtRest' aktiviert und auf den Cluster angewendet.","danger":"caution"}}},{"id":"storage-autoscale","title":"Speicher vergrößern / Autoscaling aktivieren","aliases":["storage","disk space","autoscale","grow","disk"],"providers":{"aws-rds":{"command":"aws rds modify-db-instance --db-instance-identifier <id> --allocated-storage 200 --max-allocated-storage 1000 --apply-immediately","note":"--allocated-storage setzt die aktuelle Größe; --max-allocated-storage setzt die Obergrenze für Storage Autoscaling. Das Vergrößern erfolgt online; zwischen Speicheränderungen müssen jedoch mindestens 6 Stunden liegen, und der Speicher lässt sich nicht verkleinern.","danger":"caution"},"aws-aurora":{"command":"# Aurora storage is automatic; no manual setting required\naws rds describe-db-clusters --db-cluster-identifier <cluster> --query 'DBClusters[0].AllocatedStorage'","note":"Aurora-Speicher skaliert vollautomatisch (in Schritten von 10 GB, je nach Engine bis 128 TB) und wächst mit der Nutzung; --allocated-storage / --max-allocated-storage werden nicht gesetzt. Du überwachst nur die Nutzung; eine Größenverwaltung ist nicht nötig.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --storage-auto-increase --storage-size 200GB   # upper limit: --storage-auto-increase-limit 1000","note":"--storage-auto-increase aktiviert automatisches Wachstum, wenn die Platte fast voll ist; --storage-auto-increase-limit setzt die Obergrenze (0 = unbegrenzt). Speicher lässt sich nur vergrößern, NICHT VERKLEINERN; Anfangsgröße und Limit sorgfältig wählen.","danger":"caution"},"azure-sql":{"command":"az sql db update -g <rg> -s <server> -n <db> --max-size 500GB","note":"In Azure SQL wird die maximale Datenbankgröße mit --max-size gesetzt; die Datenbank wächst innerhalb des zugewiesenen Platzes automatisch. Die maximale Größe hängt vom gewählten Service Tier bzw. vCore-Limit ab. Im Tier Hyperscale skaliert der Speicher automatisch und nahezu unbegrenzt.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters update <name> --diskSizeGB 200   # for autoscaling, diskGBEnabled=true via extended configuration (advancedSettings / API)","note":"Die Plattengröße wird mit --diskSizeGB erhöht. In Atlas ist Storage Autoscaling (Auto-scale storage) bei Clustern ab M10 standardmäßig aktiviert; die Platte wächst automatisch, wenn sie zu 90% voll ist. Aktivieren/Deaktivieren erfolgt in der Clusterkonfiguration (autoScaling.diskGBEnabled).","danger":"caution"}}}]},"guide/best-practices.json":{"topics":[{"id":"indexing-strategy","title":"Indexierungsstrategie","category":"Performance","summary":"Indizes beschleunigen Leseabfragen, verursachen aber Schreib- und Speicherkosten; nach Bedarf und Messung anlegen.","points":["WHERE-, JOIN- und ORDER BY-Spalten ansteuern; in einem zusammengesetzten Index die selektivste Spalte zuerst (Leftmost-Prefix-Regel).","Covering Indexes verwenden: alle von der Abfrage benötigten Spalten in den Index aufnehmen, um den Tabellenzugriff (Lookup) zu vermeiden.","Ungenutzte und redundante Indizes (Duplikate oder Präfixe eines anderen Index) identifizieren und löschen; jeder Index verlangsamt INSERT/UPDATE/DELETE.","Eigenständige B-Tree-Indizes auf Spalten mit niedriger Kardinalität (z. B. Geschlecht, Boolean) vermeiden; bei Bedarf einen Partial/Filtered Index verwenden.","Mit EXPLAIN/EXPLAIN ANALYZE prüfen, ob der Index tatsächlich genutzt wird; eine Funktion oder Typkonvertierung macht den Index unbrauchbar (wie bei WHERE UPPER(name))."],"level":"junior"},{"id":"normalization","title":"Normalisierung vs. Denormalisierung","category":"Design","summary":"Normalisierung sichert Datenintegrität ohne Redundanz; Denormalisierung ist ein bewusster Kompromiss zugunsten der Leseleistung.","points":["In OLTP-Systemen standardmäßig 3NF anstreben; Datenredundanz und Update-Anomalien verhindern.","Nur bei einem gemessenen Performance-Engpass denormalisieren, und zwar begründet.","Konsistenz denormalisierter Daten mit Triggern, Materialized Views oder der Anwendungsschicht sicherstellen.","Für Reporting-/Analyse-Workloads ein separates denormalisiertes Schema (Star Schema) oder ein Lesereplikat erwägen.","Vorzeitige Denormalisierung ist technische Schuld; das Problem zuerst mit dem richtigen Index und der richtigen Abfrage lösen."],"level":"senior"},{"id":"data-types","title":"Den richtigen Datentyp wählen","category":"Design","summary":"Der korrekte und kleinstmögliche Datentyp verbessert direkt Speicherbedarf, Indexgröße und Performance.","points":["Den kleinsten Typ wählen, der den Bedarf deckt: SMALLINT für das Alter, DECIMAL/NUMERIC für Geldbeträge (niemals FLOAT).","Zeitangaben immer mit Zeitzone speichern (TIMESTAMPTZ / UTC); keine lokale Zeit speichern.","Für feste, begrenzte Wertemengen ein ENUM oder eine Lookup-Tabelle verwenden; einschränken statt Freitext zuzulassen.","Für Text variabler Länge nach dem tatsächlichen Bedarf dimensionieren oder TEXT statt eines unnötig großen VARCHAR(255) verwenden.","Bei UUID als PK auf Indexfragmentierung achten; sequenzielle UUIDs (v7) oder BIGINT Identity erwägen.","NULL-Semantik bewusst wählen; den Unterschied zwischen 'unbekannt' und 'leer' im Design explizit machen."],"level":"junior"},{"id":"naming-conventions","title":"Namenskonventionen","category":"Design","summary":"Konsistente Benennung erleichtert Lesbarkeit, Wartung und Kommunikation im Team; den Standard von Anfang an festlegen.","points":["Eine Konvention wählen und überall anwenden: snake_case ist verbreitet und robust gegenüber Problemen mit Groß-/Kleinschreibung.","Singular oder Plural für Tabellennamen festlegen; aussagekräftige, nicht abgekürzte Spaltennamen bevorzugen.","Foreign Keys so benennen, dass die Quelle erkennbar ist (z. B. customer_id), und Indizes/Constraints mit Standardpräfixen versehen (fk_, ix_, uq_).","Reservierte Wörter (order, user, group) nicht als Tabellen-/Spaltennamen verwenden.","Boolean-Spalten mit den Präfixen is_/has_ kenntlich machen; Datumsspalten konsistente Suffixe wie _at, _date geben."],"level":"junior"},{"id":"partitioning-strategy","title":"Partitionierungsstrategie","category":"Performance","summary":"Partitionierung teilt große Tabellen in handhabbare Teile; sie verbessert Wartung, Bereinigung und Abfrageleistung.","points":["Den Partitionsschlüssel nach Abfrage- und Archivierungsmustern wählen; datumsbasierte RANGE-Partitionierung ist bei Zeitreihen am häufigsten.","Prüfen, ob Partition Pruning funktioniert: Abfragen müssen den Partitionsschlüssel in der WHERE-Klausel enthalten, sonst werden alle Partitionen gescannt.","Alte Daten nicht löschen, sondern per Partition DROP/DETACH sofort und nahezu ohne Locks entfernen.","Eine übermäßige Anzahl an Partitionen erhöht Planungs- und Metadatenkosten; eine sinnvolle Granularität wählen (monatlich/wöchentlich).","Einen Wartungsjob einrichten, der künftige Partitionen automatisch anlegt; Fehler durch Schreibvorgänge ohne passende Partition verhindern."],"level":"senior"},{"id":"connection-pooling","title":"Connection Pooling","category":"Betrieb","summary":"Ein Connection Pool spart die teuren Kosten des Verbindungsaufbaus und schützt die Datenbank vor Verbindungsfluten.","points":["Nicht pro Anfrage eine neue Verbindung öffnen; einen Pool in der Anwendung oder einer Zwischenschicht nutzen (PgBouncer, HikariCP).","Den Pool nach CPU-Kernen und Last dimensionieren; ein überdimensionierter Pool würgt die Datenbank ab (ein kleiner Pool ist meist besser).","Werte für Verbindungslebensdauer, Idle- und Acquire-Timeout bewusst setzen; Leak Detection aktivieren.","Den Pool gegen das Limit max_connections der Datenbank planen; die Summe über mehrere Anwendungsinstanzen berücksichtigen.","Kurze Transaktionen verwenden, damit Verbindungen schnell in den Pool zurückkehren; innerhalb einer Transaktion nicht auf externe I/O warten."],"level":"senior"},{"id":"transaction-design","title":"Transaktionsdesign (kurz und atomar)","category":"Performance","summary":"Transaktionen sollten kurz und atomar sein und eine einzige logische Arbeitseinheit umfassen; das minimiert die Lock-Dauer.","points":["Transaktionen so kurz wie möglich halten; keine Benutzerinteraktion, externen API-Aufrufe oder langen Berechnungen darin einschließen.","Nur die Vorgänge in einer Transaktion bündeln, die gemeinsam gelingen oder scheitern müssen.","Zeilen immer in derselben Reihenfolge aktualisieren, um Lock-Konflikte zu verringern (Grundlage der Deadlock-Vermeidung).","Unnötig breite Locks vermeiden; reine Lesevorgänge nicht in unnötige Transaktionsblöcke packen.","Einen klaren Rollback-Pfad für Fehler definieren; keine teilweisen Commits oder offenen Transaktionen zurücklassen.","Lange Massenvorgänge in Batches committen; eine einzige riesige Transaktion verursacht WAL-/Undo-Bloat."],"level":"senior"},{"id":"isolation-levels","title":"Isolation Level wählen","category":"Performance","summary":"Der Isolation Level bestimmt das Gleichgewicht zwischen Konsistenz und Parallelität; ihn bewusst anhand der Geschäftsregeln wählen.","points":["Die Anomalietypen kennen: Dirty Read, Non-Repeatable Read, Phantom Read; verstehen, welcher Level welche verhindert.","Read Committed reicht für die meisten OLTP-Fälle; nur bei echtem Bedarf höher gehen.","Für kritische Geld-/Bestandsvorgänge Serializable oder explizites Locking (SELECT ... FOR UPDATE) verwenden.","Unter Serializable eine automatische Wiederholungslogik für Serialisierungsfehler einbauen.","Den Verhaltensunterschied zwischen MVCC- und Lock-basierten Engines kennen; derselbe Level kann auf verschiedenen Engines unterschiedliche Garantien bieten."],"level":"expert"},{"id":"deadlock-prevention","title":"Deadlock-Vermeidung","category":"Performance","summary":"Ein Deadlock entsteht, wenn Transaktionen gegenseitig auf ihre Ressourcen warten; er lässt sich durch Design weitgehend verhindern.","points":["Auf Ressourcen/Zeilen immer in einer konsistenten, identischen Reihenfolge zugreifen; Unterschiede in der Reihenfolge sind die häufigste Deadlock-Quelle.","Transaktionen kurz halten und die Anzahl der Locks verringern; unnötig breite Updates vermeiden.","Bei Bedarf Konflikte durch frühes Locking (SELECT ... FOR UPDATE) vorhersehbar machen.","Deadlock-Fehler in der Anwendung abfangen und die Transaktion automatisch wiederholen; Deadlocks ganz zu beseitigen ist oft unmöglich.","Deadlock-Logs/-Graphen regelmäßig prüfen; wiederkehrende Muster durch Änderungen an Indizes oder Zugriffsreihenfolge beseitigen."],"level":"expert"},{"id":"sharding-strategy","title":"Sharding / Strategie zur horizontalen Skalierung","category":"Design","summary":"Sharding überwindet die Grenzen eines einzelnen Servers, indem Daten auf mehrere Knoten verteilt werden; da es erhebliche Komplexität mit sich bringt, sollte es das letzte Mittel sein.","points":["Zuerst vertikale Skalierung, Indizierung, Caching und Lesereplikate ausschöpfen; nur bei echtem Bedarf sharden.","Den Shard Key sorgfältig wählen: wenige shardübergreifende Abfragen und eine gleichmäßige Verteilung, ohne Hotspots.","Für das Rebalancing (Resharding) Consistent Hashing oder virtuelle Shards verwenden; Wachstum von Anfang an einplanen.","Shardübergreifende JOINs und verteilte Transaktionen sind teuer; das Schema so entwerfen, dass sie minimiert werden.","Die Erzeugung global eindeutiger IDs (sequenzielle IDs im Snowflake-Stil usw.) und die Shard-Routing-Schicht lösen."],"level":"expert"},{"id":"backup-strategy","title":"Backup-Strategie (3-2-1)","category":"Backup & HA","summary":"Eine solide Backup-Strategie ist die letzte Verteidigungslinie gegen Katastrophen; ein Backup zählt erst, wenn es getestet wurde.","points":["Die 3-2-1-Regel anwenden: 3 Kopien, 2 verschiedene Medien, 1 Kopie außer Haus (Offsite/andere Region).","Full- und inkrementelle Backups mit WAL-/Binlog-Archivierung kombinieren, um Point-in-Time-Recovery (PITR) zu ermöglichen.","Backups regelmäßig testen, indem du sie TATSÄCHLICH wiederherstellst; ein ungetestetes Backup ist kein Backup.","Backups sowohl at rest als auch in transit verschlüsseln; den Zugriff einschränken und die Aufbewahrungsdauer per Richtlinie festlegen.","Eine unveränderliche (immutable) oder air-gapped Kopie gegen Ransomware vorhalten."],"level":"junior"},{"id":"hadr-design","title":"HA/DR-Design und RPO/RTO","category":"Backup & HA","summary":"Das Design für Hochverfügbarkeit und Disaster Recovery beginnt mit der Festlegung der Geschäftsziele als Kennzahlen RPO und RTO.","points":["Ziele für RPO (akzeptabler Datenverlust) und RTO (akzeptable Ausfallzeit) gemeinsam mit dem Fachbereich festlegen.","Synchrone Replikation liefert ein RPO nahe null, erhöht aber die Latenz; asynchrone ist performant, birgt aber ein Risiko von Datenverlust.","Automatisches Failover einrichten und mit regelmäßigen Failover-Übungen prüfen, ob es tatsächlich funktioniert.","Einen Quorum-/Witness-Mechanismus gegen Split-Brain einsetzen; niemals zulassen, dass beide Knoten Master werden.","Die DR-Umgebung in einer anderen physischen Region betreiben; den Ausfall einer einzelnen Region bzw. eines Rechenzentrums verkraften."],"level":"expert"},{"id":"capacity-planning","title":"Kapazitätsplanung und Wachstum","category":"Betrieb","summary":"Kapazitätsplanung verhindert überraschende Ausfälle, indem Ressourcen dem Wachstumstrend voraus bereitgestellt werden.","points":["Datenwachstum, IOPS, CPU, Speicher und Verbindungsnutzung über die Zeit messen und Trends ableiten.","Frühe Schwellenwert-Alerts für die Plattennutzung setzen (z. B. 75%/85%); handeln, bevor der Platz ausgeht.","Wachstum anhand von Prognosen planen; Spitzenlast und saisonale Schwankungen berücksichtigen.","Anzahl der Verbindungen, Table-/Index-Bloat und Archivierungsbedarf regelmäßig prüfen.","Den Skalierungsweg (vertikal, Replikate, Partitionierung/Sharding) festlegen, bevor der Engpass erreicht ist."],"level":"senior"},{"id":"migration-strategy","title":"Migrationsstrategie (minimale Ausfallzeit)","category":"Betrieb","summary":"Schema- und Datenmigrationen sollten einem umkehrbaren Plan folgen, der die Ausfallzeit minimiert, wo möglich online.","points":["Jede Migration umkehrbar und idempotent gestalten; nie ohne Rollback-Plan in Produktion gehen.","Blockierende DDL auf großen Tabellen vermeiden; Online-Schema-Change-Tools nutzen (PG: pg-osc/pgroll; MySQL: gh-ost, pt-osc).","Für rückwärts-/vorwärtskompatible Änderungen das Expand-Contract-Muster anwenden: erst hinzufügen, deployen, dann das Alte entfernen.","Große Datenmengen in kleinen Batches verschieben; keinen Lock- und WAL-Bloat mit einem einzigen riesigen UPDATE erzeugen.","Die Migration zuerst in einer produktionsnahen Umgebung mit realistischem Datenvolumen proben und ihre Dauer messen.","Zeitpunkt der Umstellung, Validierungsschritte und Rollback-Entscheidung vorab in einem schriftlichen Plan festhalten."],"level":"senior"},{"id":"schema-versioning","title":"Schema-Versionierung (Flyway/Liquibase)","category":"Betrieb","summary":"Schemaänderungen sollten mit Migrationen verwaltet werden, die beim Code liegen, versioniert sind und automatisch angewendet werden; manuelle Eingriffe sind fehleranfällig.","points":["Alle Schemaänderungen mit Tools wie Flyway/Liquibase versionieren und in der Versionsverwaltung halten.","DDL in Produktion nicht manuell ausführen; jede Änderung muss über eine geprüfte Migrationsdatei laufen.","Migrationen geordnet und eindeutig halten; eine angewendete Migrationsdatei nie ändern, sondern mit einer neuen Migration korrigieren.","Migrationen in der CI/CD-Pipeline automatisch anwenden und validieren; Schemakonsistenz über alle Umgebungen sicherstellen.","Anwendungscode und Schemaversion im Gleichschritt weiterentwickeln; für rückwärtskompatible Übergänge das Expand-Contract-Muster nutzen."],"level":"senior"},{"id":"least-privilege","title":"Prinzip der minimalen Rechte","category":"Sicherheit","summary":"Jeder Benutzer und jede Anwendung sollte nur die für die eigene Aufgabe nötigen Rechte haben; überzählige Rechte vergrößern die Angriffsfläche.","points":["Für die Anwendung separate Konten mit eingeschränkten Rechten verwenden; nie als Superuser/Admin verbinden.","Rechte an Rollen statt direkt an Benutzer vergeben; den Zugriff über Rollenmitgliedschaften steuern.","Nur die benötigten Tabellen und Vorgänge erlauben (SELECT/INSERT/UPDATE); breites GRANT ALL vermeiden.","Rechte regelmäßig prüfen und ungenutzte entziehen; den Zugriff sofort entfernen, wenn ein Mitarbeiter/Dienst ausscheidet.","Secrets (Passwörter, Connection Strings) nicht in den Code einbetten; einen Secrets Manager (Vault) nutzen und sie regelmäßig rotieren."],"level":"junior"},{"id":"encryption-basics","title":"Grundlagen der Verschlüsselung (at rest/in transit)","category":"Sicherheit","summary":"Daten sollten sowohl bei der Speicherung als auch bei der Übertragung verschlüsselt sein; Verschlüsselung ist ein Grundschutz für Compliance und gegen Datenlecks.","points":["TLS bei der Übertragung erzwingen; unverschlüsselte Verbindungen ablehnen und die Zertifikatsprüfung aktivieren.","At-Rest-Verschlüsselung (TDE oder Platten-/Volume-Verschlüsselung) anwenden, damit eine gestohlene Platte bzw. ein gestohlenes Backup nicht lesbar ist.","Schlüsselverwaltung ernst nehmen: Schlüssel in einem separaten KMS/HSM halten und regelmäßig rotieren.","Hochsensible Felder wie Kreditkartennummern und Personalausweisnummern zusätzlich in der Anwendungsschicht verschlüsseln (Field-Level).","Auch Backups verschlüsseln; den Schutz der Produktion auf Backups und Replikate ausweiten.","Sensible Daten in Test-/Entwicklungsumgebungen maskieren oder anonymisieren (Data Masking)."],"level":"senior"},{"id":"monitoring-basics","title":"Grundlagen des Monitorings (Golden Signals)","category":"Betrieb","summary":"Effektives Monitoring bedeutet, die richtigen Metriken zu sammeln und bei aussagekräftigen Schwellenwerten zu alarmieren; es erkennt Probleme, bevor Nutzer sie bemerken.","points":["Die Golden Signals überwachen: Latenz, Traffic, Fehlerrate und Sättigung.","Datenbankspezifische Metriken ergänzen: langsame Abfragen, Lock-/Wait-Zeiten, Anzahl der Verbindungen, Cache Hit Ratio, Replication Lag.","Alerts an Auswirkung und umsetzbare Schwellenwerte statt an Symptome knüpfen; laute Alerts führen zu Alert Fatigue.","Slow Query Log und Abfragestatistiken (wie pg_stat_statements) dauerhaft aktiviert lassen und regelmäßig auswerten.","Metriken historisch aufbewahren; eine Baseline für Trends und Kapazitätsplanung aufbauen.","Bei kritischen Zuständen wie verbleibendem Plattenplatz und Backup-Erfolg immer alarmieren."],"level":"junior"},{"id":"query-design","title":"Abfragedesign und Vermeidung von N+1","category":"Performance","summary":"Eine effiziente Abfrage holt nur die benötigten Daten mit minimalen Round Trips; N+1 ist der häufigste und tückischste Performance-Fehler.","points":["Statt SELECT * nur die benötigten Spalten auswählen; das verbessert Netzwerk, Speicher und Chancen auf Covering Indexes.","Das N+1-Problem verhindern: statt Einzelabfragen in einer Schleife einen JOIN oder eine Batch-Abfrage (IN) verwenden; im ORM Eager Loading aktivieren.","Große Ergebnismengen paginieren; bei großen Datenmengen Keyset-(Seek-)Pagination statt OFFSET bevorzugen.","Geschäftslogik möglichst der Abfrage überlassen; nicht die ganze Tabelle holen und in der Anwendung filtern/aggregieren.","Tatsächlichen Plan und Zeilenschätzungen mit EXPLAIN ANALYZE untersuchen; Full Table Scans und Fehlschätzungen beseitigen.","Häufige, teure Aggregationsabfragen mit einer Materialized View oder einer vorberechneten Summary-Tabelle unterlegen."],"level":"junior"},{"id":"large-table-management","title":"Verwaltung großer Tabellen (Archivierung/Partitionierung)","category":"Performance","summary":"Stetig wachsende Tabellen verlangsamen mit der Zeit Abfragen, Wartung und Backups; sie mit Archivierung und Partitionierung unter Kontrolle halten.","points":["Heiße (aktive) und kalte (historische) Daten trennen; alte Daten in eine Archivtabelle oder auf günstigen Speicher verschieben.","Die Tabelle nach Datum/Bereich partitionieren; alte Daten per Partition DROP statt DELETE sofort entfernen.","Große Löschungen/Updates in kleinen Batches durchführen; ein einziges riesiges DELETE erzeugt Locks, WAL-Bloat und Table Bloat.","Bloat und Tabellen-/Indexgröße regelmäßig überwachen; bei Bedarf online reorganisieren (VACUUM/Reorg).","Indizes behalten, die zu häufigen Abfragen passen; unnötige Indizes auf einer großen Tabelle zu pflegen ist sehr teuer."],"level":"senior"},{"id":"caching-strategy","title":"Caching-Strategie","category":"Performance","summary":"Ein Cache senkt Latenz und Last, indem er wiederholte Lesezugriffe von der Datenbank fernhält; die eigentliche Herausforderung ist die Invalidierung.","points":["Den Cache für teure, häufig gelesene, selten geänderte Daten nutzen; nicht versuchen, alles zu cachen.","Eine Invalidierungsstrategie wählen: TTL, Aktualisierung beim Schreiben (Write-Through) oder Löschen beim Schreiben (Write-Invalidate).","Die Toleranz für veraltete Daten anhand der Geschäftsregeln festlegen; wo strikte Konsistenz nötig ist, sich nicht auf den Cache verlassen.","Cache Stampedes (viele Anfragen füllen einen leeren Cache gleichzeitig) mit Locking oder Request Coalescing verhindern.","Die Cache Hit Ratio überwachen; eine niedrige Hit Ratio zeigt, dass der Cache keinen Nutzen bringt.","Ein Cache ist eine Beschleunigungsschicht, kein dauerhafter Zustand; das System muss auch bei Verlust des Cache korrekt weiterarbeiten."],"level":"senior"},{"id":"statistics-maintenance","title":"Disziplin bei Statistiken und Wartung","category":"Betrieb","summary":"Aktuelle Statistiken und regelmäßige Wartung sorgen dafür, dass der Query Planner richtige Entscheidungen trifft und die Performance mit der Zeit nicht nachlässt.","points":["Statistiken aktuell halten (ANALYZE / Auto-Analyze); veraltete Statistiken drängen den Planner zu einem falschen Plan.","VACUUM/Bereinigung regelmäßig ausführen, um tote Zeilen und Bloat zu entfernen; bei MVCC-Engines ist das kritisch.","Indexfragmentierung überwachen und bei Bedarf online neu aufbauen (REINDEX/Rebuild).","Statistiken nach einem Bulk Load manuell aktualisieren; verhindern, dass der Planner mit der alten Verteilung arbeitet.","Wartungsjobs in verkehrsarmen Zeitfenstern planen, Dauer/Auswirkung überwachen und automatisieren.","Auf Plan-Regressionen achten (eine Abfrage wird plötzlich langsam); die Aktualität der Statistiken ist das Erste, was zu prüfen ist."],"level":"senior"}]},"guide/best-practices-2.json":{"topics":[{"id":"iac","title":"Infrastruktur als Code (IaC) verwalten","category":"Betrieb","summary":"Datenbankserver, Netzwerk und Konfiguration sollten in versioniertem Code statt von Hand definiert werden, damit Umgebungen reproduzierbar, prüfbar und umkehrbar sind.","points":["Definitionen für Server, Parameter Groups, Netzwerk und Benutzer als Code mit Tools wie Terraform/Pulumi/CloudFormation in der Versionsverwaltung halten.","Manuelle Änderungen in der Konsole (Click-Ops) verbieten; Drift Detection ausführen und vom Code abweichende Ressourcen korrigieren.","Unterschiede zwischen Umgebungen (dev/stage/prod) über Variablen verwalten; produktionsnahe Umgebungen aus demselben Modul bauen.","Die State-Datei remote und gesperrt halten (z. B. S3 + DynamoDB-Lock); keine Secrets als Klartext in den State gelangen lassen.","Änderungen über einen Plan/Apply-Ablauf prüfen; vor die Produktion Freigabeschritte setzen."],"level":"senior"},{"id":"automation-idempotency","title":"Automatisierung und Idempotenz","category":"Betrieb","summary":"Betriebsaufgaben sollten von manuell wiederholten Befehlen in idempotente Automatisierung überführt werden, die bei beliebig vielen Läufen dasselbe Ergebnis liefert.","points":["Jedes Skript idempotent schreiben, sodass eine erneute Ausführung weder fehlschlägt noch den Zustand beschädigt (z. B. CREATE IF NOT EXISTS statt CREATE).","Wiederkehrende Wartungsaufgaben (Backup, Bereinigung, Statistiken) an geplante, protokollierte Automatisierung binden; nicht manuellen Läufen überlassen.","Explizite Vorbedingungsprüfungen und sichere Abbrüche in Skripte einbauen; ein erneuter Lauf eines halb fertigen Vorgangs sollte ihn dort fortsetzen, wo er stehen blieb.","Automatisierung aus Code ausführen (Ansible/Skripte + CI); nicht davon abhängen, dass jemand in Produktion Befehle in ein Terminal tippt.","Für kritische Vorgänge Dry-Run-Unterstützung bieten; die Auswirkung vor dem Anwenden zeigen."],"level":"senior"},{"id":"observability","title":"Observability (Metriken/Logs/Traces)","category":"Betrieb","summary":"Observability kombiniert Metriken, Logs und verteilte Traces, damit du von „was ist passiert“ zu „warum ist es passiert“ gelangst.","points":["Alle drei Signale gemeinsam erfassen: Metriken (Trends), Logs (Kontext), Traces (Anfragepfad); keines allein zeigt die Ursache.","Abfragen Ende-zu-Ende verfolgen: Anwendungs-Traces mit der Datenbanklatenz korrelieren (mit Standards wie OpenTelemetry).","Strukturierte Logs (JSON) mit Correlation-/Trace-ID ausgeben; Freitext-Logs erschweren die Suche.","Labels mit hoher Kardinalität vermeiden; Kardinalitätsexplosion und Kosten unter Kontrolle halten.","SLOs definieren und das Error Budget verfolgen; Observability an Geschäftsziele knüpfen."],"level":"senior"},{"id":"alerting-strategy","title":"Alerting-Strategie (rauscharm, umsetzbar)","category":"Betrieb","summary":"Eine gute Alerting-Strategie weckt einen Menschen nur bei echten, umsetzbaren Zuständen; Rauschen führt zu Alert Fatigue und Burnout.","points":["Bei Auswirkung und Nutzererlebnis (SLO-Verletzungen) statt bei Symptomen alarmieren; nicht bei jeder Metrikschwankung pagen.","Jeder Alert braucht einen klaren Owner, eine Bedingung und ein Runbook; ein Alert ohne definierte Aktion ist wertlos.","Hysterese und Dauer (for: 5m) bei Schwellenwerten verwenden; verhindern, dass kurzzeitige Spitzen Alerts auslösen.","Paging von Benachrichtigungen (Ticket/E-Mail) trennen; nur für das pagen, was sofortiges Eingreifen erfordert.","Alerts regelmäßig prüfen; ständig auslösende und ignorierte Alerts korrigieren oder entfernen.","Stille Katastrophen wie fehlgeschlagene Backups, volle Platten und gestoppte Replikation immer abdecken."],"level":"senior"},{"id":"dr-testing","title":"Disziplin bei DR-/Failover-Übungen","category":"Backup & HA","summary":"Ein Disaster-Recovery-Plan ist erst verlässlich, wenn er mit regelmäßigen, realistischen Übungen validiert wurde; ein ungetesteter Plan ist nur eine Annahme.","points":["Failover- und Restore-Übungen einplanen und regelmäßig durchführen; ein einmal im Jahr gesichteter Papierplan reicht nicht.","Bei jeder Übung das tatsächliche RTO und RPO messen; die Abweichung vom Ziel berichten und nachbessern.","Geplantes Failover in einer produktionsnahen Umgebung mit echtem Traffic proben; nicht nur den Standby starten und stoppen.","Szenarien wie Regionsausfall, Plattenausfall und Datenkorruption in Game Days simulieren.","Nach jeder Übung Erkenntnisse im Runbook festhalten; aus dem Gedächtnis ausgeführte Schritte in Automatisierung überführen."],"level":"expert"},{"id":"blue-green-deploy","title":"Blue-Green / Deployment ohne Ausfallzeit","category":"Betrieb","summary":"Blue-Green- und Canary-Deployments betreiben die neue Version parallel zur alten und verlagern den Traffic schrittweise, was Ausfallzeit und Risiko minimiert.","points":["Zwei Produktionsumgebungen (Blue/Green) betreiben; die neue Version in die inaktive deployen, validieren und dann den Traffic in einem Schritt umschalten.","Geht etwas schief, den Traffic sofort auf die alte Umgebung zurückschalten; ein Rollback sollte kein erneutes Deployment erfordern.","Bei Canary einen kleinen Prozentsatz des Traffics an die neue Version senden, die Metriken beobachten und schrittweise erhöhen.","Das Datenbankschema vom Deployment entkoppeln: beide Codeversionen müssen mit demselben Schema funktionieren (Expand-Contract).","Health-Checks und Schwellen für automatisches Rollback vorab festlegen; die Wartezeit auf manuelle Entscheidungen verkürzen."],"level":"senior"},{"id":"expand-contract","title":"Rückwärtskompatible Schemaänderungen (Expand-Contract)","category":"Design","summary":"Das Expand-Contract-Muster teilt eine Schemaänderung in Hinzufügen (Expand), dann Migrieren und schließlich Entfernen des Alten (Contract) auf und ermöglicht so einen Übergang ohne Ausfallzeit.","points":["Zuerst erweitern: die neue Spalte/Tabelle rückwärtskompatibel hinzufügen (nullable oder mit Default), ohne die alte zu brechen.","Die Anwendung so deployen, dass sie per Dual-Write oder Dual-Read mit dem alten und dem neuen Schema funktioniert.","Daten im Hintergrund in kleinen Batches in die neue Struktur migrieren und validieren; nicht mit einer riesigen Migration sperren.","Erst zurückbauen, wenn alle Instanzen auf die neue Version umgestellt sind und die Validierung abgeschlossen ist: die alte Spalte bzw. den alten Code entfernen.","Jeden Schritt in einem separaten Deployment ausführen; nie „hinzufügen und das Alte gleichzeitig löschen“, sonst wird ein Rollback unmöglich.","NOT NULL und andere Constraints zuletzt hinzufügen, sobald die Daten konsistent sind."],"level":"expert"},{"id":"secrets-management","title":"Secrets Management (Passwörter/Schlüssel)","category":"Sicherheit","summary":"Datenbankpasswörter, API-Schlüssel und Connection Strings dürfen nicht in Code oder Konfiguration eingebettet sein; sie sollten mit einem zentralen Secrets Manager sicher gespeichert und rotiert werden.","points":["Secrets nicht in Code, Repositories, env-Dateien oder Images einbetten; einen Secret Store wie Vault, AWS Secrets Manager oder KMS verwenden.","Secrets zur Laufzeit in Anwendungen injizieren; nicht als Klartext auf der Platte liegen lassen.","Passwörter und Schlüssel regelmäßig rotieren; wo möglich kurzlebige, automatisch erzeugte Zugangsdaten verwenden.","Zugriff mit minimalen Rechten und Audit Log gewähren; es muss nachvollziehbar sein, wer wann auf welches Secret zugegriffen hat.","Die Codebasis nach geleakten Secrets durchsuchen (git-secrets, trufflehog); ein geleaktes Secret sofort widerrufen und rotieren.","Menschlichen Zugriff temporär und freigabebasiert (Just-in-Time) statt persönlich und dauerhaft gestalten."],"level":"senior"},{"id":"cost-optimization","title":"Optimierung der Cloud-Kosten","category":"Betrieb","summary":"Die Kosten einer Cloud-Datenbank lassen sich ohne Performance-Einbußen durch Right-Sizing, Storage Tiering und Bereinigung ungenutzter Ressourcen deutlich senken.","points":["Instanzen anhand tatsächlicher Nutzungsmetriken dimensionieren; Server mit dauerhaft niedriger Auslastung verkleinern (Right-Sizing).","Für vorhersehbare Grundlast reservierte Kapazität/Savings Plans nutzen; variable Last On-Demand belassen.","Kalte Daten auf ein günstigeres Storage Tier verschieben; heiße Daten von selten genutzten trennen.","Ungenutzte Ressourcen bereinigen: verwaiste Snapshots, ungenutzte Replikate und vergessene Testdatenbanken sind Kostenlecks.","Kosten über Tags Verantwortlichen zuordnen und Budget-Alerts einrichten; Überraschungen sofort sehen, nicht erst am Monatsende.","Backup-Aufbewahrung und Anzahl der Replikate gegen den Geschäftsbedarf prüfen; ein Zuviel ist Risiko und Kostenfaktor zugleich."],"level":"senior"},{"id":"multi-tenancy","title":"Mandantenfähiges Design (Multi-Tenant)","category":"Design","summary":"In mandantenfähigen Systemen ist das Isolationsmodell (gemeinsame Tabelle, separates Schema, separate Datenbank) ein bewusster Kompromiss zwischen Sicherheit, Kosten und Skalierbarkeit.","points":["Das Isolationsmodell wählen: gemeinsame Tabelle (tenant_id), Schema pro Mandant oder Datenbank pro Mandant; jedes hat ein anderes Verhältnis von Kosten zu Isolation.","Im gemeinsamen Modell jede Abfrage mit einem tenant_id-Filter versehen; mit Row-Level Security (RLS) auf Engine-Ebene sicherstellen, dass nichts durchsickert.","tenant_id in den relevanten Indizes an die erste Stelle setzen; mandantenübergreifende Scans und Hotspots verhindern.","Ressourcenlimits und Quoten gegen den Noisy-Neighbor-Effekt anwenden; ein Mandant darf die anderen nicht ausbremsen.","Von Anfang an einen Weg (Mandantenmigration) vorsehen, um große oder Premium-Mandanten in ein separates Schema bzw. eine separate Datenbank zu verlagern.","Backup, Löschung und Datenportabilität pro Mandant mitdenken; Löschanfragen nach KVKK/DSGVO müssen erfüllbar sein."],"level":"expert"},{"id":"time-series-design","title":"Design für Zeitreihendaten","category":"Design","summary":"Zeitreihendaten sind wegen hohen Schreibvolumens und zeitbasierter Abfragemuster besonders; sie sollten mit datumsbasierter Partitionierung, Rollups und einer Aufbewahrungsrichtlinie entworfen werden.","points":["Daten mit zeitstempelbasierter RANGE-Partitionierung aufteilen; alte Daten günstig per Partition DROP entfernen.","Rohdaten nicht einfach anhäufen, sondern Zusammenfassungen in regelmäßigen Intervallen erzeugen (Rollup/Continuous Aggregate); Abfragen schnell auf den Zusammenfassungen laufen lassen.","Zeit- und Tag-Komponenten (Tag/Quelle) im Index an die erste Stelle setzen; das typische Abfragemuster ist „bestimmte Quelle, bestimmter Bereich“.","Das richtige Werkzeug wählen: bei hohem Volumen können Zeitreihen-Engines wie TimescaleDB und ClickHouse weit effizienter sein als relationale.","Die Aufbewahrungsrichtlinie von Anfang an festlegen; Rohdaten kurz, aggregierte Daten lange aufbewahren.","Zeit immer in UTC speichern; Ortszeit und Sommerzeitwechsel in der Abfrageschicht behandeln."],"level":"senior"},{"id":"event-sourcing","title":"Grundlagen von Event Sourcing / CQRS","category":"Design","summary":"Event Sourcing speichert nicht den Zustand, sondern die Ereignisse, die ihn ändern; CQRS trennt Lese- und Schreibmodell, sodass beide unabhängig skalieren.","points":["Statt den aktuellen Zustand zu überschreiben, einen unveränderlichen Ereignisstrom anhäufen; den aktuellen Zustand durch Abspielen der Ereignisse ableiten.","Mit CQRS das Schreibmodell (Commands) vom Lesemodell (Queries) trennen; die Leseseite mit denormalisierten Projektionen beschleunigen.","Das Ereignisschema versionieren und rückwärtskompatibel weiterentwickeln; alte Ereignisse nie löschen, sondern per Upcasting in die neue Version überführen.","Akzeptieren, dass Leseprojektionen eventually consistent sind, und die Verzögerung im Design berücksichtigen.","Snapshots erstellen, um nicht jedes Mal einen langen Ereignisstrom von Anfang an abspielen zu müssen.","Dieses Muster nicht überall einsetzen, sondern in Domänen, die einen Audit Trail, komplexe Geschäftsregeln und Historie über die Zeit brauchen; die mitgebrachte Komplexität nicht unterschätzen."],"level":"expert"},{"id":"data-retention","title":"Richtlinie zu Datenaufbewahrung und Archivierung","category":"Betrieb","summary":"Nicht alle Daten werden ewig aufbewahrt; eine definierte Aufbewahrungs- und Archivierungsrichtlinie nach rechtlichen, fachlichen und Kostenanforderungen sorgt für Compliance und Performance.","points":["Die Aufbewahrungsdauer jedes Datentyps nach fachlichen und rechtlichen Anforderungen (KVKK/DSGVO) festlegen; ein vages „für alle Fälle behalten“ ist keine Richtlinie.","Abgelaufene Daten automatisch archivieren oder löschen; nicht manueller Bereinigung überlassen, die vergessen wird, während Tabellen anschwellen.","Heiße Daten auf schnellem Speicher, Archive auf günstigem/kaltem Speicher halten; nach Zugriffshäufigkeit staffeln.","Den Mechanismus zur Bearbeitung von Anfragen auf Löschung/Anonymisierung personenbezogener Daten von Anfang an vorsehen (Recht auf Löschung).","Archivierte Daten bei Bedarf abrufbar und abfragbar halten; archiviert darf nicht unzugänglich bedeuten.","Löschungen im Audit Log festhalten; was wann und warum gelöscht wurde, muss nachvollziehbar sein."],"level":"senior"},{"id":"change-management","title":"Change Management und Sicherheit in Produktion","category":"Betrieb","summary":"Jede Änderung an einer Produktionsdatenbank muss geprüft, umkehrbar und nachvollziehbar sein; unkontrollierte manuelle Eingriffe sind die häufigste Ursache für Ausfälle.","points":["Keine Ad-hoc-DDL/DML von Hand in Produktion ausführen; jede Änderung sollte über eine im Peer Review geprüfte Migration oder ein Skript laufen.","Änderungen in einem verkehrsarmen Zeitfenster mit bereitliegendem Rollback-Plan anwenden; Produktion nicht ungeplant anfassen.","Riskante Befehle zuerst in einer produktionsnahen Umgebung mit echten Datenmengen proben; Dauer und Sperrwirkung messen.","Produktionszugriff einschränken und auditieren: wer wann welchen Befehl ausgeführt hat, muss protokolliert sein (Just-in-Time-Zugriff).","Vor einem Delete/Update die WHERE-Klausel und die Anzahl betroffener Zeilen prüfen; innerhalb einer Transaktion ausführen und zuerst mit einem SELECT bestätigen.","Änderungen an einen Kalender und an Kommunikation knüpfen; andere Teams nicht mit unangekündigten Produktionsänderungen überraschen."],"level":"senior"}]},"guide/best-practices-3.json":{"topics":[{"id":"plan-reading-expert","title":"Wie man einen Ausführungsplan liest","category":"Performance","summary":"Ein Ausführungsplan zeigt die Schätzungen des Optimizers und die gewählten physischen Operatoren; der Engpass verbirgt sich in der Abweichung zwischen geschätzten und tatsächlichen Zeilenzahlen.","points":["Zugriffsmethoden unterscheiden: Full/Table Scan dient breiten Lesezugriffen auf großen Tabellen, Index Seek/Range Scan selektiven Prädikaten, Index Full Scan sortierten Lesezugriffen; Rows Removed by Filter bzw. die Aufteilung in Predicate/Access zeigt, wo Selektivität verloren geht.","Die Join-Methode zur Last passend wählen: Nested Loop ist ideal für eine kleine äußere Menge + indizierte innere Menge, Hash Join für große Equi-Joins, Merge Join für zwei vorsortierte Mengen; eine falsche Wahl ist meist Folge einer schlechten Kardinalitätsschätzung.","Fehler bei der Kardinalitätsschätzung aufspüren: geschätzte und tatsächliche Zeilen mit EXPLAIN ANALYZE (PG), DBMS_XPLAN ALLSTATS LAST (Oracle) oder SET STATISTICS PROFILE (MSSQL) vergleichen; eine Abweichung über 10x deutet darauf hin, dass die Planwahl grundlegend falsch sein kann.","Den Plan nach realen Kosten lesen: den Operator mit der meisten Zeit bzw. den meisten logischen Lesezugriffen (logical reads, buffers) finden; die „cost“-Einheit des Optimizers ist eine Schätzung, nicht der tatsächliche Engpass.","Warnungen zu Spill/Temp-Speicher erkennen: ein Hash-/Sort-Operator, der nicht in den Speicher passt und auf die Platte ausweicht (PG 'Sort Method: external merge', MSSQL Sort/Hash Warning mit SpillToTempDb im Plan), deutet auf zu wenig work_mem bzw. Sortierbereich hin.","Den Operatorbaum von innen nach außen und von unten nach oben lesen: innere Knoten laufen zuerst und Zeilen fließen nach oben; die Gesamtkosten je Operator berechnen, indem loops mit actual time multipliziert werden."],"level":"expert"},{"id":"parameter-sniffing-deep","title":"Parameter Sniffing und Bind Peeking","category":"Performance","summary":"Der Optimizer erstellt und cacht einen Plan auf Basis des Parameterwerts des ersten Aufrufs; erben spätere Werte mit anderer Verteilung diesen Plan, kann sich die Performance drastisch ändern.","points":["Das Symptom erkennen: dieselbe Abfrage ist mit manchen Parametern schnell und mit anderen sehr langsam; das zeigt, dass der gecachte Plan für einen untypischen „ersten“ Wert kompiliert wurde.","Bei Spalten mit schiefer Verteilung ist das Risiko hoch; Histogramme machen den Optimizer wertsensitiv, während ein einzelner gecachter Plan ihn wertunabhängig hält, und diese Spannung ist die Quelle des Problems.","In MSSQL erstellt OPTION (RECOMPILE) bei jeder Ausführung einen Plan, OPTIMIZE FOR UNKNOWN wählt einen ausgewogenen Plan auf Basis der durchschnittlichen Dichte, und OPTIMIZE FOR (@p = value) fixiert einen typischen Wert.","In Oracle erzeugen Adaptive Cursor Sharing und bind-aware Cursor für schief verteilte Werte unterschiedliche Pläne; in PG wird die Wahl zwischen Custom und Generic Plan mit plan_cache_mode gesteuert.","Für eine dauerhafte Lösung Pläne nach Wert trennen: Statement Splitting, Filtered/Partial Indexes oder einen eigenen Zugriffspfad für stark untypische Werte entwerfen.","In OLTP verursacht übermäßiges RECOMPILE CPU- und Kompilierungskosten (Hard Parse); bei häufig ausgeführten Abfragen messen und die Balance halten."],"level":"expert"},{"id":"plan-stability","title":"Planstabilität: Baselines, Hints und Plan Guides","category":"Performance","summary":"Engine-spezifische Persistenzmechanismen dienen dazu, verifizierte Pläne kritischer Abfragen einzufrieren und unerwartete Plan-Regressionen bei Statistik-/Versionsänderungen zu verhindern.","points":["In Oracle genehmigte Pläne mit SQL Plan Baselines (SPM) fixieren; neue Pläne werden nur als Kandidaten gesammelt, bis sie „accepted“ sind, und mit evolve kontrolliert verifiziert.","In MSSQL Plan-Regressionen mit Query Store erkennen und den bekannt guten Plan mit „Force Plan“ erzwingen; Automatic Plan Correction kann das automatisieren.","PostgreSQL hat kein eingebautes Einfrieren von Plänen; Stabilität entsteht durch die Erweiterung pg_hint_plan oder durch Umschreiben von Abfragen, Extended Statistics und Indexdesign.","Hints als letztes Mittel, begründet und kommentiert einsetzen; ein Hint setzt das physische Schema und die Datenverteilung voraus und wird schädlich, wenn sich diese ändern.","Das Inventar an Baselines/erzwungenen Plänen regelmäßig prüfen; fixierte Pläne können mit wachsenden Daten ihre Optimalität verlieren, daher nicht in die Falle „stabil, aber langsam“ tappen.","Pläne kritischer Abfragen vor einem Versions-Upgrade erfassen; Baselines als Sicherheitsnetz gegen Verhaltensänderungen des Optimizers vorbereiten."],"level":"expert"},{"id":"latch-mutex-contention","title":"Konkurrenz um Latches, Mutexe und Spinlocks","category":"Performance","summary":"Latches/Mutexe sind leichtgewichtige Locks, die Strukturen im Speicher schützen; bei hoher Parallelität verbrennt die Konkurrenz um sie CPU in Spinlock-Schleifen und stoppt die Skalierung.","points":["Locks von Latches unterscheiden: Locks schützen logische Daten (Zeile/Tabelle) für die Dauer einer Transaktion, Latches schützen physische Speicherstrukturen (Buffer, Hash Chains) für Mikrosekunden; ihre Diagnosemethoden unterscheiden sich.","In Oracle deutet Konkurrenz um den Latch 'cache buffers chains' meist auf einen Hot Block hin; eine kleine, häufig gelesene Tabelle oder die vordere Kante eines monoton wachsenden Index (Right-Hand Index) ist eine häufige Ursache.","Inserts mit sequenziell steigenden Schlüsseln (Identity/Sequence) verursachen Konkurrenz um die letzte Seite bzw. den letzten Block (Hot Page, PAGELATCH); mit Hash-Partitionierung, einem Reverse-Key-Index oder einem randomisierten Schlüssel verteilen.","In MSSQL Spinlock-Statistiken und PAGELATCH_*-Waits überwachen; bei Allokationskonkurrenz in tempdb mehrere Datendateien und eine passende tempdb-Konfiguration verwenden.","Spinlock-Konkurrenz zeigt sich als hohe CPU-Last bei niedrigem Durchsatz; die Lösung ist, die Konkurrenz auf Code-/Architekturebene zu verringern, und mehr Hardware verschlimmert sie oft.","NUMA-Bewusstsein und die Begrenzung der Verbindungsanzahl (Pooling) verringern die Latch-Konkurrenz; übermäßige Parallelität kann den Latch-Druck erhöhen."],"level":"expert"},{"id":"wait-event-methodology","title":"Methodik der Wait-Event-Analyse","category":"Performance","summary":"Performance steuern, indem gemessen statt geraten wird, wo das System seine Zeit mit Warten verbringt; die Top-down-Wait-Analyse ist die wissenschaftliche Methode, die den Engpass systematisch eingrenzt.","points":["DB Time in ihre Bestandteile zerlegen: CPU-Zeit von Wartezeit trennen; mit dem größten Anteil beginnen und nicht in Mikrooptimierung abdriften.","Den Top Wait Event finden: in Oracle mit ASH/AWR und „Top Wait Events“, in PG mit pg_stat_activity + pg_wait_sampling, in MSSQL mit sys.dm_os_wait_stats und Query Store.","Die Wait-Klasse interpretieren: I/O-Waits deuten auf Speicher/Pläne, Lock-Waits auf das Parallelitätsdesign, Latch-Waits auf Hotspots und Netzwerk-/Commit-Waits auf Konfigurationsprobleme.","Bis auf Session-/Abfrageebene vordringen: von den gesamten Waits ausgehen, zum SQL mit dem größten Anteil und dann zu dessen Plan gehen, um die Ursache zu isolieren (Drill-down).","Die Messung an das Lastfenster knüpfen: die Differenz (Delta) kumulativer Zähler zwischen zwei Zeitpunkten bilden; Summen täuschen, es zählen Raten und Zeitintervalle.","Idle-/harmlose Waits aus dem Rauschen herausfiltern; nicht jeder Wait ist ein Problem, entscheidend sind die Foreground Waits, die zur Antwortzeit der Nutzer beitragen."],"level":"expert"},{"id":"plan-regression-detection","title":"Regressionen von Ausführungsplänen erkennen","category":"Performance","summary":"Eine plötzlich langsame Abfrage wird meist durch einen Planwechsel und nicht durch eine Codeänderung verursacht; Regressionen proaktiv zu erkennen ist günstiger als reaktive Brandbekämpfung.","points":["Planhistorie aufbewahren und vergleichen: Planwechsel und Trends von Dauer/Ressourcen für dieselbe sql_id/query_hash mit MSSQL Query Store und Oracle AWR/SQL Monitor verfolgen.","Den Auslöser der Regression identifizieren: Statistik-Updates, wachsendes Datenvolumen, Versions-Upgrades, Parameteränderungen oder gelöschte Indizes sind die häufigsten Ursachen.","Automatisches Alerting einrichten: einen Alert auslösen, wenn sich der Plan-Hash ändert und durchschnittliche Dauer/logische Lesezugriffe einen Schwellenwert überschreiten; Metriken pro Ausführung statt kumulativer Werte verfolgen.","Einen schnellen Rückweg vorbereiten: den bekannt guten Plan vorübergehend mit Force Plan (MSSQL) oder einer SQL Plan Baseline (Oracle) fixieren und die Ursache später beheben.","Bei statistikbedingten Regressionen gesperrte/aktuelle Statistiken und die Aktualität der Histogramme prüfen; die automatische Statistiksammlung ist womöglich in kritischen Stunden geplant.","Vor einem Upgrade Regressionstests durchführen: eine repräsentative Last gegen den alten und den neuen Optimizer abspielen und Planunterschiede berichten (ein Ansatz ähnlich Oracle SPA)."],"level":"expert"},{"id":"mvcc-vacuum-internals","title":"Interna von MVCC und Vacuum/Undo","category":"Interna","summary":"MVCC verhindert Blockierungen zwischen Lesern und Schreibern, indem alte Zeilenversionen aufbewahrt werden; wie und wo diese Versionen liegen (Undo oder in der Tabelle), bestimmt die Wartungsstrategie.","points":["In PostgreSQL verbleiben alte Versionen als Dead Tuples in der Tabelle selbst; VACUUM bereinigt sie, sonst entstehen Bloat und Indexwachstum.","Oracle und MySQL/InnoDB halten alte Versionen in Undo-/Rollback-Segmenten; erschöpft ein lang laufender Lesezugriff das Undo, treten Fehler wie 'ORA-01555 snapshot too old' auf.","Autovacuum an die Last anpassen: bei Tabellen mit vielen Updates/Deletes den Scale Factor senken und den Cost Delay verringern; aggressiven Tabellen eigene Einstellungen pro Tabelle geben.","Die Visibility Map gesund halten; Index-Only Scans und die Verfolgung eingefrorener Tuples hängen von ihr ab, und die Performance sinkt, wenn sie vernachlässigt wird.","Das Risiko eines Transaction ID Wraparound überwachen: in PG die Metrik datfrozenxid/age verfolgen und verhindern, dass ein aggressives Anti-Wraparound-Vacuum während der Geschäftszeiten anläuft.","HOT-Updates (Heap-Only Tuple) fördern: Updates, die keine indizierte Spalte ändern, erfolgen in place, ohne den Index wachsen zu lassen; ein passender fillfactor unterstützt das."],"level":"expert"},{"id":"wal-redo-log-mechanics","title":"Mechanik von WAL/Redo/Transaktionslog","category":"Interna","summary":"Dauerhaftigkeit wird erreicht, indem die Änderung ins Log geschrieben wird, bevor Datenseiten auf die Platte geschrieben werden; Rate und Größe des Logstroms bestimmen sowohl die Dauerhaftigkeit als auch die Schreibleistung.","points":["Das Write-Ahead-Prinzip verstehen: Damit ein Commit zurückkehrt, genügt es, dass die zugehörigen Log-Einträge auf dauerhaftem Speicher liegen; geänderte Datenseiten werden später verzögert geschrieben.","Group Commit und Log-Flush-Verhalten tunen: die fsync-Häufigkeit gegen die Anforderungen der Last an die Dauerhaftigkeit abwägen, in PG mit commit_delay/synchronous_commit, in Oracle über das Verhalten des Log Writer.","Die Rate der Log-Erzeugung (WAL-/Redo-Generierung) überwachen; ein plötzlicher Anstieg deutet auf Massen-Updates, Full Page Images nach einem Checkpoint (full_page_writes/FPI) oder Indexwartung hin und erhöht den DR-Lag.","Das Log ist nicht nur Quelle für die Recovery, sondern auch für Replikation und CDC; logische Replikation und WAL Shipping speisen sich aus diesem Strom, daher die Archivierungs-/Bereinigungsrichtlinie entsprechend gestalten.","Die Log-Platte von der Datenplatte trennen und auf Speicher mit niedriger Latenz legen; Log-Schreibvorgänge sind sequenziell und latenzempfindlich, und der Commit-Durchsatz hängt direkt davon ab.","Unkontrolliertem Logwachstum vorbeugen: in MSSQL das richtige Wiederherstellungsmodell und regelmäßige Log-Backups verwenden; in PG sicherstellen, dass Archivierung/Replication Slots nicht zurückfallen."],"level":"expert"},{"id":"buffer-pool-management","title":"Verwaltung von Buffer/Shared Pool und Eviction","category":"Interna","summary":"Der Buffer Pool verringert Platten-I/O, indem er heiße Daten im Speicher hält; der Algorithmus, der entscheidet, welche Seite wann verdrängt wird, bestimmt Hit Ratio und Stabilität.","points":["Nicht allein auf die Buffer Cache Hit Ratio zielen; selbst eine hohe Quote kann die Latenz physischer Lesezugriffe verbergen, daher auch absolute I/O und Wartezeit betrachten.","Den Eviction-Algorithmus verstehen: damit reines LRU nicht durch sequenzielle Scans verschmutzt wird, nutzt PG Clock-Sweep, Oracle eine Touch-Count-/LRU-Variante und InnoDB ein LRU mit Midpoint Insertion.","Verhindern, dass große einmalige Scans heiße Daten aus dem Pool spülen (Cache Pollution); mit separaten Buffer Pools oder scanfreundlichen Zugriffsstrategien schützen.","Anteil der Dirty Pages und Schreibdruck steuern: zu viele Dirty Pages verursachen I/O-Spitzen beim Checkpoint, zu aggressives Schreiben erzeugt unnötige I/O; die Balance messen.","Den Speicher am Working Set dimensionieren; Ziel ist, die aktiv genutzte Menge abzudecken, nicht die gesamten Daten, und Überdimensionierung bringt abnehmenden Nutzen.","Im Oracle Shared Pool Konkurrenz um Library/Dictionary Cache und häufige Hard Parses überwachen; Bind-Variablen statt Literal-SQL verringern den Druck auf den Shared Pool."],"level":"expert"},{"id":"checkpoint-tuning","title":"Checkpoint-Tuning","category":"Interna","summary":"Ein Checkpoint verkürzt die Wiederherstellungszeit, indem er Dirty Pages auf die Platte schreibt; zu häufige Checkpoints erzeugen I/O-Spitzen, zu seltene eine lange Recovery, und dieser Kompromiss muss getunt werden.","points":["Wiederherstellungszeit gegen I/O im laufenden Betrieb abwägen: seltene Checkpoints bedeuten weniger Schreiblast, aber längere Crash Recovery, häufige das Gegenteil.","In PG Checkpoints mit checkpoint_timeout und max_wal_size zeitlich strecken; die I/O-Spitze glätten, indem Schreibvorgänge mit checkpoint_completion_target über das Intervall verteilt werden.","Der Indirect Checkpoint von MSSQL (target recovery time) sorgt für kontinuierliches Schreiben im Hintergrund; er macht die Latenz vorhersehbar, indem er plötzliche große Checkpoint-Wellen verhindert.","Checkpoint-Häufigkeit mit der Rate der Log-/WAL-Erzeugung korrelieren; in schreibintensiven Phasen überwachen, ob der Größen-Trigger statt des Zeit-Triggers greift.","full_page_writes (PG) protokolliert bei der ersten Änderung nach einem Checkpoint eine vollständige Seite; bei zu häufigen Checkpoints schwillt das WAL-Volumen an und erhöht DR- und Plattenkosten.","Symptome eines „Checkpoint-Einbruchs“ erkennen: steigende Schreiblatenz und fsync-Waits während Checkpoints; Warteschlange und Verteilung des Speichers prüfen."],"level":"expert"},{"id":"statistics-strategy","title":"Strategie zur Statistiksammlung","category":"Interna","summary":"Die Qualität der Optimizer-Entscheidungen hängt von Aktualität und Auflösung der Statistiken ab; Stichprobenrate, Histogramme und Extended Statistics bestimmen die Kardinalitätsschätzungen.","points":["Die Stichprobenrate nach Datengröße und Schiefe wählen: eine zu kleine Stichprobe liefert bei schiefer Verteilung falsche Schätzungen, eine zu große verursacht unnötige Sammelkosten.","Schiefe mit Histogrammen modellieren: PG default_statistics_target, Oracle-Histogramme FREQUENCY/HYBRID und MSSQL-Statistikhistogramme machen häufige und unausgewogene Werte für den Optimizer sichtbar.","Korrelationen zwischen Spalten mit Extended Statistics erfassen: PG CREATE STATISTICS (ndistinct/dependencies) und Oracle Extended Stats beheben Schätzfehler bei voneinander abhängigen Spalten.","Sammelzeitpläne von der Last entkoppeln: verhindern, dass der automatische Statistik-/Gather-Job in kritischen Stunden läuft und I/O sowie Planinstabilität verursacht.","Das Risiko veralteter Statistiken bei schnell wachsenden Tabellen steuern; schwellenwertbasierte automatische Updates können hinterherhinken, daher für kritische Tabellen manuelle/geplante Sammlung ergänzen.","Wissen, dass Statistikänderungen Plan-Regressionen verursachen können; in kritischen Umgebungen Statistiken sperren (locked stats) oder kontrolliert per Pending Publish veröffentlichen."],"level":"expert"},{"id":"partition-pruning-join","title":"Partition Pruning und Partition-wise Joins","category":"Skalierung/Architektur","summary":"Partitionierung teilt eine Tabelle nicht nur auf; der eigentliche Gewinn ist, dass Abfragen irrelevante Partitionen nie lesen (Pruning) und Joins pro Partition parallel laufen.","points":["Den Partitionsschlüssel nach dem häufigsten und selektivsten Filter wählen; Pruning funktioniert nur, wenn das Abfrageprädikat den Partitionsschlüssel enthält, sonst werden alle Partitionen gescannt.","Statisches und dynamisches Pruning unterscheiden: konstante Prädikate schließen Partitionen zur Kompilierzeit aus, Bind-/Join-Werte zur Laufzeit (Runtime Pruning); den Plan entsprechend prüfen.","Für Partition-wise Joins beide Tabellen auf demselben Schlüssel mit kompatiblem Schema partitionieren; der Optimizer zerlegt einen großen Join in kleine Joins Partition für Partition und parallelisiert sie.","Anzahl und Granularität der Partitionen ausbalancieren: zu viele Partitionen bedeuten Metadaten- und Planungskosten, zu wenige schwaches Pruning; nach dem Zugriffsmuster dimensionieren.","Lifecycle-Management mit Partitionierung kombinieren: alte Partitionen per DETACH/DROP entfernen oder archivieren; das ergibt eine sofortige, logarme Bereinigung statt DELETE.","Bedenken, dass Funktionen/Typkonvertierungen oder Ausdrücke, die den Schlüssel verdecken, das Pruning verhindern; die Anzahl gescannter Partitionen mit EXPLAIN prüfen."],"level":"expert"},{"id":"shard-key-selection","title":"Wahl des Shard Key und Hotspots","category":"Skalierung/Architektur","summary":"Der Shard Key bestimmt das Schicksal eines verteilten Systems; eine schlechte Wahl häuft Last auf einem einzigen Knoten an (Hot Shard), stoppt die Skalierung und macht shardübergreifende Abfragen teuer.","points":["Einen Schlüssel mit hoher Kardinalität wählen, der die Last gleichmäßig verteilt; ein Schlüssel mit niedriger Kardinalität oder ein zeitlicher/monoton steigender Schlüssel häuft Last auf einem Shard an und erzeugt einen Hotspot.","Den Schlüssel so entwerfen, dass die häufigsten Abfragen auf einem einzigen Shard aufgelöst werden; shardübergreifende Abfragen (Scatter-Gather) vervielfachen Latenz und Koordinationskosten.","Hash Sharding gleicht die Last aus, verstreut aber Bereichsabfragen; Range Sharding eignet sich für Bereichsabfragen, erhöht aber das Hotspot-Risiko; nach dem Zugriffsmuster wählen.","Umverteilung beim Hinzufügen/Entfernen von Knoten mit Consistent Hashing minimieren; ein fester Modulo-Hash verschiebt bei geänderter Knotenanzahl fast alle Daten.","Den Resharding-Plan von Anfang an entwerfen; den Schlüssel später zu ändern ist teuer und riskant, daher das Wachstumsszenario vorab modellieren.","Bei Aufteilung nach Mandant/Kunde einen dedizierten Shard oder eine Sub-Partitionierungsstrategie (Split) vorbereiten, damit ein riesiger Mandant (Whale) nicht einen einzelnen Shard füllt."],"level":"expert"},{"id":"read-write-splitting","title":"Read-Write Splitting und Routing zu Replikaten","category":"Skalierung/Architektur","summary":"Lesezugriffe an Replikate zu leiten entlastet den schreibenden Primary; da Replication Lag jedoch Konsistenzgarantien brechen kann, muss das Routing bewusst erfolgen.","points":["Beim Verteilen der Leselast auf Replikate den Replication Lag kontinuierlich überwachen; ein veraltetes Replikat kann Nutzern alte Daten zeigen.","Abläufe mit Read-your-Writes-Bedarf (der schreibende Nutzer liest sofort) an den Primary leiten oder mit Causal Reads/Tokens ein aktuelles Replikat garantieren.","Das Routing in der Infrastrukturschicht statt in der Anwendung umsetzen: transparent über einen Proxy/Router verteilen (ProxySQL für MySQL; für PG Pgpool-II oder HAProxy mit getrennten Lese-/Schreib-Endpoints, da PgBouncer Lese- und Schreibzugriffe nicht trennt; bei MSSQL Read-Only Routing über Application Intent).","Ein Replikat aus dem Pool nehmen, wenn der Lag-Schwellenwert überschritten ist; Lesezugriffe an ein Replikat mit hohem Lag zu senden erzeugt unter dem Anschein von „Skalierung“ inkonsistente Daten.","Schwere, lag-tolerante Lesezugriffe wie Analytics/Reporting auf einem dedizierten Replikat isolieren; OLTP-Replikate von dieser Last abschirmen.","Testen, dass sich das Lese-Routing in einem Failover-Szenario automatisch aktualisiert; ein Router, der den neuen Primary nicht kennt, verursacht Split-Brain oder Ausfallzeit."],"level":"expert"},{"id":"connection-pool-sizing","title":"Rechnung zur Größe des Connection Pool","category":"Skalierung/Architektur","summary":"Die Poolgröße wird per Rechnung auf Basis von Little's Law und Kernanzahl bestimmt, nicht nach Gefühl; zu viele Verbindungen senken den Durchsatz durch Konkurrenz und Context Switching.","points":["Little's Law anwenden: benötigte Parallelität ≈ Anfragerate (Durchsatz) × durchschnittliche Verarbeitungszeit; die Zielparallelität aus gemessenen Werten ableiten, nicht aus Vermutungen.","Ein kleiner Pool ist oft schneller: übersteigen die aktiven Verbindungen die physische Parallelität von Kernen/Platten, senken CPU-Context-Switching und Latch-Konkurrenz den Durchsatz.","Als grober Ausgangspunkt einen Kern-Pool mit einer Verbindungsanzahl nahe der Kernanzahl verwenden (z. B. wenige echte Verbindungen hinter PgBouncer) und unter Last messend tunen.","Den Pool auf Anwendungsseite zusammen mit max_connections der Datenbank planen; N Anwendungsinstanzen × Poolgröße dürfen das Datenbanklimit nicht überschreiten.","Auf Engines mit hohen Prozesskosten pro Verbindung wie PG PgBouncer/Transaction Pooling einsetzen; jede Verbindung kostet Speicher und Scheduling.","Pool-Sättigung, Wartezeit in der Warteschlange und Timeouts überwachen; eine ständig wachsende Warteschlange signalisiert entweder einen Engpass oder Unterdimensionierung, daher den Pool nicht blind vergrößern."],"level":"expert"},{"id":"lock-escalation-deadlock","title":"Lock Escalation und Muster zur Deadlock-Vermeidung","category":"Zuverlässigkeit","summary":"Viele Zeilen-Locks können zu einem Table Lock eskalieren und die Parallelität zunichtemachen; ein Deadlock ist die vermeidbare Folge einer inkonsistenten Lock-Reihenfolge.","points":["Große Massenvorgänge, die Lock Escalation auslösen, aufteilen (Batch/Chunk); Millionen Zeilen in einer einzigen Transaktion zu sperren verursacht Escalation und Blocking.","Das Grundmuster zur Vermeidung von Deadlocks ist eine konsistente Lock-Reihenfolge: alle Codepfade müssen Ressourcen in derselben Reihenfolge sperren (z. B. nach Primärschlüssel).","Transaktionen kurz halten und eine einzige atomare Arbeitseinheit ausführen; Transaktionen mit Benutzerinteraktion, Remote-Aufrufen oder langen Berechnungen halten Locks unnötig lange.","Den Isolation Level bewusst wählen: unnötig hohe Isolation verursacht übermäßiges Locking, unnötig niedrige Anomalien; in MVCC-Engines vermeiden Lesezugriffe die meisten Locks ohnehin.","Deadlock-Graphen/-Traces sammeln (MSSQL Deadlock Graph, PG log_lock_waits, Oracle Trace); das Opfer protokollieren und wiederkehrende Muster bis zur Ursache verfolgen.","Retry-Logik implementieren: die als Deadlock-Opfer gewählte Transaktion idempotent wiederholen; sie ist ein Sicherheitsnetz für Konkurrenz, die sich durch Design nicht vermeiden lässt."],"level":"expert"},{"id":"long-transaction-bloat","title":"Lange Transaktionen und Verwaltung von Bloat/Undo","category":"Zuverlässigkeit","summary":"Eine offen gelassene lange Transaktion blockiert in MVCC-Engines die Bereinigung alter Versionen und setzt eine Kette aus Bloat, Undo-Druck und Performance-Einbruch in Gang.","points":["Lang laufende/offene Transaktionen überwachen und alarmieren: idle in transaction in PG und lange Abfragen mit aktivem Undo in Oracle stoppen die Bereinigung (Vacuum/Undo-Rückgewinnung).","In PG hindert eine einzige lange Transaktion VACUUM daran, Dead Tuples zu entfernen; Tabellen und Indizes blähen sich auf, und eine einzige „Zombie“-Session kann die ganze Datenbank verlangsamen.","Hängende Transaktionen automatisch mit idle_in_transaction_session_timeout und statement_timeout beenden; eine Verteidigungslinie gegen Leaks der Anwendung aufbauen.","In Oracle/InnoDB erschöpft ein langer Lesezugriff das Undo und erzeugt 'snapshot too old'; Undo-Bereich/Retention nach der längsten Abfrage der Last dimensionieren.","Batch-Jobs mit kurzen Commit-Intervallen aufteilen; eine einzige riesige Transaktion häuft Undo/WAL an und verlängert die Rollback-Zeit gefährlich.","Vorhandenen Bloat messen und zurückgewinnen: Bloat mit pgstattuple messen und Platz sowie Indexeffizienz mit VACUUM FULL/pg_repack oder einer Online-Reorganisation zurückgewinnen."],"level":"expert"},{"id":"dr-game-day","title":"DR-Übungen (Game Day) und Validierung von RPO/RTO","category":"Zuverlässigkeit","summary":"Ein ungetesteter Backup- und Failover-Plan ist eine Annahme; die DR-Fähigkeit ist erst bewiesen, wenn RPO/RTO in realistischen Übungen gemessen wurden.","points":["RPO und RTO aus den fachlichen Anforderungen ableiten und in der Praxis messen: Backup-/Replikations-Lag bestimmt das RPO, Wiederherstellungs- + Validierungszeit das RTO; Ziel und Realität vergleichen.","Backups regelmäßig und automatisch wiederherstellen: ein nie wiederhergestelltes Backup ist ein Schrödinger-Backup; ohne Restore-Test ist die Behauptung, dass ein Backup existiert, unbelegt.","Game-Day-Übungen in einer produktionsnahen Umgebung mit realistischen Ausfallszenarien planen (Verlust des Primary, Regionsausfall, Datenkorruption).","Failover- und Failback-Prozeduren Ende-zu-Ende durchspielen; DNS-/Verbindungsrouting, Neuverbindung der Anwendung und Beförderung des Replikats einbeziehen.","Die Fähigkeit zur Point-in-Time-Recovery (PITR) belegen: in einem Szenario logischer Korruption testen, dass du mit Log-Archiven + Full Backups zu einem bestimmten Zeitpunkt zurückkehren kannst.","Aus jeder Übung gemessenes RTO/RPO, Überraschungen und Maßnahmen ableiten; das Runbook aktualisieren und die Verbesserung in der nächsten Übung prüfen."],"level":"expert"},{"id":"observability-slo","title":"Observability und SLOs/Golden Signals","category":"Zuverlässigkeit","summary":"Um die Datenbank nach geschäftlicher Auswirkung zu steuern, die Golden Signals (Latenz, Traffic, Fehler, Sättigung) an SLOs knüpfen und mit einem Error Budget priorisieren.","points":["Die vier Golden Signals überwachen: Latenz, Traffic (Durchsatz), Fehlerrate (Errors) und Sättigung; sie fassen den Zustand zusammen, ohne in die Ursache einzutauchen.","Latenz mit Perzentilen (p95/p99) statt Mittelwerten messen; der Mittelwert verbirgt die Tail Latency und das schlechteste Nutzererlebnis.","SLOs und ein Error Budget definieren; bei Symptomen (SLO-Verletzungen) alarmieren, nicht bei jeder Schwankung einer Low-Level-Metrik, um Alert Fatigue zu vermeiden.","Die USE-Methode (Utilization, Saturation, Errors) auf Ressourcen anwenden: Sättigung von CPU, Speicher, Platten-I/O und Connection Pool getrennt beobachten.","Monitoring über Schichten hinweg korrelieren: die Kette Abfrage -> Wait Event -> Ressourcensättigung -> Geschäftsmetrik auf einer gemeinsamen Zeitachse sehen.","Metriken + Logs + Traces gemeinsam einrichten; Metriken allein beantworten das „Was“, Logs/Traces das „Warum“, und ohne sie wächst die MTTR."],"level":"expert"},{"id":"least-privilege-roles","title":"Minimale Rechte und Rollendesign","category":"Sicherheit","summary":"Wenn jedes Konto nur die für seine Aufgabe minimal nötigen Rechte hat, verkleinert sich der Schadensradius eines Einbruchs; Rollen bieten eine handhabbare Abstraktion statt direkt vergebener Rechte.","points":["Rechte an Rollen statt an Benutzer vergeben; Rollen nach Funktion entwerfen (Leser, Schreiber, DDL, Betrieb) und Benutzer Rollen zuweisen, direkte Grants vermeiden.","Anwendungskonten nur die benötigten Objekt- und Vorgangsrechte geben; DDL, SUPERUSER/DBA und breite Schemarechte dürfen auf einem produktiven Anwendungskonto nicht existieren.","Menschlichen Administratorzugriff personalisieren und auditieren: individuelle Identitäten + bei Bedarf erhöhten Zugriff (JIT/Break-Glass) statt eines gemeinsamen Admin-Kontos verwenden.","Breite PUBLIC-/Standardrechte entziehen; Default Privileges und Kontrollen auf Schemaebene konfigurieren, damit Standardrechte nicht auf neue Objekte durchsickern.","Privilegierte Konten regelmäßig prüfen (Rezertifizierung der Zugriffe); ungenutzte Konten, Konten ausgeschiedener Mitarbeiter und überprivilegierte Konten regelmäßig bereinigen.","Funktionstrennung durchsetzen; dieselbe Person darf nicht die Produktion ändern und zugleich den Audit Trail löschen können, daher das Schreiben des Audits mit einem separaten Recht schützen."],"level":"expert"},{"id":"encryption-decision","title":"Verschlüsselungsentscheidungen: at rest, in transit, TDE","category":"Sicherheit","summary":"Verschlüsselung wird nach dem Bedrohungsmodell gewählt; jede Schicht schützt gegen einen anderen Angriff, und die Schlüsselverwaltung ist oft kritischer als der Verschlüsselungsalgorithmus.","points":["Die Bedrohung klären: at rest (gestohlene Platten/Backups), in transit (Abhören des Netzwerks) und auf Anwendungsebene (für niemanden einsehbar, auch nicht für DBAs) sind unterschiedliche Schutzarten; entscheiden, welche du brauchst.","Für Daten in transit TLS bei Client-Server-Verbindungen erzwingen; die Zertifikatsprüfung nicht deaktivieren, da du sonst für Man-in-the-Middle-Angriffe (MITM) offen bist.","Für at rest TDE mit Optionen zur Dateisystem-/Plattenverschlüsselung vergleichen; TDE schützt Datendateien und Backups, aber weder Daten im Speicher noch autorisierten DBA-Zugriff.","Für die sensibelsten Felder Verschlüsselung auf Anwendungs-/Spaltenebene oder einen clientseitigen Ansatz wie Always Encrypted nutzen, damit selbst der DBA keinen Klartext sieht.","Schlüsselverwaltung ernst nehmen: Schlüssel getrennt von den Daten in einem KMS/HSM speichern sowie Rotation und Zugriffskontrolle planen; gelangt der Schlüssel nach außen, ist die Verschlüsselung wertlos.","Performance- und Funktionsauswirkungen messen: auf verschlüsselten Spalten sind Indizierung/Suche eingeschränkt und TDE erhöht die CPU-Kosten; die Entscheidung als gemessenen Kompromiss treffen."],"level":"expert"},{"id":"audit-strategy","title":"Audit-Strategie","category":"Sicherheit","summary":"Der Audit Trail liefert Belege für Sicherheitsereignisse und Compliance-Anforderungen; was protokolliert wird, wohin der Trail geht und wie lange er aufbewahrt wird, bestimmt das Verhältnis von Rauschen zu Beweiskraft.","points":["Was auditiert wird, aus Bedrohungen und Compliance ableiten: privilegierte Vorgänge, Anmeldungen/fehlgeschlagene Anmeldungen, Schemaänderungen (DDL) und Zugriffe auf sensible Daten priorisieren.","Nicht in die Falle tappen, alles zu protokollieren; übermäßiges Auditing verschlechtert die Performance und ertränkt das Signal im Rauschen, daher kritische Ereignisse gezielt und zweckgebunden erfassen.","Den Audit Trail auf ein separates, sicheres Ziel schreiben, das der Produktions-DBA nicht ändern kann (externer Log Collector/SIEM); Manipulation des Trails durch Funktionstrennung verhindern.","Engine-Funktionen nutzen: den Detailgrad mit Oracle Unified/Fine-Grained Audit, MSSQL SQL Audit und PG pgAudit an den Bedarf anpassen.","Die Aufbewahrung nach Compliance-Anforderungen (KVKK/DSGVO/PCI-DSS usw.) festlegen und die Archivierungs-/Bereinigungsrichtlinie automatisieren; die Integrität des Trails wahren.","Den Trail nicht nur sammeln, sondern regelmäßig prüfen und an Alerts knüpfen: Auditing schafft erst Wert, wenn es Anomalien meldet und in Maßnahmen umsetzt, nicht als totes Archiv."],"level":"expert"}]},"guide/best-practices-4.json":{"topics":[{"id":"oracle-rac-cache-fusion","title":"Oracle RAC: Cache Fusion und Interconnect","category":"Oracle","summary":"In RAC teilen sich mehrere Instanzen dieselbe Datenbank; Cache Fusion wahrt die Kohärenz, indem Blöcke über den Interconnect statt über die Platte zwischen Knoten übertragen werden.","points":["Braucht eine Instanz einen Block, der im Buffer Cache einer anderen Instanz liegt, überträgt Cache Fusion ihn RAM-zu-RAM über den privaten Interconnect; er wird nicht auf die Platte geschrieben und wieder gelesen (Disk Ping).","Global Cache Service (GCS/LMS-Prozesse) verwaltet Besitz und Modus (shared/exclusive) der Blöcke, Global Enqueue Service (GES) verwaltet Locks und Enqueues.","Jeder Block hat einen Master-Knoten; der Ort eines Blocks wird im Global Resource Directory verfolgt und kann mit DRM (Dynamic Resource Mastering) je nach Zugriffsintensität neu zugeordnet werden (Remastering).","Der Interconnect erfordert ein dediziertes Netzwerk mit niedriger Latenz; Jumbo Frames (MTU 9000) und bei Bedarf RDMA/RoCE oder InfiniBand verwenden und ihn nie mit dem öffentlichen Netzwerk teilen.","Die Wait Events 'gc buffer busy' und 'gc cr/current block busy' deuten auf Konkurrenz um Hot Blocks hin; ein größerer Sequence CACHE, Reverse-Key-Indizes oder Lastverteilung durch Partitionierung können helfen.","Steigt die Statistik gc blocks lost weiter an, gibt es Paketverluste auf dem Interconnect; NIC, Switch-Puffer und Socket-Puffergrößen (rmem/wmem) prüfen."],"level":"expert"},{"id":"oracle-rac-node-eviction","title":"Oracle RAC: Node Eviction und Split-Brain","category":"Oracle","summary":"Clusterware entfernt einen nicht reagierenden Knoten aus dem Cluster, um Split-Brain zu verhindern; die Entscheidung basiert auf Netzwerk-Heartbeat, Disk-Heartbeat und Mehrheit der Voting Disks.","points":["CSSD überwacht zwei getrennte Heartbeats: den Netzwerk-Heartbeat über den Interconnect (misscount, Standard 30 s) und den Disk-Heartbeat über die Voting Disks (disktimeout, Standard 200 s).","Bei einem Split-Brain bestimmt die Mehrheit der Voting Disks den überlebenden Subcluster (Kohorte); daher muss die Anzahl der Voting Disks ungerade sein (3 oder 5) und sie müssen in getrennten Failure Groups liegen.","Die Eviction erfolgt meist durch einen Neustart des Knotens oder durch Fencing ohne Neustart (nur der Stack wird neu gestartet); Ziel ist, den fehlerhaften Knoten am Schreiben auf den gemeinsamen Speicher zu hindern.","Häufige Ursachen für Evictions: Verlust des Interconnect, CPU-Mangel für CSSD unter hoher Last, I/O-Latenz der Voting Disks und Zugriffsprobleme auf OCR/Voting Disks.","Für die Ursachenanalyse ocssd.log, CSSD-Traces, Alert Log und OS-Meldungen zusammen mit Daten aus CHM/oclumon (Cluster Health Monitor) prüfen."],"level":"expert"},{"id":"oracle-asm-redundancy-rebalance","title":"Oracle ASM: Redundanz von Disk Groups und Rebalance","category":"Oracle","summary":"ASM schützt Daten, indem sie über die Platten einer Disk Group verteilt (Striping) und pro Failure Group gespiegelt werden; beim Hinzufügen oder Entfernen von Platten verteilt ASM die Extents automatisch neu.","points":["Redundanzstufen: EXTERNAL (keine Spiegelung, Schutz übernimmt das Storage-Array), NORMAL (2 Kopien, verkraftet den Verlust 1 Platte), HIGH (3 Kopien, verkraftet 2 gleichzeitige Verluste).","ASM spiegelt Failure Groups, nicht Platten; der primäre Extent liegt in einer Failure Group und seine Spiegelkopien in anderen Failure Groups, sodass selbst beim Ausfall einer ganzen Failure Group keine Daten verloren gehen.","Das Hinzufügen oder Entfernen einer Platte löst ein Rebalance aus, das die Extents neu verteilt; ASM_POWER_LIMIT (0-1024) bestimmt das Verhältnis von Geschwindigkeit zu Ressourcenverbrauch.","Die Größe der AU (Allocation Unit) wird beim Anlegen der Disk Group festgelegt; bei großen Datenbanken verbessert eine AU von 4 MB die I/O-Effizienz.","Voting Disks und OCR können in einer Disk Group mit Redundanz NORMAL/HIGH liegen; eine Quorum Failure Group wird nur für Voting Disks genutzt und enthält keine Daten-Extents."],"level":"expert"},{"id":"oracle-data-guard-modes","title":"Oracle Data Guard: Standby-Typen und Schutzmodi","category":"Oracle","summary":"Data Guard ermöglicht Disaster Recovery, indem Redo an einen Standby übertragen wird; die Wahl zwischen physischem/logischem Standby und den drei Schutzmodi bestimmt das Verhältnis von Datenverlust zu Verfügbarkeit.","points":["Ein physischer Standby ist eine blockgenaue Kopie des Primary (Redo Apply/MRP); mit Active Data Guard kann er geöffnet schreibgeschützt abgefragt werden. Ein logischer Standby wandelt Redo in SQL um und wendet es an (SQL Apply) und kann abweichende Strukturen/Indizes enthalten.","Maximum Performance (Standard): Redo wird asynchron (ASYNC) übertragen, und die Primary wartet nicht auf eine Bestätigung; keine Performance-Einbußen, aber ein geringes Risiko von Datenverlust.","Maximum Availability: Redo wird synchron (SYNC/AFFIRM) übertragen; ist die Standby nicht erreichbar, hält die Primary nicht an, sondern fällt auf das Verhalten von MAX PERFORMANCE zurück und synchronisiert neu, sobald die Standby wieder verfügbar ist.","Maximum Protection: Die Primary führt keinen Commit aus, bevor mindestens eine Standby bestätigt hat; das garantiert null Datenverlust, aber die Primary fährt herunter, wenn alle Standbys ausfallen.","Fast-Start Failover (FSFO) nutzt einen Observer-Prozess, um bei Ausfall der Primary automatisch ein Failover durchzuführen; RPO=0 wird mit FastStartFailoverThreshold und synchronem Transport angestrebt.","Überwache den Apply-Lag auf der Standby mit v$dataguard_stats und den Metriken 'apply lag'/'transport lag'; Real-Time Apply wendet Redo an, bevor es archiviert wird."],"level":"expert"},{"id":"oracle-exadata-offload","title":"Oracle Exadata: Smart Scan und Storage Indexes","category":"Oracle","summary":"Auf Exadata übernehmen die Storage Cells einen Teil der Abfrageverarbeitung (Offload) und liefern nur die relevanten Daten an den DB-Knoten; Smart Scan, Storage Indexes und HCC reduzieren die I/O drastisch.","points":["Bei Full-Scan-Abfragen führt Smart Scan Prädikatfilterung und Spaltenprojektion in der Storage Cell aus; an den DB-Knoten gehen nur die benötigten Zeilen/Spalten statt ganzer Blöcke, was den Interconnect-Verkehr verringert.","Smart Scan funktioniert nur mit Direct Path Reads; bei Blöcken aus dem Buffer Cache oder bestimmten Operationen greift er nicht. Miss den Nutzen mit den Statistiken 'cell physical IO bytes eligible for predicate offload' und 'cell physical IO interconnect bytes returned by smart scan' ('cell physical IO bytes saved by storage index' zeigt nur die Einsparung durch den Storage Index).","Ein Storage Index hält für jede 1-MB-Storage-Region die Min/Max-Werte der Spalten im Speicher; liegt das Abfrageprädikat außerhalb dieses Bereichs, wird die Region übersprungen, ohne gelesen zu werden (wirkt wie ein negativer Index).","Storage Indexes sind nicht persistent; sie liegen im Speicher und werden beim Neustart der Cell neu aufgebaut. Physisch sortierte Daten (Clustering) erhöhen die Überspringrate.","HCC (Hybrid Columnar Compression) komprimiert Daten spaltenweise innerhalb von Compression Units; QUERY LOW/HIGH ist für abfrageintensive Daten gedacht, ARCHIVE LOW/HIGH für selten genutzte Archivdaten und erreicht sehr hohe Kompression bei hohem CPU-Aufwand."],"level":"expert"},{"id":"oracle-awr-ash-analysis","title":"Performance-Analyse mit Oracle AWR und ASH","category":"Oracle","summary":"AWR sammelt periodisch Performance-Snapshots, ASH sampelt aktive Sessions jede Sekunde; zusammen beantworten sie anhand der Wait Events die Frage 'wo ist die Zeit geblieben'.","points":["Standardmäßig erstellt AWR alle 60 Minuten einen Snapshot und bewahrt ihn 8 Tage auf; ein AWR-Bericht zwischen zwei Snapshots zeigt, welche SQL-Anweisungen und Wait Events die DB Time verbraucht haben (erfordert die Lizenz für das Diagnostics Pack).","Beginne die Analyse immer mit 'Top 10 Foreground Events by Total Wait Time' und der DB Time; betrachte dort das Verhältnis von CPU, I/O und Concurrency, statt dich in einzelnen Statistiken zu verlieren.","ASH sampelt v$active_session_history einmal pro Sekunde und erfasst kurzlebige, plötzliche Probleme (Spitzen); es macht kurzzeitige Lock-/Latch-Stürme, die AWR übersieht, nach SQL_ID und blockierender Session sichtbar.","Average Active Sessions (AAS = DB Time / verstrichene Zeit) fasst die Systemlast in einer einzigen Zahl zusammen; liegt AAS über der Anzahl der CPU-Kerne, ist das System gesättigt.","Für das SQL-Tuning nimm die teuerste SQL_ID aus dem AWR-Abschnitt 'SQL ordered by Elapsed Time/Gets', prüfe mit ASH, auf welchen Schritt diese SQL-Anweisung wartet, und untersuche dann den Plan Zeile für Zeile mit einem SQL-Monitor-Bericht."],"level":"expert"},{"id":"oracle-scn-read-consistency","title":"Oracle SCN und Lesekonsistenz","category":"Oracle","summary":"Die System Change Number (SCN) ist die logische Uhr der Datenbank; Oracle trennt Leser von Schreibern, indem es aus Undo ein konsistentes Abbild (Lesekonsistenz) zum SCN des Abfragebeginns erzeugt.","points":["Die SCN steigt mit jedem Commit monoton an und wird in Redo/Undo, in der Control File und in den Block-Headern gespeichert; ist die SCN der letzten Änderung eines Blocks größer als die Abfrage-SCN, wird der Block 'zurückgerollt'.","Für die Lesekonsistenz erzeugt Oracle eine CR-Kopie (Consistent Read) des aktuellen Blocks und rollt sie mithilfe von Undo auf die Start-SCN der Abfrage zurück; daher gilt: 'Leser blockieren keine Schreiber, und Schreiber blockieren keine Leser'.","Reicht Undo nicht aus oder wird undo_retention überschritten, erhält eine lang laufende Abfrage 'ORA-01555 snapshot too old'; Abhilfe schafft ein größerer Undo-Tablespace, eine angepasste Retention oder eine kürzere Abfrage.","Konsistenz auf Anweisungsebene verwendet für jede SQL-Anweisung eine eigene SCN; eine SERIALIZABLE- oder Read-only-Transaktion behält für alle Anweisungen die eine SCN vom Transaktionsbeginn.","In RAC wird die SCN über alle Instanzen synchron gehalten (Lamport oder Broadcast-on-Commit); das Fortschreiten der SCN wird für globale Konsistenz zwischen den Knoten koordiniert."],"level":"expert"},{"id":"postgres-patroni-failover","title":"PostgreSQL: automatisches Failover mit Patroni","category":"PostgreSQL","summary":"Patroni ermöglicht Leader-Wahl und automatisches Failover, indem es den Zustand des PostgreSQL-Clusters in einem verteilten Konsensspeicher (DCS) hält; mit einem Watchdog verhindert es Split-Brain.","points":["Cluster-Zustand und Leader-Lock liegen in einem DCS wie etcd/Consul/ZooKeeper; der Leader erneuert regelmäßig einen Lock mit TTL. Kann er ihn nicht erneuern, läuft der Lock ab und eine neue Leader-Wahl beginnt.","Beim Failover wählt Patroni die aktuellste Replica (die mit der höchsten LSN); der neue Leader wird promotet, und die übrigen Replicas verbinden sich neu mit ihm (reinitialize/rewind).","Der Watchdog (softdog) ist ein Sicherheitsnetz auf Hardwareebene, das die Maschine zurücksetzt, wenn der alte Leader den Kontakt zum DCS verliert; so wird verhindert, dass zwei Knoten gleichzeitig Leader sind (Split-Brain).","maximum_lag_on_failover verhindert, dass eine weit zurückliegende Replica Leader wird und Datenverlust verursacht; synchronous_mode kann RPO=0 anstreben, erhöht aber die Schreiblatenz.","Clients sollten sich nicht direkt mit einem Knoten verbinden, sondern über HAProxy mit dem REST-Health-Check von Patroni oder über eine VIP; so wird der Verkehr bei einem Leader-Wechsel automatisch zum neuen Leader geleitet.","Eine DCS-Mehrheit (Quorum) ist Pflicht; verwende einen etcd-Cluster mit 3 oder 5 Mitgliedern. Ist der DCS nicht erreichbar, geht Patroni auf Nummer sicher und kann den aktuellen Leader auf Read-only herabstufen."],"level":"expert"},{"id":"postgres-logical-replication-upgrade","title":"PostgreSQL: Major-Upgrade ohne Ausfallzeit mit logischer Replikation","category":"PostgreSQL","summary":"Da logische Replikation Zeilenänderungen versionsübergreifend übertragen kann, ermöglicht sie Major-Upgrades mit nahezu null Ausfallzeit; alte und neue Version laufen eine Zeit lang parallel.","points":["Logische Replikation dekodiert Änderungen logisch aus dem WAL (INSERT/UPDATE/DELETE); anders als physische Replikation funktioniert sie über verschiedene Major-Versionen und Plattformen hinweg.","Ablauf: Lege auf der neuen Version ein leeres Schema an, definiere auf der alten Version eine PUBLICATION und auf der neuen eine SUBSCRIPTION; zuerst werden die Tabellen kopiert (Initial Sync), danach fließen die laufenden Änderungen.","Jede Tabelle braucht eine REPLICA IDENTITY; bei Tabellen ohne Primärschlüssel erfordert die Replikation von UPDATE/DELETE REPLICA IDENTITY FULL, sonst tritt ein Fehler auf.","DDL-Änderungen werden nicht automatisch repliziert, und aktuelle Sequence-Werte werden nicht übertragen; setze die Sequences beim Cutover auf der neuen Version manuell vor, sonst entstehen Schlüsselkonflikte.","Stoppe beim Cutover kurz die Anwendung, die auf die alte Version schreibt, und richte sie auf die neue Version aus, sobald der Lag null erreicht (mit pg_stat_replication prüfen); halte bei Bedarf eine umgekehrte Subscription für ein schnelles Rollback bereit.","Einschränkungen: Large Objects und einige DDL werden nicht repliziert; wal_level=logical sowie ausreichende Werte für max_replication_slots/max_wal_senders müssen gesetzt sein."],"level":"expert"},{"id":"postgres-pgvector-similarity","title":"PostgreSQL: Ähnlichkeitssuche mit pgvector","category":"PostgreSQL","summary":"pgvector speichert Vektor-Embeddings und führt eine approximative Nächste-Nachbarn-Suche (ANN) aus; HNSW- und IVFFlat-Indizes bestimmen den Kompromiss aus Genauigkeit, Geschwindigkeit und Speicher.","points":["Wähle den Distanzoperator nach Anwendungsfall: <-> L2 (euklidisch), <=> Kosinus, <#> inneres Produkt; der Index muss dieselbe Operatorklasse wie die Abfrage verwenden, sonst wird er nicht genutzt.","HNSW baut einen mehrschichtigen Graphen auf; er liefert hohen Recall und schnelle Abfragen, braucht aber mehr Speicher und wird langsam aufgebaut. m (Anzahl der Verbindungen) und ef_construction steuern die Aufbauqualität, ef_search die Genauigkeit zur Abfragezeit.","IVFFlat teilt die Vektoren in lists Cluster auf und durchsucht pro Abfrage nur probes Cluster; es braucht weniger Speicher, aber der Index muss NACH dem Laden der Daten erstellt werden (auf einer leeren Tabelle sind die Cluster bedeutungslos).","Beginne bei IVFFlat mit lists ≈ rows/1000 bis 1M Zeilen und ≈ sqrt(rows) über 1M; mehr probes erhöht den Recall, verlangsamt aber die Abfrage – stimme diesen Kompromiss anhand von Messungen ab.","ANN liefert Näherungsergebnisse; ist 100 % Genauigkeit erforderlich, braucht es einen exakten Scan ohne Index. Bei gefilterten Suchen (WHERE) kann der Index-Recall sinken; erwäge bei Bedarf einen iterativen Scan oder einen partiellen Index.","Vektordimension und Zeilenanzahl bestimmen den Speicherbedarf; halte maintenance_work_mem während des Aufbaus groß genug, sonst lagert der HNSW-Aufbau auf die Festplatte aus und wird sehr langsam."],"level":"expert"},{"id":"postgres-citus-distributed","title":"PostgreSQL: verteilte Skalierung mit Citus","category":"PostgreSQL","summary":"Citus skaliert PostgreSQL horizontal, indem es Tabellen anhand einer Verteilungsspalte in Shards aufteilt und auf Worker-Knoten verteilt; die Wahl der Verteilungsspalte bestimmt die Performance.","points":["Der Coordinator-Knoten nimmt die Abfrage entgegen und leitet sie anhand der Verteilungsspalte an die relevanten Shards weiter; Worker-Knoten speichern die Shards und verarbeiten sie parallel. Shards sind normale PostgreSQL-Tabellen (nach Hash-Bereich aufgeteilt).","Die Verteilungsspalte sollte der gemeinsame Filter der meisten Abfragen und JOINs sein (z. B. tenant_id); Zeilen mit demselben Spaltenwert landen im selben Shard, sodass JOINs und Fremdschlüssel lokal auf demselben Knoten bleiben (Colocation).","Colocation ist entscheidend: Miteinander verknüpfte Tabellen laufen ohne knotenübergreifendes Umverteilen der Daten (Repartition), wenn sie mit derselben Verteilungsspalte und Shard-Anzahl verteilt sind.","Mache kleine Tabellen, die häufig mit allen Shards gejoint werden, zu Referenztabellen; sie werden vollständig auf jeden Worker kopiert und vermeiden Netzwerkkosten bei verteilten JOINs.","Abfragen ohne die Verteilungsspalte werden an alle Shards verteilt und sind teuer; in mandantenfähigen SaaS-Anwendungen reduziert ein Filter auf tenant_id die Abfrage auf einen einzigen Shard (Router Execution)."],"level":"expert"},{"id":"postgres-timescaledb-hypertable","title":"PostgreSQL: TimescaleDB-Hypertables und Continuous Aggregates","category":"PostgreSQL","summary":"TimescaleDB skaliert Zeitreihendaten mit Hypertables, die sie automatisch in Chunks nach Zeitintervallen aufteilen, und mit vorberechneten Continuous Aggregates.","points":["Eine Hypertable sieht von außen wie eine einzige Tabelle aus, ist intern aber nach der Zeitspalte (und optional einer Space-Spalte) in Chunks aufgeteilt; Abfragen greifen nur auf die Chunks im relevanten Zeitraum zu (Chunk Exclusion).","Setze chunk_time_interval so, dass die aktivsten Chunks in den Speicher passen (shared_buffers); zu klein verursacht übermäßigen Chunk-/Planungsaufwand, zu groß führt zu schwacher Exclusion.","Ein Continuous Aggregate ist eine inkrementell aktualisierte Materialized View; eine Refresh-Policy hält sie im Hintergrund aktuell und berechnet dabei nur die geänderten Zeit-Buckets neu.","Mit Real-Time Aggregation kombiniert ein Continuous Aggregate die materialisierten älteren Daten mit den neuesten Daten der Rohtabelle und liefert so zur Abfragezeit aktuelle Ergebnisse.","Alte Chunks lassen sich mit spaltenorientierter Kompression stark verkleinern; eine Retention-Policy entfernt Chunks ab einem bestimmten Alter automatisch mit drop_chunks (weit günstiger als zeilenweises DELETE)."],"level":"expert"},{"id":"postgres-vacuum-xid-wraparound","title":"PostgreSQL: VACUUM und XID-Wraparound","category":"PostgreSQL","summary":"MVCC hinterlässt tote Zeilenversionen; VACUUM räumt sie auf und markiert Zeilen als 'frozen' und verhindert so Datenverlust durch den Wraparound der 32-Bit-Transaktions-ID (XID).","points":["PostgreSQL verwendet zirkuläre 32-Bit-XIDs; jede Zeilenversion trägt ein xmin. Werden ausreichend alte Zeilen nicht eingefroren, erscheinen beim XID-Wraparound 'vergangene' Transaktionen als 'zukünftige', und Zeilen können plötzlich unsichtbar werden.","autovacuum erledigt zwei Aufgaben: regelmäßiges Aufräumen (Dead Tuples, Bloat) und Einfrieren; für Tabellen, die sich autovacuum_freeze_max_age nähern, wird ein verpflichtendes 'Anti-Wraparound'-Vacuum ausgelöst, selbst wenn autovacuum deaktiviert ist.","Je näher der Wraparound rückt, desto mehr Warnungen erscheinen im Log; kommt die Grenze sehr nahe, verweigert die Datenbank neue Schreibvorgänge und wird schreibgeschützt, um Datenverlust zu verhindern – Abhilfe schafft ein aggressives VACUUM (FREEZE).","Überwache die älteste nicht eingefrorene XID mit pg_database.datfrozenxid und age(); lang offene Transaktionen (idle in transaction) und ungenutzte Replication Slots blockieren den Fortschritt von VACUUM und erhöhen Bloat und Wraparound-Risiko.","Unter hoher Schreiblast führt ein zurückfallendes autovacuum zu Bloat; erhöhe autovacuum_vacuum_cost_limit, verringere naptime und setze für große/stark genutzte Tabellen tabellenspezifische autovacuum-Einstellungen.","VACUUM gibt Speicherplatz in der Regel nicht an das Betriebssystem zurück, sondern markiert ihn zur Wiederverwendung. Physisches Verkleinern erfordert VACUUM FULL (sperrt die Tabelle) oder pg_repack (online)."],"level":"expert"},{"id":"mysql-innodb-cluster","title":"MySQL: Group Replication und InnoDB Cluster","category":"MySQL","summary":"InnoDB Cluster ist ein auf Group Replication basierender Mehrheits-Cluster (Quorum), der Hochverfügbarkeit bietet, indem er der Anwendung über MySQL Router einen einzigen Verbindungspunkt bereitstellt.","points":["Group Replication verteilt Transaktionen über eine Paxos-basierte Gruppenkommunikationsschicht an die Gruppe; eine Transaktion wird erst nach Zustimmung einer Mehrheit der Gruppe (Quorum) committet, was automatisches Failover und Konsistenz sicherstellt.","Im Single-Primary-Modus (Standard) sind Schreibvorgänge nur auf einem Knoten erlaubt, und die Gruppe wählt bei Ausfall der Primary automatisch eine neue; im Multi-Primary-Modus nehmen alle Knoten Schreibvorgänge an, aber die Konfliktbehandlung (Certification) wird komplexer.","MySQL Router liest die Cluster-Metadaten und bietet der Anwendung zwei Ports: Schreibvorgänge gehen an die aktuelle Primary (6446), Lesevorgänge an die Replicas (6447); beim Failover wird das Routing aktualisiert, ohne dass die Anwendung ihre Verbindung ändern muss.","Der Verlust der Mehrheit (z. B. 2 von 3 Knoten ausgefallen) blockiert die Gruppe; um Split-Brain zu verhindern, nimmt der Cluster keine neuen Schreibvorgänge an, und die Wiederherstellung erfolgt manuell mit group_replication_force_members.","Group Replication erfordert zeilenbasiertes Binlog (ROW-Format), GTID und InnoDB; ein Primärschlüssel auf jeder Tabelle ist Pflicht, sonst kann der Knoten dem Cluster nicht beitreten.","Ein neu beitretender Knoten synchronisiert sich zunächst per State Transfer von einem vorhandenen Donor (Distributed Recovery: Clone-Plugin oder Binlog) und wechselt dann in den laufenden Stream."],"level":"expert"},{"id":"mysql-semisync-replication","title":"MySQL: Semi-Sync-Replikation","category":"MySQL","summary":"Semisynchrone Replikation verringert Datenverlust, da die Primary vor der Bestätigung eines Commits wartet, bis mindestens eine Replica die Transaktion in ihr Relay Log übernommen hat.","points":["Bei asynchroner Replikation bestätigt die Primary den Commit, ohne auf die Replica zu warten; stürzt die Primary ab, gehen noch nicht übertragene Transaktionen verloren. Semi-Sync wartet auf eine Empfangsbestätigung von mindestens N Replicas (rpl_semi_sync_source_wait_for_replica_count).","AFTER_SYNC (Standard, empfohlen): Die Primary erhält zuerst die Bestätigung der Replica und meldet dann den Commit an den Client; so ist garantiert, dass jede Transaktion, die der Client als 'erfolgreich' sieht, auf mindestens einer Replica vorhanden ist.","Im Modus AFTER_COMMIT committet die Primary zuerst lokal und wartet dann auf die Replica; hinkt die Replica hinterher, können andere Sessions Daten sehen, die noch nicht repliziert sind (ähnlich einem Phantom Read).","Wird rpl_semi_sync_source_timeout überschritten, fällt die Primary automatisch auf asynchron zurück und hält nicht an; sie kehrt zu Semi-Sync zurück, sobald die Replica wieder da ist – die 'Garantie' ist also bedingt.","Semi-Sync garantiert 'empfangen', nicht 'auf der Replica angewendet'; für striktes RPO=0 sind Group Replication oder eine externe Konsensschicht besser geeignet."],"level":"expert"},{"id":"mysql-undo-purge","title":"MySQL InnoDB: Undo-Tablespaces und Purge","category":"MySQL","summary":"InnoDB hält alte Zeilenversionen für MVCC im Undo Log; Purge-Threads räumen Undo-Datensätze und tote Versionen auf, die kein Leser mehr benötigt.","points":["Jede Änderung erzeugt einen Undo-Log-Datensatz für Rollback und konsistente Lesevorgänge; eine lang laufende Transaktion hält alte Read Views am Leben, blockiert so den Purge-Fortschritt und lässt Undo wachsen (die History List Length steigt).","Wächst die History List Length (SHOW ENGINE INNODB STATUS) ständig, kommt Purge nicht hinterher; Hauptursache sind meist lange offene Transaktionen oder aktiv gehaltene alte Lesevorgänge (z. B. mysqldump --single-transaction).","innodb_purge_threads steuert die Parallelität von Purge, und innodb_max_purge_lag verzögert DML künstlich, wenn Purge weit zurückliegt, damit die Last Purge nicht überrollt.","In MySQL 8 liegt Undo in eigenen Undo-Tablespaces, und wachsende Undo-Dateien lassen sich mit automatischem Truncate (innodb_undo_log_truncate) verkleinern; in älteren Versionen lag Undo im System-Tablespace und schrumpfte nie.","Aufgeblähtes Undo zwingt konsistente Lesevorgänge, längere Versionsketten zu durchlaufen (I/O und CPU); kurze Transaktionen und Disziplin bei autocommit minimieren den Undo-Druck."],"level":"expert"},{"id":"mysql-buffer-pool-lru","title":"MySQL InnoDB: Buffer-Pool-Instanzen und LRU","category":"MySQL","summary":"Der InnoDB-Buffer-Pool hält Daten- und Indexseiten im Speicher; der LRU-Algorithmus mit Midpoint Insertion verhindert, dass Table Scans heiße Daten aus dem Speicher verdrängen.","points":["Auf einem dedizierten Datenbankserver wird der Buffer Pool typischerweise auf ~70-80 % des RAM gesetzt (innodb_buffer_pool_size; auf einem geteilten Server weniger); Ziel ist, das häufig genutzte Working Set im Speicher zu halten und Festplatten-I/O zu minimieren.","Die LRU-Liste ist in die Teillisten 'young' (heiß) und 'old' (kalt) aufgeteilt; eine neu gelesene Seite wird nicht am Kopf der Liste, sondern am Kopf des Old-Bereichs eingefügt (Midpoint, ~37 %), sodass ein einmaliger Scan keine heißen Seiten verdrängt.","Eine Seite im Old-Bereich wird nur dann nach young befördert, wenn nach Ablauf von innodb_old_blocks_time erneut auf sie zugegriffen wird; das verhindert, dass große Full Scans den Buffer Pool verunreinigen.","innodb_buffer_pool_instances teilt den Pool in mehrere unabhängige Instanzen; da jede Instanz ihren eigenen Mutex hat, sinkt die Mutex-Contention im Buffer Pool bei hoher Concurrency (sinnvoll bei großen Pools).","Überwache die Trefferquote des Buffer Pools und das Verhältnis freier/Dirty Pages; viele Dirty Pages und aggressives Flushing können einen 'Checkpoint-Sturm' auslösen, und innodb_io_capacity legt die Flush-Rate passend zum Storage fest."],"level":"expert"},{"id":"sqlserver-always-on-ag","title":"SQL Server: Always On Availability Groups","category":"SQL Server","summary":"Eine Availability Group repliziert eine Gruppe von Datenbanken auf synchrone/asynchrone Replicas; auf WSFC bietet sie automatisches Failover, lesbare Replicas und Read-only Routing.","points":["Bei einer Replica mit synchronem Commit wartet die Primary vor dem Commit, bis die Secondary den Log-Datensatz gehärtet hat; das sorgt für RPO=0, erhöht aber bei hinterherhinkender Replica die Commit-Latenz auf der Primary.","Eine Replica mit asynchronem Commit lässt die Primary nicht warten; sie eignet sich für entfernte Disaster-Recovery-Standorte (DR), aber beim Failover können Daten verloren gehen (RPO>0), und es wird nur ein erzwungenes manuelles Failover (FORCE_FAILOVER_ALLOW_DATA_LOSS) unterstützt.","Automatisches Failover erfolgt nur zwischen einem Replica-Paar im Modus synchroner Commit und 'automatisches Failover', solange das WSFC-Quorum besteht; Health-Checks erfolgen über sp_server_diagnostics und die Database Health Detection.","Eine lesbare Secondary Replica entlastet die Primary vom Reporting; dank Read-only Routing werden Clients, die sich mit ApplicationIntent=ReadOnly verbinden, über den Listener automatisch zu einer lesbaren Replica geleitet.","Abfragen auf einer lesbaren Replica verwenden automatisch Snapshot Isolation; dafür wird geänderten Zeilen auf der Primary ein 14-Byte-Versions-Tag hinzugefügt, und der tempdb-Version-Store auf der Secondary wird belastet; außerdem können die Daten auf der Replica veralten, wenn das Redo Apply hinterherhinkt.","Der AG-Listener bietet Clients mit einem virtuellen Netzwerknamen + IP einen einzigen Verbindungspunkt; beim Failover wechselt der Listener zur neuen Primary, und die Anwendung arbeitet weiter, ohne ihren Connection String zu ändern."],"level":"expert"},{"id":"sqlserver-columnstore","title":"SQL Server: Columnstore-Indizes und Batch Mode","category":"SQL Server","summary":"Ein Columnstore-Index speichert Daten spaltenweise mit hoher Kompression; Batch-Mode-Verarbeitung und Segment Elimination beschleunigen analytische Abfragen erheblich.","points":["Die Daten werden in Rowgroups (~1 Million Zeilen) in Spaltensegmente aufgeteilt, und jedes Segment wird komprimiert; gelesen werden nur die Spalten, die die Abfrage benötigt, was die I/O deutlich reduziert.","Jedes Segment enthält Min/Max-Metadaten; liegt das Abfrageprädikat außerhalb des Wertebereichs des Segments, wird es übersprungen, ohne gelesen zu werden (Segment Elimination) – daher ist das sortierte Laden der Daten sehr wichtig.","Batch Mode verarbeitet Zeilen in Gruppen von ~900 statt einzeln und steigert so die Arbeit pro CPU-Zyklus; bei Aggregaten und Joins ist er um ein Vielfaches schneller als Row Mode (ab SQL 2019+ ist Batch Mode auch auf Rowstore möglich).","Ein Clustered Columnstore ist ideal für große analytische Faktentabellen bzw. DW-Faktentabellen; für gemischte OLTP- und Analyse-Workloads lässt sich mit einem Nonclustered Columnstore auf einer Rowstore-Tabelle Real-Time Operational Analytics (HTAP) umsetzen.","Heiße (noch nicht komprimierte) Zeilen werden zeilenweise im Deltastore gehalten und vom Tuple Mover komprimiert, sobald er voll ist; viele kleine Ladevorgänge verursachen Fragmentierung, daher ist Bulk Loading vorzuziehen."],"level":"expert"},{"id":"sqlserver-inmemory-oltp","title":"SQL Server: In-Memory OLTP (Hekaton)","category":"SQL Server","summary":"In-Memory OLTP beseitigt Engpässe bei hochgradig parallelen OLTP-Workloads, indem es speicheroptimierte Tabellen mit Lock-/Latch-freien Strukturen und nativ kompilierten Prozeduren verarbeitet.","points":["Speicheroptimierte Tabellen liegen vollständig im Speicher, ebenso alle Zeilenversionen; Concurrency wird mit Lock-freiem optimistischem MVCC gehandhabt, wodurch klassische Latch- und Lock-Contention entfällt.","Für die Dauerhaftigkeit werden Änderungen weiterhin ins Log geschrieben und in Checkpoint-Dateien (data/delta) geschrieben; SCHEMA_ONLY-Tabellen werden nicht protokolliert und beim Neustart geleert (ideal z. B. für flüchtige Staging-/Session-Daten).","Eine nativ kompilierte Stored Procedure übersetzt T-SQL in Maschinencode (DLL) und vermeidet so den Interpretationsaufwand; sie kann nur auf speicheroptimierte Tabellen zugreifen und unterstützt eine eingeschränkte T-SQL-Teilmenge.","Da die Concurrency optimistisch ist, werden Konflikte zur Laufzeit erkannt; ein Write-Write-Konflikt rollt die Transaktion mit einem Fehler zurück (Wiederholung nötig), daher muss die Anwendung eine Retry-Logik enthalten.","Passende Szenarien: sehr hohe Insert-Raten (z. B. IoT-/Telemetrie-Ingest), heiße Tabellen mit Lock-/Latch-Engpässen und temporäre Tabellen, die tempdb nutzen; alle Daten müssen in den Speicher passen, und der Speicherdruck muss genau überwacht werden."],"level":"expert"},{"id":"sqlserver-query-store","title":"SQL Server: Planverwaltung mit Query Store","category":"SQL Server","summary":"Query Store speichert Abfragetexte, Ausführungspläne und Laufzeitstatistiken dauerhaft; damit erkennst du Planregressionen und kannst einen bestimmten Plan erzwingen.","points":["Query Store hält in der Datenbank alle Pläne, die eine Abfrage im Lauf der Zeit verwendet hat, samt Dauer-/CPU-/Lesemetriken pro Plan; so beantwortet es mit historischen Daten die Frage 'warum war diese Abfrage gestern schnell und heute langsam'.","Planregressionen (ein neu kompilierter Plan ist schlechter als der alte) findest du mit den Berichten 'Regressed Queries' und 'Top Resource Consumers'; die Stabilität stellst du wieder her, indem du den bekannten guten Plan mit sys.sp_query_store_force_plan erzwingst.","Mit Query Store Hints in SQL Server 2022 lassen sich Hints (z. B. RECOMPILE, MAXDOP) anwenden, ohne einen Plan zu erzwingen oder den Abfragetext zu ändern; das ist sehr nützlich, um Abfragen von Drittanwendungen zu tunen.","Die automatische Plankorrektur (Automatic Tuning, FORCE_LAST_GOOD_PLAN) erkennt Regressionen, erzwingt automatisch den vorherigen guten Plan und macht dies rückgängig, wenn keine Verbesserung eintritt; aktiviere sie in Produktion unter Beobachtung.","Setze den Capture Mode (ALL/AUTO/CUSTOM) und die Speichergröße korrekt; AUTO filtert seltene/unbedeutende Abfragen heraus, um Overhead und Platz zu sparen, und ein voller Query Store wechselt in den Read-only-Modus und sammelt keine Daten mehr."],"level":"expert"},{"id":"mongodb-sharding-deep","title":"MongoDB: Sharding, Chunks und der Balancer","category":"MongoDB","summary":"MongoDB teilt Daten nach Shard Key in Chunks auf und verteilt sie auf Shards; der Balancer gleicht die Chunks aus, und die Wahl des Shard Keys bestimmt Verteilung und Abfrage-Routing.","points":["Die Daten werden nach Shard-Key-Bereichen in Chunks aufgeteilt und auf die Shards verteilt; der mongos-Router prüft den Shard Key und leitet die Abfrage nur an den relevanten Shard (targeted), andernfalls an alle Shards (Scatter-Gather).","Ein guter Shard Key hat hohe Kardinalität, geringe Häufigkeit und steigt nicht monoton; ein monoton steigender Key (z. B. Zeitstempel/ObjectId) lädt alle neuen Schreibvorgänge auf einen einzigen Shard (Hotspot), Hashed Sharding verteilt sie dagegen.","Wird die Chunk-Anzahl zwischen den Shards ungleich, migriert der Balancer Chunks im Hintergrund; die Konsistenz zwischen Quelle und Ziel bleibt während der Migration erhalten, sie verursacht aber I/O-Kosten und lässt sich in ein Wartungsfenster legen.","Ein Jumbo Chunk überschreitet die maximale Chunk-Größe und lässt sich wegen geringer Kardinalität des Shard Keys nicht teilen; er kann nicht migriert werden und erzeugt ein Ungleichgewicht – Abhilfe schafft ein selektiverer (zusammengesetzter) Shard Key.","Zone Sharding (tag-aware) bindet bestimmte Shard-Key-Bereiche an bestimmte Shards; damit wird die Datenplatzierung geografisch oder nach Hardware gesteuert (z. B. EU-Daten auf einem Shard in der EU-Region).","Auch wenn neuere Versionen statt der Chunk-Anzahl größenbasiertes Balancing und automatische Defragmentierung einführen, bleibt das Prinzip gleich: Für gezielte Abfragen muss der Shard Key in jedem Lese-/Schreibvorgang enthalten sein."],"level":"expert"},{"id":"mongodb-oplog-election","title":"MongoDB: Replica-Set-Oplog und Wahlen","category":"MongoDB","summary":"In einem Replica Set protokolliert die Primary Schreiboperationen im Oplog, und die Secondaries wenden sie an; fällt die Primary aus, wird per Raft-ähnlicher Wahl eine neue Primary bestimmt.","points":["Das Oplog ist eine Capped Collection, die jede Änderung auf der Primary in idempotenter (sicher wiederholbarer) Form protokolliert; Secondaries lesen das Oplog fortlaufend (tail) und wenden es an, um synchron zu bleiben.","Die Oplog-Größe bestimmt das Replikationsfenster; ist eine Secondary länger offline, als das Oplog abdeckt, wird sie 'stale' und braucht eine vollständige Neusynchronisation (Initial Sync) – das Oplog muss daher passend zur Workload dimensioniert sein.","Fällt die Primary aus, stimmen die Secondaries nach einem Raft-ähnlichen Protokoll ab; der Kandidat mit der Stimmenmehrheit wird Primary, daher wird eine ungerade Stimmenzahl (bei Bedarf mit Arbiter) beibehalten, um die Mehrheit zu sichern.","writeConcern 'majority' garantiert, dass ein Schreibvorgang eine Mehrheit der Knoten erreicht hat und beim Failover nicht zurückgerollt wird (Rollback-Schutz); w:1 ist schneller, aber der Schreibvorgang kann bei Ausfall der Primary verloren gehen.","readConcern und Read Preference steuern die Aktualität von Lesevorgängen; Lesen von Secondaries verteilt die Last, kann aber wegen Replikations-Lag veraltete Daten liefern, während 'majority'/'linearizable' stärkere Garantien bieten.","Landet die alte Primary nach einer Netzwerkpartition in der Minderheit, stuft sie sich zur Secondary herab; beim Wiederanschluss an die neue Primary verschiebt sie ihre nicht committeten Schreibvorgänge in eine Rollback-Datei (Majority Write Concern verhindert das)."],"level":"expert"},{"id":"mongodb-wiredtiger-checkpoint","title":"MongoDB: WiredTiger-Checkpoints und das Journal","category":"MongoDB","summary":"WiredTiger sorgt für Dauerhaftigkeit durch periodische Checkpoints und ein Write-Ahead-Journal; mit Concurrency auf Dokumentebene und Kompression erreicht es hohen Durchsatz.","points":["Standardmäßig erstellt WiredTiger etwa alle 60 Sekunden einen Checkpoint und schreibt einen konsistenten In-Memory-Snapshot auf die Festplatte; zwischen den Checkpoints bleibt die Dauerhaftigkeit erhalten, weil jeder Schreibvorgang im Journal (WAL) protokolliert wird.","Ohne Journal gehen bei einem Absturz die Schreibvorgänge nach dem letzten Checkpoint verloren; mit Journal lädt die Recovery den letzten Checkpoint und spielt die nachfolgenden Operationen aus dem Journal ein.","Concurrency Control auf Dokumentebene erlaubt es, verschiedene Dokumente derselben Collection gleichzeitig zu aktualisieren; MVCC-Snapshots sorgen für konsistente Lesevorgänge.","Der WiredTiger-Cache (standardmäßig ~50 % des RAM) hält häufig genutzte Daten und Indizes; passt das Working Set nicht in den Cache, nehmen Eviction und Festplatten-I/O zu, und ein gesättigter Cache verschlechtert die Performance stark.","Die Speicherkompression ist standardmäßig snappy (Balance aus Geschwindigkeit und Rate); zstd/zlib erreichen höhere Raten, brauchen aber mehr CPU. Indizes werden zusätzlich per Präfixkompression verkleinert."],"level":"expert"},{"id":"redis-cluster-hashslot","title":"Redis Cluster: Hash Slots und Resharding","category":"Redis","summary":"Redis Cluster skaliert horizontal, indem es den Keyspace in 16384 Hash Slots aufteilt und auf die Knoten verteilt; Clients finden den richtigen Knoten über MOVED-/ASK-Weiterleitungen.","points":["Jeder Key wird über CRC16(key) mod 16384 einem Slot zugeordnet; jeder Master besitzt bestimmte Slot-Bereiche, sodass der Cluster Daten ohne zentralen Router verteilt.","Wendet sich ein Client an den falschen Knoten, antwortet dieser mit MOVED und nennt den dauerhaften Besitzer des Slots, woraufhin der Client seine Slot-Map aktualisiert. Während des Reshardings wird für den migrierenden Slot eine temporäre ASK-Weiterleitung verwendet.","Resharding verschiebt Slots (samt ihren Keys) im laufenden Betrieb von einem Knoten zum anderen; die Migration erfolgt Key für Key, und der Cluster bleibt dabei durchgehend verfügbar.","Multi-Key-Operationen (MSET, Transaktionen, Lua) funktionieren nur mit Keys im selben Slot; mit Hash Tags ({...}) werden zusammengehörige Keys im selben Slot gruppiert, z. B. user:{123}:name und user:{123}:email.","Gib jedem Master für Hochverfügbarkeit mindestens eine Replica; fällt ein Master aus, wird seine Replica promotet. Der Cluster entscheidet per Mehrheit der Master, und ohne Mehrheit werden Schreibvorgänge gestoppt.","Ist ein Slot nicht zugewiesen oder hat er keinen Master, lehnt der Cluster alle Schreibvorgänge ab (wenn cluster-require-full-coverage aktiv ist); alle 16384 Slots müssen zugewiesen sein."],"level":"expert"},{"id":"redis-replication-psync","title":"Redis: Replikation und PSYNC","category":"Redis","summary":"Bei der asynchronen Redis-Replikation überträgt der Master einen Daten-Snapshot an die Replica und streamt danach die folgenden Befehle; PSYNC führt nach kurzen Verbindungsabbrüchen eine partielle Neusynchronisation durch.","points":["Bei der ersten Synchronisation erzeugt der Master einen RDB-Snapshot und sendet ihn an die Replica (Full Resync); währenddessen puffert er eingehende Schreibvorgänge in einem Replication Backlog Buffer und streamt sie nach dem RDB.","PSYNC verfolgt den Replikations-Offset der Replica und die Replikations-ID des Masters; liegen nach einem kurzen Verbindungsabbruch die verpassten Daten noch im Backlog Buffer, wird nur der fehlende Teil gesendet (Partial Resync) und ein teurer Full Resync vermieden.","repl-backlog-size bestimmt, wie lange ein Verbindungsabbruch toleriert wird; bei hohen Schreibraten erzwingt ein kleiner Backlog bei Abbrüchen einen Full Resync, also RDB-Erzeugung auf dem Master und Netzwerkkosten.","Die Replikation ist asynchron: Der Master bestätigt einen Schreibvorgang, ohne auf Replicas zu warten, daher können bei einem plötzlichen Absturz des Masters die letzten Schreibvorgänge verloren gehen; mit dem Befehl WAIT lässt sich warten, bis eine bestimmte Anzahl Replicas sie empfangen hat, das ist aber keine echte synchrone Garantie.","Mit Diskless Replication (repl-diskless-sync) kann der Master das RDB direkt in den Socket streamen, ohne es auf die Festplatte zu schreiben; das beschleunigt die initiale Synchronisation in Umgebungen mit langsamen Festplatten, aber schnellem Netzwerk."],"level":"expert"},{"id":"redis-memory-fragmentation","title":"Redis: Speicherfragmentierung und jemalloc","category":"Redis","summary":"Redis verwendet standardmäßig jemalloc; Allokationen und Freigaben unterschiedlicher Größe führen mit der Zeit zu Speicherfragmentierung, sodass RSS größer erscheint als die tatsächlich genutzten Daten.","points":["mem_fragmentation_ratio = used_memory_rss / used_memory; ein Wert nahe 1 ist ideal, deutlich über 1 (z. B. >1.5) deutet auf Fragmentierung hin, und unter 1 bedeutet, dass Daten auf die Festplatte ausgelagert werden (schlecht).","Fragmentierung entsteht, wenn ständig Keys unterschiedlicher Größe angelegt und gelöscht werden und unbrauchbare Lücken in den jemalloc-Arenen hinterlassen; häufige Änderungen an großen Werten verschärfen das.","Active Defragmentation (activedefrag yes) verdichtet Speicherblöcke zur Laufzeit mithilfe von jemalloc und reduziert so die Fragmentierung; ihre Schwellenwerte (active-defrag-ignore-bytes, -threshold-lower/upper) werden abgestimmt, um den Overhead zu begrenzen.","Setze maxmemory und maxmemory-policy korrekt; da der von jemalloc tatsächlich belegte RSS maxmemory übersteigen kann, halte maxmemory unter dem RAM, um einen OOM des Betriebssystems zu vermeiden.","Ist used_memory niedrig, RSS aber dauerhaft hoch, setzt ein Neustart (oder ein per Failover neu gestarteter Knoten) die Fragmentierung meist dauerhaft zurück; Active Defrag macht Neustarts in den meisten Fällen überflüssig."],"level":"expert"},{"id":"elasticsearch-shard-sizing","title":"Elasticsearch: Shard-Dimensionierung","category":"Elasticsearch","summary":"Jeder Shard ist ein Lucene-Index und verursacht feste Ressourcenkosten; zu viele kleine Shards (Oversharding) oder zu große Shards beeinträchtigen Performance und Stabilität.","points":["Faustregel: Halte einen Shard im Bereich von 10-50 GB; sehr große Shards verlangsamen Recovery und Rebalancing, sehr kleine Shards (Oversharding) belasten Cluster State und Heap unnötig.","Jeder Shard und vor allem jedes gemappte Feld verbraucht Heap. Die alte Regel \"~20 Shards pro GB Heap\" gilt ab 8.3 nicht mehr (der Heap-Overhead pro Shard ist stark gesunken); aktuelle Empfehlung ist, auf Data Nodes ~1 KB Heap pro gemapptem Feld und Index plus Overhead einzuplanen und die Shards pro Knoten unter cluster.max_shards_per_node (Standard 1000) zu halten. Zu viele Shards vergrößern weiterhin den Cluster State und erhöhen den Koordinationsaufwand.","Die Abfragelatenz hängt von der Shard-Anzahl ab: Eine Abfrage wird an alle relevanten Shards verteilt und die Ergebnisse werden zusammengeführt; mehr Shards als nötig verlangsamen durch Koordinationsaufwand selbst kleine Abfragen.","Verwende für Zeitreihen-/Log-Daten zeitbasierte Indizes (Data Streams) statt eines riesigen Index; so wird das Verschieben/Löschen alter Daten pro Index (ILM) günstig und die Shards bleiben handhabbar.","Die Anzahl der Primary Shards wird beim Anlegen des Index festgelegt und lässt sich später nicht ändern (nur per Reindex/Split/Shrink); plane sie daher von Anfang an anhand der Wachstumsprognose richtig, während sich die Replica-Anzahl dynamisch anpassen lässt."],"level":"expert"},{"id":"elasticsearch-ilm-tiers","title":"Elasticsearch: ILM und Hot-Warm-Cold-Frozen-Tiers","category":"Elasticsearch","summary":"Index Lifecycle Management verwaltet Zeitreihendaten wirtschaftlich, indem es Indizes je nach Lebenszyklusphase automatisch zwischen Kosten-/Performance-Tiers verschiebt.","points":["Eine ILM-Policy führt einen Index durch Phasen: hot (aktive Schreibvorgänge + häufige Abfragen, schnelle SSD), warm (Schreiben abgeschlossen, gelegentliche Abfragen), cold (seltene Abfragen, günstigere Hardware), frozen (sehr selten, per Searchable Snapshots aus dem Object Storage bedient).","Rollover legt einen neuen Index an, wenn der Hot-Index eine bestimmte Größe/ein bestimmtes Alter/eine bestimmte Dokumentanzahl erreicht; so wächst kein einzelner Index endlos, und jede Phase wird pro Index verwaltet.","Der Frozen Tier hält Daten per Searchable Snapshots im Object Storage (S3/Blob) und lädt sie nur bei Bedarf teilweise in den Speicher; das senkt die Festplattenkosten stark, erhöht aber die Abfragelatenz.","Beim Wechsel nach warm/cold werden Storage und Heap mit Force Merge, Shrink und weniger Replicas optimiert; da sich die Daten in diesen Phasen nicht mehr ändern, können die Segmente zu einem einzigen Segment zusammengeführt werden.","Ordne Data Tiers den Knotenrollen zu (data_hot/data_warm/data_cold/data_frozen); ILM nutzt Shard-Allocation-Filter, um den Index auf Knoten mit der passenden Hardware zu platzieren."],"level":"expert"},{"id":"elasticsearch-segment-merge","title":"Elasticsearch: Segment Merging und Force Merge","category":"Elasticsearch","summary":"Lucene erzeugt bei jedem Refresh neue unveränderliche Segmente; das Merging im Hintergrund fasst Segmente zusammen und entfernt sie, während Force Merge ein manueller Vorgang ist, der mit Vorsicht einzusetzen ist.","points":["Jeder Refresh (Standard 1 s) erzeugt ein neues Segment, und Löschungen werden im Segment als 'Tombstones' markiert; viele kleine Segmente verlangsamen Lesevorgänge, daher fasst das Hintergrund-Merging sie zusammen.","Gelöschte/aktualisierte Dokumente werden erst bei einem Merge physisch von der Festplatte entfernt; bei hoher Update-Last sammeln sich 'deleted docs' in den Segmenten, und der Festplatten-/Speicherverbrauch wächst.","Bei Indizes, in die nicht mehr geschrieben wird (read-only), verbessert ein Force Merge auf 1 Segment die Abfragegeschwindigkeit und die Speichereffizienz; das ist ein typischer Schritt in der ILM-Warm-Phase.","Führe NIE einen Force Merge auf einem aktiv beschriebenen Index aus; das entstehende sehr große Segment nimmt nicht am normalen Merge-Zyklus teil, spätere Löschungen können nicht bereinigt werden, und es belastet I/O und Heap lange Zeit.","Ein vorübergehend erhöhtes refresh_interval während Bulk Loads (z. B. -1 oder 30s) erzeugt weniger, größere Segmente, verringert die Merge-Last und verbessert den Schreibdurchsatz."],"level":"expert"},{"id":"elasticsearch-node-roles","title":"Elasticsearch: Knotenrollen und Cluster-Topologie","category":"Elasticsearch","summary":"Elasticsearch-Knoten übernehmen Rollen wie master, data, coordinating und ingest; in großen Clustern sorgt die Trennung der Rollen für Stabilität und Skalierbarkeit.","points":["Master-eligible-Knoten verwalten den Cluster State (Indizes, Shard-Platzierung, Mappings); verwende für stabile Wahlen eine ungerade Anzahl (meist 3) dedizierter Master-Knoten und verlasse dich auf ein Quorum (Mehrheit), um Split-Brain zu verhindern.","Data Nodes hosten Shards und übernehmen Indexierung und Abfrage-I/O; sie verbrauchen am meisten CPU/RAM/Festplatte und können in Data Tiers (hot/warm/cold/frozen) aufgeteilt werden.","Ein reiner Coordinating-Knoten hat keine weiteren Rollen, verteilt Anfragen an die relevanten Shards und führt die Ergebnisse zusammen (Scatter-Gather); er schützt Data Nodes, indem er aufwendiges Zusammenführen von Suchen/Aggregationen übernimmt, und dient als Load Balancer.","Ein Ingest-Knoten verarbeitet Daten vor der Indexierung über Pipelines (Transformation, Anreicherung); das Auslagern aufwendiger Ingest-Pipelines auf dedizierte Ingest-Knoten schützt die Abfrage-Performance der Data Nodes.","Lass Master-Knoten nie schwere Abfrage-/Datenlast mittragen; ein unter Last verlangsamter Master verzögert Aktualisierungen des Cluster State und verursacht Instabilität im gesamten Cluster (verzögerte Shard-Allokation, Wahlen)."],"level":"expert"}]},"guide/glossary.json":{"terms":[{"term":"ACID","full":"Atomicity, Consistency, Isolation, Durability","definition":"Die vier Kerneigenschaften, die Transaktionen zuverlässig machen: Atomicity (alles oder nichts), Consistency (Regeln bleiben gewahrt), Isolation (parallele Transaktionen beeinträchtigen sich nicht) und Durability (committete Daten gehen nicht verloren). Sie bilden die Transaktionsgarantie relationaler Datenbanken.","tags":["transaction","transaction","acid","consistency","consistency"]},{"term":"BASE","full":"Basically Available, Soft state, Eventually consistent","definition":"Ein gelockerter Konsistenzansatz als Alternative zu ACID, bevorzugt in verteilten und NoSQL-Systemen. Das System priorisiert ständige Verfügbarkeit und akzeptiert, dass die Daten mit der Zeit konsistent werden (Eventual Consistency). Strikte Konsistenz wird gegen Skalierbarkeit eingetauscht.","tags":["base","distributed","distributed","eventual-consistency","nosql"]},{"term":"MVCC","full":"Multi-Version Concurrency Control","definition":"Ein Concurrency-Verfahren, das mehrere Versionen derselben Zeile vorhält, damit Leser und Schreiber arbeiten können, ohne sich gegenseitig zu sperren. Jede Transaktion sieht ihren eigenen Snapshot der Daten, sodass Lesevorgänge keine Schreibvorgänge blockieren und umgekehrt. Verwendet in Systemen wie PostgreSQL und Oracle.","tags":["concurrency","concurrency","lock","mvcc","snapshot"]},{"term":"WAL","full":"Write-Ahead Log","definition":"Das Prinzip, dass ein Log-Datensatz dauerhaft auf die Festplatte geschrieben wird, BEVOR die Änderung in die Datendateien geschrieben wird. Darauf beruhen Crash Recovery und Dauerhaftigkeit. In PostgreSQL heißt es WAL, in SQL Server Transaktionsprotokoll.","tags":["wal","log","recovery","recovery","durability"]},{"term":"Redo-Log","full":"","definition":"Das Log, das Änderungen an der Datenbank aufzeichnet und dazu dient, bei einem Absturz verlorene Arbeit erneut anzuwenden (redo). Bei der Recovery werden damit Änderungen nachgefahren, die committet, aber noch nicht auf die Festplatte geschrieben waren. Das Redo-Log von Oracle entspricht dem allgemeinen WAL-Konzept.","tags":["redo","log","recovery","recovery","oracle"]},{"term":"Undo / Rollback Segment","full":"","definition":"Der Bereich, der das vorherige Abbild der von einer Transaktion geänderten Daten speichert. Er dient zwei Zwecken: dem Rückgängigmachen einer Änderung per Rollback und der Bereitstellung der alten Version für Leser unter MVCC. In Oracle heißt er Undo/Rollback Segment, in anderen Systemen Undo/Version Store.","tags":["undo","rollback","rollback","mvcc","segment"]},{"term":"Buffer Pool / Buffer Cache","full":"","definition":"Der Speicher-Cache, in dem häufig genutzte Daten- und Indexseiten gehalten werden. Da Festplattenzugriffe teuer sind, hängt die Datenbank-Performance stark von der Trefferquote hier ab (Cache Hit Ratio). In MySQL InnoDB heißt er Buffer Pool, in SQL Server Buffer Cache.","tags":["buffer-pool","buffer-cache","cache","cache","memory"]},{"term":"Dirty Page","full":"","definition":"Eine Datenseite, die im Speicher geändert, aber noch nicht auf die Festplatte geschrieben wurde. Die Dauerhaftigkeit wird durch WAL geschützt; die Seite selbst bleibt dirty, bis der Checkpoint oder der Background Writer sie auf die Festplatte schreibt. Zu viele Dirty Pages verlängern die Recovery-Zeit.","tags":["dirty-page","dirty-page","memory","checkpoint","io"]},{"term":"LSN","full":"Log Sequence Number","definition":"Eine stetig steigende, eindeutige Sequenznummer, die jedem Datensatz im WAL/Transaktionsprotokoll zugewiesen wird. Mit ihr wird bestimmt, bis zu welchem Log-Datensatz eine Seite aktuell ist und wo die Recovery fortfahren soll. Sie verfolgt auch den Fortschritt bei der Replikation.","tags":["lsn","log","sequence-number","recovery","replication"]},{"term":"SCN","full":"System Change Number","definition":"In Oracle ein monoton steigender logischer Zeitstempel, der jedem Commit-Punkt in der Datenbank zugewiesen wird. Er dient der Lesekonsistenz, MVCC und der Reihenfolge von Ereignissen bei der Recovery. Man kann ihn als Gegenstück von Oracle zur LSN betrachten.","tags":["scn","oracle","timestamp","read-consistency","commit"]},{"term":"Isolationsstufe","full":"Isolationsstufe","definition":"Die Einstellung, die festlegt, inwieweit parallele Transaktionen die Änderungen der jeweils anderen sehen können. Die Stufen sind Read Uncommitted, Read Committed, Repeatable Read und Serializable; mit steigender Stufe nimmt die Konsistenz zu und die Concurrency ab. Sie steuert die Anomalien Dirty, Non-repeatable und Phantom Read.","tags":["isolation","isolation-level","transaction","concurrency","concurrency"]},{"term":"Dirty Read","full":"","definition":"Eine Transaktion liest eine Änderung, die eine andere Transaktion noch nicht committet hat (und eventuell zurückrollt). Da die gelesenen Daten ungültig sein können, ist dies eine Konsistenzanomalie. Möglich nur in der Isolationsstufe Read Uncommitted.","tags":["dirty-read","anomaly","isolation","transaction","consistency"]},{"term":"Non-repeatable Read","full":"","definition":"Beim zweimaligen Lesen derselben Zeile innerhalb derselben Transaktion erscheint ein anderer Wert, weil eine andere Transaktion zwischenzeitlich committet hat. Kann bei Read Committed auftreten; ab Repeatable Read wird es verhindert.","tags":["non-repeatable-read","anomaly","isolation","transaction","non-repeatable"]},{"term":"Phantom Read","full":"","definition":"Die Ergebnismenge ändert sich, wenn eine Abfrage innerhalb derselben Transaktion mit derselben Bedingung erneut ausgeführt wird, weil zwischenzeitlich eingefügt/gelöscht wurde (neue 'Phantom'-Zeilen erscheinen). Laut SQL-Standard wird es bei Serializable verhindert; PostgreSQL verhindert es auch bei Repeatable Read.","tags":["phantom-read","phantom","anomaly","serializable","isolation"]},{"term":"Deadlock","full":"","definition":"Eine Situation, in der zwei oder mehr Transaktionen jeweils auf einen Lock warten, den die andere hält, sodass keine fortfahren kann. Die Datenbank erkennt dies und löst den Zyklus auf, meist indem sie die günstigste Transaktion als 'Opfer' wählt und zurückrollt. Locks stets in derselben Reihenfolge anzufordern verringert das Risiko.","tags":["deadlock","deadlock","lock","lock","concurrency"]},{"term":"Lock Escalation","full":"","definition":"Hält eine Transaktion viele Zeilen-/Seiten-Locks, wandelt die Datenbank sie in einen einzigen gröberen Lock um (z. B. einen Tabellen-Lock). Das spart Speicher für die Lock-Verwaltung, senkt aber die Concurrency und kann Blockierungen verstärken. Vor allem in SQL Server anzutreffen.","tags":["lock-escalation","lock","lock","concurrency","sql-server"]},{"term":"Latch","full":"","definition":"Eine leichtgewichtige, kostengünstige Concurrency-Sperre, die In-Memory-Strukturen (z. B. Buffer-Pool-Seiten) für sehr kurze Zeit schützt. Anders als Transaktions-Locks schützt sie die physische Speicherstruktur statt logischer Daten und wird nur sehr kurz gehalten.","tags":["latch","lock","memory","concurrency","internal"]},{"term":"B-Tree-Index","full":"Balanced Tree Index","definition":"Der häufigste Indextyp, der Daten in einer sortierten, balancierten Baumstruktur hält. Er ist effizient für Gleichheits- und Bereichsabfragen, Sortierung und Präfixsuchen. In den meisten Datenbanken ist er der Standard-Indextyp.","tags":["btree","index","index","search","range"]},{"term":"Hash-Index","full":"","definition":"Ein Indextyp, der einen Key über eine Hash-Funktion auffindet. Er ist nur für exakte Gleichheitssuchen (=) sehr schnell; für Bereichsabfragen oder Sortierung ist er nicht nutzbar. Verfügbar z. B. in PostgreSQL und in MySQL-Memory-Tabellen.","tags":["hash-index","index","index","equality","search"]},{"term":"Bitmap-Index","full":"","definition":"Ein Index, der für jeden distinkten Wert Bit-Arrays hält und sich für Spalten mit geringer Kardinalität (wenige distinkte Werte) eignet. Er erlaubt es, mehrere Bedingungen schnell mit AND/OR zu kombinieren; effektiv für Data-Warehouse-/OLAP-Workloads, aber ineffizient bei hoher Schreiblast.","tags":["bitmap-index","index","index","olap","cardinality"]},{"term":"Covering Index","full":"","definition":"Ein Index, der alle Spalten enthält, die eine Abfrage benötigt; die Abfrage wird allein aus dem Index bedient, ohne auf die Tabelle (Heap/Clustered) zuzugreifen. Das verbessert die Performance, da teure Zeilen-Lookups entfallen. Er kann mit INCLUDE-Spalten angelegt werden.","tags":["covering-index","index","index","performance","lookup"]},{"term":"Clustered Index","full":"","definition":"Ein Index, der die Zeilen der Tabelle physisch nach dem Indexschlüssel sortiert hält; die Tabelle selbst wird in dieser Reihenfolge gespeichert. Eine Tabelle kann nur einen davon haben. Er ist schnell bei Bereichsabfragen; in InnoDB ist der Primärschlüssel immer clustered.","tags":["clustered-index","index","index","physical","primary-key"]},{"term":"Kardinalität","full":"","definition":"Die Anzahl distinkter Werte in einer Spalte. Hohe Kardinalität (z. B. E-Mail) bedeutet viele verschiedene Werte, niedrige Kardinalität (z. B. Geschlecht) wenige. Sie ist eine entscheidende Kennzahl für den Optimizer bei der Indexwahl und der Schätzung der Plankosten.","tags":["cardinality","cardinality","statistics","optimizer","distinct"]},{"term":"Selektivität","full":"","definition":"Gibt an, welch kleinen Anteil der Gesamtzeilen eine Filterbedingung zurückliefert. Hohe Selektivität (wenige Zeilen) macht die Indexnutzung attraktiv; bei niedriger Selektivität (viele Zeilen) kann ein Full Scan günstiger sein. Der Optimizer nutzt sie bei der Planwahl.","tags":["selectivity","selectivity","optimizer","index","statistics"]},{"term":"Ausführungsplan","full":"","definition":"Der Fahrplan mit den Schritten (Scan-Art, Join-Methode, Sortierung usw.), die der Optimizer zur Ausführung einer Abfrage gewählt hat. Die Analyse mit EXPLAIN/EXPLAIN ANALYZE deckt die Ursache von Performance-Problemen auf. Wichtig ist der Vergleich von geschätzten und tatsächlichen Kosten.","tags":["execution-plan","query-plan","explain","optimizer","performance"]},{"term":"Kostenbasierter Optimizer","full":"CBO","definition":"Der Query Optimizer, der die Kosten möglicher Ausführungspläne anhand von Statistiken schätzt und den günstigsten wählt. Aktuelle Statistiken sind für richtige Entscheidungen unerlässlich; veraltete Statistiken führen zu schlechten Plänen. Er ist der Standardansatz in modernen Datenbanken.","tags":["cbo","optimizer","cost","statistics","query-plan"]},{"term":"Statistiken","full":"Statistiken","definition":"Metadaten, die für eine Tabelle/einen Index gesammelt werden, damit der Optimizer Zeilenanzahl, Kardinalität und Werteverteilung schätzen kann. Aktuelle Statistiken bedeuten gute Abfragepläne; veraltete müssen mit ANALYZE/UPDATE STATISTICS aktualisiert werden. Sie gehören zu den häufigsten Ursachen schlechter Performance.","tags":["statistics","statistics","optimizer","analyze","query-plan"]},{"term":"Histogramm","full":"","definition":"Eine Statistikstruktur, die die Werteverteilung einer Spalte zusammenfasst, indem sie sie in Bereiche (Buckets) aufteilt. Damit kann der Optimizer Zeilenanzahlen genauer schätzen, vor allem bei ungleichmäßigen Verteilungen. Sie ist ein Bestandteil der Statistiken.","tags":["histogram","statistics","statistics","optimizer","distribution"]},{"term":"Partition","full":"","definition":"Eine Technik, die eine große Tabelle physisch in kleinere Teile aufteilt (z. B. nach Datum oder Bereich), während sie logisch eine einzige Tabelle bleibt. Abfragen durchsuchen nur den relevanten Teil (Partition Pruning), und Wartung sowie Archivierung werden einfacher. Unterschied zum Sharding: Sie bleibt auf demselben Server bzw. derselben Instanz.","tags":["partition","partitioning","scaling","pruning","big-data"]},{"term":"Sharding","full":"","definition":"Ein Verfahren, das Daten anhand eines Shard Keys horizontal auf mehrere separate Server/Datenbanken verteilt. Es ermöglicht horizontale Skalierung über die Grenzen einer einzelnen Maschine hinaus, erschwert aber die Abfrage- und Transaktionsverwaltung. Unterschied zur Partitionierung: Die Daten verteilen sich auf verschiedene Knoten.","tags":["sharding","scaling","distributed","distributed","horizontal-scaling"]},{"term":"Replikation (synchron/asynchron)","full":"","definition":"Das Kopieren von Daten von einem Primary-Knoten auf eine oder mehrere Kopien (Replicas). Bei synchroner Replikation wartet der Commit auf die Bestätigung der Replica (kein Datenverlust, höhere Latenz); bei asynchroner Replikation wartet er nicht (schnell, aber Datenverlust möglich, wenn die Replica zurückliegt). Verwendet für HA und Leseskalierung.","tags":["replication","replication","synchronous","asynchronous","ha"]},{"term":"Logische vs. physische Replikation","full":"","definition":"Physische Replikation erzeugt eine exakte Kopie auf Ebene von Festplattenblöcken/WAL (gleiche Version, gesamte Datenbank). Logische Replikation überträgt Änderungen auf Zeilen-/SQL-Ebene; sie erlaubt die selektive Replikation von Tabellen und die Übertragung über Versionen und unterschiedliche Schemata hinweg. Für CDC- und Upgrade-Szenarien wird logische Replikation bevorzugt.","tags":["logical-replication","physical-replication","replication","cdc","wal"]},{"term":"Failover","full":"","definition":"Die automatische oder manuelle Übergabe des Dienstes an einen Standby-Knoten, wenn der Primary-Knoten ausfällt. Sie minimiert die Ausfallzeit; der RPO bestimmt, wie viele Daten verloren gehen dürfen, der RTO, wie lange die Wiederherstellung dauert. Sie greift bei ungeplanten Ausfällen.","tags":["failover","ha","high-availability","standby","rpo"]},{"term":"Switchover","full":"","definition":"Ein geplanter, kontrollierter Tausch der Primary- und Standby-Rollen (z. B. für Wartungsarbeiten). Unterschied zum Failover: Es handelt sich um einen geplanten Vorgang, nicht um einen Ausfall, und er erfolgt in der Regel ohne Datenverlust.","tags":["switchover","planned","ha","maintenance","standby"]},{"term":"RPO","full":"Recovery Point Objective","definition":"Der maximal akzeptable Datenverlust nach einem Desaster (als Zeitspanne angegeben). Bei einem RPO von 5 Minuten werden z. B. höchstens 5 Minuten Datenverlust toleriert. Backup-Häufigkeit und Replikationsart werden so gewählt, dass dieses Ziel erreicht wird.","tags":["rpo","data-loss","dr","disaster-recovery","backup"]},{"term":"RTO","full":"Recovery Time Objective","definition":"Die maximale Zielzeit, bis das System nach einem Ausfall wieder betriebsbereit ist. Bei einem RTO von 1 Stunde muss der Dienst z. B. spätestens nach 1 Stunde wieder laufen. HA/DR-Architektur und Automatisierungsgrad werden auf dieses Ziel ausgelegt.","tags":["rto","recovery-time","dr","disaster-recovery","ha"]},{"term":"HA","full":"High Availability","definition":"Das Designziel, ein System trotz Ausfällen ohne oder mit nur sehr kurzer Unterbrechung im Betrieb zu halten. Erreicht wird es durch Redundanz, automatisches Failover und Health-Checks. Meist wird es als Verfügbarkeit in Prozent angegeben (z. B. 99.99%).","tags":["ha","high-availability","high-availability","failover","redundancy"]},{"term":"DR","full":"Disaster Recovery","definition":"Die Pläne und Prozesse, um Systeme und Daten nach großen Katastrophen wie Brand, Hochwasser oder regionsweitem Ausfall wiederherzustellen, meist in einer geografisch getrennten Region. Gemessen wird sie an RPO- und RTO-Zielen. HA zielt auf alltägliche Ausfälle, DR auf große Katastrophen.","tags":["dr","disaster-recovery","disaster-recovery","rpo","rto"]},{"term":"Quorum","full":"","definition":"Die minimale Mehrheit von Knoten (meist N/2 + 1), die in einem verteilten System für eine gültige Entscheidung (z. B. Leader-Wahl oder Schreibbestätigung) erforderlich ist. Sie verhindert Split-Brain und wahrt die Konsistenz. Deshalb haben Cluster meist eine ungerade Knotenanzahl.","tags":["quorum","majority","distributed","distributed","split-brain"]},{"term":"Split-Brain","full":"","definition":"Eine Situation, in der nach einer Netzwerkpartition beide Hälften eines Clusters sich für die Primary halten und unabhängig weiterschreiben; das führt zu widersprüchlichen Daten und Korruption. Verhindert wird es durch Quorum- und Fencing-Mechanismen. Es gehört zu den gefährlichsten Fehlerszenarien in HA-Clustern.","tags":["split-brain","distributed","quorum","ha","consistency"]},{"term":"TDE","full":"Transparent Data Encryption","definition":"Ein für die Anwendung transparentes Verschlüsselungsverfahren, das Datenbankdateien (Daten und Log) verschlüsselt auf der Festplatte hält. Es schützt die Daten auch dann, wenn Festplatten oder Backup-Dateien gestohlen werden (Data at Rest). Daten im Netzwerk (in Transit) schützt es nicht; dafür ist TLS nötig.","tags":["tde","encryption","encryption","security","security"]},{"term":"RBAC","full":"Role-Based Access Control","definition":"Ein Zugriffskontrollmodell, bei dem Berechtigungen nicht direkt Benutzern, sondern Rollen zugewiesen werden und Benutzer Mitglieder von Rollen werden. Es vereinfacht die Rechteverwaltung und erleichtert die Umsetzung des Least-Privilege-Prinzips. Die Verwaltung bleibt auch bei wachsender Benutzerzahl skalierbar.","tags":["rbac","role","privilege","security","security"]},{"term":"Connection Pool","full":"","definition":"Ein Pool, der Verbindungen vorab aufbaut und wiederverwendet, statt die Datenbankverbindungen der Anwendung ständig zu öffnen und zu schließen. Er senkt die Kosten für den Verbindungsaufbau, begrenzt den Ressourcenverbrauch und steuert die Last. Er schützt die Datenbank, indem er übermäßig viele Verbindungen verhindert.","tags":["connection-pool","connection-pool","performance","resource","application"]},{"term":"Prepared Statement","full":"","definition":"Ein Konstrukt, bei dem eine SQL-Anweisung einmal geparst und geplant und dann wiederholt mit unterschiedlichen Parameterwerten ausgeführt wird. Es verbessert die Performance durch Wiederverwendung des Plans und schützt vor SQL-Injection. Es wird zusammen mit Bind-Variablen verwendet.","tags":["prepared-statement","bind","security","sql-injection","performance"]},{"term":"Bind-Variable","full":"","definition":"Ein Parameter, der über einen Platzhalter übergeben wird, statt den Wert in den SQL-Text einzubetten. Sie nutzt den Plan Cache effizient, weil dieselbe Abfrage für andere Werte nicht neu geplant werden muss, und verhindert SQL-Injection. Entscheidend für Abfragen, deren Literalwerte sich häufig ändern.","tags":["bind-variable","parameter","prepared-statement","plan-cache","security"]},{"term":"Parameter Sniffing","full":"","definition":"Der Optimizer erzeugt und cacht den Plan einer vorbereiteten/parametrisierten Abfrage anhand des Parameterwerts der ersten Ausführung. Ist dieser Wert untypisch, kann sich ein Plan festsetzen, der für spätere, andere Werte schlecht funktioniert. Lösungen sind Neukompilierung (recompile) und Optimizer-Hints.","tags":["parameter-sniffing","plan-cache","optimizer","performance","sql-server"]},{"term":"VACUUM / Bloat","full":"","definition":"Durch MVCC sammeln sich tote Versionen gelöschter/aktualisierter Zeilen in der Tabelle an; dieses Aufblähen heißt Bloat. VACUUM gibt den Platz dieser toten Zeilen frei und aktualisiert Statistiken (PostgreSQL). Wird es nicht regelmäßig ausgeführt, wächst der Plattenbedarf und die Performance sinkt.","tags":["vacuum","bloat","bloated","mvcc","postgresql"]},{"term":"Compaction","full":"","definition":"In LSM-Tree-basierten Speichern (z. B. Cassandra, RocksDB) der Vorgang, viele kleine Datendateien zusammenzuführen und tote/veraltete Datensätze zu bereinigen. Er verbessert die Leseperformance und gibt Plattenplatz frei. Er ist das Gegenstück zu VACUUM in der LSM-Welt.","tags":["compaction","lsm-tree","merge","cassandra","rocksdb"]},{"term":"Oplog","full":"Operations Log","definition":"In MongoDB eine spezielle Collection fester Größe (capped), in der alle auf der Primary ausgeführten Schreiboperationen der Reihe nach protokolliert werden. Replica-Set-Mitglieder lesen daraus, um mit der Primary synchron zu bleiben. Es ist die Grundlage für CDC und Replikation.","tags":["oplog","mongodb","replication","cdc","log"]},{"term":"Binlog","full":"Binary Log","definition":"In MySQL das binäre Log, das alle datenändernden Operationen (oder Zeilenänderungen) aufzeichnet. Es dient der Replikation, der Point-in-Time-Recovery und CDC. Es kann im Format row, statement oder mixed geführt werden.","tags":["binlog","mysql","replication","cdc","recovery"]},{"term":"CDC","full":"Change Data Capture","definition":"Eine Technik, die Insert-/Update-/Delete-Änderungen in einer Datenbank erfasst und an andere Systeme streamt (Data Warehouse, Suchmaschine, Message Queue). Sie arbeitet meist mit geringem Overhead, indem sie das Transaktionsprotokoll liest (Binlog, WAL, Oplog). Sie ist die Grundlage der Echtzeit-Datenintegration.","tags":["cdc","change-data-capture","data-streaming","binlog","integration"]},{"term":"Materialized View","full":"","definition":"Eine View, die das Ergebnis einer Abfrage physisch auf der Festplatte speichert; anders als eine normale View wird sie nicht bei jeder Abfrage neu berechnet. Sie beschleunigt teure Aggregationen und Joins, ihre Daten veralten aber und müssen per Refresh aktualisiert werden. Verbreitet in OLAP/Reporting.","tags":["materialized-view","view","cache","olap","reporting"]},{"term":"Fremdschlüssel","full":"","definition":"Eine Constraint für referenzielle Integrität, mit der eine Spalte einer Tabelle auf den Primär-/Kandidatenschlüssel einer anderen Tabelle verweist. Sie verhindert Verweise auf nicht existierende Zeilen und wahrt Beziehungen. Regeln wie ON DELETE/UPDATE CASCADE legen das Verhalten abhängiger Zeilen fest.","tags":["foreign-key","foreign-key","integrity","relationship","constraint"]},{"term":"Normalisierung","full":"Normalisierung","definition":"Der Prozess, Daten in zusammenhängende Tabellen aufzuteilen, um Redundanz und das Risiko von Inkonsistenzen zu verringern (Normalformen wie 1NF, 2NF, 3NF). Sie verhindert Update-Anomalien und wird in OLTP-Systemen bevorzugt. Dafür können Abfragen mehr Joins erfordern.","tags":["normalization","normalization","design","oltp","redundancy"]},{"term":"Denormalisierung","full":"Denormalisierung","definition":"Eine Technik, die Daten bewusst dupliziert, um die Anzahl der Joins zu verringern und die Leseperformance zu verbessern. Sie beschleunigt Abfragen, bringt aber Datenduplikate und komplexere Updates mit sich. Bevorzugt für Reporting/OLAP und leselastige Workloads.","tags":["denormalization","denormalization","performance","olap","read"]},{"term":"OLTP","full":"Online Transaction Processing","definition":"Ein Systemtyp, der eine große Zahl kurzer, häufiger und paralleler Transaktionen verarbeitet (Insert/Update/Delete, Lesen einzelner Zeilen); z. B. Banking, E-Commerce. Er erfordert geringe Latenz und hohe Concurrency und wird meist normalisiert entworfen. Er ist das operative Gegenstück zu OLAP.","tags":["oltp","transaction","transaction","operational","normalization"]},{"term":"OLAP","full":"Online Analytical Processing","definition":"Ein Systemtyp, der komplexe Aggregations- und Analyseabfragen über große Datenmengen ausführt; z. B. Reporting, Business Intelligence. Es laufen wenige, aber schwere Abfragen, und er wird meist denormalisiert und spaltenorientiert entworfen. Er ist das analytische Gegenstück zu OLTP.","tags":["olap","analytics","reporting","data-warehouse","columnar"]},{"term":"ETL","full":"Extract, Transform, Load","definition":"Der Prozess, Daten aus Quellen zu extrahieren (Extract), nach Geschäftsregeln zu transformieren (Transform) und in das Zielsystem, meist ein Data Warehouse, zu laden (Load). Die Variante, bei der die Transformation nach dem Laden im Ziel erfolgt, heißt ELT. Es ist der klassische Ansatz der Datenintegration.","tags":["etl","data-integration","data-warehouse","elt","transformation"]},{"term":"Hot/Cold Data","full":"","definition":"Häufig genutzte, aktuelle Daten heißen 'hot'; selten genutzte alte/Archivdaten heißen 'cold'. Hot Data liegt auf schnellem/teurem Storage, Cold Data auf günstigem/langsamem Storage (Data Tiering). Eine Strategie, um Kosten und Performance auszubalancieren.","tags":["hot-data","cold-data","hot-cold","archive","tiering"]},{"term":"Indexfragmentierung","full":"","definition":"Mit der Zeit führen Inserts/Deletes/Updates dazu, dass die logische Reihenfolge der Indexseiten von der physischen abweicht und der Füllgrad der Seiten sinkt. Das verursacht unnötige I/O und verschwendeten Platz und verschlechtert die Performance. Behoben wird sie mit REBUILD oder REORGANIZE.","tags":["index-fragmentation","fragmentation","index","maintenance","performance"]},{"term":"Two-Phase Commit","full":"2PC","definition":"Ein Protokoll, das einen atomaren Commit für eine verteilte Transaktion über mehrere Datenbanken/Ressourcen hinweg sicherstellt. Es arbeitet in zwei Phasen: Zuerst fragt der Coordinator alle Teilnehmer, ob sie bereit sind (prepare); stimmen alle zu, wird committet, lehnt einer ab, rollen alle zurück. Es ist langsam und anfällig für Ausfälle des Coordinators.","tags":["two-phase-commit","2pc","distributed","distributed","atomic"]}]},"guide/glossary-2.json":{"terms":[{"term":"LSM Tree","full":"Log-Structured Merge Tree","definition":"Eine Speicherstruktur, die Schreibvorgänge zunächst in einer In-Memory-Struktur (Memtable) sammelt und als sortierte Blöcke auf die Festplatte schreibt. Sie wandelt zufällige in sequenzielle Schreibvorgänge um und erreicht so hohen Schreibdurchsatz; Lesevorgänge durchsuchen mehrere Ebenen. Sie liegt RocksDB, Cassandra und LevelDB zugrunde.","tags":["storage","storage engine","lsm","write"]},{"term":"B+ Tree","full":"","definition":"Eine balancierte Baumstruktur, in der alle Daten in den Blattknoten liegen und innere Knoten nur Routing-Schlüssel enthalten. Verkettete Blätter beschleunigen Range Scans. Sie ist die häufigste Struktur für Indizes relationaler Datenbanken.","tags":["index","index structure","storage","storage engine","btree"]},{"term":"Bloom Filter","full":"","definition":"Eine probabilistische Datenstruktur, die mit wenig Speicher prüft, ob ein Element in einer Menge enthalten ist. Sie antwortet mit \"definitiv nicht\" oder \"wahrscheinlich ja\"; falsch-positive Ergebnisse sind möglich, falsch-negative nicht. Wird in LSM-basierten Engines eingesetzt, um unnötige Festplattenzugriffe zu vermeiden.","tags":["storage","storage engine","probabilistic","filter","performance"]},{"term":"Consistent Hashing","full":"","definition":"Ein Verteilungsverfahren, das sicherstellt, dass beim Hinzufügen oder Entfernen von Knoten nur ein kleiner Teil der Keys verschoben wird. Keys und Knoten werden auf einem virtuellen Ring platziert. Wird zur Datenverteilung in verteilten Caches und NoSQL-Systemen eingesetzt.","tags":["distributed systems","distributed","hashing","sharding","scaling"]},{"term":"Vector Clock","full":"","definition":"Eine Struktur, die pro Knoten einen Zähler führt, um die kausale Reihenfolge von Ereignissen in verteilten Systemen zu verfolgen. Sie bestimmt, ob zwei Updates voneinander abstammen oder gleichzeitig (konfliktbehaftet) sind. Wird für Konflikterkennung und Eventual Consistency eingesetzt.","tags":["distributed systems","distributed","clock","causality","consistency"]},{"term":"Gossip-Protokoll","full":"","definition":"Ein Kommunikationsverfahren, bei dem jeder Knoten seinen Zustand periodisch an einige zufällig gewählte Peers weitergibt, sodass sich Informationen wie eine Epidemie im gesamten Cluster verbreiten. Es braucht keinen zentralen Coordinator, ist skalierbar und fehlertolerant. Wird für die Mitgliederverwaltung in Cassandra und Consul genutzt.","tags":["distributed systems","distributed","gossip","cluster","membership"]},{"term":"Raft","full":"","definition":"Ein Konsensalgorithmus, der leichter verständlich sein soll als Paxos. Er wählt einen Leader, hängt alle Schreibvorgänge an ein über den Leader repliziertes Log an und macht sie mit Mehrheitsbestätigung dauerhaft. Wird von etcd und Consul verwendet.","tags":["distributed systems","distributed","consensus","raft","replication"]},{"term":"Paxos","full":"","definition":"Der klassische Konsensalgorithmus, der garantiert, dass sich eine Mehrheit über ein unzuverlässiges Netzwerk auf einen einzigen Wert einigt. Er ist korrekt, gilt aber als komplex in der Umsetzung; er bildet die theoretische Grundlage vieler verteilter Systeme. Wird in Systemen wie Google Chubby eingesetzt.","tags":["distributed systems","distributed","consensus","paxos","agreement"]},{"term":"Leader-Wahl","full":"","definition":"Der Vorgang, in einem verteilten Cluster einen einzelnen Knoten (den Leader) zur Koordination einer bestimmten Aufgabe auszuwählen. Fällt der aktuelle Leader aus, wird automatisch ein neuer gewählt. Meist ist eine Mehrheitszustimmung (Quorum) nötig, um Split-Brain zu verhindern.","tags":["distributed systems","distributed","leader","election","coordination"]},{"term":"Quorum Write/Read","full":"","definition":"Die Mindestanzahl von Knoten, die nötig ist, damit ein Lese- oder Schreibvorgang als gültig gilt. Gilt W + R > N, ist garantiert, dass die neuesten Daten erreicht werden. Damit lässt sich der Kompromiss zwischen Konsistenz und Verfügbarkeit abstimmen.","tags":["distributed systems","distributed","quorum","consistency","replication"]},{"term":"WAL Shipping","full":"Write-Ahead Log Shipping","definition":"Das Senden der vom Primary-Server erzeugten WAL-Datensätze an Standby-Server und ihr erneutes Einspielen dort. So hält die Standby eine nahezu aktuelle Kopie der Primary-Datenbank. Es ist die Grundlage der PostgreSQL Streaming Replication.","tags":["replication","replication","wal","high-availability","standby"]},{"term":"Log Shipping","full":"","definition":"Ein einfaches Replikationsverfahren, bei dem Backups des Transaktionsprotokolls in regelmäßigen Abständen auf einen Standby-Server kopiert und dort wiederhergestellt werden. Die Verzögerung ist höher als beim Streaming, dafür ist die Einrichtung einfach. Häufig für Disaster Recovery in SQL Server genutzt.","tags":["replication","replication","log","backup","standby"]},{"term":"Snapshot Isolation","full":"","definition":"Eine Isolationsstufe, in der jede Transaktion einen konsistenten Snapshot der Daten zum Zeitpunkt ihres Starts sieht. Leser blockieren keine Schreiber; sie ist jedoch anfällig für einige Anomalien wie Write Skew. Umgesetzt zusammen mit MVCC.","tags":["isolation level","isolation","mvcc","snapshot","concurrency"]},{"term":"Serializable Snapshot Isolation (SSI)","full":"Serializable Snapshot Isolation","definition":"Ein Verfahren, das auf Basis von Snapshot Isolation echte Serialisierbarkeit bietet, indem es gefährliche Lese-Schreib-Abhängigkeiten verfolgt. Wird ein Konflikt erkannt, wird eine der Transaktionen zurückgerollt. Verwendet in der SERIALIZABLE-Stufe von PostgreSQL.","tags":["isolation level","isolation","serializable","ssi","concurrency"]},{"term":"Gap Lock","full":"","definition":"Ein Lock auf die Lücke zwischen Indexeinträgen (einen noch nicht existierenden Bereich), der verhindert, dass neue Zeilen in diesen Bereich eingefügt werden. Dient zur Vermeidung von Phantom Reads. Kommt in der Stufe REPEATABLE READ von InnoDB zum Einsatz.","tags":["locking","lock","innodb","phantom","concurrency"]},{"term":"Next-Key Lock","full":"","definition":"Die Kombination aus einem Lock auf einen Indexeintrag und einem Lock auf die Lücke davor (Gap Lock). Sie verhindert Phantom-Inserts, indem sie sowohl die vorhandene Zeile als auch den Bereich schützt. Es ist das Standardverhalten von InnoDB beim Sperren von Zeilen.","tags":["locking","lock","innodb","gap","concurrency"]},{"term":"Optimistic Locking","full":"","definition":"Ein Verfahren, das von seltenen Konflikten ausgeht, ohne Locks arbeitet und erst beim Commit prüft, ob sich die Daten geändert haben. Meist mit einer Versionsnummer oder einem Zeitstempel umgesetzt. Wird ein Konflikt festgestellt, wird der Vorgang wiederholt.","tags":["locking","lock","optimistic","concurrency","concurrency"]},{"term":"Pessimistic Locking","full":"","definition":"Ein Verfahren, das von Konflikten ausgeht und beim Lesen oder Ändern von Daten sofort einen Lock setzt. Andere Transaktionen warten, bis der Lock freigegeben wird. Es schützt die Datenintegrität in Umgebungen mit hoher Contention, senkt aber die Concurrency.","tags":["locking","lock","pessimistic","concurrency","concurrency"]},{"term":"Idempotenz","full":"","definition":"Die Eigenschaft, dass eine Operation bei einmaliger oder mehrfacher Ausführung dasselbe Ergebnis liefert. Sie ermöglicht sichere Wiederholungen nach Netzwerkfehlern. In verteilten Systemen verhindert sie Seiteneffekte durch doppelte Anfragen.","tags":["distributed systems","distributed","idempotent","retry","reliability"]},{"term":"Circuit Breaker","full":"","definition":"Ein Entwurfsmuster, das das System schützt, indem es Aufrufe an einen ständig fehlschlagenden Dienst vorübergehend unterbindet. Solange der Schaltkreis \"offen\" ist, werden Anfragen schnell abgewiesen, und nach einer Weile erfolgt ein Testaufruf. Es verhindert kaskadierende Ausfälle.","tags":["distributed systems","distributed","fault tolerance","resilience","pattern"]},{"term":"Saga Pattern","full":"","definition":"Ein Muster, das lang laufende Transaktionen über mehrere Dienste hinweg verwaltet, indem jeder Schritt eine lokale Transaktion ist und bei Fehlern Kompensationstransaktionen ausgeführt werden. Es bietet in einer verteilten Umgebung Eventual Consistency statt ACID. Verbreitet in Microservice-Architekturen.","tags":["distributed systems","distributed","saga","microservices","pattern"]},{"term":"Outbox Pattern","full":"","definition":"Ein Muster, das die Nachricht in derselben Transaktion in eine \"Outbox\"-Tabelle schreibt, um Datenbank-Update und Nachrichtenveröffentlichung atomar zu machen. Ein separater Prozess liest diese Tabelle und veröffentlicht die Nachricht. So werden Inkonsistenzen zwischen Daten und Event-Veröffentlichung verhindert.","tags":["distributed systems","distributed","outbox","messaging","pattern"]},{"term":"Fan-out","full":"","definition":"Die Verteilung einer einzelnen Operation auf viele Teiloperationen oder Ziele; z. B. das Schreiben eines Beitrags in die Feeds Tausender Follower. Das kann zur Schreibzeit (Write Fan-out) oder zur Lesezeit (Read Fan-out) erfolgen. Eine wichtige Designentscheidung bei der Skalierung.","tags":["distributed systems","distributed","scaling","fanout","architecture"]},{"term":"Hotspot","full":"","definition":"Eine übermäßige Lastkonzentration auf einem bestimmten Knoten, einer Partition oder einem Key. Diese Stelle wird zum Engpass und begrenzt die Performance des gesamten Systems. Ein schlecht gewählter Sharding Key ist eine häufige Ursache.","tags":["distributed systems","distributed","hotspot","performance","sharding"]},{"term":"Skew (Datenungleichgewicht)","full":"","definition":"Ungleichmäßige Verteilung von Daten oder Last auf Partitionen. Einige Partitionen laufen über, während andere leer bleiben, und die parallele Effizienz sinkt. Ausgeglichen wird dies durch Key-Wahl oder Salting.","tags":["distributed systems","distributed","skew","imbalance","sharding"]},{"term":"Partieller Index","full":"","definition":"Ein Index, der nur die Tabellenzeilen abdeckt, die eine WHERE-Bedingung erfüllen. Er verringert die Indexgröße, macht die Wartung günstiger und beschleunigt die häufig abgefragte Teilmenge. Wird z. B. genutzt, um nur aktive Zeilen zu indexieren.","tags":["index","index type","postgresql","partial","performance"]},{"term":"Expression Index","full":"","definition":"Ein Index, der statt auf einer Spalte auf dem Ergebnis eines Ausdrucks oder einer Funktion aufgebaut ist. Auf LOWER(email) angelegt, beschleunigt er z. B. Suchen ohne Beachtung der Groß-/Kleinschreibung. Die Abfrage muss denselben Ausdruck verwenden.","tags":["index","index type","expression","function","performance"]},{"term":"GIN-Index","full":"Generalized Inverted Index","definition":"Ein invertierter Indextyp, der mehrere Bestandteile innerhalb eines Werts indexiert. Ideal, wenn ein Feld viele Keys enthält, etwa bei Arrays, JSONB und Volltextsuche. Verwendet in PostgreSQL.","tags":["index","index type","postgresql","gin","jsonb"]},{"term":"GiST-Index","full":"Generalized Search Tree","definition":"Ein baumbasiertes, erweiterbares Index-Framework; für verschiedene Datentypen lassen sich eigene Suchstrategien definieren. Gut geeignet für Geodaten (Geometrie), Bereichs- und Nächste-Nachbarn-Abfragen. Es bildet die Grundlage von PostGIS.","tags":["index","index type","postgresql","gist","geospatial"]},{"term":"TOAST","full":"The Oversized-Attribute Storage Technique","definition":"Die Technik von PostgreSQL, große Feldwerte, die nicht in eine Seite passen, zu komprimieren und in einem separaten Bereich zu speichern. Große Text- und JSONB-Werte werden transparent aufgeteilt. Sie erhält die Performance, indem sie die Haupttabelle kompakt hält.","tags":["storage","storage engine","postgresql","toast","large-values"]},{"term":"Index-Only Scan","full":"","definition":"Ein Scan, der das Ergebnis allein aus dem Index liefert, ohne die Tabelle überhaupt zu lesen, weil alle benötigten Daten im Index liegen. Er reduziert die Festplattenzugriffe erheblich. In PostgreSQL arbeitet er mit der Visibility Map zusammen.","tags":["index","index scan","scan","performance","postgresql"]},{"term":"HTAP","full":"Hybrid Transactional/Analytical Processing","definition":"Ein Ansatz, der transaktionale (OLTP) und analytische (OLAP) Workloads gemeinsam in einem einzigen System ausführt. Er beseitigt die Verzögerung, die durch das Verschieben der Daten in einen separaten Speicher zur Analyse (ETL) entsteht. Kombiniert meist Zeilen- und Spaltenspeicher.","tags":["analytics","oltp","olap","htap","architecture"]},{"term":"Sharding Key","full":"","definition":"Die Spalte oder Spalten, die bestimmen, auf welchem Shard eine Zeile landet. Ein gut gewählter Key verteilt die Last gleichmäßig und verhindert Hotspots. Eine schlechte Wahl führt zu Datenungleichgewicht und teuren Cross-Shard-Abfragen.","tags":["distributed systems","distributed","sharding","key","scaling"]},{"term":"Rebalancing","full":"","definition":"Die Neuverteilung von Daten auf Partitionen, wenn Knoten hinzugefügt oder entfernt werden oder die Last ungleich wird. Ziel ist, die Last auszugleichen und Ressourcen effizient zu nutzen. Während des Vorgangs entstehen Netzwerk- und I/O-Kosten.","tags":["distributed systems","distributed","rebalancing","scaling","sharding"]},{"term":"Fencing","full":"","definition":"Ein Mechanismus, der Datenkorruption verhindert, indem er einen veralteten oder isolierten Knoten daran hindert, auf eine gemeinsame Ressource zu schreiben. Meist wird ein steigendes \"Fencing Token\" verwendet, damit veraltete Anfragen abgewiesen werden. Es begrenzt die Auswirkungen von Split-Brain.","tags":["distributed systems","distributed","fencing","safety","split-brain"]},{"term":"Watermark","full":"","definition":"In Stream-Processing-Systemen eine Fortschrittsmarke, die anzeigt, dass alle Events bis zu einem bestimmten Zeitpunkt als eingetroffen gelten. Wird genutzt, um verspätete Events zu behandeln und zu entscheiden, wann Fensterberechnungen abgeschlossen werden.","tags":["stream processing","streaming","watermark","event time","event"]},{"term":"Tombstone","full":"","definition":"Eine spezielle Markierung, die geschrieben wird, um anzuzeigen, dass ein Datensatz gelöscht wurde. In LSM- und verteilten Systemen, in denen Daten nicht sofort physisch gelöscht werden, stellt sie sicher, dass sich die Löschung auf alle Replicas ausbreitet. Die eigentliche Bereinigung erfolgt bei der Compaction.","tags":["storage","storage engine","lsm","tombstone","delete"]},{"term":"Compaction (LSM)","full":"","definition":"Das Zusammenführen und Neuschreiben der sortierten Dateien (SSTables) auf der Festplatte in LSM Trees. Sie verbessert Leseperformance und Plattennutzung, indem sie alte Versionen und Tombstones bereinigt. Sie läuft im Hintergrund und erzeugt Schreiblast.","tags":["storage","storage engine","lsm","compaction","maintenance"]},{"term":"Read Repair","full":"","definition":"Eine Technik, die eine beim Lesen erkannte Inkonsistenz zwischen Replicas behebt, indem sie den neuesten Wert auf die zurückliegenden Knoten schreibt. Das System macht sich so über den normalen Leseverkehr selbst konsistent. Verwendet in Systemen wie Cassandra.","tags":["distributed systems","distributed","repair","consistency","replication"]},{"term":"Hinted Handoff","full":"","definition":"Das Speichern eines Schreibvorgangs als \"Hint\" auf einem anderen Knoten, solange der Zielknoten vorübergehend nicht erreichbar ist, und dessen Zustellung, sobald der Knoten zurückkehrt. So gehen Schreibvorgänge bei vorübergehenden Ausfällen nicht verloren. Es unterstützt Eventual Consistency.","tags":["distributed systems","distributed","handoff","availability","replication"]},{"term":"Eventual Consistency","full":"","definition":"Ein schwaches Konsistenzmodell, das garantiert, dass alle Replicas letztlich zum selben Wert konvergieren, sobald keine neuen Updates mehr erfolgen. Es lässt kurzzeitige Inkonsistenzen zu, bietet aber hohe Verfügbarkeit und geringe Latenz. In vielen NoSQL-Systemen der Standard.","tags":["distributed systems","distributed","consistency","eventual","nosql"]},{"term":"Strong Consistency","full":"","definition":"Ein Konsistenzmodell, bei dem alle Leser den neuesten Wert sofort sehen, sobald ein Schreibvorgang abgeschlossen ist. Es ist intuitiv zu nutzen, aber da es Koordination erfordert, sind die Kosten bei Latenz und Verfügbarkeit hoch. Bevorzugt in Finanzsystemen.","tags":["distributed systems","distributed","consistency","strong","strong consistency"]},{"term":"Linearisierbarkeit","full":"","definition":"Die stärkste Konsistenzstufe; sie garantiert, dass jede Operation zu einem einzigen Zeitpunkt zwischen ihrem Beginn und Ende wirksam zu werden scheint und dass sich alle Operationen in eine einzige Echtzeit-Reihenfolge einfügen. Das System verhält sich, als gäbe es nur eine einzige Kopie. Die Umsetzung ist teuer.","tags":["distributed systems","distributed","consistency","linearizability","strong consistency"]},{"term":"Two-Phase Locking (2PL)","full":"Two-Phase Locking","definition":"Eine zweiphasige Sperrregel, bei der eine Transaktion zuerst nur Locks anfordert (Wachstumsphase) und danach nur Locks freigibt (Schrumpfungsphase). Diese Disziplin garantiert Serialisierbarkeit. In der strikten Form (Strict 2PL) werden Locks bis zum Commit gehalten.","tags":["locking","lock","2pl","serializable","concurrency"]},{"term":"Deadlock-Erkennung","full":"","definition":"Ein Mechanismus, der einen Deadlock erkennt, bei dem zwei oder mehr Transaktionen gegenseitig auf von ihnen gehaltene Locks warten. Meist wird er durch die Suche nach einem Zyklus im Wait-for-Graphen gefunden, und eine Transaktion wird als Opfer gewählt und zurückgerollt. Danach läuft das System wieder.","tags":["locking","lock","deadlock","deadlock detection","concurrency"]}]},"guide/glossary-3.json":{"terms":[{"term":"Write Amplification","full":"Write Amplification","definition":"Ein einzelner logischer Schreibvorgang verursacht in der Speicherschicht viel mehr physische Schreibvorgänge (LSM-Compaction, Neuschreiben von Seiten, WAL, Löschen von SSD-Blöcken). Hohe Werte verschleißen Festplatten und senken den Schreibdurchsatz.","tags":["storage","lsm","performance","ssd"]},{"term":"Read Amplification","full":"Read Amplification","definition":"Ein einzelner logischer Lesevorgang erfordert tatsächlich mehrere physische/interne Lesevorgänge. Das typische Beispiel ist die Suche nach einem Key über viele Ebenen in einem LSM Tree; Bloom Filter und Compaction verringern das.","tags":["storage","lsm","performance"]},{"term":"LSM-tree","full":"Log-Structured Merge Tree","definition":"Eine Datenstruktur, die Schreibvorgänge zunächst in einer In-Memory-Struktur (Memtable) aufnimmt, als sortierte Blöcke (SSTables) auf die Festplatte schreibt und im Hintergrund zusammenführt (Compaction). Ideal für schreiblastige Workloads; verwendet von RocksDB und Cassandra.","tags":["storage","lsm","write-heavy"]},{"term":"B+tree","full":"B+ Tree","definition":"Die klassische Struktur von Indizes relationaler Datenbanken. Alle Daten liegen in den Blattknoten, und die Blätter sind als verkettete Liste verbunden; sie bietet ausgewogene Leseperformance für Punkt- und Bereichsabfragen.","tags":["index","btree","storage"]},{"term":"Fill Factor","full":"Fill Factor","definition":"Der Wert, der angibt, wie voll eine Index-/Tabellenseite anfangs befüllt wird. Freier Platz verringert Page Splits bei späteren Updates/Inserts; fillfactor in PG, fill factor in MSSQL.","tags":["index","storage","tuning"]},{"term":"HOT Update","full":"Heap-Only Tuple Update","definition":"In PostgreSQL erzeugt ein Update, das keine indexierte Spalte ändert, die neue Zeilenversion auf derselben Seite und aktualisiert die Indizes nicht. Das verringert Index-Bloat und senkt die Update-Kosten.","tags":["postgres","mvcc","update","performance"]},{"term":"Bloat","full":"Table/Index Bloat","definition":"Eine Tabelle oder ein Index belegt in MVCC-Engines durch nicht bereinigte tote Zeilenversionen oder leere Seiten mehr Platz als nötig. Das erhöht die Scan-Kosten; der Platz wird mit VACUUM/Reorg zurückgewonnen.","tags":["mvcc","postgres","maintenance","bloat"]},{"term":"Autovacuum","full":"Automatic VACUUM","definition":"Der PostgreSQL-Hintergrundprozess, der automatisch Dead Tuples bereinigt, Statistiken sammelt und Transaktions-IDs einfriert. Über Schwellen- und Kostenparameter wird er an die Workload angepasst.","tags":["postgres","vacuum","maintenance"]},{"term":"Freeze / Wraparound","full":"Transaction ID Freeze / Wraparound","definition":"In PostgreSQL das Markieren alter Zeilen als 'frozen', um zu verhindern, dass 32-Bit-Transaktions-IDs überlaufen (Wraparound). Wird es vernachlässigt, nimmt die Datenbank keine Schreibvorgänge mehr an, um Datenverlust zu verhindern.","tags":["postgres","mvcc","vacuum","wraparound"]},{"term":"Checkpoint","full":"Checkpoint","definition":"Der Vorgang, bei dem Dirty Pages im Speicher auf die Festplatte geschrieben werden und der Punkt festgehalten wird, ab dem die Recovery beginnt. Er verkürzt die Crash Recovery; seine Häufigkeit ist ein Kompromiss zwischen I/O-Spitzen und Recovery-Zeit.","tags":["recovery","wal","internals","checkpoint"]},{"term":"Fuzzy Checkpoint","full":"Fuzzy Checkpoint","definition":"Eine Checkpoint-Art, die Dirty Pages kontinuierlich und zeitlich verteilt im Hintergrund schreibt, ohne das System anzuhalten. Sie hält Antwortzeiten vorhersagbar, indem sie plötzliche I/O-Spitzen (Checkpoint Spikes) vermeidet.","tags":["recovery","checkpoint","internals"]},{"term":"Redo","full":"Redo-Log / Roll Forward","definition":"Der Datensatz, der dazu dient, die Änderungen einer Transaktion bei der Recovery erneut anzuwenden (Roll Forward). Er stellt committete Änderungen wieder her, die vor einem Absturz noch nicht auf die Festplatte gelangt waren.","tags":["recovery","oracle","wal","redo"]},{"term":"Undo","full":"Undo Log / Rollback","definition":"Der Datensatz, der dazu dient, eine Transaktion zurückzurollen und alte Zeilenversionen für MVCC bereitzustellen. In Oracle/InnoDB werden alte Versionen aus den Undo-Segmenten gelesen.","tags":["mvcc","oracle","innodb","undo","rollback"]},{"term":"MVCC Snapshot","full":"MVCC Snapshot","definition":"Die logische Momentaufnahme, die bestimmt, welche Zeilenversionen sichtbar sind, damit eine Transaktion konsistent liest. Sie wird zu Beginn der Transaktion oder der Anweisung erstellt (abhängig von der Isolationsstufe).","tags":["mvcc","concurrency","isolation","snapshot"]},{"term":"Isolationsanomalie","full":"Isolation Anomalies","definition":"Inkonsistenzen, die parallele Transaktionen bei unzureichender Isolation erzeugen: Dirty Read (Lesen nicht committeter Daten), Non-repeatable Read (dieselbe Zeile ändert sich), Phantom (neue Zeilen erscheinen) und Write Skew (scheinbar unabhängige Schreibvorgänge verletzen gemeinsam eine Regel).","tags":["isolation","concurrency","anomaly","acid"]},{"term":"2PL","full":"Two-Phase Locking","definition":"Ein Protokoll, das Serialisierbarkeit mit Locks sicherstellt: Eine Transaktion fordert zuerst nur Locks an (Wachstumsphase) und gibt sie ab einem bestimmten Punkt nur noch frei (Schrumpfungsphase). Strict 2PL hält Locks bis zum Commit.","tags":["concurrency","locking","serializable","2pl"]},{"term":"MVTO","full":"Multi-Version Timestamp Ordering","definition":"Ein Verfahren zur Concurrency Control, das Transaktionen Zeitstempel zuweist und Lese-/Schreibvorgänge auf mehrversionierten Daten danach ordnet. Es löst Konflikte mit Zeitstempeln statt mit Locks.","tags":["concurrency","mvcc","timestamp","protocol"]},{"term":"Mutex","full":"Mutual Exclusion","definition":"Ein Primitiv für gegenseitigen Ausschluss, das garantiert, dass jeweils nur ein Thread auf eine Ressource zugreift. In Datenbanken übernimmt es beim Schutz gemeinsamer Speicherstrukturen eine Latch-ähnliche Rolle.","tags":["concurrency","internals","mutex"]},{"term":"Spinlock","full":"Spinlock","definition":"Ein sehr kurzlebiger Lock, bei dem der Thread in einer Schleife wartet (Busy-Wait), statt zu schlafen, bis der Lock freigegeben wird. Günstig für sehr kurze kritische Abschnitte; bei Contention verbrennt er CPU.","tags":["concurrency","internals","spinlock","cpu"]},{"term":"Buffer Pool","full":"Buffer Pool / Buffer Cache","definition":"Ein Cache, der Festplatten-I/O reduziert, indem er häufig genutzte Datenseiten im Speicher hält. Der Eviction-Algorithmus (LRU, Clock-Sweep) entscheidet, welche Seite verdrängt wird; die Größe wird nach dem Working Set festgelegt.","tags":["memory","cache","internals","buffer"]},{"term":"Working Set","full":"Working Set","definition":"Die Datenmenge, auf die eine Workload in einem bestimmten Zeitfenster aktiv zugreift. Ist der Buffer Pool groß genug für diese Menge, wird physische I/O minimiert; sie unterscheidet sich von der Gesamtdatengröße.","tags":["memory","cache","sizing","performance"]},{"term":"Skew","full":"Data Skew","definition":"Eine Situation, in der Werte nicht gleichmäßig verteilt sind und einige weitaus häufiger vorkommen. Sie ist die Hauptursache für falsche Kardinalitätsschätzungen, Probleme mit Parameter Sniffing und Hotspots beim Sharding; behandelt wird sie mit Histogrammen.","tags":["statistics","distribution","skew","performance"]},{"term":"Plan Cache","full":"Plan Cache","definition":"Der Speicherbereich, in dem kompilierte Ausführungspläne zur Wiederverwendung abgelegt werden. Er vermeidet die Kosten des Hard Parse, birgt aber Risiken durch Parameter Sniffing und veraltete Pläne; Plan Cache in MSSQL, Library Cache in Oracle.","tags":["optimizer","plan-cache","parsing"]},{"term":"Hard/Soft Parse","full":"Hard Parse / Soft Parse","definition":"Hard Parse: Die SQL-Anweisung wird von Grund auf geparst, optimiert und ein Plan erzeugt (teuer). Soft Parse: Ein vorhandener Plan wird im Cache gefunden und wiederverwendet (günstig). Bind-Variablen erhöhen den Anteil der Soft Parses.","tags":["parsing","oracle","performance","plan-cache"]},{"term":"Connection Storming","full":"Connection Storm","definition":"Eine große Zahl neuer Datenbankverbindungen wird innerhalb kurzer Zeit gleichzeitig geöffnet. Das überlastet die Datenbank durch Kosten für den Verbindungsaufbau und Speicherdruck; Ursache ist meist ein erschöpfter Pool oder eine Reconnect-Welle.","tags":["connections","pooling","overload"]},{"term":"Thundering Herd","full":"Thundering-Herd-Problem","definition":"Viele Clients greifen nach einem Ereignis (Cache-Ablauf, Freigabe eines Locks, Rückkehr eines Dienstes) gleichzeitig auf dieselbe Ressource zu. Abgemildert wird das mit Backpressure, Jitter und Single-Flight-Mustern.","tags":["concurrency","cache","overload","resilience"]},{"term":"Backpressure","full":"Backpressure","definition":"Ein System bremst den Produzenten oder weist ihn ab, statt Last anzunehmen, die es nicht bewältigen kann (Queue-Limits, Rate Limits). So wird bei Überlast eine kontrollierte Leistungsminderung statt eines Zusammenbruchs möglich.","tags":["resilience","flow-control","overload"]},{"term":"Lease","full":"Lease","definition":"Ein für eine feste Dauer an einen Knoten vergebenes Leader- bzw. Ressourcenrecht, das nach Ablauf automatisch ungültig wird. Da es zeitlich begrenzt ist, verhindert es, dass ein hängender Knoten für immer Leader bleibt.","tags":["distributed","leader-election","lease"]},{"term":"Read-Your-Writes","full":"Read-Your-Writes Consistency","definition":"Session-Konsistenz, die garantiert, dass ein Benutzer seine eigene Änderung beim unmittelbar nächsten Lesen immer sieht. Beim Routing auf Replicas mit Lag wird sie durch spezielles Routing bzw. Tokens erreicht.","tags":["distributed","consistency","session","replication"]},{"term":"Causal Consistency","full":"Causal Consistency","definition":"Ein Konsistenzmodell, stärker als Eventual Consistency, das garantiert, dass kausal zusammenhängende Operationen auf allen Knoten in derselben Reihenfolge sichtbar werden. Unabhängige (gleichzeitige) Operationen können in unterschiedlicher Reihenfolge erscheinen.","tags":["distributed","consistency","causal"]},{"term":"CAP","full":"Consistency, Availability, Partition tolerance","definition":"Das Theorem, nach dem ein verteiltes System während einer Netzwerkpartition nur entweder Konsistenz oder Verfügbarkeit wählen kann. Ohne Partition ist dieser Kompromiss nicht zwingend.","tags":["distributed","cap","consistency","theory"]},{"term":"PACELC","full":"PACELC-Theorem","definition":"Ein Modell, das CAP erweitert: Bei einer Partition (P) fällt die Wahl zwischen Availability und Consistency, andernfalls (E) zwischen Latency und Consistency. Es berücksichtigt auch den Kompromiss zwischen Latenz und Konsistenz im Normalbetrieb.","tags":["distributed","cap","pacelc","latency","theory"]},{"term":"Hot Partition","full":"Hot Partition / Hot Spot","definition":"Last, die sich unverhältnismäßig auf einer einzigen Partition bzw. einem einzigen Shard ballt. Ursache ist ein Shard Key mit geringer Kardinalität, ein zeitbasierter oder monoton steigender Key; der Knoten wird zum Engpass und die Skalierung endet.","tags":["distributed","sharding","hot-spot","skew"]},{"term":"Columnstore","full":"Column-Oriented Storage","definition":"Ein Format, bei dem Daten spalten- statt zeilenweise gespeichert werden. Analytische Abfragen lesen nur die benötigten Spalten, mit hoher Kompression und vektorisierter Ausführung; ideal für OLAP.","tags":["olap","columnar","analytics","compression"]},{"term":"Vektorisierte Ausführung","full":"Vektorisierte Ausführung","definition":"Abfrageoperatoren verarbeiten jeweils einen Block (Batch) von Spaltenwerten statt einzelner Zeilen. Durch bessere Nutzung von CPU-Caches und SIMD steigt der Durchsatz analytischer Abfragen stark.","tags":["olap","execution","performance","columnar"]},{"term":"Zone Map / Min-Max","full":"Zone Map / Min-Max Index","definition":"Leichtgewichtige Metadaten, die für jeden Datenblock den minimalen und maximalen Spaltenwert speichern. Sie senken die Scan-Kosten, indem Blöcke, die das Abfrageprädikat nicht erfüllen können, ungelesen übersprungen werden (Block Skipping).","tags":["olap","index","pruning","columnar"]},{"term":"Logische Replikation","full":"Logische Replikation","definition":"Replikation, die Änderungen auf Zeilenebene (logische Events) statt physischer Blöcke veröffentlicht und abonniert. Sie erlaubt ausgewählte Tabellen sowie versionsübergreifende und bidirektionale Szenarien; in PG bieten das Publication/Subscription.","tags":["replication","postgres","logical","cdc"]},{"term":"GTID","full":"Global Transaction Identifier","definition":"Eine global eindeutige Kennung, die in der MySQL-Replikation jeder Transaktion zugewiesen wird. Da die Replica-Position über die Transaktions-ID statt über Datei/Offset verfolgt wird, werden Failover und Neukonfiguration sicher und einfach.","tags":["mysql","replication","gtid","failover"]},{"term":"High Water Mark","full":"High Water Mark","definition":"Die höchste je genutzte Blockgrenze in einem Segment bzw. einer Tabelle. In Oracle liest ein Full Table Scan bis zu dieser Grenze; da sie nach Massenlöschungen nicht sinkt, kann das zum Scannen leerer Blöcke und zu Bloat führen.","tags":["oracle","storage","internals","scan"]}]},"playbooks/playbooks.json":{"schemaVersion":1,"playbooks":[{"id":"db-slow","title":"Datenbank ist langsam","icon":"🐢","symptom":"Anwendungsweite Langsamkeit; Abfragen dauern viel länger als üblich, und Benutzer beschweren sich.","steps":[{"title":"Kläre zuerst, was genau langsam ist","detail":"Ist alles langsam oder nur eine bestimmte Abfrage/Maske? Wann hat es begonnen, und fällt das mit einem Deployment/Patch/Datenwachstum zusammen? Betrifft es das ganze System oder eine einzelne DB? So grenzt du die Suche auf eine Achse ein: CPU, I/O, Sperren oder Planänderung."},{"title":"Aktive Sessions und Wait Events prüfen","detail":"Was läuft gerade, und was wartet? Ein dominierendes Wait Event (I/O, Lock, Log) zeigt auf den Engpass.","engines":{"oracle":"SELECT sid, username, status, event, sql_id FROM v$session WHERE status='ACTIVE' AND type='USER';","postgres":"SELECT pid, state, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state='active';","mysql":"SELECT id, user, command, time, state, info FROM information_schema.processlist WHERE command<>'Sleep' ORDER BY time DESC;","mssql":"SELECT session_id, status, wait_type, wait_time, blocking_session_id FROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1);","mongodb":"db.currentOp({ active: true, secs_running: { $gt: 1 } })","redis":"SLOWLOG GET 10"}},{"title":"Auf Blockierungen prüfen","detail":"Die häufigste Ursache für Langsamkeit ist eine Session, die andere blockiert. Finde die blockierende Session und ermittle den Anfang der Kette.","engines":{"oracle":"SELECT blocking_session, sid, seconds_in_wait, event FROM v$session WHERE blocking_session IS NOT NULL;","postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;","mysql":"SELECT * FROM sys.innodb_lock_waits\\G","mssql":"SELECT session_id, blocking_session_id, wait_type FROM sys.dm_exec_requests WHERE blocking_session_id<>0;"}},{"title":"Die teuersten Abfragen ermitteln","detail":"Finde die 10 Abfragen mit dem höchsten Ressourcenverbrauch. Meist stammen 90 % des Problems von einer Handvoll Abfragen.","engines":{"oracle":"SELECT sql_id, elapsed_time/1e6 sec, executions, sql_text FROM v$sql ORDER BY elapsed_time DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT calls, total_exec_time, mean_exec_time, query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;","mysql":"SELECT digest_text, count_star, sum_timer_wait/1e12 sec FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;","mssql":"SELECT TOP 10 total_elapsed_time/execution_count avg_us, execution_count, SUBSTRING(t.text,1,200) FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t ORDER BY total_elapsed_time DESC;"}},{"title":"Plan der verdächtigen Abfrage untersuchen","detail":"Hole dir EXPLAIN für die langsame Abfrage. Achte auf Full Table Scans, fehlende Indizes, schlechte Join-Reihenfolge oder veraltete Statistiken. Hat sich der Plan plötzlich geändert, erwäge, Statistiken zu sammeln.","engines":{"oracle":"SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));","postgres":"EXPLAIN (ANALYZE, BUFFERS) <query>;","mysql":"EXPLAIN ANALYZE <query>;","mssql":"SET STATISTICS PROFILE ON; <query>; SET STATISTICS PROFILE OFF;","mongodb":"db.my_collection.find({...}).explain('executionStats')  // COLLSCAN or IXSCAN?"}},{"title":"Systemressourcen prüfen","detail":"Prüfe auf OS-Ebene CPU, I/O-Wartezeit (iowait), Speicher und Plattenbelegung. Prüfe die Cache Hit Ratio; ist sie niedrig, liegt eventuell ein Speicherengpass vor. Das Problem kann auch außerhalb der DB liegen (volle Platte, Noisy-Neighbor-VM)."},{"title":"Entscheiden: schnelle oder dauerhafte Lösung","detail":"Für sofortige Entlastung: die ausufernde Abfrage beenden, den fehlenden Index anlegen, Statistiken sammeln. Für eine dauerhafte Lösung: Abfrage/Index neu entwerfen, Speichereinstellungen korrigieren, Planstabilität sicherstellen (Baseline/Hint)."}],"related":["active-sessions","blocking-sessions","top-slow-queries","explain-plan","cache-hit-ratio"]},{"id":"disk-full","title":"Platte / Tablespace voll","icon":"💽","symptom":"Fehler wie 'unable to extend', 'disk full', 'log file full'; Schreibvorgänge kommen zum Erliegen.","steps":[{"title":"Keine Panik; finde zuerst heraus, was voll ist","detail":"Ist es eine Datafile, das Transaktionsprotokoll, der Archiv-/Redo-Bereich, Temp oder die OS-Platte? Am falschen Ort aufzuräumen hilft nicht. Prüfe die Platte auf OS-Ebene mit df -h und innerhalb der DB die Views zur Speicherbelegung."},{"title":"Belegung messen","detail":"Welcher Tablespace bzw. welche Datei oder welches Verzeichnis ist voll, und zu wie viel Prozent?","engines":{"oracle":"SELECT d.tablespace_name, ROUND((1-(d.alloc-NVL(f.free,0))/d.maxb)*100,1) pct_free FROM (SELECT tablespace_name, SUM(bytes) alloc, SUM(GREATEST(bytes,maxbytes)) maxb FROM dba_data_files GROUP BY tablespace_name) d LEFT JOIN (SELECT tablespace_name, SUM(bytes) free FROM dba_free_space GROUP BY tablespace_name) f ON f.tablespace_name = d.tablespace_name;","postgres":"SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY 2 DESC; -- use OS df for the disk","mysql":"SELECT table_schema, ROUND(SUM(data_length+index_length)/1048576,1) mb FROM information_schema.tables GROUP BY table_schema ORDER BY mb DESC;","mssql":"SELECT name, size/128.0 size_mb, size/128.0 - CAST(FILEPROPERTY(name,'SpaceUsed') AS int)/128.0 free_mb FROM sys.database_files;","redis":"INFO memory  # used_memory vs maxmemory"}},{"title":"Die größten Verbraucher finden","detail":"Ermittle die größten Tabellen/Segmente, die größten Log-/Archivdateien und nicht mehr benötigte alte Backups. Meist ist eine einzelne ausufernde Tabelle oder eine Ansammlung von Archivlogs schuld.","engines":{"oracle":"SELECT segment_name, bytes/1048576 mb FROM dba_segments ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;","mssql":"SELECT TOP 10 OBJECT_NAME(object_id), SUM(reserved_page_count)*8/1024 mb FROM sys.dm_db_partition_stats GROUP BY object_id ORDER BY mb DESC;"}},{"title":"Platz sicher freigeben (schnell)","detail":"Für Logs/Archive: sichern und dann alte Archivlogs/Backups bereinigen (Oracle: RMAN delete archivelog; MSSQL: Log-Backup erstellen und das Log verkleinern). In Redis: maxmemory-policy und Eviction. Lösche NIE eine aktive Datafile manuell; lösche NIE Archivlogs ohne Backup."},{"title":"Kapazität dauerhaft erhöhen","detail":"Erweitere die Datafile oder aktiviere Autoextend (Oracle: ALTER DATABASE DATAFILE ... AUTOEXTEND ON MAXSIZE), vergrößere die Platte, füge einen Tablespace hinzu. Behebe die Ursache: Log-Rotation, Job zur Archivbereinigung, Aufbewahrungsrichtlinie."},{"title":"Wiederholung verhindern","detail":"Richte Alarme für die Plattenbelegung ein (Schwellen 80 %/90 %), automatisiere die Archiv-/Log-Bereinigung und lege die Aufbewahrungsrichtlinie klar fest. So musst du dieses Playbook nicht erneut öffnen."}],"related":["free-space","largest-tables","database-size","reclaim-space","last-backup-status"]},{"id":"blocking","title":"Blockierungs- und Lock-Krise","icon":"🔒","symptom":"Abfragen hängen, 'lock wait timeout', viele Sessions warten, aber die CPU-Last ist niedrig.","steps":[{"title":"Den Anfang der Blockierungskette finden (Root Blocker)","detail":"Ermittle, wer wen blockiert; der eigentliche Verursacher ist die Session, die selbst auf nichts wartet, andere aber warten lässt. Konzentriere dich auf die Session an der Spitze der Kette.","engines":{"oracle":"SELECT blocking_session, sid, serial#, username, seconds_in_wait, sql_id FROM v$session WHERE blocking_session IS NOT NULL ORDER BY seconds_in_wait DESC;","postgres":"SELECT pid, usename, pg_blocking_pids(pid) blocked_by, state, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;","mysql":"SELECT waiting_pid, waiting_query, blocking_pid, blocking_query FROM sys.innodb_lock_waits;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_resource FROM sys.dm_exec_requests WHERE blocking_session_id<>0;","mongodb":"db.currentOp({ waitingForLock: true })"}},{"title":"Verstehen, was der Root Blocker tut","detail":"Ist es eine offene, nicht committete Transaktion? Hat die Anwendung sie 'idle in transaction' hinterlassen? Ist es ein langer Batch? Verstehe, worum es sich handelt, bevor du blind beendest – vielleicht ist er gleich fertig.","engines":{"oracle":"SELECT s.sid, s.prev_sql_id, t.start_time, t.used_ublk FROM v$transaction t JOIN v$session s ON t.ses_addr=s.saddr;","postgres":"SELECT pid, state, xact_start, now()-xact_start age, query FROM pg_stat_activity WHERE pid=<root_pid>;"}},{"title":"Entscheiden: warten, über die Anwendung lösen oder beenden","detail":"Ist er gleich fertig, warte. Kann die Anwendung committen/zurückrollen, löse das aus. Steht der Betrieb still, beende andernfalls den Root Blocker. Bedenke vor dem Beenden die Auswirkungen (Rollback-Dauer, Daten)."},{"title":"Root Blocker bei Bedarf beenden","detail":"Beende nur die Session an der Spitze der Kette; die darunter liegenden Sessions werden automatisch freigegeben.","engines":{"oracle":"ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;","postgres":"SELECT pg_terminate_backend(<root_pid>);","mysql":"KILL <root_id>;","mssql":"KILL <root_session_id>;","mongodb":"db.killOp(<opid>)"}},{"title":"Ursache beheben","detail":"Ein fehlender Index kann breite Sperren verursachen; die Anwendung hält Transaktionen eventuell zu lange offen; die Isolationsstufe ist vielleicht falsch. Lege Indizes an, verkürze Transaktionen, setze einen Timeout für 'idle in transaction' und wähle eine passende Isolationsstufe."}],"related":["blocking-sessions","kill-session","open-transactions"]},{"id":"connection-storm","title":"Connection Storm (zu viele Verbindungen)","icon":"🌊","symptom":"'too many connections', 'ORA-00018', 'remaining connection slots reserved' – neue Verbindungen werden abgewiesen.","steps":[{"title":"Aktuellen Zustand messen","detail":"Wie viele Verbindungen gibt es, wo liegt das Limit, und wie viele sind aktiv bzw. idle?","engines":{"oracle":"SELECT COUNT(*) total, SUM(CASE WHEN status='ACTIVE' THEN 1 ELSE 0 END) active FROM v$session WHERE type='USER';","postgres":"SELECT count(*), state FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY state;","mysql":"SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';","mssql":"SELECT COUNT(*), status FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY status;","redis":"INFO clients"}},{"title":"Quelle ermitteln: wer/von wo","detail":"Welche Anwendung, welcher Host bzw. Benutzer öffnet die Verbindungen? Meist ist der Pool eines einzelnen Anwendungsservers explodiert oder es gibt ein Connection Leak (viele Sessions im Zustand idle in transaction).","engines":{"postgres":"SELECT client_addr, usename, state, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY 1,2,3 ORDER BY 4 DESC;","mysql":"SELECT host, user, count(*) FROM information_schema.processlist GROUP BY host, user ORDER BY 3 DESC;","mssql":"SELECT host_name, program_name, login_name, COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY host_name, program_name, login_name ORDER BY 4 DESC;"}},{"title":"Sofort Luft verschaffen","detail":"Räume lange inaktive bzw. idle-in-transaction-Verbindungen auf. Trenne bei Bedarf die Verbindungen der verursachenden Anwendung gesammelt. Nutze den für den Admin-Login reservierten Slot (PG superuser_reserved_connections)."},{"title":"Die richtige Lösung: Connection Pooling","detail":"max_connections unbegrenzt zu erhöhen verbraucht Speicher und verschiebt das Problem nur. Die richtige Lösung ist ein Connection Pool auf Anwendungsseite (HikariCP, PgBouncer, ProxySQL) mit einer sinnvollen Pool-Größe. Die Pool-Größe liegt typischerweise bei etwa (cores*2 + Anzahl der Platten) – siehe den Rechner unter Tools > Connection Pool."},{"title":"Leaks und Wiederholung verhindern","detail":"Stelle sicher, dass die Anwendung Verbindungen schließt (finally/using). Setze idle_in_transaction_session_timeout und wait_timeout. Richte einen Alarm für die Verbindungsanzahl ein."}],"related":["connection-count","kill-session","open-transactions"]},{"id":"replication-lag","title":"Replikationsverzögerung / gestoppt","icon":"🔁","symptom":"Die Standby/Replica fällt zurück oder ist ganz stehen geblieben; Read Replicas zeigen veraltete Daten.","steps":[{"title":"Wie groß ist der Lag, und wächst er oder bleibt er konstant","detail":"Wie groß ist der Lag in Sekunden/Bytes? Ist es ein konstanter Versatz oder wächst er ständig? Wächst er, kommt die Replica beim Apply nicht hinterher; ist er konstant, gab es einen kurzzeitigen Stillstand.","engines":{"oracle":"SELECT name, value, unit FROM v$dataguard_stats WHERE name LIKE '%lag%';","postgres":"SELECT client_addr, state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;","mysql":"SHOW REPLICA STATUS\\G  -- Seconds_Behind_Source","mssql":"SELECT ar.replica_server_name, drs.redo_queue_size, drs.log_send_queue_size FROM sys.dm_hadr_database_replica_states drs JOIN sys.availability_replicas ar ON drs.replica_id=ar.replica_id;","redis":"INFO replication  # master_repl_offset vs slave offset"}},{"title":"Läuft die Replikation, gibt es Fehler","detail":"Wie ist der Zustand des IO-/SQL-Threads und des Apply-Prozesses, ist die Verbindung abgerissen? Gibt es einen Fehler (z. B. kollidierende Zeile, fehlende Tabelle), ist das Apply gestoppt; lies die Fehlermeldung.","engines":{"mysql":"SHOW REPLICA STATUS\\G  -- Replica_IO_Running / Replica_SQL_Running / Last_Error","postgres":"SELECT status, last_msg_receipt_time, latest_end_lsn FROM pg_stat_wal_receiver;  -- on the standby","oracle":"SELECT process, status, sequence#, thread# FROM v$managed_standby WHERE process LIKE 'MRP%' OR process LIKE 'RFS%';"}},{"title":"Den Engpass finden","detail":"Warum kommt sie nicht hinterher? Single-Threaded Apply plus hohe Schreiblast (PG/MySQL wenden meist seriell an), ein I/O-Engpass auf der Standby, das Netzwerk oder eine lange Abfrage, die das Apply blockiert (PG hot_standby_feedback / Recovery-Konflikt)?"},{"title":"Den Lag abbauen","detail":"Apply beschleunigen: paralleles Apply aktivieren (MySQL replica_parallel_workers – vor 8.0.26 slave_parallel_workers; PG hat keines, aber es gibt Recovery-Einstellungen), die I/O der Standby verbessern, die Abfrage auflösen, die das Apply blockiert. Ist sie mit einem Fehler stehen geblieben: Fehler beheben (kollidierende Zeile manuell auflösen) und die Replikation fortsetzen."},{"title":"Neu aufbauen, wenn sie defekt ist","detail":"Liegt die Replica weit zurück oder ist sie defekt, ist eine Neusynchronisation am saubersten: Baue die Standby aus einem frischen Backup neu auf (PG pg_basebackup, MySQL clone/xtrabackup, Oracle RMAN duplicate). Wichtig: Finde vor dem Neuaufbau die Ursache, sonst passiert es wieder."}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"high-cpu","title":"Hohe CPU","icon":"🔥","symptom":"Server-CPU bei 90 %+; das gesamte System ist langsam und der Load Average ist hoch.","steps":[{"title":"Prüfen, ob wirklich die DB die CPU verbraucht","detail":"Prüfe auf OS-Ebene mit top/mpstat, ob der CPU-verbrauchende Prozess tatsächlich die DB ist. Manchmal ist ein benachbarter Prozess oder ein Backup/Virenscanner der Verbraucher. Ist iowait hoch, liegt das Problem bei der Platte, nicht bei der CPU."},{"title":"Die Abfragen mit dem höchsten CPU-Verbrauch finden","detail":"Ermittle die Top-Abfragen nach CPU-Zeit. Meist verbrennen wenige Abfragen (schlechter Plan, Full Scan, fehlender Index) die CPU.","engines":{"oracle":"SELECT sql_id, cpu_time/1e6 cpu_sec, executions, sql_text FROM v$sql ORDER BY cpu_time DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT query, calls, total_exec_time, mean_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;","mysql":"SELECT digest_text, count_star, sum_timer_wait/1e12 sec FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;","mssql":"SELECT TOP 10 total_worker_time/execution_count avg_cpu, execution_count, SUBSTRING(t.text,1,200) FROM sys.dm_exec_query_stats q CROSS APPLY sys.dm_exec_sql_text(q.sql_handle) t ORDER BY total_worker_time DESC;"}},{"title":"Auf Plan-/Indexprobleme prüfen","detail":"Hole dir den Plan der verursachenden Abfrage. Full Table Scans, explodierende Nested Loops, Hash-Join-Spills oder schlechte Pläne durch Parameter Sniffing/veraltete Statistiken verbrennen CPU. Lege einen Index an oder sammle Statistiken."},{"title":"Sofort entlasten","detail":"Beende die ausufernde Abfrage und begrenze sie bei Bedarf mit Resource Manager/Resource Governor. Bei plötzlich aufgetretener Last prüfe das letzte Deployment bzw. die letzte Parameteränderung."},{"title":"Dauerhaft beheben","detail":"Korrigiere Abfrage/Index, sichere die Planstabilität und plane den Job zum Sammeln der Statistiken. Liegt die CPU ständig bei 100 %, kann Skalierung nötig sein (mehr CPU, Verteilung der Lesezugriffe auf Read Replicas)."}],"related":["top-slow-queries","explain-plan","active-sessions","update-statistics"]},{"id":"memory-pressure","title":"Speicherdruck / OOM","icon":"🧠","symptom":"Der OOM Killer beendet den Prozess, 'out of memory', niedrige Cache Hit Ratio, übermäßiges Swapping, Redis 'OOM command not allowed'.","steps":[{"title":"Wer verbraucht den Speicher","detail":"Ist es der allokierte Speicher der DB (Buffer Pool/SGA), der Speicher pro Session (PGA/Sort/work_mem) oder das OS insgesamt? Prüfe auf OS-Ebene free/vmstat und innerhalb der DB die Speicher-Views.","engines":{"oracle":"SELECT component, current_size/1048576 mb FROM v$memory_dynamic_components WHERE current_size>0;","postgres":"SHOW shared_buffers; SHOW work_mem; -- work_mem * connections * number of sorts can blow up in total","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW STATUS LIKE 'Innodb_buffer_pool%';","redis":"INFO memory  # used_memory, maxmemory, mem_fragmentation_ratio"}},{"title":"Konfiguration von Last trennen","detail":"Übersteigt die Summe der Speichereinstellungen den physischen RAM (Over-Commit)? Berechne shared_buffers + (work_mem * Parallelität). Oder ist es eine kurzzeitige Last (eine riesige Sortierung/ein riesiger Hash, viele Verbindungen)?"},{"title":"Redis-spezifisch: Eviction und Fragmentierung","detail":"Hat used_memory maxmemory erreicht, prüfe maxmemory-policy (bei noeviction werden Schreibvorgänge abgewiesen). Ist mem_fragmentation_ratio hoch, nutze MEMORY PURGE / activedefrag. Finde große Keys mit --bigkeys.","engines":{"redis":"CONFIG GET maxmemory-policy\nMEMORY DOCTOR\nredis-cli --bigkeys"}},{"title":"Sofort entlasten","detail":"Beende die ausufernde Abfrage/Session, die Speicher frisst, senke vorübergehend den Session-Speicher wie work_mem, trenne unnötige Verbindungen. Swappt das System, hat das Verlassen des Swaps Vorrang."},{"title":"Richtig dimensionieren","detail":"Verteile den RAM richtig mit dem Rechner unter Tools > Memory/Buffer Pool. Allgemeine Regel: auf einem dedizierten Server Buffer Pool/SGA ~70-80 % des RAM; der gesamte Session-Speicher multipliziert mit der Parallelität darf den RAM nicht übersteigen. Füge bei Bedarf RAM hinzu."}],"related":["cache-hit-ratio","show-config-param","reclaim-space"]},{"id":"log-growth","title":"Aufblähen von Transaktionsprotokoll/Undo","icon":"📈","symptom":"Die Datei des Transaktionsprotokolls oder der Undo-/Redo-Bereich wächst schnell; 'log full', 'ORA-01555 snapshot too old', 'unable to extend undo'.","steps":[{"title":"Die offene Transaktion finden, die das Log wachsen lässt","detail":"Log/Undo kann erst bereinigt werden, wenn die älteste aktive Transaktion beendet ist. Eine einzige lang offene Transaktion kann das gesamte Log festhalten.","engines":{"oracle":"SELECT s.sid, s.username, t.start_time, t.used_ublk FROM v$transaction t JOIN v$session s ON t.ses_addr=s.saddr ORDER BY t.start_time;","postgres":"SELECT pid, state, xact_start, now()-xact_start age, query FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start;","mysql":"SELECT trx_id, trx_started, trx_mysql_thread_id FROM information_schema.innodb_trx ORDER BY trx_started;","mssql":"DBCC OPENTRAN;"}},{"title":"Engine-spezifische Ursache verstehen","detail":"MSSQL: Im Recovery-Modell FULL wächst das Log unbegrenzt, wenn keine Log-Backups erstellt werden. Oracle: ORA-01555 = unzureichende Undo-Retention/-Größe + eine lange Abfrage. PG: Rückt ein Replication Slot nicht vor, sammelt sich WAL an; eine lange Transaktion blockiert VACUUM."},{"title":"Die richtige Maßnahme für das MSSQL-Log","detail":"Erstelle ein Log-Backup (BACKUP LOG) – dadurch wird das Log wiederverwendbar. Soll das Recovery-Modell tatsächlich SIMPLE sein, ändere es. Verkleinere das Log danach bei Bedarf (und nur bei Bedarf).","engines":{"mssql":"BACKUP LOG [DB] TO DISK='C:\\backup\\DB_log.trn';\n-- Then, if needed: DBCC SHRINKFILE (N'DB_log', 512);"}},{"title":"PG-WAL / Replication Slot prüfen","detail":"Ein Replication Slot, der nicht vorrückt (inaktiv), führt zur Ansammlung von WAL. Prüfe vor dem Löschen den Besitzer des Slots (slot_type, database, plugin): Es kann eine vorübergehend getrennte Standby oder ein logischer Subscriber/CDC (z. B. Debezium) sein. Wird der Slot gelöscht, muss die angeschlossene Standby neu aufgebaut und die Subscriber-/CDC-Seite neu synchronisiert werden. Lösche ihn nur, wenn er wirklich ungenutzt ist; zum dauerhaften Schutz setze mit max_slot_wal_keep_size (PG13+) eine Obergrenze.","engines":{"postgres":"SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) retained FROM pg_replication_slots ORDER BY 3 DESC;\n-- If unneeded: SELECT pg_drop_replication_slot('slot_name');"}},{"title":"Undo/Retention abstimmen und Wiederholung verhindern","detail":"Oracle: Vergrößere den UNDO-Tablespace, setze undo_retention passend zu langen Abfragen (oder RETENTION GUARANTEE). Verhindere lang offene Transaktionen in der Anwendung. Überwache, dass der Log-Backup-Job läuft."}],"related":["open-transactions","reclaim-space","last-backup-status"]},{"id":"deadlocks","title":"Deadlock-Analyse","icon":"♻️","symptom":"'deadlock detected', 'ORA-00060', 'chosen as deadlock victim' – Transaktionen sperren sich gegenseitig, und eine wird abgebrochen.","steps":[{"title":"Deadlocks sind normal, sollten aber nicht häufig sein","detail":"Die DB erkennt den Deadlock und löst ihn, indem sie ein Opfer wählt – das ist so vorgesehen. Ziel ist, die Häufigkeit zu senken. Die Anwendung sollte die Opfertransaktion wiederholen. Erfasse zuerst den Deadlock-Graphen."},{"title":"Deadlock-Graph/-Details abrufen","detail":"Welche zwei (oder mehr) Sessions, welche Ressourcen und welche Lock-Modi sind kollidiert?","engines":{"oracle":"-- Check the trace file referenced in the alert log (deadlock graph). Also:\nSELECT * FROM dba_hist_active_sess_history WHERE event LIKE 'enq: TX%' ORDER BY sample_time DESC;","postgres":"-- log_lock_waits=on; deadlock details are in postgresql.log. Check the deadlock_timeout setting.","mysql":"SHOW ENGINE INNODB STATUS\\G  -- LATEST DETECTED DEADLOCK section","mssql":"-- Deadlock XML in the system_health Extended Events session (ring_buffer, recent events):\nSELECT x.ev.value('@timestamp','datetime2') AS event_time, x.ev.query('.') AS deadlock_xml\nFROM (SELECT CAST(t.target_data AS XML) td FROM sys.dm_xe_session_targets t\n      JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address\n      WHERE s.name = N'system_health' AND t.target_name = N'ring_buffer') r\nCROSS APPLY r.td.nodes('//RingBufferTarget/event[@name=\"xml_deadlock_report\"]') AS x(ev);\n-- For longer history, the event_file target:\n-- SELECT CAST(event_data AS XML) FROM sys.fn_xe_file_target_read_file(N'system_health*.xel', NULL, NULL, NULL) WHERE object_name = N'xml_deadlock_report';"}},{"title":"Das Konfliktmuster auflösen","detail":"Häufigste Ursache: Zwei Transaktionen greifen in UMGEKEHRTER Reihenfolge auf Ressourcen zu. Greife in allen Codepfaden in DERSELBEN Reihenfolge auf Ressourcen zu. Zweithäufigste Ursache: breite Bereichssperren wegen eines fehlenden Index – lege den benötigten Index an."},{"title":"Dauer und Umfang der Sperren verringern","detail":"Halte Transaktionen kurz (keine Benutzerinteraktion innerhalb einer Transaktion). Wähle eine passende Isolationsstufe. Setze bei Bedarf Zeilensperren frühzeitig (SELECT ... FOR UPDATE mit deterministischer Reihenfolge)."},{"title":"Retry in der Anwendung ist Pflicht","detail":"Deadlocks lassen sich nicht vollständig vermeiden; implementiere für die Opfertransaktion eine automatische Wiederholung (Exponential Backoff). So sieht der Benutzer keinen Fehler."}],"related":["blocking-sessions","open-transactions","list-indexes"]},{"id":"db-down","title":"Datenbank öffnet nicht / nicht erreichbar","icon":"🚨","symptom":"Keine Verbindung zum Dienst, 'connection refused', die Instanz startet nicht, der Listener antwortet nicht.","steps":[{"title":"Wo bricht es ab: Netzwerk, Dienst oder Instanz","detail":"Ist der Server erreichbar (ping/ssh)? Läuft der DB-Prozess/-Dienst (ps/systemctl/services.msc)? Lauscht der Port (ss -ltnp / telnet host port)? So ordnest du das Problem einer Schicht zu: Netzwerk / Dienst / Datenbankinstanz."},{"title":"Dienst und Listener prüfen","detail":"Läuft der DB-Prozess nicht, versuche ihn zu starten; läuft er, aber Verbindungen schlagen fehl, prüfe Listener/Port und die Authentifizierungsschicht.","engines":{"oracle":"-- OS: lsnrctl status  (listener)\n-- SQL*Plus: STARTUP;  (if the instance is down)\nSELECT status FROM v$instance;","postgres":"-- OS: systemctl status postgresql ; pg_isready -h host\n-- Logs: the last lines tell why it did not start","mysql":"-- OS: systemctl status mysqld ; mysqladmin ping","mssql":"-- Services / SQL Server Configuration Manager; SQL Server Error Log","redis":"redis-cli ping  # expect PONG"}},{"title":"Logs lesen – dort steht die Antwort","detail":"Öffnet die DB nicht, steht der Grund im Log: Alert Log (Oracle), postgresql.log / Startup, mysqld-Error-Log, SQL Server ERRORLOG, Redis-Log. Volle Platte, beschädigte Datei, Berechtigungen, Portkonflikt, fehlende Datei – alles zeigt sich hier."},{"title":"Häufige Ursachen angehen: Platte, Berechtigungen, Port, Korruption","detail":"Ist die Platte zu 100 % voll (häufigste Ursache)? Sind die Berechtigungen des Datenverzeichnisses defekt? Ist der Port von einem anderen Prozess belegt? Ist eine Recovery nötig (nach einem Absturz)? Ist eine Control File oder ein Redo-Log beschädigt? Gehe jede Ursache anhand der Meldung im Log gezielt an."},{"title":"Wiederherstellen und einen Incident-Bericht führen","detail":"Die Crash Recovery läuft meist automatisch durch; hab Geduld. Liegt Korruption vor, stelle aus dem Backup/per PITR wieder her (siehe das Korruptions-Playbook). Schreibe danach eine Ursachenanalyse (RCA): was passiert ist, warum und wie eine Wiederholung verhindert wird."}],"related":["version-uptime","connect-cli","last-backup-status"]},{"id":"corruption","title":"Datenkorruption","icon":"🧯","symptom":"'ORA-01578', 'page corruption', 'checksum mismatch', 'MISCONF', I/O-Konsistenzfehler beim Lesen.","steps":[{"title":"Ruhe bewahren und die Ausbreitung stoppen","detail":"Panische Eingriffe verschlimmern die Lage. Bestimme zuerst den Umfang und stoppe nach Möglichkeit Schreibvorgänge auf das betroffene Objekt. Ist es hardwarebedingt (Platte/Controller), prüfe auch die OS-/Hardware-Logs – eine Reparatur der DB repariert nicht die Platte."},{"title":"Umfang bestimmen: wie viel ist beschädigt","detail":"Betrifft es einen einzelnen Block, eine Seite oder ein Objekt, oder ist es weit verbreitet? Führe eine Integritätsprüfung aus.","engines":{"oracle":"RMAN> VALIDATE DATABASE;  -- or: SELECT * FROM v$database_block_corruption;","postgres":"-- With data_checksums enabled it shows up in the logs; check indexes/heap with pg_amcheck:\n-- pg_amcheck --heapallindexed -d mydb","mysql":"CHECK TABLE customer;  -- for InnoDB, innodb_force_recovery is the last resort","mssql":"DBCC CHECKDB ('MyDB') WITH NO_INFOMSGS, ALL_ERRORMSGS;","redis":"redis-check-rdb dump.rdb\n# AOF (Redis 7+ multi-part: under appendonlydir/, pass the manifest file):\nredis-check-aof appendonlydir/appendonly.aof.manifest\n# --fix TRUNCATES everything after the corrupted point (writes in that part are lost); back up the directory first:\n# redis-check-aof --fix appendonlydir/appendonly.aof.manifest"}},{"title":"Priorität: Wiederherstellung aus dem Backup","detail":"Bei Korruption ist die Wiederherstellung aus einem guten Backup der sicherste Weg. Wird Recovery auf Block-/Seitenebene unterstützt, stelle nur den beschädigten Teil wieder her (Oracle RMAN BLOCKRECOVER, MSSQL Page Restore). Reparaturwerkzeuge (DBCC REPAIR_ALLOW_DATA_LOSS, innodb_force_recovery) VERURSACHEN DATENVERLUST – sie sind das letzte Mittel."},{"title":"Integrität prüfen","detail":"Führe nach der Wiederherstellung die Prüfung erneut aus (DBCC CHECKDB / RMAN VALIDATE / CHECK TABLE) und bestätige, dass sie ohne Befund zurückkommt. Prüfe außerdem die Datenkonsistenz auf Anwendungsseite (Zeilenanzahlen, kritische Summen)."},{"title":"Ursache: meist Hardware","detail":"Korruption entsteht meist durch Ausfälle von Platte, Speicher oder Controller. Teste bzw. ersetze die Hardware, aktiviere Checksummen (data_checksums, PAGE_VERIFY CHECKSUM) und verschärfe die Routine aus Backup und Verifizierung. Plane regelmäßige DBCC CHECKDB / RMAN VALIDATE."}],"related":["last-backup-status","restore-dump","db-down"]},{"id":"long-running-query","title":"Lang laufende Abfrage / lang laufender Job","icon":"⏱️","symptom":"Eine einzelne Abfrage bzw. ein Job läuft seit Stunden, ohne fertig zu werden; sie verbraucht Ressourcen oder beeinträchtigt andere.","steps":[{"title":"Abfrage und Laufzeit erfassen","detail":"Welche Abfrage, wie lange läuft sie schon, welcher Benutzer bzw. welche Anwendung?","engines":{"oracle":"SELECT sid, serial#, sql_id, last_call_et/60 running_min, username FROM v$session WHERE status='ACTIVE' AND type='USER' ORDER BY last_call_et DESC;","postgres":"SELECT pid, now()-query_start dur, usename, query FROM pg_stat_activity WHERE state='active' ORDER BY query_start;","mysql":"SELECT id, user, time, state, info FROM information_schema.processlist WHERE command='Query' ORDER BY time DESC;","mssql":"SELECT session_id, total_elapsed_time/1000 sec, status, command FROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1) ORDER BY total_elapsed_time DESC;","mongodb":"db.currentOp({ active: true, secs_running: { $gt: 60 } })"}},{"title":"Macht sie Fortschritte oder hängt sie","detail":"Läuft sie wirklich, oder wartet sie auf einen Lock? Prüfe die Fortschrittsanzeigen (Oracle v$session_longops, PG-Views pg_stat_progress_*). Wartet sie, wechsle zum Blocking-Playbook.","engines":{"oracle":"SELECT sid, opname, sofar, totalwork, ROUND(sofar/NULLIF(totalwork,0)*100,1) pct, time_remaining FROM v$session_longops WHERE sofar<totalwork;","postgres":"SELECT * FROM pg_stat_progress_create_index;  -- or pg_stat_progress_vacuum / _cluster / _analyze"}},{"title":"Entscheiden: warten oder abbrechen","detail":"Macht sie Fortschritte und ist bald fertig, warte (die Rollback-Kosten eines Abbruchs können alles noch verlängern). Hängt sie, läuft sie fehlerhaft oder beeinträchtigt sie die Produktion, beende sie. Bedenke vor dem Abbruch die Rollback-Dauer."},{"title":"Bei Bedarf beenden","detail":"Breche bevorzugt nur die Abfrage ab (die Session bleibt bestehen); funktioniert das nicht, beende die Session.","engines":{"oracle":"ALTER SYSTEM CANCEL SQL 'sid,serial#';  -- 18c+; otherwise KILL SESSION","postgres":"SELECT pg_cancel_backend(pid);  -- otherwise pg_terminate_backend(pid)","mysql":"KILL QUERY <id>;","mssql":"KILL <session_id>;","mongodb":"db.killOp(<opid>)"}},{"title":"Dauerhaft beheben","detail":"Optimiere die Abfrage (Index, Plan, Paginierung/Batching), verlege schwere Jobs in lastarme Zeiten und führe sie bei Bedarf in Teilen aus. Erwäge für wiederkehrende Berichte eine Materialized View bzw. Read Replica."}],"related":["active-sessions","kill-session","explain-plan","top-slow-queries"]},{"id":"xid-wraparound","title":"Postgres XID-Wraparound / Autovacuum-Krise","icon":"🧬","symptom":"'database is not accepting commands to avoid wraparound data loss'; autovacuum kommt nicht hinterher, datfrozenxid altert.","steps":[{"title":"Die Dringlichkeit verstehen","detail":"PostgreSQL-Transaktions-IDs sind 32-Bit-Werte; nähern sie sich der Erschöpfung, STOPPT der Server SCHREIBVORGÄNGE, um Datenverlust zu verhindern. Das ist eine kritische, zeitkritische Lage. Die gute Nachricht: Sie ist lösbar; es braucht Disziplin, keine Panik."},{"title":"Messen, wie nah du an der Grenze bist","detail":"Ermittle für jede Datenbank das Alter der ältesten nicht eingefrorenen XID; die Nähe zu autovacuum_freeze_max_age (Standard 200M) zeigt die Gefahr.","engines":{"postgres":"SELECT datname, age(datfrozenxid) AS xid_age\nFROM pg_database ORDER BY xid_age DESC;"}},{"title":"Die ältesten Tabellen finden","detail":"Das Problem geht meist von wenigen großen bzw. häufig beschriebenen Tabellen aus; konzentriere dich auf die ältesten relfrozenxid-Werte.","engines":{"postgres":"SELECT relname, age(relfrozenxid) AS xid_age\nFROM pg_class WHERE relkind IN ('r','m','t')\nORDER BY xid_age DESC LIMIT 20;"}},{"title":"Beseitigen, was VACUUM blockiert","detail":"Lang offene Transaktionen, verwaiste Replication Slots und Prepared Transactions blockieren das Aufräumen (Freeze). Finde und löse sie – sonst kommt VACUUM nicht voran, egal wie lange es läuft.","engines":{"postgres":"SELECT pid, now()-xact_start AS age, state FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY age DESC;\nSELECT slot_name, active FROM pg_replication_slots WHERE NOT active;\nSELECT * FROM pg_prepared_xacts;"}},{"title":"Ein aggressives FREEZE ausführen","detail":"Sind die Blocker beseitigt, erzwinge das Einfrieren, beginnend mit den ältesten Tabellen. Selbst wenn der Server keine neuen Transaktions-IDs mehr vergibt, wechsle nicht in den Single-User-Modus (postgres --single): Laut aktueller PostgreSQL-Dokumentation ist er nicht mehr nötig und sollte vermieden werden. Es genügt, sich im normalen Modus als Superuser zu verbinden und VACUUM auszuführen; ab PG14+ greift der Failsafe-Mechanismus von VACUUM, wenn der Wraparound näher rückt.","engines":{"postgres":"VACUUM (FREEZE, VERBOSE) oldest_table;\n-- Entire database: vacuumdb --all --freeze --jobs=4"}},{"title":"Dauerhaft: Autovacuum stärken und überwachen","detail":"Erhöhe autovacuum_vacuum_cost_limit, senke autovacuum_naptime und setze für stark beschriebene Tabellen tabellenspezifische Storage-Parameter. Richte einen Alarm auf age(datfrozenxid) ein; erlebe diese Krise nie wieder."}],"related":["open-transactions","reclaim-space"]},{"id":"tempdb-contention","title":"tempdb-Contention (SQL Server)","icon":"🌀","symptom":"PAGELATCH_UP/EX-Waits, Contention auf den tempdb-Allokationsseiten (PFS/GAM/SGAM), allgemeine Langsamkeit.","steps":[{"title":"Die Contention bestätigen","detail":"PAGELATCH-Waits auf tempdb-Systemseiten (etwa 2:1:1) deuten auf Allokations-Contention hin.","engines":{"mssql":"SELECT session_id, wait_type, wait_duration_ms, resource_description\nFROM sys.dm_os_waiting_tasks\nWHERE wait_type LIKE 'PAGELATCH%' AND resource_description LIKE '2:%'\nORDER BY wait_duration_ms DESC;"}},{"title":"tempdb-Nutzung aufschlüsseln","detail":"Wer nutzt wie viel tempdb? Interne Objekte (Sort-/Hash-Spills), der Version Store oder Benutzerobjekte?","engines":{"mssql":"SELECT * FROM sys.dm_db_file_space_usage;\nSELECT TOP 10 session_id, user_objects_alloc_page_count, internal_objects_alloc_page_count\nFROM sys.dm_db_task_space_usage ORDER BY internal_objects_alloc_page_count DESC;"}},{"title":"Die Dateikonfiguration korrigieren","detail":"Verwende für tempdb mehrere GLEICH große Datendateien, entsprechend der Anzahl der CPU-Kerne (bis zu 8). Ab SQL 2016+ wird das bei der Installation empfohlen; in älteren Versionen nutze die Trace Flags 1117/1118. Lege die Dateien auf eine schnelle Platte (lokale NVMe)."},{"title":"Die ausufernde Abfrage finden","detail":"Abfragen mit großen Sort-/Hash-Spills lassen tempdb explodieren. Nimm diejenigen mit unzureichenden Memory Grants oder schlechten Plänen ins Visier; verringere Spills mit Indizes/Plänen.","engines":{"mssql":"SELECT TOP 10 t.text, mg.requested_memory_kb, mg.granted_memory_kb\nFROM sys.dm_exec_query_memory_grants mg CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) t\nORDER BY mg.requested_memory_kb DESC;"}},{"title":"Entsprechungen in anderen Datenbanken","detail":"Dieselbe 'Contention um temporären Platz' ist in Oracle der TEMP-Tablespace (v$tempseg_usage / v$sort_usage) und in PostgreSQL temp_files und Überlauf von work_mem. Die eigentliche Lösung ist immer dieselbe: die auslagernde Abfrage korrigieren und den temporären Speicher beschleunigen."}],"related":["free-space","top-slow-queries","reclaim-space"]},{"id":"cert-expiry","title":"TLS / Zertifikat abgelaufen","icon":"🔐","symptom":"'certificate has expired', 'SSL handshake failed', Clients können sich plötzlich nicht mehr verbinden (ohne Codeänderung).","steps":[{"title":"Wo bricht es ab: Server, Client oder CA?","detail":"Ist das Serverzertifikat abgelaufen, ein Client-Zertifikat oder eine CA in der Kette? Das Symptom lautet meist: 'Gestern ging es, heute nicht, am Code wurde nichts geändert'."},{"title":"Ablaufdatum prüfen","detail":"Lies das Ablaufdatum des Zertifikats direkt vom Server (eine schnelle, datenbankunabhängige Methode).","engines":{"postgres":"openssl s_client -connect host:5432 -starttls postgres </dev/null 2>/dev/null | openssl x509 -noout -dates","mongodb":"openssl s_client -connect host:27017 </dev/null 2>/dev/null | openssl x509 -noout -enddate","redis":"openssl s_client -connect host:6379 </dev/null 2>/dev/null | openssl x509 -noout -enddate"}},{"title":"Neues Zertifikat installieren und laden","detail":"Ersetze das Zertifikat und lade den Dienst in den meisten Fällen neu, ohne ihn neu zu starten.","engines":{"postgres":"-- Renew ssl_cert_file/ssl_key_file, then:\nSELECT pg_reload_conf();","mysql":"ALTER INSTANCE RELOAD TLS;","mssql":"-- SQL Server Configuration Manager > Certificate; a service restart is required","mongodb":"// Renew the net.tls.certificateKeyFile file (at the same path), then load it without a restart (5.0+):\ndb.adminCommand({rotateCertificates: 1})","redis":"CONFIG SET tls-cert-file /path/new.crt\nCONFIG SET tls-key-file /path/new.key","elasticsearch":"# ES watches the certificate/key files (PEM, PKCS#12) defined under xpack.security.*.ssl and\n# reloads them automatically when the file at the SAME path changes (no restart needed).\n# If the path, file name or keystore password changes, a rolling restart is required.\n# Note: POST _nodes/reload_secure_settings is only for reloadable keystore settings, not for TLS certificates.\n# Verify: check the new expiry date with openssl s_client and look in the ES logs for\n# an entry showing the certificate was reloaded."}},{"title":"Prüfen","detail":"Bestätige mit dem obigen openssl-Befehl, dass der TLS-Handshake mit dem neuen Zertifikat gelingt und das Ablaufdatum verlängert wurde; stelle mit einem Client eine End-to-End-Verbindung her."},{"title":"Dauerhaft: nie wieder","detail":"Richte 30 Tage vor Ablauf des Zertifikats einen Alarm ein; nutze automatische Erneuerung (ACME/Let's Encrypt, HashiCorp Vault, cert-manager). Automatisiere das Neuladen nach der Erneuerung."}],"related":["connect-cli"]},{"id":"upgrade-rollback","title":"Versions-Upgrade / Patch-Rollback","icon":"⏪","symptom":"Probleme nach einem Upgrade/Patch: Performance-Regression, Inkompatibilität mit der Anwendung oder die Datenbank öffnet nicht.","steps":[{"title":"Erst innehalten: Hast du einen Rückfallplan?","detail":"Hast du VOR dem Produktions-Upgrade kein Vollbackup + (wenn möglich) keinen Storage-Snapshot erstellt, ist Fix-Forward der sicherste Weg. In den meisten Datenbanken bedeutet ein Rollback die Wiederherstellung aus dem Backup. Dieser Schritt hätte eigentlich VOR dem Upgrade geplant sein sollen."},{"title":"Das Problem einordnen","detail":"Handelt es sich um eine Planregression des Optimizers (Abfragen wurden langsamer), eine Inkompatibilität von Anwendung/Treiber oder ein Problem beim Öffnen/Korruption? Der Lösungsweg unterscheidet sich danach stark."},{"title":"Rollback-Weg je Datenbank","detail":"Oracle: Patch-Rollback mit datapatch oder Flashback Database (wenn du einen Guaranteed Restore Point angelegt hast). PostgreSQL: Rollback nach pg_upgrade = alte Binaries + Backup des alten Datenverzeichnisses. MySQL: Ein In-Place-DOWNGRADE wird generell NICHT UNTERSTÜTZT – aus dem Backup wiederherstellen. SQL Server: Es gibt kein Downgrade – aus dem Backup vor dem Upgrade wiederherstellen. In jedem Fall gilt: Snapshot/Backup ist deine Versicherung."},{"title":"Bei Planregression: ohne Rollback beheben","detail":"Meist muss nicht die ganze Version zurückgerollt werden; geändert haben sich nur die Pläne.","engines":{"oracle":"ALTER SYSTEM SET optimizer_features_enable='19.1.0';  -- pin to the old behavior\n-- or lock in the good plan with a SQL Plan Baseline","postgres":"ANALYZE;  -- fresh statistics first\n-- if needed, temporarily: steer the plan with settings such as SET enable_nestloop=off;"}},{"title":"Dauerhaft: beim nächsten Mal","detail":"Teste das Upgrade zuerst in Staging mit realer Workload, wende Canary-/Rolling-Upgrades an, erstelle einen Guaranteed Restore Point / Snapshot und dokumentiere das Rückfallverfahren."}],"related":["last-backup-status","restore-dump","version-uptime"]},{"id":"es-red-cluster","title":"Elasticsearch-Cluster red/yellow & nicht zugewiesene Shards","icon":"🟥","symptom":"_cluster/health 'red' oder 'yellow'; nicht zugewiesene Shards; Lese-/Schreibvorgänge eingeschränkt oder Daten nicht erreichbar.","steps":[{"title":"Farbe und Ursache","detail":"yellow = Replica Shards sind nicht zugewiesen (Daten vorhanden, Ausfallsicherheit reduziert). red = mindestens ein PRIMARY Shard fehlt (diese Daten sind nicht erreichbar). Bestimme zuerst, welcher Fall vorliegt und warum.","engines":{"elasticsearch":"GET _cluster/health?level=indices\nGET _cluster/allocation/explain"}},{"title":"Nicht zugewiesene Shards und Grund auflisten","detail":"Die Spalte unassigned.reason nennt die Ursache (NODE_LEFT, ALLOCATION_FAILED, DISK usw.).","engines":{"elasticsearch":"GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state"}},{"title":"Die häufigen Ursachen angehen","detail":"1) Disk Watermark → oberhalb von low erhält der Knoten keine neuen Shards; bei flood_stage werden Indizes schreibgeschützt. 2) Ein Knoten ist ausgefallen bzw. hat den Cluster verlassen. 3) Die Allokation wurde deaktiviert gelassen. 4) Replica-Anzahl > Anzahl geeigneter Knoten."},{"title":"Beheben","detail":"Hebe die Blockade je nach zutreffender Ursache auf.","engines":{"elasticsearch":"# If disk space was freed, remove the read-only block (7.4+ also lifts it automatically once below the high watermark):\nPUT _all/_settings {\"index.blocks.read_only_allow_delete\": null}\n# If allocation was left restricted, return it to the default (all):\nPUT _cluster/settings {\"persistent\":{\"cluster.routing.allocation.enable\":null}}\n# Retry failed allocations:\nPOST _cluster/reroute?retry_failed=true"}},{"title":"Wenn weiterhin red und der Primary wirklich verloren ist","detail":"Stelle den betroffenen Index aus einem Snapshot wieder her oder führe aus den Quelldaten einen Reindex durch (_reindex). Dauerhaft: Alarme für Disk Watermarks, eine passende Replica-Anzahl (≥1) und Überwachung der Knotengesundheit."}],"related":["replication-status","list-replicas","free-space"]},{"id":"time-skew","title":"Clock Skew / NTP-Problem","icon":"⏱️","symptom":"Uhrzeitabweichungen zwischen Cluster-Knoten; Inkonsistenzen bei Replikation/Wahlen, 'certificate not yet valid', Zeitsprünge in Logs.","steps":[{"title":"Das Symptom erkennen","detail":"Clock Skew ist tückisch: Wahl-Flapping bei Mongo/Redis Sentinel, TLS-Fehler 'not yet valid/expired', Kerberos-Fehler, rückwärts laufende Zeitstempel in Logs. In verteilten Systemen ist er die Wurzel der meisten 'unerklärlichen' Merkwürdigkeiten."},{"title":"Die Abweichung messen","detail":"Vergleiche auf jedem Knoten Zeitquelle und Offset: `timedatectl`, `chronyc tracking` (oder `ntpq -p`) und `date -u` über die Knoten hinweg. Wie viele ms/s liegen sie auseinander?"},{"title":"NTP/chrony korrigieren","detail":"Läuft chronyd/ntpd, erreicht es seine Zeitquellen, ist es synchronisiert? Starte den Dienst und korrigiere die Quelle; bei großen Offsets stelle vorsichtig um (ein plötzlicher Sprung kann bei manchen DBs etwas auslösen)."},{"title":"Die betroffene Komponente prüfen","detail":"Prüfe nach der Korrektur der Uhr: Ist die Wahl im MongoDB Replica Set stabil, ist Redis Sentinel/Cluster gesund, gelingen TLS-Handshakes, haben sich die Metriken zum Replikations-Lag erholt?"},{"title":"Dauerhaft","detail":"Mache NTP auf allen Knoten verpflichtend und überwache den Offset (Alarm: >100ms). Kläre auf virtuellen Maschinen die Einstellung zur Zeitsynchronisation zwischen Host und Gast; nutze vorzugsweise eine einzige, zuverlässige Zeitquellen-Hierarchie."}],"related":["replication-status"]}]},"playbooks/hadr.json":{"schemaVersion":1,"playbooks":[{"id":"hadr-oracle-dataguard","title":"Einrichtung von Oracle Data Guard (Physical Standby)","icon":"🛡️","symptom":"Ziel: Disaster Recovery und Hochverfügbarkeit für Oracle mit einer Physical Standby (Data Guard) einrichten; bei Ausfall der Primary auf die Standby umschalten können.","steps":[{"title":"Primary in den Modus FORCE LOGGING und ARCHIVELOG versetzen","detail":"Damit die Standby mit der Primary konsistent bleibt, müssen alle Änderungen ins Redo geschrieben werden. FORCE LOGGING protokolliert selbst NOLOGGING-Operationen. Für den ARCHIVELOG-Modus wird die Datenbank im Zustand MOUNT gestartet.","engines":{"oracle":"ALTER DATABASE FORCE LOGGING;\n-- For ARCHIVELOG:\nSHUTDOWN IMMEDIATE;\nSTARTUP MOUNT;\nALTER DATABASE ARCHIVELOG;\nALTER DATABASE OPEN;"}},{"title":"Standby-Redo-Log-Dateien hinzufügen","detail":"Standby-Redo-Logs (SRL) sollten eine Gruppe mehr als die Online-Redo-Logs haben und gleich groß sein. Sie werden auf Primary und Standby angelegt; für Real-Time Apply sind sie erforderlich. Ist im Beispiel das Online Redo 200M groß, sind auch die SRLs 200M groß.","engines":{"oracle":"ALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl01.log') SIZE 200M;\nALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl02.log') SIZE 200M;\nALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl03.log') SIZE 200M;\nALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl04.log') SIZE 200M;"}},{"title":"log_archive_dest und zugehörige Parameter setzen","detail":"Definiere auf der Primary dest_2, um Redo an die Standby zu übertragen. Setze DB_UNIQUE_NAME, LOG_ARCHIVE_CONFIG (die db_unique_name beider Seiten), FAL_SERVER und STANDBY_FILE_MANAGEMENT=AUTO. So werden Dateien auf der Standby automatisch angelegt.","engines":{"oracle":"ALTER SYSTEM SET LOG_ARCHIVE_CONFIG='DG_CONFIG=(orcl_prim,orcl_stby)';\nALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=orcl_stby ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=orcl_stby';\nALTER SYSTEM SET FAL_SERVER='orcl_stby';  -- for role changes; on the standby FAL_SERVER='orcl_prim'\nALTER SYSTEM SET STANDBY_FILE_MANAGEMENT=AUTO;"}},{"title":"Standby mit RMAN DUPLICATE FOR STANDBY erstellen","detail":"Nachdem auf dem Standby-Server Passwortdatei und statischer Listener/tnsnames eingerichtet sind, verbindet sich RMAN mit der Primary und mit der Standby (AUXILIARY). FROM ACTIVE DATABASE kopiert ohne Backup über das Netzwerk; DORECOVER wendet Redo an.","engines":{"oracle":"rman TARGET sys/password@orcl_prim AUXILIARY sys/password@orcl_stby\nRUN {\n  DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE\n  DORECOVER\n  NOFILENAMECHECK;\n}"}},{"title":"Redo Apply (MRP) starten und prüfen","detail":"Starte die Managed Recovery, während die Standby im Zustand MOUNT ist; nutze USING CURRENT LOGFILE für Real-Time Apply. Prüfe mit v$managed_standby, dass der Apply-Prozess (MRP0) läuft.","engines":{"oracle":"ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;\nSELECT process, status, sequence# FROM v$managed_standby WHERE process LIKE 'MRP%';"}},{"title":"Data Guard Broker aktivieren","detail":"Der Broker zentralisiert die Konfiguration und reduziert Failover/Switchover auf einen einzigen Befehl. Setze DG_BROKER_START=TRUE auf beiden Instanzen und erstelle und aktiviere dann die Konfiguration mit dgmgrl. Ab 12c verwaltet der Broker den Redo-Transport: Wurde LOG_ARCHIVE_DEST_2 manuell gesetzt, schlägt ADD DATABASE mit ORA-16698 fehl; leere den Parameter zuerst mit ALTER SYSTEM SET LOG_ARCHIVE_DEST_2=''.","engines":{"oracle":"ALTER SYSTEM SET DG_BROKER_START=TRUE;\n-- In dgmgrl:\nCREATE CONFIGURATION dgconf AS PRIMARY DATABASE IS orcl_prim CONNECT IDENTIFIER IS orcl_prim;\nADD DATABASE orcl_stby AS CONNECT IDENTIFIER IS orcl_stby;\nENABLE CONFIGURATION;"}},{"title":"Synchronisationsrückstand überwachen und Tests abschließen","detail":"Die Sequence-Differenz zwischen Primary und Standby muss laufend überwacht werden; ein wachsender Rückstand deutet auf einen Netzwerk-/Apply-Engpass hin. dgmgrl SHOW CONFIGURATION sollte 'SUCCESS' anzeigen. Teste optional einen Rollenwechsel per Switchover.","engines":{"oracle":"-- dgmgrl (SHOW DATABASE shows the Transport Lag / Apply Lag values):\nSHOW CONFIGURATION;\nSHOW DATABASE orcl_stby;\n-- SQL (standby): SELECT name, value FROM v$dataguard_stats WHERE name LIKE '%lag';"}}],"related":["replication-status","promote-failover","last-backup-status"]},{"id":"hadr-mssql-alwayson","title":"Einrichtung einer SQL Server Always On Availability Group","icon":"🔁","symptom":"Ziel: automatisches Failover und eine lesbare Secondary Replica für SQL Server mit einer Always On Availability Group (AG) einrichten.","steps":[{"title":"Windows Server Failover Cluster (WSFC) einrichten","detail":"Always On setzt einen darunterliegenden WSFC voraus. Installiere das Feature Failover Clustering auf allen Knoten und konfiguriere Netzwerk und Witness. Erstelle den Cluster mit PowerShell.","engines":{"mssql":"-- PowerShell (the Failover-Clustering feature must be installed on every node):\nNew-Cluster -Name sqlclu -Node node1,node2 -StaticAddress 10.0.0.50\n-- Witness:\nSet-ClusterQuorum -NodeAndFileShareMajority \\\\fs01\\quorum"}},{"title":"Always On auf jeder Instanz aktivieren","detail":"Aktiviere das Feature Always On Availability Groups mit dem SQL Server Configuration Manager oder PowerShell. Starte danach den SQL-Server-Dienst neu. Damit wird die Cluster-Mitgliedschaft an die Instanz gebunden.","engines":{"mssql":"-- PowerShell:\nEnable-SqlAlwaysOn -ServerInstance node1\\SQL01 -Force\nEnable-SqlAlwaysOn -ServerInstance node2\\SQL01 -Force"}},{"title":"Datenbank sichern und auf der Secondary WITH NORECOVERY wiederherstellen","detail":"Die Datenbank, die der AG hinzugefügt wird, muss im Recovery-Modell FULL sein. Erstelle auf der Primary ein Voll- und ein Log-Backup; stelle sie auf der Secondary WITH NORECOVERY wieder her (damit sie weiter Redo empfangen kann). Das ist manuelles Seeding; die Alternative ist automatisches Seeding.","engines":{"mssql":"-- On the primary:\nBACKUP DATABASE app TO DISK='\\\\fs01\\bak\\app.bak';\nBACKUP LOG app TO DISK='\\\\fs01\\bak\\app.trn';\n-- On the secondary:\nRESTORE DATABASE app FROM DISK='\\\\fs01\\bak\\app.bak' WITH NORECOVERY;\nRESTORE LOG app FROM DISK='\\\\fs01\\bak\\app.trn' WITH NORECOVERY;"}},{"title":"Endpoints erstellen und die Availability Group definieren","detail":"Erstelle einen HADR-Endpoint auf Port 5022 für den Redo-Verkehr zwischen den Knoten. Der synchrone Modus (SYNCHRONOUS_COMMIT) bietet automatisches Failover ohne Datenverlust; der asynchrone (ASYNCHRONOUS_COMMIT) hat Verzögerung, ist aber performant für entferntes DR.","engines":{"mssql":"CREATE ENDPOINT hadr_endpoint STATE=STARTED\n  AS TCP (LISTENER_PORT=5022)\n  FOR DATA_MIRRORING (ROLE=ALL, ENCRYPTION=REQUIRED ALGORITHM AES);\n\nCREATE AVAILABILITY GROUP appag\n  FOR DATABASE app\n  REPLICA ON\n    'NODE1\\SQL01' WITH (ENDPOINT_URL='TCP://node1:5022', AVAILABILITY_MODE=SYNCHRONOUS_COMMIT, FAILOVER_MODE=AUTOMATIC, SECONDARY_ROLE(ALLOW_CONNECTIONS=ALL)),\n    'NODE2\\SQL01' WITH (ENDPOINT_URL='TCP://node2:5022', AVAILABILITY_MODE=SYNCHRONOUS_COMMIT, FAILOVER_MODE=AUTOMATIC, SECONDARY_ROLE(ALLOW_CONNECTIONS=ALL));"}},{"title":"Secondary der AG beitreten lassen und Datenbank hinzufügen","detail":"Führe auf der Secondary-Instanz JOIN für die AG aus und füge dann die wiederhergestellte Datenbank der AG hinzu. Dieser Schritt ist bei manuellem Seeding nötig; bei automatischem Seeding erfolgt er auf der Primary über SET(SEEDING_MODE).","engines":{"mssql":"-- On the secondary:\nALTER AVAILABILITY GROUP appag JOIN;\nALTER DATABASE app SET HADR AVAILABILITY GROUP = appag;"}},{"title":"Availability Group Listener erstellen","detail":"Der Listener ist der virtuelle Name samt IP, mit dem sich Anwendungen verbinden; nach einem Failover werden Verbindungen automatisch zur neuen Primary umgeleitet. Verwende im Connection String der Anwendung diesen Namen und MultiSubnetFailover=True.","engines":{"mssql":"ALTER AVAILABILITY GROUP appag\n  ADD LISTENER 'appag-listener' (WITH IP ((N'10.0.0.60', N'255.255.255.0')), PORT=1433);"}},{"title":"Failover testen und Synchronisationszustand prüfen","detail":"Ist die Synchronisation 'HEALTHY', teste einen Rollenwechsel per geplantem Failover. Für automatisches Failover muss der Modus synchron und FAILOVER_MODE=AUTOMATIC sein. Überwache den Zustand mit sys.dm_hadr_availability_replica_states.","engines":{"mssql":"SELECT ag.name, ars.role_desc, ars.synchronization_health_desc\nFROM sys.dm_hadr_availability_replica_states ars\nJOIN sys.availability_groups ag ON ag.group_id=ars.group_id;\n-- Planned failover (on the node that will become the new primary):\nALTER AVAILABILITY GROUP appag FAILOVER;"}}],"related":["replication-status","promote-failover","list-replicas"]},{"id":"hadr-postgres-streaming","title":"PostgreSQL Streaming Replication + automatisches Failover","icon":"🐘","symptom":"Ziel: eine Hot Standby für PostgreSQL mit Streaming Replication einrichten und Patroni/repmgr für automatisches Failover ergänzen.","steps":[{"title":"WAL- und Verbindungsparameter auf der Primary setzen","detail":"Für Streaming Replication muss wal_level mindestens 'replica' sein. max_wal_senders muss die Anzahl der Replicas + Backup-Verbindungen abdecken, und ein vorzeitiges Entfernen von WAL muss mit wal_keep_size oder einem Replication Slot verhindert werden. Die Änderungen erfordern einen Neustart.","engines":{"postgres":"ALTER SYSTEM SET wal_level = 'replica';\nALTER SYSTEM SET max_wal_senders = 10;\nALTER SYSTEM SET max_replication_slots = 10;\nALTER SYSTEM SET hot_standby = on;\n-- To apply the postgresql.conf change:\n-- systemctl restart postgresql"}},{"title":"Replikationsrolle und pg_hba-Zugriff definieren","detail":"Lege eine Rolle mit dem Attribut REPLICATION an, mit der sich die Standby verbindet. Füge in pg_hba.conf eine Zeile hinzu, die den Zugriff auf die Datenbank 'replication' von der Standby-IP erlaubt, und lade mit pg_reload_conf() neu.","engines":{"postgres":"CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'secret';\n-- Add to pg_hba.conf:\n-- host replication replicator 10.0.0.20/32 scram-sha-256\nSELECT pg_reload_conf();"}},{"title":"Persistenten Replication Slot auf der Primary anlegen","detail":"Ein physischer Replication Slot verhindert, dass die Primary benötigtes WAL entfernt, auch wenn die Standby zurückfällt; die Slot-Verwaltung ist sicherer als wal_keep_size. Den Slot-Namen verwenden wir in der Standby-Konfiguration.","engines":{"postgres":"SELECT pg_create_physical_replication_slot('standby1_slot');\nSELECT slot_name, active, restart_lsn FROM pg_replication_slots;"}},{"title":"Base Backup auf dem Standby-Server mit pg_basebackup erstellen","detail":"Erstelle bei leerem Datenverzeichnis der Standby eine vollständige Kopie von der Primary. Das Flag -R schreibt automatisch die Datei standby.signal und primary_conninfo; -C kann zusätzlich den Slot anlegen. Der Slot-Name wird mit -S übergeben.","engines":{"postgres":"pg_basebackup -h 10.0.0.10 -U replicator -D /var/lib/pgsql/data \\\n  -R -X stream -S standby1_slot -P -v"}},{"title":"primary_conninfo und hot_standby prüfen, Standby starten","detail":"Prüfe primary_conninfo (host, user, slot) in der von -R geschriebenen postgresql.auto.conf; das Vorhandensein der Datei standby.signal bestimmt den Standby-Modus. Mit hot_standby=on nimmt die Standby Read-only-Abfragen an. Starte den Dienst.","engines":{"postgres":"-- Expected in postgresql.auto.conf:\n-- primary_conninfo = 'host=10.0.0.10 user=replicator password=secret'\n-- primary_slot_name = 'standby1_slot'\n-- systemctl start postgresql\nSELECT pg_is_in_recovery();  -- should return true on the standby"}},{"title":"Replikations-Lag von der Primary aus überwachen","detail":"Auf der Primary zeigt pg_stat_replication für jede Standby die gesendete/wiedergegebene LSN und den Lag. write/flush/replay lag sollten klein bleiben; sync_state ist 'sync', wenn eine synchrone Replica verwendet wird.","engines":{"postgres":"SELECT client_addr, state, sync_state,\n       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes,\n       write_lag, flush_lag, replay_lag\nFROM pg_stat_replication;"}},{"title":"Patroni oder repmgr für automatisches Failover ergänzen","detail":"Reine Streaming Replication erfordert ein manuelles Promote. Automatisches Failover braucht einen Cluster-Manager: Patroni (Leader-Wahl über etcd/Consul + automatisches Promote) oder repmgr (Überwachung/Failover mit repmgrd). Stelle auf Anwendungsseite mit HAProxy/pgBouncer einen einzigen Endpoint bereit. Ist ein manuelles Promote nötig: pg_ctl promote. Stelle vor dem Promote sicher, dass die alte Primary wirklich gestoppt ist und Anwendungen nicht auf sie schreiben können (Split-Brain-Risiko). Um die alte Primary als Standby der neuen Primary wieder einzubinden, ist pg_rewind nötig; dafür müssen wal_log_hints = on oder Daten-Checksummen aktiviert sein.","engines":{"postgres":"-- Manual promote (if there is no cluster manager):\npg_ctl promote -D /var/lib/pgsql/data\n-- or via SQL:\nSELECT pg_promote();\n-- With Patroni: patronictl -c /etc/patroni.yml switchover mycluster"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-mysql-innodb-cluster","title":"Einrichtung von MySQL InnoDB Cluster (Group Replication)","icon":"🐬","symptom":"Ziel: Hochverfügbarkeit mit automatischem Failover für MySQL mit InnoDB Cluster (Group Replication + MySQL Shell + MySQL Router) einrichten.","steps":[{"title":"Voraussetzungen erfüllen","detail":"InnoDB Cluster erfordert auf jedem Knoten MySQL 8.0+, eine eindeutige server_id, den GTID-Modus und die Storage Engine InnoDB. Alle Instanzen müssen sich gegenseitig über das Netzwerk erreichen, und Tabellen müssen Primärschlüssel haben. Lege einen clusterAdmin-Benutzer für die Administration an.","engines":{"mysql":"-- my.cnf on each instance:\n-- server_id unique, gtid_mode=ON, enforce_gtid_consistency=ON\nCREATE USER 'icadmin'@'%' IDENTIFIED BY 'secret';\nGRANT ALL PRIVILEGES ON *.* TO 'icadmin'@'%' WITH GRANT OPTION;"}},{"title":"Jede Instanz mit MySQL Shell konfigurieren","detail":"In mysqlsh macht dba.configureInstance() jeden Knoten für Group Replication geeignet; es korrigiert fehlende Parameter und fordert bei Bedarf einen Neustart an. Wiederhole das auf allen Knoten.","engines":{"mysql":"-- mysqlsh:\ndba.configureInstance('icadmin@node1:3306')\ndba.configureInstance('icadmin@node2:3306')\ndba.configureInstance('icadmin@node3:3306')"}},{"title":"Cluster erstellen (Seed-Knoten)","detail":"Verbinde dich mit einem Knoten und starte den Cluster mit dba.createCluster(); dieser Knoten wird die erste Primary. Zurückgegeben wird ein Cluster-Objekt, das für weitere Ergänzungen verwendet wird.","engines":{"mysql":"-- mysqlsh, while connected to node1:\nvar cluster = dba.createCluster('appCluster')"}},{"title":"Die übrigen Instanzen zum Cluster hinzufügen","detail":"Füge die Secondary-Knoten mit cluster.addInstance() hinzu. Wähle als recoveryMethod clone (physischer Klon, schnell für einen neuen Knoten) oder incremental (über GTID). Mindestens 3 Knoten sorgen für eine ungerade Mehrheit und automatisches Failover.","engines":{"mysql":"-- mysqlsh:\ncluster.addInstance('icadmin@node2:3306', {recoveryMethod: 'clone'})\ncluster.addInstance('icadmin@node3:3306', {recoveryMethod: 'clone'})"}},{"title":"MySQL Router bootstrappen","detail":"MySQL Router routet zwischen Anwendung und Cluster; er stellt Ports für Lesen/Schreiben (6446) und Read-only (6447) bereit und leitet den Verkehr bei einem Wechsel der Primary automatisch um. Bootstrap erzeugt die Konfiguration aus den Cluster-Metadaten.","engines":{"mysql":"mysqlrouter --bootstrap icadmin@node1:3306 --user=mysqlrouter --directory /etc/mysqlrouter\n# Then: mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf &"}},{"title":"Cluster-Status prüfen und Failover verifizieren","detail":"cluster.status() zeigt Rolle (R/W Primary, R/O Secondary) und Zustand (ONLINE) jedes Knotens. clusterName und Topologie sollten gesund sein. Teste das Failover, indem du die Primary herunterfährst und die automatische Wahl einer neuen Primary beobachtest.","engines":{"mysql":"-- mysqlsh:\nvar cluster = dba.getCluster('appCluster')\ncluster.status()\n-- Members via SQL:\nSELECT member_host, member_role, member_state FROM performance_schema.replication_group_members;"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-mongodb-replicaset","title":"Einrichtung von MongoDB Replica Set + Sharding","icon":"🍃","symptom":"Ziel: ein Replica Set mit automatischem Failover für MongoDB einrichten und es für horizontale Skalierung zu einem Sharded Cluster erweitern.","steps":[{"title":"mongod-Prozesse mit demselben replSet-Namen starten","detail":"Starte jedes Replica-Set-Mitglied mit demselben --replSet-Namen und einer erreichbaren --bind_ip. In Produktion wird interne Authentifizierung per keyFile oder TLS empfohlen. Strebe für eine eindeutige Mehrheit eine ungerade Anzahl stimmberechtigter Mitglieder an.","engines":{"mongodb":"mongod --replSet rs0 --port 27017 --dbpath /data/n1 --bind_ip localhost,10.0.0.11\nmongod --replSet rs0 --port 27017 --dbpath /data/n2 --bind_ip localhost,10.0.0.12\nmongod --replSet rs0 --port 27017 --dbpath /data/n3 --bind_ip localhost,10.0.0.13"}},{"title":"Replica Set initialisieren","detail":"Verbinde dich mit mongosh mit einem Mitglied, rufe rs.initiate() auf und definiere alle Mitglieder. Das löst die erste Wahl aus, und eine Primary wird bestimmt. Prüfe die Rollen PRIMARY/SECONDARY mit rs.status().","engines":{"mongodb":"rs.initiate({\n  _id: 'rs0',\n  members: [\n    { _id: 0, host: '10.0.0.11:27017' },\n    { _id: 1, host: '10.0.0.12:27017' },\n    { _id: 2, host: '10.0.0.13:27017' }\n  ]\n})\nrs.status()"}},{"title":"Mitglieder hinzufügen, Prioritäten und bei Bedarf einen Arbiter setzen","detail":"Füge Mitglieder später mit rs.add() hinzu. priority bestimmt, welches Mitglied bevorzugt Primary wird (0 = wird nie Primary). Ein Arbiter stimmt nur ab und hält keine Daten; er dient bei gerader Anzahl von Datenknoten als Tie-Breaker, für Datensicherheit ist aber ein echtes Mitglied vorzuziehen. In Arbiter-basierten Topologien (vor allem PSA) lässt sich Majority Write Concern nicht erfüllen, wenn ein Datenknoten ausfällt, und Schreibvorgänge warten; ab 5.0 muss der clusterweite Standard-Write-Concern vor dem Hinzufügen eines Arbiters eventuell explizit mit setDefaultRWConcern gesetzt werden.","engines":{"mongodb":"rs.add({ host: '10.0.0.14:27017', priority: 0.5 })\n// Arbiter only if really needed (a data-bearing member is preferred):\nrs.addArb('10.0.0.15:27017')\n// change priority:\ncfg = rs.conf(); cfg.members[0].priority = 2; rs.reconfig(cfg)"}},{"title":"Config-Server-Replica-Set für Sharding einrichten","detail":"Die Metadaten des Sharded Clusters liegen in einem Config-Server-Replica-Set (--configsvr). Es wird selbst als Replica Set initialisiert; für Hochverfügbarkeit werden 3 Mitglieder empfohlen.","engines":{"mongodb":"mongod --configsvr --replSet cfgrs --port 27019 --dbpath /data/cfg1 --bind_ip localhost,10.0.0.21\n-- mongosh (on the config server):\nrs.initiate({ _id: 'cfgrs', configsvr: true, members: [\n  { _id: 0, host: '10.0.0.21:27019' },\n  { _id: 1, host: '10.0.0.22:27019' },\n  { _id: 2, host: '10.0.0.23:27019' } ] })"}},{"title":"Shard-Replica-Sets und den mongos-Router starten","detail":"Jeder Shard ist ein eigenes, mit --shardsvr gestartetes Replica Set (die Dauerhaftigkeit der Daten ergibt sich aus der Replikation innerhalb des Shards). Der mongos-Router wird mit Verweis auf die Config Server gestartet; Anwendungen verbinden sich mit mongos.","engines":{"mongodb":"mongod --shardsvr --replSet shard1rs --port 27018 --dbpath /data/s1 --bind_ip localhost,10.0.0.31\n# initiate shard1rs with rs.initiate, then mongos:\nmongos --configdb cfgrs/10.0.0.21:27019,10.0.0.22:27019,10.0.0.23:27019 --port 27017 --bind_ip localhost,10.0.0.40"}},{"title":"Shards hinzufügen und die Collection sharden","detail":"Verbinde dich mit mongos und füge jedes Shard-Replica-Set mit sh.addShard() hinzu. Aktiviere Sharding für die Datenbank und verteile die Collection mit sh.shardCollection() nach einem Shard Key. Die Key-Wahl ist entscheidend: Er sollte hohe Kardinalität und eine gleichmäßige Verteilung haben. Überwache die Chunk-Verteilung mit sh.status().","engines":{"mongodb":"sh.addShard('shard1rs/10.0.0.31:27018')\nsh.addShard('shard2rs/10.0.0.32:27018')\nsh.enableSharding('appdb')\nsh.shardCollection('appdb.events', { customer_id: 'hashed' })\nsh.status()"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-redis-sentinel","title":"Hochverfügbarkeit mit Redis Sentinel + Cluster","icon":"⚡","symptom":"Ziel: eine Master-Replica-Umgebung mit automatischem Failover für Redis per Sentinel einrichten; bei Bedarf an horizontaler Skalierung in den Redis-Cluster-Modus wechseln.","steps":[{"title":"Master und Replicas konfigurieren","detail":"Betreibe einen Master und mindestens zwei Replicas. Replicas folgen dem Master mit REPLICAOF ('replicaof <host> <port>' kann auch in redis.conf stehen). Aktiviere AOF oder RDB für Persistenz; Authentifizierung mit requirepass/masterauth wird empfohlen.","engines":{"redis":"# at runtime on the replica nodes:\nREPLICAOF 10.0.0.10 6379\n# to make it persistent, redis.conf:\n# replicaof 10.0.0.10 6379\n# masterauth secret\nINFO replication"}},{"title":"Sentinel mit monitor und quorum konfigurieren","detail":"Definiere in der sentinel.conf jedes Sentinel-Knotens den zu überwachenden Master und das Quorum. quorum ist die Mindestanzahl an Sentinels, die nötig ist, um den Master als 'down' zu betrachten. In Produktion werden 3 Sentinels auf 3 separaten Servern empfohlen (gegen Split-Brain).","engines":{"redis":"# sentinel.conf:\nsentinel monitor mymaster 10.0.0.10 6379 2\nsentinel down-after-milliseconds mymaster 5000\nsentinel failover-timeout mymaster 60000\nsentinel parallel-syncs mymaster 1"}},{"title":"Sentinel-Prozesse starten","detail":"Starte Sentinel auf jedem Knoten. Sentinels erkennen sich gegenseitig und die Replicas automatisch; sie schreiben die Konfiguration zur Laufzeit in die Datei. Daher muss sentinel.conf beschreibbar sein.","engines":{"redis":"redis-sentinel /etc/redis/sentinel.conf\n# or:\nredis-server /etc/redis/sentinel.conf --sentinel"}},{"title":"Status über Sentinel abfragen und Failover testen","detail":"Frage den aktuellen Master und die Replicas mit SENTINEL-Befehlen ab. Anwendungen sollten den aktuellen Master erfahren, indem sie Sentinel mit 'get-master-addr-by-name' fragen. Prüfe das automatische Umschalten mit einem geplanten Failover.","engines":{"redis":"SENTINEL master mymaster\nSENTINEL get-master-addr-by-name mymaster\nSENTINEL replicas mymaster\n# planned switchover:\nSENTINEL failover mymaster"}},{"title":"Alternative: Daten mit Redis Cluster partitionieren","detail":"Reichen Speicher/Durchsatz eines einzelnen Masters nicht aus, nutze Redis Cluster: Die Daten werden in 16384 Hash Slots aufgeteilt und auf mehrere Master verteilt; jeder Master hat eine eigene Replica. Die Anwendung muss einen clusterfähigen Client verwenden. Richte ihn mit redis-cli --cluster create ein.","engines":{"redis":"redis-cli --cluster create \\\n  10.0.0.11:6379 10.0.0.12:6379 10.0.0.13:6379 \\\n  10.0.0.21:6379 10.0.0.22:6379 10.0.0.23:6379 \\\n  --cluster-replicas 1"}},{"title":"Cluster-Zustand prüfen","detail":"Prüfe nach dem Erstellen des Clusters, dass alle Slots zugewiesen und die Knoten verbunden sind. cluster_state sollte 'ok' sein, und alle 16384 Slots sollten als zugewiesen erscheinen. Überwache die Verteilung von Knoten und Slots mit CLUSTER-Befehlen.","engines":{"redis":"redis-cli --cluster check 10.0.0.11:6379\nredis-cli -c -h 10.0.0.11 CLUSTER INFO\nredis-cli -c -h 10.0.0.11 CLUSTER NODES"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-elasticsearch-ha","title":"Ausfallsicherheit für Elasticsearch: Replica + Snapshot + CCR","icon":"🔎","symptom":"Ziel: Ausfallsicherheit für Elasticsearch mit Replica Shards, dedizierten Master-/Data-Knoten, Snapshot-Backups (SLM) und Cross-Cluster Replication (CCR) aufbauen.","steps":[{"title":"Rollen trennen: dedizierte Master- und Data-Knoten","detail":"Für die Stabilität des Clusters werden die stimmberechtigten Master-eligible-Knoten (vorzugsweise 3, eine ungerade Mehrheit) von der Datenlast getrennt. Weise die Rollen mit node.roles in elasticsearch.yml zu und definiere Seed Hosts für die Discovery. 3 Master verhindern Split-Brain.","engines":{"elasticsearch":"# elasticsearch.yml on master nodes:\n# node.roles: [ master ]\n# on data nodes:\n# node.roles: [ data, ingest ]\n# discovery.seed_hosts: [ m1, m2, m3 ]\n# cluster.initial_master_nodes: [ m1, m2, m3 ]"}},{"title":"Replica-Anzahl des Index festlegen","detail":"number_of_replicas ist die Anzahl der Kopien, die für jeden Primary Shard gehalten werden; sie sollte mindestens 1 sein, damit die Daten beim Verlust eines Data-Knotens verfügbar bleiben. Sie lässt sich auf einem laufenden Index dynamisch ändern; die Replica-Anzahl muss kleiner als die Anzahl der Data-Knoten sein.","engines":{"elasticsearch":"PUT /orders/_settings\n{\n  \"index\": { \"number_of_replicas\": 1 }\n}\n# default via template (composable template; the old _template API is legacy):\n# PUT _index_template/order-tpl { \"index_patterns\": [\"order*\"], \"template\": { \"settings\": { \"number_of_replicas\": 1 } } }"}},{"title":"Snapshot-Repository definieren","detail":"Backups werden in ein Repository geschrieben: ein gemeinsames Dateisystem (fs) oder die Plugins für S3/GCS/Azure. Damit sich die Daten auch nach dem Löschen des Clusters wiederherstellen lassen, muss das Repository auf dauerhaftem Speicher außerhalb des Clusters liegen.","engines":{"elasticsearch":"PUT /_snapshot/backup_repo\n{\n  \"type\": \"fs\",\n  \"settings\": { \"location\": \"/mnt/es_backups\", \"compress\": true }\n}"}},{"title":"Automatische Snapshots mit SLM planen","detail":"Snapshot Lifecycle Management (SLM) erstellt regelmäßig Snapshots und wendet eine Aufbewahrungsrichtlinie an. Ein Cron-Ausdruck legt die Häufigkeit fest, außerdem, welche Indizes einbezogen und wie viele Snapshots aufbewahrt werden.","engines":{"elasticsearch":"PUT /_slm/policy/daily_backup\n{\n  \"schedule\": \"0 30 1 * * ?\",\n  \"name\": \"<daily-{now/d}>\",\n  \"repository\": \"backup_repo\",\n  \"config\": { \"indices\": [\"*\"] },\n  \"retention\": { \"expire_after\": \"30d\", \"min_count\": 5, \"max_count\": 50 }\n}"}},{"title":"Snapshot und Wiederherstellung prüfen","detail":"Löse die SLM-Policy manuell aus, um zu prüfen, ob sie funktioniert; der Snapshot-Status sollte SUCCESS sein. Führe regelmäßig Wiederherstellungstests durch, denn ein ungetestetes Backup ist kein Backup.","engines":{"elasticsearch":"POST /_slm/policy/daily_backup/_execute\nGET /_snapshot/backup_repo/_all\n# restore:\nPOST /_snapshot/backup_repo/<snapshot_name>/_restore"}},{"title":"Entferntes DR mit Cross-Cluster Replication (CCR) einrichten","detail":"Für regionsübergreifendes Disaster Recovery folgt ein zweiter Cluster (Follower) den Indizes im entfernten Primary-Cluster (Leader). Definiere zuerst den Remote-Cluster und verbinde dann den Follower-Index mit dem Leader; Änderungen auf dem Leader fließen automatisch nach. CCR ist ein Platinum-Feature (kommerziell).","engines":{"elasticsearch":"# define the remote cluster on the follower cluster:\nPUT /_cluster/settings\n{ \"persistent\": { \"cluster\": { \"remote\": { \"primary\": { \"seeds\": [\"leader1:9300\"] } } } } }\n# create the follower index:\nPUT /orders-follower/_ccr/follow\n{ \"remote_cluster\": \"primary\", \"leader_index\": \"orders\" }"}}],"related":["replication-status","list-replicas","last-backup-status"]}]},"playbooks/security.json":{"schemaVersion":1,"playbooks":[{"id":"harden-sql","title":"Security Hardening: SQL-Datenbanken (Oracle, PostgreSQL, MySQL, SQL Server)","icon":"🔒","symptom":"Ziel: relationale Datenbanken nach CIS-Art härten – Standardkonten, strenge Passwortrichtlinie, Least Privilege, Netzwerkbeschränkung, TLS, Auditing und Verschlüsselung (TDE).","steps":[{"title":"Standard- bzw. nicht benötigte Konten entfernen oder sperren, Beispielschemata löschen","detail":"Mit der Installation ausgelieferte Standardkonten und Beispielschemata sind eine bekannte Angriffsfläche. Sperre oder entferne die ungenutzten; benenne Top-Level-/Dienstkonten um.","engines":{"oracle":"-- Lock unused default accounts and expire their passwords:\nALTER USER SCOTT ACCOUNT LOCK PASSWORD EXPIRE;\nALTER USER HR ACCOUNT LOCK PASSWORD EXPIRE;\n-- Audit locked/open accounts:\nSELECT username, account_status FROM dba_users ORDER BY account_status;\n-- Remove sample schemas: $ORACLE_HOME/demo/schema/drop_sch.sql","postgres":"-- Keep the default superuser 'postgres' role; do NOT drop/rename it.\n-- Audit roles:\nSELECT rolname, rolsuper, rolcanlogin FROM pg_roles ORDER BY rolsuper DESC;\n-- Remove unneeded login roles:\nDROP ROLE IF EXISTS test_user;\n-- Drop sample/test databases: DROP DATABASE IF EXISTS test;","mysql":"# Interactive hardening wizard (removes anonymous users, the test DB, remote root):\nmysql_secure_installation\n-- Manual: remove anonymous and test accounts:\nDROP USER IF EXISTS ''@'localhost';\nDROP USER IF EXISTS ''@'%';\nDROP DATABASE IF EXISTS test;\nDELETE FROM mysql.db WHERE Db LIKE 'test%'; FLUSH PRIVILEGES;","mssql":"-- Disable and rename the 'sa' account:\nALTER LOGIN sa DISABLE;\nALTER LOGIN sa WITH NAME = [sa_disabled_acct];\n-- Restrict the guest user — only in USER databases\n-- (guest must stay enabled in master, tempdb and msdb; disabling it in tempdb/msdb causes problems):\nUSE appdb;\nREVOKE CONNECT FROM guest;\n-- Remove sample DBs: DROP DATABASE AdventureWorks;"}},{"title":"Strenge Passwortrichtlinie und Ablauf erzwingen","detail":"Schwache oder nie geänderte Passwörter sind der häufigste Einstiegspunkt. Erzwinge Limits für Anmeldeversuche, Komplexität und Passwortlaufzeit; speichere Passwörter mit einem starken Hash.","engines":{"oracle":"-- Attempt limit, lifetime and complexity via a profile:\nCREATE PROFILE app_profile LIMIT\n  FAILED_LOGIN_ATTEMPTS 5\n  PASSWORD_LIFE_TIME 90\n  PASSWORD_REUSE_MAX 10\n  PASSWORD_VERIFY_FUNCTION ora12c_verify_function;\nALTER USER app_user PROFILE app_profile;","postgres":"-- Strong hash algorithm (SCRAM):\nSHOW password_encryption;              -- should be 'scram-sha-256'\nALTER SYSTEM SET password_encryption = 'scram-sha-256';\n-- Load the passwordcheck extension for complexity:\n-- shared_preload_libraries = 'passwordcheck'\n-- Lifetime: ALTER ROLE app_user VALID UNTIL '2026-12-31';","mysql":"-- Install the validate_password component and set the policy:\nINSTALL COMPONENT 'file://component_validate_password';\nSET PERSIST validate_password.policy = 'STRONG';\nSET PERSIST validate_password.length = 14;\n-- Attempt limit and lifetime:\nALTER USER app_user PASSWORD EXPIRE INTERVAL 90 DAY\n  FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1;","mssql":"-- Enforce the Windows password policy and expiration on the login:\nALTER LOGIN app_user WITH CHECK_POLICY = ON, CHECK_EXPIRATION = ON;\n-- Audit policy/expiration status:\nSELECT name, is_policy_checked, is_expiration_checked\nFROM sys.sql_logins;"}},{"title":"Least Privilege: PUBLIC-/breite Rechte entziehen, rollenbasierte Rechte vergeben","detail":"Breite Rechte, die allen (PUBLIC) gewährt werden, erleichtern Lateral Movement. Entziehe sie, lege Rollen an und vergib nur das Nötige.","engines":{"oracle":"-- Revoke dangerous PUBLIC privileges:\nREVOKE EXECUTE ON UTL_HTTP FROM PUBLIC;\nREVOKE EXECUTE ON UTL_FILE FROM PUBLIC;\nREVOKE EXECUTE ON DBMS_SCHEDULER FROM PUBLIC;\n-- Role-based privileges:\nCREATE ROLE app_read;\nGRANT SELECT ON app.orders TO app_read;\nGRANT app_read TO app_user;","postgres":"-- Revoke the default PUBLIC privileges on the schema:\nREVOKE ALL ON DATABASE appdb FROM PUBLIC;\nREVOKE CREATE ON SCHEMA public FROM PUBLIC;\n-- Role-based, read-only access:\nCREATE ROLE app_read NOLOGIN;\nGRANT USAGE ON SCHEMA app TO app_read;\nGRANT SELECT ON ALL TABLES IN SCHEMA app TO app_read;\nGRANT app_read TO app_user;","mysql":"-- Audit and revoke broad global privileges:\nSHOW GRANTS FOR app_user;\nREVOKE ALL PRIVILEGES, GRANT OPTION FROM app_user;\n-- Role-based, object-level privileges (MySQL 8+):\nCREATE ROLE app_read;\nGRANT SELECT ON appdb.* TO app_read;\nGRANT app_read TO app_user;\nSET DEFAULT ROLE app_read TO app_user;","mssql":"-- Revoke excessive privileges granted to the public role:\nREVOKE SELECT ON SCHEMA::dbo FROM public;\n-- Role-based privileges:\nCREATE ROLE app_read;\nGRANT SELECT ON SCHEMA::app TO app_read;\nALTER ROLE app_read ADD MEMBER app_user;\n-- At server level: limit fixed server roles (sysadmin)."}},{"title":"Netzwerk/Zugriff beschränken – nur bekannte Clients dürfen sich verbinden","detail":"Vermeide, dass die Datenbank offen für alle lauscht. Beschränke die Listen-Adresse, wende host-/IP-basierte Zugriffsregeln an und begrenze den Port per Firewall.","engines":{"oracle":"# listener.ora / sqlnet.ora — valid node checking:\nTCP.VALIDNODE_CHECKING = YES\nTCP.INVITED_NODES = (10.0.0.5, 10.0.0.6)\nTCP.EXCLUDED_NODES = (0.0.0.0/0)\n# After the change: lsnrctl reload","postgres":"# postgresql.conf — listen only on specific interfaces:\nlisten_addresses = '10.0.0.10,localhost'\n# pg_hba.conf — host/IP + strong authentication:\nhostssl appdb app_user 10.0.0.0/24 scram-sha-256\nhost    all      all       0.0.0.0/0      reject\n# Reload: SELECT pg_reload_conf();","mysql":"# my.cnf — bind only to a specific interface:\n[mysqld]\nbind-address = 10.0.0.10\n-- Host-based user (only from the application server):\nCREATE USER 'app_user'@'10.0.0.5'\n  IDENTIFIED BY '****' REQUIRE SSL;\n-- Never give remote root the '%' host.","mssql":"# Limit the TCP port with the firewall (application subnet only):\nNew-NetFirewallRule -DisplayName 'SQL-1433' -Direction Inbound `\n  -Protocol TCP -LocalPort 1433 -RemoteAddress 10.0.0.0/24 -Action Allow\n-- Audit listening addresses:\nSELECT * FROM sys.dm_tcp_listener_states;\n-- Disable the SQL Browser service if not used."}},{"title":"TLS/verschlüsselte Verbindungen erzwingen","detail":"Netzwerkverkehr ist passivem Mitschneiden ausgesetzt. Konfiguriere ein Serverzertifikat und verlange von Clients verschlüsselte Verbindungen.","engines":{"oracle":"# sqlnet.ora — TCPS/TLS (server certificate in the wallet):\nWALLET_LOCATION = (SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/etc/oracle/wallet)))\nSSL_CLIENT_AUTHENTICATION = FALSE\nSSL_VERSION = 1.2\n# Listen only on TCPS (port 2484) in listener.ora. Note: SQLNET.ENCRYPTION_SERVER is Native Network Encryption, not TLS.","postgres":"# postgresql.conf:\nssl = on\nssl_cert_file = 'server.crt'\nssl_key_file  = 'server.key'\nssl_min_protocol_version = 'TLSv1.2'\n# Use 'hostssl' lines in pg_hba.conf; reject unencrypted connections over plain 'host'.","mysql":"# my.cnf:\n[mysqld]\nrequire_secure_transport = ON\nssl_cert = /etc/mysql/server-cert.pem\nssl_key  = /etc/mysql/server-key.pem\nssl_ca   = /etc/mysql/ca.pem\n-- At user level: ALTER USER app_user REQUIRE SSL;","mssql":"-- Force connection encryption (SQL Server Configuration Manager):\n-- Protocols for MSSQLSERVER > Properties > Force Encryption = Yes\n-- Assign a certificate; then restart the service.\n-- Check the status:\nSELECT session_id, encrypt_option FROM sys.dm_exec_connections;"}},{"title":"Auditing aktivieren – sichtbar machen, wer was getan hat","detail":"Ein Audit Trail ist unerlässlich, um privilegierte Zugriffe und verdächtige Aktivitäten zu überwachen. Protokolliere Anmeldungen, Rechteänderungen und Zugriffe auf sensible Objekte.","engines":{"oracle":"-- Create and enable a policy with Unified Auditing:\nCREATE AUDIT POLICY sec_pol\n  ACTIONS LOGON, LOGOFF, ALTER USER, GRANT, CREATE USER, DROP USER;\nAUDIT POLICY sec_pol;\n-- View the records:\nSELECT * FROM unified_audit_trail ORDER BY event_timestamp DESC;","postgres":"# With the pgaudit extension:\n# postgresql.conf:\nshared_preload_libraries = 'pgaudit'\npgaudit.log = 'ddl, role, write'\n-- Create the extension:\nCREATE EXTENSION pgaudit;\n-- log_connections = on and log_disconnections = on should also be enabled.","mysql":"-- Load the Enterprise/Percona audit_log plugin:\nINSTALL PLUGIN audit_log SONAME 'audit_log.so';\nSET PERSIST audit_log_policy = 'ALL';\n-- MariaDB: server_audit plugin\n-- INSTALL SONAME 'server_audit'; SET GLOBAL server_audit_logging=ON;","mssql":"-- Create and start a Server Audit:\nCREATE SERVER AUDIT sec_audit\n  TO FILE (FILEPATH = 'C:\\SQLAudit\\');\nALTER SERVER AUDIT sec_audit WITH (STATE = ON);\nCREATE SERVER AUDIT SPECIFICATION sec_spec\n  FOR SERVER AUDIT sec_audit\n  ADD (FAILED_LOGIN_GROUP), ADD (DATABASE_ROLE_MEMBER_CHANGE_GROUP)\n  WITH (STATE = ON);"}},{"title":"Datenverschlüsselung anwenden (at rest / TDE)","detail":"Wird eine Platte, ein Backup oder eine Datei gestohlen, müssen die Daten unlesbar sein. Aktiviere Data-at-Rest-Verschlüsselung (TDE) auf Spalten-, Tablespace- oder Datenbankebene.","engines":{"oracle":"-- Transparent Data Encryption (keystore + tablespace):\nADMINISTER KEY MANAGEMENT CREATE KEYSTORE '/etc/oracle/wallet'\n  IDENTIFIED BY \"WalletPwd1\";\nADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY \"WalletPwd1\" WITH BACKUP;\nCREATE TABLESPACE sec_ts DATAFILE '/u01/sec01.dbf' SIZE 100M\n  ENCRYPTION USING 'AES256' DEFAULT STORAGE(ENCRYPT);","postgres":"-- There is no built-in block-level TDE; two common approaches:\n-- 1) Column-level pgcrypto:\nCREATE EXTENSION pgcrypto;\nINSERT INTO t(ssn) VALUES (pgp_sym_encrypt('123-45', 'my_key'));\nSELECT pgp_sym_decrypt(ssn, 'my_key') FROM t;\n-- 2) File system/block encryption (LUKS) or an EDB/Cybertec TDE build.","mysql":"# InnoDB tablespace encryption — keyring COMPONENT (8.0.24+; the keyring_file plugin was removed in 8.4)\n# 1) Global manifest in the mysqld binary directory: mysqld.my\n{ \"components\": \"file://component_keyring_file\" }\n# 2) In plugin_dir: component_keyring_file.cnf\n{ \"path\": \"/var/lib/mysql-keyring/component_keyring_file\", \"read_only\": false }\n# 3) Restart the server; verify:\n-- SELECT * FROM performance_schema.keyring_component_status;\n-- Encrypt the table:\nALTER TABLE app.orders ENCRYPTION = 'Y';\n-- For new tables: SET PERSIST default_table_encryption = ON;\n# Before 8.0.24 (8.0 only): early-plugin-load = keyring_file.so + keyring_file_data\n# WARNING: back up the keyring file SEPARATELY from the data directory and securely;\n# if the keyring is lost, encrypted tables and their backups become permanently unreadable.","mssql":"-- Transparent Data Encryption (DEK + certificate) — the key and certificate must be in master:\nUSE master;\nCREATE MASTER KEY ENCRYPTION BY PASSWORD = '<StrongPassword1>';\nCREATE CERTIFICATE tde_cert WITH SUBJECT = 'TDE Cert';\n-- MANDATORY: back up the certificate with its private key immediately and store it off the server.\n-- Without this backup, if the server is lost the encrypted DB and its backups CANNOT be restored.\nBACKUP CERTIFICATE tde_cert TO FILE = 'D:\\secure\\tde_cert.cer'\n  WITH PRIVATE KEY (FILE = 'D:\\secure\\tde_cert.pvk',\n    ENCRYPTION BY PASSWORD = '<CertBackupPassword1>');\nUSE appdb;\nCREATE DATABASE ENCRYPTION KEY\n  WITH ALGORITHM = AES_256 ENCRYPTION BY SERVER CERTIFICATE tde_cert;\nALTER DATABASE appdb SET ENCRYPTION ON;\n-- Progress: SELECT DB_NAME(database_id), encryption_state, percent_complete FROM sys.dm_database_encryption_keys;"}},{"title":"Patches aktuell halten und nicht benötigte Features deaktivieren","detail":"Bekannte Schwachstellen sind das am häufigsten ausgenutzte Risiko. Spiele Sicherheitspatches regelmäßig ein; deaktiviere ungenutzte Protokolle, Plugins und Features (Angriffsfläche verkleinern).","engines":{"oracle":"-- Check the installed version and patches:\nSELECT version_full FROM v$instance;\n-- Apply the quarterly Critical Patch Update (CPU): OPatch/opatchauto\n-- Remove unused components (e.g. XML DB, APEX) and\n-- disable the external procedure (extproc) listener.","postgres":"# Check the version and minor patch level:\nSELECT version();\n# Upgrade to the current minor release with the package manager (e.g. latest 16.x).\n# Remove unused extensions: DROP EXTENSION IF EXISTS ...;\n# postgresql.conf: do not load unused language/PL extensions.","mysql":"-- Check the version: SELECT VERSION();\n-- Upgrade to the LTS/security release (8.0.x/8.4 LTS).\n-- Disable local file reading:\nSET PERSIST local_infile = OFF;\n-- Remove unused plugins: UNINSTALL PLUGIN ...;","mssql":"-- Check the product version/patch level:\nSELECT SERVERPROPERTY('ProductVersion'), SERVERPROPERTY('ProductLevel');\n-- Apply the latest Cumulative Update (CU).\n-- Disable unused features (with sp_configure):\nEXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;\nEXEC sp_configure 'Ole Automation Procedures', 0; RECONFIGURE;"}}],"related":["list-users","create-user-grant","enable-audit","change-password"]},{"id":"harden-nosql","title":"Security Hardening: MongoDB / Redis / Elasticsearch","icon":"🛡️","symptom":"Ziel: NoSQL-/Datenspeicher nach CIS-Art härten – Authentifizierung, Netzwerkbeschränkung, Sperren gefährlicher Befehle, TLS, Least-Privilege-Rollen und Auditing.","steps":[{"title":"Authentifizierung AKTIVIEREN – nicht authentifizierten Zugriff unterbinden","detail":"Diese Produkte werden oft mit standardmäßig DEAKTIVIERTER Authentifizierung ausgeliefert und kompromittiert, wenn sie offen im Internet stehen. Aktiviere die Authentifizierung und lege einen Administrator an.","engines":{"mongodb":"// mongod.conf:\nsecurity:\n  authorization: enabled\n// Create an administrator user (first time, via the localhost exception):\nuse admin\ndb.createUser({ user: \"admin\", pwd: passwordPrompt(),\n  roles: [ { role: \"userAdminAnyDatabase\", db: \"admin\" } ] })\n// Then connect with: mongosh -u admin --authenticationDatabase admin","redis":"# redis.conf — ACL (Redis 6+): disable the default user, define named users:\nuser default off\nuser admin on >AdminPassword789! ~* &* +@all\nuser app on >AppPassword456! ~app:* +@read +@write\n# Note: requirepass is a legacy compatibility setting that only sets the 'default' user's password;\n# do not confuse it with ACL users. You can also keep users in an aclfile (persisted with ACL SAVE).","elasticsearch":"# 8.x: security (authentication + TLS) is configured automatically on first start; elasticsearch.yml:\nxpack.security.enabled: true\n# Set/reset the password of the 'elastic' user:\nbin/elasticsearch-reset-password -u elastic\n# (elasticsearch-setup-passwords is deprecated since 8.0)\n# Create separate users with dedicated roles for Kibana/clients instead of 'elastic'."}},{"title":"Netzwerk beschränken – nur benötigte Interfaces und Ports öffnen","detail":"Vermeide, dass der Dienst auf 0.0.0.0 für alle lauscht. Binde die Adresse an ein internes Interface/Loopback und aktiviere den Protected Mode.","engines":{"mongodb":"# mongod.conf — internal interface + loopback only:\nnet:\n  bindIp: 127.0.0.1,10.0.0.10\n  port: 27017\n# Never use bindIpAll: true; limit the port with a firewall.","redis":"# redis.conf:\nbind 127.0.0.1 10.0.0.10\nprotected-mode yes\nport 6379\n# protected-mode blocks external access when no password is set;\n# still limit 6379 with a firewall.","elasticsearch":"# elasticsearch.yml — pin the listening interface:\nnetwork.host: 10.0.0.10\nhttp.port: 9200\ntransport.port: 9300\n# Open only the required ports in the firewall;\n# 9300 (transport) must never be exposed externally."}},{"title":"Gefährliche Befehle einschränken","detail":"Ein einziger Befehl kann alle Daten löschen. Benenne destruktive/administrative Befehle um oder entferne sie, oder blockiere Wildcard-Ziele.","engines":{"mongodb":"// Use role-based control instead of a separate command block:\n// Grant destructive privileges (dropDatabase, dropCollection) only to\n// dbAdmin/clusterAdmin roles; do not grant them to the application user.\ndb.grantRolesToUser(\"app\", [ { role: \"readWrite\", db: \"appdb\" } ])\n// Do NOT grant dbAdmin/root to the application user.","redis":"# For Redis 6+ the preferred way is ACLs (do not give the application user dangerous commands):\n# ACL SETUSER app ... -@dangerous\n# Legacy method — disable/rename commands in redis.conf:\nrename-command FLUSHALL \"\"\nrename-command FLUSHDB \"\"\nrename-command KEYS \"\"\nrename-command DEBUG \"\"\n# Caution: renaming CONFIG can break Sentinel, CONFIG REWRITE and management tools.","elasticsearch":"# elasticsearch.yml — block wildcard/all-index deletion:\naction.destructive_requires_name: true\n# This rejects DELETE /* or DELETE /_all;\n# bulk deletion is not possible without naming the index explicitly."}},{"title":"TLS/verschlüsselte Verbindungen aktivieren","detail":"Zugangsdaten und Daten dürfen nicht unverschlüsselt über ein offenes Netzwerk laufen. Aktiviere TLS für Client- und knotenübergreifenden Verkehr.","engines":{"mongodb":"# mongod.conf — client TLS:\nnet:\n  tls:\n    mode: requireTLS\n    certificateKeyFile: /etc/ssl/mongodb.pem\n    CAFile: /etc/ssl/ca.pem\n# Client: mongosh --tls --tlsCAFile ca.pem","redis":"# redis.conf — enable TLS, close the plain port:\nport 0\ntls-port 6379\ntls-cert-file /etc/redis/redis.crt\ntls-key-file  /etc/redis/redis.key\ntls-ca-cert-file /etc/redis/ca.crt","elasticsearch":"# elasticsearch.yml — HTTP and transport TLS:\nxpack.security.http.ssl.enabled: true\nxpack.security.http.ssl.keystore.path: http.p12\nxpack.security.transport.ssl.enabled: true\nxpack.security.transport.ssl.verification_mode: certificate\nxpack.security.transport.ssl.keystore.path: transport.p12"}},{"title":"Least-Privilege-Rollen vergeben – breite Rollen vermeiden","detail":"Gib jedem Benutzer nur den Zugriff, den seine Aufgabe erfordert. Verwende statt breiter/administrativer Rollen feingranulare Rollen, die auf bestimmte Datenbanken, Key-Muster oder Indizes begrenzt sind.","engines":{"mongodb":"// Instead of broad readWrite, grant only on the required DB;\n// define a custom role if needed:\ndb.createRole({ role: \"ordersRW\",\n  privileges: [ { resource: { db: \"appdb\", collection: \"orders\" },\n    actions: [ \"find\", \"insert\", \"update\" ] } ], roles: [] })\ndb.grantRolesToUser(\"app\", [ { role: \"ordersRW\", db: \"appdb\" } ])","redis":"# Limit by key pattern and command category with ACLs:\nACL SETUSER app on >AppPassword456! ~app:* +@read +@write -@dangerous\n# Only keys with the 'app:' prefix, read/write; dangerous commands forbidden.\nACL LIST\nACL WHOAMI","elasticsearch":"# Define a fine-grained role (read-only on specific indices):\nPOST /_security/role/orders_read\n{ \"indices\": [ { \"names\": [ \"orders-*\" ],\n    \"privileges\": [ \"read\" ] } ] }\n# Assign it to the user:\nPOST /_security/user/app\n{ \"password\": \"...\", \"roles\": [ \"orders_read\" ] }"}},{"title":"Auditing/Logging aktivieren – Zugriffe und Änderungen überwachen","detail":"Unbefugte Zugriffsversuche und administrative Vorgänge müssen aufgezeichnet werden. Aktiviere das Audit Log und leite es an eine zentrale Stelle weiter.","engines":{"mongodb":"# Auditing (requires MongoDB Enterprise) — mongod.conf:\nauditLog:\n  destination: file\n  format: JSON\n  path: /var/log/mongodb/audit.json\n# Filter: capture events such as authenticate, createUser, dropDatabase.","redis":"# Redis has no full audit; use ACL LOG to see rejected\n# authentication/command attempts:\nACL LOG\nACL LOG RESET\n# Also: route the loglevel notice and logfile settings to a central collector.","elasticsearch":"# elasticsearch.yml — enable security auditing:\nxpack.security.audit.enabled: true\nxpack.security.audit.logfile.events.include:\n  [ authentication_failed, access_denied, connection_denied ]\n# Records are written to the <cluster>_audit.json file."}}],"related":["list-users","create-user-grant","enable-audit","change-password"]}]},"playbooks/cloud.json":{"schemaVersion":1,"playbooks":[{"id":"cloud-managed-db","title":"Cloud-verwaltete Datenbanken (RDS/Aurora/Cloud SQL/Atlas/Azure SQL)","icon":"☁️","symptom":"Ziel: die wesentlichen Unterschiede zwischen Cloud-verwalteten Datenbanken und selbst verwalteten Installationen kennen; verstehen, wie Backup, Parameter, Failover, Monitoring, Upgrades und Konnektivität in der Cloud funktionieren.","steps":[{"title":"Umdenken: Du greifst auf einen Dienst zu, nicht auf einen Server","detail":"Bei verwalteten Datenbanken hast du KEINEN Shell-Zugriff auf das OS, das Dateisystem oder den Datenbankprozess; du kannst dich nicht per SSH anmelden, Konfigurationsdateien nicht von Hand bearbeiten und nicht Superuser werden. Du erhältst nur die Verwaltungs-API/Konsole des Anbieters und eine normale SQL-Verbindung. AWS RDS/Aurora: Verwaltung auf Ressourcenebene (Instanz/Cluster), der Server ist verborgen. GCP Cloud SQL: nur Instanz-API, kein OS. MongoDB Atlas: vollständig verwalteter Cluster, kein Zugriff auf mongod. Azure SQL Database: PaaS, weder Server noch OS sind sichtbar (Managed Instance bietet etwas mehr Kontrolle). Die meisten Reflexe aus selbst verwalteten Umgebungen wie 'in die Datei schauen', 'den Prozess beenden' oder 'ein OS-Werkzeug ausführen' greifen also nicht."},{"title":"Backup: automatische Snapshots + Point-in-Time-Recovery (PITR)","detail":"Du planst keine Backups; der Anbieter erstellt sie automatisch. Die Wiederherstellung bedeutet meist 'auf einen bestimmten Zeitpunkt zurücksetzen' (PITR) oder das Erstellen einer neuen Instanz aus einem Snapshot; die bestehende Instanz wird nicht überschrieben. AWS RDS/Aurora: automatische Backups + Aufbewahrungsfenster; eine sekundengenaue PITR-Wiederherstellung öffnet sich als neue Instanz bzw. neuer Cluster; manuelle Snapshots sind ebenfalls möglich. GCP Cloud SQL: automatische Backups + PITR über Binary Logs; die Wiederherstellung erfolgt in eine neue Instanz. MongoDB Atlas: kontinuierliches Cloud-Backup + PITR (M10+); Wiederherstellung aus Snapshots. Azure SQL: eingebaute automatische Backups, 1-35 Tage Aufbewahrung, PITR auf jeden Zeitpunkt und Wiederherstellung gelöschter DBs. Allgemeine Regel: Prüfe die Backup-Konfiguration (Fenster, Aufbewahrungsdauer) und führe regelmäßig Wiederherstellungstests durch."},{"title":"Parameter: Parametergruppen / Flags statt Konfigurationsdatei","detail":"Statt postgresql.conf oder my.cnf von Hand zu bearbeiten, nutzt du die Parameter-Abstraktion des Anbieters; manche Parameter sind gesperrt, manche erfordern einen Neustart (statisch). AWS RDS/Aurora: Einstellung über eine DB Parameter Group (und eine Cluster Parameter Group); dynamische Parameter greifen sofort, statische beim Reboot. GCP Cloud SQL: 'Database Flags' werden über Konsole/gcloud gesetzt. MongoDB Atlas: begrenzte 'Additional Settings' + Oplog-Größe usw.; die meisten internen Parameter werden verwaltet. Azure SQL: Die meisten Einstellungen auf Serverebene werden verwaltet; das Verhalten ändert sich über SCOPED CONFIGURATION und die Wahl der Dienstebene. Prüfe vor dem Anwenden, ob der Parameter dynamisch oder statisch ist."},{"title":"Hochverfügbarkeit / Failover: verwaltetes Multi-AZ und Replicas","detail":"Du baust das Failover nicht selbst; du setzt ein Häkchen und überlässt es dem Anbieter. Standby und automatisches Failover werden verwaltet, und Verbindungen werden über einen stabilen Endpoint zur neuen Primary umgeleitet. AWS RDS: Multi-AZ (synchrone Standby, automatisches Failover); Aurora: mehrfach kopierter Storage + automatisches Failover, der Cluster-Endpoint zeigt auf den Writer. GCP Cloud SQL: HA-Konfiguration (regional, Standby in einer zweiten Zone) per Klick; Read Replicas werden separat hinzugefügt. MongoDB Atlas: Replica Set integriert, Multi-Region-/Multi-Cloud-Bereitstellung verfügbar, Failover automatisch. Azure SQL: eingebaute HA (lokale Replicas in Business Critical), eine zonenredundante Option und regionsübergreifende Active Geo-Replication / Failover Groups. Die Anwendung muss sich immer mit dem Endpoint verbinden, nie mit einer festen IP."},{"title":"Monitoring: Metrik- und Log-Dienst des Anbieters","detail":"Da du keine OS-Werkzeuge (top, iostat) ausführen kannst, überwachst du über die Metrikplattform des Anbieters; dort liegen Metriken zu CPU, Verbindungsanzahl, IOPS, Replikations-Lag und Storage. AWS: CloudWatch-Metriken + Enhanced Monitoring + Performance Insights (Analyse auf Abfrageebene). GCP Cloud SQL: Cloud Monitoring (früher Stackdriver) + Query Insights. MongoDB Atlas: integrierte Atlas-Metriken/-Diagramme + Performance Advisor + Echtzeit-Panel. Azure SQL: Azure Monitor + SQL Analytics + Query Performance Insight. Richte immer Schwellenwert-Alarme ein (Storage-Belegung, CPU, Verbindungslimit, Replica-Lag)."},{"title":"Versions-Upgrades und Wartung: über die Konsole, im Wartungsfenster","detail":"Statt Binaries von Hand zu aktualisieren, wählst du eine Zielversion; Minor-Patches laufen meist automatisch, während Major-Upgrades geplant werden und in der Regel Ausfallzeit bzw. ein HA-Failover mit sich bringen. AWS RDS/Aurora: Wahl der Engine-Version + Option für automatisches Minor-Upgrade + risikoarme Major-Upgrades mit Blue/Green Deployments. GCP Cloud SQL: Wartungsfenster + Major-Upgrade per Self-Service. MongoDB Atlas: Versions-Upgrade per Klick, rollierend mit nahezu null Ausfallzeit. Azure SQL: wird von der Plattform automatisch gepatcht (der Versionsbegriff ist weitgehend abstrahiert); das Verhalten wird über den Kompatibilitätsgrad gesteuert. Teste es immer zuerst auf einer Test-/Staging-Kopie und lege das Wartungsfenster außerhalb der Geschäftszeiten."},{"title":"Konnektivität und Sicherheit: Endpoint + IAM/TLS","detail":"Verbindungen laufen immer über den vom Anbieter bereitgestellten DNS-Endpoint und sind verschlüsselt (TLS); Netzwerkzugriff und Identität lassen sich an das Identitätssystem der Cloud koppeln. AWS RDS/Aurora: Instanz-/Cluster-Endpoint + Security Group für das Netzwerk + optionale IAM-Datenbankauthentifizierung + TLS (rds-ca-Zertifikat). GCP Cloud SQL: öffentliche IP + Authorized Networks oder private IP/VPC, Cloud SQL Auth Proxy, IAM-Datenbankauthentifizierung, TLS. MongoDB Atlas: SRV-Connection-String (mongodb+srv://), IP-Zugriffsliste + Private Endpoint/Peering, SCRAM oder IAM/X.509, TLS verpflichtend. Azure SQL: Server-FQDN-Endpoint + Firewall/Private Link + Authentifizierung über Microsoft Entra ID (früher Azure AD) + TLS verpflichtend. Regel: Verwende im Connection String den Endpoint statt einer festen IP, erzwinge TLS und verbinde dich nach Möglichkeit mit einer Cloud-IAM-Identität."}],"related":["last-backup-status","promote-failover","connect-cli"]}]},"playbooks/automation.json":{"schemaVersion":1,"playbooks":[{"id":"auto-ansible","title":"Datenbankautomatisierung mit Ansible","icon":"🤖","symptom":"Ziel: Installation, Konfiguration, Benutzer- und Backup-Aufgaben der Datenbank mit Ansible wiederholbar und idempotent machen (das erneute Ausführen desselben Playbooks beschädigt das System nicht).","steps":[{"title":"Inventory und Gruppen definieren","detail":"Gruppiere die Server im Inventory nach Datenbankrolle; lege Primary-, Replica- und Backup-Server in getrennte Gruppen. Verwende ein statisches INI-/YAML-Inventory oder für die Cloud ein dynamisches Inventory (aws_ec2, gcp_compute). Lege Gruppenvariablen unter group_vars/ und hostspezifische unter host_vars/ ab.","engines":{"postgres":"[pg_primary]\ndb1.example.local\n\n[pg_replica]\ndb2.example.local\n\n[postgres:children]\npg_primary\npg_replica"}},{"title":"Die passende Collection installieren (community.postgresql / community.mysql / community.mongodb)","detail":"Ansible Core liefert Datenbankmodule inzwischen in separaten Collections aus; installiere die Collection für deine Engine über requirements.yml. Verwende community.postgresql (postgresql_db, postgresql_user, postgresql_privs) für PostgreSQL, community.mysql (mysql_db, mysql_user) für MySQL/MariaDB und community.mongodb für MongoDB. Auf dem Zielhost muss der passende Python-Client (psycopg2, PyMySQL, pymongo) installiert sein.","engines":{"postgres":"# requirements.yml\ncollections:\n  - name: community.postgresql\n\n# install:\nansible-galaxy collection install -r requirements.yml","mysql":"collections:\n  - name: community.mysql","mongodb":"collections:\n  - name: community.mongodb"}},{"title":"Secrets mit Ansible Vault verschlüsseln","detail":"Lass Datenbankpasswörter, Replikations-Secrets und Superuser-Zugangsdaten nicht im Klartext liegen; halte sie mit ansible-vault in einer verschlüsselten Variablendatei. Entschlüssele zur Laufzeit mit --ask-vault-pass oder --vault-password-file. Secrets lassen sich so sicher in Git speichern, und das Playbook bleibt idempotent.","engines":{"postgres":"ansible-vault create group_vars/postgres/vault.yml\n# contents:\nvault_pg_admin_password: \"S3cret!\"\n\n# run:\nansible-playbook site.yml --ask-vault-pass"}},{"title":"Rollenstruktur aufbauen (roles/postgres, roles/mysql)","detail":"Lege pro Engine eine Rolle an und teile die Logik in die Unterordner tasks/, defaults/, templates/ und handlers/ auf. Diese Struktur verbessert die Wiederverwendbarkeit: defaults/main.yml für Standardwerte, templates/ für Jinja2-Konfigurationsvorlagen, handlers/ für die Logik 'nur neu starten, wenn sich etwas geändert hat'. Das Gerüst der Rolle erzeugst du mit ansible-galaxy init roles/postgres.","engines":{"postgres":"ansible-galaxy init roles/postgres\n# roles/postgres/\n#   defaults/main.yml\n#   tasks/main.yml\n#   templates/postgresql.conf.j2\n#   handlers/main.yml"}},{"title":"Installations- und Konfigurations-Tasks idempotent schreiben","detail":"Installiere das Paket, initialisiere das Datenverzeichnis und erzeuge postgresql.conf/my.cnf aus einer Vorlage; starte den Dienst über einen Handler neu, wenn sich die Konfiguration ändert. Module sind idempotent: Ein bereits installiertes Paket wird nicht erneut installiert, und der Handler wird nicht ausgelöst, wenn sich die Datei nicht geändert hat. Erzeuge die Konfigurationsvorlage mit dem Modul template und verknüpfe sie per 'notify' mit dem Handler.","engines":{"postgres":"- name: postgresql.conf template\n  ansible.builtin.template:\n    src: postgresql.conf.j2\n    dest: /etc/postgresql/16/main/postgresql.conf\n  notify: restart postgresql\n\n# handlers/main.yml\n- name: restart postgresql\n  ansible.builtin.service:\n    name: postgresql\n    state: restarted","mysql":"- name: my.cnf template\n  ansible.builtin.template:\n    src: my.cnf.j2\n    dest: /etc/mysql/my.cnf\n  notify: restart mysql"}},{"title":"Datenbank- und Benutzer-Tasks definieren","detail":"Lege Datenbank, Benutzer und Rechte mit Modulen an; das Passwort stammt aus einer Vault-Variable. Die Module von community.postgresql sind idempotent: Existiert der Benutzer bzw. die DB, werden sie nicht neu angelegt, sondern nur die Differenz angewendet. Vergib Rechte separat mit postgresql_privs; in MySQL wird das Feld priv von mysql_user verwendet.","engines":{"postgres":"- name: application database\n  community.postgresql.postgresql_db:\n    name: appdb\n    state: present\n\n- name: application user\n  community.postgresql.postgresql_user:\n    name: app_user\n    password: \"{{ vault_app_password }}\"\n    db: appdb\n    state: present","mysql":"- name: application user\n  community.mysql.mysql_user:\n    name: app_user\n    password: \"{{ vault_app_password }}\"\n    priv: 'appdb.*:ALL'\n    state: present"}},{"title":"Backup-Task und Zeitplan hinzufügen","detail":"Verteile das Backup-Skript aus einer Vorlage und plane es mit ansible.builtin.cron; so wird auch der Backup-Job als Code verwaltet. Mache Backup-Zielverzeichnis, Aufbewahrungsdauer und Zeitplan zu Variablen. Das Modul cron ist idempotent und erkennt den Eintrag am 'name', sodass erneutes Ausführen ihn nicht dupliziert.","engines":{"postgres":"- name: nightly backup (cron)\n  ansible.builtin.cron:\n    name: \"pg dump nightly\"\n    minute: \"0\"\n    hour: \"2\"\n    job: \"pg_dump -Fc appdb > /backup/appdb_$(date +\\\\%F).dump\""}}],"related":["schedule-job","check-version","logical-dump"]},{"id":"auto-terraform","title":"Datenbank-Provisioning mit Terraform (IaC)","icon":"🏗️","symptom":"Ziel: Datenbankinfrastruktur (Instanz, Parametergruppe, Netzwerk) mit Terraform als Code definieren (Infrastructure as Code); den Ablauf aus plan/apply, Remote State und Drift-Management einrichten.","steps":[{"title":"Provider wählen und konfigurieren (aws / google / azurerm)","detail":"Definiere den Provider für deine Cloud und fixiere seine Version. Lege im Block required_providers den Provider hashicorp/aws für AWS, hashicorp/google für GCP und hashicorp/azurerm für Azure fest. Das Fixieren der Version sorgt für einheitliches Verhalten im Team.","engines":{"postgres":"terraform {\n  required_providers {\n    aws = {\n      source  = \"hashicorp/aws\"\n      version = \"~> 5.0\"\n    }\n  }\n}\n\nprovider \"aws\" {\n  region = \"eu-central-1\"\n}"}},{"title":"Remote State und Locking einrichten","detail":"Halte die State-Datei nicht lokal; nutze für Teamarbeit und Sicherheit ein Remote Backend. Üblich sind das S3-Backend + eine DynamoDB-Lock-Tabelle auf AWS, das gcs-Backend auf GCP und das azurerm-Backend (Storage Account) auf Azure. Remote State verhindert per Locking gleichzeitige Applies und macht den State zur einzigen Quelle der Wahrheit. Da Passwörter im Klartext im State stehen können, halte das Backend verschlüsselt und zugriffsbeschränkt.","engines":{"postgres":"terraform {\n  backend \"s3\" {\n    bucket         = \"company-tfstate\"\n    key            = \"db/terraform.tfstate\"\n    region         = \"eu-central-1\"\n    dynamodb_table = \"tf-lock\"\n    encrypt        = true\n  }\n}"}},{"title":"Datenbankressource definieren","detail":"Schreibe die Hauptressource für deine Engine und Cloud: aws_db_instance auf AWS (bzw. aws_rds_cluster für Aurora), google_sql_database_instance auf GCP, azurerm_mssql_database auf Azure (für SQL Server unter einem azurerm_mssql_server). Gib hier Engine, Version, Klasse, Storage und Backup-Fenster an.","engines":{"postgres":"resource \"aws_db_instance\" \"pg\" {\n  identifier        = \"app-db\"\n  engine            = \"postgres\"\n  engine_version    = \"16\"\n  instance_class    = \"db.t3.medium\"\n  allocated_storage = 50\n  db_name           = \"appdb\"\n  username          = \"pgadmin\"\n  password          = var.db_password\n  backup_retention_period = 7\n}","mssql":"resource \"azurerm_mssql_database\" \"db\" {\n  name      = \"appdb\"\n  server_id = azurerm_mssql_server.srv.id\n  sku_name  = \"S1\"\n}"}},{"title":"Parametergruppe als Code schreiben","detail":"Definiere Datenbankeinstellungen (max_connections, work_mem, Zeichensatz) mit einer Parameterressource statt manuell in der Konsole. Verwende aws_db_parameter_group auf AWS, settings.database_flags innerhalb von google_sql_database_instance auf GCP und die Ressource für die Serverkonfiguration auf Azure. Die Einstellungen sind dann versioniert und reproduzierbar.","engines":{"postgres":"resource \"aws_db_parameter_group\" \"pg\" {\n  name   = \"app-pg16\"\n  family = \"postgres16\"\n\n  parameter {\n    name  = \"max_connections\"\n    value = \"200\"\n  }\n}\n# inside aws_db_instance:\n# parameter_group_name = aws_db_parameter_group.pg.name"}},{"title":"Secrets über Variablen und einen Secrets Manager übergeben","detail":"Schreibe das Passwort nicht fest in eine .tf-Datei; definiere es als sensible Variable (variable ... sensitive = true) und liefere den Wert über eine TF_VAR_-Umgebungsvariable, einen Secrets Manager (AWS Secrets Manager, Vault) oder ein CI-Secret. Beachte: Der Wert wird in die State-Datei geschrieben, daher muss der Remote State verschlüsselt sein. Am saubersten ist es, das Passwort gar nicht an Terraform zu übergeben und es stattdessen aus einer Secrets-Manager-Ressource zu lesen.","engines":{"postgres":"variable \"db_password\" {\n  type      = string\n  sensitive = true\n}\n\n# CI/shell:\nexport TF_VAR_db_password=\"$(vault kv get -field=pw secret/db)\""}},{"title":"Den Ablauf aus plan und apply ausführen","detail":"Bereite zuerst mit terraform init Provider und Backend vor, sieh dir mit terraform plan an, was sich ändert, gib frei und wende mit terraform apply an. Wende nichts an, ohne die Plan-Ausgabe geprüft zu haben; der sichere Ablauf in CI ist, den 'plan' als PR-Kommentar zu posten und nach menschlicher Freigabe anzuwenden. Erkenne destruktive Änderungen (z. B. Neuanlage der Instanz) am Marker -/+ in der Plan-Ausgabe.","engines":{"postgres":"terraform init\nterraform plan -out=plan.tfplan\nterraform apply plan.tfplan"}},{"title":"Drift erkennen und beheben","detail":"Nimmt jemand eine manuelle Änderung in der Konsole vor, weichen reale Infrastruktur und State voneinander ab (Drift). Unterschiede, die in terraform plan auftauchen, 'obwohl sich nichts hätte ändern sollen', decken Drift auf; die Differenz wird mit terraform refresh/plan ermittelt. Übernimm die Änderung entweder in den Code oder bringe die Infrastruktur per apply auf den Stand des Codes zurück. Verbiete manuelle Änderungen grundsätzlich; der Code sollte die einzige Quelle der Wahrheit sein.","engines":{"postgres":"# drift check (when no changes are expected):\nterraform plan\n# expect 'No changes'; any diff means drift\n# bring an existing resource into state with import:\nterraform import aws_db_instance.pg app-db"}}],"related":["check-version","apply-patch","schedule-job"]},{"id":"auto-k8s-operator","title":"Datenbanken auf Kubernetes (Operator)","icon":"☸️","symptom":"Ziel: die Datenbank mit einem Operator auf Kubernetes betreiben; statt eines reinen StatefulSet die vom Operator bereitgestellten Funktionen für automatisches Failover, persistenten Storage und Backup nutzen.","steps":[{"title":"Warum ein Operator: Ein StatefulSet allein reicht nicht","detail":"Ein einfaches StatefulSet liefert Pods mit stabilen Identitäten und PVCs, aber nicht die Logik für Failover, Replikationsaufbau, Backup und Upgrades. Ein Operator verpackt dieses datenbankspezifische Betriebswissen als Controller + Custom Resource (CRD). Du schreibst eine CR namens 'Cluster', und der Operator verwaltet das StatefulSet, den Service, das Secret und die Backup-Jobs dahinter. In Produktion wird der Betrieb von Datenbanken mit einem Operator in der Regel bevorzugt.","engines":{"postgres":"# operators for PostgreSQL:\n#  - CloudNativePG (CNPG)\n#  - Zalando postgres-operator\n#  - Crunchy PGO"}},{"title":"Operator wählen und installieren","detail":"Wähle einen Operator für deine Engine: CloudNativePG oder Zalando postgres-operator für PostgreSQL; für MySQL und MongoDB sind die Percona-Operatoren (Percona Operator for MySQL / for MongoDB) verbreitet. Installiere den Operator mit Helm oder einem Bundle-Manifest; dieser Schritt fügt dem Cluster die CRDs und den Controller-Pod hinzu.","engines":{"postgres":"# CloudNativePG installation\nkubectl apply --server-side -f \\\n  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.24.0.yaml","mysql":"# Percona Operator for MySQL (Helm)\nhelm install my-op percona/pxc-operator","mongodb":"# Percona Operator for MongoDB (Helm)\nhelm install psmdb-op percona/psmdb-operator"}},{"title":"Storage planen (StorageClass / PVC)","detail":"Der Zustand der Datenbank liegt in PVCs; gib in der CR die zu verwendende StorageClass an. Wähle eine StorageClass mit latenzarmem Block Storage, idealerweise lokalähnlicher SSD; Netzwerkdateisysteme (NFS) werden für die meisten Datenbanken nicht empfohlen. Lege PVC-Größe und Erweiterbarkeit (allowVolumeExpansion) von Anfang an richtig fest, denn Verkleinern ist schwierig.","engines":{"postgres":"spec:\n  storage:\n    storageClass: fast-ssd\n    size: 50Gi"}},{"title":"Cluster-CR anwenden (Replicas und automatisches Failover)","detail":"Gib in der CR die Anzahl der Instanzen/Replicas an; der Operator richtet synchrone/asynchrone Replikation ein und führt bei Ausfall der Primary ein automatisches Failover durch, indem er eine Replica promotet. Die Anwendung sollte sich mit dem vom Operator erstellten Service '-rw' (read-write) verbinden; dieser Service zeigt immer auf die aktuelle Primary. Das Verbindungsziel ändert sich beim Failover daher nicht.","engines":{"postgres":"apiVersion: postgresql.cnpg.io/v1\nkind: Cluster\nmetadata:\n  name: app-db\nspec:\n  instances: 3\n  storage:\n    storageClass: fast-ssd\n    size: 50Gi\n# the application connects to: app-db-rw"}},{"title":"Backups über die CR nach S3 schreiben","detail":"Operatoren verwalten auch Backups deklarativ: Du definierst in der CR das primäre Backup-Ziel (Object Storage/S3) und die WAL-Archivierung. In CloudNativePG werden die S3-Angaben mit spec.backup.barmanObjectStore gemacht (seit 1.26 veraltet; für neue Deployments das Barman Cloud Plugin und seine Ressource ObjectStore verwenden); kontinuierliche WAL-Archivierung + Base Backups ermöglichen Point-in-Time-Recovery. Referenziere Zugangsdaten aus einem Kubernetes Secret.","engines":{"postgres":"spec:\n  backup:\n    barmanObjectStore:\n      destinationPath: s3://company-backup/app-db\n      s3Credentials:\n        accessKeyId:\n          name: s3-creds\n          key: ACCESS_KEY_ID\n        secretAccessKey:\n          name: s3-creds\n          key: SECRET_ACCESS_KEY"}},{"title":"Geplante Backup- und Restore-CRs","detail":"Erstelle eine Backup-CR für ein einmaliges Backup und eine ScheduledBackup-CR für geplante; erstelle regelmäßige Base Backups mit einem Cron-Ausdruck. Auch Wiederherstellungen sind deklarativ: Mit 'bootstrap.recovery' bootstrappst du einen neuen Cluster aus einem vorhandenen Object-Store-Backup. Führe regelmäßig Wiederherstellungsübungen durch; Backups zu erstellen reicht nicht, teste die Wiederherstellung.","engines":{"postgres":"apiVersion: postgresql.cnpg.io/v1\nkind: ScheduledBackup\nmetadata:\n  name: nightly-backup\nspec:\n  schedule: \"0 0 2 * * *\"\n  cluster:\n    name: app-db"}}],"related":["setup-cdc-replication","logical-dump","restore-dump"]},{"id":"auto-schema-migration","title":"CI/CD für Schemamigrationen (Flyway/Liquibase)","icon":"🧬","symptom":"Ziel: Änderungen am Datenbankschema mit versionierten Migrationsdateien verwalten; sie in CI/CD mit Flyway oder Liquibase automatisch, aber kontrolliert anwenden.","steps":[{"title":"Versionierte Migrationsdateien anlegen (Vxxx__name.sql)","detail":"Schreibe jede Schemaänderung in eine benannte Datei mit steigender Versionsnummer; die Flyway-Konvention lautet V<version>__<description>.sql (z. B. V1__initial_schemas.sql, V2__orders_table.sql). Diese Dateien liegen in Git und werden in Versionsreihenfolge angewendet. Angewendete Migrationen werden in einer Tracking-Tabelle protokolliert (in Flyway flyway_schema_history), sodass jede Migration genau einmal läuft.","engines":{"postgres":"-- V2__orders_table.sql\nCREATE TABLE orders (\n  id       bigserial PRIMARY KEY,\n  amount    numeric(12,2) NOT NULL,\n  created_at   timestamptz DEFAULT now()\n);"}},{"title":"Unveränderlichkeit mit Checksummen wahren","detail":"Flyway/Liquibase speichern die Checksumme jeder angewendeten Migration. Bearbeitest du später eine bereits angewendete Datei, stimmt die Checksumme nicht mehr, und das Werkzeug bricht mit einem Fehler ab. Regel: Bearbeite NIE eine angewendete Migration; führe die Korrektur mit einer neuen Versionsdatei durch. So ist garantiert, dass alle Umgebungen (dev/staging/prod) dieselbe Historie haben.","engines":{"postgres":"# repair on checksum mismatch (use with care):\nflyway repair\n# the right way: add the change in a new Vx file"}},{"title":"Baseline für eine bestehende Datenbank setzen","detail":"Verwende baseline, wenn du das Migrationswerkzeug an eine bereits befüllte Datenbank ohne Historie anbindest; es behandelt den aktuellen Zustand als 'Startversion' und versucht nicht, vorhandene Objekte neu anzulegen. Migrationen nach baselineVersion werden angewendet. In einer von Grund auf neu aufgebauten Umgebung ist keine Baseline nötig; die Migrationen laufen ab V1.","engines":{"postgres":"flyway baseline -baselineVersion=1 -baselineDescription=\"existing schema\""}},{"title":"In der CI-Pipeline automatisch migrieren","detail":"Führe das Migrationswerkzeug in einem CI-Schritt aus, um Änderungen automatisch auf die Test- und Staging-Datenbanken anzuwenden; der Befehl migrate führt nur neue (nicht angewendete) Dateien aus und ist idempotent. Wird die Migration zuerst auf eine temporäre/kurzlebige Datenbank angewendet und validiert, fällt fehlerhaftes SQL auf, bevor es Prod erreicht. Das Schema muss VOR dem Deployment des Anwendungscodes migriert werden (mit abwärtskompatiblen Änderungen).","engines":{"postgres":"# CI step (Flyway)\nflyway -url=jdbc:postgresql://staging/appdb \\\n  -user=ci -password=$PW migrate","mysql":"flyway -url=jdbc:mysql://staging/appdb \\\n  -user=ci -password=$PW migrate"}},{"title":"Manuelles Freigabe-Gate für Prod hinzufügen","detail":"Test/Staging laufen automatisch, aber das migrate auf Prod sollte eine manuelle Freigabe erfordern; füge in CI/CD einen Schritt 'Approval Gate' (manuelle Freigabe) hinzu. Zeige vor der Freigabe mit 'flyway info' oder 'liquibase status', welche Migrationen laufen werden. Erstelle vor dem Anwenden auf Prod immer ein frisches Backup, damit du bei Problemen zurückrollen kannst.","engines":{"postgres":"# what will run before approval?\nflyway info\n# liquibase equivalent:\nliquibase status --verbose"}},{"title":"Umkehrbarkeit und Rollback-Strategie festlegen","detail":"Flyway Community hat kein automatisches Undo; verfolge das Prinzip Forward-only-Migration: Statt einen Fehler rückgängig zu machen, schreibe eine neue Migration, die ihn korrigiert. Liquibase-Changesets können einen Rollback-Block definieren und mit 'liquibase rollback' zurückgerollt werden. Die robusteste Strategie: Halte Änderungen klein und abwärtskompatibel, erstelle vor riskanten Operationen (Löschen von Spalten) ein Backup und bereite einen schriftlichen Rollback-Plan vor.","engines":{"postgres":"-- Liquibase rollback example (inside a changeset)\n--changeset dev:2\nCREATE TABLE orders (...);\n--rollback DROP TABLE orders;"}}],"related":["apply-patch","logical-dump","check-version"]},{"id":"auto-backup","title":"Automatisierte Backups (pgBackRest/Barman/Xtrabackup/cron)","icon":"💾","symptom":"Ziel: Datenbank-Backups automatisieren; mit geplanter Voll- + inkrementeller Sicherung + WAL-/Binlog-Archivierung, Aufbewahrung, Verifizierung und einer Offsite-Kopie eine zuverlässige Wiederherstellungskette aufbauen.","steps":[{"title":"Das richtige Backup-Werkzeug wählen","detail":"Wähle ein Werkzeug für deine Engine: pgBackRest oder Barman für PostgreSQL (physisches Backup + WAL-Archiv + PITR); Percona XtraBackup (physisches Online-Backup) für MySQL/MariaDB oder mysqldump/mydumper (logisch) für kleine Datenmengen. Physische Werkzeuge liefern bei großen Datenbanken schnelle, konsistente Backups + Point-in-Time-Recovery; logische Dumps sind portabel, aber bei großen Datenmengen langsam.","engines":{"postgres":"# pgBackRest or Barman","mysql":"# physical: xtrabackup\n# logical: mysqldump / mydumper"}},{"title":"Kette aus Voll- + inkrementellem Backup + kontinuierlichem Archiv aufbauen","detail":"Richte Point-in-Time-Recovery mit wöchentlichen/täglichen Vollbackups, inkrementellen/differenziellen Backups dazwischen und kontinuierlicher Archivierung von WAL (PostgreSQL) bzw. Binlog (MySQL) ein. pgBackRest verwaltet die Backup-Typen full/diff/incremental und das WAL-Archiv; in PostgreSQL richtest du archive_command auf pgBackRest aus. Inkrementelle Backups + Archiv ermöglichen die Rückkehr zu jedem beliebigen Zeitpunkt.","engines":{"postgres":"# postgresql.conf\narchive_mode = on\narchive_command = 'pgbackrest --stanza=appdb archive-push %p'\n\n# full backup\npgbackrest --stanza=appdb --type=full backup\n# incremental\npgbackrest --stanza=appdb --type=incr backup","mysql":"# full\nxtrabackup --backup --target-dir=/backup/full\n# incremental\nxtrabackup --backup --target-dir=/backup/incr1 \\\n  --incremental-basedir=/backup/full"}},{"title":"Backups mit cron/Timer planen","detail":"Führe Backup-Befehle regelmäßig mit cron oder einem systemd-Timer aus: zum Beispiel ein Vollbackup in der Nacht auf Sonntag und inkrementelle an den übrigen Nächten. Plane außerhalb der Geschäftszeiten und stelle sicher, dass sich full- und incr-Jobs nicht überschneiden. Schreibe die Ausgabe in Logs und löse bei Fehlern einen Alarm aus.","engines":{"postgres":"# crontab\n0 1 * * 0 pgbackrest --stanza=appdb --type=full backup\n0 1 * * 1-6 pgbackrest --stanza=appdb --type=incr backup"}},{"title":"Aufbewahrungsrichtlinie festlegen","detail":"Sammle Backups nicht endlos an; lege fest, wie viele Vollbackups und wie viele Tage Archiv aufbewahrt werden, und lass das Werkzeug alte automatisch verfallen. In pgBackRest wird das mit repo1-retention-full und den Einstellungen zur Archivaufbewahrung gesteuert, in Barman mit retention_policy. Die Aufbewahrung muss zu deinen Wiederherstellungszielen passen (wie viele Tage du zurückgehen musst).","engines":{"postgres":"# pgbackrest.conf\n[global]\nrepo1-retention-full=4\nrepo1-retention-archive=8","mysql":"# purge old backups with cron (example)\nfind /backup -type d -mtime +14 -exec rm -rf {} +"}},{"title":"Backups offsite / nach S3 kopieren","detail":"Backups dürfen nicht auf derselben Maschine bzw. in derselben Region wie die Datenbank bleiben; kopiere sie an einen anderen Ort oder in Object Storage (S3, GCS). pgBackRest kann direkt in S3-kompatiblen Storage schreiben; andernfalls synchronisiere mit rsync/rclone zu einem Offsite-Ziel. Im Katastrophenfall (Verlust der lokalen Platte/Region) rettet dich nur die Offsite-Kopie.","engines":{"postgres":"# pgbackrest.conf S3 repository\n[global]\nrepo1-type=s3\nrepo1-s3-bucket=company-backup\nrepo1-s3-region=eu-central-1\nrepo1-s3-endpoint=s3.eu-central-1.amazonaws.com"}},{"title":"Backup verifizieren","detail":"Dass ein Backup existiert, heißt nicht, dass es sich wiederherstellen lässt; prüfe die Integrität regelmäßig. In pgBackRest zeigen der Befehl 'verify' und die Konsistenzprüfungen des Archivs, in XtraBackup ein erfolgreicher prepare-Schritt (--prepare), dass das Backup intakt ist. Führe die Verifizierung nach jedem Backup automatisch aus und löse bei Fehlern einen Alarm aus.","engines":{"postgres":"pgbackrest --stanza=appdb verify","mysql":"# make the backup consistent (verification)\nxtrabackup --prepare --target-dir=/backup/full"}},{"title":"Regelmäßige Wiederherstellungsübungen durchführen","detail":"Der wichtigste Schritt: Stelle das Backup regelmäßig TATSÄCHLICH auf einem separaten Server wieder her und prüfe, dass die Datenbank öffnet und die Daten konsistent sind. Mit dieser Übung misst du deine Wiederherstellungszeit (RTO) und findest Lücken im Verfahren außerhalb eines Incidents. Ein ungetestetes Backup ist kein Backup.","engines":{"postgres":"# restore + PITR on a separate server\npgbackrest --stanza=appdb \\\n  --type=time --target=\"2026-07-01 03:00:00\" restore","mysql":"# copy the prepared backup back to the data directory\nxtrabackup --copy-back --target-dir=/backup/full"}}],"related":["logical-dump","restore-dump","schedule-job"]},{"id":"auto-gitops-bluegreen","title":"GitOps & Blue-Green / Deployment ohne Ausfallzeit","icon":"🟦","symptom":"Ziel: Schema- und Anwendungsänderungen ohne Ausfallzeit ausrollen; sich mit Expand-Contract-Migrationen, Blue-Green-Umgebungen, Feature Flags und automatischem Rollback dem Zero-Downtime-Ziel nähern.","steps":[{"title":"GitOps: Git zur einzigen Quelle der Wahrheit machen","detail":"Definiere Infrastruktur, Schemamigrationen und Anwendungsversion in Git; das Deployment wird durch einen Merge ins Repository ausgelöst (GitOps). Ein Reconciler (Argo CD/Flux oder CI/CD) wendet den Sollzustand auf die reale Umgebung an. Jede Änderung ist dann nachvollziehbar, wiederholbar und lässt sich bei Bedarf auf einen früheren Commit zurücksetzen.","engines":{"postgres":"# repo/\n#   migrations/  (Vxxx__*.sql)\n#   k8s/         (Deployment, Service)\n#   flags/       (feature flag config)"}},{"title":"Schema abwärtskompatibel ändern (Expand-Contract)","detail":"Für null Ausfallzeit ändere das Schema nicht in einem einzigen brechenden Schritt, sondern teile es in zwei Phasen. EXPAND: Füge die neue Struktur hinzu (neue Spalte/Tabelle), sodass alter und neuer Code gleichzeitig laufen können (nullable Spalte, mit Default, doppeltes Schreiben). Erst NACHDEM die Anwendung auf die neue Struktur umgestellt ist, CONTRACT: Entferne die nicht mehr genutzte alte Spalte/Tabelle in einer separaten Migration. Alte und neue Anwendungsversion laufen dann während des Deployments problemlos parallel.","engines":{"postgres":"-- EXPAND (V5): backward-compatible addition\nALTER TABLE customer ADD COLUMN email text;\n\n-- for a while the code writes/reads both columns\n\n-- CONTRACT (V9): drop the old column\nALTER TABLE customer DROP COLUMN mail_old;"}},{"title":"Riskantes Verhalten hinter ein Feature Flag legen","detail":"Führe den neuen Codepfad (etwa das Lesen der neuen Spalte) bedingt hinter einem Feature Flag aus; schalte das Flag ein, wenn das Schema bereit ist, und bei Problemen wieder aus, ohne das Deployment anzufassen. Ein Flag trennt das Ausrollen des Codes (Deploy) von der Aktivierung des Features (Release). Die Expand-Phase wird ausgeliefert, die Verhaltensänderung aber kontrolliert aktiviert und lässt sich schnell zurücknehmen.","engines":{"postgres":"-- application layer (pseudocode)\n-- if flag('new_email_field'):\n--     SELECT email FROM customer ...\n-- else:\n--     SELECT mail_old FROM customer ..."}},{"title":"Blue-Green-Umgebung einrichten","detail":"Halte zwei identische Umgebungen vor: 'blue' (live) und 'green' (neue Version). Rolle die neue Anwendungsversion auf green aus und validiere sie, bevor sie echten Verkehr erhält; ist sie bereit, schalte Router/Verkehr auf green um. Wichtig: Die Datenbank ist meist GEMEINSAM genutzt, daher muss das Schema mit beiden Versionen kompatibel sein (das stellt Expand-Contract sicher). Auch wenn sich beim Umschalten das Verbindungsziel ändert, bedeutet Schemakompatibilität keine Ausfallzeit.","engines":{"postgres":"# traffic is switched to green (new version)\n# shared DB: schema must be compatible with both blue and green"}},{"title":"Deployment per Monitoring validieren (Canary/Beobachtung)","detail":"Überwache vor dem Umschalten des gesamten Verkehrs Metriken wie Fehlerrate, Latenz, Fehler-/Lock-Anzahl der Datenbank und Replikations-Lag; sende optional einen kleinen Prozentsatz des Verkehrs (Canary) an green und beobachte. Sind die Gesundheitsindikatoren gut, erhöhe den Verkehr schrittweise; sind sie schlecht, fahre nicht fort. Das Monitoring liefert die Grundlage für die Entscheidung zum automatischen Rollback.","engines":{"postgres":"-- live health queries\nSELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Lock';\nSELECT * FROM pg_stat_replication;"}},{"title":"Automatisches Rollback vorbereiten","detail":"Überschreiten die Metriken den Schwellenwert, muss das Deployment automatisch zurückgerollt werden: Bei Blue-Green wird der Verkehr schnell zurück auf blue (die alte Version) geschaltet. Das Rollback der Anwendung erfolgt sofort, da blue noch läuft. Auf Schemaseite ist ein Rollback meist NICHT NÖTIG: Dank Expand-Contract ist das neue Schema auch mit dem alten Code kompatibel, daher genügt das Zurückrollen der Anwendung. Deshalb sollten Schemaänderungen unabhängig vom Anwendungs-Deployment bleiben und stets abwärtskompatibel sein.","engines":{"postgres":"# traffic instantly returns to blue (application rollback)\n# schema is not rolled back: expand-contract is compatible with old code\n# if truly needed: write a new corrective migration"}}],"related":["setup-cdc-replication","apply-patch","check-version"]}]},"install/index.json":{"schemaVersion":1,"categories":[{"id":"install","title":"Installation"},{"id":"ha","title":"Hochverfügbarkeit & Replikation"},{"id":"backup","title":"Backup & Recovery"},{"id":"ops","title":"Täglicher Betrieb & Störungen"},{"id":"pooling","title":"Verbindungen & Lastverteilung"},{"id":"patch","title":"Patches & Minor-Updates"},{"id":"upgrade","title":"Major-Upgrade & Migration"},{"id":"monitoring","title":"Monitoring & Verwaltung"},{"id":"security","title":"Sicherheit & Härtung"}],"guides":[{"id":"pg-install","file":"install/guides/pg-install.json","group":"PostgreSQL","engineId":"postgres","category":"install","title":"PostgreSQL-Installation auf einem einzelnen Server (produktionsreif)","summary":"Produktionsreife PostgreSQL-Installation auf einem einzelnen Server aus dem PGDG-Repository: separates Datenverzeichnis, Data Checksums, aus RAM/CPU berechnete Einstellungen, sicherer Zugriff (peer + scram-sha-256), Logg","level":"Einsteiger","duration":"30–45 Min.","topology":"1 Server","tags":["postgresql","postgres","installation","install","pgdg","initdb","tuning","rhel","ubuntu"],"order":1,"steps":23,"scenarios":[]},{"id":"pg-streaming","file":"install/guides/pg-streaming.json","group":"PostgreSQL","engineId":"postgres","category":"ha","title":"PostgreSQL Streaming Replication (Primary + Standby)","summary":"Fügt einem installierten Primary per pg_basebackup einen oder mehrere Hot Standbys hinzu (Replikationsbenutzer, pg_hba, Replication Slot, asynchroner oder synchroner ANY/FIRST-Modus, Lag-Überwachung) und erzeugt die Betr","level":"Mittel","duration":"45–90 Min.","topology":"1 Primary + 1..N Standbys","tags":["postgresql","Streaming Replication","replikation","standby","Hot Standby","pg_basebackup","Replication Slot","synchronous","synchrone Replikation","failover","switchover","pg_promote","pg_rewind","ha","kaskadierende Replikation","verzögerter Standby","recovery_min_apply_delay","Slot bereinigen","pg_wal voll","synchronous_standby_names","Standby neu aufbauen"],"order":2,"steps":24,"scenarios":[{"k":"mode","v":"install","l":"Einrichtung: Standbys zum Primary hinzufügen","m":"Standby hinzufügen / neu aufbauen Streaming Replication einrichten Standby hinzufügen Hot Standby pg_basebackup Read Replica Lese-Replica physische Replikation physischer Standby standby.signal primary_conninfo Einem installierten Primary werden per pg_basebackup ein oder mehrere Hot Standbys hinzugefügt (Slot, synchron/asynchron, Überwachung). Der Primary bleibt in Betrieb; die Erstkopie belastet"},{"k":"mode","v":"switchover","l":"Geplanter Switchover (Rollentausch, kein Datenverlust)","m":"Rollenwechsel switchover geplanter Switchover Rollenwechsel geplanter wechsel Primary-Wartung Standby zum Primary machen pg_promote pg_ctl promote Geplante Wartung: Der Primary wird sauber heruntergefahren, der erste Standby wird promotet, der alte Primary kommt als Standby zurück. Kein Datenverlust; die Schreibunterbrechung dauert meist unter einer Minute."},{"k":"mode","v":"failover","l":"Manueller Failover (Primary ausgefallen, Standby promoten)","m":"Rollenwechsel could not connect to the primary server Connection refused primary down Primary abgestürzt Primary nicht erreichbar failover manueller Failover Standby promoten pg_promote split-brain could not receive data from WAL stream Primary-Server verloren Der Primary ist ausgefallen oder nicht erreichbar: Der alte Primary wird isoliert (Fencing), der aktuellste Standby wird von Hand promotet."},{"k":"mode","v":"rejoin","l":"Alten Primary mit pg_rewind als Standby wieder einbinden","m":"Rollenwechsel requested timeline is not a child of this server's history target server must be shut down cleanly could not find previous WAL record pg_rewind diverged Timeline alten Primary wieder einbinden rejoin old primary Nach einem Failover wird der alte Primary mit pg_rewind auf den neuen Primary abgeglichen und als Standby wieder eingebunden; deutlich schneller als eine Vollkopie."},{"k":"mode","v":"cascade","l":"Neuen Standby hinzufügen, der von einem Standby versorgt wird (Cascading)","m":"Standby hinzufügen / neu aufbauen cascading replication Cascade Standby verkettete Replikation Standby von Standby entferntes Rechenzentrum DR-Standby Der neue Standby bezieht WAL vom ersten Standby statt vom Primary: In einem entfernten Rechenzentrum oder bei vielen Standbys sinkt die Netz- und WAL-Sender-Last des Primary. Der Primary ist nicht betroffen."},{"k":"mode","v":"delayed","l":"Verzögerten Standby (Delayed Standby) einrichten","m":"Standby hinzufügen / neu aufbauen recovery_min_apply_delay verzögerter Standby zeitverzögerter Standby versehentlich gelöscht DROP TABLE rückgängig machen accidental delete pg_wal_replay_pause Ein Standby, der WAL erst Stunden später anwendet (`recovery_min_apply_delay`): um versehentlich gelöschte Daten zu retten, ohne aus dem Backup wiederherzustellen. Er ist kein Failover-Kandidat."},{"k":"mode","v":"rebuild","l":"Standby von Grund auf neu aufbauen","m":"Standby hinzufügen / neu aufbauen requested WAL segment has already been removed could not receive data from WAL stream database system identifier differs wal_status lost exceeds max_slot_wal_keep_size Standby neu aufbauen Standby beschädigt re-clone standby could not start WAL streaming wal_removed Standby zurückgefallen Der Standby ist zurückgefallen, sein WAL wurde gelöscht oder er ist beschädi"},{"k":"mode","v":"slotclean","l":"pg_wal wächst, Platte läuft voll (ungenutzte Slots bereinigen)","m":"Wartung und Troubleshooting No space left on device could not write to file \"pg_wal pg_wal voll pg_wal wächst WAL-Aufblähung Platte voll replication slot inactive max_slot_wal_keep_size pg_wal-Verzeichnis groß WAL disk full Die Ursache des WAL-Rückstaus wird ermittelt (inaktiver Slot, Archivierungsfehler, wal_keep_size); der ungenutzte Replication Slot wird gelöscht und WAL-Speicher zurückgewonnen"},{"k":"mode","v":"syncchg","l":"Synchronen Standby hinzufügen / entfernen (synchronous_standby_names)","m":"Wartung und Troubleshooting canceling wait for synchronous replication synchronous_standby_names COMMIT hängt commit hangs sync standby synchroner Standby sync_state potential synchronous_commit Schreibt die synchrone Liste gemäß dem Modus im Formular (ANY/FIRST, k) neu oder stellt auf asynchron zurück; wird per Reload übernommen, kein Restart nötig. Eine falsche Einstellung kann Commits hängen la"}]},{"id":"pg-patroni","file":"install/guides/pg-patroni.json","group":"PostgreSQL","engineId":"postgres","category":"ha","title":"Cluster mit automatischem Failover mit Patroni + etcd + HAProxy","summary":"Richtet einen PostgreSQL-Cluster mit automatischem Failover ein und betreibt ihn: Auf jedem Node verwaltet Patroni PostgreSQL, die Leader-Wahl läuft über ein etcd mit 3 (oder 5) Mitgliedern, Anwendungen verbinden sich mi","level":"Fortgeschritten","duration":"2–3 Stunden","topology":"3 PostgreSQL- + etcd-Nodes (gemeinsam oder getrennt) + HAProxy (+ VIP)","tags":["postgresql","patroni","etcd","haproxy","keepalived","vip","automatisches Failover","auto failover","switchover","ha","Hochverfügbarkeit","cluster","PostgreSQL-Cluster","patronictl","dcs","reinit","pg_rewind","edit-config","pause","resume","etcdctl member","quorum","failsafe","Node hinzufügen","Node entfernen","patroni upgrade","Cluster herunterfahren"],"order":3,"steps":40,"scenarios":[{"k":"mode","v":"install","l":"Installation: neuer Patroni-Cluster","m":"Installation Patroni-Installation patroni install etcd-Installation haproxy automatisches Failover automatic failover Hochverfügbarkeit Hochverfügbarkeit HA-Cluster Baut einen etcd- + Patroni- + HAProxy- (+ VIP-)Cluster von Grund auf. Auf den Servern darf kein PostgreSQL-Cluster existieren: initdb führt Patroni aus (pg-install wird nicht angewendet)."},{"k":"mode","v":"ops","l":"Betriebs-Runbook: Switchover, Failover-Test, Wartungsmodus, Parameteränderung","m":"Prüfung und Übung Failover-Test failover test Übung DR drill runbook Spielt die grundlegenden Vorgänge auf einem neu aufgebauten Cluster der Reihe nach durch: Status, Switchover, Failover-Test (der Leader wird gestoppt, kurze Schreibunterbrechung), Wartungsmodus, Parameteränderung. Führe das in einem Wartungsfenster aus."},{"k":"mode","v":"health","l":"Ist der Cluster gesund? (Health Check)","m":"Prüfung und Übung patronictl list Pending restart Maintenance mode: on start failed patroni health Cluster-Status replica lag Verzögerung Timeline abweichend Nur lesend: Rollen, Lag, Timeline, REST API, etcd-Zustand, Slots, ausstehender Restart und Wartungsmodus."},{"k":"mode","v":"switchover","l":"Geplanter Switchover (Leader auf einen anderen Node verlegen)","m":"Rollenwechsel candidate name does not match with sync_standby Switchover failed does not exist in cluster or is tagged as nofailover switchover Leader wechseln Rollenwechsel geplante Wartung Vor einer Wartung geht die Leader-Rolle kontrolliert auf eine Replica über; einige Sekunden Schreibunterbrechung, kein Datenverlust."},{"k":"mode","v":"rejoin","l":"Failover ist passiert, der alte Leader tritt nicht wieder bei (pg_rewind / reinit)","m":"Rollenwechsel requested timeline is not a child of this server's history could not find previous WAL record pg_rewind: error target server needs to use either data checksums or wal_log_hints start failed diverged alter Leader nach Failover pg_rewind: fatal could not find common ancestor timeline history mismatch Der ausgefallene alte Leader sollte nach dem Start als Replica beitreten; schlägt pg_r"},{"k":"mode","v":"reinit","l":"Eine Replica von Grund auf neu aufbauen (reinit)","m":"Mitgliederwartung requested WAL segment has already been removed could not receive data from WAL stream invalid record length invalid page patronictl reinit Replica beschädigt Replica im Rückstand rebuild replica replica start failed creating replica Die Replica ist beschädigt, weit zurück oder hat WAL verloren: Ihr Datenverzeichnis wird gelöscht und vom Leader (oder aus einem Backup) neu aufgebau"},{"k":"mode","v":"addmember","l":"Neuen Node zum Cluster hinzufügen","m":"Mitgliederwartung Node hinzufügen add member neue Replica scale out Cluster erweitern Füge den neuen Node am Ende der Liste im Formular hinzu: Die Software wird installiert, pg_hba und HAProxy werden auf den bestehenden Nodes aktualisiert, und der Node tritt als Replica bei."},{"k":"mode","v":"rmmember","l":"Node aus dem Cluster entfernen","m":"Mitgliederwartung Node entfernen remove member decommission Server stilllegen Replica entfernen Ein Node, der Replica ist, wird dauerhaft entfernt: Patroni wird gestoppt, DCS-Eintrag und Slot werden bereinigt, und er wird aus HAProxy entfernt."},{"k":"mode","v":"editcfg","l":"PostgreSQL- oder Patroni-Einstellung ändern (edit-config)","m":"Einstellungen und Wartung edit-config Pending restart postgresql.conf-Einstellung verloren ALTER SYSTEM Parameter ändern max_connections shared_buffers change parameter work_mem Parameter verloren Einstellung wird nicht übernommen Die Einstellung wird in den DCS geschrieben und auf alle Nodes angewendet; bei Einstellungen, die einen Restart erfordern, `restart --pending` (zuerst die Replicas)."},{"k":"mode","v":"pause","l":"Automatisches Failover für die Wartung anhalten (pause / resume)","m":"Einstellungen und Wartung Maintenance mode: on patronictl pause patronictl resume maintenance mode Wartungsmodus automatisches Failover abschalten Im Pause-Modus fasst Patroni PostgreSQL nicht an und führt kein Failover aus; für Netzwerk-/etcd-Wartung oder manuelle Eingriffe. Danach resume."},{"k":"mode","v":"upgrade","l":"Patroni-Software aktualisieren (rolling)","m":"Einstellungen und Wartung patroni upgrade Patroni-Update Patroni-Version dnf update patroni apt upgrade patroni patroni 3 to 4 Der Cluster wird pausiert, Patroni wird Node für Node aktualisiert und neu gestartet; PostgreSQL läuft weiter, es gibt kein Failover."},{"k":"mode","v":"stopstart","l":"Gesamten Cluster stoppen / starten (richtige Reihenfolge)","m":"Einstellungen und Wartung Cluster herunterfahren Cluster starten shutdown order vollständiges Herunterfahren geplantes Herunterfahren Rechenzentrumswartung Stromausfall Geplantes vollständiges Herunterfahren (Storage-, Rechenzentrumswartung): zuerst die Replicas, dann der Leader, zuletzt etcd; Hochfahren in umgekehrter Reihenfolge."},{"k":"mode","v":"etcdmember","l":"etcd-Mitglied austauschen oder hinzufügen (etcdctl member)","m":"etcd (DCS) etcdserver: unhealthy cluster member has already been bootstrapped Peer URLs already exists member count is unequal etcdctl member etcd member add etcd member remove etcd-Mitglied unhealthy Ein ausgefallenes etcd-Mitglied wird entfernt und ein neuer Server hinzugefügt (oder die Anzahl der Mitglieder erhöht); die etcd-Liste von Patroni wird aktualisiert."},{"k":"mode","v":"dcsloss","l":"etcd-Quorum verloren: Der Leader ist schreibgeschützt","m":"etcd (DCS) Failed to get list of machines DCS is not accessible demoting self etcdserver: request timed out etcdserver: no leader cannot execute INSERT in a read-only transaction quorum failsafe etcd quorum lost kein Leader no leader Ohne etcd-Quorum setzt Patroni den Leader auf schreibgeschützt, und es gibt auch kein Failover. etcd wird wiederhergestellt oder aus einem einzelnen Mitglied neu aufg"}]},{"id":"pg-pgbouncer","file":"install/guides/pg-pgbouncer.json","group":"PostgreSQL","engineId":"postgres","category":"pooling","title":"PgBouncer-Verbindungspool","summary":"Installation von PgBouncer aus dem PGDG-Repository: Pool-Modus transaction oder session, Authentifizierung mit scram-sha-256 über auth_query (eine SECURITY-DEFINER-Funktion) oder userlist.txt, Pool-Größen berechnet aus m","level":"Mittel","duration":"30–45 min","topology":"PgBouncer auf dem Datenbankserver oder auf einem eigenen Server; bei Patroni vor HAProxy/VIP","tags":["postgresql","pgbouncer","connection pool","verbindungspool","pooling","transaction pooling","auth_query","userlist","scram-sha-256","so_reuseport","max_client_conn","default_pool_size","patroni","haproxy","vip","failover"],"order":4,"steps":15,"scenarios":[]},{"id":"pg-pgbackrest","file":"install/guides/pg-pgbackrest.json","group":"PostgreSQL","engineId":"postgres","category":"backup","title":"Backup und PITR mit pgBackRest","summary":"Vollständige, differenzielle und inkrementelle Backups mit pgBackRest, kontinuierliche WAL-Archivierung und Point-in-Time-Recovery (PITR). Für einen Einzelserver, Streaming Replication (Primary + Standbys) oder einen Pat","level":"Mittel","duration":"45–90 Min.","topology":"1 Server, Primary + Standbys oder ein Patroni-Cluster; optional ein Backup-Server","tags":["postgresql","pgbackrest","Sicherung","backup","restore","pitr","wal","archive","s3","minio","Verschlüsselung","retention","Wiederherstellung","patroni","streaming","standby","cron","backup-standby","systemd timer","delta restore","db-include","target-action","restore point","xid","lsn","clone","Klonen","Katastrophe","disaster recovery","verify","expire","type=standby","Notfallübung"],"order":10,"steps":28,"scenarios":[{"k":"purpose","v":"kur","l":"pgBackRest-Installation + erstes Backup + Test-Restore","m":"Installation stanza-create archive_command archive-push pgbackrest einrichten pgbackrest install Backup einrichten backup setup Backup-Zeitplan full diff incr geplantes Backup ERROR: [087] archive_mode must be enabled WAL-Archivierung einrichten erstes Backup pgBackRest ist noch nicht eingerichtet: Software, Repository, WAL-Archivierung, erstes Backup, Zeitplanung und ein Test-Restore."},{"k":"purpose","v":"geri","l":"Katastrophenfall: In-Place-Restore (auf den letzten Stand oder einen Zeitpunkt)","m":"Restore (derselbe Server) recovery_target_time recovery_target_xid recovery_target_lsn pg_create_restore_point PITR point in time recovery auf Zeitpunkt zurücksetzen versehentlich gelöscht DROP TABLE DELETE without WHERE UPDATE without WHERE TRUNCATE restore point restore to time Daten wiederherstellen Setze die Datenbank an Ort und Stelle auf einen Zeitpunkt zurück, den Backup und Archiv abdecken"},{"k":"purpose","v":"delta","l":"Beschädigte oder gelöschte Dateien im Datenverzeichnis (mit Delta-Restore reparieren)","m":"Restore (derselbe Server) invalid page in block could not read block page verification failed checksum verification failed could not open file \"base/ could not access status of transaction missing chunk number No such file or directory could not read file beschädigter Block data corruption block corruption gelöschte Datendatei ERROR:  could not read block PANIC:  could not open file PANIC:  could "},{"k":"purpose","v":"dbinc","l":"Nur eine einzelne Datenbank wiederherstellen (--db-include)","m":"Restore (derselbe Server) db-include DROP DATABASE einzelne Datenbank single database restore Datenbank gelöscht Tabelle wiederherstellen table restore einzelne Tabelle wiederherstellen Der Cluster bleibt in Betrieb: Die einzelne Datenbank wird in eine temporäre Instanz (separates Verzeichnis und Port) wiederhergestellt, mit pg_dump exportiert und unter neuem Namen in den Live-Server geladen. Auch"},{"k":"purpose","v":"clone","l":"Backup auf einem anderen Server wiederherstellen (Klon / Test / Reporting)","m":"Neuer Server: Klon, Katastrophe, Standby repo1-cipher-pass clone Klonen Testumgebung Testumgebung aktualisieren refresh test staging Restore auf neuen Server Reporting-Server verschlüsseltes Repository Der Live-Server wird nicht angefasst. Das Backup wird auf einem neuen Server geöffnet; die WAL-Archivierung wird abgeschaltet, damit der Klon nicht in das Live-Repository schreibt. Bei einem verschl"},{"k":"purpose","v":"dr","l":"Server komplett verloren: Backup auf einem neuen Server öffnen und ihn ersetzen","m":"Neuer Server: Klon, Katastrophe, Standby PANIC: could not locate a valid checkpoint record Katastrophe disaster recovery DR Serververlust Plattenausfall server lost Datenverzeichnis verloren neuer Primary verschlüsseltes Repository could not open file \"global/pg_control\" Server startet nicht PostgreSQL startet nicht Platten- oder Serververlust: Der neue Server wird aus dem Backup geöffnet und wird"},{"k":"purpose","v":"standby","l":"Neuen Standby aus dem Backup aufbauen (--type=standby)","m":"Neuer Server: Klon, Katastrophe, Standby type=standby Standby aufbauen Replica hinzufügen Standby aus Backup standby rebuild statt pg_basebackup add standby Neuer Standby ohne Kopierlast auf dem Primary: Die Dateien kommen aus dem Backup, fehlendes WAL wird aus dem Archiv und per Streaming ergänzt. Der Primary bleibt in Betrieb."},{"k":"purpose","v":"verify","l":"Sind die Backups wirklich intakt? (info / check / verify und Restore-Übung)","m":"Prüfung und Wartung ERROR: [082] ERROR: [028] ERROR: [056] pgbackrest verify pgbackrest check pgbackrest info Backup-Prüfung backup verification restore test Notfallübung wann war das letzte Backup verify command ist das Backup intakt restore drill monatliche Prüfung Rein lesende Prüfungen und eine Restore-Übung in ein separates Verzeichnis, die den Live-Server nicht berührt. Zusätzlich wird ein m"},{"k":"purpose","v":"retention","l":"Aufbewahrungsdauer ändern und alte Backups bereinigen (expire)","m":"Prüfung und Wartung No space left on device WARN: option 'repo1-retention-full' is not set repo1-retention-full expire retention Aufbewahrung Repository voll Backup-Platte voll alte Backups löschen Repo voll Bucket voll Backup-Platte vollgelaufen Ändert die Einstellungen repo1-retention-full/diff, zeigt zuerst mit --dry-run, was gelöscht würde, und führt dann expire aus. Ein gelöschtes Backup läss"}]},{"id":"pg-pitr-basebackup","file":"install/guides/pg-pitr-basebackup.json","group":"PostgreSQL","engineId":"postgres","category":"backup","title":"Sicherung und PITR mit pg_basebackup + WAL-Archiv (ohne pgBackRest)","summary":"Physische Sicherung mit den Bordmitteln von PostgreSQL, ohne Zusatzsoftware: kontinuierliche WAL-Archivierung mit archive_command, nächtliches Base Backup mit pg_basebackup, Aufbewahrung mit pg_archivecleanup. Wiederhers","level":"Mittel","duration":"45–90 Min.","topology":"1 Server; bei Streaming oder Patroni ist das Archivverzeichnis auf allen Nodes gemeinsam (NFS)","tags":["postgresql","pg_basebackup","archive_command","restore_command","pitr","point in time recovery","recovery.signal","recovery_target_time","pg_archivecleanup","WAL-Archiv","Timeline","Base Backup","Sicherung","Wiederherstellung","pg_verifybackup","cron","Restore-Übung"],"order":12,"steps":14,"scenarios":[{"k":"op","v":"kur","l":"WAL-Archivierung und nächtliches Base Backup einrichten","m":"Einrichtung archive_mode archive_command archive_timeout pg_stat_archiver wal_level WAL-Archiv WAL archiving continuous archiving kontinuierliche Archivierung pg_basebackup cron Base Backup nächtliche Sicherung nightly backup WAL-Archivierung einrichten Ersteinrichtung: kontinuierliche WAL-Archivierung mit archive_command, ein pg_basebackup-Skript, Zeitplanung in /etc/cron.d, Aufbewahrung und WAL-"},{"k":"op","v":"pitr","l":"Auf einen bestimmten Punkt zurückgehen (PITR: Zeit, xid, LSN, Restore Point)","m":"Wiederherstellung recovery ended before configured recovery target was reached requested recovery stop point is before consistent recovery point PITR point in time recovery point in time recovery.signal restore_command recovery_target_time recovery_target_xid recovery_target_lsn recovery_target_name pg_create_restore_point DROP TABLE TRUNCATE versehentlich gelöscht accidental delete Wiederherstell"},{"k":"op","v":"drill","l":"Restore-Übung (in separatem Verzeichnis, ohne die Produktion anzufassen)","m":"Prüfung und Wartung backup successfully verified pg_verifybackup pg_amcheck restore test restore drill Restore-Übung Sicherung prüfen backup verify RTO lässt sich die Sicherung zurückspielen Um zu belegen, dass sich die Sicherung tatsächlich zurückspielen lässt (monatlich empfohlen): Das letzte Base Backup wird geprüft (pg_verifybackup), in ein separates Verzeichnis entpackt und mit dem Archiv an "},{"k":"op","v":"cleanup","l":"Alte Base Backups und WAL-Archiv bereinigen","m":"Prüfung und Wartung No space left on device could not write to file archive command failed pg_archivecleanup Archivplatte voll Archiv voll disk full alte Sicherungen löschen Aufbewahrung retention pg_wal wächst archive_status .ready Die Archiv- oder Sicherungsplatte läuft voll, oder die Aufbewahrung hat sich geändert: Alte Base Backups werden gelöscht, pg_archivecleanup entfernt WAL, das älter als"}]},{"id":"pg-logical-repl","file":"install/guides/pg-logical-repl.json","group":"PostgreSQL","engineId":"postgres","category":"ha","title":"PostgreSQL Logical Replication (Publication / Subscription)","summary":"Logical Replication auf Tabellenebene zwischen zwei PostgreSQL-Clustern: wal_level=logical, Prüfung der Replica Identity, Publication und Subscription, initiale Synchronisation, Monitoring, Behandlung von Konflikten (Con","level":"Fortgeschritten","duration":"45–90 Min. (+ Dauer der initialen Kopie)","topology":"2 Server: Publisher + Subscriber; der Publisher kann ein Einzelserver, eine Streaming-Replication-Umgebung oder ein Patroni-Cluster sein","tags":["postgresql","logical replication","logische replikation","publication","subscription","cdc","datenmigration","migration","upgrade","conflict","replica identity","sequence","patroni","failover slot","permanent slots","sync_replication_slots"],"order":15,"steps":25,"scenarios":[{"k":"purpose","v":"sync","l":"Kontinuierliche Replikation (Reporting, Integration, Teilkopie)","m":"logical replication publication subscription CREATE SUBSCRIPTION tabellenreplikation teilreplikation Ausgewählte Tabellen fließen kontinuierlich in einen anderen Cluster (Reporting-Server, Befüllung eines Data Warehouse, Integration zwischen Diensten). Der Subscriber bleibt beschreibbar."},{"k":"purpose","v":"migrate","l":"Migration: neuer Server / neue Major-Version + Cutover","m":"upgrade ohne downtime near zero downtime cutover servermigration logical upgrade Wechsel auf einen neuen Server oder eine neue Major-Version mit Sekunden an Downtime: initiale Kopie und laufender Abgleich, danach ein kurzer Schreibstopp und der Cutover."}]},{"id":"pg-dump-restore","file":"install/guides/pg-dump-restore.json","group":"PostgreSQL","engineId":"postgres","category":"backup","title":"PostgreSQL: logische Sicherung und Wiederherstellung (pg_dump / pg_restore)","summary":"Logische Sicherung und Wiederherstellung mit pg_dump, pg_dumpall und pg_restore auf Ebene von Tabelle, Schema, Datenbank oder ganzem Cluster; nur Struktur (DDL) oder nur Daten, Umzug auf einen anderen Server bzw. eine an","level":"Mittel","duration":"20–90 Min. (je nach Datenmenge)","topology":"1 Server, Streaming Replication (Primary + Standby) oder ein Patroni-Cluster; beim Umzug Quelle + Ziel, bei zeitgesteuerter Remote-Sicherung zusätzlich ein Backup-Server","tags":["postgresql","postgres","pg_dump","pg_restore","pg_dumpall","logische Sicherung","logical backup","Export","Import","Dump","restore","Wiederherstellung","Datenbankmigration","Migration","Upgrade","globals","systemd timer","cron","pgpass","Datensicherung","patroni","streaming replication","Replica","Standby","haproxy","hot standby","pg_wrapper","cron.d","flock"],"order":15,"steps":9,"scenarios":[{"k":"mode","v":"tables","l":"Einzelne Tabelle(n) exportieren / importieren","m":"Export / Import pg_dump -t Tabelle exportieren Tabelle importieren table export Sicherung einzelner Tabelle no matching tables were found Struktur und Daten der ausgewählten Tabellen (inklusive Indizes, Constraints, Trigger) werden gesichert und in eine andere Datenbank geladen."},{"k":"mode","v":"schema","l":"Ein Schema exportieren / importieren","m":"Export / Import pg_dump -n Schema exportieren schema export no matching schemas were found Alle Objekte des Schemas (Tabellen, Views, Funktionen, Sequenzen) werden gesichert."},{"k":"mode","v":"db","l":"Einzelne Datenbank sichern / wiederherstellen","m":"Export / Import pg_dump pg_restore Datenbanksicherung database backup database restore errors ignored on restore Eine ganze Datenbank (Struktur + Daten) wird in einen einzigen Dump geschrieben und in dieselbe oder eine andere Datenbank zurückgeladen. Rollen (globals) sind nicht enthalten."},{"k":"mode","v":"cluster","l":"Ganzer Cluster (Rollen + Tablespaces + jede Datenbank)","m":"Export / Import pg_dumpall pg_dumpall -g globals Sicherung ganzer Cluster Rollen sichern Globals mit pg_dumpall -g, ein eigener pg_dump je Datenbank; die Wiederherstellung wird auf einem temporären Test-Cluster geprobt."},{"k":"mode","v":"ddl","l":"Nur Schemastruktur (DDL)","m":"Struktur und Daten getrennt pg_dump -s --schema-only DDL-Export nur Struktur Definitionen von Tabellen, Indizes, Views und Funktionen ohne Daten; zur Prüfung, für die Versionsverwaltung oder zum Aufbau einer leeren Umgebung."},{"k":"mode","v":"data","l":"Nur Daten","m":"Struktur und Daten getrennt pg_dump -a --data-only nur Daten --disable-triggers circular foreign-key constraints Nur die Zeilen (COPY) werden übertragen, während die Struktur auf dem Ziel bereits vorhanden ist."},{"k":"mode","v":"migrate","l":"Auf anderen Server / andere Version umziehen","m":"Umzug und Recovery Serverumzug Migration dump restore upgrade aborting because of server version mismatch auf neue Version migrieren Auf dem Zielserver verbindet sich der pg_dump der neuen Version über das Netz mit der Quelle; die Übertragung läuft über eine Pipe oder eine Datei."},{"k":"mode","v":"partial","l":"Einzelne Tabelle / einzelnes Objekt aus einem Dump wiederherstellen","m":"Umzug und Recovery einzelne Tabelle wiederherstellen pg_restore -L pg_restore --list DROP TABLE gelöschte Tabelle gelöschte Zeilen versehentlich gelöscht table restore Aus einem vorhandenen Custom- oder Directory-Dump wird mit einer Listendatei (-L) nur eine Tabelle samt ihren Indizes, Constraints und Daten wiederhergestellt."},{"k":"mode","v":"sched","l":"Zeitgesteuerte Sicherung (systemd timer / cron)","m":"Zeitgesteuerte Sicherung pg_dump cron systemd timer automatische Sicherung scheduled backup tägliche Sicherung Aufbewahrung Sicherungen Tägliche Sicherung von Globals + Datenbanken, Lesbarkeitsprüfung, Aufbewahrungsfrist, Log und Fehlermeldung."}]},{"id":"pg-minor-update","file":"install/guides/pg-minor-update.json","group":"PostgreSQL","engineId":"postgres","category":"patch","title":"PostgreSQL-Minor-Update (Patch)","summary":"Minor-Update innerhalb derselben Hauptversion (z. B. 17.4 → 17.x): Prüfung der Release Notes, Backup und Aufbewahrung der vorhandenen RPM/deb-Pakete für den Rückweg. Auf einem einzelnen Server mit kurzer Downtime; in ein","level":"Mittel","duration":"20–60 Min.","topology":"1 Server, Primary + Standbys oder Patroni-Cluster","tags":["postgresql","minor","patch","aktualisierung","update","upgrade","rolling","switchover","dnf","apt","downgrade","rollback","extension","patroni","patronictl","failover"],"order":20,"steps":17,"scenarios":[{"k":"purpose","v":"upd","l":"Minor-Update","m":"minor update Minor-Update patch CVE Sicherheitsupdate dnf upgrade postgresql apt upgrade postgresql Wechsel auf eine neue Minor-Version innerhalb derselben Hauptversion (z. B. 17.4 → 17.6): Sicherheits- und Fehlerkorrekturen. Auf einem einzelnen Server kurze Downtime, im Cluster rolling."},{"k":"purpose","v":"rb","l":"Zurückrollen: auf die vorherige Minor-Version zurück (Downgrade)","m":"downgrade zurückrollen rollback auf vorherige Version zurück undefined symbol In der neuen Minor-Version ist ein Fehler bzw. eine Regression aufgetreten: Mit den gesicherten RPM/deb-Paketen der vorherigen Version wird zurückgerollt. Das Datenverzeichnis bleibt unverändert."}]},{"id":"pg-major-upgrade","file":"install/guides/pg-major-upgrade.json","group":"PostgreSQL","engineId":"postgres","category":"upgrade","title":"PostgreSQL-Major-Upgrade (pg_upgrade / logische Replikation)","summary":"Upgrade auf eine neue Hauptversion (z. B. 16 → 17): Wahl der Methode (pg_upgrade --link / --copy / --clone oder nahezu unterbrechungsfreie Migration mit logischer Replikation), pg_upgrade --check, Kompatibilität der Exte","level":"Fortgeschritten","duration":"1–3 Stunden (+ abhängig von der Datenmenge)","topology":"In-place auf demselben Server (pg_upgrade; Einzelserver, Primary + Standbys oder Patroni-Cluster) oder auf einen neuen Server (logisch)","tags":["postgresql","major","upgrade","Versionsupgrade","pg_upgrade","pg_upgradecluster","link","clone","logische Replikation","Migration","migration","rsync","rollback","patroni","patronictl remove","replica","streaming replication"],"order":30,"steps":24,"scenarios":[{"k":"purpose","v":"upg","l":"Major-Upgrade","m":"Major-Upgrade pg_upgrade pg_upgradecluster Versionsupgrade Hauptversion upgrade postgresql 17 upgrade postgresql 18 Upgrade auf eine neue Hauptversion (z. B. 16 → 17): Vorabprüfung, Backup, pg_upgrade oder logische Replikation, Verifizierung und Aufräumen."},{"k":"purpose","v":"rb","l":"Rollback (Upgrade fehlgeschlagen / abgebrochen)","m":"Upgrade zurücknehmen rollback pg_control.old database files are incompatible with server Upgrade fehlgeschlagen Das Upgrade ist mittendrin abgebrochen oder die neue Version macht Probleme: Rückkehr zur alten Version. Wurde bei --link der neue Cluster bereits gestartet, geht es nur noch über das Backup zurück."}]},{"id":"pg-security","file":"install/guides/pg-security.json","group":"PostgreSQL","engineId":"postgres","category":"security","title":"PostgreSQL-Sicherheitshärtung (Statusprüfung, Netzwerk, TLS, Authentifizierung, Berechtigungen, Audit, Verschlüsselung)","summary":"Kein Installationsleitfaden: Er findet und schließt die Sicherheitslücken eines laufenden PostgreSQL-Servers. Zuerst listet die schreibgeschützte Statusprüfung die Befunde nach Schweregrad auf und nennt für jeden Befund ","level":"Mittel","duration":"5 Min. (Prüfung) – 3 Stunden (vollständige Härtung)","topology":"Einzelner PostgreSQL-Server + Anwendungsserver (Client-Prüfung); Umgebungen mit Replica/Patroni in den Hinweisen","tags":["postgresql","postgres","Sicherheit","security","hardening","Härtung","cis benchmark","pg_hba","tls","ssl","sslmode","verify-full","scram-sha-256","md5","pgaudit","audit","Überwachung","public schema","Berechtigung","Rolle","luks","pgcrypto","Verschlüsselung","encryption","firewall"],"order":40,"steps":3,"scenarios":[{"k":"scenario","v":"scan","l":"Statusprüfung: Welche Lücken habe ich? (schreibgeschützt)","m":"Einstieg Sicherheitsprüfung security audit hardening check CIS Sicherheitslücke Schwachstelle vulnerability Pentest-Befund Prüfbericht Prüfabfrage CIS-Prüfung Sicherheitsscan security scan Der erste Scan: pg_hba, Listen-Adresse, TLS, md5-Passwörter, Superuser, PUBLIC-Berechtigungen, Audit, Dateiberechtigungen und Firewall; er ändert nichts."},{"k":"scenario","v":"all","l":"Vollständige Härtung (alle Bereiche der Reihe nach)","m":"Einstieg hardening Härtung vollständige Sicherheit full hardening CIS-Benchmark umsetzen STIG Neu installierter oder nie gehärteter Server: Alle Bereiche werden in sicherer Reihenfolge umgesetzt; PostgreSQL wird ein- bis zweimal neu gestartet (Wartungsfenster erforderlich)."},{"k":"scenario","v":"net","l":"Netzwerk und Verbindungen: Wer darf sich von wo verbinden? (listen, firewall, pg_hba)","m":"Netzwerk und Identität pg_hba.conf 0.0.0.0/0 trust listen_addresses firewall no pg_hba.conf entry pg_hba.conf rejects connection Port 5432 offen FATAL: no pg_hba.conf entry for host 28000 Connection refused listen_addresses = * host all all 0.0.0.0/0 Zugriff von außen Zugriff einschränken PostgreSQL lauscht auf allen Schnittstellen, pg_hba enthält 0.0.0.0/0 oder trust, der Port ist für alle offen."},{"k":"scenario","v":"tls","l":"Verbindungen verschlüsseln (TLS, sslmode=verify-full)","m":"Netzwerk und Identität ssl tls sslmode verify-full hostssl certificate verify failed SSL off no encryption server.crt Zertifikat abgelaufen certificate expired SSL error: certificate verify failed does not match host name root certificate file does not exist server does not support SSL, but SSL was required self-signed certificate Zertifikatskette certificate chain hostnossl ssl = off oder Clients"},{"k":"scenario","v":"auth","l":"Authentifizierung und Passwörter (scram-sha-256, Remote-Login für postgres)","m":"Netzwerk und Identität md5 scram-sha-256 password_encryption password authentication failed authentication method 10 not supported postgres Remote-Login Passwortrichtlinie password policy 28P01 FATAL: password authentication failed for user SCRAM authentication requires libpq version 10 or above pass-the-hash VALID UNTIL Passwortablauf Methode md5/password, md5-gehashte Passwörter, der Benutzer po"},{"k":"scenario","v":"priv","l":"Minimale Rechte: Rollen, PUBLIC und das Anwendungskonto","m":"Berechtigungen und Audit-Trail public schema PUBLIC GRANT REVOKE superuser least privilege minimale Rechte permission denied for schema Rollendesign DEFAULT PRIVILEGES 42501 permission denied for table must be owner of table pg_read_server_files pg_execute_server_program SECURITY DEFINER REASSIGN OWNED Berechtigungsbericht Das Anwendungskonto ist Eigentümer der Tabellen oder Superuser, jeder kann "},{"k":"scenario","v":"audit","l":"Audit: Wer hat was getan? (pgaudit, zentrales Logging)","m":"Berechtigungen und Audit-Trail pgaudit audit Audit-Trail audit log AUDIT: SESSION log_connections syslog SIEM DSGVO PCI shared_preload_libraries could not access file \"pgaudit\" pgaudit must be loaded via shared_preload_libraries log_statement wer hat was getan who changed Der Prüfer oder das Sicherheitsteam verlangt eine Aufzeichnung von DDL- und Rollenänderungen; Installation von pgaudit und Weit"},{"k":"scenario","v":"tde","l":"Daten auf dem Datenträger verschlüsseln (LUKS / pgcrypto)","m":"Daten und Backups TDE transparent data encryption Festplattenverschlüsselung encryption at rest LUKS dm-crypt pgcrypto pg_tde cryptsetup luksFormat pgp_sym_encrypt at-rest encryption Gefordert ist Schutz gegen einen gestohlenen Datenträger oder ein gestohlenes Backup. PostgreSQL hat kein eingebautes TDE: LUKS auf Datenträgerebene (Downtime für den Umzug) oder pgcrypto auf Spaltenebene."},{"k":"scenario","v":"backup","l":"Sicherheit der Backups (verschlüsselte Backups, Berechtigungen)","m":"Daten und Backups Backup-Verschlüsselung backup encryption repo-cipher-type pgbackrest cipher gpg .pgpass Backup-Berechtigungen cipher: none repo-cipher-pass unverschlüsseltes Backup unencrypted backup Backup-Dateien sind unverschlüsselt oder liegen in einem für alle lesbaren Verzeichnis; Repository-Verschlüsselung von pgBackRest oder pg_dump + GPG, .pgpass und Verzeichnisberechtigungen. Keine Dow"},{"k":"scenario","v":"os","l":"Betriebssystemseite (Dienstkonto, Berechtigungen, Core Dumps, SELinux)","m":"Server und Version SELinux permissive chmod 700 data directory has invalid permissions core dump postgres-Konto sudo avc: denied LimitCORE core dumped passwd -l Das Konto postgres hat ein nutzbares Passwort, die Berechtigungen des Datenverzeichnisses sind zu offen, SELinux steht auf Permissive/Disabled. Die Core-Dump-Einstellung greift beim nächsten Neustart."},{"k":"scenario","v":"patch","l":"Patches und Version: Bin ich auf einer unterstützten Version?","m":"Server und Version EOL end of life Support beendet Minor-Version CVE Sicherheitspatch security release out of support server_version Patch-Zeitplan minor update pgsql-announce Prüft Version, Supportende und ausstehende Minor-Updates; zum Einspielen verweist er auf den Patch-Leitfaden. Schreibgeschützt."}]},{"id":"mysql-install","file":"install/guides/mysql-install.json","group":"MySQL","engineId":"mysql","category":"install","title":"MySQL-Installation auf einem einzelnen Server (produktionsreif)","summary":"Installiert einen einzelnen MySQL-8.4-LTS-Server (oder 8.0) von Grund auf und produktionsreif aus dem offiziellen Repository: getrennte Verzeichnisse für Daten, Binlogs und Logs, eine nach RAM/CPU/Plattentyp berechnete m","level":"Einsteiger","duration":"30–45 Min.","topology":"1 Server","tags":["mysql","installation","install","8.4","lts","innodb","my.cnf","tuning","gtid","binlog","rhel","ubuntu"],"order":1,"steps":24,"scenarios":[]},{"id":"mysql-replication","file":"install/guides/mysql-replication.json","group":"MySQL","engineId":"mysql","category":"ha","title":"MySQL-GTID-Replikation (asynchron / semisynchron)","summary":"Richtet aus installierten MySQL-Servern eine GTID-basierte Replikation mit 1 Source + N Replicas ein: Replikationsbenutzer, Übertragung der vorhandenen Daten mit dem Clone-Plugin oder mysqldump, SOURCE_AUTO_POSITION, TLS","level":"Mittel","duration":"45–90 Min.","topology":"1 Source + 1..5 Replicas","tags":["mysql","replikation","replication","gtid","semi-sync","semisync","replica","source","clone","failover","read_only"],"order":2,"steps":21,"scenarios":[]},{"id":"mysql-innodb-cluster","file":"install/guides/mysql-innodb-cluster.json","group":"MySQL","engineId":"mysql","category":"ha","title":"MySQL InnoDB Cluster (Group Replication + MySQL Router)","summary":"Baut aus 3 (oder 5–9) installierten MySQL-Servern im selben Rechenzentrum einen Single-Primary-InnoDB-Cluster auf: dba.configureInstance, createCluster und addInstance mit Clone über die MySQL Shell AdminAPI; MySQL-Route","level":"Fortgeschritten","duration":"90–120 Min.","topology":"3–9 MySQL-Nodes + 1 Router-Server","tags":["mysql","innodb cluster","group replication","mysql shell","mysqlsh","adminapi","mysql router","hochverfügbarkeit","failover","quorum","clone","single-primary"],"order":3,"steps":39,"scenarios":[]},{"id":"mysql-xtrabackup","file":"install/guides/mysql-xtrabackup.json","group":"MySQL","engineId":"mysql","category":"backup","title":"Backup, Restore und PITR mit Percona XtraBackup","summary":"Physische Backups mit Percona XtraBackup: Installation der passenden XtraBackup-Version, Backup-Benutzer mit auth_socket, komprimiertes/verschlüsseltes Full- + Incremental-Backup-Skript, Zeitsteuerung per systemd-Timer o","level":"Mittel","duration":"45–90 Min.","topology":"1 Server (Einzelserver, Source, Replica oder Secondary eines InnoDB Clusters); Backup-Speicher auf lokaler Platte oder NFS","tags":["mysql","xtrabackup","percona","Sicherung","backup","restore","incremental","pitr","binlog","Wiederherstellung","zstd","Verschlüsselung","replica","slave-info","safe-slave-backup","innodb cluster","cron","systemd timer","flock"],"order":4,"steps":26,"scenarios":[{"k":"scenario","v":"setup","l":"Backups einrichten, zeitlich planen und Restore testen (kompletter Ablauf)","m":"Einrichtung und Probe xtrabackup installieren Backup-Skript backup script systemd timer cron Restore-Probe xtrabackup Sicherung physisches Backup physical backup hot backup incremental backup full backup percona-xtrabackup XtraBackup-Installation, Backup-Benutzer, Full- + Incremental-Backup-Skript, Zeitsteuerung und eine Restore-Probe (inklusive PITR). Wähle das, wenn du es zum ersten Mal einricht"},{"k":"scenario","v":"r_full","l":"Vollständiger Restore: zum letzten Full-Backup zurückkehren","m":"Restore restore wiederherstellen copy-back datadir verloren InnoDB: corruption MY-011971 MY-012592 Database page corruption mysqld startet nicht full restore ERROR 2002 Can't connect to local MySQL server through socket MY-012574 Unable to lock ./ibdata1 datadir gelöscht Plattenausfall disk failure Datenverlust Das Datenverzeichnis ist verloren/beschädigt oder der Server startet nicht; die Rückkeh"},{"k":"scenario","v":"r_inc","l":"Restore aus einer Full- + Incremental-Kette (bis zum letzten Incremental)","m":"Restore incremental restore apply-log-only incremental-dir to_lsn from_lsn This incremental backup seems not to be proper for the target Incremental-Kette incremental chain xtrabackup_checkpoints applying incremental Du willst auf den Stand des letzten Incremental-Backups (oder eines Incrementals deiner Wahl) zurück; die Incrementals werden mit `--apply-log-only` der Reihe nach auf das Full-Backup"},{"k":"scenario","v":"r_pitr","l":"Wiederherstellung auf einen Zeitpunkt (PITR): vor einem fehlerhaften DELETE / DROP","m":"Zeitpunkt PITR point in time versehentlich gelöscht DROP TABLE falsches DELETE TRUNCATE mysqlbinlog stop-datetime stop-position exclude-gtids binlog replay falsches UPDATE DELETE ohne WHERE accidental delete accidental drop DROP DATABASE rückgängig machen undo delete Wiederherstellung mit Binlog Versehentlich gelöschte oder beschädigte Daten: Das letzte Backup wird zurückgespielt und die Binlogs w"},{"k":"scenario","v":"r_table","l":"Einzelne Tabelle wiederherstellen (--export + IMPORT TABLESPACE)","m":"Zeitpunkt einzelne Tabelle single table restore IMPORT TABLESPACE DISCARD TABLESPACE transportable tablespace --export .cfg ERROR 1808 ERROR 1812 Schema mismatch ERROR 1813 Tablespace exists ERROR 1146 Table doesn't exist tablespace is missing eine Tabelle wiederherstellen table restore ibd-Datei Du brauchst nur den gesicherten Stand einer Tabelle; ohne den ganzen Server zurückzuspielen, wird die "},{"k":"scenario","v":"r_replica","l":"Neue Replica aus einem Backup aufbauen","m":"Migration und Klonen neue Replica Replica einrichten xtrabackup_binlog_info xtrabackup_slave_info gtid_purged slave-info replication from backup 1236 Replica aus Backup aufbauen seed replica Could not find first log file name in binary log index file purged required binary logs 1236 Replica neu aufbauen Du willst eine neue (oder neu aufzubauende) Replica statt per Clone über das Netz aus einem vor"},{"k":"scenario","v":"r_other","l":"Backup auf einem anderen Server zurückspielen (Migration, Test, Klon)","m":"Migration und Klonen anderer Server Testserver restore test Migration migration Klon clone to test rsync server_uuid in Testumgebung laden Backup-Prüfung restore drill Backup öffnen Serverumzug Du willst ein Produktions-Backup auf einem Test-/Entwicklungsserver öffnen, einen Server umziehen oder auf einem separaten Server prüfen, dass sich das Backup wirklich zurückspielen lässt. Der Quellserver i"}]},{"id":"mysql-upgrade","file":"install/guides/mysql-upgrade.json","group":"MySQL","engineId":"mysql","category":"upgrade","title":"MySQL 8.0 → 8.4 LTS Upgrade (in-place)","summary":"Aktualisiert ein installiertes MySQL 8.0 auf demselben Server (in-place) auf 8.4 LTS: Einzelserver, Source + Replicas (zuerst die Replicas, zuletzt die Source) oder InnoDB Cluster (Router → Shell → Metadaten → Secondarie","level":"Mittel","duration":"60–120 Min. (abhängig von der Datenmenge)","topology":"1 Server, Source + Replicas oder InnoDB Cluster (3+ Mitglieder + MySQL-Router-Server)","tags":["mysql","upgrade","8.0","8.4","lts","aktualisierung","checkForServerUpgrade","mysql_native_password","authentication_policy","rollback","rückfall","innodb cluster","group replication","mysql router","upgradeMetadata","setPrimaryInstance","rolling upgrade"],"order":5,"steps":18,"scenarios":[]},{"id":"mysql-repl-ops","file":"install/guides/mysql-repl-ops.json","group":"MySQL","engineId":"mysql","category":"ops","title":"MySQL-Replikation: täglicher Betrieb und Störungsszenarien (8.0 / 8.4)","summary":"Runbook für den täglichen Betrieb und die Störungsbehebung einer bestehenden Source-+-Replica-Topologie (async / semi-sync, GTID oder Binlog-Position): Zustands- und Lag-Prüfung, geplanter Switchover, Failover, Wiederein","level":"Fortgeschritten","duration":"5 Min. (Prüfung) – 2 Std. (Neuaufbau)","topology":"1 Source + 1..5 Replicas (async / semi-sync)","tags":["mysql","replikation","replication","gtid","errant gtid","errant transaction","switchover","failover","replica","source","super_read_only","semi-sync","show replica status","seconds_behind_source","replikationsverzögerung","lag","1062","1032","1236","duplicate entry","can't find record","gtid_next","sql_replica_skip_counter","source_delay","verzögerte replica","delayed replica","clone","xtrabackup","mysqldump","purge binary logs","binlog_expire_logs_seconds","wait_for_executed_gtid_set","change replication source to","runbook","8.0","8.4"],"order":12,"steps":5,"scenarios":[{"k":"scenario","v":"health","l":"Status- und Lag-Prüfung (ist die Replikation gesund?)","m":"Überwachung und Prüfung SHOW REPLICA STATUS SHOW SLAVE STATUS Seconds_Behind_Source Seconds_Behind_Master Replica_IO_Running Slave_IO_Running Replica_SQL_Running: No replikationsverzögerung replication lag replica lag Rpl_semi_sync_source_status 1045 2003 2026 Error connecting to source replikationsstatus Replica_IO_Running: No Replica_IO_Running: Connecting Slave_IO_Running: No Slave_SQL_Running:"},{"k":"scenario","v":"switchover","l":"Geplanter Switchover (Source-Rolle an eine Replica übergeben)","m":"Rollenwechsel switchover geplanter wechsel rollenwechsel role change promote replica source wechseln wartung geplante wartung graceful takeover graceful-master-takeover source ersetzen primary wechseln change primary neue source Source und Replicas sind gesund; du übergibst die Source-Rolle geplant an eine Replica, etwa für Wartung, Hardwaretausch oder ein Upgrade. Schreibzugriffe stehen einige Mi"},{"k":"scenario","v":"failover","l":"Source verloren: Failover auf die am weitesten fortgeschrittene Replica","m":"Rollenwechsel failover source abgestürzt source down master down primary down Error connecting to source Can't connect to MySQL server 2003 2013 Lost connection am weitesten fortgeschrittene replica most advanced replica Replica_IO_Running: Connecting Slave_IO_Running: Connecting 2005 Unknown MySQL server host error reconnecting to source source nicht erreichbar source heruntergefahren primary abg"},{"k":"scenario","v":"rejoin","l":"Alte Source nach dem Failover als Replica wieder eingliedern","m":"Rollenwechsel alte source old source old master rejoin wieder eingliedern errant split brain 1236 alter primary old primary alter master wieder da split-brain Replica has more GTIDs than the source has Slave has more GTIDs than the master has wiedereingliederung reintegrate Ein Failover wurde durchgeführt, der alte Source-Server ist wieder da. Er wird zuerst für Schreibzugriffe gesperrt, seine Bin"},{"k":"scenario","v":"rebuild","l":"Replica von Grund auf neu aufbauen (clone / XtraBackup / mysqldump)","m":"Wiederherstellung 1236 Cannot replicate because the source purged required binary logs purged required binary logs neu aufbauen rebuild replica reseed clone instance neue replica datenabweichung data drift Could not find first log file name in binary log index file the source has purged binary logs containing GTIDs that the replica requires the master has purged binary logs containing GTIDs that t"},{"k":"scenario","v":"errant","l":"Errant GTID: Die Replica hat Transaktionen, die es auf der Source nicht gibt","m":"Wiederherstellung errant gtid errant transaction gtid_subtract extra gtid überzählige gtid Having extra GTID events incompatible GTID set 1236 GTID_SUBTRACT gtid_executed abweichung auf replica geschrieben write on replica super_read_only aus Replica has more GTIDs than the source has Es gibt Transaktionen, die direkt auf die Replica geschrieben wurden (während super_read_only aus war) oder von ei"},{"k":"scenario","v":"sqlerr","l":"Replikation gestoppt: 1062 Duplicate entry / 1032 Can't find record","m":"Wiederherstellung 1062 1032 1452 1146 1007 1008 Duplicate entry Can't find record HA_ERR_KEY_NOT_FOUND HA_ERR_FOUND_DUPP_KEY Could not execute Write_rows event Could not execute Update_rows event Could not execute Delete_rows event Replica_SQL_Running: No Slave_SQL_Running: No Coordinator stopped because there were error(s) in the worker(s) sql_slave_skip_counter sql_replica_skip_counter gtid_next"},{"k":"scenario","v":"delayed","l":"Verzögerte Replica (SOURCE_DELAY): einrichten oder gelöschte Daten zurückholen","m":"Täglicher Betrieb SOURCE_DELAY MASTER_DELAY delayed replica verzögerte replica versehentlich gelöscht DROP TABLE falsches DELETE SQL_Delay SQL_BEFORE_GTIDS SQL_Remaining_Delay versehentliches DELETE DELETE ohne WHERE accidental delete accidental drop dropped table tabelle gelöscht datenwiederherstellung UNTIL SQL_BEFORE_GTIDS Du willst eine Replica bewusst N Sekunden hinter der Source halten (Verz"},{"k":"scenario","v":"repoint","l":"Replica an eine andere Source umhängen (Kette / Neuverbindung)","m":"Täglicher Betrieb repoint change source kettenreplikation chained replication intermediate zwischen-replica relay replica CHANGE MASTER TO CHANGE REPLICATION SOURCE TO cascading replication kaskadierte replikation replica an andere source anbinden SOURCE_HOST ändern MASTER_HOST ändern log_replica_updates Du willst eine Replica unter eine andere Replica hängen (Kette / Zwischen-Replica) oder wieder"},{"k":"scenario","v":"binlog","l":"Binlog-Bereinigung und Aufbewahrungsdauer (Platte läuft voll)","m":"Täglicher Betrieb PURGE BINARY LOGS PURGE MASTER LOGS binlog_expire_logs_seconds expire_logs_days binlog platte voll No space left on device errno 28 binlog bereinigung binlog retention Disk is full writing OS errno 28 1867 was not purged because it was being read 1373 Target log not found in binlog index binlog wächst binlog_expire_logs_auto_purge binlog_transaction_compression Binlogs füllen die"}]},{"id":"mysql-ic-ops","file":"install/guides/mysql-ic-ops.json","group":"MySQL","engineId":"mysql","category":"ops","title":"MySQL InnoDB Cluster: täglicher Betrieb und Störungsszenarien (MySQL Shell 8.4)","summary":"Keine Installation: ein szenariobasiertes Runbook für den täglichen Betrieb und die Störungsbehebung eines bereits laufenden InnoDB Clusters (Group Replication + MySQL Router, eingerichtet mit der MySQL Shell AdminAPI). ","level":"Fortgeschritten","duration":"5 Min. (Prüfung) – 1 Stunde (Mitglied hinzufügen)","topology":"3–9 MySQL-Nodes (Single-Primary) + MySQL Router","tags":["mysql","innodb cluster","group replication","mysql shell","mysqlsh","adminapi","cluster.status","addinstance","removeinstance","rejoininstance","rebootclusterfromcompleteoutage","forcequorumusingpartitionof","setprimaryinstance","setoption","setinstanceoption","rescan","mysql router","bootstrap","quorum","no_quorum","split brain","expelTimeout","memberWeight","autoRejoinTries","exitStateAction","consistency","clone","MISSING","UNREACHABLE","RECOVERING","ERROR","runbook","8.4"],"order":13,"steps":5,"scenarios":[{"k":"scenario","v":"health","l":"Cluster-Status und Health Check (ist alles in Ordnung?)","m":"Überwachung und Prüfung cluster.status status() OK_PARTIAL OK_NO_TOLERANCE OK_NO_TOLERANCE_PARTIAL instanceErrors replicationLag flow control RECOVERING applier queue cluster-status Cluster is ONLINE and can tolerate can tolerate up to ONE failure zustandsprüfung health check queue wächst replication_group_members group_replication_member_stats zustand des clusters Nur lesend: `cluster.status({ext"},{"k":"scenario","v":"add","l":"Neues Mitglied hinzufügen (addInstance: Clone / Incremental)","m":"Mitglieder addInstance mitglied hinzufügen node hinzufügen add instance recoveryMethod clone incremental 3092 Having extra GTID events MYSQLSH 51166 The target instance must be either cloned or fully provisioned Clone Donor Error kapazität erhöhen toleranz erhöhen OK_NO_TOLERANCE Du willst dem Cluster einen neuen MySQL-Node hinzufügen, die Ausfalltoleranz erhöhen (3 → 5) oder einen entfernten Node"},{"k":"scenario","v":"remove","l":"Mitglied entfernen (removeInstance, bei Bedarf force)","m":"Mitglieder removeInstance mitglied entfernen node entfernen remove instance force MISSING decommission Cluster.removeInstance is not reachable and cannot be safely removed node löschen verkleinern server zurückgeben Du willst einen Node dauerhaft aus dem Cluster entfernen (Hardware-Rückgabe, Verkleinerung) oder ein Node ist verloren und muss aus den Metadaten gelöscht werden (`force`). Beim Entfer"},{"k":"scenario","v":"rejoin","l":"Ausgefallenes Mitglied wieder aufnehmen (MISSING / ERROR / OFFLINE)","m":"Störung und Wiederherstellung rejoinInstance MISSING ERROR OFFLINE UNREACHABLE expelled aus der gruppe ausgeschlossen member was expelled MY-011735 MY-011526 incompatible GTID set (MISSING) This member has more executed transactions than those present in the group has an incompatible GTID set with the seed instance Error connecting to all peers Member join failed group_replication is stopped mitgl"},{"k":"scenario","v":"reboot","l":"Gesamter Cluster ausgefallen: neu starten (rebootClusterFromCompleteOutage)","m":"Störung und Wiederherstellung rebootClusterFromCompleteOutage complete outage kompletter ausfall alle nodes ausgefallen stromausfall Group Replication is not active OFFLINE Unable to find a cluster PRIMARY member The cluster is offline Dba.rebootClusterFromCompleteOutage Dba.getCluster: This function is not available through a session to a standalone instance metadata exists, instance belongs to t"},{"k":"scenario","v":"quorum","l":"Verlust der Mehrheit (NO_QUORUM): Quorum mit den verbliebenen Mitgliedern erzwingen","m":"Störung und Wiederherstellung NO_QUORUM forceQuorumUsingPartitionOf quorum verlust der mehrheit split-brain split brain UNREACHABLE majority partition 3100 Cluster has no quorum cannot process write transactions split-brain! Instance is not part of the majority group schreibzugriffe hängen writes hang forceQuorum Die meisten Mitglieder sind gleichzeitig verloren gegangen (z. B. 2 von 3), die übrig"},{"k":"scenario","v":"switch","l":"Geplanter Primary-Wechsel (setPrimaryInstance)","m":"Rolle und Routing setPrimaryInstance primary wechseln switchover primary übergeben The appointed primary member has a version that is greater group_replication_set_as_primary Cluster.setPrimaryInstance runningTransactionsTimeout geplanter primary-wechsel primary verschieben planned switchover Du willst die Primary-Rolle für Wartung (OS-Patch, Hardware), Lastverteilung oder eine DR-Probe geplant an"},{"k":"scenario","v":"router","l":"Router hinzufügen oder neu bootstrappen","m":"Rolle und Routing mysqlrouter MySQL Router bootstrap --force routerErrors re-bootstrap Router listRouters removeRouterMetadata 6446 6447 previously configured in this host Unable to connect to the metadata server Unable to fetch all configuration options The provided server is currently not in a InnoDB cluster group with quorum mysqlrouter.service failed router verbindet nicht router installieren "},{"k":"scenario","v":"options","l":"Cluster-Optionen ändern (consistency, expelTimeout, memberWeight…)","m":"Konfiguration setOption setInstanceOption consistency BEFORE_ON_PRIMARY_FAILOVER expelTimeout group_replication_member_expel_timeout memberWeight autoRejoinTries exitStateAction OFFLINE_MODE READ_ONLY options() group_replication_consistency group_replication_member_weight group_replication_autorejoin_tries group_replication_exit_state_action mitglied wird oft ausgeschlossen failover-priorität Du w"},{"k":"scenario","v":"rescan","l":"Metadaten mit der tatsächlichen Gruppe abgleichen (rescan)","m":"Konfiguration rescan metadata is not managed not registered in the metadata newlyDiscoveredInstances unavailableInstances instanceErrors obsolete Use cluster.rescan() Instance is not managed by InnoDB cluster Use cluster.rescan() to repair metadaten inkonsistent metadaten abweichend `cluster.status()` nennt ein Mitglied, das nicht in den Metadaten steht, oder einen alten Eintrag, der nicht mehr in"}]},{"id":"mysql-logical-backup","file":"install/guides/mysql-logical-backup.json","group":"MySQL","engineId":"mysql","category":"backup","title":"Logische Sicherung und Wiederherstellung in MySQL (mysqldump, MySQL Shell)","summary":"Logische Sicherung und Wiederherstellung mit mysqldump oder MySQL Shell (util.dumpInstance / dumpSchemas / dumpTables + util.loadDump): eine Tabelle, ein Schema oder die ganze Instanz; nur Struktur oder nur Daten; Migrat","level":"Mittel","duration":"20–90 Min. (je nach Datenmenge)","topology":"1 Server (bei Migration und Instanz-Wiederherstellung + Zielserver)","tags":["mysql","mysqldump","mysql shell","mysqlsh","dumpInstance","dumpSchemas","dumpTables","loadDump","logische Sicherung","logical backup","export","import","Wiederherstellung","restore","Migration","migration","login-path","mysql_config_editor","systemd timer","cron","Sicherung","replica","dump-replica","source-data","innodb cluster","cron","systemd timer","flock","login-path"],"order":15,"steps":8,"scenarios":[{"k":"mode","v":"table","l":"Tabelle(n) exportieren / importieren","m":"Sicherung und Wiederherstellung Tabellensicherung table export Tabelle kopieren copy table mysqldump Tabelle Eine oder mehrere Tabellen sichern, in ein anderes Schema bzw. auf einen anderen Server kopieren oder wiederherstellen. Die Quelle bleibt in Betrieb; auf InnoDB-Tabellen werden keine Locks gesetzt."},{"k":"mode","v":"schema","l":"Ein Schema (Datenbank) sichern / wiederherstellen","m":"Sicherung und Wiederherstellung Schemasicherung Datenbanksicherung database backup dumpSchemas mysqldump database Eine komplette Datenbank (Schema) sichern oder in ein anderes Schema bzw. auf einen anderen Server laden. Die Quelle bleibt in Betrieb; gleichnamige Tabellen im Ziel werden gelöscht und neu angelegt."},{"k":"mode","v":"instance","l":"Ganze Instanz (Benutzer + alle Schemas)","m":"Sicherung und Wiederherstellung alle Datenbanken all databases dumpInstance Benutzer migrieren full logical backup Die Sicherung wird auf diesem Server erstellt, die Wiederherstellung erfolgt auf einem leeren Server: Benutzer, Berechtigungen und alle Schemas. Zum Neuaufbau eines Servers oder zum Wechsel von Version/Plattform."},{"k":"mode","v":"ddl","l":"Nur Struktur (DDL)","m":"Struktur und Daten getrennt nur Struktur schema only no-data ddlOnly CREATE TABLE export Definitionen von Tabellen, Views, Triggern, Prozeduren und Events; keine Daten. Zum Schemavergleich, zum Aufbau einer leeren Umgebung oder um die Struktur unter Versionskontrolle zu stellen."},{"k":"mode","v":"data","l":"Nur Daten","m":"Struktur und Daten getrennt nur Daten data only no-create-info dataOnly Datenübertragung Nur die Zeilen werden übertragen, während die Struktur im Ziel bereits vorhanden ist (z. B. Testdaten auffrischen). Trigger im Ziel können während des Ladens auslösen; der Guide prüft das."},{"k":"mode","v":"migrate","l":"Migration auf einen anderen Server","m":"Migration Migration migration Serverwechsel auf neuen Server migrieren 8.0 to 8.4 migration dump and load nach HeatWave migrieren Du arbeitest auf dem Zielserver und verbindest dich über das Netz mit login-path zur Quelle. Beim Cutover stoppt die Anwendung das Schreiben; die Ausfallzeit entspricht der Dauer von Dump + Laden. Für den Wechsel von Server, Betriebssystem oder Version."},{"k":"mode","v":"extract","l":"Einzelne Tabelle aus einem vollständigen Dump extrahieren","m":"Wiederherstellung einzelne Tabelle wiederherstellen Tabelle aus Dump extrahieren extract table from dump DROP TABLE rückgängig machen gelöschte Zeilen ERROR 1146 Table doesn't exist Du hast einen vollständigen Dump und brauchst nur eine Tabelle (gelöschte Zeilen, per DROP gelöschte Tabelle). Sie wird ohne Eingriff in das ursprüngliche Schema in ein separates Schema extrahiert, oder die gelöschte T"},{"k":"mode","v":"sched","l":"Zeitgesteuerte Sicherung (systemd timer / cron)","m":"Zeitgesteuerte Sicherung zeitgesteuerte Sicherung scheduled backup cron mysqldump systemd timer automatische Sicherung nächtliche Sicherung Automatische logische Sicherung jeden Tag zu einer festen Uhrzeit: eigener Backup-Benutzer, login-path, Skript, systemd timer oder cron, Aufbewahrungsdauer und Fehlerbenachrichtigung. Die Quelle bleibt in Betrieb."}]},{"id":"mysql-security","file":"install/guides/mysql-security.json","group":"MySQL","engineId":"mysql","category":"security","title":"MySQL-Sicherheitshärtung (Statusprüfung, Netzwerk, TLS, Authentifizierung, Berechtigungen, Audit, Verschlüsselung)","summary":"Dies ist kein Installationsguide: Er findet und schließt die Lücken eines laufenden MySQL-8.4-/8.0-Servers. Zuerst listet die rein lesende Statusprüfung die Befunde nach Schweregrad auf und nennt für jeden Befund die zug","level":"Mittel","duration":"5 Min. (Prüfung) – 3 Std. (vollständige Härtung)","topology":"Ein MySQL-Server + Anwendungsserver (Client-Prüfung); Replica/InnoDB Cluster in den Hinweisen","tags":["mysql","sicherheit","security","hardening","härtung","cis benchmark","tls","ssl","require_secure_transport","verify_identity","caching_sha2_password","mysql_native_password","validate_password","audit_log","audit","percona","tde","keyring","innodb encryption","root","anonymer benutzer","berechtigungen","rolle","login-path"],"order":40,"steps":2,"scenarios":[{"k":"scenario","v":"scan","l":"Statusprüfung: Welche Lücken habe ich? (rein lesend)","m":"Einstieg sicherheitsprüfung security audit hardening check CIS sicherheitslücke schwachstelle vulnerability pentest-befund mysql_secure_installation prüfabfrage sicherheitsscan security scan KRITISCH Der erste Scan: anonyme, passwortlose und Remote-root-Konten, Zugriff von überall (%), schwaches Authentifizierungs-Plugin, globale Berechtigungen, TLS, Audit, Verschlüsselung, Dateiberechtigungen und"},{"k":"scenario","v":"all","l":"Vollständige Härtung (alles nacheinander)","m":"Einstieg hardening härtung vollständige sicherheit full hardening cis benchmark anwenden STIG Neu installierter oder nie gehärteter Server: Alle Bereiche werden in einer sicheren Reihenfolge angewendet; MySQL wird mehrmals neu gestartet (Wartungsfenster nötig)."},{"k":"scenario","v":"net","l":"Netzwerk und Verbindungen: Wer darf sich von wo verbinden? (bind_address, Firewall, Host-Einschränkung)","m":"Netzwerk und Identität bind_address 0.0.0.0 '%' host '%' firewall 3306 33060 Host is not allowed to connect ERROR 1130 ERROR 2003 Can't connect to MySQL server skip_name_resolve zugriff von außen zugriff einschränken MySQL lauscht auf allen Interfaces, Konten haben den Host '%', der Port ist für alle offen. Eine Änderung von bind_address erfordert einen Neustart."},{"k":"scenario","v":"tls","l":"Verbindungen verschlüsseln (TLS, require_secure_transport, VERIFY_IDENTITY)","m":"Netzwerk und Identität ssl tls require_secure_transport ssl-mode VERIFY_IDENTITY VERIFY_CA ERROR 3159 Connections using insecure transport are prohibited server-cert.pem zertifikatslaufzeit ERROR 2026 SSL connection error certificate verify failed ERROR 3888 zertifikatskette certificate chain Das automatisch erzeugte selbstsignierte Zertifikat ist in Gebrauch, unverschlüsselte Verbindungen werden "},{"k":"scenario","v":"auth","l":"Authentifizierung und Passwörter (caching_sha2, Passwortrichtlinie, root)","m":"Netzwerk und Identität mysql_native_password caching_sha2_password validate_password password_history FAILED_LOGIN_ATTEMPTS remote root anonymous user anonymer benutzer ERROR 1045 Access denied Plugin 'mysql_native_password' is not loaded ERROR 1524 ERROR 1819 ERROR 3955 ERROR 3118 Account is locked ERROR 2061 Authentication requires secure connection auth_socket Konten mit mysql_native_password, "},{"k":"scenario","v":"priv","l":"Minimale Berechtigungen: Rollen und ein Anwendungskonto ohne DDL","m":"Berechtigungen und Audit-Trail GRANT REVOKE ALL PRIVILEGES SUPER FILE least privilege minimale berechtigungen CREATE ROLE ERROR 1142 command denied ERROR 3530 ERROR 1227 you need (at least one of) the SUPER privilege DEFAULT ROLE berechtigungsbericht Berechtigungen CREATE/ALTER/DROP oder *.* auf dem Anwendungskonto, Konten mit FILE/SUPER; Lese-/Schreib-/DDL-Rollen und ein Berechtigungsbericht. Kei"},{"k":"scenario","v":"audit","l":"Audit: Wer hat was getan?","m":"Berechtigungen und Audit-Trail audit audit_log audit_log_filter audit-trail general_log Enterprise Audit Percona audit SIEM DSGVO PCI audit_log_filter_set_filter ERROR 1126 MySQL-Enterprise-Lizenz wer hat was getan Der Prüfer verlangt eine Protokollierung von Sessions und Abfragen. MySQL Community hat kein eingebautes Audit: Enterprise Audit, der Audit Log Filter von Percona Server oder eingeschrä"},{"k":"scenario","v":"tde","l":"Daten auf der Disk verschlüsseln (InnoDB-Tablespace-Verschlüsselung, Keyring)","m":"Daten und Backups TDE encryption at rest ENCRYPTION='Y' keyring component_keyring_file ERROR 3185 Can't find master key from keyring binlog_encryption innodb_redo_log_encrypt ALTER INSTANCE ROTATE INNODB MASTER KEY mysqld.my keyring_component_status master key Verschlüsselung von Tabellen, Redo, Undo und Binlog gegen den Diebstahl einer Disk oder einer Backup-Kopie; ein Neustart für die Installati"},{"k":"scenario","v":"backup","l":"Sicherheit der Backups (verschlüsselte Backups, login-path, Berechtigungen)","m":"Daten und Backups backup verschlüsseln backup encryption mysqldump gpg xtrabackup --encrypt login-path mysql_config_editor .my.cnf passwort xbcrypt --encrypt-key-file unverschlüsseltes backup unencrypted backup Backups unverschlüsselt oder in einem für alle lesbaren Verzeichnis, Klartextpasswort in Skripten; mysqldump + GPG oder XtraBackup --encrypt, ein Backup-Konto und login-path."},{"k":"scenario","v":"os","l":"Betriebssystemseite (Dienstkonto, Berechtigungen, Core Dump, SELinux)","m":"Server und Version SELinux permissive secure_file_priv local_infile core dump mysql-konto .mysql_history ERROR 1290 secure-file-priv option ERROR 3948 Loading local data is disabled avc: denied Datenverzeichnis oder Zertifikatsdateien sind für andere zugänglich, secure_file_priv ist leer, local_infile ist aktiv, SELinux ist deaktiviert. Manche Einstellungen werden erst beim Neustart wirksam."},{"k":"scenario","v":"patch","l":"Patches und Versionen: Bin ich auf einer unterstützten Version?","m":"Server und Version EOL end of life supportende MySQL 8.0 EOL CPU Critical Patch Update CVE sicherheitspatch patchplan MySQL 8.0 end of life 8.4 LTS Version, Supportende (8.0 endete im April 2026) und ausstehende Updates; zum Einspielen verweist er auf den Upgrade-Guide. Rein lesend."}]},{"id":"mssql-linux-install","file":"install/guides/mssql-linux-install.json","group":"SQL Server","engineId":"mssql","category":"install","title":"SQL Server-Installation unter Linux (RHEL / Ubuntu, produktionsreif)","summary":"Installiert SQL Server 2022 oder 2025 aus dem Microsoft-Repository auf einem einzelnen Linux-Server (RHEL-Familie oder Ubuntu): Wahl der Edition, sichere Übergabe des sa-Kennworts, mssql-tools18 (sqlcmd/bcp), getrennte V","level":"Einsteiger","duration":"40–60 Min.","topology":"1 Server","tags":["sql server","mssql","linux","installation","install","mssql-conf","sqlcmd","tempdb","maxdop","rhel","ubuntu","2022","2025"],"order":1,"steps":22,"scenarios":[]},{"id":"mssql-windows-install","file":"install/guides/mssql-windows-install.json","group":"SQL Server","engineId":"mssql","category":"install","title":"SQL Server 2022 unter Windows installieren (unattended, produktionsreif)","summary":"Installiert SQL Server 2022 unbeaufsichtigt (unattended) auf einem einzelnen Windows Server mit einer ConfigurationFile.ini, die aus deinen Angaben erzeugt wird (Features, Instanz, Dienstkonten/gMSA, Sortierung, Verzeich","level":"Mittel","duration":"45–75 Min.","topology":"1 Windows Server","tags":["sql server","mssql","windows","installation","install","unattended","configurationfile.ini","setup.exe","gmsa","tempdb","maxdop","ifi","lock pages in memory","2022"],"order":2,"steps":18,"scenarios":[]},{"id":"mssql-ag-windows","file":"install/guides/mssql-ag-windows.json","group":"SQL Server","engineId":"mssql","category":"ha","title":"SQL Server Always On Availability Group (Windows, WSFC)","summary":"Richtet auf Windows-Servern mit installiertem SQL Server 2022 eine Always On Availability Group ein: Windows Server Failover Cluster (WSFC) und Quorumzeuge (Dateifreigabe / Cloudzeuge), Aktivieren von Always On, Mirrorin","level":"Fortgeschritten","duration":"90–150 Min.","topology":"2–9 Windows-Knoten (WSFC) + Zeuge (Witness)","tags":["sql server","mssql","always on","verfügbarkeitsgruppe","ag","wsfc","failovercluster","listener","zeuge","automatic seeding","hadr","windows","hochverfügbarkeit"],"order":3,"steps":25,"scenarios":[]},{"id":"mssql-ag-linux","file":"install/guides/mssql-ag-linux.json","group":"SQL Server","engineId":"mssql","category":"ha","title":"SQL Server Always On Availability Group (Linux, Pacemaker)","summary":"Richtet aus drei Linux-Servern (RHEL 9 oder Ubuntu 22.04) mit installiertem SQL Server 2022 eine von Pacemaker verwaltete Always On AG ein: mssql-server-ha, hadr.hadrenabled, per Zertifikat authentifizierte Datenbankspie","level":"Fortgeschritten","duration":"120–180 min","topology":"3 Linux-Nodes: 3 synchrone Replicas oder 2 synchrone + eine configuration-only Replica","tags":["sql server","mssql","always on","verfügbarkeitsgruppe","ag","linux","pacemaker","corosync","pcs","stonith","fencing","mssql-server-ha","ocf:mssql:ag","cluster_type external","configuration only","listener","virtuelle ip","rhel","ubuntu","hochverfügbarkeit"],"order":4,"steps":30,"scenarios":[]},{"id":"mssql-cu-patch","file":"install/guides/mssql-cu-patch.json","group":"SQL Server","engineId":"mssql","category":"patch","title":"SQL Server Cumulative Update (CU) einspielen","summary":"Spielt auf einer installierten SQL Server 2022/2025-Instanz ein Cumulative Update (CU) innerhalb derselben Hauptversion ein: unter Linux mit dnf/apt, unter Windows als unbeaufsichtigte Installation (/quiet) mit der CU-Se","level":"Mittel","duration":"30–90 Min.","topology":"1 Server oder eine AG (2+ Replicas)","tags":["sql server","mssql","cu","cumulative update","patch","aktualisierung","rolling","availability group","always on","yum","apt","kb"],"order":4,"steps":12,"scenarios":[]},{"id":"mssql-ag-ops","file":"install/guides/mssql-ag-ops.json","group":"SQL Server","engineId":"mssql","category":"ops","title":"Betrieb von SQL Server Always On AG (Windows und Linux)","summary":"Keine Installation, sondern Betrieb im Alltag und Störungsszenarien einer installierten Always On Availability Group: Health Check, Diagnose von Verzögerung und Log-Wachstum, geplantes Failover, erzwungenes Failover (FOR","level":"Fortgeschritten","duration":"15–90 min (je nach Szenario)","topology":"AG mit 2–9 Replicas (Windows WSFC oder Linux Pacemaker)","tags":["sql server","mssql","always on","availability group","ag","failover","forced failover","force_failover_allow_data_loss","suspend","resume","automatic seeding","read-only routing","listener","pacemaker","pcs","wsfc","secondary lag","log_send_queue","redo_queue","required_synchronized_secondaries_to_commit"],"order":5,"steps":3,"scenarios":[{"k":"op","v":"health","l":"AG-Health-Check (Dashboard-Abfragen)","m":"Überwachung und Diagnose NOT SYNCHRONIZING NOT_HEALTHY DISCONNECTED RESOLVING PARTIALLY_HEALTHY AG-Zustand availability group health sys.dm_hadr dashboard Replica-Status Msg 35206 connection timeout AG dashboard Show Dashboard Ist alles in Ordnung? Replica-/Datenbankstatus, Failover-Bereitschaft, Cluster und letzte Rollenwechsel. Ändert nichts."},{"k":"op","v":"lag","l":"Secondary fällt zurück / Logdatei wächst (Diagnose der Verzögerung)","m":"Überwachung und Diagnose AVAILABILITY_REPLICA log_reuse_wait log_send_queue redo_queue HADR_SYNC_COMMIT secondary lag secondary_lag_seconds Msg 9002 9002 transaction log is full Verzögerung redo blocked Log wächst Die Send-/Redo-Queue wächst, die Daten auf dem Secondary sind veraltet, das Log auf dem Primary läuft bald voll (log_reuse_wait = AVAILABILITY_REPLICA) oder Commits sind langsam (HADR_SY"},{"k":"op","v":"failover","l":"Geplantes Failover (Switchover, kein Datenverlust)","m":"Rollenwechsel planned failover manueller Failover switchover ALTER AVAILABILITY GROUP FAILOVER pcs resource move Msg 41142 41142 Msg 19407 Rolle wechseln Primary verschieben Failover Cluster Manager Move Wartungs-Failover Verschiebt den Primary für Wartung, Patching oder Tests auf einen synchronen, SYNCHRONIZED Secondary; einige Sekunden Unterbrechung, kein Datenverlust."},{"k":"op","v":"forced","l":"Primary verloren: erzwungenes Failover (FORCE_FAILOVER_ALLOW_DATA_LOSS)","m":"Rollenwechsel FORCE_FAILOVER_ALLOW_DATA_LOSS forced failover erzwungenes Failover quorum lost Quorumverlust FixQuorum forcequorum disaster recovery DR Msg 41131 Msg 35250 Standortverlust primary down Msg 47104 ForceQuorum Cluster is running in ForceQuorum state RESOLVING_NORMAL Primary abgestürzt Datenverlust Der Primary (oder der ganze Standort / das Cluster-Quorum) ist verloren und Warten ist ke"},{"k":"op","v":"suspend","l":"Datenverschiebung anhalten / fortsetzen (SUSPEND / RESUME)","m":"Wartung SUSPENDED SET HADR SUSPEND SET HADR RESUME is_suspended suspend_reason data movement Datenverschiebung angehalten Msg 35264 35264 Msg 35265 SUSPEND_FROM_REDO SUSPEND_FROM_USER SUSPEND_FROM_PARTNER hängt Datenbank anhalten Das Senden von Daten an einen Secondary vorübergehend anhalten (Netzwerkwartung, Massenverarbeitung) oder eine im Status SUSPENDED hängende Datenbank fortsetzen."},{"k":"op","v":"mode","l":"Synchronen / asynchronen Modus ändern","m":"Wartung SYNCHRONOUS_COMMIT ASYNCHRONOUS_COMMIT availability mode REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT required_synchronized_secondaries_to_commit synchrone Replikation asynchron Msg 35211 35211 Msg 35212 35212 FAILOVER_MODE Ein Replica zwischen SYNCHRONOUS_COMMIT und ASYNCHRONOUS_COMMIT umstellen (entfernter Standort, langsames Netzwerk, DR-Übung) und den Commit-Schutz (REQUIRED_SYNCHRONIZE"},{"k":"op","v":"adddb","l":"Datenbank zur AG hinzufügen (Automatic Seeding oder Backup/Restore)","m":"Änderung ADD DATABASE automatic seeding SET HADR AVAILABILITY GROUP Msg 35250 Msg 1478 1478 seeding failed Datenbank hinzufügen Msg 1475 1475 might contain bulk logged changes manual seeding Eine neue oder vorhandene Datenbank in die AG aufnehmen: Automatic Seeding für kleine/mittlere Datenbanken, aus dem Backup für große Datenbanken oder bei langsamem Netzwerk."},{"k":"op","v":"addrep","l":"Replica (Node) hinzufügen","m":"Änderung ADD REPLICA Add-ClusterNode pcs cluster node add neue Replica Replica hinzufügen Msg 41158 41158 Failed to join local availability replica JOIN Einen neuen Secondary zur AG hinzufügen: Der Node tritt dem Cluster bei, der Endpoint wird vorbereitet, ADD REPLICA + JOIN, die Datenbanken kommen per Seeding."},{"k":"op","v":"removerep","l":"Replica (Node) entfernen","m":"Änderung REMOVE REPLICA Remove-ClusterNode pcs cluster node remove Replica entfernen decommission Msg 35223 35223 Node entfernen Einen Secondary, der dauerhaft abgeschaltet oder neu aufgebaut wird, aus der AG und dem Cluster entfernen."},{"k":"op","v":"routing","l":"Read-Only Routing (Leseweiterleitung)","m":"Konnektivität read-only routing READ_ONLY_ROUTING_URL READ_ONLY_ROUTING_LIST ApplicationIntent=ReadOnly readable secondary lesbarer Secondary Msg 978 978 Msg 976 976 not accessible for queries Msg 10786 ApplicationIntent Verbindungen mit ApplicationIntent=ReadOnly über den Listener an lesbare Secondaries weiterleiten (Reporting-Last vom Primary nehmen)."},{"k":"op","v":"listener","l":"Listener erstellen oder ändern (IP, Port)","m":"Konnektivität listener ADD LISTENER MODIFY LISTENER MultiSubnetFailover RegisterAllProvidersIP Msg 19471 19471 Msg 41009 virtualip Msg 19456 19456 HostRecordTTL Listener-Port DNS-Eintrag Den Listener erstellen, falls keiner existiert, seinen Port ändern oder (Windows) eine IP für ein zweites Subnetz hinzufügen."}]},{"id":"mssql-backup","file":"install/guides/mssql-backup.json","group":"SQL Server","engineId":"mssql","category":"backup","title":"SQL Server: Backup-Strategie, Automatisierung und Wiederherstellung","summary":"Richtet die Sicherung einer installierten SQL Server-Instanz ein (Linux oder Windows; einzelne Instanz, FCI oder AG-Replicas) und stellt aus der Sicherung wieder her. Sicherung: Wahl des Wiederherstellungsmodells, wöchen","level":"Mittel","duration":"30–90 Min.","topology":"1 Server, Failoverclusterinstanz oder Always On AG-Replicas (Restore auf einen anderen Server: Quelle + Ziel)","tags":["sql server","mssql","backup","sicherung","datensicherung","restore","wiederherstellung","pitr","stopat","point in time","differential","transaction log","copy_only","compression","checksum","encryption","sicherungsverschlüsselung","sql server agent","job","ola hallengren","maintenance solution","aufbewahrung","verifyonly","headeronly","filelistonly","norecovery","standby","with move","aufgabenplanung","cron","availability group","always on","backup preference","automated_backup_preference","fn_hadr_backup_is_preferred_replica","copy_only","sql agent","sysjobhistory","cron","aufgabenplanung","page restore","suspect_pages","824","823","33111","18456","orphaned user","sp_help_revlogin","master restore","single user","msdb restore","model restore","stopatmark","tail-log","no_truncate","recovery_pending","suspect","log chain","lsn"],"order":15,"steps":11,"scenarios":[{"k":"mode","v":"strategy","l":"Backup-Strategie einrichten (geplante Sicherungen + Aufbewahrung)","m":"Sicherung sicherungsplan backup strategy logdatei wächst LOG_BACKUP Msg 9002 transaction log is full sql agent job Msg 3013 3013 BACKUP DATABASE is terminating abnormally keine sicherung sicherungsjob Neuer Server oder Datenbank ohne regelmäßige Sicherung: Full- / Differential- / Log-Jobs, Aufbewahrung und Überwachung werden eingerichtet."},{"k":"mode","v":"oneoff","l":"Einmalige Sicherung erstellen (z. B. COPY_ONLY vor einer Änderung)","m":"Sicherung copy_only einmalige sicherung sicherung vor änderung manuelle sicherung Msg 4214 4214 Msg 4208 4208 BACKUP LOG is not allowed while the recovery model is SIMPLE Eine einzelne Sicherung vor einer Änderung, einem Patch oder Upgrade oder als Kopie für eine Testumgebung; COPY_ONLY unterbricht die Kette nicht."},{"k":"mode","v":"verify","l":"Sicherungen prüfen (Verlauf, VERIFYONLY, Test-Restore)","m":"Probe und Kontrolle VERIFYONLY HEADERONLY FILELISTONLY test restore sicherung beschädigt Msg 3241 Msg 3183 Msg 3203 Um nachzuweisen, dass sich die Sicherungen tatsächlich wiederherstellen lassen: Sicherungsverlauf, VERIFYONLY, HEADERONLY / FILELISTONLY und Test-Restore in eine temporäre Datenbank + CHECKDB."},{"k":"mode","v":"chain","l":"Ist die Sicherungskette intakt? (LSN-Lücke, fehlende Datei)","m":"Probe und Kontrolle Msg 4305 4305 too recent to apply LSN log chain kette unterbrochen broken log chain Msg 3136 last_log_backup_lsn sicherungskette Vor einem Restore oder als regelmäßige Kontrolle: rekonstruiert die Kette aus dem msdb-Verlauf und findet LSN-Lücken, auf dem Datenträger fehlende Dateien und dazwischen erstellte Sicherungen. Ändert nichts."},{"k":"mode","v":"restore","l":"Datenbank aus der Sicherung wiederherstellen (Kette, STOPAT / STOPATMARK, MOVE / REPLACE, anderer Server)","m":"Wiederherstellung und Zeitpunkt STOPAT STOPATMARK STOPBEFOREMARK point in time PITR versehentlich gelöscht DELETE rückgängig machen WITH MOVE REPLACE tail-log Msg 3154 Msg 3159 Msg 5133 Msg 1834 Msg 3156 The tail of the log for the database Msg 3101 3101 Exclusive access could not be obtained Msg 4335 4335 RESTORE DATABASE Daten wurden gelöscht oder falsch aktualisiert, eine Kopie der Datenbank wi"},{"k":"mode","v":"damaged","l":"Datenbank lässt sich nicht öffnen: Datendatei fehlt oder ist beschädigt (Tail-Log + Restore)","m":"Verlust / Beschädigung Msg 945 945 RECOVERY_PENDING recovery pending SUSPECT Msg 5120 5120 Msg 17204 Msg 17207 Msg 5181 FCB::Open failed Operating system error 2 mdf gelöscht datendatei fehlt database cannot be opened Msg 823 NO_TRUNCATE CONTINUE_AFTER_ERROR EMERGENCY Die Datenbank ist RECOVERY_PENDING oder SUSPECT, .mdf/.ndf ist nicht lesbar, die Instanz läuft aber: zuerst wird mit NO_TRUNCATE ei"},{"k":"mode","v":"page","l":"Einige Seiten beschädigt (Msg 823 / 824): Page Restore","m":"Verlust / Beschädigung Msg 824 824 Msg 823 823 Msg 829 829 suspect_pages torn page incorrect checksum beschädigte seite page corruption page restore CHECKDB found Msg 8928 Msg 8939 Msg 8976 repair_allow_data_loss Abfragen oder CHECKDB melden Fehler auf bestimmten Seiten (msdb.dbo.suspect_pages): nur die beschädigten Seiten werden aus der Sicherung wiederhergestellt. Unter Enterprise/Developer blei"},{"k":"mode","v":"master","l":"master-Datenbank wiederherstellen (Einzelbenutzermodus)","m":"Systemdatenbanken master Msg 3108 3108 single user mode single-user einzelbenutzermodus -m Msg 3417 3417 Msg 17113 Msg 18461 master beschädigt force-setup REBUILDDATABASE master ist beschädigt oder Logins / Verbindungsserver / Servereinstellungen sind verloren: die Instanz wird im Einzelbenutzermodus (-m) gestartet, master wird wiederhergestellt, SQL Server fährt sich danach selbst herunter. Erfor"},{"k":"mode","v":"sysdb","l":"msdb- oder model-Datenbank wiederherstellen","m":"Systemdatenbanken msdb model Msg 3168 3168 jobs verschwunden msdb suspect Msg 1807 msdb beschädigt model beschädigt Jobs, Sicherungsverlauf, Database Mail (msdb) oder die Vorlage für neue Datenbanken (model) sind beschädigt / verloren: Restore mit REPLACE aus einer Sicherung desselben Builds; der Agent steht kurz still."},{"k":"mode","v":"orphan","l":"Benutzer können sich nach dem Restore nicht anmelden (verwaiste Benutzer, Login-Zuordnung)","m":"Migration und Zugriff Msg 18456 18456 Login failed for user orphaned user orphan verwaister benutzer sp_help_revlogin sp_change_users_login Msg 15023 15023 Msg 916 Msg 4060 login zuordnen SID Die Datenbank wurde auf einen anderen Server verschoben oder dort wiederhergestellt, die Anwendung erhält 'Login failed': die Logins werden mit SID und Kennwort-Hash von der Quelle übertragen, verbliebene Ben"},{"k":"mode","v":"enc","l":"Verschlüsselte Sicherung lässt sich nicht öffnen (Zertifikat fehlt, Msg 33111)","m":"Migration und Zugriff Msg 33111 33111 Cannot find server certificate with thumbprint verschlüsselte sicherung encrypted backup zertifikat certificate TDE Msg 15581 15581 master key Die Sicherung (oder die Sicherung einer TDE-Datenbank) ist mit einem Zertifikat verschlüsselt, das auf diesem Server fehlt: der Thumbprint wird ermittelt, das Zertifikat mit dem privaten Schlüssel geladen und die Sicher"}]},{"id":"mssql-bcp-export","file":"install/guides/mssql-bcp-export.json","group":"SQL Server","engineId":"mssql","category":"backup","title":"SQL Server-Export/-Import auf Tabellenebene (bcp, sqlpackage)","summary":"Keine Sicherung, sondern logische Datenübertragung: Tabellen mit bcp exportieren und in eine andere Datenbank laden (natives oder Zeichenformat, Formatdatei, Batch, TABLOCK, Identitätsspalte, UTF-8), ein Abfrageergebnis ","level":"Mittel","duration":"20–60 Min.","topology":"1 Server (die Befehle laufen auf dem SQL Server selbst oder auf einem Verwaltungsserver)","tags":["sql server","mssql","bcp","massenkopieren","export","import","queryout","formatdatei","native","csv","utf-8","sqlpackage","bacpac","dacpac","extract","publish","azure sql","datenübertragung","migration","tabelle kopieren"],"order":16,"steps":8,"scenarios":[{"k":"op","v":"table","l":"Tabellen übertragen: bcp out → bcp in","m":"Datenübertragung bcp bcp out bcp in massenkopieren tabelle kopieren tabelle übertragen Unexpected EOF encountered in BCP data-file Unable to open BCP host data-file Eine oder mehrere Tabellen in eine andere SQL Server-Datenbank kopieren; die Tabellen müssen auf dem Ziel bereits existieren."},{"k":"op","v":"query","l":"Abfrageergebnis in eine Datei schreiben: bcp queryout (CSV/Text)","m":"Export queryout csv csv export abfrageergebnis in datei export to file Das Ergebnis einer Abfrage in eine CSV-/Textdatei schreiben, die an ein anderes System (Excel, ETL, Reporting) geht."},{"k":"op","v":"bacpac","l":"Schema + Daten: sqlpackage Export / Import (.bacpac)","m":"Datenübertragung bacpac sqlpackage SqlPackage Export SqlPackage Import Migration nach Azure SQL Database SQL71564 SQL71501 Schema und Daten in einer einzigen Datei übertragen; vor allem für die Migration nach Azure SQL Database oder für kleine/mittlere Datenbanken."},{"k":"op","v":"dacpac","l":"Nur Schema: sqlpackage Extract / Script / Publish (.dacpac)","m":"Schema dacpac Extract Publish schemavergleich schema compare BlockOnPossibleDataLoss The schema update is terminating because data loss might occur Nur das Schema extrahieren, mit dem Ziel vergleichen und ein Änderungsskript erzeugen oder ein leeres Schema in einer neuen Umgebung bereitstellen."}]},{"id":"mssql-security","file":"install/guides/mssql-security.json","group":"SQL Server","engineId":"mssql","category":"security","title":"SQL Server-Sicherheitshärtung (Linux und Windows: Bewertung, Netzwerk, TLS, Authentifizierung, Berechtigungen, Audit, TDE)","summary":"Kein Installationsguide: Der Guide findet und schließt die Lücken einer laufenden SQL Server-Instanz 2019 / 2022 / 2025 (Linux oder Windows). Zuerst listet die schreibgeschützte Sicherheitsbewertung die Lücken nach Schwe","level":"Mittel","duration":"5 Min. (Bewertung) – 3 Std. (vollständige Härtung)","topology":"Einzelne SQL Server-Instanz (Linux oder Windows) + Anwendungsserver; Always On AG in den Hinweisen","tags":["sql server","mssql","sicherheit","security","hardening","härtung","cis benchmark","stig","force encryption","tls","zertifikat","sa","check_policy","xp_cmdshell","trustworthy","server audit","audit","tde","transparent data encryption","backup encryption","mssql-conf","windows","linux"],"order":40,"steps":2,"scenarios":[{"k":"scenario","v":"scan","l":"Sicherheitsbewertung: Welche Lücken habe ich? (schreibgeschützt)","m":"Einsteiger sicherheitsbewertung security audit hardening check CIS STIG sicherheitslücke schwachstelle vulnerability pentest-befund SQL Vulnerability Assessment prüfabfrage sicherheitsprüfung security scan KRITISCH Der erste Scan: sa, Kennwortrichtlinie, leere Kennwörter, sysadmin-Mitglieder, Berechtigungen von public/guest, xp_cmdshell, unverschlüsselte Verbindungen, Audit, TDE und Zertifikatssic"},{"k":"scenario","v":"all","l":"Vollständige Härtung (alles der Reihe nach)","m":"Einsteiger hardening härtung vollständige sicherheit full hardening cis benchmark anwenden STIG Neu installierte oder nie gehärtete Instanz: alle Bereiche werden in sicherer Reihenfolge umgesetzt; SQL Server wird ein- bis zweimal neu gestartet (Wartungsfenster nötig)."},{"k":"scenario","v":"net","l":"Netzwerk und Verbindungen: Wer darf sich von wo verbinden? (Listen-Adresse, Firewall, Browser)","m":"Netzwerk und Identität 1433 1434 SQL Browser firewall ListenOnAllIPs network.ipaddress Error 53 Error 40 TCP/IP Error 26 Error locating Server/Instance Specified Login timeout expired A network-related or instance-specific error zugriff von außen zugriff einschränken SQL Server lauscht auf allen Schnittstellen, 1433 ist für alle offen, SQL Browser läuft ohne Bedarf. Eine Änderung der Listen-Adress"},{"k":"scenario","v":"tls","l":"Verbindungen verschlüsseln (Force Encryption, Zertifikat, Prüfung auf dem Client)","m":"Netzwerk und Identität Force Encryption TrustServerCertificate Encrypt=Mandatory certificate chain was issued by an authority that is not trusted SSL Provider error 0x80090325 tlscert zertifikatsablauf TDS 8.0 strict The target principal name is incorrect certificate verify failed Encrypt=Strict Force Strict Encryption Unable to load user-specified certificate zertifikatskette certificate chain Ve"},{"k":"scenario","v":"auth","l":"Authentifizierung und Kennwörter (sa, CHECK_POLICY, Windows-Authentifizierung)","m":"Netzwerk und Identität sa CHECK_POLICY CHECK_EXPIRATION mixed mode Login failed for user Error 18456 Error 18486 account is locked out kennwortrichtlinie password policy PWDCOMPARE Error 18452 Login is from an untrusted domain Error 18470 The account is disabled Msg 15118 Msg 33062 sa_disabled Windows Authentication sa aktiviert, keine Kennwortrichtlinie für SQL-Logins, leere/schwache Kennwörter; "},{"k":"scenario","v":"priv","l":"Minimale Berechtigungen und Angriffsfläche (Rollen, public, guest, xp_cmdshell)","m":"Berechtigungen und Audit-Trail db_owner sysadmin public guest xp_cmdshell TRUSTWORTHY CONTROL SERVER least privilege minimale berechtigungen Msg 262 Msg 229 permission was denied Msg 15247 Msg 916 db_ddladmin EXECUTE AS berechtigungsbericht Anwendungsbenutzer ist db_owner oder sysadmin, zusätzliche Berechtigungen für public/guest, xp_cmdshell/TRUSTWORTHY aktiviert; Rollendesign und Berechtigungsbe"},{"k":"scenario","v":"audit","l":"Audit: Wer hat was getan? (Server Audit, Database Audit Specification)","m":"Berechtigungen und Audit-Trail SQL Server Audit server audit audit specification fn_get_audit_file C2 audit login auditing audit-trail SIEM DSGVO PCI sqlaudit Server Audit Specification Database Audit Specification Msg 33072 wer hat was getan Der Prüfer verlangt Aufzeichnungen über Anmeldungen, Berechtigungs- und Schemaänderungen; Server Audit in eine Datei (oder das Windows-Ereignisprotokoll), We"},{"k":"scenario","v":"tde","l":"Daten auf der Disk verschlüsseln (TDE)","m":"Daten und Sicherungen TDE transparent data encryption database encryption key DEK service master key Msg 33111 Cannot find server certificate with thumbprint encryption at rest Msg 15208 ENCRYPTION_IN_PROGRESS percent_complete Msg 15581 zertifikatssicherung TDE gegen das Öffnen von Disk, Datendatei oder Sicherung auf einem anderen Server: DMK, Zertifikat, DEK. Erfolgt online; die Zertifikatssicher"},{"k":"scenario","v":"backup","l":"Sicherheit der Sicherungen (verschlüsselte Sicherung, Zertifikat, Berechtigungen)","m":"Daten und Sicherungen backup encryption sicherungsverschlüsselung WITH ENCRYPTION BACKUP CERTIFICATE sicherungsberechtigungen Msg 33111 Msg 15151 RESTORE VERIFYONLY key_algorithm Msg 15240 Sicherungen sind unverschlüsselt, das Sicherungsverzeichnis ist für alle offen, Kennwörter stehen im Klartext in Skripten; BACKUP … WITH ENCRYPTION, Zertifikatssicherung und Verzeichnisberechtigungen. Keine Down"},{"k":"scenario","v":"os","l":"Betriebssystemseite (Dienstkonto, Dateiberechtigungen, SELinux)","m":"Server und Version service account sql server dienstkonto LocalSystem gMSA virtual account icacls SELinux mssql-benutzer NT Service\\MSSQLSERVER mssql-server-selinux unconfined_service_t Configuration Manager Log On Der Dienst läuft als LocalSystem oder mit einem Administratorkonto, Daten-/Sicherungsverzeichnisse sind für alle offen, SELinux ist deaktiviert; ein Wechsel des Dienstkontos erfordert e"},{"k":"scenario","v":"patch","l":"Patches und Version: Bin ich auf einer unterstützten Version und dem neuesten CU?","m":"Server und Version CU cumulative update GDR EOL end of support support beendet CVE sicherheitsupdate ProductUpdateLevel ProductUpdateReference Latest updates patch-zeitplan Version, CU-Stand und Supportende (2016 endete im Juli 2026); verweist zum Einspielen auf den CU-Patch-Guide. Schreibgeschützt."}]},{"id":"mongo-replset","file":"install/guides/mongo-replset.json","group":"MongoDB","engineId":"mongodb","category":"ha","title":"MongoDB-Replica-Set installieren (3 Mitglieder, Keyfile und Autorisierung)","summary":"Baut ein MongoDB-8.0- (oder 7.0-)Replica-Set mit drei Mitgliedern von Grund auf: offizielles Repository, XFS-Datenverzeichnis, WiredTiger-Cache passend zum RAM, Keyfile-Authentifizierung zwischen den Mitgliedern, Autoris","level":"Mittel","duration":"60–90 min","topology":"3 Server (1 Primary + 2 Secondaries), ungerade Anzahl datentragender Mitglieder","tags":["mongodb","mongo","replica set","replset","rs.initiate","keyfile","failover","stepdown","write concern","wiredtiger","hochverfügbarkeit","ha","rhel","ubuntu"],"order":1,"steps":36,"scenarios":[]},{"id":"mongo-sharded","file":"install/guides/mongo-sharded.json","group":"MongoDB","engineId":"mongodb","category":"ha","title":"Installation eines MongoDB Sharded Cluster (Config Server + Shards + mongos)","summary":"Ein Sharded Cluster für den Fall, dass Datenmenge oder Schreiblast ein einzelnes Replica Set übersteigt: ein Config-Server-Replica-Set mit drei Mitgliedern, zwei oder drei Shard-Replica-Sets und zwei mongos-Router; inter","level":"Fortgeschritten","duration":"2–3 Stunden","topology":"3 Config Server + 2×3 Shard-Mitglieder + 2 mongos (mindestens 11 Server; für Tests weniger)","tags":["mongodb","sharding","sharded cluster","mongos","config server","shard key","addShard","balancer","hashed","skalierung","rhel","ubuntu"],"order":2,"steps":60,"scenarios":[]},{"id":"mongo-upgrade","file":"install/guides/mongo-upgrade.json","group":"MongoDB","engineId":"mongodb","category":"upgrade","title":"MongoDB Rolling Upgrade (7.0 → 8.0): Replica Set und Sharded Cluster","summary":"Aktualisiert ein laufendes MongoDB-7.0-Replica-Set oder einen Sharded Cluster unterbrechungsfrei (rolling) auf 8.0: Vorprüfungen (Version, FCV, Mitgliederstatus, Treiberkompatibilität), Backup, zuerst die Secondaries, da","level":"Fortgeschritten","duration":"60–120 min (unabhängig von der Datenmenge; einige Minuten pro Mitglied)","topology":"Replica Set (3+ Mitglieder) oder Sharded Cluster (Config Server + Shards + mongos)","tags":["mongodb","upgrade","Versionsupgrade","rolling upgrade","8.0","7.0","featureCompatibilityVersion","fcv","stepdown","replica set","sharded","balancer","config server","mongos","fcv","featureCompatibilityVersion","rolling upgrade"],"order":3,"steps":30,"scenarios":[]},{"id":"mongo-backup","file":"install/guides/mongo-backup.json","group":"MongoDB","engineId":"mongodb","category":"backup","title":"MongoDB-Backup und -Restore (mongodump, mongorestore, Snapshot)","summary":"Richtet für ein installiertes MongoDB eine Backup-Routine ein und beschreibt die Wiederherstellung aus dem Backup Szenario für Szenario. Backup: mongodump einer einzelnen Collection, einer Datenbank oder des gesamten Dep","level":"Mittel","duration":"45–90 Min.","topology":"Backup-Server oder ein Member + Replica Set / Einzelinstanz / Sharded Cluster (mongos)","tags":["mongodb","mongo","Sicherung","backup","restore","Wiederherstellung","mongodump","mongorestore","oplog","oplogReplay","nsFrom","nsTo","mongoexport","mongoimport","fsyncLock","lvm snapshot","systemd timer","cron","Aufbewahrung","balancer","activeWindow","config server","cron","systemd timer","flock","oplog","Restore-Szenarien","point in time recovery","pitr","oplogLimit","nsInclude","dryRun","sharded restore","Klon","gelöschte Collection","versehentlich gelöscht"],"order":15,"steps":20,"scenarios":[{"k":"scenario","v":"setup","l":"Backup einrichten: mongodump, Zeitplanung und Test-Restore","m":"Backup mongodump Backup einrichten backup setup systemd timer cron Aufbewahrung mongorestore backup user backup.yaml Failed: can't create session oplog overflow mongodump was unable to capture all new oplog entries Datensicherung Backup-Benutzer, Credential-Datei, manueller und zeitgesteuerter mongodump, Test-Restore unter neuem Namen; optional Snapshot und mongoexport. Wenn du das zum ersten Mal "},{"k":"scenario","v":"r_dry","l":"Lässt sich das Backup wirklich wiederherstellen? (--dryRun und Probe auf einer temporären Instanz)","m":"Probe und Prüfung dryRun --dryRun restore test Restore-Probe Backup prüfen backup verify gzip: invalid compressed data unexpected EOF archive is corrupt beschädigtes Archiv gzip -t lässt sich das Backup einspielen Ohne Live-Daten anzufassen: Integrität des Archivs, enthaltene Namespaces und Oplog, optional ein vollständiger Restore in einen temporären mongod mit Dokumentzählung."},{"k":"scenario","v":"r_full","l":"Alle Daten aus dem Backup wiederherstellen (vollständiger Restore, --drop)","m":"Restore full restore vollständiger Restore vollständige Wiederherstellung --drop mongorestore --drop Datenverlust data loss ransomware Erpressung alle Daten gelöscht dropDatabase no oplog file to replay oplogReplay applyOps not authorized … applyOps admin.tempusers Daten sind weitgehend verloren oder beschädigt, und das Deployment kehrt auf den Zeitpunkt des Backups zurück. Schreibvorgänge nach de"},{"k":"scenario","v":"r_ns","l":"Einzelne Datenbank oder Collection zurückholen (--nsInclude, unter neuem Namen oder an Ort und Stelle)","m":"Restore --nsInclude --nsFrom --nsTo einzelne Collection single collection restore Collection gelöscht collection dropped drop collection gelöschte Dokumente deleted documents deleteMany namespace restore E11000 duplicate key error $merge orders_restored einzelne Datenbank wiederherstellen Eine Collection/Datenbank wurde gelöscht oder beschädigt, der Rest ist intakt: Nur dieser Namespace kommt aus "},{"k":"scenario","v":"r_pitr","l":"Zu einem bestimmten Zeitpunkt zurückkehren: Backup + Oplog (versehentliches Drop, fehlerhaftes Update/Delete)","m":"Point-in-Time-Recovery point in time PITR Zeitpunkt-Wiederherstellung --oplogReplay --oplogLimit oplog replay oplog.rs versehentlich gelöscht accidental drop fehlerhaftes Update wrong update vor dem Drop Timestamp Oplog überschrieben rollback to time auf bestimmte Uhrzeit zurück Die fehlerhafte Operation geschah nach dem Backup, und die Schreibvorgänge bis zu diesem Zeitpunkt werden ebenfalls gebr"},{"k":"scenario","v":"r_other","l":"Backup in einen anderen Cluster / auf einen anderen Server einspielen (Klon, Testumgebung, Migration)","m":"Migration und Klon clone Klon Restore auf anderen Server restore to another cluster migration Servermigration Testumgebung refresh nsExclude admin.system.users Invalid feature compatibility version cannot use --oplogReplay with excludes specified Kopie in Testumgebung Das Quell-Deployment bleibt unberührt: Das Backup wird in ein anderes Replica Set oder eine andere Instanz eingespielt (Test-/Repor"},{"k":"scenario","v":"r_shard","l":"Sharded Cluster wiederherstellen: Grenzen von mongodump und die richtige Methode","m":"Migration und Klon sharded restore sharded cluster restore mongos restore shard key shardCollection config server restore balancer inconsistent inkonsistentes Backup PBM Percona Backup Ops Manager --oplog mode only supported on full dumps Backup über mongos Die gesicherte Topologie ist ein Sharded Cluster: logischer Restore über mongos (Shard Keys neu anlegen, Balancer) oder physischer Wiederaufba"}]},{"id":"mongo-ops","file":"install/guides/mongo-ops.json","group":"MongoDB","engineId":"mongodb","category":"ops","title":"MongoDB-Betrieb im Alltag und Störungsszenarien (Replica Set und Sharded Cluster)","summary":"Keine Installation: ein szenariobasiertes Runbook, um ein installiertes MongoDB Replica Set und einen Sharded Cluster im Alltag zu betreiben und nach einem Ausfall wiederherzustellen. Health Check, Mitglieder hinzufügen/","level":"Fortgeschritten","duration":"5 min (Prüfung) – mehrere Stunden (Initial Sync, Drain, Reshard)","topology":"Replica Set (3+ Mitglieder) oder Sharded Cluster (Config Server + Shard-Replica-Sets + mongos)","tags":["mongodb","mongo","replica set","sharded cluster","rs.status","rs.add","rs.remove","rs.reconfig","force reconfig","stepDown","priority","hidden","delayed","secondaryDelaySecs","arbiter","resync","initial sync","too stale","oplog","replSetResizeOplog","FCV","setFeatureCompatibilityVersion","addShard","removeShard","movePrimary","balancer","activeWindow","jumbo","chunk","reshardCollection","shard key","Betrieb","störung"],"order":20,"steps":3,"scenarios":[{"k":"scenario","v":"health","l":"Health Check: Mitglieder, Verzögerung, Oplog-Fenster (bei Sharding Shards und Balancer)","m":"Überwachung und Prüfung rs.status rs.status() replication lag Verzögerung replLag printSecondaryReplicationInfo sh.status health Zustandsprüfung NotYetInitialized RECOVERING STARTUP2 UNKNOWN (not reachable/healthy) DOWN ReplicaSetNoPrimary Server selection timed out lastHeartbeatMessage Nur lesend: Mitgliederstatus, Replikationsverzögerung, Oplog-Fenster, Konfiguration; im Sharded Cluster Shard-Li"},{"k":"scenario","v":"add","l":"Neues Mitglied zum Replica Set hinzufügen (Initial Sync)","m":"Mitgliederverwaltung rs.add Mitglied hinzufügen add member initial sync neuer Secondary newlyAdded STARTUP2 scale out rs.add() Quorum check failed Found two member configurations with same host field votes: 0 Ein neuer Server ist aufgebaut (gleiche Version, Keyfile, replSetName); er wird mit priority 0 / votes 0 hinzugefügt und erhält Stimme und Priority, sobald der Initial Sync abgeschlossen und "},{"k":"scenario","v":"remove","l":"Mitglied aus dem Replica Set entfernen","m":"Mitgliederverwaltung rs.remove Mitglied entfernen remove member decommission Server entfernen REMOVED Our replica set config is invalid or we are not a member of it rs.remove() Der Server wird stillgelegt oder ist dauerhaft verloren: Das Mitglied wird aus der Konfiguration entfernt und es wird geprüft, dass die verbleibenden Stimmen weiterhin eine Mehrheit bilden. Ist es der Primary, zuerst stepDo"},{"k":"scenario","v":"member_cfg","l":"Rolle eines Mitglieds ändern: Priority-, Hidden-, Delayed-Mitglied","m":"Mitgliederverwaltung priority hidden delayed secondaryDelaySecs slaveDelay verstecktes Mitglied verzögertes Mitglied votes rs.reconfig priority must be 0 when hidden=true only one voting member can be added or removed at a time rs.reconfig() Die Priorität eines Mitglieds für die Primary-Rolle ändern, es zum versteckten (hidden) Mitglied für Reporting/Backup oder zum verzögerten (delayed) Mitglied "},{"k":"scenario","v":"arbiter","l":"Arbiter durch datentragendes Mitglied ersetzen (PSA → PSS)","m":"Mitgliederverwaltung arbiter arbiterOnly PSA rs.addArb majority write concern cache pressure w: majority wartet Rejecting reconfig where the new config has a PSA topology implicit default write concern majority commit point Replica Set mit Primary + Secondary + Arbiter: Fällt ein Datenmitglied aus, werden Majority-Schreibvorgänge und Cache-Druck zum Problem. Ein neues Datenmitglied wird hinzugefüg"},{"k":"scenario","v":"resync","l":"Zurückgefallenes oder beschädigtes Mitglied neu synchronisieren (Resync)","m":"Ausfall und Recovery too stale too stale to catch up RS102 resync neu synchronisieren initial sync RECOVERING WiredTiger error WT_PANIC beschädigte Datendatei data corruption OplogStartMissing maintenance mode InitialSyncFailure fassert checksum mismatch Das Mitglied hängt in RECOVERING und meldet 'too stale to catch up', oder seine Datendateien sind beschädigt: Das Datenverzeichnis des Mitglieds "},{"k":"scenario","v":"force","l":"Mehrheit verloren: mit den verbliebenen Mitgliedern erzwungen neu konfigurieren (Force Reconfig)","m":"Ausfall und Recovery force reconfig force: true no primary kein Primary Mehrheitsverlust majority lost NotWritablePrimary not master no replset config split brain DR site Standortverlust ReplicaSetNoPrimary Server selection timed out rs.reconfig force Die meisten Mitglieder sind dauerhaft verloren (Standort-/Serververlust), die verbliebenen können keinen Primary wählen: rs.reconfig(cfg, {force: tr"},{"k":"scenario","v":"stepdown","l":"Geplanter Primary-Wechsel (rs.stepDown, bestimmtes Mitglied zum Primary machen)","m":"Wartung stepDown rs.stepDown replSetStepDown primary wechseln switchover wartung maintenance rs.freeze No electable secondaries caught up InterruptedDueToReplStateChange PrimarySteppedDown Primary-Wechsel Auf dem Primary-Server stehen Wartung/Patches an: Der Primary tritt zurück, ein aufgeholter Secondary wird Primary. Die Anwendung kann einige Sekunden lang Schreibfehler erhalten."},{"k":"scenario","v":"oplog","l":"Oplog-Größe ändern (replSetResizeOplog)","m":"Wartung oplog oplog window Oplog-Fenster replSetResizeOplog oplogSizeMB oplogMinRetentionHours too stale oplog overflow rs.printReplicationInfo getReplicationInfo log length start to end Das Oplog-Fenster ist zu kurz: Secondaries werden nach Wartungen 'too stale', das Backup kann das Oplog nicht erfassen oder es reicht nicht für eine Point-in-Time-Recovery. Es wird auf jedem Mitglied ohne Downtime"},{"k":"scenario","v":"fcv","l":"Feature Compatibility Version (FCV) prüfen und abschließen","m":"Wartung FCV featureCompatibilityVersion setFeatureCompatibilityVersion Invalid feature compatibility version IncompatibleServerVersion nach dem Upgrade UPGRADE PROBLEM requires confirm: true getParameter featureCompatibilityVersion Nach dem Upgrade steht die FCV noch auf der alten Version oder die Mitglieder laufen mit unterschiedlichen Versionen: Binary-Version und FCV aller Mitglieder werden gep"},{"k":"scenario","v":"addshard","l":"Shard zum Cluster hinzufügen (sh.addShard)","m":"Sharded cluster addShard sh.addShard Shard hinzufügen add shard scale out neuer Shard shardAdded is not running with --shardsvr in use by another shard Die Kapazität reicht nicht mehr: Ein neues Shard-Replica-Set (shardsvr) ist aufgebaut und wird zum Cluster hinzugefügt; der Balancer verschiebt die Chunks schrittweise."},{"k":"scenario","v":"rmshard","l":"Shard entfernen (removeShard: Drain und movePrimary)","m":"Sharded cluster removeShard remove shard Shard entfernen drain draining movePrimary dbsToMove jumboChunks moveCollection collectionsToMove ShardNotFound draining started successfully Ein Shard wird stillgelegt: Seine Chunks werden auf die anderen Shards verlagert (Drain), die Datenbanken, deren Primary Shard er ist, werden verschoben (movePrimary), danach wird er aus dem Cluster entfernt."},{"k":"scenario","v":"balancer","l":"Balancer: Zeitfenster, stoppen / starten, pro Collection deaktivieren","m":"Sharded cluster balancer activeWindow stopBalancer startBalancer sh.stopBalancer disableBalancing Balancer-Fenster chunk migration moveChunk isBalancerRunning getBalancerState Balancer timed out Chunk-Migrationen erzeugen Last zur Hauptlastzeit, Migrationen müssen für Wartung/Backup pausieren oder eine einzelne Collection soll nicht ausgeglichen werden."},{"k":"scenario","v":"chunks","l":"Ungleiche Verteilung oder Jumbo Chunks (Diagnose und Lösung)","m":"Sharded cluster jumbo jumbo chunk chunk too big ChunkTooBig unausgeglichen imbalance hot shard balancerCollectionStatus clearJumboFlag moveChunk failed getShardDistribution low cardinality splitFind Cannot split on the boundary jumboChunks Ein Shard ist deutlich voller als die anderen, der Balancer verschiebt nicht, es gibt als 'jumbo' markierte Chunks oder moveChunk schlägt fehl: Verteilung, Jumb"},{"k":"scenario","v":"reshard","l":"Shard Key ändern (reshardCollection)","m":"Sharded cluster reshardCollection reshard Shard Key ändern change shard key refineCollectionShardKey abortReshardCollection commitReshardCollection forceRedistribution ReshardCollectionInProgress ReshardCollectionAborted unshardCollection Der aktuelle Shard Key erzeugt einen Hot Shard, Jumbo Chunks oder Scatter-Gather-Abfragen: Die Collection wird online mit einem neuen Shard Key neu verteilt (5.0"},{"k":"mcfg","v":"priority","l":"Priority ändern (Vorrang bei der Primary-Wahl)","m":"Ändert den Vorrang des Mitglieds bei der Primary-Wahl (z. B. das Mitglied im Hauptrechenzentrum bevorzugen, das DR-Mitglied auf 0 setzen). Eine Wahl kann stattfinden; eine kurze Schreibunterbrechung ist möglich."},{"k":"mcfg","v":"hidden","l":"Verstecktes (hidden) Mitglied: für Anwendungen unsichtbar, für Backup/Reporting","m":"Das Mitglied ist für Anwendungen unsichtbar (es werden keine Lesezugriffe dorthin geleitet); es ist für Reporting, Analytics oder Backup reserviert. Es erhält priority 0 und kann nicht Primary werden."},{"k":"mcfg","v":"delayed","l":"Verzögertes (delayed) Mitglied: Schutz vor versehentlichem Löschen","m":"Das Mitglied wendet das Oplog mit der angegebenen Verzögerung an; versehentlich gelöschte Daten bleiben so lange auf ihm erhalten. Es wird hidden, priority 0 und votes 0."},{"k":"mcfg","v":"normal","l":"Normalisieren (priority 1, sichtbar, ohne Verzögerung, stimmberechtigt)","m":"Macht die vorherige Rolle rückgängig: priority 1, sichtbar, ohne Verzögerung, stimmberechtigt."},{"k":"bal_op","v":"window","l":"Zeitfenster festlegen (activeWindow)","m":"Migrationen laufen nur zu bestimmten Zeiten (z. B. nachts); tagsüber entsteht keine Last."},{"k":"bal_op","v":"unwindow","l":"Zeitfenster entfernen (jederzeit ausführen)","m":"Das Zeitfenster wird entfernt; der Balancer migriert bei Bedarf zu jeder Uhrzeit (z. B. für einen schnellen Ausgleich nach dem Hinzufügen eines Shards)."},{"k":"bal_op","v":"stop","l":"Balancer stoppen (Wartung/Backup)","m":"Vor Backup, Upgrade oder Wartung: Die laufende Migration wird abgeschlossen, eine neue startet nicht. Starte ihn nach Abschluss der Arbeiten unbedingt wieder."},{"k":"bal_op","v":"start","l":"Balancer starten","m":"Aktiviert einen gestoppten Balancer; der Ausgleich wird dort fortgesetzt, wo er aufgehört hat."},{"k":"bal_op","v":"coll_off","l":"Balancing für eine einzelne Collection deaktivieren","m":"Nur die Chunks einer Collection werden nicht migriert (bewusste Platzierung, problematische Collection). Die übrigen werden weiter ausgeglichen."},{"k":"bal_op","v":"coll_on","l":"Balancing für eine einzelne Collection aktivieren","m":"Aktiviert das für die Collection deaktivierte Balancing wieder."}]},{"id":"mongo-security","file":"install/guides/mongo-security.json","group":"MongoDB","engineId":"mongodb","category":"security","title":"MongoDB-Sicherheitshärtung (Autorisierung, TLS, Rollen, Audit)","summary":"Szenariobasiertes Runbook, um ein laufendes MongoDB (Einzelserver, Replica Set oder Sharded Cluster) ohne Ausfallzeit zu härten: schreibgeschütztes Sicherheitsaudit, rollierende Aktivierung der Autorisierung mit keyFile ","level":"Fortgeschritten","duration":"10 Min. (Audit) – 2 Std. (vollständige Härtung)","topology":"Einzelserver, Replica Set mit 3+ Mitgliedern oder Sharded Cluster","tags":["mongodb sicherheit","mongodb security","hardening","härtung","CIS","authorization","keyFile","transitionToAuth","TLS","requireTLS","x.509","SCRAM-SHA-256","custom role","least privilege","bindIp","audit log","auditLog","encryption at rest","LUKS","Percona"],"order":20,"steps":8,"scenarios":[{"k":"op","v":"audit","l":"Sicherheitsstatus prüfen (schreibgeschützt)","m":"Audit sicherheitsaudit security audit security checklist CIS MongoDB hardening härtung Access control is not enabled for the database auth deaktiviert bindIp 0.0.0.0 exposed mongodb mongodb aus dem internet erreichbar ransom ransomware getCmdLineOpts security checklist MongoDB auditbericht Ändert nichts. Zugriff ohne Authentifizierung, lauschende Adressen, TLS, Authentifizierungsmechanismen, hochp"},{"k":"op","v":"all","l":"Vollständige Härtung (alles nacheinander)","m":"Audit vollständige härtung full hardening CIS benchmark sicherheitsprojekt pentest befund audit befunde Audit → interne Authentifizierung + Autorisierung (ohne Ausfallzeit mit transitionToAuth) → Benutzer mit minimalen Rechten → SCRAM-SHA-256 → bindIp/Firewall → TLS (allowTLS → preferTLS → requireTLS) → Audit-Log (Enterprise/Percona) → abschließendes Audit. Im Replica Set rollierend; jeder riskant"},{"k":"op","v":"auth","l":"Autorisierung deaktiviert: Autorisierung aktivieren (keyFile, ohne Ausfallzeit)","m":"Identität und Zugriff Access control is not enabled for the database Read and write access to data and configuration is unrestricted authorization disabled authorization: enabled auth deaktiviert no authentication transitionToAuth keyFile internal authentication interne mitgliedsauthentifizierung zugriff ohne authentifizierung permissions on /etc/mongod.keyfile are too open Authentication failed _"},{"k":"op","v":"users","l":"Benutzer mit minimalen Rechten und Custom Roles (Anwendung, Backup, Monitoring)","m":"Identität und Zugriff least privilege minimale rechte custom role createRole root benutzer anwendung verbindet sich als root not authorized on Unauthorized command find requires authentication requires authentication readWrite clusterMonitor backup rolle createUser grantRolesToUser monitor_user backup_user Anwendungen verbinden sich mit root oder einem gemeinsamen Admin-Konto. Es werden eine Custo"},{"k":"op","v":"scram","l":"SCRAM-SHA-1 deaktivieren, nur SCRAM-SHA-256","m":"Identität und Zugriff SCRAM-SHA-1 SCRAM-SHA-256 authenticationMechanisms Received authentication for mechanism SCRAM-SHA-1 which is not enabled Unable to use SCRAM-SHA-256 based authentication for user without any SCRAM-SHA-256 credentials registered MD5 mechanism Die Anmeldedaten älterer Benutzer sind mit SHA-1 gespeichert, oder das Audit hat SCRAM-SHA-1 als aktiv gemeldet. Die Benutzer werden au"},{"k":"op","v":"bind","l":"Netzwerkzugriff einschränken (bindIp 0.0.0.0, Firewall)","m":"Netzwerk und Verschlüsselung bindIp 0.0.0.0 bindIpAll bind_ip_all 0.0.0.0:27017 port 27017 offen shodan aus dem internet erreichbar firewall javascriptEnabled noscripting net.bindIp ufw allow 27017 firewall-cmd 27017 mongod lauscht auf allen Schnittstellen (0.0.0.0, `bindIpAll`), oder der Port ist für alle offen. bindIp wird auf localhost + die interne IP des Servers reduziert, die Firewall lässt "},{"k":"op","v":"tls","l":"Verbindungen verschlüsseln: TLS (allowTLS → preferTLS → requireTLS)","m":"Netzwerk und Verschlüsselung TLS SSL net.tls.mode requireTLS preferTLS allowTLS unverschlüsselte verbindung cleartext no SSL certificate provided by peer SSL peer certificate validation failed certificate verify failed x.509 x509 clusterAuthMode Hostname/IP does not match certificate's altnames self-signed certificate in certificate chain rotateCertificates allowConnectionsWithoutCertificates Der "},{"k":"op","v":"auditlog","l":"Audit-Log aktivieren (Enterprise / Percona)","m":"Nachvollziehbarkeit und Datenschutz audit log auditLog audit trail wer hat gelöscht who dropped Unrecognized option: auditLog unrecognised option '--auditDestination' DSGVO PCI DSS protokollierung Wer sich wann angemeldet hat, Benutzer-/Rollenänderungen und Drop-Operationen müssen protokolliert werden. In MongoDB Enterprise und Percona Server for MongoDB wird das mit `auditLog` aktiviert; Communit"},{"k":"op","v":"ear","l":"Encryption at Rest (Daten auf der Festplatte verschlüsseln)","m":"Nachvollziehbarkeit und Datenschutz encryption at rest festplattenverschlüsselung enableEncryption encryptionKeyFile KMIP Vault LUKS dm-crypt datenverschlüsselung Unrecognized option: security.enableEncryption Unable to retrieve key Unable to use encryption at rest cryptsetup luksFormat Wird eine Festplatte oder ein Backup gestohlen, sollen die Daten nicht lesbar sein. Auf Enterprise/Percona wird "}]},{"id":"redis-sentinel","file":"install/guides/redis-sentinel.json","group":"Redis","engineId":"redis","category":"ha","title":"Hochverfügbarkeit mit Redis Sentinel (Master + Replicas + 3 Sentinels)","summary":"Richtet von Grund auf eine hochverfügbare Installation von Redis 7.2 / 8.2 oder Valkey auf drei Servern ein: ein Master, zwei Replicas und auf jedem Server ein Sentinel. Schritt für Schritt werden maxmemory nach RAM, Per","level":"Mittel","duration":"45–60 Min.","topology":"3 Server: auf jedem Redis (1 Master + 2 Replicas) und Sentinel","tags":["redis","valkey","sentinel","replica","replication","failover","quorum","hochverfügbarkeit","ha","cache","rhel","ubuntu"],"order":1,"steps":27,"scenarios":[]},{"id":"redis-cluster","file":"install/guides/redis-cluster.json","group":"Redis","engineId":"redis","category":"ha","title":"Redis-Cluster-Installation (3 Master + 3 Replicas)","summary":"Richtet von Grund auf einen Redis Cluster ein (Redis 7.2 / 8.2 oder Valkey): Die 16384 Hash-Slots werden auf drei (oder mehr) Master verteilt, jeder Master hat eine Replica auf einem anderen Server. Schritt für Schritt w","level":"Mittel","duration":"45–60 Min.","topology":"6 Instanzen: 6 Server × 1 oder 3 Server × 2","tags":["redis","valkey","cluster","redis cluster","sharding","hash slot","reshard","failover","replica","skalierung","rhel","ubuntu"],"order":2,"steps":33,"scenarios":[]},{"id":"redis-backup","file":"install/guides/redis-backup.json","group":"Redis","engineId":"redis","category":"backup","title":"Redis-Backup und -Restore (RDB, AOF, Replica und Cluster)","summary":"Richtet für ein installiertes Redis oder Valkey (Einzelinstanz, Master/Replica mit Sentinel oder Redis Cluster) eine Backup-Routine ein und beschreibt die Wiederherstellung aus dem Backup. Schritt für Schritt: RDB-Backup","level":"Mittel","duration":"30–60 Min.","topology":"Einzelinstanz, Replica oder Redis-Cluster-Server (Redis 7.2 / 8.x, Valkey)","tags":["redis","valkey","sicherung","backup","restore","wiederherstellung","rdb","dump.rdb","bgsave","lastsave","aof","appendonly","bgrewriteaof","appendonlydir","redis cluster","systemd timer","cron","aufbewahrung","cron","systemd timer","flock","REDISCLI_AUTH","sentinel"],"order":15,"steps":19,"scenarios":[]},{"id":"redis-ops","file":"install/guides/redis-ops.json","group":"Redis","engineId":"redis","category":"ops","title":"Redis / Valkey: täglicher Betrieb und Störungsszenarien (Cluster und Sentinel)","summary":"Keine Installation: ein szenariobasiertes Runbook, um einen installierten Redis Cluster oder einen von Sentinel verwalteten Master/Replica-Aufbau im Alltag zu betreiben und bei Störungen wiederherzustellen (Redis 7.2, Re","level":"Fortgeschritten","duration":"5 Min. (Prüfung) – 1 Std. (Reshard)","topology":"Redis Cluster (3+ Master + Replicas) oder Sentinel (1 Master + Replicas + 3 Sentinels)","tags":["redis","valkey","redis cluster","sentinel","redis-cli --cluster","cluster check","add-node","del-node","reshard","rebalance","CLUSTER FAILOVER","FORCE","TAKEOVER","CLUSTER FORGET","cluster fix","SENTINEL FAILOVER","SENTINEL RESET","quorum","CKQUORUM","INFO memory","MEMORY DOCTOR","eviction","maxmemory","bigkeys","memkeys","hotkeys","Betrieb","Störung"],"order":20,"steps":3,"scenarios":[{"k":"scenario","v":"c_health","l":"Cluster-Health-Check (--cluster check, CLUSTER INFO / NODES)","m":"Redis Cluster: Überwachung cluster_state:fail CLUSTERDOWN CLUSTERDOWN Hash slot not served --cluster check CLUSTER INFO CLUSTER NODES fail? pfail slots not covered Not all 16384 slots are covered [ERR] Nodes don't agree about configuration MOVED ASK CLUSTERDOWN The cluster is down cluster_slots_fail Cluster-Zustand cluster health cluster_state Nur lesend: Sind alle Slots abgedeckt, stimmen die Nod"},{"k":"scenario","v":"c_add","l":"Node zum Cluster hinzufügen (Master oder Replica)","m":"Redis Cluster: Node-Verwaltung add-node --cluster add-node Node hinzufügen Node hinzufügen scale out --cluster-slave --cluster-replica cluster-master-id neuer Master neue Replica Node is not empty. Either the node already knows other nodes New node added correctly Kapazität erhöhen Eine neue Instanz (cluster-enabled yes, leer) ist bereit: Sie tritt dem Cluster als Master bei und erhält Slots oder "},{"k":"scenario","v":"c_remove","l":"Node aus dem Cluster entfernen (Slots leeren + del-node)","m":"Redis Cluster: Node-Verwaltung del-node --cluster del-node Node entfernen Node entfernen scale in Slots leeren cluster-weight Node … is not empty is not empty! Reshard data away and try again CLUSTER RESET SOFT Node außer Betrieb nehmen Ein Master oder Replica wird außer Betrieb genommen: Die Slots des Masters werden auf die anderen Master verschoben, danach wird der Node aus dem Cluster entfernt."},{"k":"scenario","v":"c_reshard","l":"Slots neu verteilen (Rebalance / Reshard)","m":"Redis Cluster: Node-Verwaltung rebalance reshard --cluster rebalance --cluster reshard Slot-Verteilung ungleich verteilt imbalance use-empty-masters MIGRATE importing migrating BUSYKEY IOERR error or timeout reading to target instance has slots in importing state has slots in migrating state Calling MIGRATE No rebalancing needed Slots verschieben Slots oder Speicher sind zwischen den Mastern ungle"},{"k":"scenario","v":"c_failover","l":"Manueller Failover: Replica zum Master machen (CLUSTER FAILOVER, FORCE, TAKEOVER)","m":"Redis Cluster: Störungen und Wartung CLUSTER FAILOVER failover FORCE TAKEOVER manueller Failover Master-Wartung Replica zum Master machen Manual failover timed out ERR Master is down or failed, please use CLUSTER FAILOVER FORCE ERR You should send CLUSTER FAILOVER to a replica Master wechseln Auf dem Server des Masters steht Wartung an (geplant, kein Datenverlust), oder der Master ist nicht erreic"},{"k":"scenario","v":"c_replace","l":"Ausgefallenen Master / Node ersetzen (FORGET, neue Replica, --cluster fix)","m":"Redis Cluster: Störungen und Wartung CLUSTER FORGET --cluster fix Node verloren failed node fail noaddr slots not covered CLUSTERDOWN replace node ausgefallener Master handshake ERR Can't forget my master! CLUSTERDOWN Hash slot not served Server verloren Plattenausfall Der Server eines Node ist dauerhaft verloren: Ist seine Replica Master geworden, wird der alte Node vergessen und an seiner Stelle"},{"k":"scenario","v":"s_health","l":"Sentinel-Status: Master, Replicas, Sentinels, Quorum","m":"Sentinel SENTINEL MASTERS SENTINEL master get-master-addr-by-name CKQUORUM +sdown +odown -odown +switch-master NOQUORUM NOGOODSLAVE Sentinel-Status +tilt -failover-abort-not-elected s_down o_down master_link_status:down sentinel health Nur lesend: die Master-Adresse, die die Sentinels sehen, die Replicas, die anderen Sentinels, CKQUORUM und die letzten Failover-Ereignisse. Beginne hier, wenn du in"},{"k":"scenario","v":"s_failover","l":"Manueller Failover mit Sentinel (SENTINEL FAILOVER)","m":"Sentinel SENTINEL FAILOVER sentinel failover switch-master geplanter Failover INPROG NOGOODSLAVE -failover-abort-no-good-slave NOGOODSLAVE No suitable replica to promote Sentinel-Wartung Auf dem Server des Masters steht Wartung an: Sentinel macht die geeignetste Replica zum Master und richtet die übrigen Replicas und die Clients auf den neuen Master aus. Unterbrechung 1–2 s; kein Datenverlust, sol"},{"k":"scenario","v":"s_replace","l":"Node im Sentinel-Aufbau hinzufügen / entfernen / ersetzen (SENTINEL RESET)","m":"Sentinel SENTINEL RESET sentinel reset replicaof neue Replica neuer Sentinel Sentinel hinzufügen Sentinel entfernen alte Replica wird noch angezeigt num-other-sentinels known-replica known-sentinel alter Sentinel wird noch angezeigt Eine Replica oder ein Sentinel wurde dauerhaft entfernt oder neu hinzugefügt: Die neue Replica verbindet sich mit REPLICAOF, veraltete Einträge der Sentinels werden mi"},{"k":"scenario","v":"s_quorum","l":"Quorum und Sentinel-Einstellungen ändern (SENTINEL SET)","m":"Sentinel SENTINEL SET quorum down-after-milliseconds failover-timeout parallel-syncs CKQUORUM NOQUORUM Failover zu spät unnötiger Failover Die Zahl der Sentinels hat sich geändert, oder der Failover löst zu spät/zu früh aus: quorum, down-after-milliseconds, failover-timeout und parallel-syncs werden auf allen Sentinels geändert."},{"k":"scenario","v":"memory","l":"Speicherdruck: Diagnose von Belegung, Fragmentierung, Eviction","m":"Speicher und Keys OOM command not allowed when used memory > 'maxmemory' OOM maxmemory used_memory evicted_keys eviction maxmemory-policy MEMORY DOCTOR mem_fragmentation_ratio Fragmentierung activedefrag client-output-buffer-limit Speicher voll Out of memory: Killed process oom-killer used_memory_rss mem_clients_normal omem used_memory nähert sich maxmemory, Fehler 'OOM command not allowed', Evict"},{"k":"scenario","v":"bigkeys","l":"Große und heiße Keys finden (--bigkeys, --memkeys, --hotkeys)","m":"Speicher und Keys --bigkeys --memkeys --hotkeys big key großer Key hot key häufig abgerufener Key MEMORY USAGE UNLINK slowlog latency SCAN HGETALL KEYS * --keystats Latenzspitzen latency spike Speicher oder CPU eines Node liegen höher als bei den anderen, es gibt Latenzspitzen, DEL dauert lange: Die größten und am häufigsten abgerufenen Keys werden ermittelt und sicher bereinigt (UNLINK)."},{"k":"add_role","v":"master","l":"Master (erhält Slots: nach dem Hinzufügen Rebalance)","m":"Tritt als leerer Master bei und erhält dann per Rebalance Slots. Erhöht die Kapazität; für den neuen Master sollte zusätzlich eine Replica hinzugefügt werden."},{"k":"add_role","v":"replica","l":"Replica (Kopie eines Masters)","m":"Wird Kopie eines Masters und übernimmt, wenn der Master ausfällt. Datenmenge in Höhe des Master-Speichers geht über das Netz (Full Sync)."},{"k":"rs_mode","v":"rebalance","l":"Rebalance: Slots gleichmäßig auf die Master verteilen (einschließlich leerer Master)","m":"Alle Master erhalten einen gleichen Anteil (einschließlich eines neuen Masters ohne Slots). Zuerst wird eine Simulation angezeigt. Der häufigste Vorgang nach dem Hinzufügen eines neuen Masters."},{"k":"rs_mode","v":"move","l":"Reshard: bestimmte Anzahl Slots von einem Master auf einen anderen verschieben","m":"Eine bestimmte Anzahl Slots wird von einem Master auf einen anderen verschoben: um einen heißen/großen Master zu entlasten oder einen Master schrittweise zu leeren."},{"k":"fo_mode","v":"default","l":"Geplant (Standard): gleicht mit dem Master ab und übernimmt, kein Datenverlust","m":"Der Master muss laufen."},{"k":"fo_mode","v":"force","l":"FORCE: Master nicht erreichbar, ohne Abgleich übernehmen","m":"Die letzten Schreibvorgänge des Masters können verloren gehen; die Mehrheit der Master muss trotzdem abstimmen."},{"k":"fo_mode","v":"takeover","l":"TAKEOVER: auch die Mehrheit der Master fehlt, ohne Abstimmung übernehmen","m":"Letzter Ausweg: Risiko von Split-Brain und Epoch-Kollision."},{"k":"srep_op","v":"add_replica","l":"Neue Replica hinzufügen","m":"Redis auf dem neuen Server verbindet sich mit dem aktuellen Master; die Sentinels erkennen ihn von selbst."},{"k":"srep_op","v":"rm_replica","l":"Replica dauerhaft entfernen","m":"Die Replica wird heruntergefahren und ihr Eintrag auf allen Sentinels bereinigt."},{"k":"srep_op","v":"add_sentinel","l":"Neuen Sentinel hinzufügen","m":"Auf einem neuen Server wird ein Sentinel hinzugefügt (die Gesamtzahl der Sentinels sollte ungerade bleiben: 3 → 5)."},{"k":"srep_op","v":"rm_sentinel","l":"Sentinel dauerhaft entfernen","m":"Der Sentinel wird heruntergefahren und sein Eintrag auf den anderen bereinigt; danach wird das Quorum überprüft."}]},{"id":"redis-security","file":"install/guides/redis-security.json","group":"Redis","engineId":"redis","category":"security","title":"Redis-/Valkey-Security-Härtung (ACL, TLS, gefährliche Befehle)","summary":"Weder Installation noch täglicher Betrieb: ein szenariobasiertes Runbook, um ein laufendes Redis oder Valkey (Einzelserver, Replica + Sentinel oder Cluster) ohne Ausfallzeit zu härten. Schreibgeschütztes Security-Audit, ","level":"Fortgeschritten","duration":"5 Min. (Audit) – 90 Min. (vollständige Härtung)","topology":"Einzelserver, Master + Replica + Sentinel oder Redis Cluster","tags":["redis sicherheit","redis security","valkey","hardening","härtung","ACL","ACL SETUSER","aclfile","requirepass","masterauth","protected-mode","bind","rename-command","enable-protected-configs","TLS","tls-replication","sentinel passwort","CIS"],"order":20,"steps":7,"scenarios":[{"k":"op","v":"audit","l":"Security-Statusaudit (nur lesend)","m":"Audit redis sicherheitsaudit redis security audit CIS Redis hardening härtung redis offen erreichbar exposed redis unauthorized redis redis ohne passwort -NOAUTH crackit redis cryptominer kinsing NOAUTH Authentication required redis sicherheitsprüfung redis pentest Ändert nichts. Zugriff ohne Passwort, der Benutzer default, Listen-Adressen, protected-mode, gefährliche Befehle, TLS, das Sentinel-Pa"},{"k":"op","v":"all","l":"Vollständige Härtung (alles der Reihe nach)","m":"Audit vollständige härtung full hardening redis sicherheitsprojekt pentest befund Audit → ACL-Benutzer (Admin, Anwendung, Replikation, Sentinel) und Deaktivieren des Benutzers default → bind/protected-mode/Firewall → gefährliche Befehle und Protected Configs → TLS → Dateirechte → abschließendes Audit. Auf Replicas/Sentinel rolling; riskante Schritte verlangen eine Bestätigung."},{"k":"op","v":"acl","l":"ACL-Benutzer und Deaktivieren des Benutzers default (Redis 6+)","m":"Identität und Rechte ACL ACL SETUSER aclfile default user default benutzer NOPERM WRONGPASS invalid username-password pair -NOAUTH Authentication required user default on nopass masteruser least privilege minimale rechte NOPERM this user has no permissions to run the NOPERM this user has no permissions to access one of the keys used as arguments ACL LOG passwort pro benutzer ACL SAVE Alle verbinde"},{"k":"op","v":"auth","l":"Redis ohne Passwort: requirepass / masterauth (Sofortmaßnahme)","m":"Identität und Rechte requirepass masterauth auth-pass redis ohne passwort no password MASTER aborted replication with an error: NOAUTH Unable to AUTH to MASTER -NOAUTH ERR AUTH <password> called without any password configured for the default user Client sent AUTH, but no password is set passwort setzen Es gibt überhaupt kein Passwort und keine Zeit für die Umstellung auf ACL. Allen Nodes wird im "},{"k":"op","v":"net","l":"Netzwerkzugriff einschränken (bind 0.0.0.0, protected-mode, Firewall)","m":"Netzwerk und Verschlüsselung bind 0.0.0.0 protected-mode no DENIED Redis is running in protected mode 0.0.0.0:6379 port 6379 offen shodan firewall DENIED Redis is running in protected mode because protected mode is enabled and no password is set for the default user *:6379 Could not create server TCP listening socket Redis lauscht auf allen Interfaces oder läuft mit `protected-mode no`. bind wird "},{"k":"op","v":"cmds","l":"Gefährliche Befehle und CONFIG-Missbrauch (FLUSHALL, CONFIG, DEBUG, MODULE)","m":"Netzwerk und Verschlüsselung CONFIG SET dir dbfilename authorized_keys crontab FLUSHALL FLUSHDB DEBUG MODULE LOAD enable-protected-configs enable-debug-command enable-module-command rename-command @dangerous ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config ERR DEBUG command not allowed CONFIG SET dbfilename rename-command CONFIG schadmodul SLAVEOF exploit Geg"},{"k":"op","v":"tls","l":"Verbindungen verschlüsseln: TLS (Clients, Replikation, Sentinel, Cluster Bus)","m":"Netzwerk und Verschlüsselung TLS SSL tls-port tls-replication tls-cluster tls-auth-clients unverschlüsselte verbindung Error accepting a client connection: error:0A00010B:SSL routines::wrong version number SSL_connect certificate verify failed rediss:// Connection reset by peer tls-cert-file verschlüsselte verbindung encryption in transit Der Verkehr ist unverschlüsselt. Der TLS-Port wird im laufe"},{"k":"op","v":"perms","l":"Dateirechte und Dienstbenutzer","m":"Spuren und Dateien redis.conf rechte world readable chmod dump.rdb appendonly läuft als root running as root dateirechte Permission denied 640 User=redis redis root redis.conf (enthält Passwörter), die ACL-Datei, RDB/AOF und Logdateien sind für andere Benutzer lesbar, oder Redis läuft als root. Die Rechte werden auf 640/750 verschärft und der Besitz auf den Dienstbenutzer gesetzt."}]},{"id":"es-cluster","file":"install/guides/es-cluster.json","group":"Elasticsearch","engineId":"elasticsearch","category":"install","title":"Elasticsearch-Cluster installieren (3+ Nodes, TLS, Kibana)","summary":"Baut einen Elasticsearch-8.x-/9.x-Cluster mit 3 oder mehr Nodes von Grund auf: Elastic-Repository, Heap passend zum RAM, Memory Lock, vm.max_map_count, automatische Sicherheitskonfiguration auf dem ersten Node (TLS + ela","level":"Mittel","duration":"60–90 Min.","topology":"3+ Elasticsearch-Nodes (+ optionaler Kibana-Server)","tags":["elasticsearch","elastic","kibana","cluster","elasticsearch cluster","installation","install","enrollment","tls","heap","snapshot","rhel","ubuntu"],"order":1,"steps":54,"scenarios":[]},{"id":"es-rolling-upgrade","file":"install/guides/es-rolling-upgrade.json","group":"Elasticsearch","engineId":"elasticsearch","category":"upgrade","title":"Elasticsearch-Rolling-Upgrade (8.x Minor oder 8.19 → 9.x)","summary":"Keine Installation: Ein laufender Elasticsearch-Cluster wird ohne Ausfallzeit auf eine neue Version gebracht, indem die Nodes einzeln aktualisiert werden (Minor-Upgrade innerhalb von 8.x oder Major-Upgrade 8.19 → 9.x). S","level":"Fortgeschritten","duration":"15–30 Min. pro Node","topology":"Bestehender Cluster mit 3+ Nodes (+ Kibana)","tags":["elasticsearch","upgrade","rolling upgrade","minor","major","9.x","8.19","kibana","upgrade assistant","deprecation","snapshot","rollback","rolling upgrade","node rollen","master-eligible","deprecation api","versionlock"],"order":2,"steps":34,"scenarios":[{"k":"path","v":"minor","l":"Innerhalb derselben Hauptversion (z. B. 8.18 → 8.19, 9.1 → 9.2)","m":"Innerhalb derselben Hauptversion (8.18 → 8.19, 9.1 → 9.2): Das Repository bleibt gleich, die Deprecation-Prüfung wird trotzdem durchgeführt. Das Überspringen von Minor-Zwischenversionen wird unterstützt."},{"k":"path","v":"major","l":"Hauptversion: 8.19.x → 9.x","m":"Der Quell-Cluster muss vorher auf 8.19.x (letzter Patch) aktualisiert sein. Die Vorabprüfungen umfassen Upgrade Assistant / Deprecation API und die System Feature Migration."}]},{"id":"es-snapshot","file":"install/guides/es-snapshot.json","group":"Elasticsearch","engineId":"elasticsearch","category":"backup","title":"Elasticsearch Snapshot und Restore (fs-/S3-Repository, SLM)","summary":"Richtet für einen installierten Elasticsearch-8.x-/9.x-Cluster Snapshots (Backups) ein und beschreibt die Wiederherstellung: Repository auf einem gemeinsamen Dateisystem (NFS + path.repo) oder in S3 (Keystore), Aktivieru","level":"Mittel","duration":"45–90 Min.","topology":"Elasticsearch-8.x-/9.x-Cluster (1–N Nodes) + gemeinsames Verzeichnis (NFS) oder S3-kompatibler Object Storage","tags":["elasticsearch","snapshot","restore","sicherung","backup","wiederherstellung","repository","path.repo","nfs","s3","keystore","slm","snapshot lifecycle","aufbewahrung","rename_pattern","feature_states","readonly repository","migration","slm","snapshot lifecycle","quartz cron","utc","netrc"],"order":15,"steps":33,"scenarios":[]},{"id":"es-ops","file":"install/guides/es-ops.json","group":"Elasticsearch","engineId":"elasticsearch","category":"ops","title":"Elasticsearch: täglicher Betrieb und Störungsszenarien (8.x / 9.x)","summary":"Keine Installation: ein szenariobasiertes Runbook, um einen installierten Elasticsearch-8.x-/9.x-Cluster im Alltag zu betreiben und ihn wiederherzustellen, wenn er ausfällt. Zustand und Allocation Explain, Lösung nicht z","level":"Fortgeschritten","duration":"5 Min. (Prüfung) – Stunden (Reindex, Node leeren)","topology":"Elasticsearch-8.x-/9.x-Cluster (1–N Nodes), TLS und Authentifizierung aktiviert","tags":["elasticsearch","elastic","cluster health","red","yellow","unassigned","allocation explain","reroute","retry_failed","allocate_stale_primary","watermark","flood stage","read_only_allow_delete","exclude","voting_config_exclusions","enrollment token","reindex","alias","ILM","rollover","shrink","split","snapshot restore","betrieb","fehlersuche"],"order":20,"steps":6,"scenarios":[{"k":"scenario","v":"health","l":"Cluster-Zustand: Status, Nodes, nicht zugewiesene Shards, Disk","m":"Monitoring und Diagnose red yellow _cluster/health _cat/shards UNASSIGNED allocation explain _cluster/allocation/explain status red status yellow cluster zustand pending tasks master_not_discovered_exception NoNodeAvailableException circuit_breaking_exception heap cluster zustandsprüfung Nur lesend: green/yellow/red, Node-Liste, nicht zugewiesene (UNASSIGNED) Shards und ihre Ursache (Allocation Ex"},{"k":"scenario","v":"unassigned","l":"Nicht zugewiesenen (UNASSIGNED) Shard beheben (nach Explain)","m":"Monitoring und Diagnose UNASSIGNED unassigned shards NODE_LEFT ALLOCATION_FAILED max_retry shard has exceeded the maximum number of retries retry_failed no_valid_shard_copy cannot allocate because a previous copy of the primary shard existed allocate_stale_primary allocate_empty_primary the node is above the high watermark same_shard awareness filter NO_VALID_SHARD_COPY CLUSTER_RECOVERED REPLICA_A"},{"k":"scenario","v":"watermark","l":"Disk-Watermark-Einstellungen anzeigen / ändern","m":"Disk und Blocks watermark disk.watermark low disk watermark high disk watermark flood stage disk watermark platte voll disk full cluster.routing.allocation.disk cluster.routing.allocation.disk.watermark.high disk usage exceeded plattenschwelle Shards werden wegen der Plattenbelegung verschoben oder nicht zugewiesen; die Watermark-Schwellen (low / high / flood_stage) sollen vorübergehend oder dauer"},{"k":"scenario","v":"readonly","l":"Index ist schreibgeschützt: Block read_only_allow_delete entfernen","m":"Disk und Blocks read_only_allow_delete FORBIDDEN/12/index read-only / allow delete (api) TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark cluster_block_exception index.blocks.read_only_allow_delete flood stage schreibgeschützt read-only blocked by: [FORBIDDEN/12/index read-only / allow delete (api)] index read-only schreiben abgewiesen Schreibzugriffe werden mit 'FORBIDDEN/12/index r"},{"k":"scenario","v":"add_node","l":"Node zum Cluster hinzufügen (Enrollment Token)","m":"Node-Verwaltung node hinzufügen add node enrollment token elasticsearch-create-enrollment-token elasticsearch-reconfigure-node scale out join cluster master not discovered master not discovered yet, this node has not previously joined a bootstrapped cluster kapazität erweitern neuer node Mehr Kapazität wird gebraucht: Auf einem neuen Server ist Elasticsearch installiert; er tritt dem Cluster mit e"},{"k":"scenario","v":"remove_node","l":"Node sicher entfernen (Exclude, leeren, herunterfahren)","m":"Node-Verwaltung node entfernen remove node decommission cluster.routing.allocation.exclude._name exclude voting_config_exclusions relocating node leeren voting configuration node außer betrieb serveraustausch Ein Node wird außer Betrieb genommen oder seine Hardware ersetzt: Seine Shards werden per Allocation Exclude auf die anderen Nodes verschoben, es wird geprüft, ob er leer ist, ist er master-e"},{"k":"scenario","v":"reindex","l":"Reindex: Umstellung ohne Ausfallzeit auf einen neuen Index mit Alias bei Mapping-/Einstellungsänderung","m":"Index-Verwaltung _reindex reindex mapping ändern mapper_parsing_exception illegal_argument_exception: mapper alias _aliases unterbrechungsfreie umstellung zero downtime task version_conflict_engine_exception cannot be changed from type mapper [ analyzer ändern Der Typ eines Mapping-Felds, der Analyzer oder die Shard-Anzahl muss sich ändern: Ein neuer Index wird angelegt, die Daten werden mit _rein"},{"k":"scenario","v":"ilm","l":"Index Lifecycle (ILM) und Rollover einrichten / Fehler beheben","m":"Index-Verwaltung ILM index lifecycle rollover _rollover _ilm/explain is_write_index rollover_alias ilm policy index.lifecycle.name ERROR step illegal_argument_exception: index.lifecycle.rollover_alias rollover_alias does not point to index ILM ERROR index wird nicht gelöscht log index wächst Log-/Zeitreihen-Indizes wachsen weiter oder werden nicht gelöscht: Eine ILM-Policy (Rollover + Delete), ein"},{"k":"scenario","v":"resize","l":"Shard-Anzahl korrigieren: _shrink (verringern) / _split (erhöhen)","m":"Index-Verwaltung _shrink _split shrink split shard anzahl number_of_shards too many shards zu viele shards oversharding index.blocks.write must be read-only to resize index must have all shards allocated on the same node to shrink index number_of_routing_shards Die Shard-Anzahl des Index wurde ungünstig gewählt: Sehr kleine Shards (Oversharding) werden mit _shrink zusammengeführt, sehr große Shard"},{"k":"scenario","v":"snaprestore","l":"Einzelnen Index aus einem Snapshot unter neuem Namen wiederherstellen","m":"Index-Verwaltung snapshot restore _restore einzelnen index wiederherstellen rename_pattern rename_replacement index gelöscht deleted index index_not_found_exception cannot restore index because an open index with same name already exists snapshot_missing_exception versehentlich gelöscht index wiederherstellen Ein Index wurde gelöscht oder ist beschädigt: Nur dieser Index wird aus dem Snapshot-Repo"},{"k":"un_fix","v":"retry","l":"Wiederholungslimit überschritten: erneut versuchen (_cluster/reroute?retry_failed)","m":"max_retry shard has exceeded the maximum number of retries ALLOCATION_FAILED explain: 'shard has exceeded the maximum number of retries'. Zuerst wird der eigentliche Fehler (Disk, Mapping, Speicher) behoben."},{"k":"un_fix","v":"replicas","l":"Mehr Replicas als Nodes: Replicas verringern","m":"same_shard a copy of this shard is already allocated to this node explain: same_shard (eine Kopie kann nicht auf denselben Node gelegt werden); auf einem Single-Node-Cluster Replicas 0."},{"k":"un_fix","v":"filter","l":"Allocation Filtering / Exclude blockiert: Filter entfernen","m":"filter node does not match index setting [index.routing.allocation node matches cluster setting [cluster.routing.allocation.exclude explain: Filter-Decider ('node does not match index setting [index.routing.allocation…]' oder ein Cluster-Exclude)."},{"k":"un_fix","v":"disk","l":"Disk Watermark blockiert","m":"disk_threshold the node is above the low watermark the node is above the high watermark explain: Decider disk_threshold. Verweist auf das Watermark-Szenario."},{"k":"un_fix","v":"stale","l":"Letztes Mittel: Primary aus einer alten (stale) Kopie zuweisen","m":"no_valid_shard_copy all the copies of its data in the cluster are stale or corrupt allocate_stale_primary Die aktuelle Kopie des Primarys ist verloren, es gibt nur eine alte Kopie: Die letzten Schreibvorgänge in dieser Kopie gehen verloren."},{"k":"un_fix","v":"empty","l":"Allerletztes Mittel: leeren Primary zuweisen (alle Daten des Shards gehen verloren)","m":"no_valid_shard_copy there are no copies of its data in the cluster allocate_empty_primary Es gibt keine Kopie; wenn sich der Index nicht aus einem Snapshot wiederherstellen lässt."},{"k":"wm_op","v":"show","l":"Nur anzeigen (Plattenbelegung und aktuelle Schwellen)","m":"Nur lesend: Disk je Node und die aktuellen Schwellen."},{"k":"wm_op","v":"set","l":"Schwellen ändern","m":"Die Schwellen vorübergehend anheben (z. B. auf einer großen Platte `50gb` freier Platz statt eines Prozentwerts). Setze sie auf den Standard zurück, sobald das Problem gelöst ist."},{"k":"wm_op","v":"reset","l":"Auf Standard zurücksetzen (85% / 90% / 95%)","m":"Entfernt zuvor geänderte Schwellen und kehrt zum Standard zurück (85% / 90% / 95%)."},{"k":"ilm_op","v":"setup","l":"Neue Policy + Template + erster Index (Write Alias)","m":"Legt die Policy (Rollover + Löschen), das Index-Template und den ersten Index (`-000001`, Write Alias) an. Für einen neuen Log-Stream."},{"k":"ilm_op","v":"debug","l":"Warum kommen bestehende Indizes nicht weiter? (Explain, Retry)","m":"Indizes führen keinen Rollover durch oder werden nicht gelöscht: `_ilm/explain` zeigt den fehlgeschlagenen Schritt; ist die Ursache behoben, erfolgt ein Retry."},{"k":"ilm_op","v":"rollover","l":"Rollover manuell auslösen","m":"Ein neuer Write Index wird angelegt, ohne auf die Bedingungen zu warten (nach einer Mapping-Änderung oder bei einem aufgeblähten Index)."},{"k":"rs_kind","v":"shrink","l":"_shrink: Shard-Anzahl verringern (das Ziel muss ein Teiler der aktuellen Anzahl sein)","m":""},{"k":"rs_kind","v":"split","l":"_split: Shard-Anzahl erhöhen (das Ziel muss ein Vielfaches der aktuellen Anzahl sein)","m":""}]},{"id":"es-security","file":"install/guides/es-security.json","group":"Elasticsearch","engineId":"elasticsearch","category":"security","title":"Elasticsearch-Sicherheitshärtung (Security, TLS, Rollen, Audit)","summary":"Keine Installation und kein Tagesbetrieb: ein szenariobasiertes Runbook zur Härtung eines laufenden Elasticsearch-Clusters. Lesendes Sicherheitsaudit, Aktivieren der deaktivierten Security zusammen mit Transport-TLS (vol","level":"Fortgeschritten","duration":"10 Min. (Audit) – 2 Stunden (vollständige Härtung)","topology":"Elasticsearch-Cluster mit 1–12 Nodes (+ Kibana)","tags":["elasticsearch sicherheit","elasticsearch security","hardening","härtung","xpack.security","TLS","elasticsearch-certutil","elasticsearch-users","API key","role mapping","elastic superuser","anonymous","audit logging","IP filtering","kibana_system","service account token","CIS"],"order":20,"steps":4,"scenarios":[{"k":"op","v":"audit","l":"Sicherheitsstatus prüfen (nur lesend)","m":"Audit elasticsearch sicherheitsaudit elasticsearch security audit CIS Elasticsearch hardening härtung offenes elasticsearch exposed elasticsearch xpack.security.enabled missing authentication credentials for REST request security_exception elasticsearch sicherheitsprüfung elasticsearch pentest RISK Ändert nichts. Ob Security aktiv ist, Zugriff ohne Authentifizierung, HTTP-/Transport-TLS, anonymer "},{"k":"op","v":"all","l":"Vollständige Härtung (alles der Reihe nach)","m":"Audit vollständige härtung full hardening elastic sicherheitsprojekt pentest befund Audit → Security + Transport-/HTTP-TLS (falls deaktiviert; vollständiger Cluster-Neustart) → Benutzer/Rollen/API Keys, anonymen Zugriff abschalten, elastic aus der täglichen Arbeit nehmen → Netzwerk und IP-Filterung → Audit Logging (falls die Lizenz es erlaubt) → Zugriff auf das Snapshot-Repository → Kibana-Verbind"},{"k":"op","v":"enable","l":"Security deaktiviert: Security und Transport-TLS aktivieren","m":"Identität und Verschlüsselung xpack.security.enabled: false security disabled zugriff ohne authentifizierung Elasticsearch built-in security features are not enabled Transport SSL must be enabled if security is enabled invalid configuration for xpack.security.transport.ssl 7.x upgrade sicherheit Security is disabled xpack.security.transport.ssl keine authentifizierung Ein Cluster mit `xpack.securi"},{"k":"op","v":"httptls","l":"HTTP-(REST-)Verkehr auf TLS umstellen","m":"Identität und Verschlüsselung http.ssl xpack.security.http.ssl.enabled received plaintext http traffic on an https channel plaintext http https 9200 elasticsearch-certutil http SSLHandshakeException curl: (52) Empty reply from server http:// 9200 passwort im klartext im netz Security ist aktiv, aber Port 9200 spricht unverschlüsseltes HTTP: Passwörter sind im Netz lesbar. HTTP-Zertifikate werden v"},{"k":"op","v":"users","l":"Benutzer, Rollen, API Keys; elastic und anonymer Zugriff","m":"Identität und Verschlüsselung elastic benutzer elastic superuser superuser role mapping API key api_key elasticsearch-users anonymous xpack.security.authc.anonymous action [indices:data/write/bulk[s]] is unauthorized is unauthorized for user unable to authenticate user action [indices:data/read/search] is unauthorized for user api key ablaufzeit api key ohne ablauf break-glass Anwendungen verbinde"},{"k":"op","v":"auditlog","l":"Audit Logging aktivieren","m":"Nachvollziehbarkeit und Zugriff audit log xpack.security.audit.enabled audit logging _audit.json audit trail wer hat gelöscht DSGVO PCI DSS xpack.security.audit.logfile.events.include zugriffsprotokoll Wer auf welchen Index zugegriffen hat, fehlgeschlagene Anmeldeversuche, Autorisierungsablehnungen und Änderungen an Sicherheitseinstellungen müssen protokolliert werden. `xpack.security.audit.enable"},{"k":"op","v":"net","l":"Netzwerkzugriff und IP-Filterung","m":"Nachvollziehbarkeit und Zugriff network.host 0.0.0.0 0.0.0.0:9200 port 9200 offen xpack.security.transport.filter xpack.security.http.filter ip filtering shodan firewall 0.0.0.0:9300 network.host: 0.0.0.0 _site_ öffentliche IP 9200/9300 lauschen auf allen Interfaces oder sind für alle erreichbar. `network.host` wird auf die interne IP beschränkt; mit Firewall und Elasticsearch-IP-Filterung sind nu"},{"k":"op","v":"snapshot","l":"Zugriff auf das Snapshot-Repository absichern","m":"Nachvollziehbarkeit und Zugriff snapshot repo path.repo repository_exception s3.client access_key readonly repository snapshot sicherheit snapshot_operator Object Lock backup sicherheit Das Snapshot-Verzeichnis ist für alle lesbar, die Berechtigung zur Verwaltung des Repositorys ist zu weit gefasst oder Cloud-Zugangsschlüssel stehen in elasticsearch.yml. Verzeichnisrechte, eine Snapshot-Operator-R"},{"k":"op","v":"kibana","l":"Kibana-Verbindung absichern","m":"Nachvollziehbarkeit und Zugriff kibana_system elasticsearch.username: elastic kibana.yml passwort service account token elasticsearch.serviceAccountToken xpack.encryptedSavedObjects.encryptionKey Kibana server is not ready yet unable to retrieve version information from Elasticsearch nodes value of \"elastic\" is forbidden serviceAccountToken kibana.keystore Kibana verbindet sich mit dem Benutzer `e"}]},{"id":"mon-prometheus-grafana","file":"install/guides/mon-prometheus-grafana.json","group":"Monitoring","engineId":null,"category":"monitoring","title":"Datenbank-Monitoring mit Prometheus + Grafana","summary":"Richtet einen zentralen Monitoring-Server (Prometheus, Alertmanager, Grafana) ein und ergänzt die überwachten Server um node_exporter und die gewählten Datenbank-Exporter: PostgreSQL, MySQL, Oracle, SQL Server, MongoDB, ","level":"Mittel","duration":"60–120 min","topology":"1 Monitoring-Server + überwachte Server","tags":["prometheus","grafana","alertmanager","node_exporter","postgres_exporter","mysqld_exporter","oracledb_exporter","sql_exporter","mongodb_exporter","redis_exporter","elasticsearch_exporter","datenbank-überwachung","monitoring","alert","dashboard"],"order":1,"steps":44,"scenarios":[]},{"id":"ora19-install","file":"install/guides/ora19-install.json","group":"Oracle","engineId":"oracle","category":"install","title":"Installation Oracle Database 19c Single Instance (EE / SE2)","summary":"Installiert Oracle Database 19c von Grund auf auf einem einzelnen Server mit Oracle Linux / RHEL 7.9, 8 oder 9: Vorbereitung des Betriebssystems (Preinstall-RPM oder manuell), OFA-Verzeichnisse, Silent-Installation der S","level":"Mittel","duration":"90–150 Min.","topology":"1 Server","tags":["oracle","19c","Installation","install","runInstaller","dbca","netca","silent","release update","applyRU","opatch","cdb","pdb","hugepages","rman","oracle linux","rhel","asm","ol7","multipath","oracle restart"],"order":1,"steps":38,"scenarios":[]},{"id":"ora23-install","file":"install/guides/ora23-install.json","group":"Oracle","engineId":"oracle","category":"install","title":"Oracle AI Database 26ai / 23ai installieren (Linux)","summary":"Installiert Oracle AI Database 26ai (das Release, das 23ai ablöst, Versionsnummer 23.26.x) auf einem einzelnen Server; du wählst einen von drei offiziellen Wegen: OFA-Installation aus dem Gold-Image-ZIP für die Produktio","level":"Mittel","duration":"45–120 Min.","topology":"1 Server","tags":["oracle","26ai","23ai","ai database","installation","install","rpm","free","gold image","setupDBHome","dbca","cdb","pdb","oracle linux","rhel","asm","oracle restart"],"order":2,"steps":30,"scenarios":[]},{"id":"ora-storage-prep","file":"install/guides/ora-storage-prep.json","group":"Oracle","engineId":"oracle","category":"install","title":"Speichervorbereitung für Oracle: Multipath, persistente Gerätenamen, ASM-Disks","summary":"Wird einmal vor der Installation von Oracle Grid Infrastructure, Oracle Restart oder der Datenbank ausgeführt: Die SAN-LUNs werden über die WWID identifiziert, bei Bedarf werden DM-Multipath-Aliase eingerichtet, und die ","level":"Mittel","duration":"30–90 Min.","topology":"1 Server oder 2–8 RAC-Nodes + SAN-/iSCSI-/VM-Disks","tags":["oracle","asm","multipath","device-mapper-multipath","dm-multipath","multipath.conf","wwid","udev","udev-regel","asmlib","oracleasm","afd","asm filter driver","lvm","xfs","fstab","san","lun","speichervorbereitung","storage","disk","rac","oracle restart"],"order":3,"steps":10,"scenarios":[]},{"id":"ora-grid-asm","file":"install/guides/ora-grid-asm.json","group":"Oracle","engineId":"oracle","category":"install","title":"Oracle Restart: Grid Infrastructure 19c + ASM (einzelner Server)","summary":"Installation von Oracle Restart (Grid Infrastructure 19c Standalone, HA_CONFIG) und ASM auf einem einzelnen Server: Benutzer grid und ASM-Gruppen, persistente Plattennamen (udev, ASMLib oder ASM Filter Driver; Platten mi","level":"Fortgeschritten","duration":"90–150 Min.","topology":"1 Server + nicht gemeinsam genutzte ASM-Platten","tags":["oracle","19c","grid infrastructure","oracle restart","asm","gridSetup","HA_CONFIG","udev","asmlib","oracleasm","afd","asm filter driver","srvctl","asmcmd","installation","multipath","ol7"],"order":3,"steps":30,"scenarios":[]},{"id":"ora19-patch-ru","file":"install/guides/ora19-patch-ru.json","group":"Oracle","engineId":"oracle","category":"patch","title":"Oracle Database 19c Release Update (RU) einspielen","summary":"Vierteljährliches Release Update für eine 19c-Datenbank als Single Instance oder unter Oracle Restart (Grid Infrastructure + ASM): OPatch-Aktualisierung, Vorabprüfungen auf Konflikte und Platz, Sicherung des ORACLE_HOME ","level":"Mittel","duration":"60–120 Min. (Ausfallzeit: In-Place 30–60 Min., Out-of-Place 10–20 Min.)","topology":"1 Server (Single Instance oder Oracle Restart); für RAC der Rolling-Patch-Guide","tags":["oracle","19c","patch","release update","ru","opatch","opatchauto","datapatch","ojvm","out-of-place","rollback","restore point","oracle restart","rac","rolling","standby-first","data guard","standby","1265700.1"],"order":10,"steps":25,"scenarios":[]},{"id":"ora19-rac","file":"install/guides/ora19-rac.json","group":"Oracle","engineId":"oracle","category":"ha","title":"Installation von Oracle RAC 19c (Grid Infrastructure + ASM + RAC-Datenbank)","summary":"Installiert Oracle Grid Infrastructure 19c (Flex ASM, SCAN) und eine Oracle RAC 19c-Datenbank von Grund auf auf zwei oder mehr Nodes: Netz- und DNS-Planung, Vorbereitung der Shared Disks (udev oder ASMLib; mit Multipath ","level":"Fortgeschritten","duration":"4–6 Stunden","topology":"2–8 Nodes + Shared Disks (SAN/iSCSI) + SCAN im DNS","tags":["oracle","rac","19c","grid infrastructure","clusterware","asm","scan","gridsetup","dbca","srvctl","crsctl","cluster","ha","udev","asmlib","multipath","ol7","scan","ocr","voting disk"],"order":10,"steps":68,"scenarios":[]},{"id":"ora-rac-patch","file":"install/guides/ora-rac-patch.json","group":"Oracle","engineId":"oracle","category":"patch","title":"Oracle RAC 19c Rolling Release Update (opatchauto)","summary":"Spielt ein vierteljährliches GI Release Update ohne Ausfallzeit (rolling) in einen laufenden RAC-19c-Cluster mit 2–8 Nodes ein: OPatch-Aktualisierung, Konfliktprüfung mit opatchauto analyze, OCR-Backup und Rückfallpunkt,","level":"Fortgeschritten","duration":"45–90 Min. pro Node","topology":"Bestehender RAC 19c mit 2–8 Nodes","tags":["oracle","rac","19c","patch","release update","ru","opatchauto","opatch","datapatch","rolling","grid infrastructure","rollback","scan","service","standby-first","srvctl"],"order":11,"steps":38,"scenarios":[]},{"id":"ora-rac-ops","file":"install/guides/ora-rac-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Oracle RAC: täglicher Betrieb und Fehlerszenarien","summary":"Täglicher Betrieb und Störungsbehebung für einen installierten, laufenden Oracle-RAC-Cluster (Grid Infrastructure 19c): Health Check, Diagnose von Node Evictions, Node in Wartung nehmen, gesamten Cluster geplant herunter","level":"Fortgeschritten","duration":"15 Min. – 3 Std. (je nach Szenario)","topology":"Bestehender RAC mit 2–8 Nodes (Grid Infrastructure 19c; Unterschiede zu 23ai/26ai in den Hinweisen)","tags":["oracle","rac","grid infrastructure","clusterware","crsctl","srvctl","olsnodes","cluvfy","addnode","node hinzufügen","node entfernen","delete node","ocr","olr","voting disk","votedisk","ocrconfig","ocrcheck","scan","vip","oifcfg","interconnect","eviction","ocssd","misscount","service","relocate","application continuity","taf","fan","täglicher betrieb","störung"],"order":12,"steps":10,"scenarios":[{"k":"scenario","v":"health","l":"Cluster-Health-Check (täglich / vor Wartungsarbeiten)","m":"Prüfung und Diagnose crsctl check cluster crsctl stat res -t cluvfy OFFLINE-Ressource CRS-4535 CRS-4530 CRS-4639 health check zustandsprüfung INTERMEDIATE CRS-4537 CRS-2765 CRS-2878 CRS-4700 CRS-4701 crsctl check crs cluster-zustand cluster status ressource offline resource offline orachk tfactl prüfung vor wartung Ändert nichts: Geprüft werden Clusterware-Stack, Ressourcen, OCR/Voting, ASM, Inter"},{"k":"scenario","v":"eviction","l":"Ein Node wurde aus dem Cluster entfernt oder hat sich selbst neu gestartet (Eviction-Diagnose)","m":"Prüfung und Diagnose CRS-1609 CRS-1610 CRS-1611 CRS-1612 CRS-1615 CRS-1625 CRS-1632 CRS-1656 CRS-8011 node eviction evicted node reboot split brain ocssd.trc misscount disktimeout node aus cluster entfernt CRS-1601 CRS-1606 CRS-1607 CRS-1608 CRS-1613 CRS-1614 CRS-8013 ORA-29740 ORA-29770 IPC Send timeout network heartbeat disk heartbeat node neu gestartet server hat sich selbst neu gestartet reboo"},{"k":"scenario","v":"stopnode","l":"Einen Node für Wartungsarbeiten stoppen und wieder starten","m":"Starten / Stoppen crsctl stop crs crsctl start crs node-wartung OS patch reboot CRS-4133 CRS-4123 drain_timeout srvctl stop instance CRS-2673 CRS-2677 CRS-2675 crsctl disable crs crsctl enable crs wartungsmodus maintenance kernel update kernel-aktualisierung firmware Wähle dieses Szenario, wenn auf einem Node ein Betriebssystem-Patch, ein Kernel-Update oder eine Hardwarewartung ansteht. Die übrige"},{"k":"scenario","v":"stopall","l":"Gesamten Cluster geplant herunterfahren und starten","m":"Starten / Stoppen crsctl stop cluster -all crsctl start cluster -all komplett herunterfahren cluster shutdown geplante ausfallzeit srvctl stop database CRS-2677 CRS-4133 CRS-4123 crsctl stop crs crsctl start crs SAN-Wartung rechenzentrumswartung datacenter maintenance power off alle server herunterfahren Wähle dieses Szenario, wenn alle Nodes gleichzeitig heruntergefahren werden müssen, etwa bei S"},{"k":"scenario","v":"addnode","l":"Neuen Node zum Cluster hinzufügen","m":"Node hinzufügen / entfernen addnode.sh addNode gridSetup.sh -addNode node hinzufügen add node cluvfy stage -pre nodeadd dbca -addInstance INS-06006 PRVG- PRVF- INS-13013 INS-40912 CLSRSC-184 PRVG-11050 PRVF-4664 PRVF-7617 cvuqdisk new node neuen server hinzufügen cluster erweitern Wähle dieses Szenario, wenn ein neuer Server in den Cluster aufgenommen wird (Kapazitätserweiterung, Hardwareerneuerun"},{"k":"scenario","v":"delnode","l":"Node aus dem Cluster entfernen","m":"Node hinzufügen / entfernen crsctl delete node deinstall -local dbca -deleteInstance node entfernen delete node remove node cluvfy stage -post nodedel crsctl unpin css CRS-4658 CRS-4661 runInstaller -updateNodeList srvctl remove instance decommission server aus cluster entfernen alten server ausbauen Wähle dieses Szenario, wenn ein Server dauerhaft aus dem Cluster ausscheidet (Rückgabe, Hardwarede"},{"k":"scenario","v":"service","l":"Service anlegen, ändern, verlagern oder löschen","m":"Services und Netzwerk srvctl add service srvctl modify service srvctl relocate service srvctl remove service ORA-12514 PRCD-1026 preferred available TAF Application Continuity TAC FAN service verlagern PRKO-2017 PRCR-1090 ORA-12514: TNS:listener does not currently know of service srvctl start service srvctl status service failback service nicht zurückgekehrt drain_timeout service anlegen create se"},{"k":"scenario","v":"net","l":"Änderung von SCAN, VIP, Listener-Port oder Interconnect","m":"Services und Netzwerk srvctl modify scan srvctl modify scan_listener srvctl modify nodeapps srvctl modify listener oifcfg setif oifcfg delif oifcfg getif IP-Änderung SCAN IP VIP-Änderung interconnect private network ORA-12545 listener port PRCS-1034 PRKO-2310 PRCR-1079 oifcfg HAIP 169.254 port-änderung SCAN-Änderung VIP change remote_listener ASMNET1LSNR_ASM Wähle dieses Szenario, wenn sich SCAN-N"},{"k":"scenario","v":"ocr_backup","l":"OCR- und OLR-Backup erstellen, Backups prüfen","m":"OCR, OLR und Voting ocrconfig -manualbackup ocrconfig -showbackup ocrconfig -local -manualbackup OLR-Backup OCR-Sicherung OCR backup ocrcheck ocrdump PROT- ocrconfig -copy ocrconfig -showbackuploc OLR backup wo liegt das OCR-Backup manuelles OCR-Backup Keine Ausfallzeit: manuelles OCR-Backup, OLR-Backup auf jedem Node und Kopie der Backups außerhalb der Cluster-Disks. Wähle das Szenario vor jedem "},{"k":"scenario","v":"ocr_restore","l":"OCR beschädigt oder CRS-Diskgruppe verloren: aus dem Backup wiederherstellen","m":"OCR, OLR und Voting PROT-602 PROC-26 CRS-4535 CRS-4000 CRS-1606 ocrconfig -restore crsctl start crs -excl -nocrs OCR corruption OCR beschädigt CRS-Diskgruppe verloren voting disk verloren ORA-15017 ORA-15063 PROT-35 PROT-19 ORA-15018 ORA-15033 ocrdump OCR restore CRSD startet nicht Logical corruption check failed Wähle dieses Szenario, wenn `ocrcheck` eine Beschädigung zeigt, CRSD nicht startet (P"},{"k":"scenario","v":"olr_restore","l":"Clusterware startet auf einem Node nicht: OLR beschädigt","m":"OCR, OLR und Voting ocrcheck -local ocrconfig -local -restore OLR beschädigt OLR corrupt ohasd startet nicht CRS-4124 CRS-4639 PROCL- CRS-4123 ohasd.trc olr.loc ocrconfig -local -showbackup Local registry integrity check failed OHASD startet nicht einzelner Node startet nicht Wähle dieses Szenario, wenn bei laufenden übrigen Nodes `crsctl start crs` nur auf einem Node OHASD nicht starten kann und "},{"k":"scenario","v":"ocrvote_move","l":"OCR und Voting Files in eine andere Diskgruppe verlagern","m":"OCR, OLR und Voting crsctl replace votedisk crsctl query css votedisk ocrconfig -add ocrconfig -delete voting disk verlagern votedisk OCR verlagern storage migration ORA-15274 ocrconfig -backuploc asmcmd spmove asmcmd pwmove CRS-Diskgruppe verlagern Voting File verlagern OCR move Storage-Wechsel Ohne Ausfallzeit: ocrconfig -add/-delete und crsctl replace votedisk. Wähle dieses Szenario bei einem S"}]},{"id":"ora-asm-ops","file":"install/guides/ora-asm-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Oracle ASM: täglicher Betrieb und Störungsszenarien","summary":"Täglicher Betrieb der ASM-Diskgroups in einer installierten Oracle-Restart- oder RAC-Umgebung und Vorgehen im Störungsfall: Kapazitätsprüfung (usable_file_mb), Rebalance-Überwachung, Disks hinzufügen/ersetzen/entfernen, ","level":"Fortgeschritten","duration":"10 Min. – einige Stunden (je nach Rebalance-Dauer)","topology":"Oracle Restart (Einzelserver) oder RAC mit 2–8 Nodes, Grid Infrastructure 19c (Unterschiede in 23ai/26ai in den Hinweisen)","tags":["oracle","asm","disk group","diskgroup","add disk","drop disk","replace disk","rebalance","v$asm_operation","usable_file_mb","disk_repair_time","online disk","offline disk","failgroup","compatible.asm","asmcmd","spbackup","md_backup","asm_diskstring","dsset","udev","asmlib","afd","multipath","rac","oracle restart","täglicher Betrieb","Störung"],"order":13,"steps":4,"scenarios":[{"k":"op","v":"capacity","l":"Kapazität und Zustand der Diskgroups prüfen","m":"Prüfung und Überwachung usable_file_mb required_mirror_free_mb asmcmd lsdg Diskgroup voll ASM Belegung free_mb Kapazität ORA-15041 ORA-01653 ORA-17502 ORA-15041: diskgroup space exhausted diskgroup space exhausted disk group full ASM disk group full usable_file_mb negativ negative usable_file_mb ASM kein Platz mehr asmcmd du Ändert nichts: Ist eine Diskgroup voll, wie viel Platz ist noch frei (ric"},{"k":"op","v":"rebal","l":"Rebalance überwachen, beschleunigen oder verlangsamen","m":"Prüfung und Überwachung v$asm_operation gv$asm_operation rebalance REBALANCE POWER asm_power_limit asmcmd lsop est_minutes resync compact Rebalance langsam rebalance slow Rebalance hängt rebalance hung ERRS ARB0 wann ist Rebalance fertig Fortschritt und Restdauer eines laufenden Rebalance/Resync (v$asm_operation, est_minutes) sowie Ändern der POWER. Die Datenbank bleibt geöffnet; nur die I/O-Last "},{"k":"op","v":"add","l":"Neue Disk zu einer Diskgroup hinzufügen","m":"Disk-Operationen ADD DISK ALTER DISKGROUP ADD DISK Disk hinzufügen Platz schaffen Kapazität erweitern ORA-15041 ORA-15031 ORA-15033 ORA-15014 ORA-15410 ORA-15032 ORA-15075 ORA-17502 add disk neue LUN new LUN Diskgroup vergrößern extend diskgroup In der Diskgroup wird der Platz knapp oder sie muss wachsen: neue LUN auf allen Nodes vorbereiten (Multipath/udev/ASMLib/AFD), mit ADD DISK hinzufügen, Re"},{"k":"op","v":"replace","l":"Eine Disk durch eine neue ersetzen (REPLACE DISK)","m":"Disk-Operationen REPLACE DISK Disk ersetzen replace disk ORA-15145 storage migration Disk erneuern ORA-15221 ORA-15033 replace failed disk ausgefallene Disk ersetzen disk replacement Storage-Migration storage migration ASM Ersetzt eine ausgefallene (offline) oder zu erneuernde Disk in einem einzigen Vorgang durch eine neue; kopiert werden nur die Daten dieser Disk (12.1+, NORMAL/HIGH). Bei EXTERNA"},{"k":"op","v":"drop","l":"Disk aus einer Diskgroup entfernen","m":"Disk-Operationen DROP DISK Disk entfernen UNDROP DISKS FORMER ORA-15041 ORA-15067 LUN zurückgeben drop disk DROPPING Disk abhängen remove disk from diskgroup LUN freigeben Um eine LUN zurückzugeben oder aus dem Storage zu entfernen: Mit DROP DISK werden die Daten auf die übrigen Disks verlagert; die Disk wird erst abgehängt, wenn der Rebalance beendet und die Disk FORMER ist. Die Datenbank bleibt "},{"k":"op","v":"failed","l":"Eine Disk ist offline oder ausgefallen (NORMAL/HIGH Redundancy)","m":"Disk-Operationen ONLINE DISK OFFLINE DISK disk_repair_time repair_timer mode_status OFFLINE ORA-15042 ORA-15040 ORA-15130 ORA-15063 missing disk disk offline fast mirror resync ORA-15080 ORA-15081 ORA-27061 ORA-27072 ORA-15066 WARNING: Write Failed WARNING: Read Failed remaining active paths: 0 Disk ist offline gegangen SYNCING Disk nicht lesbar I/O error ASM In einer NORMAL/HIGH-Diskgroup erschei"},{"k":"op","v":"create","l":"Neue Diskgroup anlegen","m":"Diskgroup-Verwaltung CREATE DISKGROUP Diskgroup anlegen au_size compatible.asm compatible.rdbms failgroup ORA-15018 ORA-15221 ORA-15072 ORA-15031 ORA-15033 create diskgroup new disk group neue Diskgroup Zum Anlegen einer neuen Daten-, FRA- oder Anwendungs-Diskgroup: Redundancy, AU-Größe, Attribute compatible.asm/rdbms; bei RAC Mount auf allen Nodes und die Clusterware-Ressource. Vorhandene Diskgro"},{"k":"op","v":"mount","l":"Diskgroup mounten oder dismounten","m":"Diskgroup-Verwaltung srvctl start diskgroup srvctl stop diskgroup ALTER DISKGROUP MOUNT DISMOUNT ORA-15027 ORA-15017 Diskgroup lässt sich nicht mounten v$asm_client ORA-15063 ORA-15040 ORA-15130 ORA-15032 ORA-15042 PRCR-1065 CRS-2674 MOUNT FORCE diskgroup dismounted Diskgroup öffnet nicht insufficient number of disks Um eine Diskgroup geplant herunterzufahren (Dismount) oder eine heruntergefahrene"},{"k":"op","v":"spbackup","l":"Sicherung von ASM-spfile, Passwortdatei und Metadaten","m":"Konfiguration asmcmd spbackup asmcmd spget asmcmd pwget asmcmd pwcopy md_backup md_restore ASM spfile Sicherung orapwASM asmcmd spcopy GPnP profile profile.xml ASM password file backup ASM metadata backup ASM Sicherung Keine Downtime: ASM-spfile, Passwortdatei, Diskgroup-Struktur (md_backup) und bei RAC das GPnP-Profil werden außerhalb von ASM gesichert, damit sich die ASM-Konfiguration auch bei V"},{"k":"op","v":"dstring","l":"ASM-Discovery-String (asm_diskstring) ändern","m":"Konfiguration asm_diskstring asmcmd dsset asmcmd dsget discovery string Discovery-String ORA-15014 kfod ORA-15020 discovered duplicate ASM disk afd_dsset asm_diskstring change Disk nicht gefunden Um das Pfadmuster zu ändern, mit dem ASM nach Disks sucht (z. B. Umstieg auf Multipath, neues udev-Verzeichnis, ASMLib/AFD): zuerst mit kfod testen, dann asmcmd dsset. Alle vorhandenen Disks (inklusive OC"}]},{"id":"ora-autoupgrade","file":"install/guides/ora-autoupgrade.json","group":"Oracle","engineId":"oracle","category":"upgrade","title":"19c → Oracle AI Database 26ai (23ai) Upgrade: AutoUpgrade","summary":"Für ein Major-Release-Upgrade (19c → 26ai); für ein vierteljährliches RU innerhalb desselben Release gilt die Anleitung „Oracle Database 19c Release Update (RU) einspielen“. Mit AutoUpgrade, dem von Oracle empfohlenen We","level":"Fortgeschritten","duration":"2–4 Std. (Ausfallzeit: 30–90 Min. je nach Datenbankgröße und Komponenten)","topology":"1 Server, Oracle Restart oder RAC (26ai Home auf allen Nodes)","tags":["oracle","upgrade","autoupgrade","19c","26ai","23ai","noncdb","pdb","target_cdb","restore point","restoration","zeitzone","compatible","rollback","rac","oracle restart","cluster_database","srvctl"],"order":20,"steps":25,"scenarios":[]},{"id":"ora-dataguard","file":"install/guides/ora-dataguard.json","group":"Oracle","engineId":"oracle","category":"ha","title":"Oracle Data Guard 19c Physical Standby (mit Broker; Single Instance, Oracle Restart, RAC)","summary":"Baut für eine laufende 19c-Primary-Datenbank mit RMAN DUPLICATE … FROM ACTIVE DATABASE eine Physical Standby auf und nimmt sie in den Data Guard Broker auf. Ergebnis: eine Standby, die Redo in Echtzeit empfängt und anwen","level":"Fortgeschritten","duration":"2–4 Stunden (je nach Datenmenge)","topology":"Primary (Single Instance / Oracle Restart / RAC mit 2–8 Nodes) + Physical Standby (Single Instance / Oracle Restart / RAC) + optionaler Observer","tags":["oracle","data guard","dataguard","standby","physical standby","dgmgrl","broker","rman duplicate","switchover","failover","fast-start failover","observer","19c","dr","rac","oracle restart","srvctl","standby redo log","apply instance"],"order":20,"steps":43,"scenarios":[]},{"id":"ora-datapump","file":"install/guides/ora-datapump.json","group":"Oracle","engineId":"oracle","category":"backup","title":"Oracle Data Pump: Export / Import, Migration, Schema-Refresh und Transportable","summary":"Tabellen-, Schema-, Tablespace- oder Full-Export und -Import mit expdp/impdp, Migration zwischen zwei Datenbanken (Dumpdatei + scp + Prüfsumme oder NETWORK_LINK) und DDL-Extraktion aus einem Dump (SQLFILE). Inklusive Vor","level":"Mittel","duration":"30–90 min (je nach Datenmenge)","topology":"1 Datenbank (Single Instance, Oracle Restart oder RAC); bei einer Migration Quelle + Ziel","tags":["oracle","data pump","datapump","expdp","impdp","export","import","dump","parfile","remap_schema","network_link","sqlfile","table_exists_action","flashback_time","Datenmigration","migration","logical backup","logische Sicherung","rac","scan","cluster","acfs","cron","wallet","Zeitplanung","refresh","Schema-Refresh","prod to test","drop user cascade","transportable tablespace","tts","xtts","full transportable","transportable=always","dbms_tts","transport_set_check","endian","rman convert","cross-platform","11g to 19c","non-cdb to pdb","Verschlüsselung","encryption_password","Komprimierung","attach","kill_job","start_job","continue_client","orphan","dba_datapump_jobs","lob","securefile","data_options"],"order":20,"steps":16,"scenarios":[{"k":"op","v":"export","l":"Export: Dumpdatei aus der Datenbank erzeugen","m":"Export / Import expdp export Dump erstellen logische Sicherung logical backup ORA-39001 ORA-39000 ORA-39070 ORA-31633 ORA-01555 ORA-39095 ORA-39094 ORA-00439 LRM-00123 UDE-00002 ORA-39127 Eine Tabelle, ein Schema, ein Tablespace oder die gesamte Datenbank wird in eine Dumpdatei geschrieben (expdp). Die Datenbank bleibt geöffnet, die Anwendung läuft weiter. Es ist eine logische Kopie: Sie ersetzt k"},{"k":"op","v":"import","l":"Import: Dumpdatei in die Datenbank laden","m":"Export / Import impdp import Dump laden REMAP_SCHEMA REMAP_TABLESPACE TABLE_EXISTS_ACTION ORA-39002 ORA-39087 ORA-39142 ORA-39143 ORA-39083 ORA-12899 ORA-39171 ORA-39082 ORA-31684 ORA-31640 ORA-39059 ORA-01917 ORA-39151 ORA-39111 ORA-01653 ORA-30036 Eine zuvor erstellte Dumpdatei wird mit impdp geladen; Schema-, Tablespace- und Tabellennamen lassen sich umbenennen (Remap). Das Ziel bleibt geöffnet"},{"k":"op","v":"move","l":"Migration: Daten von der Quelldatenbank ins Ziel übertragen","m":"Migration und Refresh Datenmigration migration NETWORK_LINK database link scp checksum ORA-31679 ORA-02085 ORA-39183 Export → Dateikopie → Import oder direkt mit NETWORK_LINK ohne Dumpdatei. Wähle diese Karte, wenn das Schema im Ziel noch nicht existiert (Erstbefüllung); existiert dasselbe Schema im Ziel bereits und soll durch den Stand aus der Produktion ersetzt werden, wähle die Karte \"Schema-Re"},{"k":"op","v":"sqlfile","l":"Nur die DDL aus einer Dumpdatei extrahieren (SQLFILE)","m":"Export / Import SQLFILE DDL extrahieren Dump-Inhalt was ist im Dump metadata DBMS_METADATA Es wird nichts geladen; die CREATE-Anweisungen der Objekte werden in eine .sql-Datei geschrieben. Die Datenbank wird nicht verändert."},{"k":"op","v":"refresh","l":"Schema-Refresh von Produktion nach Test","m":"Migration und Refresh refresh Schema-Refresh Test aktualisieren prod to test Produktion nach Test UAT-Refresh DROP USER CASCADE ORA-01940 ORA-02380 ORA-01917 ORA-31684 ORA-00031 TABLE_EXISTS_ACTION=REPLACE Ein Schema, das in Test/UAT bereits existiert, wird durch seinen aktuellen Stand aus der Produktion ersetzt: Backup des Zielschemas, Beenden der Sessions, DROP USER … CASCADE oder TABLE_EXISTS_A"},{"k":"op","v":"tts","l":"Tablespace durch Kopieren der Datendateien übertragen (Transportable Tablespace)","m":"Transportable (Kopie der Datendateien) transportable tablespace TTS XTTS TRANSPORT_TABLESPACES TRANSPORT_DATAFILES DBMS_TTS TRANSPORT_SET_CHECK endian cross-platform RMAN CONVERT ORA-29335 ORA-29341 ORA-29342 ORA-29345 ORA-29349 ORA-19722 ORA-01647 Große Tablespaces (Hunderte GB und mehr) werden nicht zeilenweise, sondern durch Kopieren der Datendateien übertragen; gleiche oder andere Plattform/En"},{"k":"op","v":"fulltts","l":"Gesamte Datenbank übertragen: Full Transportable (11g/12c → 19c, Non-CDB → PDB)","m":"Transportable (Kopie der Datendateien) full transportable TRANSPORTABLE=ALWAYS FULL=Y VERSION=12 11g to 19c 11g nach 19c non-CDB to PDB Migration nach PDB migration upgrade by migration ORA-39123 ORA-01647 ORA-29349 ORA-39127 Mit FULL=Y TRANSPORTABLE=ALWAYS werden die Benutzer-Tablespaces als Datendateien und alles andere (Benutzer, PL/SQL, Berechtigungen) als Metadaten in eine neue PDB übertragen"},{"k":"op","v":"jobfix","l":"Abgebrochener / hängender Data-Pump-Job: überwachen, fortsetzen, beenden","m":"Überwachung und Fehler ATTACH START_JOB CONTINUE_CLIENT KILL_JOB STOP_JOB dba_datapump_jobs orphan master table NOT RUNNING IDLING hängt resumable ORA-39171 ORA-31626 ORA-31633 ORA-39002 ORA-31634 ORA-39097 ORA-39126 Ein expdp/impdp-Job hängt (resumable wait), wurde gestoppt oder ist abgebrochen; du willst dich mit ATTACH anhängen und ihn fortsetzen, ihn mit KILL_JOB beenden oder eine verwaiste Ma"}]},{"id":"ora-dataguard-ops","file":"install/guides/ora-dataguard-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Oracle Data Guard: täglicher Betrieb und Fehlerszenarien (19c / 23ai)","summary":"Runbook für den täglichen Betrieb und die Fehlerbehebung einer bestehenden Data-Guard-Konfiguration (Physical Standby); für die Ersteinrichtung nutzt du den Guide zur Data-Guard-Installation. Wenn du nicht sicher bist, w","level":"Fortgeschritten","duration":"5 Min. (Prüfung) – 4 Std. (Neuaufbau)","topology":"Primary (Single Instance / Oracle Restart / RAC) + Physical Standby (Single Instance / Oracle Restart / RAC) + optionaler Observer","tags":["oracle","data guard","dataguard","standby","physical standby","dgmgrl","broker","switchover","failover","reinstate","archive gap","fal","recover from service","roll forward","snapshot standby","active data guard","redo apply","mrp","apply lag","transport lag","protection mode","fast-start failover","fsfo","observer","standby_file_management","standby redo log","unnamed","rman","runbook","dr","rac","19c","23ai"],"order":21,"steps":5,"scenarios":[{"k":"scenario","v":"health","l":"Status- und Lag-Prüfung (ist alles in Ordnung?)","m":"Überwachung und Prüfung ORA-16809 ORA-16810 ORA-16853 ORA-16855 ORA-16857 ORA-16766 ORA-16825 apply lag transport lag Verzögerung lag SHOW CONFIGURATION WARNING Data-Guard-Status ORA-16854 ORA-16856 ORA-16191 ORA-16198 ORA-03135 ORA-16714 ORA-16792 ORA-16047 Standby hängt hinterher hoher Transport Lag Data-Guard-Gesundheitsprüfung health check Schreibgeschützt: Konfiguration, Transport/Apply Lag, "},{"k":"scenario","v":"switchover","l":"Geplanter Switchover (Rollen tauschen)","m":"Rollenwechsel switchover Rollenwechsel role transition DR-Übung ORA-16475 ORA-16470 ORA-16467 Ready for Switchover ORA-16541 ORA-16516 switchover_status TO STANDBY SESSIONS ACTIVE switchback Rollen zurücktauschen geplante Wartung Standortwechsel Primary und Standby sind gesund und du willst die Rollen geplant tauschen (Wartung, DR-Übung, Rollen zurücktauschen); es gehen keine Daten verloren, die A"},{"k":"scenario","v":"failover","l":"Primary verloren: Failover auf die Standby","m":"Ausfall und Recovery failover Primary abgestürzt primary down Standortverlust disaster ORA-16472 ORA-01034 ORA-12541 FAILOVER TO ACTIVATE PHYSICAL STANDBY ORA-12514 ORA-12170 ORA-03113 ORA-16625 ORA-16778 Primary nicht erreichbar Rechenzentrumsausfall FAILOVER TO IMMEDIATE Notfall Die Primary ist nicht erreichbar und kommt nicht zurück (Verlust von Server, Storage oder Standort); die Standby wird "},{"k":"scenario","v":"reinstate","l":"Nach einem Failover die alte Primary als Standby zurückholen (Reinstate)","m":"Ausfall und Recovery reinstate ORA-16661 ORA-16795 ORA-38729 ORA-16653 needs to be reinstated alte Primary flashback ORA-16649 split-brain alte Primary öffnet nicht FastStartFailoverAutoReinstate REINSTATE DATABASE standby_became_primary_scn Ein Failover wurde durchgeführt und der Server der alten Primary ist wieder da; ist Flashback Database aktiv, wird sie ohne Neuaufbau zur Standby der neuen Pr"},{"k":"scenario","v":"gap","l":"Archive Gap: Die Standby wartet auf fehlende archivierte Logs","m":"Ausfall und Recovery archive gap Archive Gap v$archive_gap FAL Fetching gap ORA-00308 ORA-16401 ORA-27037 ORA-16055 gap sequence waiting for log WAIT_FOR_GAP FAL[client] Media Recovery Waiting for thread fehlendes archiviertes Log missing archive log ORA-16089 `v$archive_gap` liefert Zeilen oder das Alert Log zeigt FAL-/Gap-Meldungen; Apply hängt bei einer Sequence. Die Datenbanken bleiben geöffne"},{"k":"scenario","v":"rollfwd","l":"Standby weit zurück oder archivierte Logs weg: Roll Forward (RECOVER FROM SERVICE)","m":"Ausfall und Recovery roll forward Standby vorrollen RECOVER STANDBY DATABASE FROM SERVICE nologging ORA-01578 ORA-26040 Standby im Rückstand resync neu synchronisieren ORA-10567 BACKUP INCREMENTAL FROM SCN Standby Tage im Rückstand NONLOGGED BLOCK incremental roll forward Die archivierten Logs aus dem Gap gibt es nirgends mehr, NOLOGGING-Operationen haben die Standby beschädigt oder die Standby li"},{"k":"scenario","v":"rebuild","l":"Standby von Grund auf neu aufbauen","m":"Ausfall und Recovery rebuild Standby neu aufbauen re-create standby ORA-16795 DUPLICATE Standby verloren Steuerdatei verloren ORA-00205 ORA-01157 Standby-Dateien gelöscht DROP DATABASE Plattenverlust auf der Standby Reinstate (kein Flashback) und Roll Forward sind nicht möglich oder die Dateien/die Steuerdatei der Standby sind verloren; die Standby wird gelöscht und mit DUPLICATE neu erstellt. Die"},{"k":"scenario","v":"snapshot","l":"In Snapshot Standby umwandeln / zurückwandeln (beschreibbare Kopie für Tests)","m":"Test und Reporting snapshot standby CONVERT TO SNAPSHOT STANDBY Testumgebung read write standby ORA-16000 ORA-38784 ORA-16433 CONVERT TO PHYSICAL STANDBY SNAPSHOT STANDBY guaranteed restore point Standby für Tests öffnen Du willst die Standby vorübergehend im Read-Write-Modus für Tests öffnen, danach die Änderungen verwerfen und zur Physical Standby zurückkehren; die Primary ist nicht betroffen un"},{"k":"scenario","v":"apply","l":"Redo Apply stoppen / starten, Apply-Instanz wechseln, read-only öffnen","m":"Täglicher Betrieb MRP redo apply APPLY-OFF APPLY-ON apply instance read only Active Data Guard ORA-16766 ORA-16136 ORA-01153 real-time query WAIT_FOR_LOG APPLYING_LOG READ ONLY WITH APPLY OPEN READ ONLY PreferredApplyInstance intended state Apply gestoppt MRP läuft nicht Du willst MRP für Wartung stoppen oder neu starten, Apply auf einer RAC-Standby auf eine andere Instanz verlagern oder die Stand"},{"k":"scenario","v":"prot","l":"Schutzmodus, Fast-Start Failover und Observer","m":"Täglicher Betrieb protection mode MaxAvailability MaxPerformance MaxProtection SYNC ASYNC FSFO fast-start failover observer ORA-16627 ORA-16651 ORA-16820 ORA-16819 ORA-16072 ORA-16647 NetTimeout LogXptMode FASTSYNC protection_level Datenschutzmodus Du willst zwischen MaxPerformance / MaxAvailability / MaxProtection wechseln, FSFO aktivieren oder deaktivieren oder den Observer verwalten."},{"k":"scenario","v":"files","l":"Datendateien, Redo Logs und Standby Redo Logs hinzufügen / UNNAMED-Datei","m":"Täglicher Betrieb standby_file_management UNNAMED ORA-01111 ORA-01110 ORA-01157 ORA-01274 Datendatei hinzufügen add datafile Redo Log hinzufügen standby redo log SRL ORA-16789 db_file_name_convert ADD STANDBY LOGFILE ORA-01136 Datendatei hinzugefügt Standby Auf der Primary soll eine Datendatei oder ein Redo Log hinzugefügt werden, auf der Standby ist eine UNNAMED-Datei entstanden (ORA-01111/ORA-01"},{"k":"scenario","v":"backup","l":"RMAN-Backup von der Standby erstellen","m":"Täglicher Betrieb rman standby backup Backup von der Standby archivelog deletion policy RMAN-08137 recovery catalog offload backup RMAN-06820 BACKED UP 1 TIMES CONFIGURE DB_UNIQUE_NAME RMAN-Backup auf der Standby Du willst RMAN-Backups auf der Standby ausführen, um die Primary von der Backup-Last zu befreien, und die Löschrichtlinie für archivierte Logs entsprechend einstellen."}]},{"id":"ora-backup-strategy","file":"install/guides/ora-backup-strategy.json","group":"Oracle","engineId":"oracle","category":"backup","title":"Oracle-Backup-Strategie und Automatisierung (RMAN + Data Pump)","summary":"Richtet für eine bestehende Datenbank ein dauerhaftes, automatisches Backup-Schema ein (die Wiederherstellung im Fehlerfall behandelt ein eigener Guide: \"Restore und Recovery mit RMAN\"): wöchentlich Level 0 + täglich Lev","level":"Mittel","duration":"60–120 Min. (ohne Dauer des ersten Level 0)","topology":"1 Server (Single Instance oder Oracle Restart) oder RAC (2–8 Nodes)","tags":["oracle","rman","sicherung","backup","strategie","level 0","level 1","inkrementell","archivelog","fra","aufbewahrung","block change tracking","data pump","expdp","crontab","cron","systemd timer","dbms_scheduler","automatisierung","wallet","mkstore","rac","oracle restart","snapshot controlfile","asm","/etc/cron.d","ol7","recovery catalog","create catalog","register database","restore-übung","restore drill","restore validate","preview"],"order":21,"steps":33,"scenarios":[]},{"id":"ora-rman-restore","file":"install/guides/ora-rman-restore.json","group":"Oracle","engineId":"oracle","category":"backup","title":"Restore und Recovery mit RMAN","summary":"Führt dich im Störfall Schritt für Schritt durch die richtige RMAN-Wiederherstellung: 19 Szenarien — Verlust von Datendatei, SYSTEM/UNDO, Tempfile, beschädigtem Block, Online-Redo-Log, Controlfile und spfile; vollständig","level":"Fortgeschritten","duration":"30 Min. – einige Stunden (abhängig von der Datenbankgröße)","topology":"1 Server (Single Instance oder Oracle Restart) oder RAC; im Szenario mit neuem Host Quelle + Ziel","tags":["oracle","rman","restore","recovery","wiederherstellung","pitr","point in time","resetlogs","recover table","controlfile","spfile","autobackup","datafile","preview","validate","dbid","neuer server","disaster recovery","rac","oracle restart","cluster_database","srvctl","asm","block corruption","beschädigter block","ORA-01578","redo log","ORA-00313","clear logfile","tempfile","tspitr","flashback database","restore point","dropped pdb","unplug","from service","data guard","restore archivelog","catalog start with","set newname","undo","system tablespace"],"order":22,"steps":10,"scenarios":[{"k":"scenario","v":"preview","l":"Probelauf: RESTORE … PREVIEW / VALIDATE (ändert nichts)","m":"Probelauf und Backup-Einträge RESTORE PREVIEW RESTORE VALIDATE restore probelauf restore test funktioniert das backup backup test restore drill restore übung RMAN VALIDATE ist das backup intakt Nichts ist kaputt; du willst ohne Schreiben von Daten sehen, ob die Backups für eine Wiederherstellung wirklich ausreichen (die Datenbank bleibt geöffnet). Auch der erste Schritt für die monatliche Restore-"},{"k":"scenario","v":"catalog","l":"Backups in ein anderes Verzeichnis verschoben / RMAN findet kein Backup (CATALOG)","m":"Probelauf und Backup-Einträge CATALOG START WITH RMAN-06023 RMAN-06025 RMAN-06026 ORA-19505 ORA-27037 EXPIRED backup verschoben backup moved no backup found CROSSCHECK NFS geändert backup nicht gefunden Die Backup Pieces sind vorhanden, aber RMAN kennt sie nicht (verschoben, NFS geändert, von einem anderen Server kopiert): Du bekommst RMAN-06023/06025. Die Datenbank bleibt geöffnet."},{"k":"scenario","v":"arch","l":"Nur Archive Logs wiederherstellen (RESTORE ARCHIVELOG)","m":"Probelauf und Backup-Einträge RESTORE ARCHIVELOG ORA-00308 archive log wiederherstellen archive log restore archive gap LogMiner SET ARCHIVELOG DESTINATION Für LogMiner, eine Standby-Lücke oder manuelles Recovery werden die Archive Logs eines bestimmten Sequence-Bereichs auf der Platte gebraucht; die Datenbank bleibt geöffnet, keine Datei wird überschrieben."},{"k":"scenario","v":"datafile","l":"Verlust/Beschädigung einer einzelnen Datendatei oder eines Tablespace (Datenbank bleibt geöffnet)","m":"Verlust / Beschädigung ORA-01157 ORA-01110 ORA-01116 ORA-27041 ORA-00376 ORA-01113 datendatei verloren datendatei gelöscht missing datafile lost datafile tablespace verloren ORA-27037 ORA-01135 ORA-01115 ORA-01114 datendatei beschädigt Die Datenbank ist geöffnet, nur eine Datendatei oder ein Tablespace ist nicht lesbar (ORA-01157/01110/01116); alles andere läuft. Gehört die Datei zu SYSTEM/UNDO, w"},{"k":"scenario","v":"block","l":"Beschädigter Block vorhanden (ORA-01578, Block Media Recovery)","m":"Verlust / Beschädigung ORA-01578 ORA-26040 ORA-01110 beschädigter block block corruption corrupt block v$database_block_corruption RECOVER CORRUPTION LIST block media recovery nologging ORA-19566 Marked Corrupt Corrupt block relative dba DBVERIFY Eine Abfrage liefert ORA-01578/ORA-26040, das Backup bricht mit ORA-19566 ab oder VALIDATE hat beschädigte Blöcke gefunden; der Rest der Datei ist intakt"},{"k":"scenario","v":"sysdf","l":"SYSTEM- oder UNDO-Datendatei verloren (Datenbank lässt sich nicht öffnen)","m":"Verlust / Beschädigung ORA-01157 ORA-01110 ORA-01116 ORA-01147 ORA-00376 ORA-01541 SYSTEM datafile UNDO datafile undo tablespace verloren system tablespace lost datenbank öffnet nicht ORA-01113 system01.dbf undotbs Die verlorene oder beschädigte Datei gehört zum SYSTEM- oder UNDO-Tablespace: Sie kann nicht offline gesetzt werden, die Datenbank (bei einer PDB-Datei nur diese PDB) wird heruntergefah"},{"k":"scenario","v":"temp","l":"Tempfile verloren oder beschädigt (TEMP-Tablespace)","m":"Verlust / Beschädigung ORA-01157 ORA-01187 ORA-25153 ORA-01116 ORA-01122 ORA-01110 tempfile temp tablespace temporärer tablespace TEMP verloren temp01.dbf dba_temp_files Sortier-/Hash-Operationen liefern ORA-01157/ORA-01187/ORA-25153, und die Datei ist ein Tempfile (die Nummer in der Fehlermeldung ist größer als der Wert von `db_files`, z. B. `data file 201`). Ein Restore ist nicht nötig; es wird "},{"k":"scenario","v":"redo","l":"Online-Redo-Log verloren oder beschädigt","m":"Verlust / Beschädigung ORA-00313 ORA-00312 ORA-00327 ORA-00366 ORA-00367 ORA-00353 ORA-00354 ORA-00316 ORA-01624 redo log verloren online redo lost redo log CLEAR LOGFILE ORA-00314 ORA-00320 ORA-00321 ORA-00340 ORA-00350 ORA-00470 CLEAR UNARCHIVED Im Alert Log steht ORA-00313/00312 (kann nicht geöffnet werden) oder ORA-00327/00366/00367 (beschädigt). Fehlt nur ein Member oder ist die Gruppe INACTI"},{"k":"scenario","v":"controlfile","l":"Alle Controlfile-Kopien verloren","m":"Verlust / Beschädigung ORA-00205 ORA-00202 ORA-00210 ORA-27037 controlfile verloren control file lost RESTORE CONTROLFILE autobackup ORA-00206 ORA-00214 ORA-00221 ORA-00227 controlfile beschädigt Die Instanz findet im MOUNT-Schritt kein Controlfile oder kann es nicht lesen (ORA-00205/00202/00210); Datendateien und Redo sind intakt. Ist eine der Kopien intakt, ist kein Restore nötig (das zeigt der "},{"k":"scenario","v":"spfile","l":"spfile verloren","m":"Verlust / Beschädigung ORA-01078 LRM-00109 ORA-01565 spfile verloren spfile lost parameter file RESTORE SPFILE CRS-5017 PRCR-1079 spfile gelöscht Das spfile wurde gelöscht oder ist beschädigt; die Instanz startet nicht (ORA-01078/LRM-00109) oder läuft, aber die Datei fehlt. Datendateien werden nicht angefasst, es gehen keine Daten verloren; läuft die Instanz noch, gibt es auch keine Downtime."},{"k":"scenario","v":"full","l":"Vollständiger Restore, gleicher Server (Datendateien verloren; Controlfile und Redo intakt)","m":"Verlust / Beschädigung alle datendateien verloren all datafiles lost full restore RESTORE DATABASE plattenausfall storage failure platte defekt LUN verloren datendateien gelöscht Alle oder die meisten Datendateien sind weg, Controlfile und Online-Redo-Logs sind intakt; die Datenbank wird heruntergefahren, es gehen keine Daten verloren. Ist die alte Platte bzw. das alte Verzeichnis der Dateien nich"},{"k":"scenario","v":"relocate","l":"Platte weg: Datenbank in ein anderes Verzeichnis / nach ASM wiederherstellen (SET NEWNAME)","m":"Verlust / Beschädigung SET NEWNAME SWITCH DATAFILE ALL restore in anderes verzeichnis restore nach ASM new location platte gewechselt mount point verloren relocate ORA-15001 ORA-15040 disk group dismounted Die Platte oder Diskgruppe mit den Datendateien ist nicht nutzbar; Controlfile und Redo sind intakt. Die Dateien werden in ein neues Verzeichnis oder eine ASM-Diskgruppe wiederhergestellt, die D"},{"k":"scenario","v":"pitr","l":"Rückkehr zu Zeitpunkt / SCN (PITR, RESETLOGS)","m":"Zeitpunkt-Recovery und Rückgängigmachen PITR point in time recovery zeitpunkt wiederherstellen zurück zu SCN falsches DELETE falsches UPDATE TRUNCATE logischer fehler logical error UNTIL TIME daten gelöscht DROP USER UNTIL SCN RESETLOGS Falsches DELETE/UPDATE/TRUNCATE oder fehlerhafte Anwendungsversion: Setzt die Datenbank (oder nur die PDB) auf einen früheren Zeitpunkt zurück; alle Änderungen dan"},{"k":"scenario","v":"flashback","l":"Mit Flashback Database zurücksetzen (Restore Point / SCN / Zeit)","m":"Zeitpunkt-Recovery und Rückgängigmachen FLASHBACK DATABASE FLASHBACK PLUGGABLE DATABASE restore point guaranteed restore point zurücksetzen upgrade zurückrollen nach test zurücksetzen FLASHBACK_ON FLASHBACK TO RESTORE POINT Flashback ist aktiv oder es gibt einen Guaranteed Restore Point, und keine Datei fehlt: Ohne Restore wird die Datenbank innerhalb von Minuten zurückgesetzt (Enterprise Edition)"},{"k":"scenario","v":"tspitr","l":"Einen Tablespace auf einen früheren Zeitpunkt zurücksetzen (TSPITR)","m":"Zeitpunkt-Recovery und Rückgängigmachen TSPITR tablespace point in time RECOVER TABLESPACE AUXILIARY DESTINATION DBMS_TTS tablespace zurücksetzen ORA-29308 Die fehlerhafte Operation betraf nur Objekte in einem Tablespace; der Rest der Datenbank soll auf dem aktuellen Stand bleiben (Enterprise Edition). Der Tablespace ist während des Vorgangs offline, und alle Objekte darin werden auf den Zielzeitp"},{"k":"scenario","v":"table","l":"Einzelne Tabelle wiederherstellen (RECOVER TABLE, ab 12c Enterprise Edition)","m":"Zeitpunkt-Recovery und Rückgängigmachen RECOVER TABLE tabelle gelöscht DROP TABLE tabelle wiederherstellen table recovery ORA-00942 REMAP TABLE ORA-38305 FLASHBACK TABLE recyclebin PURGE Eine einzelne Tabelle wurde versehentlich gelöscht oder beschädigt; der Rest der Datenbank soll unverändert bleiben (ab 12c Enterprise Edition). Wurde sie mit DROP gelöscht und kein PURGE ausgeführt, prüfe zuerst "},{"k":"scenario","v":"pdbdrop","l":"Gelöschte PDB zurückholen (DROP PLUGGABLE DATABASE)","m":"Zeitpunkt-Recovery und Rückgängigmachen DROP PLUGGABLE DATABASE PDB gelöscht dropped PDB RMAN-06813 ORA-65011 ORA-65179 KEEP DATAFILES PDB zurückholen unplug DBMS_PDB.RECOVER Eine PDB wurde mit `DROP PLUGGABLE DATABASE` gelöscht. Sind die Dateien noch vorhanden, wird sie per XML wieder eingehängt; wurden sie gelöscht, wird sie aus dem Backup geholt (ab 21c ein einziger Befehl, unter 19c über eine "},{"k":"scenario","v":"newhost","l":"Restore auf einen anderen Server (neuer Host, aus Controlfile-Autobackup)","m":"Anderer Server und Standby neuer server anderer server serververlust disaster recovery DR new host klon clone testumgebung server abgebrannt server lost restore to another host Der Server ist komplett weg oder es soll eine Kopie/Testumgebung aufgebaut werden: Das Backup wird auf einem anderen Server wiederhergestellt (nur Single Instance, Dateisystem). Wiederhergestellt wird bis zum letzten gesich"},{"k":"scenario","v":"fromsvc","l":"Data Guard: Datei über das Netz vom Standby/Primary holen (FROM SERVICE)","m":"Anderer Server und Standby RESTORE FROM SERVICE FROM SERVICE RECOVER STANDBY DATABASE restore vom standby Data Guard standby hängt hinterher archive gap standby controlfile ORA-01110 v$archive_gap apply lag Data Guard ist vorhanden: Du holst eine auf dem Primary verlorene Datendatei vom Standby bzw. einen zurückliegenden Standby oder ein verlorenes Standby-Controlfile vom Primary, ohne Backup Piec"}]},{"id":"ora-goldengate","file":"install/guides/ora-goldengate.json","group":"Oracle","engineId":"oracle","category":"ha","title":"Oracle GoldenGate Microservices: unidirektionale Replikation Oracle → Oracle","summary":"Repliziert ein PDB-Schema mit GoldenGate 21c oder 23ai/26ai Microservices kontinuierlich (mit Sekunden Verzögerung) in eine andere Datenbank. Am Ende laufen auf der Quelle Integrated Extract und ein Distribution Path, au","level":"Fortgeschritten","duration":"3–5 Stunden","topology":"Quell-DB-Server (OGG-Quell-Deployment) + Ziel-DB-Server (OGG-Ziel-Deployment); bei RAC GoldenGate auf Node 1 (oder mit XAG als Cluster-Ressource)","tags":["oracle","goldengate","ogg","microservices","replikation","replication","cdc","integrated extract","parallel replicat","distribution path","adminclient","oggca","21c","23ai","26ai","rac","scan","xag","acfs","tranlogoptions"],"order":30,"steps":42,"scenarios":[]},{"id":"ora-goldengate-ops","file":"install/guides/ora-goldengate-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Oracle GoldenGate: täglicher Betrieb und Störungsszenarien (Microservices 21c / 23ai)","summary":"Runbook für den täglichen Betrieb und die Störungsbehebung einer installierten GoldenGate-Microservices-Replikation (zur Installation siehe den GoldenGate-Installationsguide). Wenn du nicht sicher bist, was zu tun ist, b","level":"Fortgeschritten","duration":"5 min (Prüfung) – 2 Stunden (Upgrade, Tabelle hinzufügen)","topology":"Quellserver (Quell-Deployment, Integrated Extract, Distribution Path) + Zielserver (Ziel-Deployment, Parallel Replicat)","tags":["oracle","goldengate","ogg","microservices","adminclient","runbook","abend","extract","replicat","lag","stats","info all","heartbeat","report","discard","reperror","handlecollisions","cdr","konflikt","add trandata","schematrandata","aftercsn","atcsn","mapexclude","tableexclude","resync","purge trail","purgeoldextracts","trail","alter extract scn","etrollover","upgrade","patch","opatch","oggHome","data guard","switchover","failover","handledlfailover","OGG-00664","OGG-01004","OGG-01296","OGG-02912","21c","23ai","26ai"],"order":31,"steps":8,"scenarios":[{"k":"scenario","v":"health","l":"Zustands- und Lag-Prüfung (läuft alles?)","m":"Überwachung und Prüfung INFO ALL lag verzögerung STATS heartbeat GG_LAG Service Manager ggserr.log OGG-01027 OGG-01738 Long Running Transaction STOPPED RUNNING Distribution Path DISTPATH Receiver INFO DISTPATH replikation verzögert replication lag Nur lesend: Service Manager und Microservices, INFO ALL, Lag, STATS, Heartbeat-Verzögerung, Capture-Status in der Datenbank und ggserr.log; es wird nich"},{"k":"scenario","v":"ext_abend","l":"Extract ABENDED (mit Fehler gestoppt)","m":"Störung (ABEND) Extract ABENDED ABEND OGG-00664 OGG-00446 OGG-02171 OGG-02912 OGG-02022 OGG-02091 OGG-01044 OGG-01668 ORA-01291 ORA-01017 ORA-28001 ORA-04031 ORA-01031 missing log archived log not found logmining OGG-00868 Extract STARTING Extract gestoppt capture ABORTED Der Extract auf der Quelle ist ABENDED oder startet ständig neu: Anhand des OGG-xxxxx-Fehlers in der Report-Datei wird die Ursa"},{"k":"scenario","v":"rep_abend","l":"Replicat ABENDED (Anwenden auf das Ziel schlägt fehl)","m":"Störung (ABEND) Replicat ABENDED OGG-01004 OGG-01296 OGG-01154 OGG-00869 OGG-01161 OGG-01169 OGG-01431 OGG-00199 OGG-00423 ORA-01403 ORA-00001 ORA-02291 ORA-00942 ORA-01653 no data found unique constraint discard REPERROR HANDLECOLLISIONS CDR konflikt Replicat gestoppt ORA-01400 ORA-12899 value too large Der Replicat auf dem Ziel konnte eine Transaktion nicht anwenden: Aus Report- und Discard-Date"},{"k":"scenario","v":"add_table","l":"Befüllte Tabelle in die Replikation aufnehmen (Initial Load + AFTERCSN)","m":"Tabellenverwaltung tabelle hinzufügen add table ADD TRANDATA ADD SCHEMATRANDATA AFTERCSN ATCSN initial load erstbefüllung flashback_scn MAPEXCLUDE neue tabelle Nimmt eine vorhandene, bereits befüllte Tabelle ohne Downtime in eine laufende Replikation auf: ADD TRANDATA, expdp/impdp zu einer konsistenten SCN und ein eigener Replicat, der nur die Änderungen dieser Tabelle nach dieser SCN anwendet (AF"},{"k":"scenario","v":"remove_table","l":"Tabelle aus der Replikation herausnehmen","m":"Tabellenverwaltung tabelle entfernen remove table TABLEEXCLUDE MAPEXCLUDE DELETE TRANDATA exclude Die Tabelle wird nicht mehr repliziert: TABLEEXCLUDE im Extract (oder die TABLE-Zeile entfernen), MAPEXCLUDE im Replicat, bei Bedarf DELETE TRANDATA. Die Prozesse werden kurz neu gestartet."},{"k":"scenario","v":"resync","l":"Einzelne Tabelle neu synchronisieren (Resync)","m":"Tabellenverwaltung resync neu synchronisieren out of sync datenabweichung abweichend Veridata OGG-01004 ORA-01403 ORA-00001 row count zeilenanzahl unterschiedlich Die Daten einer Tabelle auf dem Ziel weichen von der Quelle ab (manuelle Änderung, übersprungener Fehler, unvollständiger Initial Load): Die Tabelle wird zu einer konsistenten SCN neu aus der Quelle geladen, und ein temporärer Replicat w"},{"k":"scenario","v":"purge","l":"Trail-Dateien füllen die Platte: Bereinigung (Purge Trail)","m":"Wartung trail purge PURGEOLDEXTRACTS Purge Trail platte voll No space left on device trail bereinigen var/lib/data trail-verzeichnis voll ENOSPC Das Trail-Verzeichnis wächst oder die Platte ist fast voll: Du siehst, welche Dateien gerade gelesen werden; ein Purge-Trail-Task (das Gegenstück zu PURGEOLDEXTRACTS in Classic) wird mit USECHECKPOINTS definiert; im Notfall werden bereits gelesene Dateien"},{"k":"scenario","v":"reposition","l":"Extract ab einer bestimmten SCN neu positionieren","m":"Wartung reposition ALTER EXTRACT SCN BEGIN SCN ETROLLOVER ab scn starten trail-verlust first_scn zurückspulen Es gibt eine SCN, ab der der Extract neu beginnen soll (Trail-Verlust, falscher Startpunkt, Zurücksetzen auf dem Ziel): Er wird mit ALTER EXTRACT … SCN und ETROLLOVER ab einer neuen Trail-Datei gestartet. Dieselben Daten können ein zweites Mal am Ziel ankommen."},{"k":"scenario","v":"upgrade","l":"GoldenGate-Versions-Upgrade / Patch (Microservices)","m":"Wartung upgrade patch aktualisierung OPatch out-of-place oggHome Service Manager upgrade bundle patch 21c 23ai 26ai release update GoldenGate patch Service Manager PATCH Wechsel auf ein neues GoldenGate-Release oder einen Patch: Out-of-Place (neues Home installieren, Service Manager und Deployments per REST ins neue Home übernehmen) oder In-Place mit OPatch. Extract/Replicat stehen kurz; Rollback "},{"k":"scenario","v":"dg_role","l":"Extract nach einem Data-Guard-Switchover/Failover auf der Quelle weiterbetreiben","m":"Rollenwechsel Data Guard switchover failover role transition HANDLEDLFAILOVER ORA-01033 ORA-16000 ORA-01109 neue primary standby FSFO ORA-12514 rollenwechsel Extract neue Primary FAILOVERTARGETDESTID Auf der Quelldatenbank gab es einen Switchover oder Failover; der Extract muss sich mit der neuen Primary verbinden und ab seinem Checkpoint weiterlaufen: Geprüft werden Verbindung (Credential-Alias/S"}]},{"id":"ora-security","file":"install/guides/ora-security.json","group":"Oracle","engineId":"oracle","category":"security","title":"Oracle Database Security Hardening (Prüfung, Netzwerk, TLS, Authentifizierung, Berechtigungen, Unified Auditing, TDE)","summary":"Dies ist kein Installations-Guide: Er findet und schließt die Lücken einer laufenden Oracle-19c-/23ai-(26ai-)Datenbank (Single Instance, Oracle Restart oder RAC; CDB/PDB). Zuerst listet die schreibgeschützte Sicherheitsp","level":"Fortgeschritten","duration":"5 Min. (Prüfung) – 4 Std. (vollständiges Hardening)","topology":"Single Instance / Oracle Restart / RAC (2–8 Nodes), CDB + PDB; Anwendungsserver (Client-Prüfung)","tags":["oracle","sicherheit","security","hardening","datenbank härten","cis benchmark","stig","tcps","wallet","orapki","sqlnet.ora","native network encryption","TCP.VALIDNODE_CHECKING","ALLOWED_LOGON_VERSION_SERVER","profile","ORA12C_STRONG_VERIFY_FUNCTION","unified auditing","AUDSYS","DBMS_AUDIT_MGMT","TDE","keystore","ADMINISTER KEY MANAGEMENT","UTL_FILE","PUBLIC","standardpasswort"],"order":40,"steps":2,"scenarios":[{"k":"scenario","v":"scan","l":"Sicherheitsprüfung: Welche Lücken habe ich? (schreibgeschützt)","m":"Hier beginnen datenbank sicherheitsprüfung security audit hardening check CIS STIG DBSAT sicherheitslücke schwachstelle vulnerability pentest befund dba_users_with_defpwd prüfabfrage sicherheitsscan security scan KRITISCH Der erste Scan: Konten mit Standardpasswort und offene Konten, alte Passwort-Verifier, Profile, DBA-/ANY-Rechte, an PUBLIC vergebene UTL_*-PL/SQL-Packages, Auditing, TDE, unversc"},{"k":"scenario","v":"all","l":"Vollständiges Hardening (alle Bereiche der Reihe nach)","m":"Hier beginnen hardening datenbank härten komplette sicherheit full hardening cis benchmark umsetzen STIG Neu installierte oder nie gehärtete Datenbank: Alle Bereiche werden in einer sicheren Reihenfolge umgesetzt; Listener und Datenbank werden ein- bis zweimal neu gestartet (Wartungsfenster erforderlich)."},{"k":"scenario","v":"net","l":"Netzwerk und Verbindungen: Wer darf sich von wo verbinden? (Listener, TCP.VALIDNODE_CHECKING, Firewall)","m":"Netzwerk und Authentifizierung TCP.VALIDNODE_CHECKING TCP.INVITED_NODES listener 1521 5500 ORA-12537 TNS-12537 ORA-12541 ADMIN_RESTRICTIONS firewall TNS-12546 ORA-12170 invited nodes listener.log zugriff von außen zugriff einschränken Der Listener ist für jede Adresse offen, Invited Nodes sind nicht definiert, 1521/5500 sind für alle erreichbar. Der Listener wird neu gestartet (bestehende Sessions"},{"k":"scenario","v":"tls","l":"Verbindungen verschlüsseln (TCPS-Wallet oder Native Network Encryption)","m":"Netzwerk und Authentifizierung TCPS SSL TLS wallet orapki SQLNET.ENCRYPTION_SERVER ORA-28860 ORA-29024 ORA-28759 ORA-29106 ORA-12696 ORA-12660 SSL_SERVER_DN_MATCH zertifikat abgelaufen ORA-29003 ORA-29002 TNS-12560 TLS zertifikatskette certificate chain native network encryption Der Netzwerkverkehr ist unverschlüsselt; TLS (TCPS, Wallet, Zertifikat, SSL_SERVER_DN_MATCH auf dem Client) oder zertifi"},{"k":"scenario","v":"auth","l":"Authentifizierung und Passwörter (12C-Verifier, Profile, Standardkonten)","m":"Netzwerk und Authentifizierung ALLOWED_LOGON_VERSION_SERVER ORA-28040 ORA-01017 ORA-28000 ORA-28001 ORA-28003 ORA-28007 password_versions 10G PROFILE ORA12C_STRONG_VERIFY_FUNCTION standardpasswort default password SCOTT ORA-65066 ORA-00988 password_versions 10G passwortprofil password policy FAILED_LOGIN_ATTEMPTS 10G-/11G-Passwort-Verifier, kein Passwortprofil, Konten mit Standardpasswort oder Bei"},{"k":"scenario","v":"priv","l":"Least Privilege: Rollen, PUBLIC und das Anwendungskonto","m":"Berechtigungen und Audit Trail PUBLIC UTL_FILE UTL_TCP UTL_HTTP UTL_SMTP DBA role RESOURCE ANY privilege least privilege minimale rechte ORA-01031 ORA-00942 schema-only account PLS-00201 ORA-04021 GRANT ANY UNLIMITED TABLESPACE berechtigungsbericht DBA-/RESOURCE-/ANY-Rechte beim Anwendungskonto, UTL_FILE/UTL_TCP/UTL_HTTP an PUBLIC vergeben; Schema-Owner, Lese-/Schreibrollen und Berechtigungsberich"},{"k":"scenario","v":"audit","l":"Auditing: Wer hat was getan? (Unified Auditing, AUDSYS-Bereinigung)","m":"Berechtigungen und Audit Trail unified auditing AUDIT POLICY AUDSYS DBMS_AUDIT_MGMT UNIFIED_AUDIT_TRAIL ORA_LOGON_FAILURES ORA_SECURECONFIG UNIFIED_AUDIT_SYSTEMLOG SYSAUX voll audit trail SIEM ORA-46267 unified audit policy wer hat was gemacht who changed Der Auditor verlangt Nachweise über Anmeldungen, Konto- und Schemaänderungen; Unified Audit Policies, Bereinigung von AUDSYS nach Aufbewahrungsf"},{"k":"scenario","v":"tde","l":"Daten auf der Platte verschlüsseln (TDE)","m":"Daten und Backups TDE transparent data encryption keystore wallet ADMINISTER KEY MANAGEMENT WALLET_ROOT ORA-28365 ORA-28374 ORA-46665 ENCRYPT_NEW_TABLESPACES encryption at rest ORA-28367 ORA-46658 ORA-28353 Advanced Security master key TDE_CONFIGURATION TDE gegen das Auslesen von Datafiles, Backups oder Plattenkopien: WALLET_ROOT, Keystore, Master Key, Tablespace-Verschlüsselung (online). Erforder"},{"k":"scenario","v":"backup","l":"Sicherheit der Backups (RMAN-Verschlüsselung, Automatisierung ohne Passwort, Berechtigungen)","m":"Daten und Backups RMAN encryption CONFIGURE ENCRYPTION ORA-19913 expdp ENCRYPTION secure external password store mkstore SEPS backup berechtigungen ORA-19914 Advanced Security Lizenz backup unverschlüsselt unencrypted backup ENCRYPTION_PWD_PROMPT RMAN-/Data-Pump-Backups sind unverschlüsselt, Skripte enthalten Klartextpasswörter, das Backup-Verzeichnis ist für alle lesbar; RMAN Encryption, ENCRYPTI"},{"k":"scenario","v":"os","l":"Betriebssystemseite (oracle-Konto, Dateiberechtigungen, Core Dumps, SELinux)","m":"Server und Version SELinux orapw oracle konto dateiberechtigungen core dump ORACLE_HOME berechtigungen sudo avc: denied passwd -l oracle setuid 6751 limits.d Das Konto oracle hat ein Passwort und kann sich anmelden, die Berechtigungen auf Passwortdatei / Network Admin / Wallet sind zu offen, SELinux ist deaktiviert. Das Core-Dump-Limit greift in einer neuen Session bzw. nach einem Neustart."},{"k":"scenario","v":"patch","l":"Patches und Version: Bin ich auf dem neuesten RU?","m":"Server und Version RU release update CPU Critical Patch Update opatch lspatches datapatch dba_registry_sqlpatch CVE support beendet opatch Doc ID 742060.1 patch zeitplan Installiertes RU, datapatch-Status und Support-Zeitplan; verweist zum Einspielen auf den RU-Patch-Guide. Schreibgeschützt."}]},{"id":"ora-oem","file":"install/guides/ora-oem.json","group":"Oracle","engineId":"oracle","category":"monitoring","title":"Installation von Oracle Enterprise Manager Cloud Control 13c Release 5 (13.5)","summary":"Installiert Oracle Enterprise Manager Cloud Control 13.5: Eine vorhandene 19c-Datenbank wird als Management Repository vorbereitet (dokumentierte Parameter, Zurücksetzen der Optimizer-Einstellungen), auf dem OMS-Server e","level":"Fortgeschritten","duration":"3–5 Stunden","topology":"OMS-Server + Repository-Datenbankserver (+ zu überwachende Zielserver)","tags":["oracle","enterprise manager","oem","em13c","13.5","cloud control","oms","agent","emctl","emcli","monitoring","überwachung","rac","cluster","discover_db","data guard","exportconfig"],"order":40,"steps":32,"scenarios":[]}]}}