Obsáhle jsem se tím zabýval a mou obecnou filozofií je používat metodu frekvence použití. Je to těžkopádné, ale umožňuje vám provádět skvělé analýzy dat:
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;
Obecně platí, že budete mít podobné cesty na jedné doméně, ale různé QueryString pro každou cestu.
Původně jsem to navrhl tak, aby byly všechny části indexovány v jediné tabulce (protokol, doména, cesta, řetězec dotazu), ale myslím si, že výše uvedené je méně náročné na prostor a lépe z toho získávají lepší data.
text
má tendenci být pomalý, takže po nějakém použití můžete změnit "Cesta" na varchar. Většina serverů umírá po přibližně 1 kB pro adresu URL, ale viděl jsem několik velkých a udělal bych chybu, pokud bych neztratil data.
Váš dotaz na vyhledávání je těžkopádný, ale pokud jej v kódu abstrahujete, žádný problém:
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;
Uložte číslo portu, pokud není standardní (jiné než 80 pro http, jiné než 443 pro https), jinak jej uložte jako NULL, což znamená, že by nemělo být zahrnuto. (Logiku můžete přidat do MySQL, ale bude mnohem ošklivější.)
Vždy (nebo nikdy) bych odstranil "/" z cesty stejně jako "?" z QueryString pro úsporu místa. Pouze ztráta by byla schopna rozlišit mezi
http://www.example.com/
http://www.example.com/?
Což, je-li to důležité, pak bych změnil váš připínáček tak, abyste ho nikdy nesvlékali a pouze zahrnuli. Technicky,
http://www.example.com
http://www.example.com/
Jsou stejné, takže odstranění lomítka cesty je vždy v pořádku.
Takže k analýze:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords
Použili bychom něco jako parse_url
v PHP vytvořit:
array(
[scheme] => 'http',
[host] => 'www.example.com',
[path] => '/my/path/to/my/file.php',
[query] => 'id=412&crsource=google+adwords',
)
Potom byste zaškrtli/vložili (s příslušnými zámky, které nejsou zobrazeny):
SELECT D.ID FROM Domain D
WHERE
D.Protocol=0
AND D.Domain='www.example.com'
AND D.Port IS NULL
(pokud neexistuje)
INSERT INTO Domain (
Protocol, Domain, Port
) VALUES (
0, 'www.example.com', NULL
);
Pak máme naše $DomainID
vpřed...
Poté vložte do DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';
(pokud neexistuje, vložte jej podobně)
Pak máme naše $DomainPathID
vpřed...
SELECT U.ID FROM URL
WHERE
DomainPath=$DomainPathID
AND QueryString='id=412&crsource=google+adwords'
a v případě potřeby vložte.
Nyní mi dovolte upozornit důležitě , že výše uvedené schéma bude pro vysoce výkonné weby pomalé. Vše byste měli upravit tak, aby se používal nějaký hash pro urychlení SELECT
s. Stručně řečeno, technika je taková:
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'));
Záměrně jsem to odstranil z výše uvedeného, aby to bylo jednoduché, ale porovnávání TEXTu s jiným TEXTem pro výběry je pomalé a přerušuje se pro opravdu dlouhé řetězce dotazů. Nepoužívejte ani index s pevnou délkou, protože by se také zlomil. Pro řetězce libovolné délky, kde záleží na přesnosti, je míra selhání hash přijatelná.
Nakonec, pokud můžete, proveďte hash MD5 na straně klienta, abyste si ušetřili odesílání velkých objektů BLOB na server za účelem provedení operace MD5. Většina moderních jazyků podporuje vestavěný MD5:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');
Ale to jsem odbočil.