Spring Boot NoSQL: MongoDB, Redis, and Cassandra
Learn how to integrate MongoDB, Redis, and Cassandra with Spring Data in your Spring Boot applications for scalable NoSQL solutions.
Learn how to integrate MongoDB, Redis, and Cassandra with Spring Data in your Spring Boot applications for scalable NoSQL solutions. The guide uses practical examples to explain when to use nosql databases, architecture comparison and shows how to apply the ideas in a Spring Boot project. It closes with common pitfalls and production checks so you can apply the pattern with fewer surprises.
Spring Boot NoSQL Databases
Spring Data abstracts NoSQL databases in Spring Boot projects. It handles the boilerplate so you can focus on modeling your data and writing queries that scale. This post walks through MongoDB, Redis, and Cassandra, with practical implementation details, failure scenarios, and trade-offs you will face when choosing between them.
When to Use NoSQL Databases
Introduction
NoSQL databases trade some relational guarantees or query conventions for models and scaling strategies suited to particular workloads. This guide compares common NoSQL approaches in Spring Boot, explains how repositories and templates fit each model, and highlights the consistency, indexing, and data-modeling decisions that matter in production.
Architecture Comparison
Understanding how these databases differ at the architecture level helps when debugging production issues or deciding which one fits your use case.
graph TB
subgraph MongoDB_Architecture
M1[Mongos Router] --> RS1[Replica Set 1]
M1 --> RS2[Replica Set 2]
RS1 --> P1[Primary]
RS1 --> S1[Secondary]
RS1 --> S2[Secondary]
end
subgraph Redis_Architecture
R1[Redis Client] --> Sentinel1[Redis Sentinel]
Sentinel1 --> RP1[Redis Primary]
Sentinel1 --> RFe1[Redis Replica]
Sentinel1 --> RFe2[Redis Replica]
RP1 --> AOF[AOF Persistence]
RP1 --> RDB[RDB Snapshot]
end
subgraph Cassandra_Architecture
C1[Cassandra Client] --> CC[Coordinator Node]
CC --> PN1[Partition Node 1]
CC --> PN2[Partition Node 2]
CC --> PN3[Partition Node 3]
end
MongoDB uses a mongos router to shard data across replica sets. Reads go to primaries by default, with secondaries available for read scaling. Redis relies on Sentinel for failover and supports both RDB snapshots and AOF persistence. Cassandra uses a coordinator node that routes requests based on partition keys, with data replicated across multiple nodes based on the configured replication factor.
Failure Scenarios
Every distributed system fails eventually. Knowing what breaks and how these databases respond matters for building resilient systems.
Connection Failures
MongoDB throws MongoTimeoutException when it cannot connect to a primary within the configured timeout. Your application needs connection pooling and retry logic. Spring Data MongoDB automatically retries certain operations, but bulk writes and transactions require explicit handling.
Redis connection failures usually manifest as RedisConnectionFailureException. If Sentinel is configured, clients should detect failover and reconnect automatically, but this window of unavailability can range from milliseconds to tens of seconds depending on Sentinel configuration.
Cassandra connection failures lead to NoHostAvailableException. The coordinator node concept means failures can be partial, affecting only certain partition ranges. Applications should implement connection pool exhaustion handling and backpressure.
Data Inconsistency
MongoDB replica sets provide eventual consistency by default for secondary reads. If you read from secondaries during replication lag, you get stale data. Using ReadPreference.SECONDARY_PREFERRED or ReadPreference.NEAREST trades consistency for latency.
Redis replication is asynchronous. If a primary fails before replicating writes to replicas, data loss occurs. Redis Sentinel can promote a replica, but acknowledged writes that never reached the replica are gone. For critical data, consider Redis Cluster with write concerns.
Cassandra is eventually consistent by design. Concurrent writes to the same partition key resolve based on timestamp, with the latest write winning. This works for many use cases but breaks applications expecting strict ordering. Use LIGHTWEIGHT_TRANSACTIONS for conditional writes when you need linearizability.
Timeout Issues
MongoDB operations have socket timeouts and server selection timeouts. Long-running queries like aggregations can exceed defaults. Index your queries and set appropriate timeouts for batch operations.
Redis blocking commands like BLOP and transactions without explicit WATCH can block indefinitely. Set timeout in redis.conf and use connection pool limits to prevent resource exhaustion.
Cassandra drivers apply per-request timeouts that default to 12 seconds. Batch statements with many partitions can exceed this. Break large batches into smaller chunks and tune timeout values based on your SLAs.
Trade-off Comparison
| Aspect | MongoDB | Redis | Cassandra |
|---|---|---|---|
| Data Model | Document (BSON) | Key-value, structures | Wide column (CQL) |
| Primary Use | Flexible schemas, content | Caching, sessions | Time-series, append-heavy |
| Read Latency | 1-10ms | Sub-millisecond | 5-20ms |
| Write Throughput | Moderate | Very high | Very high |
| Scalability | Horizontal sharding | Vertical + clustering | Horizontal everywhere |
| Consistency | Tunable per query | Strong with Sentinel | Tunable per query |
| Query Language | MongoDB query JSON | Commands API | CQL (SQL-like) |
| Indexing | Secondary, compound, text | Hash, sorted sets | Secondary via SASI, manual |
| Transactions | Multi-document ACID | Lua scripts + transactions | Lightweight transactions |
| Memory Model | Disk-based, memory-mapped | Full in-memory option | Disk-based, row-level |
MongoDB wins on developer experience and schema flexibility. Redis wins on speed and simplicity for caching. Cassandra wins on write scaling and geographic distribution. Many architectures use all three for different purposes.
Implementation with Spring Data
Spring Data provides consistent abstractions across these databases. Here is how each one looks in practice.
MongoDB with MongoRepository
// Entity
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private BigDecimal price;
private List<String> tags;
private LocalDateTime createdAt;
// getters and setters
}
// Repository
public interface ProductRepository extends MongoRepository<Product, String> {
List<Product> findByNameContainingIgnoreCase(String name);
List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
@Query("{ 'tags': { $in: ?0 } }")
List<Product> findByTagIn(List<String> tags);
}
// Service usage
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public Page<Product> findByPriceRange(BigDecimal min, BigDecimal max, Pageable pageable) {
return repository.findByPriceBetween(min, max, pageable);
}
}
Redis with RedisTemplate
// Configuration
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
return template;
}
}
// Service usage
@Service
public class CacheService {
private final RedisTemplate<String, Object> template;
public CacheService(RedisTemplate<String, Object> template) {
this.template = template;
}
public void cacheUserSession(String userId, UserSession session) {
template.opsForValue().set(
"session:" + userId,
session,
Duration.ofMinutes(30)
);
}
public UserSession getUserSession(String userId) {
return (UserSession) template.opsForValue().get("session:" + userId);
}
public void incrementViewCount(String productId) {
template.opsForValue().increment("views:" + productId);
}
public void addToCart(String cartId, String productId) {
template.opsForSet().add("cart:" + cartId, productId);
}
public Set<Object> getCartItems(String cartId) {
return template.opsForSet().members("cart:" + cartId);
}
}
Cassandra with CassandraTemplate
// Entity
@Table("orders")
public class Order {
@PrimaryKey
private UUID orderId;
@PrimaryKeyColumn(name = "customer_id", ordinal = 0)
private UUID customerId;
private String status;
private List<OrderItem> items;
private BigDecimal total;
@Indexed
private LocalDateTime createdAt;
// getters and setters
}
// Repository
public interface OrderRepository extends CassandraRepository<Order, OrderKey> {
@AllowFiltering
List<Order> findByCustomerId(UUID customerId);
List<Order> findByStatus(String status);
}
// Service usage
@Service
public class OrderService {
private final CassandraTemplate template;
public OrderService(CassandraTemplate template) {
this.template = template;
}
public Order createOrder(Order order) {
order.setOrderId(UUID.randomUUID());
order.setCreatedAt(LocalDateTime.now());
return template.insert(order);
}
public List<Order> findRecentOrders(UUID customerId) {
return template.select(
Query.query(Criteria.where("customer_id").is(customerId))
.sort(Sort.by(Sort.Direction.DESC, "created_at"))
.limit(10),
Order.class
);
}
}
Observability Checklist
Monitoring NoSQL databases requires attention to connection health, query performance, and resource utilization.
Connection Metrics to Track:
- Active connections by state (idle, active, waiting)
- Connection pool exhaustion events
- Connection acquisition latency
- Failed connection attempts per second
Query Performance Monitoring:
- Average query latency by collection or key pattern
- Slow query log entries (queries exceeding threshold)
- Query result sizes and scan depths
- Index hit rates versus collection scans
Resource Utilization:
- Memory usage and eviction rates (especially for Redis)
- Disk I/O and storage consumption
- CPU utilization by operation type
- Network throughput and saturation
For Spring Boot applications, Micrometer integration provides standard metrics. MongoDB, Redis, and Cassandra all have auto-configured metric exporters when the appropriate starters are included.
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
Security Notes
NoSQL databases have their own attack surface. Understanding the risks helps you build defenses.
NoSQL Injection
MongoDB query injection occurs when user input is interpolated into query documents without sanitization. Never use string concatenation to build queries:
// VULNERABLE - Do not do this
String query = "{ 'username': '" + userInput + "' }";
collection.find(query);
// SAFE - Use parameterized queries
Query query = new Query(Criteria.where("username").is(userInput));
collection.find(query);
Redis injection targets command parsing. User input that flows into eval() or similar commands without validation can execute arbitrary commands. Always validate and sanitize input before using it in Lua scripts or commands.
Cassandra CQL injection follows similar patterns. Use parameterized queries via PreparedStatement to prevent injection.
Authentication and Authorization
Enable authentication on all NoSQL databases in production. MongoDB supports SCRAM-SHA-1 and SCRAM-SHA-256. Redis supports ACLs and requirepass. Cassandra uses internal authentication and optional LDAP integration.
Use separate credentials per application with least-privilege access. Spring Security modules for each database handle credential management and connection encryption.
Encrypt data in transit with TLS. Most drivers support TLS connections with certificate verification. For Redis, this requires configuring ssl yes and appropriate truststores.
Security and Compliance Notes
NoSQL databases have distinct attack surfaces. The flexible data model, network exposure, and operational complexity each create security considerations.
MongoDB NoSQL Injection
MongoDB query injection occurs when user input flows into query documents without parameterization. Building query strings with string concatenation allows attackers to inject operators like $where or $ne. Always use Criteria.where() and Query objects with parameterized values. Never concatenate user input into BSON documents.
Redis Command Injection
Redis commands are space-separated arguments. If user input reaches eval() or similar commands without validation, attackers can execute arbitrary Redis commands. Validate all input before using it in Lua scripts or commands. Disable dangerous commands (FLUSHDB, CONFIG) in production Redis.
Cassandra CQL Injection
Cassandra Query Language supports parameterized queries. Use PreparedStatement for all CQL queries rather than string concatenation. CQL injection follows the same patterns as SQL injection — user input that flows into query strings creates vulnerabilities.
Authentication Configuration
Enable authentication on all NoSQL databases in production. MongoDB uses SCRAM-SHA-1 or SCRAM-SHA-256. Redis supports ACLs. Cassandra uses internal authentication with optional LDAP integration. Never run production NoSQL databases without authentication.
TLS Encryption in Transit
All NoSQL database connections crossing network boundaries should use TLS. Configure MongoDB with tls: true in connection strings. Configure Redis with ssl yes and appropriate truststores. Cassandra drivers support TLS with certificate verification. Enable encryption for data in transit between application servers and databases.
Network Access Controls and Firewalls
Restrict database access to application servers only using firewall rules or cloud security groups. NoSQL databases should not be directly accessible from the internet or developer workstations in production. Use bastion hosts or VPN for legitimate administrative access.
Data Encryption at Rest
Enable encryption at rest for NoSQL databases storing sensitive data. Cloud-managed databases offer encryption by default. For self-hosted databases, use filesystem-level encryption or database-native encryption features. Encryption keys should be managed separately from the data they protect.
Production Failure Scenarios
NoSQL databases trade ACID guarantees for scalability and speed, but that trade-off introduces its own failure modes. Understanding them helps you build more resilient systems.
MongoDB Replica Set Failover Causing Connection Interruption
MongoDB replica set elections cause brief unavailability when the primary steps down. During failover, writes are rejected until a new primary is elected. Applications receive MongoNotWritableException or MongoTimeoutException. Configure appropriate serverSelectionTimeoutMS and handle MongoException with retry logic. For critical write operations, use sessions and retry on transient errors.
Redis Sentinel Failover with Split Brain Risk
Redis Sentinel promotes a replica to primary during failover. If the old primary is still reachable to some clients but not to the Sentinel majority, you get split brain — two primaries accepting writes to the same keys. Configure Sentinel with an odd number of nodes and appropriate quorum settings. For critical data, use Redis Cluster which shards data and avoids single-primary dependencies.
Cassandra Node Failure Causing Partition Unavailability
Cassandra’s peer-to-peer model means any node can serve requests for its partition range. When a node fails, the coordinator routes requests to replicas. If multiple nodes fail simultaneously or network partitions occur, some partition ranges become unavailable. Use sufficient replication factors and monitor node health. Cassandra’s eventual consistency means reads may return stale data during partition recovery.
Redis Key Expiration Not Immediate
Redis EXPIRE sets a TTL, but key expiration is not instantaneous — Redis uses a background task that runs every 100ms. In high-throughput systems, keys may live up to 1 second past their TTL. For use cases requiring precise expiration timing, use sorted sets with timestamps as scores and query for expired keys explicitly, or accept the slight delay and design accordingly.
MongoDB Schema Drift in Flexible Schemas
MongoDB’s schema-less nature means documents in the same collection can have different fields. Without schema governance, application code that assumes certain fields exist breaks when documents without those fields are encountered. Use database validation rules ($jsonSchema) or enforce schema at the application layer to prevent field-not-found errors.
Common Pitfalls / Anti-Patterns
Working with NoSQL in Spring Boot has rough edges. Here is what tends to trip people up.
Ignoring eventual consistency assumptions. Reading from MongoDB secondaries without understanding replication lag leads to bugs where users see stale data. Understand your read preference and its implications.
Redis key proliferation without expiration. Cached data accumulates when TTLs are not set or cleanup is not automated. Monitor memory usage and set appropriate expiration policies.
Cassandra batch statement misuse. Batch statements in Cassandra are not like relational batches. They are meant for partitioning multiple writes to the same partition, not for bulk operations across many partitions. Misuse leads to coordinator node overload.
Skipping index design. MongoDB and Cassandra both require explicit indexing strategies. Collections without proper indexes degrade as data grows. Plan indexes based on query patterns before they become production problems.
Underestimating operational complexity. Each NoSQL database brings operational requirements. MongoDB needs replica set configuration and sharding planning. Redis needs Sentinel or Cluster setup for high availability. Cassandra requires understanding of consistency levels and compaction strategies. Factor this into your architecture decisions.
Quick Recap Checklist
Use this when evaluating or implementing NoSQL in Spring Boot:
- Data model fits the database type (document, key-value, wide-column)
- Consistency requirements understood and match database capabilities
- Connection pooling configured with appropriate pool sizes
- Authentication enabled for all database connections
- TLS encryption enabled for data in transit
- Indexes designed for query patterns
- Monitoring metrics exported (connections, latency, resource usage)
- Timeout values configured appropriately for workload
- Retry logic implemented for transient failures
- Backup and recovery procedures documented
Interview Questions
MongoDB is a document store that uses BSON documents with flexible schemas. It works well for content management, product catalogs, and use cases where data structures evolve over time. Redis is a key-value store that also supports data structures like lists, sets, and sorted sets. Its primary strengths are caching, session management, and real-time analytics where sub-millisecond latency matters. Cassandra is a wide-column database using a SQL-like query language (CQL) designed for write-heavy workloads across distributed systems. It excels at time-series data, IoT applications, and scenarios requiring geographic distribution with high write throughput.
Spring Data provides repositories and templates that offer a consistent interface across MongoDB, Redis, and Cassandra. MongoRepository, RedisTemplate, and CassandraTemplate each follow similar patterns for CRUD operations. The abstraction works well for standard operations but breaks down when you need database-specific features like MongoDB aggregation pipelines, Redis Lua scripting, or Cassandra lightweight transactions. For advanced use cases, you often need to drop down to the native driver or template methods that bypass the repository abstraction.
Never concatenate user input directly into query strings or documents. Use parameterized queries through Spring Data repositories and templates, which handle escaping automatically. For MongoDB, use Criteria.where() and Query objects instead of raw JSON strings. For Redis, validate all input before using it in commands and avoid dynamic Lua scripts with user-provided values. Apply input validation at the application boundary, limit input lengths, and use allowlists for expected values when possible. Additionally, enable authentication and network encryption to prevent unauthorized access to the database.
Start with Micrometer for metrics export since Spring Boot auto-configures it for MongoDB, Redis, and Cassandra when the appropriate starters are present. Track connection pool metrics (active, idle, waiting connections), query latency percentiles, and error rates per database. Set up health indicators for each data store and configure alerting thresholds based on baseline performance. For production systems, export metrics to Prometheus or Datadog and create dashboards showing connection health, latency distributions, and resource utilization side by side. Include slow query logs and set up alerts for anomalous patterns.
Eventual consistency means reads may return stale data immediately after writes. The window of inconsistency varies based on network conditions and database configuration. For read-heavy applications, you can often accept this trade-off. For write-heavy or consistency-sensitive operations, use read preferences that prefer primaries in MongoDB, configure write concerns in Cassandra, or accept replicas with Redis Sentinel. In application code, design for eventual consistency by deferring reads after writes when necessary, using version vectors to detect conflicts, or implementing application-level checks to confirm write success before proceeding. For critical operations requiring linearizability, use database-specific features like Cassandra lightweight transactions or MongoDB transactions.
Choose MongoDB when your data structure varies across records and evolves over time, such as product catalogs with different attributes per category or user profiles with dynamic fields. MongoDB works well when you need multi-document transactions with ACID guarantees, complex aggregation pipelines for reporting, or secondary indexes for flexible querying. If your team needs a shorter learning curve and you want to avoid managing complex infrastructure, MongoDB Atlas provides a managed option that reduces operational burden. Avoid MongoDB when you have strict latency requirements below 1ms or when your data model is stable and fits cleanly into relational structures.
Redis persistence options are RDB snapshots and AOF append-only files. RDB creates point-in-time snapshots at configured intervals, producing compact binary files that restore quickly. The downside is potential data loss between snapshots if Redis crashes. AOF logs every write operation to a file, offering better durability with options like appendfsync everysec that balance performance and safety. AOF files grow larger and restore slower than RDB, but provide better protection against data loss. For critical data, consider using both together or replica nodes with AOF for durability without sacrificing primary performance.
Cassandra partition design determines data distribution and query performance. A partition key groups data stored together on a single node, so design keys that keep related data accessible in one read while avoiding partitions that grow unbounded. Wide rows work well for time-series data where you append columns per timestamp. Keep partition sizes under 100MB to avoid compaction issues and long repair times. For Spring Data Cassandra, use composite keys with @PrimaryKeyColumn annotations and always query by partition key first. Denormalization is expected in Cassandra, unlike relational databases where normalization reduces redundancy.
MongoDB uses MongoClient with connection pools configured via MongoClientOptions. Set maxPoolSize based on concurrent request patterns and minPoolSize for baseline connections. Redis RedisConnectionFactory manages pools, with Lettuce being the default since Spring Boot 2.0 offering better performance than Jedis. Configure pool settings via LettucePoolingClientConfiguration. Cassandra uses Session objects with pooling handled by the driver, configuring PoolingOptions with setMaxConnectionsPerHost based on expected query volume. For all databases, monitor pool exhaustion in production using metrics and set appropriate timeouts to fail fast when pools are depleted.
For MongoDB, configure spring.data.mongodb.timeout for generic timeouts, spring.data.mongodb.connect-timeout for connection establishment, and spring.data.mongodb.socket-timeout for read/write operations. Redis uses spring.redis.timeout for generic timeouts and spring.redis.lettuce.pool.* for connection pool timeouts. Cassandra uses spring.data.cassandra.request.timeout for query timeouts and spring.data.cassandra.connect-timeout for initial connections. Always set timeouts lower than your SLA requirements so failures fail fast rather than hanging indefinitely.
MongoDB aggregation pipelines process documents through stages like $match, $group, $sort, and $project, offering power similar to SQL GROUP BY with JOINs. Pipelines can use indexes at early stages for efficiency and handle array manipulation natively. Limitations in Spring Data include incomplete pipeline support in repository methods, requiring MongoTemplate with Aggregation objects for complex operations. Memory constraints exist since pipelines without proper indexing can require scanning entire collections. For very large aggregations, consider running aggregations on replica set secondaries to avoid impacting primary write performance.
MongoDB backups use mongodump for logical backups or filesystem snapshots for physical backups. For replica sets, point-in-time recovery is possible with oplog access. Tools like MongoDB Atlas provide automated continuous backups. Redis backup is simpler using BGSAVE or LASTSAVE commands for RDB files, with BGREWRITEAOF for AOF compaction. For production Redis, use replica nodes dedicated to backup rather than backing up the primary. Cassandra uses nodetool snapshot for point-in-time snapshots with nodetool clearsnapshot for cleanup. For all databases, test restore procedures regularly, store backups in geographically separate locations, and consider application-level backup validation to confirm data integrity.
MongoDB schema changes like adding fields work without migrations since documents are schema-less, but removing fields or renaming requires migration scripts that update existing documents. Use db.collection.updateMany() with $rename or $unset operators in migration scripts. Spring Boot's ApplicationListener on ApplicationReadyEvent can run migrations on startup. For Cassandra, schema changes require careful planning since columns can only be added, not removed or renamed without data migration. Use CQL ALTER TABLE to add columns, then run migration jobs to backfill derived data. For both databases, test migrations on staging data before production and plan for zero-downtime migrations using rolling deployments where old and new schema coexist temporarily.
Reading from MongoDB secondaries with eventual consistency means data may be stale by the replication lag window, which varies from milliseconds to seconds depending on network and write volume. Performance benefits include read scaling and reduced load on primaries. Mitigation strategies include setting ReadPreference.SECONDARYPREFERRED to fallback to primary when secondaries lag, monitoring replication lag via rs.printSecondaryReplicationInfo(), and alerting when lag exceeds thresholds. For applications requiring consistent reads, use ReadPreference.primary() or ReadPreference.primaryPreferred(). Design queries to tolerate short inconsistency windows, and avoid reading secondaries for operations where stale data creates business problems like inventory counts or financial balances.
Redis Sentinel provides automatic failover with a primary-replica setup using Sentinel nodes to monitor and vote on primary health. Sentinel is simpler to operate and works well when you need HA with a single primary handling all writes. Limitations include write operations limited to one primary and no built-in sharding. Redis Cluster shards data across multiple primaries, each handling a subset of key space with replica redundancy per shard. Choose Cluster when you need horizontal scaling for both reads and writes or when dataset size exceeds a single instance memory. Choose Sentinel for simpler deployments, smaller datasets, or when your application naturally write-heavy to a single primary. Many architectures use Sentinel for small to medium workloads and graduate to Cluster when scale demands it.
Redis runs as a single-threaded C process that leverages epoll or kqueue for I/O multiplexing, keeping all data in memory without JVM involvement. This means no garbage collection pauses affecting latency, predictable performance, and minimal memory overhead. Lettuce, Spring Boot's default Redis client, uses Netty for async I/O but data stays in native memory outside the JVM heap. MongoDB is a separate server process written in C++, with the JVM only handling client-side operations through the MongoDB Java driver. Cassandra is also a separate JVM process on the server side. The JVM heap size matters differently for each: for Spring Boot applications using these databases, tune heap for your application code and driver buffers rather than data storage since data lives in the database processes themselves.
Rate limiting in Redis uses atomic operations to increment counters with expiration. A simple approach uses INCR with EXPIRE to set a window, or combined with Lua scripts for sliding window algorithms. In Spring Boot, inject RedisTemplate and use opsForValue().increment() with expire(). For sliding window rate limiting, use a sorted set with timestamps as scores and remove entries outside the window. Configure Redis template with StringRedisSerializer for key names. Set appropriate TTLs to prevent key accumulation, and use Redis Cluster for horizontal scaling since rate limit state should be local to each partition or replicated across nodes.
Lightweight transactions (LWT) in Cassandra use IF conditions with INSERT, UPDATE, or DELETE to provide linearizable consistency for single partition operations. The trade-off is significant performance impact since LWT requires a consensus round-trip across replicas, taking 4x the latency of regular writes. LWT is synchronous across replicas, blocking until a quorum confirms the condition. Use LWT only for critical operations like checking account balance before deducting funds or implementing distributed locks. For high-throughput write paths, design around eventual consistency rather than LWT, using timestamps and last-write-wins for conflict resolution. In Spring Data Cassandra, invoke LWT through Query with if_() conditions.
Spring Data MongoDB supports multi-document transactions starting with MongoDB 4.0+ and replica set deployments. Use MongoTransactionManager with @Transactional annotations, but transactions require replica set or sharded cluster topology and do not work on standalone MongoDB instances. Transaction overhead is significant since they involve network round-trips to multiple nodes and acquire locks on involved documents. Keep transactions short and avoid operations spanning many collections. Transactions cannot be nested and have timeout defaults that may need tuning. For use cases requiring ACID across multiple documents, evaluate whether the data genuinely belongs in MongoDB or if a relational database better fits the consistency requirements.
For MongoDB, monitor connection pool size and wait time, query latency percentiles especially for aggregations, replication lag between primary and secondaries, document scan versus index usage ratio, and disk I/O queue depth. For Redis, monitor memory usage and eviction rates via used_memory and evicted_keys, command latency especially for slow commands like SMEMBERS on large sets, connected client count approaching file descriptor limits, and replication lag for replica nodes. For Cassandra, monitor read/write latency percentiles by consistency level, tombstone counts which indicate deleted data still consuming space, compaction queue sizes, and dropped messages indicating node overload. In Spring Boot, Micrometer automatically exports these when using the appropriate starters, but ensure your monitoring platform collects them at sufficient granularity for alerting.
Further Reading
Deepen your understanding of NoSQL databases in Spring Boot with these resources.
MongoDB:
- Spring Data MongoDB Documentation - Official reference for MongoDB repositories, templates, and aggregation pipelines
- MongoDB Indexing Strategies - Understanding compound, text, and geospatial indexes
- MongoDB Schema Validation - Enforcing document structure with JSON Schema
Redis:
- Spring Data Redis Documentation - Redis templates, repositories, and Redis Cluster support
- Redis Persistence Guide - RDB snapshots versus AOF append-only files
- Redis Sentinel Documentation - High availability and failover configuration
Cassandra:
- Spring Data Cassandra Documentation - CQL template operations and repository support
- Cassandra Data Modeling Guide - Designing schemas for partition efficiency
- Cassandra Consistency Levels - Tuning read and write consistency
Architecture Patterns:
- CQRS with NoSQL - Command Query Responsibility Segregation patterns
- Database Sharding Strategies - Horizontal partitioning approaches
- Event Sourcing and NoSQL - Combining event sourcing with document stores
Spring Boot Integration:
- Spring Boot Data Commons - Common patterns across Spring Data modules
- Micrometer Metrics Export - Exporting application metrics for monitoring
Conclusion
Spring Data makes integrating NoSQL databases into Spring Boot applications straightforward, but each database comes with distinct trade-offs. MongoDB offers the best developer experience for flexible schemas, Redis excels at caching and session management with sub-millisecond latency, and Cassandra handles write-heavy distributed workloads where geographic replication matters.
For a deeper dive into Spring Boot patterns, see the Spring Boot roadmap which covers everything from basics to advanced integrations. If you are designing data storage for a new service, also consider the System Design resources to understand how NoSQL fits into larger architecture patterns.
Category
Related Posts
Spring Boot Build Tools: Maven & Gradle
Configure Maven and Gradle for Spring Boot projects—plugins, dependency management, packaging JARs and WARs, and build automation essentials.
Embedded Web Servers in Spring Boot: Tomcat, Jetty, Undertow
Configure embedded servers in Spring Boot: compare Tomcat, Jetty, and Undertow, tune thread pools, enable access logs, and switch implementations.
JUnit 5 & Jupiter: Lifecycle, Nested & Parameterized Tests
Explore JUnit 5 Jupiter features: master test lifecycle annotations, organize tests with @Nested, and parameterize tests with @CsvSource and @MethodSource.