Por que as junções são ruins quando se considera a escalabilidade?


92

Por que as junções são ruins ou 'lentas'. Eu sei que ouvi isso mais uma vez. Eu encontrei esta citação

O problema é que as junções são relativamente lentas, especialmente em conjuntos de dados muito grandes, e se forem lentas, seu site ficará lento. Leva muito tempo para tirar todas essas informações do disco e colocá-las todas juntas novamente.

fonte

Sempre pensei que eles eram rápidos, especialmente quando procurava um PK. Por que eles são 'lentos'?

sql  join 

Respostas:


98

Escalabilidade tem tudo a ver com pré-computação, espalhando ou reduzindo o trabalho repetido ao essencial, a fim de minimizar o uso de recursos por unidade de trabalho. Para escalar bem, você não faz nada que não precisa em volume e as coisas que você realmente faz são feitas com a maior eficiência possível.

Nesse contexto, é claro que juntar duas fontes de dados separadas é relativamente lento, pelo menos em comparação com não juntá-las, porque é um trabalho que você precisa fazer ao vivo no ponto em que o usuário o solicita.

Mas lembre-se de que a alternativa é não ter mais duas partes separadas de dados; você tem que colocar os dois pontos de dados diferentes no mesmo registro. Você não pode combinar duas partes diferentes de dados sem uma consequência em algum lugar, então certifique-se de entender a compensação.

A boa notícia é que os bancos de dados relacionais modernos são bons em associações. Você não deve realmente pensar em junções como lentas com um bom banco de dados bem usado. Existem várias maneiras amigáveis ​​de escalabilidade para obter junções brutas e torná-las muito mais rápidas:

  • Junte-se a uma chave substituta (coluna autonumer / identidade) em vez de uma chave natural. Isso significa comparações menores (e, portanto, mais rápidas) durante a operação de junção
  • Índices
  • Visualizações materializadas / indexadas (pense nisso como uma junção pré-calculada ou desnormalização gerenciada )
  • Colunas calculadas. Você pode usar isso para hash ou de outra forma pré-calcular as colunas-chave de uma junção, de forma que o que seria uma comparação complicada para uma junção agora seja muito menor e potencialmente pré-indexado.
  • Partições de tabela (ajuda com grandes conjuntos de dados, distribuindo a carga em vários discos ou limitando o que poderia ter sido uma análise de tabela para uma análise de partição)
  • OLAP (pré-calcula os resultados de certos tipos de consultas / junções. Não é bem verdade, mas você pode pensar nisso como uma desnormalização genérica )
  • Replicação, grupos de disponibilidade, envio de log ou outros mecanismos para permitir que vários servidores respondam a consultas de leitura para o mesmo banco de dados e, assim, dimensione sua carga de trabalho entre vários servidores.
  • Uso de uma camada de cache como o Redis para evitar a repetição de consultas que precisam de junções complexas.

Eu iria mais longe e diria que a principal razão pela qual os bancos de dados relacionais existem é para permitir que você faça junções com eficiência * . Certamente não é apenas para armazenar dados estruturados (você pode fazer isso com construções de arquivo simples como csv ou xml). Algumas das opções que listei permitirão que você construa completamente sua junção com antecedência, de modo que os resultados já estejam prontos antes de você emitir a consulta - como se você tivesse desnormalizado os dados (reconhecidamente ao custo de operações de gravação mais lentas).

Se você tiver uma junção lenta, provavelmente não está usando o banco de dados corretamente.

A desnormalização deve ser feita somente depois que essas outras técnicas falharem. E a única maneira de realmente julgar o "fracasso" é definir metas de desempenho significativas e comparar essas metas. Se você não mediu, é muito cedo para pensar na desnormalização.

* Ou seja, existem como entidades distintas de meras coleções de tabelas. Um motivo adicional para um rdbms real é o acesso simultâneo seguro.


14
Os índices provavelmente devem estar no topo da lista. Muitos desenvolvedores ( tossem ) parecem esquecê-los ao testar em um pequeno conjunto de dados e, em seguida, colocam o banco de dados de joelhos na produção. Tenho visto consultas que são executadas na ordem de 100.000 vezes mais rápido simplesmente adicionando índices. E isso é índices arbitrários, mesmo sem fazer qualquer análise de dados em profundidade para determinar a melhor combinação para correspondência de prefixo mais à esquerda.
Duncan

Acho que tenho a ordem certa - apenas a maioria dos desenvolvedores já faz o primeiro item e, portanto, os índices são o primeiro item em que eles precisam fazer alterações.
Joel Coehoorn

Em seu terceiro item, você menciona "Visões materializadas / indexadas". Você está falando sobre visualizações regulares do SQL ou algo mais?
slolife

As visualizações sql regulares do @slolife são como executar uma consulta extra em segundo plano durante o uso de uma consulta que faz referência à visualização. Mas você também pode dizer ao sql server para "materializar" algumas visualizações. Quando você fizer isso, o sql server manterá uma cópia extra dos dados da visão, assim como uma tabela normal, de forma que quando você referenciar a visão em uma consulta, ele não precisará mais executar esta consulta em segundo plano porque os dados já estão lá . Você também pode colocar índices diferentes na exibição e não na tabela de origem, para ajudá-lo a ajustar o desempenho.
Joel Coehoorn

Obrigado Joel. Vou ter que olhar para isso.
slolife

29

As junções podem ser mais lentas do que evitá-las por meio da desnormalização, mas se usadas corretamente (junção em colunas com índices apropriados e assim por diante) elas não são inerentemente lentas .

A desnormalização é uma das muitas técnicas de otimização que você pode considerar se o seu esquema de banco de dados bem projetado exibir problemas de desempenho.


2
... exceto no MySQL, que parece ter problemas de desempenho com um grande número de junções, independentemente da aparência de seus índices. Ou pelo menos foi no passado.
Powerlord

2
Entendido, se houver problemas conhecidos com o DBMS específico (e talvez até mesmo com a versão), esse conselho pode fazer sentido, mas como um conselho geral, é bastante enganoso se você estiver usando um banco de dados relacional. Os referidos mecanismos de armazenamento não relacional estão se tornando mais populares. O SimpleDB e o CouchDB da Amazon ( couchdb.apache.org ) são exemplos. Se você se sentir melhor deixando o modelo relacional para trás, provavelmente também deve deixar os produtos otimizados para trás e procurar outras ferramentas.
Tendayi Mawushe

13

o artigo diz que eles são lentos quando comparados à ausência de junções. isso pode ser alcançado com a desnormalização. portanto, há uma compensação entre velocidade e normalização. não se esqueça da otimização prematura também :)


mesmo esta não é uma regra rígida, se você unir em uma tabela, mysql pode usar um índice para realizar essa união - essa união de índice pode remover muitos registros e outro índice para qualquer cláusula where nas tabelas. Se você não entrar, o mysql normalmente usará apenas um índice (que pode não ser o mais eficiente), não importa como sua cláusula where é formada.
leeeroy

11

Em primeiro lugar, a raison d'être (razão de ser) de um banco de dados relacional é ser capaz de modelar relacionamentos entre entidades. As junções são simplesmente os mecanismos pelos quais atravessamos esses relacionamentos. Eles certamente têm um custo nominal, mas sem junções, realmente não há razão para ter um banco de dados relacional.

No mundo acadêmico, aprendemos coisas como as várias formas normais (1ª, 2ª, 3ª, Boyce-Codd, etc.), e aprendemos sobre diferentes tipos de chaves (primária, estrangeira, alternativa, única, etc.) e como essas coisas se encaixam para criar um banco de dados. E aprendemos os rudimentos de SQL e também manipulamos a estrutura e os dados (DDL e DML).

No mundo corporativo, muitos dos conceitos acadêmicos se revelaram substancialmente menos viáveis ​​do que fomos levados a acreditar. Um exemplo perfeito é a noção de chave primária. Academicamente, é esse atributo (ou coleção de atributos) que identifica exclusivamente uma linha na tabela. Portanto, em muitos domínios de problemas, a chave primária acadêmica adequada é um composto de 3 ou 4 atributos. No entanto, quase todo mundo no mundo corporativo moderno usa um inteiro sequencial gerado automaticamente como a chave primária de uma tabela. Por quê? Duas razões. A primeira é porque torna o modelo muito mais limpo quando você está migrando FKs para todos os lugares. A segunda, e mais pertinente a essa questão, é que recuperar dados por meio de junções é mais rápido e mais eficiente em um único inteiro do que em 4 colunas varchar (como já foi mencionado por algumas pessoas).

Vamos cavar um pouco mais fundo agora em dois subtipos específicos de bancos de dados do mundo real. O primeiro tipo é um banco de dados transacional. Essa é a base para muitos aplicativos de e-commerce ou gerenciamento de conteúdo que conduzem sites modernos. Com um banco de dados de transação, você está otimizando fortemente em direção ao "rendimento da transação". A maioria dos aplicativos comerciais ou de conteúdo precisa equilibrar o desempenho de consulta (de certas tabelas) com o desempenho de inserção (em outras tabelas), embora cada aplicativo tenha seus próprios problemas específicos de negócios para resolver.

O segundo tipo de banco de dados do mundo real é um banco de dados de relatórios. Eles são usados ​​quase exclusivamente para agregar dados de negócios e gerar relatórios de negócios significativos. Eles são tipicamente moldados de forma diferente dos bancos de dados de transações onde os dados são gerados e são altamente otimizados para velocidade de carregamento de dados em massa (ETLs) e desempenho de consulta com conjuntos de dados grandes ou complexos.

Em cada caso, o desenvolvedor ou DBA precisa equilibrar cuidadosamente as curvas de funcionalidade e desempenho, e há muitos truques para melhorar o desempenho em ambos os lados da equação. No Oracle, você pode fazer o que é chamado de "plano de explicação" para ver especificamente como uma consulta é analisada e executada. Você está procurando maximizar o uso adequado de índices pelo banco de dados. Um não-não realmente desagradável é colocar uma função na cláusula where de uma consulta. Sempre que você fizer isso, você garante que o Oracle não usará nenhum índice nessa coluna específica e provavelmente verá uma varredura de tabela completa ou parcial no plano de explicação. Esse é apenas um exemplo específico de como uma consulta pode ser escrita que acaba sendo lenta e não tem nada a ver com junções.

E enquanto estamos falando sobre varreduras de tabela, elas obviamente afetam a velocidade da consulta proporcionalmente ao tamanho da tabela. Uma varredura completa da tabela de 100 linhas nem é perceptível. Execute a mesma consulta em uma tabela com 100 milhões de linhas e você precisará voltar na próxima semana para o retorno.

Vamos falar sobre normalização por um minuto. Este é outro tópico acadêmico amplamente positivo que pode ser excessivamente estressado. Na maioria das vezes, quando falamos sobre normalização, realmente queremos dizer a eliminação de dados duplicados, colocando-os em sua própria tabela e migrando um FK. As pessoas geralmente ignoram toda a coisa de dependência descrita por 2NF e 3NF. E ainda assim, em um caso extremo, certamente é possível ter um banco de dados BCNF perfeito que é enorme e uma besta completa para escrever código porque é muito normalizado.

Então, onde nos equilibramos? Não existe uma única resposta melhor. Todas as melhores respostas tendem a ser um meio-termo entre facilidade de manutenção da estrutura, facilidade de manutenção de dados e facilidade de criação / manutenção de código. Em geral, quanto menos duplicação de dados, melhor.

Então, por que as junções às vezes são lentas? Às vezes, é um design relacional ruim. Às vezes, é uma indexação ineficaz. Às vezes, é um problema de volume de dados. Às vezes, é uma consulta escrita de maneira horrível.

Desculpe por uma resposta tão prolixa, mas me senti compelido a fornecer um contexto mais substancial em torno de meus comentários, em vez de apenas recitar uma resposta de quatro pontos.


10

Pessoas com bancos de dados do tamanho de um terrabyte ainda usam junções, se eles podem fazê-los funcionar em termos de desempenho, então você também pode.

Existem muitos motivos para não desnaturar. Primeiro, a velocidade das consultas selecionadas não é a única ou mesmo a principal preocupação com os bancos de dados. A integridade dos dados é a primeira preocupação. Se você desnormalizar, terá que implementar técnicas para manter os dados desnormalizados à medida que os dados pai mudam. Portanto, suponha que você armazene o nome do cliente em todas as tabelas em vez de unir à tabela do cliente no client_Id. Agora, quando o nome do cliente muda (100% de chance de alguns dos nomes dos clientes mudarem com o tempo), agora você precisa atualizar todos os registros do filho para refletir essa mudança. Se você fizer isso com uma atualização em cascata e tiver um milhão de registros de filhos, quão rápido você acha que isso vai ser e quantos usuários vão sofrer problemas de bloqueio e atrasos em seus trabalhos enquanto isso acontece? Além disso, a maioria das pessoas que desnormalizam porque "

A desnormalização é um processo complexo que requer um entendimento completo do desempenho e integridade do banco de dados para ser feito corretamente. Não tente desnormalizar, a menos que você tenha esse conhecimento na equipe.

As junções são bastante rápidas se você fizer várias coisas. Primeiro use uma chave suggorgate, uma junção interna é quase sempre a junção mais rápida. Em segundo lugar, sempre indexe a chave estrangeira. Use tabelas derivadas ou condições de junção para criar um conjunto de dados menor para filtrar. Se você tiver um banco de dados grande e muito complexo, contrate um profissional de banco de dados com experiência em particionamento e gerenciamento de bancos de dados enormes. Existem várias técnicas para melhorar o desempenho sem eliminar as junções.

Se você só precisa de capacidade de consulta, então sim, você pode projetar um datawarehouse que pode ser desnormalizado e preenchido por meio de uma ferramenta ETL (otimizada para velocidade) e não pela entrada de dados do usuário.


8

As junções são lentas se

  • os dados estão indexados indevidamente
  • resultados mal filtrados
  • juntando consulta mal escrita
  • conjuntos de dados muito grandes e complexos

Portanto, é verdade, quanto maiores seus conjuntos de dados, mais processamento você precisará para uma consulta, mas verificar e trabalhar nas três primeiras opções acima geralmente produzirá ótimos resultados.

Sua fonte oferece a desnormalização como uma opção. Isso é bom apenas enquanto você esgotou as alternativas melhores.


7

As junções podem ser lentas se grandes porções de registros de cada lado precisarem ser verificadas.

Como isso:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id

Mesmo que um índice seja definido em account_customer, todos os registros desse último ainda precisam ser verificados.

Para a lista de consulta this, os otimizadores decentes provavelmente nem mesmo considerarão o caminho de acesso do índice, fazendo a HASH JOINou a em MERGE JOINvez disso.

Observe que para uma consulta como esta:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id
WHERE   customer_last_name = 'Stellphlug'

a junção provavelmente será rápida: primeiro, um índice on customer_last_nameserá usado para filtrar todos os Stellphlug's (que, obviamente, não são muito numerosos), então uma varredura de índice account_customerserá emitida para cada Stellphlug para encontrar suas transações.

Apesar do fato de que esses podem ter bilhões de registros em accountse customers, apenas alguns precisarão realmente ser digitalizados.


mas é difícil evitá-lo. projete seu aplicativo de forma que esse tipo de consulta não seja executado com muita frequência.
Andrey

1
Se um índice for definido na accounts(account_customer)maioria dos RDBMSes, ele usará esse índice para descobrir exatamente quais linhas do customersbanco de dados precisam ser verificadas.
Jemfinch

sim, mas não é uma operação barata de qualquer maneira. você pode armazenar soma em algum campo e atualizar em cada transação.
Andrey

@jemfinch: não, não vão. Isso exigiria a varredura de todo o índice apenas para filtrar os clientes e, em seguida, a varredura do índice do cliente em um loop aninhado. A HASH JOINseria muito mais rápido, então é o que será usado, exceto em todos os principais bancos de dados MySQL, exceto , que apenas fará o início customersem um loop aninhado (já que é menor em tamanho)
Quassnoi

4

Joins are fast.As junções devem ser consideradas uma prática padrão com um esquema de banco de dados normalizado corretamente. As junções permitem que você junte grupos distintos de dados de uma forma significativa. Não tema a adesão.

A ressalva é que você deve compreender a normalização, a junção e o uso adequado dos índices.

Cuidado com a otimização prematura, pois a falha número um de todos os projetos de desenvolvimento é cumprir o prazo. Depois de concluir o projeto e entender as compensações, você pode quebrar as regras se puder justificar.

É verdade que o desempenho da junção é degradado de forma não linear à medida que o tamanho do conjunto de dados aumenta. Portanto, ele não é escalável tão bem quanto as consultas de uma única tabela, mas ainda o faz.

Também é verdade que um pássaro voa mais rápido sem asas, mas apenas para baixo.


3

As junções exigem processamento extra, pois precisam examinar mais arquivos e mais índices para "juntar" os dados. No entanto, "conjuntos de dados muito grandes" são todos relativos. Qual é a definição de grande? No caso de JOINs, acho que é uma referência a um grande conjunto de resultados, não a esse conjunto de dados geral.

A maioria dos bancos de dados pode processar rapidamente uma consulta que seleciona 5 registros de uma tabela primária e junta 5 registros de uma tabela relacionada para cada registro (assumindo que os índices corretos estejam no lugar). Essas tabelas podem ter centenas de milhões de registros cada, ou até bilhões.

Uma vez que seu conjunto de resultados comece a crescer, as coisas vão desacelerar. Usando o mesmo exemplo, se a tabela primária resultar em 100 mil registros, haverá 500 mil registros "unidos" que precisam ser encontrados. Retirar tantos dados do banco de dados com atrasos na adição.

Não evite JOINs, apenas saiba que pode ser necessário otimizar / desnormalizar quando os conjuntos de dados ficarem "muito grandes".


3

Também do artigo que você citou:

Muitos sites em megaescala com bilhões de registros, petabytes de dados, muitos milhares de usuários simultâneos e milhões de consultas por dia estão usando um esquema de fragmentação e alguns até defendem a desnormalização como a melhor estratégia para arquitetar a camada de dados.

e

E, a menos que você seja um site muito grande, provavelmente não precisa se preocupar com esse nível de complexidade.

e

É mais sujeito a erros do que ter o banco de dados fazendo todo esse trabalho, mas você é capaz de escalar além do que até mesmo os bancos de dados mais avançados podem suportar.

O artigo discute mega-sites como o Ebay. Nesse nível de uso, você provavelmente terá que considerar algo diferente do gerenciamento de banco de dados relacional simples. Mas no curso "normal" dos negócios (aplicativos com milhares de usuários e milhões de registros), as abordagens mais caras e sujeitas a erros são exageradas.


2

As junções são consideradas uma força oposta à escalabilidade porque normalmente são o gargalo e não podem ser facilmente distribuídas ou paralelas.


Não tenho certeza se isso é verdade. Eu sei que o Teradata certamente é capaz de distribuir joins entre os amplificadores. Obviamente, certos tipos de junções podem ser mais complicados / intratáveis ​​do que outros.
Cade Roux

índices podem ser particionados em RDBMS variando de mysql a oracle. AFAIK que escala (é distribuído e pode ser paralelo).
Irracional

2

Tabelas projetadas corretamente contendo os indicadores adequados e consultas escritas corretamente nem sempre são lentas. Onde você já ouviu isso:

Por que as junções são ruins ou 'lentas'

não tem ideia do que estão falando !!! A maioria das junções será muito rápida. Se você tiver que unir muitas linhas de uma vez, poderá sofrer um golpe em comparação com uma tabela desnormalizada, mas isso remete a Tabelas projetadas corretamente, saiba quando desnormalizar e quando não. em um sistema de relatórios pesado, divida os dados em tabelas desnormalizadas para relatórios ou até mesmo crie um data warehouse. Em um sistema transacional pesado normalize as tabelas.


1

A quantidade de dados temporários gerados pode ser enorme com base nas junções.

Por exemplo, um banco de dados aqui no trabalho tinha uma função de pesquisa genérica em que todos os campos eram opcionais. A rotina de pesquisa fez uma junção em todas as tabelas antes de a pesquisa começar. Isso funcionou bem no início. Mas, agora que a tabela principal tem mais de 10 milhões de linhas ... nem tanto. As pesquisas agora levam 30 minutos ou mais.

Fui incumbido de otimizar o procedimento armazenado de pesquisa.

A primeira coisa que fiz foi se algum dos campos da tabela principal estivesse sendo pesquisado, fiz uma seleção para uma tabela temporária apenas nesses campos. ENTÃO, juntei todas as tabelas com aquela tabela temporária antes de fazer o resto da pesquisa. Pesquisas em que um dos campos principais da tabela agora levam menos de 10 segundos.

Se nenhum dos campos da tabela principal começar a ser pesquisado, faço otimizações semelhantes para outras tabelas. Quando terminei, nenhuma pesquisa leva mais de 30 segundos, com a maioria menos de 10.

A utilização da CPU do servidor SQL também diminuiu.


@BoltBait: A mensagem final é que você deve sempre tentar reduzir o número de linhas antes de realizar uma junção?
unutbu

Certamente funcionou maravilhas no meu caso. Mas eu não otimizaria um sistema até que se tornasse necessário.
BoltBait

normalmente nenhum dado temporário é gerado nas junções (dependendo, é claro, da seletividade, memória disponível e tamanho dos buffers de junção), AFAIK; entretanto, os dados temporários são normalmente criados por ordem e distintos se não houver índice que possa ser usado para tais operações.
Unreason

1

Embora as junções (presumivelmente devido a um design normalizado) possam obviamente ser mais lentas para recuperação de dados do que uma leitura de uma única tabela, um banco de dados desnormalizado pode ser lento para operações de criação / atualização de dados, uma vez que a pegada da transação geral não será mínima.

Em um banco de dados normalizado, uma parte dos dados viverá em apenas um lugar, portanto, a área ocupada por uma atualização será a mínima possível. Em um banco de dados desnormalizado, é possível que a mesma coluna em várias linhas ou entre tabelas precise ser atualizada, o que significa que a área de cobertura seria maior e a chance de bloqueios e impasses pode aumentar.


1

Bem, sim, selecionar linhas de uma tabela desnormalizada (assumindo índices decentes para sua consulta) pode ser mais rápido do que selecionar linhas construídas a partir da junção de várias tabelas, principalmente se as junções não tiverem índices eficientes disponíveis.

Os exemplos citados no artigo - Flickr e eBay - são casos excepcionais da IMO, portanto têm (e merecem) respostas excepcionais. O autor chama a atenção para a falta de RI e a extensão da duplicação de dados no artigo.

A maioria dos aplicativos - novamente, IMO - se beneficia da validação e duplicação reduzida fornecida pelos RDBMSs.


0

Eles podem ser lentos se feitos de maneira descuidada. Por exemplo, se você fizer um 'select *' em uma junção, provavelmente demorará um pouco para obter as coisas de volta. No entanto, se você escolher cuidadosamente quais colunas retornar de cada tabela e com os índices apropriados no lugar, não haverá problema.

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.