AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 339990
Accepted
Federico Baù
Federico Baù
Asked: 2024-06-04 21:49:07 +0800 CST2024-06-04 21:49:07 +0800 CST 2024-06-04 21:49:07 +0800 CST

Solução MySQL para tabelas associativas muitas para muitas que escalam mais de bilhões de entradas

  • 772

Cenário

Imagine que temos um tableusuário e um itemusuário. Essas 2 tabelas possuem uma tabela associativa chamada user_itempara definir um many to manyrelacionamento.

  • Começamos 100 itemregistros

  • Temos 500 milhões userde registros.

  • Portanto devemos gerar 50_000_000_000 user_item(50 bilhões)

  • Poderíamos potencialmente ter ainda mais

  • Não será fácil fragmentar nem particionar porque isso tornará qualquer outra operação mais lenta (caso contrário, precisaremos verificar tudo)

  • Assuma como padrão de consulta ( INSERT, SELECT, UPDATE) padrões m2m básicos/típicos (que podem ser encontrados em qualquer tutorial ou exemplo

Pergunta

Qual é o melhor design ou solução conhecida para lidar com bilhões de relacionamentos Muitos para Muitos em um banco de dados, independentemente de um esquema?

Esquema

Imagine este esquema simples

CREATE DATABASE IF NOT EXISTS `playground` CHARACTER SET = latin1;
USE playground;

CREATE TABLE IF NOT EXISTS `user`
(
    `id`   BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255)    NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `user__name_fk` (`name`)
) ENGINE = InnoDB
  DEFAULT CHARSET = latin1
  ROW_FORMAT = DYNAMIC;

CREATE TABLE IF NOT EXISTS `item`
(
    `id`   BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255)    NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `user__name` (`name`)
) ENGINE = InnoDB
  DEFAULT CHARSET = latin1
  ROW_FORMAT = DYNAMIC;

CREATE TABLE IF NOT EXISTS `user_item`
(
    `user_id` BIGINT UNSIGNED NOT NULL,
    `item_id` BIGINT UNSIGNED NOT NULL,
    PRIMARY KEY (`user_id`, `item_id`),
    INDEX `user_item__item` (`item_id`),

    FOREIGN KEY `user_id_fk` (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE,
    FOREIGN KEY `item_id_fk` (`item_id`) REFERENCES `item` (`id`) ON DELETE CASCADE
) ENGINE = InnoDB
  DEFAULT CHARSET = latin1
  ROW_FORMAT = DYNAMIC;

-- create some default items
INSERT INTO `item` (`name`) VALUES ('item_1'), ('item_2'), ('item_3'), ('item_4'), ('item_5'), ('item_6'), ('item_7'), ('item_8'), ('item_9'), ('item_10');
-- create some users
INSERT INTO `user` (`name`) VALUES ('user_1'), ('user_2'), ('user_3'), ('user_4'), ('user_5'), ('user_6'), ('user_7'), ('user_8'), ('user_9'), ('user_10');
INSERT INTO `user_item` (`user_id`, `item_id`) VALUES (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10);

Mais informações

Não estou perguntando como usar o relacionamento muitos para muitos no MySQL, eu sei disso. Estou perguntando qual é a solução mais conhecida para um problema de escala, ou seja, quando o número de registros relacionados está crescendo exponencialmente em uma escala tão grande.

Além disso, intencionalmente não adicionei nenhum padrão de consulta ( INSERT, SELECT, UPDATE) porque é irrelevante . Suponha o padrão M2M mais típico. Não quero perder o foco na questão real que é sobre escalabilidade e enorme quantidade de dados.

Deve haver algum truque ou alguma solução alternativa conhecida, certo? Também estou considerando um banco de dados NoSQL, então a resposta pode incluir qualquer coisa não relacionada ao MySQL (ou qualquer banco de dados SQL),

Sinto que este deveria ser um problema comum que muitas grandes empresas enfrentarão e, portanto, deveria haver uma solução comum (ou poucas). A causa raiz desse problema é que, embora o MySQL seja ótimo para criar relacionamentos, ele aumentará exponencialmente a tabela associativa m2m .

Os 500 milhões x 100 == 50 bilhões são apenas um exemplo. Mas poderia teoricamente acontecer.

Esclarecimento

  • Deixei a consulta de propósito porque você pode assumir a mais fácil.
  • Tenho certeza que se eu der alguns exemplos, começarei a aparecer a otimização na consulta específica, essa não é a questão
  • Estou fazendo uma pergunta de alto nível e, se não houver uma solução real conhecida, um não explicando o porquê seria suficiente (presumindo que esteja correto)

Aqui está um exemplo de uma consulta simples de muitos para muitos.

SELECT user.*, item.* FROM user
LEFT JOIN user_item ON user.id = user_item.user_id
LEFT JOIN item ON item.id = user_item.item_id
WHERE user.name = 'user_1';

Perguntas semelhantes, mas não iguais

  • Melhor design para um problema de relacionamento muitos:muitos
  • Projete uma tabela muitos para muitos em escala
  • Muitos, muitos para muitos relacionamentos, design de banco de dados com MySql
  • melhor design de banco de dados para relações aninhadas (muitos para muitos para muitos?)
mysql
  • 4 4 respostas
  • 91 Views

4 respostas

  • Voted
  1. J.D.
    2024-06-06T00:50:24+08:002024-06-06T00:50:24+08:00

    Prefácio

    Acho que infelizmente você está entendendo mal:

    1. Como os bancos de dados funcionam em seu nível central e
    2. O que algumas pessoas desta comunidade têm tentado comunicar a você em relação a isso.

    Espero que esta resposta ajude a esclarecer isso.

    Vou começar dizendo que trabalhei com tabelas grandes (cerca de 1 TB de tamanho) que continham dezenas de bilhões de linhas, com consultas que normalmente eram executadas em menos de 1 segundo, em hardware muito modesto (4 CPUs, 16 GB de BATER).

    Também salientarei que o tamanho dos dados em repouso não é uma razão para escolher um sistema de banco de dados específico, pois todos eles lidam com a mesma quantidade de dados de forma relativamente igual do ponto de vista do desempenho (independentemente de você estar falando sobre bancos de dados NoSQL ou RDBMS) .

    Organização de dados

    Uma quantidade X de dados sempre levará Y tempo para ser lida no disco. Por exemplo, em uma tabela de 100 GB, 1 GB de dados sempre levará um tempo consistentemente constante para ser lido no disco, não importa qual sistema de banco de dados está sendo usado para armazenar esses dados. Da mesma forma que uma caminhonete pode transportar 100 tijolos de um lugar para outro, não importa a cor da caminhonete, ainda levará um tempo consistente para transportar os tijolos.

    O problema é localizar os tijolos que o cliente deseja transportar da pilha de todos os tijolos (a mesa). Se um cliente deseja 100 tijolos vermelhos , e os tijolos estão todos armazenados em uma grande pilha desorganizada no chão (uma estrutura de dados Heap não indexada), entre 10 cores diferentes, localizar esses 100 tijolos vermelhos levará muito tempo para ser classificado. todos os tijolos para encontrar.

    Insira índices

    Os índices são um recurso (que quase todos os sistemas de banco de dados oferecem) como forma de organizar os dados em uma estrutura de dados eficiente para pesquisa (geralmente uma árvore B por padrão). Um índice classifica os dados na tabela na ordem das colunas especificadas em sua definição, que então classifica os dados relacionados juntos, tornando a pesquisa muito mais eficiente do que uma tabela heap desorganizada.

    Então, se aplicarmos um índice à nossa pilha aleatória de tijolos, digamos por cor, então todos os tijolos vermelhos serão empilhados um ao lado do outro, depois os tijolos azuis todos juntos, depois os tijolos amarelos todos juntos etc. para cavar toda a pilha desorganizada de tijolos , podemos simplesmente caminhar diretamente para a seção vermelha e pegar os primeiros 100 tijolos que pudermos. Obviamente, isso é significativamente mais eficiente do que ter que procurar sem rumo em todos os tijolos.

    O mesmo se aplica à forma como os dados são organizados em uma tabela de um banco de dados. Se os índices apropriados forem criados nessa tabela, então esses índices poderão ser procurados diretamente nas linhas que correspondem à consulta que está solicitando essas linhas, em vez de ter que examinar toda a tabela.

    Por que as consultas são relevantes

    Vou reafirmar o que acabei de dizer acima com alguma ênfase:

    Se os índices apropriados forem criados nessa tabela, então esses índices poderão ser procurados diretamente nas linhas que correspondem à consulta que está solicitando essas linhas.

    E um lembrete sobre como funcionam os índices:

    Um índice classifica os dados da tabela na ordem das colunas especificadas em sua definição...

    Portanto, para que um índice seja eficaz e útil para uma consulta específica, ele precisa ser definido adequadamente com base nas colunas usadas especificamente nessa consulta.

    No meu exemplo anterior sobre tijolos, cor era o campo pesquisado. Se classificássemos os tijolos por cor (definissemos o índice por cor) e o cliente quisesse os tijolos por tamanho, o índice que criamos não nos ajudaria. Isso é semelhante à WHEREcláusula em uma consulta. Saber quais consultas são executadas com mais frequência e, ainda mais importante, quais predicados ( JOINe WHEREcláusulas) essas consultas usam, nos ajuda a definir os índices mais apropriados para atender a essas consultas.

    Tudo anda de mãos dadas e é quase impossível falar sobre desempenho na prática sem falar sobre as questões que você deseja especificamente ter um bom desempenho.

    Outra razão pela qual saber quais consultas serão mais importantes para otimizar é porque isso pode influenciar o design e a arquitetura da sua tabela. E há outras razões também, mas muitas para serem analisadas em profundidade em uma resposta aqui. Então vou me ater ao mais importante (na minha opinião) para esta discussão, que são os índices.

    Final

    Suponho que de alguma forma você precise 'dividir' os dados .. e não há muita outra solução, estou errado?

    Sim e não. Você está certo sobre a divisão dos dados , mas está errado sobre como pensa que deve fazer isso, com particionamento ou fragmentação. Estas não são as ferramentas certas para esta discussão generalizada sobre ajuste de desempenho e são usadas apenas para propósitos muito específicos. Em vez disso, você deseja Índices novamente, que dividem os dados para você na forma como eles são organizados na estrutura de dados subjacente, novamente, um B-Tree .

    As árvores B têm uma O(log2(n))complexidade de tempo de pesquisa devido à forma como organizam os dados. Isso significa que em uma tabela com 50 bilhões de linhas, também conhecida como n = 50 billion, o tempo de pesquisa é log2(50 billion) = ~36. Em outras palavras, para buscar um índice B-Tree em uma tabela com 50 bilhões de linhas, apenas 36 delas precisam ser rastreadas para localizar os dados solicitados, na pior das hipóteses. Minha calculadora gráfica pode pesquisar 36 linhas de dados em milissegundos.

    Como você pode ver, gerenciar os dados a serem pesquisados ​​de forma eficiente é um problema resolvido, ao contrário do que você pensava inicialmente.

    • 3
  2. Andrea B.
    2024-06-05T19:54:35+08:002024-06-05T19:54:35+08:00

    A melhor solução para o seu problema é aquela que você já projetou.

    Isso ocorre porque todos os RDBMS são projetados para lidar exatamente com isso: um monte de tabelas, com relações muitos-para-muitos, e potencialmente bilhões de linhas, onde você SELECT, INSERT ou DELETE linhas.

    Qualquer sistema de armazenamento "especial", como NoSQL, armazenamento colunar, particionamento, etc. é útil apenas quando você tem necessidades "especiais" e seus padrões de uso não são mais genéricos.

    Mas para todo o processamento normal de dados OLTP e OLAP em um banco de dados, o esquema que você forneceu é típico porque é o melhor para resolver o problema na ausência de características mais específicas de dados ou padrões de uso.

    Se precisar acessar algumas linhas por vez, você precisará de índices e terá todos os necessários. Se você precisa acessar uma grande porcentagem de dados, nada é mais eficiente do que uma verificação completa.

    Mais otimização só é possível quando seus dados não são mais “genéricos”, mas apresentam algumas particularidades (na composição, ou no padrão de acesso) que podem ser otimizadas com alguns ajustes, mas desde que você não tenha essas particularidades, não há otimizações possíveis e o dimensionamento não é um problema, porque os RDBMS são projetados exatamente para esta tarefa.

    É claro que a resposta será mais lenta quando houver bilhões de registros, mas não existe uma maneira geral de armazenar esses bilhões de registros de maneira mais eficiente, se você tiver o padrão de acesso típico.

    • 2
  3. Rick James
    2024-06-05T00:47:26+08:002024-06-05T00:47:26+08:00
    • Como são apenas 100 itens... Se não houver muito mais itens, mude para SMALL UNSIGNED(máximo de 65K). Isso economizará 30 GB. (Reduzindo uma tabela -> menos E/S e melhor armazenamento em cache -> talvez melhor desempenho de consulta.)

    • Considere ter a item VARCHAR(..)tag in user_item em vez da needing the tag table. If the strings are usually "short", the cost of the extra space will be offset by avoiding an extra lookup. [I did this quite successfully withpicture_id` and.]

    • 50 bilhões de linhas não são um problema sério se você estiver sempre usando um dos índices.

    • Considere INT UNSIGNED- user_ideconomizará 4 bytes BIGINT, mas está limitado a 4 bilhões. Verifique SELECT MAX(user_id) FROM userse você [por qualquer motivo] já está chegando à casa dos bilhões.

    • "caso contrário, precisamos verificar tudo" - Qual consulta está causando uma grande verificação?

    • É ON DELETE CASCADEimportante? [Isso pode não importar.]

    • PARTITIONingé útil para excluir dados antigos, mas não para desempenho de arquivos SELECTs.

    • A fragmentação pode ser útil quando você tem tantos dados que a gravação se torna um problema. No entanto, a configuração e a manutenção são um incômodo.

    • Ao entrar em tabelas com tamanho de terabytes, você deve calcular quanto tempo levará para preencher a tabela. 50 bilhões de linhas, mesmo em lote, podem levar semanas ou meses. Um controlador RAID de hardware pode ajudar um pouco.

    • 1
  4. Best Answer
    Federico Baù
    2024-06-06T13:04:23+08:002024-06-06T13:04:23+08:00

    Ok, marcarei minha resposta como aceita, mas se surgir outra resposta mais completa no futuro, posso alterá-la, realmente não me importo, importante é a resposta mais correta/completa para minha pergunta.

    Minha pergunta foi muito mal interpretada e seguiu em direções diferentes (exceto Andrea B. uma).

    Porém, especialmente graças às 'conversas' nos comentários, alguma parte da resposta surge, então se combinarmos comentários + pedaços de resposta acho que podemos responder corretamente. Não marquei a resposta de Andrea B. porque depois de aprendê-la tenho uma resposta mais completa.


    Então, como podemos resolver uma possível tabela, neste caso ManytoMany - Tabela associativa (que por natureza é a mais fácil de crescer em tamanho em comparação com outras).

    Podemos assumir que o esquema em si está bem projetado em seu máximo (tipo de dados de coluna, etc.), bem como os padrões de consulta ( SELECT, INSERT...) imagine/suponha que eles já foram otimizados ao máximo e realmente não há melhor maneira de fazer eles. E se alguém que está lendo esta resposta não tem certeza, pode dar uma olhada neste guia de Muitos para Muitos de Rick James , é muito legal, eu acho.

    Mas no seu caso, imagine que você também sabe que as linhas dessa tabela vão crescer exponencialmente, e mais cedo ou mais tarde ultrapassarão os bilhões, o que você faz?

    Em primeiro lugar, parece que não existe uma forma ou 'técnica' aceita que funcione o tempo todo, então todas as soluções abaixo podem ou não funcionar (também conhecidas como soluções alternativas).

    Entre todas as 'soluções' parece que elas têm uma coisa em comum, existem basicamente alguma forma de:

    Algoritmo de dividir e conquistar

    Em outras palavras, você precisa encontrar uma maneira de dividir os dados tanto quanto possível para torná-los mais gerenciáveis. Mas, ao mesmo tempo, não divida muito porque você pode acabar na pior posição, por exemplo, você ainda precisa ser capaz de recuperar SELECTdados de maneira rápida, então lembre-se de evitar 'escanear' tudo isso 'dividida' 'parte' (aqui ainda estou falando em um nível muito alto e é uma ideia abstrata, não estou falando em nada em específico!)

    Aqui estão as técnicas

    • Dividir dados na WHEREcláusula
    • Divida os dados noINDEX
    • Dividir dados sobre uso | o conceito de 'Arquivo'
    • Fragmentação

    Dividir dados na WHEREcláusula

    Este é o mais óbvio e bastante básico, mas pode não funcionar para todas as consultas ou às vezes você realmente precisa encontrar algo e verificar 'todos os dados'. Obviamente aqui vêm todas as LIMITboas OFFSETpráticas.

    Além disso, copiei e colei dos comentários (desculpe, colocaria um link para o comentário, se fosse possível)

    O conteúdo de uma WHEREcláusula é muito relevante para o desempenho. Buscar uma linha com um valor exato será rápido se a indexação for boa; buscar com OR ou RLIKE leva a uma varredura da tabela - sendo tão lenta quanto a tabela é grande

    Você nunca quer fazer um CROSS JOIN, especialmente quando o conjunto de resultados tem 50 bilhões de linhas. Portanto, o Otimizador examinará as cláusulas WHEREe ONpara evitar isso. [Daí o repetido apelo ao OP pelas consultas que serão executadas.]

    Divida os dados noINDEX

    Retirado de JD Answer on the Finale (depois de falar sobre tijolos) é realmente muito interessante.

    I won't add what he wrote to avoid repetition so you should check out that answer but the technique is true. While I personally know it I think is different if you think it in that way and have that explanation in mind. Really we are just using indexing to split 'data population', Divide-and-conquer.

    Split data on usage | the 'Archive' concept

    Use Least recently used (LRU) for instance

    An example of "access pattern" is News articles. Most people search for "recent" articles -- the articles on the current hot news topics. This tends to bias queries toward the "end" of the table. This can be helped by LRU and by clustering on date.

    So this may not work for all cases for sure but, if you can implement a logic where basically, you avoid access all data if you know is 'old' or less used. Here may be different ways to achieve this

    • bias queries toward the "end" of the table with a DATETIME column (probably) - (or could be even id bigger than <VALUE> etc..
    • What if you can move 'all data' into a separate table (or database) and is access only when requested or the data you are looking for is not there.

    For the second point, for instance, if you recognize a pattern in the data where, lots of data (maybe even most) is not accessed anymore you could move that (or rename the table) into another one ( user_item_archive ) then you create a new user_item and work on the fresh one. This is oversimplified and depends on use case but the important is the concept.

    I'm pretty sure there are articles online about this concept, if someone finds any (or if I will in the future) we could add here.

    Sharding

    This is the only 'well known' technique but it's a double-edged sword for sure.

    This blog on percona is very interesting

    Someone mention partition, but I don't recommended because it as IMO little to none use cases and I see it more for when you need to delete data. Accessing data with partition already may lead to a lot of trouble.

    Furthermore from this answer by Rick James while the first part tries to optimize the schema example (which is good but it was just an example) The last 2 bits are interestings IMO

    Sharding may be useful when you have so much data that writing becomes a problem. However, the setup and maintenance is a hassle.

    When you get into terabyte-sized tables, you should compute how long it will take to fill up the table. 50 billion rows, even if batched, could take weeks or months. A hardware RAID controller could help a little

    • -1

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve