"MySQL Practical Combat 45 강의" - Study Notes 33 MySQL Server 쿼리 결과 전송 프로세스 / 대용량 데이터 쿼리가 innoDB bufferPool에 미치는 영향 / 메모리 제거 알고리즘 LRU 및 innoDB 개선 LRU

이 글은 "대형 쿼리가 메모리 부족을 일으킬까?"라는 질문을 통해 MySQL 쿼리 결과를 클라이언트에게 보내는 과정을 소개합니다. 관련된 지식 포인트는 다음과 같습니다. 클라이언트 및 데이터 전송, innoDB bufferPool, 메모리 제거 알고리즘 LRU 및 innoDB 개선 LRU에 대한 대량의 데이터 쿼리의 영향;

질문: 대규모 쿼리는 데이터베이스 호스트의 메모리를 "소진"합니까?

예: 내 호스트 메모리가 100G 밖에 없는데 이제 200G 대형 테이블 에서 전체 테이블 스캔을 수행하려고 합니다 . 데이터베이스 호스트의 메모리가 모두 사용됩니까?

—— 답은 아니오입니다 ! 예를 들어 논리적으로 백업할 때 전체 데이터베이스를 스캔하는 것 아닌가요? 따라서 큰 테이블에 대해 풀 테이블 스캔을 해도 문제가 없을 것 같은데, 그 과정은 어떤가요?

일반적인 아이디어는 이미 존재할 수 있습니다. 즉, 일괄 처리 아이디어, 여러 쿼리가 메모리에 로드된 다음 MySQL 클라이언트로 전송됩니다. 서버 계층이 이를 처리하는 방법을 살펴보겠습니다.

MySQL Server 쿼리 결과 전송 프로세스

다음과 같은 명령을 사용하여 200G InnoDB 테이블에서 전체 테이블 스캔을 수행하고 스캔 결과를 클라이언트에 저장한다고 가정합니다 .

mysql -h$host -P$port -u$user -p$pwd -e "select * from db1.t" > $target_file

InnoDB 데이터는 기본 키 인덱스에 저장되므로 전체 테이블 스캔 은 실제로 테이블 t의 기본 키 인덱스를 직접 스캔합니다 . 그런 다음 클라이언트로 반환됩니다. 그렇다면 이 " 결과 집합 " 은 어디에 있습니까 ?

——실제로 서버는 완전한 결과 집합을 저장할 필요가 없으며 데이터를 가져오고 보내는 프로세스는 다음과 같습니다.

1. 라인을 얻어서 net_buffer에 쓰고, net_buffer가 가득 찰 때까지 라인을 반복해서 얻고 네트워크 인터페이스를 호출하여 보냅니다.
2. 전송에 성공하면 net_buffer를 지우고 계속해서 다음 라인을 가져와서 씁니다. 3. 전송
하는 경우 함수가 EAGAIN 또는 WSAEWOULDBLOCK을 반환하면 로컬 네트워크 스택(소켓 전송 버퍼)이 가득 차서 전송을 계속하기 전에 네트워크 스택이 다시 쓰기 가능해질 때까지 기다린다는 의미입니다.

이 프로세스에서 다음을 확인할 수 있습니다.

(1) MySQL 질의 결과는 일괄적으로 전송되며, 일괄 처리의 크기는 net_buffer의 크기(기본값 16K)로 한번에 모든 결과를 확인하는 것이 아니라 한번에 모두 패키징하여 전송한다. 일괄 발송 됩니다 .

(2) 소켓 송신 버퍼가 가득 차 있는지 여부도 MySQL 결과 반환에 영향을 미칩니다. 즉, 클라이언트가 느리게 수신하면 결과를 보낼 수 없기 때문에 MySQL 서버가 송신 블록에 들어가 실행 시간이 길어 집니다. 이 트랜잭션 의 시간 ;

즉, MySQL은 "읽는 동안 게시"입니다. 이 개념은 매우 중요합니다! 즉, 클라이언트가 느리게 수신하면 MySQL 서버가 결과를 보낼 수 없으며 이 트랜잭션의 실행 시간이 길어집니다 .

MySQL 스레드 상태 클라이언트로 보내기&데이터 보내기

클라이언트에게 보내기

상황 시뮬레이션: 의도적으로 클라이언트가 소켓 수신 버퍼의 내용을 읽지 않도록 한 다음 결과를 보기 위해 서버에 프로세스 목록을 표시합니다.

State의 값은 항상 "Sending to client"로 서버측 네트워크 스택이 꽉 찼다는 것을 알 수 있습니다. 데이터 읽기 속도가 매우 느리면 클라이언트가 다음 데이터 행을 가져오는 데 시간이 오래 걸리고 위와 같은 상황이 발생할 수 있습니다 .

따라서 일반적인 온라인 비즈니스의 경우 쿼리가 많은 결과를 반환하지 않는 경우 mysql_store_result 인터페이스(MySQL 클라이언트에서 사용하는 기본 메서드이기도 함)를 사용하여 쿼리 결과를 로컬 메모리에 직접 저장하는 것이 좋습니다 . 클라이언트가 거의 20G의 메모리를 차지하게 하는 대규모 쿼리를 실행하는 것과 같이 비전형적인 비즈니스에 대한 쿼리 데이터의 양이 매우 크고 클라이언트의 로컬 캐시를 한 번에 저장할 수 없는 경우, 이 경우 mysql_use_result 인터페이스 대신 사용해야 합니다 .

데이터 보내기

"Sending to client"와 매우 유사해 보이는 상태는 "Sending data"이며, 실제로 MyAQL 클라이언트가 데이터를 수신하는 것과는 거의 관련이 없습니다.

실제로 쿼리 문의 상태 변경은 다음과 같습니다.

  1. MySQL 쿼리 문이 실행 단계에 들어간 후 먼저 상태를 "데이터 전송 중"으로 설정합니다.
  2. 그런 다음 실행 결과의 열 관련 정보(메타 데이터)를 클라이언트에 보냅니다.
  3. 문의 흐름을 계속 실행하십시오.
  4. 실행이 완료되면 상태를 빈 문자열로 설정합니다.

즉, "데이터 보내기"는 반드시 "데이터 보내기"를 의미하지는 않지만 실행기 프로세스의 모든 단계에 있을 수 있습니다 .

즉, "Sending to client"는 스레드가 " 클라이언트가 결과를 받기를 기다리는 중 " 상태일 때만 표시되며, "Sending data"로 표시되면 " 실행 중 " 만을 의미합니다. ;

위의 분석에서 우리는 서버 계층의 경우 쿼리 결과가 일괄적으로 클라이언트에 전송 되므로 전체 테이블을 스캔하고 쿼리에서 많은 양의 데이터를 반환해도 메모리가 폭발하지 않는다는 것을 알 수 있습니다 .

버퍼 풀로 쿼리 속도 향상

메모리의 데이터 페이지는 BufferPool에서 관리됩니다. WAL에서 BufferPool은 업데이트를 가속화하는 역할을 합니다(랜덤 스토리지 대신 리두로그를 먼저 작성하여 디스크 IO의 랜덤 쓰기 압력을 줄임) . 쿼리 속도를 높이는 것입니다 .

WAL 후에 데이터를 읽으면 디스크를 읽어야 합니까, 아니면 복귀하기 전에 리두로그에서 데이터를 업데이트해야 합니까? 사실 그럴 필요는 없습니다. 이때 최신 데이터는 메모리에 있고(메모리에 있는 데이터가 아직 있는 경우) 이전 데이터는 여전히 디스크에 있습니다. 쿼리는 "더티"를 직접 반환할 수 있습니다. 메모리의 페이지" ;

쿼리에 대한 버퍼 풀의 가속 효과는 중요한 지표에 따라 달라집니다. 메모리 적중률 ;

show engine innodb status 결과에서 시스템의 현재 BP 적중률을 확인할 수 있으며, 일반적으로 안정적인 서비스를 제공하는 온라인 시스템의 경우 응답 시간이 요구 사항을 충족하면 메모리 적중률이 99% 이상이어야 합니다.

 

예를 들어 사진의 적중률은 99.1%입니다.

쿼리에 필요한 모든 데이터 페이지를 메모리에서 직접 가져올 수 있다면 그것이 가장 좋고 해당 적중률이 100%이지만 일반적으로 메모리 크기가 메모리보다 작기 때문에 실제 프로덕션에서는 달성하기 어렵습니다. 총 데이터 크기 예, 버퍼 풀이 가득 차서 디스크에서 데이터 페이지를 읽어야 하는 경우 이전 데이터 페이지를 제거 해야 합니다 .

보충: 리두 로그 및 버퍼 변경

리두 로그와 변경 버퍼의 두 가지 개념은 실제로 혼동하기 쉽습니다.여기서 변경 버퍼의 타이밍을 추가합니다.변경 버퍼 메커니즘이 항상 적용되는 것은 아닙니다.작동할 데이터 페이지가 현재 메모리에 없을 때만, 디스크에서 읽고 먼저 로드해야 합니다. 변경 버퍼는 데이터 페이지에만 유용합니다. 그 목적은 업데이트하기 전에 디스크에서 메모리로 데이터를 읽을 필요가 없도록 하여 무작위 IO 읽기의 압력을 줄이는 것입니다. ; 데이터를 실제로 읽어야 하는 경우 IO 읽기를 트리거하고 메모리에서 병합을 완료하고 더티 페이지를 생성하며 리두 로그에 업데이트를 기록합니다(디스크를 즉시 플러시할 필요는 없지만 예약된 작업은 디스크 플러시를 담당합니다.) 자세한 내용은 이전 기사 "MySQL Practical Combat 45 강의"——Study Notes 09 "Ordinary Index 및 Unique Index, Change Buffer and Redo Log" 의 마지막 섹션을 참조하십시오. ;

메모리 제거 알고리즘 LRU 및 innoDB 개선된 LRU

InnoDB 메모리 관리는 LRU(최소 최근 사용) 알고리즘을 사용하며, 이 알고리즘의 핵심은 가장 오래 사용되지 않은 데이터를 제거하는 것입니다.

기존 LRU 알고리즘 모델

다음 그림은 전통적인 LRU 알고리즘의 기본 모델입니다.

(1) 액세스 데이터 페이지 P3, P3이 캐시에 있으므로 P3을 연결 목록의 맨 앞으로 이동 (
2) 액세스 데이터 페이지 Px, Px가 캐시에 없으므로 새 데이터 페이지를 만들어야 합니다. Buffer Pool Space for Px에 적용되나 메모리가 꽉 찼기 때문에 연결 리스트의 마지막에 있는 데이터 페이지 Pm의 메모리를 지우고 Px의 내용을 저장한 후 헤드로 이동해야 함 연결된 목록;

효과 관점에서 가장 오랫동안 액세스되지 않은 데이터 페이지 Pm은 제거됩니다 .

전체 테이블 스캔에서 InnoDB의 LRU 개선

이 알고리즘은 언뜻 보기에는 괜찮아 보이지만 전체 테이블 스캔을 고려한다면 문제가 없을까요? ——예 , 메모리의 데이터가 지속적으로 업데이트되고 전체 메모리 적중률이 낮아집니다 .

예를 들어, 이 알고리즘에 따라 200G 테이블을 스캔하려고 하는데 이 알고리즘에 따라 스캔하면 현재 BufferPool에 있는 모든 데이터가 제거되고 스캔 과정에서 액세스한 데이터 페이지의 내용에 저장됩니다. 한 번 액세스하면 메모리 공간이 부족하여 LRU에 의해 빠르게 제거됩니다 .

이는 비즈니스 서비스를 수행하는 라이브러리에 큰 영향을 미치며 BufferPool의 메모리 적중률이 급격히 떨어지고 디스크 압력이 증가하며 SQL 문의 응답이 느려지는 것을 볼 수 있습니다.

따라서 InnoDB는 이 LRU 알고리즘을 직접 사용할 수 없으며 실제로 InnoDB는 LRU 알고리즘을 개선했습니다.

InnoDB의 구현에서 전체 LRU 연결 목록은 5:3의 비율에 따라 젊은 영역과 이전 영역으로 나뉩니다 .

(1) Young 영역의 경우 메모리에 있는 기존 Page에 접근할 때 갱신 규칙은 기존 LRU 알고리즘과 동일하며 연결 ​​리스트의 맨 앞에 위치시킨다
. 현재 Linked List에 존재하지 않는 경우 여전히 Linked List의 마지막에 있는 데이터 페이지 Pm을 제거하는 것이지만 새로 삽입된 데이터 페이지 Px는 LRU_old에 위치한다; (3) 이전 영역의 데이터
페이지 접근할 때마다 다음과 같은 판단을 내려야 합니다.

a. 데이터 페이지가 LRU 목록에 1000ms 이상 존재하면 목록의 선두로 이동 b.
데이터 페이지가 LRU 목록에 1000ms 미만 존재하면 위치가 변경되지 않음;

1000ms의 시간은 innodb_old_blocks_time 매개변수에 의해 제어되며 기본값은 1000(밀리초)입니다.

이 전략은 전체 테이블 스캔과 같은 작업을 처리하도록 조정된 것으로 보입니다.

개선된 LRU 알고리즘의 작동 논리를 보기 위해 방금 스캔한 200G의 기록 데이터 테이블을 예로 들어 보겠습니다.

  1. 스캔 프로세스 중에 새로 삽입해야 하는 데이터 페이지는 먼저 이전 영역에 배치됩니다.
  2. 데이터 페이지에 여러 개의 레코드가 있으므로 이 데이터 페이지는 여러 번 액세스되지만 순차적 스캔으로 인해 동일한 페이지의 데이터 스캔 속도가 매우 빠르며 데이터 페이지에 처음 액세스하고 마지막으로 액세스합니다. 액세스 시간 액세스 간격은 1000ms를 초과하지 않으므로 이 데이터 페이지는 여전히 이전 영역에 보관됩니다.
  3. 후속 데이터를 계속 스캔하면 이전 데이터 페이지는 다시 액세스되지 않으므로 연결 목록의 헤드(즉, Young 영역)로 이동할 기회가 없으며 제한된 캐시로 인해 곧 LRU에 의해 제거됩니다. 공간;

이 전략의 가장 큰 개선점은 "최신 사용"의 의미를 재정의하는 것 입니다 . 방문 횟수의 전통적인 특성 외에도 액세스하는 데 약간 더 긴 시간이 필요하므로 이를 방지합니다. 전체 테이블 스캔의 일종 전체 캐시 브러시 오프 상황 ;

개선된 알고리즘의 가장 큰 장점은 이 큰 테이블을 스캔하는 과정에서 BufferPool도 사용되지만 Young 영역에 전혀 영향을 미치지 않아 정상적인 비즈니스에 대한 BufferPool의 쿼리 적중률을 보장한다는 것입니다.

다음 글: 미정

이 장 참고자료: 33 | 이렇게 많은 데이터를 확인하면 데이터베이스 메모리가 폭발할까?

추천

출처blog.csdn.net/minghao0508/article/details/131072552