100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
GCP

Firestore and Bigtable Basics

A comparison of Google Cloud's two flagship NoSQL databases: Firestore for flexible document data and Bigtable for massive-scale wide-column workloads.

Databases & IdentityIntermediate9 min readJul 10, 2026
Analogies

Two NoSQL Databases, Two Very Different Jobs

Firestore is a serverless document database organized into collections and documents, with strong consistency, real-time listeners, and native mobile/web SDKs, making it the default choice for app backends like chat apps, e-commerce carts, and user profiles. Bigtable is a wide-column NoSQL database built for massive throughput and low single-digit-millisecond latency at petabyte scale, and it is the engine behind Google products like Search and Maps as well as external use cases such as time-series metrics, IoT sensor data, and ad-tech event logging. Choosing between them comes down to access pattern and scale: Firestore optimizes for flexible queries over moderate data volumes, while Bigtable optimizes for extremely high write/read throughput with queries limited mostly to row-key lookups and scans.

🏏

Cricket analogy: Firestore is like a scorecard app tracking every player's stats per match in a flexible, queryable format, while Bigtable is like the ball-by-ball tracking system at the IPL ingesting millions of sensor events per second from Hawk-Eye across every stadium simultaneously.

Firestore Data Modeling and Real-Time Queries

Firestore organizes data as documents (JSON-like key-value maps) grouped into collections, and documents can contain subcollections, enabling hierarchical structures like users/{userId}/orders/{orderId}. Queries are indexed automatically for single fields, and composite indexes are created (often automatically suggested) for multi-field queries; Firestore also supports real-time listeners that push updates to connected clients the instant underlying data changes, which is why it's popular for chat and collaborative apps. Because Firestore charges per document read/write/delete rather than per query complexity, denormalizing data to minimize document reads is a common and encouraged pattern, unlike traditional relational normalization.

🏏

Cricket analogy: Firestore's subcollections are like a team's document containing a nested subcollection of every player's innings-by-innings stats, letting you drill from team to player to individual match without a separate join table.

Bigtable Schema Design and Row-Key Strategy

Bigtable stores data in sparse, sorted tables where rows are keyed by a single row key, and all query performance hinges on that row-key design because the only efficient access patterns are exact row-key lookup and contiguous row-key range scans. A common design pattern for time-series data is to prefix the row key with a reversed timestamp or a hashed/salted prefix (like a device ID) to avoid hotspotting, where all recent writes land on the same tablet server and overload it. Column families group related columns and are defined upfront, while individual columns within a family can be added dynamically without a schema migration, and Bigtable retains multiple timestamped versions of each cell for time-travel-style access.

🏏

Cricket analogy: Row-key design in Bigtable is like organizing a stadium's ball-by-ball log by match-ID-then-over-then-ball so any specific ball can be fetched instantly, while a poorly chosen key like just 'team name' would create hotspots during India's matches.

python
# Firestore: adding a document with a subcollection
from google.cloud import firestore
db = firestore.Client()
order_ref = db.collection("users").document("u123").collection("orders").document()
order_ref.set({
    "item": "Wireless Mouse",
    "amount": 29.99,
    "status": "processing",
})

# Bigtable: writing a row with a salted, time-series-friendly row key
from google.cloud import bigtable
client = bigtable.Client(project="my-project")
table = client.instance("iot-instance").table("sensor-readings")
row_key = f"device42#{9999999999 - int(time.time())}".encode()
row = table.direct_row(row_key)
row.set_cell("readings", "temperature", "21.4")
row.commit()

Firestore has two modes historically: Native mode (document model with real-time sync) and Datastore mode (legacy, no real-time listeners); new projects should use Native mode unless migrating an existing Datastore application.

A common Bigtable mistake is using a monotonically increasing timestamp as the row-key prefix (e.g., raw Unix time first), which concentrates all new writes on the last tablet in the key range and creates a hotspot; salt or reverse the timestamp instead.

  • Firestore is a serverless document database optimized for flexible queries and real-time sync at app scale.
  • Bigtable is a wide-column database optimized for massive throughput and low-latency access at petabyte scale.
  • Firestore models data as documents and collections, with automatic single-field indexing and composite indexes for multi-field queries.
  • Denormalization is encouraged in Firestore because billing is per document operation, not per query complexity.
  • Bigtable performance depends entirely on row-key design; only exact lookups and range scans are efficient.
  • Salting or reversing timestamps in Bigtable row keys prevents write hotspotting on a single tablet server.
  • Column families in Bigtable are defined upfront, but individual columns can be added dynamically.

Practice what you learned

Was this page helpful?

Topics covered

#GCP#GCPFundamentalsStudyNotes#CloudComputing#FirestoreAndBigtableBasics#Firestore#Bigtable#Two#NoSQL#StudyNotes#SkillVeris