-noSQL 과 RDB 의 특징, 차이에 대해 말씀해주세요. 어느 상황에 어떤 데이터베이스를 쓰는게 좋겠습니까?
자유로운 저장 : 오브젝트 안의 오브젝트 허용 (중복 허용) => 자유로운저장
RDBMS : 중복허용 X 테이블을 나누어 저장해야함 : 학생이 듣는 수업 => 학생 테이블 + 수업 테이블
확장의 얘기는 RDBMS도 클러스터도 가능
1) RDBMS
장점 - Data를 Cloumn과 Row 형태로 저장
데이터의 분류, 정렬, 탐색 속도가 비교적 빠름.
SQL이라는 구조화 된 질의를 통해 데이터를 다룰 수 있음.
작업의 완전성을 보장함.
데이터의 UPDATE가 빠름.단점
반드시 스키마 규격에 맞춰서 데이터를 다뤄야 함.
데이터 처리에 대한 부하 발생시, 처리가 어려움.
성능
MySQL 서버는 멀티스레드 프로그램이다.
서버로 요청이 들어올 때마다 배정되는 포그라운드 스레드도 있고
계속 돌고있는 백그라운드 스레드도 있다.
그런데 스레드가 너무 많아지면 성능이 떨어진다.
MongoDB는 single-thread 로 query 들을 queue에 쌓아놓고 하나씩 실행한다.
MySQL은 multi-thread 로 query 를 처리하는데, MongoDB는 single-thread 로 query 들을 queue에 쌓아놓고 하나씩 실행한다.
쾌적한? query 만 사용한다면 non-blocking 방식인 mongoDB가 성능이 빠른게 이론적인 결론이다. (program level에서 lock은 너무나 느리다.)
정확한 성능을 분석한 글은 찾지 못했는데, 그 이유가 관계형 DB(Mongodb)와 비 관계형 DB(Mysql)의 성능은 비교 대상이 아니라고 한다.(document-based vs relational)
단순히 MongoDB가 더 빠르다, Mysql은 느리다 라는 말은 잘못됐다.
대표적으로 Mysql은 multi-thread 방식이고 MongoDB는 single-thread, non-blocking 방식이기 때문에 관계가 복잡하지 않은 Query에 대해서는 MongoDB가 빠르다고 한다.
https://exmemory.tistory.com/107
RDBMS 파티션
서비스의 크기가 점점 커지게 되면서 다양하고 많은 Table들이 존재하게 되었다.
VLDB(Very Large DBMS)전체 DB가 하나의 DBMS에 다 들어가기 힘들어지는 DBMS이 자연스럽게 등장하였고 하나의 DBMS가 많은 Table을 관리하다 보니 느려지는 이슈가 발생하게 되었다.
Partitioning이란?
큰 Table이나 인덱스를 관리하기 쉬운 단위로 분리하는 방법을 의미한다.
샤딩
같은 테이블 스키마를 가진 데이터를 다수의 데이터베이스에 분산하여 저장하는 방법을 의미합니다.
application level에서도 가능하지만 database level에서도 가능합니다.
Horizontal Partitioning이라고 볼 수 있습니다.
RDBMS 장점:
ACID 준수: 데이터베이스 시스템이 "ACID를 준수"하면 데이터베이스 시스템의 원자성, 일관성, 격리, 영속성을 측정하는 우선순위를 충족합니다. 데이터베이스가 ACID를 더 많이 준수할수록 데이터베이스 트랜잭션 유효성을 보장하고, 이상 발생을 줄이고, 데이터 무결성을 보호하고, 안정적인 데이터베이스 시스템을 생성하는 데 더 많은 기여를 합니다. 일반적으로 SQL 기반 RDBMS는 ACID 준수 수준이 높지만, NoSQL 데이터베이스는 구조화되지 않은 데이터를 처리할 때 속도와 유연성을 얻기 위해 이 특징적인 장점을 포기합니다.
일관된 데이터 시스템에 적합: SQL 기반 RDBMS를 사용하면 원래 생성하는 구조로 정보가 유지됩니다. 방대한 양의 데이터를 처리하기 위한 동적 정보 시스템이 필요하지 않고 수많은 데이터 유형을 처리하지 않는다면, RDBMS는 뛰어난 속도와 안정성을 제공합니다.
우수한 지원 옵션: RDBMS 데이터베이스는 40년 이상 사용되어 오고 있기 때문에 지원을 받고, 추가 제품을 구하고, 다른 시스템의 데이터를 통합하기가 더 쉽습니다.
RDBMS 단점:
확장성 문제와 샤딩의 어려움: RDBMS는 NoSQL 데이터베이스에 비해 대규모 성장에 대응해 확장하는 것이 어렵습니다. 이러한 데이터베이스는 샤딩 측면에서도 문제가 있습니다. 샤딩은 관리가 쉽도록 큰 데이터베이스를 작은 부분으로 나누는 프로세스입니다. 앞으로 몇 년 동안 큰 변화가 없을 것으로 예상되는 보수적인 데이터베이스를 처리하고 있다면 RDBMS 솔루션과 관련된 샤딩 및 확장 문제와는 관련이 없을 수 있습니다. 반면, 앞으로 몇 년 동안 확장하고 성장할 계획이라면 비관계형 데이터베이스 시스템(NoSQL 기반)이 요구에 더 적합할 수 있습니다.
NoSQL 형식으로 효율성이 떨어짐: 현재 대부분의 RDBMS는 NoSQL 데이터 형식과 호환되지만, 비관계형 데이터베이스만큼 효율적으로 작동하지는 않습니다.
적합한 데이터베이스 유형을 선택하는 방법
다양한 유형의 데이터베이스가 있다는 점을 고려할 때 하나를 선택하기란 혼란스러울 수 있습니다. 데이터베이스 관리 시스템을 선택할 때 염두에 두어야 하는 몇 가지 요소는 다음과 같습니다.
원자성이 최우선 순위인 경우 관계형 데이터베이스를 고수하세요. 데이터베이스 관리의 원자성은 데이터베이스의 일관성을 향상시키고, 원자성 트랜잭션의 원칙에 의존합니다. 이들은 복합 작업으로 간주되는 일련의 작업입니다. 다시 말해서, 원자성 트랜잭션의 모든 작업이 일어나거나 전혀 일어나지 않습니다. 원자성 트랜잭션의 가장 간단한 예로는 계좌 A에서 계좌 B로 돈을 이체하는 작업을 들 수 있습니다. 계좌 A의 잔액을 차감하고 해당 금액이 계좌 B에 추가되어야 하기 때문에 두 작업이 모두 성공해야 트랜잭션이 성공합니다.
데이터 전략이 수직 확장을 기초로 하는 경우 관계형 데이터베이스가 바람직합니다. 수직 확장은 시스템에 더 많은 서버를 추가하는 대신 서버에 더 많은 컴퓨팅 능력을 추가합니다. 사용자 수가 제한적이고 관련된 쿼리가 많지 않은 경우 선호됩니다. 이러한 점에서 수직 확장은 비즈니스를 상대하는 스타트업에 적합할 수 있습니다. 수직 확장의 기본적인 장점은 속도와 단순성입니다.
반면 사용자나 쿼리 측면에서 더 큰 부하가 예상되는 경우 수평 확장이 훨씬 저렴한 솔루션입니다. NoSQL 데이터베이스는 수평 확장을 사용합니다. 서버에 더 많은 컴퓨팅 능력을 추가하는 대신 서버 전체에서 부하를 분산하기 때문에 이와 같은 이름이 붙게 되었습니다. 수평 확장과 NoSQL 데이터베이스는 비즈니스에 더 많은 탄력성을 제공합니다. 그러나 이 시스템에서는 결합 작업을 실행하는 것이 어렵습니다.
ACID 준수보다 속도가 더 중요한 경우 문서 데이터베이스와 같은 비관계형 데이터베이스가 더 나은 적합합니다. 예를 들어 센서 데이터와 같은 실시간 데이터의 경우, 속도를 위해 데이터 무결성이 일부 손상되는 것이 용인될 수 있습니다. 비관계형 데이터베이스에서 각 레코드는 독립적인 엔터티입니다. 따라서 데이터베이스 크기에 관계없이 여러 쿼리를 동시에 실행할 수 있습니다.
지도 같이 복잡한 공간분석을 요구하는 경우는
NoSQL이 적합하지 않습니다.
하지만, 단순 반복적인 질의기능이 주로 사용되는
“서비스”라면 고려해볼만한 기능입니다.
사례 : NoSQL은 일단 쏟아져 들어오는 데이터는 각자 알아서 받은 다음에 그 내용들을 다른 데이터베이스에 반영하자는 것이다. 일시적으로는 무결성에 문제가 좀 생기더라도 일단 분산 처리 능력을 최대화 하는 쪽에 초점을 맞추고 있는 것이다.
분산 :
MYSQL 클러스터링 https://bcho.tistory.com/1062
관계형 데이터 베이스를 수평적으로 확장하는 것이 어려운 이유는 관계형 모델이 일관성을 보장하도록 설계되어 있다는 사실과 관련이 있습니다. 즉 동일한 데이터 베이스를 쿼리 하는 클라이언트는 항상 최신의 데이터를 보게 해야 합니다. 여러 시스템에 걸쳐 관계형 데이터 베이스를 수평으로 확장하는 경우 클라이언트가 A노드가 아닌 B노드에 데이터를 쓸 수 있고 처음 쓴 B노드와 다른 노드들이 연결되는 시간이 존재해 변경 사항이 반영되는 업데이트 지연이 발생할 수 있어 일관성을 보장하기 어려워집니다.
몽고디비 분산시스템
반대로 NoSQL 데이터베이스는 주로 최종 일관성을 목표로 합니다. 최종 일관성은 새로 작성된 데이터가 반드시 즉시 사용할 수 있어야 하는 것은 아니지만 결국 데이터베이스의 다른 노드에서 사용할 수 있게 됨(보통 몇 ms내에)을 의미합니다. 이렇게 하면 데이터의 가용성이 향상되는 이점이 있습니다. 작성된 최신 데이터를 즉시 볼 수 없더라고 오류가 발생하지 않고 이전 버전의 데이터를 볼 수 있습니다.
SQL 데이터베이스 사용이 더 좋을 때 은행
관계를 맺고 있는 데이터가 자주 변경되는 애플리케이션의 경우
NoSQL에서는 여러 컬렉션을 모두 수정해야 하기 때문에 비효율적
변경될 여지가 없고, 명확한 스키마가 사용자와 데이터에게 중요한 경우
NoSQL 데이터베이스 사용이 더 좋을 때 - 데이터 센서
정확한 데이터 구조를 알 수 없거나 변경/확장 될 수 있는 경우
읽기를 자주 하지만, 데이터 변경은 자주 없는 경우
=> 데이터 중복을 계속 업데이트 해야 함
데이터가 여러 컬렉션에 중복되어 있기 때문에 수정 시 모든 컬렉션에서 수행해야 함 (SQL에서는 중복 데이터가 없으므로 한 번만 수행이 가능)
데이터베이스를 수평으로 확장해야 하는 경우 (막대한 양의 데이터를 다뤄야 하는 경우)
해외사례
Google Bigtable
모든 URL 기반의 문서 정보 수집 (Key/Value 기반의 Bigtable에 저장)
수집한 정보를 Map/Reduce로 색인 (색인 데이터도 Bigtable에 저장)
Document에서 사용하는 정보가 계속 추가될 수 있다. (URL, title, 본문, meta tag 등)
Digg.com Cassandra
내 친구가 추천한 정보를 보여주기 위해서 NoSQL이 필요해짐
Twitter Cassandra
모든 사용자의 글을 하나의 DB에 저장할 수 없다면 내가 팔로우하는 timeline은 어떻게 보여줄 수 있을까?
예를 들어 페이스북의 경우 내가 게시글을 작성했다 하더라도 다른 사람이 바로 볼 수 있으면 좋지만 꼭 그렇지 않더라도 큰 영향은 없습니다. 1~2초후에 내 게시글을 볼 수 있어도 크게 문제가 없다는 말이죠. 즉, 현재 정확한 데이터가 아니더라도(업데이트 전의 데이터) 사용자가 데이터를 사용할 수 있도록 합니다.
버퍼 캐시 : 자주 사용되는 테이블의 데이터 정보가 저장되어 있는 일종의 ‘가속 장치’이므로 검색 데이터가 버퍼 캐시 안에 있다면 데이터는 빠르게 조회되어 출력
인덱스 -> b+트리 구조
수정이 자주 일어 나지 않는 테이블에 유리
예제로 가져온 쿼리를 살펴보자.
select name from table where age = 22;
간단한 쿼리이다. age 컬럼이 index로 설정되어 있다고 가정하고 이 쿼리에서 index를 통해 데이터를 어떻게 조회해오는지 살펴보자.
우선 root node로 가서 age = 22인 값의 leaf노드로 가기위한 경로를 안내받는다.
root node는 branch node의 경로를 안내해주고 (branch node가 없어서 root node 바로 아래 leaf node가 있다면 바로 leaf node의 위치를 알려줄 것이다.) branch node로 가면 또 아래의 branch node혹은 leaf node의 경로를 알려줄 것이다.
그렇게 leaf node까지 도착하게 되면 leaf node는 index 의 값과 디스크의 주소값을 가지고 있다.
만약 위 쿼리에서 select name 이 아닌 select age로 조회했으면 어떨까?
B+Tree 를 통해 age값이 22인걸 이미 알고있으니 디스크로 접근할 필요가 없다.
하지만 우리는 age가 아닌 name을 조회했기 때문에 디스크까지 접근해야한다.
이 때 디스크 I/O가 발생하고 age 22에 저장되어있는 디스크 B주소로 접근하게 된다.
그러면 최종적으로 디스크 B주소에 있는 철수 라는 데이터를 가져오고 조회결과를 보여주게 된다.
B-Tree는 최상위의노드를 Root, 중간의 노드를 Branch, 최하위 노드를 Leaf 라고 부른다. Leaf노드는 실제 데이터 레코드를 찾아가기 위한 주소 값을 가지고 있다.
디스크에서 읽는 것은 메모리에서 읽는것보다 성능이 훨씬 떨어집니다.
(데이터베이스도 인덱스가 없다면 전체 데이터를 일일이 액세스하고서야 원하는 데이터를 찾을 수 있다.)
결국 인덱스 성능을 향상시킨다는 것은 디스크 저장소에 얼마나 덜 접근하게 만드느냐, 인덱스 Root에서 Leaf까지 오고가는 횟수를 얼마나 줄이느냐에 달려있습니다.
인덱스의 갯수는 3~4개 정도가 적당합니다.
너무 많은 인덱스는 새로운 Row를 등록할때마다 인덱스를 추가해야하고, 수정/삭제시마다 인덱스 수정이 필요하여 성능상 이슈가 있습니다.
인덱스 역시 공간을 차지합니다. 많은 인덱스들은 그만큼 많은 공간을 차지합니다.
특히 많은 인덱스들로 인해 옵티마이저가 잘못된 인덱스를 선택할 확률이 높습니다.
조인
https://velog.io/@leesinji8/JOIN
먼저, SQL Server의 성능을 향상 시킬 수 있다.
저장 프로시저를 처음에 실행하면 최적화, 컴파일 단계를 거쳐 그 결과가 캐시(메모리)에 저장되게 되는데, 이 후에 해당 SP를 실행하게 되면 캐시(메모리)에 있는 것을 가져와서 사용하므로 실행속도가 빨라지게 된다.
그렇기 때문에 일반 쿼리를 반복해서 실행하는 것보다 SP 를 사용하는게 성능적인 측면에서 좋다.
두번째, 유지보수 및 재활용 측면에서 좋다.
C#, Java등으로 만들어진 응용프로그램에서 직접 SQL문을 호출하지 않고 저장 프로시저의 이름을 호출하도록 설정하여 사용하는 경우가 많은데, 이때 개발자는 수정요건이 발생할때 코드 내 SQL문을 건드리는게 아니라 SP 파일만 수정하면 되기 때문에 유지보수 측면에서 유리해진다.
또한 한번 저장 프로시저를 생성해 놓으면, 언제든 실행이 가능하기 때문에 재활용 측면에서 매우 좋다.
셋째, 보안을 강화할 수 있다.
사용자별로 테이블에 권한을 주는게 아닌 저장 프로시저에만 접근 권한을 주는 방식으로 보안을 강화할 수 있다.
실제 테이블에 접근하여 다양한 조작을 하는 것은 위험하기 때문에 실무에서는 실제로 개발자에게는 sp권한만 주는 방식을 많이 사용한다.
마지막으로, 네트워크의 부하를 줄일 수 있다.
클라이언트에서 서버로 쿼리의 모든 텍스트가 전송될 경우 네트워크에는 큰 부하가 발생하게 된다. 하지만 저장 프로시저를 이용한다면 저장프로시저의 이름, 매개변수 등 몇글자만 전송하면 되기 때문에 부하를 크게 줄일 수 있다.
단점
대부분의 경우에는 성능이 향상되나 항상 그렇지는 않다.
앞에서 저장 프로시저를 실행할 때 최적화 단계를 수행한다고 말씀드렸다. 최적화 단계에서 인덱스를 사용할지 안할지를 결정하게 되는데, 다들 아시는 것처럼 인덱스를 사용한다고 항상 수행결과가 빨라지지 않는다.
만약에 가져올 데이터가 다량인데 인덱스를 사용하면 오히려 성능이 바빠지게 될 것이다.
저장 프로시저는 첫번째 수행 시에 최적화가 이루어져서 인덱스 사용여부가 결정되어 버린다.
만약에 첫번째 수행때 데이터를 몇건만 가져오도록 파라미터가 설정되어 있다면, 인덱스를 사용하도록 최적화되어 컴파일 됐을 것이다.
그런데 두번째 수행에서 많은 건수의 데이터를 가져오도록 파라미터가 들어가면..? 일반 쿼리문이었다면 파라미터가 달라졌으니 다시 최적화되어 컴파일 되겠지만... 안타깝게도 저장 프로시저는 그냥 인덱스를 사용하는 프로시저를 실행시켜 버릴 것이다.
이렇게 되어 버리면 성능에 크게 문제가 될 것이다. 이를 방지 하기 위해서는 저장 프로시저를 다시 컴파일 해줘야한다.
저장 프로시저의 장점
저장 프로시저를 사용하게되면 여러 가지 장점들이 있습니다.
하나의 요청으로 여러 SQL문을 실행 할 수 있습니다.
네트워크 소요 시간을 줄일 수 있습니다.
만약 동일한 쿼리를 1000번 2000번 호출하는 것보다 SP를 이용해서 구현한다면 SP를 호출할 때 한 번만 네트워크를 경유하기 때문에 네트워크 소요시간을 줄이고 성능을 개선할 수 있습니다.
개발 업무를 구분해 개발 할 수 있습니다.
순수한 애플리케이션만 개발하는 조직과 DBMS 관련 코드를 개발하는 조직이 따로 있다면, DBMS 개발하는 조직에서는 데이터베이스 관련 처리하는 SP를 만들어 API처럼 제공하고 애플리케이션 개발자는 SP를 호출해서 사용하는 형식으로 역할을 구분하여 개발이 가능합니다.
저장 프로시저의 단점
저장 프로시저의 장점에 대해 알아보았다면 이제 단점에 대해서도 알아볼 필요가 있습니다.
처리 성능이 낮습니다.
문자나 숫자 연산에 저장 프로시저를 사용한다면 오히려 C나 JAVA보다 느린 성능을 보여줍니다.
디버깅이 어렵습니다.
DB 확장이 매우 힘듭니다.
서비스 사용자가 많아져 서버수를 늘려야할 때, DB 수를 늘리는 것이 더 어렵습니다.
서비스 확장을 위해 서버수를 늘릴경우 DB 수를 늘리는 것보다 WAS의 수를 늘리는 것이 더 효율적이기 때문에 대부분의 개발에서 DB에는 최소의 부담만 주고 대부분의 로직은 WAS에서 처리할 수 있게 합니다.
프로시저의 장점은 데이터베이스와의 통신을 최소화 할 수 있고, 개발자 입장에서는 DBA가 요구한 데이터만 던져주면 알아서 처리할 수 있어서 편하다는 정도?
단점은, 윗분들 말씀대로 유지보수가 힘들고, 데이터 분석도 힘들다는 점이죠.
낙관적 락(Optimistic Lock) 이란
낙관적 락(Optimisstic Lock)은 DB의 Lock을 사용하지 않고 Version관리를 통해 애플리케이션 레벨에서 처리한다.
대부분의 트랜잭션이 충돌하지 않는다고 가정하는 방법이다.
DB의 Lock 기능을 이용하지 않고, JPA가 제공하는 버전 관리 기능을 사용한다.
트랜잭션 커밋 전에는 트랜잭션 충돌을 알 수 없다.
두 번의 갱신 분실 문제
두 트랜잭션이 같은 데이터를 변경했을 때, 한 트랜잭션의 결과만 남는것을 두 번의 갱실문제라고 한다.
두 번의 갱실 문제는 DB의 트랜잭션 범위를 벗어나는 문제이다.
따라서 추가적인 처리가 필요한데, 이를 해결하기 위한 세 가지 방법이 있다.
마지막 커밋만 인정
이전 커밋은 모두 무시하고, 가장 마지막 커밋만 반영한다.
디폴트로 이 방법이 사용된다.
최초 커밋만 인정
최초 커밋을 반영하고, 이후 커밋들은 오류를 발생시킨다.
JPA가 제공하는 버전 관리 기능을 이용하면 쉽게 구현할 수 있다.
비관적 락(Pessimistic Lock)
모든 트랜잭션은 충돌이 발생한다고 가정하고 우선 Lock을 거는 방법이다.
낙관적 락(Optimistic Lock)과는 달리 DB의 Lock 기능을 이용한다. 주로 select for update 구문을 사용하고, 버전 정보는 사용하지 않는다. (사용하도록 할 수도 있다.)
엔티티가 아닌 스칼라 타입(int, String 같은 타입)을 조회할 때도 사용할 수 있다.
트랜잭션 커밋하기 전에, 데이터를 수정하는 시점에 미리 트랜잭션 충돌을 감지할 수 있다.
Lock을 획득할 때까지 트랜잭션은 대기하므로, Timeout을 설정할 수 있다.
비관적락 은 데이터의 무결성이 중요하고, 충돌이 많이 발생하여 잦은 롤백으로 인한 효율성 문제가 발생하는것이 예상되는 시나리오에서좋습니다.
낙관적락 은 실제로 데이터 충돌이 자주 일어나지 않을것이라고 예상되는 시나리오에서 좋습니다.
어떤 서비스의 이용자 테이블이 있다고 가정합시다. 이용자 id 를 여러 테이블에서 FK 로 참조하고 있습니다. 그런데 이용자 테이블에 환경설정, 개인정보 등 정보를 한데 저장하다보니 Column 이 40개가 넘게 있는 상태입니다. 문제를 진단해 주시고, 해결 방안도 제시해 주시기 바랍니다.
https://dung-beetle.tistory.com/53