Skip to main content
Why CacheeHow It Works
All Verticals5G TelecomAd TechAI InfrastructureFraud DetectionGamingTrading
PricingDocsBlogSchedule DemoLog InStart Free Trial
← Back to Blog
Observability

Your Cache Hit Rate Is Lying to You — What to Measure Instead

Your dashboard shows an 85% cache hit rate. Looks healthy. The number is green, trending upward, and nobody has filed a ticket about it in months. But your users are still complaining about latency. Your database is still running hot. Page loads still spike during peak traffic. The hit rate metric is lying — not because it is wrong, but because it is lying by omission. It treats every cache interaction as equal, and in production, they never are. Here are the four metrics that actually tell you whether your cache is doing its job.

Why Hit Rate Lies

Cache hit rate is a ratio: hits divided by total lookups. It answers one question — “what percentage of requests found data in the cache?” — and answers it accurately. The problem is that this question is almost useless in isolation. An 85% hit rate on 1 million reads per hour means 150,000 misses. If those misses are evenly distributed across cheap, fast queries — config flags, feature toggles, static metadata — then 85% might genuinely be fine. But that is never what happens in production.

In reality, cache misses cluster around your most expensive operations. The queries that take 50ms, 100ms, 200ms to resolve from the origin database are exactly the queries that are hardest to cache effectively: they have complex parameters, short TTLs, or high cardinality. Meanwhile, the keys that are easy to cache — the ones with long TTLs, simple lookups, and high reuse — inflate your hit rate by being hit thousands of times per minute. Your 85% hit rate is dominated by config flags returning in 0.5ms. Your 15% miss rate is dominated by aggregation queries returning in 200ms.

The math exposes the lie: 850,000 hits at 1ms average = 850 seconds of saved origin time. 150,000 misses at 50ms average = 7,500 seconds of origin load. Your cache is saving 850 seconds while allowing 7,500 seconds of database work to pass through uncached. The hit rate says 85%. The reality is that your cache is only intercepting 10% of your origin load by cost.

Hit rate treats all keys as equal. A cache hit on a static config flag and a cache hit on a 200ms aggregation query both count as “1 hit.” A miss on a 1ms lookup and a miss on a query that joins six tables and scans 2 million rows both count as “1 miss.” This is like measuring a hospital’s effectiveness by counting the percentage of patients who walk out alive, without distinguishing between someone who came in for a flu shot and someone who came in with a cardiac arrest. The number is technically correct. It is also meaningless for decision-making.

Metric 1: Miss Cost

Instead of counting misses, measure what each miss costs. Miss cost is the latency (or resource consumption) incurred by every request that falls through to the origin. A miss on a key that resolves in 1ms from the database is irrelevant — caching it would save almost nothing. A miss on a key that requires a 200ms aggregation query, locks a database connection for a quarter-second, and consumes measurable CPU on your read replica is catastrophic. Ten of those per second will saturate a database connection pool. A hundred of them will take down your read replica.

Weighted miss cost is the metric that replaces hit rate as your primary indicator. Compute it as the sum of (miss count per key * origin latency per key) across all keys. This gives you a single number — total origin time consumed by cache misses — that directly correlates with your database load and your user-facing tail latency. When weighted miss cost goes down, your database gets healthier and your P99 improves. When it goes up, trouble is coming — even if your hit rate is climbing.

# Weighted miss cost calculation weighted_miss_cost = sum( miss_count[key] * origin_latency_p50[key] for key in all_keys ) # Example: 85% hit rate, but miss cost reveals the truth config_flags: 800K hits, 20K misses @ 1ms = 20s origin time aggregations: 50K hits, 130K misses @ 50ms = 6,500s origin time # Total miss cost: 6,520s — 99.7% from aggregations

Metric 2: Origin Load Factor

Origin Load Factor answers a different question: what percentage of your database’s capacity is being consumed by traffic that should have been cached? This connects your cache performance directly to your infrastructure cost and your failure risk. If your PostgreSQL primary is running at 70% CPU and 60% of that load comes from queries that missed the cache, your cache is responsible for 42% of your database’s total resource consumption. No hit rate percentage captures this relationship.

Measure origin load factor by tagging database queries that originate from cache misses (most cache libraries support miss callbacks or fallthrough handlers) and tracking their aggregate resource consumption — CPU time, I/O wait, connection hold time. When your origin load factor exceeds 40%, your cache is not protecting your database. It is providing the illusion of protection while letting the expensive traffic through. This is the metric that predicts database-driven outages. Hit rate does not predict outages. A system can maintain 95% hit rate right up until the moment the database falls over from the 5% that got through.

85% Hit Rate (Looks Good)
62% Origin Load Factor
6,500s Weighted Miss Cost

Metric 3: P99 Spread

P99 spread is the gap between your median response time (P50) and your 99th percentile response time (P99). If your P50 is 2ms and your P99 is 200ms, you have a 100x spread. That spread is almost always caused by cache misses. The 99% of requests that hit the cache return in 1–3ms. The 1% that miss fall through to the origin and return in 50–200ms. A tight P50-to-P99 spread means your cache is consistently protecting users. A wide spread means some users are getting the cached experience and others are getting the raw-database experience — and those unlucky users are the ones filing support tickets.

A truly effective cache should collapse this spread. If your hit rate reaches 99%+, the P99 converges toward the P50 because almost no requests fall through to the slow path. But here is the insight: you can have a 95% hit rate and still have a massive P99 spread if the 5% that miss are the slowest queries. Conversely, you could have an 80% hit rate with a tight spread if the misses are all on fast keys. The spread tells you what hit rate cannot: whether cache misses are impacting the experience your users actually have.

Wide P99 Spread (85% Hit Rate, Expensive Misses)

P50 (cache hit)
2 ms
P90 (cache hit)
5 ms
P99 (cache miss)
200 ms
Spread 100x

Collapsed P99 Spread (99%+ Hit Rate, Predictive Warming)

P50 (cache hit)
1.5 ms
P90 (cache hit)
2 ms
P99 (warm hit)
4 ms
Spread 2.7x

Metric 4: First-Request Latency

First-request latency measures how long the first request for a key takes after the cache is cold — after a deploy, after a TTL expiry, after a cache flush, or when a never-before-seen key is requested. Hit rate tells you nothing about these moments because, by definition, there are no hits to count. But these are the moments your users feel the most. The first visitor after a deploy hits an empty cache. Every key misses. Page load times triple. Your monitoring shows a latency spike that recovers over 30–60 seconds as the cache warms up. Then everything looks fine again — until the next deploy.

First-request latency is the metric that reveals your cold start vulnerability. If your median first-request latency is 150ms and you deploy 10 times per day, you are inflicting 10 cold-start episodes on your users daily — each one lasting until the cache re-warms. TTL-based expiry creates the same problem at a smaller scale: every key that expires forces the next requester to absorb the full origin latency. At scale, with millions of keys expiring on staggered TTLs, there is a constant background hum of first-request penalties that hit rate completely hides. Cache warming strategies exist specifically to eliminate this metric — pre-loading keys before they are requested so that first-request latency converges toward cached-request latency.

Cold start in numbers: A typical application with 50,000 cached keys and 5-minute TTLs expires ~167 keys per second. Each expiry forces one user to absorb the full origin latency on their next request. At 167 cold-start penalties per second, that is 167 users per second getting the slow path — invisible in hit rate because the metric resets after every miss is backfilled.

What Actually Fixes This

The common thread across all four metrics is that traditional caching optimizes for the wrong objective. LRU, LFU, and TTL-based eviction all optimize for hit count — they keep frequently accessed keys warm and evict infrequently accessed keys. But hit count is not the metric that matters. What matters is miss cost — the total impact of the requests that get through to the origin. An ideal cache would not prioritize keeping a config flag warm (accessed 10,000 times/min, 1ms origin cost) over keeping an aggregation query warm (accessed 100 times/min, 200ms origin cost). The aggregation query generates 20x more origin load per miss, even though it is accessed 100x less frequently.

Predictive caching optimizes for miss cost instead of hit count. By learning the origin latency, access frequency, and expiry patterns of every key, an ML-driven cache can make intelligent eviction and pre-warming decisions. It keeps expensive-to-fetch keys warm even if they are accessed infrequently. It pre-loads keys before TTL expiry so that first-request latency disappears. It prioritizes cache space for the keys that generate the most origin load when they miss — not the keys that are accessed the most often. The result is that all four metrics improve simultaneously: weighted miss cost drops, origin load factor falls, P99 spread collapses, and first-request latency converges toward zero.

If your cache is not improving performance despite a healthy hit rate, the diagnosis is almost always the same: you are caching the easy keys and missing the expensive ones. Switching from hit-rate-optimized eviction to cost-optimized eviction — combined with architectural changes that push hit rates above 99% — is the difference between a cache that looks good on dashboards and a cache that actually protects your infrastructure.

Further Reading

Also Read

Stop Measuring the Wrong Metric. Start Measuring Impact.

Cachee optimizes for miss cost, not hit count. See how predictive pre-warming and L1 in-process caching collapse all four metrics that matter.

Start Free Trial Schedule Demo