Como criar um banco de dados para armazenar uma lista classificada?

42

Eu estou olhando para armazenar uma lista classificada dentro de um banco de dados. Quero executar as seguintes operações com eficiência.

  1. Inserir (x) - Insere o registro x na tabela
  2. Excluir (x) - exclui o registro x da tabela
  3. Antes (x, n) - Retorne os registros 'n' que precedem o registro x na lista classificada.
  4. Depois (x, n) - Retorna os registros 'n' que sucedem o registro x na lista classificada.
  5. Primeiro (n) - Retorna os primeiros registros 'n' da lista classificada.
  6. Last (n) - Retorna os últimos registros 'n' da lista classificada.
  7. Compare (x, y) - Dados dois registros xey da tabela, encontre se x> y.

O método simples em que pude pensar é armazenar algum tipo de atributo 'rank' na tabela e consultar, classificando esse atributo. Porém, nesse método, inserir / modificar um registro com uma classificação torna-se uma operação cara. Há um método melhor?

Especificamente, estou procurando implementar a tabela usando o SimpleDB da Amazon. Mas uma resposta geral para um banco de dados relacional também deve ser útil.

Atualização no perfil de carregamento:

Como estou planejando isso para um aplicativo Web, isso depende do número de usuários que usam o aplicativo.

Se houver 100 mil usuários ativos (super otimismo: P), minha estimativa aproximada por dia será

500k seleciona, 100k insere e exclui, 500k atualizações

Eu esperava que a tabela crescesse até 500k no total.

Estou procurando otimizar as atualizações, inserir e comparar as operações. A classificação dos itens mudará constantemente e eu preciso manter a tabela atualizada.

chitti
fonte
Elabore um pouco no seu perfil de carga esperado. Quantas seleções / inserções / atualizações por dia? Para quais operações você deseja otimizar? Quão grande você espera que a tabela cresça por dia ou fique no total?
Nick Chammas
Isso é para um quadro de classificação de jogadores? De qualquer forma, atualizei minha resposta abaixo com feedback com base no seu perfil de carga projetada.
Nick Chammas
Não, não é um quadro de classificação de jogadores.
Chitti
Que abordagem você acabou usando?
Nick Chammas
Eu nem tenho certeza do que está sendo perguntado aqui ou do que você não precisa fazer na lista de coisas que você precisa fazer.
Evan Carroll

Respostas:

22

Se a classificação não for completamente arbitrária, mas derivar de alguma outra propriedade (por exemplo, nome, pontuação do jogador etc.), dê uma boa olhada na resposta de Joel .

Se for uma propriedade arbitrária dos seus dados, ela deverá ser armazenada como uma coluna na sua tabela de registros. Supondo que o SimpleDB da Amazon seja semelhante ao RDBMS típico, você poderá indexar esta coluna e satisfazer rapidamente todas as suas consultas acima com a estratégia de indexação apropriada. Isso é normal para um RDBMS.

Como você espera alta atividade de inserção e atualização, mas também uma atividade de leitura relativamente alta, recomendo fazer o seguinte:

  • Agrupe a tabela na classificação, especialmente se a grande maioria das suas consultas for contra a classificação. Caso contrário, ou se a escolha de uma chave de cluster não estiver disponível no SimpleDB, crie um índice com a classificação como a coluna principal. Isso satisfaria as perguntas 3-6.
  • Um índice no registro primeiro e depois a classificação (ou, no mundo do SQL Server, apenas a INCLUDEclassificação de registro e classificação, ou apenas o registro se você tiver agrupado na classificação) atenderiam à consulta 7.
  • As operações 1 e 2 podem ser otimizadas espaçando seus dados adequadamente (ou seja, definindo o FILLFACTORno SQL Server). Isso é especialmente importante se você agrupar na classificação.
  • À medida que você insere ou atualiza as classificações, mantenha o máximo de espaço possível entre os números de classificação para minimizar a possibilidade de que você precisará reclassificar um registro existente para acomodar uma inserção ou atualização de classificação. Por exemplo, se você classifica seus registros em etapas de 1000, deixa espaço suficiente para cerca da metade de muitas alterações e inserções com o mínimo de chance de precisar classificar novamente um registro não diretamente envolvido nessas alterações.
  • Todas as noites, reorganize todos os registros para redefinir as diferenças de classificação entre eles.
  • Você pode ajustar a frequência das novas classificações em massa, bem como o tamanho da diferença de classificação para acomodar o número esperado de inserções ou atualizações em relação ao número de registros existentes. Portanto, se você possui 100 mil registros e espera que suas inserções e atualizações sejam 10% disso, deixe espaço suficiente para 10 mil novos postos e re-classifique todas as noites.
  • Reordenar os registros de 500K é uma operação cara, mas feita uma vez por dia ou semana fora do horário de expediente deve ser adequada para um banco de dados como esse. Essa reclassificação em massa fora do horário comercial para manter as diferenças de classificação é o que poupa a necessidade de reclassificar muitos registros para cada atualização ou inserção de classificação durante o horário normal e de pico.

Se você espera 100K + lê em uma tabela de tamanho 100K +, não recomendo usar a abordagem de lista vinculada. Não será bem dimensionado para esses tamanhos.

Nick Chammas
fonte
As classificações são modificáveis. Espero que as fileiras mudem constantemente e que novos registros sejam inseridos constantemente. Estou preocupado com o caso, quando insiro um novo elemento com uma classificação, e as classificações de todos os registros abaixo do novo registro na ordem de classificação precisam ser alteradas. Não é uma operação cara quando tenho milhares de registros no meu banco de dados?
Chitti
@chitti - Ah, isso é uma preocupação. Você pode espaçar suas classificações (por exemplo, 0, 1000, 2000, 3000, ...) e periodicamente re-classificar todos os registros à medida que as lacunas de classificação são preenchidas. Isso não será escalável se você esperar muito mais do que algumas dezenas de milhares de registros.
Nick Chammas
1
@chitti - Isso é meio engraçado, na verdade. Esse é exatamente o problema dos mecanismos de banco de dados ao indexar dados, porque eles estão solicitando e reorganizando-os à medida que os dados são adicionados ou alterados. Se você olhar para cima FILLFACTOR, verá que o objetivo principal é criar esse espaço extra para registros em um índice, assim como as diferenças de classificação que descrevi criam espaço para alterações e inserções de classificação.
Nick Chammas
2
Obrigado pela resposta atualizada. A 'classificação' é uma propriedade arbitrária dos meus dados. Estou quase convencido de que uma coluna de índice personalizada é o que eu preciso. Confira este link SO com uma pergunta semelhante. A resposta principal fornece recomendações sobre como lidar com essa coluna de classificação.
Chitti
@chitti - A resposta aceita para essa pergunta SO é ótima. Ele sugere a mesma abordagem que detalhei aqui, com a sugestão adicional de usar casas decimais em vez de números inteiros para expandir bastante sua flexibilidade na atribuição e alteração de classificações. Ótima descoberta.
Nick Chammas 14/09
13

Eu geralmente uso o método "rank" que você descreve. Em vez de mexer nas linhas de atualização quando os itens precisavam ser reordenados, muitas vezes consegui excluir todos os registros da lista e reinserir novos itens na ordem correta. Este método é claramente otimizado para recuperação.

Uma abordagem alternativa seria modelar os registros como uma lista vinculada usando uma coluna de chave estrangeira reflexiva "predecessora" na tabela:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

Você pode recuperar facilmente uma lista e adicionar e remover itens com pouca sobrecarga, mas obter os registros na ordem correta será complicado. Talvez haja uma maneira inteligente de fazer isso em uma única consulta, provavelmente com muitas junções de tabelas com alias.

Uso essa última abordagem frequentemente quando estou modelando um relacionamento no estilo de árvore (categorias, pastas, conjuntos e subconjuntos). Eu geralmente tive uma função recursiva de algum tipo para reconstruir a árvore completa no meu aplicativo.

bpanulla
fonte
2
O modelo de lista vinculada é limpo. Para recuperar essa hierarquia em ordem no SQL Server, você usaria um CTE recursivo .
Nick Chammas
Construir essa hierarquia seria muito caro para uma mesa alta, no entanto. A vantagem é que alterações / inserções de classificação / etc podem ser feitas facilmente. Dependendo do perfil de carga esperado da chitti, essa pode ser a melhor abordagem.
Nick Chammas
A opção de lista vinculada parece a melhor idéia para todas as operações, exceto Comparar. Alguma idéia de como eu implementaria o Compare sem ter que rastrear o caminho entre os dois elementos que estão sendo comparados?
Chitti
Se você possui os IDs dos itens, acho que Compare () seria simples, a menos que eu não entendesse o que você quis dizer com Compare (). Quando você disse: "encontre se x> y", você quis dizer "encontre se x precede y"? Não vejo como isso é fácil sem um índice personalizado ou um procedimento armazenado que percorra a lista (ou o interessante recurso CTE mencionado por @Nick).
bpanulla
5
Esse tipo de solução também se aproxima de um modelo de dados gráficos ( en.wikipedia.org/wiki/Graph_theory ). Um sistema de armazenamento otimizado para armazenar nós e arestas do gráfico pode ser uma solução melhor que um RDBMS. As lojas Triple e Quad e os bancos de dados gráficos como o Neo4J são muito bons nisso.
bpanulla
6

Eu acho que a coisa a fazer é armazenar a propriedade ou propriedades que são usadas para calcular a classificação e criar um índice sobre elas. Em vez de tentar forçar o banco de dados a armazenar fisicamente os dados na ordem de classificação ou usar uma lista vinculada gerenciada manualmente, por que não deixar o mecanismo de banco de dados fazer o que foi projetado?

Joel Brown
fonte
2
E se as 'propriedades usadas para calcular a classificação' forem arbitrárias? Por exemplo: um conjunto de entradas do carrinho de compras que são reordenadas com base nas ações arbitrárias do usuário.
Chitti
Quando você diz que a classificação é arbitrária, o que você quer dizer? Tem que haver um algoritmo que você usa para calcular qual deve ser a classificação. Por exemplo: "com base nas entradas do carrinho de compras" - Com base em como? Deve haver algo armazenado no banco de dados que seja o driver para o cálculo da classificação. Pode ser uma combinação de várias coisas, mas essas coisas devem ser armazenadas de alguma forma na tabela do cliente ou em tabelas relacionadas ao cliente. Se estiver nos dados, você poderá criar uma função que os calcule. Se você pode calcular, pode armazená-lo e indexá-lo.
Joel Brown
Digamos que precisamos manter a ordem dos itens em um carrinho de compras e a ordem pode ser 'arbitrariamente' alterada pelo usuário usando uma interface do usuário da web. Como você armazenaria essa lista de itens em um banco de dados e como manteria a ordem de classificação?
Chitti
Se o entendi corretamente, "alterando arbitrariamente" a ordem dos itens em um carrinho de compras significa que o usuário pode arrastar itens para cima e para baixo em uma lista e soltá-los onde quiser. Eu acho que isso me parece um pouco artificial. Por que os usuários fariam isso? Se eles pudessem fazer, eles fariam muito? O uso de uma sequência simples de itens em um carrinho realmente preocupa muito o desempenho? Parece-me que um número de sequência de um a um número de itens no carrinho + o FK do pedido forneceria o índice necessário. Apenas atualize os itens quando alguém for arrastado.
Joel Brown
3
O carrinho de compras é apenas um exemplo que dei para mostrar que há casos em que o 'ranking' pode ser arbitrário. Pode ser que não tenha sido um ótimo exemplo. A fila de netflix dvd pode ser um exemplo melhor. Por uma questão de argumento, imagine uma fila netflix com 100 mil itens que podem ser reordenados arbitrariamente pelo usuário e ele faz isso a cada minuto. Como você projetaria um banco de dados para armazenar a lista ordenada de filmes nesta aplicação hipotética?
chitti 15/09/11
1

Essas são as limitações de um não-RDBMS como o simpleDB. Os recursos que você precisa não podem ser implementados no lado do DB no simpleDB, eles precisam ser implementados no lado / aplicativo de programação.

Para um RDBMS como SQL server, os recursos necessários são rudimentares para o índice em cluster.

  • Inserir (x) - insira o registro x na tabela> Inserir simples.
  • Excluir (x) - exclua o registro x da tabela> Exclusão simples.
  • Antes (x, n) - Retorne os registros 'n' que precedem o registro x na lista classificada. > Selecione os n principais resultados em que x seja menor que o valor e classifique por cláusula.

  • Depois (x, n) - Retorna os registros 'n' que sucedem o registro x na lista classificada. > Selecione os n principais resultados em que x é maior que o valor e ordena por cláusula.

  • Primeiro (n) - Retorna os primeiros registros 'n' da lista classificada. > Selecione os n principais resultados.

  • Last (n) - Retorna os últimos registros 'n' da lista classificada. > Selecione os melhores resultados após a ordem, por desc.

  • Compare (x, y) - Dados dois registros xey da tabela, encontre se x> y. > Instrução TSQL IF.
StanleyJohns
fonte
O SimpleDB fornece índices automáticos, classificação e uma linguagem de consulta básica . Meu problema continuará mesmo se eu escolher um RDBMS. O problema é que a classificação dos dados no meu banco de dados muda arbitrariamente e eles não podem ser capturados como uma única propriedade (a menos que eu use uma coluna de classificação personalizada) que possa ser indexada.
chitti 14/09/11
0

Aqui está o que eu costumava classificar novamente minha tabela do Postgres após cada inserção:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

Para o meu caso de uso, o desempenho não é uma preocupação, mas a confiança de que nunca irá quebrar ou agir de maneira estranha é importante.

Marca
fonte