Armazenando JSON no banco de dados vs. tendo uma nova coluna para cada chave


211

Estou implementando o seguinte modelo para armazenar dados relacionados ao usuário em minha tabela - eu tenho 2 colunas - uid(chave primária) e uma metacoluna que armazena outros dados sobre o usuário no formato JSON.

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['foo@bar.com','bar@foo.com']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['sann@bar.com','sann@foo.com']}
--------------------------------------------------

É este um caminho melhor (em termos de performance, design-wise) do que o modelo de uma coluna-per-propriedade, onde a mesa terá muitas colunas como uid, name, emailid.

O que eu gosto no primeiro modelo é que você pode adicionar o maior número possível de campos, sem limitações.

Além disso, eu estava pensando, agora que implementei o primeiro modelo. Como faço para executar uma consulta, como, eu quero buscar todos os usuários que têm nome como 'foo'?

Pergunta - Qual é a melhor maneira de armazenar dados relacionados ao usuário (lembrando que o número de campos não é fixo) no banco de dados usando - JSON ou coluna por campo? Além disso, se o primeiro modelo for implementado, como consultar o banco de dados conforme descrito acima? Devo usar os dois modelos, armazenando todos os dados que podem ser pesquisados ​​por uma consulta em uma linha separada e os outros dados em JSON (é uma linha diferente)?


Atualizar

Como não haverá muitas colunas nas quais eu preciso executar a pesquisa, é aconselhável usar os dois modelos? Chave por coluna para os dados que preciso pesquisar e JSON por outros (no mesmo banco de dados MySQL)?


40
ótima pergunta! mas por que você não aceitou uma resposta? que ajudaria outros usuários (como eu)
Sahar Ch.

Respostas:


197

Actualizado 4 de junho de 2017

Como essa pergunta / resposta ganhou popularidade, achei que valeria a pena ser atualizado.

Quando esta pergunta foi publicada originalmente, o MySQL não tinha suporte para tipos de dados JSON e o suporte no PostgreSQL estava em sua infância. Desde 5.7, o MySQL agora suporta um tipo de dados JSON (em um formato de armazenamento binário) e o PostgreSQL JSONB amadureceu significativamente. Ambos os produtos fornecem tipos JSON de alto desempenho que podem armazenar documentos arbitrários, incluindo suporte para indexar chaves específicas do objeto JSON.

No entanto, continuo defendendo minha afirmação original de que sua preferência padrão, ao usar um banco de dados relacional, ainda deve ser coluna por valor. Os bancos de dados relacionais ainda são construídos com a suposição de que os dados dentro deles serão razoavelmente bem normalizados. O planejador de consultas possui melhores informações de otimização ao examinar colunas do que ao examinar chaves em um documento JSON. Chaves estrangeiras podem ser criadas entre colunas (mas não entre chaves em documentos JSON). Importante: se a maioria do seu esquema for volátil o suficiente para justificar o uso de JSON, convém pelo menos considerar se um banco de dados relacional é a escolha certa.

Dito isto, poucas aplicações são perfeitamente relacionais ou orientadas a documentos. A maioria dos aplicativos possui uma mistura de ambos. Aqui estão alguns exemplos em que eu pessoalmente achei o JSON útil em um banco de dados relacional:

  • Ao armazenar endereços de email e números de telefone para um contato, é muito mais fácil gerenciar o armazenamento deles como valores em uma matriz JSON do que em várias tabelas separadas

  • Salvando preferências arbitrárias do usuário da chave / valor (onde o valor pode ser booleano, textual ou numérico e você não deseja ter colunas separadas para diferentes tipos de dados)

  • Armazenando dados de configuração que não possuem esquema definido (se você estiver criando o Zapier ou o IFTTT e precisar armazenar dados de configuração para cada integração)

Tenho certeza de que existem outros também, mas esses são apenas alguns exemplos rápidos.

Resposta original

Se você realmente deseja adicionar quantos campos quiser, sem limitação (que não seja um limite arbitrário de tamanho de documento), considere uma solução NoSQL como o MongoDB.

Para bancos de dados relacionais: use uma coluna por valor. Colocar um blob JSON em uma coluna torna praticamente impossível consultar (e muito lento quando você realmente encontra uma consulta que funciona).

Os bancos de dados relacionais aproveitam os tipos de dados durante a indexação e devem ser implementados com uma estrutura normalizada .

Como uma observação lateral: isso não significa que você nunca deve armazenar JSON em um banco de dados relacional. Se você estiver adicionando metadados verdadeiros ou se o seu JSON estiver descrevendo informações que não precisam ser consultadas e são usadas apenas para exibição, pode ser um exagero criar uma coluna separada para todos os pontos de dados.


1
Como não haverá muitas colunas nas quais eu preciso executar a pesquisa, é aconselhável usar os dois modelos? Chave por coluna para os dados que preciso pesquisar e JSON por outros (no mesmo banco de dados MySQL)?
ShuklaSannidhya

3
@Ann Você deve usar uma coluna por valor para os dados que deseja ler ou consultar com frequência. Colocar o nome de alguém no JSON não faz sentido, porque, mesmo que você não faça uma consulta com base nele, provavelmente precisará dele com muita frequência. Isso representa muita decodificação desnecessária no lado do aplicativo. A menos que você realmente sinta que seus dados estão melhor representados como JSON (e confie em mim, provavelmente não), você não deve recorrer a isso.
Colin M

5
" virtually impossible to query" - hoje psql permite-lhe procurar e indexar seu jsonb
ted

1
@ted true. No entanto, no momento da redação desta resposta, ela não estava realmente disponível. Além disso, esta pergunta faz referência ao MySQL no qual a capacidade não está presente.
Colin M

3
@ CololinM, sim, eu sei que meu comentário é 3 anos mais novo que o seu post. A razão pela qual saí é porque pode ser útil e a decisão pode mudar para os outros. Quanto à referência ao MySQL: poderia ser verdade, mas tem "For relational databases"em sua resposta = P
ted

68

Como a maioria das coisas "depende". Não é certo ou errado / bom ou ruim por si só armazenar dados em colunas ou JSON. Depende do que você precisa fazer com isso mais tarde. Qual é a sua maneira prevista de acessar esses dados? Você precisará cruzar outros dados?

Outras pessoas responderam muito bem quais são as vantagens técnicas.

Poucas pessoas discutiram que seu aplicativo e recursos evoluem com o tempo e como essa decisão de armazenamento de dados afeta sua equipe.

Como uma das tentações de usar o JSON é evitar a migração do esquema e, portanto, se a equipe não for disciplinada, é muito fácil colar outro par de chave / valor em um campo JSON. Não há migração para isso, ninguém se lembra para que serve. Não há validação nele.

Minha equipe usou o JSON ao longo das colunas tradicionais no postgres e, a princípio, era a melhor coisa desde o pão fatiado. O JSON era atraente e poderoso, até que um dia percebemos que a flexibilidade tinha um custo e, de repente, é um verdadeiro problema. Às vezes, esse ponto surge muito rapidamente e fica difícil mudar, porque criamos muitas outras coisas em cima dessa decisão de design.

Horas extras, adicionando novos recursos, tendo os dados em JSON, resultaram em consultas de aparência mais complicada do que o que poderia ter sido adicionado se mantivéssemos as colunas tradicionais. Então começamos a pescar certos valores-chave em colunas para que pudéssemos fazer junções e fazer comparações entre valores. Péssima ideia. Agora tivemos duplicação. Um novo desenvolvedor viria a bordo e ficaria confuso? Qual é o valor que eu devo salvar novamente? O JSON ou a coluna?

Os campos JSON tornaram-se gavetas de lixo eletrônico para pequenos pedaços disso e daquilo. Sem validação de dados no nível do banco de dados, sem consistência ou integridade entre documentos. Isso empurrou toda essa responsabilidade para o aplicativo, em vez de obter uma verificação rígida de tipo e restrição de colunas tradicionais.

Olhando para trás, o JSON nos permitiu iterar muito rapidamente e obter algo fora da porta. Foi ótimo. No entanto, depois que atingimos um determinado tamanho de equipe, a flexibilidade também nos permitiu ficar com uma longa corda de dívida técnica, que diminuiu o progresso subsequente na evolução dos recursos. Use com cuidado.

Pense muito sobre qual é a natureza dos seus dados. É a base do seu aplicativo. Como os dados serão usados ​​ao longo do tempo. E como é provável que mude?


6
"sua flexibilidade também nos permitiu nos pendurar com uma longa corda de dívida técnica" metáfora muito agradável!
Antoine Gallix

Depois de muitos anos de desenvolvimento e trabalhando com pessoas diferentes, se eu escrever sobre esse assunto, escreverei a mesma coisa. Existem tantos desenvolvedores agora, onde muitos deles, mesmo com anos de experiência, não conseguem subir de nível. Temos que manter tudo simples e para mim as duas coisas que sempre devemos considerar que podem "estruturar" o sucesso é a escalabilidade e a manutenção do código.
JohnnyJaxs 10/03

27

Apenas lançando-o por aí, mas o WordPress tem uma estrutura para esse tipo de coisa (pelo menos o WordPress foi o primeiro lugar em que o observei, provavelmente se originou em outro lugar).

Ele permite chaves ilimitadas e é mais rápido pesquisar do que usar um blob JSON, mas não tão rápido quanto algumas das soluções NoSQL.

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

EDITAR

Para armazenar histórico / várias chaves

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
.................

e consulta através de algo como isto:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc

1
Eu ficaria curioso para ver se uma solução NoSQL realmente funciona melhor do que uma consulta relacional em uma chave de índice corretamente. Eu suspeitaria que deveria ser mais ou menos o mesmo em um exemplo de nível 1 como este.
de Bruno

+1. Eu também notei! Mas fornece uma tabela enorme (em termos de linhas). Além disso, você não pode armazenar vários valores, por exemplo, se o usuário alterar seu nome, mas também quero preservar o nome antigo; nesse caso, precisarei do modelo de dados do tipo JSON.
ShuklaSannidhya 31/03/2019

@ Sann, se você quiser manter o valor antigo em JSON, também precisará renomear a chave: você pode fazer isso com um EAV (que é o que este exemplo é) ou JSON. Não é particularmente diferente.
Bruno

Ele fornece uma tabela enorme, mas, quanto aos valores duplicados, você encontra o mesmo problema com o JSON - não pode ter chaves duplicadas no mesmo nível (por exemplo, duas chaves de "nome") e espera um comportamento previsível.
Adam

Claro que você não pode ter chaves duplicadas, mas pode ter uma matriz associada a essa chave. Confira a emailidchave no exemplo que dei na minha pergunta.
ShuklaSannidhya 12/03

13

a desvantagem da abordagem é exatamente o que você mencionou:

torna MUITO lento encontrar coisas, pois cada vez que você precisa fazer uma pesquisa de texto nela.

o valor por coluna corresponde à sequência inteira.

Sua abordagem (dados baseados em JSON) é adequada para dados pelos quais você não precisa pesquisar e só precisa exibir junto com seus dados normais.

Edit: Apenas para esclarecer, o exposto acima vale para bancos de dados relacionais clássicos. O NoSQL usa JSON internamente e provavelmente é uma opção melhor se esse for o comportamento desejado.


1
Então você quer dizer, eu devo usar os dois. Chave por coluna para os dados que preciso pesquisar e JSON para outros, certo?
ShuklaSannidhya 31/03/2019

4
sim. dessa maneira, você obtém o desempenho necessário pesquisando os campos de dados por coluna e pega o blob JSON para usar no código quando necessário.
Nick Andriopoulos

9

Basicamente, o primeiro modelo que você está usando é chamado de armazenamento baseado em documento. Você deve dar uma olhada no popular banco de dados NoSQL baseado em documentos, como MongoDB e CouchDB . Basicamente, nos bancos de dados baseados em documentos, você armazena dados em arquivos json e pode consultar esses arquivos json.

O segundo modelo é a estrutura popular de banco de dados relacional.

Se você quiser usar o banco de dados relacional como o MySql, sugiro que você use apenas o segundo modelo. Não faz sentido usar o MySql e armazenar dados como no primeiro modelo .

Para responder sua segunda pergunta, não há como consultar um nome como 'foo' se você usar o primeiro modelo .


É aconselhável usar os dois modelos? Chave por coluna para os dados que preciso pesquisar e JSON por outros (no mesmo banco de dados)?
ShuklaSannidhya

@Sann - haha. Isso é duplicação de dados. Você precisará garantir que as duas partes dos dados sejam sempre as mesmas. Mesmo que os dados sejam diferentes a qualquer momento, eles não estão limpos e podem levar a sérios problemas. Então, minha resposta é NÃO
Girish

Mas a redundância não é dispendiosa quando os dados redundantes são pequenos, por exemplo, existem apenas dois campos nos quais eu preciso executar a pesquisa, por isso crio duas novas colunas para eles, [talvez] os remova dos meus dados JSON [/ talvez] . Isso não será duplicação cara, certo?
ShuklaSannidhya

Se você está olhando para o desempenho, o MongoDB e o CouchDB fornecem operações de leitura e gravação mais rápidas que o MySql, porque eles não oferecem muitos recursos em bancos de dados relacionais que não são necessários na maioria dos casos de uso.
Girish

O benefício não poderia ser o armazenamento de objetos JSON / retornos de chamada de uma API? Por exemplo, em vez de chamar a API do YouTube para URL, polegar, etc., você pode simplesmente consultar seu banco de dados local (mysql, lite etc.) para o objeto JSON? Não sei, faz sentido para mim, especialmente se você estiver tentando fazer cache ou fazer com que um aplicativo seja executado mais rapidamente. Mas eu não sou profissional: /
markbratanov 26/03

4

Parece que você está principalmente hesitando em usar ou não um modelo relacional.

Tal como está, seu exemplo se encaixaria razoavelmente bem em um modelo relacional, mas o problema pode surgir naturalmente quando você precisar fazer esse modelo evoluir.

Se você tiver apenas um (ou alguns níveis predeterminados) de atributos para sua entidade principal (usuário), ainda poderá usar um modelo de valor de atributo de entidade (EAV) em um banco de dados relacional. (Isso também tem seus prós e contras.)

Se você prevê que obterá valores menos estruturados que deseja pesquisar usando seu aplicativo, o MySQL pode não ser a melhor opção aqui.

Se você estivesse usando o PostgreSQL, poderia obter o melhor dos dois mundos. (Isso realmente depende da estrutura real dos dados aqui ... O MySQL também não é necessariamente a escolha errada, e as opções NoSQL podem ser de seu interesse, só estou sugerindo alternativas.)

De fato, o PostgreSQL pode criar índice em funções (imutáveis) (que o MySQL não pode, tanto quanto eu sei) e em versões recentes, você pode usar o PLV8 nos dados JSON diretamente para criar índices em elementos JSON específicos de interesse, o que melhoraria a velocidade das suas consultas ao pesquisar esses dados.

EDITAR:

Como não haverá muitas colunas nas quais eu preciso executar a pesquisa, é aconselhável usar os dois modelos? Chave por coluna para os dados que preciso pesquisar e JSON por outros (no mesmo banco de dados MySQL)?

A mistura dos dois modelos não é necessariamente errada (assumindo que o espaço extra é desprezível), mas pode causar problemas se você não garantir que os dois conjuntos de dados sejam mantidos sincronizados: seu aplicativo nunca deve alterar um sem também atualizar o outro .

Uma boa maneira de conseguir isso seria fazer com que um gatilho execute a atualização automática executando um procedimento armazenado no servidor de banco de dados sempre que uma atualização ou inserção for feita. Tanto quanto sei, a linguagem de procedimentos armazenados do MySQL provavelmente não possui suporte para qualquer tipo de processamento JSON. Novamente, o PostgreSQL com suporte a PLV8 (e possivelmente outros RDBMS com linguagens de procedimento armazenado mais flexíveis) deve ser mais útil (atualizar sua coluna relacional automaticamente usando um gatilho é bastante semelhante a atualizar um índice da mesma maneira).


Além do que eu disse acima, pode valer a pena examinar os operadores para o tipo de dados JSONB no PostgreSQL 9.4 e superior.
Bruno

1

algum tempo na mesa será uma sobrecarga. vamos dizer para OLAP. se eu tiver duas tabelas, uma é a tabela ORDERS e a outra é ORDER_DETAILS. Para obter todos os detalhes do pedido, precisamos unir duas tabelas, o que tornará a consulta mais lenta quando não aumentar o número de linhas nas tabelas, digamos milhões ou mais .. a junção esquerda / direita é muito mais lenta que a junção interna. Acho que se adicionarmos JSON string / Object na respectiva entrada ORDERS, JOIN será evitado. adicionar geração de relatórios será mais rápido ...


1

resposta curta que você precisa misturar entre eles, use json para dados que você não fará relações com eles, como dados de contato, endereço, variáveis ​​de produtos


0

Você está tentando ajustar um modelo não-relacional em um banco de dados relacional, acho que você seria melhor atendido usando um banco de dados NoSQL, como MongoDB . Não existe um esquema predefinido que atenda ao seu requisito de não ter limitação ao número de campos (consulte o exemplo típico da coleção do MongoDB). Confira a documentação do MongoDB para ter uma idéia de como você consultaria seus documentos, por exemplo,

db.mycollection.find(
    {
      name: 'sann'
    }
)

2
Por curiosidade, o que fez você assumir que o modelo dele não é relacional. As informações que ele colocou acima parecem muito relacionais para mim.
Colin M

0

Como outros já apontaram, as consultas serão mais lentas. Eu sugiro adicionar pelo menos uma coluna '_ID' para consulta por isso.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.