- 하나의 트랜잭션이 수정한 행을 다른 트랜잭션이 수정할 수 없도록 막아주나, 새로운 행을 추가하는 것은 막아주지 X
- 가장 많이 사용되는 격리 수준
- 가장 낮은 격리 수준
- 여러 트랜잭션이 동시에 같은 행에 접근 불가
- MySQL8.0의 innoDB 기본값
- PostgreSQL, SQL Server, 오라클에서 기본값으로 설정
- 하나의 트랜잭션이 커밋되기 전에 다른 트랜잭션에 노출될 수 있으나 가장 빠름
- 매우 엄격한 수준
- 다른 트랜잭션이 커밋하지 않은 정보 읽을 수 X (커밋 완료된 데이터만 조회 허용)
- 데이터 무결성을 위해 되도록 사용하지 않는 것이 좋으나, 거대한 양의 데이터를 어림잡아 집계 시 사용하면 좋음
- 해당 행에 대해 격리, 이후 이 행에 대해 트랜잭션 발생 시 기다려야 함
- but 어떤 트랜잭션이 접근한 행을 다른 트랜잭션이 수정 가능
- 교착상태 발생 확률 ⬆️, 가장 성능 ⬇️
지속성 durability
⇒ 성공적으로 수행된 트랜잭션은 영원히 반영되어야 함
데이터베이스에 시스템 장애가 발생해도 원래 상태로 복구될 수 있어야 함
이를 위해 체크섬, 저널링, 롤백 등의 기능 제공
체크섬
중복 검사의 한 형태
오류 정정을 통해 송신된 자료의 무결성 보호하는 방법
저널링
파일 시스템 또는 데이터베이스 시스템에 변경 사항을 반영(commit)하기 전에 로깅하는 것
트랜잭션 등 변경 사항에 대한 로그 남기는 것
무결성
⇒ 데이터의 정확성, 일관성, 유효성 유지하는 것
이름 설명
개체 무결성
기본키로 선택된 필드에 빈 값이 들어가면 안됨
참조 무결성
서로 참조 관계에 있는 두 테이블의 데이터는 항상 일관되어야 함
고유 무결성
특정 속성에 대해 고유한 값을 가지도록 설정된 경우, 해당 속성값은 모두 고유한 값 가짐
NULL 무결성
특정 속성 값에 NOT NULL 조건이 주어진 경우, 해당 속성값은 NULL이 될 수 없음
4.4 데이터베이스의 종류
관계형 데이터베이스 RDBMS
⇒ 행과 열을 가지는 표 형식 데이터를 저장하는 형태의 데이터베이스
SQL 사용해서 조작
표준 SQL 지키지만 각각의 제품에 특화된 SQL 사용
종류: MySQL, PostgreSQL, 오라클, SQL Server, MSSQL 등
MySQL
⇒ 현재 가장 많이 사용하는 데이터베이스며 대부분의 운영체제와 호환됨
C, C++로 만들어짐
MyISAM 인덱스 압축 기술, B-트리 기반의 인덱스, 스레드 기반의 메모리 할당 시스템, 매우 빠른 조인, 최대 64개의 인덱스 제공
대용량 데이터베이스를 위해 설계
롤백, 커밋, 이중 암호 지원 보안 등의 기능 제공
모듈식 아키텍처 → 스토리지 엔진 쉽게 교체 가능
강점: 데이터 웨어하우징, 트랜잭션 처리, 고가용성 처리
스토리지 엔진 위에 커넥터 API 및 서비스 계층 두어 MySQL 데이터베이스와 쉽게 상호작용 가능
쿼리 캐시 지원
입력된 쿼리문에 대한 전체 결과 집합 저장
사용자가 작성한 쿼리가 캐시에 있는 쿼리와 동일하면 서버는 구문 분석, 최적화 및 실행 건너뛰고 캐시 출력만 표시함
PostgreSQL
⇒ MySQL 다음으로 개발자들이 선호하는 데이터베이스 기술
VACUUM: 디스크 조각이 차지하는 영역 회수할 수 있는 장치
최대 테이블의 크기 32TB
SQL뿐만 아니라 JSON 이용해서 데이터에 접근 가능
지정 시간에 복구하는 기능, 로깅, 접근 제어, 중첩된 트랜잭션, 백업 등의 기능 있음
NoSQL 데이터베이스
SQL 사용하지 않는 데이터베이스
Not only SQL이라는 슬로건에서 발생
종류: MongoDB, redis
MongoDB
⇒ 와이어드타이거 엔진이 기본 스토리지 엔진으로 장착된 키-값 데이터 모델에서 확장된 도큐먼트 기반의 데이터베이스
JSON 통해 데이터에 접근 가능
Binary JSON 형태(BJSON)로 데이터 저장됨
확장성 좋음
빅데이터 저장 시 성능 좋음
고가용성, 샤딩, 레플리카셋 지원
스키마 정해놓지 않고 데이터 삽입 => 다양한 도메인의 데이터베이스 기반으로 분석 또는 로깅 등 구현 시 좋음
도큐먼트 생성시마다 ObjectID 생성됨
redis
⇒ 인메모리 데이터베이스 & 키-값 데이터 모델 기반의 데이터베이스
기본적인 데이터 타입: 문자열 (string)
최대 512MB까지 저장 가능
셋(set), 해시(hash) 등 지원
pub/sub 기능을 통해 채팅 시스템, 다른 데이터베이스 앞단에 두고 사용하는 캐싱 계층, 단순한 키-값이 필요한 세션 정보 관리, 정렬된 셋(sorted set) 자료 구조를 이용한 실시간 순위표 서비스에 사용
4.5 인덱스 index
인덱스의 필요성
⇒ 데이터를 빠르게 찾을 수 있는 장치, 따라서 인덱스 설정 시 테이블 안에 내가 찾고자 하는 데이터를 빠르게 찾을 수 있음
B-트리
인스는 보통 B-트리 자료 구조로 구성
루트 노드, 리프 노드, 루트 노드와 리프 노드 사이에 있는 브랜치 노드로 구성
인덱스가 효율적인 이유 & 대수확장성
⇒ 효율적인 단계를 거쳐 모든 요소에 접근할 수 있는 균형 잡인 트리 구조와 트리 깊이의 대수확장성 때문
대수확장성
트리 깊이가 리프 노드 수에 비해 매우 느리게 성장하는 것
기본적으로 인덱스가 한 깊이씩 증가할 때마다 최대 인덱스 항목의 수는 4배씩 증가함
인덱스 만드는 방법
MySQL
클러스터형 인덱스
테이블당 하나 설정 가능
primary key 옵션으로 생성
기본키로 만들지 않고, unique not null 옵션 사용
세컨더리 인덱스
create index... 명령어 기반으로 생성
보조 인덱스
여러 개의 필드 값 기반으로 쿼리 많이 보낼 때 생성
MongoDB
도큐먼트 생성시 자동으로 ObjectID 형성, 해당 키가 기본키로 설정됨
부가적으로 세컨더리키도 설정해서 기본키와 세컨더리 키를 같이 사용하는 복합 인덱스 설정 가능
인덱스 최적화 기법
1) 인덱스는 비용이다
인덱스 리스트 → 컬렉션 순으로 탐색하므로 인덱스는 2번 탐색하도록 함 → 관련 읽기 비용 듦
컬렉션 수정 시 인덱스도 수정되어야 함
쿼리에 있는 필드에 인덱스 모두 설정하면 안됨
컬렉션에서 가져와야하는 데이터가 많을수록 인덱스 사용하는 것 → 비효율적
2) 항상 테스팅하기
서비스 특징에 따라 인덱스 최적화 기법이 달라지므로 항상 테스팅해야 함
explain() 함수 통해 인덱스 만들고, 쿼리 보낸 이후에 테스팅하며 걸리는 시간 최소화해야 함
3) 복합 인덱스는 같음, 정렬, 다중 값, 카디널리티 순
여러 필드 기반으로 조회 시 복합 인덱스 생성 → 이 인덱스 생성 시 같음, 정렬, 다중 값, 카디널리티 순으로 생성해야 함
인덱스 생성 순서에 따라 성능 달라짐
==이나 equal이라는 쿼리 있으면 가장 먼저 인덱스로 설정정렬에 쓰는 필드를 그 다음 인덱스로 설정다중 값을 출력해야 하는 필드 (쿼리 자체가 >, < 등 많은 값 출력) 그 다음 인덱스로 설정카디널리티가 높은 순서 기반으로 인덱스 생성(카디널리티: 유니크한 값의 정도)
4.6 조인 join
⇒ 2개 이상의 테이블을 묶어서 하나의 결과물 만드는 것
MongoDB → 관계형 데이터베이스보다 조인 연산(lookup) 성능이 떨어지므로 조인 작업이 많은 경우 관계형 데이터베이스 사용하는 것이 좋음
내부 조인 inner join
⇒ 두 테이블 간의 교집합
SELECT * FROM TableA A
INNER JOIN TableB B ON
A.key = B.key
왼쪽 조인 left outer join
⇒ 왼쪽 테이블의 모든 행 결과 테이블에 표시
SELECT * FROM TableA A
LEFT JOIN TableB B ON
A.key = B.key
오른쪽 조인 right outer join
⇒ 오른쪽 테이블의 모든 행 결과 테이블에 표시
SELECT * FROM TableA A
RIGHT JOIN TableB B ON
A.key = B.key
합집합 조인 full outer join
⇒ 왼쪽, 오른쪽 테이블의 모든 레코드 집합 생성
SELECT * FROM TableA A
FULL OUTER JOIN TableB B ON
A.key = B.key
4.7 조인의 원리
중첩 루프 조인 NLJ Nested Loop Join
⇒ 조건에 맞는 조인 하는 방법 (중첩 for문과 같은 원리)
랜덤 접근에 대한 비용 ⬆️ → 대용량의 테이블에서는 사용 X
ex) t1, t2 테이블을 조인하는 경우
첫 번째 테이블에서 행을 하나씩 읽고, 그 다음 테이블에서도 행을 하니씩 읽어 조건에 맞는 레코드를 찾아 결괏값 반환
+) 블록 중첩 루프 조인 (BNL, Block Nested Loop)
조인할 테이블을 작은 블록으로 나눠서 블록 하나씩 조인하는 방법
중첩 루프 조인에서 발전한 방법
정렬 병합 조인
⇒ 각각의 테이블을 조인할 필드 기준으로 정렬하고, 이후에 조인 작업 수행
조인할 때 사용할 적절한 인덱스가 없고, 대용량의 테이블 조인 시 사용
조인 조건으로 <, > 등 범위 비교 연산자가 있는 경우 사용
해시 조인
⇒ 해시 테이블 기반으로 조인
두 개의 테이블 조인 시 하나의 테이블이 메모리에 온전히 들어간다면, 중첩 루프 조인보다 효율적임
동등 조인에서만 사용 가능
빌드 단계, 프로브 단계로 구성
1) 빌드 단계
입력 테이블 중 하나를 기반으로 메모리 내 해시 테이블 빌드하는 단계
두 개의 테이블 중 바이트가 더 작은 테이블 기반으로 빌드함
해시 테이블의 키로 조인에 사용되는 필드 사용
2) 프로브 단계
⇒ 프로브 단계에서 레코드를 읽으며, 각 레코드에서 'persons.country_id'와 일치하는 레코드 찾아서 결과로 반환
각 테이블 한 번만 읽기 때문에 중첩 루프 조인보다 성능 좋음
시스템 변수 join_buffer_size에 의해 사용 가능한 메모리 양 제어됨, 런타임 시 조정 가능