¿Cómo almacenar mejor los ngrams de Google en una base de datos?

9

Descargué google onegrams hace unos días y ya es una gran cantidad de datos. Inserté el primero de 10 paquetes en mysql y ahora tengo una base de datos de 47 millones de registros.

Me pregunto cómo se deberían almacenar mejor los ngrams de Google en una base de datos. Quiero decir, si no estás usando onegrams, pero p. Ej. Twograms o threegrams, la cantidad será mucho mayor. ¿Puedo almacenar 500 millones de registros en una base de datos y trabajar con ellos o debería dividirlos en diferentes tablas?

¿Después de cuántos registros se debe dividir y cómo se debe dividir mejor (considerando que twograms tiene 100 archivos y, por lo tanto, probablemente alrededor de 5 mil millones de registros)? ¿Se recomienda usar el particionamiento horizontal MySQL o más bien construir el particionamiento propio (por ejemplo, a través del primer carácter de la palabra => twograms_a)?

RolandoMySQLDBA
fuente

Respuestas:

4

Hubo tantos cambios que tendría que hacer a mi primera respuesta. ¡Comienzo esta!

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;

Tablas mucho más pequeñas para información del año pero claves mucho más grandes para preservar el ngram original. También aumenté la cantidad de datos de prueba. Puede cortar y pegar esto directamente en MySQL.

CONSIDERACIÓN

Simplemente elimine ROW_FORMAT y se vuelve dinámico y comprima las tablas ngram_key mucho más pequeñas.


DiskSpace Metrics

nrgram_rec tiene 17 bytes por fila
8 bytes para ngram_id (valor máximo sin signo 18446744073709551615 [2 ^ 64 - 1])
8 bytes para 4 letras pequeñas (2 bytes cada una)
Indicador de eliminación interna MyISAM de 1 byte

Entrada de índice para ngram_rec = 10 bytes (8 (ngram_id) + 2 (año))

47 millones de filas X 17 bytes por fila = 0799 millones de bytes = 761.98577 MB
47 millones de filas X 12 bytes por fila = 0564 millones de bytes = 537.85231 MB
47 millones de filas X 29 bytes por fila = 1363 millones de bytes = 1.269393 GB

5 mil millones de filas X 17 bytes por fila = 085 mil millones de bytes = 079.1624 GB
5 mil millones de filas X 12 bytes por fila = 060 mil millones de bytes = 055.8793 GB
5 mil millones de filas X 29 bytes por fila = 145 mil millones de bytes = 135.0417 GB


ngram_key tiene 73 bytes 64 bytes para ngram (ROW_FORMAT = FIXED set varchar to char) 8 bytes para ngram_id 1 byte Indicador interno de eliminación de MyISAM

2 entradas de índice para ngram_key = 64 bytes + 8 bytes = 72 bytes

47 millones de filas X 073 bytes por fila = 3431 millones de bytes = 3.1954 GB
47 millones de filas X 072 bytes por fila = 3384 millones de bytes = 3.1515 GB
47 millones de filas X 145 bytes por fila = 6815 millones de bytes = 6.3469 GB

5 mil millones de filas X 073 bytes por fila = 365 mil millones de bytes = 339.9327 GB
5 mil millones de filas X 072 bytes por fila = 360 mil millones de bytes = 335.2761 GB
5 mil millones de filas X 145 bytes por fila = 725 mil millones de bytes = 675.2088 GB

RolandoMySQLDBA
fuente
Gracias por las dos excelentes respuestas. Tengo curiosidad, ¿cuál es la razón para usar este método blackhole + trigger para llenar la tabla?
Dolan Antenucci
El agujero negro acepta el ngram original. El disparador crea un mecanismo limpio INSERT IGNORE para dividir ngram del valor de auto_increment.
RolandoMySQLDBA
3

Aquí hay una sugerencia bastante salvaje

Convierta todos los ngrams a claves MD5 de 32 caracteres

Esta tabla contendrá todos los ngrams de cualquier tamaño (hasta 255 caracteres), 1 gramo, 2 gramos, 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 razón por la que elegí 256 particiones se debe al hecho de que la función MD5 devuelve 16 caracteres distintos (todos los dígitos hexadecimales). Los primeros dos bytes son 16 X 16, 256.

Aquí estaba el resultado en MySQL 5.5.11 en mi escritorio de 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>

Tenga en cuenta que cargué 1 gramos, 2 gramos y 3 gramos en la misma tabla, pero no tiene idea de qué MD5 pertenece a qué ngram. Por lo tanto, todos los ngrams pueden adaptarse a esta tabla. Solo recuerde insertar en la tabla ngram_blackhole y el resto ya está hecho.

Debe consultar la tabla ngram_node utilizando el MD5 () del ngram sin importar qué 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 desea separar 1-gramos, 2-gramos y 3-gramos en repositorios separados, simplemente cree otra tabla, otra tabla de agujeros negros y otro disparador en la tabla de agujeros negros para insertar en la otra tabla.

Además, si sus ngrams son más largos que 255 (si está haciendo 7 gramos u 8 gramos) simplemente aumente el tamaño VARCHAR de la columna NGRAM en la tabla ngram_blackhole.

Darle una oportunidad !!!

ACTUALIZAR

En la pregunta, se afirmó que 47 millones de filas se cargaron en mysql. Para mi diseño de tabla sugerido, tenga en cuenta lo siguiente:

ngram_node es 41 bytes por fila: 32 para NGRAM_KEY
8 para los números (2 para cada SMALLINT)
1 para el distintivo interno MyISAM DELETED

Cada entrada de índice de clave principal sería de 34 bytes
32 para NGRAM_KEY
2 para NGRAM_YEAR

47 millones de filas X 41 bytes por fila = 1.927 mil millones de bytes, aproximadamente 1.79466 GB.
47 millones de filas X 34 bytes por entrada de índice = 1.598 mil millones de bytes, aproximadamente 1.48825 GB.
El consumo de la tabla MyISAM debe ser de un total combinado de 3.28291 GB.

La pregunta también mencionó cargar 5 mil millones de filas.

5 mil millones de filas X 41 bytes por fila = 205 mil millones de bytes, aproximadamente 190.9211 GB.
5 mil millones de filas X 34 bytes por entrada de índice = 170 mil millones de bytes, aproximadamente 158.3248 GB.
El consumo de la tabla MyISAM debería ser de un total combinado de 349.2459 GB.

Tenga en cuenta que la tasa de crecimiento del espacio utilizado en la tabla MyISAM es lineal debido a la clave primaria de tamaño constante. Ahora puede planificar el espacio en disco basándose en esto.

RolandoMySQLDBA
fuente
1
Pensé en mi respuesta y tengo en mente otra sugerencia para que se use menos espacio en disco. ¡Me ocuparé de esto el lunes! Ten un buen fin de semana.
RolandoMySQLDBA