기본 콘텐츠로 건너뛰기

월99만엔 TiDB프로젝트 2주째. 기존 디비의 튜닝




2주 째에 들어왔습니다.
 업무시간 8시간 중에 거의 5~6시간은 화상회의를 켜놓고 하게 되네요.
 너무 지치는군요..
 지난 번 프로젝트가 너무 널럴해서 그랬나봐요..^^;;;

 한국과 조금 다른 것은
 모두들 발언을 신중히 하기 때문에 말은 많지 않으나,
 그 사람들의 말이 정말 이런 이유인지 아니면 다른 원인으로 인한 것인지를 찾는데 신경을 쓰다보니
 더 빨리 피곤해지는데요..

 지금은 TiDB 잘한다는 사람이 앞단의 교통정리를 헤주고 있으니 잘 하겠죠.
 정말 잘할지는 모르겠으나 월권은 귀찮으니.. 하다가 폭탄 만들면 프로젝트를 뜨는 걸로..
 SES는 SI와는 달리 프로젝트가 맘에 안들면 언제든 뜰 수 있는게 부담 없어 좋습니다.
 리더를 해도 내가 합당한 이유를 우리쪽 영업에게 이야기 하면 빼주거든요..
 요즘처럼 사람이 부족한 시기는 고를게 많아서
 아무리 비수기라 해도 사람이 부족해 뒤늦게 가격 올려서 구인 하는 경우도 있어요.
 
 첫주엔 27분기 총회가 있어서 참여를…
 잊어먹고, 나중에 녹화방송을 봤습니다.
 사원은 지금 마구 뽑아서 200명이 되었는데..
3년 전에 수십억엔 매출이 2년전에 850억, 작년에 1850억엔이었네요..
 레드오션이라는 화장품 시장에서 스타트업이 이 정도 수직상승이 가능…하네요..

 여기 정사원들은 나중에 스톡 받겠네요…좋겠다…^^;;;

 그건 그렇고..
 이번주에 기억나는게 두 가지 있었는데요..
 하나는 sql server의 리플리케이션이 끊어져 다시 걸어달라는 내용이었습니다.

 얘네들은 부하가 너무 커져서 8대의 리플리케이션용 디스트리뷰터 조차 따로 두어서 마스터의 부하를 최소한으로 운영한건 좋었는데.. 마스터의 HA구성으로 대기용 마스터에서 가져오면 될 걸 액티브 마스터에서 가져가는 방식이네요.
  레플리카는 보통 5대가 최적이고 그 이상 늘리면 데이터 동기 타이밍에 지연이 생기기 시적합니다. 그렇다고 8대는 안되요가 아니고, 영점 몇 초 정도 동기화가 딜레이 되는거라 서비스 특성상 그 정도는 괜찮다면 문잰 없습니다. 그리고 마스터를 ha구성한 게 있는데, 대기용 마스터는 아무것도 안하고 놀고 있고,
 액티브 마스터의 장애가 터져서 대기중인 ha용 마스터가 액티브 되면 모든 리플리케이션이 끊어지게 되죠.
 그걸 처음부터 대기용 마스터에서 리플리케이션해 갔으면 한 쿠션 더 먹어서 영점 몇초 더 딜레이는 되지만, 정애시 복구가 쉽게 되고, 마스터의 부하가 더 줄어들겠죠…

 암튼 이런 구성에서 일부 리플리케이션이 끊어져서 복구해 달랍니다.
 리플리케이션 설정도 무지 많네요…
  복구하는데 저야 아직 계정도 없어서 구경했는데요..
 보니까 누군가 레플리카에 업데이트를 쳐버렸네요..
 그래놓곤 갑자기 끊겼다고 복구해달라고 모른척 하는 개발팀…

MySQL엔 read only 옵션이 있어서 이런 식으로 레플리카에 업뎃을 못하게 막아서 끊기지 못하게 하는 기능이 있는데요.
Sql server엔 없던 거 같네요. 혹시 제 정보가 잘못되었으면 코멘트 부탁합니다. ^^;;

 인서트로 데이터가 깨졌으면 레플리카의 추가된 행만 삭제하고 재시도 하면 되는데,
 이번건 업뎃이라 불가능 합니다. 
 그래서 재초기화를 들어갔는데요

 마스터와 레플리카의 인덱스 구조가 달라서 미리 인덱스와 뷰를 백업합니다.
 그리고 재초기화로 다시 스냅샷 찍고 리플리케이션이 돌아온 상태에서 인덱스와 뷰를 추가 했지요.
 잘 모르시는 분들을 위해 설명 드리자면,
 마스터는 업데이트가 많기 때문에 인덱스를 촤소한으로 업뎃용 쿼리에 맞춥니다.
 레플리카는 그 목적에 따라서 각각 또는 전체의 인덱스룰 최대한으로 걸어서 select속도를 높이는게 일반적입니다.

 이렇게 해서 복구 완료 연락을 했는데요..

 갑자기 개발팀에서 noexpand 라는 힌트에러를 보여주면서 갑자기 쿼리에어 뜬다고 연락이 왔습니다.
 아무도 noexpand 힌트 에러의 의미를 모르고 있더라구요.
 리플리케이션 작업은 작업자가 수순서를 보면서 따라한거라 문제가 없었구요..

 그렇게 아무도 못찾고 있어서 제가 조금 찾아봤죠..
Noexpand라는 이름의 인덱스를 만들 이유가 없는데, 시스템 인덱스 걑은걸까?
 하고 찾아봤더니 indexed view로 인해 만들어진 view전용 앤덱스를 실데이터와 정합성 체크를 하지않고 이미 만들어진 인덱스를 이용하겠다는 옵션으로,
, 인덱스를 자주 생성하지 않게 하기 위한 옵션이었습니다.
 어? Indexed view는 엔터프라이즈나 데이터센터 에디션 전용아냐?
 하고 찾아보니 여긴 development edition이었군요…

 뭐 개발 서버니까 하면서 indexed view에 잡힌 인덱스를 봤더니 하나도 안잡혀 있었습니다.
 그래서 마스터에 접속해서 indexed view에 걸린 ibdex만 생성하는 쿼리를 만들어서 실행했습니다.
 이건 툴에서 지원을 안해주어서 chatgpt의 힘을 빌려서 indexed view에 설정된 index만 생성하는 쿼리를 만들어 달라고 하니까 이쁘게 만들어주네요..
Db팀은 내가 못찾아줬으면 묘칠 찾아 헤멨을 거라면서 고마워 하네요…

 아직 전 여기 시스템을 모르는데, 이 회사에 있는 사람들은 indexed view는 어디선가 찾아서 쓰면서도 이게 리플리케이션이 끊겼을 때 어떤 영향이 있는지 몰랐나 보네요… 복구 매뉴얼에도 없었던걸보니..

 이렇게 한 건 하고…

 며칠 뒤에는 기존엔 57초 걸렸던 쿼리가 지금음 5분 타임아웃이 되도록 안끝난다고 봐달라는 내용이었습니다.
 뭐? 쿼리는 1초 내에 전부 표시해야 하는게 아닌거?
 농담이구요..
 오래 걸린 쿼리를 봤더니 join 세 개짜리 엄청 단순한 테이블이었습니다.
 그런데 결제 이력 테이블인 purchase 테이블에서 create-date 나 update-date 가 특정 기간인 결제 이력을 가져오는거라 create-date와 update-date 에 기간을 넣고 or를 했네요.
 이렇게 하면 테이블 풀스캔을 한다는 것을 아시는지요?
 여기 개발자와 dbre팀은 풀스캔 한다는 사실을 모르고 쿼리 플랜에선 pk를 터고 있더고 보여주더랍니다.
 여긴 aws aurora였기 때문에 쿼리 플랜의 표시가 조금 다른데,
Aws aurora는 ref로 pk를 타는걸로 표시를 하네요..

Pk에 create-date도 없고 update-date도 없는데 왜 이럴까요?
 모든 Rdbms엔진이 같은 소스가 아니기 때문에 표현도 다른 듯 한데요…
Aurora의 엔진은 인덱스에 없는건 표시 안하는듯 합니다.
 원래라면 table full scan으로 create-date를 잡고 그에대한 pk로 로우를 접았어야 하는데,
 뒤의 pk로 로우를 잡는것만 표시하네요..

 그러니 잘 모르는 사람들이 보면 풀스캔이 없어 보이는 거 같습니다.
Aurora쓰시는 분은 주의를 하셔야겠어요..

 일단 or는 풀어주어야 인덱스를 타기 때문에 쿼리를 찢어서 풀었습니다

Select purchase-id
From purchase
Where create-date between a and b

Union all 

Select purchase-id
From purchase
Where update-date between a and b

 여기서 
Where in 을 쓰는 방법과
Self Join 후에 group by를 쓰는 법도 있습니다.

 저 결과가 1000라인 이하라면 where in이 빠를수 있구요,
 이번 쿼리는 유니크한 purchase-id만 가쟈오면 되므로
 그냥 union all한 뒤에 이걸 distinct를 해버렸습니다.

 몇만 밖에 안되는 라인이라 인덱스 없는 메모리 처리만으로 문제 없을 것 같아서 이렇게 했지요.
Union all을 하면 그 결과 데이터는 메모리에 올라간 새로운 테이블 같이 되므로 기존 인덱스는 사용할 수 없어 무조건 full scan으로 가게 됩니다.

 그렇게 바꾸고 쿼리를 실행해 봤죠…
 몇 초가 지났는데 표시 안됨…
 뭐가 이성하네 하면서 인덱스를 보니까

Create-date나 update-date모두 인덱스에 없었습니다.
 이 쿼리를 짠 사람은 어짜피 or 라서 인덱스 안탈 줄 알고 안만들었나 봅니다…

 인덱스를 안타는 쿼리는 인덱스를 타도록 먼들었어야 하는데 첨부터 포기를 했군요..

 인댁스를 두 개 만들어 줬습니다.
 그리고 실행.

 0.5초..
 여러번 실행…
0.4~0.5초 사이에서 제대로 결과가 나옵니다.
 이걸 왜 첨부터 57초를 기다리고 있던건지…

 이렇게 보내주자 개발팀에서 다시 문의가 왔습니다.
 기존 쿼리는 57초였는데 왜 지금은 5분이 지나도록 쿼리가 실행 안되는지를 규명해 달라구요..
 내가 해결해 줬으니 끝난거아녀?
 내가 해준게 개발팀의 자존심을 건드렸나?
 왜 이 원인을 알고싶어 하지?
 기존 db 담당자는 익숙한지
 속도가 빨랐다고 주장했던 시기의 백업을 다른 인스턴스에 복구해서
 같은 쿼리를 돌려서 시간 비교하고,
 데이터양을 비교한 자료와 제 코멘트를 같이 보여주어서 일단락 했습니다.

 제가 인덱스를 안타는 테이블은 1000줄 늘어난 정도로도 수십초도 차이날 수 있다고 했거든요..
 실제로 다른 프로젝트에서도  그런적이 은근히 있습니다.
 별로 안바뀌는 테이블이니까 하고 pk없이 만들지 마시고, 꼭 pk와 인덱스는 적절하게 걸어 놓으세요.

 이렇게 일단락을 했나 싶었는데 
 갑자기 개발팀에서 느려진 쿼리를 모두 정리해서 튜닝 요청이 왔네요…
 그랬더니 db담당자가 짜증났는지,
 우리가 첨부터 해줄필요 없다고, 하나씩 해결 못하는거 가져오라고 하고 리젝을 해버렸답니다.

 음… 또 저때문에 파란이 시적되는걸까요?

 한국이나 일본이나
 제가 들어간 프로젝트들 중에는 제가 튜닝 좀 해주면 갑자기 분위기가 쎄 해지는 경우가 있는거 같네요..
 좀 덜 튜닝 해야 하려나요?

 이렇게 또 제 자랑 한 번 해봤습니다.


giip :: Control all Robots and Devices! Free inter-RPA orchestration tool! https://giipasp.azurewebsites.net/

댓글

이 블로그의 인기 게시물

Alter table 에서 modify 와 change 의 차이 :: SQL Server

두 개의 차이를 모르는 경우가 많아서 정리합니다.  modify는 필드의 속성값을 바꿀때 사용하구요.. change는 필드명을 바꿀떄 사용합니다.  alter table tbbs modify bNote varchar(2000) NULL; alter table tbbs change bNoteOrg bNoteNew varchar(2000) NULL; change에는 원래 필드와 바꾸고 싶은 필드명을 넣어서 필드명을 바꾸는 것이죠~ 더 많은 SQL Server 팁을 보려면  https://github.com/LowyShin/KnowledgeBase/tree/master/wiki/SQL-Server giip :: Control all Robots and Devices! Free inter-RPA orchestration tool! https://giipasp.azurewebsites.net/

책에서는 안 알려주는 대규모 트래픽을 위한 설계

음성 버전 :  https://www.youtube.com/watch?v=ZZlW6diG_XM 대규모 트래픽을 커버하는 첫 페이지 만드는 법..  보통 DB를 연결할 때 대규모 설계는 어떻게 하시나요?  잘 만들었다는 전제 하에 동접 3000명 이하는  어떤 DBMS를 사용해도 문제 없이 돌아갑니다.  여기서 이미 터졌다면 이 콘텐츠를 보기 전에 DB의 기초부터 보셔야 합니다.  아.. 개발 코드가 터졌다구요? 그럼 개발자를 때리셔야지요..  만약 3000명을 넘겼다면? 이제 Write/Read를 분리해서  1 CRUD + n개의 READ Replica를 만들겠죠?  보통 Read Replica는 5개가 최대라고 보시면 됩니다.  누가 연구한 자료가 있었는데...  6번째 레플리카를 만든느 순간 마스터가 되는 서버의 효율 저하 때문에  5번째에서 6번쨰로 올릴때의 성능이 급격히 줄어든다는 연구 결과가 있습니다.  때문에 Azure에서도 replica설정할 때 5대까지 밖에 설정 못하게 되어 있지요.  유저의 행동 패턴에 따라 다르긴 하지만,  1 CRUD + 5 Read Replica의 경우 동접 15000명 정도는 커버 합니다.  즉, 동접 15000명 에서 다시 터져서 저를 부르는 경우가 많지요..  이 때부터는  회원 DB, 게시판DB, 서비스DB, 과금 DB 등등 으로 성격, 서로의 연관도에 따라 나누기 시작합니다.  물리적으로 DB가 나눠지면 Join을 못하거나 Linked Table또는 LinkDB등의 연결자를 이용해서 JOIN이 되기도 합니다.  그에 따라 성능 차이가 생기지만 가장 중요한 포인트는  서로 다른 물리적 테이블의 JOIN은 인덱스를 타지 않는다!  라는 것입니다. 즉, JOIN할 테이블들을 최소한으로 만든 뒤에 JOIN을 걸지 않으면 NoSQL처럼 느려터져 죽습니다.  양이 많은 DB에서 양이 적은 테이블을 가져와서 JOIN을 해야겠지요..  이렇게 해서 동접 10만명까지 커버를 했다 칩시다.  여기서 일반적인 동접의 기준도 서비스마

BI의 궁극판! Apache Drill을 써보자!

사실 Apache Drill 은 BI(Business Intelligence)라고 부르는 것 보다는 단순 데이터 연결 엔진이다. https://drill.apache.org/ 하지만 내가 왜 극찬을 하느냐면.. DBA로서 항상 문제가 되어왔던게, 이기종 데이터의 변환이나 처리였다. 포맷을 맞추는데 엄청난 시간이 걸리고, 데이터 임포트 실패가 무수하게 나고.. 한 번 잘못 데이터를 추출하면 다시 조정, 변환, 추출하는데 시간이 많이 걸린다. 그런데! Apache Drill은 그냥 RDB를 CSV랑 연결해서 조인해서 통계를 낼 수 있다. 그것도 표준 SQL을 사용하여! 예를 들어, CSV의 세 번째 컬럼이 price 이고, 물건의 판매이력을 PG사에서 CSV로 출력 받았다. 우리 DB와의 검증을 위해서는 수동으로 Import를 한 뒤에 포맷이 안맞아 잘리는 데이터가 있다면 다시 맞춰주고, 재 임포트를 수십 번, 그리고 나서 겨우 들어간 데이터를 조인하여 빠진 데이터를 분간한다. 숫자가 적다면 개발자가 개발로 처리할 수도 있지만, 건수가 하루에 300만건 짜리라면.. 한 달 온 파일은 9천만 건이다. 프로그램으로 고작 처리하는 것이 초당 500건. 거의 20만초, 에러 없이 약 56시간.. 에러가 생기면 다시 56시간.. ㅠㅡㅠ 이런게 현실이기 때문에 쿼리 말고는 방법이 없다. apache drill 의 진면목을 보자! 이번에는 좀 범용 적인 MySQL DB와 붙여 보자. . 난 이번에는 Mac에서 작업을 했기 때문에 그냥 다운 받아서 풀었음.. https://drill.apache.org/download/ 여기서 자기 OS에 맞는 버전을 받아서 설치하시길.. 압축을 풀고 나면 MySQL 커넥터를 붙여야 한다. https://dev.mysql.com/downloads/connector/j/5.1.html 여기서 다운로드 이런 커넥터 들을 붙일 때마다 콘피그를 수정해 줘야 하지만, 몇 번만