본문 바로가기

전체 글183

Transactional-Readonly가 어플리케이션에 미치는 영향 개요Hot Key를 관찰하려고 부하테스트를 하던 도중, 세션 자동 생성에 이어 이상한 점을 하나 더 발견했다.우선, 짧게 실험 결과를 요약하고 가면..1. 실험 목적:Hot Key가 Redis 또는 시스템에 어떤 영향을 미치는지 확인2. 실험 조건:key: "popular:market:items"TTL: 무제한 - '인기 검색' 은 redis만 사용하도록 할 것이기 때문. 물론 fallback도 존재함.JMeter: 3000유저 / 1초 ramp / 1000 loopRedis: 단일 인스턴스, 로컬실험 도중 1-2번의 Key Update 진행하여, 시스템에 미치는 영향 확인사용한 툴: Prometheus, Grafana, Jmeter3. 결과 요약:Redis key rate(GET)초당 5000~6000.. 2025. 4. 4.
JWT Token BlackList TTL을 효율적으로 설정하기 개요기존에는 BlackList의 TTL을 대강 AccessToken의 Exp기간과 똑같이 잡았었는데,유저가 만료시간 직전에 로그아웃을 하는 경우 Redis에 불필요하게 오랫동안 BlackList를 저장하게 된다. 30분짜리 AccessToken의 만료시간이 1분 남았을 때,블랙리스트로 30분간 저장해놓게 된다면 29분간 쓸모없는 데이터가 생기는 것이다. 솔직히 블랙리스트 하나 당 216B밖에 되지 않아서 무시해도 될 수준 아닌가? 싶지만, 100만건이 한번에 몰리면 216MB가 된다.거기다 우리는 RefreshToken도 Redis로 관리하므로 함께 생각하는 편이 좋다.  해결public void addToBlackList(String token) { String blackPrefix = jwtSe.. 2025. 4. 3.
Actuator가 자동으로 Session을 생성한다 개요Redis Hot Key Issue를 겪어보려고 Jmeter로 부하테스트를 돌리는 도중에 이슈가 발생했다.정확히는 Hot Key 부하테스트와 관련은 없지만, Redis Insight를 뒤적거리다 발견했다.어플리케이션을 켜놓고 가만히 놔뒀는데, 세션이 스스로 생성되었다..하나에 632B라 무시할만한 수준이라고 생각할 수도 있지만, 15초에 한번 Metrics를 수집하며 이것의 TTL이 30분이니까 서버가 지속될 때 최대 약 74KB * 인스턴스 개수 만큼 Redis에 쌓인다.그리고 어쨋든 버그기도 하고.. 거슬린다. 대체 왜 생긴걸까?  파악너무 예전에 만들었던 Session + Security 구조라 기억이 잘 안 나니까 우선 세션이 어디에서 생성되는지 로그를 찍어보자.@Slf4j@Componentp.. 2025. 4. 2.
조회 성능 향상시키기(6) Redis Hot Key Issue Redis Hot Key?Hot Key는 말 그대로 "너무 자주 접근되는 단일 키" 이다. 이때 요청이 모두 읽기일 경우와, 일부라도 쓰기가 섞인 경우는 시스템 관점에서 부하의 성격이 완전히 달라진다.  MESI 프로토콜MESI 프로토콜은 멀티코어 CPU 환경에서 캐시 일관성(Coherency)을 유지하기 위한 프로토콜이다.MESI는 각각의 캐시 라인이 4가지 상태 중 하나를 가지도록 해서, 각 CPU 코어가 동일한 메모리에 주소에 접근하더라도 데이터 불일치를 막아주는 방식이다. MESI의 4가지 상태는 다음과 같다:Modified (수정)이 캐시 라인만 최신 데이터를 가지고 있고, 메모리에는 반영 안 됨다른 캐시에 없음 + dirty 상태Exclusive (배타적)이 캐시 라인만 데이터 가지고 있고, .. 2025. 4. 1.
9. 운영체제 9장 메모리 (4) 페이징 페이징페이징은 연속된 물리 메모리 공간 없이도 프로세스가 실행될 수 있도록 하는 메모리 관리 기법이다.물리 메모리는 프레임이라는 고정 크기 블록으로, 논리 메모리는 페이지라는 동일 크기 블록으로 나눠지고, 이 둘은 일대일로 매핑된다.프로그램이 메모리에 올라갈 때 페이지 테이블을 통해 논리 주소의 페이지 번호를 물리 주소의 프레임 번호로 바꾸고, 거기에 페이지 오프셋을 더해 실제 주소를 계산한다. 이 작업을 MMU가 자동으로 해주고, 덕분에 물리 주소가 연속되지 않아도 된다. 그래서 외부 단편화가 원천적으로 사라진다.하지만 페이지 단위로만 할당하기 때문에 프로세스 크기와 페이지 크기가 안 맞으면 마지막 페이지에서 내부 단편화가 생긴다.예를 들어 2048B짜리 페이지를 썼는데, 마지막 페이지에 1000B만 .. 2025. 3. 31.
8. 운영체제 9장 메모리 (3) 연속 메모리 할당 가변 파티션 방식연속 메모리 할당은 메모리를 나눠주는 방식 중 하나이다.그 중에서도 가변 파티션 방식은 프로세스 크기에 맞춰 딱 맞게 메모리 덩어리를 나눠주는 것이다.처음엔 메모리 전체가 하나의 큰 hole로 비어있고, 요청이 들어온다면 그 크기만큼 잘라서 주고, 남은 공간은 hole로 남긴다.시간이 지나면서 프로세스가 종료되면 메모리 여기저기 작은 hole들이 생기고, 이 hole들을 다시 잘 활용하지 못하면 조각처럼 되어서 공간 낭비가 생긴다.이 모든 문제를 통틀어서 동적 메모리 할당 문제라고 하고, 그래서 운영체제는 어떤 hole에 새로운 프로세스를 배치해야 할지 결정해야 한다.이걸 메모리 할당 전략이라고 한다. 대표적인 전략은 3가지가 있다.1. 최초 적합: 앞에서부터 순차적으로 찾다가 딱 들어맞.. 2025. 3. 30.
7. 운영체제 9장 메모리 (2) with java spring.. 논리 주소 vs 물리 주소CPU가 주소를 생성하면 그건 진짜 주소가 아니라 "논리 주소" 이다. 반면 진짜 RAM에서의 위치를 "물리 주소" 라고 한다.프로그램이 메모리를 사용하려 할 때, CPU는 논리 주소를 생성하고 이걸 실제 물리 주소로 바꾸는 작업이 필요한데, 이 작업을 해주는 게 MMU(Memory Management Unit)이라는 하드웨어다. 논리 주소와 물리 주소는 보통 다르고 매핑을 통해 연결된다. 예를 들어, CPU가 346번지를 접근하려 할 때 운영체제가 "너는 14000부터 써" 라고 했다면 실제 접근 위치는 14346이 된다. 이 때 14000이라는 값을 재배치 레지스터라고 한다.  동적 적재지금까지의 설명은 모두 프로세스가 실행되려면 메모리에 전부 올라와 있어야 한다는 전제였다... 2025. 3. 29.
6. 운영체제 9장 메모리 (1) with redis.. 기본 하드웨어CPU는 계산을 담당하는 놈이고, 계산하려면 데이터가 필요하다.근데 이 데이터는 항상 CPU 내부에 있지는 않고, 보통 RAM에 있다. CPU는 자기 내부에 Register라는 아주 빠른 공간에서만 직접 데이터를 꺼내 쓸 수 있다.그래서 뭔가를 계산하려면 일단 메모리에 있는 데이터를 레지스터로 가져와야 하는데, 메모리에서 레지스터까지 데이터를 가져오는 건 시간이 꽤 걸린다. CPU는 명령 하나를 1-3 Clock cycle에 처리 가능하다. 나노초 단위로 움직이며 굉장히 빠르다.하지만 CPU가 연산하려면 데이터가 필요하고, 그 데이터는 RAM에 있고, CPU가 메모리에게 값을 요청한 사이 지연시간이 생긴다.이 시간 동안 CPU는 계산을 못 하고 가만히 기다려야 하고, 이걸 Stall(지연) .. 2025. 3. 28.
AOP(트랜잭션)와 try-catch 개요2025.03.08 - [팀 프로젝트/최종 프로젝트] - 트러블슈팅: 크롤링 시 트랜잭션 범위가 너무 길다 트러블슈팅: 크롤링 시 트랜잭션 범위가 너무 길다현재 문제점크롤링을 진행하는 메서드 전체에 트랜잭션이 잡혀있다.크롤링 해올 페이지가 적은 상황에선 문제가 안 되지만, 여러 페이지를 한 트랜잭션에 포함시킬 때 문제가 될 수 있다.중간roqkfchqh.tistory.com이 문제를 해결할 때, 한 클래스 내에서 트랜잭션 같이 사용하면 정상적으로 작동하지 않는다는 걸 어렴풋이 알고는 있었지만,왠진 모르겠지만 그냥 되길래(??) 잠시 묻어두고 넘어갔었다. 그러다 지금 AOP를 공부하다가 깨달았다.Spring의 트랜잭션은 AOP 기반이다. 즉, 프록시를 통해 메서드가 호출될 때만 트랜잭션이 적용된다. 그.. 2025. 3. 27.
조회 성능 향상시키기(5) Semaphore로 비동기 쿼리 동시 실행 제어하기 개요현재 로직에는 잠재적인 문제가 존재한다비동기 방식으로 캐시 갱신 쿼리를 실행할 경우, 최소 1초에 한 번 실행될 수 있다.하지만 일시적인 DB 부하로 인해 쿼리 실행 시간이 1초 이상 걸리는 상황이 발생한다면, 다음과 같은 문제가 생길 수 있다 1. 동일 작업이 중복 실행되어 jvm의 스레드 자원이 낭비된다.2. 캐시 갱신 타이밍이 겹치면서 race condition이 발생할 수 있다.3. 불필요한 느린 쿼리들이 중복 호출될 수 있다(현재 기준 동일 네트워크 상에서 평균 600ms로 빠른 편이 아니다) -> 결과적으로 어플리케이션, DB, Redis 모두에 영향을 끼칠 수 있다.  Semaphore세마포어를 활용해 비동기 메서드 실행을 하나의 스레드로 제한하는 방식으로 문제를 해결할 수 있다고 판단했.. 2025. 3. 26.