Favoreciendo la inmutabilidad en el diseño de bases de datos

26

Uno de los elementos en Joshua Bloch's Effective Java es la noción de que las clases deberían permitir la mutación de las instancias lo menos posible, y de preferencia ninguna.

A menudo, los datos de un objeto se conservan en una base de datos de alguna forma. Esto me ha llevado a pensar en la idea de la inmutabilidad dentro de una base de datos, especialmente para aquellas tablas que representan una sola entidad dentro de un sistema más grande.

Algo con lo que he estado experimentando recientemente es la idea de intentar minimizar las actualizaciones que hago en las filas de la tabla que representan estos objetos, y tratar de realizar inserciones tanto como pueda.

Un ejemplo concreto de algo con lo que estaba experimentando recientemente. Si sé que podría agregar un registro con datos adicionales más adelante, crearé otra tabla para representar eso, algo así como las siguientes dos definiciones de tabla:

create table myObj (id integer, ...other_data... not null);
create table myObjSuppliment (id integer, myObjId integer, ...more_data... not null);

Es de esperar que sea obvio que estos nombres no son textuales, sino solo para demostrar la idea.

¿Es este un enfoque razonable para el modelado de persistencia de datos? ¿Vale la pena tratar de limitar las actualizaciones realizadas en una tabla, especialmente para rellenar valores nulos para datos que podrían no existir cuando se creó originalmente el registro? ¿Hay momentos en que un enfoque como este puede causar dolor severo más adelante?

Ed Carrel
fuente
77
Siento que esta es una solución sin problemas ... Debería actualizar, en lugar de crear adaptaciones elaboradas para evitar la actualización.
Fosco
Creo que era más una cuestión de tener una idea intuitiva de una solución en mente, y querer ejecutarla por la mayor cantidad de personas posible, y en el proceso de darme cuenta de que esta podría no ser la mejor solución al problema que tengo. Puedo abrir una pregunta diferente con el problema, siempre que no pueda encontrarlo en otro lugar.
Ed Carrel
1
Puede haber buenas razones para evitar actualizaciones en las bases de datos. Sin embargo, cuando surgen estas razones, es más un problema de optimización y, como tal, no debe hacerse sin la prueba de que hay un problema.
dietbuddha
66
Creo que hay un fuerte argumento para la inmutabilidad dentro de las bases de datos. Soluciona muchos problemas. Creo que los comentarios negativos no han venido de personas de mente abierta. Las actualizaciones en el lugar son la causa de tantos problemas. Yo diría que lo tenemos todo al revés. Las actualizaciones in situ son la solución heredada a un problema que ya no existe. El almacenamiento es barato. ¿Por que hacerlo? Cuántos sistemas de bases de datos tienen registros de auditoría, sistemas de versiones, necesitan una replicación distribuida que, como todos sabemos, requiere la capacidad de soportar la latencia para la escala. La inmutabilidad resuelve todo esto.
cirrus
@Fosco Algunos sistemas son absolutamente necesarios para nunca eliminar datos (incluido el uso UPDATE). Como los registros médicos del doctor.
Izkata

Respuestas:

25

El objetivo principal de la inmutabilidad es garantizar que no haya un instante en el tiempo cuando los datos en la memoria estén en un estado no válido. (El otro se debe a que las anotaciones matemáticas son en su mayoría estáticas, por lo que las cosas inmutables son más fáciles de conceptualizar y modelar matemáticamente). En la memoria, si otro hilo intenta leer o escribir datos mientras se está trabajando, podría terminar corrompiéndose, o podría estar en un estado corrupto. Si tiene varias operaciones de asignación a los campos de un objeto, en una aplicación multiproceso, otro subproceso podría intentar trabajar con él en algún momento intermedio, lo que podría ser malo.

La inmutabilidad soluciona esto al escribir primero todos los cambios en un nuevo lugar en la memoria y luego hacer la asignación final como un paso de reescritura del puntero al objeto para señalar el nuevo objeto, que en todas las CPU es atómico operación.

Las bases de datos hacen lo mismo con las transacciones atómicas : cuando inicia una transacción, escribe todas las actualizaciones nuevas en un nuevo lugar en el disco. Cuando finaliza la transacción, cambia el puntero en el disco a donde están las nuevas actualizaciones, lo que hace en un breve instante durante el cual otros procesos no pueden tocarlo.

Esto también es exactamente lo mismo que su idea de crear nuevas tablas, excepto que es más automática y más flexible.

Entonces, para responder a su pregunta, sí, la inmutabilidad es buena en las bases de datos, pero no, no necesita hacer tablas separadas solo para ese propósito; puede usar cualquier comando de transacción atómica disponible para su sistema de base de datos.

Rei Miyasaka
fuente
Gracias por la respuesta. Esta perspectiva era justo lo que necesitaba para darme cuenta de que mi intuición estaba tratando de combinar confusamente un par de ideas diferentes en un solo patrón.
Ed Carrel
8
Hay algo más que atmoicidad. El argumento que veo con mayor frecuencia a favor de la inmutabilidad en un contexto OOP es que los objetos inmutables solo requieren que valide su estado una vez, en el constructor. Si son mutables, entonces todos los métodos que pueden cambiar su estado también deben verificar que el estado resultante sea válido, lo que puede agregar una complejidad significativa a la clase. Este argumento también se aplica potencialmente a las bases de datos, pero es mucho más débil, ya que las reglas de validación de db tienden a ser declarativas en lugar de procedimentales, por lo que no necesitan duplicarse para cada consulta.
Dave Sherohman
24

Depende de los beneficios que espere obtener de la inmutabilidad. La respuesta de Rei Miyasaka se dirigió a uno (evitar estados intermedios no válidos), pero aquí hay otro.

La mutación a veces se denomina actualización destructiva : cuando mutas un objeto, el estado anterior se pierde (a menos que tomes medidas adicionales para preservarlo explícitamente de alguna manera). En contraste, con datos inmutables, es trivial representar simultáneamente el estado antes y después de alguna operación, o representar múltiples estados sucesores. Imagine intentar implementar una búsqueda de amplitud mutando un solo objeto de estado.

Esto probablemente aparece en el mundo de la base de datos con mayor frecuencia como datos temporales . Digamos el mes pasado que estaba en el plan Básico, pero el día 16 se cambió al plan Premium. Si simplemente sobrescribimos algún campo que indicara en qué plan está, podríamos tener dificultades para realizar la facturación correctamente. También podríamos perder la capacidad de analizar tendencias. (¡Oye, mira lo que hizo esta campaña publicitaria local!)

Eso es lo que viene a mi mente cuando dices "inmutabilidad en el diseño de bases de datos", de todos modos.

Ryan Culpepper
fuente
2
No estoy de acuerdo con tu tercer párrafo. Si desea tener un historial (registro de auditoría, registro de cambios del plan, etc.), debe crear una tabla separada para esto. Duplicar los 50 campos de la Customertabla solo para recordar que el usuario cambió el plan no trae nada más que un gran inconveniente de rendimiento, selecciones más lentas con el tiempo, minería de datos más complicada (en comparación con los registros) y más espacio desperdiciado.
Arseni Mourzenko
66
@MainMa: quizás debería haber dicho "ir a leer sobre bases de datos temporales" en su lugar. Mi ejemplo pretendía ser un bosquejo de lo que son los datos temporales; No pretendo que esa sea siempre la mejor manera de representar datos cambiantes. Por otro lado, aunque el soporte para los datos temporales es bastante pobre en la actualidad, espero que la tendencia sea acomodar los datos temporales en la base de datos, en lugar de relegarlos a representaciones de "segunda clase" como los registros de cambios.
Ryan Culpepper
¿Qué sucede si mantenemos un historial de cambios en una tabla de auditoría (el arranque por resorte y la hibernación, por ejemplo, ofenden esta capacidad)?
Mohammad Najar
14

Si está interesado en los beneficios que puede obtener de la inmutabilidad en una base de datos, o al menos en una base de datos que ofrece la ilusión de la inmutabilidad, marque Datomic.

Datomic es una base de datos inventada por Rich Hickey en alianza con Think Relevance, hay muchos videos donde explican la arquitectura, los objetivos y el modelo de datos. Buscar infoq, uno en particular se titula Datomic, Database as a Value . En confreaks puede encontrar una nota clave que Rich Hickey dio en la conferencia euroclojure en 2012. confreaks.com/videos/2077-euroclojure2012-day-2-keynote-the-datomic-architecture-and-data-model

Hay una charla en vimeo.com/53162418 que está más orientada al desarrollo.

Aquí hay otro de stuart halloway en.pscdn.net/008/00102/videoplatform/kv/121105techconf_close.html

  • Datomic es una base de datos de hechos en el tiempo, llamados datos, en 5 tuplas [E, A, V, T, O]
    • E ID de entidad
    • Un nombre de atributo en la entidad (puede tener espacios de nombres)
    • V Valor del atributo
    • T ID de transacción, con esto tienes noción de tiempo.
    • O Una operación de aserción (valor presente o actual), rechazo (valor pasado);
  • Utiliza su propio formato de datos, llamado EDN (notación de datos extensible)
  • Las transacciones son ACID
  • Utiliza el registro de datos como lenguaje de consulta, que es declarativo como consultas recursivas SQL +. Las consultas se representan con estructuras de datos, y extendidas con su lenguaje jvm, no necesita usar clojure.
  • La base de datos está desacoplada en 3 servicios separados (procesos, máquinas):
    • Transacción
    • Almacenamiento
    • Motor de consultas.
  • Puede escalar por separado cada servicio.
  • No es de código abierto, pero hay una versión gratuita (como en cerveza) de Datomic.
  • Puede establecer un esquema flexible.
    • conjunto de atributos está abierto
    • agregar nuevos atributos en cualquier momento
    • sin rigidez en la definición o consulta

Ahora, dado que la información se almacena como hechos a tiempo:

  • todo lo que debe hacer es agregar datos a la base de datos, nunca los eliminará (excepto cuando lo exija la ley)
  • puedes guardar todo en caché para siempre. Query Engine, vive en el servidor de aplicaciones como una base de datos en memoria (para los idiomas jvm, los idiomas que no son jvm tienen acceso a través de una API REST).
  • puede consultar a partir del tiempo en el pasado.

La base de datos es un valor y un parámetro para el motor de consulta, el QE gestiona la conexión y el almacenamiento en caché. Como puede ver la base de datos como un valor y una estructura de datos inmutable en la memoria, puede fusionarla con otra estructura de datos hecha a partir de valores "en el futuro" y pasarla al QE y consultar con valores futuros, sin cambiar la base de datos real .

Hay un proyecto de código abierto de Rich Hickey, llamado codeq , puede encontrarlo en github Datomic / codeq, que amplía el modelo git y almacena referencias a objetos git en una base de datos libre de datos, y realiza consultas de su código, usted puede ver un ejemplo de cómo usar datomic.

Puede pensar en datomic como un ACID NoSQL, con datums puede modelar tablas o documentos o tiendas Kv o gráficos.

kisai
fuente
7

La idea de evitar actualizaciones, y preferir las inserciones, es uno de los pensamientos detrás de la construcción de su almacenamiento de datos como fuente de eventos, una idea que a menudo encontrará utilizada junto con CQRS. En un modelo de origen de eventos, no hay actualización: un agregado se representa como la secuencia de su "transformación" (eventos) y, como resultado, el almacenamiento es de solo agregado.
Este sitio contiene discusiones interesantes sobre CQRS y el abastecimiento de eventos, si tiene curiosidad al respecto.

Mathias
fuente
El CQRS y el abastecimiento de eventos se destacan en estos días.
Gulshan
6

Esto tiene una relación muy estrecha con lo que se conoce como "Dimensiones que cambian lentamente" en el mundo del almacenamiento de datos, y las tablas "Temporales" o "Bitemporales" en otros dominios.

La construcción básica es:

  1. Utilice siempre una clave sustituta generada como clave principal.
  2. El identificador único de lo que esté describiendo se convierte en la "clave lógica".
  3. Cada fila debe tener al menos una marca de tiempo "ValidFrom" y opcionalmente una marca de tiempo "ValidTo" e incluso más opcionalmente un indicador de "Última versión".
  4. En la "creación" de una entidad lógica, inserte una nueva fila con un "Válido desde" de la marca de tiempo actual. El ValidTo opcional se establece en "forever" (9999-12-31 23:59:59) y Last Version en "True".
  5. En una actualización posterior de la entidad lógica. Al menos inserta una nueva fila como arriba. También es posible que deba ajustar ValidTo en la versión anterior a "now () - 1 segundo" y la última versión a "False"
    1. En la eliminación lógica (¡esto solo funciona con la marca de tiempo ValidTo!), Establece el indicador ValidTo en la fila actual en "now () -1 segundo".

Las ventajas de este esquema es que puede recrear el "estado" de su entidad lógica en cualquier momento, tiene un historial de su entidad a lo largo del tiempo y minimiza la contención si su "entidad lógica" se usa mucho.

Las desventajas son que almacena muchos más datos y necesita mantener más índices (como mínimo en Logical Key + ValidFrom + ValidTo). Un índice en Logical Key + Latest Version acelera enormemente la mayoría de las consultas. ¡También complica tu SQL!

Si esto vale la pena hacerlo a menos que realmente necesite mantener un historial y tenga un requisito para recrear el estado de sus entidades en un momento dado, depende de usted.

James Anderson
fuente
1

Otra posible razón para tener una base de datos inmutable sería admitir un mejor procesamiento paralelo. Las actualizaciones que se producen fuera de servicio pueden alterar los datos de forma permanente, por lo que debe producirse un bloqueo para evitarlo y destruir el rendimiento paralelo. Muchas inserciones de eventos pueden ir en cualquier orden, y el estado finalmente será correcto siempre que se procesen todos los eventos. Sin embargo, en la práctica, es tan difícil trabajar con esto en comparación con las actualizaciones de la base de datos que realmente necesitaría mucho paralelismo para considerar hacer las cosas de esta manera; no lo recomiendo.

psr
fuente
0

Descargo de responsabilidad: soy prácticamente un novato en DB: p

Dicho esto, este enfoque de satélites de datos tiene un impacto inmediato en el rendimiento:

  • Buena menos tráfico en la mesa primaria
  • Buenas filas más pequeñas en la tabla primaria
  • Mal que requiere los datos del satélite significa que es necesaria otra búsqueda
  • Mal más espacio ocupado si todos los objetos existen en ambas tablas

dependiendo de sus requisitos, puede aceptar esto o no, pero sin duda es un punto a tener en cuenta.

Matthieu M.
fuente
-1

No veo cómo su esquema puede llamarse "inmutable".

¿Qué sucede cuando cambia un valor almacenado en la tabla suplementaria? Parece que necesitaría realizar una actualización en esa tabla.

Para que una base de datos sea realmente inmutable, debería ser mantenida únicamente por "INSERTOS". Para esto, necesita algún método para identificar la fila "actual". Esto casi siempre termina siendo terriblemente ineficiente. Tiene que copiar todos los valores sin cambios anteriores o juntar el estado actual de varios registros cuando realiza una consulta. La selección de la fila actual generalmente necesita un SQL terriblemente desordenado como ( where updTime = (SELECT max(updTime) from myTab where id = ?).

Este problema surge mucho en DataWarehousing, donde debe mantener un historial de los datos a lo largo del tiempo y poder seleccionar el estado para cualquier momento dado. La solución suele ser tablas "dimensionales". Sin embargo, mientras resuelven el problema de DW "quién fue el representante de ventas en enero pasado". No proporcionan ninguna de las ventajas que ofrecen las clases inmutables de Javas.

En una nota más filosófica; existen bases de datos para almacenar el "estado" (saldo bancario, consumo de electricidad, puntos de brownie en StackOverflow, etc., etc.) intentar crear una base de datos "sin estado" parece un ejercicio bastante inútil.

James Anderson
fuente
Para un solo registro, WHERE id = {} ORDER BY updTime DESC LIMIT 1generalmente no es demasiado ineficiente.
Izkata
@Izkata - intente poner en el medio de una unión de tres mesas :-)
James Anderson