Google ngramlarını bir veritabanında en iyi şekilde nasıl saklayabilirim?


9

Birkaç gün önce google onegrams'ı indirdim ve zaten çok büyük miktarda veri var. 10 paketin ilkini mysql'e ekledim ve şimdi 47 milyon kayıt veritabanım var.

Bir kişinin Google ngramlarını en iyi bir veritabanında nasıl saklaması gerektiğini merak ediyorum. Yani, bir gram kullanmıyorsanız, ancak iki gram veya üç gram kullanıyorsanız, miktar çok daha büyük olacaktır. Bir veritabanında 500 milyon kayıt saklayabilir ve onunla çalışabilir miyim yoksa farklı tablolara mı bölmeliyim?

Biri kaç kayıttan sonra bölebilir ve en iyi nasıl bölebilir (iki gramın 100 dosyası ve dolayısıyla yaklaşık 5 milyar kayıt olduğunu düşünerek)? MySQL yatay bölümlemenin kullanılması veya kendi bölümlemesinin oluşturulması önerilir (örneğin, word => twograms_a kelimesinin ilk karakteri aracılığıyla).

Yanıtlar:


4

İlk cevabımda yapmak zorunda olduğum çok fazla değişiklik vardı, buna başladım !!!

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;

Yıl bilgisi için çok daha küçük tablolar, ancak orijinal ngramı korumak için çok daha büyük tuşlar. Ayrıca test verisi miktarını arttırdım. Bunu doğrudan MySQL'e kesip yapıştırabilirsiniz.

UYARI

ROW_FORMAT'ı kaldırmanız yeterlidir ve dymanic olur ve ngram_key tablolarını çok daha küçük sıkıştırır.


DiskSpace Metrikleri

nrgram_rec satır başına 17 byte
- ngram_id (maks işaretsiz değer 18446744073709551615 [1 2 ^ 64]) 8 bayt
4 smallints 8 bayt (2 adet bayt)
1 bayt MyISAM iç silme bayrağı

Ngram_rec = 10 bayt (8 (ngram_id) + 2 (yıl)) için Dizin Girişi

47 milyon satır X satır başına 17 bayt = 0799 milyon bayt = 761.98577 MB
47 milyon satır X satır başına 12 bayt = 0564 milyon bayt = 537.85231 MB
47 milyon satır X satır başına 29 bayt = 1363 milyon bayt = 1.269393 GB

5 milyar satır X satır başına 17 bayt = 085 milyar bayt = 079.1624 GB
5 milyar satır X satır başına 12 bayt = 060 milyar bayt = 055.8793 GB
5 milyar satır X satır başına 29 bayt = 145 milyar bayt = 135.0417 GB


ngram_key'de ngram için 73 bayt 64 bayt vardır (ROW_FORMAT = SABİT varchar'ı char olarak ayarlar) ngram_id 1 bayt MyISAM dahili silme bayrağı için 8 bayt

Ngram_key = 64 bayt + 8 bayt = 72 bayt için 2 Dizin Girişi

47 milyon satır X satır başına 073 bayt = 3431 milyon bayt = 3.1954 GB
47 milyon satır X satır başına 072 bayt = 3384 milyon bayt = 3.1515 GB
47 milyon satır X satır başına 145 bayt = 6815 milyon bayt = 6.3469 GB

5 milyar satır X satır başına 073 bayt = 365 milyar bayt = 339.9327 GB
5 milyar satır X satır başına 072 bayt = 360 milyar bayt = 335.2761 GB
5 milyar satır X satır başına 145 bayt = 725 milyar bayt = 675.2088 GB


İki harika cevap için teşekkürler. Merak ediyorum, tabloyu doldurmak için bu kara delik + tetikleme yöntemini kullanmanın nedeni nedir?
Dolan Antenucci

Kara delik orignal ngramı kabul eder. Tetikleyici, ngramı auto_increment değerinden ayırmak için temiz bir INSERT IGNORE mekanizması oluşturur.
RolandoMySQLDBA

3

İşte oldukça vahşi bir öneri

Tüm ngramları 32 karakterlik MD5 tuşlarına dönüştürme

Bu tablo, herhangi bir boyuttaki (255 karaktere kadar), 1 gram, 2 gram vb. Tüm ngramları tutacaktır.

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 bölüm seçmem sebebi, MD5 işlevinin 16 farklı karakter (tüm onaltılık basamaklar) döndürmesinden kaynaklanıyor. İlk iki bayt 16 X 16, 256'dır.

İşte Windows 7 Masaüstümde MySQL 5.5.11'deki sonuç

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>

Aynı tabloya 1 gram, 2 gram ve 3 gram yüklediğimi ancak MD5'in hangi ngrama ait olduğuna dair hiçbir fikriniz olmadığını lütfen unutmayın. Böylece, tüm ngramlar bu tek masaya uyarlanabilir. Sadece ngram_blackhole masaya yerleştirmeyi unutmayın ve gerisi sizin için yapılır.

Hangi ngram olursa olsun, ngramın MD5 () 'ini kullanarak ngram_node tablosunu sorgulamanız gerekir.

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 gram, 2 gram ve 3 gram ayrı depolara ayırmak istiyorsanız, diğer tabloya eklemek için başka bir tablo, başka bir kara delik tablo ve kara delik tablosunda başka bir tetikleyici oluşturmanız yeterlidir.

Ayrıca, ngramlarınız 255'ten uzunsa (7 gram veya 8 gram yapıyorsanız), ngram_blackhole tablosundaki NGRAM sütununun VARCHAR boyutunu artırın.

Bir şans ver !!!

GÜNCELLEME

Soruda, mysql'e 47 milyon sıra yüklendiği ifade edildi. Önerilen tablo düzenim için lütfen aşağıdakilere dikkat edin:

ngram_node satır başına 41 bayttır: NGRAM_KEY
8 için 32 sayı için (her SMALLINT için 2)
dahili MyISAM DELETED bayrağı için 1

Her bir birincil anahtar dizini girişi,
NGRAM_YEAR için NGRAM_KEY
2 için 34 bayt 32 olacaktır

47 milyon satır X satır başına 41 bayt = 1.927 milyar bayt, yaklaşık 1.79466 GB.
Endeks girişi başına 47 milyon satır X 34 bayt = 1.598 milyar bayt, yaklaşık 1.48825 GB.
MyISAM tablo tüketimi toplam 3.28291 GB civarında olmalıdır.

Soru aynı zamanda 5 milyar sıra yüklendiğinden de bahsetti.

5 milyar satır X satır başına 41 bayt = 205 milyar bayt, yaklaşık 190.9211 GB.
Endeks girişi başına 5 milyar satır X 34 bayt = 170 milyar bayt, yaklaşık 158.3248 GB.
MyISAM tablo tüketimi toplam 349.2459 GB civarında olmalıdır.

Sabit boyutlu birincil anahtar nedeniyle MyISAM tablosunda kullanılan alanın büyüme hızının doğrusal olduğunu lütfen unutmayın. Şimdi buna göre disk alanı için biraz planlama yapabilirsiniz.


1
Cevabımı düşündüm ve daha az disk alanı kullanmak için başka bir öneri var aklımda. Pazartesi günü bunu ele alacağım !!! İyi hafta sonları.
RolandoMySQLDBA
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.