개발놀이터

데이터베이스 Failure Mode 본문

CS 지식/데이터베이스

데이터베이스 Failure Mode

마늘냄새폴폴 2024. 7. 1. 20:36

요즘 이 사이트를 보면서 공부하고있는데 정말 좋은 것 같습니다. 

 

https://roadmap.sh/backend

 

Backend Developer Roadmap: What is Backend Development?

Learn what backend development is, what backend developers do and how to become one using our community-driven roadmap.

roadmap.sh

 

제가 공부했던 내용도 있고 처음 보는 내용도 있는데 처음 본 내용 중에 요즘 공부하고있는 데이터베이스와 관련된 내용으로 골라서 공부해봤습니다. 

 

데이터베이스 Failure Mode

저도 처음엔 Failure Mode..? 이게 뭐지? 하고 공부했었는데요. 별건 아니더라구요. 그냥 데이터베이스에서 일어날 수 있는 문제상황들에 대해서 정리해놓은 것 같습니다. 

 

하나씩 살펴보면서 간단하게 개요만 알아보고 하나씩 깊이있게 공부해보겠습니다. 

 

Read Contention

Contention이라는 단어가 조금 어색해서 찾아보니 "경합" 이라는 뜻이었습니다. 손바닥으로 박수를 짝! 하고 치듯이 동시에 일어나는 상황을 말하는 것 같습니다. 

 

Read Contention은 데이터베이스에 존재하는 데이터에 여러개의 프로세스가 동시에 접근하는 상황을 일컫습니다. 보통 읽기 연산이 많이 요청되는 상황에서 발생하고 만약 발생한다면 응답시간이 길어지고 데이터베이스의 처리량이 감소합니다. 

 

이를 완화하는 방법 중 가장 확실한 방법은 레플리카를 두어서 읽기 연산의 부하를 나눠서 부담하는 것입니다. 레플리카와 같이 쓰이는 것은 캐시 서버를 두어서 읽기 연산의 부담을 캐시서버와 같이 부담하는 방법도 있습니다. 

 

또한, 쿼리의 성능 자체를 좋게 만들어서 데이터베이스의 처리량을 늘리는 방법도 있을 수 있겠습니다. 그리고 많은 곳에서 사용될 것 같지는 않지만 데이터베이스 샤딩을 통해 처리하는 방법도 있더군요. 

 

 

Write Contention

Write Contention은 Read Contention과 마찬가지인 개념으로 데이터베이스에 존재하는 데이터에 여러 프로세스가 동시에 접근하여 쓰기 연산을 수행하려고 할 때 발생하는 현상입니다. 

 

Read Contention과 마찬가지로 쓰기 수행에 대한 응답 시간이 늘어나고 처리량에 문제가 생길 수 있습니다. 또한, Write Contention은 Read Contention과 다르게 치명적인 문제가 발생할 수 있는데 바로 데드락입니다. 

 

데드락이 발생하면 프로세스가 데이터베이스의 락을 잡고 서로의 락을 기다리게 되어 데이터베이스가 뻗어버릴 수 있습니다. 데드락은 업계에서도 종종 벌어지는 문제인 것 같습니다. 

 

이를 완화하는 방법은 동시성 제어를 최적화하는 방법이 있습니다. 또한, 쓰기 작업을 배치작업을 통해 한번에 처리하면 이를 완화할 수 있습니다. 

 

 

Contention을 고려할 때 추가적으로 고려해야하는 것이 데이터베이스의 종류 (NoSQL인지 RDBMS인지) 와 격리수준입니다. 

 

높은 격리수준을 채택할수록 데이터간 정합성과 일관성이 보장되지만 이 격리수준을 유지하기위해 높은 Read Contention이 발생할 수 있습니다. 

 

또한, RDBMS인지 NoSQL인지에 따라서 Contention에 대처하는 방법도 다르기 때문에 현재 우리가 사용하는 데이터베이스의 종류에 따라 완화 방법을 올바르게 사용해야할 것 같네요. 

 

 

Thundering Herd

Read Contention과 Write Contention은 데이터베이스 문제 상황이라고 하기엔 조금 포괄적이라서 쉽게 와닿지 않았지만 개인적으로 Thundering Herd는 진짜 큰일나겠다 싶은 문제상황이었습니다. 

 

Thundering Herd는 데이터베이스의 읽기 연산의 부하를 줄이기위해 캐싱 솔루션을 사용했다면 캐시에 적혀있는 데이터의 만료시간을 주의해야한다는 것입니다. 

 

만약 캐시에 데이터가 만료되었는데 데이터를 요청 (Fetch) 하면 순식간에 엄청난 데이터 요청이 캐시 서버를 거치고 Cache Miss가 나기 때문에 엄청난 트래픽이 데이터베이스에 요청을 보낼 것입니다. 

 

이렇게 되면 순간적으로 (Thundering이라는 말처럼 번개치듯이) 트래픽때문에 스파크가 팍! 하고 튀면서 데이터베이스 서버가 순간 죽어버릴 수 있습니다. 

 

이런 장애상황은 많은 회사에서 캐시 서버가 데이터베이스의 부하를 담당하고 있는 경우 주로 발생하는 문제입니다. 저도 장애회고로 두세번정도 관련 포스팅을 본적이 있을 정도로 굉장히 유서깊은 장애상황인 것 같습니다. 

 

이를 완화하기위한 다양한 방법들이 있는데 가장 마음에 들었던 해결방법 하나만 소개해보고자합니다. 

 

Request Coalescing

Request Coalescing (발음은 [코-얼-레싱] 이라고 발음하더군요... 어려워라) 은 이렇게 Thundering Herd가 의심되는 데이터가 Cache Hit을 할때마다 만료시간을 갱신시켜주는 방법입니다. 

 

Request Coalescing 에 대해 검색해보면 Go lang에서 제공해주는 기능이 있는 것 같습니다. 아마 Spring Boot로 개발한다면 RedisTemplate을 이용해서 일일히 업데이트 시켜주면 될 것 같습니다. 

 

 

Database Cascade

만약 JPA를 공부해보신 분들이라면 친숙한 단어인 Cascade (발음은 [캐스-케이드] 라고 합니다) 문제입니다. 오잉? Cascade가 문제가 될게 있나? 싶지만 꽤나 큰 문제가 발생할 수 있습니다. 

 

Cascade에 대해 잠깐 짚고 넘어가자면 연관관계로 묶여있는 데이터에서 상위 데이터가 업데이트되면 하위 데이터까지 같이 업데이트 시켜주는 것을 말합니다. 

 

JPA에선 Cascade.ALL이라는 옵션으로 부모자식간 엔티티의 업데이트를 자동으로 해주는 기능이 있죠. 

 

Cascade를 잘못 사용했을 경우 생기는 문제에 대해서 한번 공부해봤습니다. 

 

1. 성능 문제

Cascade를 적용하면 데이터베이스의 부하를 증가시키는 문제가 발생할 수 있습니다. 생각해보면 그렇죠. UPDATE쿼리가 두번 혹은 그이상 날아가는 것이니까요. 

 

2. Lock Contention

Cascade를 설정하면 UPDATE과정에서 exclusive lock을 획득해야하고 (데이터베이스 일관성 때문) 그 과정에서 데드락이 발생할 수 있습니다. 이 문제는 위에서 언급했다시피 꽤나 큰 문제로 번질 수 있습니다. 

 

3. 예상치못한 쿼리

이 문제도 꽤나 심각한데 만약 의도치않은 UPDATE문이 발생하면 이를 디버깅하기가 굉장히 힘듭니다. 일단 의도치않은 쿼리가 날아간 것 부터 굉장히 치명적이지만 문제를 해결하기도 굉장히 까다로워진다는 것이 큰 문제로 번질 수 있습니다. 

 

4. 트랜잭션 관리의 어려움

Cascade를 실행하는 과정에서 트랜잭션의 복잡성이 올라가고 트랜잭션이 쿼리를 실행하는 시간을 확장시켜 트랜잭션을 관리하기 더욱 어려워집니다. 또한, Cascade 도중 문제가 생겨서 롤백을 해야하는 상황에서도 트랜잭션을 관리하기가 굉장히 까다로워질 수 있습니다. 

 

 

Deadlock

데드락 문제는 데이터베이스의 문제중 1, 2위를 다투지않을까싶을 정도로 굉장히 골치아프면서 자주 등장하는  문제입니다. 데드락에 대한 개념에 대해서는 아래의 링크에 자세히 설명해두었으니 참고해주시면 감사하겠습니다!

 

https://coding-review.tistory.com/359

 

교착상태 (DeadLock) 와 기아상태 (Starvation)

잘 기억은 안나지만 2022년 여름 주말이었는데 지인들끼리 다같이 모여서 쿠팡이츠로 점심을 시켜먹으려고 했는데 서버가 내려가 주문이 안된 사태가 있었습니다. 여자친구가 당시 쿠팡이츠에

coding-review.tistory.com

 

간단하게 데드락은 프로세스가 데이터베이스 연산도중 락을 획득하고 서로가 서로의 락을 획득하기위해 대기하고 있는 상황을 말합니다. 

 

처음에 제가 데드락에 대해서 공부할 땐 "뭐야 스프링부트는 스레드별로 트랜잭션이 생성되고 각 트랜잭션은 데이터베이스의 격리수준에 따라서 데이터가 격리되는데 스레드별로 데이터베이스 연산이 진행되는거 아냐? 데드락이 걸릴 일이 있어?" 라고 짧은 생각을 가지고 있었습니다. 

 

하지만 분산 트랜잭션에 대해서 공부하면서 분산 트랜잭션을 적용하면 이 데드락 상황이 굉장히 빈번하게 발생할 수 있겠다는 생각을 했습니다. 

 

재수가 없으면 이런 상황이 발생할 수 있기 때문입니다. 

 

 

데드락이 발생하면 데이터베이스의 자원이 계속해서 병목현상을 일으키고 이는 데이터베이스 서버의 장애로 이어질 수 있는 큰 문제입니다. 

 

데드락을 해결하기 위해서는 잡혀있는 락을 모두 풀어버리는 쿼리를 만들어서 손수 풀어주거나 (이것도 복잡하면 불가능한 방법입니다.) 데이터베이스 서버를 내렸다가 다시 올리는 방법이 있습니다. 

 

Corruption

Corruption은 데이터베이스에서 일어날 수 있는 상황 중에서 앞서 얘기했던 데이터베이스 연산 중 발생할 수 있는 문제들과 다르게 외부적인 영향에 의해 발생할 수 있는 문제를 말합니다. 

 

하나씩 살펴보도록 하죠. 

 

1. 하드웨어 문제

데이터베이스 서버의 디스크나 메모리에 물리적인 손상이 가해졌을 때 발생할 수 있는 문제입니다. 

 

2. 소프트웨어 문제

소프트웨어라함은 데이터베이스의 엔진 그자체에 버그가 있을 수 있고 (그런 일은 잘 벌어지지 않지 않을까 싶습니다만...) 우리의 애플리케이션에서 발생하는 버그로 인해 데이터베이스에 영향을 끼치는 문제 상황입니다. 

 

3. 파워 문제

데이터베이스 서버에 전력을 공급해주는 파워에 문제가 발생했을 경우 생기는 문제입니다. 

 

4. 휴먼 에러

말그대로 사람이 잘못해서 벌어진 상황이고 관리자 혹은 유저가 데이터베이스의 연산을 데이터베이스가 의도하지 않은 방식으로 실행하다가 발생하는 문제입니다. 

 

5. 악성 코드

바이러스나 멀웨어같은 악성코드에 의해 데이터베이스 파일이 깨지거나 문제가 발생하는 상황을 말합니다. 혹은 해킹으로 직접 데이터베이스 서버에 침투하여 문제가 발생하는 경우도 포함됩니다. 

 

 

Corruption을 완화하기 위한 방법으로서는 규칙적으로 백업을 하거나 DBMS를 업데이트 하거나, 코드 리뷰로 휴먼 에러를 방지하고, 보안 대책을 세우는 등 다양한 방법으로 데이터베이스 서버를 지켜야합니다. 

 

 

마치며

이번 포스팅에선 데이터베이스 문제 상황에 대해 정의하고 간략하게 개요정도로 공부해봤습니다. 제가 잘 모르던 용어들도 나와서 흥미롭게 공부했던 것 같습니다. 

 

그리고 공부를 하면 할수록 보는 시야가 넓어져서 공부하는 재미가 있는 요즘입니다. 백엔드 로드맵을 몇번 보면서 어떤 내용으로 포스팅을 해야할지 또 정해야겠네요. 

 

재밌었습니다! 여러분도 긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요~