fbfbf1의 등록된 링크

키자드에 등록된 총 1413개의 포스트를 확인하실 수 있습니다.

Naver Blog

2023년 SK TECH SUMMIT 후기

2023년 11월 16일 ~ 17일 SK TECH SUMMIT이 열려서 16일에 참가하러 갔다. 작년에는 그랜드 워커힐에서 열렸는데 이번에는 코엑스에서 열렸다. 글쓰기 전에 참고할만 내용들을 먼저 적는다. 첫째 날 기준이니 둘째 날은 또 다를 수 있다. 코엑스 규모에 비해 사람들이 훨씬 많아서 인기 많은 세션은 줄 서서 들어가야 된다. 그렇기에 좀만 늦게 가면 듣고 싶은 세션 못 듣는다. 애초에 공간이 작아서 어느 정도 사람이 들어가면 스태프분들이 인원 조정을 해서 못 들어간다. 사람이 많아서 그런지 데이터가 초반에 잘 안 터졌다. 나는 SKT 요금제에 갤럭시 S23 plus였는데 잘 안 터졌다. 코엑스 와이파이도 잘 안됐다. 나는 봉은사역에서 내려서 7번 출구로 나갔다. 코엑스 쪽으로 쭉 들어가서 컨벤션 센터 로비 1층으로 올라가면 된다. 참고로 혹시 KOSIGN 2023이 본다면 잘못 간 것이다. 이쪽이 아니다. 에스컬레이터를 타고 올라왔을 때 No Brand 버거쪽으로 가야 된

Naver Blog

[Elasticsearch] 색인 과정에서의 낙관적 동시성 제어

낙관적 동시성 제어 인덱스에 색인 과정 시 프라이머리 샤드 변경 내용은 레플리카 샤드로 복제가 된다. 이때 분산 클러스터 특성상 여러 요청이 들어오게 되면 어떤 요청이 먼저 복제본 샤드로 들어갈지는 보장할 수 없다. 엘라스틱 서치에서는 이러한 상황에서 값이 역전되는 상황을 막기 위해서 _seq_no을 사용한다. _seq_no _seq_no은 각 프라이머리 샤드마다 들고 있는 시퀀스 숫자 값이고 매 작업마다 1씩 증가한다. _seq_no 값을 이용해서 역전되는 상황을 막는다. 시간 작업 _seq_no 오후 11시 40분 인덱스에 "name" : "ryool" 색인 0 오후 11시 43분 12초 인덱스에 "name" : "ryool1"으로 색인 1 오후 11시 43분 13초 인덱스에 "name" : "ryool2"으로 색인 2 위와 같은 상황에서 최종 결과는 name : ryool2가 되어야 된다. 만약 동시성 제어를 하지 않는다면 13초 작업이 먼저 들어오고 12초 작업이 나중에 들어

Naver Blog

[DB] 데이터베이스 모델링과 설계

데이터베이스 설계 과정 3가지 주요 단계 요구사항 분석 어느 데이터를 저장할지, 각 아이템이 어떻게 관계되는지 데이터베이스에 필요한 사항 결정 및 기록 개념적 설계 데이터베이스 요구사항을 파악한 후에는 데이터베이스 설계에 대한 공식 설명으로 다듬는다. 논리적 설계 데이터베이스 설계를 기존의 데이터베이스 관리 시스템에 맞춘다. 관계형 엔티티 모델 데이터베이스는 객체인 엔티티와 엔티티 간의 연결 정보인 관계를 저장함 대학교 데이터베이스에는 학생, 수업, 등록자 수에 대한 정보를 저장하는데 여기서 학생, 수업이 엔티티 재고와 판매 데이터베이스에는 제품, 고객, 매출 정보를 저장하는데 제품과 고객이 엔티티이고 제품과 고객의 관계가 판매이다. 엔티티 표현 방법 판매 데이터베이스 예시 Entity RelationShip (ER) 모델을 사용해서 엔티티를 표현한다. 엔티티 집합은 엔티티 이름이 담긴 직사각형으로 표현된다. 속성 일반적으로 엔티티의 특징이나 속성을 저장하기 위해 데이터베이스를 사용

Naver Blog

[Spring] Springboot DispatcherServlet 동작 과정

디스패처 서블릿 Http 프로토콜로 들어오는 모든 요청을 가장 먼저 받아서 적합한 컨트롤러에게 위임해 주는 Front Controller Front Controller는 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리하는 Controller 디스패처 서블릿을 통해 요청을 처리할 컨트롤러를 찾아서 위임하고, 그 결과를 받아온다. 디스패처 서블릿 동작 과정 DispatcherServlet의 계층 구조는 다음과 같다. HttpServlet은 HttpServlet을 구현하기 위한 추상 클래스 특정 Http 메서드를 지원하기 위해서는 do로 시작하는 메서드를 오버라이딩 해야 함 doPatch는 지원하지 않는다. HttpServlet을 Spring이 구현한 추상 클래스 스프링이 모든 유형의 서블릿 구현을 위해 정의한 공통 클래스 FramworkServlet은 스프링 웹 프레임워크의 기반이 되는 서블릿 do로 시작하는 메서드 오버라이딩하고 있고, do로 시작하

Naver Blog

[Redis] Redis 운영과 관리, RDB, AOF, 장애상황

Redis와 Memcached Memcached는 캐시 솔루션이고 Memcached에 저장소의 개념이 추가된 것이 Redis 기능 Redis Memcached 속도 초당 100000QPS 이상 초당 100,000 QPS 이상 자료구조 key-value List Hash Set Sorted Set 등 지원 Key-value만 지원 안정성 특성 잘못 이해할 경우 프로세스 장애 발생 장애 거의 없음 응답 속도의 균일성 Memcached에 비해서 균일성이 떨어질 수 있음 전체적으로 균일함 응답속도의 균일성을 보면 대규모 트래픽으로 많은 데이터가 업데이트되면 Redis는 Memcached에 비해 속도가 균일하지 않다. 이것은 메모리 할당 구조의 차이 때문에 발생한다. Redis는 jemalloc을 사용해서 매번 malloc과 free를 통해서 메모리 할당이 이루어진다. 반면 Memcached는 slab 할당자를 이용해서 내부적으로 메모리 할당을 다시 하지 않고 관리한다. 이런 차이로 인해서

Naver Blog

[Redis] 복제

Redis의 주요 특징 중 하나가 DBMS에서 제공하는 것과 유사한 복제 기능이 있음 복제 기능은 장애 발생 시 빠른 서버 교체나 장비 교체 등에 사용 가능 Redis 복제 모델 Redis는 마스터/슬레이브 형태의 복제 모델 제공함 이를 통해서 마스터의 변경 사항이 슬레이브로 전파됨 한 대의 슬레이브는 오직 하나의 마스터만 가진다. 위 그림처럼 슬레이브에서 마스터로 접속함 이 점을 이용해서 슬레이브가 다른 마스터로도 동작할 수 있음 Mac 기준 Redis Port 여러 개로 띄우기 기존 Redis.conf를 복사한 redis2.conf를 만들어줌 맥북 기준 /opt/homebrew/etc에 기존 redis.conf 파일 위치 이걸 redis2.conf로 복사 vi redis2.conf 해서 Port 6380으로 해줌 2. redis-server 명령어 사용 그다음 redis-server /opt/homebrew/etc/redis2.conf 명령어 실행 정상 실행되면 위와 같이 뜸 3

Naver Blog

[Redis] HA와 Sentinel

High Availability Redis는 HA를 위해서 기본적으로 master/slave 형태로 서비스한다. 이 경우 마스터 장애 발생 시 slave가 마스터 대신 서비스하도록 변경해 주는 기능이 필요함 http://redisgate.kr/redis/introduction/redis_release7.php 참고로 현재는 redis 7.x까지 나옴 너무 먼 과거 버전인 Redis 2.6 버전부터는 slave-read-only가 yes로 설정되어 있어서 쓰기 요청은 실패하기에 slave 전환 기능은 필요함 장애 발생 후 작업 마스터의 장애를 정확히 판별함 슬레이브를 마스터로 승격 해당 작업 내용을 클라이언트에게 통지 Redis는 Sentinel 데몬을 이용해서 1, 2, 3 작업을 처리한다. 다만 3번에 대해서는 sentinel에서는 이미 장애가 발생한 master에 접속된 client를 알 수 없기에 해당 알림을 원하는 client는 pub/sub으로 sentinel에 등록해야 함

Naver Blog

[Elasticsearch] 인덱스 생명 주기 관리(Index Lifecycle Management)

ILM 인덱스를 hot-warm-cold-frozen-delete 페이즈로 구분해서 지정한 기간이 지나면 인덱스를 다음 페이즈로 전환시키고 지정한 작업을 수행하도록 하는 기능 인덱스와 데이터 스트림을 매우 편하게 관리할 수 있게 해준다. ILM을 통해서 hot 페이즈에서는 매일 자동으로 roll-over 수행하고 거기에 더해 샤드 사이즈가 8GB 크기가 넘어가면 날짜가 넘어가지 않아도 롤오버를 수행하도록 한다. 노드 phase 컨셉 컨셉 설명 HOT 현재 업데이트가 수행되고 있고 읽기 작업도 가장 많은 상태 WARM 인덱스에 더 이상 업데이트가 수행되지는 않지만 읽기 작업은 들어오는 상태 COLD 인덱스에 더 이상 업데이트가 수행되지 않고 읽기 작업도 가끔씩만 들어오는 상태, 검색은 되어야 하나 속도가 느려도 괜찮은 상황 FROZEN 인덱스에 더 이상 업데이트 안 되고 읽기 작업도 거의 없는 상태 검색은 되어야 하지만 속도가 상당히 느려도 괜찮은 상황 DELETE 인덱스가 더 이상

Naver Blog

[Spring] SpringBootApplication run 메서드 실행 과정 1 ~ 10단계

SpringBootApplication run SpringBootApplication Class의 run 메서드는 위와 같이 구성이 되어 있고 크게 13단계를 가지고 있다. 1단계 StopWatch로 실행 시간 측정 시작 System.nanoTime()을 통해서 시간 측정을 시작한다. timeTakenToStartUp에 전체 실행 시간을 저장하고 이걸 newStarupInfoLogger에 저장한다. 위에서. logStarted 메서드를 따라가보면 메시지를 찍는 부분을 볼 수 있다. StarupInfoLogger에서 start 메시지를 만든다. 해당 부분에 debug을 찍고 메시지를 확인해 보면 만들어지는 걸 볼 수 있고 실제 로그랑 똑같이 찍히는 것도 볼 수 있다. 2단계 BootStrapContext 생성 BootStrapContext를 생성하는 createBootStrapContext 단계이다. BootStrapContext는 애플리케이션 컨텍스트가 준비될 때까지 환경 변수들을

Naver Blog

[Spring] SpringBootApplication 실행과정 11 ~ 13단계

11단계 Context Refresh 후처리 단계 애플리케이션 컨텍스트의 refresh 단계가 마무리되고 후처리를 하는 단계 현재는 메서드가 비어있는 상태이다. 12단계 실행 시간 출력 및 리스너 started 처리 StartUpInfoLogger를 통해서 걸린 시간을 로그로 남기고 listeners.started로 리스너들을 started 처리한다. 13단계 Runners 실행 마지막으로 Runner를 호출하는 단계이다. 애플리케이션이 실행된 이후에 초기화하는 작업이 필요할 때 사용할 수 있는 방법이 Runner를 등록하는 방법이다. Runner에는 2가지가 존재한다. String을 파라미터로 넘기는 경우에는 CommandLineRunner 다른 타입을 파라미터로 넘기는 경우에는 ApplicationRunner를 사용할 수 있다. 스프링 부트 기본 상태에서 실행을 하게 되면 구현한 Runner가 없기 때문에 Runner를 실행하지는 않는다. CommandLineRunner나 App

Naver Blog

[서평] 러닝 MySQL (MySQL 운영 종합 가이드, 데이터베이스 설계부터 비용 최적화까지)

안녕하세요 이번에 한빛미디어에서 MySQL에 대해서 배울 수 있는 새로운 책이 나와서 서평을 작성해 봅니다. 표지는 역시 한빛미디어 특유의 하얀색 배경에 생물이 그려져 있네요 뒷면에는 책의 내용이 요약되어 있고 서평이 작성이 되어있습니다. 책을 보기 전에 추천사를 한 번씩 보는 편입니다. 추천사를 읽으면 대략적으로 책의 내용을 알 수 있어서 봅니다. 추천사의 내용이 굉장히 좋네요 이 책의 대상 독자는 MySQL을 처음 사용하는 사람과 데이터베이스로 MySQL을 시작하려는 사람입니다. 책의 두께만 보고 처음 DB에 대해서 공부하려는 사람에게 적합할까 싶었는데 책의 내용을 보니 처음 공부를 해도 적합하다고 생각이 들었습니다. 책의 목차를 보시면 아시겠지만 정말 처음부터 다 알려줍니다. 설치부터 해서 쿼리, 데이터베이스 설계, 고급 쿼리 트랜잭션 및 잠금 그리고 운영환경에서 알면 좋은 것들도 다 알려줍니다. 책에서 MySQL 설치 방법을 하나하나 다 알려줍니다. 각 운영체제별로 어떻게 설

Naver Blog

화담숲

4시간 정도 걸려서 예약을 한 화담숲 모노레일은 예약하지 못해서 그냥 걷는(?) 코스로 갔다. 모노레일 타고 정상으로 가서 내려오는 게 안 힘들고 좋다. 물론 중간중간에 모노레일 예매할 수 있다. 10월 28일 토요일에 베스트 드라이버인 오기사님과 함께 화담숲 갔다. 곤지암 리조트 안에 화닾숨이 위치해 있어서 곤지암 리조트로 가야 된다. 주차 구역은 잘 되어있다. 주차 공간은 크고 주차하기 힘들면 어쩌나 했는데 황제주차 했다~ 리프트 타고도 갈 수 있는데 줄이 엄청 길어서 걸어서 입구까지 갔다 한 5분 걸으면 된다. 화담숲 가는 길도 잘 되어있다. 화담숲 입구 쪽 토요일에 갔는데 사람들 꽤 있었다. 입구 앞에 편의점도 있어서 간단한 게 뭐 사 먹고 들어갔다. 참고로 음식물 반입 금지이나.. 들어가 보면 사람들 죄다 먹고 있다 ㅋㅎ 들어가면 바로 포토존 있다. 여기서 사람들 많이 찍는다. LG에서 만들었다고 한다. 좀 들어가면 바로 민물고기생태관 있는데 본격적으로 구경하기 전에 보기

Naver Blog

[Spring] SpringApplication의 refreshContext

refreshContext springboot의 run 메서드에서 동작하는 기능 중 하나인 refreshContext이다. 동작 과정 먼저 shutdownhook을 등록을 해준다 shutdownhook은 프로그램의 종료를 감지해 프로그램 종료 시에 후처리 작업을 진행하는 기술 프로세스가 갑자기 죽어버리면 연결 종료, 자원 반납 등의 처리를 못 하는데 shutdownhook을 사용해서 프로그램이 종료되어도 별도의 쓰레드가 올바른 프로그램 종료를 위한 작업 처리할 수 있게 한다. 스프링은 DisposableBean 인터페이스나 @PreDestroy 또는 @Bean에 소멸자 메서드를 지정해서 소멸자를 구현할 수 있는데 SpringApplication이 여기에 shutdownHook을 추가해서 소멸자 메서드를 처리할 수 있게 한다. 그리고 applicationContext를 refresh 해준다. refresh는 각 applicationContext에 맞게 구현이 되어 있다. 기본으로 하게

Naver Blog

[Oracle] 비관적, 낙관적 동시성 제어

비관적 동시성 제어 (Pessimistic Concurrency Control) 사용자들이 같은 데이터를 동시에 수정할 것이라고 가정함 따라서 한 사용자가 데이터를 읽는 시점에 Lock을 걸고 조회 또는 갱신 처리가 완료될 때까지 Lock을 유지 Locking은 첫 번째 사용자가 트랜잭션을 완료하기 전까지 다른 사용자들이 그 데이터를 수정할 수 없게 만들기에 비관적 동시성 제어를 잘못 사용하면 동시성을 저해 받게 됨 잘 사용하면 약이 됨 비관적 동시성 제어 구현 SELECT 포인트, 방문횟수, 방문일시, 구매실적 FROM 고객 WHRER 고객번호 = :cust_num FOR UPDATE; -- 여기서 무언가 발생하면 문제 발생 UPDATE 고객 SET 적립포인트 = :적립포인트 WHERE 고객번호 = :cust_num SELECT 문 이후와 UPDATE 문 이전에 다른 트랜잭션이 같은 고객 레코들을 변경하면 문제가 발생할 수 있다. 이를 위해서 FOR UPDATE를 사용해서 해당 고

Naver Blog

[BOJ] 11724번 연결 요소의 개수 구하기 Java

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.StringTokenizer; public class Main { static ArrayList<Integer>[] edges; static boolean[] visited; public static void main(String[] args) throws IOException { final BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer stringTokenizer = new StringTokenizer(bf.readLine()); int N = Integer.valueOf(stringTokenizer.nextToken());

Naver Blog

[백준] 2023번 신기한 소수 구하기 Java

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer; public class Main { static int N; public static void main(String[] args) throws IOException { final BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); final StringTokenizer st = new StringTokenizer(bf.readLine()); N = Integer.valueOf(st.nextToken()); dfs(2, 1); dfs(3, 1); dfs(5, 1); dfs(7, 1); } static void dfs(int startNumber, int numberLength) {

Naver Blog

[Spring] ApplicationContext

ApplicationContext ApplicationContext는 빈들의 생성과 의존성 주입 등의 역할을 하는 DI(Dependeny Injection Context) 의존성 관계는 다음과 같다. 최상위인 BeanFactory는 1개의 bean을 찾기 위한 메서드들을 가지고 있음 여러 개의 Bean 중에서 하나를 찾을 수 있는 이유 BeanFactory는 1개의 bean을 찾는 메서드들을 가지고 있지만 AppilcationContext가 상속받는 ListableBeanFactory와 HierarchicalBeanFactory를 통해서 동일한 타입의 빈이 여러 개 존재할 때 List에서 Bean을 찾아서 주입해 준다. HierarchicalBeanFactory는 여러 BeanFactory들 간의 계층 관계를 설정하기 위한 퍼블릭 인터페이스를 가진다. @Autowired ApplicationContext는 AutowireCapableBeanFactory를 합성 관계로 가지고 있기에

Naver Blog

[Spring] @SpringBootApplication

@SpringBootApplication Springboot 프로젝트를 생성하면 @SpringBootApplication이 붙은 클래스가 자동 생성됨 @SpringBootApplication 애노테이션을 보면 위와 같다. 메서드 설명 exclude 특정 클래스를 자동 설정에서 제외한다. excludeName 특정 클래스의 이름으로 자동 설정에서 제외함 scanBasePackages 컴포넌트 스캔을 진행할 베이스 패키지 설정함 scanBasePackagesClaases 컴포넌트 스캔을 진행할 베이스 클래스 설정함 nameGenerator 빈 이름 생성을 담당할 클래스를 설정함 proxyBeanMethods @Bean 메서드로 프록시 방식으로 처리하도록 함 proxyBeanMethods @Bean으로 빈을 등록하는 메서드에 프록시 패턴을 적용할 것인지를 결정한다. 기본 값은 true로 @Bean 메서드에 프록시가 기본적으로 적용된다. @Bean 메서드에 프록시가 필요한 이유는 해당 메서

Naver Blog

[Spring] SpringBootApplication 생성과 초기화

SpringBootApplication SpringApplication Class의 run 메서드는 ConfigurableApplicationContext를 반환하고 있음 SpringApplication Class가 어떠한 부모 클래스나 인터페이스를 가지고 있지 않기에 run 메서드 내부에서 ApplicationContext를 만들어서 실행하고 반환하는 걸 알 수 있음 SpringBootApplication 초기화 및 실행 과정 SpringApplication의 생성자는 위와 같다. primarySources는 메인 클래스이고 Assert.notNull을 통해서 null이면 에러 반환하게 되어있다. 메인 클래스가 null인지 검사한 후에는 5가지 단계를 거친다. 1단계 클래스 패스로부터 애플리케이션 타입을 추론 WebApplicationType.deduceFromClassPath()로 애플리케이션 타입을 추론한다. SpringBoot는 애플리케이션 실행 초기에 현재 애플리케이션 타입

Naver Blog

[Spring] @Aspect AOP

@Aspect 스프링이 제공하는 @Aspect로 포인트컷과 어드바이스로 구성된 어드바이저 생성 기능 지원 @Aspect 애노테이션 기반 프록시를 적용할 때 필요함 @Compont @Aspect가 있어도 스프링 빈으로 등록해줘야 함 @Around 포인트컷 표현식을 적음 @Around의 메서드가 어드바이스(Advice)가 됨 ProceedingJoinPoint joinpoint Advice의 MethodInvocation과 유사함 내부에 실제 호출 대상, 전달 인자, 어떤 객체와 어떤 메서드가 호출되었는지 정보 포함 proceed 실제 호출 대상인 target을 호출한다. 테스트에 사용할 Hello 클래스를 만들고 테스트를 실행해 보면 정상적으로 어드바이스가 적용된 걸 볼 수 있다. @Aspect 설명 스프링이 제공하는 자동 프록시 생성기인 AnnotationAwareAspectJAutoProxyCreator이다. 자동 프록시 생성기가 @Aspect가 붙은 Class를 찾아서 Advis

Naver Blog

[Spring] AOP, Pointcut, @Around

AOP 적용 방식 컴파일 시점 클래스 로딩 시점 런타임 시점 (프록시) 컴파일 시점 (Weaving) .java 소스 코드를 컴파일러를 사용해서. class를 만드는 시점에 부가 기능 로직 추가 가능 AspectJ 컴파일러는 Aspect를 확인해서 해당 클래스가 적용 대상인지 먼저 확인하고, 적용 대상인 경우에 부가 기능 로직을 적용함 원본 로직에 부가 기능 로직이 추가되는 것을 Weaving이라고 함 단점 특별한 컴파일러도 필요하고 복잡함 클래스 로딩 시점 자바를 실행하면 자바 언어는 .class 파일을 JVM 내부의 클래스 로더에 보관함 이때 주간에서 .class 파일을 조작해서 JVM에 올릴 수 임ㅅ음 런타임 시점 런타임 시점은 컴파일 타임 끝나고, 클래스 로더에 클래스도 다 올라간 상태에서 자바가 실행이 된 다음 상태를 의미 main 메서드가 실행된 다음 자바 언어가 제공하는 범위에서 부가 기능 적용해야 함 런타임 시점에서는 실제 대상 코드는 그대로 유지되고, 대신에 프록시를

Naver Blog

[Spring] Pointcut execution

예제 코드 먼저 메서드용 / 클래스용 애노테이션을 만들어준다. 테스트에서 사용할 클래스도 생성 테스트 코드를 만들어서 AopServiceImpl 메서드의 정보를 출력해 보면 위와 같이 나온다. execution은 위 메서드 정보를 매칭해서 포인트컷 대상을 찾아낸다. execution execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) execution(접근제어자? 반환타입 선언타입?메서드이름(파라미터) 예외?) execution을 통해서 메서드 실행 조인포인트를 매칭함 ?로 표시되어 있는 건 생략 가능하다. 패턴을 지정할 때 *와 같은 패턴 사용 가능 setExpression에 위에서 얻은 메서드 정보를 적어주고 테스트 코드를 돌려보면 정상 작동한다. 흠 근데 왜 matches에 MemberService.class를 넣으면 통과하는

Naver Blog

[Spring] 로그 출력 AOP, 재시도 AOP

애노테이션 활용 로그 출력 AOP @LogTrace 애노테이션이 붙은 곳에 AOP를 적용하기 위해서 애노테이션 생성 @Before를 사용하고 @annotation이 붙은 곳에 적용할 수 있게 포인트 컷 적용 테스트 사용할 class를 생성하고 메서드에 @LogTrace 애노테이션을 붙여준다. 테스트 코드를 돌려보면 ProducerService와 ProductRepository에 AOP가 적용이 된 걸 볼 수 있다. Retry AOP 적용 Retry에서 사용할 애노테이션을 만든다. default를 5로 해서 기본으로 5번의 리트라이를 하게 만든다. proceed()를 사용해야 되기에 @Around를 사용해 주고 pointCut 부분에 annotation을 달아준다. 이때 원래는 패키지명부터 다 작성해도 되지만 annotation 이름만 Pointcut 부분에 쓰고 메서드에서 Retry를 파라미터로 받으면 된다. 위 코드에서는 예외 / 정상인 상황에 대해서 일단 다 적용이 되는 코드다.

Naver Blog

[Spring] AOP 주의 사항 내부 호출

프록시 내부 호출 문제 Spring은 프록시 방식의 AOP 사용 AOP를 적용하려면 항상 프록시를 통해서 대상 객체를 호출해야 함 프록시를 거치지 않고 대상 객체를 직접 호출하게 되면 AOP가 적용되지 않고, Advice도 호출되지 않음 프록시 객체 주입 AOP를 적용하면 스프링은 대상 객체 대신 Proxy를 스프링 빈으로 등록함 그렇기에 의존관계 (DI) 주입 시에 항상 프록시 객체를 주입함 하지만 대상 객체 내부에서 메서드 호출이 발생하면 프록시를 거치지 않고 대상 객체를 직접 호출하는 문제 발생함 예제 코드 AOP를 적용할 LogAspect를 만들어준다. AOP를 적용할 Class를 만들어주고 메서드를 2개 만들어준다. 이때 external에서 internal를 호출하게 만들어준다. 테스트 코드를 만들고 각각 external, internal을 호출해 본다. 먼저 외부 메서드만 호출해서 로그를 보면 external에만 AOP가 적용된 걸 볼 수 있다. 위의 사항을 그림으로 나타

Naver Blog

[Oracle] 테이블 랜덤 액세스

SQL 튜닝에서는 랜덤 I/O가 중요하다. 질문 파티션 Pruning은 조건절에 해당하는 파티션만 읽는 기능이라고 알고 있다. 인덱스를 이용해도 조건절에 해당하는 데이터만 골라서 읽는데, 파티션 Prunning이 왜 필요한지 궁금하다. 관리적 측면을 제외하고 성능적 측면에서 굳이 파티션이 필요할까? 인덱스로 검색해도 빠른데 왜 굳이 파티셔닝을 할까? 인덱스로 검색하는 데 왜 느릴까? 인덱스 ROWID는 물리적 주소? 논리적 주소? SQL이 참조하는 컬럼을 인덱스가 모두 포함하는 경우가 아니면, 인덱스를 스캔 한 후에 반드시 테이블을 액세스한다. TABLE ACCESS BY INDEX ROWID라고 표시된 부분 인덱스를 스캔하는 이유는 검색 조건을 만족하는 소량의 데이터를 인덱스에서 빨리 찾고 거기서 테이블 레코드를 찾아가기 위한 주소값인 ROW를 얻음 ROWID는 논리적 주소다. 디스크 상에서 테이블 레코드를 찾아가기 위한 위치 정보를 담는다. 메인 메모리 DB와 비교 메인 메모리

Naver Blog

[Oracle] 트랜잭션, 트랜잭션 Isolation Level

동시성 제어 오라클에서 동시성 제어란 동시에 실행되는 트랜잭션 수를 최대화하면서도 입력, 수정, 삭제, 검색 시 데이터의 무결성이 유지될 수 있도록 노력하는 것이다. 여러 개의 트랜잭션이 동시에 수행될 때, DB 애플리케이션은 이들 트랜잭션이 서로 간섭을 일으키는 현상을 최소화하고 데이터의 일관성과 무결성이 보장되도록 개발되어야 한다. 트랜잭션 (All or Nothing) 트랜잭션이란 여러 개의 수정 작업이 하나의 작업처럼 전부 처리되거나 아예 전부 처리가 안 되도록 하는 것의 최소 단위이다. 트랜잭션 특징 ACID라고 부른다. Atomicity, Consistency, Isolation, Durability 4단어의 앞 글자를 따서 만든 단어이다 먼저 원자성은 더 이상 분해가 불가능한 업무의 최소단위를 의미한다. 일관성은 트랜잭션이 그 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 변환한다. 즉 트랜잭션 실행의 결과로 데이터베이스 상태가 모순되지 않는다. 격리

Naver Blog

JDK 동적 프록시

JDK 동적 프록시 동적 프록시 기술을 사용하면 직접 프록시 클래스를 생성하지 않아도 된다. 동적 프록시가 객체를 동적으로 런타임에 대신 만들어준다. 또한 동적 프록시에 원하는 실행 로직을 지정할 수 있다. JDK 동적 프록시는 인터페이스를 기반으로 프록시를 동적으로 만들어준다. 인터페이스가 필수 JDK 동적 프록시 예제 JDK 동적 프록시를 위해서 Interface를 만들어준다. Interface를 구현한 2개의 클래스를 생성함 JDK 동적 프록시를 사용하기 위해서는 java.lan.reflect에 있는 InvocationHandler를 구현하면 된다. InvocationHandler의 invoke 메서드는 3개의 파라미터를 제공한다. Object proxy는 프록시 자신 Method method는 호출한 메서드 Object[] args는 메서드를 호출할 때 전달한 인수 실제 사용은 위와 같이 한다. 빨간색 네모 동적 프록시에 적용할 핸들러 로직 Interface로 선언을 하고 i

Naver Blog

CGLIB

CGLIB (Code Generator Library) 바이트 코드를 조작해서 동적으로 클래스를 생성하는 라이브러리 인터페이스가 없어도 구체 클래스만 가지고 동적 프록시를 만들어낼 수 있음 스프링을 사용하면 외부 라이브러리 추가 없이 사용 가능 CGLIB 적용을 위해서 인터페이스와 인터페이스를 구현한 서비스 클래스를 만든다. 인터페이스 없이 구체 클래스만 있는 서비스 클래스도 만든다. CGLIB를 사용하기 위해서는 MethodInterceptor를 구현해서 사용하면 된다. 패키지 위치가 중요하다. Object var1 CGLIB가 적용된 객체 Method var2 호출된 메서드 Object[] var3 메서드를 호출하면서 전달된 인수 MethodProxy var4 메서드 호출에 사용 TimeMethodInterceptor는 위와 같이 구현한다. 먼저 메서드를 실행할 target를 생성자로 받을 수 있게 만든다. 프록시가 호출할 실제 대상 그다음 methodProxy의 invoke를

Naver Blog

[Spring] ProxyFactory

ProxyFactory 스프링은 동적 프록시를 통합해서 편리하게 만들어주는 ProxyFactory를 제공 ProxyFactory를 통해서 인터페이스가 있으면 JDK 동적 프록시 사용, 인터페이스 없고 구체 클래스만 있다면 CGLIB 사용할 수 있게 할 수 있다. 의존 관계 JDK 동적 프록시와 CGLIB를 함께 사용하려면 InvocationHandler와 MethodInterceptor를 각각 만들 필요 없고 Advice를 만들면 된다. InvocationHandler와 MethodInterceptor는 Advice를 호출하게 됨 ProxyFactory를 사용하면 Advice를 호출하는 전용 InvocationHandler, MethodInterceptor를 내부에서 사용 TimeAdvice 구현 MethodInvocation 내부에는 다음 메서드를 호출하는 방법 현재 프록시 객체 인스턴스 arguments 메서드 정보 포함 Advice를 사용할 때는 org.aopalliance.i

Naver Blog

[Spring] 포인트컷, 어드바이스, 어드바이저

포인트 컷 (Pointcut) 어디에 부가 기능을 적용할지/말지를 판단하는 필터링 로직 주로 클래스와 메서드 이름으로 필터링 어드바이스 (Advice) 프록시가 호출하는 부가 기능 프록시 로직임 어드바이저 (Advisor) 하나의 포인트컷과 하나의 어드바이스를 가지고 있는 것 부가 기능 로직을 적용할 때 포인트컷으로 어디에 적용할지 선택 어드바이스로 어떤 로직을 적용할지 선택 어디에 / 어떤 로직을 모두 알고 있는 것이 어드바이저 Advisor 코드 interface가 있기에 JDK 동적 프록시가 적용된 걸 볼 수 있다. new DefaultPointCutAdvisor 하나의 포인트컷과 하나의 어드바이스를 넣어주면 된다. Pointcut.True 항상 True를 반환하는 포인트컷 new TimeAdvice() 구현한 Advice를 넘겨줌 proxyFactory.addAdvisor 프록시 팩토리에 적용할 어드바이저를 지정함 어드바이저는 내부에 포인트컷과 어드바이스를 모두 가지고 있음

Naver Blog

[Spring] Bean 후처리기

일반적인 스프링 Bean 등록 @Bean이나 ComponentScan으로 스프링 빈을 등록하면 스프링은 대상 객체를 생성하고 스프링 컨테이너 내부의 Bean 저장소에 등록함 이후 스프링 컨테이너를 통해 등록한 스프링 Bean을 조회해서 사용 Bean 후처리기 스프링이 Bean 저장소에 등록할 목적으로 생성할 객체를 Bean 저장소에 등록하기 직전에 조작하려면 Bean 후처리기를 사용하면 된다. Bean을 생성한 후에 처리하는 용도로 사용 바꿔치기 가능 Bean 후처리기에서 스프링 Bean 객체를 조작하거나 다른 객체로 바꿔치기 가능 스프링 빈 등록 코드 예제 @Configuration과 @Bean을 통해서 first만 Bean에 등록한다. AnnotationConfigApplicationContext를 통해서 BasicConfig.class 설정 파일을 스프링 Bean으로 등록한다. getBean으로 beanFirst는 찾을 수 있고 beanSecond는 찾을 수 없다. Bean 후

Naver Blog

[Mybatis] ResultMap을 쓰지 않아도 자바 객체와 매핑 되는 경우

사용할 자바 객체 사용할 쿼리 사용할 DB 테이블 DB 테이블 이름은 Member 사용할 코드 케이스 1 : @ToString만 있는 경우 사이즈는 2개가 나오나 null 값으로만 나온다. 케이스 2 : @NoArgsConstructor 추가 및 쿼리에 alias 추가 사실 기본생성자는 자동으로 만들어지긴 한다. alias를 추가하면 정상 작동한다. 케이스 3 : @AllArgsConstructor 추가 및 alias 없는 경우 정상적으로 데이터가 나온다. 이 케이스 때문에 좀 헷갈렸다. 분명 자바 객체와 DB 테이블 컬럼의 이름이 다른데 왜 정상결과가 나오나 했는데 전체 생성자가 있으면 생성자 순서대로 값을 할당해준다. 그렇기에 순서가 바뀌면 값이 제대로 나오지 않는다. 순서를 바뀌면 위와 같이 나온다. 케이스 4 : @NoArgsConstructor와 ResultMap 사용, alias는 없음 ResultMap을 사용해도 정상작동한다. 쿼리 순서가 바뀌어도 잘 작동한다. 케이스

Naver Blog

[KAFKA] Java에서 Kafka로 Date 타입의 데이터를 직렬화해서 넣을 때 형식이 변경되는 문제점

상황 DB에는 2023-08-22 15:00:17로 저장이 되어 있는 Date 데이터가 Kafka에 넣으면 다른 형식으로 변경이 되어서 넣어짐 DB 데이터 MySQL 기준 create_dt에 2023-08-19 14:00:37로 되어있음 DB에서 데이터를 가져오고 Kafka에 넣을 자바 객체 Spring boot에서 데이터 확인 및 KafkaProducer 코드 먼저 위 코드를 돌려서 print 문을 확인해보면 DB에서 가져올 때는 정상적으로 가져온다. 다만 이걸 Kafka에서 확인을 해보면 createDt가 밀리세컨트 형식으로 바뀌어진다. Jackson 라이브러리를 활용한 해결법 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind JsonFormat를 사용하면 문제를 해결할 수 있다. 위와 같이 설정 후에 다시 Produce 해보면 원하는 형식으로 들어간다. 밀리세컨트 형식으로 들어간 이유 J

Naver Blog

[ElasticStack] routing

라우팅 엘라스틱서치가 인덱스를 구성하는 샤드 중 몇 번 샤드를 대상으로 작업을 수행할지 지정하기 위해 사용한 값 라우팅 값은 문서를 색인할 때 문서마다 하나씩 지정할 수 있다. 작업 대상 샤드 번호는 지정된 라우팅 값을 해시한 후 주 샤드의 개수로 나머지 연산을 수행한 값 라우팅 값을 지정하지 않으면 _id 값의 해시값을 기반으로 샤드가 배정됨 routing_factor = num_routing_shards / num_primary_shards shard_num = (hash(_routing) % num_routing_shards) / routing_factor 색인 시 라우팅 값을 지정했으면 조회, 업데이트, 삭제, 검색 등의 작업에서도 똑같이 라우팅 지정해야 됨 검색할 때 라우팅 값을 명시하면 단일 샤드를 대상으로 검색함 인덱스 내에서의 _id 고유성 보장 라우팅 값을 명시하지 않으면 전체 샤드를 대상으로 검색을 요청함 성능도 떨어지고 검색 결과도 달라질 수 있음 단건 조회 AP

Naver Blog

[ElasticStack] Update By Query, Delete By Query

Update By Query, Delete By Query 검색 쿼리를 통해서 주어진 조건을 만족하는 문서를 찾은 뒤 문서를 대상으로 업데이트나 삭제 작업을 실행 query_test index에 age를 10으로 색인한다. 버전 충돌, 재시도 횟수 등도 확인 가능 POST query_test/_update_by_query { "script" : { "source": "ctx._source.age += params.plus", "lang": "painless", "params": { "plus" : 1 } }, "query" : { "exists": { "field": "age" } } } age라는 field가 있으면 1을 더하는 script이다. 결과를 확인하면 11인 걸 볼 수 있다. 알아야 할 점 Update By Query는 _doc을 이용한 업데이트를 지원하지 않음 script를 통한 업데이트만을 지원함 문맥 정보 중에서 ctx._now를 사용할 수는 없다. Conflict

Naver Blog

[ElasticStack] tasks API

Tasks API update by query 요청 시에는 wait_for_completion 매개변수를 false로 지정하면 비동기로 처리 가능 비동기 요청을 받으면 엘라스틱서치는 작업을 task로 등록한 뒤 즉시 task의 id가 포함된 응답을 반환 노드의 id와 해당 노드 내 task의 id를 :로 연결한 형태 client는 이 값을 가지고 tasks api를 호출해서 작업을 확인하거나 취소 가능 GET _tasks/tH2u83L4QWCR9jn2vqzphA:36455 GET API를 사용하면 task의 상태를 확인할 수 있다. _tasks는 tasks 관리 API 중 조회 API를 호출하는 것 작업이 진행 중이면 completed가 false로 나오고 response 필드가 존재하지 않는다. task 취소 POST _tasks/tH2u83L4QWCR9jn2vqzphA:47715/_cancel 작업도 취소할 수 있다. 위의 경우는 취소에 실패한 경우 응답에 node_failure

Naver Blog

[ElasticSearch] Cluster 구축 - elasticsearch.yml 수정

구성 환경 elasticsearch : 7.17.x 별 이유는 없음 깔아보니 7.17이어서 엘라스틱서치는 homebrew로 설치 안 했음 mac m1 node 3대로 클러스터를 구성함 local에서 구성함 node1, node2, node3 폴더를 만들고 각각 엘라스틱서치를 설치함 설정값 참고 https://blog.naver.com/fbfbf1/223135286925 node1 elasticsearch.yml elasticsearch-cluster는 임의로 만든 파일임 node1에 관한 elasticsearch.yml path.data에 data 파일은 임의로 만들었다. bin 폴더에서 실행해 보자 localhost:9200로 들어가면 확인할 수 있다. node1을 먼저 실행시키면 cluster_uuid가 "_na_"로 된다. 다른 노드까지 실행하고 다시 보면 cluster_uuid가 추가된다. node2 elasticsearch.yml node2에 관한 elasticsearch.

Naver Blog

[ElasticSearch] mac cerebro 설치 (JDK 16 이상인 경우 에러 발생)

깃허브에서 다운 https://github.com/lmenezes/cerebro/releases Releases · lmenezes/cerebro Contribute to lmenezes/cerebro development by creating an account on GitHub. github.com 원하는 폴더에 압축해제 ./cerebro 명령어 실행 에러 발생 java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed m

Naver Blog

[ElasticStack] 힙 크기, 스와핑, bootstrap.memory_lock, max_map_count

LINUX 기준 힙 크기 7.11 버전 이상 사용 시 config/jvm.options 파일을 직접 수정하는 것보다는 config/jvm.options.d 디렉터리 밑에 별도 파일을 새로 생성해서 힙 크기를 지정하는 것이 관리가 편함 vim config/jvm.options.d/heap-size.options -Xms원하는값 -Xmx원하는값 최솟값과 최댓값을 똑같이 맞춰줘야 GG가 안 일어남 java -Xmx1m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOop UseCompressedOops 값이 true가 되는 경곗값으로 힙 크기를 정해야 한다. java -XX:+UnlockDianosticVMOptions -Xlog:gc+heap+coops=debug -Xmx30720m -version Compressed OOPs mode가 Zero base로 적용되는 경곗값을 적용해야 한다. LINUX 기준 스와핑 엘라스틱서치에서는 스와핑

Naver Blog

[ElasticSearch] 마스터 노드를 여러 개 해야 되는 이유

노드 종류 마스터 노드, 데이터 노드, 인제스트 노드, 코디네이팅 노드가 있는데 elastisearch.yml에서 설정 가능하다. 보통 Cluster를 운영할 때는 마스터 노드와 데이터 노드를 분리한다. 마스터 노드 1대 노드 3대를 사용하는데 노드 1은 마스터 노드이면서 데이터노드 노드 2, 노드 3은 데이터 노드이다. 마스터 노드가 1대만 실행이 되어도 엘라스틱 서치 클러스터가 실행이 될 수 있게 각 노드의 elasticsearch.yml에서 cluster.initial_master_nodes, discovery.seed_hosts에 node1만 적어준다. 노드 1, 노드 2, 노드 3번 다 똑같이 적어야 한다. 먼저 노드 1을 실행해서 보면 클러스터가 정상적으로 구성이 된다. 이후에 노드 2, 노드 3을 실행하자 클러스터에 정상적으로 추가가 된다. 데이터 노드인 2번만 꺼본다. 데이터 노드인 2번에 문제가 생겨서 노드가 다운됐다고 해보자. node 2번이 클러스터에서 나가면 g

Naver Blog

[서평] 육각형 개발자 시니어 개발자로 성장하기 위한 10가지 핵심 역량 최범균 지음 한빛미디어

개발자 사이에서 유명하신 최범균님이 주니어 개발자들을 위한 책을 하나 출간하셨습니다. 출간하기 전부터 여러 사이트에서 좋은 리뷰들이 많이 보여서 꼭 리뷰를 해보고 싶었는데 운 좋게 당첨이 되었네요 이 책 주니어한테 정말 좋습니다. 더 나은 개발자로 성장하기 위해서 필요한 엑기스들을 모아놓은 책이라고 생각합니다. 특히 개발과 관련된 내용도 좋은데 '일'과 관련된 부분에서도 많이 배울 수 있었습니다. 일단 책 자체도 작고 양도 많지 않아서 읽으려면 금방 읽을 수 있습니다. 책의 가독성도 좋고요 코드가 필요한 부분은 코드까지 같이 보여주기에 내용이 잘 파악이 됩니다. 1장 들어가며 간단하게 개발자가 해야 되는 일이 무엇이고 새로운 기술을 써야만 개발 능력이 향상된다고 여기지 말자라고 알려줍니다. 또한 많은 개발자들이 개발만 하면 일을 잘하는 걸로 알고 있는데 개발자는 개발뿐만 아니라 구현 기술, 설계 역량, 업무 관리와 요구 분석/공유, 리드&팔로우 등등 여러 가지도 같이 중요하다고 알려

Naver Blog

예외

예외 예외가 발생했을 때 프로그래밍 회복되도록 강제할 것인지를 생각해 봐야 한다. 일시적으로 발생하는 오류라면 동작을 다시 시도하거나 화면에 메시지를 출력해 응용프로그램의 반응성을 유지할 수 있다. 비즈니스 로직 검증 (잘못된 형식이나 연산) 시 발생한 문제는 불필요한 try / catch 구문을 줄일 수 있게 미확인 예외로 결정 예외가 발생했을 때 응용프로그램을 어떻게 회복시킬 것인지 애매한 상황 API 사용자에게 오류를 복구하라고 강제할 필요가 없다. 시스템 오류 (저장 공간이 꽉 참)가 발생했을 때 사용자가 할 수 있는 일이 없기에 시스템 오류도 미확인 예외로 지정 대다수의 예외 미확인 예외로 지정하고 꼭 필요한 상황에서만 확인된 예외로 지정해 불필요한 코드를 줄임 검증 코드 Vaildator 클래스를 만드는 것을 권장 다만 과하게 만들면 너무 많은 설정 작업이 필요하고, 여러 예외를 선언해야 하고, 사용자가 모든 예외를 처리해야 하기에 생산성이 떨어진다. 과하게 담당하면 구체

Naver Blog

[JAVA] 제네릭

제네릭 결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터를 구체적인 타입으로 대체시키는 기능 T가 타입 파라미터, 타입이 필요한 자리에 T를 사용할 수 있음을 알려주는 역할 Box Class는 T가 무엇인지 모르지만 Box 객체가 생성될 시점에 다른 타입으로 대체된다. 사용을 할 때는 위와 같이 필요한 타입을 넣어서 사용하면 된다. String 타입을 선언했기에 Long Type으로 변수를 할당해 주면 오류가 난다. 타입 파라미터를 여러 개 사용할 수도 있다. getter와 setter를 사용할 때도 타입 파라미터를 명시해 줘야 한다. 제네릭 타입 클래스 결정되지 않은 타입을 파라미터로 가지는 클래스와 인터페이스를 의미한다. 제네릭 타입은 선언부에 <> 부호가 붙고 그 사이에 파라미터들이 위치 제네릭 타입은 컴파일 시 컴파일러에 의해 제거됨 자바 코드에서 선언되고 사용된 제네릭 타입은 컴파일 시 컴파일러에 의해 자동으로 검사되어 타입으로 변환 코드 내의 모든 제네릭

Naver Blog

[JAVA] 제네릭 공변

Cage<Animal>이 Cage<Cat>의 상위 타입이 아니다. 만약 Cage<Animal>이 Cage<Cat>이 상위 타입이라고 한다면 첫 번째 빨간색 네모친 할당 부분이 가능하다. 그럼 두 번째 빨간색 네모친 부분에서 Dog은 Animal의 하위 클래스이기에 Push가 가능하다. 다만 세 번째에서 getAll을 하게 되면 List<Cat>이 리턴 값이나 두 번째에서 dog을 넣었기에 dog 리스트를 리턴한다. 이런 경우에 의해서 Cage<Animal>은 Cage<Cat>의 상위 타입이 아니다. 무변성 A가 B의 상위 타입일 때 GenericType<A>가 GenericType<B>의 상위 타입이 아니면 변성이 없다. 무변성 animal은 Cat의 상위 타입이지만 Cage<Animal>은 Cage<Cat>의 상위 타입이 아니다. 무변성일 때 문제 위와 같이 Dog을 상속받는 Small, CuteDog 구현 그리고 Dog cage에 사랑을 주는 Human class도 구현 그리고

Naver Blog

[Effective Java] 로 타입은 사용하지 말라

제네릭 클래스, 제네릭 인터페이스 클래스와 인터페이스 선언에 타입 매개변수가 쓰이면 제네릭 클래스, 인터페이스라고 부름 제네릭 클래스와 인터페이스를 통틀어 제네릭 타입이라고 함 각각의 제네릭 타입은 매개변수화 타입(Parameterized Type)을 정의 List<String>과 같이 정의 원소의 타입이 String인 리스트를 뜻하는 매개변수화 타입 로 타입 제네릭 타입을 하나 정의하면 Raw Type도 함께 정의된다. Raw Type은 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 의미 List<E>의 Raw Type은 List이다. Raw Type은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작함 Raw 타입을 사용하면 Cat을 넣어야 되는 곳에 Dog을 넣어도 컴파일 에러가 발생하지 않는다. 런타임 에러 시점에서 실수했다는 걸 발견할 수 있다. 제네릭을 사용하면 타입 선언 자체에 Cat만 넣어야 된다는 걸 알 수 있다. Dog을 넣는 코드를 작성하면 컴

Naver Blog

[Effective Java] 비검사 경고를 제거하라

비검사 경고를 제거하라 -Xlint:uncheck 옵션을 사용하면 무엇이 잘못된 지 알 수 있다. 할 수 있는 한 모든 비검사 경고를 제거하자! 모두 제거하면 타입 안정성이 확실히 보장된다. @SuppressWarnings("unchecked") 경고를 제거할 수는 없지만 타입이 안전하다고 확신할 수 있으면 @SuppressWarnings("uncheck") 애너테이션을 사용해서 경고를 숨기자 다만 타입 안전이 검증되지 않은 채로 애너테이션을 사용하면 안 된다. 위 애너테이션은 개별 지역변수 ~ 클래스 전체까지 달 수 있다. 하지만 항상 가능한 한 좁은 범위에 적용하자! 변수 선언, 아주 짧은 메서드, 생성자 절대로 클래스 전체에 적용해서는 안 된다. return 문에는 사용할 수 없다. 주석 @SuppressWarnings("uncheck")를 사용할 꺼면 경고를 무시해도 안전한 이유를 항상 주석으로 남기자 다른 사람이 코드를 이해하는 데 도움이 되고 다른 사람이 코드를 잘못 수정

Naver Blog

[Effective Java] 배열보다는 리스트를 사용하라

배열과 제네릭 타입의 차이 1 배열은 공변이다. 공변은 함께 변한다는 뜻 Sub가 Super의 하위 타입이면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다. 제네릭은 불공변이다. 서로 다른 타입 Type1, Type2가 있을 때 List<Type1>은 List<Type2>의 하위/상위 타입이 아니다 제네릭이 문제 있어 보이지만 문제가 있는 건 배열 쪽이다. 위 코드는 컴파일 단계에서는 문제가 없지만 런타임 단계에서 문제가 발생한다. List를 사용하면 처음부터 호환이 되지 않는다. 컴파일 단계에서 문제를 알 수 있게 된다. 배열과 제네릭의 차이 2 배열은 실체화(reify) 된다. 배열은 런타임에도 담기로 한 원소의 타입을 인지하고 확인 그래서 Long 배열에 String을 넣으려 하면 ArrayStoreException이 발생 제네릭은 타입 정보가 런타임에는 소거된다. 원소 타입을 컴파일 타임에만 검사하고 런타임에는 알 수 없다. 소거는 제네릭이 없는 레거시 코드와 제네

Naver Blog

[Effective Java] 이왕이면 제네릭 타입으로 만들라

Object를 사용한 Stack public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) { throw new EmptyStackException(); } final Object result = elements[--size]; elements[size] = null; return result; } public boolean isEmpty() { return size == 0; } private void ens

Naver Blog

[Effective Java] 이왕이면 제네릭 메서드로 만들라

이왕이면 제네릭 메서드로 만들라 클래스와 마찬가지로, 메서드도 제네릭으로 만들 수 있다. 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다. 로타입을 사용해서 제네릭 메서드를 만들 수 있지만 경고가 발생한다. 원소 타입을 타입 매개변수로 명시하고 메서드 안에서도 타입 매개변수만 사용하게 수정하면 경고를 없앨 수 있다. 문제 없이 사용 가능하다. 불변 객체를 여러 타입으로 활용 제네릭은 런타임에 타입 정보가 소거되기에 하나의 객체를 어떤 타입으로든 매개변수화할 수 있다. 하지만 이렇게 하려면 요청한 타입 매개변수에 맞게 매번 그 객체의 타입을 바꿔주는 정적 팩터리를 만들어야 한다. 이 패턴을 싱글턴 팩터리라고 한다. 항등함수를 담은 클래스 항등함수 객체는 상태가 없으니 요청할 때마다 새로 생성하는 것은 낭비 자바의 제네릭이 실체화된다면 항등함수를 하나씩 만들어야 했겠지만 소거 방식을 사용하면 제네릭 싱글턴 하나로 충분하다. 위와 같이 제네릭을 사용해서 만들 수 있다. ID

Naver Blog

[Effective Java] 한정적 와일드카드를 사용해 API 유연성을 높이라

불공변 서로 다른 타입 Type1과 Type2가 있을 때 List<Type1>은 List<Type2>의 하위 타입도 상위 타입도 아니다. 예를 들면 List<String>은 List<Object>의 하위 타입이 아니다는 뜻 List<Object>에는 어떤 객체든 넣을 수 있지만 List<String>에는 문자열만 넣을 수 있다. 즉, List<String>은 List<Object>가 하는 일을 제대로 수행하지 못하니 하위 타입이 될 수 없다. 불공변 방식보다 유연한 무언가 와일드카드 타입을 사용하지 않는 pushAll 메서드에는 결함이 있다. src의 원소 타입과 스택의 원소 타입이 일치하면 잘 동작한다. 하지만 Stack<Number>로 선언하고 Iterable<Integer>를 넣으면 Integer가 Number의 하위 타입이니 잘 동작할 것 같지만 실제로는 매개변수화 타입이 불공변이기에 오류 메시지가 뜬다. 해결법 한정적 와일드카드 타입이라는 특별한 매개변수화 타입 지원함 pu

Naver Blog

가을 소풍

티켓 받기 쉽지 않았어 회사에서 가을 소풍 보내줬다(?) 역시 복지는 좋다. 서울대공원, 서울랜드, 코끼리 열차 탑승권, 인당 만원 식사 쿠폰 줬다. 입장 티켓들이 기한이 좀 있어서 뭐 갈까 하다가 일단 서울랜드 한 번도 안 가봐서 서울랜드 가봤다. 비가 많이 오면 어쩌나 싶었는데 별로 안 와서 다행이었다. 여기는 서울랜드 후문! 뭔가 처음 봤을 때 서울 느낌 났다. 들어가면 보이는 분수 사진을 보면 알겠지만 정말 사람이 없었다. 아마 비가 와서 사람들이 별로 안 온 것 같다. 사람 없어서 오히려 좋았다. 감사합니당 후문에서 주차를 하고 정문에서 가을 소풍 선물(?)을 받았다. 에코백이랑 간식 받았다. 그리고 폴라로이드 사진도 찍어줘서 사진도 이쁘게 찍었다. 여기가 서울랜드 정문이다. 놀이공원들 정문은 다 비슷한 느낌이다. 뭔가 날씨가 안 좋아서 아포칼립스 느낌도 나네 간식은 뭐가 있나 봤는데 포켓몬 빵 3개에 간식거리 있었다. 포켓몬 빵 존맛탱 들어가자마자 슬러시 하나 사서 먹었다

Naver Blog

[Effective Java] 제네릭과 가변인수를 함께 쓸 때는 신중하라.

가변인수 가변인수는 메서드에 넘기는 인수의 개수를 클라이언트가 조절할 수 있게 해줌 하지만 가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어짐 내부로 감춰야 할 배열을 클라이언트에 노출하는 문제가 생김 그 결과 가변인수 매개변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고 발생 실체화 불가 타입은 런타임에는 컴파일 타임보다 타입 관련 정보를 적게 담고 있음 메서드를 선언할 때 실체화 불가 타입으로 가변인수 매개변수를 선언하면 컴파일러가 경고 발생 위 코드에서는 형변환하는 곳이 보이지 않는데도 인수를 건네서 호출하면 예외를 던진다. 마지막 줄에 형변환이 숨어 있기 때문이다. 위와 같은 사례로 타입 안전성이 깨지니 제네릭 가변인수 배열 매개변수에 값을 저장하는 건 안전하지 않음 @SafeVarargs @SafeVarargs 애네티이션으로 제네릭 가변인수 메서드 작성 시에 클라이언트 측에서 발생하는 경고를 숨길 수 있게 되었음 이 애너테이션은

Naver Blog

[Effective Java] 타입 안전 이종 컨테이너를 고려하라

단일원소 컨테이너 제네릭은 Set<E>, Map<K, V> 등의 컬렉션과 ThreadLocal<T>, AtomicReference<T> 등의 단일원소 컨테이너에도 흔히 사용됨 이런 모든 쓰임에서 매개변수화되는 대상은 원소가 아닌 컨테이너 자신 따라서 하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한됨 Set<E>은 E 한 개, Map<K, V>는 K, V 2개가 필요한 식 더 유연한 수단 타입 안전 이종 컨테이너 패턴 컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공 이러면 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해 줌 타입 안전 이종 컨테이너 패턴 예시 타입별로 즐겨 찾는 인스턴스를 저장하고 검색할 수 있는 Favorites 클래스 각 타입의 Class 객체를 매개변수화한 Map의 Key 역할 사용 class의 클래스가 제네릭이기 때문이다. class 리터럴 타입은 Class가 아닌 Class<T>다. 컴파일타임

Naver Blog

[Java] ThreadLocal

동시성 문제 여러 쓰레드가 동시에 같은 인스턴스의 필드 값을 변경하면서 발생하는 문제를 동시성 문제 다음과 같이 TestService와 test 메서드를 작성한다. sleep(1000)을 통해서 1초의 지연을 만들자. 그렇기에 1초 이후에 호출하면 순서대로 실행을 할 수 있다. 다음과 같이 2개의 쓰레드를 만들고 2초 간격으로 실행할 수 있게 만든다 그럼 결과가 정상적으로 나온다. first는 nameStore에 할당된 값이 없기에 null이 나오는 게 맞고 second는 First가 끝나고 난 뒤에 실행이 됐기에 처음 nameStore는 first가 맞다. 이번에는 sleep을 0.1초 둔다 이렇게 되면 test 메서드에서는 1초의 지연이 있는 데 이 지연이 끝나기 전에 다른 쓰레드가 접근이 가능하다. sleep이 2초일 때와 달리 First의 NameStore가 동시성 문제로 인해서 second로 나오게 된다 여기서 문제는 조회하는 부분에서 발생함 first와 second는 각각

Naver Blog

리플렉션

리플렉션 자바에서 제공하는 기술로 리플렉션을 사용하면 클래스나 메서드의 메타정보를 동적으로 획득하고, 코드도 동적으로 호출할 수 있다. 컴파일 시점이 아닌 런타임 시점에 정보를 획득할 수 있다. 위의 코드를 보면 호출하는 메서드만 다르고 전체 코드의 흐름은 같다. 호출하는 메서드만 동적으로 처리하면 공통화를 할 수 있다. 이때 사용하는 게 리플렉션이다. java.lang에 있는 Class를 통해서 리플렉션을 적용하려는 클래스의 메타정보를 획득한다. 내부 클래스는 $를 통해서 구분한다. getMethod를 통해서 해당 클래스의 callA / callB 메서드 메타 정보 획득 invoke를 통해서 getMethod를 통해서 획득한 메서드 메타정보로 실제 인스턴스의 메서드를 호출함 Invoke를 호출할 때 target 인스턴스를 넘겨주면 해당 인스턴스의 callA 메서드를 찾아서 실행함 dynamicCall를 통해서 공통 처리 로직을 만들 수 있다. Method는 java.reflect에

Naver Blog

비 오는 날 캐리비안베이 나들이

2023년 7월 15일에 여자친구랑 캐리비안베이 갔다. 비가 많이 오면 어쩌나 싶었는데 비는 별로 안 왔다. 그래서 그런지 사람도 꽤 많았다. 놀이 기구는 인기 많은 건 대기시간이 2시간이라고 써져있었다. 놀이 기구는 안 타고 계속 유스풀이랑 파도풀만 즐겼다. 티켓은 네이버에 샀고 방수팩은 다이소에서 사갔다. 모자는 파도풀이나 유스풀 들어갈 때 무조건 써야되는 줄 알았는데 아니였다. 안 쓰고 들어가도 별 문제 없다. 오랜만에 보는 캐리비안 베이 정문 처음에 베이코인 안 하고 옷 갈아입고 베이코인으로 바꿨다. 참고로.. 내 기억에는 옛날에는 구명조끼 보증금만 받았는데 요즘에는 보증금 안 받고 대여금으로 8000원 받는다 너무 비싸 그래서 그런지 길거리에 버려진 구명조끼들이 많이 보였다. 옷 갈아입고 바로 파도풀로 달려갔다. 참고로 워터슈즈 있으면 가지고 가자.. 오랜만에 파도풀 가니 재밌었다. 물도 미지근해서 별로 안 추웠다. 저기 보이는 노란색 선 앞뒤에 있는 게 제일 재밌긴 하다.

Naver Blog

[서평] 마이크로서비스 아키텍처 구축 샘 뉴먼 지음, 정성권 옭김, 한빛미디어

이번에 서평 할 책은 마이크로 서비스 아키텍처 구축입니다. 책 앞표지에는 벌집이 그려져 있네요 마이크로서비스를 의미하는 것 같네요 뒷면에는 무려 마틴 파울러의 서평까지 있습니다. 마틴 파울러가 서평 남긴 책이라서 신뢰가 많이 가는 책이네요. 책 뒷면의 이정인님이 남기신 서평이 딱 알맞은 것 같습니다. 주니어 개발자에게는 이 책을 통해서 마이크로서비스 아키텍처에 대한 이해를 높일 수 있다고 생각하네요 다만 책이 난이도가 있기 때문에 어느 정도 MSA를 다뤄본 주니어부터 읽어보는 게 좋다고 생각이 드네요 책은 1장부터 16장까지 구성이 되어있습니다. 총 3부로 기초, 구현, 사람으로 나눠져있습니다. 책은 읽기 편하게 되어 있습니다. 적재적소에 그림도 잘 배치되어 있고 소제목도 잘 나눠놓아서 읽기 편하네요. 초반에는 모놀리스 서비스에 대해서 설명을 하고 뒤에서 MSA에 대해서 설명을 합니다. MSA에 대해서 얘기할 때 빠지지 않는 게 DDD인데 여기서도 DDD를 어느 정도 설명하고 넘어갑

Naver Blog

[KAFKA] 브로커 설정

/kafka/config에서 server.properties에서 설정 broker id 카프카 브로커 정숫값 식별자 클러스터 내에서 전부 달라야함 zookeeper.connect 브로커의 메타데이터의 저장되는 주키퍼 위치 Log.dirs 모든 메시지를 Log Segment 단위로 묶어서 Log.dir 설정에 지정된 디스크 디렉토리에 저장 다수의 디렉토리를 지정할꺼면 Log.dirs를 사용 num.recovery.threads.per.data.dir 설정 가능한 스레드 풀을 사용해서 로그 세그먼트를 관리함 하나의 로그 디렉토리에 대해 하나의 스레드만이 사용됨 auto.create.topics.enable 토픽 자동 생성 막음 auto.leader.rebalance.enable 가능한 한 리더 역할이 균등하게 분산되도록 해서 카프카 클러스터 균형이 깨지는 걸 막아줌 Delete.topic.enable true로 설정하면 클러스터의 토픽을 임의로 삭제하기 못하게 막는다. num.parti

Naver Blog

[4년 전 오늘] 2019.07.29 동탄 쿠팡 알바 허브B팀 후기

2019.7.29. 4년 전 오늘 2019.07.29 동탄 쿠팡 알바 허브B팀 후기 +추가 2019.12.28 많은 분들이 제 글을 읽어주시는데 일단 매우 감사합니다. 한 가지 알아두셔야 될게 이 글은 7월에 쓴 거라 지금이랑 좀 다릅니다! 그걸 꼭 확인해 주세요 저 할 때는 9시부터였는데 아마 지금은 8시부터 일 겁니다! 그리고 제가 답변드릴 수 있는 게 있고 답변드릴 수 없는 게 있습니다. 저도 알바 몇 번... 류리상자 나 블로그 시작하고 얼마 안돼서 올린 글인데 이게 대박나서 하루에 500명 넘게 왔었다 ㅋㅋㅋ 요즘은 거의 조회수가 0이네 ㅠ

Naver Blog

[KAKFA] Producer 설정

ProducerRecord 카프카에 메시지를 쓰는 작업은 ProducerRecord 객체를 생성해서 사용한다. Record가 저장될 토픽과 밸류 지정은 필수, 키와 파티션 지정은 선택사항 ProducerRecord를 전송하는 API를 호출했을 때 Producer가 가장 먼저 하는 일은 Key와 Value를 네트워크에 전송될 수 있도록 직렬화해서 byte 배열로 변환하는 과정 파티션을 지정하지 않았으면 해당 데이터를 파티셔너로 보냄 파티셔너가 파티션을 결정하는 역할을 함 보통 ProducerRecord 객체의 Key 값 사용해서 결정 프로듀서는 레코드를 같은 토픽 파티션으로 전송될 레코들을 모은 Record Batch에 추가함 별도의 스레드가 Record Batch를 적절한 카프카 브로커에게 전송함 Producer 응답 값 메시지가 성공적으로 저장되면 브로커는 토픽, 파티션, 해당 파티션의 레코드의 오프셋을 담은 RecordMetaData 객체를 리턴한다. 에러가 발생하면 에러 리턴됨

Naver Blog

[KAFKA] 파티션, 헤더, 인터셉터

파티션 ProducerRecord는 토픽, 키, 밸류의 값을 포함한다. 대부분 키값이 지정된 레코드를 쓴다. 키의 역할은 두 가지 메시지에 함께 저장되는 추가적인 정보이기도 하지만 토픽에 속한 여러 개의 파티션 중 해당 메시지가 저장될 파티션을 결정짓는 기준점 같은 키값을 가진 모든 메시지는 같은 파티션에 저장된다. 키값이 null 기본 파티셔너 사용할 때 키값이 Null인 레코드가 주어지면 토픽의 파티션 중 하나에 랜덤으로 저장 카프카 2.4 프로듀서부터는 접착성 처리를 위해 기본 파티셔너에서 Round Robin 알고리즘 사용 접착성 처리 접착성 처리가 있은 경우 key 값이 null인 메시지들은 일단 key 값이 있는 메시지 뒤에 따라붙은 다음에 라운드 로빈 방식으로 배치됨 key 값있는 메시지 뒤에 따라붙기에 접착성 처리 헤더 레코드에는 헤더를 포함할 수 있다. 헤더는 카프카 레코드의 key/value 값을 건드리지 않고 추가 메타데이터를 심을 때 사용함 메시지의 전달 내역을

Naver Blog

[KAFKA] Consumer 설정

fetch.min.bytes 컨슈머가 브로커로부터 레코드를 얻어올 때 받는 데이터의 최소량을 지정 기본값은 1바이트 브로커가 컨슈머로부터 레코드 요청을 받았는데 새로 보낼 레코드의 양이 fetch.min.bytes보다 작을 경우, 브로커는 충분한 메시지를 보낼 수 있을 때까지 기다린 뒤 컨슈머에게 레코드를 보내준다. fetch.max.wait.ms 카프카가 컨슈머에게 응답하기 전에 데이터가 모일 때까지 얼마나 오래 기다릴 것인지를 결정한다. 기본값은 500 밀리초 카프카는 토픽에 컨슈머에게 리턴할 데이터가 부족할 경우 최소량 조건을 맞추기 위해 500밀리까지 기다리게 된다. fetch.max.wait.ms를 100밀리초, fetch.min.bytes를 1MB로 잡을 경우 카프카는 컨슈머로부터 읽기 요청을 받았을 때 리턴할 데이터가 1MB 이상 모이거나 100밀리초가 지나거나 두 조건 중 하나가 만족되는 대로 리턴하게 됨 fetch.max.bytes 컨슈머가 브로커를 폴링 할 때 카프

Naver Blog

[KAKFA] 오프셋, 커밋, 리밸런스 리스너

오프셋 커밋 컨슈머는 카프카의 특수 토픽인 __consumer_offsets 토픽에 각 파티션 별로 커밋 된 오프셋을 업데이트하도록 메시지를 보내서 커밋을 함 리밸런싱 발생 시 문제 사항 파티션에 커밋 된 오프셋이 클라이언트가 처리한 마지막 메시지의 오프셋보다 작을 경우 마지막으로 처리된 오프셋과 커밋 된 오프셋 사이의 메시지들이 두 번 처리됨 커밋 된 메시지가 클라이언트가 실제로 처리한 마지막 메시지의 오프셋보다 클 경우 마지막으로 처리된 오프셋과 커밋 된 오프셋 사이의 모든 메시지들은 컨슈머 그룹에서 누락됨 비동기적 커밋 브로커가 커밋에 응답할 때까지 기다리는 대신 요청만 보내고 처리를 계속함 문제점 commitSync는 성공하거나 재시도가 불가능한 실패가 발생할 때까지 재시도 하지만 commistAsync는 재시도 하지 않는다. CommistAsync가 서버로부터 응답을 받은 시점에 이미 다른 커밋 시도가 성공했을 수 있기에 오프셋 2000을 커밋 하는 요청을 보낸 후 브로커가

Naver Blog

넷플릭스 고스트워

https://www.netflix.com/title/80098200 고스트 워 | 넷플릭스 공식 사이트 보이지 않는 적의 출현으로 인해 유럽의 거리는 초토화가 된다. 이 위기를 해결하기 위해 나선 엘리트 특수 작전 부대와 미지의 적과의 사투가 시작된다. 전율을 부르는 SF 스릴러. www.netflix.com 나름 내용이 신선하고 재밌음 처음 보는 주제에 대한 영화임 2016년에 나온 영화인데 별로 안 알려져서 아쉽네 이런 영화가 많이 알려져야 되는데

Naver Blog

넷플릭스 프로젝트 파워

https://www.netflix.com/title/80204465 프로젝트 파워 | 넷플릭스 공식 사이트 소령이라 불리는 남자, 10대 소녀, 경찰. 이들이 뉴올리언스에서 얽힌다. 5분간 초능력을 발휘할 수 있는 약. 도시 전체를 혼돈에 빠트린 파워의 출처를 뒤쫓으면서. www.netflix.com 애매함 더 재밌게 만들 수 있었을 것 같은데 아쉽네

Naver Blog

넷플릭스 익스트랙션 1, 2

https://www.netflix.com/title/80230399 익스트랙션 | 넷플릭스 공식 사이트 어떤 일에도 쉽게 동요하지 않는 냉철한 청부업자. 그가 살아남기 위해 자신을 끊임없이 성찰한다. 마약왕의 납치된 아들을 구하러 간 방글라데시에서 그 모든 것이 시작됐다. www.netflix.com https://www.netflix.com/title/81098494 익스트랙션 2 | 넷플릭스 공식 사이트 고도로 숙련된 전직 특수 요원 타일러 레이크. 죽음의 문턱에서 가까스로 살아 돌아온 그가 또다시 위험천만한 작전에 뛰어든다. 임무는 하나. 무자비한 갱스터의 가족을 감옥에서 탈출시켜라. www.netflix.com 전형적인 특수 요원 이야기이긴한데 액션 재밌음

Naver Blog

[KAKFA] 한 개의 Consumer에서 여러 개의 Topic Subscribe

상황 한 개의 Consumer에서 2개의 Topic을 구독해야되는 상황 주키퍼 실행 카프카 브로커 실행 토픽 생성 Producer Producer를 통해서 두 개의 토픽에 각각 5000개씩 데이터를 넣음 데이터가 생성이 되는 걸 볼 수 있다. Consumer subscribe를 통해서 두 개의 토픽 이름을 적음 결과 내가 예상했던 건 동시에 두 개의 토픽에서 데이터를 읽어올꺼라고 생각해서 로그가 뒤죽박죽 나올 줄 알았다. 결과를 보니 토픽 하나부터 쭉 읽고 그 다음 토픽 쭉 읽는다. 심지어 번갈아가면서 읽지도 않는다.

Naver Blog

[Oracle] SGA, Table Full Scan, Index Range Scan

SGA System Global Area에는 DB Buffer Cache와 Library Cache 존재 라이브러리 캐시 SQL, 실행계획, DB 저장형 함수/프로시저 등을 캐싱 하는 코드 캐시 DB 버퍼 캐시 데이터 캐시 디스크에서 읽은 데이터 블록을 캐싱 해둬서 같은 블록에 대한 반복적인 I/O Call을 줄임 Table Full Scan 테이블에 속한 블록 전체를 읽어서 사용자가 원하는 데이터를 찾는 방식 Table Full Sacn을 수정하는 건 SQL 성능 향상에 큰 도움이 되지 않음 오히려 인덱스가 SQL 성능을 떨어뜨리는 경우가 더 많다. 한 번에 많은 데이터를 처리하는 집계용 SQL, 배치 프로그램은 상당수 Table Full Scan으로 유동하면 성능이 빨라진다. 조인을 포함한 SQL 이면, 조인 메서드로 해시 조인을 선택하면 된다. Index Range Scan 인덱스에서 일정량을 스캔하면서 얻은 ROWID로 테이블 레코드를 찾아가는 방식 ROWID는 테이블 레코드

Naver Blog

[Oracle] 인덱스 수직적 탐색, 수평적 탐색

인덱스 튜닝 두 가지 핵심요소 인덱스는 큰 테이블에서 소량 데이터를 검색할 때 사용한다. 첫 번째 인덱스 스캔 과정에서 발생하는 비효율 줄이기 인덱스 스캔 효율화 튜닝 두 번째 테이블 액세스 횟수 줄이기 인덱스 스캔 후 테이블 레코드를 액세스할 때 랜덤 I/O 방식을 사용하기에 랜덤 액세스 최소화 튜닝 더 중요한 걸 하나 고르면 랜덤 액세스 최소화 튜닝이 더 중요하다. 성능에 미치는 영향이 더 크다. SQL 튜닝은 랜덤 I/O 와의 전쟁!!! SQL 튜닝은 랜덤 I/O 와의 전쟁!!! DB 성능이 느린 이유는 디스크 I/O 때문이다. 인덱스 구조 대용량 테이블에서 필요한 데이터만 빠르게 효율적으로 액세스 하기 위해 사용하는 오브젝트 인덱스를 이용하면 범위 스캔이 가능하다. 범위 스캔이 가능한 이유는 인덱스가 정렬되어 있기 때문이다. 일반적으로 B+ Tree 인덱스 사용함 루트와 브랜치 블록에는 키 값을 갖지 않는 특별한 레코드가 존재 가장 왼쪽 첫 번째 레코드 LMC = LeftMo

Naver Blog

[Oracle] 인덱스 기본 사용법, Range Scan, 인덱스 사용조건

인덱스 기본 사용법 인덱스를 Range Scan 하는 게 기본 사용법이다. 인덱스 컬럼(선두 컬럼)을 가공하지 않아야 인덱스를 정상적으로 사용할 수 있다. 인덱스 정상적 사용은 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 것을 의미 리프 블록 일부만 스캔하는 index Range Scan을 의미 인덱스 컬럼을 가공하면 스캔 시작점을 찾을 수 없고 멈출 수도 없어 리프 블록 전체를 스캔 Index Full Scan 방식으로 작동한다. 인덱스 Range Scan 할 수 없는 이유 인덱스 컬럼을 가공하면 인덱스를 정상적으로 사용할 수 없다. 인덱스 스캔 시작점을 찾을 수 없기 때문이다. Index Range Scan은 인덱스에서 일정 범위를 스캔한다는 뜻 그렇기에 시작지점과 끝지점이 있어야한다. LIKE를 대한%으로 사용하면 특정 구간에 모여있기에 Range Scan이 가능하지만 %대한%을 포함하는 값은 전체 구간에 걸쳐 흩어져 있어서 Range Scan 불가능

Naver Blog

[Oracle] 인덱스를 활용한 SORT 연산 생략, ORDER BY 절 컬럼 가공

인덱스를 이용한 SORT 연산 생략 인덱스를 Range Scan 할 수 있는 이유는 데이터가 정렬되어 있기 때문 찾고자 하는 데이터가 정렬된 상태로 서로 모여있기에 전체가 아닌 일정 부분만 읽다가 멈출 수 있다. 테이블과 달리 인덱스는 정렬되어 있다. 인덱스를 사용하는 이유 인덱스가 정렬되어 있기에 Range Scan이 가능하고 소트 연산 생략 효과도 부수적으로 얻게 됨 장비번호 + 변경일자 + 변경순번 순으로 PK를 구성한다고 하면 PK 인덱스에서 장비번호, 변경일자가 같은 레코드는 변경순번 순으로 정렬된다. SELECT * FROM 상태변경이력 WHERE 장비번호 = 'C' AND 변경일자 = '20230711'; 장비번호와 변경일자를 모두 '=' 조건으로 검색할 때 PK 인덱스를 사용하면 결과 집합은 변경순번 순으로 출력된다. 옵티마이저는 위와 같은 속성을 활용해서 ORDER BY가 있어도 정렬 연산을 따로 수행하지 않는다. PK 인덱스를 스캔하면서 출력한 결과 집합은 어차피

Naver Blog

[Oracle] SELECT-LIST에서 컬럼 가공

SELECT-LIST에서 컬럼 가공 장비번호 + 변경일자 + 변경순번 위와 같이 인덱스를 구성하면 변경순번 최솟값을 구할 때도 옵티마이저는 정렬 연산을 수행하지 않는다. SELECT WIN(변경순번) FROM 상태변경이력 WHERE 장비번호 = 'C' AND 변경일자 = '20230712' 수직적 탐색을 통해 조건을 만족하는 가장 왼쪽 지점으로 내려가서 첫 번째 읽는 레코드가 바로 최솟값이기에 SELECT NVL(MAX(TO_NUMBER(변경순번)), 0) FROM 상태변경이력 WHERE 장비번호 = 'C' AND 변경일자 = '20230712' 하지만 위와 같이 변경순번을 숫자 값으로 바꾸게 되면 정렬 연산을 생략할 수 없다. SELECT NVL(TO_NUMBER(MAX(변경순번)), 0) FROM 상태변경이력 WHERE 장비번호 = 'C' AND 변경일자 = '20230712' 위와 같이 TO_NUMBER을 바깥으로 쓰게 되면 정렬 연산을 생략할 수 있다. 자동 형변환 고객 테이

Naver Blog

[Oracle] Index Range Scan, Index Full Scan

Index Range Scan B+ Tree 인덱스의 가장 일반적이고 정상적인 형태의 액세스 방식 인덱스 루트에서 리프 블록까지 수직적으로 탐색한 후에 필요한 범위만을 스캔한다. 0 SELECT STATEMENT Optimizer=ALL_ROWS 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (TABLE) 2 1 INDEX (RANGE SCAN) OF 'EMP_DEPTNO_IDX' (INDEX) Index Range Scan을 사용하려면 인덱스 선두 컬럼을 가공하지 않은 상태로 조건절에서 사용해야 함 반대로 선두 컬럼을 가공하지 않으면 index Range Scan 무조건 가능하다. Index Full Scan 수직적 탐색 없이 인덱스 리프 블록을 처음부터 끝까지 수평적으로 탐색하는 방식 Index on emp(enam, sal) select * from emp where sal > 2000 order by enam; 0 SELECT STATEMENT

Naver Blog

[ElasticStack] 노드 재시작

장애 상황이 아닌 상황에서 노드 재시작할 때 사용하는 방식 클러스터 내 샤드 할당 기능 비활성화 PUT /_cluster/settings { "persistent": { "cluster.routing.allocation.enable": "none" } } 클러스터 내에 샤드 할당 기능 비활성화 none으로 설정하면 샤드 재분배를 하지 않는다. 위 기능을 none으로 하면 한 노드의 프로세스를 중지해서 클러스터에 제외되더라도 해당 노드에 포함된 샤드를 다른 노드로 재분배하지 않는다. 프라이머리 샤드와 레플리카 샤드 데이터 동기화 프라이머리 샤드와 레플리카 샤드 간의 데이터를 똑같은 형태로 맞춘다. 두 샤드가 가지고 있는 문서가 일치해야 클러스터에서 노드가 갑작스럽게 제외되더라도 데이터의 정합성을 보장할 수 있기에 POST /_flush/synced 이 과정 이후에 노드를 재실행하면 된다. 이탈한 노드 클러스터에 합류했는지 확인 작업이 필요한 노드를 다시 재실행하고 클러스터에 합류했는지

Naver Blog

[ElasticStack] 디스크 사용량 임계치 설정

옵션 설명 기본값 cluster.routing.allocation.disk.watermark.low 특정 노드에서 임계치가 넘어가면 샤드를 더 이상 할당하지 않음 85% cluster.routing.allocation.disk.watermark.high 임계치가 넘어선 노드를 대상으로 즉시 샤드 재할당 진행, 새롭게 생성된 인덱스에 대해서도 적용됨 90% cluster.routing.allocation.disk.watermark.flood_stage 전체 노드가 임계치를 넘어서면 인덱스를 read only 모드로 변경 95% cluster.info.update.interval 임계시 설정을 체크할 주기 30s cluster.routing.allocation.disk.watermark.low 설정한 값보다 높아지면 해당 노드에는 현재 생성되어 있는 인덱스에 새로운 샤드들을 더 이상 배치하지 않는다. 새롭게 생성되는 인덱스의 샤드들은 배치함 cluster.routing.allocatio

Naver Blog

[ElasticStack] elasticsearch 설정 우선순위

transient가 1순위다. 다만 클러스터와 관련된 항목 외에 노드별로 설정할 수 있는 항목들은 elasticsearch.yml 파일에서만 설정할 수 있다. elasticsearch.yml 우선순위에 따라 노드 별로 설정해야 하는 항목과 변경되지 않고 클러스터에 공통으로 필요한 설정은 elasticsearch.yml 파일에 설정 persistent 자주 변경되지는 않지만 간헐적으로 변경이 필요한 설정은 cluster api를 통해 persistent에 디스크 사용량 임계치 설정 transient 자주 변경되는 설정은 cluster api를 통해서 transient에 설정하는 것이 좋다. 샤드 할당 활성/비활성화 설정

Naver Blog

[ElasticStack] 인덱스 복구 시 사용 가능한 최대 네트워크 트래픽 설정 변경

PUT /_cluster/settings { "persistent" : { "indices.recovery.max_bytes_per_sec" : "100mb" } } 노드 장애 발생 후 복구 시 각 노드에서 샤드의 재배치 등 데이터를 주고받게 됨 위의 설정을 통해서 네트워크 트래픽 최댓값을 설정할 수 있음 최대 초당 100MB씩 데이터를 주고받을 수 있다는 의미 기본 값은 40MB 다만 이 값이 너무 크면 다른 서비스에 영향을 줄 수 있기에 주의 필요 기본 값 40MB는 작은 편이기에 시스템 환경 고려해서 늘리는 것이 좋다.

Naver Blog

[ElasticStack] explain, retry_failed

explain GET /_cluster/allcation/explain unassigned 샤드가 발생했을 때 unassigned 샤드가 발생한 원인을 확인할 수 있는 기능 제공 retry_failed 샤드 배치 실패의 원인을 제거한 후 배치해야 할 unassigned 샤드의 개수가 너무 많으면 retry_failed를 이용해서 한 번에 여러 개의 unassigned 샤드를 배치할 수 있다. 샤드 배치가 모두 자동으로 이루어지기에 특정 노드에 배치해야할 필요가 있으면 allocate_replica를 사용하는 게 좋다. POST /_cluster/reroute?retry_failed explain와 retry_failed 먼저 explain 메서드를 통해서 unassigned 샤드 발생 원인을 빠르게 파악함 원인 제거 후 retry_failed를 이용해서 unassigned 샤드를 복구 시키는 형태로 사용 가능함 출처 : 기초부터 다지는 엘라스틱 서치

Naver Blog

[ElasticStack] 클러스터와 노드 구성

클러스터와 노드 클러스터는 여러 대의 컴퓨터를 병렬로 연결해 하나의 시스템을 구성하는 것을 의미 엘라스틱서치에서 클러스터를 구성하는 요소를 Node(노드) 클러스터는 여러 노드의 집합 노드는 엘라스틱서치가 설치되는 물리적 혹은 논리적 단위 보통 하나의 컴퓨터(혹은 서버)에 하나의 노드를 구성 물리 서버뿐만 아니라 논리 혹은 가상 서버로도 노드를 구분 HTTP 계층과 전송 계층 전송 모듈 클러스터 내부에서 사용한다. 내부에서 노드와 노드 간의 통신에서 사용 노드 간 데이터를 분산 처리하는 구조에 적합한 방식 기본 포트 9300 ~ 9399 HTTP 모듈 REST API를 통해서 노드와 외부 클라이언트 통신을 위해 사용 엘라스틱 API에서 제공하는 형태로 외부 클라이언트 앱과 통신 시 사용함 기본 포트 9200 ~ 9299 노드 엘라스틱 서치 클러스터를 구성하는 하나의 인스턴스 보통 하나의 논리적 서버로 구성되어 데이터를 저장하고 클러스터의 인덱싱과 검색 기능에 참여 단일 서버에 복수

Naver Blog

[ElasticStack] Cluster BackUp, repository, snapshot

클러스터 백업 엘라스틱서치는 데이터 백업을 위해 스냅샷을 지원함 스냅샷을 위한 저장소는 repository라고 한다. 스냅샷을 찍기 전에 먼저 repository부터 지정해야 한다. 스냅샷은 클러스터 전체를 찍거나 특정 인덱스만 찍을 수 있다. repository 등록 PUT _snapshot/fsrepo { "type" : "fs", "settings" : { "location" : "C:\\elasticsearch-7.10.1" } } 레포지토리 등록은 type에 따라 사용법이 달라진다. 파일시스템(fs), 빅데이터 파일시스템 HDFS(hdfs), 리모트 리포지토리인 아마존 (s3)와 마이크로소프트 애저(azure)와 구글 클라우드 스토리지 (gcs) 지원 fs 타입의 경우 location이라는 파라미터를 이용해 레포지토리로 사용할 노드 내부의 특정 폴더를 지정 레포지토리는 클러스터를 구성하는 마스터 노드와 데이터 노드가 동일한 위치를 공유해야 하기에 노드 설정 파일에서 레포지토

Naver Blog

[ElasticStack] 샤드, 프라이머리 샤드, 레플리카 샤드

샤드 분산 처리 시스템인 엘라스틱 서치는 여러 대의 노드를 효율적으로 활용하기 위해 데이터를 샤드라는 단위로 나눠서 분산 저장함 데이터를 분산 저장하면 클러스터의 수평적인 확장이 가능하고 작업을 분산 처리해 성능과 처리량을 높일 수 있다. 인덱스는 가상의 논리적 단위 document 인덱싱과 검색은 샤드에서 일어난다. 인덱싱을 할 때 코디네이터 노드, 최초 요청을 수신한 노드는 문서에 별도의 ID가 주어지지 않는다면 랜덤 ID 생성 _routing 파라미터가 명시되지 않았다면 ID를 이용해 document가 인덱싱될 샤드를 결정한다. shard = hash(_routing) % 프라이머리 샤드 개수 샤드 선택 공식 document가 어떤 샤드에 저장되는 지 결정하는 것을 라우팅 프라이머리 샤드와 레플리카 샤드 데이터 원본을 프라이머리 샤드에 저장하고 유실 방지하고 가용성 확보하기 위한 데이터 복제본, 레플리카 샤드를 만들어서 사용함 인덱스 설정에서 프라이머리 샤드 개수와 레플리카 샤

Naver Blog

[ElasticStack] 샤드 개수와 크기 구성 가이드, rollover, shrink API

샤드 개수 가이드 샤드가 늘어나면 더 많은 스레드를 활용해 인덱스를 병렬 검색할 수 있고 샤드가 적으면 요청을 분배하고 취합하기 오버 샤딩 6.x 까지는 프라이머리 샤드 5, 레플리카 샤드 1 7.x 버전부터 프라이머리 샤드 1, 레플리카 사드 1 기본 값 변경 많은 수의 프라이머리 샤드는 인덱스를 너무 작게 나누어서 오버샤딩 문제 발생할 가능성이 있다. 오버 샤딩은 샤드를 잘게 나누어 필요 이상의 많은 리소스를 사용하게 하는 문제 오버 샤딩 문제 각 샤드는 개별적으로 리소스를 소비한다. CPU, 메모리, 루씬 인스턴스 리소스도 필요 즉 샤드가 많을수록 시스템 성능에는 좋지 않다. 인덱스를 검색하기 위해서는 인덱스에 저장된 모든 샤드에 접근해야 함 많은 리소스가 필요하고 낭비됨 샤드 크기 가이드 검색이 많은 시스템인지 저장이 중요한 시스템인지에 따라 샤드 크기는 변경된다. 노드의 리소스 상태도 영향을 미침 엘라스틱에서는 통계적으로 샤드 하나의 크기가 10GB ~ 40GB 정도로 관리

Naver Blog

[ElasticStack] Setting

Setting 운영이나 개발 과정에서 발생하는 시스템의 설정값들을 변수화해서 상황에 맞게 사용 가능 설정은 크게 클러스터, 노드, 인덱스 3가지 레벨로 나눌 수 있다. 클러스터 > 노드 > 인덱스 레벨로 시스템 영향도가 높다. 설정 설명 클러스터 로그 레벨이나 클러스터 전반에 관한 설정 REST API를 이용해 동적으로 설정값 변경 가능 노드 네트워크 인터페이스, 보안 설정 등 같은 노드 구성에 관한 설정을 한다. elasticsearch.yml 파일에서 정적으로 수정한다. 인덱스 인덱스와 관련된 설정 프라이머 샤드, 레플리카 샤드 개수 설정 하며 정적/동적으로 설정 가능 REST API를 이용해서 동적으로 인덱스별로 설정값 변경 가능 클러스터 설정 클러스터는 하나 혹은 여러 개의 노드들의 집합 GET _cluster/settings?include_defaults=true include_defaults를 true로 지정하면 API를 이용해 직접 설정하지 않았거나 elasticsear

Naver Blog

[ElasticStack] 운영 환경 설정

노드 elasticsearch 설정 config 폴더에 elasticsearch.yml 파일이 있다. # ======================== Elasticsearch Configuration ========================= # # NOTE: Elasticsearch comes with reasonable defaults for most settings. # Before you set out to tweak and tune the configuration, make sure you # understand what are you trying to accomplish and the consequences. # # The primary way of configuring a node is via this file. This template lists # the most important settings you may want to configure for a produ

Naver Blog

[ElasticStack] logstash

logstash 플러그인 기반의 오픈 소스 데이터 처리 파이프라인 도구 로그스태시를 통해서 데이터를 가공/변환해서 엘라스틱 서치에 저장 특징 플러그인 기반 모든 형태 데이터 처리 JSON, XML event data RDBMS 데이터를 마이그레이션 elasticsearch document 리인덱싱 성능 자체 내장되어 있는 메모리와 파일 기반의 큐를 사용하기에 처리 속도와 안정성이 높다. 인덱싱할 document의 수와 용량을 종합적으로 고려해 벌크 인덱싱을 수행하고 파이프라인 배치 크기 조정을 통해 병목현상을 방지하고 성능 최적화 안정성 재시도 로직이나 오류가 발생한 document를 따로 보관하는 dead-letter-queue 내장하고 있음 파이프라인 데이터를 입력받아 실시간으로 변경하고 이를 다른 시스템에 전달하는 역할을 하는 로그스태시의 핵심 기능 입력, 필터, 출력 세 가지 구성요소로 이뤄짐 입력과 출력은 필수 필터는 옵션 { "@version" => "1", "host"

Naver Blog

[ElasticStack] Node Query Cache, Shard Request Cache, Field data cache

Cache ES는 동일한 요청에 대해 더 빠른 응답을 주기 위해 해당 쿼리의 결과를 메모리에 저장해서 캐싱 해서 사용 캐시 영역 설명 Node Query Cache 쿼리에 의해 각 노드에 캐싱 되는 영역 Shard Request Cache 쿼리에 의해 각 샤드에 캐싱 되는 영역 Field data cache 쿼리에 의해 필드를 대상으로 각 노드에 캐싱 하는 영역 Node Query Cache 검색 엔진에서 활용하기 적합한 캐시 영역 Filter Context에 의해 검색된 문서의 결과가 캐싱 되는 영역 filter Context로 구성된 쿼리로 검색하면 내부적으로 각 문서에 0과 1로 설정할 수 있는 bitset 설정 filter Context로 호출한 적이 있는 문서는 bitset을 1로 설정해서 사용자가 호출한 적이 있다는 것을 Document에 표시 bitset이 1인 문서들 중에 자주 호출되었다고 판단한 문서들을 노드의 메모리에 캐싱 다만 세그먼트 하나에 저장된 문서의 수가

Naver Blog

[Oracle] 옵티마이저, 실행계획, Cost, Hint

SQL 최적화 옵티마이저가 SQL 최적화를 맡음 옵티마이저는 미리 수집한 시스템 및 오브젝트 통계정보를 바탕으로 다양한 실행 경로를 생성해서 비교한 후 가장 효율적인 하나를 선택함 DB 성능을 결정하는 가장 핵심적인 엔진 SQL 옵티마이저 사용자가 원하는 작업을 가장 효율적으로 수행할 수 있는 최적의 데이터 액세스 경로를 선택해 주는 DBMS 핵심 엔진 최적화 단계 사용자로부터 전달받은 쿼리를 수행하는 데 후보군이 될만한 실행계획들을 찾음 데이터 딕셔너리에 미리 수집해 둔 오브젝트 통계 및 시스템 통계정보를 이용해 각 실행계획의 예상 비용 산정 최저 비용인 실행계획 선택 실행계획 SQL 옵티마이저가 생성한 처리 절차를 사용자가 확인할 수 있게 트리 구조로 표현한 것 Cost 쿼리를 수행하는 동안 발생할 것으로 예상되는 I/O 횟수 또는 예상 소요시간을 표현한 값 중요한 게 예상치다. 실행 경로를 선택하기 위해 옵티마이저가 여러 통계정보를 활용해서 계산해 낸 값 실측치가 아니기에 실제

Naver Blog

[ElasticStack] 매핑, text, keyword, 인덱스 템플릿, 다이내믹 템플릿

매핑 JSON 형태의 데이터를 루씬이 이해할 수 있도록 바꿔주는 작업 자동으로 하게 되면 다이내믹 매핑, 사용자가 직접 설정하면 명시적 매핑 다이내믹 매핑 ES의 모든 인덱스는 매핑 정보를 갖고 있지만 인덱스 생성 시 매핑 정의를 강제하지 않는다. 원본 소스 데이터 타입 다이내믹 매핑으로 변환된 데이터 타입 null 필드를 추가하지 않음 boolean boolean float float integer long object object string string 데이터 형태에 따라 date, text/keyword 필드 다만 다이내믹 매핑을 사용하게 되면 불필요한 매핑이 될 수 있다. 명시적 매핑 인덱스 매핑을 직접 정의하는 것 PUT index3 { "mappings": { "properties": { "age" : {"type": "short"}, "name" : {"type": "text"}, "gender" : {"type" : "keyword"} } } } mapping과 pr

Naver Blog

[ElasticStack] 분석기, 캐릭터 필터, 토크나이저, 토큰 필터, 커스텀 분석기

분석기 엘라스틱서치는 전문 검색을 지원하기 위해 역인덱싱 기술을 사용한다. 전문 검색은 장문의 문자열에서 부분 검색을 수행하는 것 역인덱싱은 장문의 문자열을 분석해서 작은 단위로 쪼개어 인덱싱하는 기술 역인덱싱을 이용해서 전문 검색에서 좋은 결과를 얻기 위해서는 문자열을 나누는 기준이 중요 ES에서는 캐릭터 필터, 토크나이저, 토큰 필터로 구성되어 있는 분석기 모듈을 가지고 있다. 분석기에는 반드시 하나의 토크나이저 포함 캐릭터 필터와 토큰 필터는 옵션이고 여러 개를 함께 사용해도 된다. 토큰과 용어 분석기는 필터를 통해 원문에서 불필요한 문자들을 제거한다. 이 과정까지는 문자열 자체가 분리되지 않는다. 토크나이저를 이용해서 필터링 된 문자열을 자르게 되는데 잘린 단위를 토큰 분석기에서는 하나의 토크나이저를 가진다. 토큰들이 복수의 토큰 필터를 거치며 정제되는데, 정제 후 최종으로 역인덱스에 저장되는 상태의 토큰들을 용어 토큰은 분석기 내부에서 일시적으로 존재하는 상태 인덱싱되어 있

Naver Blog

[ElasticStack] 쿼리 컨텍스트, 필터 컨텍스트

쿼리 컨텍스트, 필터 컨텍스트 쿼리 컨텍스트는 질의에 대한 유사도를 계산해 이를 기준으로 더 정확한 결과를 먼저 보여준다. 필터 컨텍스트는 유사도를 계산하지 않고 일치 여부에 따른 결과만을 반환 도큐먼트에 '피자'가 포함되어 있는지를 찾을 때는 쿼리 컨텍스트 사용 연관성을 계산해서 최대한 비슷한 도큐먼트들을 찾아준다. 연관성에 따른 스코어 결과를 제공 도큐먼트 제목이 '피자'인 문서를 찾기 위해서는 필터 컨텍스트 사용 예/아니오만 확인 -> 결과로 제공 스코어를 제공하지 않기에 스코어 계산 과정을 생략할 수 있어서 전체적인 쿼리 속도를 올릴 수 있다. 스코어 계산을 하지 않으면 결과에 대한 업데이트를 매번 수행할 필요가 없기에 캐시 사용 가능 엘라스틱 서치는 기본적으로 힙 메모리의 10% 캐시 사용 캐시를 이용한 빠른 검색을 사용하려면 필터 컨텍스트를 사용 쿼리 컨텍스트 사용 GET kibana_sample_data_ecommerce/_search { "query": { "match

Naver Blog

[ElasticStack] 유사도 스코어 BM25, TF IDF, score

유사도 스코어 쿼리 컨텍스트는 엘라스틱에서 지원하는 다양한 스코어 알고리즘 사용 가능 기본적으로는 BM25 알고리즘 이용해서 유사도 스코어 계산 유사도 스코어는 질의문과 Documen의 유사도를 표현하는 값 스코어가 높을수록 찾고자 하는 document의 가깝다는 사실을 의미 explain 옵션을 사용하면 스코어가 어떤 식으로 계산되는지를 알 수 있다. 쿼리 내부적인 최적화 방법과 어떤 경로를 통해 검색되었으며 어떤 기준으로 스코어가 계산되는지 score 계산식은 description 항목에 제공 스코어 알고리즘 BM25 쿼리 컨텍스트로 요청한 응답 값을 보면 hits된 Document는 _score 값을 가지고 있다. score는 document와 query 간의 연관성 수치로 값이 클수록 연관성이 높다 BM25 알고리즘은 검색, 추천에서 많이 사용되는 알고리즘 TF-IDF 개념에 문서 길이를 고려한 알고리즘 검색어가 문서에서 얼마나 자주 나타나는지, 검색어가 문서 내에서 중요한

Naver Blog

[ElasticStack] 리프 쿼리, full text query, term level query

리프 쿼리와 복합 쿼리 리프(leaf) 쿼리는 특정 필드에서 용어를 찾는 쿼리 match, term, range 쿼리 등이 있다. 복합(compound) 쿼리는 쿼리를 조합해서 사용되는 쿼리 bool 쿼리 전문 쿼리(full text)와 용어 수준(term level) 쿼리 전문 쿼리 전문 검색을 하기 위해 사용되고 전문 검색을 할 필드는 인덱스 매핑 시 text 타입으로 매핑 용어 수준 쿼리 정확히 일치하는 용어를 찾기 위해 사용 인덱스 매핑 시 필드를 keyword 타입으로 매핑 전문 쿼리 전문 쿼리에서는 match 쿼리를 사용한다. match 쿼리를 사용하게 되면 검색어인 hello world도 [hello, world]로 토큰으로 분리가 된다. 그럼 이 분리된 토큰들로 score를 계산하고 검색한다. 전문 쿼리는 text가 많은 필드에서 특정 용어를 검색할 때 사용함 구글, 네이버 검색 방식 용어 수준 쿼리 키워드 타입을 검색한다고 하자. 키워드 타입은 인덱싱 과정에서 분석기

Naver Blog

[ElasticStack] bool 쿼리, must, must_not, should, filter

논리 쿼리 논리 쿼리 복합 쿼리 (Commpound Query)로 전문 검색 쿼리, 용어 쿼리를 조합할 수 있다. 논리 쿼리를 조합할 수 있도록 4개의 타입 지원 타입 설명 must 쿼리를 실행하여 참인 document를 찾는다. 복수의 쿼리를 실행하면 AND 연산을 한다. must_not 쿼리를 실행하여 거짓인 document 찾는다. 다른 타입과 같이 사용할 경우 document 제외 should 단독으로 사용 시 쿼리를 실행하여 참인 document를 찾는다. 복수의 쿼리를 실행하면 OR 연산 실행 다른 타입과 같이 사용할 경우 score에만 활용 filter 쿼리를 실행해서 '예/아니요' 형식의 필터 컨텍스트를 수행 must 타입 bool 쿼리 밑에 must 쿼리를 사용한다. customer_first_name에 'mary'가 들어간 document를 검색한다. match는 전문 검색을 위한 쿼리이다. must 타입에 복수 개의 쿼리를 실행하면 AND 효과를 얻는다. 위 쿼리

Naver Blog

[ElasticStack] 집계, metric 집계, bucket 집계, 파이프라인, 부모 집계, 형제 집계

agg 집계 쿼리는 aggs 파라미터를 이용하면 쿼리 결과에 대한 집계를 생성할 수 있다. my_agg는 사용자가 지정하는 집계 이름이다. agg_type은 집계 타입을 의미한다. metric 집계와 bucket 집계 두 가지 타입의 집계가 있다. metric 집계는 통계나 계산에서 사용되고 bucket 집계는 document를 그룹핑하는데 사용된다. metric 집계 메트릭 집계는 필드의 최소/최대/합계/평균/중간값 같은 통계 결과를 보여줌 필드 타입에 따라서 사용 가능한 집계 타입에 제한이 있다. text 타입 필드는 sum / avg 같은 수치 연산은 계산할 수 없다. avg, min, max, sum, percentils, stats, cardinality, geo-centroid 제공함 stats는 필드의 min, max, sum, avg, count(document 개수)를 한 번에 볼 수 있다. cardinality 필드의 유니크한 값 개수 평균/중간값 평균값을 구할 수

Naver Blog

[Spring Batch] Rest API 이용 Job 실행

REST 방식으로 잡 실행하기 REST API 방식으로 배치 잡을 실행할 수 있긴 하나 즉시 실행할 수 있지는 않다. JobLauncher를 이용하면 REST API 방식으로 실행 가능하다. 스프링 배치는 SimpleJobLauncher 제공 잡의 실행이 기존 잡 인스턴스의 일부인지 새로운 잡의 일부인지를 판별해서 알맞게 동작 SimpleJobLauncher는 전달받은 JobParameters 조작을 지원하지 않는다. 따라서 잡에 JobParametersIncrementer를 사용해야 한다면, 해당 파라미터가 SimpleJobLauncher로 전달되기 전에 적용해야 함 JobLauncher가 사용하는 TaskExecutor를 구성해서 동기, 비동기 실행 방식을 선택할 수 있다. 기본적으로는 TaskExecutor를 사용해 Job을 동기식으로 실행 호출자와 동일한 스레드에서 잡이 수행 기존 스레드가 아닌 다른 스레드에서 잡을 실행하려면 REST 호출로 잡을 시작한 후 잡의 완료를 기다

Naver Blog

[Spring Batch] job 재시작 제어

잡 재시작 방지 JobBuilder의 preventRestart()를 호출해서 잡을 다시 시작할 수 없게 만들 수 있다. preventRestart() 메서드를 호출하면 잡이 실패하거나 어떤 이유로 중지돼도 다시 실행할 수 없다. 재시작 횟수 제한 재시작 횟수 제한은 job 대신 Step 수준에서 제공함 startLimit이 2로 구성되어 있기에 이 Job은 2번까지만 실행 가능 첫 실행은 한 번의 시도이기에 이후에 한 번의 시도만 더 허용한다. allowStartIfComplete allowStartIfComplete true로 설정하면 STEP이 COMPLETE로 종료되어도 재실행 할 수 있다. 동일한 파라미터로 잡을 한 번만 실행시킬 수 있지만 스텝에는 이 규칙이 반드시 적용되지 않는다. 완료된 스텝을 두 번 이상 실행 가능 단 Job의 ExitStatus가 COMPLETE라면 모든 Step에 allowStartIfComplete(true)를 적용해 구성해도 이와 관계없이 Jo

Naver Blog

[Spring Batch] ItemReader Jdbc cursor, paging

ItemReader 인터페이스 ItemReader<T> 인터페이스는 Step 입력을 제공할 때 사용하는 read 단일 메서드 정의 org.springframework.batch.item 사용하자. Spring Batch가 read 메서드를 호출하면, 해당 메서드는 Step 내에서 처리할 Item 한 개를 반환 Step에서 Item 개수를 세어서 Chunk 내의 데이터가 몇 개나 처리되었는지 관리 데이터베이스 Reader 데이터베이스는 내장된 트랜잭션 기능 제공 JDBC, Hibernate, JPA 등을 통해서 DB에서 데이터를 읽어올 수 있다. JDBC 대용량 데이터를 한 번에 메모리에 적재하는 건 좋지 않다. JdbcTemplate를 사용하면 전체 ResultSet에서 한 row 씩 순서대로 가져오면서, 모든 row를 필요한 도메인 객체로 변환해서 메모리에 적재하기에 좋지 않다. Spring Batch에서는 한 번에 처리할 만큼의 레코드만 로딩하는 Cursor와 Paging을 제공

Naver Blog

[Spring Batch] ItemReader 에러 처리

Skip Spring batch에서는 특정 예외가 발생했을 때 레코드를 건너뛰는 skip 기능 제공함 두 가지를 고려 어떤 조건에서 레코드를 건너뛸 것인가 얼마나 많은 레코드를 건너뛰게 할 것인가. 어떤 예외를 무시할 것인가 faultTolerant는 fault를 허용한다는 의미이다. 레코드를 건너뛰려면 스프링 배치가 어떤 예외를 건너뛰게 할지, 몇 번까지 예외를 허용할지를 설정 위 예에서는 RuntimeException을 13회까지 건너뛰게 설정함 즉 무시하고 넘어간다는 의미 itemReader에서 한 건씩 읽다가 예외가 발생하면 해당 item을 skip 하고 다음 item itemProcessor에서는 예외가 발생하면 해당 chunk의 첫 단계로 돌아가서 ItemReader로부터 다시 데이터를 받는다. noSkip을 사용하면 건너뛰지 말아야 할 대상을 지정할 수 있다. writer에서 RuntimeException을 일으켜본다. 14번째 하고 끝나버린다. 근데 위에 보면 이미 처

Naver Blog

[Spring Batch] Multi Thread Step

다중 스레드 스텝 스텝은 기본적으로 단일 스레드로 처리 다중 스레드 스텝은 잡의 실행을 병렬화하는 가장 쉬운 방법 TaskExecutor 다중 스레드 스텝 개념을 적용하면 배치 Job이 TaskExecutor 추상화를 사용해서 각 청크가 자체 스레드에서 실행되게 할 수 있다. 다중 스레드 스텝을 사용하면 Job 내의 모든 Step은 각 청크를 독립적으로 처리하면서 하나의 스레드 풀 내에서 처리되게 구성 가능 청크가 처리될 때 스프링 배치는 해당 청크에서 어떤 일이 수행됐는 지 추척 스레드 중 하나에서 오류가 발생하면, 잡 처리는 일반적인 스프링 배치 기능에 따라 롤백되거나 종료 step에서 TaskExecutor 구현체를 정의하고 참조해서 사용하면 된다. 다중 스레드 기능 사용 가능 이 잡을 실행하면 스프링은 Step내에서 실행되는 각 청크용으로 새 스레드를 생성해 각 청크를 병렬로 실행 다중 쓰레드 설정을 하고 Thread 이름 로그를 추가하면 Thread 이름이 여러 개가 오고

Naver Blog

[Spring Batch] 병렬 스텝

병렬 스텝 스텝을 병렬로 실행한다. 위의 그림에서 Job은 일단 단일 Step으로 실행 병렬로 두 개의 flow 처리 위쪽은 Step 2를 실행하고 완료되면 Step 3 실행 아래쪽은 Step 4실행 flow 1, 2가 모두 완료되면 Step 5 실행 병렬 Step 구성 Spring의 TaskExecutor를 사용한다. 각 플로우가 자체 스레드에서 실행되기에 여러 Flow를 병렬로 실행 가능 FlowBuilder FlowBuilder의 split를 메서드를 사용해서 병렬로 실행 split 메서드는 TaskExecutor를 argument로 받아서 SplitBuilder를 반환한다. SplitBuilder를 사용하면 원하는 만큼 많은 flow 객체 추가 가능 각 flow는 TaskExecutor의 규약에 따라서 자체 스레드에서 실행 이런 실행 메커니즘은 각 스텝이나 Step의 flow를 병렬로 실행하는 기능 제공 springframework의 flow 사용 split 유의 사항 spl

1 2 3 4 5 6 7 8 9 10