¿Debería un sistema multiinquilino con SQL Server 2016, Shard o tener aislamiento de inquilino a través de una base de datos separada por inquilino?

12

Dado el caso de uso:

  • Los datos del inquilino no deben cruzarse, un inquilino no necesita los datos de otro inquilino.
  • Cada inquilino podría tener un gran volumen de datos históricos.
  • SQL Server está alojado en la instancia de AWS EC2.
  • Cada inquilino es geográficamente distante.
  • Existe la intención de utilizar herramientas de visualización de terceros como PowerBI Embedded
  • Se espera que el volumen de datos crezca con el tiempo.
  • El costo del sistema es limitado.
  • La solución debe ser mantenible sin un DBA de producción 24/7
  • La solución debería poder escalar horizontalmente.
  • El número total de inquilinos es inferior a 50

¿Cuál sería una arquitectura recomendada? ¿Hay implementaciones de referencia para este caso de uso? Creo que muchas personas ya podrían haber enfrentado este problema para el desarrollo de software empresarial.

Creo que esta es una situación diferente de manejar un número creciente de inquilinos en la arquitectura de bases de datos de múltiples inquilinos . El caso de uso mencionado en esa pregunta trata de un mayor número de inquilinos, que es muy diferente de tener muy pocos (50) inquilinos grandes. La arquitectura mencionada podría ser una solución aquí, que es de lo que quiero saber más.

DS
fuente

Respuestas:

16

El problema con los fragmentos es que la aplicación tiene que saber qué fragmento consultar. En general, esto se hace compartiendo algo como el cliente. Adaptaré una de mis publicaciones de blog anteriores para usar como respuesta.

Cuando crea una aplicación para muchos clientes, hay dos formas comunes de diseñar las bases de datos:

  • Opción A: poner todos los clientes en la misma base de datos
  • Opción 2: construir una base de datos por cliente

Poner a todos los clientes en la misma base de datos

Es simple: simplemente agregue una tabla de Cliente en la parte superior del esquema, agregue una tabla de ClientUsers para asegurarse de que las personas solo vean sus propios datos, y listo.

Beneficios de este enfoque:

Gestión de esquemas más fácil. Cuando los desarrolladores implementan una nueva versión de la aplicación, solo tienen que hacer cambios de esquema en una base de datos. No hay que preocuparse de que diferentes clientes no estén sincronizados o tengan la versión incorrecta.

Ajuste de rendimiento más fácil. Podemos verificar el uso del índice y las estadísticas en un solo lugar, implementar mejoras fácilmente y ver los efectos inmediatamente en todos nuestros clientes. Con cientos o miles de bases de datos, incluso el cambio más pequeño puede ser difícil de coordinar. Podemos verificar el contenido de la memoria caché de procedimientos y saber con certeza qué consultas o procedimientos almacenados son los más intensivos en toda nuestra aplicación, mientras que si usamos bases de datos separadas por cliente, es posible que tengamos más dificultades para agregar el uso de consultas en diferentes planes de ejecución.

Más fácil de construir una API externa. Si necesitamos otorgar acceso a toda nuestra base de datos para que personas externas creen productos, podemos hacerlo más fácilmente si todos los datos están en una sola base de datos. Si la API tiene que lidiar con la agrupación de datos de múltiples bases de datos en múltiples servidores, agrega tiempo de desarrollo y prueba. (Por otro lado, esa cosa de "servidores múltiples" comienza a insinuar una restricción para el escenario de una base de datos para gobernarlos a todos: una base de datos generalmente significa que toda nuestra carga afecta solo a un servidor de base de datos). En su caso , con PowerBI, tener a todos en una base de datos facilitará la administración de las conexiones.

Alta disponibilidad más fácil y recuperación ante desastres. Es realmente muy simple administrar la duplicación de la base de datos, el envío de registros, la replicación y la agrupación si todo lo que nos preocupa es una sola base de datos. Podemos construir una gran infraestructura rápidamente.

Poner a cada cliente en su propia base de datos o fragmento

Todavía necesita una lista de clientes, pero ahora se convierte en un directorio: para cada cliente, también realiza un seguimiento del fragmento en el que vive. Al inicio, su aplicación consulta esta tabla y la almacena en la memoria caché. Cuando necesita datos para un cliente, se conecta directamente a ese fragmento (base de datos y servidor).

Beneficios de este enfoque:

Restauraciones más sencillas para un solo cliente. Los clientes son bolsas de carne poco fiables. (Excepto los míos, son fiables sacos de carne). Tienen todo tipo de momentos de "oops" en los que quieren recuperar todos sus datos a un punto en el tiempo, y eso es un gran dolor en la parte trasera si sus datos se mezclan con otros datos del cliente en las mismas tablas. Las restauraciones en un escenario de base de datos de un solo cliente son extremadamente fáciles: simplemente restaure la base de datos del cliente. Nadie más está afectado.

Exportaciones de datos más fáciles. A los clientes les encanta tener en sus manos sus datos. Quieren la seguridad de saber que pueden obtener sus datos en cualquier momento que quieran, evitando el temido escenario de bloqueo de proveedores, y quieren hacer sus propios informes. Con los datos de cada cliente aislados en su propia base de datos, simplemente podemos darles una copia de su propia copia de seguridad de la base de datos. No tenemos que construir API de exportación de datos.

Escalabilidad de servidores múltiples más fácil. Cuando nuestra aplicación necesita más potencia de la que podemos obtener de un solo servidor, podemos dividir las bases de datos entre varios servidores. También podemos distribuir la carga geográficamente, colocando servidores en Asia o Europa para estar más cerca de los clientes.

Ajuste de rendimiento por cliente más fácil. Si algunos clientes usan diferentes características o informes, podemos construir un conjunto especializado de índices o vistas indexadas solo para esos clientes sin aumentar el tamaño de los datos de todos. De acuerdo, hay cierto riesgo aquí: al permitir diferencias de esquema entre los clientes, acabamos de hacer que nuestras implementaciones de código sean un poco más riesgosas y nuestra gestión del rendimiento más difícil.

Gestión de seguridad más fácil. Siempre que hayamos bloqueado correctamente la seguridad con un usuario por base de datos, no tenemos que preocuparnos de que el Cliente X acceda a los datos del Cliente Y. Sin embargo, si solo usamos un inicio de sesión único para todos, entonces realmente no hemos abordado esta preocupación.

Ventanas de mantenimiento más fáciles. En un entorno global donde los clientes se encuentran dispersos por todo el mundo, es más fácil desconectarlos para realizar tareas de mantenimiento si podemos hacerlo en grupos o zonas.

Cual de estos es correcto para ti?

No hay una elección correcta: debe conocer las fortalezas y debilidades de su propia empresa. Tomemos dos de mis clientes como ejemplos.

La empresa A se destaca en el ajuste del rendimiento del hardware. Son muy, muy buenos para extraer el último rendimiento del hardware, y no les importa reemplazar su hardware de SQL Server en un ciclo de 12-18 meses. (¡Actualizan los servidores web cada 4-6 meses!) Su talón de Aquiles es el cumplimiento extremo y los requisitos de seguridad. Tienen necesidades de auditoría increíbles, y es más fácil para ellos implementar controles a prueba de balas en un único servidor, una única base de datos que administrar esos requisitos en miles de bases de datos en docenas de servidores. Eligieron una base de datos, un servidor, muchos clientes.

La empresa 2 sobresale en las prácticas de desarrollo. Administrar cambios de esquema e implementaciones de código en miles de bases de datos simplemente no es un problema para ellos. Tienen clientes en todo el mundo y están procesando transacciones de tarjetas de crédito para esos clientes durante todo el día. Necesitan la capacidad de distribuir la carga geográficamente, y no quieren reemplazar servidores en todo el mundo cada 12-18 meses. Eligieron una base de datos para cada cliente, y está dando sus frutos cuando comienzan a colocar servidores SQL en Asia y Europa para sus clientes offshore.

Brent Ozar
fuente
"En su caso, con PowerBI, tener a todos en una base de datos facilitará la administración de las conexiones". En este momento, PowerBI Embedded no tiene seguridad de nivel de fila y, por lo tanto, tener a todos los inquilinos en una base de datos está causando dudas sobre este caso de uso, consulte: community.powerbi.com/t5/Developer/… , a la luz de esta información, ¿podría reformularla? esto o sugerir una alternativa o corregir mi entendimiento?
DS
Además, "Poner a cada cliente en su propia base de datos o fragmento" podría explicar la diferencia aquí entre estas dos sugerencias
DS
Solo diré que tener que implementar en más de una base de datos no es tan malo como parece. En 2017 tenemos muchas opciones que facilitan la implementación de cambios en las bases de datos 1, 5 o 900. Y cuando tiene excepciones para clientes específicos, generalmente se pueden introducir en esas bases de datos de manera que no interfieran con el código común.
Aaron Bertrand
5

Otra consideración que aún no he visto en otras respuestas.

Tener un diseño que permita a muchos inquilinos en una sola base de datos dará flexibilidad más adelante. Si las demandas de carga / escalado / seguridad / ubicación geográfica sugieren más adelante que un inquilino debe tener una base de datos separada, se puede crear restaurando la base de datos correcta en la nueva instancia. Los datos de los otros inquilinos aún están protegidos por cualquier mecanismo que esté en su lugar. Los datos ahora obsoletos pueden eliminarse poco a poco de las bases de datos antiguas y nuevas según lo permita el tiempo.

Lo opuesto no es verdad. La consolidación de muchas bases de datos de un solo inquilino requerirá mucho más trabajo.

Michael Green
fuente
4

Una práctica que hace que los modelos de múltiples inquilinos sean mucho más fáciles, aunque rompa la normalización *, es incluir una columna en cada tabla para el inquilino. Podrías llamarlo TenantID. De esa manera, cada consulta ejecutada en la base de datos puede filtrar en TenantID en cada tabla, y puede usar el particionamiento de la base de datos para aislar los datos de cada inquilino y acelerar las consultas al tener particiones alineadas. Es mucho más fácil tener a todos los inquilinos en una base de datos de esta manera.

* No siempre rompe la normalización, pero puede. Por ejemplo, si tiene una Persony una PersonAddresstabla. La Persontabla tendrá TenantID, PersonIDcomo clave principal. La PersonAddresstabla tendrá TenantID, PersonID, AddressTypeIDcomo clave principal lo que sugiero.

Normalmente solo PersonIDsería suficiente, porque podrías unirlo de nuevo a la Personmesa para encontrar el Tenant. Le sugiero que continúe TenantIDcon cada tabla subsiguiente, incluso cuando una clave más delgada funcionaría.

Tenía entendido que llevar cualquier información a una tabla que pudiera derivarse de otros datos se consideraba romper la normalización. Pero quizás el uso de teclas delgadas es solo una práctica recomendada.

Matthew Sontum
fuente
Gracias, estoy de acuerdo con la sugerencia y para agregarla, me gustaría mencionar este campo TenantID debe ser un tipo entero y no un GUID, nos quemamos de esa manera por el rendimiento.
DS
3
Pero incluso si elige llevar el TenantID a las tablas secundarias, lo que no tiene que hacer, una clave más amplia no significa que la normalización esté "rota". Al igual que elegir un GUID sobre IDENTIDAD (una clave más amplia) no interrumpe la normalización, ni elegir una clave natural más amplia en lugar de usar sustitutos.
Aaron Bertrand