Patrones Multi-inquilino Explicados
La multi-inquilinidad es la práctica de servir a muchos clientes, o inquilinos, desde una infraestructura compartida, manteniendo los datos de cada inquilino aislados de los de cualquier otro inquilino.
Cada patrón para hacer esto en PostgreSQL, desde una columna tenant_id hasta una base de datos completamente separada por cliente, se ubica en algún lugar de un espectro subyacente entre la simplicidad operativa y la fortaleza del aislamiento.
Esta página explica ese espectro directamente, para que las páginas más concretas de esta sección (consultas de esquema compartido, políticas de RLS, esquema por inquilino, base de datos por inquilino) se lean como puntos a lo largo de él en lugar de técnicas no relacionadas.
Resumen
- Los patrones multi-inquilino intercambian simplicidad operativa por fortaleza de aislamiento, y cada patrón concreto se ubica en un punto específico de ese compromiso.
- Por Qué Importa: Elegir muy poco aislamiento arriesga fugas de datos entre inquilinos y problemas de rendimiento de vecinos ruidosos; elegir demasiado aislamiento multiplica el costo operativo y de migración.
- Conceptos Clave: inquilino, esquema compartido, esquema por inquilino, base de datos por inquilino, seguridad a nivel de fila (RLS), vecino ruidoso.
- Cuándo Usar: Cualquier producto SaaS que sirva a más de un cliente desde infraestructura PostgreSQL compartida.
- Limitaciones / Compromisos: Un aislamiento más fuerte significa informes entre inquilinos más lentos y un trabajo de migración y copia de seguridad multiplicado.
- Temas Relacionados: seguridad a nivel de fila, agrupación de conexiones por inquilino, migraciones de esquema a escala, aislamiento impulsado por el cumplimiento.
Fundamentos
El patrón más simple, esquema compartido con una columna tenant_id, coloca las filas de cada inquilino en las mismas tablas, distinguidas solo por un valor tenant_id.
CREATE TABLE projects (
tenant_id uuid NOT NULL REFERENCES tenants (tenant_id),
project_id bigint GENERATED ALWAYS AS IDENTITY,
name text NOT NULL,
PRIMARY KEY (tenant_id, project_id)
);Este es el patrón más económico de operar, porque una migración, una copia de seguridad y un grupo de conexiones sirven a todos los inquilinos a la vez.
Esquema por inquilino sube un nivel en el espectro al dar a cada inquilino un esquema PostgreSQL dedicado con DDL de tabla duplicado, obteniendo aislamiento de espacio de nombres a costa de ejecutar cada migración una vez por esquema de inquilino.
Base de datos por inquilino avanza aún más, dando a cada inquilino una base de datos completamente separada con ciclos de copia de seguridad, restauración y actualización independientes, que es el aislamiento más fuerte que ofrece PostgreSQL dentro de un solo clúster.
Cada paso a lo largo de este espectro, desde el esquema compartido hasta la base de datos por inquilino, compra una contención de radio de explosión más fuerte a cambio de más infraestructura para operar y más lugares donde una migración puede fallar.
Mecánicas e Interacciones
La multi-inquilinidad de esquema compartido pone toda la carga de corrección en que cada consulta recuerde filtrar por tenant_id, lo cual es una garantía frágil en la que confiar para algo tan serio como fugas de datos entre inquilinos.
La seguridad a nivel de fila (RLS) existe para hacer que esa garantía sea estructural en lugar de convencional, al adjuntar una política directamente a la tabla que PostgreSQL aplica independientemente de si la consulta de la aplicación recordó una cláusula WHERE tenant_id = ....
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects FORCE ROW LEVEL SECURITY;
CREATE POLICY projects_tenant_isolation ON projects
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);RLS es una red de seguridad superpuesta al filtrado a nivel de aplicación, no un sustituto del mismo, porque una política todavía depende de que la sesión establezca correctamente app.tenant_id para cada conexión.
El problema del vecino ruidoso es el modo de falla característico del patrón de esquema compartido: la carga de trabajo inusualmente grande de un inquilino consume una proporción desproporcionada de CPU, E/S o contención de bloqueos, degradando el rendimiento para todos los demás inquilinos que comparten las mismas tablas y grupo de conexiones.
Detectarlo requiere observabilidad por inquilino, como agrupar pg_stat_statements o registros de auditoría por tenant_id, ya que PostgreSQL en sí mismo no tiene un concepto nativo de un inquilino para aislar recursos.
El esquema por inquilino y la base de datos por inquilino reducen el riesgo de vecinos ruidosos estructuralmente, ya que los datos de cada inquilino y, en el caso de la base de datos por inquilino, el grupo de conexiones, están físicamente separados de los de cualquier otro inquilino.
Consideraciones Avanzadas y Aplicaciones
Los requisitos de cumplimiento a menudo obligan a la decisión de aislamiento en lugar de dejarla puramente como una compensación de rendimiento.
Un cliente empresarial regulado puede requerir contractualmente que sus datos nunca compartan almacenamiento físico o un grupo de conexiones con ningún otro inquilino, lo que efectivamente exige la base de datos por inquilino independientemente de lo que sugieran las economías del esquema compartido.
Muchos productos SaaS reales no eligen un patrón para todo el sistema; ejecutan esquema compartido + RLS para la mayoría de los inquilinos y reservan esquema por inquilino o base de datos por inquilino para un nivel empresarial dispuesto a pagar por ese aislamiento.
Ese enfoque por niveles significa que la capa de enrutamiento de conexiones, no solo la capa de esquema, tiene que saber qué patrón utiliza un inquilino determinado, lo que agrega complejidad real a la aplicación a cambio de servir a ambos extremos del mercado desde un solo producto.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Esquema compartido + tenant_id | Más económico de operar; una migración sirve a todos los inquilinos | La corrección depende de que cada consulta filtre correctamente; riesgo de vecino ruidoso | Productos SaaS en etapas iniciales, la mayoría de los niveles de inquilinos de autoservicio |
| Esquema compartido + RLS | Aislamiento aplicado por la base de datos como red de seguridad | Todavía comparte recursos físicos; la corrección de la política depende de la configuración de la sesión | Cualquier producto de esquema compartido que busque defensa en profundidad |
| Esquema por inquilino | Aislamiento de espacio de nombres más fuerte; exportación de datos por inquilino más fácil | Las migraciones se ejecutan una vez por esquema de inquilino; más difícil de escalar a miles de inquilinos | Inquilinos del mercado medio que necesitan más aislamiento sin separación completa de la base de datos |
| Base de datos por inquilino | Aislamiento más fuerte; copia de seguridad, restauración y escalado independientes por inquilino | Mayor costo operativo; los informes entre inquilinos requieren federación | Inquilinos regulados o empresariales con requisitos de aislamiento contractuales |
La agrupación de conexiones también debe reconsiderarse en cada punto de este espectro, ya que PgBouncer agrupa por base de datos, no por esquema, por lo que los inquilinos de esquema por inquilino todavía comparten un grupo, mientras que los inquilinos de base de datos por inquilino necesitan un grupo por base de datos o enrutamiento dinámico.
Conceptos Erróneos Comunes
- "La seguridad a nivel de fila reemplaza la necesidad de filtrar por tenant_id en las consultas." RLS es una red de seguridad que atrapa filtros faltantes, pero confiar solo en ella todavía depende de que la sesión establezca correctamente el contexto del inquilino, por lo que el filtrado a nivel de aplicación sigue siendo la defensa principal.
- "El esquema por inquilino escala a cualquier número de inquilinos tan bien como el esquema compartido." Ejecutar una migración por esquema de inquilino se vuelve operativamente pesado mucho antes de que sea técnicamente imposible, por lo que este patrón generalmente se reserva para un nivel limitado del mercado medio en lugar de miles de inquilinos de autoservicio.
- "La base de datos por inquilino es solo sobre seguridad." También es una herramienta de escalado y aislamiento independiente del cumplimiento, que permite escalar o restaurar la carga de trabajo de un inquilino sin afectar a ningún otro inquilino.
- "Tienes que elegir un patrón para todo el producto." Muchos sistemas SaaS de producción mezclan patrones por nivel de inquilino, ejecutando esquema compartido para la mayoría de los clientes y un aislamiento más fuerte solo para los inquilinos que lo necesitan o pagan por él.
- "Una columna tenant_id es suficiente por sí sola, sin otras precauciones." Sin RLS o una aplicación equivalente, una sola cláusula
WHERE tenant_id = ...faltante en cualquier ruta de consulta es una fuga de datos entre inquilinos, por lo que la multi-inquilinidad de esquema compartido se considera el eslabón más débil en este espectro sin salvaguardias adicionales.
Preguntas Frecuentes
¿Cuál es el espectro central que describe esta página?
Los patrones multi-inquilino van desde el esquema compartido (más económico, aislamiento más débil) a través del esquema por inquilino hasta la base de datos por inquilino (más caro, aislamiento más fuerte).
Cada patrón concreto de esta sección es un punto en ese mismo espectro.
¿Cuál es el patrón multi-inquilino más simple en PostgreSQL?
Un esquema compartido donde las filas de cada inquilino viven en las mismas tablas, distinguidas por una columna tenant_id en la que cada consulta debe filtrar.
Es el más económico de operar, pero pone el mayor peso en la corrección de las consultas.
¿Reemplaza la seguridad a nivel de fila el filtrado por tenant_id en las consultas de la aplicación?
No, RLS es una red de seguridad aplicada por la base de datos que atrapa las consultas que carecen de un filtro de inquilino, pero aún depende de que la sesión establezca correctamente el contexto del inquilino.
Complementa el filtrado de la aplicación en lugar de reemplazarlo.
¿Qué es el problema del vecino ruidoso?
Es cuando la carga de trabajo desproporcionadamente pesada de un inquilino degrada el rendimiento de otros inquilinos que comparten las mismas tablas y grupo de conexiones.
Es más visible en la multi-inquilinidad de esquema compartido y se reduce a medida que el aislamiento se fortalece.
¿Cuándo debería un producto pasar del esquema compartido al esquema por inquilino?
Cuando un conjunto limitado de inquilinos necesita un aislamiento de espacio de nombres más fuerte o una exportación de datos por inquilino más fácil de la que puede ofrecer una tabla compartida, sin el costo operativo completo de bases de datos separadas.
Se vuelve más difícil de justificar una vez que el número de inquilinos crece a miles.
¿Cuándo se vuelve necesaria la base de datos por inquilino?
Lo más común es cuando el contrato o requisito regulatorio de un inquilino exige que sus datos nunca compartan almacenamiento físico o un grupo de conexiones con ningún otro inquilino.
También es útil cuando la escala de un inquilino realmente necesita ciclos de copia de seguridad y restauración independientes.
¿Puede un solo producto usar más de un patrón multi-inquilino a la vez?
Sí, y muchos sistemas SaaS reales lo hacen: esquema compartido + RLS para la mayoría de los inquilinos, con esquema por inquilino o base de datos por inquilino reservados para un nivel empresarial.
Esto requiere que la capa de enrutamiento de conexiones sepa qué patrón utiliza cada inquilino.
¿Cómo interactúa la agrupación de conexiones con estos patrones?
PgBouncer agrupa por base de datos, no por esquema, por lo que los inquilinos de esquema por inquilino todavía comparten un grupo, mientras que los inquilinos de base de datos por inquilino típicamente necesitan un grupo por base de datos o enrutamiento dinámico.
Este es un costo operativo que crece junto con la fortaleza del aislamiento.
¿Es suficiente una restricción UNIQUE en una columna en un esquema multi-inquilino?
Solo si está limitada al inquilino, como UNIQUE (tenant_id, slug).
Una restricción UNIQUE (slug) global evita incorrectamente que dos inquilinos diferentes utilicen el mismo valor.
¿Cómo se detecta un inquilino vecino ruidoso en un esquema compartido?
A través de la observabilidad por inquilino, como agrupar la actividad de consulta o pg_stat_statements por tenant_id, ya que PostgreSQL no tiene un concepto nativo de inquilino para aislar recursos automáticamente.
Este trabajo de detección es lo que generalmente desencadena un movimiento a un nivel de aislamiento más fuerte para ese inquilino.
¿El aislamiento más fuerte siempre significa un mejor rendimiento?
No necesariamente para análisis entre inquilinos: la base de datos por inquilino aísla bien las cargas de trabajo, pero hace que cualquier informe que abarque inquilinos requiera federación en lugar de una simple unión.
La fortaleza del aislamiento y la conveniencia de las consultas entre inquilinos se mueven en direcciones opuestas.
¿Qué debería impulsar la elección entre estos patrones para un nuevo producto?
El número esperado de inquilinos, los requisitos de cumplimiento y si algún inquilino está dispuesto a pagar por un aislamiento más fuerte.
Los productos en etapas iniciales recurren por defecto al esquema compartido y solo mueven inquilinos específicos en el espectro cuando un requisito concreto lo exige.
Relacionados
- Conceptos Básicos de Multi-inquilino - ejemplos prácticos en todo el espectro de aislamiento
- Esquema Compartido + tenant_id - patrones de indexación y consulta para el patrón compartido
- Multi-inquilinidad con Seguridad a Nivel de Fila - plantillas de políticas y detalles de aplicación
- Esquema por Inquilino - compromisos del aislamiento de espacio de nombres
- Base de Datos por Inquilino - aislamiento de nivel empresarial
- Fundamentos de Modelado de Datos - el proceso de modelado en el que encaja una decisión de multi-inquilinidad
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17) y PgBouncer 1.x.