본문 바로가기
Lecture

대규모 서비스에서 발생하는 데이터 처리, 백엔드 엔지니어링의 역할과 범위

by Renechoi 2023. 7. 4.

패스트 캠퍼스(The Red: 4천만 MAU를 지탱하는 서비스 설계와 데이터 처리 기술 by 카카오페이지 기술전략이사 윤진석) 필기록입니다.

 


데이터의 개념과 종류에 대한 이해 

 

 

Datum -> Data -> information 

각각의 데이터들이 관계로 연결되면 정보가 된다. 

(20년 크리스마스는 영하 12도로 추웠다.) 

 

 

데이터는 크게 양적, 질적으로 나뉠 수 있다. 

양적인 데이터는 다시 이산형과 연속형으로 나뉠 수 있고, 질적 데이터는 명목형과 순서형으로 나뉜다. 

 

인터넷에서 수집되는 데이터는 다음과 같이 구분할 수 있다. 

 

 

 

암묵적으로 제공되는 데이터가 많다. -> 비정형 데이터 

 

시계열 정보가 더해질 수 있다. 

 

 

 

 

시스템로그와 유저로그로도 나눌 수 있다. 

 

시스템 로그 : Server Log, Client Log 

유저 로그 : 행동, 서비스 로그, 마케팅 데이터 

 


 

 

관계형 데이터베이스와 NoSql 그리고 파일 저장소 

 

 

자료구조를 다음과 같이 나눠보자. 

- 단순 구조: Integer, String

- 선형 구조: List, Queue,,,

- 비선형구조: Tree, Graph,,,

- 파일구조: Text, Sequence, Index 

 

 

 

1억 개 그 이상의 element를 갖는 자료구조를 어떻게 만들 수 있을까? 

 

1) 파일 -> 파일 기반의 리스트 구현 

 

public static void main(String[] args) {
	BigList bigList = new BigList(new File("/tmp/list.seq")); 
    for (int i = 0; i < 100000000; i++) {
	bigList.add(i); 
    }
	System.out.println(bigList.get(12345678)); 
    bigList.flush();
	bigList.close();
}

public class BigList implements FileBasedList { 
	// write your code
}

 

파일 기반의 리스트를 다음과 같이 만들수 있다. 

 

public interface FileBasedList { 
	
    /**
	* 파일 기반 리스트 컨스트럭터 
	* @param path
	* @return list object
	*/
public FileBasedList FileBasedList(File path);

	/**
	* @return the size of list 
    */
public int getSize();
	
    /**
	* @param value to be added to list 
    */
public void add(int value);
	
    /**
	* @param index specific index of list 
    * @return value
	*/
public int get(int index);

	/**
	* Flushes to disk 
    */
public void flush();

/**
* Closes FileBasedList 
*/
public void close(); }

 

 

구현 방식에 대해 살펴보자. 

 

파일의 헤더에 리스트 사이즈의 값을 적고 인덱스 번호, element의 값을 하나씩 추가하는 방식으로 

 

 

 

이러한 파일을 바이너리 파일이라고도 하고 시퀀스 파일이라고도 한다. 

 

 

7번째 element를 뽑아오라는 get 함수를 구하면 헤더값을 건너뛰고 하나의 엘리먼트가 8바이트 이기 때문에 index*8을 해서 점프를 하면, 해당하는 엘레먼트를 구해올 수 있다. 

 

 public int get(int index) {
long header = 4; 
file.skip(header + (index * 8)); 
int idx = bytesToInt(file.read(4)); 
return bytesToInt(file.read(4));
}

 

 

하나의 Element는 index와 value로 구성되어 있다. 다음과 같은 element schema를 가질 수 있다. 

 

public class Element() { 
int index;
int value;
...
readFields(InputStream in); 
writeFields(OutputStream out);
}

 

 

 

Object 모델의 id와 value를 relational 모델로 가져오면 

 

id와 value 칼럼 기반의 db 테이블을 만들 수 있다. 

 

 

 

 

 

 

SQL은 Parser를 통해 자바 코드로 구현된 내용과 같은 컴퓨터 언어로 전환되어 쿼리를 수행하는 execution 엔진을 통해 수행한다. 

 

이때 list.remove()가 발생하면 어떡할까? 

 

바이너리 파일로 작성된 구조 => 풀스캔이 생긴다. 

 

public get(int index) {
long header = 4;
file.skip(header + (index * 8));
int idx = bytesToInt(file.read(4));
if(index > idx) get(index++); // 우측으로 풀 스캔 
else get(index--); // 좌측으로 풀 스캔
return bytesToInt(file.read(4));
}

 

=> O(n)의 수행시간 

 

이를 방지하기 위해 인덱싱을 하는데, 물리적인 구조는 바이너리 구조라고 하더라도 논리적인 구조를 위한 트리 형태의 인덱스 생성 

 

 

 

자료구조의 본질적인 특징

1) 메모리 또는 디스크에 쓰고 읽을 수 있다

2) 관계형 DBMS이건 NoSQL이건 파일이건 -> 기본적인 자료구조의 데이터 저장소다 

 

 

 

데이터 처리 성능 차이 

 

Colum wise Vs Row wise 

 

 

유저 테이블이 있을 때 전세계 인구가 80억이라고 가정해보자. 

 

성별로 인구수를 뽑으라는 쿼리가 주어질 때,

row 기준에서는 젠더 칼럼만 필요하기 때문에 건너 뛰는 즉 stride access가 필요하므로 512GB를 읽어야 한다. 

결과적으로 column보다 500배 이상 느리다. 

 

 

이를 해결하기 위해 테이블 파티셔닝이라는 기법이 사용된다. 

 

기존의 유저 테이블에서 한 부분을 잘라서 만들어놓는 방식 -> vertical partitioning 

 

 

 

또 다른 방법으로는 수평 파티셔닝으로 샤딩이라고 한다. 

 

 

전화 번호부 책을 찢어서 서로 찾아내고 summation 하는 것과 같은 분산처리 방식 

 

 

저장 공간 효율 측면에서도 row wise는 null을 허용하기 때문에 고정된 스키마로서 공간 확보 필요성을 요구받는다. 이때 일관된 저장이 된다는 장점은 있다. 

 

column wise의 경우 비어 있는 공간을 굳이 채우지 않는다. 

 

 

 

NoSQL은 앞서 살펴본 내용 중에 장점만을 모은 것이 기본 취지이다. 

- Schema 변경 용이

- Sharding 분산 저장

- Distributed Query Processing

- Compact Store 

 

-> 인터넷의 등장과 함께 폭발적으로 성장했는데, 단순히 빅 데이터만을 위해서 사용되지는 것은 아니다. 

-> 오늘날 모든 NoSql은 4가지 속성을 따르는데 성능과 관리성 측면에서도 용이한 부분을 제공한다. 

 

 


 

서비스를 위한 실시간 속성 데이터 처리 

 

컴퓨터 시스템의 종류 

 

일괄처리, 분산, 시분할, 실시간 시스템 

 

실시간 시스템이란 ? 

 

자원이 한정되어 있는 상황에서 작업 수행이 요청되었을 때 제한된 시간 안에 처리해주는 것 

 

즉, 작업 처리에 걸리는 시간을 일관되게 유지할 수 있는 정도에 달려 있음 

- 일괄처리 (batch) ->  FIFO 스케줄링

- 이벤트 드리븐 구동 방식 -> 우선 순위 또는 선점형 스케쥴링 

- 시분할 스케줄링 -> 라운드 로빈

- 멀티 태스킹 -> 자원 분배(작업의 동시 수행 /병렬 처리) 

 

데이터 작업 요청의 처리 스케줄링은 새로 탄생한 게 아니라 컴퓨터 과학의 기본과 동일하다. 

-> 핵심은 스케줄링이다. 

 

 

데이터 베이스 시스템은 어떤 방식인가? 

 

- 실시간 시스템 + 멀티 태스킹 방식 

- MySql은 기본적으로 multi-threads 

- 데이터 처리 관점으로 보면 무작위순으로 처리-> 동시성, 락 등의 이슈 

- 최근엔 thread Scheduler 등장

-> 기본적으로 multi 태스킹인데 최근에는 event driven으로 가려고 하고 있다. 

 

 

다음과 같은 예를 살펴보자. 

 

매출 통계를 월 단위로 뽑아달라는 요구 사항에 대해 

 

기존 방식은 시스템 선정과 접근 방식이 잘못되었었다. 실시간 시스템 + 멀티 태스킹을 RDBMS를 통해 처리하려고 했던 것. 

 

그러면 어떻게 해야할까? 

 

분산 시스템 + 일괄처리(batch)

혹은 

실시간 시스템 + 이벤트 드리븐

이 적절하다. 

 

예를 들어 다음과 같은 데이터에 대해 생각해보자. 

 

 

2018년 1월의 데이터는 배치처리를 통해 처리하면 좋고

최근 3개월의 매출 데이터는 Unbounded Data에 가깝기 때문에 계속 쏟아지므로 이벤트 드리븐이 적절하다. 

 

이미 쏟아져서 저장되어 있는 특정 데이터들에 대해서는 batch processing이 좋다. 

streaing processing으로 처리되는 현재 쏟아지고 있는 데이터들은 실시간 시스템으로 처리한다. 

 

예를 들어 키워드를 검색할 때, 특정 기간 동안 어떤 유저가 어떤 데이터를 얼마나 검색했는지를 추출해야 하면 batch processing. 

실시간으로 검색이 진행될 때 모든 검색어를 검색 엔진에 전달할 필요가 없다. 금칙어 같은 검색어에 대해서는 필터링 처리가 가능하다. 

 

 

 

 

 

 

실시간 인기 검색어에 대해 살펴보자. 

 

기대횟수와 관측횟수, 표준편차, 순위차 보정 등의 데이터 처리 기법을 통해 알고리즘을 구현해서 사용하는 예시이다. 

 

 

이때 기대 횟수 배치 프로세싱은 과거 데이터이기 때문에 분산처리와 batch를 사용한다. 

 

실시간 순위 프로세싱에 대해서는 streaming 처리를 하는데, 각각의 word에 대해 counting, 집계, 연산을 통해 처리한다. 

 

 

 

실시간 관련해서 한가지 사례를 살펴보자. 

 

고객 포인트 소멸 정책에 대한 케이스 

 

- 서비스 유저 수백 ~ 수천만명에 대해, 가입 또는 프로모션 참여로 받은 포인트를 결제 또는 소멸시효에 따라 차감해야 하는 요구사항 

- 조건: 마이 페이지 조회가 빈번하고, 응답속도가 빨라야 한다. 

 

 

 

억 단위 이상의 테이블에 대해서, 묵직한 쿼리를 새벽에 배치 쿼리를 실행한다. 

 

materialized view를 수백 ~ 수천 만 rows로 줄일 수 있다. 

 

여기서 문제는 이것이다. 배치 쿼리가 도는 와중에 포인트 수정이 발생하면? 

-> 정합성 문제 발생

-> 온갖 락 관련 장애와 포인트 오류 문의 발생 

 

-> 실시간 시스템에 대해 멀티태스킹 + RDBMS가 맞지 않다는 것을 보여준다. 

 

이를 실시간 시스템 + event_driven으로 해결해보면 어떨까? 

 

 

pub - sub 패턴을 통해서 해결된다. 

 

고객을 통해서 결제 이벤트나 소멸 이벤트가 발생하는 것을 listerning 하고 포인트 내역 테이블에 기록한다. 

 

그러면 데이터베이스는 publisher가 된다. 이를 구독하는 subscriber가 있다면, 즉각즉각 잔여 포인트 합계를 업데이트한다. 

 

이를 확장하면 클라이언트가 subscriber가 될 수도 있다. 즉 화면을 열어놓으면서도 업데이트 되는 것을 확인할 수 있다. 

 

최근에는 Real-time database도 등장했다. 

 

과거에도 실시간 데이터베이스 연구는 많았지만 인터벌이 존재하는 polling 기법이 활용되었다. 

 

최근은 pub-sub 패턴을 근간으로 거의 real-time에 가깝게 동기화를 실현한다. 

 

 

드라이버가 고객을 태우러 가고 있는 경로가 찍힐 때, 그 내용이 유저한테도 찍혀야 한다. 

 

Waf와 LB를 거쳐서 pub-sub에 도달하는데, redis와 같은 인메모리 데이터로 cluster 형태로 저장해서 데이터를 처리한다. 

 

 

 

결론 

-> 관계형 데이터베이스만으로는 오늘날의 실시간 상호작용 서비스를 구현하기 어렵다. 

-> 컴퓨터 시스템 구조, 스케줄링, 멀티 태스킹, 관계형 DB + 

-> NoSql + PubSub 디자인 패턴 + 실시간/시계열 데이터 처리 

 

 


 

 

데이터 분석과 응용을 위한 배치 처리 

 

기업에서 데이터를 분석하는 목적은 무엇일까?

-> 시장과 고객을 이해하고 제품을 고도화 

 

 

고객 분석을 위한 테스트 기법 

- 횡단적 조사

- 종단적 조사

 

 

횡단적 조사의 예를 살펴보자. 

 

-> 신규 고객 혹은 기존 중 어디에 더 집중해야 할지 인사이트를 얻게 된다. 

 

이를 분석할 때 

규모의 문제가 발생한다. 

 

 

이전 장에서 살펴보았듯이 event driven으로 해결할 수 있지만 MapReduce 분산 프로그래밍 모델을 통해 살펴보자. 

 

 

MapReduce 

맵과 리듀스라는 두 개의 함수로 프로그래밍을 하는데, 병렬처리 된다. 

X와 Y가 있을 때 동일한 map함수가 분산처리를 하고 해당 결과에 대해 reduce한다. 

 

예를 들어 특정 날짜, 결제 금액을 날짜 단위로 reduce 하고자 한다면 중간에 group by를 day로 한다. 

 

365일 라인으로 줄어들고 이를 통해 daily 집계가 가능하다. 

 

 

 

날짜별 가입자 코호트에 대한 분석 

 

유저 테이블에서 매체별 가입자수, 결제에서 구매 횟수를 가져온다고 할 때, 

 

 

즉, 특정일에 가입 이벤트를 통해 가입한 고객과 일반적인 상황에서 평균적으로 가입한 고객, 둘의 코호트를 분리해서 분석하고자 한다면 어떻게 해야할까? 

 

이를 분산처리를 구현하면 다음과 같다. 

 

날짜로 코호트를 분리하고, 유저 기준으로 shuffle을 한다. 

그 다음은 특정 날짜에 몇 번 구매했다와 같은 기준으로 reduce 시키고 

코호트 기준으로 shuffle하고 

원하는 자료를 뽑아낼 수 있다. 

 

데이터베이스 단일 처리로도 가능하지만 이와 같이 분산 처리를 통해 보다 빠르게 할 수 있다. 

 

 

ML를 활용한 데이터에 대해서도 예시를 들어 살펴보자. 

 

웹툰 서비스에서 장르별 인기도를 분석할 때, 나이대 feature를 기준으로 분석해본다면?

 

다음과 같은 요인 분석에 따라 개인화된 데이터가 나와야 한다. 

 

 

유저를 feature space (특징 공간)에 임베딩하기. 

 

 

 

멀티 db 소스에 유저의 특징이 산재되어 있다고 할때, 데이터를 추출하고 변형해야 하는 과정 (ETL)에서 차원을 가진 공간으로 분류. 

-> 기계 학습을 위한 전처리 

 

특징 별로 개인화된 데이터를 진열하면 타겟 마케팅을 기준으로 비즈니스 성과를 향상 시킬 수 있다. 

 

 

 

쉬지 않고 데이터를 관측하고 변화량을 반영하는 기술이 필요하다. 

 

 


 

데이터 처리의 최신 기술 동향과 설계 방향 

 

 

확장성 있고 빠른 응답 속도를 요구하는 서비스 시스템이 필요하다.

 

밑단에 primary DB를 두는데 이때 이 DB는 관계형 데이터베이스를 일반적으로 쓴다. 

 

data를 capture 하는 솔루션들이 다양하다.

 

즉 변이 일어나는 것을 listening 하다가 이를 반영한다. 

 

primary DB의 이벤트 버스를 두고, search Index, GraphIndex, Time Series 등에 대한 변경 사항을 감지한다. 

 

-> 실시간 시스템 + Event-Driven 방식 

-> realtime 베이스로서 수행하게 된다. 

 

Data Lake의 경우 대용량 데이터 소스로서 기능한다. 

 

구체적인 spec을 살펴보면 다음과 같다. 

 

 

 


백엔드 엔지니어링의 역할과 범위 

 

지금까지 서버 사이드 쪽의 기술 부채를 해결하기 위한 문제와, 사례, 학습 등을 살펴보았다. 

 

백엔드 엔지니어링이라는 말 자체는 매우 광범위하다. 

 

백엔드에서 일어나는 일들 

 

- 클라이언트-서버간 서버 구현

   - 통신 프로토콜 디자인: API spec design 등

   - API 버전 관리: backward compatibility 등

- 확장성과 안정성

   - Scalable, low latency, stability 등

- 서버 소프트웨어 스택 선정

   - OS 부터 모두 

- 서버 소프트웨어 버전 관리

   - 유지보수 

- 데이터 흐름 설계 

   -데이터 처리에 대한 아키텍처

- 백 오피스 운영툴 관리

   -매뉴얼 시스템 운영관리툴 등 

 

 

 

필요한 지식들 

- 네트워크

- 데이터베이스

- I/O

- 분산/병렬 시스템

- 자료구조

- 운영체제

- 알고리즘

- 이산수학 

 

 

 

커리어는 크게 기술 트랙, 관리 트랙, 사업 트랙을 생각해볼 수 있다. 

 

 

 

 

성장 방향성 : 어느 방향으로 가든지 모르는 것보다 아는 게 낫다 ! 

 

기본적인 것에 집중하되, 배우는 것에 거부감을 가지지 말자. 

 

 

반응형