Ampliación de Spanner Change Watcher

Google Cloud Spanner Change Watcher es una biblioteca de código abierto para Google Cloud Spanner para observar y publicar cambios desde una base de datos de Cloud Spanner. La biblioteca admite diferentes implementaciones estándar para observar cambios en las tablas. Los ejemplos de este artículo requieren la versión 1.1.0 o superior de Spanner Change Watcher.

Uno de los aspectos más importantes al utilizar Spanner Change Watcher es cómo mantener las consultas que vigilan la (s) tabla (s) en busca de cambios lo más eficientes posible. Agregar un índice secundario para la columna de marca de tiempo de confirmación es una de las opciones más utilizadas. Esto tiene el inconveniente de que puede convertirse en un punto de acceso de escritura, ya que los valores del índice aumentarán monótonamente. Agregar una columna de fragmentos al índice secundario es una estrategia común para mitigar este inconveniente.

Sin embargo, agregar una columna de fragmentos a una base de datos existente que ya está siendo utilizada por una o más aplicaciones puede ser un desafío, ya que también podría requerir modificaciones en las aplicaciones existentes. Este artículo le mostrará cómo podría:

  1. Agregue una columna de fragmentación a una base de datos existente sin la necesidad de modificar ninguna aplicación existente.
  2. Agregue un índice secundario y utilícelo para consultar de manera eficiente la tabla en busca de modificaciones de datos.
  3. Agregue una columna procesada (booleana) opcional que indique si una fila se ha procesado o no, y use esta columna para eliminar filas del índice secundario cuando se hayan procesado las filas.

Agregar una columna de fragmentos calculada

Este ejemplo asume que su base de datos aún no contiene una columna de fragmentos lógicos. Si es así, también puede usar esa columna en lugar de agregar una columna de fragmentos calculada.

Una buena columna de fragmentos debe contener un valor que garantice que las escrituras se distribuyan de manera uniforme en varios servidores de Cloud Spanner . La cantidad de valores distintos en la columna de fragmentos debe ser al menos tantos como servidores hay en su instancia de Cloud Spanner . No se recomiendan demasiados valores distintos en la columna de fragmentos, ya que la forma más eficaz de sondear los cambios es incluir todos los valores de fragmentos posibles en las consultas de sondeo o configurar un observador independiente para cada fragmento.

Por tanto, el módulo de un valor hash es un buen valor para una columna de fragmentos. Cloud Spanner admite varias funciones de hash . Este ejemplo usa la función FARM_FINGERPRINT , pero cualquiera de las funciones hash disponibles funcionaría. El número de valores distintos se reduce luego a 37 tomando el módulo 19 del hash (el rango de valores de fragmentos es, por lo tanto, [-18, 18]).

Supongamos que nuestra base de datos ya contiene la siguiente tabla:

La siguiente columna de fragmentos calculados se puede agregar a la definición de la tabla sin la necesidad de modificar ninguna aplicación existente:

Cloud Spanner completará automáticamente esta columna de fragmentos para cada fila de la tabla sin la necesidad de cambiar las mutaciones existentes o las declaraciones DML que generan las aplicaciones existentes. Este ejemplo calcula el fragmento mediante el hash de dos columnas de datos, pero puede usar cualquier combinación de columnas para calcular un fragmento siempre que la entrada sea lo suficientemente diferente para que la mayoría de las filas generen valores de hash aleatorios.

Agregar un índice secundario (filtrado nulo)

Ahora podemos agregar un índice secundario para el fragmento y confirmar columnas de marca de tiempo sin tener que preocuparnos por crear un punto de acceso . Crearemos el índice secundario como un índice filtrado por nulos. Esto nos permitirá eliminar automáticamente las filas del índice secundario que ya no son necesarias, lo que puede mantener el índice secundario pequeño para las tablas que reciben solo o en su mayoría inserciones y ninguna o pocas actualizaciones.

Si ShardId o LastModified son nulos para una fila determinada, esa fila no se incluirá en el índice secundario. ShardId siempre será no nulo en este ejemplo, ya que es un valor hash calculado del nombre completo de un Singer. Sin embargo, podríamos optar por permitir que una tarea en segundo plano o Cloud Function establezca LastModified en nulo para las filas que no se han actualizado recientemente. Esto eliminará automáticamente esas filas del índice secundario y mantendrá el índice más pequeño.

Esto es especialmente eficaz para tablas en las que la mayoría de las mutaciones son inserciones. La tabla de datos seguirá creciendo, mientras que el índice se puede mantener pequeño eliminando las entradas que se insertaron hace más de X sin necesidad de eliminar los datos reales.

Usar el índice en el supervisor de cambios de llave inglesa

La definición de nuestra tabla e índice de Singers ahora es como se muestra en el siguiente script:

Ahora configuraremos un observador de tablas en Spanner Change Watcher que monitoreará esta tabla en busca de cambios y obligará al observador a usar el índice secundario que hemos creado. Esto se hace estableciendo dos valores en el observador:

  1. Establecer un ShardProvider: un ShardProvider se puede usar para ver solo una parte específica (fragmento) de la tabla. En este caso, usaremos F ixedShardProvider que agregará una cláusula WHERE ShardId IS NOT NULL AND ShardId IN (...) a cada consulta de sondeo. En este ejemplo, configuraremos el proveedor de fragmentos para que observe todos los valores de fragmentos posibles, por lo que la lista de valores será igual a todos los enteros entre -18 y 18 inclusive. Puede parecer extraño crear un ShardProvider que incluya todos los posibles valores de fragmentos, pero esto en realidad ayuda a generar una consulta de sondeo de buen rendimiento para Cloud Spanner.
  2. Establecer una sugerencia de tabla: se puede usar una sugerencia de tabla para obligar a Cloud Spanner a usar un índice secundario específico para la consulta.

El observador de la tabla anterior sondeará la tabla Singers en busca de cambios utilizando el índice secundario en ShardId y LastModified. La consulta de sondeo incluirá la siguiente cláusula WHERE:

DONDE ShardId NO ES NULL Y ShardId IN (-18, -17, ..., 17, 18) Y LastModified> @lastCommitTimestamp.

Esta cláusula WHERE implica que tanto ShardId como LastModified no deben ser nulos, por lo que también sabemos que podemos usar nuestro índice filtrado por nulos.

Eliminar entradas del índice secundario

Podemos mantener pequeño nuestro índice secundario que utiliza el observador de la tabla eliminando regularmente las entradas del índice que ya no necesitamos. Esto se hace estableciendo el valor de LastModified en nulo para las filas que no se han actualizado durante más de un cierto período de tiempo. Es importante que hagamos esto solo cuando sepamos que estas filas no serán actualizadas por algún otro proceso al mismo tiempo, ya que eso podría hacer que las actualizaciones transaccionales de estas filas no sean recogidas por el observador de cambios.

La secuencia de comandos de ejemplo a continuación establecerá LastModified en nulo para todas las filas que no se hayan actualizado en las últimas 24 horas. Esta secuencia de comandos debe ejecutarse como DML particionada para garantizar que no exceda los límites de transacciones de Cloud Spanner.

Utilice una columna procesada

El ejemplo anterior supone que está bien establecer LastModified en nulo para las filas que no se han actualizado durante un tiempo. Si desea mantener el valor LastModified para las filas más antiguas, puede introducir una columna procesada adicional que indique si se ha procesado una actualización y ya no es necesario que el observador de la tabla la considere. Esta columna procesada se puede utilizar para establecer automáticamente la columna ShardId en nula cuando la fila se ha procesado. Como un índice filtrado por nulos solo contendrá entradas donde todas las columnas incluidas no sean nulas, no importa si establecemos la columna ShardId o la columna de marca de tiempo de confirmación en nula. Ambos harán que la fila se elimine del índice secundario.

La siguiente tabla y la definición de índice crearán una tabla que calcula automáticamente un ShardId para las filas que no se han marcado como procesadas. Por lo tanto, el índice solo contendrá las filas que no se hayan marcado como procesadas.

La tabla anterior y la definición de índice se pueden utilizar en combinación con FixedShardProvider. Sin embargo, requiere que las declaraciones de actualización existentes se modifiquen para actualizar también la columna Procesado a FALSO (o nulo). No es necesario modificar las instrucciones de inserción existentes, ya que insertarán un valor nulo en la columna Procesado. La cláusula CASE WHEN Processed tratará tanto a null como a FALSE como valores falsos y establecerá el ShardId en no nulo.

Configurar un supervisor de cambios

El observador de cambios se configura exactamente de la misma manera para esta tabla que para la versión que no incluía la columna Procesado.

El observador de la tabla no considerará ninguna fila en la que ShardId sea nulo y lo hará de manera eficiente, ya que puede usar el índice secundario que solo contiene las filas relevantes.

Eliminar entradas del índice secundario

Eliminar entradas del índice secundario que ya no son necesarias es ligeramente diferente al ejemplo anterior. Las entradas ahora se eliminan estableciendo la columna Procesado en verdadero. Esto actualizará automáticamente la columna ShardId a nula. La columna LastModified permanecerá intacta.

Aplicación de referencia

El directorio de muestras de Cloud Spanner Change Watcher también incluye una pequeña aplicación de referencia que se puede usar para probar y comparar diferentes configuraciones. La configuración predeterminada de la aplicación de referencia es la configuración base recomendada para los observadores de cambios que deben procesar un alto rendimiento de escritura y es igual al ejemplo de este artículo. Esta configuración puede manejar aproximadamente entre 3000 y 5000 mutaciones por segundo por tabla en una instancia de Cloud Spanner de un solo nodo.

Puede cambiar los parámetros de entrada de la aplicación comparativa para probar diferentes configuraciones en su propia instancia de Cloud Spanner. Se puede encontrar más información sobre la aplicación Benchmark en este artículo .

Conclusión

La creación de un índice secundario en una columna de marca de tiempo de confirmación de una tabla que recibe una gran cantidad de operaciones de escritura puede crear un punto de acceso . Agregar una columna de fragmentos como la primera columna del índice puede mitigar eso. Si su base de datos no contiene una columna de fragmentos lógicos, puede agregar una como una columna calculada que calcula un valor hash de una o más de las columnas de datos en la tabla. Cloud Spanner completará automáticamente las columnas calculadas, lo que significa que no es necesario que modifique ninguna de sus aplicaciones existentes para poder usar dicha columna.

La creación de un índice secundario en el fragmento y las columnas de marca de tiempo de confirmación como índice filtrado por nulos también habilita la posibilidad de eliminar las entradas antiguas que ya no son necesarias del índice secundario estableciendo uno de los valores del índice en nulo. Los ejemplos anteriores muestran cómo se puede lograr estableciendo el valor de la marca de tiempo de confirmación en nulo.

Sin embargo, si desea conservar los valores de la marca de tiempo de confirmación para las entradas antiguas, también puede introducir una columna Procesada separada que indique si la fila se ha procesado o no. Esta columna se puede usar para establecer automáticamente la columna de fragmentos calculada en nula, lo que también elimina automáticamente la fila del índice secundario.


Scaling up Spanner Change Watcher se publicó originalmente en Google Cloud - Community on Medium, donde las personas continúan la conversación destacando y respondiendo a esta historia.