Google ngramをデータベースに保存するのに最適な方法は?


9

数日前にgoogle onegramをダウンロードしましたが、すでに大量のデータがあります。10個のパッケージの最初のパッケージをmysqlに挿入すると、4700万個のレコードデータベースができました。

どのようにしてGoogle ngramをデータベースに最適に保存すればよいのでしょうか。つまり、1グラムを使用していない場合、たとえば2グラムや3グラムを使用すると、量ははるかに多くなります。1つのデータベースに5億のレコードを保存して使用できますか、それとも別のテーブルに分割する必要がありますか?

いくつのレコードを分割する必要があり、どのように最適に分割する必要がありますか(2グラムには100個のファイルがあり、したがって約50億のレコードがあると考えます)。MySQLの水平パーティションを使用するか、独自のパーティションを構築することをお勧めしますか(たとえば、wordの最初の文字=> twograms_aを使用)。

回答:


4

私が最初の答えに加えなければならない変更がたくさんあったので、これを始めます!!!

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テーブルをはるかに小さく圧縮できます。


DiskSpaceメトリック

nrgram_recは、行ごとに17バイト、ngram_idは
8バイト(最大の符号なし値18446744073709551615 [2 ^ 64-1])
、4つのsmallintは8バイト(各2バイト)
1バイトMyISAM内部削除フラグ

ngram_recのインデックスエントリ= 10バイト(8(ngram_id)+ 2(yr))

4700万行X 1行あたり17バイト= 0799百万バイト= 761.98577 MB
4700万行X 1行あたり12バイト= 0564百万バイト= 537.85231 MB
4700万行X 1行あたり29バイト= 1億3600万バイト= 1.269393 GB

50億行X 17バイト/行= 0850億バイト= 079.1624 GB
50億行X 12バイト/行= 060億バイト= 055.8793 GB
50億行X 29バイト/行= 1450億バイト= 135.0417 GB


ngram_keyには73バイト64ビットのngram(ROW_FORMAT = FIXED set varchar to char)8バイトのngram_id 1バイトMyISAM内部削除フラグ

ngram_keyの2つのインデックスエントリ= 64バイト+ 8バイト= 72バイト

4,700万行X 073バイト/行= 3431百万バイト= 3.1954 GB
4,700万行X 072バイト/行= 3384百万バイト= 3.1515 GB
4,700万行X 145バイト= 6億8,151万バイト= 6.3469 GB

50億行X 073バイト/行= 365億バイト= 339.9327 GB
50億行X 072バイト/行= 360億バイト= 335.2761 GB
50億行X 1行あたり145バイト= 725億バイト= 675.2088 GB


2つのすばらしい答えをありがとう。テーブルにデータを入力するためにこのブラックホール+トリガーメソッドを使用する理由は何ですか?
Dolan Antenucci、2011年

ブラックホールは元のngramを受け入れます。トリガーは、ngramをauto_increment値から分割するクリーンなINSERT IGNOREメカニズムを作成します。
RolandoMySQLDBA、2011年

3

これはかなりワイルドな提案です

すべてのngramを32文字のMD5キーに変換します

このテーブルには、任意のサイズ(255文字まで)、1グラム、2グラムなどのすべての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進数字)を返すという事実に由来します。最初の2バイトは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グラム、2グラム、3グラムを同じテーブルにロードしたが、どのMD5がどのngramに属しているかの手掛かりがないことに注意してください。したがって、すべてのngramをこの1つのテーブルに組み込むことができます。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グラム、2グラム、および3グラムを別々のリポジトリーに分離する場合は、別のテーブル、別のブラックホールテーブル、およびブラックホールテーブルに別のトリガーを作成して、他のテーブルに挿入します。

また、ngramが255よりも長い場合(7グラムまたは8グラムを実行している場合)、ngram_blackholeテーブルのNGRAM列のVARCHARサイズを増やしてください。

試してみる !!!

更新

質問では、4700万行がmysqlにロードされたと述べられています。私が提案するテーブルレイアウトでは、次の点に注意してください。

ngram_nodeは1行あたり41バイトです:数値のNGRAM_KEYは32
(数字は各SMALLINTに2)
内部MyISAM DELETEDフラグは1

各主キーインデックスエントリは34バイト
32 NGRAM_KEY
2の場合はNGRAM_YEARの場合

4700万行X 1行あたり41バイト= 19億7,200万バイト、約1.79466 GB。
4700万行Xインデックスエントリあたり34バイト= 15億9800万バイト、約1.48825 GB。
MyISAMテーブルの消費量は、合計で約3.28291 GBになるはずです。

また、50億行をロードすることについても質問されました。

50億行X 1行あたり41バイト= 2050億バイト、約190.9211 GB。
50億行Xインデックスエントリあたり34バイト= 1700億バイト、約158.3248 GB。
MyISAMテーブルの消費量は、合計で約349.2459 GBになるはずです。

MyISAMテーブルで使用されるスペースの増加率は、一定サイズの主キーのために線形であることに注意してください。これに基づいて、ディスク容量の計画を行うことができます。


1
私は自分の答えについて考えましたが、使用するディスク容量を減らすために、別の提案も考えています。月曜日にこれを取り上げます!!! 良い週末を。
RolandoMySQLDBA 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.