MySQL의 Undo Log는 레코드의 변경 사항만 저장할까? 아니면 변경된 레코드 전체를 저장할까?

초코칩
2024년 02월 08일 12:20

의문
MySQL에서 Undo Log는 트랜잭션에서 트랜잭션의 롤백 대비용이면서도, 트랜잭션의 격리 수준을 유지하면서 높은 동시성을 제공하는 역할을 한다. 트랜잭션의 격리 수준이라는 개념이 있는데, 이는 동시에 여러 트랜잭션이 데이터를 변경하거나 조회할 때 한 트랜잭션의 작업 내용이 다른 트랜잭션에 어떻게 보일지를 결정하는 기준이다.
RealMySQL 8.0 1권에서 MySQL 아키텍처 부분에서 Undo Log 파트를 읽다가 의문이 들었다. 아래는 Undo Log에서 UPDATE 쿼리가 실행되고 나서의 Undo Log 변화 모습이다.
위 그림에서는 변경 전 값만 Undo Log로 복사한다고 적혀있다. 그러면 자신보다 트랜잭션 번호가 낮은 트랜잭션에게는 Undo Log의 정보와 InnoDB 버퍼 풀의 정보를 병합해서 반환하는 것일까? 차라리 ‘Undo Log에 변경된 레코드 전체를 저장하는 방식이 더 효율적이지 않을까’라는 의문이 생겼다.
이에 대한 의문을 구글에 찾아보기도 하고 지인들에게 물어봤지만, 명확한 답변을 얻기 힘들었다. 확실한 답변을 위해 직접 MySQL이 구현되어 있는 레포를 찾아봤다.
Undo Log 찾아보기
Undo Log의 정체를 찾아보기 위해, 트랜잭션 커밋 관련 함수를 먼저 찾아봤다.
1. 트랜잭션 커밋하는 함수 확인하기.
트랜잭션을 커밋하는 함수를 trx/trx0trx.cc
파일에서 찾아볼 수 있다.
trx_commit_for_mysql(trx_t *trx)
함수는 MySQL에 트랜잭션을 커밋하는 함수이며, 매개변수로 trx_t
구조체의 트랜잭션 포인터를 입력 받고 반환값으로 트랜잭션 처리 상태(dberr_t
) Enum 값을 반환한다.
우리가 주목해야할 부분은 2466번째 라인이다.
이느 GTID 지속성을 위해 undo 세그먼트를 업데이트하는 부분이다. trx_undo_gtid_add_update_undo()
함수에 대해 알아보자.
https://github.com/mysql/mysql-server/blob/trunk/storage/innobase/trx/trx0trx.cc#L2434
2. Undo 세그먼트 업데이트하는 함수 확인하기.
이제부터 trx/trx0undo.cc
파일에 있는 함수를 다룬다. 트랜잭션과 관련된 언두 로그 함수들을 모아놓은 파일이다.
trx_undo_gtid_add_update_undo()
함수는 GTID(Globally Unique Transaction ID)와 관련된 Undo 세그먼트를 처리하는 함수다. 만약 현재 트랜잭션이 Insert Only인지 또는 명시적으로 GTID를 지속하도록 요청된 경우, Mutex 락을 걸고 Update Undo 세그먼트를 할당하려고 시도한다.
우리가 주목해야할 부분은 612번째 라인이다. trx_undo_assign_undo()
에 대해 알아보자.
https://github.com/mysql/mysql-server/blob/trunk/storage/innobase/trx/trx0undo.cc#L584
3. Undo 로그 할당/재사용하는 함수 확인하기.
trx_undo_assign_undo()
함수는 트랜잭션에 대한 undo 로그를 할당하거나 재사용하는 역할을 한다. 매개변수는 다음과 같다.
trx
: 트랜잭션에 대한 정보를 담고 있는 구조체다.undo_ptr
: 롤백 세그먼트(rollback segment)에서 할당받을 undo 로그의 포인터를 나타내는 구조체다.type
: 수행 중인 작업이TRX_UNDO_INSERT
또는TRX_UNDO_UPDATE
중 어떤 것인지를 나타내는 값으로 삽입인지 갱신인지 나타낸다.
우리가 주목해야할 부분은 1755번째 라인부터 1771번째 라인까지이다. 이 부분부터 Undo log를 다룬다.
trx_undo_create()
함수를 호출하여 새로운 undo 로그를 생성하고, 생성된 undo 로그에 대한 포인터를 undo 변수에 저장한다. 그 후, 삽입 작업(TRX_UNDO_INSERT
)인지 업데이트 작업(TRX_UNDO_UPDATE
)인지에 따라 적절한 리스트에 undo 로그를 추가하는 역할을 한다.
https://github.com/mysql/mysql-server/blob/trunk/storage/innobase/trx/trx0undo.cc#L1687
4. 트랜잭션 undo 로그 생성하는 함수 확인하기.
트랜잭션에 대한 undo 로그를 생성하는 함수인 trx_undo_create()
함수이다. 트랜잭션과 관련된 여러 정보를 매개변수로 받는다. 그 중에서 trx_undo_t
의 자료형을 갖는 **undo
변수는 undo log를 나타낸다고 한다. trx_undo_t
를 확인해보자.
5. 트랜잭션의 Undo Log를 저장하는 구조체 알아보기.
trx_undo_t
구조체는 트랜잭션의 Undo 로그를 관리하는 데 사용된다. 내부 필드로는 type
, state
, del_marks
, trx_id
, xid
, flag
, m_gtid_storage
등이 있다. 이러한 정보들은 트랜잭션의 상태, 타입, 삭제 마크 여부, 트랜잭션 ID, XA 트랜잭션 ID, 플래그, GTID 저장 여부 등을 나타낸다. 이러한 구조체들은 Linked List 형태로 이루어져 있다.
이 중에서 top_page_no
와 top_offset
필드는 Undo 레코드의 주소를 나타낸다. 이 두 필드는 Undo 로그에서 가장 최근에 추가된 Undo 레코드의 위치를 나타내고, 해당 레코드의 주소를 찾을 수 있다. top_page_no
필드는 Undo 로그의 페이지 번호를 나타내며, 최근에 추가된 Undo 레코드가 위치한 페이지의 번호를 가리켜 해당 페이지에 액세스할 수 있다. top_offset
필드는 최근에 추가된 Undo 레코드의 페이지 내 오프셋(Offset)을 나타냅니다. 페이지 내에서 Undo 레코드의 위치를 찾을 때 사용된다.
https://github.com/mysql/mysql-server/blob/trunk/storage/innobase/include/trx0undo.h#L338
여기서 명확하게 알 수 있는 점은 Undo Log가 레코드 단위로 저장된다는 것이다.
기타
GTID
GTID는 Global Transaction Identifiers의 약자로 전역으로 트랜잭션을 구별할 수 있는 식별자이다. GTID를 사용하게 되면, 각각의 트렌젝션들은 고유한 전역식별자를 갖게 된다master 서버에서 수행된(commited) 트랜젝션들이 slave 서버(들)에 적용되어 지는 것에대한 추적 또한 쉽다.
TID가 존재하는데, GTID가 필요한 이유는 분산 환경에서의 유일성과 다중 마스터-레플리카에서의 일관성이다.
분산 환경에서의 유일성
TID는 주로 단일 데이터베이스 서버에서 트랜잭션을 식별하는 데 사용된다. 그러나 여러 서버 간에 데이터베이스가 분산되어 있는 경우, TID만으로는 각 서버 간에 트랜잭션을 고유하게 식별하기 어려울 수 있다. 이런 경우 GTID를 사용하여 전역적으로 트랜잭션을 식별함으로써 분산 환경에서 트랜잭션의 유일성을 보장할 수 있다.
다중 마스터-레플리카의 일관성
다중 마스터 레플리케이션 환경에서는 여러 서버 간에 트랜잭션을 동기화해야 한다. GTID를 사용하면 어떤 서버에서 트랜잭션이 발생했는지를 명확하게 식별할 수 있어, 일관성을 유지하는 데 도움을 준다.
mtr
mini-transaction은 InnoDB 처리의 내부 단계 중 하나로, DML(데이터 조작 언어) 작업 중에 내부 데이터 구조에 물리적인 수준의 변경을 가할 때 사용된다. mini-transaction은 롤백의 개념이 없는 내부 처리 단계로, 단일 트랜잭션 내에서 여러 개의 mini-transaction이 발생할 수 있다. mini-transaction은 충돌 복구 시 사용되는 redo 로그에 정보를 기록합니다. 또한 백그라운드 스레드에 의한 purge processing 중에 일반 트랜잭션의 컨텍스트 외부에서도 발생할 수 있다.
기본적으로 mini-transaction은 InnoDB 엔진이 데이터를 물리적으로 조작하고 변경할 때 발생하는 내부적인 작업 단위로 이해할 수 있다. 이러한 작업은 특히 데이터 조작 작업이 수행될 때 레코드를 수정하고, 그 변경 내용을 redo 로그에 기록하여 시스템이 중단되었을 때 복구할 수 있도록 한다. mini-transaction은 각각의 작은 단위로 동작하며, 트랜잭션의 컨텍스트 내부 또는 외부에서 다양한 작업에 사용될 수 있다.