Descripción general de los esquemas

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 del esquema. También se introducen las tablas intercaladas, que pueden mejorar el rendimiento de las consultas cuando se consultan tablas en una relación principal-secundaria.

Un esquema es un espacio de nombres que contiene objetos de bases de datos, como tablas, vistas, índices y funciones. Usas esquemas 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.

También puedes segmentar y almacenar aún más las filas en tu tabla de base de datos en diferentes regiones geográficas. Para obtener más información, consulta la descripción general de la partición geográfica.

Datos con escritura sólida

Los datos en Spanner se escriben de forma sólida. 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 de forma única cada fila. Las claves primarias siempre se indexan para una búsqueda rápida de filas. Si quieres actualizar o borrar las filas existentes de 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 con dialecto de GoogleSQL pueden tener tablas sin clave primaria.

Es posible que tu aplicación ya tenga un campo que sirva naturalmente como clave primaria. Por ejemplo, para una tabla Customers, podría haber un CustomerId proporcionado por la aplicación que puede funcionar bien como clave primaria. En otros casos, es posible que debas generar una clave primaria cuando insertes la fila. Por lo general, se trata de un valor entero único sin importancia comercial (una clave primaria sustituta).

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. Esta opción es poco recomendable, ya que Spanner divide los datos entre servidores según los intervalos de claves. Esto significa que las inserciones se dirigirán a un solo servidor y se 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 usar un UUID de versión 4, ya que esta utiliza valores aleatorios en los bits de orden superior. No uses un algoritmo de 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 formas de definir relaciones de superior y 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 entre tablas principales y secundarias. Con la intercalación, Spanner coloca físicamente las filas secundarias junto con las filas superiores en el almacenamiento. La ubicación conjunta 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. De esta manera, 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 junto a una fila de Customers. Esta relación principal-secundaria se aplica cuando se intercala con la cláusula INTERLEAVE IN PARENT. Las tablas secundarias INTERLEAVE IN comparten las mismas características de intercalación física de filas, pero Spanner no aplica la integridad referencial entre la tabla principal y la secundaria.

Para asociar una tabla secundaria con una tabla principal, usa DDL que declare la tabla secundaria como intercalada en la tabla principal y que incluya la clave primaria de la tabla principal 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.

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 entre tablas primarias y secundarias como tablas intercaladas o como claves externas, pero no de ambas formas. Para obtener más información sobre las claves externas y su comparación con las 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 secundaria intercalada de otra, la tabla debe tener una clave primaria compuesta que incluya todos los componentes de la clave primaria de la tabla superior, en el mismo orden y, por lo general, una o más columnas adicionales de la tabla secundaria.

Spanner almacena las filas ordenadas por valores de clave primaria, con filas secundarias insertadas entre las filas superiores. Consulta una ilustración de filas intercaladas en Crea tablas intercaladas más adelante en esta página.

En resumen, Spanner puede ubicar físicamente juntas las filas de las tablas relacionadas. En los ejemplos de esquema, se muestra cómo se ve esta disposición física.

Divisiones de bases de datos

Puedes definir jerarquías de relaciones superiores y secundarias intercaladas de hasta siete capas de profundidad, lo que significa que puedes ubicar juntas las 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 administre tu base de datos. Pero ¿qué sucede cuando crecen las tablas relacionadas 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 la base de datos crece, 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 automáticamente límites de división según el tamaño y la carga, lo que cambia la cantidad de divisiones en la base de datos.

División basada en la carga

Veamos un ejemplo de cómo Spanner realiza la división basada en la carga para mitigar los hotspots de lectura. Supongamos que tienes una base de datos que contiene una tabla con 10 filas que se leen con más frecuencia que el resto. Spanner puede agregar límites de división entre cada una de esas 10 filas para que cada una sea controlada por un servidor diferente, 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 los hotspots de modo que la capacidad de procesamiento de lectura mejore cada pocos minutos hasta que satures los recursos de tu instancia o te encuentres con casos en los que no se puedan agregar nuevos límites de división (porque tienes una división que abarca solo una fila sin elementos secundarios intercalados).

Esquemas con nombre

Los esquemas con nombre te ayudan a organizar los datos similares. Esto te ayuda a encontrar objetos rápidamente en la consola de Google Cloud , aplicar privilegios y evitar colisiones de nombres.

Los esquemas con nombre, como otros objetos de base de datos, se administran con 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 objetos de la base de datos. Por ejemplo, podrías crear un esquema llamado warehouse para la unidad de negocios del almacén. Las tablas que usan este esquema podrían incluir: product, order y customer information. O bien, podrías crear un esquema llamado fulfillment para la unidad de negocios de cumplimiento. 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, es fulfillment.product. Esto evita confusiones en situaciones en las que varios objetos comparten el mismo nombre.

En el DDL de CREATE SCHEMA, los objetos de tabla reciben un FQN, por ejemplo, sales.customers, y un nombre corto, por ejemplo, sales.

Los siguientes objetos de la 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 Cómo administrar esquemas con nombre.

Usa el control de acceso detallado con esquemas con nombre

Los esquemas con nombre te permiten otorgar acceso a nivel del esquema a cada objeto del esquema. 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 la base de datos, como tablas, columnas y filas de la tabla.

Para obtener más información, consulta Cómo otorgar privilegios de control de acceso detallado a esquemas con nombre.

Ejemplos de esquemas

En los ejemplos de esquema de esta sección, se muestra cómo crear tablas principales y secundarias con y sin intercalación, y se ilustran los diseños físicos correspondientes de los datos.

Crea una tabla principal

Supongamos que quieres crear una aplicación de música y necesitas una tabla que almacene filas de datos de cantantes:

Tabla Singers con cinco filas y cuatro columnas

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 PRIMARY KEY,
FirstName  STRING(1024),
LastName   STRING(1024),
SingerInfo BYTES(MAX),
);

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 secundaria intercalada de otra tabla).
  • En el caso de las bases de datos con dialecto de GoogleSQL, las columnas de clave primaria suelen tener la anotación NOT NULL (aunque puedes omitirla si deseas permitir valores NULL en columnas de clave. Para obtener más información, consulta Columnas de clave.
  • 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 el tipo STRING o BYTES 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 tipos varchar y character varying de PostgreSQL. Para obtener más información, consulta Tipos de datos escalares para las bases de datos con dialecto de GoogleSQL y Tipos de datos de PostgreSQL para las bases de datos con dialecto 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 almacenadas por clave primaria (“Singers(1)” y, luego, “Singers(2)”, en donde el número entre paréntesis es el valor de la clave primaria).

Filas de ejemplo de una tabla almacenada en orden de clave primaria

En el diagrama anterior, se ilustra un ejemplo de límite de división entre las filas con las claves Singers(3) y Singers(4), en el que los datos de las divisiones resultantes se asignan a diferentes servidores. A medida que aumenta esta tabla, es posible que se almacenen en diferentes ubicaciones las filas de datos de Singers.

Crea tablas principales y secundarias

Supongamos que ahora quieres agregar algunos datos básicos sobre los álbumes de cada cantante a la aplicación de música.

Tabla Albums con cinco filas y tres columnas

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 PRIMARY KEY,
 FirstName  STRING(1024),
 LastName   STRING(1024),
 SingerInfo BYTES(MAX),
);

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)
);

La disposición física de las filas de Singers y Albums es similar al siguiente diagrama, con filas de la tabla Albums almacenadas por clave primaria contigua y, luego, filas de Singers almacenadas por clave primaria contigua:

Diseño físico de las filas

Un detalle 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 distinta a las de la tabla Singers, y ambas divisiones podrían moverse independientemente una de otra.

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 podría generar una penalización en el rendimiento debido a la necesidad de coordinar 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 las filas de las dos tablas juntas a lo largo de la dimensión de la clave primaria. En el siguiente ejemplo, se explica esta situación de forma más detallada.

Crea tablas intercaladas

Una tabla intercalada es una tabla que declaras como secundaria intercalada de otra tabla porque quieres que las filas de la tabla secundaria se almacenen físicamente con la fila superior asociada. Como se mencionó anteriormente, la clave primaria de la tabla principal debe ser la primera parte de la clave primaria compuesta de la tabla secundaria.

Después de intercalar una tabla, la acción es permanente. No puedes deshacer la intercalación. En su lugar, debes volver a crear la tabla y migrar los datos a ella.

Mientras diseñas tu aplicación de música, supongamos que te das cuenta de que la app necesita acceder con frecuencia a las filas de la tabla Albums cuando accede a una fila de 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 de localidad de datos sólida. 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 PRIMARY KEY,
 FirstName  STRING(1024),
 LastName   STRING(1024),
 SingerInfo BYTES(MAX),
 );

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 secundaria Albums, también es la clave primaria de su tabla principal Singers.
  • La anotación ON DELETE CASCADE indica que, cuando se borra una fila de la tabla principal, también se borran automáticamente sus filas secundarias. Si una tabla secundaria no la incluye (o si agregas la anotación ON DELETE NO ACTION), debes borrar manualmente las filas secundarias para borrar la fila superior.
  • Las filas intercaladas se ordenan primero según las filas de la tabla principal y, luego, según las filas contiguas de la tabla secundaria que comparten la clave primaria de la tabla principal. Por ejemplo, "Singers(1)", luego "Albums(1, 1)" y, luego, "Albums(1, 2)".
  • Si se divide esta base de datos, se conserva la relación de localidad de datos de cada cantante y los datos de sus álbumes, siempre y cuando el tamaño de una fila de Singers y todas sus filas de Albums permanezcan por debajo del límite de tamaño de división y no haya ningún punto de acceso en ninguna de estas filas de Albums.
  • La fila superior debe existir antes de que puedas insertar filas secundarias. La fila principal puede ya existir en la base de datos o puede insertarse antes de la inserción de las filas secundarias en la misma transacción.

Las filas de Albums se intercalan entre las filas de Singers

Supongamos que deseas modelar Projects y su Resources como tablas intercaladas. En ciertos casos, podría ser útil INTERLEAVE IN, es decir, la capacidad de no requerir que exista la fila Projects para que existan las entidades que se encuentran debajo de ella (por ejemplo, se borró un proyecto, pero sus recursos deben limpiarse antes de borrarlo).

GoogleSQL

CREATE TABLE Projects (
  ProjectId   INT64 NOT NULL,
  ProjectName STRING(1024),
) PRIMARY KEY (ProjectId);

CREATE TABLE Resources (
  ProjectId    INT64 NOT NULL,
  ResourceId   INT64 NOT NULL,
  ResourceName STRING(1024),
) PRIMARY KEY (ProjectId, ResourceId),
  INTERLEAVE IN Projects;

PostgreSQL

CREATE TABLE Projects (
  ProjectId   BIGINT PRIMARY KEY,
  ProjectName VARCHAR(1024),
);

CREATE TABLE Resources (
  ProjectId    BIGINT,
  ResourceId   BIGINT,
  ResourceName VARCHAR(1024),
  PRIMARY KEY (ProjectId, ResourceId)
) INTERLEAVE IN Projects;

Ten en cuenta que, en este ejemplo, usamos la cláusula INTERLEAVE IN Projects en lugar de INTERLEAVE IN PARENT Projects. Esto indica que no aplicamos la relación principal-secundaria entre los proyectos y los recursos.

En este ejemplo, las filas Resources(1, 10) y Resources(1, 20) pueden existir en la base de datos incluso si la fila Projects(1) no existe. Projects(1) se puede borrar incluso si Resources(1, 10) y Resources(1, 20) aún existen, y el borrado no afecta estas filas de Resources.

Crea 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:

Tabla Songs con seis filas y cuatro columnas

Songs debe tener una clave primaria que incluya todas las claves primarias de las tablas que se encuentran en un nivel superior de 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 PRIMARY KEY,
 FirstName  STRING(1024),
 LastName   STRING(1024),
 SingerInfo BYTES(MAX),
);

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.

Los datos de Songs se intercalan entre los de Albums, que, a su vez, se intercalan entre los datos de Singers.

En este ejemplo, a medida que aumenta la cantidad de cantantes, Spanner agrega límites de división entre los cantantes para conservar la localidad de los datos entre un cantante y los datos de sus á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 si se detecta un hotspot en las filas secundarias, Spanner intentará agregar límites de división para aislar esa fila del hotspot junto con todas las filas secundarias debajo de ella.

En resumen, una tabla superior junto con todas sus tablas secundarias y descendientes forman una jerarquía de tablas en el esquema. Si bien cada tabla de la jerarquía es independiente en cuanto a lógica, intercalarlas físicamente de esta manera puede mejorar el rendimiento, ya que las tablas se unen de forma previa, lo que te permite acceder a filas relacionadas al mismo tiempo y minimizar los accesos al almacenamiento.

Uniones con tablas intercaladas

Si es posible, une los datos de las tablas intercaladas por clave primaria. Como cada fila intercalada suele almacenarse físicamente en la misma división que su fila principal, Spanner puede realizar uniones por clave primaria de forma local, lo que minimiza el acceso al almacenamiento y el tráfico de red. En el siguiente ejemplo, Singers y Albums se unen según 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;

Grupos locales

Spanner usa grupos de localidad para conservar las relaciones de localidad de los datos en las columnas de la tabla. Si no creas explícitamente ningún grupo de localidad para tus tablas, Spanner agrupa todas las columnas en el grupo de localidad default y almacena los datos de todas las tablas en el almacenamiento SSD. Puedes usar grupos de localidades para hacer lo siguiente:

  • Usa el almacenamiento por niveles. El almacenamiento por niveles es una función de almacenamiento completamente administrada que te permite elegir si deseas almacenar tus datos en unidades de estado sólido (SSD) o en unidades de disco duro (HDD). De forma predeterminada, sin usar el almacenamiento por niveles, Spanner almacena todos los datos en el almacenamiento SSD.

  • Usa el agrupamiento de columnas para almacenar las columnas especificadas por separado de otras columnas. Debido a que los datos de las columnas especificadas se almacenan por separado, leer datos de esas columnas es más rápido que si todos los datos se agruparan. Para usar el agrupamiento de columnas, debes crear un grupo de localidad sin especificar ninguna opción de almacenamiento por niveles. Spanner usa grupos de localidad para almacenar las columnas especificadas por separado. Si se especifica, las columnas heredan su política de almacenamiento por niveles de la tabla o del grupo de localidad predeterminado. Luego, usa la declaración DDL CREATE TABLE para establecer un grupo de localidad para las columnas especificadas o usa la declaración DDL ALTER TABLE para modificar el grupo de localidad que usa la columna de una tabla. La instrucción DDL determina las columnas que se almacenan en el grupo de localidad. Por último, puedes leer los datos de estas columnas de manera más eficiente.

Columnas de clave

En esta sección, se incluyen algunas notas sobre las columnas clave.

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.

Almacenar valores NULL en una clave primaria

En GoogleSQL, si deseas almacenar valores NULL en una columna de clave primaria, omite la cláusula NOT NULL para esa columna en el esquema. (Las bases de datos de dialecto 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, como SingerId es la clave primaria, solo puede haber una fila que almacene NULL en esa columna.

CREATE TABLE Singers (
  SingerId   INT64 PRIMARY KEY,
  FirstName  STRING(1024),
  LastName   STRING(1024),
);

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 la omite.

CREATE TABLE Singers (
  SingerId   INT64 PRIMARY KEY,
  FirstName  STRING(1024),
  LastName   STRING(1024),
);

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 de tipo ARRAY:

  • Las columnas de clave de una tabla
  • Las columnas de clave de un índice

Diseña para multiusuario

Si almacenas datos que pertenecen a diferentes clientes, te recomendamos que implementes la opción de multiusuario. Por ejemplo, es posible que un servicio de música requiera 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 independiente para cada cliente. En este ejemplo, cada base de datos tiene su propia tabla Singers:

Base de datos 1: Ackworth Records
SingerId FirstName LastName
1MarcRichards
2CatalinaSmith
Base de datos 2: Cama Records
SingerId FirstName LastName
1AliciaTrentor
2GabrielWright
Base de datos 3: Eagan Records
SingerId FirstName LastName
1BenjamínMartínez
2HannahHarris

Multiusuario administrado por esquema

Otra forma de diseñar para multiusuario 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, podrías incluir una columna de clave CustomerId en tus tablas. Si defines CustomerId como primera columna de clave, los datos de cada cliente tendrán una buena localidad. Luego, Spanner puede usar de manera eficaz las divisiones de bases de datos para maximizar el rendimiento en función del tamaño de los datos y los patrones de carga. En el siguiente ejemplo, hay una sola tabla Singers para todos los clientes:

Base de datos de multiusuario de Spanner
CustomerId SingerId FirstName LastName
11MarcRichards
12CatalinaSmith
21AliciaTrentor
22GabrielWright
31BenjamínMartínez
32HannahHarris

Existen restricciones que debes tener en cuenta en caso de que debas tener bases de datos separadas para cada usuario:

  • Existen límites para la cantidad de bases de datos por instancia y la cantidad de tablas e índices por base de datos. Según la cantidad de clientes, es posible que no puedas tener bases de datos ni tablas separadas.
  • Agregar nuevos índices no entrelazados y tablas puede tomar mucho tiempo. Es posible que no puedas conseguir el rendimiento que deseas si el diseño del esquema depende de agregar nuevos índices y tablas.

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 separadas para cada cliente de tu aplicación, no pongas todos los índices y tablas en la misma base de datos. En su lugar, repártelos en muchas bases de datos para mitigar los problemas de rendimiento que se producen por la creación de una gran cantidad de índices.

Para obtener más información sobre otros patrones de administración de datos y diseño de aplicaciones para la arquitectura multiusuario, consulta Implementación de arquitectura multiusuario en Spanner.