
Problem
Ostatnio znaleźliśmy wąskie gardło w pipeline klienta. Jeden model blokował cały downstream, budując się ponad 50–60 minut. Ponieważ modele downstream były mocno zależne od tego kluczowego zestawu danych, musieliśmy skrócić czas budowy — twardy limit to jedna godzina łącznie. Reszta pipeline zajmowała około 10 minut, ale byliśmy zbyt blisko limitu i generowało to ogromne koszty.

Dochodzenie
Jak powinni robić wszyscy deweloperzy Snowflake i dbt, zaczęliśmy od analizy danych. Zaskoczyło nas, że źródło miało tylko ~40 tys. wierszy. To nic.
Sprawdziliśmy profiler zapytań w Snowflake — nic nie wyglądało podejrzanie. Brak ciężkich transformacji, joinów czy agregacji. To był po prostu prosty model stagingowy, dynamicznie rzutowany makrem dbt (zob. także Automating Salesforce → dbt Models: Dynamic Metadata-Driven Modeling).
Ale coś się nie zgadzało. Jeśli zapytanie było tak proste, co mogło trwać 40 minut?
Wtedy spojrzeliśmy na same dane — i wtedy to zauważyliśmy: ponad 500 kolumn. Większość nieużywana. Tabela była szeroka, ekstremalnie szeroka. Deweloperzy Salesforce nieustannie dodawali i aktualizowali pola, przez co schemat rozrastał się poziomo w czasie.
Na początku nie uznaliśmy tego za problem wydajnościowy. Po głębszym zanurzeniu w profilowaniu zapytań Snowflake i dokumentacji stało się oczywiste: szerokie tabele to cichy zabójca wydajności, nawet przy małej liczbie wierszy.
Dlaczego?
Snowflake to baza kolumnowa, ale to nie znaczy, że lubi setki kolumn.
Każda kolumna ma własne metadane, kompresję i statystyki w mikropartycjach (Snowflake przechowuje dane w porcjach ~16 MB). Podczas planowania zapytania, nawet jeśli wybierzesz tylko kilka kolumn, Snowflake nadal musi odczytać metadane i zdekompresować bloki powiązane ze wszystkimi kolumnami w tych partycjach.
W praktyce prowadzi to do:
- Dłuższego czasu kompilacji zapytania — parsowanie i planowanie dla 500+ kolumn jest wolne.
- Większej liczby odczytów mikropartycji — pruning staje się nieskuteczny, bo statystyki min/max są przechowywane per kolumna.
- Gorszego wykorzystania cache — cache wyników i kolumn Snowflake fragmentuje się przy zbyt wielu atrybutach.
- Wyższych kosztów warehouse — skok zużycia CPU i I/O przez nadmierną obsługę metadanych.
Nawet proste zapytanie:
select * from raw.salesforce_account limit 10;
wewnętrznie może skanować setki bloków kolumn, dekompresując struktury, które dodają sekundy, a nawet minuty do czasu wykonania przy powtarzaniu na skalę.
Liczba wierszy wtedy nie ma znaczenia — Snowflake nadal musi parsować i ładować metadane kolumn z każdej mikropartycji dotkniętej zapytaniem. Im więcej kolumn, tym więcej struktur metadanych, słowników kompresji i statystyk kolumn do rozwiązania, zanim zwróci choć jeden wiersz.
W praktyce złożoność czasowa rośnie nieproporcjonalnie. Nie jest liniowa względem liczby kolumn — rośnie prawie wykładniczo, gdy optymalizator zaczyna obsługiwać setki deskryptorów kolumn na partycję. Dlatego tabela z 40 000 wierszy i 500 kolumnami może działać wolniej niż tabela z 40 milionami wierszy i 30 kolumnami.
Wniosek
Nasze makro dynamicznej ekstrakcji nie miało mechanizmu filtrowania — po prostu pobierało wszystko ze źródła. Naprawiliśmy to, dodając parametr include=, pozwalający wskazać kolumny faktycznie potrzebne downstream.
Po wdrożeniu tej zmiany czas budowy tego modelu spadł z ~55 minut do poniżej 1 minuty.

Joachim Hodana - Software & Data Engineer
The Hidden Cost of Wide Tables in Snowflake został pierwotnie opublikowany w Lortech Solutions Blog na Medium, gdzie rozmowa trwa dalej dzięki podświetleniom i odpowiedziom czytelników.


