728x90
반응형
728x90
반응형
반응형

포인트

  • 하루 100만건 = 초당 10건의 트랜젝션(TPS)
  • 10TPS는 별 문제 없는 양, 트렌젝션의 정확한 처리가 중요
  • 각 거래별 멱등성을 위한 트렌젝션 아이디 필요(UUID)
  • 금액은 double이 아닌 string으로 직렬화/역직렬화에 사용되는 숫자 정밀도가 다를 수 있기 때문(반올림 이슈)

디비

  • 성능(nosql)보다는 안정성, ACID를 위한 관계형 데이터 베이스 선호

데이터 저장 시 고려

  • 결제 -> 지갑; 결제 -> 원장 테이블에 저장 시 상태값을 계속 변경하여 관리..
  • 지갑/원장 테이블의 상태가 모두 바뀌어야 결제 테이블 상태 업데이트..

시스템 구성 요소가 비동기적으로 통신하는 경우 정확성 보장?

  • 관련 상태값이 특정 시간동안 부적절한 상태로 남는지 확인하는 배치 돌려서 개발자에게 알람
  • 조정: 관련 서비스 간의 상태를 주기적으로 비교하여 일치하는지 확인(마지막 방어선)
    • 약간 파일 대사 같은 느낌.. 원장과 외부 업체와 데이터 비교

조정 시 불일치가 발생하면

  1. 어떤 유형의 문제인지 파악하여 해결 절차를 자동화
  2. 어떤 유형의 문제인지는 알지만 자동화 할 수 없으면(작업 비용이 너무 높으면) 수동으로
  3. 분류가 불가(판단이 안됨) 수동으로 처리하면서 계속 이슈 트래킹필요

 

동기 통신

  • 성능 저하
  • 장애 격리 곤란
  • 높은 결합도/낮은 확장성(트래픽 증가 대응 힘듦)

비동기 통신

  • 단일 수신자: 큐 하나를 한 수신자가 처리. 병렬 처리를 위해 스레드 여러개가 동시에 처리하게 함. 큐에서 구독 후 바로 삭제됨
    • 보통 rabbitMQ사용
  • 다중 수신자: 큐 하나를 여러 수신자가 처리. 동일한 메세지를 각기 다른 호흡으로 읽어감. 읽어도 삭제되지 않음. 하나의 메세지를 여러 서비스에 연결할 때
    • 보통 카프카 사용
  • 실패 시 재시도 큐(일시적 오류) / dead letter queue로 이동(모든 재시도 실패)

 

정확히 한 번 전달? exactly once

  • 최소 한 번 실행 at least once: (지수적 백오프 사용하여) 재시도; 시스템 부하와 컴퓨팅 자원 낭비
  • 최대 한 번 실행 at most once: 멱등성 보장; 고유 키를 멱등 키로 잡아야
    • 광클 시 키 값을 확인하여 이미 처리되었는지 확인하고 되었으면 재처리 하지 않고 이전 상태와 동일한 결과 반환
    • 결제가 되었지만 타임아웃 나서 결과가 시스템에 전달되지 못함 ->  다시 결제 시도 시 이중 결제로 판단하여 종전 결과 반환

 

분산 환경에서 데이터 불일치가 발생할 수 있는데 일반적으로 멱등성조정 프로세스를 활용한다.

디비 불일치는 master-replica 동기화 

 

728x90
반응형
반응형

환경: java17

 

자바11에 로컬 변수에 대한 타입 추론이 가능해졌다. 즉 아래와 같이 구체적인 타입을 선언하지 않고 var로 선언이 가능해졌다.

var items = (List<SinyutnoriRanking>) chunk.getItems();

그리고 자바 17을 사용중이다. 그런데 아래와 같은 경고창을 만난다.

Unchecked cast: 'java.util.List<capture<? extends xx.Ranking>>' to 'java.util.List<xx.Ranking>'

위 경고는 컴파일러가 타입 추론을 못해서 발생하는데, 자바 17이면 당연히 자바 11의 내용을 알고 있기에 컴파일러 단에서 타입추론이 되는거 아니야? 하는 생각이 들었다.

제네릭과 타입 추론의 한계

Java 11에서 var를 도입하면서 로컬 변수의 타입 추론이 가능해졌지만, 제네릭 타입의 안전성을 보장하기 위해 여전히 unchecked 경고가 발생할 수 있다.

 

  • var를 사용하면 컴파일러가 변수의 타입을 자동으로 추론하지만, 제네릭 타입에서 타입 캐스팅이 일어나는 경우 여전히 unchecked 경고가 발생할 수 있다.
  • 제네릭 타입을 사용할 때, Java 컴파일러는 타입 소거(Type Erasure)를 사용하여 런타임에 타입 정보를 제거한다. 이로 인해, 컴파일러가 타입 안전성을 완전히 보장할 수 없는 경우 경고를 발생시킨다.
  • 이런 경우 var를 사용하더라도 타입 캐스팅이 명시적이든 암시적이든 타입 안전성을 보장할 수 없으므로 @SuppressWarnings("unchecked")가 필요하다.
List<String> list = (List<String>) new ArrayList(); // Unchecked cast warning

var list = (List<String>) new ArrayList(); // 경고 발생

 

@SuppressWarnings("unchecked")

타입 안정성을 보장할 수 없는 상황에서 컴파일러 경고를 무시하도록 명시적으로 @SuppressWarnings("unchecked")를 추가한다. 이는 컴파일러에게 "나는 이 경고를 알고 있으며, 이 코드가 안전하다는 것을 보장할 수 있다"라는 신호를 주는 것이다.

 

참고로 최신 버전의 IntelliJ IDEA나 다른 IDE를 사용해도, 컴파일러 경고 자체는 여전히 발생할 수 있다는 점..!

728x90
반응형
반응형

환경: mac

 

설치

brew install redis

 

접속

// 단일 노드 접근
redis-cli -h alpha-redis-master.abc.net -p 6006 -a {pwd}

// 클러스터 접근
redis-cli -c -h alpha-redis-master.abc.net -p 6001 -a {pwd}
728x90
반응형

'서버 세팅 & tool > vm on mac' 카테고리의 다른 글

[terminal] alias in mac terminal  (0) 2022.06.08
[parallels] local server not working on parallels  (0) 2022.04.11
[vm] nginx 설치  (0) 2022.02.24
[parallels] nox...... 99%.....  (0) 2022.02.23
[vm] axon server 설치  (0) 2022.01.12
반응형

2024.09.21 - [개발/java] - [java8+] 함수형 프로그래밍 @FunctionalInterface

 

일급 객체 (First-Class Object)

  1. 변수에 할당할 수 있다: 함수를 변수에 할당할 수 있다.
  2. 인자로 전달할 수 있다: 함수를 다른 함수의 인자로 전달할 수 있다.
  3. 반환값으로 사용할 수 있다: 함수를 다른 함수의 반환값으로 사용할 수 있다.
import java.util.function.Function;

public class FirstClassObjectExample {
    public static void main(String[] args) {
        // 함수가 변수에 할당됨
        Function<String, String> greet = name -> "Hello, " + name + "!";

        // 함수가 매개변수로 전달됨
        sayHello(greet, "Alice");

        // 함수가 반환 값으로 사용됨
        Function<String, String> greetFn = getGreetFunction();
        System.out.println(greetFn.apply("Bob"));
    }

    public static void sayHello(Function<String, String> fn, String name) {
        System.out.println(fn.apply(name));
    }

    public static Function<String, String> getGreetFunction() {
        return name -> "Hi, " + name + "!";
    }
}

고차 함수 (Higher-Order Function)

고차 함수는 다음 중 하나 이상의 조건을 만족하는 함수:

  1. 다른 함수를 인자로 받을 수 있다.
  2. 다른 함수를 반환할 수 있다.

Java 8의 Stream API는 함수형 프로그래밍의 개념을 적극 활용한다. 메서드 체이닝을 통해 고차 함수의 형태로 map, filter, reduce 등의 연산을 수행할 수 있다.

Java에서 콜백을 처리할 때 고차 함수가 자주 사용된다. 특정 작업이 완료되었을 때 실행할 동작을 함수로 전달할 수 있다.

import java.util.function.Consumer;
import java.util.function.Function;

public class HigherOrderFunctionExample {
    public static void main(String[] args) {
        // 함수를 매개변수로 받는 고차 함수
        repeat(3, i -> System.out.println("Hello, " + i));

        // 함수를 반환하는 고차 함수
        Function<Integer, Integer> doubleFn = createMultiplier(2);
        System.out.println(doubleFn.apply(5));  // 10
    }

    public static void repeat(int n, Consumer<Integer> action) {
        for (int i = 0; i < n; i++) {
            action.accept(i);
        }
    }

    public static Function<Integer, Integer> createMultiplier(int multiplier) {
        return value -> value * multiplier;
    }
}

용어가 비슷해서 헷갈리지만 다른....(객체 지향 관련 개념)

일급 컬렉션 (First-class Collection)

일급 컬렉션은 컬렉션을 직접 사용하지 않고 컬렉션을 래핑하는 클래스를 만들어, 해당 클래스를 통해서만 컬렉션을 조작하도록 하는 디자인 패턴이다. 이를 통해 코드의 명확성과 유지 보수성을 높이고, 불변성을 보장할 수 있다.

  1. 불변성 보장: 일급 컬렉션 클래스는 컬렉션에 대한 직접적인 접근을 방지하고, 불변성을 유지하도록 도와준다.
  2. 비즈니스 로직 캡슐화: 컬렉션에 대한 비즈니스 로직을 일급 컬렉션 클래스 내부에 캡슐화하여 코드의 응집도를 높임.
  3. 컬렉션 관련 메서드 제공: 컬렉션을 조작하기 위한 메서드들을 일급 컬렉션 클래스에서 제공하여, 코드의 명확성을 높임.
  4. 특정 타입의 컬렉션 강제: 일급 컬렉션은 특정 타입의 컬렉션만을 다루도록 강제할 수 있다.
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

public class Products {
    private final List<Product> products;

    public Products(List<Product> products) {
        this.products = new ArrayList<>(products);
    }

    // 불변성을 유지하기 위해 컬렉션 반환 시 복사본 제공
    public List<Product> getProducts() {
        return Collections.unmodifiableList(products);
    }

    // 전체 가격 계산 같은 비즈니스 로직 캡슐화
    public double totalPrice() {
        return products.stream()
                       .mapToDouble(Product::getPrice)
                       .sum();
    }

    // 제품 추가 메서드
    public Products addProduct(Product product) {
        List<Product> newProducts = new ArrayList<>(products);
        newProducts.add(product);
        return new Products(newProducts);
    }
}
728x90
반응형
반응형
  • 인당 3계좌까지 가능
  • 계좌 생성 시 코드 입력 시 나와 친구 모두 0.5% 추가 이율


코드:

  • 0125025418
  • 0125025417
  • 0125025416
728x90
반응형
반응형

코루틴 - 코틀린; 비동기

코루틴(Coroutine)은 비동기 프로그래밍동시성 처리를 위한 경량 실행 단위/함수
일반적으로 코루틴은 실행을 일시 중단하고(중단점 제공), 필요한 시점에 다시 시작할 수 있는 기능을 가지고 있음

  • 핵심 아이디어: 사용자 수준의 스케줄링
    • 코루틴은 명시적인 중단 지점을 통해 비동기 작업을 관리
    • 단일 스레드에서도 여러 코루틴을 실행할 수 있음
  • 동작 방식:
    • 비동기: 코루틴은 주로 비동기 작업을 처리하는 데 사용. 코루틴은 일시 중단과 재개가 가능하여, 비동기 네트워크 호출이나 파일 I/O 작업을 쉽게 처리할 수 있음
    • 런타임이 코루틴의 상태를 관리하고, 필요 시 다시 스케줄링
    • 예: suspend 함수 호출 시 작업을 중단하고 다른 작업을 실행

장점:

  • 명시적인 상태 관리로 복잡한 비동기 로직 처리에 강력.
  • 메모리 및 리소스 효율성이 높음.

단점:

  • 프로그래머가 중단 지점을 명시적으로 관리해야
  • 자바에서는 안됨(Kotlin에서 주로 사용)

코루틴의 특징

  1. 경량 스레드:
    • 코루틴은 스레드와 유사하게 보이지만, 실제 스레드를 생성하지 않고 실행되므로 더 적은 리소스를 사용함
    • 코루틴은 스레드보다 가벼운 구조로, 많은 수의 코루틴을 동시에 실행할 수 있어 메모리 사용량을 줄이고 성능을 향상시킴
  2. 비동기 작업 처리:
    • await 또는 yield 같은 키워드를 통해 작업의 흐름을 중단하고, 나중에 재개할 수 있음
    • 코루틴은 비동기적으로 실행되며, 다른 작업과 동시에 진행될 수 있어 CPU 자원을 효율적으로 사용할 수 있게 해줌
  3. 스케줄링 제어:
    • 코루틴은 프로그래머가 명시적으로 실행 순서를 제어할 수 있음
  4. 언제든 중단/재개 가능:
    • 작업의 중간에서 멈췄다가 나중에 다시 이어서 실행할 수 있어 효율적인 비동기 작업 처리가 가능
    • 코루틴은 실행 상태를 유지할 수 있어, 중단된 지점에서 다시 시작할 수 있음

언제 코루틴을 사용하나?

  1. I/O 작업:
    • 네트워크 요청, 파일 읽기/쓰기 등 시간이 오래 걸리는 작업에서 UI 스레드나 메인 스레드를 차단하지 않고 비동기적으로 처리.
  2. 동시성 프로그래밍:
    • 여러 작업을 병렬로 처리할 때 스레드보다 더 효율적으로 관리 가능.
  3. UI 프로그래밍:
    • 애니메이션, 이벤트 처리, 사용자 인터페이스 업데이트 등 비동기 작업을 자연스럽게 구현.
  4. 백그라운드 작업:
    • CPU 집약적인 작업이나 긴 대기 시간이 필요한 작업을 수행하면서 메인 스레드를 차단하지 않음.

 

코루틴 자바 지원 X

자바의 virtual thread랑 비교?

코루틴은 비동기 작업을 간편하게 처리하기 위해 설계된 반면, 버추얼 스레드는 높은 동시성을 요구하는 환경에서 효율적으로 스레드를 관리하기 위한 방법

  • 코루틴: 비동기 작업을 중단하고 재개할 수 있는 경량 구성 요소로, 비동기 작업 처리가 주 용도
  • 버추얼 스레드: 동기적인 프로그래밍 모델을 유지하면서도 높은 동시성을 처리할 수 있는 경량 스레드로, 비동기 코드의 복잡성을 줄임

 

버추얼 스레드 - 자바; 대규모동시성동기

  • 핵심 아이디어: JVM이 직접 관리
    • 전통적인 스레드는 OS 커널에서 관리되지만, 버추얼 스레드는 JVM 내부에서 관리되어 더 적은 자원을 소비
    • 각 작업은 자체적인 스레드처럼 동작하므로 프로그래머가 명시적으로 중단점을 관리할 필요가 없음
    • 버추얼 스레드는 동기적으로 작업을 수행. 전통적인 스레드와 유사한 방식으로 작동하지만, 더 가볍고 효율적
  • 동작 방식:
    • 동기적 코드 작성: 버추얼 스레드는 비동기 작업을 동기적인 코드 스타일로 작성할 수 있게 한다. 이는 비동기 코드의 복잡성을 줄이고, 동기적인 프로그래밍 모델을 유지. (동기 방식도 대규모 동시성 지원 가능! 많은 스레드를 동시에 처리 가능)
    • 차단 호출(예: I/O 작업) 발생 시 자동으로 OS 스레드에서 분리
      • IO 대기 상태에서는 스레드 반환해 대규모 작업 가능
    • 기존 스레드 풀보다 더 많은 수의 동시 작업 가능

장점:

  • 기존 스레드 API와 호환성 높음 (학습 곡선 낮음).
    • synchronized 블록, wait/notify 메서드 등을 그대로 사용 가능
  • 차단 호출도 자동으로 처리하므로 더 간단한 코드 작성 가능.

단점:

  • 자바 19 이상의 JVM에서만 사용 가능.
  • 특정 시나리오에서는 기존 스레드 풀만큼의 효율성 제공 어려움.

전통적인 스레드와의 비교

  • 전통적인 스레드:
    • OS에서 관리되며, 각각의 스레드는 상당한 메모리와 자원을 차지
    • 많은 수의 스레드를 생성하면 성능 문제가 발생할 수
  • 버추얼 스레드:
    • JVM에서 관리되며, 매우 가벼움
    • 대량의 동시 작업을 처리할 때 효율적
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        executor.submit(() -> {
            Thread.sleep(1000);
            return "Task completed!";
        });
    }
}

위 코드에서 10,000개의 Virtual Thread를 동기적으로 처리하지만, 비동기를 사용하지 않아도 효율적으로 실행됨

 

정리

 

2024.11.26 - [개발/java] - [병렬처리] Folkjoinpool & executorService

 

대규모 처리를 동시에 하려면 비동기 작업이 필수 아닌가?

 

그럼 비동기는 언제?

IO작업이 많을 때, 대기 시간을 최소화하고 자원을 효율적으로 활용할 수 있을 때

비동기는 주로 대기 시간이 많은 작업에서 활용됩니다. 이 대기 시간은 CPU가 직접 연산하는 시간이 아니라, 외부 시스템에서 응답을 기다리는 시간으로 인해 발생합니다. 비동기를 선택하는 주요 이유는 자원 효율성대규모 처리 능력입니다.

DB, file, API, 네트워크..

대규모 데이터 처리(ETL, 스트리밍)

작업 성격에 따른 선택

 

  • I/O 바운드 작업:
    대기 시간이 많은 작업에서 비동기를 사용하면 자원 활용이 극대화됩니다.
    • :
      • 외부 API 호출
      • 데이터베이스 쿼리
      • 파일 읽기/쓰기
      • 네트워크 요청
    • 추천 방식:
      • 비동기 프로그래밍: CompletableFuture, Kotlin Coroutines, async/await.
      • Reactive Streams: Project Reactor, RxJava.
      • Event-driven frameworks: Node.js.
  • CPU 바운드 작업:
    복잡한 계산, 데이터 변환 등 CPU가 주로 사용되는 작업에서는 동기나 멀티스레드 기반의 병렬 처리가 적합합니다.
    • :
      • 데이터 처리(대규모 연산)
      • 이미지 변환, 암호화
      • 머신 러닝 모델 실행
    • 추천 방식:
      • 동기 처리: 단일 스레드에서 작업.
      • 멀티스레드 기반 병렬 처리: Java ForkJoinPool, Parallel Streams, ExecutorService.

 

 

  • 멀티스레드는 작업을 물리적으로 병렬로 실행하는 방식이고,
  • 비동기는 작업을 논리적으로 대기 시간을 줄이며 논블로킹으로 처리하는 방식입니다.
  • 비동기는 멀티스레드를 사용하지 않을 수도 있지만, 필요하면 내부적으로 멀티스레드를 활용할 수도 있습니다. (예: Java NIO, Kotlin Coroutines).

성능 비교

https://tech.kakaopay.com/post/coroutine_virtual_thread_wayne/

728x90
반응형
반응형

환경: springbatch5, java17, mysql

 

MyBatisBatchItemWriter<GmahjongRanking> writer = new MyBatisBatchItemWriterBuilder<GmahjongRanking>().sqlSessionFactory(casualDb)
    .statementId(Constant.GAME_MAPPER + "insertGmahjongDayRank")
    .build();
<insert id="insertGmahjongTotalRank" parameterType="com.hangame.batch.casual.application.model.gmahjong.ranking.GmahjongRanking">

INSERT INTO GAME (regdate, memberid, wincnt, defeatcnt, slevel, rating, ranking, avatarid, nickname, oranking)
VALUES (#{registerDate}, #{memberId}, #{winCount}, #{defeatCount}, #{level}, #{rating}, #{ranking}, #{avatarId}, #{nickname}, #{oRanking})

</insert>

insert 문이 이렇게 있을 때 insert문이 1개만 나가는지, 청크 수만큼 나가는지 궁금해졌다.

insert 문이 1개만 나간다는 의미는 values 뒤로 n개 붙은 문이 한번 나가는 것이고

청크 수 만큼 나간다는 것은 insert 문 자체가 n 개 있다는 뜻.

 

MyBatisBatchItemWriter의 write 함수를 살펴보면 아래와 같다.

while 문으로 청크를 돌아서 sql을 만들고 들고 있다가 한 번에 실행한다.

ExecutorType.BATCH로 설정된 SqlSessionTemplate에서는, update() 메서드 호출 시 쿼리를 바로 실행하지 않고 내부 배치 큐에 저장하고 flushStatements()를 호출하면, 지금까지 배치 큐에 저장된 모든 SQL 문을 한 번에 실행

  • 장점:
    1. 네트워크 요청 최소화: 각 SQL 문을 개별적으로 실행하지 않고, 배치로 묶어서 처리
    2. 성능 향상: 배치 처리 시 JDBC 드라이버가 여러 쿼리를 내부적으로 최적화
  • 주의점:
    1. 메모리 사용량: 배치 큐에 저장된 쿼리가 많아질 경우 메모리 사용량이 증가
    2. 트랜잭션 관리: 배치 처리 중 하나의 쿼리가 실패하면, 전체 배치가 롤백

 

 

그럼 values 뒤로 쫙 붙여서 한번에 쏘고 싶다면?

우선 mapper를 수정하고

@Bean(INSERT_NINE_RATING_RANKING_WRITER)
@StepScope
public ItemWriter<BadukEnrichedRanking> insertNineRatingRankingWriter() {
    return chunk -> {
      @SuppressWarnings("unchecked") var items = (List<BadukEnrichedRanking>) chunk.getItems();
      var splittedNineRankings = ListUtil.splitList(items, SPLIT_LIST_SIZE);

      splittedNineRankings.forEach(badukNineRankingMapper::insertNineRankings);
    };
}

MyBatisBatchItemWriter를 안 쓰고 수동으로 itemWriter를 만든 후 

chunk를 sublist로 쪼갠 후 foreach 에 연결시킨다.

그러면 1 insert 의 values에 여러 개가 붙고 각 호출이 개별적인 SQL 실행을 하게 된다.

혹시 배치 방식으로 바꾸려면..

return chunk -> {
    @SuppressWarnings("unchecked")
    var items = (List<BadukEnrichedRanking>) chunk.getItems();
    var splittedNineRankings = ListUtil.splitList(items, SPLIT_LIST_SIZE);

    // Batch 처리 활성화
    try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
        var mapper = sqlSession.getMapper(BadukNineRankingMapper.class);

        splittedNineRankings.forEach(mapper::insertNineRankings);

        // 배치 실행
        sqlSession.flushStatements();
        sqlSession.commit();
    }
};

 


MyBatisBatchItemWriter(ExecutorType.BATCH):

  • ExecutorType.BATCH 모드에서는 하나의 SqlSession을 열고 여러 쿼리를 실행한 후 한 번에 flushStatements()를 호출하여 쿼리들을 모아서 데이터베이스에 전송
  • 이 모드는 SQL 세션을 한 번만 열고, 여러 개의 쿼리를 하나의 트랜잭션 내에서 실행. 세션을 닫기 전에 모든 쿼리가 메모리에 쌓이고, flushStatements()를 호출하여 한 번에 실행되므로 성능 면에서 효율적

forEach 방식 (기본 SqlSession):

  • 반면에 forEach를 사용하여 각각의 항목을 처리하는 경우, 매번 update 또는 insert가 실행될 때마다 SqlSession을 생성
  • 이 방식은 각각의 쿼리가 별도의 세션을 사용하거나, 적어도 별도의 쿼리 실행이 이루어지는 방식. 즉, SQL 세션을 매번 열고 update 또는 insert를 실행한 후 세션을 닫고, 다시 열어서 쿼리를 실행하는 방식
728x90
반응형
반응형
  • 주키퍼(Zookeeper)는 분산 시스템의 코디네이션 서비스
  • ZNode 기반 트리구조로 데이터를 관리
  • 고가용성 고성능

 

주요 기능

  1. 분산 설정 관리:
    • 서버간 동기화를 위한 분산 코디네이션 기능
    • 분산 시스템에서 여러 애플리케이션이 동일한 설정 데이터를 공유해야 할 때 중앙 저장소 역할을 함
  2. 리더 선출(Leader Election):
    • 분산 시스템에서 리더를 선출할 때, 투표 과정을 관리하고 리더를 선출. 이를 지속적으로 유지 및 관리
    • 카프카 브로커들
  3. 동기화(Synchronization):
    • 분산 환경에서 여러 노드(서버 또는 프로세스)가 동일한 상태를 유지하도록 지원, 데이터 일관성 유지
    • 로컬 캐시 동기화 시
  4. 이벤트 감시(Watches):
    • Zookeeper의 데이터가 변경되면 이벤트 알림을 애플리케이션에 전달하여 실시간 업데이트를 함
    • watch 기능을 이용한 변경 이벤트 감지
  5. 분산 락 관리:
    • 분산 환경에서의 데이터 접근을 제어하고 동기화를 위한 락관리

 

특징

  • 일관성 보장:
    • Zookeeper는 CAP 이론에서 CP(Consistency와 Partition Tolerance)를 보장
    • 항상 데이터의 일관성을 우선
  • 높은 가용성:
    • 클러스터를 통해 고가용성을 제공하며, 노드 장애 시에도 서비스가 중단되지 않음
  • 간단한 API:
    • 클라이언트가 쉽게 사용할 수 있도록 간단한 API를 제공
  • 쓰기 지연, 읽기 최적화:
    • 쓰기 연산은 느릴 수 있지만 읽기 연산은 매우 빠름



기본 아키텍처

  • ZNode: 주키퍼에서 데이터를 저장하는 단위로, 파일 시스템과 유사한 구조를 가짐
  • 클라이언트: 주키퍼에 연결하여 데이터를 읽고 쓰는 애플리케이션
  • 서버: 주키퍼의 데이터 저장 및 처리를 담당하는 노드

장점

  • 분산 환경에서 복잡한 작업을 단순화함
  • 높은 신뢰성과 일관성 제공
  • 트랜젝션을 원자적으로 처리
  • 다양한 분산 시스템과의 통합 용이

단점

  • 쓰기 연산의 성능이 낮음(읽기 최적화)
  • 클러스터 크기가 커질수록 성능 저하 가능
  • ZooKeeper 장애 시 의존적인 애플리케이션에 문제가 발생할 수 있음

ZNode란?

 

  • ZNode는 ZooKeeper가 관리하는 데이터 구조의 단위
  • 트리 구조(파일 시스템과 유사)로 구성되며, 각 노드가 ZNode에 해당
  • ZNode는 데이터와 상태 정보를 저장하고, ZooKeeper의 API를 통해 접근 가능

 

ZNode 기반 트리 구조의 특징

  1. 트리 형태:
    • ZooKeeper 데이터는 / 루트에서 시작하여 트리 형태
      • 예: /app/config/db, /app/config/cache.
  2. 데이터 크기 제한:
    • 각 ZNode는 최대 1MB 크기의 데이터 저장
    • ZNode는 주로 작은 상태 정보를 저장하며, 대용량 데이터는 다른 저장소를 사용해야 함
  3. 노드 유형:
    • Persistent ZNode:
      • 노드가 생성된 후 삭제 요청이 있을 때까지 유지
    • Ephemeral ZNode:
      • 클라이언트 세션이 종료되면 자동으로 삭제
    • Sequential ZNode:
      • 노드 이름에 고유한 순번을 추가하여 생성
  4. 원자적 연산:
    • ZNode의 데이터 변경은 원자적으로 이루어지며, 동시성이 보장
  1.  

로컬 캐시 동기화 예시

이벤트를 감지하는 timestamp를 변경하여 (값을 수정하고) 변경 이벤트를 각 서버로 전달
https://youtu.be/BUV4A2F9i7w?si=c04ObhVx1skzBmDR

  • 디비 갱신
  • 레디스 만료
  • 주키퍼가 만료되었다는 이벤트를 각 서버에 전달
  • 각 서버는 이벤트를 받고 로컬 캐시 만료처리
  • 그 이후 새 요청이 들어오면 레디스/로컬 캐시를 최신 데이터로 갱신

세팅

자바 필요

주키퍼 압축 풀고 conf/zoo.cfg 설정 해야 함

# 기본 설정
tickTime=2000
initLimit=10
syncLimit=5

# 데이터 디렉토리 경로
dataDir=/var/lib/zookeeper

# 클라이언트 연결 포트
clientPort=2181

# 서버 설정 (클러스터 구성 시 필요)
# server.1=zookeeper1:2888:3888
# server.2=zookeeper2:2888:3888

 

  • server.3=zookeeper3:2888:3888 설정은 클러스터 내에서
    • 서버 3이 호스트 이름 zookeeper3, 데이터 통신 포트 2888, 리더 선출 포트 3888을 사용한다는 것을 의미
  • myid파일
echo "3" > /var/lib/zookeeper/myid

 

  • Zookeeper는 실행될 때 데이터 디렉토리(dataDir)에 위치한 myid 파일을 읽음
    • dataDir=/var/lib/zookeeper
  • myid 파일에 기록된 숫자를 사용하여, 설정 파일(zoo.cfg)의 server.X 항목 중 자신에게 해당하는 항목을 찾음
  • 이를 통해 클러스터 내에서 자신의 역할과 통신할 포트를 결정

 

728x90
반응형
반응형
ORDER BY NULL은 쿼리의 결과를 정렬하지 않도록 지정하는 구문

 

언제 쓸까?

데이터베이스에서 정렬 작업은 비용이 많이 드는 작업이다. 결과를 정렬할 필요가 없는 경우, ORDER BY NULL을 사용하여 불필요한 정렬 작업을 피할 수 있다.

정렬이 필요 없다면 성능 향상 가능!

GROUP BY와 함께 사용할 때: MySQL은 GROUP BY를 실행할 때 암묵적으로 정렬을 수행한다. 하지만 특정 상황에서는 이 정렬이 불필요할 수 있다. ORDER BY NULL을 사용하면 MySQL에 정렬을 생략하도록 지시하여 성능을 향상할 수 있다.

 

728x90
반응형

'개발 > sql' 카테고리의 다른 글

[파티셔닝] 하는법, 쓰는법  (0) 2024.11.25
비관락/낙관락 쓰기락/읽기락 베타락/공유락  (1) 2024.11.09
2 Phase Lock & mysql -> MVCC  (3) 2024.11.06
[분산] mysql 네임드락  (0) 2024.11.01
[p6spy] 설정 방법  (0) 2024.10.21
반응형

환경: springboot3.4, java17

 

최신 버전의 스프링부트를 쓰면 어느 새부터 아래와 같은 워닝을 만나는데 상당히 신경 쓰인다. 그동안 Page 인터페이스를 아주 많이 사용했던 터라 혹시 안되거나 deprecated 된다면 난감하기 때문이다..

찾아보니 springboot3.3부터 변경되었다고 한다!

2024-12-12 13:57:23 WARN  [ration$PageModule$WarningLoggingModifier.changeProperties    : 156] Serializing PageImpl instances as-is is not supported, meaning that there is no guarantee about the stability of the resulting JSON structure!
	For a stable JSON structure, please use Spring Data's PagedModel (globally via @EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO))
	or Spring HATEOAS and Spring Data's PagedResourcesAssembler as documented in https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html#core.web.pageables.

내용은 직렬화 시 안정적이지 않으니 Spring Data의 PagedModel 또는 PagedResourcesAssembler를 사용하여 안정적인 JSON 구조를 생성하라는 것이다.

그동안 직렬화할 때 PageImpl을 직접 직렬화하였는데, 더이상 안정적인 방식이 아니니 아래 두 방식 중 하나를 고르라는 뜻

  • HATEOAS를 쓰면 PagedResourcesAssembler, 그렇지 않으면  PagedModel

프로젝트 전역으로 설정하는 방식은 아래와 같다.

@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)

https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html#core.web.pageables

PageSerializationMode 에는 아래와 같이 두 가지가 있다.

DIRECT

PageImpl 객체를 직접 JSON으로 직렬화함

  • 직렬화된 JSON 구조는 PageImpl의 내부 구조에 따라 다를 수 있음
  • JSON 구조가 API의 변경이나 버전 업그레이드에 따라 변할 수 있으므로, 안정성이 떨어질 수 있음
  • 이 모드는 이전 버전의 Spring Data에서 기본적으로 사용되던 방식

DTO

DTO(Data Transfer Object)를 사용하여 페이지 데이터를 직렬화

  • 안정적이고 일관된 JSON 구조를 제공
  • DTO를 사용함으로써 페이지 데이터의 구조가 API 변경에 영향을 덜 받음
  • 이 모드는 PagedModel 또는 PagedResourcesAssembler와 함께 사용되며, 이를 통해 클라이언트가 예측 가능한 형식의 데이터를 수신할 수 있다.

 

설정하고 기존과 똑같이 페이징하면 된다.

@GetMapping
public Page<BaseResponse> getPrices(

참고로 HATEOAS

HATEOAS는 REST API 설계의 원칙 중 하나로, 클라이언트가 서버 응답에 포함된 하이퍼미디어(hypermedia)를 통해 애플리케이션 상태를 동적으로 탐색할 수 있도록 하는 방식이다. 이 원칙은 REST의 자기 설명(self-descriptive) 특성을 강화한다

HATEOAS의 구성 요소

  • 링크(Link): API 응답에 포함된 URL. 다음 가능한 액션을 안내.
  • 상태(State): 현재 리소스의 상태.
  • 동작(Action): 링크를 따라가면 수행할 수 있는 작업.

HATEOAS의 사용 이유

  • API 탐색성 증가:
    • 클라이언트는 추가적인 문서 없이 서버 응답에 포함된 링크를 통해 어떤 작업이 가능한지 동적으로 파악할 수 있음
  • 클라이언트-서버 결합도 감소:
    • 클라이언트는 서버가 제공하는 링크를 따라가기 때문에 특정 엔드포인트에 강하게 의존하지 않음
  • 유연한 확장성:
    • 서버에서 새로운 액션이나 엔드포인트를 추가하더라도, 클라이언트는 변경 없이 새로운 기능을 사용할 수 있음
  • 자기 설명적 API:
    • 서버 응답에 포함된 하이퍼미디어가 클라이언트에게 리소스 상태 및 가능한 작업을 설명하므로 API의 문서화와 유지보수가 용이
728x90
반응형

+ Recent posts