스키마 레지스트리 : 필요성과 개요

    스키마의 필요성

    나 혼자 DB를 개발하고, DB에서 나온 데이터로 무언가를 만든다고 가정해보자. 이럴 때는 내가 정한대로 할 수 있기 때문에 스키마 변경을 아주 쉽게 할 수 있다. 그렇지만 회사에서 큰 시스템을 만들 때는 그렇게 할 수 없다. 데이터를 공급하는 사람과 데이터를 가져가는 사람이 다를 수 있고, 이런 경우에는 스키마 변경에 대해 협의를 해야한다. 회사가 커지면 커질수록 하나의 스키마에 대해서 관련된 사람이 많을 수 있고 더 많은 협의가 필요해진다.

    공급하는 쪽도 마찬가지다. 공급하는 쪽에 새로운 사람이 와서 스키마에 맞지 않는 데이터가 들어간다고 가정해보자. 그러면 메세지를 읽는 쪽에서 파싱을 할 때 큰 에러가 발생해서 시스템 전체가 다운되는 문제가 발생할 수 있다. 이런 이유들 때문에 데이터의 중앙집중보관소 역할을 하는 카프카는 하나의 공통된 규약으로 데이터를 넣고 읽는 것을 보장하기 위해 스키마가 필요하다. 


    스키마리스의 문제점

    최근 들어 오픈 소스 분산 DB 아키텍쳐가 많이 사용된다. 주로 Key - Value 형식으로 데이터를 저장하는 Redis 같은 것들이 많이 등장했다. 이런 녀석들은 아래와 같은 특성을 주로 보인다고 한다. 

    • 분산 시, 네트워크 이동을 줄이고 효율적인 데이터 저장 / 조회를 하도록 Key - Value 저장소 형태로 초기 발전함.
    • 이 경우, 스키마가 없는 데이터 저장구조를 주로 사용함.  (Key - Value 형태)

    스키마가 없기 때문에 자유롭게 넣을 수 있다. 그래서 데이터를 넣는 사람 입장에서는 자유롭게 넣을 수 있지만, 가져다 쓰는 사람 입장에서는 문제가 될 수 있다. 왜냐하면 데이터 타입이 너무 자유롭기 때문에 데이터를 신뢰할 수 없고 파싱에 많은 시간을 써야하기 때문이다. 

     


    스키마 레지스트리 개요 

    DB에서는 DB Table 자체가 스키마 역할을 하고 있다. 그렇지만 메세지를 직렬화해서 저장하는 카프카에는 스키마가 따로 존재하지 않는다. 이런 특성을 가진 카프카에서 스키마를 잘 활용하기 위해서는 스키마 레지스트리라는 별도의 어플리케이션을 사용하는 것이 좋다. 

    스키마 레지스트리는 카프카의 프로듀서, 컨슈머와 직접 통신할 수 있고 다음과 같은 역할을 한다.

    • 프로듀서는 메세지를 보내기 전에 스키마 레지스트리에 저장된 스키마인지 확인한다.
      • 저장된 스키마라면 스키마 ID를 받아와서 저장한다.
      • 저장되지 않은 스키마라면 스키마 레지스트리에서 등록하고 스키마 ID를 받아와서 저장한다.
      • 스키마 ID + 메세지를 카프카로 보낸다. 
    • 컨슈머는 메세지를 읽어왔을 때, 스키마 ID로 스키마 레지스트리에 저장된 스키마인지 확인한다.
      • 저장된 스키마라면 스키마 ID를 받아와서 저장한다. 
      • 저장되지 않은 스키마라면 에러를 발생시킨다. 

    스키마 레지스트리는 스키마 정보를 등록하기 위해 대표적으로 Avro, Json, Protobuf 타입을 지원한다. 


    Avro의 필요성

    여기서는 Avro와 스키마 레지스트리의 필요성을 복합적으로 함께 알아보고자 한다. 

    오픈 소스 데이터 부호화 (포멧팅 + 직렬화)

    분산 DB에서 가장 중요한 것은 각 노드끼리 데이터를 주고 받을 때, 그 데이터의 크기가 충분히 작아야 한다는 점이다. 그렇지 않을 경우 분산 DB의 노드 갯수가 증가해도 분산 DB의 성능이 선형적으로 증가하지 않는다. 이 부분이 큰 모래주머니가 되어서 성능 향상에 걸림돌이 된다. 

    종합해보면 데이터를 전송할 때는 다음 특성을 만족해야한다. 

    1. 효율적인 데이터 압축이 필요함. 
    2. 스키마 정의 / 변경에 대응하기 위한 파일 포멧이 필요함 

    위의 특성을 만족하는 여러가지 오픈 소스 데이터 형식들이 나왔는데, Avro는 그 중의 하나다. 

     

    Avro의 형태와 장점

    Avro는 각 메세지마다 Schema / Payload를 가진다. 아래 이미지를 보면 Avro가 어떤 형태의 메세지인지를 더욱 잘 알 수 있다. 

    Avro는 Schema + Payload를 가진다. 이 때, Avro Payload는 이진 포멧 형태로 압축된다. 위의 이미지에서 JSON과 비교해보면 Payload 자체는 74바이트에서 34바이트로 줄어드는 것을 볼 수 있다. 그런데 Avro의 한 가지 문제점은 모든 Avro 메세지마다 스키마가 포함되고, 이 스키마의 크기가 Payload보다 더 크다는 점이다. 스키마의 데이터 크기만 해결하다면 JSON보다 더 작은 용량으로 데이터를 보낼 수 있게 된다. 

    스키마의 데이터 크기를 해결하기 위해 스키마 레지스트리가 추가된다. 스키마 레지스트리는 스키마를 저장하고 프로듀서나 컨슈머에게 필요한 스키마 ID만 전달해준다. 스키마 ID를 전달받은 프로듀서는 스키마 대신에 스키마 ID를 넣어서 카프카에 전송한다. 74 바이트짜리 데이터가 38바이트로 줄어들어서 전송되는 것이다. 위와 같은 이유 때문에 JSON 대신 Avro와 스키마 레지스트리를 함께 사용하기도 한다.


    스키마 레지스트리 동작 방식

    스키마 레지스트리의 동작 방식은 다음과 같다.

    1. 프로듀서는 KafkaAvroSerializer를 이용해서 프로듀서 로컬 캐시에 현재 메세지에 맞는 스키마가 있는지 확인한다. 만약 스키마가 확인되지 않으면, AvroProducer는 스키마 레지스트리에게 스키마 등록 요청을 보낸다.
    2. 스키마 레지스트리는 요청 받은 스키마가 카프카 내부 토픽(_schemas)에 저장된 스키마와 동일한 스키마인지 확인한다. 동일하지 않은 스키마라면 진화한 스키마인지 확인한다. 만약 진화한 스키마거나 새롭게 등록하는 스키마라면 스키마를 등록하고 새로운 스키마 ID를 프로듀서에게 응답해준다.
    3. AvroProducer는 스키마 레지스트리에게 받은 스키마 ID를 Avro의 스키마에 저장하고, Payload를 이진부호 포멧으로 바꾼 후 카프카로 메세지를 보낸다. 이 때, 스키마 ID를 통해서 스키마 전체의 크기를 줄여서 보낸다. Payload는 이진부호 포멧으로 해서 압축했기 때문에 메세지의 크기를 줄여서 보낸다.
    4. AvroConsumer는 스키마 ID를 가진 메세지가 왔을 때, KafkaAvroDeserializer를 이용해서 역직렬화해서 메세지를 읽는다. 이 때, 스키마 ID가 컨슈머의 로컬 캐시에 저장되어 있지 않다면 스키마 레지스트리에 요청해서 스키마 ID에 대응되는 스키마를 받아온다. 받아온 스키마를 로컬 캐시에 저장하고, 메세지를 역직렬화한다. 

    스키마 레지스트리를 이용하면 주고 받는 메세지의 크기 자체를 줄이는 장점을 얻을 수 있다. 또한 사전에 정의되지 않은 형식의 메세지는 전송할 수 없기 때문에 메세지를 받는 입장에도 이 부분을 고민하지 않아도 된다. 

     

    참고

    댓글

    Designed by JB FACTORY