
MySQL은 프로세스 기반이 아닌 쓰레드 기반으로 작동하는데, 크게 포그라운드 쓰레드와 백그라운드 쓰레드로 구분된다. 대부분의 쓰레드가 백그라운드이고, 소수의 쓰레드가 포그라운드이다. 백그라운드 쓰레드는 MySQL 설정에 따라 바뀔 수 있으며, 동일한 이름의 쓰레드는 동일 작업을 병렬로 처리하는 쓰레드다.
MySQL은 클라이언트 커넥션이 생성되면 쓰레드를 생성해 해당 커넥션을 처리하는데, MySQL의 앞단에서 클라이언트와 통신하기 때문에 포그라운드 쓰레드라고 하며, 클라이언트의 요청을 처리하기 때문에 사용자 스레드라고도 한다.
포그라운드 쓰레드는 클라이언트가 요청하는 쿼리 문장을 처리한다. 커넥션이 생성되면 쓰레드 캐시에 사용 가능한 쓰레드가 있는 경우, 캐싱된 쓰레드에 커넥션을 할당한다. 만약 쓰레드 캐시가 비어있는 경우, 새로운 쓰레드를 만든다. 커넥션이 종료되면 쓰레드는 쓰레드 캐시로 되돌아간다. 이때 쓰레드 캐시에 이미 일정 개수 이상의 쓰레드가 있다면 종료시킨다. 쓰레드 캐시 크기는 thead_cache_size 변수로 설정 가능하다. 만약 초당 요청 개수가 많아진다면 쓰레드 캐시 크기를 늘려 성능을 향상시킬 수 있지만, 무작정 성능이 좋아지는 것은 아니다. 왜냐하면 쓰레드는 lock 획득 대기 또는 I/O 등으로 인해 blocking 될 때까지 실행되는데, 쓰레드 개수가 많아질 수록 과도한 context switching, 메모리 부족, 쓰레드 간 동기화로 인한 병목 현상 등이 발생할 수 있기 때문이다.
MySQL 엔터프라이즈 에디션은 쓰레드 풀을 제공해 위 문제를 해결한다. 쓰레드가 실행되는 동안 항상 쿼리를 처리하는 것은 아니기 때문에, 쓰레드와 커넥션 관계를 1:1로 두지 않는 것이다. 아래 그림은 쓰레드 풀 모델에서 커넥션을 처리하는 흐름을 나타낸 것이다.

MySQL에 커넥션 요청이 오게 되면, 이들은 큐에 보관되고 수신 쓰레드가 라운드-로빈 방식에 따라 이들을 하나씩 꺼내 쓰레드 그룹에 할당한다. 쓰레드 그룹 안에는 쿼리를 처리하는 쿼리 작업 쓰레드와 각 커넥션에 대한 쓰레드 컨텍스트 자료 구조인 THD가 있다.
쓰레드 풀의 크기는 CPU 코어의 개수와 맞추는 것이 CPU의 프로세서 친화도를 높이는데 좋다. 쓰레드 그룹 내 쿼리 작업 쓰레드는 기본적으로 설정된 개수만큼 존재하지만, 성능 향상을 위해 여러 개가 할당될 수도 있다. 만약 쓰레드 그룹 내 작업 쓰레드가 모두 처리 중이라면 새로운 작업 쓰레드를 추가할지, 기존 작업 쓰레드가 처리를 완료할 때가지 기다릴지 선택해야 한다. 쓰레드 풀의 타이머 쓰레드는 주기적으로 쓰레드 그룹의 상태를 체크해 작업 쓰레드가 지금 처리 중인 작업을 끝내지 못하면 새로운 쓰레드를 생성해 그룹에 추가한다.
또한 포그라운드 쓰레드는 데이터를 데이터 버퍼나 캐시로부터 가져오는데, 이때 miss가 나는 경우 직접 디스크의 데이터나 인덱스 파일로부터 읽어와 처리한다. MyISAM 엔진은 디스크 쓰기 작업까지 포그라운드 쓰레드가 처리하지만, InnoDB 엔진은 데이터 버퍼나 캐시까지만 포그라운드 쓰레드가 처리하고, 버퍼에서 디스크로 기록하는 작업은 백그라운드 쓰레드가 처리한다.
MyISAM 엔진은 크게 해당 사항이 없지만 InnoDB 엔진은 insert 버퍼 병합, 데이터를 디스크에 기록, 데이터를 버퍼로 읽어오기, 잠금이나 데드락 모니터링 등의 작업을 백그라운드에서 수행한다.
읽기 쓰레드와 쓰기 쓰레드 개수는 병렬 처리를 위해 2개 이상 설정이 가능한데, 읽기 작업은 주로 포그라운드 쓰레드에서 처리되어 많이 필요하지 않지만 쓰기 작업은 상당한 양의 작업을 백그라운드에서 처리하기 때문에 쓰기 쓰레드는 디스크를 최적으로 사용할 수 있도록 설정하는 것이 좋다.
읽기 작업은 지연 처리할 수 없지만, 쓰기 작업은 버퍼링을 통해 지연 처리할 수 있다. InnoDB에서는 데이터가 변경되는 경우 데이터가 디스크에 완전히 쓰일 때까지 기다리지 않아도 되지만 MyISAM의 일반적인 쿼리는 쓰기 버퍼링을 지원하지 않기 때문에 그럴 수 없다.(지연된 쓰기가 있으나 일반적이지 않다.)