MySQL은 크게 두 레이어로 구성됩니다:
스토리지 엔진은 인덱스 컬럼에 대한 단순 비교 조건(=, >, <, BETWEEN 등)만 처리할 수 있습니다. 이를 넘어서는 조건들은 MySQL 서버 레이어에서 추가로 걸러집니다.
1. MySQL의 레이어 구조
MySQL은 플러그인 방식의 스토리지 엔진 아키텍처를 사용합니다. 클라이언트 요청은 다음 흐름을 거칩니다:
스토리지 엔진은 "이 인덱스로 이 범위의 레코드를 읽어와"라는 명령만 이해합니다. 복잡한 WHERE 조건, 함수 호출, 서브쿼리 등은 MySQL 서버 레이어의 몫입니다.
2. 인덱스 조건 vs 필터 조건
EXPLAIN에서 key와 Extra 컬럼을 보면 어디서 필터링이 일어나는지 알 수 있습니다:
key: 스토리지 엔진이 사용하는 인덱스Extra: Using where: MySQL 서버 레이어에서 추가 필터링 수행Extra: Using index condition: ICP 사용 (스토리지 엔진에서 일부 조건 푸시다운)스토리지 엔진에서 처리되는 조건
WHERE age = 30WHERE age BETWEEN 20 AND 40WHERE (col1, col2) = (10, 20)MySQL 서버 레이어에서만 처리되는 조건 (Using where)
WHERE YEAR(created_at) = 2026WHERE salary * 1.1 > 50000WHERE name LIKE '%kim%' (앞에 %가 있으면)WHERE user_id IN (SELECT ...)WHERE t1.col = t2.colWHERE non_indexed_col = 'value'WHERE col1 = 10 OR col2 = 20 (Index Merge 없이)WHERE varchar_col = 123 (문자열-숫자 비교)실무 예시
SELECT * FROM users
WHERE age >= 30
AND YEAR(created_at) = 2026;
이 쿼리에서:
age >= 30: age 인덱스를 사용해 스토리지 엔진에서 필터링YEAR(created_at) = 2026: 함수 호출이므로 MySQL 서버에서 필터링 (Using where 표시)성능 영향
스토리지 엔진에서 필터링되지 못하면, 불필요한 레코드까지 MySQL 서버로 올라와 추가 처리 비용이 발생합니다. 예를 들어:
-- 나쁜 예: 함수 사용으로 인덱스 미사용
WHERE YEAR(created_at) = 2026
-- 좋은 예: 범위 조건으로 변경해 인덱스 사용
WHERE created_at >= '2026-01-01'
AND created_at < '2027-01-01'
ICP (Index Condition Pushdown)의 역할
MySQL 5.6부터 도입된 ICP는 일부 조건을 스토리지 엔진으로 "푸시다운"해서 MySQL 서버로 올라오는 레코드 수를 줄입니다. 하지만 함수나 서브쿼리는 여전히 푸시다운되지 않습니다.
쿼리 조건 중 인덱스를 사용할 수 있는 것은 스토리지 엔진에서 필터링되고, 함수/연산 등 복잡한 조건은 MySQL 서버 레이어에서 처리됩니다.
| 조건 예시 | 처리 위치 | EXPLAIN Extra |
|---|---|---|
WHERE age = 30 |
스토리지 엔진 | (없음 - 인덱스 사용) |
WHERE age BETWEEN 20 AND 40 |
스토리지 엔진 | (없음 - 범위 스캔) |
WHERE name LIKE 'Kim%' |
스토리지 엔진 | (없음 - 인덱스 범위) |
WHERE name LIKE '%Kim%' |
MySQL 서버 | Using where |
WHERE YEAR(created) = 2026 |
MySQL 서버 | Using where |
WHERE salary * 1.1 > 50000 |
MySQL 서버 | Using where |
WHERE id IN (SELECT ...) |
MySQL 서버 | Using where |
WHERE age > 30 AND city = 'Seoul' (ICP) |
스토리지 엔진 (일부) | Using index condition |
WHERE non_indexed = 'val' |
MySQL 서버 (풀 스캔 후) | Using where |
1. 함수를 피하고 범위 조건으로 변경
-- 나쁜 예
WHERE DATE_FORMAT(created_at, '%Y-%m') = '2026-03'
-- 좋은 예
WHERE created_at >= '2026-03-01'
AND created_at < '2026-04-01'
2. 연산은 우변으로 이동
-- 나쁜 예
WHERE salary * 1.1 > 50000
-- 좋은 예
WHERE salary > 50000 / 1.1
3. EXPLAIN으로 확인
Using where가 표시되면 MySQL 서버 레이어에서 추가 필터링이 일어난다는 신호입니다. 가능하면 조건을 인덱스로 푸시다운할 수 있도록 쿼리를 재작성하세요.
4. ICP 활용
복합 인덱스의 뒷부분 컬럼 조건은 ICP를 통해 스토리지 엔진으로 푸시다운될 수 있습니다. optimizer_switch에서 index_condition_pushdown=on인지 확인하세요.