Как лучше хранить Google Ngrams в базе данных?

9

Несколько дней назад я скачал гуглограммы Google, и это уже огромный объем данных. Я вставил первый из 10 пакетов в MySQL, и теперь у меня 47 миллионов записей.

Мне интересно, как лучше хранить Google Ngrams в базе данных. Я имею в виду, если вы не используете онграммы, а, например, две или триграммы, сумма будет намного больше. Могу ли я хранить 500 миллионов записей в одной базе данных и работать с ней или я должен разделить ее на разные таблицы?

После того, сколько записей нужно разделить, и как лучше разделить (учитывая, что в двух граммах есть 100 файлов и, следовательно, около 5 миллиардов записей)? Рекомендуется использовать горизонтальное разбиение MySQL или построить собственное разбиение (например, через первый символ слова => twograms_a).

RolandoMySQLDBA
источник

Ответы:

4

Было так много изменений, которые я должен был внести в свой первый ответ, я начинаю этот !!!

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;

Гораздо меньшие таблицы для годовой информации, но гораздо большие ключи, чтобы сохранить исходный ngram. Я также увеличил количество тестовых данных. Вы можете вырезать и вставить это прямо в MySQL.

ПРЕДОСТЕРЕЖЕНИЕ

Просто удалите ROW_FORMAT, и он станет dymanic и сожмите таблицы ngram_key намного меньше.


Метрики DiskSpace

nrgram_rec имеет 17 байтов в строке
8 байтов для ngram_id (максимальное значение без знака 18446744073709551615 [2 ^ 64 - 1])
8 байтов для 4 строчных символов (по 2 байта в каждом)
1-байтовый внутренний флаг удаления MyISAM

Индексная запись для ngram_rec = 10 байт (8 (ngram_id) + 2 (год))

47 миллионов строк X 17 байтов в строке = 0799 миллионов байтов = 761,98577 МБ
47 миллионов строк X 12 байтов в строке = 0564 миллионов байтов = 537,85231 МБ
47 миллионов строк X 29 байтов в строке = 1363 миллиона байтов = 1,269393 ГБ

5 миллиардов строк X 17 байт на строку = 085 миллиардов байт = 079,1624 ГБ
5 миллиардов строк X 12 байт на строку = 060 миллиардов байт = 055,8793 ГБ
5 миллиардов строк X 29 байт на строку = 145 миллиардов байт = 135,0417 ГБ


ngram_key имеет 73 байта 64 байта для ngram (ROW_FORMAT = ИСПРАВЛЕНО установить varchar в char) 8 байтов для ngram_id 1 байт внутренний флаг удаления MyISAM

2 записи индекса для ngram_key = 64 байта + 8 байтов = 72 байта

47 миллионов строк X 073 байта на строку = 3431 миллион байтов = 3,1954 ГБ
47 миллионов строк X 072 байта на строку = 3384 миллиона байтов = 3,1515 ГБ
47 миллионов строк X 145 байтов на строку = 6815 миллионов байтов = 6,3469 ГБ

5 миллиардов строк X 073 байта на строку = 365 миллиардов байтов = 339,9327 ГБ
5 миллиардов строк X 072 байта на строку = 360 миллиардов байтов = 335,2761 ГБ
5 миллиардов строк X 145 байтов на строку = 725 миллиардов байтов = 675,2088 ГБ

RolandoMySQLDBA
источник
Спасибо за два отличных ответа. Мне любопытно, в чем причина использования этого метода чёрной дыры + триггера для заполнения таблицы?
Долан Антенуччи
Черная дыра принимает оригнал нграм. Триггер создает чистый механизм INSERT IGNORE для отделения ngram от значения auto_increment.
RolandoMySQLDBA
3

Вот довольно дикое предложение

Конвертировать все нграммы в 32-символьные ключи MD5

Эта таблица будет содержать все нграммы любого размера (до 255 символов), 1 грамма, 2 грамма и т. Д.

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;

Причина, по которой я выбрал 256 разделов, связана с тем, что функция MD5 возвращает 16 различных символов (все шестнадцатеричные цифры). Первые два байта - это 16 X 16, 256.

Вот результат в MySQL 5.5.11 на моем рабочем столе 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>

Пожалуйста, обратите внимание, что я загрузил 1-грамм, 2-грамм и 3-грамм в одну и ту же таблицу, но вы не знаете, какой MD5 принадлежит какому ngram. Таким образом, все ngram могут быть преобразованы в эту таблицу. Просто не забудьте вставить в таблицу ngram_blackhole, а все остальное за вас.

Вы должны запросить таблицу ngram_node, используя MD5 () ngram, независимо от того, какой 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)

Если вы хотите разделить 1-грамм, 2-грамм и 3-грамм на отдельные репозитории, просто создайте другую таблицу, другую таблицу с черными дырами и еще один триггер на столе с черной дырой, чтобы вставить его в другую таблицу.

Кроме того, если ваши ngram длиннее 255 (если вы делаете 7 грамм или 8 грамм), просто увеличьте размер VARCHAR столбца NGRAM в таблице ngram_blackhole.

Попробуйте!

ОБНОВИТЬ

В вопросе было указано, что в MySQL было загружено 47 миллионов строк. Для моего предложенного расположения таблицы, пожалуйста, обратите внимание на следующее:

ngram_node - 41 байт на строку: 32 для NGRAM_KEY
8 для чисел (2 для каждого SMALLINT)
1 для внутреннего флага MyISAM DELETED

Каждая запись индекса первичного ключа будет 34 байта
32 для NGRAM_KEY
2 для NGRAM_YEAR

47 миллионов строк X 41 байт на строку = 1,927 миллиарда байтов, около 1,77966 ГБ.
47 миллионов строк X 34 байта на каждый элемент индекса = 1,598 миллиарда байтов, около 1,48825 ГБ.
Потребление таблицы MyISAM должно составлять примерно 3,28291 ГБ.

В вопросе также упоминается загрузка 5 миллиардов строк.

5 миллиардов строк X 41 байт в строке = 205 миллиардов байтов, что составляет около 190,9211 ГБ.
5 миллиардов строк X 34 байта на каждый элемент индекса = 170 миллиардов байтов, что составляет около 158,3248 ГБ.
Потребление таблицы MyISAM должно составлять примерно 349,2459 ГБ.

Обратите внимание, что скорость роста пространства, используемого в таблице MyISAM, линейна из-за первичного ключа постоянного размера. Теперь вы можете планировать дисковое пространство на основе этого.

RolandoMySQLDBA
источник
1
Я подумал о своем ответе и имею в виду другое предложение, чтобы использовать меньше дискового пространства. Я займусь этим в понедельник !!! Хороших выходных.
RolandoMySQLDBA