여러 개의 단일 INSERT 또는 하나의 여러 행 INSERT가 더 빠릅니다.


184

MySQL에 데이터를 삽입하는 코드의 한 부분을 최적화하려고합니다. 하나의 거대한 다중 행 INSERT를 만들기 위해 INSERT를 연결해야합니까? 아니면 여러 개의 개별 INSERT가 더 빠릅니까?

답변:


287

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

행을 삽입하는 데 필요한 시간은 다음 요소에 의해 결정되며 숫자는 대략적인 비율을 나타냅니다.

  • 연결 : (3)
  • 서버로 쿼리 보내기 : (2)
  • 구문 분석 쿼리 : (2)
  • 행 삽입 : (1 × 행 크기)
  • 인덱스 삽입 : (1 × 인덱스 수)
  • 결산 : (1)

이것으로부터 하나의 큰 명령문을 보내면 삽입 명령문 당 7의 오버 헤드가 절약되며 텍스트를 더 읽으면 다음과 같이 표시됩니다.

동일한 클라이언트에서 동시에 많은 행을 삽입하는 경우 여러 값 목록과 함께 INSERT 문을 사용하여 한 번에 여러 행을 삽입하십시오. 이것은 별도의 단일 행 INSERT 문을 사용하는 것보다 훨씬 빠릅니다 (일부 경우에 여러 번 더 빠름).


27
여러 단일 INSERT가 동일한 데이터베이스 트랜잭션 내에있는 경우이 답변이 어떻게 적용됩니까?
Pinch

2
단일 insert 문을 사용하여 한 번에 몇 개의 행을 삽입 할 수 있습니까? 한 번에 10000 개의 행을 삽입 할 수 있습니까?
Naresh Ramoliya

10
@Pinch ~ 1.5k 업 서트 (삽입 / 업데이트)를 수행하는 동안 트랜잭션을 사용하면 작업 시간이 ~ 1.5 초에서 ~ 0.2 초로 줄었습니다. 즉, 단일 행 인서트에 비해 86 % 더 빨랐습니다. 제길.
fgblomqvist

1
참고 : MSSQL과는 매우 다른 것 같습니다 : stackoverflow.com/questions/8635818/…
marsze

반복되는 다중 단일 삽입을 삽입하기 위해 준비된 명령문을 사용하는 것은 어떻습니까?
priyabagus

151

나는이 질문을 받았다 거의 2 년 반 후에이 질문에 대답 해요 알고 있지만 난 그냥 지금 실제로 삽입 당 여러 VALUE 블록을하고 쇼 것을 내가 일하고 있어요 프로젝트에서 일부 하드 데이터를 제공하고 싶었다 훨씬 순차적 인 단일 VALUE 블록 INSERT 문보다 빠릅니다.

C # 에서이 벤치 마크를 위해 작성한 코드는 ODBC를 사용하여 MSSQL 데이터 소스 (~ 19,000 행, 모든 쓰기 시작 전에 읽음) 및 MySql .NET 커넥터 (Mysql.Data. *)에서 메모리로 데이터를 읽습니다. 준비된 명령문을 통해 메모리의 데이터를 MySQL 서버의 테이블에 삽입하십시오. 준비된 INSERT 당 VALUE 블록 수를 동적으로 조정할 수있는 방식으로 작성되었습니다 (즉, 한 번에 n 개의 행을 삽입하여 실행하기 전에 n 값을 조정할 수 있음). 각 n에 대해 여러 번.

단일 VALUE 블록 (예 : 한 번에 한 행)을 실행하는 데 5.7-5.9 초가 걸렸습니다. 다른 값은 다음과 같습니다.

한 번에 2 행 : 3.5-3.5 초
한 번에 5 행 : 2.2-2.2 초
한 번에 10 행
: 1.7-1.7 초 한 번에 50 행 : 1.17-1.18 초
한 번에 100 행 : 1.1-1.4 초
한 번에 500 행
: 1.1-1.2 초 한 번에 1000 행 : 1.17-1.17 초

따라서 2 ~ 3 개의 쓰기를 함께 묶어도 n = 5와 n = 10 사이의 어딘가에 도달 할 때까지 속도가 크게 향상됩니다 (런타임이 n 배만큼 줄어 듭니다). n = 10에서 n = 50 범위의 어딘가에서 개선은 무시할만한 수준이됩니다.

사람들이 (a) 다중 준비 아이디어를 사용할지 여부와 (b) 명령문 당 작성할 VALUE 블록 수 (최대 쿼리 크기를 초과하여 쿼리를 푸시하기에 충분히 큰 데이터로 작업하려는 경우)를 결정하는 데 도움이되는 희망 MySQL의 경우 서버의 max_allowed_packet 값에 따라 크거나 작은 많은 장소에서 기본적으로 16MB라고 생각합니다.)


1
설명 요청 : 시간은 "행당 초"또는 "초"입니다.
EngrStudent 12

3
초 총-행 당 초는 ~ 19,000 행으로 나눈 것입니다. 비록 작은 숫자이지만 쉽게 비교할 수있는 숫자를 찾고 있다면 행 / 초가 더 나은 메트릭입니다.
Jon Kloske

덧붙여, 나는 내이 관련 답변에 위의 설명하는 방법에 대한 몇 가지 예를 들어 .NET 코드가있다 : stackoverflow.com/questions/25377357/...
존 Kloske

18

트랜잭션 엔진을 사용하는지 여부와 자동 커밋을 설정했는지 여부가 주요 요인입니다.

자동 커밋은 기본적으로 켜져 있으며 그대로두고 싶을 것입니다. 따라서 수행하는 각 삽입은 자체 트랜잭션을 수행합니다. 즉, 행당 하나의 삽입을 수행하면 각 행마다 트랜잭션을 커밋하게됩니다.

단일 스레드를 가정하면 서버는 모든 행에 대해 일부 데이터를 디스크에 동기화해야합니다. 데이터가 지속적 저장 위치 (RAID 컨트롤러의 배터리 지원 램)에 도달 할 때까지 기다려야합니다. 이것은 본질적으로 다소 느리며 아마도 이러한 경우 제한 요소가 될 것입니다.

물론 트랜잭션 엔진 (보통 innodb)을 사용하고 내구성을 줄이기 위해 설정을 조정하지 않았다고 가정합니다.

또한 단일 인서트를 사용하여 이러한 인서트를 수행한다고 가정합니다. 여러 버전의 MySQL을 사용하면 일부 MySQL 버전에서 innodb에서 그룹 커밋이 작동하기 때문에 약간의 혼란을 겪습니다. 즉, 자체 커밋을 수행하는 여러 스레드가 트랜잭션 로그에 대한 단일 쓰기를 공유 할 수 있습니다. 이는 영구 저장소에 대한 동기화가 적기 때문에 좋습니다. .

반면에, 결과는 다열 인서트를 사용하고 싶다는 것입니다.

비생산적인 방법에는 한계가 있지만 대부분의 경우 10,000 행 이상입니다. 따라서 최대 1,000 개의 행을 배치하면 안전 할 것입니다.

MyISAM을 사용하고 있다면, 다른 많은 것들이 있지만, 나는 당신을 지루하지 않을 것입니다. 평화.


1
특정 시점 이후에 생산성이 떨어지는 이유가 있습니까? 나는 그것이 전에도 일어난 것을 보았지만 왜 그런지 확신하지 못했습니다.
Dhruv Gairola 3

1
트랜잭션을 사용할 때 MySQL 삽입을 일괄 처리 할 때 어떤 점이 있는지 아십니까 . 내 기본 라이브러리 (Java JDBC-mysql-connector-java-5.1.30)가 실제로 커밋하지 않으면 다중 값 SQL 명령을 생성 해야하는 문제를 해결할 수 있는지 궁금합니다.
RTF

@ RTF 구현 특정 동작이므로 상황에서 해당 동작을 결정하기 위해 작은 테스트를 수행해야하지만 많은 경우 트랜잭션이 유사한 성능 향상을 제공해야한다고 생각합니다.
Jasmine Hegman

9

와이어를 통해 가능한 한 많은 인서트를 보내십시오. 실제 인서트 속도는 동일해야하지만 네트워크 오버 헤드가 줄어들면 성능이 향상됩니다.


7

일반적으로 데이터베이스 호출 횟수가 적을수록 (빠르고 효율적 임) 더 좋으므로 데이터베이스 액세스를 최소화하는 방식으로 삽입물을 코딩하십시오. 연결 풀을 사용하지 않는 한 각 데이터베이스 액세스는 연결을 작성하고 SQL을 실행 한 다음 연결을 해제해야합니다. 상당히 약간의 오버 헤드!


지속적인 연결이 사용되면 어떻게됩니까?
dusoft

6
여전히 오버 헤드가 있습니다. 수천 개의 인서트를 수행하는 경우 운송 시간 (각 개별 인서트에 대한 왕복 시간) 만 빠르게 인식 할 수 있습니다.
RC.

4

당신은 할 수 있습니다 :

  • 자동 커밋이 해제되어 있는지 확인
  • 열린 연결
  • 한 번의 트랜잭션으로 여러 배치의 인서트를 보냅니다 (약 4000-10000 행의 크기입니까?)
  • 연결을 닫습니다

서버 저울 (그 결정적으로 확인을 얼마나 잘에 따라 PostgreSQl, Oracle그리고 MSSQL), 다중 스레드와 다중 연결로 위의 일을.


3

일반적으로 연결 오버 헤드로 인해 여러 인서트가 느려집니다. 한 번에 여러 개의 인서트를 사용하면 인서트 당 오버 헤드 비용이 줄어 듭니다.

사용중인 언어에 따라 db로 이동하기 전에 프로그래밍 / 스크립팅 언어로 배치를 작성하고 각 삽입을 배치에 추가 할 수 있습니다. 그런 다음 하나의 연결 작업을 사용하여 큰 배치를 실행할 수 있습니다. 다음 은 Java의 예입니다.


3

MYSQL 5.5 하나의 sql insert 문은 ~ 300에서 ~ 450ms가 걸렸습니다. 아래 통계는 인라인 다중 삽입 통계에 대한 것입니다.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

나는 인라인이 갈 길이라고 말합니다 :)


0

삽입과 관련하여 Mysql과 MariaDB가 얼마나 나쁜지에 대해서는 우스운 일입니다. 필자는 mysql 5.7과 mariadb 10.3을 테스트했지만 그 차이는 없습니다.

NVME 디스크, 70,000 IOPS, 1.1 GB / sec seq 처리량을 가진 서버에서 이것을 테스트했으며 전이중 (읽기 및 쓰기)이 가능합니다.
서버는 고성능 서버이기도합니다.
20GB의 램을 제공했습니다.
데이터베이스가 완전히 비어 있습니다.

내가받는 속도는 다중 행 삽입을 할 때 초당 5000 개의 삽입이었습니다 (1MB에서 최대 10MB 청크의 데이터로 시도)

단서 :
다른 스레드를 추가하고 SAME 테이블에 삽입하면 갑자기 2x5000 / sec가 발생합니다. 하나 이상의 스레드와 총 15000의 초당 속도가 있습니다.

한 번의 스레드 삽입을 수행하면 디스크에 순차적으로 쓸 수 있음을 의미합니다 (인덱스 제외). 스레드를 사용할 때 이제 훨씬 더 많은 임의 액세스를 수행해야하므로 실제로 가능한 성능이 저하됩니다. 그러나 현실 검사는 mysql이 너무 최적화되어 스레드가 많은 도움이된다는 것을 보여줍니다.

이러한 서버에서 가능한 실제 성능은 아마도 초당 수백만 개이고 CPU는 유휴 상태이며 디스크는 유휴 상태입니다.
그 이유는 mysql이 내부 지연을 갖는 것처럼 mariadb이기 때문입니다.


@Craftables는 외부 개발이 필요하며 mysql에서는 수행 할 수 없습니다. 스레드는 서버에 여러 연결을 사용하고 쿼리를 여러 청크로 분할하는 것을 의미합니다 (예 : 기본 키를 기준으로 짝수로 분할). 매우 큰 테이블에서이 방법을 사용하여 최대 10,000 배의 성능을 얻을 수있었습니다. 여러 스레드를 사용하고 mysql이 고도로 최적화 된 경우 40,000 초 동안 실행되는 쿼리는 2-3 분 안에 완료 될 수 있습니다.
John

@John 흥미롭고 정말 멋진 응용 프로그램이있을 수 있지만 ... 쿼리를 여러 청크로 분할하면 트랜잭션을 어떻게 처리합니까? 또한 다음 시나리오를 고려하십시오. 테이블 x에는 동일한 테이블 'id'와 관련된 'parent_id'열이 있습니다. 데이터 어딘가에 INSERT INTO x ( id, parent_id) VALUES (1, NULL) 있습니다. 다음 값 세트 중 하나가 해당 행에 연결됩니다. 덩어리로 분할하고 해당 세트를 다른 덩어리로 가져 오면 첫 번째 덩어리보다 먼저 처리되어 전체 프로세스가 실패 할 수 있습니다. 그 문제를 어떻게 해결할 수 있습니까?
zozo

@zozo 이것은 대량 삽입 및 대량 쿼리에 유용합니다. 트랜잭션은 많은 데이터 버퍼링을 포함하므로 어쨌든 성능을 망치게됩니다. 그러나 다중 스레드 삽입 또는 쿼리에서 트랜잭션을 사용할 수도 있습니다.
John

-2

여러 인서트가 더 빠르지 만 나사산이 있습니다. 또 다른 thrik은 임시 검사 삽입을 훨씬 더 빠르게 제한합니다. 테이블의 유무는 중요하지 않습니다. 예를 들어 외래 키 비활성화를 테스트하고 속도를 즐기십시오.

SET FOREIGN_KEY_CHECKS=0;

다음과 같은 방법으로 삽입 후 다시 켜야합니다.

SET FOREIGN_KEY_CHECKS=1;

이는 대량의 데이터를 삽입하는 일반적인 방법입니다. 데이터 무결성이 손상 될 수 있으므로 외래 키 검사를 비활성화하기 전에 데이터 무결성을 관리해야합니다.


1
왜 ppl이 두 가지 이유로이 문제를 제기했는지 전혀 모릅니다. 1. 질문과는 아무런 관련이 없습니다. 검사는 이유가 있습니다. 데이터 일관성을 보장하기 위해 있습니다. 속도가 느려져서 데이터를 삽입하거나 변경하지 않아야합니다. 올바른 방법으로 쿼리를 최적화하십시오. 업무상 중요한 환경에서 이는 어느 정도 조심해야하는지에 관계없이 앱의 죽음을 의미합니다.
zozo

1
어쩌면이 옵션은 큰 테이블을 가져 오는 데 매우 효과적이며 매우 실용적이며 일부 사람들은 데이터를 훨씬 빠르게 삽입하는 방법을 알 수 있습니다.
MSS
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.