빅데이터 기반 분석은 많은 엔터프라이즈에서 기본적으로 고려하는 부분이 되었으며, 이에 대해 기본적으로 이해하는데 도움이 되는 몇가지 기본 지식들을 정리해보았다.

 

최근의 데이터 분석의 추세는 Data Gravity 라는 용어로 시작할 수 있을 것 같다. 기술적인 용어라기 보다는 추세를 비유하는 용어에 가까운데, 이 개념은 다음과 같다

 

  • 개념 : 데이터 규모가 커지고 무거워질 수록 비즈니스를 끌어당기는 중력(Gravity) 이 강해지는 현상을 나타내는 개념
  • 성질 : 데이터를 저장할 스토리지, 데이터를 운용 및 관리할 인력, 데이터 활용을 위한 어플리케이션 등 비즈니스는 데이터에 기반해서 함께 이동한다

즉, 데이터 기반의 의사 결정을 통해 서비스의 방향이 결정되고 향후 비즈니스 가치를 판단하는 기준이 바뀔 수 있다는 측면이다.

이러한 데이터 기반의 의사 결정을 위해 데이터를 다루는 큰 두가지 틀이 있을 수 있다.

 

(1) Data Warehouse (데이터 웨어하우스)

  • 데이터를 저장해두는 창고. 데이터 분석이 필요할 때 창고의 데이터를 가져다 이용하자는 개념
  • 분석을 위해 OLAP 툴 또는 SQL 을 이용하여 최종 정보 이용자들이 활용한다
  • 데이터들에 대해 정형화된 형식으로 통합해 단일 형식으로 구성
  • 복잡한 데이터 모델에 대해 갖고 있는 데이터 모델에 제약을 맞춰야하는 이유 등으로 인해 데이터 모델 통합이 어렵고, 인프라 증설에 따른 운영 비용이 많이 발생

 

데이터 웨어하우스는 잘 구성된 데이터를 규격화된 데이터베이스에 적재해놓고 쿼리를 조합해서 필요한 정보를 얻어내는 방식이다.

가장 친숙하게는 관계형 데이터베이스(RDB) 에서 쿼리를 통해 데이터를 조회하는 것도 웨어하우싱 방식의 접근 방식이라 할 수 있겠다.

 

장점으로는 적재(Storing)된 데이터를 정해진 Case 에 대해서는 다루기가 아주 쉽다. 정형화된 형식이기 때문에 마케팅, 사업 등 엔지니어가 아니라도 쉽게 조회 및 집계 등이 가능하다.

단점으로는 데이터를 잘 구성해서 적재해주는 측면이 중요하다. 특히 최근에 생성되는 비정형 데이터를 사용할 수 있는 형태의 정형 데이터로 바꿔줘야 한다. 또한 데이터가 적재되는 방식은 데이터가 사용되는 방식과 다른 경우가 많아 확장성을 갖추기가 쉽지 않다.

 

 

(2) Data Lake (데이터 레이크)

  • 정형 데이터로 구성된 전통적 소스 외에도 수많은 비정형 데이터들을 실시간으로 수집, 정제, 통합하여 활용하기 위해 확장된 개념
  • Raw 데이터 형식으로 저장했다가 나중에 쉽게 분석할 수 있도록 구성
    • 이를 위해 분산 시스템 환경에서 데이터를 독립적으로 쪼개고 다시 취합하는 기법을 사용
    • 하둡과 같은 맵리듀스 방식이 대표적
  • 데이터를 구축하고 활용하는 기술이 어려워서 습득과 활용이 쉽지 않다

데이터 레이크는 데이터 웨어하우스에서 접근 방식을 바꾼 방법이다. 비정형 데이터를 정형화시키지 않고 Raw 데이터 형태로 적재하고, 사용할 때 필요에 맞게끔 구성하는 방법이다.

 

장점으로는 데이터의 수집(Ingestion) 및 적재(Storing) 에 있어서 부담을 덜 수 있다. 데이터의 정형화에 대한 부담이 적기 때문에 새로이 비즈니스 확장이나 새로운 데이터가 들어오더라도 비정형으로 그냥 적재하면 된다. 따라서 데이터의 수집 자체가 더 용이하다.

단점으로는 결국 데이터는 사용 시에 Use Case 에 맞게 형변환되어야 한다. 따라서 적재된 데이터의 사용을 위해 별도의 인프라 및 시스템이 필요한 경우가 많다. 이런 측면으로 인해 비개발자가 다루기가 쉽지 않다.

 

 

최근의 추세는 Data Lake 형태로 온갖 데이터를 몰아넣고, 이를 적절한 사례에 맞게 가져가 쓰는 것으로 보인다.

비즈니스 확장성이 중요해진 추세다보니, 언제 어떤 데이터가 어떻게 쓰일지 모르고, 따라서 일단 적재해놓고 적합한 사용 사례에 맞게 데이터 분석 시스템을 모두 갖춰놓고, 이용하는 방식으로 많이 사용되어지는 것 같다.

이제 빅데이터도 어느새 알아야하는 기본 지식이 되어가기 때문에, 기반 지식들을 알아두면 많이 도움이 될 것으로 보인다.

 



Cache 가 저장된 데이터를 Flush 시키는 전략은 캐시 서버의 유지에 있어서 중요한 부분이다.


가령 캐시에 저장된 데이터가 실제 데이터와 동기화가 안되어 잘못된 값이 참조된다거나, 이전 데이터가 만료되지 않는 경우,

혹은 저장된 정보인데 Key 값이 Mismatch 나거나 불필요하게 Flush 되어 Cache hit-ratio 가 낮아진다면 이는 전체 서버 구조에 영향을 미치게 된다.


다음은 Cache 서버를 사용할 때 유의해야할 몇가지 Cache 의 Flushing 전략 들을 정리해보았다.


주로 사용되는 캐시인 Redis 위주로 정리가 되어있으며, Cache 자체에 대한 범용적인 정리이지만 예시는 Redis 위주이며 캐시의 종류에 따라 차이가 있을 수 있다.



1. Cache 의 Flushing 은 일반적으로 Active 한 방법과 Passive 한 방법이 있음을 이해하자.


 : Active 한 방법은 별도의 요청에 따라 Cache 를 Flush 시키는 방법이고, Passive 한 방법은 자체에 Expire Time 을 이용해서 외부에서 별도 요청없이 캐시를 비우게끔 만드는 전략이다.

 

 만약 Active Flushing 을 하지 않는다면, 해당 캐시는 수동적으로만 갱신되므로 실시간적인 데이터 동기화 및 캐싱이 불가능하다.

 Passive Flushing 을 하지 않는다면, Active 한 방식으로 Flushing 되지않는 데이터는 영구히 캐시 서버에 남아서 공간을 차지하게 되며 이는 큰 비용이 될 수 있다.


 반대로 지나치게 Active 한 Flushing 이 많아진다면, Cache 는 Read 요청 대비 Write 요청의 비율이 높아져 쓰는 효율이 없어지게 되고, Passive Flushing 도 지나치게 짧은 단위로 Flushing 이 일어난다면 Cache hit ratio 가 줄어들 것이므로 설계에 유의가 필요하다.

 

 일반적으로 API 의 설계에 맞춰 Active Flushing 타이밍을 정확히 맞추고, Passive Flushing 을 위한 Expire Time 의 설정은 선험적 지식 또는 벤치마킹 등에 의해 측정되어 설정되게 된다.

 


2. Cache Refresh / Expire reset 기준을 확인하는 것이 중요하다.


 : 사용하는 캐시 서버 및 설정에 따라 다를 수 있지만, Cache Refresh 의 기준을 확인하는 것은 중요하다. 

 가령 Redis 의 경우 Cache hit Read 기준으로 Expire reset 이 발생하기 때문에, 계속적으로 Cache hit 이 발생하면 자동으로 Expire 는 되지않는다.

 단순 기능이라면 큰 문제가 안되겠지만 캐시를 바라보는 경우가 많을 경우 문제가 생기기 쉬운 상황이므로 잘 체크해주어야 한다.


 즉, 지속적으로 Cache-hit 이 발생하는 한, Active 한 방식의 Flushing 이 아닌 Passive Flushing 을 기대하는 건 잘못된 방식이다.

 개인적으로 QA 프로세스에서 Caching 문제가 발생하고 있었는데, 이 부분을 간과해서 문제가 해결되지 않은 버전이 Live 에 포함될 뻔 한 일이 있었다.



3. 사용하는 캐시 서버의 Expire 로직을 이해하자.


 : Cache 를 사용할 때 캐시 서버의 로직을 파악하고 있는 것이 중요하다. 중요한 것은 사용하는 Cache 서버가 어떻게 내부적으로 동작하냐는 것이다.

 가령 Redis 의 경우 일정한 주기를 갖고 랜덤하게 key 들을 뽑아서 expire 을 검사하고 expiration rate 가 높을 경우 다시 일정 주기로 검사하는 루틴을 지닌다.

 이러한 구조를 이해하는 것은 서버 설정하는 데 있어서 기본이 되며 보다 나은 환경을 구축하는 데 도움을 준다.




캐시에 대한 현업에서 이슈들로 겪으면서 가장 중요했던 기본 내용들만 정리해보았다.


실제 이슈와 그에 대한 트러블 슈팅은 따로 포스팅에 정리하도록 하겠다.




Hibernate 를 이용한 토이프로젝트를 수행하면서... 큰 삽질을 해서 정리해보는 포스팅 ㅜㅜ


hbm2ddl.auto 설정은 hibernate 포함된 매핑 설정으로 Database 스키마 생성 스크립트를 만드는데 사용된다


시작시마다 Mapping 설정대로 Schema 세팅할 있는데 설정 값에 따라 다음과 같이 동작한다.


-      Create : Session Factory 실행될 스키마를 지우고 다시 생성. 생성 후에는 classpath import.sql 파일이 있는지 찾아 등록된 쿼리문을 실행한다.


-      Create-drop : 기본적으로 create 동일하지만 session 팩토리가 내려갈 db 스키마를 삭제한다. (이걸로 설정했다가 디비 날려먹었었다.)


-      Update ; 도메인 객체구성과 스키마를 비교해서 도메인 객체에 맞춰서 db 스키마를 변경한다. 데이터나 스키마를 지우지 않는다.


-      Validate : 도메인 객체구성과 db스키마가 같은지만 확인할 변경하지 않는다. 다를 session Factory 시작시 확인하고 예외를 발생시킨다.


다음은 Hibernate 사용시 알아두어야 하는 JPA 프로퍼티들이다.






ORM(Object Relational Model)은 사물을 추상화시켜 이해하려는 OOP적 사고방식과 DataModel을 정형화하여 관리하려는 RDB 사이를 연결할 계층의 역할로 제시된 패러다임으로 RDB의 모델을 OOP에 Entity 형태로 투영시키는 방식을 사용한다. 


이는 시스템에 따라, 사용하는 Database 및 DB Connector 에 따라 달라질 수 있는 데이터 매핑 구조를 객체지향형태로 통일시켜, SQL 구조의 Database를 OOP 구조의 형태로 매핑시키려는 패러다임이다.


OOP 적 구조와 SQL 구조의 차이는 데이터를 다루는 방법에서 나타난다. OOP 적 구조에서 모든 데이터는 객체이며, 각 객체는 독립된 데이터와 독립된 함수를 지닌다. 


반면 SQL 에서 데이터는 테이블단위로 관리되며 객체들을 조회하기 위한 명령어를 사용한다.

ORM 은 각 테이블 또는 구분하고자하는 데이터 단위로 객체를 구현하고, 데이터간의 관계를 형성한다. 가령 개인 정보 저장을 위한 RDB의 테이블 Personal 은 다음과 같은 객체 Personal 로 매핑될 수 있다.



이러한 ORM을 구현하는 대표적인 프레임워크가 Hibernate이며, 이를 Java 표준 방식으로 정의한 것이 JPA이다. 실무에서 개발을 할 때에는 JPA 와 Hibernate 를 같이 사용한다.


위와 같은 매핑을 ORM 을 제공하는 프레임워크들은 매우 간단한 방식으로 지원한다. 가령 JPA와 Hibernate 와 같은 Java 의 ORM 들은 Annotation 을 이용해서 간단한 매핑이 가능하다.

이러한 중간 계층을 Persistence Layer라 하며, ORM 프레임워크 들은 기존의 Entity Bean이 수행하던 Persistence Bridge 의 역할을 ORM 패러다임의 Entity 방식으로 가능하게끔 한다. 


적용되는 기준은 Data Entity와 Object 모델이 Getter와 Setter 그리고 Repository를 이용한 표준 형식으로 정의되며, RDB에서 표현할 수 있는 Join 관계 및 참조 관계, Native 쿼리 등 거의 모든 부분의 매핑이 가능하다.


ORM 은 데이터를 다루는 새로운 관점을 제시하지만 적용하는 데 있어 몇가지 고려해야할 장단점을 갖고 있다. 이는 ORM이 흔히 겪는 RDB와 OOP 사이의 아직 풀지 못한 숙제들을 말하며 다음과 같다.


1. 세밀함의 불일치(RDB 데이터 타입은 Vendor마다 다르며, 더이상 정규화가 힘들다.)


2. 하위 타입 문제 (RDB에는 상속의 개념이 없다.)


3. 동일성 문제(Java의 경우 Equals로 평가 가능하지만, DB는 PK 기준으로 한다.)


4. 연관 관계 – FK는 연관의 방향성을 갖지 못하기 때문에 N:N 모델의 경우 Link 테이블을 도입해야 한다.


5. 데이터 검색 – RDB는 한번에 많이 가져올지(메모리) 빈번하게 데이터를 가져올지(성능) 고민해야 한다.


위의 장단점은 표준 ORM 패러다임이 기존 시스템에 비해 갖는 괴리감 및 과제들이며, 많은 프레임워크들이 위의 숙제를 해결하기 위한 기능들을 제공한다.


하지만 무엇보다도 ORM 을 도입하는 데 있어서 중요하게 생각해야 할 점은 ORM 은 결코 RDB를 개발자로부터 분리시키는 기술이 아니라, 오히려 개발자에게 높은 RDB 지식을 요한다는 사실이다.

개발자는 객체 구조의 설계 시에도 Database 의 스키마를 고려해야하는 숙제를 갖게 되는 것이다.


이는 진입 장벽이 높아지는 단점으로 여겨질 수도 있으나 오히려 숙련된 개발자가 있다면, DB와 코드를 좀 더 직관적으로 연결하고 다룰 수 있게 하는 기술이 될 수 있다.


ORM 의 개념은 아직 우리나라에서는 익숙하지 않다. 아직 많은 개발자들이 SQL Mapper로 대표되는 MyBatis를 선호하는 추세이지만 전세계적으론 Application Service 기술 동향에 있어서 데이터를 다루는 기술의 하나로 각광받고 있다.



* 향후 포스팅의 카테고리를 옮길 예정입니다.



 MyBatis 를 접하고 업무를 할 때 대부분의 일은 Sql Mapper 를 만들고, SELECT, INSERT, UPDATE, DELETE 와 같은 CRUD 작업을 위한 DML(Data Manipulation Language) 를 작성하는 업무이지만, 간혹 Create Table, Drop Table 과 같은 DDL(Data Definition Language) 을 작성해야할 경우가 있다.


 Mybatis를 이용해서 DDL 쿼리도 적용이 가능하다. 

예를들어 CreateTable / DropTable / ShowCreateTable / DescTable 등인데 이 내용이 Mybatis 백서에도 제대로 안나와있다.

결론적으로 말하면 그냥 사용하면 되는데, 사용방법은 다음과 같다.



<select id="existTable" resultType="Integer">
SELECT count(TABLE_NAME)
FROM
INFORMATION_SCHEMA.TABLES
where
TABLE_SCHEMA = #{databaseName, jdbcType=VARCHAR}
AND
TABLE_NAME=#{tableName, jdbcType=VARCHAR}
</select>

<select id="showCreateTable" resultType="java.util.Map" parameterType="java.lang.String">
show create table ${value}
</select>

<update id="dropTable" parameterType="java.lang.String">
drop table IF EXISTS ${value}
</update>

<update id="createNewTable" parameterType="java.lang.String">
${value}
</update>


 (참고로 위의 예제에 ${value} 부분은 MyBatis 의 동적쿼리문을 사용한 부분으로 Parameter Type 을 String 으로 한다면 value 라는 명칭을 사용해야 한다. 내부적으로 String.value 를 이용하기 때문이다. 또한 위의 예시에서 createNewTable 로 매핑된 부분의 ${value} 에는 Create Table 쿼리가 포함되면 된다.)


 다만 한가지 주의할 점은, DML(Data Manipulation Language)가 아닌 위와 같은 DDL(Data Definition Language) 들은 대부분 트랜잭션을 사용하더라도 롤백이 되지 않는다.


몇가지 SQL 구문들은 MySQL 이 암묵적으로 커밋을 하기 때문에 트랜잭션을 감싸더라도 실행하면 롤백이 불가능하다. 트랜잭션이 불가능한 대표적 구문들은 다음과 같다.


CREATE / ALTER / DROP DATABASE

CREATE /ALTER / DROP / RENAME / TRUNCATE TABLE

CREATE / DROP INDEX

CREATE / DROP EVENT

CREATE / DROP FUNCTION

CREATE / DROP PROCEDURE





 MyBatis 란 객체지향 언어인 Java 와 SQL Based 인 관계형 데이터베이스(RDBMS) 사이의 데이터를 다루는 방식의 괴리를 해결하기 위해 만들어진 Persistence Framework 의 일종이다.


 Java 에서 DB와의 Connection 을 위해 제공하는 JDBC 를 Wrapping 한 구조로 되어 있으며, 기존의 JDBC 에 비해 많은 장점들을 갖고 있다.


무엇보다 사용이 간단하고, 60% 정도의 생산성 향상이 있다고 한다.

또한 JDBC 사용시 매번 Query Statement 를 생성하는 구문을 작성해야 했던 것과 다르게 쿼리의 재사용과, 코드와의 분리가 좀 더 수월해졌기 때문에 유지 보수 측면에서도 이점을 갖는다.


 주로 mybatis-config.xml 과 같이 별도의 파일에 환경 설정을 분리시키며, 변수 형태로 DB 연결정보들을 관리한다.

간단한 설정 방법은 다음과 같다.



/* 설정 <pom.xml> */
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>



 Maven 을 사용한다면 위와 같이 먼저 pom.xml 에 MyBatis Dependency 를 기입해주어야 한다. 이후 다음과 같이 프로젝트별로 관리하는 개별 설정 파일에 내용을 기입해주면 된다.



/* 설정 <설정파일.xml> */

//Property 설정 분리
<properties resource="mybatis/config.properties">
<property name="username" value="jinsp"/>
<property name="password" value="epD@kef0"/>
</properties>


//커넥션 풀 사용
<dataSource type="POOLED">
//Database Driver
<property name="driver" value="${driver}"/>
//Database Url
<property name="url" value="${url}"/>
//Database UserName
<property name="username" value="${username}"/>
//Database Password
<property name="password" value="${password}"/>
</dataSource>



 이렇게 설정해준다면 MyBatis 의 SqlSessionFactory 를 통해 Connection 을 연결할 수 있다. MyBatis 의 사용을 위해서는 매퍼 파일(Mapper File) 이라 불리는 Xml 설정이 추가로 필요하다. 이는 다음과 같은 형태로 구성이 되어 있다.




<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="StudentRepository">
<select id="selectStudentList" resultType="student">
SELECT
`id` AS "studentId",
`name` AS "studentName"
FROM
student
</select>
</mapper>



 위의 Mapper 설정에 대한 설명을 간단히 하자면, 설정들이 StudentRepository 라는 @Repository 로 명시된 클래스에 매핑되며, 그 안에 학생들의 리스트를 조회하는 쿼리문인 selectStudentList 를 사용할 수 있게 설정되어있다는 뜻이다. selectStudentList 라는 Id 를 가진 SQL 문은 쿼리문의 결과(ResultType)를 Student 형태의 객체의 형태로 반환하며, 요소가 여러개일 경우 List 형태의 Collection 으로 반환한다.


위와 같이 설정을 하고, 위에 명시된 쿼리의 ID 를 SessionFactory 를 통해 불러와서 실행시켜주면 연동이 간단하게 끝난다.


+ Recent posts