InnoDB 가져 오기 성능


10

나는 약 10 백만 행 (또는 7GB)으로 구성된 상당히 큰 InnoDB-Table을 대량으로 가져 오는 데 어려움을 겪고 있습니다 (지금까지 작업 한 가장 큰 테이블입니다).

Inno의 가져 오기 속도를 개선하는 방법에 대한 조사를 수행했으며 현재 설정이 다음과 같이 보입니다.

/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8


import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;

데이터는 CSV파일 로 제공 됩니다.
현재 저는 2 백만, 3 백만,… 각 행으로 더 작은 '테스트 덤프'로 설정을 테스트하고 time import_script.sh성능을 비교 하는 데 사용합니다.

단점은 전체 실행 시간 만 가져 오기 때문에 전체 가져 오기가 완료 될 때까지 기다려야 결과가 나옵니다.

지금까지 내 결과 :

  • 10,000 행 : <1 초
  • 100 000 행 : 10 초
  • 300,000 행 : 40 초
  • 2 백만 행 : 18 분
  • 3 백만 줄 : 26 분
  • 4 백만 행 : (2 시간 후 취소)

'요리 책'솔루션이 없으며 최적의 설정 조합을 스스로 파악해야합니다.
설정 변경 사항에 대한 제안 이외에도 가져 오기 프로세스를보다 효과적으로 벤치마킹 할 수있는 방법에 대해 더 많은 정보를 얻을 수 있습니다. 진행 상황과 병목 현상이 발생하는 위치에 대해 더 많은 통찰력을 얻을 수 있습니다.
변경중인 설정에 대한 설명서를 읽으려고했지만 다시 부작용을 알지 못하고 잘못 선택한 값으로 성능이 저하 될 수도 있습니다.

현재 채팅 MyISAM및 가져 오기 및 테이블 엔진 변경 중에 사용할 제안을하고 싶습니다 .
이것을 시도하고 싶지만 잠시 DROP TABLE동안 쿼리가 완료되는 데 몇 시간이 걸립니다. (내 설정이 최적이 아닌 다른 표시기 인 것 같습니다).

추가 정보 :
현재 사용중인 컴퓨터에는 8GB의 RAM과 5400RPM의 솔리드 스테이트 하이브리드 하드 드라이브가 있습니다.
우리는 또한 문제의 테이블에서 쓸모없는 데이터를 제거하는 것을 목표로하지만 여전히
a) 테스트 automatic data cleanup feature하는 동안 약간의 빠른 가져 오기가
필요합니다. 최신 데이터, 마지막 가져 오기에 24 시간 이상 소요)

mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
       Table: monster
Create Table: CREATE TABLE `monster` (
  `monster_id` int(11) NOT NULL AUTO_INCREMENT,
  `ext_monster_id` int(11) NOT NULL DEFAULT '0',
  `some_id` int(11) NOT NULL DEFAULT '0',
  `email` varchar(250) NOT NULL,
  `name` varchar(100) NOT NULL,
  `address` varchar(100) NOT NULL,
  `postcode` varchar(20) NOT NULL,
  `city` varchar(100) NOT NULL,
  `country` int(11) NOT NULL DEFAULT '0',
  `address_hash` varchar(250) NOT NULL,
  `lon` float(10,6) NOT NULL,
  `lat` float(10,6) NOT NULL,
  `ip_address` varchar(40) NOT NULL,
  `cookie` int(11) NOT NULL DEFAULT '0',
  `party_id` int(11) NOT NULL,
  `status` int(11) NOT NULL DEFAULT '2',
  `creation_date` datetime NOT NULL,
  `someflag` tinyint(1) NOT NULL DEFAULT '0',
  `someflag2` tinyint(4) NOT NULL,
  `upload_id` int(11) NOT NULL DEFAULT '0',
  `news1` tinyint(4) NOT NULL DEFAULT '0',
  `news2` tinyint(4) NOT NULL,
  `someother_id` int(11) NOT NULL DEFAULT '0',
  `note` varchar(2500) NOT NULL,
  `referer` text NOT NULL,
  `subscription` int(11) DEFAULT '0',
  `hash` varchar(32) DEFAULT NULL,
  `thumbs1` int(11) NOT NULL DEFAULT '0',
  `thumbs2` int(11) NOT NULL DEFAULT '0',
  `thumbs3` int(11) NOT NULL DEFAULT '0',
  `neighbours` tinyint(4) NOT NULL DEFAULT '0',
  `relevance` int(11) NOT NULL,
  PRIMARY KEY (`monster_id`),
  KEY `party_id` (`party_id`),
  KEY `creation_date` (`creation_date`),
  KEY `email` (`email`(4)),
  KEY `hash` (`hash`(8)),
  KEY `address_hash` (`address_hash`(8)),
  KEY `thumbs3` (`thumbs3`),
  KEY `ext_monster_id` (`ext_monster_id`),
  KEY `status` (`status`),
  KEY `note` (`note`(4)),
  KEY `postcode` (`postcode`),
  KEY `some_id` (`some_id`),
  KEY `cookie` (`cookie`),
  KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8

2
10K 또는 100K 행과 같이 덜 큰 수입품을 사용해 보셨습니까?
ypercubeᵀᴹ

1
SHOW CREATE TABLE yourtable\G이 천만 행 테이블의 테이블 구조를 보여주기 위해 실행 하십시오.
RolandoMySQLDBA

@RolandoMySQLDBA 그래서 내가 (필드 이름이 가려

이중 쓰기 버퍼 ( innodb_doublewrite = 0) 를 비활성화하면 MySQL 설치가 충돌 안전하지 않습니다. MySQL 고장이 아닌 정전이 발생하면 데이터가 자동으로 손상 될 수 있습니다.
jfg956

답변:


13

먼저, InnoDB 테이블에 수백만 행을 쟁기질 할 때 InnoDB에 수행중인 작업을 알아야합니다. InnoDB 아키텍처를 살펴 보자.

InnoDB 아키텍처

왼쪽 상단에는 InnoDB 버퍼 풀이 나와 있습니다. 삽입 버퍼 전용 섹션이 있습니다. 그게 뭐에요? 버퍼 풀에서 2 차 인덱스에 대한 변경 사항을 시스템 테이블 공간 (일명 ibdata1) 내의 삽입 버퍼로 마이그레이션해야합니다. 기본적으로 innodb_change_buffer_max_size 는 25로 설정됩니다. 이는 2 차 색인 처리에 버퍼 풀의 최대 25 %를 사용할 수 있음을 의미합니다.

귀하의 경우 InnoDB 버퍼 풀에 대해 6.935GB가 있습니다. 보조 인덱스를 처리하는 데 최대 1.734GB가 사용됩니다.

자, 당신의 테이블을보십시오. 보조 인덱스가 13 개 있습니다. 처리하는 각 행은 보조 인덱스 항목을 생성하고이를 행의 기본 키와 연결 한 다음 버퍼 풀의 삽입 버퍼에서 ibdata1의 삽입 버퍼로 쌍으로 보내야합니다. 각 행마다 13 번 발생합니다. 이 값에 1000만을 곱하면 병목 현상이 발생할 수 있습니다.

단일 트랜잭션에서 천만 개의 행을 가져 오면 모든 것을 하나의 롤백 세그먼트로 쌓고 ibdata1의 UNDO 공간을 채 웁니다.

제안

제안 # 1

이 큰 테이블을 가져 오기위한 첫 번째 제안은

  • 고유하지 않은 인덱스를 모두 삭제
  • 데이터 가져 오기
  • 고유하지 않은 인덱스를 모두 만듭니다.

제안 # 2

중복 인덱스를 제거하십시오. 귀하의 경우에는

KEY `party_id` (`party_id`),
KEY `party_id_2` (`party_id`,`status`)

두 인덱스 모두로 시작하며 party_id, 13 개 중 하나의 인덱스를 제거하여 2 차 인덱스 처리를 7.6 % 이상 늘릴 수 있습니다.

ALTER TABLE monster DROP INDEX party_id;

제안 # 3

사용하지 않는 색인을 제거하십시오. 응용 프로그램 코드를 살펴보고 쿼리가 모든 인덱스를 사용하는지 확인하십시오. 당신은 조사 할 수 있습니다 PT-인덱스 사용 은 인덱스를 사용하지 않을 것을 제안 할 수 있습니다.

제안 # 4

기본값은 8M이므로 innodb_log_buffer_size 를 64M으로 늘려야합니다 . 로그 버퍼가 크면 InnoDB 쓰기 I / O 성능이 향상 될 수 있습니다.

발문

처음 두 제안을 제자리에두고 다음을 수행하십시오.

  • 고유하지 않은 13 개의 인덱스 삭제
  • 데이터 가져 오기
  • 를 제외한 모든 비 고유 인덱스 만들기 party_id인덱스를

아마도 다음이 도움이 될 것입니다

CREATE TABLE monster_new LIKE monster;
ALTER TABLE monster_new
  DROP INDEX `party_id`,
  DROP INDEX `creation_date`,
  DROP INDEX `email`,
  DROP INDEX `hash`,
  DROP INDEX `address_hash`,
  DROP INDEX `thumbs3`,
  DROP INDEX `ext_monster_id`,
  DROP INDEX `status`,
  DROP INDEX `note`,
  DROP INDEX `postcode`,
  DROP INDEX `some_id`,
  DROP INDEX `cookie`,
  DROP INDEX `party_id_2`;
ALTER TABLE monster RENAME monster_old;
ALTER TABLE monster_new RENAME monster;

로 데이터를 가져옵니다 monster. 그런 다음 이것을 실행하십시오.

ALTER TABLE monster
  ADD INDEX `creation_date`,
  ADD INDEX `email` (`email`(4)),
  ADD INDEX `hash` (`hash`(8)),
  ADD INDEX `address_hash` (`address_hash`(8)),
  ADD INDEX `thumbs3` (`thumbs3`),
  ADD INDEX `ext_monster_id` (`ext_monster_id`),
  ADD INDEX `status` (`status`),
  ADD INDEX `note` (`note`(4)),
  ADD INDEX `postcode` (`postcode`),
  ADD INDEX `some_id` (`some_id`),
  ADD INDEX `cookie` (`cookie`),
  ADD INDEX `party_id_2` (`party_id`,`status`);

시도 해봐 !!!

대안

monster_csv인덱스가없는 MyISAM 테이블이라는 테이블을 만들고 다음을 수행 할 수 있습니다 .

CREATE TABLE monster_csv ENGINE=MyISAM AS SELECT * FROM monster WHERE 1=2;
ALTER TABLE monster RENAME monster_old;
CREATE TABLE monster LIKE monster_old;
ALTER TABLE monster DROP INDEX `party_id`;

로 데이터를 가져옵니다 monster_csv. 그런 다음 mysqldump를 사용하여 다른 가져 오기를 작성하십시오.

mysqldump -t -uroot -p mydb monster_csv | sed 's/monster_csv/monster/g' > data.sql

mysqldump 파일 data.sql은 한 번에 10,000-20,000 개의 행을 가져 오는 INSERT 명령을 확장합니다.

이제 mysqldump를로드하십시오.

mysql -uroot -p mydb < data.sql

마지막으로 MyISAM 테이블을 제거하십시오.

DROP TABLE monster_csv;

나는 모든 키 (내 디자인이 아님)조차 알지 못했지만 당신의 설명은 매우 설득력있는 것처럼 보입니다. 오늘은 또 다른 시도를 시작하기에 늦었지만 내일 시험해 볼 수있는 몇 가지 훌륭한 조언이 있습니다. 계속 알려드립니다! <3
nuala

1
monsterInnoDB 테이블에 키가 없을 때 20 분 이내에 전체 데이터베이스 ( 테이블 뿐만 아니라 ) 를 가져올 수있었습니다 . 키를 추가하는 데는 약 또 다른 20 분 이 경우이 문제를 거의 해결한다고 말하고 싶습니다. 대단히 감사합니다!
nuala

8

의견을 작성하고 싶었지만 (정답이 아니기 때문에) 너무 길어졌습니다.

몇 가지 광범위한 조언을 드리겠습니다. 원하는 경우 각 정보에 대해 자세히 알아볼 수 있습니다.

  • 내구성을 줄입니다 (일부 이미 수행했습니다). 최신 버전으로 더 많은 작업을 수행 할 수 있습니다. 가져 오기에는 손상이 발생하지 않으므로 이중 쓰기 버퍼를 사용하지 않는 한 이동할 수 있습니다.
  • 버퍼링 증가 : 트랜잭션 로그 크기를 늘리고 사용 가능한 버퍼 풀 크기를 늘리십시오. 트랜잭션 로그 파일 사용 및 검사 점을 모니터링합니다. 수입에 대한 거대한 로그를 두려워하지 마십시오.
  • 큰 트랜잭션을 피하십시오. 롤백에 불필요한 데이터가 가득 차게됩니다. 이것은 아마도 가장 큰 문제 일 것입니다.
  • SQL은 병목 현상이 발생하고 SQL 오버 헤드 (handlersocket, memcached)를 피하거나 동시에 여러 스레드와 동시에로드합니다. 동시성은 너무 많거나 적지 않은 달콤한 지점에 도달해야합니다.
  • 기본 키 순서 조각화로 데이터로드는 문제 일 수 있습니다.
  • IO가 병목 현상이고 CPU 및 메모리가 느려지지 않는 경우 InnoDB 압축 테스트
  • 나중에 보조 키를 생성하고 (경우에 따라 더 빠름) 인덱싱 된 데이터를로드 하지 마십시오 . DISABLE KEYS는 InnoDB에 영향을 미치지 않습니다 . 그렇지 않으면 삽입 버퍼를 모니터하십시오 (버퍼 풀의 절반을 초과 할 수 있음).
  • 체크섬 알고리즘을 변경하거나 비활성화하면 문제가되지 않지만 고급 플래시 카드에서는 병목 현상이 발생합니다.
  • 최후의 수단 : 서버를 모니터링하여 현재 병목 현상을 찾아 완화하십시오 (InnoDB는 매우 유연합니다).

이 중 일부는 비 수입품 (정상 작동)에는 안전하지 않거나 권장되지 않습니다.


대단히 감사합니다! 인덱스에 관한 Rolando의 아이디어를 먼저 시도하고 싶지만이 "트랜잭션 롤백"항목이 여전히 문제가 될 것 같습니다. 이것에 대해 자세히 설명해 주시겠습니까? 나는 가져 오는 동안 가능한이 기능을 많이 사용하지 않으려 및 생산에 갈 때 바로 다시 활성화 할 생각 ~ 내 생각 엔 ...
nuala

1
롤란도의 제안은 제 요점 # 7입니다. 롤백 오버 헤드를 피하는 것은 SET SESSION tx_isolation='READ-UNCOMMITTED';여러 스레드를 병렬로 가져 오는 경우에만 유용하고 배치 삽입에 대한 @ypercube 주석 의 조합만큼 쉽습니다 . 전체 예는 다음과 같습니다. mysqlperformanceblog.com/2008/07/03/… 최신 InnoDB 버전의 모든 기능을 활용하십시오 : mysqlperformanceblog.com/2011/01/07/…
jynus

1
나는 더 작은 척으로 임포트하는 것을 피할 것이지만 "모두 포괄적"연산을 원하지만 멀티 스레딩이 가능성을 열 수 있다는 일반적인 인상을 받았습니다. 그것은 매우 구체적인 사례를 추측하십시오. 그러나이 조정 (귀하의 # 7)만으로도 <1 시간 만에 전체 수입을 얻는 데 도움이되었지만 Rolando의 대답을 받아 들였습니다.하지만 목록은 확실히 가치가 없으며 우리 DB가 조금씩 성장하는 즉시 참고 용으로 사용됩니다. 겁 나 :)
nuala

@yoshi에 동의합니다. 귀하의 답변은 문제 해결 및 성능 개선 측면에서보다 포괄적입니다. +1
RolandoMySQLDBA

3

좋은 팁의 대부분은 지금까지 주어졌지만 최고의 팁에 대한 설명은 많지 않았습니다. 자세한 내용을 알려 드리겠습니다.

첫째, 인덱스 생성을 지연시키는 것은 다른 응답에 충분한 세부 정보가있는 것이 좋습니다. 나는 다시 올 수 없습니다.

더 큰 InnoDB 로그 파일은 많은 도움이됩니다 (MySQL 5.6을 사용하는 경우 MySQL 5.5에서는 파일을 늘릴 수 없으므로). 7GB의 데이터를 삽입하는 경우 최소 8GB의 전체 로그 크기를 권장합니다 ( innodb_log_files_in_group기본값은 2로 유지 하고 4GB에서는 범프 innodb_log_file_size). 이 8GB는 정확하지 않습니다. 최소한 REDO 로그의 가져 오기 크기 여야하고 해당 크기의 두 배 또는 네 배가되어야합니다. InnoDB 로그 크기의 배후는 로그가 거의 가득 차면 InnoDB가 버퍼 풀을 디스크로 적극적으로 플러시하기 시작하여 로그가 가득 차는 것을 피합니다 (로그가 가득 찼을 때 InnoDB는 데이터베이스 쓰기를 수행 할 수 없습니다) 버퍼 풀의 페이지가 디스크에 기록됩니다.

더 큰 InnoDB 로그 파일이 도움이되지만 기본 키 순서로 삽입해야합니다 (삽입하기 전에 파일 정렬). 기본 키 순서로 삽입하면 InnoDB가 한 페이지를 채우고 다른 페이지를 채 웁니다. 기본 키 순서로 삽입하지 않으면 다음 삽입이 가득 찬 페이지로 끝나고 "페이지 분할"이 발생합니다. 이 페이지 분할은 InnoDB에 비용이 많이 들고 가져 오기 속도가 느려집니다.

RAM이 허용하는 한 큰 버퍼 풀이 이미 있고 테이블이 맞지 않으면 더 많은 RAM을 구입하는 것 외에는 할 수있는 것이 없습니다. 그러나 테이블이 버퍼 풀에 맞지만 버퍼 풀의 75 %보다 큰 innodb_max_dirty_pages_pct경우 가져 오는 동안 85 또는 95로 증가하려고 시도 할 수 있습니다 (기본값은 75). 이 구성 매개 변수는 더티 페이지의 백분율이이 한계에 도달하면 InnoDB가 적극적으로 버퍼 풀 플러시를 시작하도록 지시합니다. 이 매개 변수를 늘리면 (데이터 크기가 운이 좋은 경우) 가져 오는 동안 공격적인 IO를 피하고 해당 IO를 나중에 지연시킬 수 있습니다.

아마도 많은 소규모 거래에서 데이터를 가져 오는 것이 도움이 될 것입니다. REDO 로그가 어떻게 작성되는지 정확히 알지 못하지만 트랜잭션이 진행되는 동안 RAM에 버퍼링되고 (RAM이 너무 많은 디스크가 필요한 경우) 불필요한 IO가 생길 수 있습니다. 당신은 이것을 시도 할 수 있습니다 : 일단 파일이 정렬되면, 많은 청크로 분할하고 (16MB 및 다른 크기로 시도하십시오) 하나씩 가져옵니다. 또한 가져 오기 진행률을 제어 할 수 있습니다. 가져 오기를 수행하는 동안 데이터를 다른 독자에게 부분적으로 표시하지 않으려면 다른 테이블 이름을 사용하여 가져오고 나중에 색인을 작성한 다음 테이블 이름을 바꿀 수 있습니다.

하이브리드 SSD / 5400RPM 디스크에 관해서는 이것들과이를 최적화하는 방법을 모르겠습니다. 5400RPM은 데이터베이스를 느리게 보지만 SSD가이를 피할 수 있습니다. 아마도 디스크의 SSD 부분을 REDO 로그에 순차적으로 쓰면 SSD가 성능을 저하시킵니다. 나도 몰라.

시도해 보거나 조심하지 말아야 할 나쁜 팁은 다음과 같습니다. 멀티 스레드를 사용하지 마십시오 : InnoDB에서 페이지 분할을 피하기 위해 최적화하기가 매우 어렵습니다. 다중 스레드를 사용하려면 다른 테이블 (또는 동일한 테이블의 다른 파티션)에 삽입하십시오.

다중 스레드를 고려중인 경우 다중 소켓 (NUMA) 컴퓨터가있을 수 있습니다. 이 경우 The MySQL swap insanity problem 을 피하십시오 .

MySQL 5.5를 사용하는 경우 MySQL 5.6으로 업그레이드하십시오. REDO 로그 크기를 늘리는 옵션이 있으며 버퍼 풀 플러싱 알고리즘이 향상되었습니다.

수입에 행운을 빕니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.