IO: DataFileRead - Amazon Aurora

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

IO: DataFileRead

Das IO:DataFileRead-Ereignis tritt auf, wenn eine Verbindung darauf wartet, dass ein Backend-Prozess eine erforderliche Seite aus dem Speicher liest, da die Seite nicht im gemeinsam genutzten Speicher verfügbar ist.

Unterstützte Engine-Versionen

Diese Informationen zum Warteereignis werden für alle Versionen von Aurora Postgre SQL unterstützt.

Kontext

Alle Abfragen und Datenmanipulationsvorgänge (DML) greifen auf Seiten im Pufferpool zu. Zu den Anweisungen, die Lesevorgänge auslösen können, gehören SELECT, UPDATE und DELETE. Ein UPDATE kann beispielsweise Seiten aus Tabellen oder Indizes lesen. Wenn sich die angeforderte oder aktualisierte Seite nicht im gemeinsam genutzten Pufferpool befindet, kann dieser Lesevorgang zum IO:DataFileRead-Ereignis führen.

Da der gemeinsame Pufferpool endlich ist, kann er sich füllen. In diesem Fall zwingen Anfragen nach Seiten, die sich nicht im Speicher befinden, die Datenbank dazu, Blöcke von der Festplatte zu lesen. Wenn das IO:DataFileRead-Ereignis häufig auftritt, ist Ihr gemeinsam genutzter Pufferpool möglicherweise zu klein für Ihre Workload. Dieses Problem ist bei SELECT-Abfragen akut, die eine große Anzahl von Zeilen lesen, die nicht in den Pufferpool passen. Weitere Informationen zum Pufferpool finden Sie unter Puffer Pool.

Wahrscheinliche Ursachen für erhöhte Wartezeiten

Häufige Ursachen für das Ereignis IO:DataFileRead sind die folgenden:

Verbindungsspitzen

Möglicherweise stellen Sie fest, dass mehrere Verbindungen dieselbe Anzahl von IO: DataFileRead Wait-Ereignissen generieren. In diesem Fall kann eine Spitze (plötzlicher und starker Anstieg) bei IO:DataFileRead-Ereignissen auftreten.

SELECTund DML Anweisungen, die sequentielle Scans durchführen

Ihre Anwendung führt möglicherweise einen neuen Vorgang aus. Oder ein vorhandener Vorgang könnte sich aufgrund eines neuen Ausführungsplans ändern. Suchen Sie in solchen Fällen nach Tabellen (insbesondere großen Tabellen), die einen größeren seq_scan-Wert haben. Finden Sie sie, indem Sie pg_stat_user_tables abfragen. Verwenden Sie die Erweiterung pg_stat_statements, um Abfragen zu verfolgen, die mehr Lesevorgänge generieren.

CTASund CREATE INDEX für große Datensätze

A CTASist eine CREATE TABLE AS SELECT Aussage. Wenn Sie eine ausführen und dabei einen großen Datensatz als Quelle CTAS verwenden oder einen Index für eine große Tabelle erstellen, kann das IO:DataFileRead Ereignis eintreten. Wenn Sie einen Index erstellen, muss die Datenbank möglicherweise das gesamte Objekt mithilfe eines sequentiellen Scans lesen. A CTAS generiert IO:DataFile Lesevorgänge, wenn sich Seiten nicht im Speicher befinden.

Mehrere Vakuumarbeiter laufen gleichzeitig

Vakuumarbeiter können manuell oder automatisch ausgelöst werden. Wir empfehlen, eine aggressive Vakuumstrategie zu verabschieden. Wenn eine Tabelle jedoch viele aktualisierte oder gelöschte Zeilen enthält, erhöhen sich die IO:DataFileRead-Wartezeiten. Nachdem der Raum zurückgewonnen wurde, nimmt die für IO:DataFileRead aufgewendete Vakuumzeit ab.

Aufnahme großer Datenmengen

Wenn Ihre Anwendung große Datenmengen aufnimmt, können ANALYZE-Vorgänge häufiger auftreten. Der ANALYZE-Prozess kann durch einen Autovacuum Launcher ausgelöst oder manuell aufgerufen werden.

Der ANALYZE-Vorgang liest eine Teilmenge der Tabelle. Die Anzahl der zu scannenden Seiten wird berechnet, indem 30 mit dem default_statistics_target-Wert multipliziert wird. Weitere Informationen finden Sie in der SQLPostgre-Dokumentation. Der Parameter default_statistics_target akzeptiert Werte zwischen 1 und 10.000, wobei der Standardwert 100 ist.

Hungertod

Wenn die Netzwerkbandbreite oder CPU die Netzwerkbandbreite der Instanz verbraucht sind, kann das IO:DataFileRead Ereignis häufiger auftreten.

Aktionen

Abhängig von den Ursachen Ihres Warteereignisses empfehlen wir verschiedene Aktionen.

Überprüfen Sie Prädikatfilter auf Abfragen, die Wartezeiten generieren

Angenommen, Sie identifizieren bestimmte Abfragen, die IO:DataFileRead-Warteereignisse generieren. Sie können sie mit den folgenden Techniken identifizieren:

  • Performance Insights

  • Katalogansichten wie die der Erweiterung pg_stat_statements

  • Die Katalogansicht pg_stat_all_tables, wenn sie periodisch eine erhöhte Anzahl von physischen Lesevorgängen anzeigt

  • Die pg_statio_all_tables-Ansicht, wenn sie zeigt, dass _read-Zähler steigen

Wir empfehlen Ihnen, zu bestimmen, welche Filter im Prädikat (WHERE-Klausel) dieser Abfragen verwendet werden. Befolgen Sie diese Richtlinien:

  • Führen Sie den Befehl EXPLAIN aus. Geben Sie in der Ausgabe an, welche Arten von Scans verwendet werden. Ein sequentieller Scan weist nicht zwangsläufig ein Problem an. Abfragen, die sequenzielle Scans verwenden, erzeugen im Vergleich zu Abfragen, die Filter verwenden, natürlich mehr IO:DataFileRead-Ereignisse.

    Finden Sie heraus, ob die in der WHERE-Klausel aufgeführte Spalte indiziert ist. Wenn nicht, erwägen Sie, einen Index für diese Spalte zu erstellen. Dieser Ansatz vermeidet die sequentiellen Scans und reduziert die IO:DataFileRead-Ereignisse. Wenn eine Abfrage restriktive Filter enthält und immer noch sequenzielle Scans erzeugt, bewerten Sie, ob die richtigen Indizes verwendet werden.

  • Finden Sie heraus, ob die Abfrage auf eine sehr große Tabelle zugreift. In einigen Fällen kann das Partitionieren einer Tabelle die Leistung verbessern, sodass die Abfrage nur notwendige Partitionen lesen kann.

  • Untersuchen Sie die Kardinalität (Gesamtzahl der Zeilen) Ihrer Join-Vorgänge. Beachten Sie, wie restriktiv die Werte sind, die Sie den Filtern für Ihre WHERE-Klausel übergeben. Wenn möglich, stimmen Sie Ihre Abfrage ein, um die Anzahl der Zeilen zu reduzieren, die in jedem Schritt des Plans übergeben werden.

Minimieren Sie die Auswirkungen von Wartungsvorgängen

Wartungsvorgänge wie VACUUM und ANALYZE sind wichtig. Wir empfehlen, sie nicht zu deaktivieren, da Sie IO:DataFileRead-Warteereignisse im Zusammenhang mit diesen Wartungsvorgängen finden. Die folgenden Ansätze können die Auswirkungen dieser Vorgänge minimieren:

  • Führen Sie Wartungsvorgänge während außerhalb der Hauptverkehrszeiten manuell aus. Diese Technik verhindert, dass die Datenbank den Schwellenwert für automatische Vorgänge erreicht.

  • Erwägen Sie bei sehr großen Tabellen, die Tabelle zu partitionieren. Diese Technik reduziert den Overhead von Wartungsvorgängen. Die Datenbank greift nur auf die Partitionen zu, die gewartet werden müssen.

  • Wenn Sie große Datenmengen aufnehmen, sollten Sie die Funktion zur automatischen Analyse deaktivieren.

Die Auto-Vakuum-Funktion wird automatisch für eine Tabelle ausgelöst, wenn die folgende Formel zutrifft.

pg_stat_user_tables.n_dead_tup > (pg_class.reltuples x autovacuum_vacuum_scale_factor) + autovacuum_vacuum_threshold

Die Ansicht pg_stat_user_tables und der Katalog pg_class haben mehrere Zeilen. Eine Zeile kann einer Zeile in Ihrer Tabelle entsprechen. Diese Formel geht davon aus, dass die reltuples für eine bestimmte Tabelle stehen. Die Parameter autovacuum_vacuum_scale_factor (standardmäßig 0.20) und autovacuum_vacuum_threshold (standardmäßig 50 Tupel) werden normalerweise global für die gesamte Instance gesetzt. Sie können jedoch verschiedene Werte für eine bestimmte Tabelle festlegen.

Suchen Sie nach Tabellen, die unnötigerweise Speicherplatz belegen.

Um Tabellen zu finden, die mehr Speicherplatz belegen als nötig, führen Sie die folgende Abfrage aus. Wenn diese Abfrage von einer Datenbankbenutzerrolle ausgeführt wird, die nicht über die rds_superuser-Rolle verfügt, gibt sie nur Informationen zu den Tabellen zurück, für die die Benutzerrolle Leserechte besitzt. Diese Abfrage wird von SQL Postgre-Version 12 und späteren Versionen unterstützt.

WITH report AS ( SELECT schemaname ,tblname ,n_dead_tup ,n_live_tup ,block_size*tblpages AS real_size ,(tblpages-est_tblpages)*block_size AS extra_size ,CASE WHEN tblpages - est_tblpages > 0 THEN 100 * (tblpages - est_tblpages)/tblpages::float ELSE 0 END AS extra_ratio, fillfactor, (tblpages-est_tblpages_ff)*block_size AS bloat_size ,CASE WHEN tblpages - est_tblpages_ff > 0 THEN 100 * (tblpages - est_tblpages_ff)/tblpages::float ELSE 0 END AS bloat_ratio ,is_na FROM ( SELECT ceil( reltuples / ( (block_size-page_hdr)/tpl_size ) ) + ceil( toasttuples / 4 ) AS est_tblpages ,ceil( reltuples / ( (block_size-page_hdr)*fillfactor/(tpl_size*100) ) ) + ceil( toasttuples / 4 ) AS est_tblpages_ff ,tblpages ,fillfactor ,block_size ,tblid ,schemaname ,tblname ,n_dead_tup ,n_live_tup ,heappages ,toastpages ,is_na FROM ( SELECT ( 4 + tpl_hdr_size + tpl_data_size + (2*ma) - CASE WHEN tpl_hdr_size%ma = 0 THEN ma ELSE tpl_hdr_size%ma END - CASE WHEN ceil(tpl_data_size)::int%ma = 0 THEN ma ELSE ceil(tpl_data_size)::int%ma END ) AS tpl_size ,block_size - page_hdr AS size_per_block ,(heappages + toastpages) AS tblpages ,heappages ,toastpages ,reltuples ,toasttuples ,block_size ,page_hdr ,tblid ,schemaname ,tblname ,fillfactor ,is_na ,n_dead_tup ,n_live_tup FROM ( SELECT tbl.oid AS tblid ,ns.nspname AS schemaname ,tbl.relname AS tblname ,tbl.reltuples AS reltuples ,tbl.relpages AS heappages ,coalesce(toast.relpages, 0) AS toastpages ,coalesce(toast.reltuples, 0) AS toasttuples ,psat.n_dead_tup AS n_dead_tup ,psat.n_live_tup AS n_live_tup ,24 AS page_hdr ,current_setting('block_size')::numeric AS block_size ,coalesce(substring( array_to_string(tbl.reloptions, ' ') FROM 'fillfactor=([0-9]+)')::smallint, 100) AS fillfactor ,CASE WHEN version()~'mingw32' OR version()~'64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS ma ,23 + CASE WHEN MAX(coalesce(null_frac,0)) > 0 THEN ( 7 + count(*) ) / 8 ELSE 0::int END AS tpl_hdr_size ,sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024) ) AS tpl_data_size ,bool_or(att.atttypid = 'pg_catalog.name'::regtype) OR count(att.attname) <> count(s.attname) AS is_na FROM pg_attribute AS att JOIN pg_class AS tbl ON (att.attrelid = tbl.oid) JOIN pg_stat_all_tables AS psat ON (tbl.oid = psat.relid) JOIN pg_namespace AS ns ON (ns.oid = tbl.relnamespace) LEFT JOIN pg_stats AS s ON (s.schemaname=ns.nspname AND s.tablename = tbl.relname AND s.inherited=false AND s.attname=att.attname) LEFT JOIN pg_class AS toast ON (tbl.reltoastrelid = toast.oid) WHERE att.attnum > 0 AND NOT att.attisdropped AND tbl.relkind = 'r' GROUP BY tbl.oid, ns.nspname, tbl.relname, tbl.reltuples, tbl.relpages, toastpages, toasttuples, fillfactor, block_size, ma, n_dead_tup, n_live_tup ORDER BY schemaname, tblname ) AS s ) AS s2 ) AS s3 ORDER BY bloat_size DESC ) SELECT * FROM report WHERE bloat_ratio != 0 -- AND schemaname = 'public' -- AND tblname = 'pgbench_accounts' ; -- WHERE NOT is_na -- AND tblpages*((pst).free_percent + (pst).dead_tuple_percent)::float4/100 >= 1

Sie können in Ihrer Anwendung nach einer Überlastung von Tabellen und Indizes suchen. Weitere Informationen finden Sie unter Diagnostizieren einer Überlastung von Tabellen und Indizes.

Finden Sie Indizes mit unnötigem Speicherplatz

Um Indizes zu finden, die unnötigen Speicherplatz benötigen, führen Sie die folgende Abfrage aus.

-- WARNING: run with a nonsuperuser role, the query inspects -- only indexes on tables you have permissions to read. -- WARNING: rows with is_na = 't' are known to have bad statistics ("name" type is not supported). -- This query is compatible with PostgreSQL 8.2 and later. SELECT current_database(), nspname AS schemaname, tblname, idxname, bs*(relpages)::bigint AS real_size, bs*(relpages-est_pages)::bigint AS extra_size, 100 * (relpages-est_pages)::float / relpages AS extra_ratio, fillfactor, bs*(relpages-est_pages_ff) AS bloat_size, 100 * (relpages-est_pages_ff)::float / relpages AS bloat_ratio, is_na -- , 100-(sub.pst).avg_leaf_density, est_pages, index_tuple_hdr_bm, -- maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, sub.reltuples, sub.relpages -- (DEBUG INFO) FROM ( SELECT coalesce(1 + ceil(reltuples/floor((bs-pageopqdata-pagehdr)/(4+nulldatahdrwidth)::float)), 0 -- ItemIdData size + computed avg size of a tuple (nulldatahdrwidth) ) AS est_pages, coalesce(1 + ceil(reltuples/floor((bs-pageopqdata-pagehdr)*fillfactor/(100*(4+nulldatahdrwidth)::float))), 0 ) AS est_pages_ff, bs, nspname, table_oid, tblname, idxname, relpages, fillfactor, is_na -- , stattuple.pgstatindex(quote_ident(nspname)||'.'||quote_ident(idxname)) AS pst, -- index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples -- (DEBUG INFO) FROM ( SELECT maxalign, bs, nspname, tblname, idxname, reltuples, relpages, relam, table_oid, fillfactor, ( index_tuple_hdr_bm + maxalign - CASE -- Add padding to the index tuple header to align on MAXALIGN WHEN index_tuple_hdr_bm%maxalign = 0 THEN maxalign ELSE index_tuple_hdr_bm%maxalign END + nulldatawidth + maxalign - CASE -- Add padding to the data to align on MAXALIGN WHEN nulldatawidth = 0 THEN 0 WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign ELSE nulldatawidth::integer%maxalign END )::numeric AS nulldatahdrwidth, pagehdr, pageopqdata, is_na -- , index_tuple_hdr_bm, nulldatawidth -- (DEBUG INFO) FROM ( SELECT i.nspname, i.tblname, i.idxname, i.reltuples, i.relpages, i.relam, a.attrelid AS table_oid, current_setting('block_size')::numeric AS bs, fillfactor, CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?) WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS maxalign, /* per page header, fixed size: 20 for 7.X, 24 for others */ 24 AS pagehdr, /* per page btree opaque data */ 16 AS pageopqdata, /* per tuple header: add IndexAttributeBitMapData if some cols are null-able */ CASE WHEN max(coalesce(s.null_frac,0)) = 0 THEN 2 -- IndexTupleData size ELSE 2 + (( 32 + 8 - 1 ) / 8) -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8) END AS index_tuple_hdr_bm, /* data len: we remove null values save space using it fractionnal part from stats */ sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) AS nulldatawidth, max( CASE WHEN a.atttypid = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END ) > 0 AS is_na FROM pg_attribute AS a JOIN ( SELECT nspname, tbl.relname AS tblname, idx.relname AS idxname, idx.reltuples, idx.relpages, idx.relam, indrelid, indexrelid, indkey::smallint[] AS attnum, coalesce(substring( array_to_string(idx.reloptions, ' ') from 'fillfactor=([0-9]+)')::smallint, 90) AS fillfactor FROM pg_index JOIN pg_class idx ON idx.oid=pg_index.indexrelid JOIN pg_class tbl ON tbl.oid=pg_index.indrelid JOIN pg_namespace ON pg_namespace.oid = idx.relnamespace WHERE pg_index.indisvalid AND tbl.relkind = 'r' AND idx.relpages > 0 ) AS i ON a.attrelid = i.indexrelid JOIN pg_stats AS s ON s.schemaname = i.nspname AND ((s.tablename = i.tblname AND s.attname = pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE)) -- stats from tbl OR (s.tablename = i.idxname AND s.attname = a.attname)) -- stats from functionnal cols JOIN pg_type AS t ON a.atttypid = t.oid WHERE a.attnum > 0 GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9 ) AS s1 ) AS s2 JOIN pg_am am ON s2.relam = am.oid WHERE am.amname = 'btree' ) AS sub -- WHERE NOT is_na ORDER BY 2,3,4;

Finden Sie Tabellen, die für die automatische Vakuumierung berechtigt sind

Führen Sie die folgende Abfrage aus, um Tabellen zu finden, die für die automatische Vakuumierung berechtigt sind.

--This query shows tables that need vacuuming and are eligible candidates. --The following query lists all tables that are due to be processed by autovacuum. -- During normal operation, this query should return very little. WITH vbt AS (SELECT setting AS autovacuum_vacuum_threshold FROM pg_settings WHERE name = 'autovacuum_vacuum_threshold') , vsf AS (SELECT setting AS autovacuum_vacuum_scale_factor FROM pg_settings WHERE name = 'autovacuum_vacuum_scale_factor') , fma AS (SELECT setting AS autovacuum_freeze_max_age FROM pg_settings WHERE name = 'autovacuum_freeze_max_age') , sto AS (SELECT opt_oid, split_part(setting, '=', 1) as param, split_part(setting, '=', 2) as value FROM (SELECT oid opt_oid, unnest(reloptions) setting FROM pg_class) opt) SELECT '"'||ns.nspname||'"."'||c.relname||'"' as relation , pg_size_pretty(pg_table_size(c.oid)) as table_size , age(relfrozenxid) as xid_age , coalesce(cfma.value::float, autovacuum_freeze_max_age::float) autovacuum_freeze_max_age , (coalesce(cvbt.value::float, autovacuum_vacuum_threshold::float) + coalesce(cvsf.value::float,autovacuum_vacuum_scale_factor::float) * c.reltuples) as autovacuum_vacuum_tuples , n_dead_tup as dead_tuples FROM pg_class c JOIN pg_namespace ns ON ns.oid = c.relnamespace JOIN pg_stat_all_tables stat ON stat.relid = c.oid JOIN vbt on (1=1) JOIN vsf ON (1=1) JOIN fma on (1=1) LEFT JOIN sto cvbt ON cvbt.param = 'autovacuum_vacuum_threshold' AND c.oid = cvbt.opt_oid LEFT JOIN sto cvsf ON cvsf.param = 'autovacuum_vacuum_scale_factor' AND c.oid = cvsf.opt_oid LEFT JOIN sto cfma ON cfma.param = 'autovacuum_freeze_max_age' AND c.oid = cfma.opt_oid WHERE c.relkind = 'r' AND nspname <> 'pg_catalog' AND ( age(relfrozenxid) >= coalesce(cfma.value::float, autovacuum_freeze_max_age::float) or coalesce(cvbt.value::float, autovacuum_vacuum_threshold::float) + coalesce(cvsf.value::float,autovacuum_vacuum_scale_factor::float) * c.reltuples <= n_dead_tup -- or 1 = 1 ) ORDER BY age(relfrozenxid) DESC;

Reagieren Sie auf eine hohe Anzahl von Verbindungen

Wenn Sie Amazon überwachen CloudWatch, stellen Sie möglicherweise fest, dass die DatabaseConnections Metrik ansteigt. Dieser Anstieg deutet auf eine erhöhte Anzahl von Verbindungen zu Ihrer Datenbank hin. Wir empfehlen folgende Vorgehensweise:

  • Beschränken Sie die Anzahl der Verbindungen, die die Anwendung mit jeder Instance öffnen kann. Wenn Ihre Anwendung über eine eingebettete Verbindungspool-Funktion verfügt, legen Sie eine angemessene Anzahl von Verbindungen fest. Basieren Sie die Zahl darauf, was vCPUs in Ihrer Instance effektiv parallelisiert werden kann.

    Wenn Ihre Anwendung keine Verbindungspool-Funktion verwendet, sollten Sie die Verwendung von Amazon RDS Proxy oder einer Alternative in Betracht ziehen. Mit diesem Ansatz können Ihre Anwendung mehrere Verbindungen mit dem Load Balancer öffnen. Der Balancer kann dann eine begrenzte Anzahl von Verbindungen mit der Datenbank öffnen. Da weniger Verbindungen parallel laufen, führt Ihre DB-Instance weniger Kontextwechsel im Kernel durch. Abfragen sollten schneller voranschreiten und zu weniger Warteereignissen führen. Weitere Informationen finden Sie unter Amazon RDS Proxy für Aurora verwenden.

  • Wann immer möglich, nutzen Sie Reader-Nodes für Aurora Postgre SQL und Read-Replicas RDS für Postgre. SQL Wenn Ihre Anwendung einen schreibgeschützten Vorgang ausführt, senden Sie diese Anfragen an den reader-Endpunkt. Diese Technik verbreitet Anwendungsanfragen auf alle Reader-Knoten, wodurch der I/O-Druck auf den Writer-Knoten reduziert wird.

  • Ziehen Sie, Ihre DB-Instance zu skalieren. Eine Instance-Klasse mit höherer Kapazität bietet mehr Speicher, wodurch Aurora Postgre über SQL einen größeren gemeinsamen Pufferpool für Seiten verfügt. Durch die größere Größe kann die DB-Instance auch mehr Verbindungen verarbeiten vCPUs . Mehr vCPUs sind besonders hilfreich, wenn es sich bei den Vorgängen, die IO:DataFileRead Warteereignisse erzeugen, um Schreibvorgänge handelt.