Saya telah membahas ini secara ekstensif, dan filosofi umum saya adalah menggunakan metode frekuensi penggunaan. Ini rumit, tetapi memungkinkan Anda menjalankan beberapa analitik hebat pada 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;
Sebagai aturan umum, Anda akan memiliki Jalur yang serupa pada satu Domain, tetapi QueryStrings yang berbeda untuk setiap jalur.
Awalnya saya merancang ini agar semua bagian diindeks dalam satu tabel (Protokol, Domain, Jalur, String Kueri) tetapi menurut saya hal di atas kurang memakan ruang dan lebih baik untuk mendapatkan data yang lebih baik darinya.
text
cenderung lambat, sehingga Anda dapat mengubah "Jalur" menjadi varchar setelah digunakan. Sebagian besar server mati setelah sekitar 1K untuk sebuah URL, tetapi saya telah melihat beberapa yang besar dan akan berbuat salah di sisi tidak kehilangan data.
Permintaan pengambilan Anda rumit, tetapi jika Anda mengabstraksikannya dalam kode Anda, tidak ada masalah:
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;
Simpan nomor port jika tidak standar (non-80 untuk http, non-443 untuk https), jika tidak, simpan sebagai NULL untuk menandakan itu tidak boleh disertakan. (Anda dapat menambahkan logika ke MySQL tetapi menjadi jauh lebih buruk.)
Saya akan selalu (atau tidak pernah) menghapus "/" dari Path serta "?" dari QueryString untuk menghemat ruang. Hanya kehilangan yang bisa membedakan antara
http://www.example.com/
http://www.example.com/?
Yang, jika penting, maka saya akan mengubah taktik Anda untuk tidak pernah menghapusnya dan hanya memasukkannya. Secara teknis,
http://www.example.com
http://www.example.com/
Sama, jadi menghapus garis miring Path tidak masalah.
Jadi, untuk menguraikan:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords
Kami akan menggunakan sesuatu seperti parse_url
di PHP untuk menghasilkan:
array(
[scheme] => 'http',
[host] => 'www.example.com',
[path] => '/my/path/to/my/file.php',
[query] => 'id=412&crsource=google+adwords',
)
Anda kemudian akan memeriksa/menyisipkan (dengan kunci yang sesuai, tidak ditampilkan):
SELECT D.ID FROM Domain D
WHERE
D.Protocol=0
AND D.Domain='www.example.com'
AND D.Port IS NULL
(jika tidak ada)
INSERT INTO Domain (
Protocol, Domain, Port
) VALUES (
0, 'www.example.com', NULL
);
Kami kemudian memiliki $DomainID
maju...
Kemudian masukkan ke DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';
(jika tidak ada, masukkan dengan cara yang sama)
Kami kemudian memiliki $DomainPathID
maju...
SELECT U.ID FROM URL
WHERE
DomainPath=$DomainPathID
AND QueryString='id=412&crsource=google+adwords'
dan masukkan jika perlu.
Sekarang, izinkan saya mencatat penting , bahwa skema di atas akan lambat untuk situs berkinerja tinggi. Anda harus memodifikasi semuanya untuk menggunakan semacam hash untuk mempercepat SELECT
s. Singkatnya, tekniknya seperti:
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'));
Saya sengaja menghilangkannya dari atas untuk membuatnya tetap sederhana, tetapi membandingkan TEXT dengan TEXT lain untuk pemilihan lambat, dan istirahat untuk string kueri yang sangat panjang. Jangan gunakan indeks dengan panjang tetap karena itu juga akan rusak. Untuk string panjang sewenang-wenang di mana akurasi penting, tingkat kegagalan hash dapat diterima.
Terakhir, jika Anda bisa, lakukan sisi klien hash MD5 untuk menghemat pengiriman gumpalan besar ke server untuk melakukan operasi MD5. Sebagian besar bahasa modern mendukung built-in MD5:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');
Tapi saya ngelantur.