Google ngram을 데이터베이스에 가장 잘 저장하는 방법은 무엇입니까?


9

며칠 전에 Google onegrams를 다운로드했으며 이미 엄청난 양의 데이터입니다. 10 개의 패키지 중 첫 번째 패키지를 mysql에 삽입했으며 이제 4,700 만 개의 레코드 데이터베이스가 있습니다.

Google ngram을 데이터베이스에 가장 잘 저장하는 방법이 궁금합니다. 1 그램을 사용하지 않고 2 그램 또는 3 그램을 사용하면 금액이 훨씬 커집니다. 하나의 데이터베이스에 5 억 개의 레코드를 저장하고 작업 할 수 있습니까? 아니면 다른 테이블로 분할해야합니까?

몇 개의 레코드를 분할해야하고 어떻게 가장 잘 분할해야합니까 (2 그램에 100 개의 파일이 있고 아마도 약 50 억 개의 레코드가 있다는 것을 고려하면)? 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에는 ngram_id에 대해 행당 17 바이트가
8 바이트입니다 (최대 부호없는 값 18446744073709551615 [2 ^ 64-1])
4 개의 smallints에 대해 8 바이트 (각 2 바이트)
1 바이트 MyISAM 내부 삭제 플래그

ngram_rec = 10 바이트의 색인 항목 (8 (ngram_id) + 2 (yr))

47 백만 행 X 행당 17 바이트 = 7 억 9700 만 바이트 = 761.98577MB
47 백만 행 X 행당 12 바이트 = 0564 백만 바이트 = 537.85231MB
47 백만 행 X 행당 29 바이트 = 1 억 3,600 만 바이트 = 1.269393GB

50 억 행 X 행당 17 바이트 = 085 억 바이트 = 079.1624GB
50 억 행 X 행당 12 바이트 = 0,600 억 바이트 = 055.8793GB
50 억 행 X 행당 29 바이트 = 145 억 바이트 = 135.0417GB


ngram_key는 ngram의 경우 73 바이트 64 바이트 (ROW_FORMAT = FIXED varchar를 char로 설정) ngram_id의 경우 8 바이트 1 바이트 MyISAM 내부 삭제 플래그

ngram_key = 64 바이트 + 8 바이트 = 72 바이트에 대한 2 개의 인덱스 항목

47 백만 행 X 행 073 바이트 = 3 억 3,400 만 바이트 = 3.1954GB
47 백만 행 X 행 072 바이트 = 3384 백만 바이트 = 3.1515GB
47 백만 행 X 행 145 바이트 = 6815 백만 바이트 = 6.3469GB

50 억 행 × 행당 073 바이트 = 365 억 바이트 = 339.9327GB
50 억 행 × 행당 072 바이트 = 360 억 바이트 = 335.2761GB
50 억 행 × 행당 145 바이트 = 725 억 바이트 = 675.2088GB


두 가지 큰 답변에 감사드립니다. 궁금합니다.이 블랙홀 + 트리거 방법을 사용하여 테이블을 채우는 이유는 무엇입니까?
Dolan Antenucci

블랙홀은 orignal ngram을 받아들입니다. 트리거는 깨끗한 INSERT IGNORE 메커니즘을 생성하여 ngram을 auto_increment 값에서 분리합니다.
RolandoMySQLDBA

3

여기에 아주 거친 제안이 있습니다.

모든 ngram을 32 자 MD5 키로 변환

이 테이블은 모든 크기의 ngram (최대 255 자), 1-gram, 2-gram 등을 보유합니다.

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 진수)를 반환한다는 사실에서 비롯됩니다. 처음 두 바이트는 16 X 16, 256입니다.

내 Windows 7 데스크톱에서 MySQL 5.5.11의 결과는 다음과 같습니다.

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에 관계없이 ngram의 MD5 ()를 사용하여 ngram_node 테이블을 쿼리해야합니다.

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 그램을 수행하는 경우) ngram_blackhole 테이블에서 NGRAM 열의 VARCHAR 크기를 늘리십시오.

시도 해봐 !!!

최신 정보

이 질문에서 mysql에 4,700 만 행이로드되었다고 언급되었습니다. 내 제안 된 테이블 레이아웃에 대해서는 다음을 참고하십시오.

ngram_node는 행당 41 바이트입니다. NGRAM_KEY의 경우 32
, 숫자의 경우 8 (각 SMALLINT의 경우 2)
, 내부 MyISAM DELETED 플래그의 경우 1

각 기본 키 인덱스 항목은
NGRAM_YEAR의 경우 NGRAM_KEY
2의 경우 34 바이트 32입니다.

47 백만 행 × 행당 41 바이트 = 1,927 억 바이트, 약 1.79466GB
인덱스 항목 당 4,700 만 행 × 34 바이트 = 1.598 억 바이트, 약 1.48825GB
MyISAM 테이블 소비량은 약 3.28291GB입니다.

이 질문은 또한 50 억 행을로드하는 것을 언급했습니다.

50 억 행 × 행당 41 바이트 = 205 억 바이트, 약 190.9211GB
인덱스 항목 당 50 억 행 X 34 바이트 = 약 1 억 7 천 7 백 8 십 바이트
MyISAM 테이블 소비량은 약 349.2459GB입니다.

MyISAM 테이블에 사용 된 공간의 증가 속도는 일정한 크기의 기본 키로 인해 선형입니다. 이제이를 기반으로 디스크 공간을 계획 할 수 있습니다.


1
내 대답에 대해 생각하고 디스크 공간을 덜 사용하도록 다른 제안을 염두에 두었습니다. 나는 월요일에 이것을 해결할 것이다! 좋은 주말 되세요.
RolandoMySQLDBA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.