2015년 7월 16일 목요일


아래 글은  MySQL Server Blog에 Annamalai Gurusami님이 게시한 The InnoDB Change  Buffer 원문을 한국 MySQL 사용자 그룹 (MySQL Korea User Group) 멤버들(이의준, 이정아, 김영하, 류수미)이 번역한 내용입니다. 번역내용 중 이상한부분이 있으시면 덧글로 알려주시기 바랍니다. 번역자들이 전문 번역가가 아니다보니 매끄럽지 못하거나 오역이 있을 수 있습니다. 참고해서 읽어주시면 감사하겠습니다.
해당 내용의 저작권은 당연히 원문 게시자에게 있으며, 원문 작성자가 해당 글의 게시에 대해 문제가 있다고 하시면 내리겠습니다.
출처 : http://mysqlserverteam.com/the-innodb-change-buffer/

InnoDB Change Buffer

 스토리지 엔진 설계의 도전과제중 하나는 쓰기 연산(write operation) 중의 랜덤 I/O입니다. InnoDB에서 테이블은 한개의 클러스터된 인덱스와 0개 이상의 세컨드리 인덱스들을 가지고 있습니다. 이 인덱스들 각각은 B-tree입니다. 레코드가 테이블에 입력이 될 때, 그 레코드는 먼저 클러스터된 인덱스에 삽입되고, 그후 세컨드리 인덱스들에 삽입됩니다. 그래서, I/O의 결과들은 랜덤하게 디스크에 분산이 됩니다. 그 I/O 패턴은 수정(update) 및 삭제 연산(delete operation)들에서도 유사하게 랜덤합니다. 이 문제를 완화하기위해, InnoDB 스토리지 엔진은 change buffer 라고 불리는 특별한 데이터 구조를 사용합니다. (이전에는_insert buffer_라고 알려진, 여러 내부 이름들에서 사용되어진 ibuf와 IBUF을 여러분들은 볼 수 있을 겁니다.)
change buffer는 어떠한 세컨드리 인덱스의 레코드를 유지할 수 있는 능력을 가진 또다른 B-tree입니다. 그것은 또 소스 코드에서는 universal tree로 언급됩니다. InnoDB에서는 오직 하나의 change buffer가 있고, 시스템 테이블스페이스에서 유지됩니다 .이 change buffer의 root 페이지는 (space id가 0인) 시스템 테이블스페이스 안의 FSP_IBUF_TREE_ROOT_PAGE_NO (4와 같습니다)에 고정되어 있습니다. 더 자세한 사항은 ibuf_init_at_db_start()를 참조할 수 있습니다.
change buffer의 총 크기는 설정가능하며 전체 change buffer tree가 메인 메모리에서 상주할 수 있게 설계되어 있습니다. change buffer의 크기는 innodb_change_buffer_max_size 시스템 변수를 사용하여 설정됩니다.

Overview of Change Buffering (Change 버퍼링의 개략적인 소개)

change buffering은 오직 non-unique secondary index(NUSI)들에 적용할 수 있습니다. InnoDB는 NUSI에 대해 3가지 유형의 연산(insert, delete marking 그리고 delete)을 버퍼로 처리합니다. 이들 연산들은 InnoDB에서 ibufopt로 열거됩니다:

trans_01.jpeg
기억해야 할 한가지 중요한 점은 change buffering은 leaf page지향적이라는 것입니다. NUSI와 관련된 non-root leaf page가 버퍼 풀에서 이용 가능하지 않는 경우에만 NUSI에 대한 특별한 연산이 change buffer에 버퍼링됩니다. 이는 버퍼된 변경이 InnoDB 시스템에 있는 NUSI의 특정 leaf page에서 발생된 것을 미리 정의되어 있다는 것을 의미합니다. 이 추적은 NUSI leaf page에 그러한 버퍼된 연산들을 합치는 것이 B-tree page 분할이나 B-tree page 병합이라는 결과를 낳지않아야하기 때문에 필요합니다.

역자 주: leaf는  B tree에서  중간 노드가 아니라 맨 마지막에 위치하는 노드입니다. 자세한 사항은 B-tree 내용을 확인하세요.
https://en.wikipedia.org/wiki/B-tree

Special Change Buffer Fields (Change Bugger의특별한 필드들)

NUSI 레코드가 change buffer에 버퍼될 때, 4개의 특별한 change buffer 필드가 NUSI 레코드의 첫부분에 추가됩니다. 그 4개의 필드들과 그 값들은 아래에 설명되어 있습니다. change buffer 트리의 프라이머리키는 {space_id, page_no, count} 이며, count는 변경이 특정 page에 대해 버퍼되는 순서를 유지하는 것을 도와줍니다. change buffer row 형식은 일정기간을 거쳐 계속 발전되어 왔습니다. 그리고 아래의 테이블에서는 MySQL 5.5+에 대한 정보를 제공합니다.
trans_02.jpeg
change buffer 레코드들 자체의 row 형식은 항상 REDUNDANT 입니다.
역자 주: 예) ROW_FORMAT=REDUNDANT

Change Buffer Bitmap Page

각 페이지에 대한 여유 공간 정보는 change buffer bitmap 페이지 (또는 ibuf bitmap page 라고 알려진)라고 불리는 미리 정의된 페이지에서 추적됩니다. 이들 페이지들은 항상 extent descriptor page들을 따릅니다. chagne buffer bitmap page들의 미리 정의된 페이지 번호는 아래의 테이블에서 볼 수 있습니다.
a01.jpg
페이지 번호 1은 또한 FSP_IBUF_BITMAP_OFFSET으로 참조됩니다. 이들 change buffer bitmap page들은 다음 질문들에 대한 답변을 얻는데 도움을 줄 것입니다.
  • 주어진 페이지는 ibuf (insert/change buffer) 트리에 버퍼된 변경(buffered change)들을 가지고 있는지? 이 질문은 페이지가 buffer pool에서 읽어질 때 제기됩니다. 버퍼된된 변경들은 buffer pool에 넣어지기 전에 실제 페이지로 병합됩니다.
  • 주어진 페이지가 충분한 여유 공간(free space)을 가지고 있어서 변경이 버퍼될 수 있습니까? 이 질문은 우리가 NUSI의 leaf page를 변경하기를 원하는데 buffer pool에서는 이미 이용 가능하지 않을 때 제기됩니다.
다음 섹션에서 우리는 위에 나온 문제들을 해결하는데 도움이 되는 ibuf bitmap page에 저장된 정보를 알아 볼 것입니다.

Information Stored in Change Buffer Bitmap Page (Change Buffer Bitmap Page에 저장되는 정보)

change buffer bitmap page는 각 페이지를 설명하기 위해 4 비트 (IBUF_BITS_PER_PAGE)를 사용합니다. 그것은 각 페이지들을 설명하는 4 비트의 배열로 되어 있습니다. 이 전체 배열은 "ibuf bitmap" (insert/change buffer bitmap)이라고 불립니다. 이 배열은 IBUF_BITMAP( 94와 같은)과 같은 오프셋에 있는 페이지 헤더 다음에 시작됩니다. 페이지 번호가 주어지면, 그 주어진 페이지에 4비트 정보를 가지고 있는 ibuf bitmap page는 다음처럼 계산될 수 있습니다:
ulint bitmap_page_no = FSP_IBUF_BITMAP_OFFSET + ((page_no / page_size) * page_size); .
전체 계산에 대한 자세한 사항은 ibuf_bitmap_page_no_calc() 함수를 참고할 수 있습니다. 이처럼 페이지 번호가 주어지면, change buffer bitmap page 안의 오프셋은 계산을 위해 쉽게 사용될 수 있는 4 비트를 가지고 있습니다. 저는 이 글을 읽고 있는 분들에게 연습으로써 이것을 남겨둡니다 (자세한 사항은 ibuf_bitmap_page_get_bits_low() 함수 참조). 4비트에 대한 자세한 사항은 아래 테이블을 참고하세요.
a02.jpg
이는 각 페이지의 여유 공간(free space) 정보를 저장하기 위해 오직 2 비트가 사용 가능하다는 것을 의미합니다. 오직 4개의 가능한 값이 있습니다: 0, 1, 2, 3. 2개의 비트를 사용하여, 우리는 페이지에 대한 여유 공간 정보를 인코딩하려고 합니다. 규칙은 다음과 같습니다 - 사용될 change buffer를 위한 최소한 UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE 바이트의 여유 공간이 있어야 합니다.
a03.jpg

 

Tracking Free Space of Pages (여유공간 추적)

삽입 연산(insert operation- IBUF_OP_INSERT)이 버퍼되기 전에, 대상 NUSI leaf page 안의 사용가능한 여유 공간은 change buffer bitmap page내의 가능한 정보를 이용해 대략 계산되어 집니다. 이 변환은 ibuf_index_page_calc_free_from_bits() 함수에서 이루어집니다. 공식은 다음과 같습니다.



다음 표는 change buffer bitmap page 안에서 찾은 인코드된 값에서 바이트별로 의미있는 값으로의 변환을 제공합니다.
역자주 : change buffer bitmap page 에서인코드된 값(0, 1, 2, 3)은 NUSI LEAF PAGE 에서 가용여유공간에 대한 정보를 나타내줍니다. 즉, 1이라면 page size가 4 KB라고 가정했을때 128 byte의 여유공간이 있음을 의미합니다.
trans_04.jpeg
이 정보를 이용해 버퍼 될 레코드를 페이지에 넣을 수 있는지 없는지를 확인할 수 있습니다. 만약 충분한 공간이 있다면 삽입은 버퍼 될 수 있습니다. 이런 접근을 이용하는 것은, 타겟 NUSI에 이런 레코드가 병합하는 것이 페이지 분할을 야기하지는 않을 것이다라는 것을 보장합니다.

Updating Free Space Information (여유공간 정보 갱신)

삽입 또는 삭제 연산을 버퍼링한 후에, buffer bitmap page안의 여유 공간 정보는 그에 맞게 업데이트 되어져야만 합니다. (삭제 마크 연산은 여유 공간 정보를 바꾸지 않습니다)
여유공간정보를 갱신 하기위해 우리는 여유 공간을 IBUF로 엔코드된 값을 바이트로 바꾸는 것이 필요합니다.
trans_05.jpeg
위의 계산에서, max_ins_size는 페이지를 재조직한 후에 페이지 안에서 사용할 수 있는 최대 삽입사이즈 (최대 여유 공간) 입니다.

Record Count in NUSI Leaf Pages (NUSI Leaf Page들의 레코드 수)

purge 작업의 경우(IBUF_OP_DELETE), InnoDB에서 B-tree의 leaf page는 비워질 수 없기 때문에 대상 NUSI leaf page 안에 최소한 1개의 레코드는 반드시 있어야 합니다. 대상 NUSI page의 레코드 수는 알 수 없기 때문에(buffer pool에 아직 적재되지 않았기 때문에) 이후 버퍼된 삽입 연산은 계산되어 집니다. 첫 번째 삽입 연산이 버퍼되었다면 대상 NUSI page는 1개의 레코드로 추정할 것입니다. 그리고 두 번째 삽입 연산이 버퍼되었다면 대상 NUSI page는 2개의 레코드로 추정하는 식일 것입니다. 이런 식으로 대상 NUSI page의 레코드의 수가 계산됩니다. 이 recode count 계산하는 법에 근거하여, purge 작업은 버퍼된 것이거나 그렇지 않은 것입니다. 이 말인 즉슨, 삽입 혹은 삭제표시 연산은 버퍼된 것이 없다면 purge 작업은 버퍼될 수 없습니다.

Merging the Buffered Changes to Target NUSI Page (버퍼된 변경들의 대상 NUSI Page로 병합)

NUSI leaf page들에서 변경사항은 buffer pool에 NUSI leaf page가 이미 존재 하지 않다면 change buffer에 버퍼됩니다. 이런 버퍼된 연산들은 다양한 상황 속에서 실제 NUSI leaf page로 다시 병합됩니다.
  1. 이러한 NUSI leaf pages는 나중에 buffer pool 로 읽어질 때, 버퍼된 연산은 병합될 것입니다. NUSI leaf page는 인덱스 스캔 또는 사전 읽기 때문에 인덱스 색인을 하는 동안 buffer pool로 읽어 질 수 있습니다.
  2. InnoDB의 마스터 쓰레드가 주기적으로 ibuf_merge_in_background() 의 콜에 의해서 change buffer를 병합됩니다.
  3. 특정 NUSI leaf page에 너무 많은 연산들이 있을 때 버퍼됩니다.
  4. change buffer tree 최대 허용된 사이즈에 도달 했을 때 쓰입니다.
change buffer 병합 연산은 ibuf_merge_in_background() 또는 ibuf_contract() 함수 콜에 의해서 시작됩니다. change buffer 병합은 backgraound 나 foregraound에서 수행됩니다. foreground change buffer 병합은 DML 연산의 일부분으로 수행되고 이런 이유로 일반사용자가 느끼는 성능에 영향을 미칠 것입니다. background change buffer 병합은 서버의 사용량이 적을 때 주기적으로 수행 됩니다.
우리는 change buffer 병합은 B-tree 페이지 분할 또는 페이지 병합연산을 야기하지 않도록 확실하게 합니다. 또한 빈 leaf page를 발생하지 않도록 해야 합니다. 대상 NUSI leaf page는 buffer pool에 놓이기 전에 버퍼된 변경은 NUSI leaf page에 적용됩니다. 버퍼된 변경들이 페이지에 병합되면 change buffer bitmap page에 할당된 4비트 정보 또한 갱신됩니다.

Conclusion (결론)

이 기사는 InnoDB의 change buffer subsystem의 오버뷰를 제공했습니다. change buffer 안에 저장하기 전에 secondary 인덱스 레코드에 추가되는 추가적인 필드들을 설명했습니다. change buffer는 change buffer bitmap page의 사용함으로 NUSI leaf pages의 여유 공간 정보의 추적을 어떻게 유지할지에 관한 정보를 제공했습니다.
또한 빈 공간이 되지 않기 위해서 NUSI leaf pages안의 레코드의 수를 계산할 필요성을 설명하였습니다.
이 문서를 검토하고 좀 더 정확하게 할 수 있도록 도와준 Marko Makela에게 감사합니다. 문의사항이 있으면, 아래 코멘트를 게시해주시기를 바랍니다.
여기까지입니다. MySQL을 사용해주셔서 감사합니다!

참고 자료 : http://dev.mysql.com/doc/refman/5.6/en/innodb-performance-change_buffering.html

P.S. 업무로 바쁘신 와중에 번역에 참여해주신 이의준님, 이정아님,김영하님 정말 감사합니다.

댓글 없음:

댓글 쓰기