Dec 17, 2018 - 샤머니즘 TABLE 생성 논리

두 집합의 원소들 간의 대응관계일때를 맵핑이라고 하며,
Many-To-Many 맵핑 일 경우에, 보통의 개발자들은 맵핑 테이블을 만들어서 관리하기를 원합니다.

그리고 이 N:N 구조에서 어느쪽의 데이터가 더 많은 지를 우선 파악한 후, N:N 이 실제 2:5의 비율이 될지 2:13 의 비율이 될지를 파악한 후, 실제 DB를 SELECT 할때, 해당 맵핑 테이블을 얼만큼 효율적으로 검색하며 사용하질에 대한 기술적으로 많은 고민이 필요합니다.

예를 들어,
A테이블에 신규로 등록되는 페이지와, B테이블에 신규로 등록되는 페이지가 있을 때, 각각의 페이지에는 A가 등록될때 자동으로 B와 연결된 데이터들이 맵핑테이블에 등록이 되게 됩니다.
반대로 B 라는 테이블의 데이터가 등록될 때는 자동으로 B와 연결된 A의 데이터들이 맵핑 테이블에 등록되게 됩니다.

좀더 쉽게 설명하면, A테이블에 속한 데이터를 화면에 등록하는 페이지에서는,
A테이블의 데이터가 한개 등록될 때 최대 3만건의 B테이블의 데이터가 1:N 형태로 맵핑테이블에 등록이 되어야합니다.
B테이블에 속한 데이터가 화면에서 한개 등록될 떄 A군의 속한 데이터가 8천개 정도 자동으로 맵핑되어야합니다.

이는 A군의 유효한 데이터가 8천개, B군의 유효한 데이터가 3만개 로 대략적으로 1:4 의 비율로 맵핑데이터가 필요하다는 말이 됩니다.

해당 데이터 B테이블의 행이 어떠한 사유에 의해 유효한 데이터가 5만건이 늘어나게 된다면,
A테이블의 신규데이터가 새롭게 등록될대 3만건이 등록되던 데이터가, 8만건의 데이터가 등록되어야합니다. 그렇게 되면, 기존 1:4 로 등록되는 구조가 1:8 이상의 구조로 변경되게 됩니다.

그렇다면 5만건의 유효한 데이터를 그대로 유지할지, 안할지에 대한 판단이 필요합니다.
현재 Auto Increment 가 1048719446 (10억)까지 증가된 상황에서 테이블 용량은 110GB를 차지하고 있는 상황이며,
B군이 2.5배가 된다는 것은 직관적으로 20억 및 테이블 용량이 220GB가 된다는 의미와 같습니다.

그 상황에서 개발자의 접속권한으로는 전체 row 갯수를 파악하지 못할 정도로 많은 데이터이긴한데,
인덱스가 가능한 PK값들을 단독으로 수행했을 때는 0.0016초 정도안에 응답하고 있습니다.
해당 데이터를 full scan 하여 검색하는하는 로직이 존재하지 않기 때문에 개발적으로는 이슈될 사항은 없는 상태입니다.

그렇다면, 현재 상황에서 기존의 테이블을 그대로 사용해야할 것인지, 분리해서 별도의 테이블을 구축해야할지 결정해야하는데,
이 경우, 테이블을 분리할 것인가 하지 말아야할 것인가는 DBA의 판단으로 넘겨야할 거 같습니다.

index size 라든가, 얼마만큼 data block 이 깨져있는지 등에 대해서는 개발자로서 접근권한이 없기 때문에 해당 근거자료로는 부족하기 때문입니다.

분리한다는 작업은 신규개발 및 일정을 산정하면 될 부분이고,
테이블을 분리하여 성능 개선이 된다면, 혹은 기존 개발된 부분에 무리가 가는 부분이 조금이라도 있다면 분리하는 것이 맞습니다.

테이블의 수직분할은 컬럼을 기준으로 테이블을 분리하는 것을 의미하고 수평분할은 Row를 기준으로 테이블을 분리하는 것을 의미합니다.

개발자로서 개발을 하면서 수직분할 작업은 했지만, 수평분할로 개발한 적은 단 한번도 없긴 하지만,
만약 해야한다면 이번이 처음으로 보입니다.

기본적으로 수직 분할을 하는 이유는 컬럼이 계속 추가 되면, 테이블의 컬럼 수가 많을수록 I/O에 대한 부하가 걸리고,
수평 분할을 하는 이유는 테이블의 Row 수가 많을수록 Index에 대한 부하가 따르기 때문입니다.

만약 한 테이블에 수많은 컬럼을 계속 추가하게 되어 존재하게 된다면,
디스크의 여러 블록에 데이터가 저장되므로 I/O 성능 저하를 불러올 수 있게 되어, 이렇게 컬럼이 많아지면 로우 체이닝과 로우 마이그레이션이 많아져서 성능이 저하되게 됩니다.

로우 체이닝(Row Chaining)
길이가 너무 커서 하나의 블록에 저장되지 못하고 다수의 블록에 나누어져 저장

로우 마이그레이션(Row Migration)
수정된 데이터를 해당 데이터 블록에 저장하지 못하고 다른 블록의 빈 공간에 저장

로우 체이닝과 로우 마이그레이션이 발생하여 많은 블록을 사용하게 되면, 불필요한 I/O가 발생하여 성능이 저하됩니다.
많은 I/O 발생은 성능에 직접적인 영향을 주므로 매우 중요한 문제며, 데이터에 대한 범위 검색을 할 경우 더 많은 I/O를 유발하므로 성능 저하를 일으킵니다.
수많은 컬럼을 동시에 조회하는 경우는 드물기 때문에,
각각의 조회 조건에 맞게 이용되는 컬럼들로 그룹을 묶어서 테이블 분할을 검토하게 됩니다. 하지만, 지금 상황은 컬럼이 추가되는 것이 아니라, row가 늘어나므로,

조회나 처리에 대한 분산을 가능하게 하는 칼럼들을 기준으로 테이블을 분리하여 성능 개선에 큰 도움을 노리는 것이 아닙니다.

그래서 만약 개발을 하게 되면, 수평분할을 고려해야합니다.
수평분할(Horizontal Partitioning)은 Row를 기준으로 테이블을 분리합니다.
만약 한 테이블에 대량의 데이터가 존재하고 트랜잭션이 몰린다면 성능 저하를 피하기 어렵습니다.

대량의 데이터가 하나의 테이블에 있으면 인덱스 정보 생성 시 부하가 커집니다.
삭제와 등록을 반복하게 되면, 인덱스를 찾아가는 깊이(Depth)가 깊어지게 되고 인덱스의 크기가 커질수록 더 많은 성능 저하를 불러오게됩니다.
조회하는 성능보다 데이터를 처리하는 성능에 더 큰 영향을 미치게 되고,
이런 경우 트랜잭션이 분산 처리될 수 있도록 테이블 수평분할을 검토하게 됩니다.

데이터 건수가 수천만 건을 넘고 처리가 많이 일어난다면 성능을 제대로 발휘할 수 없게 됩니다.
현재 A군과 B군의 맵핑테이블이 바로 그러합니다.
이때 논리적으로는 같은 테이블이지만 물리적으로 서로 다른 여러 개의 테이블 스페이스에 나누어서 저장하는 파티션 방법을 이용하면 성능 개선에 큰 도움이 됩니다.
파티션 종류로는 Range, Hash, List, Composite 파티션이 있으며,
가장 널리 이용하는 Range 파티션은 주로 날짜를 기준으로 하는 경우가 많습니다.
Range의 경우 데이터가 균등하게 나누어져서 비슷한 성능 개선을 보장하며, 보관 주기에 따라서 필요치 않는 데이터도 쉽게 지우고 관리할 수 있지만,
A군과 B군의 등록날짜들은 제 각각 이므로, 만약 하게 된다면 Hash와 List 형태가 더 올바릅니다.

이러한 부분들에 대해 DBA와 논의할 준비를 하는 중,

DBA 의 경우,

A군과 B군의 테이블에 대한 맵핑 테이블 뿐만 아니라, A군, B군, C군, D군.
이 모든 것을 다 바꾸고, 변경해야지만 이 성능저하를 피할 수 있다.

라는 논리를 펼쳤으며, 해당 논리의 근거에 대해서는 샤머니즘적인 성향이 강했습니다. 그로인해, 더 이상의 이야기는 진행할 수 없었으며, 5만건의 데이터는 기존 테이블에 INSERT 되게 되었습니다.

물론, A테이블과 B테이블의 데이터가 예전 데이터이므로, 변경을 하여야하는 것은 맞습니다.
그러나, 4년이 넘게 유지된 모든 테이블 구성을 바꿔야할 경우, 개발자 입장에선 그로인한 모든 화면 및 백도어로직이 변경되게 됩니다.

그러면 그 근거자료가 존재해야하며,
논의하기 위해 준비했던 모든 자료들, 해당 테이블의 용량, INDEX구조등에 벗어난 뜬금없는 C군과 B군의 데이터 구조까지 변경해야한다면,
그 개발 범위를 다시 산정해야합니다.

그런데 그 논리가 그냥 ‘감’, 인디언 기우제 논리라면, 해당 개발건은 고착화 될 수 밖에 없으며, 그리고 그렇게 되었기 때문에 결국 유지보수로 인하여 최소화된 추가개발을 진행할 수 밖에 없게 되며,
개발로직이 처리할 업무를 사람이 처리하게 될 것입니다.

Dec 6, 2018 - google_openRTB

문서 정리겸 정리합니다.

github에 올라온 google openrtb 를 정리해봅니다. google openRTB 연동에 관한 여러 고민이 있었습니다.

The OpenRTB specification

This library implements the protocol / data model from the latest OpenRTB Specification, so reading that should be your first step; it’s also a good introduction to the concepts of real-time bidding.

OpenRTB is only specified in terms of JSON messages, so an implementation has to provide bindings for specific programming languages so you can create and manipulate its message objects in a convenient way, i.e. not needing to deal with the raw JSON representation. We do that by first translating the specification to a Protocol Buffer descriptor, which can be used to generate model classes. This library will only build a Java model out-of-the-box, but you can easily use the same Protobuf descriptor to generate the model code for many other languages. (Other features though, are only available for Java or other JVM-based languages.)

해당 google openrtb는 구글 프로토콜 버퍼 (Protocol Buffer)를 사용하여, data model 을 구성하였습니다.

구글 프로토콜 버퍼란?

<프토토콜 버퍼>는 랭귀지 중립적, 플랫폼 중립적인 데이터 시리얼라이즈 포맷입니다.  

서로 다른 종류의 머신, 서로 다른 종류의 플랫폼에서 동일한 의미를 갖도록 데이터의 포맷을 정의한다는 점에서 <프로토콜 버퍼>라는 이름은 프로토콜(통신)을 위한 버퍼(데이터)을 뜻하게 되며,  
<프로토콜 버퍼>는 이제 구글의 데이터 공용어 (gRPC의 디폴트 데이터 포맷) 입니다.  

What is gRPC?
- 구글이 정의한 RPC
- 구글의 최신 API는 이제 REST API 뿐 아니라 gRPC API도 함께 제공함
- gRPC는 <프로토콜 버퍼>를 기본 데이터 시리얼라이즈 포맷으로 사용 (JSON 등 다른 포맷도 사용 가능)
- 다양한 랭귀지 지원: C++, Java, Python, Go, Ruby, C#, Node.js, PHP, ...
  (JSON을 <프로토콜 버퍼>로 <프로토콜 버퍼>를 JSON으로 변환 가능)
- XML보다 작고, 빠르고, 간단

구글 프로토콜 버퍼를 적용하기 위해서는, IntelliJ protobuf plugin을 설치해야합니다. IntelliJ 의 Plugins 에서는 검색이 되지 않습니다.

https://plugins.jetbrains.com/plugin/8277-protobuf-support

경로에 가서 프로그인을 설치해야합니다. 또한, maven 을 정상적으로 build 하기 위해서는 protoc 를 설치해야하며,

https://developers.google.com/protocol-buffers/

를 통해 설치하였습니다.

releases page에서, protoc-3.6.1-win32.zip 파일을 받아, 윈도우기반에서 환경설정을 하였습니다.

환경설정을 하지 않을 경우에는 경우는 intellij 에, Settings > Other Settings 에서 Protobuf Support 의 경로를 지정해주시면됩니다. 수정할 내용이 있으면 변경후, jar 파일을 만들어서 별도의 import 를 받아 처리해도 되고,

open-core lib에서 dependency 를 추가해도 됩니다. maven 을 사용하기 때문에,

<!-- https://mvnrepository.com/artifact/com.google.openrtb/openrtb-core -->
<dependency>
    <groupId>com.google.openrtb</groupId>
    <artifactId>openrtb-core</artifactId>
    <version>1.5.4</version>
</dependency>

해당 버전을 적용해도 상관없습니다.


참조


Nov 27, 2018 - openRTB

RTB - Real Time Bidding(실시간 경매 방식)

제가 openRTB 프로젝트를 시작하게 된 것은, 2년전 이 시기에 2016년 11월 경이었는데요.
다음카카오쪽에서 카카오스토리와 폴털 지면 등에 RTB를 추가하면서 해당 RTB를 대응하기 위한 구축을 위해서였습니다.

RTB 라는 것이 실시간 경매 방식을 의미하고요.
경쟁자들 사이에서 제일 비싼 값을 치르겠다고 말한 광고주가 광고 지면을 가져가는 형태가 경매 방식과 동일하여 붙여진 이름입니다.
경매 방식 중 이차가격 경매(Second-Price Auction) 등의 시스템이 openRTB에 그대로 사용됩니다.

경매를 하려면 경매를 벌일 사람들과 장소가 있어야 합니다.
[구매자]와 [판매자], [중개소] 가 필요합니다.
1:1로 물건을 사고파는 환경이라면 이 시스템은 있을 필요가 없습니다.

경매 중개소에서 구매자는 자신에게 적합한 물건을 찾고, 이를 판매하려는 판매자에게 가격을 부릅니다.
RTB 광고 시장에도 구매자와 판매자, 중개소가 등장합니다.
DSP(구매자)와 SSP(판매자), 애드 익스체인지(중개소)가 그 역활을 합니다.

제가 개발을 하면서 느낀 점은,
Global 한 OpenRTB 프로젝트의 목표와, 국내 OpenRTB의 상황의 온도차가 조금 존재했습니다.

Global 한 OpenRTB의 경우 기본적으로 바이어와 광고주, 퍼블리셔 간의 커뮤니케이션을 회사 표준안으로 통합해서 제공하여 Real-Time-Bidding 시장을 더욱더 성장시키는 것을 주목적으로 두는 것으로 이해했습니다.

한마디로, OpenRTB의 궁극의 목표는 바이어와 셀러 간의 커뮤니케이션에서 사용될 공통어를 만드는 것이었습니다.
구글의 광고 디자인이 simple 하며, 실제 openRTB의 연동 SSP(& ADExchange) 들의 디자인이 깔끔한 것이 그러한 점을 뒷받침해줍니다.

국내의 경우는 DSP 의 광고의 얼마나 사용자에게 얼만큼의 유용한 광고가 송출되는지도 중요하지만, 그걸 표현하는 디자인을 조금더 감각적으로 표현하려고 합니다.
그러하기 때문에 국내의 대부분의 광고솔류션을 가진 업체들은 RTB가 필수가 아닌,
광고 플랫폼 사이에서 광고주의 편의를 돋는 하나의 기능일 뿐 플랫폼이 존재하는 목적이 아니기 때문에,
상황에 맞는 광고플랫폼에서 그 플랫폼 성격에 맞는 RTB 시스템을 구축하여,
추후에도 해당 광고솔류션의 경쟁력을 그대로 가져가는 형태로 진행되는 형태가 국내의 OpenRTB의 최종 목적지가 되는 것은 아닌가 싶으며, 그런 부분에서 국내와 해외의 openRTB 연동에서 해당 내용을 알맞게 조휼한다는 것은 꽤 어려운 일로 보입니다.

RTB - 실시간으로 각각의 노출을 소비자가 기다리는 시간 동안 경매(입찰)
Exchange - 노출 당 입찰자들 사이에서 경매를 하는 서비스
Bidder - 노출을 얻기 위해 실시간 경매에서 경쟁하는 기업(엔티티)
Seat - 노출을 얻기 원하는 기업이 입찰을 대신할 입찰자를 고용하는 것.
Publisher - 하나 이상의 사이트를 운영하는 기업(엔티티)
Site - 특별한 경우가 아니면 광고는 웹과 응용프로그램을 포함한 콘텐츠를 지원
Deal ID - 특정 조건에 따라 광고 노출을 구매하는 Seat와 Publisher 사이에 기정 계약을 나타내는 ID

참조