찾다

 >  Q&A  >  본문

자동 증가 기본 키 없이 대규모 MySQL 테이블에 삽입하면 매우 느립니다.

<p>최근에 간단한 INSERT 문을 완료하는 데 필요한 시간 차이가 크게 증가한 것을 확인했습니다. 이러한 명령문은 평균 약 11밀리초가 걸리지만 때로는 10~30초가 걸릴 수도 있으며 실행하는 데 5분 이상 걸리는 경우도 있습니다. </p> <p>MySQL 버전은 <code>8.0.24</code>이며 Windows Server 2016에서 실행됩니다. 제가 아는 한, 서버의 리소스가 과부하된 적은 없습니다. 서버에는 사용 가능한 CPU 오버헤드가 충분하며 32GB RAM이 할당되어 있습니다. </p> <p>이것은 제가 사용하고 있는 테이블입니다: </p> <pre class="brush:php;toolbar:false;">CREATE TABLE `saved_segment` ( `recording_id` bigint unsigned NOT NULL, `index` bigint unsigned NOT NULL, `start_filetime` bigint unsigned NOT NULL, `end_filetime` bigint unsigned NOT NULL, `offset_and_size` bigint unsigned NOT NULL DEFAULT '18446744073709551615', `storage_id`tinyint unsigned NOT NULL, 기본 키(`recording_id`,`index`) ) 엔진=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci</pre> <p>이 테이블에는 다른 인덱스나 외래 키가 없으며 다른 테이블의 외래 키에 대한 참조로 사용되지 않습니다. 전체 테이블 크기는 20GB 정도이고, 행 수는 281M 정도로 그리 크지 않은 느낌입니다. </p> <p>이 테이블은 거의 대부분 읽기 전용 모드로 사용되며 초당 최대 1000회 읽기가 가능합니다. 이러한 모든 읽기는 복잡한 트랜잭션이 아닌 간단한 SELECT 쿼리에서 발생하며 기본 키 인덱스를 효율적으로 활용합니다. 이 테이블에 대한 동시 쓰기는 거의 없습니다. 이는 느린 삽입에 도움이 될지 알아보기 위해 의도적으로 수행되었지만 그렇지 않았습니다. 그때까지는 항상 최대 10개의 동시 삽입이 진행됩니다. 이 테이블에서는 UPDATE 또는 DELETE 문이 실행되지 않습니다. </p> <p>제가 문제를 겪고 있는 검색어는 모두 이런 방식으로 구성되었습니다. 거래에는 절대 나타나지 않습니다.클러스터된 기본 키를 기반으로 한 삽입은 추가 전용이 아니지만 쿼리는 거의 항상 1~20개의 인접한 행을 테이블에 삽입합니다. </p> <pre class="brush:php;toolbar:false;">saved_segment에 무시 삽입 (recording_id, `index`, start_filetime, end_filetime, offset_and_size, Storage_id) VALUES (19173, 631609, 133121662986640000, 133121663016640000, 20562291758298876, 10), (19173, 631610, 133121663016640000, 133121663046640000, 20574308942546216, 10), (19173, 631611, 133121663046640000, 133121663076640000, 20585348350688128, 10), (19173, 631612, 133121663076640000, 133121663106640000, 20596854568114720, 10), (19173, 631613, 133121663106640000, 133121663136640000, 20609723363860884, 10), (19173, 631614, 133121663136640000, 133121663166640000, 20622106425668780, 10), (19173, 631615, 133121663166640000, 133121663196640000, 20634653501528448, 10), (19173, 631616, 133121663196640000, 133121663226640000, 20646967172721148, 10), (19173, 631617, 133121663226640000, 133121663256640000, 20657773176227488, 10), (19173, 631618, 133121663256640000, 133121663286640000, 20668825200822108, 10)</pre> <p>위 쿼리에 대한 EXPLAIN 문의 출력은 다음과 같습니다. </p> <테이블 클래스="s-테이블"> <머리> <tr> <번째> ID 유형 선택 <번째> 테이블 <번째>파티션 <번째> 유형 가능한 키 <번째> 키 <번째>key_len <번째> 참조 <일>알겠습니다</일> <th>필터링됨</th> <일>추가</일> </tr> </머리> <본문> <tr> <td>1</td> <td>삽입</td> <td>저장된 세그먼트</td> <td>비어있음</td> <td>모두</td> <td>비어있음</td> <td>비어있음</td> <td>비어있음</td> <td>비어있음</td> <td>비어있음</td> <td>비어있음</td> <td>비어있음</td> </tr> </tbody> </테이블> <p>이러한 문제는 비교적 새로운 문제이며 테이블이 두 배 정도 작을 때는 눈에 띄지 않았습니다. </p> <p>테이블의 동시 삽입 수를 약 10개에서 1개로 줄여 보았습니다. 또한 삽입 속도를 더욱 높이기 위해 일부 열에서 외래 키(<code>recording_id</code>)를 제거했습니다. <code>테이블 분석</code> 및 스키마 분석에서는 실행 가능한 정보가 나오지 않았습니다.</p> <p>제가 생각한 한 가지 해결책은 클러스터된 기본 키를 제거하고 <code>(recording_id, index)</code> 열에 자동 증가 기본 키와 일반 인덱스를 추가하는 것이었습니다. 내 생각에는 이것이 삽입을 "추가 전용"으로 만드는 데 도움이 될 것입니다.저는 어떤 제안이라도 환영합니다. 미리 감사드립니다! </p> <p>편집: 댓글과 답변에서 제기된 몇 가지 요점과 질문을 다루겠습니다. </p>
P粉845862826P粉845862826462일 전597

모든 응답(1)나는 대답할 것이다

  • P粉022140576

    P粉0221405762023-08-30 00:15:37

    이 답변으로 고민하겠습니다.

    가설

    • innodb_buffer_pool_size의 값은 20MB보다 약간 작습니다.
    • 초당 1,000개의 선택 항목이 테이블의 무작위 부분에 도착하고 그런 다음

    다음 Select에 필요한 "다음" 블록이 buffer_pool에 캐시되지 않는 경우가 점점 많아지면서 최근 시스템이 I/O 바인딩되었습니다.

    간단한 해결책은 더 많은 RAM을 확보하고 이 튜너블의 설정을 늘리는 것입니다. 하지만 테이블은 구매하는 다음 한도까지만 증가합니다.

    대신 몇 가지 부분적인 해결 방법은 다음과 같습니다.

    • 숫자가 너무 크지 않으면 처음 두 열이 INT UNSIGNED(4 个字节而不是 8),甚至可能是 MEDIUMINT UNSIGNED(3 个字节) )。注意 ALTER TABLE테이블을 오랫동안 잠글 수 있습니다.
    • 이러한 시작 및 종료 시간은 분수 초가 포함된 타임스탬프처럼 보이며 항상 ".000"입니다. DATETIMETIMESTAMP 5바이트를 사용합니다(8바이트 대신).
    • 귀하의 예에서는 경과 시간이 0으로 표시됩니다. (종료-시작)이 일반적으로 매우 작은 경우 종료 시간 대신 경과 시간을 저장하면 데이터가 더욱 축소됩니다. (그러나 종료 시간을 사용하면 상황이 혼란스러울 수 있습니다.)
    • 제공하신 예시 데이터는 "연속"으로 보입니다. 이는 자동 증가만큼 효율적입니다. 이것이 표준입니까? 그렇지 않은 경우 INSERT는 I/O 스래싱의 일부일 수 있습니다.
    • 삽입 작업이 두 배로 늘어나는 보조 인덱스뿐만 아니라 인공 지능도 추가하는 것이 좋습니다.

    더보기

    네, 그렇습니다.

    이를 INDEX,或者更好的是,作为 PRIMARY KEY의 시작 부분으로 사용하면 두 가지 질문 모두에 대해 최고의 도움을 받을 수 있습니다.

    으아악

    답글:

    으아악

    다른 SQL을 제어하는 ​​데 사용되는 경우 다른 SQL에 추가하는 것을 고려해 보세요.

    으아악

    이 쿼리(어느 형식이든)에는 이미 보유하고 있는 콘텐츠가 필요합니다.

    으아악

    기타 문의사항

    으아악

    그래서 색인을 추가하세요. 또는 ...

    더 좋습니다... 이 조합은 두 가지 모두에 더 좋습니다 : SELECT 으아악

    이 조합으로

      단일 행 존재 확인은 "포함"되어 있으므로 "색인을 사용하여" 수행됩니다.
    • 또 다른 쿼리는 PK에서 함께 클러스터된 모든 관련 행을 찾습니다.
    • (PK에는 고유해야 하기 때문에 이 3개의 열이 있습니다. 이 순서로 열을 두면 두 번째 쿼리에 도움이 됩니다. 또한 INDEX가 아니라 PK이므로 인덱스 BTree의 BTree 사이에 있을 필요가 없습니다. 반송과 데이터 사이)
    • "클러스터링"
    • 은 이러한 쿼리에 필요한 디스크 블록 수를 줄여 성능을 향상시킬 수 있습니다. 이렇게 하면 buffer_pool의 "스래싱"이 줄어들어 RAM을 늘릴 필요성이 줄어듭니다.
    • 내 색인 생성 제안은 대부분 내 데이터 유형 제안과 ​​직교합니다.
    • 회신하다
      0
  • 취소회신하다