DEV Community

Cover image for What Actually Happens Inside Redis During a Snapshot (And Why It Matters in Production)
NARESH
NARESH

Posted on

What Actually Happens Inside Redis During a Snapshot (And Why It Matters in Production)

Banner

TL;DR

Redis creates snapshots without stopping your application by using the Linux fork() system call.

Instead of duplicating the entire dataset, Linux relies on Copy-on-Write, copying only the memory pages that are modified during the snapshot.

The more your application writes while a snapshot is running, the more additional memory Copy-on-Write allocates.

If Transparent Huge Pages (THP) are enabled, even a small write can trigger a much larger memory copy, leading to unexpected memory growth and latency spikes.

Understanding these Linux internals explains many Redis production best practices that otherwise seem like arbitrary tuning recommendations.


I've used Redis in several projects over the years, mostly as a cache. Like many engineers, I appreciated it because it was fast, reliable, and easy to drop into an application. I knew it stored data in memory, and I knew it could persist that data to disk when needed. That was enough for me.

Then, while working on a project, I started digging deeper into how Redis handled persistence. At first, I expected to find a straightforward implementation. Instead, I found myself reading about Linux process management, virtual memory, page tables, and a mechanism called Copy-on-Write.

That was surprising.

The more I read, the more I realized that one of Redis's most impressive features isn't purely a Redis feature at all. The reason it can create snapshots while continuing to serve requests comes from how the Linux kernel manages memory. Redis is simply designed to take advantage of it.

Once I understood what was happening under the hood, a lot of production advice that had always felt like random recommendations finally made sense.

Why does memory usage suddenly spike during a snapshot?

Why can a snapshot increase latency even though it's running in the background?

Why do experienced engineers recommend disabling Transparent Huge Pages on Redis servers?

These aren't unrelated tuning tips. They're all connected by the same underlying mechanism.

In this article, we'll follow the complete journey of a Redis snapshot, from the moment Redis decides to create one to the moment it's safely written to disk. Along the way, we'll uncover why this design is so elegant, what trade-offs it introduces, and why understanding these internals can help you avoid some surprisingly expensive production problems.


Why Taking a Snapshot Isn't as Simple as It Sounds

At first glance, creating a snapshot seems easy.

Just write everything in memory to a file.

The problem is that Redis isn't a database sitting idle while it saves data. It's actively serving thousands, sometimes millions, of requests every second. Clients are constantly reading data, updating keys, deleting values, and creating new ones.

Now imagine Redis starts copying its entire memory to disk.

What happens if a client modifies a key halfway through the snapshot?

Why Taking a Snapshot Isn't as Simple as It Sounds

The beginning of the snapshot contains the old value, while the end contains the new one.

The result is an inconsistent backup that never actually existed at any point in time.

One obvious solution would be to pause all client requests, write the snapshot, and then resume normal operations. That would certainly produce a consistent backup.

Unfortunately, it's also unacceptable for most production systems.

Even a few seconds of downtime can translate into failed requests, increased latency, and unhappy users. As datasets grow larger, the pause only gets longer.

So Redis has to solve what seems like an impossible problem.

It needs to create a perfectly consistent snapshot while continuing to accept reads and writes at full speed.

That sounds almost contradictory.

Yet that's exactly what Redis manages to do.

The interesting part is that Redis doesn't solve this problem by copying memory itself. Instead, it relies on a feature provided by the Linux kernel, one that allows two processes to temporarily share the same memory without actually duplicating it.

That's where the real story begins.


Redis's Solution: Let Someone Else Do the Work

Redis solves this problem using a command called BGSAVE (Background Save).

Instead of trying to write the snapshot itself while serving client requests, Redis creates a second process whose only responsibility is generating the snapshot.

It does this using the Linux fork() system call.

Once fork() completes, there are two processes.

The parent process continues doing what it has always done, serving reads and writes with almost no interruption.

The child process starts writing the snapshot to disk.

From the application's perspective, everything appears normal. Clients continue sending requests, responses keep flowing, and a snapshot is being created in the background at the same time.

At first, this almost feels impossible.

If both processes start with the same memory, wouldn't creating a second process instantly duplicate the entire dataset? If Redis is using 20 GB of memory, does fork() suddenly require another 20 GB?

Thankfully, no.

In reality, the operating system is much smarter than that. Instead of copying the entire dataset immediately, both processes temporarily share the same physical memory.

Only when one of them modifies a piece of data does Linux create a separate copy.

This optimization is called Copy-on-Write, and it's the reason Redis can create consistent snapshots without freezing your application or doubling memory usage the moment fork() happens.


The Secret Behind It All: Copy-on-Write

At this point, we have two processes.

The parent process is still serving client requests, while the child process is busy writing the snapshot to disk.

The obvious question is:

If both processes started with the same data, doesn't fork() immediately double the memory usage?

Surprisingly, it doesn't.

Instead of copying the entire dataset, Linux allows both processes to temporarily share the same physical memory. As long as neither process changes the data, there's no reason to create another copy.

This is what makes fork() so efficient.

Copy-on-Write

The interesting part begins when the parent process receives a write request.

Suppose a client updates the value of a key while the child process is still writing the snapshot.

Redis can't allow that update to change the snapshot because the snapshot should represent the exact state of the data when BGSAVE started.

So, before the modification happens, Linux quietly creates a new copy of only the affected memory page.

The parent process updates this new copy and continues serving requests.

Meanwhile, the child process keeps reading the original page, preserving a perfectly consistent snapshot.

This behavior is called Copy-on-Write.

The important detail is that Linux copies only the memory that changes, not the entire dataset. If only a small portion of your data is modified during the snapshot, only that portion needs to be duplicated.

That's why Redis can continue serving traffic while producing a consistent backup, without instantly requiring twice as much memory.

It's an elegant solution, but as we'll see next, it also explains why memory usage can suddenly grow during a snapshot, sometimes much more than engineers expect.


Why Memory Usage Starts Growing During a Snapshot

At first, Copy-on-Write sounds almost perfect.

Linux doesn't duplicate the entire dataset. It only copies memory when data changes.

So where do the memory spikes come from?

The answer depends entirely on your application's write traffic.

Imagine your Redis instance is using 10 GB of memory when BGSAVE starts.

Initially, the parent and child processes are sharing all 10 GB. No additional memory has been allocated yet.

Now your application continues running.

Users log in.

Shopping carts are updated.

Sessions expire.

Counters increase.

Cache entries are refreshed.

Every time Redis modifies data that still belongs to the snapshot, Linux creates a private copy of the affected memory page for the parent process.

The snapshot process continues reading the original page, while the parent process writes to the new one.

One update isn't a problem.

A few hundred aren't either.

But in a busy production system processing thousands of writes per second, these copied pages start accumulating surprisingly quickly.

Snapshot

The more data your application modifies while the snapshot is running, the more additional memory the operating system has to allocate.

That's why two Redis servers with the same dataset can behave completely differently during BGSAVE.

A mostly idle server may barely allocate any extra memory.

A write-heavy server can consume significantly more memory before the snapshot finishes.

And there's another detail that makes this even more expensive.

Sometimes Linux doesn't copy a small memory page at all.

Instead, it copies a much larger chunk of memory than you might expect.


When Copy-on-Write Becomes Surprisingly Expensive

So far, we've assumed that when Linux needs to copy memory, it copies only a small page.

Most of the time, that's true.

By default, Linux manages memory in pages that are typically 4 KB in size. If your application modifies data on one of those pages during a snapshot, Copy-on-Write duplicates only that 4 KB page.

That's efficient.

The problem starts when Transparent Huge Pages (THP) are enabled.

Instead of grouping memory into 4 KB pages, Linux may combine many of them into a much larger 2 MB page to improve memory management and reduce address translation overhead. For many applications, this is a useful optimization.

Redis, however, has a very different workload.

During a snapshot, imagine your application updates just a single key that happens to live inside one of those huge pages.

From your application's perspective, only a tiny piece of data changed.

But from the operating system's perspective, the entire 2 MB page has been modified.

Copy-on-Write now has no choice but to duplicate the whole page.

In other words, a tiny write can trigger a 2 MB memory copy instead of a 4 KB copy.

Multiply that by thousands of writes during a busy snapshot, and memory usage can increase much faster than expected. The additional copying can also consume CPU time and contribute to latency spikes while the snapshot is in progress.

That's why you'll often see one recommendation repeated in Redis production guides:

Disable Transparent Huge Pages.

It's not because THP is a bad Linux feature. It's because an optimization designed for general workloads doesn't align well with Redis's Copy-on-Write behavior during snapshots.


What This Means in Production

Understanding how snapshots work changes the way you look at Redis in production.

If memory usage suddenly increases during BGSAVE, it isn't necessarily a memory leak. It may simply be Copy-on-Write doing exactly what it's supposed to do.

If latency briefly spikes when a snapshot begins, Redis itself may not be the bottleneck. Creating the child process requires the operating system to duplicate the parent's page tables before the snapshot can even start. On large Redis instances, that work alone can take noticeable time.

And if memory usage grows much more than expected during a snapshot, Transparent Huge Pages are often one of the first things worth checking.

These are some of the practical lessons experienced Redis operators follow:

Leave enough free memory for Copy-on-Write during snapshots.

Disable Transparent Huge Pages on production Redis servers.

Monitor snapshot duration and latest_fork_usec, especially as datasets grow.

Test snapshot performance under realistic write traffic instead of only during idle periods.

None of these recommendations are arbitrary tuning tips.

They're direct consequences of how Redis and the Linux kernel work together to create snapshots without stopping your application.

Once you understand the underlying mechanism, these best practices stop feeling like rules to memorize and start feeling like common sense.


Closing Thoughts

Before diving into Redis snapshots, I thought persistence was just another feature on a checklist.

Redis stores data in memory, writes it to disk in the background, and life goes on.

The deeper I looked, the more I realized that one of Redis's most impressive capabilities isn't built on a clever Redis algorithm alone. It's the result of Redis working hand in hand with the Linux kernel.

Features like fork() and Copy-on-Write allow Redis to create consistent snapshots while continuing to serve millions of requests. At the same time, they also explain many of the production behaviors that confuse engineers, from unexpected memory growth to latency spikes during BGSAVE.

For me, that's what makes systems engineering so fascinating.

A simple Redis command eventually leads you into operating systems, virtual memory, process management, and kernel optimizations. The deeper you go, the more you realize that modern software is rarely built in isolation. Every layer depends on the layers beneath it.

The next time you see a Redis snapshot running in production, you'll know there's much more happening than "Redis is saving a file."

It's the operating system quietly doing an incredible amount of work behind the scenes.

But snapshots are only one piece of the story.

Once the data leaves memory, an entirely different set of questions begins. How does Redis replicate that data? What happens during a failover? And why can data still be lost even when replication is enabled?

We'll explore those questions in a future article.


🔗 Connect with Me

📖 Blog by Naresh B. A.

👨‍💻 Backend & AI Systems Engineer | Distributed Systems · Production ML

🌐 Portfolio: Naresh B A

📫 Let's connect on LinkedIn | GitHub: Naresh B A

Thanks for spending your precious time reading this. It's my personal take on a tech topic, and I really appreciate you being here. ❤️

Top comments (0)