2011-07-23 72 views
11

我需要在數據庫中存儲潛在的數百萬個URL。每個URL都應該是唯一的,因此我將使用ON DUPLICATE KEY UPDATE並計算重複的URL。如何在MySQL中存儲URL

但是,我無法在URL字段上創建索引,因爲我的varchar字段爲400個字符。 MySQL正在抱怨並說: 「#1071 - 指定的密鑰太長,最大密鑰長度是767字節」。 (Varchar 400需要1200個字節)

如果您需要在單臺服務器上每天處理最少500000個URL,那麼最佳方法是什麼?

我們已經在考慮對同一個應用程序使用MongoDB,所以我們可以簡單地查詢MongoDB並找到重複的URL並更新該行。但是,我不贊成使用MongoDB來解決這個問題,而且我希望在這個階段只使用MySQL,因爲我希望在開始時儘可能精簡,並且可以更快地完成項目的這一部分。 (我們還沒有玩過MongoDB,不想花時間在這個階段)

是否有任何其他的可能性使用較少的資源和時間來做到這一點。我正在考慮獲取URL的MD5哈希值並存儲它。而我可以將該字段改爲UNIQUE。我知道,會有衝突,但如果這是唯一的問題,則可以在1億個網址中添加5-10-20個重複項。

你有什麼建議嗎?我也不想花費10秒來插入一個URL,因爲它每天處理500k個URL。

你會建議什麼?

編輯:根據請求,這是表定義。 (我不是在此刻使用MD5,它是用於測試)

mysql> DESC url; 
+-------------+-----------------------+------+-----+-------------------+-----------------------------+ 
| Field  | Type     | Null | Key | Default   | Extra      | 
+-------------+-----------------------+------+-----+-------------------+-----------------------------+ 
| url_id  | int(11) unsigned  | NO | PRI | NULL    | auto_increment    | 
| url_text | varchar(400)   | NO |  |     |        | 
| md5   | varchar(32)   | NO | UNI |     |        | 
| insert_date | timestamp    | NO |  | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | 
| count  | mediumint(9) unsigned | NO |  | 0     |        | 
+-------------+-----------------------+------+-----+-------------------+-----------------------------+ 
5 rows in set (0.00 sec) 
+2

考慮到有3.4 * 10^38個可能的MD5哈希值,它不太可能在100,000,000個URL中發生5次碰撞......並且存在更大的哈希值。 –

+0

是的,沒錯。我實際上沒有計算它。 – merinn

+0

請張貼表格定義 – Bohemian

回答

8

按照DNS spec域名的最大長度爲:

DNS本身只放置一個限制對可以用來識別資源記錄特定的標籤
。該限制
涉及標籤的長度和全名。
的長度任何一個標籤被限制在1到63個八位字節之間。完整域
名稱限於255個八位字節(包括分隔符)。

255 * 3 = 765 < 767(只是勉強:-))

然而注意到,每個部件只能是63個字符長。

所以我建議將網址切分成組件位。

http://foo.example.com/a/really/long/path?with=lots&of=query&parameters=that&goes=on&forever&and=ever使用

也許這將是足夠的:

  • 協議標誌[ 「HTTP」 - > 0](商店 「HTTP」 爲0時, 「HTTPS」 爲1,等等。)
  • 子域[ 「foo」 的](255 - 63 = 192個字符:我可以減去2更因爲分鐘TLD是2個字符)
  • 域[ 「例如」],(63個字符)
  • TLD [」 COM「(4個字來處理 」信息「 TLD)
  • 路徑[ 」A /真的/長/路徑「(只要你想要的 - 存儲在單獨的表
  • queryparameters [ 「with = lots & of = query & parameters = that & goes = on &永遠&和=永遠」](存儲在單獨的密鑰/值表)很少使用可以是在一個單獨的關鍵字的表,如果實際需要
  • 端口號/認證的東西。

這給了你一些不錯的優勢:

  • 該指數僅在網址的,你需要在搜索零件
  • 查詢可以被限制在不同的(比較小的索引!) URL部分(找到例如Facebook的域中的每個網址)
  • 有太長的子域/域是假
  • 容易丟棄的查詢參數的任何URL。
  • 容易做到不區分大小寫的域名/ TLD搜索
  • 丟棄的語法糖(「://」,「」協議之後,子域/域,域/ TLD之間,‘/’TLD和路徑之間,「 ?「在查詢之前,」&「」=「在查詢中)
  • 避免主要的稀疏表問題。大多數網址不會有查詢參數,也不會有長路徑。如果這些字段在一個單獨的表中,那麼你的主表不會受到大小的影響。在進行查詢時,更多記錄將適合內存,因此查詢性能更快。
  • (這裏更有優勢)。
+0

+1對於765 <767:P –

+0

感謝+1 ..無論原因:-P – Pat

+1

請注意,處理TLD的'4個字符'不是很好的做法。從http://stackoverflow.com/questions/9238640/how-long-can-a-tld-possibly-be可能高達63個字符,目前最大的是24. – Eborbob

-1

你可以改變從varchar url_text(400),以文字,那麼你可以反對它添加全文索引,讓您搜索插入它之前存在的URL。

+0

URL的全文索引? – Karolis

+0

某些網址最長可達2083個字符,例如IE8。在通常情況下,URL不會很長,但你應該爲他們做好準備。請參閱討論http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url – woot586

+0

我的意思不是URL的長度,而是URL的全文索引的有用性:) – Karolis

0

要索引的字段最多767個字符寬,它字符集必須是ASCII或相似,因此不容是UTF8,因爲它使用每炭的3個字節,所以maximun寬對索引UTF8字段255是

當然,767 ascii url字段,超過您的最初的400個字符規格。當然,有些網址超出了767的限制。也許你可以存儲和索引第一個735字符加上md5散列。您還可以使用文本 full_url字段來保留原始值。
請注意,ascii字符集對於網址來說已經足夠了

+0

看到一個很好的替代方法[這裏](http://stackoverflow.com/questions/5147867/best-way-to-store-url-in-mysql-for-a-readwrite-intensive-application/5148006#5148006)發貼者@razzed –

0

格式良好的URL只能包含ASCII範圍內的字符 - 其他字符需要進行編碼。因此,假設您打算存儲的URL格式良好(如果它們不是,您可能需要在將它們插入數據庫之前修復它們),則可以將url_text列字符集設置爲ASCII(在MySQL中爲latin1)。使用ASCII時,一個字符是一個字節,並且您將能夠索引整個400個字符。

0

用MD5(128位)的僞碰撞的機率可以表述是這樣的:

「如果你有9個萬億不同的項目,有9萬億隻有一個機會,他們兩個人具有相同的MD5「。

從另一個角度來看,它更可能在贏得大彩票時被流星擊中。

+0

不知道是不是真的...我幾乎想要去做數學... – Ben

+0

我認爲這是不正確的:http://en.wikipedia.org/wiki/Birthday_problem# Cast_as_a_collision_problem – endolith