Distribuci贸n de datos en Apache Ignite

隆Hola! Esta publicaci贸n es una versi贸n ligeramente abreviada de mi conferencia hom贸nima en la reuni贸n de la comunidad Apache Ignite . Puede ver la versi贸n completa del video junto con las preguntas y respuestas aqu铆 , y descargar las diapositivas aqu铆 . En el informe, trat茅 de mostrar con ejemplos c贸mo se distribuyen los datos en Apache Ignite.

驴Por qu茅 necesitas distribuir algo?


Un historial bastante est谩ndar del desarrollo de cualquier sistema que requiera almacenamiento y procesamiento de datos es el logro de un cierto l铆mite. O hay una gran cantidad de datos y no se colocan f铆sicamente en el dispositivo de almacenamiento, o la carga est谩 creciendo a una velocidad tal que un servidor ya no puede procesar tantas solicitudes. Hay casos frecuentes cuando ambos ocurren.

Como regla, llegan a una de dos soluciones: fragmentar el almacenamiento existente o cambiar a una base de datos distribuida. Ambas soluciones tienen una serie de caracter铆sticas comunes, la m谩s obvia de las cuales es el uso de m谩s de un nodo para trabajar con datos. Adem谩s, a muchos nodos los llamar茅 topolog铆a.

El problema de la distribuci贸n de datos entre los nodos de topolog铆a se puede formular como un conjunto de requisitos, que nuestra distribuci贸n debe satisfacer:

  1. Se necesita un algoritmo que permita que todos los nodos de la topolog铆a y las aplicaciones del cliente lleguen a la misma conclusi贸n sobre en qu茅 nodo o nodos se encuentra el determinado objeto (o clave).
  2. Uniformidad de distribuci贸n. Cuanto m谩s uniformemente se distribuyan los datos entre nodos, m谩s uniformemente se distribuir谩 la carga en estos nodos. Aqu铆 supongo que nuestros nodos tienen aproximadamente los mismos recursos.
  3. . , , . , , , .

Lograr los dos primeros requisitos es bastante f谩cil.

Un enfoque familiar, que se usa a menudo al equilibrar la carga entre servidores funcionalmente equivalentes, dividiendo el m贸dulo N, donde N es el n煤mero de nodos en la topolog铆a y tenemos una correspondencia uno a uno entre el n煤mero de nodo y su identificador. Entonces, todo lo que tenemos que hacer es representar la clave del objeto como un valor num茅rico utilizando una funci贸n hash y tomar el resto de la divisi贸n entre N del valor obtenido.

imagen

El diagrama muestra la distribuci贸n de 16 claves en 3 nodos. Se puede ver que esta distribuci贸n es uniforme, y el algoritmo para obtener el nodo para el objeto es simple y garantiza que si todos los nodos de la topolog铆a usan este algoritmo, se obtendr谩 el mismo resultado para la misma clave y el mismo N.

Pero, 驴qu茅 sucede si introducimos el cuarto nodo en la topolog铆a?

imagen

Nuestra funci贸n ha cambiado, ahora tomamos el resto de la divisi贸n por 4, no por 3. Y si la funci贸n ha cambiado, entonces la distribuci贸n ha cambiado, y mucho.

Aqu铆, la ubicaci贸n anterior de los objetos para la versi贸n anterior de la topolog铆a de tres nodos se muestra en rojo, y la posici贸n de los objetos para la nueva versi贸n de la topolog铆a de cuatro nodos es verde, respectivamente. Esto es muy similar a los archivos diff habituales, pero en lugar de archivos tenemos nodos.

Es f谩cil ver que los datos se han movido no solo al nuevo nodo, sino que tambi茅n hubo un intercambio de datos entre los nodos que ya estaban en la topolog铆a. Aquellos. observamos tr谩fico espurio entre nodos y no se cumple el requisito de un cambio m铆nimo en la distribuci贸n.

Dos formas populares de resolver el problema de la distribuci贸n de datos, teniendo en cuenta los requisitos enumerados, son las siguientes:

  • Hash constante
  • El algoritmo de peso aleatorio m谩s grande (HRW), tambi茅n conocido como hash de Rendezvous.

Ambos algoritmos son muy simples. Sus descripciones en Wikipedia encajan en varias oraciones. Aunque es dif铆cil llamarlos obvios. Para aquellos interesados, recomiendo leer los art铆culos originales Hashing consistente y 谩rboles aleatorios: protocolos de almacenamiento en cach茅 distribuidos para aliviar puntos calientes en la World Wide Web y un esquema de mapas basado en nombres para Rendezvous . Lo m谩s comprensible, en mi opini贸n, la idea de un algoritmo de hash consistente se transmite en este curso de Stanford .

Veamos estos algoritmos con m谩s detalle.

Hashing consistente


El truco que subyace al algoritmo de hash consistente es asignar ambos nodos y objetos almacenados al mismo espacio identificador. Esto hace que nuestras entidades, objetos y nodos aparentemente diferentes sean comparables.

Para obtener dicho mapeo, simplemente aplicamos la misma funci贸n hash a las teclas de los objetos y a los identificadores de los nodos. El resultado de la funci贸n hash para el nodo se llamar谩 un token, esto nos ser谩 煤til m谩s adelante.

Representamos nuestro espacio identificador en forma de c铆rculo, es decir. simplemente asumimos que el valor identificador m谩ximo sigue inmediatamente al valor identificador m铆nimo.

Ahora, para determinar en qu茅 nodo vive el objeto, debe obtener el valor de la funci贸n hash de su clave, y luego simplemente moverse en el sentido de las agujas del reloj alrededor del c铆rculo hasta que encontremos la ficha de un nodo en el camino. La direcci贸n del movimiento no es importante, pero debe ser fija.

El movimiento imaginario en el sentido de las agujas del reloj es funcionalmente equivalente a una b煤squeda binaria en una matriz ordenada de tokens de nodo.

imagen

En el diagrama, cada sector de un color particular refleja el espacio identificador del que es responsable un nodo particular.

Si agregamos un nuevo nodo, entonces ...

imagen

... dividir谩 uno de los sectores en dos partes y asumir谩 completamente las teclas correspondientes.

En este ejemplo, el nodo 3 se hizo cargo de parte de las claves del nodo 1.

Como puede ver, este enfoque proporciona una distribuci贸n bastante desigual de los objetos entre los nodos, porque depende en gran medida de los identificadores de los propios nodos. 驴C贸mo se puede mejorar este enfoque?

Puede asignar m谩s de un token a los nodos (generalmente cientos). Esto se puede lograr, por ejemplo, introduciendo muchas funciones hash para el nodo (una por token) o aplicando repetidamente la misma funci贸n hash al token obtenido en el paso anterior. Pero no debemos olvidarnos de las colisiones. No debe haber dos nodos con el mismo token.

imagen

En este ejemplo, cada nodo tiene 4 tokens.

Qu茅 m谩s es importante mencionar: si queremos garantizar la seguridad de los datos en el caso de que un nodo abandone la topolog铆a, entonces debemos almacenar las claves en varios nodos (las llamadas r茅plicas o copias de seguridad). En el caso del algoritmo hash consistente, las r茅plicas ser谩n los siguientes nodos N-1 en el c铆rculo, donde N es el factor de replicaci贸n. Por supuesto, el orden de los nodos debe estar determinado por un token espec铆fico (por ejemplo, por el primero), porque cuando se usan m煤ltiples tokens para cada uno de ellos, la disposici贸n de los nodos puede diferir. Preste atenci贸n al esquema: no tiene un patr贸n claro de repetici贸n de nodos.

En cuanto al requisito de un cambio m铆nimo en la distribuci贸n al cambiar la topolog铆a, se cumple porque el orden mutuo de los nodos en el c铆rculo no cambia. Aquellos. eliminar un nodo de la topolog铆a no cambiar谩 la relaci贸n de orden entre los nodos restantes.

Cita hash


El algoritmo de hash de Rendezvous parece incluso m谩s simple que el hashing consistente. El algoritmo se basa en el mismo principio de invariancia de las relaciones de orden. Pero en lugar de hacer comparables nodos y objetos, solo hacemos nodos para un objeto espec铆fico comparable. Aquellos. Determinamos la relaci贸n de orden entre los nodos para cada objeto de forma independiente.

De nuevo hashing nos ayuda con esto. Pero ahora, para determinar el peso del nodo N para un objeto O dado, mezclamos el identificador del objeto con el identificador del nodo y tomamos el hash de esta mezcla. Una vez realizada esta operaci贸n para cada nodo, obtenemos un conjunto de pesos por el cual clasificamos los nodos.

El nodo que result贸 ser el primero y ser谩 responsable de almacenar el objeto.

Como todos los nodos de la topolog铆a usan los mismos datos de entrada, el resultado para ellos ser谩 id茅ntico. Que satisface el primer requisito.

imagen

Considera un ejemplo. Aqu铆 tenemos una relaci贸n de orden entre tres nodos para cuatro claves diferentes. El amarillo indica el nodo con el mayor peso, es decir el nodo que finalmente ser谩 responsable de una clave particular.

Agregue otro nodo a la topolog铆a.

imagen

Lo coloqu茅 deliberadamente en diagonal para tener en cuenta todas las opciones posibles. Aqu铆, el nodo 3, que se muestra en verde, ingres贸 a la topolog铆a. Por lo tanto, la distribuci贸n de peso de los nodos para cada una de las claves ha cambiado. El rojo indica los nodos que han cambiado su ubicaci贸n en la lista para una clave en particular, porque Los pesos de estos nodos eran menores que el peso del nodo agregado. Sin embargo, este cambio afect贸 solo a una de las claves, K3.

Derivemos traicioneramente un nodo de una topolog铆a.

imagen

Una vez m谩s, los cambios afectaron solo una clave, esta vez K1. Los objetos restantes no fueron afectados. La raz贸n, como en el caso del hashing consistente, es la invariabilidad de la relaci贸n de orden entre cualquier par de nodos. Aquellos. Se cumple el requisito de un cambio m铆nimo en la distribuci贸n y no hay tr谩fico espurio entre los nodos.

La distribuci贸n de la cita se ve bastante bien y no requiere trucos adicionales en comparaci贸n con el hashing consistente como tokens.

En caso de que queramos admitir la replicaci贸n, el siguiente nodo de la lista ser谩 la primera r茅plica del objeto, el siguiente nodo ser谩 la segunda r茅plica, etc.

C贸mo se usa el hash de encuentro en Apache Ignite


La llamada funci贸n de afinidad es responsable de la distribuci贸n de datos en Apache Ignite (consulte la interfaz AffinityFunction ). La implementaci贸n predeterminada es el hash de encuentro (consulte la clase RendezvousAffinityFunction ).

Lo primero a lo que debe prestar atenci贸n es que Apache Ignite no asigna objetos almacenados directamente a los nodos de topolog铆a. En cambio, se introduce un concepto adicional: partici贸n.

Una partici贸n es un contenedor para objetos y una unidad de replicaci贸n. Adem谩s, el n煤mero de particiones para un cach茅 particular (este es un an谩logo de la tabla en las bases de datos familiares) se establece en la etapa de configuraci贸n y no cambia durante el ciclo de vida del cach茅.

Por lo tanto, podemos mostrar objetos en particiones usando una divisi贸n de m贸dulo efectiva, y usar hashing de encuentro para mostrar particiones en nodos.

imagen

Porque el n煤mero de particiones para la memoria cach茅 es constante, luego podemos calcular la distribuci贸n de la partici贸n por nodos una vez y almacenar en cach茅 el resultado hasta que se cambie la topolog铆a.

Cada nodo calcula esta distribuci贸n de forma independiente, pero en todos los nodos con los mismos datos de entrada, esta distribuci贸n ser谩 id茅ntica.

La partici贸n puede tener varias copias, las llamamos copias de seguridad. La partici贸n primaria se llama partici贸n primaria.

Para la mejor distribuci贸n de claves entre particiones y particiones por nodos, se debe cumplir la siguiente regla: el n煤mero de particiones debe ser significativamente mayor que el n煤mero de nodos, a su vez, el n煤mero de claves debe ser significativamente mayor que el n煤mero de particiones.

Las cach茅s en Ignite se particionan y replican.

En una memoria cach茅 particionada, el n煤mero de copias de seguridad se establece en la etapa de creaci贸n de la memoria cach茅. Las particiones (primarias y copias de seguridad) se distribuyen uniformemente entre los nodos. Tal cach茅 es m谩s adecuada para trabajar con datos operativos, como proporciona el mejor rendimiento de escritura, que depende directamente de la cantidad de copias de seguridad. En general, cuantas m谩s copias de seguridad, m谩s nodos deben confirmar el registro clave.

imagen

En este ejemplo, el cach茅 tiene una copia de seguridad. Aquellos. podemos perder un nodo y no perder datos, porque Las copias de seguridad de la partici贸n nunca se almacenan en el mismo nodo que la partici贸n primaria o su otra copia de seguridad.

En la memoria cach茅 replicada, el n煤mero de copias de seguridad siempre es igual al n煤mero de nodos de topolog铆a menos 1. Es decir, cada nodo siempre contiene copias de todas las particiones.

imagen

Tal cach茅 es m谩s adecuada para trabajar con datos que rara vez cambian (por ejemplo, directorios) y proporciona la mayor disponibilidad, como podemos perder nodos N-1 (en este caso 3) sin perder datos. Tambi茅n en esta opci贸n, obtendremos el m谩ximo rendimiento de lectura si permitimos leer datos de las particiones primarias y las copias de seguridad.

Colocaci贸n de datos en Apache Ignite


Un concepto importante a tener en cuenta para obtener el mejor rendimiento es la colocaci贸n. Colocaci贸n es la colocaci贸n de cualquier objeto en el mismo lugar. En nuestro caso, los objetos son entidades almacenadas en la memoria cach茅, y un lugar es un nodo.

Si los objetos se distribuyen entre particiones de la misma funci贸n de afinidad, es l贸gico que los objetos con la misma clave de afinidad caigan en la misma partici贸n y, por lo tanto, en el mismo nodo. En Ignite, esto se llama colocaci贸n de afinidad.

Por defecto, una clave de afinidad es la clave principal de un objeto. Pero en Ignite, puede usar cualquier otro campo de un objeto como clave de afinidad.

La colocaci贸n reduce significativamente la cantidad de datos enviados entre nodos para realizar c谩lculos o consultas SQL, lo que naturalmente lleva a una reducci贸n en el tiempo dedicado a estas tareas. Considere este concepto con el ejemplo.

Deje que nuestro modelo de datos consista en dos entidades: orden (orden) y posici贸n de orden (art铆culo de orden). Un pedido puede corresponder a muchos art铆culos. Los identificadores de pedido y l铆nea de pedido son independientes, pero la l铆nea de pedido tiene una clave externa que se refiere al pedido correspondiente.

Supongamos que necesitamos realizar alguna tarea, que para cada orden debe realizar c谩lculos para las posiciones de este orden.

Por defecto, una clave de afinidad es una clave primaria. Por lo tanto, los pedidos y las posiciones se distribuir谩n entre los nodos de acuerdo con sus claves principales, que, seg煤n recuerdo, son independientes.

imagen

En el diagrama, las 贸rdenes est谩n representadas por cuadrados y posiciones en c铆rculos. El color indica que el art铆culo pertenece al pedido.

Con esta distribuci贸n de datos, nuestra tarea hipot茅tica se enviar谩 al nodo donde se encuentra el orden deseado, y luego tendr谩 que leer las posiciones de todos los dem谩s nodos, o enviar una subtarea a estos nodos y obtener el resultado del c谩lculo. Esta es una interacci贸n de red innecesaria que puede y debe evitarse.

驴Qu茅 sucede si le decimos a Ignite que los art铆culos de pedido deben colocarse en los mismos nodos que los mismos pedidos, es decir? 驴recolectar datos?

Como clave de afinidad para la posici贸n, tomamos la clave externa OrderId y este campo se utilizar谩 al calcular la partici贸n a la que pertenece el registro. Adem谩s, dentro de la partici贸n, siempre podemos encontrar nuestro objeto por la clave primaria.

imagen

Ahora, si ambas memorias cach茅 (Order y OrderItem) usan la misma funci贸n de afinidad con los mismos par谩metros, nuestros datos estar谩n cerca y no necesitaremos recorrer la red para buscar art铆culos.

Configuraci贸n de afinidad en Apache Ignite


En la implementaci贸n actual, un objeto de funci贸n de afinidad es un par谩metro de configuraci贸n de cach茅.

La funci贸n de afinidad en s铆 toma los siguientes argumentos al crear:

  • N煤mero de particiones;
  • El n煤mero de copias de seguridad (de hecho, este tambi茅n es el par谩metro de configuraci贸n de la memoria cach茅);
  • Filtro de respaldo;
  • La bandera excluye a los vecinos.

Estas configuraciones no se pueden cambiar.

Con la cantidad de particiones y copias de seguridad, todo parece estar claro. Hablar茅 sobre el filtro de respaldo y la bandera excludeNeighbours un poco m谩s tarde.

En tiempo de ejecuci贸n, la funci贸n de afinidad de entrada recibe la topolog铆a de cl煤ster actual, esencialmente una lista de nodos de cl煤ster, y calcula la distribuci贸n de particiones por nodos de acuerdo con los ejemplos que mostr茅 cuando habl茅 sobre el algoritmo de hash de encuentro.

En cuanto al filtro de respaldo, este es un predicado que le permite prohibir que las funciones de afinidad asignen particiones de respaldo a un nodo para el cual el predicado devolvi贸 falso.

Como ejemplo, supongamos que nuestros nodos f铆sicos (servidores) est谩n ubicados en el centro de datos en diferentes bastidores. Por lo general, cada bastidor tiene su propio poder independiente ...

imagen

... y si perdemos el rack, perdemos los datos.

imagen

En este ejemplo, perdimos la mitad de las particiones.

Pero si configuramos el filtro de copia de seguridad correcto, la distribuci贸n cambiar谩 de tal manera ...

imagen

... que si se pierde el bastidor, no habr谩 p茅rdida de datos y a煤n estar谩n disponibles.

imagen

El indicador excludeNeighbours realiza una funci贸n similar y, de hecho, es una abreviatura para un caso espec铆fico.

A menudo, varios nodos Ignite se ejecutan en el mismo host f铆sico. Este caso es muy similar al ejemplo con bastidores en el centro de datos, solo que ahora estamos luchando contra la p茅rdida de datos con la p茅rdida del host, no los bastidores.

imagen

El resto es igual. Puede implementar este comportamiento utilizando un filtro de respaldo. Esta bandera es un legado hist贸rico y puede eliminarse en la pr贸xima versi贸n principal de Ignite.

Parece que habl茅 sobre la funci贸n de afinidad y la distribuci贸n de datos, todo lo que un desarrollador que usa Apache Ignite necesita saber.

En conclusi贸n, veamos un ejemplo de la distribuci贸n de 16 particiones de acuerdo con la topolog铆a de 3 nodos. Por simplicidad y claridad, creemos que las particiones no tienen copias de seguridad.

Acabo de tomar y escrib铆 una peque帽a prueba que me trajo la distribuci贸n real:

imagen

Como puede ver, la uniformidad de la distribuci贸n no es ideal. Pero el error ser谩 notablemente menor con un aumento en el n煤mero de nodos y particiones. La regla principal que debe observarse es que el n煤mero de particiones es significativamente mayor que el n煤mero de nodos. Ahora, en Ignite, el n煤mero predeterminado de particiones para un cach茅 particionado es 1024.

Ahora agregue un nuevo nodo a la topolog铆a.

imagen

Parte de las partes se mudaron a 茅l. Al mismo tiempo, se observ贸 el requisito de un cambio m铆nimo en la distribuci贸n: el nuevo nodo recibi贸 parte de las particiones, mientras que los otros nodos no intercambiaron particiones.

Eliminamos de la topolog铆a el nodo que estaba presente en 茅l en la etapa inicial:

imagen

ahora todas las particiones que estaban asociadas con el nodo cero se redistribuyeron a otros nodos de la topolog铆a, sin violar nuestros requisitos de distribuci贸n.

Como puede ver, la soluci贸n a problemas complejos a menudo se basa en ideas bastante triviales, aunque no del todo obvias. Las soluciones descritas se utilizan en la mayor铆a de las bases de datos distribuidas y hacen un buen trabajo. Pero estas decisiones son aleatorias y, por lo tanto, la uniformidad de distribuci贸n dista mucho de ser ideal. 驴Se puede mejorar la uniformidad sin sacrificar el rendimiento y otros requisitos de distribuci贸n? La pregunta permanece abierta.

All Articles