Banco de Dados - Criando uma Tabela de "Eventos"

Depois de ler as dicas deisso é ótimoNettuts + artigo Eu criei um esquema de tabela que separaria dados altamente voláteis de outras tabelas sujeitas a leituras pesadas e, ao mesmo tempo, diminuiria o número de tabelas necessárias em todo o esquema do banco de dados, no entanto, não tenho certeza se isso é um bom ideia, uma vez que não segue as regras de normalização e eu gostaria de ouvir seus conselhos, aqui está a idéia geral:

Eu tenho quatro tipos de usuários modelados em umHerança da tabela de classes estrutura, na tabela principal "usuário", eu armazeno dados comuns a todos os usuários (id, username, password, de váriasflags, ...) juntamente com algunsTIMESTAMP Campos (date_created, date_updated, date_activated, date_lastLogin, ...).

Para citar a dica # 16 do artigo Nettuts + mencionado acima:

Exemplo 2: Você tem um campo "last_login" na sua tabela. Ele é atualizado sempre que um usuário faz login no site. Mas toda atualização em uma tabela faz com que o cache da consulta seja liberado. Você pode colocar esse campo em outra tabela para manter as atualizações na tabela de usuários no mínimo.

Agora fica ainda mais complicado, preciso acompanhar algumas estatísticas do usuário, como

quantosúnico vezes que um perfil de usuário foi vistoquantosúnico vezes umanúncio de umtipo específico de usuário foi clicadoquantosúnico vezes umpostar de umtipo específico de usuário foi vistoe assim por diante...

No meu banco de dados totalmente normalizado, isso adiciona cerca de 8 a 10 tabelas adicionais, não é muito, mas eu gostaria de manter as coisas simples, se pudesse, então criei o seguinte "events" mesa:

|------|----------------|----------------|---------------------|-----------|
| ID   | TABLE          | EVENT          | DATE                | IP        | 
|------|----------------|----------------|---------------------|-----------|
| 1    | user           | login          | 2010-04-19 00:30:00 | 127.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 1    | user           | login          | 2010-04-19 02:30:00 | 127.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | created        | 2010-04-19 00:31:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | activated      | 2010-04-19 02:34:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | approved       | 2010-04-19 09:30:00 | 217.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | login          | 2010-04-19 12:00:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | created        | 2010-04-19 12:30:00 | 127.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | impressed      | 2010-04-19 12:31:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:01 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:02 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:03 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:04 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:05 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | blocked        | 2010-04-20 03:19:00 | 217.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | deleted        | 2010-04-20 03:20:00 | 217.0.0.1 |
|------|----------------|----------------|---------------------|-----------|

Basicamente oID refere-se à chave primária (id) no campoTABLE tabela, acredito que o resto deve ser bem direto. Uma coisa que eu gostei nesse design é que eu posso acompanhar todos os logins de usuário em vez do último e, assim, gerar algumas métricas interessantes com esses dados.

Devido à natureza crescente doevents Também pensei em fazer algumas otimizações, como:

# 9: Como existe apenas um número finito de tabelas e um número finito (e predeterminado) de eventos, oTABLE eEVENTS colunas podem ser configuradas comoENUMs em vez deVARCHARs para economizar espaço.# 14: LojaIPs comoUNSIGNED INTs comINET_ATON() ao invés deVARCHARs.LojaDATEs comoTIMESTAMPs ao invés deDATETIMEs.Use oARCHIVE (ou oCSV?) mecanismo em vez deInnoDB / MyISAM.SomenteINSERTareiaSELECTs são suportados e os dados são compactados em tempo real.

No geral, cada evento consumiria apenas 14 bytes (não compactados), o que é bom para o meu tráfego, eu acho.

Prós:Capacidade de armazenar dados mais detalhados (como logins).Não há necessidade de projetar (e código para) quase uma dúzia de tabelas adicionais (datas e estatísticas).Reduz algumas colunas por tabela e mantém os dados voláteis separados.Contras:Não relacional (ainda não tão ruim quanto o EAV):SELECT * FROM events WHERE id = 2 AND table = 'user' ORDER BY date DESC();Sobrecarga de 6 bytes por evento (ID, TABLE eEVENT)

Estou mais inclinado a seguir essa abordagem, já que os profissionais parecem superam os contras, mas ainda estou um pouco relutante ...Estou esquecendo de algo? Quais são seus pensamentos sobre isso?

Obrigado!

@coolgeek:

Uma coisa que faço de maneira um pouco diferente é manter uma tabela entity_type e usar seu ID na coluna object_type (no seu caso, a coluna 'TABLE'). Você gostaria de fazer o mesmo com uma tabela event_type.

Só para esclarecer, você quer dizer que devo adicionar uma tabela adicional que mapeie quais eventos são permitidos em uma tabela e use a PK dessa tabela na tabela de eventos em vez de ter umTABLE / EVENT par?

@ben:

Essas são todas as estatísticas derivadas de dados existentes, não são?

As tabelas adicionais são principalmente relacionadas a estatísticas, mas os dados ainda não existem, alguns exemplos:

user_ad_stats                          user_post_stats
-------------                          ---------------
user_ad_id (FK)                        user_post_id (FK)
ip                                     ip
date                                   date
type (impressed, clicked)

Se eu largar essas tabelas, não tenho como acompanhar quem, o que ou quando, não tenho certeza de como as visualizações podem ajudar aqui.

Eu concordo que deve ser separado, mas mais porque são dados fundamentalmente diferentes. O que alguém é e o que alguém faz são duas coisas diferentes. Não acho que a volatilidade seja tão importante.

Já ouvi as duas coisas e não consegui encontrar nada no manual do MySQL que afirme que qualquer uma delas esteja certa. De qualquer forma, concordo com você que elas devem ser tabelas separadas porque representam tipos de dados (com o benefício adicional de serem mais descritivas do que uma abordagem regular).

Acho que você está sentindo falta da floresta para as árvores, por assim dizer.

O predicado da sua tabela seria "ID do usuário do IP IP no momento DATE EVENTED to TABLE", o que parece razoável, mas há problemas.

O que eu quis dizer com "não é tão ruim quanto o EAV" é que todos os registros seguem uma estrutura linear e são muito fáceis de consultar, não há estrutura hierárquica, portanto todas as consultas podem ser feitas com um simplesSELECT.

Em relação à sua segunda declaração, acho que você me entendeu errado aqui; o endereço IP não está necessariamente associado ao usuário. A estrutura da tabela deve ler algo como isto:

Endereço de IP (IP) fez algo (EVENT) ao PK (ID) da tabela (TABLE) Na data (DATE)

Por exemplo, na última linha do meu exemplo acima, ele deve ler que o IP 217.0.0.1 (algum administrador) excluiu o usuário # 2 (cujo último IP conhecido é 127.0.0.2) em 20/04/2010 03:20:00 .

Você ainda pode ingressar, digamos, em eventos do usuário, mas não pode implementar uma restrição de chave estrangeira.

Na verdade, essa é a minha principal preocupação. No entanto, não tenho certeza do que pode dar errado com esse design que não pode dar errado com um design relacional tradicional. Posso detectar algumas advertências, mas desde que o aplicativo mexa com o banco de dados saiba o que está fazendo, acho que não deve haver problemas.

Outra coisa que conta nesse argumento é que eu armazenarei muito mais eventos e cada evento mais que dobrará em comparação ao design original; faz todo o sentido usar oARCHIVE mecanismo de armazenamento aqui, a única coisa é que ele não suportaFKs (nemUPDATEs ouDELETEs)

questionAnswers(3)

yourAnswerToTheQuestion