Mysql
 sql >> Base de données >  >> RDS >> Mysql

meilleur moyen de stocker l'URL dans mysql pour une application intensive en lecture et en écriture

J'ai beaucoup traité de cela, et ma philosophie générale est d'utiliser la méthode de la fréquence d'utilisation. C'est fastidieux, mais cela vous permet d'effectuer d'excellentes analyses sur les données :

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;

En règle générale, vous aurez des chemins similaires sur un seul domaine, mais des QueryStrings différentes pour chaque chemin.

J'ai initialement conçu cela pour que toutes les parties soient indexées dans une seule table (protocole, domaine, chemin, chaîne de requête), mais je pense que ce qui précède est moins gourmand en espace et se prête mieux à l'obtention de meilleures données.

text a tendance à être lent, vous pouvez donc changer "Path" en varchar après quelques utilisations. La plupart des serveurs meurent après environ 1 000 pour une URL, mais j'en ai vu de gros et je préférerais ne pas perdre de données.

Votre requête de récupération est lourde, mais si vous l'abstrairez dans votre code, pas de problème :

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;

Stockez un numéro de port s'il n'est pas standard (non-80 pour http, non-443 pour https), sinon stockez-le comme NULL pour signifier qu'il ne doit pas être inclus. (Vous pouvez ajouter la logique au MySQL mais cela devient beaucoup plus moche.)

Je supprimerais toujours (ou jamais) le "/" du chemin ainsi que le "?" de la QueryString pour économiser de l'espace. Seule la perte serait en mesure de faire la distinction entre

http://www.example.com/
http://www.example.com/?

Ce qui, s'il est important, je changerais votre tactique pour ne jamais le supprimer et simplement l'inclure. Techniquement,

http://www.example.com 
http://www.example.com/

Sont les mêmes, donc supprimer la barre oblique du chemin est toujours OK.

Donc, pour analyser :

http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords

Nous utiliserions quelque chose comme parse_url en PHP pour produire :

array(
    [scheme] => 'http',
    [host] => 'www.example.com',
    [path] => '/my/path/to/my/file.php',
    [query] => 'id=412&crsource=google+adwords',
)

Vous cochez/insérez ensuite (avec les verrous appropriés, non illustrés) :

SELECT D.ID FROM Domain D 
WHERE 
    D.Protocol=0 
    AND D.Domain='www.example.com' 
    AND D.Port IS NULL

(si n'existe pas)

INSERT INTO Domain ( 
    Protocol, Domain, Port 
) VALUES ( 
    0, 'www.example.com', NULL 
);

Nous avons alors notre $DomainID aller de l'avant...

Insérez ensuite dans DomainPath :

SELECT DP.ID FORM DomainPath DP WHERE 
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';

(s'il n'existe pas, insérez-le de la même manière)

Nous avons alors notre $DomainPathID aller de l'avant...

SELECT U.ID FROM URL 
WHERE 
    DomainPath=$DomainPathID 
    AND QueryString='id=412&crsource=google+adwords'

et insérez si nécessaire.

Maintenant, permettez-moi de noter ce qui est important , que le schéma ci-dessus sera lent pour les sites hautes performances. Vous devez tout modifier pour utiliser un hachage quelconque pour accélérer SELECT s. En bref, la technique est la suivante :

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'));

Je l'ai délibérément éliminé de ce qui précède pour le garder simple, mais comparer un TEXTE à un autre TEXTE pour les sélections est lent et s'interrompt pour les chaînes de requête très longues. N'utilisez pas non plus un index de longueur fixe, car cela cassera également. Pour les chaînes de longueur arbitraire où la précision compte, un taux d'échec de hachage est acceptable.

Enfin, si vous le pouvez, effectuez le hachage MD5 côté client pour éviter d'envoyer de gros blobs au serveur pour effectuer l'opération MD5. La plupart des langages modernes prennent en charge MD5 intégré :

SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');

Mais je m'égare.