HyunsooZo's TIL logo HyunsooZo's TIL

DBMS가 무엇인지와 왜 사용하는지 말해주세요.

DBMS(Database Management System)은 데이터베이스 관리 시스템으로, 응용 프로그램들이 DB를 공유하며 사용할 수 있는 환경을 제공합니다.
기존 파일 시스템의 데이터 종속이나 중복, 무결성 훼손, 동시 접근의 문제 발생 등을 해결하기 위해 사용합니다.

DB를 사용하는 이유가 무엇이죠?
  • 파일시스템의 데이터 중복, 비일관성, 검색 등의 문제를 해결하기 위해 사용
  • 파일 시스템이 OS마다 다를 수 있기 때문에 OS에 종속적인 파일시스템을 이용하는 것은 프로그램의 확장성을 해침
스키마란 무엇이죠?

데이터베이스의 구조와 제약 조건에 관한 전반적인 명세를 기술한 메타데이터 집합을 의미합니다.
DB에서 데이터 개체, 속성, 관계를 형식언어로 정의한 구조로, DB 사용자가 자료를 저장, 조회, 삭제, 변경할때 DBMS는 자신이 생성한 데이터베이스 스키마를 참조해 명령을 수행하게 됩니다.

뷰가 무엇인지 설명해주시고 장점과 단점이 무엇인지 설명해주세요.

뷰는 DB에 존재하는 가상 테이블이며, 실제 테이블처럼 행과 열을 가지고 있지만 실제로 데이터를 저장하진 않습니다.
뷰를 사용하면 특정 사용자에게 테이블 전체가 아닌 필요한 칼럼만 보여줄 수 있고, 복잡한 쿼리를 단순화해서 사용하고 재사용할 수 있다는 장점이 있습니다.
그러나 한번 정의된 뷰는 변경할수 없고, 원래 테이블의 내용을 직접 수정해야 합니다.

참고 - 실제 데이터를 저장하지 않기 때문에 경우에 따라 다수의 쿼리가 발생하여 성능 이슈가 생길 수 있음, 인덱스 가질 수 없음

DB에서 사용하는 '키'에 대해 설명하고, 키의 종류에 대해 설명이 가능할까요?

키란 데이터베이스 내의 레코드나 튜플을 고유하게 식별하거나 접근할때 사용됩니다.
슈퍼키는 한 릴레이션 내에 있는 속성들의 집합으로 구성된 키로 유일성은 만족하지만, 최소성은 만족하지 못합니다.
후보키는 기본 키가 될수 있는 후보가 되는 키들을 의미하고, 슈퍼키 중에서 최소성을 만족하는 키를 의미합니다.(즉 유일성 최소성 둘다 만족)
기본키는 후보키 중에서 선택된 키로, 해당 릴레이션에서 튜플을 유일하게 식별할 수 있습니다.(null 및 중복 허용 X)
대체키는 기본키로 선택되지 않은 나머지 후보키들을 의미하고, 복합키는 두개 이상의 속성으로 구성된 키입니다.
외래키는 한 테이블의 키 중에서 다른 테이블의 레코드를 유일하게 식별할 수 있는 키를 의미합니다.

트랜잭션이 무엇인지 설명해주세요.

데이터베이스의 상태를 변화시키는 하나의 논리적 작업 단위를 의미합니다.
논리적인 작업의 쿼리 개수와 관계 없이 트랜잭션으로 묶어놓은 작업 단위 전체가 모두 적용되거나 아무것도 적용되지 않음을 보장합니다.

꼬리질문1 - 트랜잭션의 특징은 무엇이 있을까요?
트랜잭션의 특징은 앞글자를 따서 ACID라고 부릅니다.
원자성(Atomicity)은 트랜잭션을 구성하는 연산 전체가 모두 정상적으로 실행되거나 모두 취소되어야 한다는 특성입니다.
일관성(Consistency)은 트랜잭션이 실행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다는 것을 의미합니다.
고립성(Isolation)은 여러 트랜잭션이 동시에 실행될때, 각 트랜잭션은 서로에게 영향을 주지 않고 독립적으로 실행되어야 함을 의미합니다.
지속성(Durability)는 트랜잭션이 성공적으로 완료된 후, 그 결과가 데이터베이스에 영구적으로 저장되어야 한다는 것을 의미합니다.(장애나 오류가 나더라도)

트랜잭션의 상태는 어떤 것들이 있을까요?

우선 Active는 트랜잭션이 실행 중인 상태, Fail은 트랜잭션 실행에 오류가 발생해 중단된 상태를 의미합니다.
Aborted는 트랜잭션이 비정상적으로 종료되어 롤백을 수행하는 상태이고, Partitially Commit은 트랜잭션이 마지막 연산까지 실행했지만, Commit 연산이 실행되기 직전의 상태를 의미합니다.
마지막으로 Committed 상태는 트랜잭션이 성공적으로 종료되어 커밋 연산을 실행한 후의 상태를 뜻합니다.

트랜잭션 격리 수준들에 대해 설명해주세요.

동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 것을 트랜잭션의 격리 수준이라고 말합니다. 간단히 표현하자면, 특정 트랜잭션이 다른 트랜잭션에 변경한 데이터를 볼 수 있도록 허용할 지 말지를 결정하는 것 입니다.
Read Uncomitted(dirty read 발생)는 가장 낮은 격리 수준으로, 한 트랜잭션에서의 변경 내용이 아직 커밋되지 않았더라도, 다른 트랜잭션에서 그 변경내용을 조회할 수 있습니다.
Read Committed(non-repeatable-read 발생)는 한 트랜잭션에서의 변경 내용이 커밋된 후에만, 다른 트랜잭션에서 그 변경 내용을 조회할 수 있습니다.(대부분의 RDBMS)
Repeatable Read(phantom Read)는 트랜잭션 동안 동일한 쿼리를 여러번 실행하더라도, 조회하는 내용이 항상 동일함을 보장하는 격리수준입니다.
Serializable은 여러 트랜잭션이 동일한 레코드에 동시 접근할 수 없는 격리수준입니다.


참고
Dirty Read란 한 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션의 변경을 읽는것을 말합니다.(롤백될 데이터를 읽을수도 있음)
Non Repeatable Read는 한 트랜잭션 내에서 같은 쿼리를 두번 수행했을때, 두 쿼리의 결과가 서로 다른것을 말합니다.(다른 트랜잭션이 수정 또는 삭제한 경우)
Phantom Read는 한 트랜잭션내에서 같은 쿼리를 두번수행했을 때, 처음과 다음 조회 사이에 새로운 데이터 항목이 삽입되거나 삭제되어 결과의 행 수가 변경되는 현상을 의미합니다.

트랜잭션 연산 중, Commit과 Rollback에 대해 설명해주세요.

Commit 연산은 트랜잭션의 모든 변경 사항을 데이터베이스에 영구적으로 저장하는 연산이고(커밋 이후 취소 불가), 롤백은 트랜잭션의 변경 사항을 취소하고, 데이터베이스를 트랜잭션이 시작되기 전의 상태로 되돌리는 연산입니다.

이 연산들을 통해 트랜잭션의 원자성을 보장하고, 데이터의 무결성과 일관성을 유지할 수 있음.

동시성 제어에 대해 설명해주세요.

동시성 제어는 동시에 여러 트랜잭션이 수행될 때, 트랜잭션들이 DB의 일관성을 파괴하지 않도록 트랜잭션 간의 상호작용을 제어하는 것을 의미합니다.

동시성 제어 방법들에 대해 설명해주세요.

동시성 제어 방법에는 크게 락킹, 타임스탬프, MVCC(Multiversion Concurrency Control)등이 있습니다.

락킹은 데이터에 잠금을 걸어 다른 트랜잭션이 해당 항목에 접근하는 것을 제한하는 것으로, 공유락과 베타적락이 있습니다.
공유락은 데이터를 여러 트랜잭션들이 동시에 읽을 수 있게 해주지만, 변경은 허용하지 않습니다.
베타적락은 데이터 항목에 대한 모든 접근을 제한해서, 락을 소유한 트랜잭션 만이 해당 데이터 항목을 읽거나 수정할 수 있습니다.

타임스탬프 방식은 각 트랜잭션에 유일한 타임스탬프를 부여하여 트랜잭션의 우선순위를 결정합니다. 데이터를 접근할 때 해당 데이터의 타임스탬프와 트랜잭션의 타임스탬프를 비교하여 동시성을 제어합니다.

MVCC는 데이터의 여러 버전을 유지하여 동시성을 제어하는 방법입니다.
MVCC에서 데이터에 접근하는 사용자는 접근한 시점에 데이터베이스의 snapshot을 읽습니다. 이 snapshot 데이터에 대한 변경이 커밋되기 전엔, 변경사항을 다른 데이터베이스 사용자가 볼 수 없습니다.
이후 사용자가 수정을 하면 이전의 데이터를 덮어씌우는것이 아니라 새로운 버전의 데이터를 UNDO영역에 생성합니다.
이때 이전 버전의 데이터와 비교해서 변경된 내용을 기록하게 되고, 이로 인해 하나의 데이터에 대한 여러 버전의 데이터가 존재하게 됩니다.

DB Lock에 대해 설명해주세요.

DB Lock은 트랜잭션 처리의 순차성을 보장하기 위한 방법으로, 데이터에 접근하기 전에 Lock을 요청해서 Lock이 허락되면 해당 데이터에 접근할 수 있도록 하는 기법입니다.

비관적락(충돌이 일어날거라 가정)은 트랜잭션이 데이터에 접근하기 전에 해당 데이터에 대한 락을 얻어와 다른 트랜잭션의 동시 접근을 제어하는 방식으로, 공유락과 베타락이 존재합니다.
공유락은 사용중인 데이터를 다른 트랜잭션이 읽는것은 허용하나 쓰기는 허용하지 않는 방식이고, 베타락의 경우엔 읽기 쓰기 둘다를 허용하지 않는 방식입니다.

낙관적락(Application Level)은 데이터 갱신 시 충돌이 발생하지 않을 것이라 가정하는 방식으로, 초기 데이터 접근 시 락을 걸지 않고, 실제 데이터 변경 시점에 충돌 여부를 검사하게 됩니다.
각 레코드에 버전 번호나 타임스탬프를 추가해서, 데이터가 변경될 때 마다 이 값을 증가시킵니다. 이후 트랜잭션이 커밋 시점에 원래의 버전 번호나 타임스탬프와 현재 값을 비교하여 변경이 있었는지 확인하게 됩니다.
이때 만약 다른 트랜잭션에 의해 값이 변경되었다면, 충돌을 감지하게 되고, 이에 따른 조치(롤백)를 취하게 됩니다.

꼬리질문 - 어떠한 경우에 낙관적락을 사용하고, 어떠한 경우 비관적락을 사용하나요?

낙관적 락은 데이터 충돌의 빈도가 낮고, 높은 동시성이 요구될 때 적합합니다. 이는 락에 의한 대기 시간 없이 트랜잭션을 처리할 수 있기 때문입니다.
반면, 비관적락은 데이터 충돌의 빈도가 높거나 트랜잭션의 안정성이 중요한 경우에 유리합니다.
트랜잭션이 데이터에 접근하면서 바로 해당 데이터를 잠그므로, 다른 트랜잭션이 동시에 접근하는 것을 방지하게 됩니다.이로 인해 데이터의 동시 변경을 막아서 충돌을 미연에 방지할 수 있고, 충돌로 인한 롤백이나 재처리와 같은 추가적인 비용을 줄일 수 있습니다

갱신 분실 문제가 무엇인지 아시나요?

갱신 분실이란, 한 트랜잭션에서의 데이터 변경이 다른 트랜잭션에 의해 덮어쓰여져서 사라지는 현상을 말합니다.

예시
예를 들어, 두 트랜잭션이 동일한 계좌의 잔액을 동시에 업데이트하려고 하는 경우를 생각해보면, 한 트랜잭션에서 100달러를 추가하고, 다른 트랜잭션에서 50달러를 추가하려 할 때, 최종적으로는 150달러가 추가되어야 합니다. 그러나 갱신 분실 문제가 발생하면, 한 트랜잭션의 변경이 다른 트랜잭션에 의해 덮어쓰여져서, 예상했던 150달러가 아닌 100달러나 50달러만 추가될 수 있습니다.

무결성이 무엇인지, 무결성 제약 조건이 무엇인지 설명해주세요.

무결성은 데이터베이스에서 데이터의 정확성, 일관성, 그리고 신뢰성을 보장하기 위한 특성을 의미합니다.
개체 무결성은 기본키 값이 중복되거나 NULL이 되어서는 안된다는 제약 조건이고, 참조 무결성은 외래키 값이 NULL이거나 참조 대상 테이블의 기본 키 값 중 하나와 일치해야 한다는 제약 조건입니다.

JOIN 연산이 무엇인지 설명해주세요.

조인은 두개 이상의 테이블에서 관련 있는 정보를 결합하여 하나의 통합된 결과 집합을 생성하기 위해 사용됩니다.(적어도 하나의 칼럼을 공유하고 있어야 사용 가능)

꼬리질문 - JOIN 연산 종류에 대해 설명해주세요.

INNER JOIN은 양쪽테이블에 모두 내용이 있는 것만 조인되는 방식입니다.
LEFT OUTER JOIN은 왼쪽 테이블의 모든 행과 이에 해당하는 오른쪽 테이블에서의 행을 반환하고, RIGHT OUTER JOIN은 오른쪽 테이블의 모든 행과 이에 해당하는 왼쪽 테이블에서의 행을 반환합니다.(일치하는 행이 없는 경우엔 해당 행은 NULL)
CORSS JOIN은 모든 경우의 수를 출력해주는 방식이고, FULL JOIN은 두 테이블의 모든 행을 반환하는 방식으로 한쪽 테이블에만 일치하는 행이 있는 경우, 다른 테이블의 해당 부분은 NULL로 표시됩니다.

꼬리질문 - INNER JOIN과 OUTER JOIN의 차이에 대해 설명해주세요.
INNER JOIN은 두테이블에서 지정된 조건에 일치하는 행만 반환하는 반면, OUTER JOIN은 일치하지 않는 행(NULL로 채워짐)도 포함하여 보다 넓은 범위의 결과를 반환합니다.

참고 - GROUP BY 는 GROUP BY 명령어를 통해 특정 컬럼을 기준으로 연산한 결과를 집계 키로 정의하여 그룹을 짓는 역할을 합니다.(COUNT, SUM, AVG, MAX, MIN, DISTINCT)

DELETE, TRUNCATE, DROP의 차이에 대해 설명해주세요.

DELETE는 조건을 지정하여 특정 행만 삭제하는 명령어이고, TRUNCATE는 테이블의 모든 행을 제거하는 명령어 입니다.
DROP은 테이블 자체를 데이터베이스에서 완전히 제거하는 명령어 입니다.

트리거란 무엇이죠?.

트리거는 특정 테이블에 INSERT, DELETE, UPDATE와 같은 DML이 수행되었을때, 데이터베이스에서 자동으로 동작하도록 작성된 프로그램(메서드 형식의 쿼리)로, 사용자가 직접 호출하지 않고 DB에서 자동적으로 호출된다는 특징이 있습니다.

DML이 실행되고, 자동으로 정의된 프로시저가 실행됩니다.
(프로시저 : 쿼리문을 마치 하나의 메서드 형식으로 만들고 어떤 동작을 일괄적으로 처리하는 용도)

꼬리질문 - 어떤 경우 트리거를 사용하는 것이 좋나요?
예를 들어, 이커머스 플랫폼에서 수많은 주문이 실시간으로 발생할 때, 각 주문이 데이터베이스에 기록될 때마다 트리거를 사용하여 판매 집계 테이블을 업데이트 할 수 있습니다. 주문이 발생할 때마다 트리거가 실행되어 해당 상품의 판매 수량과 총액을 증가시키는 방식입니다. 이렇게 구성하면 별도의 집계 작업 없이 실시간으로 판매 현황을 조회할 수 있게 됩니다

DML, DDL, DCL이 각각 무엇인지 설명해주세요.

DDL은 데이터베이스의 스키마나 구조를 정의하거나 변경하는 데(수정, 삭제) 사용되는 언어로 CREATE, ALTER, DROP, TRUNCATE 가 있습니다.
DML은 데이터를 검색, 삽입, 수정, 삭제 하기 위한 언어로 SELECT, INSERT, UPDATE, DELETE 가 있습니다.
DCL(데이터 제어어)는 데이터베이스의 접근 권한과 같은 데이터의 보안 및 무결성을 다루는 데 사용되는 언어로 COMMIT, ROLLBACK, GRANT, REVOKE 등이 있습니다.

인덱스란 무엇인가요?

추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조입니다.
인덱스는 항상 정렬된 상태를 유지하기 때문에 원하는 값을 검색하는데 빠른 장점을 가지지만, 새로운 값을 추가 삭제 수정하는 경우엔 인덱스 또한 업데이트 해야 하므로 성능이 저하될수 있고, 추가적인 저장공간이 필요합니다.

참고
칼럼의 값과 해당 레코드가 저장된 주소를 키와 값의 쌍으로 인덱스를 정의하고 일반적으로 B+트리 자료 구조 사용.

인덱스의 종류엔 무엇이 있나요???

인덱스에는 Clustered 인덱스와 Non-Clustered 인덱스가 있습니다.
Clustered인덱스는 인덱스로 지정한 칼럼을 기준으로 물리적인 순서를 유지하도록 하는 방식입니다.
한 테이블의 하나만 존재할 수 있고, 이를 통해 데이터 검색을 빠르게 진행할 수 있습니다.
Non-Clustered 인덱스는 데이터 자체가 아닌 데이터의 위치에 대한 포인터를 저장하고 있는 인덱스입니다.
클러스터 인덱스와는 달리 실제 데이터의 물리적 저장 위치와 논리적인 순서가 동일하지 않고, 한 테이블에 여러 개의 논클러스터 인덱스를 가질 수 있으며, 실제 데이터에 접근하기 전에 해당 포인터를 통해 레코드의 위치를 찾아가야 합니다.

참고
Clusterd의 경우 테이블 레코드가 인덱스 키에 따라 정렬되기에 검색시 빠른 속도 보장
Non-Clustered 인덱스를 사용하여 데이터를 검색할 때는 먼저 인덱스를 검색하여 해당 데이터의 물리적 위치(포인터)를 찾고, 그 위치를 기반으로 실제 데이터에 접근하는 과정이 필요합니다.
Non-Clustered 인덱스는 원본 데이터의 물리적인 구조와 독립적이므로, 데이터의 추가나 수정이 일어났을 때 Clustered 인덱스에 비해 리밸런싱(재정렬) 비용이 상대적으로 적습니다.

인덱스의 장단점
테이블을 검색하는 속도가 향상됨(인덱스에 의해 데이터가 정렬된 형태를 갖기에, 풀 테이블 스캔을 할 필요가 없음. order by나 max/min도 빠르게 실행가능)
하지만 인덱스를 관리하기 위한 추가 작업및 공간이 필요하고 잘못 사용하는 경우 오히려 검색 성능이 저하됨

추가 작업 예시
INSERT : 새로운 데이터에 대한 인덱스를 추가
DELETE : 삭제하는 데이터의 인덱스를 사용하지 않는다는 작업 수행
UPDATE : 기존의 인덱스를 사용하지 않음 처리, 갱신된 데이터에 대한 인덱스 추가
데이터의 인덱스를 제거하는 것이 아니라 ‘사용하지 않음’으로 처리하고 남겨두기 때문에 수정 작업이 많은 경우 실제 데이터에 비해 인덱스가 과도하게 커지는 문제점이 발생할 수 있다. 별도의 메모리 공간에 저장되기 때문에 추가 저장 공간이 많이 필요하게 된다.

인덱스는 어떻게 생성할 수 있나요?

Created Index문을 사용하면 보조 인덱스가 생성됩니다.
테이블 생성시에 제약 조건에 Primary Key를 걸면 클러스터형 인덱스, Unique를 사용하면 보조 인덱스(non-clustered)가 생성됩니다.

참고
유니크에 Not Null까지 걸면 클러스터형 인덱스로 지정됨.

인덱스의 자료구조에 대해 설명해주세요.

인덱스는 기본적으로 B+Tree를 이용하여 구현합니다.
이는 트리 구조로 되어있어, 루트 노드에서 시작하여 원하는 값을 가진 리프노드로 찾아 내려가고, 각 노드에는 인덱스 키 값과 참조가 저장되어 있어 검색하고자 하는 값과 비교하며 탐색을 진행합니다.
이를 통해 전체 데이터를 풀 스캔하지 않아도 원하는 데이터를 효율적으로 찾아낼수 있다는 장점이 있습니다.
하지만 B+Tree 인덱스는 DML 작업(삽입, 수정, 삭제) 시 페이지 분할 등의 연산이 일어날 수 있어 성능 저하가 발생할 수 있습니다.(SELECT가 빠름)

참고
해시테이블도 인덱스 자료구조 중 하나로 알려져 있지만, 일반적인 RDBMS에서의 인덱스로는 적합하지 않습니다. 해시테이블은 키 값을 해싱하여 저장하기 때문에 범위 검색이나 특정 문자로 시작하는 값의 검색 등이 불가능합니다. 또한, SQL 쿼리에서의 부등호와 같은 연산을 처리하는 것에 해시테이블은 제한적입니다.
B트리는 각 노드에 키와 값이 함께 저장되며, 내부 노드와 리프 노드 모두 데이터를 저장할 수 있습니다.
B+트리는 리프 노드에만 실제 데이터가 저장되고, 내부 노드는 키 값만을 가집니다. 또한, 리프 노드 간에는 서로 연결된 링크가 있어 순차 접근이 빠릅니다.
B+트리는 하나의 노드에 더 많은 Key를 담을 수 있도록 하여 B-트리에 비해 B+트리의 높이가 더욱 낮아지게 됩니다.
B+트리에서 데이터 삭제는 리프 노드에서만 이루어지기 때문에 처리 과정이 간단하고 모든 리프 노드가 동일한 높이에 있기 때문에 모든 검색의 성능이 동일합니다.
B+트리의 리프 노드들은 연결 리스트처럼 서로 연결되어 있습니다. 즉, 한 리프 노드의 끝에 도달하면 다음 리프 노드로 쉽게 이동할 수 있는 포인터(주소)가 있습니다
이러한 구조의 장점은 범위 검색을 할 때 특히 나타납니다. 예를 들어, 50부터 70까지의 값을 검색한다고 가정해보겠습니다. 우리는 50이 있는 리프 노드에 먼저 접근합니다. 이후, 연결된 포인터를 따라가면서 70까지의 모든 값을 순차적으로 방문하게 됩니다.
다시 말해서, 50이 있는 리프 노드를 찾기만 하면, 그 노드로부터 시작해서 연결된 노드들을 통해 70까지의 값을 쉽게 찾아낼 수 있습니다. 이러한 방식으로 B+트리는 범위 검색을 효율적으로 처리할 수 있습니다.

해시 인덱스 방식과 B+ 트리 인덱스 방식의 차이에 대해 설명해주세요

해시 인덱스는 주로 동등 연산에 특화되어 있습니다. 이는 해시 함수를 통해 데이터의 값을 고유한 해시 값으로 변환하고, 이를 인덱스로 사용하기 때문입니다. 이 방식의 장점은 동등 비교에서 굉장히 빠른 접근 속도를 가진다는 것입니다. 하지만, 범위 검색이나 부등호 연산에는 적합하지 않습니다.
반면, B+ 트리 인덱스는 데이터를 정렬된 상태로 유지하는 트리 구조를 가집니다. 따라서 범위 검색이나 정렬된 데이터 접근에서 유리한 성능을 보입니다. 그리고 동등 비교 뿐만 아니라 부등호 연산에서도 효과적입니다.

복합 인덱스는 두 개 이상의 필드를 조합하여 만든 인덱스를 의미합니다.

인덱스로 인해 성능이 저하되는 경우는 어떤 경우인가요?

INSERT, UPDATE, DELETE와 같은 데이터의 변경 작업(DML) 시에 성능이 저하될 수 있습니다.
새로운 데이터를 삽입할 때 인덱스의 구조 상 적절한 위치를 찾아 삽입해야 합니다. 이 과정에서 페이지에 데이터가 들어갈 공간이 부족하다면 페이지 분할이 발생하게 되고, 이로 인해 추가 작업이 발생할 수 있습니다.
DELETE시 실제 그 데이터는 즉시 지워지지 않고, 사용하지 않는다는 마킹을 하게 되는데, 이러한 작업이 반복될 시 데이터베이스의 행 크기가 점점 증가할 수 있습니다.
UPDATE시 기존 값을 삭제하고 새 값을 삽입하는 형태로 동작하여, 앞서 설명드린 INSERT와 DELETE의 성능 문제를 동시에 겪게 될 수 있습니다.

어느 컬럼에 인덱스를 생성하는 것이 좋은가요?

인덱스는 WHERE 절에서 사용되는 열이나 조인에 자주 사용되는 열에 인덱스를 만드는 것이 좋습니다
그리고 데이터 중복도가 높은 열에는 인덱스를 만들어도 큰 효과가 없고, 카디널리티가 높은(중복도가 낮은)열에 만드는 것이 효과적입니다.
또한 INSERT, UPDATE, DELETE가 얼마나 자주 일어나는지도 고려해야 합니다.인덱스에 데이터 변경 작업은 오히려 부담을 주기 떄문입니다.

참고
조인에 자주 사용되는 열에 인덱스를 만드는 이유
조인 연산은 두 개 이상의 테이블에서 행들을 매칭하는 과정을 포함,인덱스가 적용된 열에서 조인을 수행할 경우, 인덱스의 특성을 활용해 매칭되는 행들만 빠르게 탐색하게 되어 성능이 크게 향상

카디널리티가 높은 열에 인덱스를 만드는 이유
카디널리티가 높은 열에 인덱스를 사용할 경우, 인덱스를 통해 검색되는 값의 범위나 특정 값이 더욱 빠르게 결정되므로 검색 성능 향상에 큰 도움이 됩니다.
반대로 카디널리티가 낮은 열(예: 성별처럼 값의 종류가 매우 제한적인 경우)에 인덱스를 사용하면 인덱스의 효과를 크게 볼 수 없습니다. 이는 대부분의 행이 비슷한 값을 가질 것이기 때문입니다.

인덱스를 매 필드마다 설정하는 것이 좋을까요?

인덱스는 두번 탐색을 강요합니다. 인덱스 리스트, 그다음 컬렉션 이렇게 두번을 탐색하기 때문에 읽기 관련 비용이 더 들게 됩니다.
또한 테이블이 수정되면 인덱스도 수정되어야 합니다. 인덱스를 수정하는 것은 a=1을 a=2처럼 값만 수정하는 것이 아니라, B트리 구조를 사용하기에 트리의 높이를 균형있게 조절하는 비용도 들고 데이터를 분산시켜서 효율적으로 조회할 수 있도록 구축하는 비용도 듭니다.
그렇기 때문에 필드에 인덱스를 무작정 다 설정하는 것은 답이 아닙니다.

DB 정규화가 무엇인지 설명해주세요.

정규화는 하나의 릴레이션에 하나의 의미만 존재하도록 릴레이션(테이블)을 분해하는 과정으로, 데이터의 일관성, 최소한의 데이터 중복, 데이터의 유연성을 위한 방법입니다.

제 1 정규형은 테이블의 모든 속성이 원자 값으로만 이루어져 있어야 한다는 규칙입니다. 즉 테이블의 모든 칼럼은 더 이상 쪼개지지 않는 단일값으로 구성되어야 합니다.
제 2 정규형은 제 1 정규형을 만족하고, 기본키를 제외한 모든 컬럼이 기본키에 대해 완전 함수 종속을 만족해야 한다는 규칙입니다. 즉 기본키의 부분 집합이 결정자가 되어선 안된다는 의미입니다.
제 3 정규형은 제 2정규형은 만족하고, 이행적 함수 종속(a-> b-> c)을 가지지 않아야 한다는 규칙을 의미합니다. 즉, 한 칼럼의 값이 다른 칼럼에 의존하는 경우, 이를 분리하여 별도의 테이블로 만들어야 합니다.
BCNF는 제 3정규형을 만족하고, 모든 결정자가 후보키가 되도록합니다.기본 키 외에 다른 컬럼이 후보키를 결정하는 경우 제거하게 됩니다.
제 4 정규형은 다중 값 종속을 제거하는 규칙을 의미합니다. 즉, 하나의 테이블에서 여러개의 다중 값 종속 관계가 발생하면, 이를 분해하여 별도의 테이블로 만들어야 합니다.
제 5 정규형은 조인 종속을 제거하는 규칙을 의미합니다.

꼬리질문 - 정규화의 장단점

이상현상 문제를 해결할 수 있고, 데이터베이스 구조 확장 시 정규화된 데이터베이스는 그 구조를 변경하지 않아도 되거나 일부만 변경해도 됩니다.
하지만 릴레이션의 분해로 인해 릴레이션 간의 연산(JOIN 연산)이 많아진다. 이로인해 질의에 대한 응답 시간이 느려질 수 있다.

꼬리질문 - 역정규화 하는 이유
정규화의 단점으로, 릴레이션 간의 JOIN 연산이 늘어나 질의 성능이 저하될 우려가 있습니다. 읽기 작업이 많이 필요한 DB의 전반적인 성능을 향상시키기 위해 역정규화를 진행합니다.

이상 현상에 대해 설명해주세요.

이상현상은 DB 테이블에서 일부 데이터를 삽입/수정/삭제 할때 발생할 수 있는 문제로 DB의 무결성과 일관성 문제를 야기할 수 있습니다.
삽입 이상은 새로운 데이터를 추가할때, 불필요한 데이터도 함께 추가해야 하는 문제입니다.
갱신 이상은 데이터를 수정할 때 일부만 변경하여 데이터가 불일치하게 되는 문제입니다.
삭제 이상은 어떤 정보를 삭제할때 원치 않은 정보까지 함께 삭제 되는 문제입니다.

삽입이상 예시 - 학생과 그의 전공 정보를 담는 테이블에서 새 전공을 추가하려 할 때, 해당 전공의 학생이 아직 없다면 그 전공은 추가할 수 없게 되는 문제가 발생합니다.
갱신이상 예시 - 같은 직원 정보가 여러 테이블에 중복 저장되어 있을 때, 한 테이블에서만 직원의 주소를 변경하면 다른 테이블과 주소 정보가 일치하지 않게 됩니다.
삭제이상 예시 - 예를 들어, 학생과 그의 전공 정보를 담는 테이블에서 마지막으로 남은 특정 전공의 학생을 삭제하면, 그 전공 정보 자체도 함께 사라져 버릴 수 있습니다.

Connection Pool에 대해 설명해주세요..

커넥션 풀은 데이터베이스와의 연결을 재 사용할 수 있도록 관리하는 풀입니다.데이터베이스와의 연결은 생성과 종료 과정이 비용이 큰 작업이기 때문에, 필요할 때마다 연결을 새로 생성하고 종료하는 것은 시스템의 성능을 저하시킬 수 있습니다.
Connection Pool은 미리 여러 개의 데이터베이스 연결을 생성해 두었다가 필요할 때 연결을 제공하고, 작업이 끝난 연결을 다시 풀에 반환합니다. 이렇게 함으로써 연결 생성과 종료의 빈번한 오버헤드를 줄이고, 빠르게 데이터베이스 연결을 얻을 수 있게 됩니다.

RDB와 NoSQL의 차이에 대해서 설명해주세요.

RDB(관계형 데이터베이스)는 스키마에 따라 데이터를 테이블 형식으로 저장합니다.
스키마에 맞춰 데이터를 관리하기 때문에 데이터의 정합성을 보장할 수 있지만, 시스템이 커질수록 쿼리가 복잡해지고 성능이 저하되며 Scale Out이 어렵다는 특징을 가집니다.
반면, NoSQL(Not Only SQL)은 고정된 스키마가 없어 데이터를 더 유연하게 저장할 수 있습니다. 이때 데이터는 key-value, document, graph 등 다양한 형식으로 저장될 수 있습니다
NoSQL은 scale-out에 뛰어나므로 대용량의 데이터를 다루는 환경에서 용이합니다.
그러나 스키마의 유연성 때문에 데이터 중복이나 데이터의 불일치 문제가 발생할 수 있습니다. 이때 중복 데이터의 변경이 필요할 경우 여러 위치에서의 수정이 필요할 수 있습니다.

참고
scale-up : 기존 서버 성능 자체를 향상(ex: CPU,RAM등 하드웨어 업데이트)
scale-out : 서버의 수를 늘려 전체 시스템의 로드를 분산시키는 것(비용이 효율적이고 확장의 한계가 적으나, 구성이 복잡하거나 통신 관련 오버헤드 생길수 있다)

RDB는 데이터의 무결성과 일관성을 보장하기 위해 복잡한 트랜잭션 관리와 잠금 메커니즘을 가지고 있습니다. 이러한 특성 때문에 데이터를 여러 서버에 분산 저장하고 관리하는 Scale-out 방식에 어려움이 있습니다. 또한, 데이터 간의 관계와 조인 연산이 복잡한 쿼리를 수행할 때 여러 서버 간의 데이터 동기화가 필요하므로 성능 저하의 위험이 있습니다
NoSQL 데이터베이스는 RDB와 달리 고정된 스키마가 없고, 데이터 간의 관계를 최소화하여 설계되기 때문에 데이터 분산 및 병렬 처리가 상대적으로 쉽습니다. 따라서, 데이터를 여러 서버에 분산 저장하고 수평 확장하는 Scale-out 방식에 더욱 유리합니다.

예를 들면, 하나의 정보가 데이터베이스의 여러 위치에 중복되어 저장되었을 때, 그 정보를 업데이트하려면 모든 중복 위치에서 변경해야 합니다. 이렇게 되면 데이터의 불일치 문제가 발생할 수 있습니다. 예를 들어, 한 사용자의 연락처 정보가 여러 곳에 중복 저장되어 있는데, 연락처 정보가 변경되었을 때, 모든 위치에서 해당 정보를 수정하지 않으면 일부 위치에는 오래된 연락처 정보가 남아 있게 되는 문제가 발생할 수 있습니다.
rdb는 보통 중복을 제거하기 위해 정규화 수행

그렇다면 RDB와 NoSQL은 어느 경우에 적합한가요?

RDB는 스키마를 기반으로 데이터를 관리해서 데이터 구조가 안정적이고, 데이터의 무결성을 보장하는데 유리합니다.
데이터간의 복잡한 관계나 높은 무결성(데이터베이스 내의 정보가 정확하고 일관되게 유지되는 특성)이 요구되는 시스템, 그리고 데이터의 구조가 자주 변경되지 않는 경우 RDB를 사용하는 것이 적합합니다.
반면 NoSQL은 유연한 데이터 구조를 허용합니다.따라서 데이터 구조가 자주 변하거나 확장성이 중요한 경우 적합합니다.
특히 대용량의 데이터를 처리하거나, 확장성과 분산 저장을 중시하는 시스템에서 NoSQL은 scale-out 측면에서 rdb에 비해 유리한 선택이 될 수 있습니다.

db clustering이 무엇인지 설명해주세요.

DB 클러스터링은 DB 스토리지는 공유하고, DB 서버를 여러대 놓는 방식입니다.(동기 방식으로 동기화 진행)
Active-Active 방식의 경우 모든 DB 서버 인스턴스가 동시에 읽기 및 쓰기 작업을 처리합니다.
이 방식은 부하 분산에 적합하지만, 모든 서버가 동일한 스토리지를 공유하기 때문에, 병목이 발생할 가능성이 있습니다.
Active-Standby 한 개의 DB 서버 인스턴스(Active)만이 쓰기 작업을 처리하며, 다른 인스턴스(Standby)는 대기 상태에 있습니다.
Active 인스턴스에 문제가 발생할 경우, Standby 인스턴스가 작업을 인계 받아 처리합니다(FailOver). FailOver가 발생할 때 짧은 시간 동안의 데이터 손실이나 중단이 발생할 수 있습니다.

병목 - 시스템의 성능이나 용량이 제한받는 현상
여러 서버가 동일한 스토리지를 공유하게 되면, 해당 스토리지에 대한 접근 요청이 동시에 증가하게 됩니다.

db replication이 무엇인지 설명해주세요.

DB 레플리케이션은 DB 서버와 DB 스토리지를 다중화하는 방식입니다.
쓰기 작업(INSERT, DELETE, UPDATE)은 Master 서버에서 처리되며, Slave 서버는 주로 읽기 작업을 처리하게 됩니다. 이를 통해 시스템의 트래픽을 효율적으로 분산시킬 수 있습니다.
하지만 Slave로 데이터를 복제할 때 비동기 방식으로 동작하기 때문에, 실시간으로 동기화되지 않아 일관성 있는 데이터를 얻지 못할 수도 있고, Master 노드가 다운되는 경우, 복구 및 대처가 까다롭다는 단점이 있습니다.

파티셔닝과 샤딩에 대해 설명해주세요.

파티셔닝(칼럼 기반, 보통 수직으로 쪼갬)은 단일 데이터베이스 내에서 테이블의 데이터를 여러 파티션으로 나누는 것을 의미합니다.
이를 통해, 쿼리 성능을 향상시키거나 데이터 관리를 더 효율적으로 할 수 있습니다. 예를들어, 일자별로 데이터를 파티션한다면 특정 일자의 데이터에 빠르게 접근할 수 있습니다.(풀 스캔 안해도 되서)
샤딩은 데이터베이스(row 기반, 수평)를 여러 개의 독립적인 데이터베이스로 분할하는 것을 의미합니다. 이를 통해 높은 트래픽이나, 큰 데이터 집합을 처리할때, 분산된 데이터베이스를 통해 부하를 분산시켜 성능 저하를 방지할 수 있습니다.

SQL Injection이 무엇인지 설명해주세요.

악의적인 사용자가 보안상의 취약점을 이용해, 임의의 SQL문을 주입하고 실행되게 하여 DB가 비정상적인 동작을 하도록 조작하는 행위를 의미합니다.
대응방법 - 입력 값에 대한 검증, Prepared Statement나 파라미터화된 쿼리 사용(값과 쿼리를 구분), Error Message 노출 금지, 웹 방화벽 사용 등

옵티마이저가 무엇인지 설명해주세요.

옵티마이저는 SQL을 (빠르고 효율적으로) 수행할 최적의 처리 경로를 생성해주는 DBMS 내부의 핵심 엔진입니다.
개발자가 SQL을 작성하면 옵티마이저가 실행 계획을 세우고, 이 계획을 바탕으로 데이터에 접근하게 됩니다.
옵티마이저가 항상 최적의 실행 경로를 보장하는 것은 아니기 때문에, 개발자가 직접 최적의 실행 경로를 작성해주는 것을 ‘힌트’라고 부릅니다.

Statement, PreparedStatement의 차이에 대해 설명해주세요.

Statement와 PreparedStatement는 SQL 쿼리를 실행하기 위한 JDBC의 두 가지 주요 방법입니다.
Statement는 주로 정적 쿼리(실행 시점에 쿼리 구조 변경 x)를 실행하는데 사용하고, PreparedStatement는 동적 쿼리를 실행하는데 사용합니다.
PreparedStatement는 쿼리를 미리 컴파일하고, 동일한 쿼리를 반복적으로 실행할때 이 컴파일된 쿼리를 재사용합니다. 이로 인해 반복적인 쿼리 수행 시 성능이 향상될 수 있습니다.

TOP