테마
서버 구조와 쿼리 실행
MySQL은 서버 층과 스토리지 엔진(storage engine) 두 단으로 나뉜다. 서버 층은 접속을 받고 SQL을 해석해 실행 방법을 정하며, 스토리지 엔진은 데이터를 실제로 저장하고 읽는다. 쿼리 하나가 파서(parser), 옵티마이저(optimizer), 엔진을 차례로 거쳐 실행되는 흐름을 그릴 수 있어야 한다.
전체 구조
다이어그램 로딩…
- 커넥션·스레드: 기본 설정에서는 클라이언트 접속(커넥션) 하나를 스레드 하나가 전담한다. 스레드를 새로 만드는 비용이 커서, 쓰고 난 스레드를 버리지 않고 재사용하는 스레드 캐시(thread cache)나 스레드 풀(thread pool)을 쓴다.
- 파서: SQL 문법을 검사하고, 문장을 서버가 다룰 수 있는 트리 구조인 파스 트리(parse tree)로 바꾼다. 여기서 걸리면 SQL 문법 오류를 뜻하는 에러 1064가 난다.
- 옵티마이저: 같은 결과를 내는 여러 방법 중 가장 싸 보이는 실행 계획(execution plan)을 고른다(옵티마이저, 실행 계획 참고). 각 방법의 비용을 추정해 비교하는 비용 기반 방식이라 테이블 크기·값 분포 같은 통계 정보에 의존한다.
- 스토리지 엔진: 실제 저장과 조회를 맡는다. 플러그인 방식이라 테이블마다 엔진을 바꿀 수 있다.
스레딩과 메모리
- 기본은 커넥션당 스레드다. 동시 접속이 최대 접속 수 설정인
max_connections를 넘으면 새 접속은 거부되고, 접속이 너무 많다는 에러 1040이 난다. 대부분의 애플리케이션이 커넥션 풀(connection pool)로 연결 수와 생성 비용을 제한하는 이유다. - 스레드 풀: 엔터프라이즈 에디션과 Percona1에서 쓸 수 있다. 일하는 스레드 수를 고정하고, 넘치는 요청은 큐에 줄 세운다. 접속이 폭증할 때 CPU가 수많은 스레드를 번갈아 실행하느라 시간을 버리는 컨텍스트 스위칭(context switching)을 막는다.
- 메모리는 두 종류다. 서버 전체가 함께 쓰는 글로벌 메모리에는 버퍼 풀(buffer pool)과 로그 버퍼(log buffer)가 있다. 접속마다 따로 잡는 세션 메모리에는 조인·정렬을 처리할 때 쓰는 조인 버퍼(join buffer)와 정렬 버퍼(sort buffer)가 있다. 세션 메모리는 커넥션 수만큼 곱해지니
max_connections와 함께 봐야 한다.
알아둘 것들
- 쿼리 캐시(query cache): 같은 쿼리의 결과를 저장해 뒀다가 그대로 돌려주던 기능으로, 8.0에서 삭제됐다. 테이블이 바뀔 때마다 그 테이블과 관련된 캐시를 무효화해야 해서 확장성이 깨졌다. 지금은 Redis 같은 앱 쪽 캐시로 대체한다.
- 복제(replication)는 바이너리 로그(binary log) 기반의 서버 층 기능이라 여러 엔진의 변경을 기록할 수 있다. 다만 트랜잭션(transaction)을 지원하지 않는 엔진을 섞으면, 변경을 확정하는 커밋(commit)과 취소하는 롤백(rollback)의 경계가 맞지 않아 일관된 복제가 어렵다. 그래서 운영에서는 InnoDB가 기본이다. 복제 구조는 복제 구조와 GTID에서 본다.
- 컴포넌트·플러그인(component·plugin): 인증, 감사(audit, 누가 무엇을 했는지 기록), 암호화 키 관리 같은 기능을 부품처럼 갈아 끼우는 구조다. 스토리지 엔진 교체가 대표 사례다.
면접 포인트
- "쿼리 실행 순서를 말해보세요" → 커넥션→파서→옵티마이저→엔진. 그림을 그리면서.
- "커넥션이 밀리면?" → 스레드 모델 설명 → 풀·
max_connections·세션 메모리 순서로. - "쿼리 캐시가 왜 사라졌나요?" → 무효화 비용 + 락 경합. 캐시는 앱 층으로.
참고
MySQL과 호환되는 오픈소스 배포판인 Percona Server를 말한다. 엔터프라이즈는 Oracle이 파는 상용 MySQL 에디션이다. ↩︎
