두 집합의 원소들 간의 대응관계일때를 맵핑이라고 하며,
Many-To-Many 맵핑 일 경우에,
보통의 개발자들은 맵핑 테이블을 만들어서 관리하기를 원합니다.
그리고 이 N:N 구조에서 어느쪽의 데이터가 더 많은 지를 우선 파악한 후, N:N 이 실제 2:5의 비율이 될지 2:13 의 비율이 될지를 파악한 후, 실제 DB를 SELECT 할때, 해당 맵핑 테이블을 얼만큼 효율적으로 검색하며 사용하질에 대한 기술적으로 많은 고민이 필요합니다.
예를 들어,
A테이블에 신규로 등록되는 페이지와,
B테이블에 신규로 등록되는 페이지가 있을 때,
각각의 페이지에는 A가 등록될때 자동으로 B와 연결된 데이터들이 맵핑테이블에 등록이 되게 됩니다.
반대로 B 라는 테이블의 데이터가 등록될 때는 자동으로 B와 연결된 A의 데이터들이 맵핑 테이블에 등록되게 됩니다.
좀더 쉽게 설명하면,
A테이블에 속한 데이터를 화면에 등록하는 페이지에서는,
A테이블의 데이터가 한개 등록될 때 최대 3만건의 B테이블의 데이터가 1:N 형태로 맵핑테이블에 등록이 되어야합니다.
B테이블에 속한 데이터가 화면에서 한개 등록될 떄 A군의 속한 데이터가 8천개 정도 자동으로 맵핑되어야합니다.
이는 A군의 유효한 데이터가 8천개, B군의 유효한 데이터가 3만개 로 대략적으로 1:4 의 비율로 맵핑데이터가 필요하다는 말이 됩니다.
해당 데이터 B테이블의 행이 어떠한 사유에 의해 유효한 데이터가 5만건이 늘어나게 된다면,
A테이블의 신규데이터가 새롭게 등록될대 3만건이 등록되던 데이터가, 8만건의 데이터가 등록되어야합니다.
그렇게 되면, 기존 1:4 로 등록되는 구조가 1:8 이상의 구조로 변경되게 됩니다.
그렇다면 5만건의 유효한 데이터를 그대로 유지할지, 안할지에 대한 판단이 필요합니다.
현재 Auto Increment 가 1048719446 (10억)까지 증가된 상황에서 테이블 용량은 110GB를 차지하고 있는 상황이며,
B군이 2.5배가 된다는 것은 직관적으로 20억 및 테이블 용량이 220GB가 된다는 의미와 같습니다.
그 상황에서 개발자의 접속권한으로는 전체 row 갯수를 파악하지 못할 정도로 많은 데이터이긴한데,
인덱스가 가능한 PK값들을 단독으로 수행했을 때는 0.0016초 정도안에 응답하고 있습니다.
해당 데이터를 full scan 하여 검색하는하는 로직이 존재하지 않기 때문에 개발적으로는 이슈될 사항은 없는 상태입니다.
그렇다면, 현재 상황에서 기존의 테이블을 그대로 사용해야할 것인지, 분리해서 별도의 테이블을 구축해야할지 결정해야하는데,
이 경우, 테이블을 분리할 것인가 하지 말아야할 것인가는 DBA의 판단으로 넘겨야할 거 같습니다.
index size 라든가, 얼마만큼 data block 이 깨져있는지 등에 대해서는 개발자로서 접근권한이 없기 때문에 해당 근거자료로는 부족하기 때문입니다.
분리한다는 작업은 신규개발 및 일정을 산정하면 될 부분이고,
테이블을 분리하여 성능 개선이 된다면, 혹은 기존 개발된 부분에 무리가 가는 부분이 조금이라도 있다면 분리하는 것이 맞습니다.
테이블의 수직분할은 컬럼을 기준으로 테이블을 분리하는 것을 의미하고 수평분할은 Row를 기준으로 테이블을 분리하는 것을 의미합니다.
개발자로서 개발을 하면서 수직분할 작업은 했지만, 수평분할로 개발한 적은 단 한번도 없긴 하지만,
만약 해야한다면 이번이 처음으로 보입니다.
기본적으로 수직 분할을 하는 이유는 컬럼이 계속 추가 되면, 테이블의 컬럼 수가 많을수록 I/O에 대한 부하가 걸리고,
수평 분할을 하는 이유는 테이블의 Row 수가 많을수록 Index에 대한 부하가 따르기 때문입니다.
만약 한 테이블에 수많은 컬럼을 계속 추가하게 되어 존재하게 된다면,
디스크의 여러 블록에 데이터가 저장되므로 I/O 성능 저하를 불러올 수 있게 되어, 이렇게 컬럼이 많아지면 로우 체이닝과 로우 마이그레이션이 많아져서 성능이 저하되게 됩니다.
로우 체이닝(Row Chaining)
길이가 너무 커서 하나의 블록에 저장되지 못하고 다수의 블록에 나누어져 저장
로우 마이그레이션(Row Migration)
수정된 데이터를 해당 데이터 블록에 저장하지 못하고 다른 블록의 빈 공간에 저장
로우 체이닝과 로우 마이그레이션이 발생하여 많은 블록을 사용하게 되면, 불필요한 I/O가 발생하여 성능이 저하됩니다.
많은 I/O 발생은 성능에 직접적인 영향을 주므로 매우 중요한 문제며, 데이터에 대한 범위 검색을 할 경우 더 많은 I/O를 유발하므로 성능 저하를 일으킵니다.
수많은 컬럼을 동시에 조회하는 경우는 드물기 때문에,
각각의 조회 조건에 맞게 이용되는 컬럼들로 그룹을 묶어서 테이블 분할을 검토하게 됩니다. 하지만, 지금 상황은 컬럼이 추가되는 것이 아니라, row가 늘어나므로,
조회나 처리에 대한 분산을 가능하게 하는 칼럼들을 기준으로 테이블을 분리하여 성능 개선에 큰 도움을 노리는 것이 아닙니다.
그래서 만약 개발을 하게 되면, 수평분할을 고려해야합니다.
수평분할(Horizontal Partitioning)은 Row를 기준으로 테이블을 분리합니다.
만약 한 테이블에 대량의 데이터가 존재하고 트랜잭션이 몰린다면 성능 저하를 피하기 어렵습니다.
대량의 데이터가 하나의 테이블에 있으면 인덱스 정보 생성 시 부하가 커집니다.
삭제와 등록을 반복하게 되면, 인덱스를 찾아가는 깊이(Depth)가 깊어지게 되고 인덱스의 크기가 커질수록 더 많은 성능 저하를 불러오게됩니다.
조회하는 성능보다 데이터를 처리하는 성능에 더 큰 영향을 미치게 되고,
이런 경우 트랜잭션이 분산 처리될 수 있도록 테이블 수평분할을 검토하게 됩니다.
데이터 건수가 수천만 건을 넘고 처리가 많이 일어난다면 성능을 제대로 발휘할 수 없게 됩니다.
현재 A군과 B군의 맵핑테이블이 바로 그러합니다.
이때 논리적으로는 같은 테이블이지만 물리적으로 서로 다른 여러 개의 테이블 스페이스에 나누어서 저장하는 파티션 방법을 이용하면 성능 개선에 큰 도움이 됩니다.
파티션 종류로는 Range, Hash, List, Composite 파티션이 있으며,
가장 널리 이용하는 Range 파티션은 주로 날짜를 기준으로 하는 경우가 많습니다.
Range의 경우 데이터가 균등하게 나누어져서 비슷한 성능 개선을 보장하며, 보관 주기에 따라서 필요치 않는 데이터도 쉽게 지우고 관리할 수 있지만,
A군과 B군의 등록날짜들은 제 각각 이므로, 만약 하게 된다면 Hash와 List 형태가 더 올바릅니다.
이러한 부분들에 대해 DBA와 논의할 준비를 하는 중,
DBA 의 경우,
A군과 B군의 테이블에 대한 맵핑 테이블 뿐만 아니라, A군, B군, C군, D군.
이 모든 것을 다 바꾸고, 변경해야지만 이 성능저하를 피할 수 있다.
라는 논리를 펼쳤으며, 해당 논리의 근거에 대해서는 샤머니즘적인 성향이 강했습니다. 그로인해, 더 이상의 이야기는 진행할 수 없었으며, 5만건의 데이터는 기존 테이블에 INSERT 되게 되었습니다.
물론, A테이블과 B테이블의 데이터가 예전 데이터이므로, 변경을 하여야하는 것은 맞습니다.
그러나, 4년이 넘게 유지된 모든 테이블 구성을 바꿔야할 경우, 개발자 입장에선 그로인한 모든 화면 및 백도어로직이 변경되게 됩니다.
그러면 그 근거자료가 존재해야하며,
논의하기 위해 준비했던 모든 자료들, 해당 테이블의 용량, INDEX구조등에 벗어난 뜬금없는 C군과 B군의 데이터 구조까지 변경해야한다면,
그 개발 범위를 다시 산정해야합니다.
그런데 그 논리가 그냥 ‘감’, 인디언 기우제 논리라면, 해당 개발건은 고착화 될 수 밖에 없으며,
그리고 그렇게 되었기 때문에 결국 유지보수로 인하여 최소화된 추가개발을 진행할 수 밖에 없게 되며,
개발로직이 처리할 업무를 사람이 처리하게 될 것입니다.