Normalization

儲存大量連結的正確方法?

  • October 18, 2021

我正在對多個站點進行小型爬網,並且我有很多由 ID ( example.com/foo= 354) 表示的連結。

我目前正在儲存link -> link參考資料和連結文本。因此,在下表頁面“2845”中包含一個指向 4479 的連結,其中包含文本“About Us”。沒什麼大不了的,只有3NF。

+----------+----------+-----------------+
| url_1_id | url_2_id | text            |
+----------+----------+-----------------+
|     2845 |     4479 | About Us        |
|     2845 |     4480 | Who We Are      |
|     2845 |     4481 | What We Do      |
|     2845 |     4482 | Core Principles |
|     2845 |     4483 | Research Staff  |
+----------+----------+-----------------+

但是,根據我的計算(大多數頁面每個包含 500-1000 個連結),當我只解析 200k 個頁面時,我應該有 6GB 的連結數據。

有沒有更好的方法來儲存連結數據?特別是如果有一種很好的方法來規範所有網站頁面的重複導航菜單。

如果您不能或不會使用 Graph DB(它應該是此類應用程序的最佳 DB),那麼您可以測試數據的稍微抽象的分解。這是我對您的任務採取的一種方法。

首先,您有特定的 URL 連結。例如,在另一個答案的末尾可以找到指向joanolo的連結https://dba.stackexchange.com/users/112361/joanolo 。一個頁面上可能有多個這樣的連結(即 joanolo 的評論每個都有相同的連結)。您想如何處理頁面上的多個相同連結?我選擇以特定方式處理相同的連結,如下所述。我定義表來處理 URL 連結,如下所示:

CREATE TABLE URLLinks (
   URLLinksID bigint identity(1,1),
   URLParams nvarchar(max) NULL,
   URLsID bigint not null,
   secure bit default 0 not null,
   CONSTRAINT pk_URLLINKS Primary Key (URLsID, URLParams, Secure) 
   )

請注意,URL 連結包括連結上的任何參數,以及連接是否安全。對於這第一遍,我忽略了其他類型的連結,例如 ftp://

URLLinks 表中的 URLsID 欄位是對 URLs 表的引用。這包含 URL 的文本,例如dba.stackexchange.com/users/112361/joanolo在上面的範例 URL 中。任何將此作為 URL 的連結都將通過 URLsID 引用此 URL 條目。這是非常簡單的 URL 表:

CREATE TABLE URLs (
   URLsID bigint identity(1,1),
   URLText nvarchar(max) not null
   )

在這兩個表之間,我們擁有重新創建 URL 所需的所有資訊,包括任何參數,而無需複制 URLText。

請注意,如果值得的話,我們可以將 URLText 分解成一個更小的片段鏈,並建構一個將正確片段連結在一起的 URL 鏈。我敢打賭,這個連結,一個 8 字節的 ID 指向每個片段,父片段(如果有)和鏈(24 字節 + 成本)將比使用 URL 文本在空間和成本上更昂貴直接,所以我現在不探索那條路。

要將連結與其頁面關聯,可以使用 URLRefs 表:

URLRefs (
   URLID bigint not null,
   ParentURLID bigint not null,
   LinkText nvarchar(max) not null,
   LinkCount int default 1 not null
   )

注意欄位LinkCount。這將計算頁面上出現相同 URLLink 的次數,而不是每次出現都有單獨的條目。這可能會或可能不會總體上節省空間和精力,但它確實可以在 dba.stackexchange.com 上聽到,所以我採用了這種方法。在頁面上從不重複連結的網站上,這將始終為 1。

使用這些元素,您應該擁有最緊湊的非圖形描述頁面,其中 URL 指向頁面,而 URL 指向…(除非您建構特殊情況來處理例如菜單,如 joanolo 建議的那樣。)

例如,上面顯示的 joanolo 連結在 URLLinks 中只有一個條目,在 URLs 中只有一個條目,在 URLRefs 中對於連結出現的每個不同頁面都有一個條目。如果他在 100 個頁面中有 500 個這樣的連結,那麼他在 URL 和 URLLinks 中仍然只有一個連結,在 URLRefs 中只有 100 個條目。

可能的方法:

  1. 如果您的問題可以通過圖表很好地表示(我認為是),我建議您考慮使用Graph Database。它針對這種場景進行了優化:您有頁面(節點),您有頁面之間的連結(頂點),並且您有一些與其中任何一個相關聯的屬性。檢查Neo4JOrientDB以了解它們在您的場景中可以做什麼。

  1. 如果您想節省一些空間,請考慮一種以緊湊方式表示*菜單的方法。*也就是說,會有非常多的情況,比如:

page1 - links_to - home

page1 - links_to - legalese page1

  • links_to - the_company

page1 - links_to - our_products

page1 - links_to - our_services

page1 - links_to - 聯繫人

page1 - links_to - 獨特或不常見的東西 1

page1 - links_to - 獨特或不常見的東西 2

然後再一次

(第 2 頁相同)

(第 3 頁相同)

如果你確定了這個結構,你可以創建一些“元結構”,比如“standard_menu”。(這種方法的困難部分是決定哪些連結集合應該被視為菜單,哪些不是,以及小的差異,例如包含略有變化的菜單的頁面

$$ such as “I don’t link to myself” $$).

這個資料結構應該可以幫助你:

CREATE TABLE urls
(
   id_url serial PRIMARY KEY,
   url text NOT NULL,
   UNIQUE(url)
) ;

CREATE TABLE menus
(
   id_menu serial PRIMARY KEY
) ;

CREATE TABLE menu_links
(
   id_menu integer not null REFERENCES menus(id_menu),
   id_url_to integer not null REFERENCES urls(id_url),
   link_text text not null,
   PRIMARY KEY (id_menu, id_url_to)
) ;

CREATE TABLE from_page_to_page
(
   id_url_from integer not null REFERENCES urls(id_url),
   id_url_to integer not null REFERENCES urls(id_url),
   link_text text not null,
   PRIMARY KEY (id_url_from, id_url_to, link_text)
) ;

CREATE TABLE from_page_to_menu
(
   id_url_from integer not null REFERENCES urls(id_url),
   id_menu integer not null REFERENCES menus(id_menu),
   PRIMARY KEY (id_url_from, id_menu)
) ;

最後,您可以通過創建“菜單連結”和“非菜單連結”的 UNION 來獲得將現有表的等價物組合在一起的視圖:

CREATE VIEW all_links AS

SELECT 
   id_url_from, id_url_to, link_text 
FROM 
   from_page_to_page
UNION ALL
SELECT 
   id_url_from, id_url_to, link_text
FROM 
   from_page_to_menu
   JOIN menu_links ON menu_links.id_menu = from_page_to_menu.id_menu ;

或者,每次你有類似的東西時,你總是可以通過“link_group”更改名稱“menu”:

page1 - links_to - page_a

page1 - links_to - page_b

page1 - links_to - page_c

page1 - links_to - page_z

page2 - links_to - page_a

page2 - links_to - page_b

page2 - links_to - page_c

page2 - links_to - page_y

page3 - links_to

  • page_a page3 - links_to - page_b page3 - links_to

  • page_c page3

  • links_to - page_x

(忽略文本標籤,這些實際上是 12 對數字)

您可以將其轉換為:

link_grup_1 - page_a

link_grup_1 - page_b

link_grup_1 - page_c

page_1 - link_grup - link_grup_1

page_1 - links_to - page_z page_2 - link_grup

  • link_grup_1 page_2

  • links_to - page_y

page_3 - link_grup - link_grup_1

page_3 - links_to - page_x

(這些只是 9 對數字)。

一旦許多頁面共有的項目數量超過兩個或三個,節省的費用就會增加。

引用自:https://dba.stackexchange.com/questions/160112