요약
- 슬로우 쿼리 로그에 대한 내용과 쿼리 튜닝을 위한 여러 지표에 대한 내용을 알게 됨.
- 트랜잭션과 잠금에 대한 내용을 이해하게 됨.
- MySQL 에서 InnoDB 와 MyISAM, MEMORY 스토리지 엔진에 따른 트랜잭션 여부 및 예시를 통해 트랜잭션을 통한 데이터 정합성의 중요성을 이해하게 됨.
- 트랜잭션의 범위를 최소화 하고, 네트워크 통신이 필요한 지연이 있을 경우, 해당 부분을 트랜잭션에서 반드시 제외하자는 가이드를 알게 됨.
발췌
- 잠금(Lock) 은 동시성을 제어하기 위한 기능이다.
- 트랜잭션은 데이터의 정합성을 보장하기 위한 기능이다.
- 프로그램의 코드에서 라인 수는 한두 줄이라고 하더라도 네트워크 작업이 있는 경우에는 반드시 트랜잭션에서 배제해야 한다.
메모
슬로우 쿼리 로그
슬로우 쿼리 통계
- 분석 결과의 최상단에 표시됨
- 모든 쿼리를 대상으로 슬로우 쿼리 로그의 실행 시간(Exec time), 잠금 대기 시간(Lock time) 에 대한 평균 & 최대/최소 값을 표시함.
실행 빈도 및 누적 실행 시간순 랭킹
- 각 쿼리별 응답 시간과 실행 횟수를 보여줌
pt-query-digest
명령 실행 시 --order-by
옵션으로 정렬 순서를 변경할 수 있음.
- Query ID 는 실행된 쿼리 문장을 정규화(쿼리에 사용된 리터럴을 제거) 해서 만들어진 해시값임.
- 일반적으로 같은 모양의 쿼리라면 동일한 Query ID를 가지게 된다.
쿼리별 실행 횟수 및 누적 실행 시간 상세 정보
- Query ID 별 쿼리를 쿼리 랭킹에 표시된 순서대로 자세한 내용을 보여줌
- 랭킹별 쿼리에서 대상 테이블에 대해 어떤 쿼리인지만을 표시함.
- 실제 상세한 내용은 개별 쿼리의 정보를 확인해보면 됨.
- 여기서는 쿼리가 얼마나 실행됐는지, 쿼리 응답시간에 대한 히스토그램같은 상세 내용을 볼 수 있음.
트랜잭션과 잠금
- MySQL 의 동시성에 영향을 미치는 잠금(Lock), 트랜잭션, 트랜잭션 격리 수준(Isolation level) 을 살펴본다.
- 트랜잭션은 작업의 완전성을 보장해 준다.
- 논리적인 작업 셋을 모두 완벽하게 처리하거나, 처리하지 못할 경우 원 상태로 복구함.
- 작업의 일부만 적용되는 현상(Partial update)을 방지해주는 기능이다.
- 잠금(Lock) 은 동시성을 제어하기 위한 기능이다.
- 트랜잭션은 데이터의 정합성을 보장하기 위한 기능이다.
- 격리 수준은 하나의 트랜잭션 내에서, 혹은 여러 트랜잭션 간의 작업 내용을 어떻게 공유 및 차단할지 결정하는 레벨을 의미함.
트랜잭션
- 트랜잭션은 데이터베이스 서버에서 개발자에게 엄청난 큰 혜택을 제공한다..
- 트랜잭션을 지원하지 않는 MyISAM 과 트랜잭션을 지원하는 InnoDB 처리 방식 차이를 살펴본다.
MySQL에서의 트랜잭션
- 트랜잭션이 꼭 여러 개의 변경 작업을 수행하는 쿼리가 조합됐을 때만 의미있는 개념이 아님.
- 트랜잭션은 하나의 논리적인 작업 셋에 하나의 쿼리가 있든 두 개 이상의 쿼리가 있든 관계 없이, 논리적인 작업 셋 자체가 100%(COMMIT) 적용되거나 0%(ROLLBACK)로 아무것도 적용되지 않아야 함을 보장해 주는 것임.
mysql> CREATE TABLE tab_myisam (fdpk INT NOT NULL, PRIMARY KEY (fdpk) ) ENGINE=MYISAM;
mysql> INSERT INTO tab_myisam (fdpk) VALUES (3);
mysql> CREATE TABLE tab_innodb (fdpk INT NOT NULL, PRIMARY KEY (fdpk) ) ENGINE=INNODB;
mysql> INSERT INTO tab_innodb (fdpk) VALUES (3);
mysql> SET autocommit=ON;
mysql> INSERT INTO tab_myisam (fdpk) VALUES (1),(2),(3);
mysql> INSERT INTO tab_innodb (fdpk) VALUES (1),(2),(3);
- 위 와 같이 MyISAM 스토리지 엔진과 InnoDB 엔진을 구분하여 테이블 및 데이터를 삽입하는 쿼리임.
- 이미 3인 값이 있는 상태에서 1,2,3을 한번에 삽입하는 예시이다.
- 이때, 이미 3이 있으므로 두 엔진 모두 Duplicate entry 에러가 뜬다.
- 하지만, 이후 테이블을 조회하면 MyISAM 의 데이터는 1,2,3이 저장되어 있고, InnoDB 는 3만이 저장되어 있다.
- InnoDB 스토리지 엔진은 쿼리 중 일부라도 오류가 발생하면 전체를 원 상태로 만든다. (트랜잭션 원칙)
- MyISAM (=MEMORY 스토리지 엔진도 동일하게 작동함) 테이블에서 발생하는 이러한 현상을 부분 업데이트 라고 표현하며 이러한 부분 업데이트 현상은 테이블 데이터 정합성을 맞추기 상당히 어렵게 만든다.
- 위 예시와 같이 2개 이상의 쿼리를 실행하는 경우, 실패에 대한 재처리 작업을 고민해야 함.
- MyISAM, 에서 첫번째 쿼리 실패, 성공에 대한 분기와 함께 다음 쿼리에 대한 분기가 계속해서 이어지게 됨. → 상당히 복잡한 로직이 추가되야 함.
- 근데, 트랜잭션이 지원되지 않는 MyISAM 에 레코드를 INSERT 할 때는 이 방법 외에는 방법이 없음.
- 이렇게 하지 않으면 부분 업데이트가 될 수 있기에 쓰레기 데이터가 테이블에 남아있을 가능성이 높음.
- InnoDB 의 경우, 위 문제를 간단한 코드로 완벽 구현 가능하다.
주의사항
- 트랜잭션 또한 DBMS의 커넥션과 동일하게 꼭 필요한 최소의 코드에만 적용하는 것이 좋다.
- 프로그램 코드에서 트랜잭션의 범위를 최소화 하라는 의미임.
- 실제로 DBMS 에 데이터를 저장하는 작업이 시작되는 부분부터 트랜잭션을 시작하게 만들어서 트랜잭션 소유 시간을 최대한 짧게 가져가도록 한다. (미리 트랜잭션을 시작할 필요 없다.)
- 메일 전송이나 FTP 파일 전송 작업, 혹은 네트워크를 통해 원격 서버와 통신하는 작업은 어떻게 해서든 DBMS 의 트랜잭션 내에서 제거하는 것이 좋음.
- 메일 서버와 통신할 수 없는 상황이 발생하면 웹 서버 뿐 아니라 DBMS 서버 까지 위험해지는 상황이 발생하게 된다.