데이터베이스 시스템에서 반복적인 쿼리 실행은 성능 병목 현상을 유발할 수 있습니다. MySQL의 쿼리 캐시는 이러한 반복적인 쿼리의 결과를 메모리에 저장하여, 동일한 쿼리가 다시 실행될 때 데이터베이스 서버의 부하를 줄이고 응답 시간을 단축하는 데 기여합니다.
쿼리 캐시 동작 방식
클라이언트가 MySQL 서버로 SQL 쿼리를 전송하면, 서버는 다음 단계를 거칩니다:
- 들어온 쿼리를 쿼리 캐시에서 먼저 확인합니다.
- 만약 동일한 쿼리(문자열까지 정확히 일치)의 결과가 캐시에 존재한다면, 서버는 캐시된 결과를 즉시 클라이언트에게 반환합니다. 이 과정에서 SQL 파싱, 최적화, 실행 등의 복잡한 단계를 건너뛸 수 있어 매우 효율적입니다.
- 캐시에 해당 쿼리 결과가 없다면, MySQL 서버는 쿼리를 파싱하고 실행 계획을 최적화한 후, 스토리지 엔진을 통해 실제 데이터를 검색합니다.
- 쿼리 실행 결과는 클라이언트에게 반환되며, 동시에 쿼리 캐시에 저장되어 다음 동일 쿼리 요청에 대비합니다.
주의할 점은 캐시된 데이터와 관련된 테이블의 내용이 변경되면, 해당 테이블을 참조하는 모든 캐시된 쿼리 결과가 즉시 무효화된다는 것입니다. 따라서 데이터 변경이 빈번한 테이블에 대한 쿼리는 쿼리 캐시의 효과가 미미하거나 오히려 오버헤드가 될 수 있습니다.
쿼리 캐시 설정 확인
MySQL 8.0 버전부터는 쿼리 캐시 기능이 완전히 제거되었습니다. 이전 버전에서 쿼리 캐시 설정을 확인하는 방법은 다음과 같습니다.
쿼리 캐시의 활성화 상태 및 관련 크기 설정을 확인하려면 다음 명령어를 사용합니다:
SHOW VARIABLES LIKE 'query_cache_type';
SHOW VARIABLES LIKE 'query_cache_size';
현재 쿼리 캐시의 통계 정보를 확인하여 얼마나 효율적으로 작동하고 있는지 파악할 수 있습니다. 주요 통계 변수는 다음과 같습니다:
SHOW STATUS LIKE 'Qcache%';
| 변수명 | 설명 |
|---|---|
| Qcache_free_blocks | 쿼리 캐시 내 사용 가능한 메모리 블록 수 |
| Qcache_free_memory | 쿼리 캐시 내 사용 가능한 총 메모리 크기 |
| Qcache_hits | 쿼리 캐시가 성공적으로 적중된 횟수 (캐시로부터 결과를 반환한 횟수) |
| Qcache_inserts | 쿼리 캐시에 새로운 쿼리 결과가 추가된 횟수 |
| Qcache_lowmem_prunes | 메모리 부족으로 인해 캐시에서 제거된 쿼리 수 |
| Qcache_not_cached | 캐시되지 않은 쿼리 수 (캐시 타입 설정 또는 캐시 불가 조건으로 인해) |
| Qcache_queries_in_cache | 현재 쿼리 캐시에 저장된 쿼리 수 |
| Qcache_total_blocks | 쿼리 캐시 내 총 메모리 블록 수 |
쿼리 캐시 활성화
MySQL의 쿼리 캐시는 기본적으로 비활성화되어 있을 수 있습니다. query_cache_type 파라미터를 통해 캐시 동작 방식을 설정할 수 있습니다. 가능한 값은 다음과 같습니다:
OFF(또는0): 쿼리 캐시 기능을 완전히 비활성화합니다.ON(또는1): 쿼리 캐시 기능을 활성화합니다.SELECT문은 캐시 조건에 부합하면 자동으로 캐시됩니다. 단,SQL_NO_CACHE힌트가 명시된 쿼리는 캐시되지 않습니다.DEMAND(또는2): 쿼리 캐시 기능을 '요청 시' 활성화합니다. 이 모드에서는SELECT문에SQL_CACHE힌트가 명시된 경우에만 캐시됩니다.
쿼리 캐시를 활성화하려면 MySQL 설정 파일(일반적으로 my.cnf 또는 my.ini)에 다음 설정을 추가합니다:
[mysqld]
query_cache_type = ON
query_cache_size = 64M
설정 변경 후에는 MySQL 서비스를 재시작해야 적용됩니다. 이후에 반복적으로 실행되는 쿼리를 테스트하고 Qcache_hits 값을 확인하여 캐시가 정상적으로 작동하는지 검증할 수 있습니다.
SELECT 문에서 쿼리 캐시 제어
개별 SELECT 쿼리에 대해 쿼리 캐시의 사용 여부를 명시적으로 지정할 수 있습니다.
SQL_CACHE: 쿼리 캐시가 활성화되어 있고 캐시 조건에 부합한다면, 이 쿼리의 결과를 캐시하도록 지시합니다.SQL_NO_CACHE: 이 쿼리에 대해 쿼리 캐시를 사용하지 않도록 지시합니다. 서버는 캐시를 확인하지도 않고, 이 쿼리 결과를 캐시하지도 않습니다.
예시:
SELECT SQL_CACHE product_id, product_name FROM products WHERE category_id = 1;
SELECT SQL_NO_CACHE user_id, username FROM users WHERE status = 'active';
쿼리 캐시 무효화 조건
다음과 같은 경우에는 쿼리 캐시가 작동하지 않거나 캐시된 결과가 무효화됩니다:
- **SQL 문 불일치**: 쿼리 문자열이 단 한 글자라도 다르거나 대소문자가 다르면 캐시를 적중하지 못합니다. 예를 들어,
SELECT COUNT(*) FROM articles;와select count(*) FROM articles;는 다른 쿼리로 간주됩니다. - **비결정적 함수 포함**:
NOW(),RAND(),CURRENT_DATE(),UUID(),USER(),DATABASE()등과 같이 실행할 때마다 다른 결과를 반환할 수 있는 함수를 포함하는 쿼리는 캐시되지 않습니다. 예를 들어, 다음 쿼리는 캐시되지 않습니다:SELECT order_id, total_amount FROM orders WHERE order_date < NOW(); - **테이블을 사용하지 않는 쿼리**:
SELECT 'Hello World';와 같이 실제 데이터베이스 테이블을 참조하지 않는 쿼리는 캐시되지 않습니다. - **시스템 데이터베이스 쿼리**:
mysql,information_schema,performance_schema등 MySQL의 내부 관리용 데이터베이스에 대한 쿼리는 캐시되지 않습니다. - **저장 프로시저, 트리거, 이벤트 내부 쿼리**: 저장된 프로그램(Stored Program) 내에서 실행되는 쿼리는 캐시되지 않습니다.
- **테이블 데이터 변경**: 캐시된 쿼리와 관련된 테이블의 데이터가
INSERT,UPDATE,DELETE,TRUNCATE TABLE,ALTER TABLE등의 명령으로 변경되면, 해당 테이블을 사용하는 모든 캐시된 쿼리 결과는 즉시 무효화되고 캐시에서 제거됩니다.