InnoDB 복합 인덱스와 Left-most Prefix 원칙 정리

InnoDB에서 여러 컬럼을 묶어 복합 인덱스를 만들면 정의된 순서대로 값을 결합해 B-Tree에 저장됨. 예를 들어 (colA, colB, colC) 인덱스면 colA 기준으로 정렬되고 같은 colA 그룹 내에서 colB, 이어서 colC 순으로 정렬됨. 이 구조 때문에 왼쪽 접두사(Left-most prefix) 원칙이 성립하며, 인덱스 정의의 선두 컬럼부터 조건이 주어질수록 효율이 높아짐 개념과 배경 복합 인덱스는 정의 순서대로 키를 묶어 B-Tree에 저장 InnoDB의 보조 인덱스는 리프에 보조키와 함께 PK를 포함함. PK 길이가 길면 보조 인덱스 크기와 I/O에 직접 영향 left-most prefix 원칙이 핵심. 인덱스의 선두 컬럼부터 연속해서 매칭될 때 탐색 범위가 급격히 줄어듦 동작 방식 예시 (colA, colB) 인덱스 구조 정렬 키 순서: colA → colB → 내부적으로 PK 참조 왼쪽 컬럼 우선 필터링 WHERE colA = ? 또는 WHERE colA = ? AND colB = ? 형태는 인덱스를 온전히 사용 가능 WHERE colB = ?처럼 선두 컬럼 없이 후행 컬럼만 조건일 때는 인덱스 이용이 제한되거나 범위가 매우 넓어짐. 최신 버전에서 옵티마이저가 제한적으로 우회 전략을 쓰는 경우가 있으나 일반적인 기대값으로 두기 어려움 왼쪽 접두사 부분 사용 (colA, colB)에서 colA만 조건으로 사용해도 인덱스 사용 가능 colA와 colB를 모두 조건으로 사용하면 탐색 범위가 더 좁아짐 colB만 조건이면 left-most prefix가 깨져 효과가 급감할 수 있음 정렬과 결합 사용의 기본 인덱스 키 순서와 같은 ORDER BY colA, colB는 추가 정렬 비용 감소에 도움 WHERE 조건이 인덱스 선두를 건너뛰거나 정렬 순서가 키 정의와 다르면 이점이 줄어듦 여러 컬럼을 묶는 이유 다중 조건 검색 성능 향상. 예: WHERE colA = ? AND colB BETWEEN ? AND ?는 (colA, colB)로 빠른 범위 스캔 가능 커버링 인덱스 활용. 예: SELECT colA, colB FROM T WHERE colA = ? AND colB = ?에서 테이블 추가 접근 없이 인덱스만으로 결과 충족 가능 쿼리 패턴 최적화. 실제 WHERE 절에서 자주 함께 쓰는 컬럼을 순서까지 고려해 묶는 설계가 유효. 드물게 쓰이거나 고카디널리티 이점이 없는 컬럼을 무리하게 포함하면 쓰기 비용만 증가 주의할 점 인덱스 정의 순서가 성능을 좌우. 자주 필터링되는 컬럼, 카디널리티가 높은 컬럼을 선두에 두는 것이 일반적 권장 너무 많은 컬럼을 묶으면 인덱스 폭과 관리 비용 증가. 보통 2~3개, 많아도 4개 이하로 목적에 맞게 제한 (A, B, C)일 때 WHERE B = ?처럼 선두가 빠지면 인덱스 사용이 제한. 패턴에 따라 (B, A) 같은 보완 인덱스 검토 InnoDB 보조 인덱스는 PK를 함께 가진다는 점을 고려. PK가 길면 복합 인덱스 크기와 캐시 효율, 쓰기 비용에 불리 ORDER BY, GROUP BY와 결합 시 인덱스 정의 순서와 일치할 때 이점. 중간에 범위 조건이 끼거나 순서가 어긋나면 효과 하락 범위 조건 이후 컬럼 활용 제한 가능. 예를 들어 colA BETWEEN ...가 선두에 오면 그 뒤 colB는 정렬이나 추가 필터에서 기대만큼 활용되지 않을 수 있음 마무리 복합 인덱스는 왼쪽부터 순차적으로 정렬되는 구조이며 left-most prefix 원칙이 적용됨 다중 컬럼 조건, 범위 스캔, 정렬이나 그룹 연산에 유용하지만 인덱스 정의 순서가 쿼리 패턴과 맞지 않으면 이점을 잃음 불필요하게 많은 컬럼을 포함하면 쓰기 부담과 스토리지 사용량만 증가. 실제 트래픽 패턴과 카디널리티를 근거로 최소한의 조합과 올바른 순서를 선택하는 것이 핵심 참고자료 https://dev.mysql.com/doc/refman/8.0/en/multiple-column-indexes.html https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html https://dev.mysql.com/doc/refman/8.0/en/covering-indexes.html

November 30, 2025

JavaScript에서 빈 배열은 truthy인가 falsy인가

개념/배경 JavaScript에서 조건식은 내부적으로 ToBoolean 연산을 거쳐 참거짓을 판정함 이때 객체 타입은 모두 truthy로 간주되며 배열도 객체에 해당함 따라서 빈 배열 []과 빈 객체 {} 역시 조건문에서 true로 평가됨 반대로 falsy는 정해진 값만 해당됨 false, 0, -0, 0n, “”, null, undefined, NaN 예시 다음은 빈 배열이 조건문에서 어떻게 평가되는지 확인하는 간단한 코드 if ([]) { console.log("빈 배열은 truthy입니다!") } else { console.log("빈 배열은 falsy입니다!") } // 출력: "빈 배열은 truthy입니다!" 배열이 비어 있는지를 논리적으로 구분하려면 길이를 확인해야 함 ...

November 29, 2025

백오프 Backoff 재시도 전략 정리 고정·선형·지수·지터

개요 백오프는 실패한 작업을 즉시 재시도하지 않고 일정 시간 대기 후 다시 시도하는 전략을 말함 연속 실패 시 대기 시간을 점진적으로 늘려 서비스와 네트워크에 가해지는 부하를 낮추는 목적 RPC, 데이터베이스, 외부 API 호출, 트랜잭션 재시도 등에서 일반적으로 사용 왜 쓰는가 과부하 방지 실패 직후 동시 재시도를 막아 서버와 네트워크 폭주를 예방 일시적 장애 흡수 순간적인 지연이나 혼잡이 해소될 시간을 벌어 성공 확률을 높임 비용 절감 불필요한 재시도 횟수와 리소스 낭비 감소 대표 백오프 패턴 고정 백오프 Fixed 매번 동일 대기 시간 사용 예) 1초 → 1초 → 1초 선형 백오프 Linear 시도할 때마다 일정 간격으로 증가 예) 1초 → 2초 → 3초 → 4초 지수 백오프 Exponential 보통 2배로 증가하는 지수 증가 사용, 실무에서 기본값으로 가장 흔함 예) 1초 → 2초 → 4초 → 8초 → 16초 지수 백오프 + 지터 Jitter 지수 증가에 무작위성을 섞어 동시 재시도 동기화를 깨뜨림 예) 1초 → 2.3초 → 4.1초 → 8.6초 실전 포인트 동시성 환경에서는 지터 필수 동일한 주기 백오프만으로는 다수 클라이언트가 같은 타이밍에 몰려 서버를 다시 두들김 상한 설정 최대 대기 시간과 최대 재시도 횟수 캡을 두어 꼬리 길어짐 방지 실패 예산과 타임아웃 연계 전체 호출 타임아웃 내에서 재시도 예산을 배분, 1회 호출과 재시도들이 전체 SLA를 초과하지 않도록 관리 멱등성 보장 재시도 가능한 작업은 멱등성을 만족해야 안전, 아니면 보상 로직 필요 서버 힌트 활용 Retry-After 등 서버가 제시하는 대기 힌트가 있으면 우선 적용 지터 방식 선택 전체 구간 무작위 분포 Full jitter가 단순하고 효과적이라는 보고가 많음 간단 예시 아래는 지수 백오프의 최소 구현 예시이며, 지터와 최대 대기 시간 제한은 상황에 맞게 추가 권장 ...

November 28, 2025

AMM DEX에서 슬리피지 이해와 허용치 설정

개요 슬리피지(slippage)는 내가 예상한 가격과 실제 체결 가격의 차이를 의미함 분산형 거래소 환경에서는 블록 포함 지연과 유동성 구조 때문에 발생 빈도가 높음 슬리피지가 발생하는 이유 유동성 부족 특정 토큰 페어의 유동성 풀이 얕으면 작은 주문에도 가격 영향 커짐 대량 주문 시장 규모 대비 큰 금액을 한 번에 실행 시 풀 내 토큰 비율이 급격히 변해 체결 평균가가 악화됨 시장 변동성 트랜잭션 제출 시점과 블록에 포함되는 시점 사이 외부 가격 급변으로 기대 가격과 괴리 발생 AMM에서의 동작 대부분의 DEX는 AMM을 사용하며 풀 내 두 토큰의 비율로 가격이 정해짐. 단순화된 상수곱 x·y=k 모델 기준으로 설명함 ...

November 25, 2025

MySQL 테이블 이름 변경 RENAME TABLE vs ALTER TABLE 정리

개요 테이블 이름 변경은 ALTER TABLE로도 가능하지만, RENAME TABLE을 쓰면 다수 테이블을 한 번에 처리 가능하며 같은 서버 내 다른 데이터베이스로 이동까지 가능함 핵심 차이 ALTER TABLE RENAME은 단일 테이블 대상 RENAME TABLE은 여러 테이블을 한 문장으로 변경 가능 스키마 간 이동 지원 current_db.table에서 other_db.table로 변경 가능 동일 트랜잭션처럼 동작하는 원자성 제공, 다중 변경 중 하나라도 실패 시 전체 미적용 권한 요구 사항 존재, 원본 테이블에 ALTER와 DROP, 대상 스키마에 CREATE 권한 필요 트리거와 외래키 메타데이터는 함께 유지되나, 뷰나 저장 프로시저의 하드코딩된 참조는 자동 갱신되지 않음 사용법 단일 테이블 이름 변경 RENAME TABLE old_table TO new_table; 단일 테이블 이름 변경 ALTER 사용 ALTER TABLE old_table RENAME new_table; 다수 테이블 이름 일괄 변경 RENAME TABLE old_table1 TO new_table1, old_table2 TO new_table2, old_table3 TO new_table3; 다른 데이터베이스로 이동 같은 서버 내 RENAME TABLE current_db.table_name TO other_db.table_name;주의 사항 대상 이름이 이미 존재하면 실패 교차 서버 이동 불가, 같은 서버 인스턴스 내 스키마 간 이동만 가능 실행 시 메타데이터 락 획득, 짧은 구간 동안 읽기나 쓰기 대기 가능 배치 변경 전 사전 검증 권장, 이름 충돌 여부와 권한 확인 뷰나 프로시저 참조는 수동 점검 필요, 린트나 간단한 탐색 쿼리로 영향 범위 확인 권장 정리 단일 변경만 필요하면 ALTER로 충분하나, 다수 변경이나 스키마 이동까지 고려하면 RENAME TABLE이 더 실용적 선택 원자적 일괄 변경과 스키마 간 이동을 활용하되, 권한과 의존성 영향 검증을 선행할 것 ...

November 24, 2025

Prisma에는 왜 JOIN이 없을까: ORM 패턴, 스키마, 내부 동작 정리

개요 Prisma로 관계형 데이터베이스를 다루다 보면 자연스럽게 드는 질문이 있음 Prisma에서는 JOIN이 어디로 갔나 하는 질문임 개발자가 작성하는 Prisma Client API에는 JOIN이 없고, 서브쿼리도 보이지 않음 정말로 없는지, 없다면 왜 그런지, 어떤 트레이드오프가 있는지 정리함 ORM이란 무엇인가 ORM은 객체와 관계형 데이터베이스 간의 매핑을 제공하는 아이디어이자 구현체 집합을 의미함 애플리케이션에서 모델을 통해 데이터베이스 테이블을 간접 제어하는 추상화 계층 제공 SQL을 직접 작성하지 않고 데이터 접근 로직을 일관된 API로 수행 가능 데이터베이스 의존성 완화 효과 기대 ...

November 23, 2025

JavaScript Promise.all과 Map 정리 – 동시 비동기 처리와 키-값 컬렉션 기본

개요 여러 비동기 작업을 한 번에 묶어 처리하거나 결과를 모아야 하는 경우가 잦음 키-값 기반으로 데이터를 구조화해 저장하고 순회해야 하는 요구도 흔함 이 글은 Promise.all의 동작과 주의점, Map의 핵심 사용법을 개발자 관점에서 요약 정리함 Promise.all 개념과 정의 여러 Promise를 단일 Promise로 집계하는 유틸리티 모든 입력이 이행되면 결과를 같은 순서의 배열로 반환 하나라도 거부되면 즉시 거부로 끝나는 fail fast 특성 보유 입력은 Promise와 값 혼합 가능하며 값은 내부적으로 Promise.resolve로 이행 처리됨 ...

November 21, 2025

MECE 개념 정리: 상호배제와 전체포괄로 구조화하는 분류 원칙

개요 MECE는 Mutually Exclusive Collectively Exhaustive의 약자이며 상호배제와 전체포괄을 뜻함 겹치지 않게 나누되 합치면 전체가 되는 분류 원칙을 말함 영어권에서는 미씨로 읽고, 한국어로는 엠이씨이 또는 미시로 불림 컨설팅 실무에서 대중화된 사고 틀로 문제 분해, 요건 정의, 케이스 분류에 자주 사용됨 개념과 정의 상호배제는 요소들이 서로 겹치지 않는 상태를 의미함 중복 없이 하나의 요소가 정확히 하나의 범주에만 속함 전체포괄은 분류된 요소들을 모두 합치면 전체 모수를 빠짐없이 덮는 상태를 의미함 누락 없이 전체 공간을 커버하는 분할이 목표임 ...

November 20, 2025

Prisma where 관계 필터 some vs every 동작 차이와 주의점

관계형 데이터에서 Prisma의 where 절은 자식 레코드 기준으로 부모를 거르는 필터를 제공함. 특히 some과 every는 겉보기엔 비슷하지만 결과 집합을 크게 바꾸는 핵심 차이가 있음. 단일 필드만으로 필터링해도 동일하지 않을 수 있어 주의 필요 개념 some 관계된 레코드 중 적어도 하나가 조건을 만족하면 부모 포함 존재성 검사에 해당, 하나라도 매칭되면 true every 모든 관계된 레코드가 조건을 만족해야 부모 포함 단 하나라도 위배되면 제외됨 관계된 레코드가 하나도 없으면 vacuously true로 간주되어 조건 만족으로 처리됨 동작 원리 some은 존재량화, every는 전칭량화에 해당함 차이는 자식 레코드가 0개이거나 2개 이상일 때 두드러짐. 1개일 때는 조건이 동일하다면 결과가 같아질 수 있음 특히 자식이 없는 경우 every는 기본적으로 참으로 평가되어 부모가 포함됨. 빈 결과를 제외하려면 추가 조건 필요 간단 예시 model Post { id Int @id @default(autoincrement()) title String comments Comment[] } model Comment { id Int @id @default(autoincrement()) text String postId Int post Post @relation(fields: [postId], references: [id]) }댓글 text가 ‘interesting’인 항목을 기준으로 게시글을 거르는 케이스를 가정 ...

November 17, 2025

JavaScript Array.prototype.map 개념과 사용법 정리

개념/배경 Array.prototype.map은 배열의 각 요소에 대해 콜백을 호출해 변환된 결과로 새 배열을 만드는 메서드임 원본 배열 불변 유지, 동일 길이의 새 배열 생성이 핵심 특징임 기본 문법 const newArray = array.map((element, index, array) => { return element; }); element 현재 요소 index 현재 인덱스 array 원본 배열 참조 콜백은 요소 수만큼 호출되고, 콜백의 반환값이 새 배열의 같은 위치에 배치됨 사용 예시 숫자 배열 변환 const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map((n) => n * 2); // [2, 4, 6, 8, 10] 객체 배열에서 특정 속성 추출 ...

November 15, 2025