Wie speichere ich Google ngrams am besten in einer Datenbank?


9

Ich habe vor einigen Tagen Google Onegrams heruntergeladen und es ist bereits eine riesige Datenmenge. Ich habe das erste von 10 Paketen in MySQL eingefügt und jetzt habe ich eine 47-Millionen-Datensatzdatenbank.

Ich frage mich, wie man Google ngrams am besten in einer Datenbank speichern soll. Ich meine, wenn Sie keine Onegramme verwenden, sondern z. B. Twogramme oder Dreigramme, ist der Betrag viel größer. Kann ich 500 Millionen Datensätze in einer Datenbank speichern und damit arbeiten oder sollte ich sie auf verschiedene Tabellen aufteilen?

Nach wie vielen Datensätzen sollte man es teilen und wie sollte man es am besten teilen (wenn man bedenkt, dass Twograms 100 Dateien und damit wahrscheinlich etwa 5 Milliarden Datensätze hat)? Wird empfohlen, die horizontale Partitionierung von MySQL zu verwenden oder eine eigene Partitionierung zu erstellen (z. B. über das erste Zeichen von word => twograms_a).

Antworten:


4

Es gab so viele Änderungen, die ich an meiner ersten Antwort vornehmen musste. Ich beginne diese !!!

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;

Viel kleinere Tabellen für Jahresinformationen, aber viel größere Schlüssel, um das ursprüngliche Ngramm zu erhalten. Ich habe auch die Menge der Testdaten erhöht. Sie können dies ausschneiden und direkt in MySQL einfügen.

VORBEHALT

Entfernen Sie einfach ROW_FORMAT und es wird dymanisch und komprimieren Sie die ngram_key-Tabellen viel kleiner.


DiskSpace-Metriken

nrgram_rec hat 17 Bytes pro Zeile
8 Bytes für ngram_id (maximaler Wert ohne Vorzeichen 18446744073709551615 [2 ^ 64 - 1])
8 Bytes für 4 Smallints (jeweils 2 Bytes)
1 Byte internes MyISAM- Löschflag

Indexeintrag für ngram_rec = 10 Bytes (8 (ngram_id) + 2 (Jahr))

47 Millionen Zeilen x 17 Bytes pro Zeile = 0799 Millionen Bytes = 761,98577 MB
47 Millionen Zeilen x 12 Bytes pro Zeile = 0564 Millionen Bytes = 537,85231 MB
47 Millionen Zeilen x 29 Bytes pro Zeile = 1363 Millionen Bytes = 1,269393 GB

5 Milliarden Zeilen x 17 Bytes pro Zeile = 085 Milliarden Bytes = 079,1624 GB
5 Milliarden Zeilen x 12 Bytes pro Zeile = 060 Milliarden Bytes = 055,8793 GB
5 Milliarden Zeilen x 29 Bytes pro Zeile = 145 Milliarden Bytes = 135,0417 GB


ngram_key hat 73 Bytes 64 Bytes für ngram (ROW_FORMAT = FIXED setze varchar auf char) 8 Bytes für ngram_id 1 Byte internes MyISAM-Löschflag

2 Indexeinträge für ngram_key = 64 Bytes + 8 Bytes = 72 Bytes

47 Millionen Zeilen x 073 Bytes pro Zeile = 3431 Millionen Bytes = 3,1954 GB
47 Millionen Zeilen x 072 Bytes pro Zeile = 3384 Millionen Bytes = 3,1515 GB
47 Millionen Zeilen x 145 Bytes pro Zeile = 6815 Millionen Bytes = 6,3469 GB

5 Milliarden Zeilen x 073 Bytes pro Zeile = 365 Milliarden Bytes = 339,9327 GB
5 Milliarden Zeilen x 072 Bytes pro Zeile = 360 Milliarden Bytes = 335,2761 GB
5 Milliarden Zeilen x 145 Bytes pro Zeile = 725 Milliarden Bytes = 675,2088 GB


Danke für die zwei tollen Antworten. Ich bin gespannt, warum diese Blackhole + Trigger-Methode zum Auffüllen der Tabelle verwendet wird.
Dolan Antenucci

Das Schwarze Loch akzeptiert das ursprüngliche Ngramm. Der Trigger erstellt einen sauberen INSERT IGNORE-Mechanismus, um ngram vom Wert auto_increment zu trennen.
RolandoMySQLDBA

3

Hier ist ein ziemlich wilder Vorschlag

Konvertieren Sie alle Ngramme in MD5-Schlüssel mit 32 Zeichen

Diese Tabelle enthält alle Gramm jeder Größe (bis zu 255 Zeichen), 1 Gramm, 2 Gramm usw.

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;

Der Grund, warum ich 256 Partitionen ausgewählt habe, liegt in der Tatsache begründet, dass die MD5-Funktion 16 verschiedene Zeichen (alle hexadezimalen Ziffern) zurückgibt. Die ersten beiden Bytes sind 16 x 16, 256.

Hier war das Ergebnis in MySQL 5.5.11 auf meinem Windows 7 Desktop

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>

Bitte beachten Sie, dass ich 1 Gramm, 2 Gramm und 3 Gramm in dieselbe Tabelle geladen habe, aber Sie haben keine Ahnung, welches MD5 zu welchem ​​Gramm gehört. Somit können alle ngramme in diese eine Tabelle nachgerüstet werden. Denken Sie daran, in die Tabelle ngram_blackhole einzufügen, und der Rest ist für Sie erledigt.

Sie müssen die Tabelle ngram_node mit MD5 () des ngram abfragen, unabhängig davon, welches ngram verwendet wird.

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)

Wenn Sie 1 Gramm, 2 Gramm und 3 Gramm in separate Repositorys trennen möchten, erstellen Sie einfach eine weitere Tabelle, eine weitere Blackhole-Tabelle und einen weiteren Trigger auf der Blackhole-Tabelle, um sie in die andere Tabelle einzufügen.

Wenn Ihre Ngramme länger als 255 sind (wenn Sie 7 Gramm oder 8 Gramm machen), erhöhen Sie einfach die VARCHAR-Größe der NGRAM-Spalte in der Tabelle ngram_blackhole.

Versuche es !!!

AKTUALISIEREN

In der Frage wurde angegeben, dass 47 Millionen Zeilen in MySQL geladen wurden. Beachten Sie für mein vorgeschlagenes Tabellenlayout Folgendes:

ngram_node ist 41 Bytes pro Zeile: 32 für NGRAM_KEY
8 für die Zahlen (2 für jede SMALLINT)
1 für das interne MyISAM DELETED-Flag

Jeder Primärschlüsselindexeintrag wäre 34 Bytes
32 für NGRAM_KEY
2 für NGRAM_YEAR

47 Millionen Zeilen x 41 Bytes pro Zeile = 1,927 Milliarden Bytes, ungefähr 1,79466 GB.
47 Millionen Zeilen x 34 Bytes pro Indexeintrag = 1,598 Milliarden Bytes, ungefähr 1,48825 GB.
Der MyISAM-Tabellenverbrauch sollte zusammen etwa 3,28291 GB betragen.

In der Frage wurde auch das Laden von 5 Milliarden Zeilen erwähnt.

5 Milliarden Zeilen x 41 Bytes pro Zeile = 205 Milliarden Bytes, ungefähr 190,9211 GB.
5 Milliarden Zeilen x 34 Bytes pro Indexeintrag = 170 Milliarden Bytes, ungefähr 158,3248 GB.
Der MyISAM-Tabellenverbrauch sollte insgesamt etwa 349,2459 GB betragen.

Bitte beachten Sie, dass die Wachstumsrate des in der MyISAM-Tabelle verwendeten Speicherplatzes aufgrund des Primärschlüssels mit konstanter Größe linear ist. Auf dieser Grundlage können Sie jetzt den Speicherplatz planen.


1
Ich habe über meine Antwort nachgedacht und mir einen anderen Vorschlag überlegt, damit weniger Speicherplatz verwendet wird. Ich werde das am Montag ansprechen !!! Ein schönes Wochenende wünsche ich ihnen.
RolandoMySQLDBA
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.