Cloud SQL vs AlloyDB vs Spanner: Choosing the Right GCP Database
GCP offers three managed relational databases — Cloud SQL, AlloyDB, and Cloud Spanner — that look similar on the surface but solve fundamentally different problems. This guide clarifies which to choose based on workload characteristics: scale requirements, consistency needs, geographic distribution, and cost.
Google Cloud gives you three managed relational database options, and choosing the wrong one for your workload is an expensive mistake to undo. Cloud SQL is familiar MySQL or PostgreSQL managed hosting. AlloyDB is PostgreSQL-compatible with dramatically higher performance. Cloud Spanner is a globally distributed database with external consistency that doesn't look like anything else on the market.
The decision isn't "which is best" — it's "which fits your workload characteristics." This guide walks through the decision framework, the technical differences that matter, and the common mistakes teams make when selecting among these three.
Decision Framework at a Glance
Before diving into details, here's the quick selection heuristic:
| If you need... | Choose... |
|---|---|
| Standard PostgreSQL/MySQL workload | Cloud SQL |
| PostgreSQL + 4x higher performance | AlloyDB |
| Global distribution + strong consistency | Cloud Spanner |
| >99.999% availability SLA | Cloud Spanner |
| Horizontal write scaling | Cloud Spanner |
| OLTP + HTAP in one system | AlloyDB |
| MySQL compatibility | Cloud SQL (MySQL) — not AlloyDB or Spanner |
| Minimum operational cost | Cloud SQL |
| < $1,000/month budget | Cloud SQL |
Cloud SQL: The Workhorse
Cloud SQL is managed MySQL, PostgreSQL, and SQL Server on Compute Engine VMs. It's the most familiar option and the right choice for the majority of web applications, APIs, and internal tools.
When Cloud SQL Is the Right Choice
You're lifting and shifting an existing workload: If you have a PostgreSQL or MySQL application running on-premises or on another cloud, Cloud SQL is the path of least resistance. Connection strings change, almost nothing else does.
Your dataset fits on one machine: Cloud SQL scales vertically. The largest instances support up to 128 vCPUs, 864 GB RAM, and 64 TB storage. If your database fits here (which most do), Cloud SQL works fine.
You need MySQL compatibility: AlloyDB and Spanner are PostgreSQL-compatible (or custom). If you need MySQL specifically, Cloud SQL is your only managed GCP option.
Budget is a constraint: Cloud SQL is dramatically cheaper than AlloyDB for equivalent specs. For a 4-vCPU/26GB instance: Cloud SQL costs ~$250/month vs AlloyDB's ~$380/month.
Cloud SQL Performance Characteristics
# Create a Cloud SQL PostgreSQL instance with HA
gcloud sql instances create prod-postgres --database-version=POSTGRES_15 --tier=db-n1-standard-8 --region=europe-west4 --availability-type=REGIONAL --storage-size=100GB --storage-auto-increase --backup-start-time=02:00 --deletion-protection
# Enable insights for query performance monitoring
gcloud sql instances patch prod-postgres --insights-config-query-insights-enabled --insights-config-query-string-length=1024 --insights-config-record-application-tags --insights-config-record-client-address
Cloud SQL Limitations to Know
- No horizontal write scaling: All writes go to the primary. Read replicas help with read scaling but not write scaling.
- Regional single-primary: HA configuration has a standby in the same region. Cross-region read replicas exist but failover isn't automatic.
- Connection limits: PostgreSQL has hard connection limits. For high-concurrency applications, use Cloud SQL Auth Proxy + PgBouncer for connection pooling.
# Run Cloud SQL Auth Proxy (handles IAM auth, no SQL passwords needed)
./cloud-sql-proxy my-project:europe-west4:prod-postgres --port=5432 --credentials-file=/path/to/service-account-key.json
# Better: use Workload Identity with the proxy on GKE
# The proxy SA only needs roles/cloudsql.client
AlloyDB: PostgreSQL with Serious Performance
AlloyDB is Google's fully managed PostgreSQL-compatible database built on a disaggregated storage architecture. It's not "Cloud SQL with more features" — it's a ground-up reimplementation of the PostgreSQL storage layer.
What Makes AlloyDB Faster
AlloyDB separates compute and storage. The primary instance handles query processing; a distributed storage layer handles persistence. This architecture enables:
- 4x higher OLTP throughput than standard Cloud SQL for equivalent compute
- 100x faster analytical queries on fresh OLTP data (using columnar engine for AP queries)
- Near-zero storage I/O latency due to the in-memory database cache layer
- Faster HA failover (<60 seconds vs Cloud SQL's ~60-120 seconds)
When AlloyDB Is the Right Choice
High-concurrency OLTP: If you're seeing Cloud SQL CPU regularly above 60% and you've already optimized your queries, AlloyDB's storage architecture may give you the headroom you need.
Mixed OLTP + Analytics: AlloyDB includes a columnar engine that automatically maintains a column-oriented copy of your OLTP data. This lets you run analytical queries against fresh data without a separate data warehouse or ETL lag. Not a replacement for BigQuery at petabyte scale, but excellent for operational analytics.
You're already on PostgreSQL: AlloyDB is wire-compatible with PostgreSQL 14+. Migration is generally drop-in for applications (though not for PL/pgSQL extensions that rely on internals).
# Create an AlloyDB cluster and primary instance
gcloud alloydb clusters create prod-cluster --region=europe-west4 --password=StrongPassword123!
gcloud alloydb instances create primary --cluster=prod-cluster --region=europe-west4 --instance-type=PRIMARY --cpu-count=8 --database-flags=max_connections=1000
# Create a read pool (replicas) for read scaling
gcloud alloydb instances create read-pool --cluster=prod-cluster --region=europe-west4 --instance-type=READ_POOL --cpu-count=4 --read-pool-node-count=2
AlloyDB Omni: On-Premises and Other Clouds
AlloyDB Omni is the operator-deployed version that runs on Kubernetes anywhere — on-premises, AWS, Azure, or any cloud. This enables consistent PostgreSQL behavior across environments while you migrate to GCP, or for data sovereignty requirements.
# AlloyDB Omni on GKE
apiVersion: alloydbomni.dbadmin.goog/v1
kind: DBCluster
metadata:
name: prod-cluster
namespace: alloydb-omni
spec:
databaseVersion: "15.7.1"
primarySpec:
resources:
cpu: "8"
memory: "32Gi"
storage:
storageClass: "premium-rwo"
storageSize: "500Gi"
allowExternalIncomingTraffic: true
Cloud Spanner: Globally Distributed Consistency
Cloud Spanner is unlike any other database you've used. It offers:
- External consistency (stronger than serializable isolation in most databases)
- Horizontal scaling: Add nodes to increase throughput linearly
- Global distribution: Multi-region with automatic failover
- 99.999% availability SLA on multi-region configurations
- SQL (ANSI 2011 compliant) with extensions
What Spanner Is NOT
Spanner is not a drop-in replacement for PostgreSQL. It has:
- A different SQL dialect (though a PostgreSQL-compatible interface exists)
- No auto-increment primary keys (use UUIDs or bit-reversal to avoid hotspots)
- Higher cost per vCPU and storage than Cloud SQL
- Eventual consistency for interleaved tables in some configurations
- Learning curve for hot-spot avoidance in schema design
When Spanner Is the Right Choice
Global applications with consistency requirements: If you have users in multiple continents and need strong consistency across all regions simultaneously (not eventual consistency), Spanner is the only managed database that delivers this.
Write throughput beyond a single machine: If your write volume exceeds what a single primary instance can handle (rough limit: ~10,000-20,000 writes/second for Cloud SQL/AlloyDB), Spanner can scale horizontally to millions of writes/second.
Financial systems and inventory: Spanner's external consistency makes it ideal for workloads where split-brain scenarios are financially catastrophic — payment processing, inventory deduction, ledger systems.
# Create a multi-region Spanner instance
gcloud spanner instances create prod-instance --config=nam-eur-asia1 --description="Global production database" --nodes=3
# Create a database
gcloud spanner databases create payments --instance=prod-instance
# Create a schema (Spanner DDL)
gcloud spanner databases ddl update payments --instance=prod-instance --ddl='CREATE TABLE Transactions (
TransactionId STRING(36) NOT NULL,
UserId STRING(36) NOT NULL,
Amount INT64 NOT NULL,
Currency STRING(3) NOT NULL,
Status STRING(20) NOT NULL,
CreatedAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
) PRIMARY KEY (TransactionId)'
Spanner Schema Design: Avoiding Hot Spots
The most common Spanner performance mistake is using sequential primary keys (auto-increment). Spanner partitions data by key range, so sequential inserts all go to the same tablet — creating a write hotspot.
-- BAD: Sequential key creates write hotspot
CREATE TABLE Orders (
OrderId INT64 NOT NULL, -- Sequential values all hit the same split
...
) PRIMARY KEY (OrderId)
-- GOOD: UUID distributes writes across splits
CREATE TABLE Orders (
OrderId STRING(36) NOT NULL, -- UUID4 distributes evenly
UserId STRING(36) NOT NULL,
...
) PRIMARY KEY (OrderId)
-- GOOD: Bit-reversal for timestamp-based keys
-- Reverse the bits of a timestamp to distribute writes
CREATE TABLE Events (
EventId STRING(36) NOT NULL,
-- Store as: REVERSE(TIMESTAMP) + random suffix
EventTimestamp TIMESTAMP NOT NULL,
...
) PRIMARY KEY (EventId)
Interleaved Tables for Efficient Parent-Child Queries
Spanner's interleaving feature physically co-locates parent and child rows, making parent-child joins very fast:
CREATE TABLE Users (
UserId STRING(36) NOT NULL,
Email STRING(256),
CreatedAt TIMESTAMP NOT NULL,
) PRIMARY KEY (UserId);
-- Interleave OrderItems within Orders within Users
CREATE TABLE Orders (
UserId STRING(36) NOT NULL,
OrderId STRING(36) NOT NULL,
TotalCents INT64 NOT NULL,
) PRIMARY KEY (UserId, OrderId),
INTERLEAVE IN PARENT Users ON DELETE CASCADE;
CREATE TABLE OrderItems (
UserId STRING(36) NOT NULL,
OrderId STRING(36) NOT NULL,
ItemId STRING(36) NOT NULL,
Quantity INT64 NOT NULL,
) PRIMARY KEY (UserId, OrderId, ItemId),
INTERLEAVE IN PARENT Orders ON DELETE CASCADE;
Cost Comparison
For a production-scale workload with HA, 100GB storage:
| Database | Config | Monthly Cost |
|---|---|---|
| Cloud SQL PostgreSQL | db-n1-standard-8, HA | ~$480 |
| AlloyDB | 8 vCPU primary, 2-node read pool | ~$850 |
| Cloud Spanner | 1 node, regional | ~$900 |
| Cloud Spanner | 3 nodes, multi-region | ~$3,600 |
Spanner's multi-region configuration is expensive. For most workloads, Cloud SQL or AlloyDB with read replicas is more cost-effective. Spanner's cost is justified when the alternative is building your own distributed database or suffering global replication lag.
Migration Paths
To Cloud SQL: Use pg_dump/pg_restore for PostgreSQL or mysqldump for MySQL. For online migration with minimal downtime, use Database Migration Service (DMS).
To AlloyDB: AlloyDB uses the same PostgreSQL wire protocol. Most applications are drop-in. Use DMS for online migration from Cloud SQL or on-premises PostgreSQL.
To Spanner: Requires schema redesign (UUIDs, no foreign keys in original Spanner dialect, interleaving). Use the Spanner migration tool (harbourbridge) for initial assessment.
# Assess migration complexity from PostgreSQL to Spanner
harbourbridge schema --driver=pgdump --prefix=my-migration < pg_dump_output.sql
For the data pipeline layer that feeds these databases, see our Cloud Dataflow guide. For observability on database performance, see our GCP observability guide.