Macro Gains utilizing Snowflake’s Micro-Partitions

Snowflake handles data storage and recovery differently than traditional database systems. In this article, we’ll compare the contrast between the classic row-based storage to Snowflake’s columnar approach and how it fits into its storage units, called Micro-Partitions, then look at how recovery has evolved -From backup-based point-in-time restores to Snowflake’s Time Travel and Fail-safe benefits.

Row-Based Storage

Row based databases store records row by row and all the columns of a single customer/order/event are physically close together.
That’s perfect when your workload is fetching one row at a time and updating one row, then committing. Which is why row storage shines in transactional systems.
The real issues start when your needs change from fetching that one record into analyzing all records. Analytics queries that scan a lot of rows and rarely touch more than a few columns still drag single rows and columns the Analyst never asked for, because that’s how the data is laid out on disk.

SELECT
    DATE_TRUNC('day', order_ts) AS day,
    SUM(order_total) AS revenue
FROM
    orders
WHERE
    order_ts >= '2026-01-01'
GROUP BY
   1
ORDER BY
    1;

This query only needs order_ts and order_total but often ends up scanning pages that contain entire rows, including all other columns and that extra baggage ends up costing more disk I/O, more memory/cache pressure and slower scans.
Snowflake assumes the analytics pattern by default, that’s why it stores data in a columnar format, then groups it in ranges into micro-partitions with additional metadata helping scans to skip reading data that isn’t needed.

Here is a picture showcasing the total partitions in our data, in comparison to the actual partitions scanned to make up our query results.

Snowflake’s Storage Format

Snowflake stores data in its own optimized format inside your chosen cloud provider’s object storage and then utilizes its Query Processing Layer (“cloud compute instances”) to read/transform it. Snowflake does not use Its own Cloud Storage, instead it takes advantage of the chosen provider’s when creating your Snowflake Account:

  • Amazon Web Services – S3 bucket
  • Microsoft Azure – Azure Blob Storage / ADLS Gen2 (containers)
  • Google Cloud Platform – Google Cloud Storage (GCS buckets)

It leverages the stability and availability of those cloud providers, eliminating the need for businesses to keep and maintain large and dusty server rooms.
It stores your tables in its proprietary columnar format objects called Micro Partitions. They, instead of keeping your records in a Row Based Format, like you would typically see in older DBMS, store them in a Columnar Format and order them for optimized aggregation queries.

Artistic expression of Micro-Partitions 1 and 2 with rows they represent. The values highlighted represent one record in the data.

SELECT
    DATE_TRUNC('day', order_ts) AS day,
    SUM(order_total) AS revenue
FROM
    orders
WHERE
    order_ts >= '2026-01-01'
GROUP BY
   1
ORDER BY
    1;

This query only needs order_ts and order_total. In a columnar layout Snowflake can focus on reading and processing primarily those columns rather than dragging along every field in a wide orders table.

Columnar storage tends to compress better because columnar values are often similar (dates are dates, integers are integers etc.). Better compression usually means:
Less I/O, fewer bytes moved around and scans stay cheap as data grows.

Indexing and Partitioning Burdens

Once a row-based system starts serving analytics, the predictable pattern is that queries get slower as the data grows. Engineers offset that by placing indexes and partitioning. These tools work, but they are not magic and they come with their costs that compound over time.
Indexes exists to help avoid scanning large portions of the table.

-- common “help the dashboard” index
CREATE INDEX idx_orders_order_ts ON orders(order_ts);
-- and then another team needs this
CREATE INDEX idx_orders_customer_ts ON orders(customer_id, order_ts);
-- and another query wants this
CREATE INDEX idx_orders_region_ts ON orders(region, order_ts);

Practical Burdens

  • Every insert and update now updates multiple indexes
  • Each index becomes a significant portion of the tables storage
  • Operational tuning (removing unneeded ones) costs time

Analytics demands slicing data by different dimensions constantly so you end up paying for either the maintenance costs for a lot of indexes, accepting slower scans or building summary tables.

Partitioning

Partitioning is another lever of old DBMS.

PTITION BY RANGE (order_ts);

Partitioning can be a big win when queries align with a partition key but it adds real constraints. One partition may be hammered a lot while others sit cold, redesigning partitions means heavier rewrites and downtime risks, and it’s a design choice that falls on the engineer’s shoulders.
Snowflake avoids putting you in that loop. Instead of asking you to design partitions, it stores data in micro-partitions automatically and uses metadata to prune what isn’t needed, so the system often skips records without the need to manually build giant sets of indexes.

Snowflake’s “Indexing and Partitioning”

In snowflake, you don’t create indexes for tables and you usually don’t manually partition tables either. Instead, snowflake organizes stored data into micro-partitions and uses metadata to skip irrelevant chunks of data at query time.
At the time of querying it prunes and skips useless data while keeping the data you need.
That is possible because snowflake, in its metadata keeps ranges of values for each micro partition and queries only the partitions that fit into the required values, by defining a cluster key.
A clustering key is an explicit expression for ordering the records inside those micro-partitions and it provides the actual range of values inside. A user can define a clustering key on a value they see fit, depending on the business needs. By default, if we don’t define a clustering key, the data will be clustered by load date.

CREATE TABLE <name> ... CLUSTER BY ( <expr1> [ , <expr2> ... ] )

Clustering keys are important because defining a clustering key on values with low cardinality may cause bad clustering, introducing clustering depth i.e. overlapping ranges of micro-partitions.

Snowflake keeps those micro-partitions between 50-500MB of uncompressed bytes and they are immutable, meaning that when new values get inserted or values inside get updated, Snowflake creates new micro-partitions to replace the old ones. This enables it’s great point-of-time recovery and concurrency capabilities.

Concurrency

Traditional databases keep transactions correct through locking. In OLTP workloads – lots of short reads/writes and these locks are held to help the system feel predictable.
The locking problem in practice
Analytics queries tend to be long running with big scans, joins and aggregations. Even if they are just reading, they can hold locks to provide isolation. Meanwhile, the application is trying to write constantly.
This creates familiar failure models like long queries locking and preventing updates and inserts from happening, a transaction updating rows that prevents reporting queries from happening and deadlocks.
Snowflake is designed so analytics workloads don’t hold a transactional lock, and doesn’t produce statements like “don’t run reports during business hours”.  It separates compute and storage really efficiently.

Because multiple Warehouses(compute nodes) can read the same underlying data you can split the loads.
               -ETL/ELT warehouse for loads and transforms
               -BI/dashboard warehouse for user queries
               -Data science/adhoc warehouse for experimentation
So when a dashboard runs a huge scan, it doesn’t have to steal CPU and memory from ingestion jobs.
Snowflake still has to ensure correctness, and it achieves that exactly through how snowflake keeps Micro-Partitions and its internal metadata.

Engineer A: Updates 15 records in a table.
Engineer B: Wants to visualize the data in a report.
How snowflake does this is by re-creating the micro-partitions. Engineer A’s update creates a new Micro-Partition with the updated data, Engineer B at the time of update views the old micro-partitions before the time of change. If after the table is updated Engineer B runs another select, he is going to be able to see the changes Engineer A made immediately.
They both run queries on different compute nodes, so no concurrency issues arise when it comes to compute. They both share the same Storage concurrently and they are able to do so because of that same mechanism of metadata management and table reconstruction via micro-partitions.

This partition re-arrangement is something you don’t have to manage, it all happens internally via Metadata holding information about those objects and reconstructing them seamlessly at time of need.

Backups and PITR in Traditional Systems

In classic database systems, keeping backups and transaction logs is part of the workflows engineers operate.
The typical cliché incident: The junior runs an ETL job, an update without a where clause or a delete in the wrong schema, the business has to have backups from before that happened in case of this scenario.

What Recovery Typically Looks Like

  1. Find the right restore point.
  2. Restore from backup and replay logs to a timestamp.
  3. Validate and extract what you need.
  4. Merge data back carefully.

This costs time and operational overhead.

With Snowflake, recovery stops being an event and starts being a feature. Micro-partitions and Time Travel let you rewind to a specific point in time and pull back the exact table, schema, or rows you need—without rebuilding servers, coordinating restore windows, or worrying about clobbering new writes. Fail-safe is another feature that sits further back as a safety net after the time travel period ends.

Time travel lets you access historical data for a defined time travel retention period – whether data is changed (DELETE/UPDATE/MERGE) or the objects was dropped(UNDROP).

Retention Periods

Snowflake controls how far back you can go with the DATA_RETENTION_TIME_IN_DAYS setting that can be set on account, database, schema and table objects that also inherit it from their parent objects.

ACCOUNT (7 days)

   │

   └── DATABASE (inherits 7 days)

          │

          └── SCHEMA (overridden to 3 days)

                 │

                 ├── TABLE A (inherits 3 days)

                 └── TABLE B (overridden to 1 day)

Default Time Travel Retention is 1 day(24 hours) and it applies to accounts on the standard edition of snowflake.
Enterprise editions and above enable up to 90 Days of time travel.

  • Standard Tables: up to 90 days (“extended time travel”).
  • Transient Tables: Time Travel retention can be 0 or 1 day.
  • Temporary Tables: Time Travel can be 0 or 1 day, but the table is purged when the session ends (so the effective window may be shorter).

Fai-safe is on it’s own 7 days on top of the Time Travel Retention Time, that applies to the Standard tables in an account, but to have access you have to contact Snowflake support.

  1. Query a table “as of” a timestamp


Use AT to see what the table looked like at a specific time:

SELECT
   *
FROM
   orders AT (
    TIMESTAMP => '2026-02-25 10:42:00'::timestamp
  )
WHERE
   order_id = 12345;

  1. Query the state before a specific statement


If you know the statement that broke things (from query history), BEFORE is super surgical:

SELECT
   COUNT(*)
FROM
   orders BEFORE (
  STATEMENT => '01b3a2c9-0000-1234-0000-000000000000'
  );

  • Restore a table by cloning a past version (fast “undo” pattern)
    This is a common incident move: recreate the table as it was before the bad change:
  • CREATE OR REPLACE TABLE orders_restored
    CLONE orders BEFORE (
      TIMESTAMP => '2026-02-25 10:42:00'::timestamp
      );


    Then you can validate and swap (or selectively merge rows back) without touching prod until you’re confident.


  • Undrop an accidentally dropped object


If someone drops a table (or schema) by mistake, and you’re still within retention:

UNDROP TABLE orders

UNDROP relies on Time Travel and only works if the drop happened within the object’s retention period (default 24h).

  1. Set retention (be explicit in prod)


At table level:

ALTER TABLE
   orders
SET
   DATA_RETENTION_TIME_IN_DAYS = 7;

The way snowflake achieves all these functions is again because of the way metadata keeps event times for a table’s micro-partitions.
If we want to bring back our old table version, snowflake’s metadata reconstructs the version of the table by gathering the needed micro-partitions that existed before the changes.

Conclusion

Traditional systems can absolutely deliver analytics and recovery – you just spend time earning it through indexes, partitioning strategies, locking workarounds, and restore procedures. Snowflake shifts that burden into the platform: columnar storage and micro-partitions reduce how much data you need to touch in the first place, and Time Travel turns recovery into a controlled, SQL-driven workflow instead of an infrastructure event. The result is faster iteration, fewer “don’t run that query” rules, and a lot less anxiety when something inevitably goes wrong.

Contact us

Get in touch and ask us anything. We're happy to answer every single one of your questions.

  • 6A Maria Luiza Blvd, Plovdiv
    4000, Bulgaria
  • Ulpia Tech LinkedIn Ulpia Tech Twitter


    To top