Mysql
 sql >> Datenbank >  >> RDS >> Mysql

beste Möglichkeit, URLs für eine lese- und schreibintensive Anwendung in MySQL zu speichern

Ich habe mich ausführlich damit auseinandergesetzt, und meine allgemeine Philosophie ist es, die Frequency-of-Use-Methode anzuwenden. Es ist umständlich, aber Sie können damit einige großartige Analysen der Daten durchführen:

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;

Als allgemeine Regel haben Sie ähnliche Pfade auf einer einzelnen Domain, aber unterschiedliche QueryStrings für jeden Pfad.

Ich habe dies ursprünglich so entworfen, dass alle Teile in einer einzigen Tabelle (Protokoll, Domäne, Pfad, Abfragezeichenfolge) indiziert werden, aber ich denke, dass das Obige weniger platzintensiv ist und sich besser eignet, um bessere Daten daraus zu gewinnen.

text neigt dazu, langsam zu sein, sodass Sie "Path" nach einiger Verwendung in einen Varchar ändern können. Die meisten Server sterben nach etwa 1K für eine URL, aber ich habe einige große gesehen und würde auf der Seite bleiben, keine Daten zu verlieren.

Ihre Abrufabfrage ist umständlich, aber wenn Sie sie in Ihrem Code abstrahieren, kein Problem:

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;

Speichern Sie eine Portnummer, wenn sie nicht dem Standard entspricht (nicht 80 für http, nicht 443 für https), speichern Sie sie andernfalls als NULL, um anzugeben, dass sie nicht enthalten sein sollte. (Sie können die Logik zu MySQL hinzufügen, aber es wird viel hässlicher.)

Ich würde immer (oder nie) das "/" aus dem Pfad sowie das "?" aus dem QueryString für Platzeinsparungen. Nur Verlust wäre in der Lage, zwischen

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

Was, wenn wichtig, dann würde ich Ihren Ansatz ändern, um es niemals zu entfernen und es einfach einzuschließen. Technisch gesehen

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

Sind gleich, also ist das Entfernen des Pfad-Schrägstrichs immer in Ordnung.

Also zum Analysieren:

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

Wir würden so etwas wie parse_url verwenden in PHP zu erzeugen:

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

Sie würden dann prüfen/einfügen (mit entsprechenden Sperren, nicht gezeigt):

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

(falls nicht vorhanden)

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

Wir haben dann unsere $DomainID vorwärts...

Dann in DomainPath einfügen:

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

(falls nicht vorhanden, analog einfügen)

Wir haben dann unsere $DomainPathID vorwärts...

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

und ggf. einfügen.

Nun, lassen Sie mich wichtig anmerken , dass das obige Schema für Hochleistungsseiten langsam sein wird. Sie sollten alles ändern, um eine Art Hash zu verwenden, um SELECT zu beschleunigen s. Kurz gesagt, die Technik ist wie folgt:

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

Ich habe es absichtlich aus dem obigen entfernt, um es einfach zu halten, aber das Vergleichen eines TEXT mit einem anderen TEXT für Auswahlen ist langsam und bricht bei wirklich langen Abfragezeichenfolgen. Verwenden Sie auch keinen Index mit fester Länge, da dieser ebenfalls beschädigt wird. Für Zeichenfolgen beliebiger Länge, bei denen es auf Genauigkeit ankommt, ist eine Hash-Fehlerrate akzeptabel.

Führen Sie schließlich, wenn möglich, die MD5-Hash-Clientseite aus, um das Senden großer Blobs an den Server für die MD5-Operation zu sparen. Die meisten modernen Sprachen unterstützen das integrierte MD5:

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

Aber ich schweife ab.