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

Guarde Todo con Elixir y Mnesia

by
Length:LongLanguages:

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

En uno de mis artículos anteriores escribí sobre mesas de almacenamiento Erlang (o simplemente ETS), que permiten tuplas de datos arbitrarios para ser almacenado en memoria. También discutimos en disco ETS (DETS), que proporcionan una funcionalidad un poco más limitada, pero que pueda guardar su contenido en un archivo.

A veces, sin embargo, pueden requerir una solución más potente para almacenar los datos. Conocer Mnesia, distribuidas una en tiempo real sistema de gestión de base de datos inicialmente introducido en Erlang. Mnesia tiene un modelo de datos relacional u objeto híbrido y un montón de características interesantes, incluyendo la replicación y búsquedas de datos.

En este artículo, usted aprenderá:

  • Cómo crear un esquema Mnesia y empezar todo el sistema.
  • Qué tipo de tabla está disponible y cómo crearlos.
  • Cómo realizar las operaciones CRUD y cuál es la diferencia entre funciones "transaccional" y "sucio".
  • Cómo modificar tablas y agregar índices secundarios.
  • Cómo utilizar el paquete de Amnesia para simplificar el trabajo con bases de datos y tablas.

¿Vamos a empezar, siempre?

Introducción a Mnesia

Así que, como ya se mencionó anteriormente, Mnesia es un objeto y un modelo de datos relacional que escala muy bien. Tiene un lenguaje de consulta DMBS y admite transacciones atómicas, al igual que cualquier otra solución popular (Postgres o MySQL, por ejemplo). Tablas de Mnesia pueden almacenarse en el disco y en memoria, pero los programas se pueden escribir sin el conocimiento de la ubicación de los datos reales. Por otra parte, puede replicar sus datos a través de múltiples nodos. También nota que Mnesia corre en la misma instancia de la viga que otro código.

Mnesia es un módulo de Erlang, debe acceder con un átomo:

Aunque es posible crear un alias como esta:

Datos en Mnesia se organizan en tablas que tienen sus propios nombres como átomos (que es muy similar al ETS). Las tablas pueden tener uno de los siguientes tipos:

  • :set - el tipo por defecto. No se pueden tener varias filas con exactamente la misma clave primaria (vamos a ver en el momento de cómo definir una clave principal). Las filas no se piden de ninguna manera particular.
  • :ordered_set—como :set, pero los datos están ordenados por la clave primaria. Más adelante veremos que algunos leen las operaciones se comportarán diferentemente con: tablas de ordered_set.
  • :bag—varias filas tengan la misma clave, pero las filas todavía no pueden ser completamente idénticas.

Tablas tienen otras propiedades que se pueden encontrar en los documentos oficiales (vamos a discutir algunos de ellos en la siguiente sección). Sin embargo, antes de comenzar a crear tablas, necesitamos un esquema, así que vamos a proceder a la siguiente sección y agregue uno.

Crear un Esquema y las Tablas

Para crear un nuevo esquema, vamos a utilizar un método con un nombre bastante sorprendente: create_schema/1. Básicamente, va a crear una nueva base de datos en un disco. Acepta un nodo como argumento:

Un nodo es un VM Erlang que maneja sus comunicaciones, la memoria y otras cosas. Nodos pueden conectarse unos a otros, y no se limitan a una PC, puede conectarse a otros nodos a través de Internet.

Después de ejecutar el código anterior, un nuevo directorio denominado Mnesia.nonode@nohost se crea que va a contener la base de datos. nonode@nohost es el nombre del nodo. Podemos crear las tablas, sin embargo, Mnesia debe ser iniciado. Esto es tan simple como llamar a la función de start/0:

Mnesia debe ser iniciado en todos los nodos participantes, que normalmente tiene una carpeta que los archivos se escriben (en nuestro caso, esta carpeta se denomina Mnesia.nonode@nohost). Todos los nodos que componen el sistema Mnesia están escritos en el esquema, y más tarde usted puede Agregar o quitar nodos individuales. Por otra parte, al poner en marcha, los nodos de intercambian de información de esquema para asegurarse que todo está bien.

Si Mnesia iniciado con éxito, una: átomo ok se devolverá como resultado. Más adelante puede dejar el sistema llamando stop/0:

Ahora podemos crear una nueva tabla. Por lo menos, deberíamos proporcionar su nombre y una lista de atributos para los registros (piense en ellos como columnas):

Si el sistema no está funcionando, no se creó la tabla y un {: aborted, {: node_not_running,: nonode@nohost}} error se devolverá en su lugar. También, si la tabla ya existe, usted conseguirá un {: aborted, {: already_exists,: user}} error.

Así se llama nuestra nueva tabla :user y tiene tres atributos: : id, :name, y :surname. Tenga en cuenta que el primer atributo en la lista siempre se utiliza como la clave principal, y podemos utilizarlo para buscar rápidamente un registro. Más adelante en el artículo, veremos cómo escribir consultas complejas y agregar índices secundarios.

También, recuerde que es el tipo predeterminado para la tabla :set, pero esto puede cambiar muy fácilmente:

Usted puede incluso hacer su tabla de sólo lectura estableciendo la :access_mode a :read_only:

Después de que el esquema y la tabla se crean, el directorio va a tener un schema.DAT archivo así como algunos archivos .log. ¡Ahora diríjase a la sección siguiente e insertar algunos datos a nuestra tabla nueva!

Operaciones de Escritura

Para almacenar algunos datos en una tabla, es necesario utilizar una función write/1. Por ejemplo, vamos a añadir un nuevo usuario llamado John Doe:

Observe que hemos especificado nombre de la tabla y atributos del usuario para almacenar. Intente ejecutar el código... y fracasa miserablemente con una {:aborted, :no_transaction} error. ¿Por qué está ocurriendo? Bueno, esto es porque la función de write/1 debe ejecutarse en una transacción. Si, por alguna razón, no quiere seguir con una transacción, la operación de escritura puede hacerse una manera"sucia" con dirty_write/1:

Este enfoque generalmente no se recomienda, así que en lugar de ello vamos a construir una única transacción muy simple con la ayuda de la función de transaction:

transaction acepta una función anónima que tiene una o más operaciones agrupadas. Tenga en cuenta que en este caso el resultado es {:atomic, :ok}, no solo :ok como lo fue con la función dirty_write. El beneficio principal aquí es que si algo sale mal durante la transacción, todas las operaciones se deshacen.

En realidad, es un principio de atomicidad, que dice que todas las operaciones deben ocurrir o no operaciones deben ocurrir en el caso de un error. Supongamos, por ejemplo, usted está pagando a sus empleados sus salarios, y de repente algo va mal. La operación se detiene, y definitivamente no quiere terminar en una situación cuando algunos empleados sus salarios y otros no. Cuando las transacciones atómicas son realmente útiles.

La función de transaction tenga como muchos escriben operaciones según sea necesario:

Curiosamente, pueden actualizarse datos utilizando la función de write así. Sólo proporcionan la misma clave y nuevos valores para el resto de atributos:

Observe, sin embargo, que esto no va a funcionar para las mesas de la :bag tipo. Debido a que dichas tablas permiten múltiples registros tener la misma clave, simplemente terminarás con dos registros: [{:user, 2, "Kate", "Brown"}, {:user, 2, "Kate", "Smith"}] . Aún así, :bag tablas no permite registros completamente idénticos a existir.

Operaciones de Lectura

Bien, ahora que ya tenemos algunos datos en nuestra tabla, ¿por qué no tratar de leerlos? Así como con las operaciones de escritura, se puede realizar Lee de una manera "sucia" o "transaccional". La "manera sucia" es más simple (¡pero es el lado oscuro de la fuerza, Luke!):

Así que dirty_read devuelve una lista de registros encontrados basándose en la clave proporcionada. Si la tabla es un :set o un :ordered_set, la lista tendrá solamente un elemento. Para tablas :bag, la lista puede, por supuesto, tienen varios elementos. Si no hay registros se encontraron, la lista estaría vacía.

Ahora vamos a intentar realizar la misma operación pero utilizando el enfoque transaccional:

¡Excelente!

¿Son hay cualquier otra función útil para la lectura de datos? ¡Por supuesto! Por ejemplo, usted puede agarrar el primero o el último registro de la tabla:

dirty_first y dirty_last tienen sus contrapartes transaccionales, es decir, primeras y últimas, que deben ser envuelto en una transacción. Todas estas funciones devolución la clave de registro, pero tenga en cuenta que en ambos casos obtenemos 2 como resultado a pesar de que tenemos dos registros con las teclas 2 y 3. ¿Por qué está ocurriendo?

Parece que para el :set y :bag tablas, dirty_first y dirty_last funciones (así como first y last) son sinónimos porque los datos no están ordenados en un orden específico. Si, sin embargo, tienes una :ordered_set tabla, los registros se ordenarán por las llaves, y el resultado sería:

También es posible el gancho agarrador la siguiente o la anterior clave mediante el uso de dirty_next y dirty_prev (o siguiente y anterior):

Si no existen más registros, un átomo especia se devuelve :"$end_of_table". También, si la tabla es un :set o :bag, dirty_next y dirty_prev son sinónimos.

Por último, usted puede conseguir todas las claves de una tabla mediante dirty_all_keys/1 o all_keys/1:

Eliminar Operaciones

Para eliminar un registro de una tabla, utilice dirty_delete o delete:

Esto va a quitar todos los registros con una clave determinada.

Asimismo, puede eliminar toda la tabla:

No hay ninguna contraparte 'sucia' para este método. Obviamente, después de que se elimina una tabla, usted no puede escribir cualquier cosa y un {: aborted, {: no_exists,: user}} error se devolverá en su lugar.

Por último, si usted está realmente en un estado de ánimo de eliminar, el esquema entero puede eliminarse utilizando delete_schema/1:

Esta operación se volverá un {{:error, {'Mnesia is not stopped everywhere', [:nonode@nohost]}} error si Mnesia no se detiene, así que no olvides hacerlo:

Operaciones Más Complejas de la Lectura

Ahora que hemos visto los conceptos básicos de trabajar con Mnesia, vamos a profundizar un poco y ver cómo escribir consultas avanzadas. En primer lugar, son funciones match_object y dirty_match_object que pueden utilizarse para buscar un registro basado en uno de los atributos siempre:

Los atributos que usted no se marcan con el :_ átomo. Se puede configurar sólo el apellido, por ejemplo:

También puede proporcionar criterios búsquedas personalizadas usando select y dirty_select. Para ver esto en acción, digamos en primer lugar rellenar la tabla con los siguientes valores:

Ahora lo que quiero hacer es encontrar todos los registros que sera como el nombre y cuyas claves son menos de 5, lo que significa que la resultante de la lista debe contener sólo "Will Smith" y "Will Smoth". Aquí está el código correspondiente:

Las cosas son un poco más complejas aquí, así que vamos a discutir este fragmento de código paso a paso.

  • En primer lugar, tenemos el {:user, :"$1", :"$2", :"$3"} parte. Aquí estamos proporcionando el nombre de tabla y una lista de parámetros posicionales. Debe ser escritos en esta forma extraño para que podemos utilizar más adelante. $1 corresponde a la :id, $2 es el nombre, y $3 es el apellido.
  • A continuación hay una lista de funciones de protección que deben aplicarse a los parámetros dados. {: <, :"$1", 5} significa que queremos seleccionar sólo los registros cuyo atributo marcado como $1 (es decir, :id) es de menos de 5. {: ==,: "$2", "Voluntad"}, en cambio, significa que estamos seleccionando los registros con el :name a "Will".
  • Por último, [: "$$"] significa que quisiéramos incluir todos los campos en el resultado. Se puede decir [:"$2"] para mostrar sólo el nombre. Nota, por cierto, que el resultado contiene una lista de listas: [[3, "A", "Smith"], [4, "Will", "Smoth"]].

También puede marcar algunos atributos como los que no te importa para usar el :_ átomo. Por ejemplo, vamos a ignorar el apellido:

En este caso, sin embargo, el apellido no se incluirán en el resultado.

Modificación de las Tablas

Realizar Transformaciones

Supongamos ahora que queremos modificar nuestra tabla mediante la adición de un nuevo campo. Esto puede hacerse mediante la función transform_table, que acepta el nombre de la tabla, una función para aplicar a todos los registros y la lista de nuevos atributos:

En este ejemplo estamos agregando un atributo nuevo denominado :salary (se incluye en el último argumento). En cuanto a la función de transformación (segundo argumento), estamos creando este nuevo atributo a un valor aleatorio. También se puede modificar cualquier atributo dentro de esta función de transformación. Este proceso de cambio de los datos se conoce como una "migración", y este concepto debe ser familiar a los desarrolladores del mundo de Rails.

Ahora puede simplemente agarrar información acerca de los atributos de la tabla utilizando table_info:

El atributo :salary existe! Y, por supuesto, sus datos también:

Usted puede encontrar un ejemplo ligeramente más complejo de utilizar funciones create_table tanto transform_table en el sitio web ElixirSchool.

Agregar Indices

Mnesia le permite hacer cualquier atributo indexado utilizando la función add_table_index. Por ejemplo, vamos a hacer nuestro atributo de :surname indexada:

Si el índice ya existe, se producirá un error {:aborted, {:already_exists, :user, 4}}.

Como la documentación de los Estados de esta función, los índices no vienen gratis. Específicamente, ocupan espacio adicional (proporcional al tamaño de la mesa) y hacer insertar las operaciones un poco más lentas. Por otro lado, permiten buscar los datos más rápidamente, por lo es un intercambio justo.

Usted puede buscar por un campo indizado con función dirty_index_read o index_read:

Aquí estamos utilizando el índice secundario :surname a buscar un usuario.

Uso de Amnesia

Puede ser algo tedioso trabajar con el módulo Mnesia directamente, pero por suerte existe un paquete llamado Amnesia (¡duh!) que le permite realizar operaciones triviales con mayor facilidad.

Por ejemplo, puede definir su base de datos y una tabla como esta:

Esto va a definir una base de datos llamada Demo con una tabla de usuario. El usuario va a nombre de un nombre, un apellido, un correo electrónico (un campo indizado) y un id (clave principal a autoincrement).

A continuación, usted puede crear fácilmente el esquema mediante la tarea de mezcla incorporado:

En este caso, será la base de datos basada en disco, pero hay algunas otras opciones disponibles que se pueden establecer. También es una tarea de gota que, obviamente, destruir la base de datos y todos los datos:

Es posible destruir la base de datos y el esquema:

Tener la base de datos y el esquema en su lugar, es posible realizar varias operaciones contra la mesa. Por ejemplo, cree un nuevo registro:

O un usuario por id:

Por otra parte, se puede definir un cuadro de mensaje estableciendo una relación con la tabla User con un user_id como una clave externa:

Las tablas pueden tener un montón de funciones auxiliares en el interior, por ejemplo, para crear un mensaje o los mensajes:

Ahora puede encontrar el usuario, crear un mensaje para ellos o una lista de todos sus mensajes con facilidad:

Muy simple, ¿no? Algunos otros ejemplos de uso pueden encontrarse en el sitio oficial de Amnesia.

Conclusión

En este artículo, hablamos sobre el sistema de gestión de base de datos de Mnesia disponible para Erlang y Elixir. Han discutido los principales conceptos de este DBMS y han visto cómo crear un esquema, base de datos y tablas, así como realizar todas las operaciones principales: crear, leer, actualizar y destruir. Además de eso, han aprendido cómo trabajar con índices, cómo transformar las tablas y cómo utilizar el paquete de Amnesia para simplificar el trabajo con bases de datos.

Realmente espero que este artículo fue útil y tienes ganas de probar Mnesia en acción así. Como siempre, te doy gracias por permanecer conmigo y hasta la próxima vez!

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.