En esta página, se analizan los requisitos del esquema de Spanner, cómo usar el esquema para crear relaciones jerárquicas y las características de esquema. También se presentan tablas intercaladas, que pueden mejorar el rendimiento de las consultas cuando se consultan tablas en una relación de superior y secundario.
Un esquema es un espacio de nombres que contiene objetos de base de datos, como tablas, vistas, índices y funciones. Los esquemas se usan para organizar objetos, aplicar privilegios de control de acceso detallados y evitar conflictos de nombres. Debes definir un esquema para cada base de datos en Spanner.
Datos muy escritos
Los datos en Spanner están bien escritos. Los tipos de datos incluyen tipos escalares y complejos, que se describen en Tipos de datos en GoogleSQL y Tipos de datos de PostgreSQL.
Selecciona una clave principal.
Las bases de datos de Spanner pueden contener una o más tablas. Las tablas se estructuran como filas y columnas. El esquema de la tabla define una o más columnas de la tabla como la clave primaria de la tabla, que identifica cada fila de manera única. Las claves primarias siempre se indexan para una búsqueda rápida de filas. Si quieres actualizar o borrar filas existentes en una tabla, esta debe tener una clave primaria. Una tabla sin columnas de clave primaria solo puede tener una fila. Solo las bases de datos del dialecto de GoogleSQL pueden tener tablas sin una clave primaria.
A menudo, tu aplicación ya tiene un campo que se ajusta naturalmente a la clave primaria. Por ejemplo, para una tabla Customers
, puede haber una CustomerId
proporcionada por la aplicación que funcione bien como clave primaria. En otros casos, es posible que debas generar una clave primaria cuando insertes la fila. Por lo general, este sería un valor de número entero único sin importancia comercial (una clave primaria subrogada).
En todos los casos, debes tener cuidado de no crear hotspots con la clave primaria que elijas. Por ejemplo, si insertas registros con una clave que es un número entero que aumenta monótonamente, siempre se insertará al final del espacio de claves. Esto no es recomendable porque Spanner divide los datos entre servidores por rangos de claves, lo que significa que tus inserciones se dirigirán a un solo servidor, lo que creará un hotspot. Existen técnicas que pueden distribuir la carga entre varios servidores y evitar los hotspots:
- Genera un hash de la clave y almacénalo en una columna. Usa la columna de hash (o la columna de hash más la columna de la clave única) como clave primaria.
- Cambia el orden de las columnas en la clave primaria.
- Usa un identificador único universal (UUID). Se recomienda el UUID de la versión 4, ya que usa valores aleatorios en los bits de orden superior. No uses un algoritmo UUID (como uno de versión 1) que almacene la marca de tiempo en los bits de orden superior.
- Revierte los bits de los valores secuenciales.
Relaciones entre tablas superiores y secundarias
Existen dos maneras de definir las relaciones superior-secundario en Spanner: la intercalación de tablas y las claves externas.
La intercalación de tablas de Spanner es una buena opción para muchas relaciones superior-secundario. Con la intercalación, Spanner ubica físicamente
las filas secundarias con las principales en el almacenamiento. La colocación puede mejorar
significativamente el rendimiento. Por ejemplo, si tienes una tabla Customers
y una tabla Invoices
, y tu aplicación recupera con frecuencia todas las facturas de un cliente, puedes definir Invoices
como una tabla secundaria intercalada de Customers
. Al hacerlo, declaras una relación de localidad de datos entre dos tablas independientes. Le indicas a Spanner que almacene una o más filas de Invoices
con una fila Customers
.
Para asociar una tabla secundaria con una superior, debes usar un DDL que declare la tabla secundaria como intercalada en ella y, además, incluir la clave primaria de la tabla superior como la primera parte de la clave primaria compuesta de la tabla secundaria. Para obtener más información sobre la intercalación, consulta Crea tablas intercaladas más adelante en esta página.
Las claves externas son una solución de elementos principales y secundarios más general y abordan casos prácticos adicionales. No están limitadas a las columnas de clave primaria, y las tablas pueden tener varias relaciones de clave exterior, tanto como un superior en algunas relaciones, y un elemento secundario en otras. Sin embargo, una relación de clave externa no implica la ubicación conjunta de las tablas en la capa de almacenamiento.
Google recomienda que elijas representar las relaciones superior-secundario como tablas intercaladas o como claves externas, pero no como ambas. Para obtener más información sobre las claves externas y su comparación con tablas intercaladas, consulta Descripción general de las claves externas.
Claves primarias en tablas intercaladas
Para la intercalación, cada tabla debe tener una clave primaria. Si declaras que una tabla es un elemento secundario intercalado de otra tabla, la tabla debe tener una clave primaria compuesta que incluya todos los componentes de la clave primaria del elemento superior, en el mismo orden, y, por lo general, una o más columnas adicionales de la tabla secundaria.
Spanner almacena las filas en orden ordenado por valores de clave primaria, con filas secundarias insertadas entre las filas superiores. Observa una ilustración de las filas intercaladas en la sección Crea tablas intercaladas más adelante en esta página.
En resumen, Spanner puede colocar físicamente filas de tablas relacionadas. En los ejemplos de esquema, se muestra cómo se ve este diseño físico.
Divisiones de bases de datos
Puedes definir jerarquías de relaciones intercaladas entre tablas superiores y secundarias de hasta siete capas de profundidad, lo que significa que puedes colocar filas de siete tablas independientes. Si el tamaño de los datos de las tablas es pequeño, es probable que un solo servidor de Spanner pueda manejar la base de datos. Pero ¿qué sucede cuando las tablas relacionadas crecen y comienzan a alcanzar los límites de recursos de un servidor individual? Spanner es una base de datos distribuida, lo que significa que, a medida que crece la base de datos, Spanner divide los datos en fragmentos llamados “divisiones”. Las divisiones individuales pueden moverse de forma independiente entre sí y asignarse a distintos servidores, que pueden estar en diferentes ubicaciones físicas. Una división contiene un rango de filas contiguas. Las claves de inicio y fin de este rango se denominan “límites de división”. Spanner agrega y quita de forma automática estos límites según el tamaño y la carga, lo que cambia la cantidad de divisiones de la base de datos.
División basada en la carga
Como ejemplo de cómo Spanner realiza la división basada en la carga para mitigar los hotspots de lectura, supongamos que tu base de datos contiene una tabla con 10 filas que se leen con mayor frecuencia que todas las otras filas de la tabla. Spanner puede agregar límites de división entre cada una de esas 10 filas para que un servidor diferente las controle, en lugar de permitir que todas las lecturas de esas filas consuman los recursos de un solo servidor.
Como regla general, si sigues las prácticas recomendadas para el diseño de esquemas, Spanner puede mitigar hotspots de modo que la capacidad de procesamiento de lectura mejore cada pocos minutos hasta que satures los recursos de tu instancia o encuentres casos en los que no se puedan agregar nuevos límites de división (porque tienes una división que cubre solo una fila sin elementos secundarios intercalados).
Esquemas con nombre
Los esquemas con nombre te ayudan a organizar datos similares juntos. Esto te ayuda a encontrar objetos con rapidez en la consola de Google Cloud, aplicar privilegios y evitar conflictos de nombres.
Los esquemas con nombre, al igual que otros objetos de base de datos, se administran mediante DDL.
Los esquemas con nombre de Spanner te permiten usar nombres completamente calificados (FQN) para consultar datos. Los FQN te permiten combinar el nombre del esquema y el nombre del objeto para identificar los objetos de la base de datos. Por ejemplo, puedes crear un esquema llamado warehouse
para la unidad de negocios del almacén. Las tablas que usan este esquema pueden incluir: product
, order
y customer information
. También puedes crear un esquema llamado fulfillment
para la unidad de negocios de entrega.
Este esquema también podría tener tablas llamadas product
, order
y customer
information
. En el primer ejemplo, el FQN es warehouse.product
y, en el segundo ejemplo, el FQN es fulfillment.product
. Esto evita confusiones en situaciones en las que varios objetos comparten el mismo nombre.
En el DDL CREATE SCHEMA
, los objetos de tabla reciben un FQN, por ejemplo, sales.customers
, y un nombre corto, por ejemplo, sales
.
Los siguientes objetos de base de datos admiten esquemas con nombre:
TABLE
CREATE
INTERLEAVE IN [PARENT]
FOREIGN KEY
SYNONYM
VIEW
INDEX
FOREIGN KEY
SEQUENCE
Para obtener más información sobre el uso de esquemas con nombre, consulta Administra esquemas con nombre.
Usa el control de acceso detallado con esquemas con nombre
Los esquemas con nombre te permiten otorgar acceso a nivel de esquema a cada objeto en él. Esto se aplica a los objetos de esquema que existen en el momento en que otorgas acceso. Debes otorgar acceso a los objetos que se agreguen más adelante.
El control de acceso detallado limita el acceso a grupos completos de objetos de base de datos, como tablas, columnas y filas en la tabla.
Para obtener más información, consulta Otorga privilegios de control de acceso detallados a esquemas con nombre.
Ejemplos de esquemas
En los ejemplos de esquema de esta sección, se muestra cómo crear tablas superiores y secundarias con y sin intercalación, y se ilustran los diseños físicos correspondientes de los datos.
Crea una tabla superior
Supongamos que estás creando una aplicación de música y necesitas una tabla que almacene filas de datos de cantantes:
Ten en cuenta que la tabla contiene una columna de clave primaria, SingerId
, que aparece a la izquierda de la línea en negrita, y que las tablas están organizadas por filas y columnas.
Puedes definir la tabla con el siguiente DDL:
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId);
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA );
Ten en cuenta lo siguiente sobre el esquema de ejemplo:
Singers
es una tabla que se encuentra en la raíz de la jerarquía de la base de datos (porque no está definida como un elemento secundario intercalado de otra tabla).- Para las bases de datos del dialecto GoogleSQL, las columnas de clave primaria suelen tener anotaciones
NOT NULL
(aunque puedes omitir esta anotación si deseas permitir valoresNULL
en columnas de clave. Para obtener más información, consulta Columnas de claves. - Las columnas que no se incluyen en la clave primaria se denominan columnas sin clave y pueden tener una anotación
NOT NULL
opcional. - Las columnas que usan los tipos
STRING
oBYTES
en GoogleSQL deben definirse con una longitud, que representa la cantidad máxima de caracteres Unicode que se pueden almacenar en el campo. La especificación de longitud es opcional para los tiposvarchar
ycharacter varying
de PostgreSQL. Si deseas obtener más información, consulta Tipos de datos escalares para bases de datos de dialectos de GoogleSQL y tipos de datos de PostgreSQL para bases de datos de dialectos de PostgreSQL.
¿Cómo es la disposición física de las filas de la tabla Singers
? En el siguiente diagrama, se muestran las filas de la tabla Singers
que almacena la clave primaria (“Singers(1)” y, luego, “Singers(2)”, en la que el número entre paréntesis es el valor de la clave primaria.
En el diagrama anterior, se ilustra un límite de división de ejemplo entre las filas con claves Singers(3)
y Singers(4)
, con los datos de las divisiones resultantes asignados a diferentes servidores. A medida que crece esta tabla, es posible que se almacenen en diferentes ubicaciones filas de datos de Singers
.
Crear tablas superiores y secundarias
Supongamos que ahora deseas agregar algunos datos básicos sobre los álbumes de cada cantante a la aplicación de música.
Ten en cuenta que la clave primaria de Albums
se compone de dos columnas: SingerId
y AlbumId
. Esto permite asociar cada álbum con su cantante. En el siguiente esquema de ejemplo, se definen las tablas Albums
y Singers
en la raíz de la jerarquía de la base de datos, lo que las convierte en tablas del mismo nivel.
-- Schema hierarchy: -- + Singers (sibling table of Albums) -- + Albums (sibling table of Singers)
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId); CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId);
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA ); CREATE TABLE albums ( singer_id BIGINT, album_id BIGINT, album_title VARCHAR, PRIMARY KEY (singer_id, album_id) );
El diseño físico de las filas de Singers
y Albums
se parece al siguiente diagrama, con filas de la tabla Albums
almacenadas por clave primaria contigua y, luego, filas de Singers
almacenadas por clave primaria contigua:
Una nota importante sobre el esquema es que Spanner no supone ninguna relación de localidad de datos entre las tablas Singers
y Albums
, ya que son tablas de nivel superior. A medida que crece la base de datos, Spanner puede agregar límites de división entre cualquiera de las filas. Esto significa que las filas de la tabla Albums
podrían terminar en una división diferente a las filas de la tabla Singers
, y las dos divisiones podrían moverse de forma independiente entre sí.
Según las necesidades de la aplicación, podría ser aceptable permitir que los datos de Albums
se ubiquen en divisiones diferentes a las de los datos de Singers
. Sin embargo, esto puede generar una penalización de rendimiento debido a la necesidad de coordinar las lecturas y actualizaciones en distintos recursos. Si tu aplicación necesita recuperar con frecuencia información sobre todos los álbumes de un cantante en particular, debes crear Albums
como una tabla secundaria intercalada de Singers
, que coloca filas de las dos tablas junto con la dimensión de la clave primaria. En el siguiente ejemplo, se explica esto con más detalle.
Crea tablas intercaladas
Una tabla intercalada es una tabla que declaras como elemento secundario intercalado de otra tabla porque quieres que las filas de esa tabla se almacenen físicamente con la fila superior asociada. Como se mencionó antes, la clave primaria de la tabla superior debe ser la primera parte de la clave primaria compuesta de la tabla secundaria.
Mientras diseñas tu aplicación de música, supongamos que te das cuenta de que necesita acceder con frecuencia a las filas de la tabla Albums
cuando accede a una fila Singers
. Por ejemplo, cuando accedes a la fila Singers(1)
, también debes acceder a las filas Albums(1, 1)
y Albums(1, 2)
. En este caso, Singers
y Albums
deben tener una relación sólida de localidad de datos. Para declarar esta relación de localidad de datos, crea Albums
como una tabla secundaria intercalada de Singers
.
-- Schema hierarchy: -- + Singers -- + Albums (interleaved table, child table of Singers)
La línea en negrita del siguiente esquema muestra cómo crear Albums
como una tabla intercalada de Singers
.
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId); CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA ); CREATE TABLE albums ( singer_id BIGINT, album_id BIGINT, album_title VARCHAR, PRIMARY KEY (singer_id, album_id) ) INTERLEAVE IN PARENT singers ON DELETE CASCADE;
Notas sobre este esquema:
SingerId
, que es la primera parte de la clave primaria de la tabla secundariaAlbums
, también es la clave primaria de su tabla superiorSingers
.- La anotación
ON DELETE CASCADE
indica que, cuando se borra una fila de la tabla superior, también se borran automáticamente sus filas secundarias. Si una tabla secundaria no tiene esta anotación, o la anotación esON DELETE NO ACTION
, debes borrar las filas secundarias antes de borrar la fila superior. - Las filas intercaladas se ordenan primero por filas de la tabla superior y, luego, por filas contiguas de la tabla secundaria que comparten la clave primaria de la tabla superior. Por ejemplo, "Singers(1)", "Albums(1, 1)" y, luego, "Albums(1, 2)".
- La relación de localidad de los datos de cada cantante y los datos de sus álbumes se conservan si esta base de datos se divide, siempre que el tamaño de una fila
Singers
y todas sus filasAlbums
se mantengan por debajo del límite de tamaño de división y que no haya un hotspot en ninguna de estas filas deAlbums
. - La fila superior debe existir antes de que puedas insertar filas secundarias. La fila superior ya puede existir en la base de datos o se puede insertar antes de la inserción de las filas secundarias en la misma transacción.
Cómo crear una jerarquía de tablas intercaladas
La relación de superior y secundaria entre Singers
y Albums
se puede extender a más tablas descendientes. Por ejemplo, puedes crear una tabla intercalada llamada Songs
como secundaria de Albums
para almacenar la lista de pistas de cada álbum:
Songs
debe tener una clave primaria que incluya todas las claves primarias de las tablas que se encuentran en un nivel superior en la jerarquía, es decir, SingerId
y AlbumId
.
-- Schema hierarchy: -- + Singers -- + Albums (interleaved table, child table of Singers) -- + Songs (interleaved table, child table of Albums)
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId); CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE; CREATE TABLE Songs ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, TrackId INT64 NOT NULL, SongName STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId, TrackId), INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA ); CREATE TABLE albums ( singer_id BIGINT, album_id BIGINT, album_title VARCHAR, PRIMARY KEY (singer_id, album_id) ) INTERLEAVE IN PARENT singers ON DELETE CASCADE; CREATE TABLE songs ( singer_id BIGINT, album_id BIGINT, track_id BIGINT, song_name VARCHAR, PRIMARY KEY (singer_id, album_id, track_id) ) INTERLEAVE IN PARENT albums ON DELETE CASCADE;
En el siguiente diagrama, se representa una vista física de las filas intercaladas.
En este ejemplo, a medida que aumenta la cantidad de cantantes, Spanner agrega límites divididos entre cantantes para preservar la localidad de los datos entre un cantante y sus datos de álbumes y canciones. Sin embargo, si el tamaño de una fila de cantante y sus filas secundarias supera el límite de tamaño de división o se detecta un hotspot en las filas secundarias, Spanner intenta agregar límites de división para aislar esa fila de hotspot junto con todas las filas secundarias debajo.
En resumen, una tabla superior junto con todas sus tablas secundarias y descendientes forman una jerarquía de tablas en el esquema. Aunque cada tabla de la jerarquía es independiente desde el punto de vista lógico, intercalarlas físicamente de esta manera puede mejorar el rendimiento, ya que las tablas se unen de forma previa y te permiten acceder juntas a las filas relacionadas, a la vez que se minimizan los accesos al almacenamiento.
Uniones con tablas intercaladas
Si es posible, une los datos de las tablas intercaladas por clave primaria. Debido a que cada fila intercalada se suele almacenar de manera física en la misma división que su fila superior, Spanner puede realizar uniones localmente según la clave primaria, lo que minimiza el acceso al almacenamiento y el tráfico de red. En el siguiente ejemplo, Singers
y Albums
se unen en la clave primaria SingerId
.
GoogleSQL
SELECT s.FirstName, a.AlbumTitle FROM Singers AS s JOIN Albums AS a ON s.SingerId = a.SingerId;
PostgreSQL
SELECT s.first_name, a.album_title FROM singers AS s JOIN albums AS a ON s.singer_id = a.singer_id;
Columnas de clave
Esta sección incluye algunas notas sobre las columnas clave.
Cómo cambiar las claves de la tabla
No es posible modificar las claves de una tabla. Es decir, no puedes agregar una columna de clave a una tabla existente ni quitar una columna de clave que ya exista.
Almacena valores NULL en una clave primaria
En GoogleSQL, si deseas almacenar NULL en una columna de clave primaria, omite la cláusula NOT NULL
para esa columna en el esquema. (Las bases de datos de dialectos de PostgreSQL no admiten valores NULL en una columna de clave primaria).
Este es un ejemplo de omisión de la cláusula NOT NULL
en la columna de clave primaria SingerId
. Ten en cuenta que, debido a que SingerId
es la clave primaria, solo puede haber una fila que almacene NULL
en esa columna.
CREATE TABLE Singers ( SingerId INT64, FirstName STRING(1024), LastName STRING(1024), ) PRIMARY KEY (SingerId);
La propiedad para admitir valores NULL de la columna de clave primaria debe coincidir entre las declaraciones de las tablas superior y secundaria. En este ejemplo, no se permite NOT NULL
para la columna Albums.SingerId
porque Singers.SingerId
lo omite.
CREATE TABLE Singers ( SingerId INT64, FirstName STRING(1024), LastName STRING(1024), ) PRIMARY KEY (SingerId); CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
Tipos no permitidos
Las siguientes columnas no pueden ser del tipo ARRAY
:
- Las columnas de clave de una tabla
- Las columnas de clave de un índice
Diseña para multiusuarios
Se recomienda implementar la función multiusuario si almacenas datos que pertenecen a diferentes clientes. Por ejemplo, un servicio de música podría querer almacenar el contenido de cada sello discográfico por separado.
Multiusuario clásico
La forma clásica de diseñar para multiusuario es crear una base de datos separada para cada cliente. En este ejemplo, cada base de datos tiene su propia tabla Singers
:
SingerId | FirstName | LastName |
---|---|---|
1 | Marc | Richards |
2 | Catalina | Smith |
SingerId | FirstName | LastName |
---|---|---|
1 | Alicia | Trentor |
2 | Gabriel | Wright |
SingerId | FirstName | LastName |
---|---|---|
1 | Benjamín | Martínez |
2 | Hannah | Harris |
Multiusuario administrado por el esquema
Otra forma de diseñar para multiusuarios en Spanner es tener todos los clientes en una sola tabla en una sola base de datos y usar un valor de clave primaria diferente para cada cliente. Por ejemplo, puedes incluir una columna de claves CustomerId
en tus tablas. Si haces que CustomerId
sea la primera columna de clave, los datos de cada cliente tendrán una buena localidad. Spanner puede usar de manera eficaz divisiones de bases de datos para maximizar el rendimiento según el tamaño de los datos y los patrones de carga. En el siguiente ejemplo, hay una sola tabla Singers
para todos los clientes:
CustomerId | SingerId | FirstName | LastName |
---|---|---|---|
1 | 1 | Marc | Richards |
1 | 2 | Catalina | Smith |
2 | 1 | Alicia | Trentor |
2 | 2 | Gabriel | Wright |
3 | 1 | Benjamín | Martínez |
3 | 2 | Hannah | Harris |
Si debes tener bases de datos separadas para cada usuario, debes tener en cuenta las siguientes restricciones:
- Existen límites para la cantidad de bases de datos por instancia y la cantidad de índices y tablas por base de datos. Según la cantidad de clientes, es posible que no sea posible tener bases de datos o tablas separadas.
- Agregar índices no intercalados y tablas nuevas puede llevar mucho tiempo. Es posible que no puedas obtener el rendimiento que deseas si el diseño del esquema depende de agregar índices y tablas nuevos.
Si quieres crear bases de datos independientes, podrías tener más éxito si distribuyes las tablas en bases de datos de manera que cada base tenga una cantidad baja de cambios de esquema por semana.
Si creas índices y tablas diferentes para cada cliente de tu aplicación, no coloques todos los índices y tablas en la misma base de datos. En su lugar, divídelos en muchas bases de datos para mitigar los problemas de rendimiento con la creación de una gran cantidad de índices.
Si deseas obtener más información sobre otros patrones de administración de datos y el diseño de aplicaciones para multiusuarios, consulta Implementa multiusuarios en Spanner.