Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

melhor maneira de armazenar url no mysql para um aplicativo de leitura e gravação intensiva


Eu lidei com isso extensivamente, e minha filosofia geral é usar o método de frequência de uso. É complicado, mas permite que você execute ótimas análises sobre os dados:
CREATE TABLE URL (
   ID            integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   DomainPath    integer unsigned NOT NULL,
   QueryString   text
) Engine=MyISAM;

CREATE TABLE DomainPath (   
   ID            integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   Domain        integer unsigned NOT NULL,
   Path          text,
   UNIQUE (Domain,Path)
) Engine=MyISAM;

CREATE TABLE Domain (   
   ID            integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   Protocol      tinyint NOT NULL,
   Domain        varchar(64)
   Port          smallint NULL,
   UNIQUE (Protocol,Domain,Port)
) Engine=MyISAM;

Como regra geral, você terá Caminhos semelhantes em um único Domínio, mas QueryStrings diferentes para cada caminho.

Eu originalmente projetei isso para ter todas as partes indexadas em uma única tabela (Protocol, Domain, Path, Query String), mas acho que o acima ocupa menos espaço e é melhor para obter melhores dados.

text tende a ser lento, então você pode alterar "Path" para um varchar após algum uso. A maioria dos servidores morre após cerca de 1K por um URL, mas eu já vi alguns grandes e erraria por não perder dados.

Sua consulta de recuperação é complicada, mas se você a abstrair em seu código, não há problema:
SELECT CONCAT(
    IF(D.Protocol=0,'http://','https://'),
    D.Domain,
    IF(D.Port IS NULL,'',CONCAT(':',D.Port)), 
    '/', DP.Path, 
    IF(U.QueryString IS NULL,'',CONCAT('?',U.QueryString))
)
FROM URL U
INNER JOIN DomainPath DP ON U.DomainPath=DP.ID
INNER JOIN Domain D on DP.Domain=D.ID
WHERE U.ID=$DesiredID;

Armazene um número de porta se não for padrão (não-80 para http, não-443 para https), caso contrário, armazene-o como NULL para significar que não deve ser incluído. (Você pode adicionar a lógica ao MySQL, mas fica muito mais feio.)

Eu sempre (ou nunca) tiraria o "/" do Caminho, assim como o "?" do QueryString para economia de espaço. Só a perda seria capaz de distinguir entre
http://www.example.com/
http://www.example.com/?

O que, se for importante, eu mudaria sua tática para nunca tirá-lo e apenas incluí-lo. Tecnicamente,
http://www.example.com 
http://www.example.com/

São os mesmos, portanto, remover a barra Path está sempre OK.

Então, para analisar:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords

Usaríamos algo como parse_url em PHP para produzir:
array(
    [scheme] => 'http',
    [host] => 'www.example.com',
    [path] => '/my/path/to/my/file.php',
    [query] => 'id=412&crsource=google+adwords',
)

Você então verificaria/inseriria (com bloqueios apropriados, não mostrados):
SELECT D.ID FROM Domain D 
WHERE 
    D.Protocol=0 
    AND D.Domain='www.example.com' 
    AND D.Port IS NULL

(se não existir)
INSERT INTO Domain ( 
    Protocol, Domain, Port 
) VALUES ( 
    0, 'www.example.com', NULL 
);

Temos então nosso $DomainID daqui para frente...

Em seguida, insira no DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE 
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';

(se não existir, insira-o da mesma forma)

Temos então nosso $DomainPathID daqui para frente...
SELECT U.ID FROM URL 
WHERE 
    DomainPath=$DomainPathID 
    AND QueryString='id=412&crsource=google+adwords'

e insira se necessário.

Agora, deixe-me observar importante , que o esquema acima será lento para sites de alto desempenho. Você deve modificar tudo para usar um hash de algum tipo para acelerar SELECT s. Em suma, a técnica é como:
CREATE TABLE Foo (
     ID integer unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
     Hash varbinary(16) NOT NULL,
     Content text
) Type=MyISAM;

SELECT ID FROM Foo WHERE Hash=UNHEX(MD5('id=412&crsource=google+adwords'));

Eu deliberadamente o eliminei do acima para mantê-lo simples, mas comparar um TEXT com outro TEXT para seleções é lento e quebra para strings de consulta muito longas. Não use um índice de tamanho fixo porque isso também quebrará. Para strings de comprimento arbitrário onde a precisão é importante, uma taxa de falha de hash é aceitável.

Finalmente, se você puder, faça o lado do cliente de hash MD5 para salvar o envio de blobs grandes ao servidor para fazer a operação MD5. A maioria das linguagens modernas suporta MD5 integrado:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');

Mas eu discordo.