Snow­flake kennt drei Arten von Caching. In die­sem Blog­bei­trag möchte ich anhand von Bei­spie­len diese genauer beleuch­ten und nütz­li­che Schluss­fol­ge­run­gen dar­aus zie­hen. Snow­flake hat sich in den letz­ten Jah­ren von einem Cloud Data Ware­house zur Data Cloud ent­wi­ckelt, einer Plat­form, die mit ihren unter­schied­li­chen Workloads bis dato nicht dage­we­sene Mög­lich­kei­ten bie­tet und weit mehr Berei­che abde­cken als ein klas­si­sches Ware­house. Um die Caching Ebe­nen bes­ser ein­ord­nen zu kön­nen, wer­fen wir einen kur­zen Blick auf die Snow­flake Architektur.

Snow­flake Architektur

Snow­flake baut auf den drei gros­sen Cloud Ser­vice Pro­vi­dern auf: Google, AWS und Azure. Das ermög­licht auch eine Cross-Cloud-Stra­te­gie. Snow­flake selbst besteht aus drei von ein­an­der getrenn­ten Ebenen: 

Snowflake Caching – am Beispiel erklärt Bild1
Abbil­dung 1 Snow­flake Architektur

Sto­rage Ebene: Hier wer­den Daten Spal­ten-basiert und von Snow­flake opti­miert in Mikro-Par­ti­tio­nen abge­legt. Diese sind zu jeder Zeit ver­schlüs­selt und red­un­dant gespei­chert. Snow­flake unter­stützt ACID-Trans­ak­tio­nen, die Daten sind in die­ser Schicht daher immer konsistent.

Com­pute Ebene: Die Rechen­schicht ist voll­stän­dig von der Spei­cher­schicht ent­kop­pelt. Dies ermög­licht eine belie­bige Anzahl von Workloads auf ihren eige­nen dedi­zier­ten Com­pute Clus­tern – in Snow­flake Warehou­ses genannt, selbst wenn diese Workloads auf die­sel­ben Daten zugrei­fen. Je nach­dem ob viele gleich­zei­tige Anfra­gen oder reine Rechen­leis­tung gefragt ist, kön­nen unter­schied­lich große und unter­schied­lich viele Clus­ter unab­hän­gig von ein­an­der ein­ge­setzt wer­den. Snow­flake nennt diese Archi­tek­tur: Multi-Clus­ter, Shared Data Archi­tek­tur. Ab dem Start eines Clus­ters ist der Min­dest­ver­brauch eine Minute, auch wenn der Clus­ter nur eine Sekunde läuft. Danach rech­net Snow­flake pro Sekunde ab. Jeder Clus­ter hat sei­nen eige­nen loka­len Spei­cher, des­sen Gebrauch in den Ware­house Cre­dits ent­hal­ten ist.

Cloud Ser­vice Ebene: Sie bil­det quasi das Gehirn von Snow­flake ab. Zugang hat man hier nur über die zur Ver­fü­gung gestell­ten Schnitt­stel­len. Hier wer­den die Benut­zer Accounts und Ses­si­ons, Meta­da­ten, Trans­ak­tio­nen, Abfra­ge­pläne, Sicherheit/Governance und viele andere Dienste intern ver­wal­tet. Dank der nahezu unbe­grenz­ten Res­sour­cen in der Cloud, ist auch diese Ebene hoch­gra­dig ska­lier­bar. Der Gebrauch der Ser­vice Ebene ist kos­ten­frei, außer der Ver­brauch eines Benut­zers über­steigt mehr als 10% der gesam­ten, täg­lich-genutz­ten Com­pute-Zeit. Die­ser 10%-Threshold wurde zum 01.02.2020 eingeführt.

Snow­flake Caching

Es gibt drei Arten von Caches: (1) Ware­house Cache, (2) Result Cache, (3) Meta­data Cache. Im Fol­gen­den werde ich auf alle ein­ge­hen und anhand von Bei­spie­len zei­gen, was damit gemeint ist.

Snowflake Caching – am Beispiel erklärt Bild2
Abbil­dung 2 Über­sicht über die ver­schie­de­nen Snow­flake Caches
(1) Ware­house Cache

Der Ware­house Cache befin­det sich auf der Com­pute Ebene und ist abhän­gig vom ver­wen­de­ten Ware­house. Er wird daher oft auch “Local Disk Cache” genannt. Die Spei­cher­ka­pa­zi­tät ist abhän­gig von der Größe des gewähl­ten Warehou­ses. Wann immer Daten für eine bestimmte Abfrage benö­tigt wer­den, wer­den sie aus der Sto­rage Ebene abge­ru­fen und im Ware­house zwi­schen­ge­spei­chert. Jede wei­tere Abfrage kann nun auf diese bereits lokal lie­gen­den Daten zugrei­fen. Es müs­sen dann nur die Mikro-Par­ti­tio­nen nach­ge­la­den wer­den, die für eine wei­tere Abfrage noch feh­len. Je nach­dem wie hoch der Grad der Wie­der­wen­dung und wie aus­ge­las­tet der Cache ist, wer­den Daten auch wie­der aus dem Spei­cher ent­fernt. Durch das Her­un­ter­fah­ren eines Clus­ters leert sich der gesamte Cache. Dies geschieht aus Opti­mie­rungs­grün­den unter Umstän­den nicht sofort. Snow­flake stellt Com­pute-Zeit abhän­gig von Größe des Warehou­ses in Rech­nung. Dies kann je nach Größe eines lau­fen­den Com­pute Clus­ters auf Dauer rela­tiv kost­spie­lig werden.

Zum Bei­spiel:

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:

Snowflake Caching – am Beispiel erklärt Bild3
Abbil­dung 3 Query History

In der Query History View wer­den Abfra­gen der letz­ten 14 Tage gespei­chert. Hier sehen wir, dass bei der zwei­ten Abfrage 449.9 MB bereits lokal ver­füg­bar sind. Sie befin­den sich im Ware­house Cache. In die­sem Fall feh­len die Mikro-Par­ti­tio­nen der Spalte “C_PHONE”. Anhand der Query ID kön­nen Abfra­gen in der Query Pro­file Ansicht auch noch wei­ter ana­ly­siert werden.

Query Pro­file:

1. Abfrage:

Snowflake Caching – am Beispiel erklärt Bild4
Abbil­dung 4 Ergeb­nis der ers­ten Abfrage

2. Abfrage:

Snowflake Caching – am Beispiel erklärt Bild5
Abbil­dung 5 Ergeb­nis der zwei­ten Abfrage

Von beson­de­rem Inter­esse ist hier die Über­sicht. Sie gibt die Infor­ma­tion “Per­cen­tage scan­ned from cache” direkt an. Dar­über hin­aus sehen wir eine erheb­li­che Per­for­mance-Stei­ge­rung der zwei­ten Abfrage. Sie ist schnel­ler, obwohl sie mehr Daten (“Scan­ned Bytes”) ver­ar­bei­tet hat.

(2) Result Cache

Wir fah­ren das Ware­house her­un­ter und star­ten es erneut. Der Ware­house Cache ist nun leer. Nun füh­ren wir die glei­chen Abfra­gen noch ein­mal aus und stel­len über­ra­schen­der­weise noch ein ande­res Ver­hal­ten fest:

  • Die Ergeb­nisse sind unmit­tel­bar ver­füg­bar und
  • Es wur­den gar keine Daten gela­den und prozessiert

Tat­säch­lich muss nicht ein­mal ein Ware­house gestar­tet wer­den, um die Abfrage-Ergeb­nisse zu erhal­ten. Damit kom­men wir zur zwei­ten Caching Ebene.

Der Result Cache befin­det sich auf der Cloud Ser­vice Ebene. Hier wer­den kom­plette Query-Results für 24 Stun­den gespei­chert und unab­hän­gig vom Ware­house zur Ver­fü­gung gestellt. Andere Benut­zer kön­nen die Ergeb­nisse eben­falls abfra­gen. Wenn das Ergeb­nis inner­halb der 24 Stun­den erneut abge­ru­fen wird, wird diese Frist neu gesetzt. Dadurch kann die Spei­che­rung von Abfrage-Ergeb­nis­sen im Result Cache bis auf 31 Tage ver­län­gert werden.

Zum Bei­spiel:

# 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:

Snowflake Caching – am Beispiel erklärt Bild6
Snowflake Caching – am Beispiel erklärt Bild7
Abbil­dung 6 Query History

15 Mil­lio­nen Ein­träge in 58 Mil­li­se­kun­den. Dabei benö­tigt das Kom­pi­lie­ren der Abfrage die meiste Zeit. Das Ergeb­nis ist sofort ver­füg­bar, unab­hän­gig vom ver­wen­de­ten Ware­house und unab­hän­gig vom Benut­zer. In der Pro­fil-Ansicht die­ser Abfrage heißt es: “Query Result Reuse”.

Query Pro­file:

Snowflake Caching – am Beispiel erklärt Bild8
Abbil­dung 7 Query Pro­file
Result Cache deaktivieren

Der Result Cache ist stan­dard­mäs­sig akti­viert. Er kann auf Account‑, Ses­sion- und User-Ebene deak­ti­viert werden:

Zum Bei­spiel:

ALTER ACCOUNT SET USE_CACHED_RESULT = TRUE;
ALTER SESSION SET USE_CACHED_RESULT = FALSE;
ALTER USER TestUser1 SET USE_CACHED_RESULT = FALSE;

Es gibt aller­dings einige Vor­aus­set­zun­gen, um die Ergeb­nisse aus der Cloud Ser­vice Ebene, die über die Benut­zer-und Ware­house-Gren­zen hin­aus gehen, abgrei­fen zu kön­nen. Die Ergeb­nisse im Result Cache kön­nen nur als Gan­zes abge­fragt wer­den. Dazu muss die Abfrage iden­tisch sein und zu den sel­ben Ergeb­nis­sät­zen füh­ren. Der Benut­zer muss also über die Pri­vi­le­gien ver­fü­gen, die Daten sehen zu dür­fen und die zu Grunde lie­gen­den Daten dür­fen sich nicht ver­än­dert haben.

Result Scan

Snow­flake bie­tet mit result_scan(Query_ID) eine Tabel­len-Funk­tion für die Mög­lich­keit an, Ergeb­nis­sätze aus dem Result Cache wei­ter ver­ar­bei­te­ten zu kön­nen. Man hat aller­dings kei­nen direk­ten Zugriff auf die Ergeb­nis­sätze ande­rer Benut­zer. Ich kann also nicht ein­fach die Query_ID von einem ande­rem Benut­zer ver­wen­den, son­dern muss zunächst die SQL-Abfrage selbst aus­füh­ren, um das Ergeb­nis aus dem Result Cache ein­mal als Gan­zes abzu­ru­fen. Danach kann ich wie auf eine gewöhn­li­che Tabelle auf das Ergeb­nis zugreifen.

Zum Bei­spiel:

# 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 dar­auf hin­wei­sen, dass Result_Scan() auf einer gro­ßen Ergeb­nis­menge nicht beson­ders per­for­mant ist, da hier keine Opti­mie­run­gen auf Grund von feh­len­den Meta­da­ten mög­lich sind.

(3) Meta­data Cache

Der Meta­data Cache befin­det sich eben­falls auf der Cloud Ser­vice Ebene. Hier wer­den ver­schie­denste Meta­da­ten und Sta­tis­ti­ken gespei­chert. Snow­flake ver­wen­det diese Daten, wie gesagt, um Abfra­gen zu opti­mie­ren. Als Benut­zer kann man hier nütz­li­che Infor­ma­tio­nen gewin­nen und unter Umstän­den Com­pute-Zeit spa­ren. Snow­flake legt für jede Daten­bank ein “Information_Schema” an, wel­ches eine Reihe von Daten über alle mög­li­chen Daten­bank­ob­jekte speichert.

Zum Bei­spiel:

SELECT count(*) FROM "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF100"."CUSTOMER";

Query History View:

Snowflake Caching – am Beispiel erklärt Bild9
Abbil­dung 8 Query History

Query Pro­file:

Snowflake Caching – am Beispiel erklärt Bild10
Abbil­dung 9 Query Profile

Snow­flake ver­steht in die­sem Fall, dass die Infor­ma­tion über die Anzahl der Ein­träge bereits in den Meta­da­ten gespei­chert ist. Es wird daher kein Ware­house ange­wor­fen. Im Information_Schema der Daten­bank fin­den wir eben diese Infor­ma­tio­nen 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

Snow­flake ver­fügt über drei Arten von Caching.
Beim Ware­house Cache hat der Anwen­der keine Kon­trolle dar­über, wel­che Daten im Cache gespei­chert sind und blei­ben. Er kann ihn nur kom­plett lee­ren. Hinzu kommt die Abwä­gung zwi­schen dem Trade-Off von “Sus­pend and Resume” und “Keep Run­ning”. Da das Her­un­ter­fah­ren alle Daten aus dem Cache löscht, hat dies wie­derum Ein­fluss auf die Per­for­mance. Ein lau­fen­des Ware­house und die Daten im Cache sind mit Kos­ten ver­bun­den. Die­sen Trade-Off gilt es bei der Grö­ßen­aus­wahl eines Warehou­ses und auch bei den Ein­stel­lun­gen eines Multi-clus­ter Warehou­ses zu berücksichtigen.

Anders ist es beim Meta­data- und Result Cache. Sie fal­len in die Cloud Ser­vice Ebene von Snow­flake. Durch den Meta­data Cache spart Snow­flake beim Opti­mie­ren bereits auto­ma­tisch Com­pute-Zeit ein. Hier fin­den sich vor allem inter­es­sante Infor­ma­tio­nen über die Daten in mei­ner Datenbank.

Mit dem Result Cache kön­nen Ergeb­nisse bis zu 31 Tage lang über Benut­zer- und Ware­house-Gren­zen hin­weg unmit­tel­bar zur Ver­fü­gung gestellt wer­den. Blitz­schnell und ohne wei­tere Kos­ten, wenn die Nut­zung den 10%-Rahmen nicht über­steigt. Er ist wohl der beein­dru­ckendste der drei Caches. Durch kluge Meta­da­ten­steue­rung und mit Hilfe von Tasks oder ande­ren Sche­du­ling-Tools, kann der volle Nut­zen der Wie­der­ver­wen­dung auto­ma­ti­siert und abge­stimmt wer­den. Es braucht keine Tabelle für Zwi­schen-Ergeb­nisse ange­legt wer­den. Die Result_Scan() Tabel­len-Funk­tion hilft bei der Wei­ter­ver­ar­bei­tung und zum wie­der­hol­ten Abru­fen der Ergebnisse.