Bagaimana cara terbaik menyimpan Google ngrams dalam basis data?

9

Saya mengunduh google onegrams beberapa hari yang lalu dan itu sudah sejumlah besar data. Saya memasukkan 10 paket pertama ke dalam mysql dan sekarang saya memiliki 47 juta database rekaman.

Saya bertanya-tanya bagaimana seseorang harus menyimpan Google ngrams dalam database terbaik. Maksud saya, jika Anda tidak menggunakan onegrams, tetapi mis. Twograms atau threegrams jumlahnya akan jauh lebih besar. Bisakah saya menyimpan 500 juta catatan dalam satu basis data dan bekerja dengannya atau haruskah saya membaginya ke tabel yang berbeda?

Setelah berapa banyak rekaman yang harus dipecah, dan bagaimana seharusnya memecahnya dengan baik (mengingat bahwa twograms memiliki 100 file dan karenanya mungkin sekitar 5 miliar catatan)? Apakah disarankan untuk menggunakan partisi horizontal MySQL atau lebih tepatnya membangun partisi sendiri (mis. Melalui karakter pertama dari kata => twograms_a).

RolandoMySQLDBA
sumber

Jawaban:

4

Ada begitu banyak perubahan yang harus saya buat pada jawaban pertama saya Saya mulai yang ini !!!

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;

Tabel yang jauh lebih kecil untuk info tahun tetapi kunci yang lebih besar untuk mempertahankan ngram asli. Saya juga meningkatkan jumlah data uji. Anda dapat memotong dan menempelkan ini langsung ke MySQL.

CAVEAT

Cukup hapus ROW_FORMAT dan itu menjadi dymanic dan kompres tabel ngram_key jauh lebih kecil.


Metrik DiskSpace

nrgram_rec memiliki 17 byte per baris
8 byte untuk ngram_id (nilai maksimum unsigned 18446744073709551615 [2 ^ 64 - 1])
8 byte untuk 4 smallints (masing-masing 2 byte)
1 byte MyISAM flag internal delete flag

Entri Indeks untuk ngram_rec = 10 byte (8 (ngram_id) + 2 (tahun))

47 juta baris X 17 byte per baris = 0799 juta byte = 761.98577 MB
47 juta baris X 12 byte per baris = 0564 juta byte = 537.85231 MB
47 juta baris X 29 byte per baris = 1363 juta byte = 1.269393 GB

5 miliar baris X 17 byte per baris = 085 miliar byte = 079,1624 GB
5 miliar baris X 12 byte per baris = 060 miliar byte = 055.8793 GB
5 miliar baris X 29 byte per baris = 145 miliar byte = 135,04 GB GB


ngram_key memiliki 73 byte 64 byte untuk ngram (ROW_FORMAT = TETAP mengatur varchar ke char) 8 byte untuk ngram_id 1 byte MyISAM flag hapus internal

2 Entri Indeks untuk ngram_key = 64 byte + 8 byte = 72 byte

47 juta baris X 073 byte per baris = 3431 juta byte = 3,1954 GB
47 juta baris X 072 byte per baris = 3384 juta byte = 3,1515 GB
47 juta baris X 145 byte per baris = 6815 juta byte = 6,3469 GB

5 miliar baris X 073 byte per baris = 365 miliar byte = 339.9327 GB
5 miliar baris X 072 byte per baris = 360 miliar byte = 335,2761 GB
5 miliar baris X 145 byte per baris = 725 miliar byte = 675,2088 GB

RolandoMySQLDBA
sumber
Terima kasih atas dua jawaban yang bagus. Saya ingin tahu, apa alasan menggunakan metode blackhole + trigger ini untuk mengisi tabel?
Dolan Antenucci
Blackhole menerima nign orignal. Pemicu menciptakan mekanisme INSERT IGNORE yang bersih untuk memisahkan ngram dari nilai auto_increment.
RolandoMySQLDBA
3

Ini saran yang sangat liar

Ubah semua ngram menjadi kunci MD5 32-karakter

Tabel ini akan menampung semua ngram dengan ukuran apa pun (hingga 255 karakter), 1 gram, 2 gram, dll.

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;

Alasan saya memilih 256 partisi berasal dari fakta bahwa fungsi MD5 mengembalikan 16 karakter yang berbeda (semua digit heksadesimal). Dua byte pertama adalah 16 X 16, 256.

Inilah hasilnya di MySQL 5.5.11 di Desktop Windows 7 saya

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>

Harap perhatikan bahwa saya memuat 1-gram, 2-gram, dan 3-gram ke dalam tabel yang sama tetapi Anda tidak memiliki petunjuk MD5 mana yang menjadi milik ngram. Dengan demikian, semua ngram dapat dipasang di tabel yang satu ini. Hanya ingat untuk memasukkan ke tabel ngram_blackhole dan sisanya dilakukan untuk Anda.

Anda harus menanyakan tabel ngram_node menggunakan MD5 () dari ngram, apa pun ngramnya.

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)

Jika Anda ingin memisahkan 1-gram, 2-gram, dan 3-gram ke dalam repositori yang terpisah, buat saja tabel lain, tabel lubang hitam lain, dan pemicu lain pada tabel lubang hitam untuk dimasukkan ke meja lainnya.

Juga, jika ngram Anda lebih panjang dari 255 (jika Anda melakukan 7 gram atau 8 gram) cukup tambahkan ukuran VARCHAR dari kolom NGRAM di tabel ngram_blackhole.

Cobalah !!!

MEMPERBARUI

Dalam pertanyaan tersebut, dinyatakan bahwa 47 juta baris dimuat ke mysql. Untuk tata letak tabel yang disarankan, harap perhatikan yang berikut:

ngram_node adalah 41 byte per baris: 32 untuk NGRAM_KEY
8 untuk angka (2 untuk setiap SMALLINT)
1 untuk bendera internal MyISAM DELETED

Setiap entri indeks kunci utama akan menjadi 34 byte
32 untuk NGRAM_KEY
2 untuk NGRAM_YEAR

47 juta baris X 41 byte per baris = 1,927 miliar byte, sekitar 1,79466 GB.
47 juta baris X 34 byte per entri indeks = 1,598 miliar byte, sekitar 1,48825 GB.
Konsumsi tabel MyISAM harus sekitar gabungan total 3,28291 GB.

Pertanyaan itu juga menyebutkan memuat 5 miliar baris.

5 miliar baris X 41 byte per baris = 205 miliar byte, sekitar 190,9211 GB.
5 miliar baris X 34 byte per entri indeks = 170 miliar byte, sekitar 158,3248 GB.
Konsumsi tabel MyISAM harus sekitar gabungan total 349.2459 GB.

Harap perhatikan bahwa laju pertumbuhan ruang yang digunakan dalam tabel MyISAM adalah linier karena kunci primer berukuran konstan. Anda sekarang dapat melakukan perencanaan untuk ruang disk berdasarkan ini.

RolandoMySQLDBA
sumber
1
Saya memikirkan jawaban saya dan memikirkan saran lain sehingga lebih sedikit ruang disk yang digunakan. Saya akan membahas ini pada hari Senin !!! Selamat berakhir pekan.
RolandoMySQLDBA