2008-12-27 13 views
10

He estado buceando en Erlang recientemente, y decidí usar Mnesia para hacer mi trabajo de base de datos dado que puede almacenar cualquier tipo de estructura de datos de Erlang sin problemas, escalar con facilidad, ser utilizado con la lista comprensiones, etc.Preservar la integridad relacional con Mnesia

Procedentes de bases de datos SQL estándar, la mayoría de las filas pueden y deben ser identificadas por una clave principal, generalmente un entero autoincrementado. Por defecto, Mnesia considera que el primer campo de una fila es su clave. Tampoco da ninguna forma de tener una clave entera autoincrementada hasta donde yo sé.

Dada tengo estos registros de ficción que representa mis tablas:

-record(user, {name, salt, pass_hash, email}). 
-record(entry, {title, body, slug}). 
-record(user_entry, {user_name, entry_title}). 

Calculo utilizando el nombre de usuario puede ser lo suficientemente bueno para algunos propósitos, como en el título de la entrada, con el fin de identificar el recurso, pero ¿cómo Voy por mantener la integridad?

Supongamos que el usuario cambia su nombre o que el título de la entrada cambia después de una edición. ¿Cómo me aseguro de que mis datos estén correctamente relacionados? Actualizar cada tabla con el nombre de usuario cuando cambia suena como una idea terrible sin importar cómo se publique.

¿Cuál sería la mejor manera de implementar algún tipo de sistema de llave primaria en Mnesia?

Además, ¿cómo funcionaría una tabla intermedia como 'user_entry' si el primer campo suele ser la clave? De lo contrario, ¿cuál sería una mejor forma de representar una relación de muchos a muchos en Mnesia?

Respuesta

9

Prefiero usar GUIDs en lugar de ints autoincrementables como claves foráneas artificiales. Hay un Erlang uuid module disponible en GitHub, o puede usar {now(), node()}, dado que now/0 doc dice: "También se garantiza que las llamadas subsiguientes a este BIF devuelvan valores continuamente en aumento".

Utilizar algo que puede cambiar como la clave principal me parece una mala idea, independientemente del sistema de base de datos.

No olvide que no necesita normalizar los datos en Mnesia ni siquiera a la primera forma normal; en su ejemplo, consideraría la siguiente estructura:

-record(user, {id, name, salt, pass_hash, email, entries}). 
-record(entry, {id, title, body, slug, users}). 

donde entries y users son listas de los identificadores. Por supuesto, esto depende de las consultas que desee.

EDITAR: fijado para ser de muchos a muchos en lugar de muchos a uno.

+0

¿Es realmente el buen camino? parece que make_ref/0 restablece su recuento después de reiniciar el shell y tendrá múltiples valores similares. ¿No significaría eso que podría obtener malas claves después de reiniciar el servidor? Combinarlo con ahora/0 podría ayudar, tal vez. –

+1

Probablemente sea mejor combinar ahora/0 con node/0, en realidad. –

+0

O para el caso, puede usar el módulo uuid: http://github.com/travis/erlang-uuid/tree/master –

8

Mnesia no admite secuencias (enteros de incremento automático) en la forma de mnesia:dirty_update_counter(Table, Key, Increment). Para usarlo necesitas una tabla con dos atributos Key y Count. A pesar del nombre, dirty_update_counter es atómico aunque no se ejecute dentro de una transacción.

Ulf Wiger hizo algo de trabajo en proporcionar características RDBMS típicas en la parte superior de mnesia en su rdbms package. Su código proporciona restricciones de clave externa, índices paramétricos, restricciones de valor de campo, etc. Lamentablemente, este código no se ha actualizado en dos años y probablemente será difícil de ejecutar sin una buena experiencia de Erlang.

Al diseñar y usar mnesia, debe recordar que mnesia no es una base de datos relacional. Es un almacén de clave/valor transaccional y es mucho más fácil de usar cuando no se normaliza.

Si sus nombres de usuario son únicos, se puede utilizar el esquema:

-record(user, {name, salt, pass_hash, email}). 
-record(entry, {posted, title, body, slug, user_name}). 

Dónde posted es el Erlang: Ahora es el momento() cuando se carga el artículo. user_name puede necesitar un índice secundario si a menudo necesita recuperar una lista de todos los artículos para un usuario. Como estos datos se dividen en dos tablas, deberá imponer cualquier restricción de integridad en el código de su aplicación (por ejemplo, no aceptar entradas sin un nombre de usuario válido).

Cada valor de campo en mnesia puede ser cualquier término erlang, por lo que si no tiene una clave única en un campo en particular, puede combinar algunos campos para obtener un valor que siempre será único: tal vez {Nombre de usuario, FechaPosionado, TiempoPosificado}. Mnesia le permite buscar claves parciales a través del mnesia:select(Table, MatchSpec). Las MatchSpecs son bastante difíciles de escribir a mano, así que recuerda que ets:fun2ms/1 puede convertir una función de psuedo erlang en una especificación de coincidencia para ti.

En este ejemplo, fun2ms nosotros genera una matchspec para buscar en una mesa de entrada de blog -record(entry, {key, title, slug, body}). donde la clave es {Username, {Year, Month, Day}, {Hour, Minute, Second}} - el nombre de usuario del autor y la fecha y hora que el artículo fue publicado. El siguiente ejemplo recupera los títulos de todas las entradas del blog de TargetUsername durante diciembre de 2008.

ets:fun2ms(fun (#entry{key={U, {Y,M,_D}, _Time}, title=T}) 
      when U=:=TargetUsername, Y=:=2008, M=:=12 -> 
       T 
      end). 
+0

Creo que "dónde" debería ser "cuándo". Gracias por las grandes explicaciones, también. –

Cuestiones relacionadas