Chapter 04. 아키텍처
2024년 3월 3일 작성
- 4.3 MyISAM 스토리지 엔진 아키텍처
- 4.4 MySQL 로그 파일
4.2.13 InnoDB와 MyISAM, MEMORY 스토리지 엔진 비교
초장부터 결론이 나오는데
8.0 버전부터는 InnoDB 스토리지 엔진 만으로 모든 기능을 구현할 수 있게 되었고, InnoDB의 기능도 계속 개선되어 MyISAM 스토리지 엔진의 기능은 도태되는 상황이고, 이후 버전에서는 MyISAM 스토리지 엔진은 없어질것으로 예상했다.
- MySQL 5.5부터는 InnoDB 스토리지 엔진이 기본 스토리지 엔진이 됐지만
- 8.0 전까지는 MySQL 서버의 시스템 테이블은 MyISAM 테이블을 사용
- 사용자 인증 관련 정보, 복제 관련 정보
- 8.0 이후부터는 시스템 테이블 또한 InnoDB 스토리지 엔진으로 교체됨 (모두!)
- 또한 공간 좌표 검색 + 전문 검색 기능 모두 InnoDB 스토리지 엔진을 지원하도록 개선됨. 책을 쓴 시점에도 MySQL의 모든 기능이 InnoDb 기반으로 개편이 되어 MyISAM 스토리지 엔진만이 가지는 장점이 없다고 했다.
4.3 MyISAM 스토리지 엔진 아키텍처
MyISAM 스토리지 엔진의 성능은
- 키 캐시
- 운영체재 캐시/버퍼 에 영향을 미친다.
4.3.1 키 캐시
MyISAM 의 키캐시 (Key Cache, 키 버퍼로도 불림): InnoDB의 버퍼 풀과 비슷한 역할
InnoDB 버퍼풀과의 차이점
- 키 캐시는 인덱스만을 대상으로 함
- 디스크 쓰기 작업에 대해서만 부분적으로 버퍼링 역할을 한다.
키 캐시가 얼마나 효율적인지 측정하는 법
- : 키를 디스크에서 읽어온 횟수 -> 캐시 미스
- : 총 캐시 조회 수 라고 하면, 캐시 hit rate는
상세 매뉴얼
- 권장 메모리 할당량 매뉴얼에서는 Hite rate를 99% 이상으로 유지하라고 함. 만약 모자르다면? -> 캐시에 사용하는 메모리 공간 늘리기
- 32비트 운영체제에서는 4GB 이하까지만 가능
- 64비트 운영체제에서는
OS_PER_PROCESS_LIMIT
까지 할당 가능
- 키 캐시 메모리 할당량 바꾸는 법
key_buffer_size
: Default 키 캐시 공간을 설정하는 파라미터.
Default 키 캐시 외에도 키 캐시를 만들어 테이블마다 사용할 키 캐시를 명시할 수 있음.
만들고_싶은_키캐시_이름.key_buffer_size
이 변수에 원하는 만큼 메모리를 할당해주면 된다.
- 새로 만든 키 캐시를 사용하려면 스토리지 엔진에 알려줘야한다.
ex)
kbuf_board.key_buffer_size = 2GB
했다면..CACHE INDEX db1.board, db2.board IN kbuf_board;
이렇게 설정해야 비로소 board 테이블의 인덱스가kbuf_board
키캐시를 사용하게 된다. 더 자세한 것은 MySQL 매뉴얼의 Multiple Key Caches 참고
4.3.2 운영체제의 캐시 및 버퍼
MyISAM 테이블을 주로 사용한다면 키캐시를 할당할 때, 운영체제가 사용할 수 있는 캐시 공간을 위해 충분한 메모리를 남겨둬야한다.
퀴즈: 왜일까용? ~~ 힌트: 키캐시는 인덱스를 캐시하는 용도로 사용한다
답: 읽은 데이터를 캐싱하거나 변경한 데이터를 버퍼링할 공간이 MyISAM 스토리지엔진에는 없다. 스토리지엔진 단에서 지원하지 않기 때문에, OS의 캐시 기능이라도 사용할 수 있도록 하는 것이 성능에 필요
상세 가이드
- 키 캐시는 최대 물리메모리의 40%를 넘지 않도록 설정
- 나머지 메모리 공간은 운영체제가 자체적인 캐시 공간으로 사용할 수 있도록
옛날글: MyISAM와의 차이를 기반으로 InnoDB 버퍼 파라미터 조정 https://m.blog.naver.com/youlcc74/150126774869
4.3.3 데이터파일과 PK(인덱스) 구조
InnoDB 스토리지 엔진
- PK로 클러스터링돼 저장된다고 했다. (PK 값이 비슷한 레코드끼리 묶어서 저장, PK 값이 레코드의 위치를 결정)
반면 MyISAM은
- PK랑 무관하게 INSERT 되는 순서대로 데이터 파일에 저장된다.
- 저장된 레코드마다 ROWID라는 물리적주솟값을 가지고 PK과 세컨더리 인덱스는 이 ROWID(물리적 주소)를 포인터로 가지게 된다.
ROWID 는 고정길이와 가변길이로 저장이 가능하다
- 고정길이 ROWID: 테이블의 최대 레코드 수(MAX_ROWS 옵션)가 정해져있는 경우 ROWID에 레코드 삽입 순서를 4바이트 정수로 저장한다.
- 가변길이 ROWID: 테이블의 최대 레코드 수가 정해져있지 않은 경우 첫번째 바이트는 ROWID 길이를 저장하고 나머지 공간에 ROWID를 저장한다. 고정길이에서와 달리 데이터 파일에서 레코드의 위치(offset) 이 사용된다.
4.4 MySQL 로그파일
개발자들은 로그 파일을 활용해서, MySQL 의 깊은 내부지식이 없어도 서버의 상태나 부하원인을 쉽게 찾을 수 있다. 따라서 책에 나오는 로그 파일들을 자세히 확인하는 습관을 들일 필요가 있다.
4.4.1 에러 로그 파일
- MySQL이 실행되는 도중에 발생하는 에러나 경고 메시지가 출력되는 로그 파일이다.
.err
라는 확장자가 붙은 파일로 생성됨.
다음 메시지들을 많이 보게 될 것
- 시작하는 과정과 관련된 정보성 메시지 / 에러메시지
mysqld: ready for connection
-> 서버가 정상적으로 기동했다는 로그, 이후 에러나 경고 메시지가 없으면 문제가 없는 것으로 판단 가능.
유즈케이스
- 설정된 변수명/값 적용 및 정상 동작 확인
- 설정 파일 변경 시
- 데이터베이스 비정상적 종료 후 다시 시작하는 경우
- 비정상적 종료 시 InnoDB의 트랜잭션 복구 메시지
- 만약에 종료 이후 잘 동작하지 못하고 복구되지 못하는 경우 이 에러 메시지를 출력하고 MySQL이 다시 종료된다.
- 이 경우 해결이 어려우니
innodb_force_recovery
파라미터를 0보다 큰값으로 설정 후 MySQL 재시작 https://dev.mysql.com/doc/refman/8.0/en/forcing-innodb-recovery.html
- 쿼리 처리도중 발생한 에러 메시지
- 쿼리에 대한 에러는 예방이 어렵다.
- 따라서 에러 로그 파일을 주기적으로 검토하고 문제가 될만한 쿼리를 확보
- Aborted connection
- 클라이언트 애플리케이션에서 정상적으로 접속 종료를 하지 못한 경우 찍힘
- 네트워크상 문제로 의도치 않게 접속이 끊어지는 때도 포함
- 이 메시지가 상당히 많이 누적된다면
- 애플리케이션의 커넥션 종료 로직을 살펴보기
- max_connect_errors 값이 너무 낮지 않은지 확인하고 필요시 증가시키기
- InnoDB 모니터링 또는 상태조회 명령 이건 모니터링을 활성화해두면 로그에 남는 것인데, 이 로그는 사이즈가 커서 계속 모니터링을 활성화해두면 파일시스템 공간을 다 사용해버릴 수 있다.
- 따라서 모니터링을 사용한 이후에는 다시 비활성화하는 습관을 들여야한다.
- MySQL 종료 메시지 서버가 종료돼있거나, 아무도 모르게 재시작되는 경우를 보게 된다면, 에러로그 파일에서 MySQL이 마지막으로 출력한 메시지를 확인하는 것이 유일한 원인 파악 방법이다.
- Received SHUTDOWN from user... -> 누군가에 의해 종료
- 아무런 메시지가 없거나 스택트레이스가 출력 -> Segmentation fault -> 버그와 관련 있는지 조사하고 MySQL 버전을 업그레이드 또는 대비책 마련하기
4.4.2 제너럴 쿼리 로그 파일
실행되는 모든 쿼리들의 목록을 확인할 수 있는 로그 파일.
- 쿼리 요청을 받으면 바로 기록, 쿼리가 실패해도 일단 로그 파일에는 남는다.
찾아본 것
- 어떻게 킬까?:
general_log
를 ON으로 바꾼다. - RDS: DB 파라미터에서
general_log
를 1로 바꾼다. 0으로 하면 끄는 것 - 많이들 제너럴 로그라고 부르는 것 같다.
- 활용 방법: general_log를 켜두고 실제 트랜잭션이나 DB 쿼리를 확인하고 싶은 상황을 테스트 한뒤 끄고 로그를 뽑아 확인한다.
4.4.3 슬로우 쿼리 로그
슬로우 쿼리 로그는 long_query_time
이상의 시간이 소요된 쿼리가 모두 기록된다.
소요 시간이 측정 가능 -> 일단 정상적으로 실행이 완료가 된 쿼리가 기록되는 것.
슬로우쿼리 로그는
- 서비스 운영 중, MySQL 서버의 전체적인 성능 저하를 검사
- 정기 점검 시 쿼리 튜닝에 활용할 수 있다.
서비스를 출시하기 전이라면 제너럴 로그로 모든 쿼리를 튜닝할 수도 있겠지만, 운영중에는 문제가 되는 쿼리를 찾기 위해 슬로쿼리 로그를 활용하는 편이 나을 것이다.
슬로우쿼리도 마찬가지로 활성화여부를 다음과 같이 확인할 수 있다. ![[Pasted image 20240224175305.png]]
- 기록을 파일로 기록할지 테이블로 기록할지 선택 가능
- 테이블:
log_output=TABLE
-> CSV 스토리지 엔진 - 파일:
log_output=FILE
-> 디스크의 파일
자세한 내용은 https://dev.mysql.com/doc/refman/8.0/en/slow-query-log.html 참고..
Slow Query Log Contents
Percona Toolkit의 pt-query-digest
제너럴 로그나, 슬로우쿼리 로그나 둘 다 내용이 상당히 많아서 직접 쿼리를 하나씩 검토하기에는 너무 많이 걸리거나 성능에 영향을 주는 쿼리를 선별하기가 어려울 수 있다.
그럴 때 Percona에서 개발한 pt-query-digest
스크립트를 이용하면 다음과같이
쿼리를 빈도/처리성능 으로 정렬해서 볼 수 있다.
사용법
결과물 세 가지 표를 보여준다.
- 슬로우 쿼리 통계: 모든 쿼리를 대상으로 로그 실행시간, 잠금 대기시간 등에 대해 평균/최대/최소 값을 표시한다.
- 실행빈도 및 누적 실행 시간순 랭킹
- 쿼리별 실행 횟수 및 누적 실행시간 상세 정보
- 아이디(쿼리 모양) 별로 모든 쿼리의 상세정보를 확인할 수 있음
세가지를 한 파일로 제공해줌. 섹션 구분해서 보면 됨.
# 170ms user time, 60ms system time, 37.28M rss, 390.39G vsz
# Current date: Tue Wed 21 11:34:00 2024
# Hostname:
# Files: ./mysql-slowquery.log.2024-02-21.total
# Overall: 122 total, 17 unique, 0.00 QPS, 0.01x concurrency _____________
# Time range: 2024-02-20T03:19:52 to 2024-02-21T11:34:21
# Attribute total min max avg 95% stddev median
# ============ ======= ======= ======= ======= ======= ======= =======
# Exec time 244s 1s 13s 2s 4s 2s 1s
# Lock time 27ms 28us 7ms 221us 224us 727us 80us
# Rows sent 14.24M 0 932.16k 119.53k 345.04k 193.73k 36.57k
# Rows examine 30.34M 30.07k 932.16k 254.68k 345.04k 182.57k 201.74k
# Bytes sent 2.35G 0 171.89M 19.72M 83.55M 35.35M 6.61M
# Query size 838.24k 57 150.12k 6.87k 9.80k 22.89k 299.03
# Bytes receiv 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0
# Created tmp 7 0 1 0.06 0 0.23 0
# Errno 0 0 0 0 0 0 0
# Read first 81 0 1 0.66 0.99 0.47 0.99
# Read key 1.55M 1 353.43k 13.02k 92.72 53.05k 0.99
# Read last 1 0 1 0.01 0 0.09 0
# Read next 13.52M 0 932.16k 113.44k 345.04k 196.76k 0
# Read prev 353.42k 0 353.42k 2.90k 0 31.11k 0
# Read rnd 0 0 0 0 0 0 0
# Read rnd nex 16.32M 0 353.76k 136.99k 345.04k 156.66k 36.57k
# Sort merge p 24 0 4 0.20 0 0.84 0
# Sort range c 0 0 0 0 0 0 0
# Sort rows 1.18k 0 88 9.94 31.70 15.54 0
# Sort scan co 48 0 1 0.39 0.99 0.48 0
# Profile
# Rank Query ID Response time Calls R/Call V/M
# ==== =================================== ============= ===== ======= ===
# 1 0x2930BB4A8AE2B11C8483B8A25C4CB95D 52.4226 21.4% 42 1.2482 0.03 SELECT orders order_details
# 2 0x6DCA79447ACE32613FE4C600ED1F1D4D 51.1631 20.9% 5 10.2326 0.17 SELECT user_details
# 3 0x437F2791F07CC1D5A8CE70B8E8FEC9F0 30.1331 12.3% 15 2.0089 0.01 SELECT orders
# 4 0xB0DA8CE70B8EC4B90D2BD4DA83ECC9F0 26.4805 10.8% 5 5.2961 0.66 SELECT orders order_details
# 5 0x78E2578A25C4CB95737F442F3D3CC9F0 15.0265 6.1% 5 3.0053 0.03 SELECT users
# 6 0x14F510BD1C4C139C6510AF6BB56367C9 10.6621 4.4% 5 2.1324 0.01 SELECT order_details
# 7 0x5DDG83F1FE8C8B7F67D7F260BCAB67C9 10.5025 4.3% 5 2.1005 0.05 SELECT users
# 8 0x74E03E26F2A9C5EEAFB67C9F30F09E6C 8.9099 3.6% 6 1.4850 0.01 SELECT users user_details
# 9 0xC9E0570F5E8AADB5A7E49E594AF9E6CE 7.3297 3.0% 5 1.4659 0.00 SELECT orders order_details
# 10 0x00487BA629A2F8197BE49E594AF2AAF7 5.8709 2.4% 5 1.1742 0.00 SELECT orders
# 11 0xF9D39C7EB32F698B9E6CDEADFAAFCAB6 5.8167 2.4% 5 1.1633 0.00 SELECT users user_details
# 12 0x8D224827A8933B9A51D7F260BCAB698F 5.7548 2.4% 5 1.1510 0.00 SELECT orders
# 13 0xC4FCD216421D20756C5EA7CED3ADC5EA 5.1070 2.1% 5 1.0214 0.00 SELECT users user_details
# MISC 0xMISC 9.3149 3.8% 9 1.0350 0.0 <4 ITEMS>
# Query 1: 0.00 QPS, 0.00x concurrency, ID 0x2930BB4A8AE2B11C8483B8A25C4CB95D at byte 926647
# This item is included in the report because it matches --limit.
# Scores: V/M = 0.03
# Time range: 2023-01-02T04:11:30 to 2023-01-02T11:34:21
# Attribute pct total min max avg 95% stddev median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count 34 42
# Exec time 21 52s 1s 2s 1s 2s 200ms 1s
# Lock time 78 21ms 106us 7ms 507us 1ms 1ms 159us
# Rows sent 0 27 0 3 0.64 1.96 0.95 0
# Rows examine 47 14.50M 353.46k 353.82k 353.62k 345.04k 0 345.04k
# Bytes sent 0 0 0 0 0 0 0 0
# Query size 97 817.44k 5.80k 150.12k 19.46k 97.04k 35.96k 6.31k
# Bytes receiv 0 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0 0
# Errno 0 0 0 0 0 0 0 0
# Read first 51 42 1 1 1 1 0 1
# Read key 0 1008 8 95 24 33.28 13.58 26.08
# Read last 0 0 0 0 0 0 0 0
# Read next 0 0 0 0 0 0 0 0
# Read prev 0 0 0 0 0 0 0 0
# Read rnd 0 0 0 0 0 0 0 0
# Read rnd nex 88 14.50M 353.44k 353.76k 353.58k 345.04k 0 345.04k
# Sort merge p 0 0 0 0 0 0 0 0
# Sort range c 0 0 0 0 0 0 0 0
# Sort rows 75 913 4 88 21.74 31.70 13.56 24.84
# Sort scan co 87 42 1 1 1 1 0 1
# String:
# Databases db_commerce
# End 2023-01-02... (1/2%), 2023-01-02... (1/2%)... 40 more
# Hosts 10.128.93.211 (6/14%), 10.128.94.172 (5/11%)... 17 more
# Start 2023-01-02... (1/2%), 2023-01-02... (1/2%)... 40 more
# Users group
# Query_time distribution
# 1us
# 10us
# 100us
# 1ms
# 10ms
# 100ms
# 1s ################################################################
# 10s+
# Tables
# ... 밑에 더 있고.. 쿼리마다 전부 보여줌
https://severalnines.com/blog/analyzing-your-sql-workload-using-pt-query-digest/ 여기 설명 잘돼있음 필요할 때 찾아보면 좋을 듯 합니다
검색해보니, MySQL 튜닝과 관련해서 많이 언급되는 것 같다. TOP10 쿼리를 뽑아 mail로 첨부파일 형식으로 주에 한번씩 보낸 블로그 포스트
더 정확하고 자세한 사용법은 Percona 공식문서 에서 확인