Comment stocker au mieux les ngrams Google dans une base de données?

9

J'ai téléchargé google onegrams il y a quelques jours et c'est déjà une énorme quantité de données. J'ai inséré le premier des 10 paquets dans mysql et j'ai maintenant une base de données de 47 millions d'enregistrements.

Je me demande comment on devrait mieux stocker les ngrams de Google dans une base de données. Je veux dire, si vous n'utilisez pas un gramme, mais par exemple des twogrammes ou trois grammes, le montant sera beaucoup plus grand. Puis-je stocker 500 millions d'enregistrements dans une base de données et travailler avec elle ou dois-je le diviser en différentes tables?

Après combien d'enregistrements doit-on le diviser et comment le diviser au mieux (étant donné que les twogrammes ont 100 fichiers et donc probablement environ 5 milliards d'enregistrements)? Est-il recommandé d'utiliser le partitionnement horizontal MySQL ou plutôt de construire son propre partitionnement (par exemple via le premier caractère du mot => twograms_a).

RolandoMySQLDBA
la source

Réponses:

4

Il y avait tellement de changements que je devais apporter à ma première réponse, je commence celle-ci !!!

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;

Des tableaux beaucoup plus petits pour les informations sur l'année mais des touches beaucoup plus grandes pour conserver le schéma d'origine. J'ai également augmenté la quantité de données de test. Vous pouvez couper et coller cela directement dans MySQL.

CAVEAT

Supprimez simplement ROW_FORMAT et cela devient dynamique et compressez les tables ngram_key beaucoup plus petites.


Mesures DiskSpace

nrgram_rec a 17 octets par ligne
8 octets pour ngram_id (valeur maximale non signée 18446744073709551615 [2 ^ 64 - 1])
8 octets pour 4 petits caractères (2 octets chacun)
1 octet indicateur de suppression interne MyISAM

Entrée d'index pour ngram_rec = 10 octets (8 (ngram_id) + 2 (an))

47 millions de lignes X 17 octets par ligne = 0799 millions d'octets = 761,98577 Mo
47 millions de lignes X 12 octets par ligne = 0564 millions d'octets = 537,85231 Mo
47 millions de lignes X 29 octets par ligne = 1363 millions d'octets = 1,269393 Go

5 milliards de lignes X 17 octets par ligne = 085 milliards d'octets = 079,1624 Go
5 milliards de lignes X 12 octets par ligne = 060 milliard d'octets = 055,8793 Go
5 milliards de lignes X 29 octets par ligne = 145 milliards d'octets = 135,0417 Go


ngram_key a 73 octets 64 octets pour ngram (ROW_FORMAT = FIXED définit varchar sur char) 8 octets pour ngram_id 1 octet Drapeau de suppression interne MyISAM

2 entrées d'index pour ngram_key = 64 octets + 8 octets = 72 octets

47 millions de lignes X 073 octets par ligne = 3431 millions d'octets = 3,1954 Go
47 millions de lignes X 072 octets par ligne = 3384 millions d'octets = 3,1515 Go
47 millions de lignes X 145 octets par ligne = 6815 millions d'octets = 6,3469 Go

5 milliards de lignes X 073 octets par ligne = 365 milliards d'octets = 339,9327 Go
5 milliards de lignes X 072 octets par ligne = 360 milliards d'octets = 335,2761 Go
5 milliards de lignes X 145 octets par ligne = 725 milliards d'octets = 675,2088 Go

RolandoMySQLDBA
la source
Merci pour les deux bonnes réponses. Je suis curieux, quelle est la raison d'utiliser cette méthode blackhole + trigger pour remplir la table?
Dolan Antenucci
Le trou noir accepte le schéma d'origine. Le déclencheur crée un mécanisme INSERT IGNORE propre pour séparer ngram de la valeur auto_increment.
RolandoMySQLDBA
3

Voici une suggestion assez folle

Convertissez tous les ngrams en touches MD5 à 32 caractères

Ce tableau contiendra tous les ngrammes de n'importe quelle taille (jusqu'à 255 caractères), 1 gramme, 2 grammes, etc.

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;

La raison pour laquelle j'ai choisi 256 partitions provient du fait que la fonction MD5 renvoie 16 caractères distincts (tous des chiffres hexadécimaux). Les deux premiers octets sont 16 X 16, 256.

Voici le résultat dans MySQL 5.5.11 sur mon bureau 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>

Veuillez noter que j'ai chargé 1 gramme, 2 grammes et 3 grammes dans la même table mais vous n'avez aucune idée de quel MD5 appartient à quel ngram. Ainsi, tous les ngrammes peuvent être intégrés dans cette seule table. N'oubliez pas d'insérer dans la table ngram_blackhole et le reste est fait pour vous.

Vous devez interroger la table ngram_node à l'aide du MD5 () du ngram, quel que soit le 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)

Si vous souhaitez séparer 1 gramme, 2 grammes et 3 grammes dans des référentiels distincts, créez simplement une autre table, une autre table de trou noir et un autre déclencheur sur la table de trou noir pour l'insérer dans l'autre table.

De plus, si vos ngrams dépassent 255 (si vous faites 7 grammes ou 8 grammes), augmentez simplement la taille VARCHAR de la colonne NGRAM dans la table ngram_blackhole.

Essaie !!!

MISE À JOUR

Dans la question, il a été déclaré que 47 millions de lignes ont été chargées dans mysql. Pour ma disposition de table suggérée, veuillez noter ce qui suit:

ngram_node est de 41 octets par ligne: 32 pour NGRAM_KEY
8 pour les nombres (2 pour chaque SMALLINT)
1 pour l'indicateur MyISAM DELETED interne

Chaque entrée d'index de clé primaire serait de 34 octets
32 pour NGRAM_KEY
2 pour NGRAM_YEAR

47 millions de lignes X 41 octets par ligne = 1,927 milliard d'octets, soit environ 1,79466 Go.
47 millions de lignes X 34 octets par entrée d'index = 1,598 milliard d'octets, environ 1,48825 Go.
La consommation de la table MyISAM devrait être d'environ un total combiné de 3,28291 Go.

La question mentionnait également le chargement de 5 milliards de lignes.

5 milliards de lignes X 41 octets par ligne = 205 milliards d'octets, soit environ 190,9211 Go.
5 milliards de lignes X 34 octets par entrée d'index = 170 milliards d'octets, soit environ 158,3248 Go.
La consommation de la table MyISAM devrait être d'environ un total combiné de 349.2459 Go.

Veuillez noter que le taux de croissance de l'espace utilisé dans la table MyISAM est linéaire en raison de la clé primaire de taille constante. Vous pouvez désormais planifier l'espace disque en fonction de cela.

RolandoMySQLDBA
la source
1
J'ai réfléchi à ma réponse et j'ai à l'esprit une autre suggestion pour que moins d'espace disque soit utilisé. J'y répondrai lundi !!! Bon week-end.
RolandoMySQLDBA