Як найкраще зберігати ngram Google в базі даних?


9

Я завантажив google onegram кілька днів тому, і це вже величезна кількість даних. Я вставив перший з 10 пакетів у mysql і тепер у мене є 47 мільйонів записів.

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

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

Відповіді:


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, і він стане диманічним і стиснути таблиці 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 (yr))

47 мільйонів рядків X 17 байт у рядку = 0799 мільйонів байт = 761,98577 МБ
47 мільйонів рядків X 12 байт у рядку = 0564 мільйонів байтів = 537,85231 МБ
47 мільйонів рядків X 29 байт за рядок = 1363 мільйонів байт = 1,2269393 ГБ

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 = ФІКСОВАНО встановити варшар у 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 ГБ


Дякую за дві чудові відповіді. Мені цікаво, в чому причина використання цього методу blackhole + тригера для заповнення таблиці?
Долан Антенуччі

Чорний отвір приймає початковий ngram. Тригер створює чистий механізм INSERT IGNORE для розділення ngram від значення auto_increment.
RolandoMySQLDBA

3

Ось досить дика пропозиція

Перетворити всі nграми в 32-символьні клавіші MD5

Ця таблиця містить усі nграми будь-якого розміру (до 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. Таким чином, всі nграми можуть змінитись у цю одну таблицю. Просто не забудьте вставити в таблицю 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-грамовий групи в окремі сховища, просто створіть іншу таблицю, іншу таблицю чорного отвору та інший тригер на таблиці чорного отвору, щоб вставити в іншу таблицю.

Крім того, якщо ваші nграми перевищують 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,558 мільярда байт, приблизно 1,44825 ГБ.
Споживання таблиці MyISAM має становити приблизно 3,28291 ГБ.

Питання також згадувало завантаження 5 мільярдів рядків.

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

Зверніть увагу, що швидкість росту простору, використовуваного в таблиці MyISAM, лінійна через первинний ключ постійного розміру. Тепер ви можете зробити деяке планування дискового простору на основі цього.


1
Я подумав над своєю відповіддю і маю на увазі ще одну пропозицію, щоб використовувати менше місця на диску. Я з цим звернусь у понеділок !!! Приємних вихідних.
RolandoMySQLDBA
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.