본문 바로가기

카테고리 없음

동시성

이 포스팅은 엔터프라이즈 애플리케이션 아키텍쳐 패턴 5장 동시성을 요약한 내용이다.

동시성이란? -> 동시성 제어, 동시성 문제

구글에서 동시성이라고 검색하면 보통 동시성에 대한 용어를 정의하기 보다는 동시성 제어, 동시성 문제에 대한 내용이 먼저 나온다. 그만큼 동시성이라는 용어를 생각할 때 제어와 문제를 다루는게 중요해서 그런것 같다.

 

동시성 문제란

  • 여러 프로세스나 스레드가 동일한 데이터를 조작하는 경우에 동시성 문제가 발생한다.
  • 다중 사용자 환경의 데이터베이스 시스템에서 동시에 실행되는 여러 트랜잭션 간의 간섭으로 인해 동시성 문제가 발생한다.

 

대표적인 동시성 문제 - 정확성을 깎고 활동성을 높여서 발생한다.

  • 손실된 업데이트(lost update) - A유저가 test.java 파일에서 특정 함수를 수정하고 있는데 B유저가 해당 파일을 읽어서 빠르게 수정하고 저장했다고 하자. 그 후 A유저가 작업을 마치고 저장을 하면 B유저가 작업한 내용은 사라진다.
  • 일관성 없는 읽기(inconsistent read) - A유저가 특정 test1.java, test2.java 파일을 순차적으로 읽는다고 하자. test1.java를 다 읽고 test2를 읽기 전에 B유저가 두 파일을 수정 했다. A유저는 test2.java파일을 읽을 때에는 B가 수정한 내용을 읽고 test1.java파일을 읽을 때에는 수정하기 전의 내용을 읽게 된다.

 

실행 컨텍스트 - 동시성의 다양한 문제 영역

시스템에서 프로세싱이 진행될 때는 일반적으로 둘 이상의 컨텍스트 안에서 진행된다. 다양한 컨텍스트에 대해 알아보자.

  • 요청과 세션 - 외부 세계와 상호작용하는 관점
  • 프로세스와 스레드 - 운영체제와 관련된 컨텍스트
  • 트랜잭션 - 데이터베이스를 처리할 때

 

격리와 불변성 - 고전적인 동시성 문제의 해결

격리

동시성 문제는 프로세스나 스레드와 같은 활성 에이전트가 동시에 둘 이상 동일한 데이터에 접근할 때 발생한다. 이 문제를 해결하는 한 가지 방법은 격리를 통해 데이터를 분리함으로써 하나의 활성 에이전트만 데이터에 접근할 수 있게 하는 것이다. 훌륭한 동시성 설계란 이러한 격리 환경을 만드는 방법을 찾고 이 환경 내에서 최대한 많은 프로그래밍 작업을 수행하는 것이다.

불변성

동시성 문제는 공유하는 데이터가 수정될 수 있을 때만 발생한다. 따라서 변경 불가능한 데이터를 인식할 수 있으면 동시성 충돌을 예방할 수 있다. 일부 데이터를 변경 불가능하게 하면 데이터를 광범위하게 공유할 수 있다.

 

낙관적 동시성 제어와 비관적 동시성 제어

  • Optimistic Locking - 자유롭게 read & write, 충돌 감지, 충돌을 해결할 방법을 생각해야함
  • Pessimistic Locking - 데이터 선점, 충돌 예방, 동시성이 제한 됨

낙관적 잠금과 비관적 잠금을 선택하는 가장 중요한 기준은 충돌의 빈도와 심각도이다. 충돌이 자주 발생하지 않거나 그 결과가 그리 심각하지 않으면 더 나은 동시성을 제공하고 구현하기도 쉬운 낙관적 잠금을 선택해야 한다. 그러나 충돌의 결과가 사용자에게 심각한 경우에는 비관적 잠금을 사용해야 한다.

 

일관성 없는 읽기 문제 - 낙관

낙관적 잠금 기법에서 발생한다.

해결방법

  • 비관적 잠금의 읽기잠금과 쓰기잠금을 활용하면 해결할 수 있다.
  • 버전 표식 - 읽은 모든 데이터의 버전 표식

교착상태 문제 - 비관

비관적 잠금 기법에서 발생한다.

해결 방법

  • 교착 상태 감지 소프트웨어를 사용한다.
  • 잠금에 시간 제한을 둔다.
  • 이미 잠금을 가진 사용자가 다른 잠금을 어등려고 할 때 교착 상태가 발생한다. 따라서 시작할 때 필요한 잠금을 모두 얻게 하고 추가 잠금을 얻지 못하게 한다.
  • 잠금을 얻는 순서에 대한 규칙을지정한다.

 

트랜잭션

트랜잭션(Transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

ACID

  • Atomicity : 트랜잭션의 연산은 데이터베이스에 모두 반영되든지 아니면 전혀 반영되지 않아야 한다.
  • Consistency : 시스템의 자원은 트랜잭션의 시작과 완료 시점에 모두 일관성 있고 손상되지 않은 상태여야 한다.
  • Isolation : 개별 트랜잭션의 결과는 트랜잭션이 성공적으로 커밋하기 전까지 다른 열려 있는 트랜잭션에서 볼 수 없어야 한다.
  • Durability : 커밋된 트랜잭션의 결과는 영구적이어야 한다. 강제 종료가 발생해도 유지돼야 한다.

 

트랜잭션 리소스

트랜잭션으로 동시성을 제어할 수 있는 모든 대상. ex) 데이터베이스, 메시지 큐, 프린터, 현금지급기 등

  • 긴 트랜잭션 : 여러 요청에 걸친 트랜잭션
  • 요청 트랜잭션 : 요청이 시작될 때 트랜잭션을 시작하고 요청이 끝날 때 트랜잭션을 끝내는 방법
  • 지연 트랜잭션 : 트랜잭션을 최대한 늦게 여는 것이다. 트랜잭션 밖에서 모든 읽기를 수행하고 업데이트를 시작할 때 트랜잭션을 여는 방식, 일관성 없는 읽기 문제가 발생할 수 있음

 

활동성을 위한 트랜잭션 격리성 저하 - Transaction Isolation Levels

  • Serializable : 여러 트랜잭션을 동시에 실행해도 순서대로 실행했을 때와 동일한 결과를 얻을 수 있는 경우
  • Repeatable read : 한 트랜잭션에서 order 테이블을 읽은 후 다른 트랜잭션에서 order 테이블을 수정하고 coomit을 했다 하더라도 기존 트랜잭션에서 다시 order를 읽으면 이전에 읽은 내용과 동일한 내용을 확인할 수 있다.
  • Read Committed : 한 트랜잭션에서 order 테이블을 읽은 후 다른 트랜잭션에서 order 테이블을 수정하고 commit을 했다. 기존 트랜잭션에서 다시 order를 읽으면 commit된 내용이 반영된 것을 확인 할 수 있다.
  • Read Uncommitted : 한 트랜잭션에서 order 테이블을 읽은 후 다른 트랜잭션에서 order 테이블을 수정하고 commit하지 않아도 기존 트랜잭션에서 다시 order 를 읽으면 수정된 내용이 반영된 것을 확인 할 수 있다.

Repeatable read

Consistent reads within the same transaction read the snapshot established by the first read. This means that if you issue several plain (nonlocking) SELECT statements within the same transaction, these SELECT statements are consistent also with respect to each other.

https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

 

비즈니스 트랜잭션과 시스템 트랜잭션

시스템 트랜잭션

일반적인 트랜잭션, RDBMS 시스템과 트랜잭션 모니터의 지원을 받아 운영된다.

비즈니스 트랜잭션

온라인 은행 시스템의 사용자에게 트랜잭션은 로그인, 계정 선택, 이체 내역 설정, 그리고 최종 이체하는 과정을 포함한다. 이를 비즈니스 트랜잭션이라고 한다.

 

비즈니스 트랜잭션 ACID

  • ACID 속성을 지원하는 확실한 방법은 한 비즈니스 트랜잭션 전체를 한 시스템 트랜잭션 안에서 실행하는 것이다. 하지만 비즈니스 트랜잭션을 완료하는 데 여러 요청을 거치는 경우가 많다. 이는 긴 시스템 트랜잭션 문제를 유발할 수 있다. -> 짧은 트랜잭션 분리 -> 오프라인 동시성 문제를 해결해야 한다.
  • 원자성과 지속성은 비즈니스 트랜잭션에서 가장 쉽게 지원할 수 있는 ACID 속성이다. 이 두 속성은 사용자가 저장을 선택할 때 비즈니스 트랜잭션의 커밋 단계를 하나의 시스템 트랜잭션 안에서 실행하는 방법으로 지원할 수 있다.
  • 격리, 일관성은 비즈니스 트랜잭션에서 적용하기 까다로운 속성이다. 애플리케이션에서 일관성을 유지하기 위해 해야 하는 역할은 모든 사용 가능한 비즈니스 규칙을 적용하는 것이다.

 

오프라인 동시성 제어를 위한 패턴

여러 데이터베이스 트랜잭션에 걸쳐 조작되는 데이터에 대한 동시성 제어를 오프라인 동시성(offline concurrency)이라고 한다.

가능하면 트랜잭션 시스템이 최대한으로 동시성 문제를 맡아서 처리하게 해야한다.

  • 낙관적 오프라인 잠금
  • 비관적 오프라인 잠금
  • 굵은 입자 잠금 : 하나의 잠금으로 여러 관련 객체의 집합을 잠근다.
  • 암시적 잠금 : 프레임워크나 계층 상위 형식 코드에서 오프라인 잠금을 얻을 수 있게 한다.

 

애플리케이션 서버 동시성

애플리케이션 서버 자체의 프로세스 동시성에 대해 알아보자. 이 동시성 문제에서는 서버가 여러 동시 요청을 어떻게 처리하며, 이것이 서버의 애플리케이션 설계에 어떤 영향을 미치는지 고려한다.

  • 세션별 프로세스(process-per-session) : 각 프로세스의 상태가 다른 프로세스로부터 완전히 격리. 리소스 소모가 크다.
  • 요청별 프로세스(process-per-request) : 프로세스 풀을 만들어서 요청에 따라 할당. 리소스 소모가 상대적으로 작다.
  • 요청별 스레드(thread-per-request) : 처리량을 더욱 개선하여 적은 리소스로 처리 가능. 격리되지 않아서 위험
  • 단일 스레드 아파트먼트(single-thread-apartment), 엔터프라이즈 자바 빈즈 : 데이터의 격리된 영역을 단일 스레드에 할당하는 중간 성격의 기법