Understanding Logical Databases in Redis OSS and Their Limitations in Production Environments
SELECT looks like free multi-tenancy until it follows you into production. Where the model breaks down, why it does not exist in Cluster Mode, and what proper isolation looks like.
SELECT is one of the first things people find in Redis, and it looks like an easy win. Sixteen numbered keyspaces inside one instance, no extra infrastructure to provision, and you keep your session data away from your cache data by typing SELECT 3. Free multi-tenancy.
It does work. The trouble is what happens when that arrangement follows you into production, which it usually does, because nobody ever schedules the work to undo it.
What a logical database actually is
In Redis OSS, a logical database is a partition of the keyspace inside a single Redis server. You get 16 of them by default, indexed 0 to 15, and you move between them with:
SELECT <db_number>
SELECT 0 puts you in the first, SELECT 1 the second, and so on. Keys in one are invisible from another, so as a namespacing mechanism it does what it says.
What it does not do is give each of those keyspaces anything of its own. They all sit in the same server process, sharing the same memory, the same CPU, the same persistence configuration and the same everything else. The separation is bookkeeping, not architecture. Almost every problem below follows from that one fact.
Where it breaks down
The isolation is the obvious one. Because there is a single process, a single tenant having a bad day is everybody's bad day. One keyspace taking an unexpected write volume eats memory the others were counting on. One expensive KEYS scan or a large sorted-set operation blocks the event loop, and every other logical database waits behind it. If the instance hits maxmemory or dies, all 16 go together. You have not built 16 databases; you have built one database with 16 labels.
Nothing lets you contain that, either. There is no way in Redis OSS to say that database 3 may use at most 2 GB. Memory limits are set for the instance, and eviction policy is set for the instance, which means an eviction triggered by one workload's growth will happily evict another workload's keys. If your tenants have uneven or spiky demand, and real tenants always do, there is no configuration that makes this safe.
Access control has the same shape. ACLs in Redis 6 and later can restrict users to key patterns, but they are not scoped per logical database, so anyone who can reach the instance can SELECT their way into any of the 16. There is no per-database credential to hand out. If two teams share an instance on the understanding that each stays in its own numbered database, that understanding is a convention, and conventions do not survive a misconfigured client or a debugging session at 2am.
Persistence is global too. RDB snapshots and AOF are instance-level settings, so you cannot give the database holding customer records durable AOF while letting a pure cache run with no persistence at all. Every workload inherits the same durability and the same fsync cost, whether it needs them or not. That usually means either paying for durability you do not want on ephemeral data, or accepting a weaker guarantee than you should on data that matters.
And then there is the day-to-day. INFO reports on the instance. Slowlog covers the instance. Your latency graph covers the instance. So when p99 doubles at 4pm you know something in one of 16 keyspaces did it, and finding out which one is archaeology. Redis will tell you how many keys live in each database and very little else.
There is one more that catches people out, which is that logical databases do not exist in Redis Cluster at all. Cluster mode supports database 0 only. So a codebase built on SELECT has to be rewritten before it can scale horizontally, and that discovery normally arrives at exactly the moment you have no time for it.
What proper multi-tenancy looks like
Redis Enterprise takes a different approach: a tenant is a database, not a number inside one. Each database in the cluster gets its own memory allocation, its own shards, its own endpoint and its own configuration, so the isolation is structural rather than clerical.
Because a database is a real object, the things you could not express before become ordinary settings. You give each one a memory limit and it cannot exceed it. You choose RDB for one and AOF for another according to what the data is worth. You issue separate credentials per database and use role-based access control to decide who gets them. Metrics, slowlog and alerts are reported per database, so the 4pm latency question has an answer instead of a suspect list.
You also stop having to choose between isolation and scale. Databases can be clustered across shards and nodes, replicated for high availability with automatic failover, and distributed across regions with Active-Active if you need local write latency in more than one geography. None of that is available to a SELECT statement.
| Concern | Redis OSS logical databases | Redis Enterprise databases |
|---|---|---|
| Isolation | Shared process, memory and CPU | Own memory allocation and shards |
| Resource limits | Instance-wide only | Per database |
| Access control | Not scoped per database | Per-database credentials with RBAC |
| Persistence | One global setting | Chosen per database |
| Monitoring | Instance-wide | Per database |
| Cluster mode | Database 0 only | Fully supported |
| High availability | Assemble it yourself | Replication and automatic failover |
| Geo-distribution | Not available | Active-Active across regions |
So when is SELECT fine?
Development and testing, mostly, where the cost of a noisy neighbour is that you laugh about it. It is also reasonable for genuinely small, genuinely related keyspaces owned by one team and one application, where you want a tidy separation and you are not pretending it is a security boundary.
The line I would draw is ownership. If the things sharing the instance are owned by different teams, hold data with different sensitivity, or have service levels somebody would be paged about, they need to be separate databases. Logical databases are a naming convention with a maxmemory setting shared between them, and they are only safe while everybody involved knows that.