미분류

반응형 데이터베이스 접근 방식에 관심을 가져야 하는 이유

7 분 읽기

새로운 Java SDK가 완전히 리액티브 및 비동기 컴포넌트를 기반으로 한다고 발표한 이후, 사용자들은 검증되고 익숙한 동기식 접근 방식에 비해 어떤 이점이 있는지 계속해서 묻고 있습니다. 흔히 “성능'이 주요 동인 중 하나이지만, 그 외에도 고려해야 할 점이 훨씬 많습니다. (별도의 게시물에서 다룰) 에러 처리를 제외하고, 다음과 같은 기능들이 두드러집니다:

  1. 데이터 스트림을 이용한 데이터 흐름 구축
  2. 머리 아픈 일 없는 효율적인 자원 활용

이 블로그 게시물의 목적은 일반적인 데이터 접근 패턴을 동기식에서 리액티브 방식으로 전환하고, 그 결과로 이 새로운 접근 방식으로 무엇을 할 수 있는지 엿볼 수 있도록 하는 것입니다. 아직 “리액티브” 코드를 작성해 본 적이 없더라도 걱정하지 마세요. 진행하면서 몇 가지 기초를 익히게 될 것입니다. 또한, 저희가 여전히 동기식 API를 지원한다는 점을 기억해 주세요. 동기식 API는 리액티브 API를 감싸는 얇은 래퍼에 불과하므로, 곧바로 깊은 물속으로 뛰어들어야 하는 부담 없이 애플리케이션을 더 강력한 접근 방식으로 점진적으로 마이그레이션할 수 있습니다.

아, 그리고 그런데 말이죠, 저희에게는 웨비나가 곧 다가옵니다 1월 22일, 당사의 SDK 엔지니어 중 한 명인 사이먼 바슬(Simon Basle)이 새로운 자바 SDK를 소개하고 리액티브 기능 일부를 시연할 예정입니다. 그와 함께 참여하여 질문을 남겨보세요!

조회 패턴

데이터베이스나 키/값 스토어에 접근할 때 매우 흔한 패턴은 조회 패턴입니다. 문서를 불러온 다음, 문서의 내용을 기반으로 더 많은 문서를 불러오는 방식입니다. 고전적인 “블로그 게시물과 댓글” 예시를 생각해 봅시다. Couchbase에 다음과 같은 블로그 게시물이 저장되어 있다고 상상해 보세요:

필요에 따라 각 콘텐츠의 전체 세부 정보를 로드하는 데 사용할 수 있는 댓글 ID 목록이 포함되어 있습니다. 댓글은 다음과 같을 수 있습니다:

이제 블로그 게시물이 로드될 때 게시물 바로 아래에 표시되도록 발행된 처음 두 개의 댓글을 로드하고 싶다고 가정해 보겠습니다. 동기식 접근 방식과 새 SDK를 사용하여 이를 달성하는 한 가지 방법은 다음과 같습니다.

오류 처리를 제쳐두더라도, 이 접근 방식에는 몇 가지 단점이 있습니다.

  • 네트워크 응답을 받기 위해 최소 3번을 기다려야 하며, 그동안 스레드는 유휴 상태로 머물러야 합니다. 그렇지 않았다면 가치 있는 작업을 수행할 수도 있었을 텐데요.
  • 모든 작업마다 개별적인 타임아웃이 존재하기 때문에 전체 프로세스에 전역 타임아웃을 적용하기는 매우 어렵습니다.

 

Java 8 람다를 사용하여 리액티브 방식으로 전환해 봅시다(Java 6/7도 지원되며, 콜백 대신 익명 클래스를 사용하기만 하면 됩니다).

연산자의 이름만 봐도 무슨 일이 일어나고 있는지 아주 명확합니다. 코드는 블로그 게시물을 로드합니다. 게시물이 도착하면 댓글 ID 목록을 추출하여 실제 댓글 문서를 로드하는 다음 메서드로 전달합니다. filter 메서드는 게시된 댓글만 통과시킵니다. 그 후, 처음 2개의 댓글만 가져온 뒤 (이미 충분히 확보된 상태에서 더 많은 문서를 로드하는 등의) 추가 작업을 방지하기 위해 구독을 해제합니다. 마지막으로, 발견된 모든 댓글을 리스트로 모으고, 전역 타임아웃을 적용한 후 블로킹합니다.

이 코드는 필요한 모든 댓글을 병렬로 불러오기 위해 “팬 아웃(fan out)”하여 클러스터의 더 많은 노드에 동시에 요청을 보내고, 그 결과 원하는 결과를 더 빠르게 반환합니다. 또한 이 코드는 전체 “작업”에 전역 타임아웃을 적용하는데, 이는 동기식 코드에서는 구현하기가 매우 까다롭습니다. 마지막으로, 이 코드는 전체적으로 조합이 가능합니다. 타임아웃을 적용하거나 “가져올” 댓글의 수를 지정하지 않고도 메서드 내부에 로직을 캡슐화할 수 있습니다. 상위 레이어는 원하는 대로 작업을 수행하는 데 필요한 연산자들을 체인처럼 연결하여 사용할 수 있습니다.

또한, 더 강력한 개념(비동기, 반응형)에서 덜 강력한 개념(동기)으로 가는 것은 매우 쉽지만, 그 반대는 불가능하다는 것을 알 수 있습니다.

우리는 여전히 맨 마지막 단계에서 블로킹을 하며, 그것도 괜찮다는 점을 유의하십시오. 대부분의 애플리케이션은 어느 시점에서든 (심지어 사용자에게 응답을 반환하기 직전에라도) 블로킹을 하게 됩니다. 전체 스택을 “리액티브”하게 만드는 것이 최고의 성능과 자원 활용도를 제공하지만, 코드의 더 큰 부분을 비동기적으로 실행되도록 여전히 만들 수 있으며 큰 이점을 얻을 수 있습니다.

쿼리 실행

당연하게도 모든 데이터베이스는 특정 기준에 따라 저장된 문서를 조회할 수 있도록 허용합니다. 대부분의 경우 하나 이상의 레코드가 반환되며, 때로는 한 번에 수천 개가 반환되기도 합니다. 데이터가 반환되면 애플리케이션 요구사항에 따라 그 내용을 수정, 결합 또는 필터링해야 하는 경우가 매우 많습니다.

다음의 예를 생각해 보겠습니다. 당신은 통신사이며 버킷에 사용자 레코드를 저장하고 있습니다. 월말에 이번 달에 가입한 모든 신규 고객이 실제로 새 휴대전화를 배송받았는지 확인하고자 합니다. 계약된 택배 회사는 배송 상태를 조회할 수 있는 웹 서비스를 제공합니다.

다음은 제공자의 모의(mock) 구현입니다:

택배 ID를 나타내는 UUID가 주어지면, 해당 소포가 이미 배송되었는지 여부를 무작위로 반환합니다.

사용者 레코드의 예시는 다음과 같습니다:

우리는 N1QL을 사용하여 지난 한 달간의 모든 사용자를 가져온 다음 소포 ID를 제공업체에 전달합니다. 다음은 동기식 버전입니다.

이 코드가 그렇게 나빠 보이진 않죠, 그렇지 않나요? 택배사의 웹 서버가 형편없어서 500밀리초 대신 가끔 10초 만에 쿼리 결과를 반환한다고 하면 어떻게 생각하시나요? 게다가 결과를 스트리밍하고 도착하는 대로 바로 작업을 수행할 수 있는 가능성도 잃게 됩니다.

운이 좋게도 2,000명의 신규 고객이 가입했다고 상상해 봅시다. 먼저 2,000명의 사용자가 조회될 때까지 기다린 다음, 택배사 웹 서버를 상대로 2,000번의 순차적(serial) 쿼리를 수행해야 합니다. 물론 실행자 서비스(executor service)로 작업을 분산시킬(fan out) 수 있지만, 그렇게 하면 오케스트레이션과 집계를 직접 처리해야 합니다.

반응형 버전으로 훨씬 더 나은 결과를 얻을 수 있습니다:

여기서 우리는 서버에서 결과가 도착하는 대로 스트리밍하고 있습니다. 결과가 나오면 소포 ID를 추출하여 소포 서버로 전송합니다. 응답이 도착하면 이를 가져와 배송 주(state)별로 그룹화합니다. 마지막으로 보기 좋은 서식을 적용하여 출력합니다. 결코 블로킹되지 않으므로, 모든 요청을 소포 서버로 보내고 (어떤 순서든) 돌아오는 대로 그룹화합니다. 일부 요청에 시간이 더 걸리더라도 먼저 도착한 다른 것들의 처리를 끝낼 수 있고 정체되지 않으므로 상관없습니다. 또한 전체 타임아웃을 적용할 수도 있습니다.

요약

이 두 가지 예시는 오늘날 귀하의 애플리케이션이 비동기 및 리액티브 실행으로부터 어떻게 혜택을 받을 수 있는지를 명확히 보여줍니다. 이는 애플리케이션 서버와 응답 시간을 느리게 만드는, 블로킹 방식의 자원 소모적인 데이터베이스 드라이버에서 벗어날 수 있는 명확한 길을 제공합니다.

우리는 가능성의 겉핥기밖에 하지 않았습니다. 우리는 데이터베이스 접근 애플리케이션을 작성하는 이 새로운 방식에서 명확한 길잡이를 제공하기 위해 샘플 프로젝트와 확장된 문서를 작업 중입니다. 여러분의 사용 사례가 다뤄지기를 원하시거나 그것이 어떻게 전환될 수 있는지 알고 싶으시다면 알려주세요.

마지막으로, 며칠 후에 다시 한 번 간단히 상기시켜 드리겠습니다. 웨비나 새로운 Java SDK에 관한 내용입니다. 참여하셔서 소개를 들으신 후, 망설이지 말고 질문해 주세요!

이 기사 공유하기

작가

마이켈 니칭거(Michael Nitschinger)는 카우치베이스(Couchbase)의 수석 소프트웨어 엔지니어입니다. 그는 JVM에서 최초의 완전한 리액티브 데이터베이스 드라이버 중 하나인 카우치베이스 자바 SDK의 아키텍트이자 메인테이너입니다. 또한 카우치베이스 스파크 커넥터를 저술하고 유지 관리하고 있습니다. 마이켈은 오픈 소스 커뮤니티에서 활발히 활동하고 있으며, RxJava 및 Netty와 같은 다양한 다른 프로젝트에도 기여하고 있습니다.

1개의 응답

  1. Jacek Laskowski 아바타
    야체크 라스코프스키

    매우 유용한 블로그 글입니다. 전체 데이터 흐름이 비동기식이고 논블로킹(non-blocking) 상태로 유지되도록 각 메서드가 내부적으로 어떻게 작동하는지에 대한 내용이 더 많았으면 좋겠습니다. 행/레코드가 도착한 후 어떻게 다음 메서드 호출로 전달되는 것일까요? 상상하기 어려울 정도여서 SDK의 내부 구조에 대해 질문을 던지게 됩니다.

댓글 남기기

카우치베이스 카펠라를 시작할 준비가 되셨나요?

개발 시작하기

NoSQL을 탐색하고, 리소스를 찾아보고, 튜토리얼을 시작하려면 개발자 포털을 확인하세요.

카펠라 프리 사용하기

단 몇 번의 클릭으로 카우치베이스(Couchbase)를 직접 체험해 보세요. Capella DBaaS는 시작하기 가장 쉽고 빠른 방법입니다.

연락해

Couchbase 제품군에 대해 더 알고 싶으신가요? 저희가 도와드리겠습니다.