Snowflake kennt drei Arten von Caching. In diesem Blogbeitrag möchte ich anhand von Beispielen diese genauer beleuchten und nützliche Schlussfolgerungen daraus ziehen. Snowflake hat sich in den letzten Jahren von einem Cloud Data Warehouse zur Data Cloud entwickelt, einer Platform, die mit ihren unterschiedlichen Workloads bis dato nicht dagewesene Möglichkeiten bietet und weit mehr Bereiche abdecken als ein klassisches Warehouse. Um die Caching Ebenen besser einordnen zu können, werfen wir einen kurzen Blick auf die Snowflake Architektur.
Snowflake Architektur
Snowflake baut auf den drei grossen Cloud Service Providern auf: Google, AWS und Azure. Das ermöglicht auch eine Cross-Cloud-Strategie. Snowflake selbst besteht aus drei von einander getrennten Ebenen:
Storage Ebene: Hier werden Daten Spalten-basiert und von Snowflake optimiert in Mikro-Partitionen abgelegt. Diese sind zu jeder Zeit verschlüsselt und redundant gespeichert. Snowflake unterstützt ACID-Transaktionen, die Daten sind in dieser Schicht daher immer konsistent.
Compute Ebene: Die Rechenschicht ist vollständig von der Speicherschicht entkoppelt. Dies ermöglicht eine beliebige Anzahl von Workloads auf ihren eigenen dedizierten Compute Clustern – in Snowflake Warehouses genannt, selbst wenn diese Workloads auf dieselben Daten zugreifen. Je nachdem ob viele gleichzeitige Anfragen oder reine Rechenleistung gefragt ist, können unterschiedlich große und unterschiedlich viele Cluster unabhängig von einander eingesetzt werden. Snowflake nennt diese Architektur: Multi-Cluster, Shared Data Architektur. Ab dem Start eines Clusters ist der Mindestverbrauch eine Minute, auch wenn der Cluster nur eine Sekunde läuft. Danach rechnet Snowflake pro Sekunde ab. Jeder Cluster hat seinen eigenen lokalen Speicher, dessen Gebrauch in den Warehouse Credits enthalten ist.
Cloud Service Ebene: Sie bildet quasi das Gehirn von Snowflake ab. Zugang hat man hier nur über die zur Verfügung gestellten Schnittstellen. Hier werden die Benutzer Accounts und Sessions, Metadaten, Transaktionen, Abfragepläne, Sicherheit/Governance und viele andere Dienste intern verwaltet. Dank der nahezu unbegrenzten Ressourcen in der Cloud, ist auch diese Ebene hochgradig skalierbar. Der Gebrauch der Service Ebene ist kostenfrei, außer der Verbrauch eines Benutzers übersteigt mehr als 10% der gesamten, täglich-genutzten Compute-Zeit. Dieser 10%-Threshold wurde zum 01.02.2020 eingeführt.
Snowflake Caching
Es gibt drei Arten von Caches: (1) Warehouse Cache, (2) Result Cache, (3) Metadata Cache. Im Folgenden werde ich auf alle eingehen und anhand von Beispielen zeigen, was damit gemeint ist.
(1) Warehouse Cache
Der Warehouse Cache befindet sich auf der Compute Ebene und ist abhängig vom verwendeten Warehouse. Er wird daher oft auch “Local Disk Cache” genannt. Die Speicherkapazität ist abhängig von der Größe des gewählten Warehouses. Wann immer Daten für eine bestimmte Abfrage benötigt werden, werden sie aus der Storage Ebene abgerufen und im Warehouse zwischengespeichert. Jede weitere Abfrage kann nun auf diese bereits lokal liegenden Daten zugreifen. Es müssen dann nur die Mikro-Partitionen nachgeladen werden, die für eine weitere Abfrage noch fehlen. Je nachdem wie hoch der Grad der Wiederwendung und wie ausgelastet der Cache ist, werden Daten auch wieder aus dem Speicher entfernt. Durch das Herunterfahren eines Clusters leert sich der gesamte Cache. Dies geschieht aus Optimierungsgründen unter Umständen nicht sofort. Snowflake stellt Compute-Zeit abhängig von Größe des Warehouses in Rechnung. Dies kann je nach Größe eines laufenden Compute Clusters auf Dauer relativ kostspielig werden.
Zum Beispiel:
SELECT C_NAME, C_ADDRESS FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER"; SELECT C_NAME, C_ADDRESS, C_PHONE FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER"; # cache leeren, suspend warehouse ALTER WAREHOUSE IF EXISTS DEMO_L SUSPEND;
Query History:
In der Query History View werden Abfragen der letzten 14 Tage gespeichert. Hier sehen wir, dass bei der zweiten Abfrage 449.9 MB bereits lokal verfügbar sind. Sie befinden sich im Warehouse Cache. In diesem Fall fehlen die Mikro-Partitionen der Spalte “C_PHONE”. Anhand der Query ID können Abfragen in der Query Profile Ansicht auch noch weiter analysiert werden.
Query Profile:
1. Abfrage:
2. Abfrage:
Von besonderem Interesse ist hier die Übersicht. Sie gibt die Information “Percentage scanned from cache” direkt an. Darüber hinaus sehen wir eine erhebliche Performance-Steigerung der zweiten Abfrage. Sie ist schneller, obwohl sie mehr Daten (“Scanned Bytes”) verarbeitet hat.
(2) Result Cache
Wir fahren das Warehouse herunter und starten es erneut. Der Warehouse Cache ist nun leer. Nun führen wir die gleichen Abfragen noch einmal aus und stellen überraschenderweise noch ein anderes Verhalten fest:
- Die Ergebnisse sind unmittelbar verfügbar und
- Es wurden gar keine Daten geladen und prozessiert
Tatsächlich muss nicht einmal ein Warehouse gestartet werden, um die Abfrage-Ergebnisse zu erhalten. Damit kommen wir zur zweiten Caching Ebene.
Der Result Cache befindet sich auf der Cloud Service Ebene. Hier werden komplette Query-Results für 24 Stunden gespeichert und unabhängig vom Warehouse zur Verfügung gestellt. Andere Benutzer können die Ergebnisse ebenfalls abfragen. Wenn das Ergebnis innerhalb der 24 Stunden erneut abgerufen wird, wird diese Frist neu gesetzt. Dadurch kann die Speicherung von Abfrage-Ergebnissen im Result Cache bis auf 31 Tage verlängert werden.
Zum Beispiel:
# Login: TESTUSER1, Warehouse: DEMO_WH
SELECT C_NAME, C_ADDRESS, C_PHONE FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER";
# Login: TESTUSER2, Warehouse: DEMO_L
SELECT C_NAME, C_ADDRESS, C_PHONE FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER";
Query History View:
15 Millionen Einträge in 58 Millisekunden. Dabei benötigt das Kompilieren der Abfrage die meiste Zeit. Das Ergebnis ist sofort verfügbar, unabhängig vom verwendeten Warehouse und unabhängig vom Benutzer. In der Profil-Ansicht dieser Abfrage heißt es: “Query Result Reuse”.
Query Profile:
Result Cache deaktivieren
Der Result Cache ist standardmässig aktiviert. Er kann auf Account‑, Session- und User-Ebene deaktiviert werden:
Zum Beispiel:
ALTER ACCOUNT SET USE_CACHED_RESULT = TRUE;
ALTER SESSION SET USE_CACHED_RESULT = FALSE;
ALTER USER TestUser1 SET USE_CACHED_RESULT = FALSE;
Es gibt allerdings einige Voraussetzungen, um die Ergebnisse aus der Cloud Service Ebene, die über die Benutzer-und Warehouse-Grenzen hinaus gehen, abgreifen zu können. Die Ergebnisse im Result Cache können nur als Ganzes abgefragt werden. Dazu muss die Abfrage identisch sein und zu den selben Ergebnissätzen führen. Der Benutzer muss also über die Privilegien verfügen, die Daten sehen zu dürfen und die zu Grunde liegenden Daten dürfen sich nicht verändert haben.
Result Scan
Snowflake bietet mit result_scan(Query_ID) eine Tabellen-Funktion für die Möglichkeit an, Ergebnissätze aus dem Result Cache weiter verarbeiteten zu können. Man hat allerdings keinen direkten Zugriff auf die Ergebnissätze anderer Benutzer. Ich kann also nicht einfach die Query_ID von einem anderem Benutzer verwenden, sondern muss zunächst die SQL-Abfrage selbst ausführen, um das Ergebnis aus dem Result Cache einmal als Ganzes abzurufen. Danach kann ich wie auf eine gewöhnliche Tabelle auf das Ergebnis zugreifen.
Zum Beispiel:
# TESTUSER1 führt eine Abfrage aus
SELECT C_NAME, C_ADDRESS, C_PHONE FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER";
# TESTUSER2 möchte das Ergebnis von TESTUSER1 analysieren und weiterverarbeiten
SELECT C_NAME, C_ADDRESS, C_PHONE
FROM table(result_scan('01a28ce3-3200-f925-0000-3aa9009de08e')) #QUERY_ID von TESTUSER1
WHERE C_PHONE like '30-%';
> ERROR: Statement 01a28ce3-3200-f925-0000-3aa9009de08e not found
# Korrekt: Ergebnis erst als Ganzes aus dem Result Cache holen, eigene Query_ID verwenden
SELECT C_NAME, C_ADDRESS, C_PHONE FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER";
SELECT C_NAME, C_ADDRESS, C_PHONE
FROM table(result_scan(last_query_id()))
WHERE C_PHONE like ’30-%’;
Ich möchte jedoch darauf hinweisen, dass Result_Scan() auf einer großen Ergebnismenge nicht besonders performant ist, da hier keine Optimierungen auf Grund von fehlenden Metadaten möglich sind.
(3) Metadata Cache
Der Metadata Cache befindet sich ebenfalls auf der Cloud Service Ebene. Hier werden verschiedenste Metadaten und Statistiken gespeichert. Snowflake verwendet diese Daten, wie gesagt, um Abfragen zu optimieren. Als Benutzer kann man hier nützliche Informationen gewinnen und unter Umständen Compute-Zeit sparen. Snowflake legt für jede Datenbank ein “Information_Schema” an, welches eine Reihe von Daten über alle möglichen Datenbankobjekte speichert.
Zum Beispiel:
SELECT count(*) FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER";
Query History View:
Query Profile:
Snowflake versteht in diesem Fall, dass die Information über die Anzahl der Einträge bereits in den Metadaten gespeichert ist. Es wird daher kein Warehouse angeworfen. Im Information_Schema der Datenbank finden wir eben diese Informationen unter TABLES > ROW_COUNT:
# Metadaten von gefüllten Tabellen der Datenbank
SELECT * FROM "SNOWFLAKE_SAMPLE_DATA"."INFORMATION_SCHEMA"."TABLES"
WHERE ROW_COUNT is not NULL;
# Ähnliche Abfrage auf die Metadaten einer Tabelle, Result_Scan() zur Anpassung
SHOW tables;
SELECT "database_name","schema_name", "name" as "table_name", "rows"
FROM table(result_scan(last_query_id()))
WHERE "rows" > 0;
Fazit
Snowflake verfügt über drei Arten von Caching.
Beim Warehouse Cache hat der Anwender keine Kontrolle darüber, welche Daten im Cache gespeichert sind und bleiben. Er kann ihn nur komplett leeren. Hinzu kommt die Abwägung zwischen dem Trade-Off von “Suspend and Resume” und “Keep Running”. Da das Herunterfahren alle Daten aus dem Cache löscht, hat dies wiederum Einfluss auf die Performance. Ein laufendes Warehouse und die Daten im Cache sind mit Kosten verbunden. Diesen Trade-Off gilt es bei der Größenauswahl eines Warehouses und auch bei den Einstellungen eines Multi-cluster Warehouses zu berücksichtigen.
Anders ist es beim Metadata- und Result Cache. Sie fallen in die Cloud Service Ebene von Snowflake. Durch den Metadata Cache spart Snowflake beim Optimieren bereits automatisch Compute-Zeit ein. Hier finden sich vor allem interessante Informationen über die Daten in meiner Datenbank.
Mit dem Result Cache können Ergebnisse bis zu 31 Tage lang über Benutzer- und Warehouse-Grenzen hinweg unmittelbar zur Verfügung gestellt werden. Blitzschnell und ohne weitere Kosten, wenn die Nutzung den 10%-Rahmen nicht übersteigt. Er ist wohl der beeindruckendste der drei Caches. Durch kluge Metadatensteuerung und mit Hilfe von Tasks oder anderen Scheduling-Tools, kann der volle Nutzen der Wiederverwendung automatisiert und abgestimmt werden. Es braucht keine Tabelle für Zwischen-Ergebnisse angelegt werden. Die Result_Scan() Tabellen-Funktion hilft bei der Weiterverarbeitung und zum wiederholten Abrufen der Ergebnisse.