A team at Shopify once tracked down a mysterious performance regression. Their checkout service, already well-optimized, suddenly started taking 300ms longer per request. Nothing in the code had changed. No new features, no dependency updates, nothing.
Turned out their cloud provider had silently migrated their compute instances to a different availability zone within the same region. The database was still in zone A, but the application servers were now in zone C. Same region, but the extra network hop added 2-3ms per database call. Their checkout flow made about 100 database calls per transaction. Do the math: 100 calls times 3ms equals 300ms of added , just because the data moved a little further away.
Data locality optimization is about systematically identifying and closing these gaps. It's not one big fix. It's dozens of small wins that compound into massive performance improvements.
At the database layer:
Covering indexes. If your query can be satisfied entirely from an index, Postgres never touches the heap table. The index is smaller, fits in RAM more easily, and avoids random I/O. A query that reads from a covering index is often 10-50x faster than one that needs to fetch rows from disk. In Postgres, EXPLAIN ANALYZE will show "Index Only Scan" when this kicks in.
Clustered tables. Physically reorder rows on disk to match your most common query pattern. If you always query orders by customer_id, clustering the table on customer_id means all of a customer's orders are stored contiguously on disk. Sequential reads are 100x faster than random reads on SSDs, and even more on spinning disks.
Partitioned tables. Postgres and MySQL support native table partitioning. Partition your orders table by month, and a query for "orders in March" only scans one partition instead of the entire table. Less data scanned means the working set fits in RAM more often.
At the application layer:
Connection pooling. Keep database connections warm and local. A cold connection to Postgres costs 3-5ms to establish (TCP handshake, TLS negotiation, authentication round trip). A pooled connection costs about 0ms because the handshake already happened. PgBouncer, Prisma's built-in pooler, and Neon's serverless proxy all handle this.
Batch queries. Instead of 50 individual SELECT statements (50 network round trips), fetch all 50 records in a single query with an IN clause (1 round trip). This is the most common locality anti-pattern in production code, and it goes by the name N+1 query problem. 's DataLoader pattern solves it by batching and deduplicating queries within a single request.
Read replicas. Put a read-only copy of your database in the same availability zone as your application servers. Writes go to the primary, reads hit the local replica. This is especially powerful when your read-to-write ratio is 10:1 or higher, which describes most web applications.
At the infrastructure layer:
Zone-aware deployment. Configure your orchestrator (, ECS) to prefer scheduling pods in the same zone as the data they access. Kubernetes topology spread constraints let you pin database-heavy pods to the same AZ as the database. Cross-zone traffic on AWS costs $0.01/GB and adds 1-3ms per call. Same-zone traffic is free and adds about 0.1ms.
placement. For static and semi-static content, push it to edge nodes. A product catalog that changes once per hour doesn't need to be fetched from the origin on every request. Vercel's edge network, Cloudflare Workers, and CloudFront all make this straightforward.
You can't fix what you don't measure. Here's what to look for:
Network between services. Run distributed tracing (Jaeger, Zipkin, Datadog APM) to measure the actual latency between your app and its dependencies. If you see 5ms to your database but 50ms to a , that microservice might be in a different zone or region. Datadog's service map will show you cross-zone calls highlighted in orange.
Cache hit ratios. If your cache has a 60% hit ratio, 40% of requests are falling through to the database. Investigate what's being missed and why. A common cause: cache keys that are too granular, leading to a huge keyspace that doesn't fit in memory. Another: short TTLs that expire before the next request. Target 95%+ hit ratio on hot paths.
Database query plans. Run EXPLAIN ANALYZE on your slow queries. Look for "Seq Scan" on large tables (missing index), "Heap Fetches" on index scans (need a covering index), or "Sort" operations (data not pre-sorted). Each of these is a locality problem: the database is reading data that is far from where it needs to be.
Cross-zone transfer costs. Check your cloud billing. AWS charges $0.01/GB for cross-AZ traffic. If you're seeing thousands of dollars in cross-zone data transfer, your services and data are probably in different zones. Google Cloud's VPC flow logs and AWS's VPC Traffic Mirroring can show you exactly which services are generating cross-zone traffic.
3 questions - Score 80% to pass
Your application makes 80 individual SELECT queries per page load, each taking 2ms. What's the simplest locality optimization?
What's a 'covering index' and why does it help locality?
Your AWS bill shows $4,000/month in cross-AZ data transfer. What's likely happening?