sql >> Databasteknik >  >> RDS >> Mysql

bästa sättet att lagra url i mysql för en läs- och skrivintensiv applikation

Jag har behandlat detta mycket, och min allmänna filosofi är att använda metoden för användningsfrekvens. Det är besvärligt, men det låter dig köra några fantastiska analyser på data:

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;

Som en allmän regel kommer du att ha liknande sökvägar på en enskild domän, men olika QueryStrings för varje sökväg.

Jag designade ursprungligen detta för att ha alla delar indexerade i en enda tabell (protokoll, domän, sökväg, frågesträng) men tycker att ovanstående är mindre utrymmeskrävande och ger bättre tillgång till bättre data.

text tenderar att vara långsam, så du kan ändra "Path" till en varchar efter viss användning. De flesta servrar dör efter cirka 1K för en URL, men jag har sett några stora och skulle ta fel på sidan om att inte förlora data.

Din hämtningsfråga är besvärlig, men om du abstraherar bort den i din kod är det inga 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;

Lagra ett portnummer om det inte är standard (icke-80 för http, icke-443 för https), annars lagras det som NULL för att visa att det inte ska inkluderas. (Du kan lägga till logiken i MySQL men det blir mycket fulare.)

Jag skulle alltid (eller aldrig) ta bort "/" från vägen såväl som "?" från QueryString för utrymmesbesparingar. Endast förlust skulle kunna skilja mellan

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

Vilket, om det är viktigt, så skulle jag ändra ditt grepp för att aldrig ta bort det och bara inkludera det. Tekniskt sett,

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

Är desamma, så att ta bort snedstrecket Path är alltid OK.

Så, för att analysera:

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

Vi skulle använda något som parse_url i PHP för att producera:

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

Du skulle då kontrollera/sätta in (med lämpliga lås, visas inte):

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

(om det inte finns)

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

Vi har då vårt $DomainID framöver...

Infoga sedan i DomainPath:

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

(om det inte finns, infoga det på samma sätt)

Vi har då vårt $DomainPathID framöver...

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

och infoga vid behov.

Låt mig nu notera viktigt , att ovanstående schema kommer att vara långsamt för högpresterande webbplatser. Du bör modifiera allt för att använda en hash av något slag för att snabba upp SELECT s. Kort sagt, tekniken är som:

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

Jag har medvetet eliminerat det från ovan för att hålla det enkelt, men att jämföra en TEXT med en annan TEXT för markeringar är långsam och går sönder för riktigt långa frågesträngar. Använd inte heller ett index med fast längd eftersom det också kommer att gå sönder. För strängar med godtycklig längd där noggrannhet spelar roll, är en hash-felfrekvens acceptabel.

Slutligen, om du kan, gör MD5-hash-klientsidan för att spara att skicka stora blobbar till servern för att utföra MD5-operationen. De flesta moderna språk stöder MD5 inbyggt:

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

Men jag avviker.



  1. Ladda ner csv från codeigniter mysql

  2. PostgreSQL:Tid för att skapa tabeller

  3. Anslut MySQL till Spring-applikationen

  4. Effektiv tilldelning av percentil/rank i MYSQL