Every Uber driver’s phone sends a GPS update every 4-5 seconds. Uber has around 5 million active drivers globally. That’s about 1 million location updates per second hitting the write path. Most systems are read-heavy. Driver tracking is unusually write-heavy, and the data model is unusual too: old locations are worthless, only the current one matters.

The Data Model#

A naive approach: a driver_locations table with columns driver_id, latitude, longitude, updated_at. Each GPS ping is an UPDATE to that row.

UPDATE driver_locations
SET latitude = ?, longitude = ?, updated_at = NOW()
WHERE driver_id = ?;

1 million updates per second to a single MySQL table isn’t realistic. Even with connection pooling, the write throughput and locking overhead would saturate the database.

Write-Heavy at Scale#

Options for high-frequency location writes:

Redis as the primary location store: SET driver:1001:location "37.7749,-122.4194" overwrites the previous value. Redis handles millions of writes per second. Location data for 5 million drivers fits comfortably in memory. Reads (for dispatch and map display) are O(1).

The trade-off: Redis is not the right place for historical location data (audit trail, ETA learning data). Write location to Redis for current state, publish to Kafka for historical processing.

graph TD A[Driver Phone: GPS ping every 5s] --> B[Location Service] B --> C[Redis: SET driver:id location - overwrites] B --> D[Kafka: append location event for history] C --> E[Dispatch Service: reads current locations] D --> F[ML Pipeline: learns speed patterns for ETA] style A fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style B fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style C fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style D fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style E fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style F fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff

Geospatial Indexing for Nearby Drivers#

A rider requests a pickup at (37.7749, -122.4194). The dispatch service needs to find available drivers within a 3km radius. Storing all drivers as raw coordinates in Redis and doing a full scan doesn’t scale.

Geohashing solves this: encode driver location as a geohash, index by geohash prefix. Redis’s GEOADD and GEORADIUS commands do this natively, storing locations in a sorted set with geohash-derived scores.

GEOADD drivers 37.7749 -122.4194 "driver:1001"
GEORADIUS drivers 37.7745 -122.4190 3 km ASC COUNT 10

When a driver moves, the old geohash entry is removed and a new one is added. This is the write cost of location updates in the geospatial index.

Handling Stale Locations#

A driver’s last known location is only useful if it’s recent. A driver whose last update was 10 minutes ago might have moved 5km. Set TTLs on driver location entries. If no update arrives within 30 seconds, the driver is considered unavailable. This keeps the index clean without explicit deletion.

At Oracle#

We tracked network element state: which base stations were active, their last heartbeat time. Initially stored in MySQL with one row per element, updated on each heartbeat. At 50K elements sending heartbeats every 30 seconds, we had 1,700 updates per second to one table. We moved to Redis with TTL-based availability detection. Elements that didn’t refresh their key within 90 seconds were flagged as offline. Response time for “show me all offline elements” dropped from 2 seconds (table scan with timestamp comparison) to under 10ms.

What I’m Learning#

For moving objects, current state is more valuable than history and history should live elsewhere. Redis with TTL-based expiry is the right tool for “what is the current state?” questions. Kafka handles “what happened over time?” questions separately.

Have you worked with systems tracking many entities that update frequently, and how did you handle the write throughput?