如何最好地將 Google ngram 儲存在數據庫中?
幾天前我下載了google onegrams,已經是海量數據了。我將 10 個包中的第一個插入 mysql,現在我有一個 4700 萬條記錄的數據庫。
我想知道如何最好地將 Google ngrams 儲存在數據庫中。我的意思是,如果您不使用一克,但例如兩克或三克,則數量會大得多。我可以在一個數據庫中儲存 5 億條記錄並使用它,還是應該將其拆分到不同的表中?
一個應該拆分多少條記錄後,應該如何最好地拆分它(考慮到 twograms 有 100 個文件,因此可能有大約 50 億條記錄)?是否建議使用 MySQL 水平分區或建構自己的分區(例如通過單詞的第一個字元 => twograms_a)。
我必須對我的第一個答案做出很多改變我開始這個!
USE test DROP TABLE IF EXISTS ngram_key; DROP TABLE IF EXISTS ngram_rec; DROP TABLE IF EXISTS ngram_blk; CREATE TABLE ngram_key ( NGRAM_ID UNSIGNED BIGINT NOT NULL AUTO_INCREMENT, NGRAM VARCHAR(64) NOT NULL, PRIMARY KEY (NGRAM), KEY (NGRAM_ID) ) ENGINE=MyISAM ROW_FORMAT=FIXED PARTITION BY KEY(NGRAM) PARTITIONS 256; CREATE TABLE ngram_rec ( NGRAM_ID UNSIGNED BIGINT NOT NULL, YR SMALLINT NOT NULL, MC SMALLINT NOT NULL, PC SMALLINT NOT NULL, VC SMALLINT NOT NULL, PRIMARY KEY (NGRAM_ID,YR) ) ENGINE=MyISAM ROW_FORMAT=FIXED; CREATE TABLE ngram_blk ( NGRAM VARCHAR(64) NOT NULL, YR SMALLINT NOT NULL, MC SMALLINT NOT NULL, PC SMALLINT NOT NULL, VC SMALLINT NOT NULL ) ENGINE=BLACKHOLE; DELIMITER $$ CREATE TRIGGER populate_ngram AFTER INSERT ON ngram_blk FOR EACH ROW BEGIN DECLARE NEW_ID BIGINT; INSERT IGNORE INTO ngram_key (NGRAM) VALUES (NEW.NGRAM); SELECT NGRAM_ID INTO NEW_ID FROM ngram_key WHERE NGRAM=NEW.NGRAM; INSERT IGNORE INTO ngram_rec VALUES (NEW_ID,NEW.YR,NEW.MC,NEW.PC,NEW.VC); END; $$ DELIMITER ; INSERT INTO ngram_blk VALUES ('rolando',1965,31,29,85), ('pamela',1971,33,21,86), ('dominique',1996,30,18,87), ('diamond',1998,13,28,88), ('rolando edwards',1965,31,29,85), ('pamela edwards',1971,33,21,86), ('dominique edwards',1996,30,18,87), ('diamond edwards',1998,13,28,88), ('rolando angel edwards',1965,31,29,85), ('pamela claricia edwards',1971,33,21,86), ('dominique sharlisee edwards',1996,30,18,87), ('diamond ashley edwards',1998,13,28,88); UPDATE ngram_rec SET yr=yr+1,mc=mc+30,pc=pc+30,vc=vc+30; INSERT INTO ngram_blk VALUES ('rolando',1965,31,29,85), ('pamela',1971,33,21,86), ('dominique',1996,30,18,87), ('diamond',1998,13,28,88), ('rolando edwards',1965,31,29,85), ('pamela edwards',1971,33,21,86), ('dominique edwards',1996,30,18,87), ('diamond edwards',1998,13,28,88), ('rolando angel edwards',1965,31,29,85), ('pamela claricia edwards',1971,33,21,86), ('dominique sharlisee edwards',1996,30,18,87), ('diamond ashley edwards',1998,13,28,88); UPDATE ngram_rec SET yr=yr+1,mc=mc+30,pc=pc+30; INSERT INTO ngram_blk VALUES ('rolando',1965,31,29,85), ('pamela',1971,33,21,86), ('dominique',1996,30,18,87), ('diamond',1998,13,28,88), ('rolando edwards',1965,31,29,85), ('pamela edwards',1971,33,21,86), ('dominique edwards',1996,30,18,87), ('diamond edwards',1998,13,28,88), ('rolando angel edwards',1965,31,29,85), ('pamela claricia edwards',1971,33,21,86), ('dominique sharlisee edwards',1996,30,18,87), ('diamond ashley edwards',1998,13,28,88); UPDATE ngram_rec SET yr=yr+1,mc=mc+30; SELECT * FROM ngram_key; SELECT * FROM ngram_rec; SELECT A.ngram NGram,B.yr Year,B.mc Matches,B.pc Pages,B.vc Volumes FROM ngram_key A,ngram_rec B WHERE A.ngram='rolando angel edwards' AND A.ngram_id=B.ngram_id;
用於年份資訊的表格要小得多,但要保留原始 ngram 的密鑰要大得多。我還增加了測試數據量。您可以將其直接剪切並粘貼到 MySQL 中。
警告
只需刪除 ROW_FORMAT ,它就會變得動態並將 ngram_key 表壓縮得更小。
磁碟空間指標
nrgram_rec 每行 17 個
字節 ngram_id 8 個字節(最大無符號值 18446744073709551615
$$ 2^64 - 1 $$)
4 個 smallints 的 8 個字節(每個 2 個字節)
1 個字節 MyISAM 內部刪除標誌
ngram_rec 的索引條目 = 10 字節 (8 (ngram_id) + 2 (yr))
4700 萬行 X 每行 17 字節 = 07.99 億字節 = 761.98577 MB
4700 萬行 X 每行 12 字節 = 05.64 億字節 = 537.85231 MB
4700 萬行 X 每行 29 字節 = 13.63 億字節 = 1.269393 GB
50 億行 X 每行 17 字節 = 0850 億字節 = 079.1624 GB
50 億行 X 每行 12 字節 = 0600 億字節 = 055.8793 GB
50 億行 X 每行 29 字節 = 1450 億字節 = 135.0417 GB
ngram_key 有 73 個字節 ngram 有 64 個字節(ROW_FORMAT=FIXED 將 varchar 設置為 char) ngram_id 有 8 個字節 1 個字節 MyISAM 內部刪除標誌
ngram_key 的 2 個索引條目 = 64 字節 + 8 字節 = 72 字節
4700 萬行 X 每行 073 字節 = 34.31 億字節 = 3.1954 GB
4700 萬行 X 每行 072 字節 = 33.84 億字節 = 3.1515 GB
4700 萬行 X 每行 145 字節 = 68.15 億字節 = 6.3469 GB
50 億行 X 每行 073 字節 = 3650 億字節 = 339.9327 GB
50 億行 X 每行 072 字節 = 3600 億字節 = 335.2761 GB
50 億行 X 每行 145 字節 = 7250 億字節 = 675.2088 GB
這是一個非常瘋狂的建議
將所有 ngram 轉換為 32 個字元的 MD5 密鑰
該表將包含任何大小(最多 255 個字元)、1-gram、2-gram 等的所有 ngram。
use test DROP TABLE ngram_node; DROP TABLE ngram_blackhole; CREATE TABLE ngram_node ( NGRAM_KEY CHAR(32) NOT NULL, NGRAM_YEAR SMALLINT NOT NULL, M_COUNT SMALLINT NOT NULL, P_COUNT SMALLINT NOT NULL, V_COUNT SMALLINT NOT NULL, PRIMARY KEY (NGRAM_KEY,NGRAM_YEAR) ) ENGINE=MyISAM PARTITION BY KEY(NGRAM_KEY) PARTITIONS 256; CREATE TABLE ngram_blackhole ( NGRAM VARCHAR(255) NOT NULL, NGRAM_YEAR SMALLINT NOT NULL, M_COUNT SMALLINT NOT NULL, P_COUNT SMALLINT NOT NULL, V_COUNT SMALLINT NOT NULL ) ENGINE=BLACKHOLE; DELIMITER $$ CREATE TRIGGER populate_ngram AFTER INSERT ON ngram_blackhole FOR EACH ROW BEGIN INSERT INTO ngram_node VALUES (MD5(NEW.NGRAM),NEW.NGRAM_YEAR,NEW.M_COUNT,NEW.P_COUNT,NEW.V_COUNT); END; $$ DELIMITER ; INSERT INTO ngram_blackhole VALUES ('rolando',1965,31,29,85), ('pamela',1971,33,21,86), ('dominique',1996,30,18,87), ('diamond',1998,13,28,88), ('rolando edwards',1965,31,29,85), ('pamela edwards',1971,33,21,86), ('dominique edwards',1996,30,18,87), ('diamond edwards',1998,13,28,88), ('rolando angel edwards',1965,31,29,85), ('pamela claricia edwards',1971,33,21,86), ('dominique sharlisee edwards',1996,30,18,87), ('diamond ashley edwards',1998,13,28,88); SELECT * FROM ngram_node;
我之所以選擇 256 個分區,是因為 MD5 函式返回 16 個不同的字元(都是十六進制數字)。前兩個字節是 16 X 16, 256。
這是我的 Windows 7 桌面上 MySQL 5.5.11 的結果
mysql> use test Database changed mysql> DROP TABLE ngram_node; Query OK, 0 rows affected (0.22 sec) mysql> DROP TABLE ngram_blackhole; Query OK, 0 rows affected (0.11 sec) mysql> CREATE TABLE ngram_node -> ( -> NGRAM_KEY CHAR(32) NOT NULL, -> NGRAM_YEAR SMALLINT NOT NULL, -> M_COUNT SMALLINT NOT NULL, -> P_COUNT SMALLINT NOT NULL, -> V_COUNT SMALLINT NOT NULL, -> PRIMARY KEY (NGRAM_KEY,NGRAM_YEAR) -> ) ENGINE=MyISAM -> PARTITION BY KEY(NGRAM_KEY) -> PARTITIONS 256; Query OK, 0 rows affected (0.36 sec) mysql> CREATE TABLE ngram_blackhole -> ( -> NGRAM VARCHAR(255) NOT NULL, -> NGRAM_YEAR SMALLINT NOT NULL, -> M_COUNT SMALLINT NOT NULL, -> P_COUNT SMALLINT NOT NULL, -> V_COUNT SMALLINT NOT NULL -> ) ENGINE=BLACKHOLE; Query OK, 0 rows affected (0.11 sec) mysql> DELIMITER $$ mysql> CREATE TRIGGER populate_ngram AFTER INSERT ON ngram_blackhole FOR EACH ROW -> BEGIN -> INSERT INTO ngram_node VALUES (MD5(NEW.NGRAM),NEW.NGRAM_YEAR,NEW.M_COUNT,NEW.P_COUNT,NEW.V_COUNT); -> END; $$ Query OK, 0 rows affected (0.05 sec) mysql> DELIMITER ; mysql> INSERT INTO ngram_blackhole VALUES -> ('rolando',1965,31,29,85), -> ('pamela',1971,33,21,86), -> ('dominique',1996,30,18,87), -> ('diamond',1998,13,28,88), -> ('rolando edwards',1965,31,29,85), -> ('pamela edwards',1971,33,21,86), -> ('dominique edwards',1996,30,18,87), -> ('diamond edwards',1998,13,28,88), -> ('rolando angel edwards',1965,31,29,85), -> ('pamela claricia edwards',1971,33,21,86), -> ('dominique sharlisee edwards',1996,30,18,87), -> ('diamond ashley edwards',1998,13,28,88); Query OK, 12 rows affected (0.18 sec) Records: 12 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM ngram_node; +----------------------------------+------------+---------+---------+---------+ | NGRAM_KEY | NGRAM_YEAR | M_COUNT | P_COUNT | V_COUNT | +----------------------------------+------------+---------+---------+---------+ | 2ca237192aaac3b3a20ce0649351b395 | 1996 | 30 | 18 | 87 | | 6f7fd3368170c562604f62fb4e92056d | 1965 | 31 | 29 | 85 | | fb201333fef377917be714dabd3776d9 | 1971 | 33 | 21 | 86 | | 4f79e21800ed6e30be4d1cb597f910c6 | 1971 | 33 | 21 | 86 | | 9068e0de9f3fd674d4fa7cbc626e5888 | 1998 | 13 | 28 | 88 | | 8a18abe90f2612827dc3a215fd1905d3 | 1965 | 31 | 29 | 85 | | be60b431a46fcc7bf5ee4f7712993e3b | 1996 | 30 | 18 | 87 | | c8adc38aa00759488b1d759aa8f91725 | 1996 | 30 | 18 | 87 | | e80d4ab77eb18a4ca350157fd487d7e2 | 1965 | 31 | 29 | 85 | | 669ffc150d1f875819183addfc842cab | 1971 | 33 | 21 | 86 | | b685323e9de65080f733b53b2305da6e | 1998 | 13 | 28 | 88 | | 75c6f03161d020201000414cd1501f9f | 1998 | 13 | 28 | 88 | +----------------------------------+------------+---------+---------+---------+ 12 rows in set (0.00 sec) mysql>
請注意,我將 1-gram、2-gram 和 3-gram 載入到同一張表中,但您不知道哪個 MD5 屬於哪個 ngram。因此,所有的 ngram 都可以改裝到這張表中。只要記住插入到 ngram_blackhole 表中,剩下的就為你完成了。
無論是哪個 ngram,都必須使用 ngram 的 MD5() 查詢 ngram_node 表。
mysql> select * from ngram_node where ngram_key=MD5('rolando edwards'); +----------------------------------+------------+---------+---------+---------+ | NGRAM_KEY | NGRAM_YEAR | M_COUNT | P_COUNT | V_COUNT | +----------------------------------+------------+---------+---------+---------+ | 6f7fd3368170c562604f62fb4e92056d | 1965 | 31 | 29 | 85 | +----------------------------------+------------+---------+---------+---------+ 1 row in set (0.05 sec)
如果您希望將 1-gram、2-gram 和 3-gram 分離到單獨的儲存庫中,只需在黑洞表上創建另一個表、另一個黑洞表和另一個觸發器以插入到另一個表中。
此外,如果您的 ngram 長於 255(如果您正在執行 7-gram 或 8-gram),則只需增加 ngram_blackhole 表中 NGRAM 列的 VARCHAR 大小。
試一試 !!!
更新
在問題中,據說有 4700 萬行被載入到 mysql 中。對於我建議的表格佈局,請注意以下幾點:
ngram_node 是每行 41 個字節: 32 用於 NGRAM_KEY
8 用於數字(每個 SMALLINT 2)
1 用於內部 MyISAM DELETED 標誌
每個主鍵索引條目將是 34 字節
32 用於 NGRAM_KEY
2 用於 NGRAM_YEAR
4700 萬行 X 每行 41 字節 = 19.27 億字節,約 1.79466 GB。
4700 萬行 X 每個索引條目 34 字節 = 15.98 億字節,大約 1.48825 GB。
MyISAM 表的消耗總量應該約為 3.28291 GB。
該問題還提到載入 50 億行。
50 億行 X 每行 41 字節 = 2050 億字節,大約 190.9211 GB。
50 億行 X 每個索引條目 34 字節 = 1700 億字節,大約 158.3248 GB。
MyISAM 表的總消耗量應約為 349.2459 GB。
請注意,MyISAM 表中使用的空間的增長率是線性的,因為主鍵大小不變。您現在可以基於此對磁碟空間進行一些規劃。