š Scaling to 93K RPM: Moving Quota Management from SQL to Redis
Source: Dev.to
What happened?
Your favorite artist announced an upcoming concert, and customers rushed to buy tickets. The eāticketing platform started returning errors and became painfully slow.
The root cause was the database CPU spiking toāÆ100āÆ% because a single āhotā rowāused to count the quota for the eventāwas being queried and updated continuously. The table looked like this:
| eventId | showtimeId | quota | reserved |
|---|---|---|---|
| 1 | 1 | 15,000 | 10,000 |
Each request read and wrote this row, causing lock contention and exhausting CPU resources.
The solution
Moving the quota counter to Redis
Redis stores data in RAM, offering far lower latency than diskābased databases. A naĆÆve implementation might look like this:
function reserveTicket(showtimeId, amount) {
const reserved = parseInt(redis.get('{key}'));
if (reserved {
// ā¦order creation logicā¦
const result = await redis.eval(reserveLuaScript, {
keys: ['{key}'],
arguments: [String(amount), String(quota)],
});
if (result !== 1) {
throw error('reservation failed');
}
});
// ā¦other logicā¦
}
Persisting the count back to the database
Because Redis is a cache, the authoritative source of truth remains the relational database. A background worker polls Redis everyāÆ5āÆseconds and writes the current count back to the DB, providing eventual consistency without sacrificing performance.
Result
- Orders per minute increased from 5,000 to 93,240.
- Database load dropped dramatically, eliminating the CPU bottleneck.
- The system now handles peak ticketāselling traffic reliably.
Key takeaways
- Diagnose the real cause of performance problems; in this case, a hot row caused DB lock contention.
- Align system design with actual usage patternsāhot rows need special handling in highāthroughput scenarios.
- Use Redis (or another ināmemory store) with atomic Lua scripts to manage counters safely.
- Keep the relational database as the source of truth and synchronize it asynchronously for durability.