跪拜 Guibai
← All articles
Backend

Scaling a Student Grade Portal from 5,000 to 500,000 Users Without Buying More Servers

By 程序员代码随笔 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Grade portals are a pure read-heavy, eventually-consistent workload that appears in many domains beyond education—tax return status, insurance claim lookup, exam results, any system with a publish-then-flood pattern. The 1,000x spike on a shoestring budget forces engineers to lean on preheating and queuing rather than over-provisioning, and the article's breakdown of which cache patterns actually help versus which are e-commerce cargo-culting is directly transferable.

Summary

Student grade portals hit a brutal traffic pattern: near-zero load for months, then a 1,000x spike the moment results are published. Unlike e-commerce flash sales, every student queries a different cache key, so lazy-loading caches fail completely. The core insight is that grades are immutable once published and users will accept a 30-second wait, which opens the door to cache preheating and asynchronous processing.

The architecture walks through five versions. V1 handles 5,000 students with a single Spring Boot instance. V2 adds Redis with mandatory preheating triggered at publish time, plus a query switch that blocks traffic until preheating finishes. V3 layers Caffeine local cache and token-bucket rate limiting for 50,000 students. V4 introduces RabbitMQ peak-shaving and MySQL read/write splitting for 200,000 candidates, where cache misses go to a queue and results arrive via push notification. V5 reaches million-scale with K8s HPA, Sentinel circuit-breaking, and CDN-hosted static queue pages.

Each version calls out which classic cache patterns actually apply to this workload. Distributed locks for cache breakdown are nearly useless when every key is accessed by one student. Bloom filters help against enumeration attacks but add maintenance overhead. The article also covers sharding strategy, warning that sharding by semester creates a hotspot on the latest term while sharding by student ID distributes load evenly.

Takeaways
Cache preheating is mandatory for any workload where each user queries a unique key; lazy-loading caches will miss on every first request and crush the database.
A query switch that blocks all traffic until preheating completes is essential infrastructure—without it, the 2–5 minute preheat window still kills the database.
Distributed locks for cache breakdown are nearly useless in student-facing grade queries because every student queries a different key; they only matter for admin dashboards that aggregate many students.
Sharding by semester is an anti-pattern: the latest semester's shard gets 100% of traffic while old semesters sit idle. Sharding by student ID distributes load evenly.
Rate limiting should happen at the gateway layer and key off JWT user ID, not source IP, because campus NAT means one aggressive student can rate-limit an entire dormitory.
Identity verification must run before any cache lookup; checking the cache first and then validating the user leaks data through side channels.
TTL randomization of 0–300 seconds across 50,000 keys naturally staggers expiration and prevents cache avalanche without extra infrastructure.
K8s HPA reacts too slowly for second-scale traffic spikes; production practice is to manually raise minReplicas an hour before publish time and pre-warm Pods.
Returning stale cached grades during a degradation event may violate education-sector data-accuracy compliance; a queue page is safer than showing possibly-wrong scores.
Cold-hot data isolation—keeping the current semester in a small table and archiving old semesters—often avoids the need for full database sharding.
Conclusions

The article's most valuable contribution is its honesty about which textbook cache patterns don't apply. Most system-design content recites cache breakdown, penetration, and avalanche as a checklist; this one explicitly marks distributed locks as low-priority for the student-facing path and explains why.

Sharding by semester being an anti-pattern is a concrete, non-obvious lesson. New engineers often shard by the most visible dimension (time), not realizing it creates a perfect hotspot on the latest period.

The query-switch pattern—blocking all traffic during preheating—is under-discussed in caching literature. Most guides focus on how to warm caches, not on the fact that the warm-up window itself is a vulnerability that needs gating.

The compliance warning about stale grade data during Sentinel degradation is a rare instance of a technical article flagging a legal risk, not just a performance tradeoff. Many engineers would default to serving cached data without considering accuracy regulations.

The advice to generate queue tokens at the gateway rather than the application layer is a sharp operational insight: if the app is down, the queue page still needs to function, so its backend must live at the edge.

Concepts & terms
Cache Preheating
Actively loading data into the cache at publish time rather than waiting for user requests to populate it. Essential when every user queries a unique key, because lazy-loading would cause 100% cache misses on the first wave of traffic.
Query Switch
A flag in Redis (CLOSED/OPEN) that gates all grade-check requests. Set to CLOSED when grades are published and cache preheating begins; set to OPEN only after preheating completes. Prevents the preheat window itself from crushing the database.
Null Object Pattern (for caches)
Caching a sentinel value like 'NULL' with a short TTL when a database query returns empty, so repeated requests for the same non-existent key don't repeatedly hit the database. Simpler than a Bloom filter for most use cases.
Token Bucket Rate Limiting
An algorithm that generates tokens at a fixed rate and consumes one per request. Allows short bursts (accumulated tokens) while capping sustained throughput. Redisson's RRateLimiter implements this in a distributed fashion.
Idempotency State Machine
Using Redis SETNX with states like INIT → PROCESSING → DONE to ensure a message is processed exactly once, even if the message queue delivers it multiple times. The article uses (studentId, semester) as the deduplication key rather than message ID.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗