본문 바로가기
데이터베이스

ANSI SQL-92에 대한 비평 (원문 : A Critique of ANSI SQL Isolation Levels) - 용어, 배경 설명

by 나무후추통 2022. 9. 17.

 위키피디아에서 Snapshot isolation에 대한 글을 읽던 중에 흥미로운 문장을 보았다.

 

그림 1. Snapshot isolation 설명 (출처 : https://en.wikipedia.org/wiki/Snapshot_isolation)

'스냅숏 격리는 ANSI SQL-92 표준의 격리 수준 정의를 비판하는 데 사용되었는데, 이는 SQL 표준이 금지한 "이상"을 나타내지 않으면서 직렬화할 수 없기 때문이다'

 대부분 DBMS 공식 매뉴얼에는 SQL-92 고립 수준에 대한 설명이 있다. 그리고 자바 SQL에도 이 고립 수준을 인터페이스에 명시했다. 근데 '표준이 잘못되었다.'라는 내용이 너무나도 이상했다. 그리고 여러 자료를 찾던 중에 매우 흥미로운 사실을 알게 되었다. 

 

 결론부터 말하면 '표준이 채택한 구현 방식'에서는 ANSI 이상현상이 맞다. 하지만 현대 DBMS의 대세는 '표준이 채택한 방식을 완벽히 구현하지 않고 Snapshot isolation'을 구현한다. 여기서 표준에 없는 이상현상이 등장한다.

 

 이 글은 왜 이러한 차이가 발생했는지를 용어와 배경, 실제 사례, 해결 방법을 정리했다. 다만, 공신력이 있는 전문가가 정리한 것이 아닌 필자가 찾아가며 조사하고 추론한 내용이다. 잘못된 정보일 수 있으니 아래 '참고자료'를 확인하길 바란다.

 

우선 용어와 역사 배경을 알 필요가 있다.

 

1. 용어 설명

1.1 직렬 가능 스케줄

 여러 트랜잭션들을 동시에 수행하기 위해서는 다양한 방법들이 있다. 그중에서 가장 간단하게 여러 개를 수행하는 방법은 '하나씩' 진행하는 것이다. 즉, (트랜잭션 1) -> (트랜잭션 2) ... 순으로 진행하는 것이다. 이러한 트랜잭션들의 실행 순서를 '스케줄'이라고 한다.

 

 하지만 하나씩 진행하다 보면 어떤 쿼리가 Read를 하는 동안 IO 대기 시간이 발생하고 나머지 연산들은 전부 대기하는 상태가 된다. 이를 개선하기 위해서 고민을 한 결과, '쓰기 연산보다 읽기 연산을 진행했을 때 트랜잭션의 고립성을 위반하지 않는다.'라는 사실을 알 수가 있었다. 즉, 읽기 작업은 섞어도 문제가 덜 발생한다. (매우 간략한 설명입니다. 자세한 내용은 아래 레퍼런스를 참고해 주시길 바랍니다.)

 

 그래서 '서로 영향이 가지 않는 선에서 쿼리들을 섞었지만, 하나씩 실행되는 스케줄과 다를 바가 없다.''직렬 가능하다(Serializable)'라고 표현한다. 그리고 이런 스케줄을 '직렬 가능한 스케줄 (Serializable schedule)'이라고 한다.

 

 여러 트랜잭션을 동시에 작업하고 싶다면 가능한 직렬 가능한 스케줄을 만들어 작업하는 것이 효율적일 것이다. 근데, '직렬 가능한 스케줄을 어떻게 만들고 제어할 것인가?'라는 새로운 문제가 등장한다. 그리고 이를 해결할 다양한 방법들이 있다.

(관련 글 : https://namuhuchutong.tistory.com/3)

1.2 Two-phase Locking

그림 2. 다양한 Two-phase Locking 기법들 (출처 : https://de.wikipedia.org/wiki/Sperrverfahren#Zwei-Phasen-Sperrprotokoll)

 락이란 데이터베이스 안에서 데이터 요소, row, page 단위로 상호작용할 수 있고 공유되는 시스템 객체이다. 주로 트랜잭션에서 사용되는 락은 공유 자원(row, page 등)에 부적절한 작업을 막기 위해서 사용된다. 

 

 예를 들어, 어떤 '트랜잭션 1''row-A'에 쓰기 작업을 진행할 예정이라면 '트랜잭션 2''row-A'를 읽으면 안 될 것이다. 이때 작업을 진행할 때 배타적인 권한을 얻을 수 있는 락을 부여하는데 이것을 'exclusive lock'이라고 한다. 이 락은 오직 하나의 트랜잭션만 작업하는 것을 허용한다.

 

 또한, '트랜잭션 1'과 '트랜잭션 2'가 'row-B' 동시에 읽어야 할 경우가 있을 것이다. 이 때는 '트랜잭션 3'이 'row-B'에 작업을 처리하지 못하도록 막아야 하지만 '트랜잭션 1'과 '트랜잭션 2'는 서로 읽을 수 있는 상태가 되어야 한다. 이러한 공유하는 락'shared lock'이라고 한다.

 

Lock Type Shared-lock Exclusive-lock
Shared-lock 공유 가능 공유 불가
Exclusive-lock 공유 불가 공유 불가

 

공유 락은 서로 공유가 가능하지만, 베타 락은 서로 공유할 수가 없다. 이를 표로 나타내면 위와 같다.

 

 만약 '쓰기 작업을 할 때 무조건 베타 락을 쓴다.'라고 규칙을 정하면 어떠한 일이 발생할까? 트랜잭션은 원자성을 보장한다. 즉, 모든 작업이 문제가 없어야 커밋이 되고 아닐 경우 롤백이 된다. 

 

그림 3. Cascading Aborts. (출처 : https://www.gatevidyalay.com/tag/cascading-abort/)

 여러 트랜잭션들이 1 -> 2 -> 3 ... 순으로 베타 락을 얻어 'row-A'에 쓰기 작업을 했을 때, 만약 1이 롤백이 된다면? 나머지 트랜잭션도 전부 롤백이 되어야 한다. 이러한 문제를 'Cascading Aborts(연쇄적 중단)'라고 한다.

 

 위 문제를 해결하고자 등장한 것이 Two-phase locking(2PL)이다. (그 외로 데드락, 기아 상태 등 여러 문제들도 해결할 수 있다.) 락을 획득하는 시점해제하는 시점을 나눠 트랜잭션 동시성을 제어하는 것이다. 2PL은 1970년대부터 꾸준히 개선되어 현재 DBMS에서도 사용되는 검증된 기법이라고 한다.

1.3 MVCC

그림 4. MySQL 언두 로그 동작 방식. (출처: Real MySQL 8.0 1권)

 여기서 의문점이 생긴다. 락은 다른 작업의 접근을 막는데 빈번한 blocking은 CPU를 최대한 활용할 수가 없다. CPU를 최대한 활용하면서 blocking을 하지 않는 동시성 제어 기법은 없을까?

 

 위 문제를 해결하기 위해 등장한 개념이 MVCC이다. 트랜잭션마다 독립된 릴레이션 정보를 복사한 뒤, 작업을 복사본에서만 처리하고 성공적으로 커밋된 결과만 데이터베이스에 반영하는 방법이다. 만약 중간에 다른 트랜잭션이 '반영이 안된 데이터'를 읽을 경우 '다양한 버전 중에서 적절한 버전을 읽도록' 처리한다.

 또한 처리된 작업이 충돌할 수가 있는데 커밋하기 전에 미리 검증하는 과정이 필요하다. 검증이 끝날 때까지는 갱신 내용은 반영이 안 되며 문제가 있다면 해당 트랜잭션은 롤백이 된다.

 

 MVCC 기법은 버전을 관리할 공간과 불필요한 버전을 삭제해야 한다. 하지만 락을 필요하지 않아 빠른 속도를 보장하고 메모리 가격의 하락, 많은 데이터를 읽는 트랜잭션(주로 데이터 분석)이 짧은 갱신 트랜잭션(일반적인 트랜잭션)을 방해하지 않는다는 점에서 매력적이라고 할 수 있다. 현대 DBMS 대부분은 MVCC를 구현한 Snapshot Isolation을 사용한다.

 

1.4 표준에 대한 비판의 시작

 근데 여기서부터 문제가 생긴다. '동시성 제어' 구현 방법이 다양한데, '직렬 가능성'을 나눈 '고립 수준'은 구현 방법마다 동일하다고 할 수 있을까? 해당 논문에서는 이것이 '모호하다'라고 표현한다. 이 근거로 'Snapshot isolation이 직렬 가능성을 보장하지 않는다.'를 제시하여 더 상세한 고립 수준을 제안하는 논문이다.

 

그렇다면 ANSI는 왜 표준에 대한 비판이 나오도록 만들었을까? 

 

2. 역사 배경

 관계형 데이터베이스는 누가 처음 만들었을까? SQL 구현체는? RDBMS는 언제 처음 나왔을까? 재미있게도 이 사건들이 모두 IBM과 연관이 있다는 것이다. 이러한 배경이 SQL 표준에 많은 영향을 주었다고 생각한다.

2.1 관계형 데이터베이스 - 1970년대 ~

 

그림 5. 에드거 F, 커드 (출처 : https://en.wikipedia.org/wiki/Edgar_F._Codd)

 관계형 데이터베이스를 논할 때는 '에드거 F, 커드 박사'가 필히 등장한다. 그는 IBM 연구소에서 일하는 동안 RDB에 매우 중요한 업적을 남긴 컴퓨터 과학자이다. 1970년 'A Relational Model of Data for Large Shared Data Banks'라는 논문에서 관계형 모델이 소개가 되었고, 이후 1974년 IBM은 관계형 모델링을 적용한 최초의 RDBMS 'System R'을 구현하였다. (당시 커드 박사와 IBM 사이에 마찰이 있었고 System R은 독립적으로 개발되었다고 한다.)

 

 여기서 관계형 모델링을 조작하기 위한 언어 'SQL(SEQUEL)'이 처음으로 구현되었다. System R은 우수한 트랜잭션 처리 능력을 보였는데, 여기서 '우수한 트랜잭션 처리'가 바로 위에서 언급한 'two-phase locking'이 적용된 것이다.

 

 System R은 이후 많은 RDBMS에 영향을 주었고 locking 기법 또한 많은 개선이 이루어져 우수한 성능을 가지게 되었다.

 

2.2 MVCC의 등장 - 1980년대

 락을 사용하는 구현은 데드락의 발생 위험을 가진다. 또한 수많은 트랜잭션들이 락을 요청할 텐데 이것을 관리할 '락 매니저'도 필요하다. 만약 데드락이 발생하면 트랜잭션의 원자성을 보장하기 위해서 실패 지점을 찾고 롤백할 트랜잭션을 선택해야 한다. 그 외 고려할 것이 많다.

 

그림 6. 데이비드 P, 리드. MVCC와 UDP, 초창기 TCP 개발에 참여한 컴퓨터 과학자 (출처 : https://en.wikipedia.org/wiki/David_P._Reed)

 그중에서 분산 데이터베이스 시스템의 '락을 어떻게 공유할 것인가?' 문제가 있다. 이에 대한 해결책으로 MVCC 개념이, 1981년 'Concurrency Control in Distributed Database Systems' 논문에서 제안이 되었다. (첫 MVCC의 개념은 1978년 데이비드 P, 리드 박사의 'Naming and Synchronization in a Decentralized Computer System'에서 제안되었고 위 논문은 이를 발전시킨 개념이라고 한다.)

 

 MVCC를 구현한 DBMS는 1984년 Interbase로 알려진다. 놀랍게도 여기서 등장한 MVCC 구현은 리드 박사의 논문과 매우 유사하게 구현이 되었는데, Interbase 개발을 책임진 짐 스타키 박사는 해당 아이디어를 Prime 사에서 개발된 DBMS에서 얻었다고 말했다. 예전부터 락 기법을 대체할 동시성 기법들이 많이 연구되었는데 우연히도 두 사람이 같은 개념에 도착했다고 볼 수 있겠다.

 

2.3 표준 정의 - 1986년 ~ 1992년

 System R 이후 관계형 모델은 그 가치를 입증받아 많은 기업들이 RDBMS를 만들어 판매하기 시작했다. 이때, Relational Software (현 오라클)은 1970년대 미 정부 기관과 해군, CIA와 계약을 체결했다고 한다.

 

 이렇게 우후죽순 늘어나는 시스템에 당연히 표준 정의가 필요할 것이고 이를 위한 첫 SQL 표준이 1986년에 정의가 되었다. 이후 1992년에 주요 개선을 이룬 SQL-92가 등장하였고 꾸준히 개선된 표준이 나오고 있다. SQL 표준은 굉장히 방대하며 이를 전부 구현한 DBMS는 하나도 없다고 한다.

 

아래부터는 제 개인적인 생각입니다.

 

 SQL-92에는 Isolation Level에 대한 언급이 있는데 흔히 알려진 Dirty Read, Non-Repeatable Read, Phantom이 여기에서 등장한다.

 

 여기서 만약 여러분들이 표준을 정의한다면 어떤 동시성 제어 방법을 채택하여 고립 수준을 나눌 것인가?

 

(1) 10년 이상 개선되어 검증된 Lock 제어 기법

(2) 아직 나온 지 얼마 안 된 MVCC 기법

 

 보수적인 입장이라면 (1)을 선택할 것이다. 또한 관계형 모델, SQL, 최초의 RDBMS가 모두 IBM 연구소와 관련 있다. ANSI는 비영리 표준 개발을 감독하는 기관이다. 여기에 IEEE(전자전기 기술자 협회)가 소속되어 있는데, 당시 IBM 연구자들이 이 기관들과 관련이 있었을 것이다.

 

 즉, (보수적으로 접근하는 표준 기관) + (IBM 연구소에서 나온 관계형 모델과 RDBMS, SQL) + (연구자라면 당연히 속해 있을 학회) + (충분히 검증된 locking 기법) + (그 외 IBM 영향을 받은 상용 RDBMS)이 SQL 표준에 많은 영향을 주었다고 생각한다.

 

 현대 DBMS는 위 SQL-92 표준을 전부 구현하지도 않고 대부분 메이저 DBMS는 Snapshot isolation을 채택해 구현했다. 당연하게도 (표준에 있는 이상현상) != (메이저 DBMS 발생하는 이상현상) 일 수밖에 없다고 생각한다.

 

3. 주관적인 결론

 'A Critique of ANSI SQL Isolation Levels'의 등장 배경은 아마 위와 같은 이유도 있을 것이다. 하지만 표준으로 정의된 이상현상이 없다고는 할 수 없다. 단지 '모호하다'라는 것이 주된 비판 내용이다. 그리고 SQL 표준을 완벽히 구현한 DBMS도 없다. 그래서 수정할 필요성을 느끼지 못하는 것이 아닐까?

 

 단, 필자의 개인적인 생각이다. 데이터베이스를 전공하신 전문가 분들은 잘못된 부분을 지적해 주시길 바란다.

 

참고자료