Almacenar JSON en la base de datos frente a tener una nueva columna para cada clave

215

Estoy implementando el siguiente modelo para almacenar datos relacionados con el usuario en mi tabla (tengo 2 columnas) uid(clave principal) y una metacolumna que almacena otros datos sobre el usuario en formato JSON.

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------

Es esta una manera mejor (en cuanto al rendimiento, diseño inteligente) que el modelo de una columna-por-propiedad, donde la mesa tendrá tantas columnas como uid, name, emailid.

Lo que me gusta del primer modelo es que puede agregar tantos campos como sea posible, no hay limitación.

Además, me preguntaba, ahora que he implementado el primer modelo. ¿Cómo realizo una consulta en él, como, quiero buscar a todos los usuarios que tienen nombre como 'foo'?

Pregunta : ¿Cuál es la mejor manera de almacenar datos relacionados con el usuario (teniendo en cuenta que el número de campos no es fijo) en la base de datos usando - JSON o columna por campo? Además, si se implementa el primer modelo, ¿cómo consultar la base de datos como se describió anteriormente? ¿Debo usar ambos modelos, almacenando todos los datos que pueden ser buscados por una consulta en una fila separada y los otros datos en JSON (es una fila diferente)?


Actualizar

Dado que no habrá demasiadas columnas en las que deba realizar la búsqueda, ¿es aconsejable utilizar ambos modelos? ¿Clave por columna para los datos que necesito buscar y JSON para otros (en la misma base de datos MySQL)?

ShuklaSannidhya
fuente
40
gran pregunta! pero ¿por qué no aceptaste una respuesta? eso ayudaría a otros usuarios (como yo)
Sahar Ch.

Respuestas:

200

Actualizado 4 de junio de 2017

Dado que esta pregunta / respuesta ha ganado cierta popularidad, pensé que valía la pena una actualización.

Cuando esta pregunta se publicó originalmente, MySQL no tenía soporte para los tipos de datos JSON y el soporte en PostgreSQL estaba en su infancia. Desde 5.7, MySQL ahora admite un tipo de datos JSON (en un formato de almacenamiento binario), y PostgreSQL JSONB ha madurado significativamente. Ambos productos proporcionan tipos JSON con rendimiento que pueden almacenar documentos arbitrarios, incluido el soporte para indexar claves específicas del objeto JSON.

Sin embargo, aún mantengo mi afirmación original de que su preferencia predeterminada, cuando usa una base de datos relacional, aún debe ser columna por valor. Las bases de datos relacionales todavía se basan en el supuesto de que los datos dentro de ellas estarán bastante bien normalizados. El planificador de consultas tiene mejor información de optimización al mirar columnas que al mirar claves en un documento JSON. Las claves externas se pueden crear entre columnas (pero no entre claves en documentos JSON). Importante: si la mayoría de su esquema es lo suficientemente volátil como para justificar el uso de JSON, es posible que desee al menos considerar si una base de datos relacional es la opción correcta.

Dicho esto, pocas aplicaciones son perfectamente relacionales u orientadas a documentos. La mayoría de las aplicaciones tienen una mezcla de ambos. Aquí hay algunos ejemplos en los que personalmente he encontrado que JSON es útil en una base de datos relacional:

  • Al almacenar direcciones de correo electrónico y números de teléfono para un contacto, donde almacenarlos como valores en una matriz JSON es mucho más fácil de administrar que varias tablas separadas

  • Guardar preferencias arbitrarias de usuario de clave / valor (donde el valor puede ser booleano, textual o numérico, y no desea tener columnas separadas para diferentes tipos de datos)

  • Almacenar datos de configuración que no tienen un esquema definido (si está creando Zapier o IFTTT y necesita almacenar datos de configuración para cada integración)

Estoy seguro de que también hay otros, pero estos son solo algunos ejemplos rápidos.

Respuesta original

Si realmente desea poder agregar tantos campos como desee sin limitación (aparte de un límite de tamaño de documento arbitrario), considere una solución NoSQL como MongoDB.

Para bases de datos relacionales: use una columna por valor. Poner un blob JSON en una columna hace que sea prácticamente imposible realizar consultas (y muy lento cuando realmente encuentra una consulta que funciona).

Las bases de datos relacionales aprovechan los tipos de datos al indexar, y están destinadas a implementarse con un sistema normalizado. estructura .

Como nota al margen: esto no quiere decir que nunca debe almacenar JSON en una base de datos relacional. Si está agregando metadatos verdaderos, o si su JSON está describiendo información que no necesita consultarse y solo se usa para mostrar, puede ser excesivo crear una columna separada para todos los puntos de datos.

Colin M
fuente
1
Dado que no habrá demasiadas columnas en las que deba realizar la búsqueda, ¿es aconsejable utilizar ambos modelos? ¿Clave por columna para los datos que necesito buscar y JSON para otros (en la misma base de datos MySQL)?
ShuklaSannidhya
3
@Sann Debe usar una columna por valor para los datos que desea leer o consultar con frecuencia. Poner el nombre de alguien en JSON no tiene sentido porque, aunque no es probable que consultes en base a él, es probable que lo necesites con mucha frecuencia. Esa es una gran cantidad de decodificación innecesaria en el lado de la aplicación. A menos que realmente sienta que sus datos están mejor representados como JSON (y confíe en mí, probablemente no lo sea), no debería recurrir a eso.
Colin M
55
" virtually impossible to query" - hoy psql le permite buscar e indexar su jsonb
ted
1
@ted verdadero. Sin embargo, al momento de escribir esta respuesta no estaba realmente disponible. Además, esta pregunta hace referencia a MySQL en el que su capacidad no está presente.
Colin M
3
@ColinM, sí, me doy cuenta de que mi comentario es 3 años más joven que tu publicación. La razón por la que lo dejé es porque puede ser útil y cambiar las decisiones para otros. En cuanto a la referencia a MySQL: podría ser cierto, pero tener "For relational databases"en su respuesta = P
ted
69

Como la mayoría de las cosas "depende". No es correcto o incorrecto / bueno o malo en sí mismo almacenar datos en columnas o JSON. Depende de lo que necesite hacer más tarde. ¿Cuál es su forma prevista de acceder a estos datos? ¿Necesitará hacer referencia cruzada a otros datos?

Otras personas han respondido bastante bien cuáles son las compensaciones técnicas.

No muchas personas han discutido que su aplicación y características evolucionan con el tiempo y cómo esta decisión de almacenamiento de datos afecta a su equipo.

Debido a que una de las tentaciones de usar JSON es evitar la migración del esquema y, por lo tanto, si el equipo no es disciplinado, es muy fácil pegar otro par clave / valor en un campo JSON. No hay migración para ello, nadie recuerda para qué sirve. No hay validación en ello.

Mi equipo usó JSON junto a columnas tradicionales en postgres y al principio fue lo mejor desde el pan rebanado. JSON era atractivo y poderoso, hasta que un día nos dimos cuenta de que la flexibilidad tenía un costo y de repente es un verdadero problema. A veces, ese punto avanza muy rápido y luego se vuelve difícil de cambiar porque hemos construido muchas otras cosas además de esta decisión de diseño.

Las horas extraordinarias, al agregar nuevas funciones, tener los datos en JSON condujeron a consultas de aspecto más complicadas que las que podrían haberse agregado si nos limitáramos a las columnas tradicionales. Entonces, comenzamos a buscar ciertos valores clave en columnas para poder hacer uniones y hacer comparaciones entre valores. Mala idea. Ahora tuvimos duplicación. ¿Un nuevo desarrollador se uniría y se confundiría? ¿Cuál es el valor en el que debería estar guardando? ¿El JSON o la columna?

Los campos JSON se convirtieron en cajones basura para pequeñas piezas de esto y aquello. Sin validación de datos a nivel de base de datos, sin consistencia o integridad entre documentos. Eso llevó toda esa responsabilidad a la aplicación en lugar de obtener un tipo duro y una verificación de restricciones de las columnas tradicionales.

Mirando hacia atrás, JSON nos permitió iterar muy rápidamente y sacar algo por la puerta. Fue grandioso. Sin embargo, después de alcanzar un cierto tamaño de equipo, su flexibilidad también nos permitió colgarnos de una larga cuerda de deuda técnica que luego ralentizó el progreso posterior de la evolución de las características. Usar con precaución.

Piense detenidamente sobre la naturaleza de sus datos. Es la base de tu aplicación. Cómo se utilizarán los datos a lo largo del tiempo. ¿Y cómo es probable que CAMBIE?

Homan
fuente
77
"su flexibilidad también nos permitió ahorcarnos con una larga cuerda de deuda técnica" ¡muy buena metáfora!
Antoine Gallix
Después de muchos años de desarrollo y trabajo con diferentes personas, si escribiera sobre este tema escribiría lo mismo. Hay tantos desarrolladores ahora, y muchos de ellos, incluso con años de experiencia, en realidad no suben de nivel. Tenemos que mantener todo simple y para mí las 2 cosas que siempre debemos considerar que pueden "enmarcar" el éxito es la escalabilidad y la capacidad de mantenimiento del código.
JohnnyJaxs
27

Solo lo lancé, pero WordPress tiene una estructura para este tipo de cosas (al menos WordPress fue el primer lugar donde lo observé, probablemente se originó en otro lugar).

Permite claves ilimitadas y es más rápido buscar que usar un blob JSON, pero no tan rápido como algunas de las soluciones NoSQL.

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

EDITAR

Para almacenar historial / claves múltiples

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

y consulta a través de algo como esto:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
Adán
fuente
1
Me gustaría ver si una solución NoSQL realmente funciona mejor que una consulta relacional en una clave de índice adecuada. Sospecho que debería ser más o menos lo mismo en un ejemplo de 1 nivel como este.
Bruno
+1. ¡También lo he notado! Pero te da una tabla enorme (en términos de filas). También usted puede no almacenar varios valores, por ejemplo, si el usuario cambia su / su nombre, pero quiero conservar el antiguo nombre también, en ese caso voy a necesitar JSON modelo de datos de tipo.
ShuklaSannidhya
@Sann, si quisieras mantener el valor anterior en JSON, también deberías cambiar el nombre de la clave: puedes hacerlo con un EAV (que es este ejemplo) o JSON. No es particularmente diferente.
Bruno
Le da una gran tabla, pero en cuanto a los valores duplicados, se encuentra con el mismo problema con JSON: no puede tener claves duplicadas en el mismo nivel (por ejemplo, dos claves "nombre") y esperar un comportamiento predecible.
Adam
Claro que no puede tener claves duplicadas, pero puede tener una matriz asociada con esa clave. Echa un vistazo a la emailidclave en el ejemplo que he dado en mi pregunta.
ShuklaSannidhya
13

El inconveniente del enfoque es exactamente lo que mencionó:

hace que sea MUY lento para encontrar cosas, ya que cada vez que necesita realizar una búsqueda de texto en él.

el valor por columna coincide con la cadena completa.

Su enfoque (datos basados ​​en JSON) está bien para los datos que no necesita buscar, y solo necesita mostrar junto con sus datos normales.

Editar: solo para aclarar, lo anterior se aplica a las bases de datos relacionales clásicas. NoSQL usa JSON internamente, y probablemente sea una mejor opción si ese es el comportamiento deseado.

Nick Andriopoulos
fuente
1
¿Quieres decir que debería usar ambos? Clave por columna para los datos que necesito buscar y JSON para otros, ¿verdad?
ShuklaSannidhya
44
si. de esa manera, obtiene el rendimiento requerido al buscar en los campos de datos por columna y toma el blob JSON para usarlo en el código cuando sea necesario.
Nick Andriopoulos
9

Básicamente, el primer modelo que está utilizando se llama almacenamiento basado en documentos. Debe echar un vistazo a la popular base de datos basada en documentos NoSQL como MongoDB y CouchDB . Básicamente, en bases de datos basadas en documentos, usted almacena datos en archivos json y luego puede consultar estos archivos json.

El segundo modelo es la estructura de base de datos relacional popular.

Si desea usar una base de datos relacional como MySql, le sugiero que use solo el segundo modelo. No tiene sentido usar MySql y almacenar datos como en el primer modelo .

Para responder a su segunda pregunta, no hay forma de consultar el nombre como 'foo' si usa el primer modelo .

Girish
fuente
¿Es aconsejable usar ambos modelos? ¿Clave por columna para los datos que necesito buscar y JSON para otros (en la misma base de datos)?
ShuklaSannidhya
@Sann - jaja. Eso es duplicación de datos. Deberá asegurarse de que ambos datos sean siempre iguales. Incluso si uno de los datos es diferente en cualquier momento, entonces sus datos no están limpios y pueden ocasionar problemas serios. Entonces, mi respuesta es NO
Girish
Pero la redundancia no es costosa cuando los datos redundantes son pequeños, por ejemplo, solo hay dos campos en los que necesito realizar una búsqueda, por lo que creo dos nuevas columnas para ellos, [tal vez] eliminarlos de mis datos JSON [/ tal vez] . Eso no será una duplicación costosa, ¿verdad?
ShuklaSannidhya
Si está buscando rendimiento, MongoDB y CouchDB proporcionan operaciones de lectura y escritura más rápidas que MySql porque no ofrecen muchas características en bases de datos relacionales que no son necesarias en la mayoría de los casos de uso.
Girish
¿No podría el beneficio estar almacenando objetos JSON / devoluciones de llamada desde una API? Por ejemplo, en lugar de llamar a la API de youtube para URL, pulgar, etc., ¿podría simplemente consultar su DB local (mysql, lite, etc.) para el objeto JSON? No lo sé, tiene sentido para mí, especialmente si está intentando almacenar en caché o hacer que una aplicación se ejecute más rápido. Pero no soy profesional: /
markbratanov
4

Parece que estás dudando principalmente si usar un modelo relacional o no.

Tal como está, su ejemplo encajaría en un modelo relacional razonablemente bien, pero el problema puede surgir, por supuesto, cuando necesite hacer que este modelo evolucione.

Si solo tiene uno (o algunos niveles predeterminados) de atributos para su entidad principal (usuario), aún podría usar un modelo de Valor de atributo de entidad (EAV) en una base de datos relacional. (Esto también tiene sus pros y sus contras).

Si anticipa que obtendrá valores menos estructurados que desea buscar utilizando su aplicación, MySQL podría no ser la mejor opción aquí.

Si estaba utilizando PostgreSQL, podría obtener lo mejor de ambos mundos. (Esta realmente depende de la estructura real de los datos aquí ... MySQL tampoco es necesariamente la elección incorrecta, y las opciones NoSQL pueden ser de interés, solo estoy sugiriendo alternativas).

De hecho, PostgreSQL puede construir índices en funciones (inmutables) (que MySQL no puede, hasta donde yo sé) y en versiones recientes, podría usar PLV8 en los datos JSON directamente para construir índices en elementos JSON específicos de interés, lo que mejoraría la velocidad de sus consultas al buscar esos datos.

EDITAR:

Dado que no habrá demasiadas columnas en las que deba realizar la búsqueda, ¿es aconsejable utilizar ambos modelos? ¿Clave por columna para los datos que necesito buscar y JSON para otros (en la misma base de datos MySQL)?

Mezclar los dos modelos no es necesariamente incorrecto (suponiendo que el espacio extra sea insignificante), pero puede causar problemas si no se asegura de que los dos conjuntos de datos estén sincronizados: su aplicación nunca debe cambiar uno sin actualizar el otro .

Una buena manera de lograr esto sería hacer que un disparador realice la actualización automática, ejecutando un procedimiento almacenado dentro del servidor de la base de datos cada vez que se realice una actualización o inserción. Hasta donde yo sé, el lenguaje de procedimiento almacenado MySQL probablemente carece de soporte para cualquier tipo de procesamiento JSON. Una vez más, PostgreSQL con soporte PLV8 (y posiblemente otros RDBMS con lenguajes de procedimientos almacenados más flexibles) debería ser más útil (actualizar su columna relacional automáticamente usando un disparador es bastante similar a actualizar un índice de la misma manera).

Bruno
fuente
Además de lo que dije anteriormente, puede valer la pena mirar a los operadores para el tipo de datos JSONB en PostgreSQL 9.4 y superior.
Bruno
1

algún tiempo se une en la mesa será una sobrecarga. digamos para OLAP. si tengo dos tablas, una es la tabla ORDERS y la otra es ORDER_DETAILS. Para obtener todos los detalles del pedido, tenemos que unir dos tablas, esto hará que la consulta sea más lenta cuando ninguna de las filas en las tablas aumente, digamos en millones más o menos ... la unión izquierda / derecha es demasiado lenta que la unión interna. Creo que si agregamos cadena / objeto JSON en la entrada ORDERS respectiva, JOIN se evitará. agregar generación de informes será más rápido ...

Ravindra
fuente
1

respuesta corta que debe mezclar entre ellos, use json para datos que no va a establecer relaciones con ellos, como datos de contacto, dirección, variables de productos

Ahmedfraije Aa
fuente
0

Está intentando ajustar un modelo no relacional en una base de datos relacional, creo que sería mejor utilizar una base de datos NoSQL como MongoDB . No existe un esquema predefinido que se ajuste a su requisito de no tener limitaciones en el número de campos (consulte el ejemplo típico de la colección MongoDB). Consulte la documentación de MongoDB para tener una idea de cómo consultaría sus documentos, p. Ej.

db.mycollection.find(
    {
      name: 'sann'
    }
)
Chris L
fuente
2
Por curiosidad, lo que te hizo suponer que su modelo no es relacional. La información que puso arriba me parece muy relacional.
Colin M
0

Como otros han señalado, las consultas serán más lentas. Sugeriría agregar al menos una columna '_ID' para consultar por eso en su lugar.

Pantalones
fuente