8. View가 무엇이고, 언제 사용할 수 있나요?
뷰는 하나 이상의 테이블이나 다른 뷰의 데이터를 볼 수 있게 하는 데이터베이스 객체이자 하나 이상의 기본 테이블로부터 유도된, 고유한 이름을 가지는 가상 테이블입니다.
뷰의 사용 목적은 아래와 같습니다.
- 주로 사용자에게 접근이 허용된 자료만을 제한적으로 보여주기 위해 사용됩니다.
- 여러 조인으로 연결된 복잡한 쿼리를 하나의 데이터로 묶어서 제공하기 위한 목적
그렇다면, View의 값을 수정해도 실제 테이블에는 반영되지 않나요?
일반적으로 수정을 허용하지 않고 수정이 가능하도록 설정하지 않는 것을 추천합니다. 특수한 상황에서 효과적으로 데이터 공개하기 위함이기 때문입니다.
그렇지만 뷰 생성 시 수정이 가능하도록 설정이 가능한 경우가 있습니다, 하지만 이 또한 복합뷰가 아니며 WHERE 절에 속한 데이터의 경우에 수정이 가능합니다.
뷰는 SQL에 대한 값을 저장한다고 볼 수 있는데, 뷰에 대한 접근을 할 때 해당 쿼리문이 수행하는건지 아니면 다른 방식으로 동작할까요?
- 뷰는 실시간으로 데이터를 조회해온다.
수정이 가능한 뷰? -> 구체화 뷰, 물리적인 뷰 -> 조금 더 피지컬하다? 물리적으로 존재하는 쪽에 가깝다. 동기화를 진행한다.
9. DB Join이 무엇인지 설명하고, 각각의 종류에 대해 설명해 주세요.
DB Join은 데이터베이스에서 하나 이상의 테이블로부터 데이터를 결합하는 작업을 말합니다. 데이터베이스에서 테이블은 관련된 정보를 저장하는 구조이며, 때로는 여러 테이블을 조합하여 더 유용한 정보를 얻기 위해 Join 연산이 필요합니다. Join 연산을 사용하면 관련 있는 데이터를 결합하여 하나의 결과 집합으로 생성할 수 있습니다.
- Inner Join:
- Inner Join은 두 개 이상의 테이블에서 일치하는 행만을 선택하여 결합합니다.
- 이 때 선택된 행은 조인 조건을 충족하는 행들로 제한됩니다.
- Inner Join을 사용하면 두 테이블 사이의 공통된 값을 가진 행만 결과에 포함됩니다.
- Left (Outer) Join:
- Left Join은 왼쪽 테이블의 모든 행을 포함하면서 오른쪽 테이블과 일치하는 행을 결합합니다.
- 만약 오른쪽 테이블에 일치하는 값이 없다면, 결과는 해당 행에 대해 NULL 값을 가질 수 있습니다.
- Left Join은 주로 왼쪽 테이블의 데이터를 기준으로 오른쪽 테이블의 데이터를 추가할 때 사용됩니다.
- Right (Outer) Join:
- Right Join은 Left Join의 반대 개념으로, 오른쪽 테이블의 모든 행을 포함하면서 왼쪽 테이블과 일치하는 행을 결합합니다.
- 마찬가지로, 왼쪽 테이블에 일치하는 값이 없는 경우 결과에는 NULL 값이 포함될 수 있습니다.
- Full (Outer) Join:
- Full Join은 두 테이블의 모든 행을 포함하면서 일치하는 행을 결합합니다.
- Full Join을 사용하면 양쪽 테이블에 일치하는 값이 없는 경우에도 결과에 해당 행이 포함됩니다.
- Self Join:
- Self Join은 하나의 테이블 내에서 자기 자신과 결합하는 작업을 의미합니다.
- 이는 테이블 내에서 서로 다른 레코드를 비교하거나 계층적 데이터를 다룰 때 유용할 수 있습니다.
사실, JOIN은 상당한 시간이 걸릴 수 있기에 내부적으로 다양한 구현 방식을 사용하고 있습니다. 그 예시에 대해 설명해 주세요.
- Nested Loop Join
- NL Join 이라고 하며 하나의 테이블의 각 행을 다른 테이블과 비교하는 방식으로 작동합니다.
- 이중 루프와 비슷한 방식으로 동작한다고 생각할 수 있습니다.
- 인덱스 스캔을 통해 각 행에 대한 후행 테이블을 조회하는 것이 성능 상 중요한 부분입니다. 만약 인덱스가 없는 경우 풀 스캔으로 동작하기 때문에 비효율적입니다.
- 가장 기본적인 알고리즘 중 하나입니다.
- Hash Join
- 키 값을 기준으로 해쉬 테이블에 저장하는 방식입니다.
- 연산을 진행하는 과정은 빠르지만 많은 메모리를 필요하게 됩니다.
- 정렬에 대한 고려를 하지 않는 것이 특징입니다.
- Sort-Merge Join
- 선행 테이블에서 조건에 만족하는 행들과 후행 테이블에서 조건에 만족하는 행들을 추출해 정렬한 다음, 순차적으로 조인하는 방식입니다.
- 조인 연산 과정에서 인덱스가 사용되지 않는 경우 사용이 가능한 방식입니다.
- 다만 정렬에 대한 비용이 필요하므로 대용량 처리는 불리합니다.
그렇다면 입력한 쿼리에서 어떤 구현 방식을 사용하는지는 어떻게 알 수 있나요?
EXPLAIN
연산을 통해 특정 조인 연산 쿼리의 실행 계획에 대한 정보를 알 수 있습니다.
앞 질문들을 통해 인덱스의 중요성을 알 수 있었는데, 그렇다면 JOIN의 성능도 인덱스의 유무의 영향을 받나요?
- 네, 받습니다. 특히 NL 알고리즘을 사용하는 경우 인덱스가 매우 중요합니다.
- 인덱스를 통해 각 행에 대한 추가 행에 대한 조회 및 접근을 진행한다면 인덱스를 통해 후행 테이블에 빠르게 접근이 가능합니다.
- 하지만 인덱스가 아닌 컬럼으로 조인을 할 경우, 후행 테이블의 모든 행을 스캔하면서 찾아가야 하므로 동작이 느려질 수 있습니다.
- 이는 조인 연산이 복잡해지면 복잡할수록 성능의 저하는 심해집니다.
10. B-Tree와 B+Tree에 대해 설명해 주세요.
- B-Tree
- Balance-Tree 라고 부르며 언제나 균형된 형태로 유지됩니다.
- 각 노드는 메모리를 가지고 키와 데이터를 가지고 있습니다.
- 데이터 추가, 삭제 시 전체 데이터에 대한 정렬이 필요합니다.
- B+Tree
- 실제 데이터는 리프 노드에만 저장되어 있으며 그 외 브랜치 노드에는 키만 저장되어 있습니다.
- 이로 인해 각 노드의 남은 메모리가 많아져 더 많은 자식 노드를 가질 수 있습니다. -> 레벨이 낮아지는 장점
- 리프노드끼리는 링크르 리스트로 연결되어 있습니다. -> 데이터 전체가 정렬되어 있다.
그렇다면, B+Tree가 B-Tree에 비해 반드시 좋다고 할 수 있을까요? 그렇지 않다면 어떤 단점이 있을까요?
- 그렇다고 할 수 없습니다.
- B+ Tree는 리프 노드에만 데이터가 저장되어 있으므로 범위 탐색이나 순차 탐색에서 장점을 가집니다.
- B- Tree는 특정 노드에 대한 접근 시 장점을 가질 수 있습니다.
DB에서 RBT를 사용하지 않고, B-Tree/B+Tree를 사용하는 이유가 있을까요?
- 많은 이유들이 있을 수 있지만 가장 큰 이유는 데이터 저장 시 키에 대한 정렬을 보장하는지 안하는지 이라 생각합니다.
- RBT의 경우 데이터 저장 및 정렬하는 과정에서 키의 순서를 보장하지 않습니다.
- 하지만 RDBMS는 인덱스를 통해 순차적인 접근을 통한 작업이 많이 발생하기에 RBT의 단점이 부각될 경우가 많을 수 있습니다.
- 이에 반해 B-Tree/B+Tree는 키에 대한 순서를 정렬 과정에서 보장하기 때문에 인덱스로 활용하고 저장하기에 더 적합하다고 할 수 있습니다.
- 그리고 B+Tree의 경우는 특히나 데이터 밀집도가 높아 더욱 더 범위 탐색에 용이한 구조로 저장이 가능합니다.
오름차순으로 정렬된 인덱스가 있다고 할 때, 내림차순 정렬을 시도할 경우 성능이 어떻게 될까요? B-Tree/B+Tree의 구조를 기반으로 설명해 주세요.
- B-Tree의 경우, 가장 오른쪽 리프 노드로 이동해 순차적으로 조회할 수 있습니다.
- B+Tree의 경우, 정렬 순서에 따라 가장 끝단 노드로부터 리스트를 선형 탐색으로 조회할 수 있습니다.
- 이러한 경우에선 B+Tree가 조금 더 효율적인 처리가 가능하다고 생각합니다.
- 다른 고민없이 리스트를 선형 탐색하면 되기 때문입니다.
11. DB Locking에 대해 설명해 주세요.
- 데이터의 일관성과 무결성을 지키기 위해 여러 사용자가 같은 데이터를 동시에 처리하지 못하도록 하는 장치를 락이라고 합니다.
- 락을 통해 멀티 쓰레드 환경에서 성능은 느려질 수 있지만, 데이터의 안정성은 확보할 수 있다는 장점이 있습니다.
- 이를 상황에 맞춰 적절히 락 수준을 선택하고 활용해야 합니다.
Optimistic Lock/Pessimistic Lock에 대해 설명해 주세요.
- Optimistic Locking (낙관적 잠금)
- Optimistic Locking은 충돌이 발생하지 않는다는 가정 하에 커밋하는 과정에서 충돌 문제가 발생하면 그 때만 처리하자라는 의미를 가집니다.
- 데이터 읽기: 사용자는 데이터를 읽어올 때 어떠한 잠금도 설정하지 않습니다. 이 때 데이터베이스는 해당 데이터의 버전 또는 타임스탬프와 같은 메타데이터를 유지합니다.
- 데이터 업데이트: 사용자가 데이터를 수정하려 할 때, 데이터베이스는 해당 데이터의 현재 버전을 확인합니다. 만약 사용자가 읽어온 버전과 일치한다면 업데이트가 허용되고, 그렇지 않으면 업데이트가 거부됩니다.
- Optimistic Locking은 잠금의 사용을 최소화하여 동시성을 높이는 장점이 있습니다. 그러나 업데이트 충돌이 발생하는 경우, 사용자는 다시 시도해야 할 수 있습니다.
- Pessimistic Locking (비관적 잠금)
- Pessimistic Locking은 충돌 문제는 언제는 발생할 수 있으므로 선제적으로 예방 및 대비하자는 의미입니다.
- 데이터 읽기: 사용자가 데이터를 읽을 때, 해당 데이터에 배타적 잠금을 설정합니다. 이로써 다른 사용자는 해당 데이터에 접근하지 못하게 됩니다.
- 데이터 업데이트 또는 반환: 사용자가 데이터 수정을 완료하면 잠금을 해제하거나, 읽기만 했을 경우에도 잠금을 해제합니다.
- Pessimistic Locking은 업데이트 충돌의 가능성을 줄이지만, 잠금이 지속되는 동안에는 다른 사용자들의 접근이 제한되므로 동시성이 낮아질 수 있습니다.
- 두 방식은 각각 장단점을 가지고 있으며, 사용 시나리오에 따라 선택되어야 합니다.
- Optimistic Locking은 충돌이 적은 환경에서 유용하며, Pessimistic Locking은 업데이트 충돌이 빈번한 환경에서 유용합니다.
외에도 공유락, 베타락
12. 트래픽이 높아질 때, DB는 어떻게 관리를 할 수 있을까요?
- 캐싱
- 자주 사용하는 데이터나 조회의 목적으로 주로 복잡한 쿼리문의 데이터의 경우, 캐시를 통해 DB에 접근하지 않도록 할 수 있습니다.
- Redis나 Memcached 같은 플랫폼도 사용할 수 있습니다.
- Replication
- Master에서 INSERT, UPDATE, DELETE에 대한 처리를 담당하고 Slave에서 SELECT에 대한 처리만 담당함으로써 몰리는 트래픽을 분산처리할 수 있습니다.
- 또한 각각 서버를 여러 서버로 준비하고 서버 다운 시 대체함으로써 데이터 유실 및 서버 장애에 대해서도 대비할 수 있습니다.
- 샤딩
- 하나의 테이블에 대한 정보도 여러 샤드에 분활 저장 및 관리함으로써 몰리는 트래픽을 분산시켜줄 수 있습니다.
- 하지만 조인을 통해 여러 테이블의 관계가 복잡해지는 경우, 고려해야 할 사항들이 많아져 오히려 더 복잡해질 수 있습니다.
- 그리고 특정 데이터에 대한 접근이 많다면 샤딩을 하더라도 분산에 대한 장점이 살아난다고 할 수 없습니다.
- 그 외에도
- 비동기 처리, 쿼리 최적화, 정규화 or 비정규화 등을 통해 쿼리문 처리 속도에 대한 성능의 최적화도 가능한 방법들입니다.
13. Schema가 무엇인가요?
스키마는 계획이나 도식(圖式)을 가리키는 영어 낱말이라는 사전적 의미를 가지며 데이터의 구조, 형식, 제약 조건 등을 정의한 것이라 할 수 있습니다.
특히 데이터베이스에선 데이터베이스를 구성하는 것들의 형태, 성질, 관계, 제약 조건등을 정의한 것이 스키마라 할 수 있습니다.
Schema의 3계층에 대해 설명해 주세요.
- 외부 스키마
- 사용자나 다른 어플리케이션에서 접근할 때, 필요한 데이터를 정의한 스키마입니다.
- 뷰가 외부 스키마에 해당한다고 볼 수 있습니다.
- 개념 스키마
- 데이터베이스의 전체적인 논리적 구조를 정의한 스키마입니다.
- 데이터베이스 내에서 데이터가 어떻게 저장되고 관계가 연결되는 지 등의 형태를 정합니다.
- 실제 데이터베이스에 해당합니다.
- 내부 스키마
- 데이터베이스 내에서 실제 데이터가 물리적으로 저장되는 방식과 형태를 정의한 스키마입니다.
- 즉, 물리적인 저장방식과 데이터 관리를 다루는 계층이라 볼 수 있습니다.
- 따지자면 스토리지 엔진과 밀접한 관계가 있습니다.
14. DB의 Connection Pool에 대해 설명해 주세요.
- 통신을 위해서 두 개체가 연결을 맺고, 통신 후 연결을 해제하는 방식을 반복하는 것은 리소스의 낭비가 발생합니다.
- 그래서 일정만큼의 연결 가능 객체 개수를 정하고 미리 생성해둔 다음, 요청이 올 때마다 객체를 사용하고 반납하는 곳이 Connection Pool 입니다.
- Connection Pool을 사용하면 재사용을 통해 리소스의 효율성이 높아질 수 있습니다.
- 만약 사용 가능한 Connection 객체가 없다면 HandOffQueue에 저장되고 차례를 기다리다 연결 가능한 상태에서 Connection이 진행됩니다.
DB와 Client가 Connection을 어떻게 구성하는지 설명해 주세요.
- 사용자가 WAS에 요청
- DB 드라이버에 커넥션 조회 요청
- DB 드라이버가 DB에 TCP/IP 커넥션 연결
- DB에 인증정보 전달
- DB 세션 생성
- DB 커넥션 생성 및 DB 드라이버에 커넥션 반환
- 클라이언트에게 커넥션 반환
Connection Pool이 많을수록 좋다?
- 공식 문서 참고
- 정해진 규칙? 식이 있다.
3 Way-Handshake?
추가로 Connection은 Thread의 개수와 매우 밀접한 관계를 가지고 있습니다. Thread 수에 비해 Connection 수가 적으면 대기시간이 길어지고 성능이 떨어질 수 있으며 반대의 경우는 불필요한 Connection이 메모리를 차지할 수 있기 때문입니다.
그리고 트랜잭션은 하나의 쓰레드 내에서 여러개가 존재할 수 있습니다. 꼭 1대1 관계는 아닙니다. 하나의 요청에 대한 처리를 하나의 쓰레드가 담당하고 이를 위한 DB Connection은 하나로 담당되는 것이 일반적입니다.
15. Table Full Scan, Index Range Scan에 대해 설명해 주세요.
- Table Full Scan : 조건에 맞는 레코드를 찾기 위해 전체 테이블을 스캔하는 방식을 의미합니다.
Index Range Scan : 인덱스가 정렬되어 있다는 점을 활용해 필요한 범위만 스캔하는 방식을 의미합니다.
- 이는 어쩔 수 없이 Index Range Scan이 더 효율적일 수 밖에 없으며 쿼리문을 최적화하는 과정에서도 인덱스의 정확한 활용을 고민해야 하는 이유가 될 수 있습니다.
가끔은 인덱스를 타는 쿼리임에도 Table Full Scan 방식으로 동작하는 경우가 있습니다. 왜 그럴까요?
- 인덱스를 활용하더라도 범위를 줄일 수 있는 힌트를 얻을 수 없는 경우이기 때문입니다.
- 이는 인덱스가 가공된 형태로 조건에 사용될 경우, 인덱스를 통한 범위를 줄여나갈 수 없습니다.
- 예시는 다음과 같습니다.
- PostFix를 비교하는 경우 : 앞의 값에 대한 힌트를 얻을 수 없어 결국 Full Scan을 진행해야 합니다.
- 메소드를 사용하는 경우 : 인덱스를 통해 계산한 값을 가지고 비교해야 하므로 Full Scan이 진행됩니다.