4.1 데이터베이스의 기본
데이터베이스 - 일정한 규칙, 규약을 통해 구조화되어 저장되는 데이터의 모음.
이러한 데이터베이스를 관리하는 시스템을 DBMS라고 부른다. 여기에서 쿼리(질의, query)를 사용하여 데이터베이스에 접근하는 것이다. MySQL, MongoDB같은 것들이 DBMS에 속한다.
SQL(Structed Query Language)은 뭐냐? 구조적 쿼리 언어. 즉, 언어의 일종이다. 이 자체가 관계형 데이터베이스를 뜻하지는 않지만, 결국 모든 관계형 데이터베이스가 이 언어를 사용하기는 하는 모양이다. 그래서 'SQL == 관계형 데이터베이스'라는 러프한 등식을 간단하게 세울 수 있을 것 같다.
그렇다면 반대로 NoSQL은 테이블(관계형의 relation)을 사용하지 않고 데이터를 저장하는 비관계형 데이터베이스를 말한다고 말할 수 있겠지.
전자는 거래, 분석 관련하여 강점을 가지고 후자는 응답성이 높고 사용량이 많은 곳에 주로 사용된다고 한다.
그럼 데이터베이스의 기본 단위를 알아보자. 먼저 간단한 데이터 개념을 보자면 entity가 있다. 이것은 여러 개의 속성을 가진 명사를 의미한다. '회원 - 이름, 아이디, 주소, 전화번호'라고 쓰일 때 이 회원이 바로 entity에 해당한다. 당연히 더 심층적으로 들어갈 수 있을 것이다.
이 entity에는 종속 관계가 있기도 한다. 위 사례에서 아이디라는 것이 회원이라는 것이 있어야지만 존재하는 것이라면 약하다고 불린다. 그 반대가 강한 것.
relation. 이것이 데이터베이스에서 정보를 구분해 저장하는 기본 단위이다. entity에서 적절한 속성이나 관계를 데이터베이스에 relation으로 담아서 관리를 한다고 이해하면 되시겠다. entity가 데이터베이스에서는 relation이라 불린다고 간단하게 말할 수도 있는 듯? 데이터베이스는 관계형와 아닌 것으로 분류할 수 있는데 이때 전자의 relation을 테이블, 후자의 것은 컬렉션이라고 부른다.
설명이 잘 안 돼있는데, 관계형 데이터베이스에서는 레코드-테이블-데이터베이스, NoSQL 데이터베이스에서는 도큐먼트-컬렉션-데이터베이스 구조로 이뤄진다고 한다.
그럼 데이터베이스의 기본 단위를 알아보자. 먼저 간단한 데이터 개념을 보자면 entity가 있다. 이것은 여러 개의 속성을 가진 명사를 의미한다. '회원 - 이름, 아이디, 주소, 전화번호'라고 쓰일 때 이 회원이 바로 entity에 해당한다. 당연히 더 심층적으로 들어갈 수 있을 것이다.
이 entity에는 종속 관계가 있기도 한다. 위 사례에서 아이디라는 것이 회원이라는 것이 있어야지만 존재하는 것이라면 약하다고 불린다. 그 반대가 강한 것.
relation. 이것이 데이터베이스에서 정보를 구분해 저장하는 기본 단위이다. entity에서 적절한 속성이나 관계를 데이터베이스에 relation으로 담아서 관리를 한다고 이해하면 되시겠다. entity가 데이터베이스에서는 relation이라 불린다고 간단하게 말할 수도 있는 듯? 데이터베이스는 관계형와 아닌 것으로 분류할 수 있는데 이때 전자의 relation을 테이블, 후자의 것은 컬렉션이라고 부른다.
설명이 잘 안 돼있는데, 관계형 데이터베이스에서는 레코드-테이블-데이터베이스, NoSQL 데이터베이스에서는 도큐먼트-컬렉션-데이터베이스 구조로 이뤄진다고 한다.
각종 용어 정리.
속성: relation에서 관리하는 고유한 이름을 갖는 정보. 그냥 우리가 아는 뜻 그대로 이해해도 문제는 되지 않을 듯.
도메인: 각 속성이 가질 수 있는 값의 집합. 성별이라는 속성이 있다면 통상 남성, 여성 정도의 집합이 나올 것이다.
필드: relation의 속성들이 데이터베이스에 표현된 이름? 방식? 값.
테이블: 이러한 필드에 맞춰서 기입되는 행 단위의 데이터. 사람 이름과 나이로 필드를 구축하고 거기에 이제 사람들을 쌓아나가면 그게 바로 표가 된다는 것이다.
대충 이렇게 용어를 설명할 수 있다. DBMS마다 필드 타입이 다르다고 한다. 자료형이 다른 것이라 보면 되겠다. 숫자 타입, 날짜 타입, 문자 타입으로 크게 분류를 할 수 있고, 더 세분화시킬 수 있는데 일단 이 정도까지만 알아보자. 자료형은 직접 쓰면서 익히는 게 최고다.
테이블(이제 관계형 데이터베이스의 relation에 특정해서 말해보자)이 데이터베이스에 층층이 쌓이고 관계를 맺으면서 데이터베이스가 구축되는 것이다. 포함 관계나 대등한 관계 등등..관계형 데이터베이스 설계 (관계 종류 1:1 / 1:M / N:M )
1:1이거나, 1:N, N:M 정도로 나눌 수 있다. 이를 그래프로 표현하기도 하는데, 이때 0개도 가능한가의 여부가 중요한 요소가 되는 듯.
링크로 들어가보면 비단 relation의 관계만이 아닐 필드간의 관계로도 이야기될 수 있는 듯하다.
테이블의 관계를 명확히 하고 테이블의 인덱스를 정하기 위해 설정된 키가 있다.
책의 설명에 따르면 유일성을 가진 슈퍼키, 그 안에 최소성을 갖춘 후보키. 그리고 그 후보키 중 기본키로 선택되는 키와 남게 되는 대체키로 구조가 되어있다. 유일성? 중복되는 값이 없다. 최소성? 최소 필드만 써서 키가 형성된다.
기본키는 PK(Primay Key)라고도 부른다. 키에 대한 자세한 설명은 링크에 들어가는 게 더 도움이 될 것 같다.
기본키는 또 자연키와 인조키로 나뉜다. 자연키는 키로 남을 수 있는 것을 선별하다가 최종적으로 남게 되는 키를 말한다고. 자연키는 가변적일 수 있다. 인조키는 아예 데이터베이스에 넣을 때 고유 식별자를 부여해서 인위적으로 생성한 키를 말한다. 이는 변하지 않는다. 당연히 인조키가 기본키로 대체로 쓰인다.
외래키FK라는 개념이 있다. 다른 테이블에서 사용되는 기본키를 참조하는 값이다.
[DataBase] 키(Key)의 개념 및 종류 (tistory.com) 모든 relation은 반드시 하나 이상의 후보키를 가진다.
기본키는 무결성도 조건으로 가지기에 Null값이 없다. [DB] 📚 데이터베이스 키(KEY) 종류 🕵️ 정리 (tistory.com) 뒤 사이트가 정리가 정말 잘 돼있는 듯. 헷갈릴 때 두고두고 참고해도 좋을 듯하다.
4.2 ERD와 정규화 과정
Entity Relationship Diagram란? 릴레이션 간의 관계들을 정의한 것. 데이터베스를 구축할 때 기초적인 뼈대 역할을 한다.
관계형 구조로 표현할 수 있는 정형 데이터를 표현하기에 매우 적합하며 디버깅이나 설계도의 역할이 되어주기도 한다.
이러한 관계도를 ERD라고 부른다. [DataBase] ERD란?
데이터베이스 속 릴레이션들의 관계를 따지는 데에 있어 잘못된 종속 관계로 인한 이상 현상을 해결하거나 메모리의 효율을 위해 릴레이션들을 분리하는 과정을 정규화 과정이라고 부른다.
이상 현상이란? 하나의 회원에 하나의 등급이 있어야 하는데 3개가 있다던가, 데이터를 삽입해야 하는데 해당 필드가 비어있다던가 하는 의도하지 않은 현상을 말한다.
이를 해결하기 위해 정규화 과정을 통해 간단하고 효율적으로 표현을 정리하는 것. 이는 정규형 원칙을 기반으로 정규형을 만들어가는 과정이다. 정규형의 종류는 다양하다. 그래도 기본적으로는 더 좋은 구조, 중복성 감소, 독립적 관계 표현 등을 추구한다.
제1 정규형: 릴레이션의 모든 도메인이 분해될 수 없는 원자 값으로만 구성되어야 한다(집합 형태면 더 나눌 수 있을 것). 중복된 값을 가지는 집합이 있으면 안 된다는 것. 반복 집합이 있으면 제거되어야만 한다.


왼쪽을 오른쪽으로 바꾸는 것. 오른쪽이 제1 정규형이다. 이것에도 쉽게 이상현상이 나타난다고 한다. 삽입 이상, 삭제 이상, 갱신 이상. 자세한 것은 [DB] 8. 정규형 참조.
제2 정규형: 릴레이션이 제1 정규형이면서 부분 함수의 종속성을 제거한 형태. 위 예시에서는 학번에 지도교수가 종속되어 있다. 이런 것을 분리시켜주는 것.
주의할 점은 동등한 릴레이션으로 분리해야 한다는 것, 그리고 정보 손실이 발생하지 않는 무손실 분해를 해야한다는 것. 그러나 이것도 아직 이상현상이 발생할 수 있다. 그 이유는 바로 이행적 함수 종속성에 있다.
그게 뭐냐? 지도교수는 학번에 종속되고, 학과는 지도교수에 종속된다. 그렇다면 학과는 학번에 이행적으로 종속되어 있다는 것.
제3 정규형: 제2 정규형이면서 모든 속성이 이행적 함수 종속을 만족하지 않는 상태.
이렇게 만들어주자는 것!
결과적으로 이렇게 모양이 바뀌게 된다.
보이스/코드 정규형: Boyce and Codd Normal Form이라 하여 BCNF라고도 부른다. 이것은 기본적으로 제3 정규형이고, 함수 종속 관계를 제거하면서 릴레이션의 모든 것들을 후보키로? 만든 상태를 말한다고.
후보키란? 슈퍼키 중에서 최소성을 만족하는 키. -- 최소성을 만족하려면 두 필드의 묶음으로 되면 안 되는 것 아닌가?
여튼 이런 식으로 제3 정규형과 모양이 같은데, 그러면서 이러한 조건까지 만족할 때 BCNF라고 부른다는 모양.
정규형에 대한 추가정리
제1 정규형: 이전 대회에서 지역을 나누는 전처리를 한 것.
제2 정규형: 평점과 유저, 책 데이터를 머지한 상태에서 원래대로 되돌리는 일.
제3 정규형에 대해. 함수 종속성(완전 함수 종속, 부분함수 종속, 이행함수 종속)의 개념 (tistory.com) 함수 종속성의 개념. 함수 종속성이란? 다양한 쌍 중에서 A를 정하면 B가 자동으로 정해지는 것. 그때 B가 종속된다고 말한다. 좌표평면에서 이차 함수를 생각해보자. x값을 넣으면 하나의 y값이 나온다. 그렇기에 y는 종속된다. 그러나 y의 값을 댄다고 해서 x값이 하나로 딱 떨어지지는 않는다. y가 4일 때, y=x2에서 x는 2, -2의 값을 가질 수 있다. 이런 개념이라고 생각하면 된다. 이럴 때 x가 결정자, y가 종속자라고 부른다.
정규형을 하는 것은 이상치를 제거하기 위함이라는 것을 감안하고 접근하면 이해가 빠르다.
보이스코드의 모양. 모든 결정자가 후보키가 되도록 만드는 일이라 강한 제3 정규형이라고도 불린다고 한다. 위에서는 강사는 결정자이나 후보키가 아니기에 이를 분리하는 과정을 거치는 것이다.
sql 워크벤치로 간단하게 ERD를 만든 모습. 어떻게 선을 연결하는 건가 싶었는데, 안 속에 필드가 생기면 그때부터 연결이 가능해지더라고.
무결성은 매우 중요한 부분이며 여기서 간단하게 다루더라도 결국 다른 부분에서 또 나오기 마련이다. 일을 같이 처리할 것인지, 어떻게 순서를 둘 것인지 등의 것들은 컴퓨터에서는 쓰레드, 네트워크에서는 사용자 병렬 처리, 데이터베이스에서는 쿼리 처리가 되는 격이다. 이는 나중에도 배울 내용이니 깊게 파두는 것도 괜찮을 것이다.
4.3 트랜잭션과 무결성
트랜잭션: 데이터베이스에서 하나의 논리적 기능을 수행하기 위한 작업의 단위. 통상 여러 개의 쿼리로 이뤄지며, 깃에서 커밋 정도의 행위라고 생각하면 된다. 이것과 관련해 4가지 특징(ACID)을 살펴볼 수 있다.
원자성Atomicity: 트랜잭션과 관련된 일이 모두 수행되었거나 되지 않았거나를 보장하는 특징. 트랜잭션 내에 다양한 쿼리가 있다. 가령 내 계좌의 돈을 빼고, 다른 이의 계좌의 넣고의 작업을 하나의 트랜잭션으로 묶는다면? 중간만 하다가 멈추거나 하는 일 없이 처음부터 안 되던가, 지정한 트랜잭션이 전부 이뤄지거나를 충족하면 원자성이 있다고 말할 수 있다.
일관성Consistency: 허용된 방식으로만 데이터가 변경돼야 함을 나타내는 특징. 마령 계좌의 잔고가 없을 때 돈을 꺼낼 수 없다라는 규칙을 준수하고 있는 것에 대해 일관성이 있다고 말할 수 있다.
격리성Isolation: 트랜잭션이 병렬적으로 들어올 때 서로 충돌하지 않는 것을 말한다. 사실 이는 비단 데이터베이스에서만 발생하는 문제가 아니다. 네트워크로는 유저의 요청들이 동시적으로 들어올 것이고, 컴퓨터 적으로는 쓰레드에서 데드락이 일어나지 않게 관리하는 것이 관련된다고 할 수 있다. 이것과 관련하여 격리 수준에 따른 분류를 할 수 있다.
지속성Durablility: 성공적으로 수행된 트랜잭션이 영원히 반영될 때의 특징. 이는 버전 관리와도 연결되며 시스템에 장애가 발생하더라도 원상태로 복구하는 회복 기능이 있어야 함을 의미하기도 한다.
트랜잭션과 관련하여 알아둬야 할 용어. 커밋과 롤백.
커밋은 한 트랜잭션의 수행을 확정지었을 때 사용하는 용어이다. 여러 개의 쿼리가 일어나고 그것이 성공적으로 일어나 이로 인한 변경 사항이 저장되었을 때, 커밋이 수행되었다고 말한다. 깃에서의 커밋과 그다지 다르지 않다.
롤백은 그 커밋을 되돌리는 행위이다. 지속성에서 말한 회복 기능과 관련된다. 깃으로 따지자면 버전 관리의 영역이라고 볼 수 있겠다.
격리성과 관련하여 더 볼 수 있는 것. 여러 개의 격리 수준이 존재한다. 다음의 순서로 분류할 수 있고, 격리의 수준은 아래로 갈수록 약해진다. 이에 따라 각 수준에서 나타날 수 있는 현상 역시 아래로 갈수록 중첩돼 나타난다고 보면 되시겠다.
- Serializable - 트랜잭션을 순차적으로 진행시키는 격리 수준.
절대 병렬적으로 일어나는 트랜잭션을 받아들이지 않기에 매우 느리고, 병목 상태가 쉽게 일어난다. - Repeatable_read - 한 트랜잭션이 수정한 행을 다른 트랜잭션이 수정하지 못하도록 막지만, 새로운 행을 추가하는 것은 막지 않는 격리 수준.
팬텀 리드: 한 트랜잭션 내에서 동일한 쿼리를 보냈을 때 해당 조회 결과가 다른 현상을 말한다.
처음에 내 계좌를 조회하고, 돈을 이체하고 내 계좌를 다시 조회하면 당연히 내 계좌의 잔고가 다르게 나타날 것이다. 이런 건 당연한 거고, 그것보다 다른 트랜잭션에서 이뤄낸 변경에 의해 조회가 다르게 나타나는 것을 말한다고 보면 된다. 다른 트랜잭션에서 하나의 행을 추가한 후에 전체 행 갯수를 조회하면 다르게 조회될 것이다. - Read_committed - 가장 흔한 격리 수준. 커밋이 된 이후의 정보만을 조회 가능하도록 함. 이게 깃의 방식이라 보면 된다. 커밋을 한 이후에야 서로의 트랜잭션이 조회되고 수정이 가능해지는 것이다.
반복가능하지 않은 조회: 한 트랜잭션 내에서 같은 행에 두 번 이상의 조회가 발생했을 때 그 값이 다른 현상. 팬텀 리드와 다르게 한 행에 대해서 말하는 것이다. 위 격리 수준에서는 한 행에서의 변경은 일어나지 않기에 이쪽이 더 많은 변경을 가진다고 이야기할 수 있겠다. - Read_uncomitted - 가장 낮은 격리 수준. 구글 문서나 노션에서 글을 같이 쓸 때 서로가 서로 쓴 사항에 대해서 수정이 가능한데, 이렇게 수정이 서로 바로바로 조회가 가능한 정도의 수준이다. 무결성이 충족되지 않는 문제가 발생한다.
더티 리드: 한 트랜잭션의 커밋되지 않은 수정사항이 다른 트랜잭션에서 조회 가능한 상태를 말한다. 딱 생각해도 굉장히 안전하지 않다.
지속성과 관련하여 더 볼 수 있는 것. 데이터베이스에서 제공하는 복구 기능은 무엇이 있을까?
체크섬: 중복 검사의 한 형태. 오류 정정을 하는 방식으로 무결성을 보호하는 단순한 방법이다.
저널링: 커밋 이전에 로그를 남기면서 진행하는 방법.
무결성이란? 데이터의 정확성과 일관성, 유효성이 유지되는 상태를 말한다. 이것은 데이터를 신뢰할 수 있는지에 대한 여부이기도 해서 지키는 것이 매우 중요하다. 외래키와 관련하여 더 볼 여지가 있는 부분이라고 한다.
4.4 데이터베이스의 종류
관계형 데이터베이스.가장 대표적인 DBMS가 MySQL. RDBMS는 표 형식의 정형 데이터를 저장하는 데이터베이스이며 SQL을 써서 조작한다. 스토리지 엔진을 쉽게 갈아끼울 수 있다. 이 엔진 위에 커넥터 api, 서비스 계층이 있고 이를 통해 개발자는 쉽게 데이터베이스와 상호작용을 할 수 있다.
데이터베이스 엔진이란?
스토리지 엔진의 정의: 데이터를 삽입, 추출, 업데이트 및 삭제하는데 사용하는 기본 소프트웨어 컴포넌트. 안에 각 엔진 별 설명도 있으니 참고.
관계형이 아닌, NoSQL 데이터베이스도 존재한다. 대표적으로 MongoDB와 redis. 이것도 이전에 한번 나온 이야기. 기본적으로 비관계형은 키와 밸류를 통해 데이터를 구축한다고 한다.
4.5 인덱스
인덱스는 목차에 붙여둔 번호를 말한다. 데이터베이스에서 인덱스를 지정하면 원하는 데이터를 빠르게 찾을 수 있게 된다.
통상 B-tree의 구조로 이뤄져 있다. 흔히 알고 있는 이분 탐색 알고리즘이나 트리 구조를 생각하면 될 듯.
이런 구조가 있을 때 12를 찾는데 드는 비용은 얼마나 될까? 10과 20을 보고 그 중간을 선택하고, 14에서 바로 아래로 내려가 11을 본 후에 12를 찾게 될 것이다. 그냥 정렬만 되어있는 상태에서 찾는 것보다 훨씬 적은 비용을 들여서 찾을 수 있는 것이다.
데이터베이스 종류와 인덱스 (velog.io) B+tree도 있는 모양이다.
인덱스는 대수확장성 때문에 시간적으로 빠르며 효율적이다. 대수확장성은 트리의 깊이가 리프 노드의 수에 비해 매우 느리게 성장하는 것을 말한다. 리프 노드가 10개에 달할 시점에도 트리의 깊이는 5에 머문다. 한 깊이당 인덱스 항목의 수는 4배씩 증가한다. 왜 4배씩일까?
MySQL에서는 클러스터형, 세컨더리 인덱스가 있으며, 전자는 테이블 당 하나를 설정할 수 있다. 세컨더리는 보조 인덱스로 여러 개의 필드 값을 기반으로 쿼리를 많이 보낼 때 생성해야 하는 인덱스.
인덱스에도 최적화가 필요하다. 일단 인덱스를 만드는 것과, 탐색 과정도 비용이다. 인덱스 리스트를 탐색한 후 컬렉션으로들어가기 때문(NoSQL 기준). 또 행 추가 등의 수정이 이뤄지면 인덱스도 같이 수정되어야 한다. 그렇기에 인덱싱이 항상 빠르다고 볼 수는 없다.
여러 필드를 기반으로 조회를 해야할 때는 복합 인덱스를 만든다. 이때 인덱스 생성의 순서가 있다.
- 값의 같음을 비교하는 ==이나 equal이란 쿼리가 있을 때 인덱스 최우선순위.
- 정렬에 쓰이는 필드에 대해 다음 우선순위
- 다중 값을 출력하는 필드에 대해 다음 우선순위
- 카디널리티(cardinality, 유니크한 값의 정도)가 높은 기준으로 다음 우선순위.
4.6 조인의 종류
조인: 두 개 이상의 테이블을 묶어 하나의 결과물을 만드는 것. 판다스에서 많이 봤던 것. 어느 쪽을 기준으로 하냐에 따라 옵션을 둘 수 있다. inner, left, right, outer
4.7 조인의 원리
조인은 조인의 원리로 이뤄진다(너무 당연한데..?). 이 원리들을 알아보자.
중첩 루프 조인: 중첩 for문과 같은 원리로 조건에 맞는 조인을 한다. 비용이 많이 들어가기에 대용량 테이블에서는 이뤄지지 않는다.
정렬 병합 조인: 조인할 필드 기준으로 정렬을 한 뒤에 조인. 조인 조건으로 범위 비교 연산자가 있을 때 쓰인다.
해시 조인: 해시 테이블 기반의 조인. MySQL에서는 두 단계로 나뉜다. 먼저 빌드 단계에서는 입력 테이블 중 하나를 기반으로 해시 테이블을 만든다. 이후 프로브 단계에서 이 테이블을 토대로 다른 테이블에 대한 결과값을 내놓는다.