1. why we use noSQL

  • 유연성 : rdb와 비교하여, 유연한 스키마를 제공하고 있기 때문에 데이터베이스를 반정형 또는 비정형적인 데이터를 취급하는데 유리하다. 이러한 스키마는 빠르고 반복적인 개발을 유도하게 된다.

  • 확장성 : noSQL 데이터베이스는 고가의 스펙이 뛰어난 서버 대신, 분산형 클러스터를 이용하기 때문에, 서버 운영에 대한 부담 절감과 더 적은 스트리지를 기반으로 운영 할 수 있게 됨에 따라 더 많은 데이터를 담을 수 있게 되었다.

  • 고성능 : 특정 비정형 데이터(document, key-value, graph … ) 및 엑세스 패턴에 대해 특화시킬 수 있기 때문에 특정 워크로드를 유사하게 받아들이는 것이 아닌 그대로 받아들일 수 있게 된다. 이에 따라 유사한 환경을 제공하는 rdb보다 더 뛰어난 성능을 보일 수 있다.

 

 

2. noSQL 데이터 유형

  • key-value : 이 데이터 유형은 rdb와 비교하여 분할성이 크기 때문에 큰 범위로써의 확장을 가능하게 한다. 이로 인해 이 데이터는 게이밍, 광고 및 iot와 같은 사례에서 뛰어난 성능을 발휘한다.

  • document : 어플리케이션 상 데이터는 객체 또는 애플리케이션 코드에서 데이터는 종종 객체 또는json으로 표현된다. 이 형식이 개발자들에게 효율적이며 직관적이기 때문이다. 이 형식을 기반으로하는 데이터베이스를 사용하면 코드 상의 데이터의 형식과 동일한 형식으로 맞출 수 있기 때문에 보다 손쉽게 데이터를 저장하고 query작업을 할 수 있다.

  • graph : SNS와 추천 엔진 등 데이터 간 연결성이 큰 어플리케이션을 쉽게 해주는 데이터이다.

  • in memory : 게이밍과 광고 기술 애플리케이션에는 밀리초의 응답 시간을 필요로 하며, 언제라도 수신 트래픽이 급등할 수 있는 리더보드, 세션 스토어, 실시간 분석을 요구하는 어플리케이션에 특화되어있다.

  • search : 많은 앱에서 로그를 수집하는 환경에서 특화되어있다.

 

 

 

3. RDS vs noSQL

  SQL Databases NOSQL Databases

스키마

구조와 데이터 모델은 사전에 정의되기 때문에 데이터 입력시 레코드에서 사전에 필터링 된 데이터를 입력해야한다. 새로운 유형의 데이터가 저장되기 위해서는 전체 데이터베이스가 변경되어야 한다.

상황에 따라 다르다. 데이터의 분류가 필요없이 즉시 새로운 정보를 추가 할 수 있으며, sql과 달리 다른 필요한 데이터를 같이 저장 할 수 있다.

확장성

수직적이다.

수요를 처리하기 위해서는 더 좋 은 성능의 서버를 이용해야한다.여러 SQL데이터베이스에다 데이터를 저장 할 수 있지만, 이에 따른 추가적인 작업이 일반적으로 필요하다.

수평적이다.

용량을 더 늘리기 위해서는 데이터베이스 관리자가 단숨히 서버나 클라우드 인스턴스를 늘리기만 하면 된다. noSQL데이터베이스는 자동적으로 데이터를 필요한만큼 분산시킨다.

성능

일반적으로 동작하고 있는 시스템에 따라 상이하다. 또한 최고 성능을 위해서 쿼리, 인덱스 및 테이블 구조를 자주 튜닝해야한다.

일반적으로 기본 하드웨어의 클러스터 크기, 네트워크 성능, DB를 호출하는 어플리케이션의 기능에 비례한다.

데이터 호출

ANSI SQL에서 유래한 다양한 명령문으로 데이터를 불러온다

객체를 기반으로 한 API Calling을 데이터를 불러온다.

일관성

강한 일관성

제품마다 상이하다.

예를 들어 MongoDB의 경우는 강한 일관성, Cassandra는 최종 일관성을 기반으로 한다. dynamoDB는 둘다 지원

 

 

최종일관성 : 데이터를 조회 하였을 때, 그 상태의 값 그대로 불러 올 수 있다.

강력한(즉시) 일관성 : 데이터를 조회 할 때, 해당 데이터가 writing상태이면, locking상태가 되어 데이터를 불러올 수 없음.

사진 출처 : https://medium.com/system-design-blog/eventual-consistency-vs-strong-consistency-b4de1f92534d

1. 테이블

  • 데이터 레코드의 집합

 

2. 항목(Item)

  • 테이블에 있는 하나의 레코드 자체를 의미

 

3. 속성(Attribute)

  • 기본적인 데이터 요소로 더 이상 나뉠 수 없는 것, 항목의 조각

  • 속성에 대한 내포 속성은 32 깊이 까지 허용

  • 대부분의 속성은 스칼라(하나의 값만 가질 수 있음)다.

 

 

 

4. primary key

DynamoDB의 PK는 두가지로 구성이 되어 있다.

출처 : https://aws.amazon.com/ko/blogs/database/choosing-the-right-dynamodb-partition-key

4.1 Partition Key

  • 하나의 속성으로 구성되는 단순 기본 키

  • Partition Key의 설계에 따라 데이터의 분산도가 달라진다.
  • Partition Key 선택 기준 (ex. 고객ID, 디바이스 ID, 모델 번호 …)

    • 고유 값이 가장 많은 속성

    • 균일한 비율로 무작위로 요청되는 속성

 

4.2. Sort Key (Range Key)

  • Partition Key와의 조합으로 복합 Primary Key를 구성할 수 있으며, 이를 이용하여 보다 유연하게 DynamoDB에 대한 요청을 처리할 수 있다.

  • Sort Key 선택 기준

    • 1:n, M:N 관계 모델링

    • 효율적 / 선택적 조회

    • 범위 조회

 

 

 

5. Secondary Index

  • Primary Key(Hash Key)만으로 DynamoDB에서 원하는 데이터에 대한 요청을 처리하는 것에 대해 비효율적인 엑세스 패턴이 발생할 수 있다.
  • 이를 방지하고자 테이블에 대해 다른 엑세스 패턴을 설계할 수 있는 Secondary Index개념이 존재하며, RDB개념에서 "View"개념과 비슷하다.
  • Secondary Index에는 GSI(Global Secondary Index) LSI(Local Secondary Index)가 존재한다.
  • GSI의 default limit 개수는 20이다(Support Center를 통해 증가 요청 가능).
  • LSI의 default limit 개수는 5이다.

 

 

GSI(Global Secondary Index)

LSI(Local Secondary Index)

키 속성

  • 기본 테이블과 파티션키, 정렬키 모두 다르게 하여 새로운 primary key를 설정 할 수 있다.

  • 그러나 Hash Key의 항목은 무조건 생성한 GSI의 attribute로 포함되어야 한다.
  • 기본 테이블과 동일한 파티션 키가 있지만, 정렬 키가 다른 인덱스다.

파티션 키 당

인덱싱 크기 제한

  • 크기 제한 없음

  • Partition Key값마다 인덱싱 된 모든 항목의 전체 크기는 10GB 이하.

읽기 일관성

  • 최종 일관성만을 지원한다.

  • 최종 일관성, 강력한 일관성을 지원한다.

프로비저닝된 처리량 소비

  • 읽기 및 쓰기에 대해 기본 테이블과는 다른 별도의 용량단위가 존재한다.

  • 기본 테이블 업데이트 시 발생되는 인덱스 업데이트에도 인덱스 용량단위를 사용한다.

 

  • 기본 테이블의 용량단위를 소비한다.

  • 기본 테이블에 쓰기를 실행할 경우, 기본 테이블의 용량단위를 쓰기 때문에 Capacity Unit이 상대적으로 GSI보다 높게 나올 수 있다.
프로젝션
  • GSI에 존재하는 attribute에 대해서만 projection을 수행할 수 있다,
  • LSI에 존재하지 않고 기본테이블에 존재하는 attrubute에 대해 query 또는 scan할 경우, 기본 테이블에서 해당 속성을 자동으로 가져와 결과를 반환한다.

 

 

6. WCU, RCU

  • AWS의 기본 사상은 "쓰는만큼만 지불"하는 개념이다. 이 개념은 DynamoDB에도 그대로 적용되며, 읽기 및 쓰기하는 용량에 따라서 과금의 정도가 다르다.
  • 읽기에 대한 용량을 RCU(Read Capacity Unit)라 부르고, 쓰기에 대한 용량을 WCU(Write Capacity Unit)라고 부른다.
  • 프리티어로 25 WCU, 25 RCU가 제공된다.
  • ex) 테이블 25개 생성, 각 테이블 별로 1 RCU, 1 WCU의 프로비저닝 된 용량을 할당 -> 총 25 RCU, 25 WCU Free Tier Limit을 넘지 않음
  • ex) 테이블 2개 생성, 각 테이블 별로 15 RCU, 15 WCU의 프로비저닝 된 용량을 할당 -> 총 30 RCU, 30 WCU Free Tier를 제외한 나머지 5 RCU 및 5WCU에 대해 과금

 

6.1. RCU (Read Capacity Unit)

읽기의 경우, Default로 최종적 일관된 읽기가 적용된다.

  • 최종적 일관된 읽기의 경우, 1RCU = 8 KB / s

  • 강력한 일관된 읽기의 경우, 1RCU = 4 KB / s

6.2. WCU (Write Capacity Unit)

  • 1 WCU = 1 KB / s

  • 계산시, 1KB 단위로 올림하여 계산한다.
    ex) item 크기가 3.6KB 항목 하나를 쓸 경우, 4KB로 계산하며, 4WCU를 소비한다.

 

 

 

6.3. 참고 - 일관성

최종적 일관된 읽기(Eventual Consistent Read)

테이블을 읽을 때, 응답은 최근 완료된 쓰기 작업의 결과를 반영하지 않을 수 있다. 그러므로, 응답에는 부실 데이터가 일부 포함될 수 있다.

 

강력한 일관된 읽기(Strongly Consistent Read)

성공한 모든 이전 쓰기 작업의 업데이트를 반영하여 최신 데이터를 기반으로 응답을 반환한다. 그러나 DynamoDB에서 이를 적용하면 다음과 같은 단점을 수반한다.

- 강력한 일관된 읽기는 최종적 일관된 읽기보다 지연 시간이 더 길 수도 있습니다.

- 글로벌 보조 인덱스에서는 강력히 일관된 읽기가 지원되지 않습니다.

- 강력한 일관된 읽기(strongly consistent read)는 네트워크 지연 또는 중단이 발생한 경우에 사용이 어려울 수 있습니다. 이 경우 DynamoDB는 서버 오류(HTTP 500)를 반환할 수도 있습니다.

 

 

 

7. 참고 문헌

 

기본적인 프로젝트를 생성하면 다음과 같은 web.xml이 생성되어 있을 것이다.

기본적으로 "/"경로를 호출할 때, 쓰일 수 있는 페이지의 형식을 <welcome-file>로 정의해 놓았다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>servlettest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

 

 

앱을 외부로 디플로이 하기 위해 자신의 프로젝트를 톰캣에 추가 하였을 것이다.

web.xml에 매핑을 하지 않았을 경우, Servlet을 호출 할 때

http://<YOUR_IP_ADDRESS>/<ROOT_CONTEXT>/<패키지 이름이 포함된 클래스 이름>

과 같이 매우 긴 주소로 호출 할 것이다.

 

 

하지만 이러한 형식은 클래스 이름이 그대로 노출되기 때문에 보안상 좋지 않습니다. 따라서 이런 방식으로 사용하지 않고, 서블릿 클래스에 대해 대응하는 매핑된 이름으로 실제 서블릿을 요청한다.

 

매핑을 web.xml에서 정의 할 수 있는데 정의하는 방식은 다음과 같다.

  <servlet>
  	<servlet-name>mappingname</servlet-name>
  	<servlet-class>package.YourServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>mappingname</servlet-name>
  	<url-pattern>/name</url-pattern>
  </servlet-mapping>

 

1. <servlet> 태그

이 태그는 프로젝트 내부의 servlet를 외부에 드러낼 수 있도록 servlet에 이름을 지어주는 역할을 한다.

<servlet-name>태그를 이용하여 서블릿의 이름을 지어주고,

<servlet-class>태그를 이용하여 이름을 지어 줄 서블릿을 선언한다.

 

 

2. <servlet-mapping> 태그

이 태그는 내부의 servlet와 외부 요청에 대한 논리적인 경로를 설정해준다.

<servlet-name>태그를 이용하여 외부에 드러낼 서블릿의 이름을 설정한 다음,

<url-pattern>태그를 이용하여 외부에서 접근할 url 경로를 설정해준다.

 

 

 

 

위를 테스트 하기 위해 나는 test패키지를 생성하여 testServlet 클래스를 생성하였다.

package test;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class testServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("doGet method is called");
		super.doGet(req, resp);
	} // get 요청에 대한 override class이며, get방식에 대해 요청을 처리하는 클래스이다.

	@Override
	public void destroy() {
		System.out.println("destroy method is called");
		super.destroy();
	} // servlet이 종료될 때 호출되는 class

	@Override
	public void init() throws ServletException {
		System.out.println("init method is called");
		super.init();
	} //최초 servlet call을 다루는 override class
	
	

}

 

 

이 Servlet 클래스를 논리적인 주소로 매핑시키기 위해 다음과 같이 web.xml을 수정하였다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>servlettest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>mappingname</servlet-name>
  	<servlet-class>test.testServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>mappingname</servlet-name>
  	<url-pattern>/name</url-pattern>
  </servlet-mapping>
</web-app>

mappingname이라는 이름으로 test 패키지의 testServlet클래스에 대해 이름을 지어준 뒤,

"/name" 이라는 논리적인 경로와 매핑시켜주었다.

 

 

 

다음은 테스트 결과 캡쳐 화면이다.

 

위와 같이 잘 호출 되었음을 알 수 있다.

+ Recent posts