본문 바로가기

전체 글

(41)
MySQL Optimizer가 Index의 Row를 예측하는 방법 ─ Index Dive 1. 배경 MySQL의 Optimizer는 쿼리를 실행하기 전에 여러 실행 계획을 비교하고,그중 비용이 가장 낮을 것으로 예상되는 계획을 선택한다 예를 들어 다음과 같이 Index가 존재하는 컬럼을 조건으로 조회한다고 가정해보자SELECT *FROM ordersWHERE status = 'READY'; status 컬럼에 Index가 존재하더라도 MySQL이 반드시 해당 Index를 사용하는 것은 아니다 조건에 해당하는 Row가 테이블의 대부분을 차지한다면 Index를 통해 Record를 조회하는 것보다Full Table Scan을 수행하는 것이 더 효율적일 수도 있기 때문이다 따라서 Optimizer가 Index 사용 여부를 판단하려면 먼저 해당 조건으로 몇 개의 Row가 조회될 것인지를 예상해야..
Java의 Lock 관리 기법 1. 배경 Java에서 여러 Thread가 하나의 공유 자원에 동시에 접근한다면데이터의 일관성 보장을 위해 synchronized 나 ReentrantLock 을 사용할 수 있다 하나의 Thread가 이미 Lock을 획득하고 있다면,다른 Thread는 Lock이 해제될 때까지 Critical Section에 진입하지 못한다 일반적으로 이런 상황을 두고 Lock을 획득하지 못한 Thread가 Blocking되어 대기한다고 표현한다그렇다면 Blocking된 Thread는 실제로 무엇을 하고 있을까? Lock을 획득할 때까지 CPU를 계속 사용하며 대기하는 Spin Wait 형태일까?아니면 실행을 중단하고 CPU 자원을 다른 Thread에 넘기는 것일까? 또 synchronized 와 Reentrant..
InnoDB의 Lock 관리 매커니즘 1. 배경 InnoDB에서는 여러 트랜잭션이 동시에 동일한 데이터를 변경할 때 일관성을 보장하기 위해 Lock을 사용한다하나의 트랜잭션이 특정 Record를 변경하고 있다면 다른 트랜잭션이 동일한 Record를 동시에 변경할 수 없도록 Lock을 획득한다-- Transaction ASTART TRANSACTION;UPDATE memberSET name = 'A'WHERE id = 1; 그렇다면 특정 Record에 어떤 트랜잭션이 Lock을 획득하고 있으며,다른 트랜잭션이 해당 Lock을 기다리고 있다는 정보는 InnoDB 내부에서 어떻게 관리될까? 단순하게 생각하면 Record 내부에 Lock 상태를 함께 저장하는 방법을 떠올릴 수도 있다 하지만 하나의 Record에는 여러 트랜잭션의 Lock 요청이 존..
TTS-Bot: Triton Ensemble 고정 화자 TTS 추론 파이프라인 구성 (1) 1. 배경 이번 프로젝트는 MLOps와 AI Model Serving에 대한 이해를 높이고,백엔드 개발자로서 AI 서비스와 연결되는 기술 스택을 직접 다뤄보기 위해 시작했다 현재 계획은 아래와 같다 Triton 기반 추론 파이프라인 구성 Triton 기반 서빙을 통한 성능 튜닝gRPC를 통한 WAS와 Triton 간 통신WebFlux 기반 스트림 비동기 처리Voice Cloning을 통한 Speaker Embedding 저장 및 활용(이 글은 프로젝트를 진행하며 작성하는 것이기에, 계획이 수정될 수 있다) 특히 ONNX나 Model의 Input/Output Tensor, 전/후처리, GPU 기반 추론 등을 실제로 구성하면서AI Serving 환경에서 각 컴포넌트가 어떤 역할을 담당하는지 이해하고자 했다 ..
CUDA Warp Divergence의 동작 원리와 Independent Thread Scheduling 1. 배경 GPU는 수많은 스레드를 병렬로 실행함으로써 높은 처리량을 얻는다 CUDA에서 스레드는 개별적으로 작성되지만 GPU에서는 Warp를 기본 단위로 명령어를 실행한다NVIDIA GPU의 Warp는 32개의 스레드로 구성되며, SIMT 방식에 따라 Warp에 속한 스레드들이 동일한 명령을 수행한다 예를 들어 다음과 같이 각 스레드가 배열의 서로 다른 원소를 계산한다고 해보자__global__ void add(int* data) { int tid = threadIndex.x; data[tid] += 1;} 각 스레드가 접근하는 데이터는 다르지만 수행하는 명령어는 동일하기 때문에,하나의 Warp에 속한 스레드들을 효율적으로 병렬 실행할 수 있다 그렇다면 다음과 같이 각 스레드마다 서로 다른 ..
Java의 Hash 자료구조 동작 원리와 안전한 Hash Key 설계 1. 배경 Java에서 HashMap 이나 HashSet 을 사용하다 보면,객체의 equals() 와 hashCode() 를 함께 재정의해야 한다는 설명을 접한다 두 객체가 같은지를 판단하는 것은 equals() 의 역할이다그렇다면 hashCode() 는 왜 필요하며, 두 메서드가 함께 재정의되지 않으면 어떤 문제가 발생할까? 이번 글에서는 Java의 HashMap 을 중심으로 다음 내용을 살펴보고자 한다 equals() 와 hashCode() 가 함께 사용되는 이유해시값을 Bucket의 위치로 변환하는 방식Hash 충돌과 Bucket 내부 구조Load Factor와 Resize 과정Record와 VO를 안전한 Hash Key로 사용하는 방법 2. equals()와 hashCode()는 ..
LongAdder는 어떻게 CAS 경쟁을 분산할까? 1. 배경 이전 글에서는 AtomicLong 이 volatile 수준의 메모리 의미와 CAS를 결합해 원자적인 값 변경을 제공하는 과정을 살펴보았다 AtomicLong 은 별도의 Lock으로 임계 영역 전체를 보호하지 않고도 여러 스레드에서 하나의 값만 안전하게 갱신할 수 있다 하지만 여러 스레드의 갱신이 하나의 값에 집중되면 CAS 실패와 재시도가 반복될 수 있다이때 먼저 갱신에 성공한 스레드를 제외한 나머지 스레드는 현재 값을 다시 읽고 연산을 재시도해야 한다 그렇다면 하나의 값에 집중된 갱신을 여러 위치로 나누어 경쟁을 줄일 수는 없을까?Java는 이러한 상황을 위해 LongAdder 를 제공한다private final LongAdder counter = new LongAdder();publi..
AtomicLong은 어떻게 Lock-Free를 달성했을까? 1. 배경 여러 스레드에서 하나의 카운터를 증가시켜야 할 때 흔히 AtomicLong 을 사용한다private final AtomicLong counter = new AtomicLong();public void increment() { counter.incrementAndGet();} 사용법 자체는 어렵지 않다하지만 내부 동작을 생각하면 몇 가지 의문이 생긴다 synchronized 를 사용하지 않고 어떻게 원자성을 보장할까?여러 스레드가 동시에 값을 변경하려 할 때 어떤 스레드가 성공할까? volatile 은 이 과정에서 어떤 역할을 할까?CPU 레지스터와 캐시는 왜 동시성 문제에 등장할까?CAS(Compare-And-Set) 방식도 경쟁이 심해지면 오버헤드가 생기는데 그 이유는 뭘까?이번..