Synchronous against asynchronous: the consequences
Write-through writes synchronously into both cache and database, guaranteeing consistency at the cost of extra latency. Write-back only writes to the cache and delays the database write asynchronously, saving latency but creating a real risk of data loss if the cache server fails. Which strategy fits depends on the consistency requirements of the specific data.
Table of Contents
- 1. Two fundamentally different write strategies
- 2. Write-through: synchronous writing in detail
- 3. Implementing write-through in PHP
- 4. Write-back: asynchronous writing in detail
- 5. Implementing write-back with a queue
- 6. Understanding the data loss risk in write-back
- 7. Measuring latency: the real difference
- 8. Decision criteria for practice
- 9. Write-through and write-back compared directly
- 10. Summary
- 11. FAQ
1. Two fundamentally different write strategies
Anyone using Redis not just for reads but also as a target for write operations has to choose between two fundamentally different strategies: write-through caching and write-back caching. Both strategies solve the same problem, namely how the cache stays current when the source data changes, but they make opposite trade-offs between consistency, latency and fault tolerance.
The fundamental difference lies in the order and synchronicity of the write operations. Write-through writes to both cache and database within the same request and waits for both confirmations before the operation counts as complete. Write-back writes only to the cache and defers the database write to a later, asynchronous step. This seemingly small shift has far reaching consequences for latency, consistency guarantees and behavior during a failure.
2. Write-through: synchronous writing in detail
In write-through caching, every write operation from the application triggers two synchronous writes: first to the database, then to Redis, or in the reverse order depending on the implementation. The application waits for confirmation from both systems before reporting the operation as successful. The result is a guarantee that many other caching strategies do not offer: cache and database are consistent at every point in time, there is no window with stale data in the cache.
The price of this guarantee is latency. Every write operation takes at least as long as the slower of the two target systems, in practice usually the database. With a typical relational database at 5 to 15 milliseconds of write latency, this time adds fully to the user's response time, and Redis barely speeds anything up here because it has to wait for the database anyway. Write-through therefore does not pay off for the write latency itself, only for the subsequent read operations, which are then guaranteed to get current data from the cache.
3. Implementing write-through in PHP
Implementing write-through in PHP follows a clear pattern: a transaction writes to the database first, and only if that step succeeded is the cache updated. If the order is reversed and the database write fails, the cache ends up holding data that does not exist in the database at all, a significantly more dangerous state than a plain cache miss.
It is also important that a failure while updating the cache does not roll back the transaction. If the database write succeeded but Redis is unreachable, the operation should still count as successful, and the stale cache entry should be actively deleted instead of erroneously updated.
<?php
declare(strict_types=1);
final class WriteThroughProductRepository
{
public function __construct(
private readonly \Predis\Client $redis,
private readonly \PDO $pdo,
private readonly int $ttlSeconds = 3600
) {
}
/**
* Write-through update: database first, then cache, synchronously.
*/
public function updatePrice(int $productId, float $price): void
{
$stmt = $this->pdo->prepare(
'UPDATE products SET price = :price WHERE id = :id'
);
$stmt->execute(['price' => $price, 'id' => $productId]);
$key = "product:{$productId}";
try {
$product = $this->loadFromDatabase($productId);
$this->redis->setex($key, $this->ttlSeconds, json_encode($product));
} catch (\Predis\Connection\ConnectionException $e) {
// Database write succeeded; drop the stale cache entry instead
$this->redis->del([$key]);
}
}
private function loadFromDatabase(int $productId): array
{
$stmt = $this->pdo->prepare('SELECT * FROM products WHERE id = :id');
$stmt->execute(['id' => $productId]);
return $stmt->fetch(\PDO::FETCH_ASSOC);
}
}
4. Write-back: asynchronous writing in detail
In write-back caching, sometimes also called write-behind, the application writes exclusively to Redis and reports the operation as complete immediately. The transfer to the database happens later, either through a background process that periodically synchronizes changed keys, or through a queue that processes write operations asynchronously. Redis itself briefly becomes the sole source of truth for the written data.
The advantage is dramatically lower latency, because a write operation only waits on Redis, and Redis write operations typically sit in the low single digit millisecond range, often even below that. In addition, write-back lets multiple changes to the same record be coalesced into a single database write, which drastically reduces the effective write load on the database for counters, rating points or session data. This efficiency comes at the cost of a structural risk, covered in detail in the next section.
# Write-back: write operation initially lands only in Redis
redis-cli SET counter:pageviews:4711 8420
redis-cli SADD dirty:products 4711
# Background process periodically reads the "dirty" set
redis-cli SMEMBERS dirty:products
# 1) "4711"
# After a successful DB sync, the marker is removed
redis-cli SREM dirty:products 4711
5. Implementing write-back with a queue
In practice, write-back is usually implemented with a combination of a Redis write and a separate queue that handles synchronization into the database. The application writes the new value to Redis and simultaneously marks the affected key as "dirty", for example in a Redis set. A separate worker process periodically reads this set, transfers the changes into the database, and only removes the marker after successful confirmation.
This pattern requires idempotency on the worker's side, because a crash between the database write and marker removal causes the same value to be synchronized again on the next run. For additive operations like counters this is harmless, but for absolute values like prices the worker must use "last value wins" semantics to safely handle duplicate processing.
<?php
declare(strict_types=1);
final class WriteBackSyncWorker
{
public function __construct(
private readonly \Predis\Client $redis,
private readonly \PDO $pdo
) {
}
/**
* Periodically flush dirty keys from Redis into the database.
*/
public function flushDirtyProducts(): void
{
$dirtyIds = $this->redis->smembers('dirty:products');
foreach ($dirtyIds as $productId) {
$raw = $this->redis->get("product:{$productId}");
if ($raw === null) {
$this->redis->srem('dirty:products', [$productId]);
continue;
}
$product = json_decode($raw, true);
$stmt = $this->pdo->prepare(
'UPDATE products SET price = :price WHERE id = :id'
);
$stmt->execute(['price' => $product['price'], 'id' => $productId]);
// Only remove the marker after a confirmed database write
$this->redis->srem('dirty:products', [$productId]);
}
}
}
6. Understanding the data loss risk in write-back
The central risk of write-back caching is data loss on a Redis server failure before the asynchronous synchronization into the database has completed. Any changes that exist only in Redis and have not yet been persisted are lost at that moment, even if Redis is configured with AOF persistence, because a window between the write operation and the disk flush always remains. This risk is the main reason write-back is not suitable for every kind of data.
The size of the risk can be controlled through the window between write operation and synchronization: a worker that synchronizes every 500 milliseconds risks losing at most 500 milliseconds worth of changes in the worst case. For counters like page views or click statistics, this risk is usually acceptable, because losing a few data points barely distorts the overall statistic. For payment data or order status the same risk is unacceptable, and write-through or a direct, synchronous database write without a cache detour is the right choice.
7. Measuring latency: the real difference
The latency difference between the two strategies can be measured directly and is often larger than developers initially assume. A write-through operation against a MySQL instance in the same data center typically sits at 8 to 20 milliseconds, dominated by the database's disk commit. A pure write-back operation against Redis is usually under one millisecond, since Redis operates in memory and the AOF flush is buffered by default.
This factor of 10 to 20 makes write-back especially attractive for high frequency write operations, such as live counters, rate limiting state or session data, where users would directly feel the latency of every single write. For rare write operations, such as an order change once a minute, the latency difference barely matters, so the additional implementation effort of write-back rarely pays off there.
# Measure Redis write latency directly
redis-cli --latency -i 1
# min: 0, max: 1, avg: 0.12 (ms) over 1 second
# Identify slow commands in the slowlog
redis-cli SLOWLOG GET 10
# Check AOF configuration, relevant for write-back risk
redis-cli CONFIG GET appendfsync
# 1) "appendfsync"
# 2) "everysec"
8. Decision criteria for practice
The choice between write-through and write-back should be made based on three questions. First: how critical is data loss for this specific data type? Financial data and order status require write-through or a direct database write, while counters and metrics can tolerate the risk of write-back. Second: how high is the write frequency? For rare write operations, the implementation effort of write-back outweighs the benefit. Third: how important is the latency of the write operation itself for the user experience?
In many real systems, both strategies run in parallel, depending on the data type. An e-commerce system typically uses write-through for orders and payment status, while product view counters and shopping cart interaction data run through write-back. This hybrid approach uses the strengths of both patterns deliberately, instead of forcing a single strategy across the entire system.
# Critical data type: write synchronously with write-through
redis-cli SET order:8842:status "paid" EX 86400
# Uncritical counter: write-back only into Redis, sync via worker
redis-cli INCR pageviews:product:4711
redis-cli SADD dirty:pageviews 4711
# Both strategies in the same system, separated by data type
redis-cli TYPE order:8842:status
redis-cli TYPE pageviews:product:4711
9. Write-through and write-back compared directly
The following table summarizes the key differences between the two write strategies and serves as a quick decision aid for practice.
| Criterion | Write-Through | Write-Back |
|---|---|---|
| Write latency | High, dominated by the database | Low, only Redis in the critical path |
| Consistency | Always current, no delay | Briefly inconsistent until synced |
| Data loss risk | Very low | Real on Redis failure before sync |
| Implementation effort | Low, two synchronous writes | Higher, worker and idempotency needed |
| Typical use | Orders, payment data | Counters, session data, metrics |
This comparison shows that there is no universally superior strategy between write-through and write-back, only a fitting choice for the given data type. Anyone who knows both patterns and combines them deliberately avoids both unnecessary latency on critical data and unnecessary data loss risk on data that should actually be written synchronously.
Mironsoft
Redis architecture, caching strategies and backend performance
Need the right write strategy for your data?
We assess your data types by consistency requirements and write frequency, and implement the right combination of write-through and write-back with clean error handling.
Data Classification
Assessing consistency requirements and risk profile per data type
Implementation
Cleanly implementing write-through and write-back with worker processes
Fault Tolerance
Deliberately reducing persistence configuration and data loss risk
10. Summary
Write-through caching and write-back caching solve the same problem with opposite trade-offs. Write-through guarantees consistency through synchronous writes to cache and database, but pays for that guarantee with latency dominated by the slower database. Write-back drastically reduces write latency by only writing Redis synchronously, but accepts a real data loss risk if Redis fails before the asynchronous synchronization completes.
The right decision depends on the specific data type: critical, consistency dependent data such as payments belongs in write-through, high frequency, loss tolerant data such as counters benefits from write-back. Most production systems combine both strategies deliberately, rather than forcing a single solution across every data type.
Write-through vs. write-back caching, the essentials at a glance
Write-Through
Synchronous writes to database and cache. Always consistent, but latency dominated by the slower database.
Write-Back
Writes only to Redis, database asynchronously via a worker. Low latency, but real data loss risk.
Decision Criterion
Assess the criticality of data loss, write frequency and latency sensitivity of the user experience.
Hybrid Use
Combine write-through for payments and orders with write-back for counters and session data.