분산 트랜잭션과 합의 - 현실의 분산 트랜잭션

Neo·2023년 5월 11일
1

현실의 분산 트랜잭션

  • 2단계 커밋으로 구현된 분산 트랜잭션에 대한 평판은 엇갈림

  • 한 가지 예로 MySQL의 분산 트랜잭션은 단일 노드 트랜잭션보다 10배 이상 느리다고 보고됨

  • 한편에서는 다른 방법으로 달성하기 어려운 중요한 안전성 보장을 제공하는 것으로 평가받음

  • 분산 트랜잭션의 두 가지 종류

    • 데이터베이스 내부 분산 트랜잭션

      • 어떤 분산 데이터베이스는 데이터베이스 노드 사이에 내부 트랜잭션을 지원함. 이 경우 트랜잭션에 참여하는 모든 노드는 동일한 데이터베이스 소프트웨어를 실행
    • 이종 분산 트랜잭션

      • 이종(heterogeneous) 트랜잭션의 참여자들은 둘 혹은 그 이상의 다른 기술을 사용함. 이를테면 두 가지 서로 다른 벤더의 데이터 베이스일 수도, 메시지 브로커처럼 비데이터베이스 시스템일 수 도 있음. 이런 시스템에 걸친 분산 트랜잭션은 시스템의 내부가 완전히 다르더라도 원자적 커밋을 보장해야 함
  • 당연한 말이지만, 데이터베이스 내부 분산 트랜잭션은 최적화를 적용하기에 쉽지만, 이종 기술에 걸친 트랜잭션은 그렇지 않은 경우가 많음

정확히 한 번 메시지 처리

  • 이종 분산 트랜잭션은 다양한 시스템들이 통합될 수 있도록 함

  • 예를 들어 메시지 큐에서 메시지 확인과 데이터베이스 쓰기를 단일 트랜잭션에서 원자적으로 커밋함으로서 구현할 수 있음

  • 분산 트랜잭션이 지원되면 메시지 브로커와 데이터베이스가 서로 다른 장비에서 실행되는 두 가지 무관한 기술이더라도 이것을 가능하게 해줌

  • 따라서, 메시지와 그 처리 과정을 원자적으로 커밋한다면 메시지가 결과적으로(effectively) 정확히 한 번(exactly once) 처리되도록 보장하는 것이 가능함

  • 하지만 이런 분산 트랜잭션은 트랜잭션의 영향을 받는 모든 시스템이 동일한 원자적 커밋 프로토콜을 사용할 수 있을 때만 가능함

XA 트랜잭션

  • X/Open XA(eXtended Architecture의 약자)는 이종 기술에 걸친 2단계 커밋을 구현하는 표준

  • XA는 네트워크 프로토콜이 아니며, 트랜잭션 코디네이터와 연결되는 인터페이스를 제공하는 CAPI일 뿐임

  • JAVA에서는 자바 트랜잭션 API(JTA)를 사용해 구현되며, JTA는 다시 JDBC를 사용하는 데이터베이스용 드라이버 다수와 자바 메시지 서비스(JMS) API를 사용하는 메시지 브로커용 드라이버에서 지원됨

  • 트랜잭션 코디네이터는 XA API를 구현, 흔히 트랜잭션을 시작하는 애플리케이션과 같은 프로세스에 로딩되는 라이브러리로서 작동함

  • XA도 마찬가지로 코디네이터가 죽으면 트랜잭션들은 코디네이터가 복구되기를 기다려야하며, 코디네이터 라이브러리는 로그를 읽어 각 트랜잭션의 커밋/어보트 결과를 복구함

  • 이후, 코디네이터는 데이터베이스 드라이버의 XA 콜백을 사용해 참여자들에게 적절히 커밋하거나 어보트하라고 요청

  • 즉 모든 통신은 클라이언트 라이브러리를 거쳐야 하므로 데이터베이스 서버는 코디네이터에 직접 연결할 수 없음

의심스러운 상태와 잠금을 유지하는 문제

  • 트랜잭션이 의심스러운 상태(코디네이터의 죽음)를 왜 그렇게 신경쓰는 걸까?

  • 시스템의 나머지 부분은 그냥 자기 일을 하고 결국엔 정리될 의심스러운 트랜잭션을 무시할 수는 없을까?

  • 그 이유는 잠금과 관련되어 있ㅅ음. 즉 커밋 후 읽기에서 설명했듯이 보통 트랜잭션은 더티 쓰기를 막기 위해 로우 수준의 독점적인 잠금을 획득하며, 직렬성 격리를 원한다면 2단계 잠금을 사용하는 데이터베이스는 트랜잭션에서 읽은 로우에도 공유 잠금을 획득해야 함

  • 데이터베이스는 트랜잭션이 커밋하거나 어보트되기 전까지 이런 잠금을 해제할 수 없으므로 2단계 커밋을 사용할 때 트랜잭션은 의심스러운 상태에 있는 동안 내내 잠금을 유지하고 있어야 함

  • 따라서 다른 트랜잭션이 그냥 자기 일을 계속하는 것은 불가능함

코디네이터의 장애 복구

  • 이론상으로는 코디네이터가 죽은 후 재시작하면 로그를 통해 그 상태를 깨끗하게 복구하고 의심스러운 트랜잭션을 해소하게 됨

  • 하지만 현실에서는 고아가 된 의심스러운 트랜잭션, 즉 코디네이터가 어떤 이유 때문인지 그 결과를 결정할 수 없는 트랜잭션이 생길 수 있음

  • 이런 고아가된 의심스러운 트랜잭션이 존재한다면, 데이터베이스는 해당 로우를 영원히 잠그고 있는 상황에 직면할 수 있음

  • 이를 위해 XA 구현에는 고아가된 의심스러운 트랜잭션을 커밋할지 어보트할지 일방적으로 결정할 수 있도록 하는 경험적 결정(heuristic decision)을 제공함

  • 하지만 경험적 결정은 2단계 커밋의 약속 체계를 위반할 수 있기 때문에 원자성을 깨트릴 수도 있음

분산 트랜잭션의 제약

  • XA 트랜잭션은 운영상 제약으로 작용할 수 있음. 특히 핵심 구현은 트랜잭션 코디네이터(트랜잭션 결과를 저장할 수 있는) 자체가 일종의 데이터베이스여야 한다는 점임
  1. 코디네이터가 복제되지 않고 단일 장비에서 실행되면 전체 시스템의 SPOF가 될 수 있음

  2. 여러 애플리케이션은 모든 영속적인 상태를 데이터베이스에 저장하고 상태 비저장 모드로 개발됨. 하지만, 코디네이터가 서버의 일부가 되면 코디네이터의 로그가 지속적인 시스템 상태의 중요한 부분이되므로 상태 비저장 모드가 아니게 됨

  3. XA는 광범위한 데이터 시스템과 호환돼야 하므로 최소 공통 분모가 될 필요가 있음. 예를 들어 여러 시스템에 걸친 교착상태를 감지할 수 없고(그렇게 하려면 시스템이 각 트랜잭션이 대기 중인 잠금에 대한 정보를 교환해야 함), SSI(직렬성 스냅숏 격리)와 함께 동작하지 않음

  4. (XA가 아닌)데이터베이스 내부 분산 트랜잭션은 그 제한이 그리 크지 않음. 예를 들어 분산 버전 SSI를 쓰면 됨. 하지만 2PC가 성공적으로 트랜잭션을 커밋하려면 모든 참여자가 응답해야한다는 문제가 남음. 결과적으로는 시스템의 어떤 부분이라도 고장나면 트랜잭션도 실패하며, 이는 내결함성을 지닌 시스템 구축의 실패를 야기함

  • 그렇다면, 여러 시스템이 서로 일관성을 유지하게 만들려는 것은 포기해야할까?

  • 이종 분산 트랜잭션의 고통 없이 같은 것을 달성하게하는 대안 방법이 있고, 이는 추후에 살펴볼 예정

0개의 댓글