When teams hit the ceiling of what Redis can do — either from memory costs, throughput limitations, or the need for persistent storage without sacrificing sub-millisecond latency — Aerospike is usually where they land. We've migrated several ad-tech clients from Redis clusters that were consuming $40,000/month in RAM to Aerospike deployments on NVMe SSDs at a third of the cost, with better tail latency.
Aerospike isn't a drop-in Redis replacement (different data model, different client protocol), but for specific workloads — real-time bidding, fraud detection, session stores at massive scale, IoT telemetry — nothing else comes close to its performance/cost ratio at high throughput.
- Aerospike delivers sub-millisecond latency at millions of TPS with data persisted to SSD — not purely in-memory
- Hybrid Memory Architecture: indexes live in DRAM, data lives on NVMe SSDs — this is why it's cost-effective at scale
- Best for: real-time bidding, fraud detection, session stores, personalization, IoT — anything needing low latency + durability + massive scale
- Not for: ad-hoc analytics, complex joins, OLAP workloads — use ClickHouse or StarRocks for those
- XDR (Cross-Datacenter Replication) enables active-active global deployments that Redis Cluster cannot match
Why Aerospike Exists: The Problem It Solves
In-memory databases like Redis give you the fastest possible reads and writes — but every byte of data has to fit in DRAM. At scale, DRAM is expensive. A Redis cluster storing 1TB of data needs roughly 1TB of DRAM across your fleet, plus replication overhead. At $8–12/GB for server DRAM, that's $8,000–$12,000 just in memory hardware for 1TB of data.
NVMe SSDs store the same data for roughly 10–50× less cost. The trade-off is that SSDs are slower — but modern NVMe SSDs are fast enough that with the right architecture, you can deliver sub-millisecond reads from SSD-resident data. That's Aerospike's core insight: keep indexes in DRAM (indexes are small — typically 64 bytes per record), serve data from NVMe, and use direct I/O to bypass the OS page cache.
The result: Aerospike can store terabytes of data and serve millions of reads per second at P99 latencies under 1ms on commodity NVMe hardware.
Architecture: Hybrid Memory Storage
How data is stored
Aerospike's storage is namespace-level and configurable per namespace:
- In-memory only: Like Redis — data and indexes both in RAM. Fastest, most expensive. Use for hot caches where data is small and re-generable.
- Hybrid (index in RAM, data on SSD): The recommended production configuration. Indexes fit in DRAM (64 bytes per record). Data is written directly to NVMe using direct I/O, bypassing the OS page cache for predictable latency.
- Index on flash (Aerospike 6+): For datasets so large that even 64-byte-per-record index overhead doesn't fit in DRAM. Stores the index on a separate flash device.
# aerospike.conf — namespace configuration
namespace users {
replication-factor 2
memory-size 8G # DRAM for indexes and metadata
storage-engine device {
device /dev/nvme0n1 # NVMe SSD for data
write-block-size 128K
data-in-memory false # data stays on SSD, not in DRAM cache
direct-io true # bypass OS page cache for predictable latency
}
}
namespace sessions {
replication-factor 2
memory-size 32G
storage-engine memory # this namespace is fully in-memory (hot cache)
}Cluster topology
Aerospike uses a shared-nothing, masterless architecture. Every node is equal — there's no primary/secondary relationship. Data is distributed across nodes using a consistent hash partition map. Any node can serve reads and writes for any key, routing internally when needed.
# Check cluster health
asinfo -v 'statistics' | grep cluster
# Or with AQL
AQL> show sets
AQL> show namespaces
# Node-level stats
asadm -e "show statistics like cluster_size"Cross-Datacenter Replication (XDR)
XDR enables active-active replication between clusters in different datacenters. Both clusters accept writes independently, and XDR syncs changes asynchronously. Conflict resolution is configurable (last-write-wins, generation-based, etc.).
This is the feature that makes Aerospike compelling for global ad-tech deployments: a bidder in Tokyo and a bidder in Virginia can both write user profile updates, and XDR reconciles them without a global consensus protocol penalizing every write with cross-region latency.
Data Model
Aerospike's data model differs from Redis and from relational databases:
Hierarchy:
Cluster
└── Namespace (like a database or keyspace)
└── Set (like a table — optional, used for grouping records)
└── Record (identified by a primary key)
└── Bins (like columns — each record can have different bins)
Example record:
Namespace: user_data
Set: profiles
Key: user:1001
Bins: {
name: "Alice Chen",
plan: "premium",
last_seen: 1737500000,
feature_flags: ["ab_test_v2", "new_checkout"]
}Supported data types per bin
- Integer, String, Bytes, Double
- List, Map (nested structures)
- GeoJSON (for geospatial queries)
- HyperLogLog (cardinality estimation, like Redis HLL)
Core Operations
Python client
import aerospike
config = {'hosts': [('aerospike-host', 3000)]}
client = aerospike.client(config).connect()
# Write a record
key = ('user_data', 'profiles', 'user:1001')
bins = {
'name': 'Alice Chen',
'plan': 'premium',
'last_seen': 1737500000,
'feature_flags': ['ab_test_v2', 'new_checkout']
}
# TTL in seconds (0 = use namespace default, -1 = no expiry)
meta = {'ttl': 86400 * 30} # 30 days
client.put(key, bins, meta)
# Read a record
(key, meta, record) = client.get(key)
print(record['name']) # Alice Chen
# Read specific bins only (reduces data transfer)
(key, meta, record) = client.select(key, ['name', 'plan'])
# Update a single bin without reading the whole record
client.put(key, {'last_seen': 1737600000}) # partial update
# Atomic counter increment (no read-modify-write needed)
client.increment(key, 'page_views', 1)
# Delete
client.remove(key)Batch operations (critical for throughput)
# Read multiple records in a single network round-trip
keys = [
('user_data', 'profiles', f'user:{i}')
for i in range(1000, 1100)
]
# Batch read — much faster than 100 individual gets
records = client.get_many(keys)
# Write with a policy: update only if record exists (no insert)
policy = {'exists': aerospike.POLICY_EXISTS_UPDATE}
client.put(key, {'plan': 'enterprise'}, {}, policy)Secondary indexes and queries
# Create a secondary index (run once)
client.index_integer_create('user_data', 'profiles', 'account_tier', 'idx_tier')
# Query using the index
from aerospike import predicates as p
query = client.query('user_data', 'profiles')
query.where(p.equals('account_tier', 2))
results = query.results()
# Range query
query.where(p.between('last_seen', 1737000000, 1737600000))
# Note: secondary index queries scan matching records across all nodes
# For high-cardinality filters, this is efficient
# For low-cardinality (e.g., boolean), prefer scan + UDFProduction Configuration
# /etc/aerospike/aerospike.conf — production baseline
service {
paxos-single-replica-limit 1 # minimum nodes for a write quorum
proto-fd-max 15000 # max file descriptors per node
work-threads 8 # match to (CPU cores * 2) roughly
}
network {
service {
address any
port 3000
access-address 10.0.1.10 # bind to private IP only
}
heartbeat {
mode mesh
address 10.0.1.10
port 3002
mesh-seed-address-port 10.0.1.11 3002
mesh-seed-address-port 10.0.1.12 3002
interval 150
timeout 10
}
fabric {
port 3001
}
}
namespace user_data {
replication-factor 2
default-ttl 30D # records expire after 30 days by default
nsup-period 120 # eviction scan every 120 seconds
memory-size 16G # DRAM for indexes
storage-engine device {
device /dev/nvme0n1
device /dev/nvme1n1 # stripe across multiple NVMe devices
write-block-size 128K
data-in-memory false
direct-io true
post-write-queue 256
}
}Performance Tuning
Benchmark your NVMe before sizing
# Aerospike Certification Tool (ACT) — validates NVMe performance
# Download: https://github.com/aerospike/act
act -d /dev/nvme0n1 -t 3600 # run 1-hour I/O stress test
# Aerospike requires:
# Sequential write bandwidth: > 1 GB/s
# Random read IOPS (4KB): > 100K IOPS for good performance
# Latency at 100% load: P99 < 1ms
# AWS recommended instance types for Aerospike:
# i3.2xlarge (1.9TB NVMe): good starting point
# i3.4xlarge (3.8TB NVMe): for larger datasets
# i3en.xlarge (2.5TB NVMe): better cost per TBKey metrics to monitor
# Real-time node stats
asadm -e "show statistics"
# Key metrics:
# client_read_success / client_read_error -- read hit rate
# client_write_success / client_write_error
# device_read_tps / device_write_tps -- SSD throughput
# memory_used_pct -- DRAM for indexes (alert > 80%)
# storage_used_pct -- SSD (alert > 75%)
# migrate_progress_recv / send -- data migration (expect during node joins)
# Latency histogram
asadm -e "show latencies"
# Shows P50, P90, P99 for reads, writes, udf, batch
# Alert thresholds: P99 reads > 5ms, P99 writes > 5msAerospike vs Redis: When to Choose Which
| Factor | Aerospike | Redis / Valkey |
|---|---|---|
| Data size | Terabytes (data on SSD) | Hundreds of GB (all in RAM) |
| Latency | Sub-ms (P99 typically 0.5–2ms) | Sub-ms (P99 typically 0.2–1ms in-memory) |
| Throughput | Millions of TPS with NVMe | Hundreds of thousands TPS per node |
| Cost at 1TB+ | 10–50× cheaper than all-RAM | Very expensive at multi-TB scale |
| Data structures | Key-value, lists, maps, geospatial | Richer (sorted sets, streams, HLL, etc.) |
| Multi-datacenter | Native XDR active-active | Redis Enterprise Geo (commercial) |
| Client ecosystem | Aerospike-specific clients | Massive (redis-py, ioredis, Jedis...) |
| Best for | Large-scale persistent NoSQL (RTB, fraud, IoT) | Caching, pub/sub, smaller datasets, richer data structures |
The decision often comes down to data size. If your dataset fits comfortably in RAM and your use case benefits from Redis' rich data structures (sorted sets, streams, pub/sub), stay with Redis/Valkey. If you're managing terabytes of data with sub-millisecond latency requirements and durability, Aerospike is worth the client migration cost.
When NOT to Use Aerospike
- Small datasets (<50GB): Redis/Valkey is simpler to operate and has a richer ecosystem. Aerospike's complexity pays off at scale.
- Complex queries and joins: Aerospike is a key-value store. Secondary indexes exist, but multi-table joins, aggregations, and SQL-style queries are not its strength.
- Analytics workloads: For aggregating billions of events, use ClickHouse or StarRocks. Aerospike is for low-latency point reads and writes, not full-table scans.
- Teams without NVMe hardware: Aerospike's performance advantage depends heavily on fast NVMe SSDs. On spinning disks or slow SSDs, the latency guarantee disappears.
Working with JusDB on Aerospike
Aerospike deployments have a steeper operational learning curve than Redis or Cassandra — namespace design, NVMe sizing, XDR configuration, and eviction policy tuning all require hands-on experience. Getting these wrong means either paying for too much hardware or hitting unexpected latency spikes under load.
We've designed and operated Aerospike clusters for ad-tech, fintech, and IoT workloads. Common engagements include migrating from over-provisioned Redis clusters, sizing NVMe-backed namespaces for specific throughput and latency targets, and setting up XDR for multi-region active-active deployments.
Our Aerospike consulting includes architecture design, benchmarking with ACT, and production cluster operations. If you're evaluating Aerospike for your workload or troubleshooting an existing deployment, reach out.
Related reading: DynamoDB to Aerospike Migration Case Study | Redis vs Valkey Guide | Aerospike Consulting