Tutorial de CockroachDB
PROFESOR/A
Jordi Conesa i Caralt
Esta publicación está bajo licencia
Creative Commons Reconocimiento, Nocomercial,
Compartirigual, (by-nc-sa). Usted puede usar, copiar y difundir
este documento o parte del mismo siempre y cuando se
mencione su origen, no se use de forma comercial y no se
2
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Índice
Introducción 3
1- Descarga del ejecutable y creación de una BD 4
2- Creación de un Clúster en CockroachDB 5
3- Configurando la BD como multiregión 10
4- Cambiando el objetivo de supervivencia 16
5- Ejecutando una follower read 18
6- Creando una tabla global 20
7- Fragmentando los datos by row 22
3
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Introducción
CockroachDB, además de la posibilidad de crear un clúster virtual a partir del comando demo,
también ofrece algunas bases de datos de prueba, especialmente adecuadas para explorar
las características vistas en la teoría. Una de estas bases de datos de prueba es la base de
datos denominada Movr. El conjunto de tablas e índices pertenecientes a esta base de datos
hace referencia a una empresa ficticia de uso de vehículos compartidos, donde encontramos
representados usuarios, sus vehículos, sus trayectos e incluso códigos promocionales, como
veremos posteriormente.
En este tutorial mostraremos cómo crear un clúster multiregión en CockroachDB y cómo crear
distintas configuraciones del mismo mediante una base de datos de ejemplo. Para ello
utilizaremos la base de datos Movr.
A continuación podemos observar parte del esquema conceptual de la base de datos Movr
(ver [Link]
4
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
1- Descarga del ejecutable y creación de una BD
Antes de nada, deberemos instalar el intérprete de comandos de CockroachDB (ver
[Link] Los encontraréis tanto
para Linux, como para sistemas Windows y Macintosh. Una vez instalados los binarios,
podemos seguir con los siguientes pasos.
CockroachDB permite cargar una base de datos de prueba para realizar algunas operaciones.
A continuación vamos a abrir el intérprete de comandos con un fragmento de la base de datos
movr. Para ello iremos donde se ha instalado el ejecutable de CockroachDB y ejecutaremos
el siguiente comando:
cockroach demo movr
A partir de ahí nos aparecerá el símbolo del sistema. Ahí podremos acceder a la base de
datos movr y visualizar sus vehículos:
USE movr;
SELECT * FROM vehicles;
Podeís ver que las consultas se realizan utilizando la síntaxis estándar SQL. Por ejemplo la
siguiente consulta obtendría los vehículos que están en Amsterdam.
SELECT id, city, status FROM vehicles WHERE city='amsterdam';
id | city | status
---------------------------------------+-----------+------------
aaaaaaaa-aaaa-4800-8000-00000000000a | amsterdam | available
bbbbbbbb-bbbb-4800-8000-00000000000b | amsterdam | available
(2 rows)
En este tutorial no nos centraremos en la sintaxis de SQL sino en analizar cómo configurar de
distintas formas un clúster de CockroachDB. Antes de continuar cerraremos la sesión actual
con un \q
5
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
2- Creación de un Clúster en CockroachDB
Para poder desplegar un clúster con la base de datos de ejemplo utilizaremos el mismo
comando, pero con el parámetro “–nodes 9” para indicar que queremos crear un clúster de 9
nodos.
cockroach demo movr –-nodes 9
Este comando creará un clúster de 9 nodos, nos creará la base de datos de ejemplo (movr) y
nos conectará a uno de los nodos.
Es importante señalar que este clúster y esta base de datos son efímeros y que al salir
de la consola SQL inicial desaparecerá.
2.1 Familiarizándonos con el clúster
Para ver los 9 nodos y los parámetros de conexión a los mismos, podemos ejecutar el
siguiente comando:
\demo ls
El resultado será parecido al siguiente:
Como podéis ver, para cada nodo, CockorachDB proporciona la siguiente información:
• (webui): provee la URL de administración del clúster a través de una aplicación web
instalada en los nodos.
• (cli): provee el comando de conexión al clúster utilizando el cliente que proporciona
CockroachDB.
6
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Vemos que ofrece la ruta del certificado de conexión, el usuario demo y el nombre de
la base de datos movr. Un buen ejercicio de inspección del clúster es, desde otro
terminal, conectarse a otro nodo del clúster. No se ejecuta desde la consola SQL de
entrada al clúster, sino desde un nuevo terminal, una vez se haya instanciado el
clúster.
cockroach sql --certs-dir=/Users/jconesa/.cockroach-demo -u demo
-p 26258 -d movr
• (sql): provee la ruta de conexión a la base de datos por uno de los nodos
(habitualmente el del puerto más bajo, y al cual se conecta la consola inicial). Un
ejercicio interesante es conectarse a otro nodo (por ejemplo de otra región), desde
algún cliente (ya sea gráfico, DBeaver, o terminal).
2.2 Familiarizándonos con la base de datos y su distribución
Al igual que en PostgreSQL podemos hacer un SHOW DATABASES para mostrar las bases
de datos disponibles:
demo@[Link]:26257/movr> SHOW DATABASES;
database_name | owner | primary_region | secondary_region | regions | survival_goal
----------------+-------+----------------+------------------+---------+----------------
defaultdb | root | NULL | NULL | {} | NULL
movr | demo | NULL | NULL | {} | NULL
postgres | root | NULL | NULL | {} | NULL
system | node | NULL | NULL | {} | NULL
(4 rows)
Fijaos en los campos primary_region, secondary_region, regions y survival_goal. Estos
campos indicaran las regiones de cada base de datos y su objetivo de supervivencia.
Si ejecutamos SHOW TABLES podremos ver las tablas de la base de datos de ejemplo:
demo@[Link]:26257/movr> SHOW TABLES;
schema_name | table_name | type | owner | estimated_row_count | locality
--------------+----------------------------+-------+-------+---------------------+-----------
public | promo_codes | table | demo | 1000 | NULL
public | rides | table | demo | 500 | NULL
public | user_promo_codes | table | demo | 5 | NULL
public | users | table | demo | 50 | NULL
7
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
public | vehicle_location_histories | table | demo | 1000 | NULL
public | vehicles | table | demo | 15 | NULL
(6 rows)
En este caso aparece un campo (locality) que también será relevante para configurar el
clúster, ya que nos permitirá indicar cual es la localidad de cada una de las tablas.
Para poder ver las regiones y zonas disponibles para el clúster desplegado podemos ejecutar
la siguiente consulta:
demo@[Link]:26257/movr> SHOW REGIONS;
region | zones | database_names | primary_region_of | secondary_region_of
---------------+---------+----------------+-------------------+----------------------
europe-west1 | {b,c,d} | {} | {} | {}
us-east1 | {b,c,d} | {} | {} | {}
us-west1 | {a,b,c} | {} | {} | {}
(3 rows)
Podemos ver que la base de datos utiliza 3 regiones (europe-west1, us-east1 y us-west1).
Fijaos que no hay ninguna base de datos asignada a cada una de las regiones.
También se puede preguntar sobre las regiones asociadas a una base de datos concreta
mediante la siguiente sentencia:
SHOW REGIONS FROM DATABASE movr;
Si la ejecutáis veréis que se devuelve un valor nulo ya que no hay ninguna región asociada a
la base de datos.
8
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Otros elementos para consultar sobre la base de datos son el objetivo de supervivencia y los
metadatos de una de las tablas de la base de datos. Veamos algunos ejemplos:
demo@[Link]:26257/movr> SHOW SURVIVAL GOAL FROM DATABASE movr;
database | survival_goal
-----------+----------------
movr | NULL
(1 row)
demo@[Link]:26257/movr> SHOW ZONE CONFIGURATION FOR TABLE rides;
target | raw_config_sql
----------------+-------------------------------------------
RANGE default | ALTER RANGE default CONFIGURE ZONE USING
| range_min_bytes = 134217728,
| range_max_bytes = 536870912,
| [Link] = 14400,
| num_replicas = 3,
| constraints = '[]',
| lease_preferences = '[]'
(1 row)
Como podemos ver la base de datos aún no tiene asignado ningún objetivo de supervivencia.
Por otro lado, los metadatos de la tabla RIDES proporcionan la siguiente información de
interés:
• El tamaño mínimo de un fragmento es de 128 MB,
• El tamaño máximo de un fragmento es de 512 MB,
• El número de réplicas es de 3, y
• No hay ninguna preferencia de líder para las réplicas de la tabla.
Con la información obtenida, podemos tener una idea de algunos de los metadatos de la base
de datos, pero ¿Cómo está geoparticionada la base de datos? Para saberlo podemos ejecutar
la sentencia SHOW RANGES, que nos mostrara los fragmentos de una tabla. Como va a
devolver mucha información, proponemos que utilicéis el comando \x antes de ejecutar la
consulta para facilitar su visualización. A continuación, mostramos un fragmento del resultado
de aplicar esta sentencia para la tabla RIDES.
demo@[Link]:26257/movr> \x
demo@[Link]:26257/movr> SHOW RANGES FROM TABLE rides with DETAILS;
-[ RECORD 1 ]
start_key | <before:/Table/107/1/"washington
dc"/"DDDDDDD\x00\x80\x00\x00\x00\x00\x00\x00\x04">
9
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
end_key |
…/1/"amsterdam"/"\xc5\x1e\xb8Q\xeb\x85@\x00\x80\x00\x00\x00\x00\x00\x01\x81"
range_id | 121
range_size_mb | 0.010289000000000000000
lease_holder | 4
lease_holder_locality | region=us-west1,az=a
replicas | {3,4,7}
replica_localities | {"region=us-east1,az=d","region=us-west1,az=a","region=europe-
west1,az=b"}
voting_replicas | {3,7,4}
non_voting_replicas | {}
learner_replicas | {}
split_enforced_until | 2262-04-11 23:47:16.854776
range_size | 10289
span_stats | {"approximate_disk_bytes": 71795, "intent_bytes": 0, "intent_count":
0, "key_bytes": 3961, "key_count": 67, "live_bytes": 10289, "live_count": 67, "sys_bytes":
1921, "sys_count": 7, "val_bytes": 6328, "val_count": 67}
-[ RECORD 2 ]
start_key |
…/1/"amsterdam"/"\xc5\x1e\xb8Q\xeb\x85@\x00\x80\x00\x00\x00\x00\x00\x01\x81"
end_key | …/1/"boston"/"8Q\xeb\x85\x1e\xb8B\x00\x80\x00\x00\x00\x00\x00\x00n"
range_id | 138
range_size_mb | 0.0097370000000000000000
lease_holder | 7
lease_holder_locality | region=europe-west1,az=b
replicas | {3,4,7}
replica_localities | {"region=us-east1,az=d","region=us-west1,az=a","region=europe-
west1,az=b"}
voting_replicas | {3,7,4}
non_voting_replicas | {}
learner_replicas | {}
split_enforced_until | 2262-04-11 23:47:16.854776
range_size | 9737
span_stats | {"approximate_disk_bytes": 55289, "intent_bytes": 0, "intent_count":
0, "key_bytes": 2966, "key_count": 58, "live_bytes": 9737, "live_count": 58, "sys_bytes": 485,
"sys_count": 6, "val_bytes": 6771, "val_count": 58}
A partir de la información obtenida, puede verse que los fragmentos están siempre replicados
3 veces (recordad que es el factor de replicación por defecto), y cada una de ellas se
encuentra situada en nodos de una región diferente. También se muestra la réplica líder (o
leaseholder). El atributo lease_holder_locality indica la región y la zona de disponibilidad de
la réplica líder para ese fragmento.
10
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
3- Configurando la BD como multiregión
Una base de datos sin una región primaria asignada, como es el caso de movr hasta el
momento, aunque distribuya los datos en diversas regiones, no puede ser considerada como
multirregional dado que no podemos asignar objetivos de supervivencia (survival goal) ni a
nivel de zona, ni al nivel de región.
A continuación, asignaremos una región primaria a movr utilizando la siguiente sentencia:
ALTER DATABASE movr PRIMARY REGION "us-east1";
Fijaos como cambia automáticamente su configuración. Mediante el comando SHOW
REGIONS podemos ver que la zona us-east1 es ahora la zona primaria de la base de datos
movr.
demo@[Link]:26257/movr> SHOW REGIONS;
region | zones | database_names | primary_region_of | secondary_region_of
---------------+---------+----------------+-------------------+----------------------
europe-west1 | {b,c,d} | {} | {} | {}
us-east1 | {b,c,d} | {movr} | {movr} | {}
us-west1 | {a,b,c} | {} | {} | {}
(3 rows)
Mediante el siguiente comando podemos ver, de forma distinta, que la base de datos movr
sólo está desplegada en la zona us-east1.
demo@[Link]:26257/movr> SHOW REGIONS FROM DATABASE movr;
database | region | primary | secondary | zones
-----------+----------+---------+-----------+----------
movr | us-east1 | t | f | {b,c,d}
(1 row)
Y dado que ahora movr es multirregión, podemos comprobar cómo, ahora ya tiene un objetivo
de supervivencia de zona:
demo@[Link]:26257/movr> SHOW SURVIVAL GOAL FROM DATABASE movr;
database | survival_goal
-----------+----------------
movr | zone
11
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Por lo tanto, todos los fragmentos se encuentran en la región primaria (us-east1). Lo mismo
sucede con el resto de tablas de la base de datos.
Otra forma de confirmar esta modificación en la distribución geográfica de los rangos es
consultando la configuración de zonas de cada una de las tablas.
demo@[Link]:26257/movr> SHOW ZONE CONFIGURATION FOR TABLE rides;
target | raw_config_sql
----------------+-------------------------------------------------
DATABASE movr | ALTER DATABASE movr CONFIGURE ZONE USING
| range_min_bytes = 134217728,
| range_max_bytes = 536870912,
| [Link] = 14400,
| num_replicas = 3,
| num_voters = 3,
| constraints = '{+region=us-east1: 1}',
| voter_constraints = '[+region=us-east1]',
| lease_preferences = '[[+region=us-east1]]'
(1 row)
Fijaos que ahora se indica que, preferiblemente, las réplicas de tipo líder deberán ubicarse en
la región principal.
A continuación, vamos a ver, cómo se distribuyen las réplicas líderes (o leaseholders) de los
distintos fragmentos. Mostramos los dos primeros fragmentos por simplicidad.
demo@[Link]:26257/movr> SHOW RANGES FROM TABLE rides with DETAILS;
-[ RECORD 1 ]
start_key | <before:/Table/107/1/"washington
dc"/"DDDDDDD\x00\x80\x00\x00\x00\x00\x00\x00\x04">
end_key |
…/1/"amsterdam"/"\xc5\x1e\xb8Q\xeb\x85@\x00\x80\x00\x00\x00\x00\x00\x01\x81"
range_id | 121
range_size_mb | 0.010289000000000000000
lease_holder | 2
lease_holder_locality | region=us-east1,az=c
replicas | {1,2,3}
replica_localities | {"region=us-east1,az=b","region=us-east1,az=c","region=us-east1,az=d"}
voting_replicas | {3,1,2}
non_voting_replicas | {}
learner_replicas | {}
split_enforced_until | 2262-04-11 23:47:16.854776
range_size | 10289
12
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
span_stats | {"approximate_disk_bytes": 70801, "intent_bytes": 0, "intent_count":
0, "key_bytes": 3961, "key_count": 67, "live_bytes": 10289, "live_count": 67, "sys_bytes":
3245, "sys_count": 7, "val_bytes": 6328, "val_count": 67}
-[ RECORD 2 ]
start_key |
…/1/"amsterdam"/"\xc5\x1e\xb8Q\xeb\x85@\x00\x80\x00\x00\x00\x00\x00\x01\x81"
end_key | …/1/"boston"/"8Q\xeb\x85\x1e\xb8B\x00\x80\x00\x00\x00\x00\x00\x00n"
range_id | 138
range_size_mb | 0.0097370000000000000000
lease_holder | 1
lease_holder_locality | region=us-east1,az=b
replicas | {1,2,3}
replica_localities | {"region=us-east1,az=b","region=us-east1,az=c","region=us-east1,az=d"}
voting_replicas | {3,1,2}
non_voting_replicas | {}
learner_replicas | {}
split_enforced_until | 2262-04-11 23:47:16.854776
range_size | 9737
span_stats | {"approximate_disk_bytes": 139327, "intent_bytes": 0, "intent_count":
0, "key_bytes": 2966, "key_count": 58, "live_bytes": 9737, "live_count": 58, "sys_bytes":
1822, "sys_count": 7, "val_bytes": 6771, "val_count": 58}
Como podemos ver, ahora todos los leaseholders se encuentran distribuidos en nodos
correspondientes a las 3 zonas de disponibilidad de us-east1. Recordemos que cada zona de
disponibilidad de nuestro clúster está compuesta por un solo nodo. El leaseholder de cada
fragmento se encuentra en el nodo número 1, en el 2, o bien en el 3, que son nodos de la
misma región us-east1 (en diferentes zonas de disponibilidad). Por lo tanto, todas las lecturas
y escrituras se resolverán localmente y la eficiencia del sistema será muy alta.
3.1 Añadiendo nuevas regiones a la base de datos
Aunque el clúster gestionado por CockroachDB está formado por tres regiones (us-east1, us-
west1 y europe-west1), la base de datos está configurada para que utilice una sóla región, us-
east1, y con un objetivo de supervivencia de zona.
A continuación, vamos a añadir una nueva región a la base de datos y veremos como quedan
configuradas las regiones para movr:
ALTER DATABASE movr ADD REGION "us-west1";
demo@[Link]:26257/movr> SHOW REGIONS;
region | zones | database_names | primary_region_of | secondary_region_of
13
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
---------------+---------+----------------+-------------------+----------------------
europe-west1 | {b,c,d} | {} | {} | {}
us-east1 | {b,c,d} | {movr} | {movr} | {}
us-west1 | {a,b,c} | {movr} | {} | {}
(3 rows)
Podemos ver que se ha añadido us_west1 como región de la base de datos movr, no obstante,
esta región no está configurada ni como primaria, ni como secundaria. A continuación,
podemos definirla como región secundaria.
demo@[Link]:26257/movr> ALTER DATABASE movr SET SECONDARY REGION "us-west1";
ALTER DATABASE
Time: 156ms total (execution 152ms / network 4ms)
demo@[Link]:26257/movr> SHOW REGIONS;
region | zones | database_names | primary_region_of | secondary_region_of
---------------+---------+----------------+-------------------+----------------------
europe-west1 | {b,c,d} | {} | {} | {}
us-east1 | {b,c,d} | {movr} | {movr} | {}
us-west1 | {a,b,c} | {movr} | {} | {movr}
(3 rows)
El objetivo de supervivencia continúa siendo de zona, ya que no lo hemos modificado
explícitamente. Donde si que ha habido algún cambio es en el número de réplicas. Si
preguntamos sobre la configuración de tabla RIDES, por ejemplo, obtendremos la siguiente
información:
demo@[Link]:26257/movr> SHOW ZONE CONFIGURATION FOR TABLE rides;
target | raw_config_sql
--------------+---------------------------------------------------------------------
TABLE rides | ALTER TABLE rides CONFIGURE ZONE USING
| range_min_bytes = 134217728,
| range_max_bytes = 536870912,
| [Link] = 14400,
| num_replicas = 4,
| num_voters = 3,
| constraints = '{+region=us-east1: 1, +region=us-west1: 1}',
| voter_constraints = '[+region=us-east1]',
| lease_preferences = '[[+region=us-east1], [+region=us-west1]]'
(1 row)
14
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
El número de réplicas ha crecido, de 3 a 4 sin que lo hayamos pedido explícitamente. ¿Qué
creéis que ha pasado?
SOLUCIÓN OCULTA
(Para ver la solución debes permitir los caracteres ocultos )
Al añadir una nueva región a la base de datos, esta crea una nueva réplica (el factor de
replicación ahora es 4) y asigna esta nueva réplica a la nueva región. Fijaos que la región
preferida para las réplicas líder continua siendo la east1, y que la réplica de la zona west1 no
participa en la votación por mayorías (ver atributo voter_constraints). Por todo ello, la localidad
de datos continua proporcionando ventajas a las consultas locales en esa región. No obstante,
la nueva réplica podría usarse para hacer follower reads.
Analizar los metadatos de los fragmentos, puede seros de ayuda para comprender lo que ha
pasado.
demo@[Link]:26257/movr> SHOW RANGES FROM TABLE rides with DETAILS;
-[ RECORD 1 ]
start_key | <before:/Table/107/1/"washington
dc"/"DDDDDDD\x00\x80\x00\x00\x00\x00\x00\x00\x04">
end_key |
…/1/"amsterdam"/"\xc5\x1e\xb8Q\xeb\x85@\x00\x80\x00\x00\x00\x00\x00\x01\x81"
range_id | 113
range_size_mb | 0.010247000000000000000
lease_holder | 2
lease_holder_locality | region=us-east1,az=c
replicas | {1,2,3,6}
replica_localities | {"region=us-east1,az=b","region=us-east1,az=c","region=us-
east1,az=d","region=us-west1,az=c"}
voting_replicas | {3,2,1}
non_voting_replicas | {6}
learner_replicas | {}
split_enforced_until | 2262-04-11 23:47:16.854776
range_size | 10247
span_stats | {"approximate_disk_bytes": 94713, "intent_bytes": 0, "intent_count": 0,
"key_bytes": 3962, "key_count": 67, "live_bytes": 10247, "live_count": 67, "sys_bytes": 4737,
"sys_count": 7, "val_bytes": 6285, "val_count": 67}
-[ RECORD 2 ]
start_key |
…/1/"amsterdam"/"\xc5\x1e\xb8Q\xeb\x85@\x00\x80\x00\x00\x00\x00\x00\x01\x81"
end_key | …/1/"boston"/"8Q\xeb\x85\x1e\xb8B\x00\x80\x00\x00\x00\x00\x00\x00n"
range_id | 119
15
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
range_size_mb | 0.0097310000000000000000
lease_holder | 2
lease_holder_locality | region=us-east1,az=c
replicas | {1,2,3,5}
replica_localities | {"region=us-east1,az=b","region=us-east1,az=c","region=us-
east1,az=d","region=us-west1,az=b"}
voting_replicas | {3,1,2}
non_voting_replicas | {5}
learner_replicas | {}
split_enforced_until | 2262-04-11 23:47:16.854776
range_size | 9731
span_stats | {"approximate_disk_bytes": 77856, "intent_bytes": 0, "intent_count": 0,
"key_bytes": 2966, "key_count": 58, "live_bytes": 9731, "live_count": 58, "sys_bytes": 3358,
"sys_count": 8, "val_bytes": 6765, "val_count": 58}
A continuación, añade la última región europe_west1 a la base de datos.
SOLUCIÓN OCULTA
-- Añadimos la nueva región
ALTER DATABASE movr ADD REGION "europe-west1";
-- Comprobamos que la base de datos tiene acceso a las 3 regiones
demo@[Link]:26257/movr> show regions
-> ;
region | zones | database_names | primary_region_of | secondary_region_of
---------------+---------+----------------+-------------------+----------------------
europe-west1 | {b,c,d} | {movr} | {} | {}
us-east1 | {b,c,d} | {movr} | {movr} | {}
us-west1 | {a,b,c} | {movr} | {} | {movr}
(3 rows)
-- Podemos ver que se mantiene el mismo objetivo de supervivéncia
demo@[Link]:26257/movr> SHOW SURVIVAL GOAL FROM DATABASE movr;
database | survival_goal
-----------+----------------
movr | zone
(1 row)
16
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
4- Cambiando el objetivo de supervivencia
Supongamos que queremos modificar el objetivo de supervivencia a nivel de región, para que
permita, no sólo la caída de una zona de disponibilidad, sino también de toda una región.
demo@[Link]:26257/movr> ALTER DATABASE movr SURVIVE REGION FAILURE;
ALTER DATABASE
Time: 170ms total (execution 170ms / network 0ms)
demo@[Link]:26257/movr> SHOW REGIONS FROM DATABASE movr;
database | region | primary | secondary | zones
-----------+--------------+---------+-----------+----------
movr | us-east1 | t | f | {b,c,d}
movr | us-west1 | f | t | {a,b,c}
movr | europe-west1 | f | f | {b,c,d}
(3 rows)
Time: 23ms total (execution 23ms / network 0ms)
demo@[Link]:26257/movr> SHOW SURVIVAL GOAL FROM DATABASE movr;
database | survival_goal
-----------+----------------
movr | region
(1 row)
Fijaos que la configuración de regiones se mantiene igual en la base de datos, pero el objetivo
de supervivencia ahora aparece a nivel de región en vez de a nivel de zona. Si consultamos
los metadatos de la tabla RIDES podemos observar algunos cambios.
demo@[Link]:26257/movr> SHOW ZONE CONFIGURATION FOR TABLE rides;
target | raw_config_sql
--------------+-------------------------------------------------------------------------------
TABLE rides | ALTER TABLE rides CONFIGURE ZONE USING
| range_min_bytes = 134217728,
| range_max_bytes = 536870912,
| [Link] = 14400,
| num_replicas = 5,
| num_voters = 5,
| constraints = '{+region=europe-west1: 1, +region=us-east1: 1, +region=us-
west1: 1}',
| voter_constraints = '{+region=us-east1: 2, +region=us-west1: 2}',
| lease_preferences = '[[+region=us-east1], [+region=us-west1]]'
(1 row)
17
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Como veis, ahora hay 5 réplicas y todas ellas participan en las votaciones a la hora de
establecer el quorum para las operaciones de escritura. Fijaos también que la preferencia para
alojar las réplicas primarias continua siendo us-east1 y us-west1 , en ese orden, debido a que
son las regiones primaria y secundaria. Fijaos también que detrás de us-east y us-west hay
un 2 en el atributo voter_constraints. Eso es porque al ser las regiones primaria y secundaria,
CocroachDB las promociona y coloca dos réplicas en cada una de ellas. Por lo tanto, ahora
el sistema funcionaría si cayera cualquiera de las tres zonas. Las lecturas de la zona us-east1
continuarían siendo locales y, por lo tanto, más rápidas. Pero en contrapartida, las escrituras
ahora siempre requerirían escribir en más de una réplica de otra región, por lo que serían más
lentas.
18
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
5- Ejecutando una follower read
Sabemos que, tal y como está configurada la base de datos, las réplicas líder estarán en la
región us-east1. Vamos a realizar un follower read desde otra zona para ver como se leen los
datos de una réplica local no líder.
Lo primero que haremos será conectarnos, vía otra sesión de terminal, a alguno de los nodos
de la zona de disponibilidad de Europa por ejemplo (nodos 7, 8 o 9). Una vez ahí podemos
hacer la siguiente consulta:
demo@localhost:26263/movr> select city, count(*) from rides group by city;
city | count
----------------+--------
amsterdam | 55
boston | 56
los angeles | 56
new york | 56
paris | 56
rome | 55
san francisco | 55
seattle | 56
washington dc | 55
Fijaos que como las réplicas líder de la tabla RIDES están en otra zona de disponibilidad, los
datos tendrán que viajar de estados unidos a Europa. Para comprobarlo podemos utilizar la
sentencia EXPLAIN ANALYZE para ver como se ha realizado la consulta (se presenta sólo
un fragmento relevante del resultado):
demo@localhost:26263/movr> explain analyze (verbose) select city, count(*) from rides group by
city;
info
----------------------------------------------------------------------------------
…
regions: europe-west1, us-east1
…
• group (streaming)
│ columns: (city, count)
│ nodes: n2
│ regions: us-east1
│ group by: city
...
(46 rows)
19
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Según el plan de ejecución, la consulta involucra dos nodos: el de europa-west1 (que es donde
se hace la consulta) y el de us-east1, que es donde están los datos (en concreto en el nodo
n2).
Ahora ejecutaremos la misma consulta utilizando un follower read. Dada la configuración de
la base de datos sabemos que una de las 5 réplicas de cada fragmento estará en el nodo local
(Europa).
demo@localhost:26263/movr> explain analyze (verbose) select city, count(*) from rides as of
system time follower_read_timestamp() group by city;
info
----------------------------------------------------------------------------------
...
regions: europe-west1
• group (streaming)
│ columns: (city, count)
│ nodes: n9
│ regions: europe-west1
│ group by: city
...│
(46 rows)
Como podemos ver ahora los datos se han obtenido localmente de la región europe-west1
(en concreto del nodo n9). Por lo tanto se ha evitado mover los datos de una región a otra.
20
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
6- Creando una tabla global
Tal y como hemos visto, las tablas locales de un clúster permiten consultar los datos siempre
de las réplicas de la región local y, por lo tanto, reducir el tiempo de respuesta de todas las
lecturas, vengan de donde vengan. Obviamente, eso no será gratuito, sino que penalizará en
gran medida a las escrituras sobre la base de datos.
Para configurar una tabla de la base de datos como global, debe utilizarse la siguiente
sentencia:
ALTER TABLE promo_codes SET LOCALITY GLOBAL;
Tras esta instrucción, podemos comprobar cómo, efectivamente, se ha modificado la localidad
de la tabla promo_codes a global, permitiendo desde ese momento lecturas locales en todas
las regiones.
demo@[Link]:26257/movr> SHOW CREATE TABLE promo_codes;
table_name | create_statement
--------------+---------------------------------------------------------
promo_codes | CREATE TABLE public.promo_codes (
| code VARCHAR NOT NULL,
| description VARCHAR NULL,
| creation_time TIMESTAMP NULL,
| expiration_time TIMESTAMP NULL,
| rules JSONB NULL,
| CONSTRAINT promo_codes_pkey PRIMARY KEY (code ASC)
| ) LOCALITY GLOBAL
21
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Además, la configuración de la tabla, muestra que ahora se pueden ejecutar lecturas globales:
demo@[Link]:26257/movr> SHOW ZONE CONFIGURATION FOR TABLE promo_codes;
target | raw_config_sql
--------------------+-------------------------------------------------------------------------
------------------
TABLE promo_codes | ALTER TABLE promo_codes CONFIGURE ZONE USING
| range_min_bytes = 134217728,
| range_max_bytes = 536870912,
| [Link] = 14400,
| global_reads = true,
| num_replicas = 5,
| num_voters = 5,
| constraints = '{+region=europe-west1: 1, +region=us-east1: 1,
+region=us-west1: 1}',
| voter_constraints = '{+region=us-east1: 2, +region=us-west1: 2}',
| lease_preferences = '[[+region=us-east1], [+region=us-west1]]'
22
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
7- Fragmentando los datos by row
Otra forma de distribuir los datos en CocroachDB es fragmentando los datos en función del
valor de un atributo de la tabla. A continuación, vamos a configurar la tabla RIDES para que
se fragmente en función de uno de sus atributos.
demo@[Link]:26257/movr> SHOW CREATE TABLE rides;
table_name | create_statement
-------------+--------------------------------------------------------------------------------
-------------------------------------------------
rides | CREATE TABLE [Link] (
| id UUID NOT NULL,
| city VARCHAR NOT NULL,
| vehicle_city VARCHAR NULL,
| rider_id UUID NULL,
| vehicle_id UUID NULL,
| start_address VARCHAR NULL,
| end_address VARCHAR NULL,
| start_time TIMESTAMP NULL,
| end_time TIMESTAMP NULL,
| revenue DECIMAL(10,2) NULL,
| CONSTRAINT rides_pkey PRIMARY KEY (city ASC, id ASC),
...
| ) LOCALITY REGIONAL BY TABLE IN PRIMARY REGION;
La tabla RIDES almacena los diferentes trayectos que se llevan a cabo con vehículos
compartidos. Uno de sus atributos (city) indica la ciudad donde se ha realizado dicho trayecto;
algunos ejemplos son: Seattle, San Francisco, New York y París. Por tanto lo más probable
es que la mayoría de lecturas y escrituras se lleven a cabo desde la región donde se efectuó
el trayecto. Por lo tanto, promoveremos ese tipo de partición.
Como recordaréis, con la configuración actual, la tabla rides tiene todas las réplicas líder en
la región primaria.
23
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
demo@[Link]:26257/movr> SHOW ZONE CONFIGURATION FOR TABLE rides;
target | raw_config_sql
--------------+-------------------------------------------------------------------------------
------------
TABLE rides | ALTER TABLE rides CONFIGURE ZONE USING
| range_min_bytes = 134217728,
| range_max_bytes = 536870912,
| [Link] = 14400,
| num_replicas = 5,
| num_voters = 5,
| constraints = '{+region=europe-west1: 1, +region=us-east1: 1, +region=us-
west1: 1}',
| voter_constraints = '{+region=us-east1: 2, +region=us-west1: 2}',
| lease_preferences = '[[+region=us-east1], [+region=us-west1]]'
A continuación, diremos a CockroachDB que queremos particionar la tabla utilizando una
estrategia by row:
demo@[Link]:26257/movr> ALTER TABLE rides SET LOCALITY REGIONAL BY ROW;
NOTICE: LOCALITY changes will be finalized asynchronously; further schema changes on this table
may be restricted until the job completes
ALTER TABLE
Time: 2.898s total (execution 2.880s / network 0.018s)
CockroachDB nos informa que hará los cambios necesarios de forma asíncrona, por lo que
es posible que tardemos a visualizar los resultados.
Para llevar a cabo la distribución, fijaos que se ha creado un nuevo campo (oculto) para
discriminar la región de interés para cada registro, llamado crdb_region.
demo@[Link]:26257/movr> SHOW COLUMNS FROM rides;
…
column_name | crdb_region
data_type | [Link].crdb_internal_region
is_nullable | f
column_default |
default_to_database_primary_region(gateway_region())::[Link].crdb_internal_region
generation_expression |
indices |
{rides_auto_index_fk_city_ref_users,rides_auto_index_fk_vehicle_city_ref_vehicles,rides_pkey}
is_hidden | t
24
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Fijaos que también se ha asignado un tipo a esta columna ([Link].crdb_internal_region).
Dicho tipo será un enumerado, cuyos posibles valores son las regiones disponibles en la base
de datos.
demo@[Link]:26257/movr> SHOW ENUMS FROM [Link];
schema | name | values | owner
---------+----------------------+----------------------------------+--------
public | crdb_internal_region | {europe-west1,us-east1,us-west1} | demo
Por defecto, CockroachDB ha asignado, para cada fila, el valor de la región donde pertenece.
Por ello, todos los registros estarán asociados a la región primaria, como podemos ver a
continuación:
demo@[Link]:26257/movr> SELECT crdb_region, count(*) FROM rides GROUP BY crdb_region;
crdb_region | count
--------------+--------
us-east1 | 500
Además, se ha modificado la configuración de la tabla RIDES para indicar que ahora debe
distribuir las réplicas líder by row:
demo@[Link]:26257/movr> SHOW CREATE TABLE rides;
table_name | create_statement
-------------+--------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
rides | CREATE TABLE [Link] (
| id UUID NOT NULL,
| city VARCHAR NOT NULL,
| vehicle_city VARCHAR NULL,
| rider_id UUID NULL,
| vehicle_id UUID NULL,
| start_address VARCHAR NULL,
| end_address VARCHAR NULL,
| start_time TIMESTAMP NULL,
| end_time TIMESTAMP NULL,
| revenue DECIMAL(10,2) NULL,
| crdb_region [Link].crdb_internal_region NOT VISIBLE NOT NULL DEFAULT
default_to_database_primary_region(gateway_region())::[Link].crdb_internal_region,
...
| LOCALITY REGIONAL BY ROW;
| ALTER TABLE [Link] CONFIGURE ZONE USING
25
MÁSTER EN INGENIERÍA DE DATOS
Almacenamiento escalable
Si queremos reubicar los registros ya existentes, aquellos que se insertaron antes de la
conversión a regional by row, hemos de llevar a cabo una actualización de los registros de la
tabla, de manera que modifique el valor del campo crdb_region a la región adecuada.
Como sólo hay 9 valores distintos:
demo@[Link]:26257/movr> select distinct city from rides;
city
-----------------
boston
rome
washington dc
new york
amsterdam
los angeles
paris
san francisco
seattle
(9 rows)
Podemos distribuirlos entre sus regiones y así garantizaríamos que todos los registros están
en el fragmento que les corresponde:
UPDATE rides SET crdb_region = CASE
WHEN city IN ('boston', 'new york', 'washington dc') THEN 'us-east1'
WHEN city IN ('los angeles', 'san francisco', 'seattle') THEN 'us-west1'
WHEN city IN ('rome', 'amsterdam', 'paris') THEN 'europe-west1'
END
WHERE TRUE;
A partir de este momento la base de datos se configurará de manera que la replica líder se
encuentre siempre en la zona que indique el atributo crdb_region.