Manejo de un número creciente de inquilinos en la arquitectura de bases de datos de múltiples inquilinos

26

Manejar un número modesto de clientes (inquilinos) en un servidor común con bases de datos separadas para cada instancia de la aplicación de los inquilinos es relativamente sencillo y normalmente es la forma correcta de hacerlo. Actualmente estoy mirando la arquitectura de una aplicación donde cada inquilino tiene su propia instancia de base de datos.

Sin embargo, el problema es que esta aplicación tendrá una gran cantidad de inquilinos (5,000-10,000) con una cantidad sustancial de usuarios, quizás 2,000 para un solo inquilino. Tendremos que apoyar el crecimiento del sistema por parte de varios inquilinos cada semana.

Además, a todos los inquilinos y sus usuarios se les presentará un proceso de inicio de sesión común (es decir, cada inquilino no puede tener su propia URL). Para hacer esto, necesito un proceso de inicio de sesión centralizado y un medio para agregar dinámicamente bases de datos al sistema y registrar usuarios.

  • ¿Cómo podría automatizarse de manera sólida el proceso de registro y creación de base de datos?

  • ¿Es probable que el proceso de creación y registro de bases de datos de inquilinos en el sistema cause problemas de rendimiento o bloqueo? Si cree que esto podría ser un problema, ¿alguien puede sugerir formas de mitigarlo?

  • ¿Cómo puedo administrar la autenticación central de manera que las credenciales de los usuarios se asocien con la base de datos de un inquilino en particular, pero el usuario puede iniciar sesión a través de una página común (es decir, a través de la misma URL de inicio de sesión, pero su aplicación de inicio estará en la base de datos de un inquilino específico) ) Los inquilinos deberán poder mantener sus propios inicios de sesión y permisos, pero un sistema de inicio de sesión central debe ser consciente de ello. ¿Alguien puede sugerir una forma de hacer esto?

  • Si necesito 'escalar' agregando múltiples servidores de bases de datos, ¿alguien puede sugerirme qué problemas tendré que enfrentar para administrar las identidades de los usuarios en los servidores (suplantación, etc.) y alguna forma de mitigar esos problemas?

coddey
fuente
1
No he tenido que lidiar con una situación como esta, pero mi intuición sería manejar el despliegue del inquilino preconfigurando servidores con tantas bases de datos de inquilinos como creas que pueden manejar y luego simplemente asignar las bases de datos de inquilinos preconstruidas como nuevos inquilinos Regístrate. De esta manera, no tiene que preocuparse por la contención de recursos al implementar DB de inquilinos al menos.
Joel Brown
1
¿Está seguro de que llegará a cerca de 5,000-10,000 inquilinos? ¿Y que todos sus inquilinos estarán en el rango de 2,000 usuarios? En mi sistema, creo que el mayor número de usuarios de nuestra aplicación para un solo inquilino fue de aproximadamente 100. Y de esos, solo 20 o más estuvieron constantemente activos. ¿Puedo preguntar cuál es la industria / aplicación?
Aaron Bertrand
@AaronBertrand Es un sistema de gestión de aprendizaje donde los servicios serán parcialmente gratuitos y parcialmente pagados.
coddey

Respuestas:

25

En el extremo inferior (500 inquilinos / 10000 usuarios) así es como lo hice. Primero, tiene una base de datos de "control" que es global, central y contiene toda la información sobre inquilinos y usuarios (realmente no creo que quiera administrarlos como inicios de sesión de autenticación SQL). Imagine una base de datos llamada "Control" con las siguientes tablas:

CREATE TABLE dbo.Instances
(
  InstanceID INT PRIMARY KEY,
  Connection VARCHAR(255)
  --, ...
);

INSERT dbo.Instances SELECT 1, 'PROD1\Instance1';
INSERT dbo.Instances SELECT 1, 'PROD2\Instance1';
-- ...

CREATE TABLE dbo.Tenants
(
  TenantID INT PRIMARY KEY,
  Name NVARCHAR(255) NOT NULL UNIQUE,
  InstanceID INT -- Foreign key tells which instance this tenant's DB is on
  --, ...
);

INSERT dbo.Tenants SELECT 1, 'MyTenant', 1;
-- ...

CREATE TABLE dbo.Users
(
  UserID INT PRIMARY KEY,
  Username VARCHAR(320) NOT NULL UNIQUE,
  PasswordHash VARBINARY(64), -- because you never store plain text, right?
  TenantID INT -- foreign key
  --, ...
);

INSERT dbo.Users SELECT 1, '[email protected]', 0x43..., 1;

En nuestro caso, cuando agregamos un nuevo inquilino, construiríamos la base de datos dinámicamente, pero no cuando el usuario administrador hizo clic en Aceptar en la interfaz de usuario ... teníamos un trabajo en segundo plano que sacaba nuevas bases de datos de una cola cada 5 minutos, configuraba el modelo como usuario único , y luego creó cada nueva base de datos en serie. Hicimos esto para (a) evitar que el usuario administrador espere la creación de la base de datos y (b) para evitar que dos usuarios administradores intenten crear una base de datos al mismo tiempo o se les niegue la capacidad de bloquear el modelo (requerido al crear una nueva base de datos )

Las bases de datos se crearon con el esquema de nombre Tenant000000xxdonde se xxrepresenta Tenants.TenantID. Esto hizo que los trabajos de mantenimiento muy fácil, en lugar de tener todo tipo de bases de datos con nombre BurgerKing, McDonalds, KFCetc. No es que estábamos en la comida rápida, apenas usar eso como un ejemplo.

La razón por la que no asignamos previamente miles de bases de datos como sugiere el comentario es que nuestros usuarios administradores generalmente tenían una idea de cuán grande sería el inquilino, si tenían alta prioridad, etc. Así que tenían opciones básicas en la interfaz de usuario que dictaminaría su tamaño inicial y la configuración de crecimiento automático, a qué subsistema de disco irían sus archivos de datos / registro, su configuración de recuperación, el programa de copia de seguridad para depender de él, e incluso sabe en qué instancia desplegar la base de datos para equilibrar mejor el uso ( aunque nuestros administradores podrían anular esto). Una vez que se crea la base de datos, la tabla de inquilinos se actualizó con la instancia elegida, se creó un usuario administrador para el inquilino, y nuestros administradores recibieron por correo electrónico las credenciales para pasarlas al nuevo inquilino.

Si está utilizando un único punto de entrada, no es posible permitir que varios inquilinos tengan usuarios con el mismo nombre de usuario. Optamos por usar la dirección de correo electrónico, que, si todos los usuarios trabajan para la empresa y usan su dirección de correo electrónico corporativa, debería estar bien. Aunque nuestra solución finalmente se volvió más compleja por dos razones:

  1. Teníamos consultores que trabajaban para más de uno de nuestros clientes y necesitaban acceso a múltiples
  2. Teníamos inquilinos que en realidad estaban compuestos por múltiples inquilinos

Entonces, terminamos con una TenantUserstabla que permitía asociar a un usuario con múltiples inquilinos.

Inicialmente, cuando un usuario inicia sesión, la aplicación conocerá la cadena de conexión solo para la base de datos de control. Cuando un inicio de sesión es exitoso, puede construir una cadena de conexión basada en la información que encontró. P.ej

SELECT i.Connection
  FROM dbo.Instances AS i
  INNER JOIN dbo.Tenants AS t
  ON i.InstanceID = t.InstanceID
  INNER JOIN dbo.TenantUsers AS u
  ON i.TenantID = u.TenantID
  WHERE u.UserID = @UserID;

Ahora la aplicación podría conectarse a la base de datos del usuario (cada usuario tenía un inquilino predeterminado ) o el usuario podía seleccionar cualquiera de los inquilinos a los que podía acceder. La aplicación simplemente recuperaría la nueva cadena de conexión y la redirigiría a la página de inicio de ese inquilino.

Si ingresa a esta área de usuario de 10MM que propone, definitivamente necesitará que esto se equilibre mejor. Es posible que desee federar la aplicación para que tengan diferentes puntos de entrada que se conecten a diferentes bases de datos de control. Si le da a cada inquilino un subdominio (por ejemplo, TenantName.YourApplicationDomain.com), puede hacerlo detrás de escena con DNS / enrutamiento sin interrumpirlos cuando necesite escalar más.

Hay mucho más en esto: como @Darin, solo estoy rascando la superficie aquí. Avíseme si necesita una consulta no gratuita. :-)

Aaron Bertrand
fuente
Gracias por compartir tu experiencia. De hecho, me ha iluminado. Mirando más sobre ello. Pero ya escribiste No libre. :(
coddey
1
Mi punto era que solo tengo mucho tiempo para asignar a asesoramiento gratuito. :-)
Aaron Bertrand
+1: casi exactamente el mismo enfoque que he usado antes. ~ el mismo número de inquilinos también funcionó muy bien.
AdaTheDev
¿Cómo manejar la relación entre la base de datos maestra y la base de datos de inquilinos? (sin el uso de disparadores, etc.)
Jitendra Pancholi
@jitendra no tiene muchas opciones: ¿cuántos datos tiene realmente en una base de datos de inquilinos que necesitan relacionarse con los datos de la base de datos maestra? Tampoco estoy seguro de entender el miedo popular a los desencadenantes: un desencadenante escrito correctamente no es algo de lo que deba temer ...
Aaron Bertrand
10

Tienes un proyecto bastante interesante. Nunca he visto a nadie tratar de implementar algo tan grande, al menos en SQL Server. Cuanto más leo tu publicación, más preguntas se me ocurren ...

En el peor de los casos, en cuanto a la infraestructura (que en realidad es el mejor de los casos, desde el punto de vista comercial), necesita 10K bases de datos por 2k usuarios. Eso es 20,000,000 de usuarios. No tendrá éxito al tratar de administrar 20 M inicios de sesión de SQL Server. OMI Solo la gran cantidad de ellos, tratando de moverlos de un servidor a otro, vigilando las colisiones de ID y las ID no coincidentes, además no estoy seguro de cómo se comportaría SQL Server con 20 M filas en sys.server_principals. Además, su aplicación web probablemente querrá conectarse como un solo número de usuarios, o muy bajo. IIS no puede agrupar conexiones a menos que sus cadenas DSN sean idénticas. Uno de los atributos de una cadena DSN es el nombre de usuario. Diferentes usuarios significa que no hay agrupación.

Necesitará rodar su propio esquema de credenciales de usuario. Tendrá que poder determinar a qué inquilino pertenece un usuario y luego su código web deberá seleccionar la base de datos adecuada. Los metadatos de los usuarios son críticos, deberán almacenarse en algún lugar, deberán agruparse o duplicarse, deberán ser rápidos y deberán estar bien protegidos (desde una perspectiva de seguridad. IOW, encriptarlo). Suponiendo que SQL es incluso una buena idea aquí, mantendría esta base de datos lejos de las instancias que los inquilinos del servidor. Esto ayuda desde el punto de vista de la seguridad y desde el punto de vista de la carga, aunque supongo que una vez que se valida a los usuarios y la aplicación web se dirige a la base de datos correcta en otra instancia, no habrá más consultas sobre los metadatos de este usuario relacionados con eso. usuario.

Pregunta rápida: ¿se debe permitir que dos usuarios diferentes, que pertenecen a dos inquilinos diferentes, tengan el mismo nombre de usuario?

Otra pregunta rápida: si te digo que trabajo para FuBar, Inc., ¿cómo lo sabes? ¿FuBar le dará una lista de usuarios y usted les devolverá una lista de nombres de usuario, o se aprovisionarán ellos mismos?

Tendrás que ir a varias instancias. Si incluso una fracción de esos usuarios decide acceder a la aplicación a la vez, una sola instancia se derretirá. No tendrá suficientes subprocesos de trabajo para ejecutar todas esas solicitudes a la vez. Si solo 1000 usuarios llegan a su instancia al mismo tiempo, probablemente se quedará sin hilos de trabajo y la solicitud comenzará a acumularse y esperar. He visto que esto suceda; El síntoma próximo es que las nuevas conexiones no podrán iniciar sesión en la instancia porque no hay hilos de trabajo disponibles para darles servicio. Si este es un comportamiento de muy corta duración, su aplicación podría sobrevivir. Si no, o su aplicación es exigente, los usuarios obtendrán errores.

Incluso si no tendrá muchos inquilinos para comenzar, debe comenzar a pensar en el futuro y la automatización porque cuando ve que su servidor está bloqueado y hay 10 nuevos inquilinos para poner en línea, es demasiado tarde y su servicio (y sus clientes y los que pronto serán ex clientes) sufrirán hasta que salga del problema.

Necesitará una forma de mover las bases de datos, desde servidores sobrecargados hasta servidores con poca carga (o nuevos). Si puede obtener o no una ventana de tiempo de inactividad dependerá de su SLA.

¿Está proporcionando una aplicación específica, como SalesForce, o estas bases de datos son solo contenedores para lo que quieran poner sus inquilinos?

¿Qué tan grandes son las bases de datos? Si no son muy grandes, simplemente puede restaurar desde un archivo de respaldo que proporciona una plantilla. (Esto no es muy diferente de lo que hace la base de datos del modelo, pero no he visto a nadie realmente usar el modelo de una buena manera desde mis días con SQL 6.5.) Una vez que la plantilla ha sido restaurada al nuevo nombre de la base de datos, podría luego personalice la nueva base de datos según sea necesario para un inquilino en particular. No puede hacer la personalización antes de tener el inquilino, obviamente. Si la base de datos es grande, puede seguir el mismo procedimiento básico, excepto que realice la restauración con anticipación, antes de que cualquier nuevo inquilino necesite el espacio. Puede mantener un par de estas bases de datos, tal vez una por instancia. Si mantiene demasiados, esto lo obligará a comprar más hardware y / o almacenamiento del que necesita,

Si esta es su propia aplicación, ¿cómo manejará las actualizaciones de los esquemas? ¿Cómo va a mantener las versiones de la base de datos correctas con las versiones del código, si está utilizando una única URL que llega a su aplicación web?

¿Cómo detecta y destruye las bases de datos que ya no están en uso? ¿Espera hasta que su grupo A / R diga que alguien no ha pagado su factura en tres meses?

Si los inquilinos están administrando los permisos, eso significa que tienen cierta comprensión del funcionamiento interno de la aplicación o que su aplicación tiene una estructura de roles muy simple. Usando algo como Blogger como un ejemplo aproximado, los usuarios pueden (leer publicaciones), (leer publicaciones y hacer comentarios), (... y crear publicaciones), (... y editar las publicaciones de otros), (... y pueden restablecer contraseñas de otros usuarios), o (... y lo que sea). Tener un rol para cada uno de esos diferentes conjuntos de derechos y asignar un usuario a un rol u otro no debería ser demasiado difícil, pero no desea que su aplicación ejecute declaraciones 'GRANT'. Tenga cuidado con los roles que tienen una jerarquía y dependen de la herencia, puede ser confuso. Si está promocionando o degradando a un usuario, yo diría que los elimine de todos los roles asociados y luego los agregue nuevamente al rol que necesitan. Oh,

Creo que solo he arañado la superficie aquí, y esta publicación ya es demasiado larga. Lo que realmente necesita es un libro, o al menos un documento técnico de alguien que haya hecho esto. La mayoría de esos tipos no hablarán si lo ven como una ventaja competitiva.

estrecho de Darin
fuente
Gracias por los comentarios. De hecho, el proyecto es interesante. Debido a la limitación de palabras, mantengo el comentario muy preciso. Es un sistema de gestión de aprendizaje donde cada inquilino tendrá alrededor de 120-150 mesas. Ningún usuario tendrá el mismo nombre de usuario independientemente del inquilino. Para reducir aún más la complejidad, se utilizará el mapeo DNS CNAME, por ejemplo, tenant1.abc.com. Ahora el punto de ebullición es: diseñarlo de manera correcta para que atienda todas las sugerencias que ha compartido y me preocupa. Obtener un documento técnico será digno de elogio, pero tal vez no sea fácil. Buscar más información si puede. !!!!
coddey