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.