Come archiviare al meglio gli ngram di Google in un database?


9

Ho scaricato google onegrams alcuni giorni fa ed è già un'enorme quantità di dati. Ho inserito il primo di 10 pacchetti in mysql e ora ho un database di 47 milioni di record.

Mi chiedo come si dovrebbe archiviare meglio gli ngram di Google in un database. Voglio dire, se non stai usando gli onegrammi, ma ad esempio twogrammi o threegrammi l'importo sarà molto più grande. Posso archiviare 500 milioni di record in un database e lavorare con esso o devo dividerlo in tabelle diverse?

Dopo quanti record si dovrebbe dividerlo e come si dovrebbe dividerlo meglio (considerando che i twogrammi hanno 100 file e quindi probabilmente circa 5 miliardi di record)? Si consiglia di utilizzare il partizionamento orizzontale MySQL o piuttosto costruire il proprio partizionamento (ad esempio tramite il primo carattere di word => twograms_a).

Risposte:


4

Ci sono stati così tanti cambiamenti che avrei dovuto apportare alla mia prima risposta, ho iniziato questo !!!

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;

Tabelle molto più piccole per informazioni sull'anno ma chiavi molto più grandi per preservare il ngram originale. Ho anche aumentato la quantità di dati di test. Puoi tagliarlo e incollarlo direttamente in MySQL.

AVVERTIMENTO

Basta rimuovere ROW_FORMAT e diventa dymanic e comprimere le tabelle ngram_key molto più piccole.


Metriche di DiskSpace

nrgram_rec ha 17 byte per riga
8 byte per ngram_id (valore massimo senza segno 18446744073709551615 [2 ^ 64 - 1])
8 byte per 4 caratteri piccoli (2 byte ciascuno)
1 byte Flag di eliminazione interno MyISAM

Voce indice per ngram_rec = 10 byte (8 (ngram_id) + 2 (yr))

47 milioni di righe X 17 byte per riga = 0799 milioni di byte = 761.98577 MB
47 milioni di righe X 12 byte per riga = 0564 milioni di byte = 537.85231 MB
47 milioni di righe X 29 byte per riga = 1363 milioni di byte = 1.269393 GB

5 miliardi di righe X 17 byte per riga = 085 miliardi di byte = 079.1624 GB
5 miliardi di righe X 12 byte per riga = 060 miliardi di byte = 055.8793 GB
5 miliardi di righe X 29 byte per riga = 145 miliardi di byte = 135.0417 GB


ngram_key ha 73 byte 64 byte per ngram (ROW_FORMAT = FIXED imposta varchar su char) 8 byte per ngram_id 1 byte Flag di eliminazione interno MyISAM

2 voci indice per ngram_key = 64 byte + 8 byte = 72 byte

47 milioni di righe X 073 byte per riga = 3431 milioni di byte = 3.1954 GB
47 milioni di righe X 072 byte per riga = 3384 milioni di byte = 3.1515 GB
47 milioni di righe X 145 byte per riga = 6815 milioni di byte = 6.3469 GB

5 miliardi di righe X 073 byte per riga = 365 miliardi di byte = 339.9327 GB
5 miliardi di righe X 072 byte per riga = 360 miliardi di byte = 335.2761 GB
5 miliardi di righe X 145 byte per riga = 725 miliardi di byte = 675.2088 GB


Grazie per le due grandi risposte. Sono curioso, qual è la ragione per usare questo metodo blackhole + trigger per popolare il tavolo?
Dolan Antenucci,

Il buco nero accetta il ngram originale. Il trigger crea un meccanismo INSERT IGNORE pulito per dividere ngram dal valore auto_increment.
RolandoMySQLDBA

3

Ecco un suggerimento abbastanza selvaggio

Converti tutti gli ngram in tasti MD5 a 32 caratteri

Questa tabella conterrà tutti i ngram di qualsiasi dimensione (fino a 255 caratteri), 1 grammo, 2 grammi, ecc.

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;

Il motivo per cui ho scelto 256 partizioni deriva dal fatto che la funzione MD5 restituisce 16 caratteri distinti (tutte cifre esadecimali). I primi due byte sono 16 X 16, 256.

Ecco il risultato in MySQL 5.5.11 sul mio desktop di Windows 7

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>

Si noti che ho caricato 1 grammi, 2 grammi e 3 grammi nella stessa tabella ma non si ha idea di quale MD5 appartiene a quale ngram. Pertanto, tutti gli ngram possono essere modificati in questa tabella. Ricorda solo di inserire nella tabella ngram_blackhole e il resto è fatto per te.

È necessario eseguire una query sulla tabella ngram_node utilizzando MD5 () di ngram, indipendentemente da quale ngram.

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)

Se si desidera separare 1 grammi, 2 grammi e 3 grammi in repository separati, è sufficiente creare un altro tavolo, un altro tavolo blackhole e un altro trigger sul tavolo blackhole per inserirlo nell'altro tavolo.

Inoltre, se i tuoi ngram sono più lunghi di 255 (se stai facendo 7 grammi o 8 grammi), aumenta la dimensione VARCHAR della colonna NGRAM nella tabella ngram_blackhole.

Provaci !!!

AGGIORNARE

Nella domanda, è stato affermato che 47 milioni di righe sono state caricate in mysql. Per il layout di tabella suggerito, tenere presente quanto segue:

ngram_node è 41 byte per riga: 32 per NGRAM_KEY
8 per i numeri (2 per ogni SMALLINT)
1 per il flag MyISAM DELETED interno

Ogni voce dell'indice della chiave primaria sarebbe 34 byte
32 per NGRAM_KEY
2 per NGRAM_YEAR

47 milioni di righe X 41 byte per riga = 1,927 miliardi di byte, circa 1,779466 GB.
47 milioni di righe X 34 byte per voce di indice = 1.598 miliardi di byte, circa 1.48825 GB.
Il consumo della tabella MyISAM dovrebbe essere di circa un totale combinato di 3,28291 GB.

La domanda menzionava anche il caricamento di 5 miliardi di righe.

5 miliardi di righe X 41 byte per riga = 205 miliardi di byte, circa 190.9211 GB.
5 miliardi di righe X 34 byte per voce di indice = 170 miliardi di byte, circa 158.3248 GB.
Il consumo della tabella MyISAM dovrebbe essere di circa un totale combinato di 349.2459 GB.

Si noti che il tasso di crescita dello spazio utilizzato nella tabella MyISAM è lineare a causa della chiave primaria di dimensioni costanti. Ora puoi fare un po 'di pianificazione per lo spazio su disco in base a questo.


1
Ho pensato alla mia risposta e ho in mente un altro suggerimento in modo che venga utilizzato meno spazio su disco. Lo affronterò lunedì !!! Passa un bel weekend.
RolandoMySQLDBA
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.