En este documento, se describen las prácticas recomendadas para actualizar esquemas.
Procedimientos previos a la actualización del esquema
Antes de emitir una actualización del esquema, realiza los siguientes pasos:
Asegúrate de que todos los datos existentes en la base de datos cumplan con las restricciones que introduce la actualización del esquema. Dado que algunas actualizaciones del esquema dependen de los datos reales, no solo del esquema actual, una actualización correcta en una base de datos de prueba no garantiza el éxito en una base de datos de producción. A continuación, hay algunos ejemplos comunes:
- Si agregas una anotación
NOT NULLa una columna existente, comprueba que la columna no contenga ningún valorNULLexistente. - Si acortas la longitud permitida de una columna
STRINGoBYTES, verifica que todos los valores existentes en esa columna cumplan con la restricción de longitud.
- Si agregas una anotación
Si escribes en una columna, tabla o índice que experimenta una actualización del esquema, asegúrate de que los valores que escribes cumplan con las restricciones nuevas.
Si eliminas una columna, una tabla o un índice, asegúrate de no estar escribiendo datos allí ni leyéndolos.
Limita la frecuencia de las actualizaciones del esquema.
Si realizas demasiadas actualizaciones del esquema en un período breve, es posible que Spanner throttle el procesamiento de las actualizaciones del esquema en cola. Esto se debe a que Spanner limita la cantidad de espacio para almacenar versiones de esquema. Es posible que se limite la actualización del esquema si hay demasiadas versiones de esquema antiguas dentro del período de retención. La tasa máxima de cambios de esquema depende de muchos factores, uno de los cuales es la cantidad total de columnas de la base de datos. Por ejemplo, una base de datos con 2,000 columnas (aproximadamente 2,000 filas en INFORMATION_SCHEMA.COLUMNS) puede realizar como máximo 1,500 cambios de esquema (menos si el cambio de esquema requiere varias versiones) dentro del período de retención. Para ver el estado de las actualizaciones de esquema en curso, usa el comando gcloud spanner operations
list y filtra por operaciones de tipo DATABASE_UPDATE_DDL. Para cancelar una actualización de esquema en curso, usa el comando gcloud spanner operations
cancel y especifica el ID de operación.
La forma en que se agrupan tus declaraciones de DDL y su orden dentro de cada lote pueden afectar la cantidad de versiones de esquema resultantes. Para maximizar la cantidad de actualizaciones de esquema que puedes realizar en un período determinado, debes usar el procesamiento por lotes que minimice la cantidad de versiones del esquema. Algunos lineamientos se describen en actualizaciones grandes.
Como se describe en versiones de esquema, algunas sentencias DDL crearán varias versiones de esquema, y estas son importantes cuando se considera el procesamiento por lotes y el orden dentro de cada lote. Existen dos tipos principales de declaraciones que pueden crear varias versiones del esquema:
- Son instrucciones que podrían necesitar reabastecer datos de índice, como
CREATE INDEX. - Instrucciones que fuerzan a Spanner a validar los datos existentes, como agregar restricciones de
NOT NULLo de longitud
Sin embargo, estos tipos de sentencias no siempre crean varias versiones del esquema. Spanner intentará detectar cuándo se pueden optimizar estos tipos de sentencias para evitar el uso de varias versiones del esquema, lo que depende del procesamiento por lotes. Por ejemplo, una sentencia CREATE INDEX que se produce en el mismo lote que una sentencia CREATE TABLE para la tabla base del índice, sin sentencias intermedias para otras tablas, puede evitar la necesidad de rellenar los datos del índice, ya que Spanner puede garantizar que la tabla base esté vacía en el momento en que se crea el índice. En la sección Actualizaciones grandes, se describe cómo usar esta propiedad para crear muchos índices de manera eficiente.
Si no puedes agrupar en lotes tus declaraciones DDL para evitar crear muchas versiones de esquema, debes limitar la cantidad de actualizaciones del esquema de una sola base de datos dentro de su período de retención. Aumenta el período en el que realizas actualizaciones del esquema para permitir que Spanner quite las versiones anteriores del esquema antes de que se creen versiones nuevas.
- Para algunos sistemas de administración de base de datos relacional, hay paquetes de software que realizan varias series de actualizaciones del esquema a una versión inferior o una posterior en la base de datos en cada implementación de producción. No se recomiendan estos tipos de procesos para Spanner.
- Spanner está optimizado para usar claves primarias en los datos de partición de las soluciones de multiusuario. Si utilizas una solución de multiusuario que usa tablas separadas para cada cliente, ten en cuenta que las actualizaciones del esquema en muchos clientes a la vez pueden generar una gran acumulación de operaciones de actualización del esquema que tardan mucho en completarse.
- Las actualizaciones del esquema que requieren validación o reabastecimiento de índice usan más recursos del servidor porque cada instrucción crea varias versiones del esquema de forma interna.
Orden de ejecución de las declaraciones en lotes
Si usas Google Cloud CLI, la API de REST o la API de RPC, puedes emitir un lote de una o más declaraciones CREATE, ALTER o DROP.
Spanner aplica las instrucciones del mismo lote en orden y se detiene en el primer error. Si aplicar una declaración da como resultado un error, esa declaración se revierte. Los resultados de las declaraciones aplicadas antes en el lote no se revierten. Esta aplicación de instrucciones en orden significa que, si deseas que las instrucciones que requieren un relleno inevitable se ejecuten en paralelo (como crear varios índices en tablas existentes grandes), debes enviar esas instrucciones en lotes separados, ya que cada relleno podría tardar mucho tiempo. Por otro lado, si creas una tabla nueva con índices, la práctica recomendada es colocarlos juntos (CREATE TABLE seguido de CREATE INDEX) en un solo lote para evitar el reabastecimiento por completo.
Spanner puede combinar y reordenar declaraciones de diferentes lotes, por lo que podría combinar declaraciones de diferentes lotes en un cambio atómico que se aplica a la base de datos. Dentro de cada cambio atómico, las declaraciones de diferentes lotes ocurren en un orden arbitrario. Por ejemplo, si un lote de instrucciones contiene ALTER TABLE table_name ALTER COLUMN column_name STRING(50) y otro lote contiene ALTER TABLE table_name ALTER COLUMN
column_name STRING(20), Spanner dejará esa columna en uno de esos dos estados, pero el estado en el que se deje no es determinístico.
Opciones para actualizaciones del esquema grandes
La mejor manera de crear una tabla y una gran cantidad de índices en esa tabla es crearlos todos al mismo tiempo, de modo que solo se cree una versión del esquema. Se recomienda crear los índices inmediatamente después de la tabla en la lista de instrucciones DDL. Puedes crear la tabla y sus índices cuando creas la base de datos o en un solo lote grande de instrucciones. Si necesitas crear muchas tablas, cada una con muchos índices, puedes incluir todas las instrucciones en un solo lote. Puedes incluir varios miles de instrucciones en un solo lote cuando todas las instrucciones se pueden ejecutar juntas con una sola versión del esquema.
Cuando una instrucción requiere reabastecer datos de índice o realizar la validación de datos, no se puede ejecutar en una sola versión del esquema. Esto sucede con las sentencias CREATE INDEX cuando ya existe la tabla base del índice (ya sea porque se creó en un lote anterior de sentencias DDL o porque hubo una sentencia en el lote entre las sentencias CREATE TABLE y CREATE INDEX que requirió varias versiones del esquema). Spanner requiere que no haya más de 10 de estas instrucciones en un solo lote. En particular, la creación de índices que requiere un reabastecimiento usa varias versiones de esquema por índice, por lo que es una buena regla general no crear más de 3 índices nuevos que requieran reabastecimiento por día (sin importar cómo se agrupen, a menos que la agrupación pueda evitar el reabastecimiento).
Por ejemplo, este lote de declaraciones usará una sola versión del esquema:
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), ) PRIMARY KEY (SingerId); CREATE INDEX SingersByFirstName ON Singers(FirstName); CREATE INDEX SingersByLastName ON Singers(LastName); CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId); CREATE INDEX AlbumsByTitle ON Albums(AlbumTitle);
En cambio, este lote usará muchas versiones de esquema, porque UnrelatedIndex requiere el reabastecimiento (ya que su tabla base ya debe existir) y que obliga a todos los siguientes índices a requerir el reabastecimiento (aunque están en el mismo lote que sus tablas base):
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, 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); CREATE INDEX UnrelatedIndex ON UnrelatedTable(UnrelatedIndexKey); CREATE INDEX SingersByFirstName ON Singers(FirstName); CREATE INDEX SingersByLastName ON Singers(LastName); CREATE INDEX AlbumsByTitle ON Albums(AlbumTitle);
Sería mejor mover la creación de UnrelatedIndex al final del lote o a un lote diferente para minimizar las versiones del esquema.
Espera a que se completen las solicitudes a la API
Cuando realices solicitudes projects.instances.databases.updateDdl (API de REST) o UpdateDatabaseDdl (API de RPC), usa projects.instances.databases.operations.get (API de REST) o GetOperation (API de RPC), respectivamente, para esperar a que se complete cada solicitud antes de iniciar una solicitud nueva. Esperar a que se complete cada solicitud permite que tu aplicación realice un seguimiento del progreso de las actualizaciones del esquema. También mantiene la acumulación de actualizaciones del esquema pendientes en un tamaño administrable.
Carga masiva
Cuando cargas datos de forma masiva en una tabla nueva, puedes crear índices secundarios antes o después de cargar los datos. La carga de datos es más rápida si creas índices después de la carga, pero esto significa que los índices deben propagarse.
Si primero cargas los datos y, luego, creas los índices, la transferencia de datos es más rápida porque solo se escribe en la tabla, y los reabastecimientos posteriores de índices pueden escribir los datos de índice en lotes optimizados que son más eficientes que escribir los datos de índice junto con los datos de la tabla. Sin embargo, el reabastecimiento de índices requiere varias versiones del esquema y tiene límites. Como se indica en Opciones para actualizaciones grandes, no debes crear más de 10 índices que requieran reabastecimiento en un solo lote, y lo mejor es no crear más de 3 índices de este tipo por día.
Como alternativa, puedes crear tablas e índices en el mismo lote, como se describe en opciones para actualizaciones grandes. Esto evita el reabastecimiento del índice, pero la carga masiva de datos será más lenta porque cada índice debe actualizarse a medida que se cargan los datos.
La mejor opción en una situación determinada depende de la cantidad de datos que se cargarán, las claves específicas de la tabla y el índice, la cantidad de índices que se necesitan y la frecuencia con la que se requerirán las operaciones de carga masiva en la misma base de datos. Una regla general es que es mejor crear los índices por separado si se debe cargar una gran cantidad de datos en cada tabla y solo se requieren unos pocos índices.