Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Elixir

Una Introducción a las Tablas ETS en Elixir

by
Length:LongLanguages:

Spanish (Español) translation by Jean Perez (you can also view the original English article)

Cuando la elaboración de un programa de Elixir, a menudo necesitan compartir un estado. Por ejemplo, en uno de mis artículos anteriores mostré cómo codificar un servidor para realizar diversos cálculos y guardar el resultado en la memoria (y más tarde hemos visto cómo hacer este servidor a prueba de balas con la ayuda de supervisores). Sin embargo, hay un problema: Si tienes un solo proceso que se encarga del estado y muchos otros procesos que acceder a él, el rendimiento puede verse afectado seriamente. Esto es simplemente porque el proceso puede servir solamente una solicitud a la vez.

Sin embargo, hay maneras de superar este problema, y hoy vamos a hablar de uno de ellos. Conoce a Mesas de Almacenamiento Erlang o simplemente tablas ETS, un almacenamiento rápido en memoria que puede albergar tuplas de datos arbitrarios. Como su nombre indica, estas tablas fueron inicialmente introducidas en Erlang, pero como con cualquier otro módulo de Erlang, podemos fácilmente utilizarlos en Elixir así.

En este artículo podrá:

  • Aprende a crear tablas ETS y las opciones disponibles durante la creación.
  • Aprender a realizar lectura, escritura, borrado y algunas otras operaciones.
  • Ver tablas ETS en acción.
  • Conoce las tablas ETS basadas en disco y cómo se diferencian de las tablas en memoria.
  • Descubre cómo convertir ETS y DETS hacia adelante y hacia atrás.

Todos los ejemplos de código funcionan con Elixir 1.4 y 1.5, que fue lanzado recientemente.

Introducción a las Tablas ETS

Como he mencionado anteriormente, las tablas ETS son almacenamiento en memoria que contienen tuplas de datos (llamados hileras). Múltiples procesos pueden acceder a la tabla por su id o nombre representada como un átomo y realizar la lectura, escribir, eliminar y otras operaciones. Tablas ETS son creadas por un proceso independiente, por lo que si este proceso se termina, se destruye la tabla. Sin embargo, no existe ningún mecanismo de recogida automática de basura, por lo que la tabla puede salir en la memoria durante mucho tiempo.

Datos de la tabla ETS son representados por una tupla {:key, value1, value2, valuen}. Usted puede ver los datos de su clave o insertar una nueva fila, pero por defecto no puede haber dos filas con la misma clave. Clave de operaciones son muy rápidas, pero si por alguna razón usted necesita producir una lista de una tabla ETS y, digamos, realizar complejas manipulaciones de los datos, es posible también.

Además, hay en disco ETS mesas disponibles que almacenan su contenido en un archivo. Por supuesto, actúan más lentamente, pero de esta manera obtienes un almacenamiento simple sin problemas. Además de eso, en memoria ETS se puede convertir fácilmente a disco y viceversa.

Por lo tanto, creo que es hora de iniciar nuestro viaje y ver cómo se crean las tablas ETS!

Crear una Tabla ETS

Para crear una tabla ETS, emplear la función de new/2. Como estamos usando un módulo de Erlang, su nombre debe escribirse como un átomo:

Nota que hasta hace poco sólo podría crear hasta 1.400 mesas por cada instancia de la viga, pero esto ya no es el caso, sólo se limita a la cantidad de memoria disponible.

El primer argumento pasado a la función new es nombre de la tabla (alias), mientras que el segundo contiene una lista de opciones. La variable cool_table ahora contiene un número que identifica la tabla en el sistema:

Ahora puede utilizar esta variable para realizar operaciones posteriores a la mesa (leer y escribir datos, por ejemplo).

Opciones Disponibles

Vamos a hablar de las opciones que puede especificar al crear una tabla. Lo primero (y un poco extraño) tener en cuenta es que por defecto se puede utilizar el alias de la tabla en modo alguno, y básicamente no tiene ningún efecto. Pero todavía, el alias se debe pasar sobre la creación de la tabla.

Para poder acceder a la tabla por su alias, usted debe proporcionar una: opción de :named_table como este:

Por cierto, si desea cambiar el nombre de la tabla, se puede hacer usando la función rename/2:

Siguiente, como ya se ha mencionado, una tabla no puede contener varias filas con la misma clave, y esto está determinado por el tipo. Hay cuatro tipos posibles de la tabla:

  • :set—que es el predeterminado. Esto significa que no puede tener varias filas con exactamente las mismas teclas. Las filas no se vuelva a pedido de alguna manera particular.
  • :ordered_set — el mismo como :set, pero las filas se ordenan los términos y condiciones.
  • :bag—varias filas tengan la misma clave, pero las filas todavía no pueden ser completamente idénticas.
  • :duplicate_bag— filas pueden ser totalmente idénticas.

Hay algo digno de mencionar con respecto a la :ordered_set tablas. Como dice la documentación de Erlang, estas tablas tratan las llaves, igual cuando se comparan iguales, no sólo cuando coinciden. ¿Qué significa eso?

Dos términos en Erlang coinciden sólo si tienen el mismo valor y mismo tipo. Tan entero 1 coincide con sólo otro número entero 1, pero no flotar 1.0 ya que tienen diferentes tipos. Dos términos son comparar igual, sin embargo, si bien tienen el mismo valor y el tipo o si ambos son numéricos y se extienden con el mismo valor. Esto significa que 1 y 1.0 se comparan iguales.

Para proporcionar el tipo de la tabla, simplemente añadir un elemento a la lista de opciones:

Otra opción interesante que puede pasar es :compressed. Significa que los datos dentro de la tabla (pero no las claves) serán — adivina — en una forma compacta. Por supuesto, las operaciones que se ejecutan sobre la mesa será más lento.

A continuación, puede controlar qué elemento de la tupla debe utilizarse como clave. De forma predeterminada, se utiliza el primer elemento (posición 1), pero esto puede cambiarse fácilmente:

Ahora lo segundo en las tuplas será tratados como claves.

La última pero no la menos opción controla los derechos de acceso de la tabla. Estos derechos determinan qué procesos son capaces de acceder a la tabla:

  • :public—cualquier proceso puede realizar cualquier operación de la tabla.
  • :protected—el valor por defecto. Sólo el proceso de propietario puede escribir a la tabla, pero pueden leer todos los procesos.
  • :private— sólo el proceso de propietario puede acceder a la tabla.

Por lo tanto, para hacer una mesa privada, escriba:

Bien, suficiente hablar acerca de las opciones, vamos a ver algunas operaciones comunes que se pueden realizar a las tablas!

Operaciones de Escritura

Para leer algo de la tabla, primero debes escribir algunos datos, así que vamos a empezar con la operación de este último. Utilice la función de insert/2 para poner los datos en la tabla:

También puede pasar una lista de tuplas como esta:

Tenga en cuenta que si la tabla tiene un tipo de :set y una nueva clave coincida con uno ya existente, se sobrescribirán los datos anteriores. Del mismo modo, si una tabla tiene un tipo de :ordered_set y una nueva clave compara igual a anterior, los datos serán sobrescrita, así que preste atención a esto.

La operación de inserción (incluso con múltiples tuplas a la vez) está garantizada para ser atómicos y aislados, lo que significa que sea todo se almacena en la tabla o nada en absoluto. También, otros procesos no serán capaces de ver el resultado intermedio de la operación. Con todo, esto es bastante similar a las transacciones SQL.

Si usted está preocupado de duplicar llaves o desea sobrescribir los datos por error, utilice la función de insert_new/2. Es similar a insertr/2 pero nunca inserta llaves duplicadas y devolverá false en lugar de otro. Este es el caso de la :bag y :duplicate_bag tablas así:

Si proporciona una lista de tuplas, se controlará cada tecla, y se cancelará la operación incluso si una de las claves se duplica.

Operaciones de Lectura

¿Genial, ahora tenemos algunos datos de nuestra tabla, como los traen? La forma más fácil es realizar la búsqueda por clave:

Recuerda que para la :ordered_set mesa, la llave debe comparar el valor proporcionado. Para todos los otros tipos de tabla, debe coincidir. También, si una tabla es un :bag o un :ordered_bag, la función de lookup/2 puede devolver una lista con varios elementos:

En lugar de traer una lista, puede tomar un elemento en la posición deseada utilizando la función de lookup_element/3:

En este código, obtenemos la fila debajo de la llave :number y entonces el elemento en la segunda posición. También funciona perfectamente con :bag o :duplicate_bag:

Si desea simplemente comprobar si algunos clave está presente en la tabla, utilice member/2, que devuelve true o false:

Usted puede también conseguir la primera o la última tecla en una tabla utilizando first/1 y last/1 respectivamente:

Además de eso, es posible determinar la anterior o la siguiente clave basado en el proporcionado. Si dicha clave no se encuentra, :"$end_of_table" se devolverá:

Sin embargo, hay que tener en cuenta que el salto de tabla usando funciones como firstnextlast or prev no es aislado. Esto significa que un proceso puede quitar o agregar más datos a la tabla mientras se iteración. Una forma para resolver este problema es mediante el uso de safe_fixtable/2, que fija la tabla y asegura que cada elemento se le trajo una sola vez. La mesa permanece fija a menos que el proceso de lanza:

Por último, si desea encontrar un elemento en la tabla y lo elimine, emplear la función de take/2:

Eliminar Operaciones

Bueno, ahora digamos que ya no necesita la tabla y el deseo de deshacerse de él. Utilice delete/1 para:

Por supuesto, puede eliminar una fila (o varias filas) por su clave, así:

Para borrar toda la tabla, utilizar delete_all_objects/1:

Y, por último, para encontrar y eliminar un objeto específico, utilice delete_object/2:

Conversión de la Tabla

Una tabla ETS se puede convertir en una lista en cualquier momento mediante la función tab2list/1:

Recuerde, sin embargo, que traer los datos de la tabla de las teclas es una operación muy rápida, y usted debe mantenerlo si es posible.

También puede volcar la tabla a un archivo con tab2file/2:

Tenga en cuenta que el segundo argumento debe ser un listaDeCaracteres (una cadena entre comillas solo).

Hay un puñado de otras operaciones disponibles que se pueden aplicar a las tablas ETS, y por supuesto no vamos a hablar de todos ellos. Realmente recomiendo el desnatar a través de la documentación de Erlang en ETS para aprender más.

Persistiendo el Estado con ETS

Para resumir los hechos que hemos aprendido hasta ahora, vamos a modificar un programa sencillo que he presentado en mi artículo sobre GenServer. Se trata de un módulo llamado CalcServer que permite realizar diversos cálculos por enviar solicitudes al servidor o ir a buscar el resultado:

Actualmente nuestro servidor no soporta todas las operaciones matemáticas, pero se puede ampliar según sea necesario. También, mi otro artículo explica cómo convertir este módulo a una aplicación y aprovechar de los supervisores de las caídas del servidor.

Ahora lo que quiero hacer es añadir otra característica: la capacidad de registrar todas las operaciones matemáticas que se realizaron junto con el argumento pasado. Estas operaciones se almacenarán en una tabla ETS para que seamos capaces de traer más adelante.

En primer lugar, modificar la función init para que un nuevo llamado mesa privada con un tipo de: se crea el :duplicate_bag. Estamos utilizando :duplicate_bag porque se pueden realizar dos operaciones idénticas con el mismo argumento:

Ahora ajustar la devolución de llamada handle_cast que registra la operación solicitada, prepara una fórmula y luego realiza el cómputo real:

Esta es la función privada de prepare_and_log:

Estamos registrando la operación inmediatamente (la función correspondiente se presentará en el momento). Volver la función apropiada o nil si no sabemos cómo manejar la operación.

En cuanto a la función log, o bien debemos apoyar una tupla (contiene el nombre de la operación y el argumento) o un átomo (que contiene sólo de la operación nombre, por ejemplo, :sqrt):

A continuación, la función de calculate, que o bien devuelve un resultado correcto o un mensaje de stop:

Por último, vamos a presentar una nueva función de interfaz para recuperar todas las operaciones realizadas por su tipo:

La llamada de la manija:

Y efectuar la búsqueda real:

Ahora prueba todo:

El resultado es correcto porque hemos realizado dos :add las operaciones con los argumentos 1 y 2. Por supuesto, usted puede ampliar este programa como mejor parezca. Aún así, no abuso mesas ETS y emplearlos cuando realmente va a aumentar el rendimiento, en muchos casos, usando immutables es una mejor solución.

Disco ETS

Antes de concluir este artículo, quería decir un par de palabras sobre tablas ETS basadas en disco o simplemente DETS.

DETS son bastante similares a ETS: utilizan tablas para almacenar datos en forma de tuplas. La diferencia, como has adivinado, es que confían en el almacenamiento de archivos en lugar de memoria y tienen menos características. DETS tienen funciones similares a las que hablamos anteriormente, pero algunas operaciones se llevan a cabo un poco diferente.

Para abrir una tabla, es necesario utilizar open_file/1 o open_file/2 — hay ninguna función de new/2 como en el: módulo de :ets. Ya que no cualquier tabla existente todavía, vamos a pegar a open_file/2, que va a crear un nuevo archivo para nosotros:

El nombre del archivo es igual al nombre de la tabla por defecto, pero esto se puede cambiar. El segundo argumento pasado a la open_file es la lista de opciones en forma de tuplas. Hay un puñado de opciones disponibles como: access o :auto_save. Por ejemplo, para cambiar un nombre de archivo, utilice la siguiente opción:

Tenga en cuenta que también hay un :type de opción que puede tener uno de los siguientes valores:

  • :set
  • :bag
  • :duplicate_bag

Estos tipos son los mismos en cuanto a las ETS. Tenga en cuenta que DETS no puede tener un tipo de :ordered_set.

Hay ninguna opción :named_table, por lo que siempre puede utilizar el nombre de la tabla para acceder a ella.

Otra cosa vale la pena mencionar es que las tablas DETS deben cerrarse correctamente:

Si no haces esto, la tabla se reparará en la próxima vez que se abre.

Realizar lectura y escritura operaciones como usted hicieron con ETS:

Tener en cuenta, sin embargo, que DETS son más lentos que ETS porque Elixir necesitará tener acceso al disco que, por supuesto, lleva más tiempo.

Tenga en cuenta que puede convertir tablas de ETS y DETS hacia adelante y hacia atrás con facilidad. Por ejemplo, vamos a utilizar to_ets/2 y copiar el contenido de los DETS tabla en memoria:

Copiar contenido de ETS para DETS usando to_dets/2:

Para resumir, ETS basado en disco es una forma sencilla de almacenar contenidos en el archivo, pero este módulo es ligeramente menos potente que ETS y las operaciones son más lentas, así.

Conclusión

En este artículo, hemos hablado sobre ETS y tablas ETS basado en disco que nos permiten almacenar términos arbitrarios en memoria o en archivos respectivamente. Hemos visto cómo crear dichas tablas, cuáles son los tipos disponibles, cómo leer y escribir operaciones, cómo destruir tablas y cómo convertir a otros tipos. Puede encontrar más información sobre ETS en la Guía de Elixir y en la página oficial de Erlang.

Una vez más, no utilice tablas ETS y tratar de pegar con immutables si es posible. En algunos casos, sin embargo, ETS pueden estar un impulso de funcionamiento agradable, saber acerca de esta solución es útil en cualquier caso.

Que hayas disfrutado este artículo. Como siempre, gracias por quedarse conmigo y nos vemos muy pronto!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.