파이썬 코드에서 동기화 문제 (경쟁상태,데드락) 등 어려운 버그에 대한 정확한 진단이 필요로 할 때가 있다.
파이썬에서의 디버깅에 대해서 공부해보자.

파이썬에서 디버깅이란(PDB) 

pycharm 에서 디버깅

파이썬에서 사용하는 디버깅 방법들

데드락 로깅모듈



https://eng.uber.com/mysql-migration/   나중에 읽어보자.!! 

반박 및 논쟁글은 여기 => https://news.ycombinator.com/item?id=12166585


Conclusion

Postgres served us well in the early days of Uber, but we ran into significant problems scaling Postgres with our growth. Today, we have some legacy Postgres instances, but the bulk of our databases are either built on top of MySQL (typically using our Schemaless layer) or, in some specialized cases, NoSQL databases like Cassandra. We are generally quite happy with MySQL, and we may have more blog articles in the future explaining some of its more advanced uses at Uber.

시작하기

sudo su postgres -c "/Library/PostgreSQL/9.4/bin/pg_ctl -m fast
-D /Library/PostgreSQL/9.4/data start"


멈추기

sudo su postgres -c "/Library/PostgreSQL/9.4/bin/pg_ctl -m fast
-D /Library/PostgreSQL/9.4/data stop"


동작확인

netstat -a | grep 5432 



PostgreSQL vs MySQL





* 참고로 우버는 PostgreSQL에서 MySQL로 갈아탔습니다. -> 참고  )https://mysqlko.wordpress.com/2016/08/05/mysql-vs-postgresql-uber/    이런것은 해당 버전에 따라서 결과는 달라지니 최근 정보로 잘 살펴봐야겠습니다.

PostgreSQL 와 
MySQL에는 근본적인 차이점이 분명히 있습니다.  두 시스템 간의 장,단점을 잘 파악해서 자신의 시스템에 사용 했으면 하는데 도움을 드리고자 유사성과 차이점에 대한 요약을 준비했습니다.


Open Source 

PostgreSQL

MySQL

PostgreSQL 은 PostgreSQL 글로벌 개발그룹에 의해 개발되고 있으며, 여러 회사와 개인적 기여자들로 이루어진 다양한 그룹이 존재합니다. 자유 오픈소스 소프트웨어이며 PostgreSQL은 PostgreSQL 라이선스로 릴리즈 되었습니다. BSD나 MIT 와 비슷한 매우 자유로운 라이선스입니다.

The MySQL development project has made its source code available under the terms of the GNU General Public License, as well as a variety of proprietary agreements.

 

지금은 오라클 소유이며, 여러 사용료를 지불해야하는 특정 에디션도 제공합니다.


ACID Compliance 

PostgreSQL

MySQL

PostgreSQL은 처음부터 ACID 를 지향했으며 모든 요구사항을 만족합니다.

MySQL는 InnoDB 와 NDB Cluster Storage engines 를 사용 했을 때만 준수하고 있습니다.


SQL Compliance

PostgreSQL

MySQL

PostgreSQL은 SQL 을 폭넓게 준수하고 있습니다.The level of conformance for each feature is clearly laid out in Appendix D of the manual, and any deviations are clearly documented in the “Reference” section of the PostgreSQL manual.


Extracted from documentation
:

PostgreSQL supports most of the major features of SQL:2011. Out of 179 mandatory features required for full Core conformance, PostgreSQL conforms to at least 160. In addition, there is a long list of supported optional features. It might be worth noting that at the time of writing, no current version of any database management system claims full conformance to Core SQL:2011.

MySQL 는 일부만 준수합니다. (e.g does not support CHECK constraints).


Extracted from documentation
:

One of our main goals with the product is to continue to work toward compliance with the SQL standard, but without sacrificing speed or reliability. We are not afraid to add extensions to SQL or support for non-SQL features if this greatly increases the usability of MySQL Server for a large segment of our user base.


Replication

PostgreSQL

MySQL

PostgreSQL supports Master-Standby replication and introduced significant enhancements producing extremely fast WAL processing that results in almost real-time replication and hot standby capabilities for standby servers.

 

Replications offered by PostgreSQL:

  • Single master to one standby

  • Single master to multiple standbys

  • Hot Standby/Streaming Replication

  • Bi-Directional replication

  • Logical log streaming replication

  • Cascading replication

MySQL supports Master-Standby replication.

 

Replications offered by MySQL:

  • Single master to one standby

  • Single master to multiple standbys

  • Single master to one standby to one or more standbys

  • Circular replication (A to B to C and back to A)

  • Master to master


Performance

PostgreSQL

MySQL

PostgreSQL is widely used in large systems where read and write speeds are crucial and data needs to validated. In addition, it supports a variety of performance optimizations that are available only in commercial solutions such as Geospatial data support, concurrency without read locks, and so on (e.g. Oracle, SQL Server).


Overall, PostgreSQL performance is utilized best in systems requiring execution of complex queries.


PostgreSQL performs well in OLTP/OLAP systems when read/write speeds are required and extensive data analysis is needed.


PostgreSQL also works well with Business Intelligence applications but is better suited for Data Warehousing and data analysis applications that require fast read/write speeds.

MySQL is a widely chosen for web based projects that need a database simply for straightforward data transactions. It is common, though, for MySQL to underperform when strained by a heavy loads or when attempting to complete complex queries.

 

MySQL performs well in OLAP/OLTP systems when only read speeds are required.

 

MySQL + InnoDB provides very good read/write speeds for OLTP scenarios. Overall, MySQL performs well with high concurrency scenarios.

 

MySQL is reliable and works well with Business Intelligence applications, as business intelligence applications are typically read-heavy.


Security

PostgreSQL

MySQL

PostgreSQL has ROLES and inherited roles to set and maintain permissions. PostgreSQL has native SSL support for connections to encrypt client/server communications. It also has Row Level Security.

 

In addition to this, PostgreSQL comes with a built-in enhancement called SE-PostgreSQL which provides additional access controls based on SELinux security policy. More details here.

MySQL implements security based on Access Control Lists (ACLs) for all connections, queries, and other operations that a user may attempt to perform. There is also some support for SSL-encrypted connections between MySQL clients and servers.


Cloud Hosting

PostgreSQL

MySQL

Supported by all major cloud service providers, including Amazon, Google, & Microsoft.

Supported by all major cloud service providers, including Amazon, Google, & Microsoft.


Community Support

PostgreSQL

MySQL

PostgreSQL has a very strong and active community that constantly improves existing features while its innovative committers strive to ensure it remains the most advanced database with new cutting-edge features and security.

MySQL has a large community of contributors who, particularly following the acquisition by Oracle, focus mainly on maintaining existing features with some new features emerging occasionally.


Concurrency Support

PostgreSQL

MySQL

PostgreSQL tackles concurrency efficiently with its MVCC implementation, which achieves very high levels of concurrency.

MySQL only has MVCC support in InnoDB.


NoSQL Features/JSON Support

PostgreSQL

MySQL

PostgreSQL supports JSON and other NoSQL features like native XML support and key-value pairs with HSTORE. It also supports indexing JSON data for faster access. (참고로 TimescaleDB 라는PostgreSQL 기반의 시계열 NoSQL DB도 있다.)

MySQL has JSON data type support but no other NoSQL feature. It does not support indexing for JSON.


Materialized Views/Temporary Tables

PostgreSQL

MySQL

Supports materialized views and temporary tables.

Supports temporary tables but does not support materialized views.


Geospatial Data Support

PostgreSQL

MySQL

PostgreSQL supports Geospatial data via the PostGIS extension. There are dedicated types and functions for geospatial data, available directly at database level, making analysis and coding easier for developers.

Geospatial data support is built in.


Programming Languages Support

PostgreSQL

MySQL

PostgreSQL supports a wide variety of programing languages including: C/C++, Java, JavaScript, .Net, R, Perl, Python, Ruby, Tcl and others; it’s even possible to run user-supplied code in separate processes (i.e. running as background workers).

Some support for server side programming in a single language which is not extensible.


Extensible Type System

PostgreSQL

MySQL

PostgreSQL has several features dedicated to extensibility. It is possible to add new types, new functions, new index types, etc.

No support for extensibility.


Comparison Summary

Feature

PostgreSQL

MySQL

Open Source

Completely Open source

Open source, but owned by Oracle and offers commercial versions

ACID Compliance

Complete ACID Compliance

Some versions are compliant

SQL Compliance

Almost fully compliant

Some versions are compliant

Concurrency Support

MVCC implementation supports multiple requests without read locks

Support in some versions.

Security

Secure from ground up with SSL support

SSL support in some versions

NoSQL/JSON Support

Multiple supported features

JSON data support only

Access Methods

Supports all standards

Supports all standards

Replication

Multiple replication technologies available:

 
  • Single master to one standby

  • Single master to multiple standbys

  • Hot Standby/Streaming Replication

  • Bi-Directional replication

  • Logical log streaming replication

Standard master-standby replication:

 
  • Single master to one standby

  • Single master to multiple standbys

  • Single master to one standby to one or more standbys

  • Circular replication (A to B to C and back to A)

  • Master to master

Materialized Views

Supported

Not supported

Temporary Tables

Supported

Supported

GeoSpatial Data

Supported

Supported

Programming Languages

Supported

Not supported

Extensible Type System

Supported

Not supported


*  CAP 은 네트워크로 분산된 DB , FileSystem, state machine (zookeeper  같은) 에서 의미를 가짐.


여담 ) 

NoSQL 이란게 유비쿼터스,클라우드,빅데이터 같은 상품명 처럼 휩쓸고 지나간 자리에는 더 이상 NoSQL 이라는 말 대신 각각 DB 들의 이름 자체로 회자되고 있습니다. "No SQL" SQL 이 아닌, "Not Only SQL" SQL 만이 아닌 등  여러 말들이 많았는데 (NewSQL 도 있고)  이제 SQL 지원은 많은 DB에서 하고 있기때문에 SQL 지원여부는 차이점이 아닌게 되버렸고, 단지 관계형이냐 아니냐 혹은 분산 지향등으로 나누다가, 이제  RDB vs No-RDB 가 아닌  A vs B vs C vs D ...등으로 각각 DB 의 개성을 인정(?) 해주는 분위기로 바뀌었습니다. 

DB 를  단지 Key-value 냐 RDB 냐 IMDB 냐에 그치지 않고 클라우드(분산)환경에서 어떤식으로 운용/서비스 되는지에 관해  촛점을 두고 바라 보는게 매우 중요해졌습니다. 이제 부터 말할 CAP 이해를 통해 DB 선택의 주요 지표로서의 역할을 재고 해보셨으면 합니다. 

* 사실 이 글 제목을 초보자님들을 위한이라고 썼지만,  사실 우리나라에 CAP 에 대해 잘 아는 개발자가 있을까 하는 생각이듭니다. (물론 저도 잘 모릅니다.) 도발적이긴 하지만 그렇다고 봅니다. 이유는 CAP 이 어려워서라기 보단 몬가 현재 분산환경과 그 이론 자체에 큰 괴리가 있어보이며,  CAP 자체의 구분 조차 명확하지 않기 때문입니다.  결국 위에 CAP 이해를 통해 DB 선택을 고려해보란 말도 어떻게 보면 '헛소리' 에 가까울수도 있습니다.  CAP 을 현재 DB 류에 1대1 매핑시키는거 자체가 어려워보이기 때문인데.. 근데 그렇게 생각한다면  이 글 왜 썼냐? 라고 묻는다면.... 잘 모르겠네요. 답이 명확하지 않은 주제라도 의미가 있지 않을까 생각하며 만약 DB 클러스터링을 선택할때 해당 제품이 말하는바와 무엇을 과대광고(high  availability!!) 하고 있구나 라는 통찰을 얻는데 도움이 되지않을까합니다.




1. 그림으로 보는  분산 모델 (기초)

             * 꿈을 이루어가는 소녀들인 도연이와 미나는 DB 하나를 같이 사용합니다. 

             *  DB 가 돌아가는 서버(노드) 가 망가져서 둘은 DB 를 사용하지 못하게 됬습니다. 

              *  DB 를 하나 더  설치해서 2개를 사용합니다. 

              *  하나의 DB 가 고장났지만, 나머지 하나를 사용 할 수 있게 되었습니다.

               *  복제본(Replica) 란  A 와 B 가 정확히 동일한 데이터를 가지고 있는것입니다.
                                 (하나가 고장나도 나머지 하나로 사용 할 수 있습니다. 데이터 안정성을 높입니다)

               *  파편본(Shard) 란  A 의 데이터중 일부를 빼서 그것을 B에 가져다 놓는것입니다.
                                    (A 에 너무 많은 데이터가 있을 경우에 B에 옮겨놓습니다. 양을 분산합니다.)

       결국 복제본과 파편본을 여러 컴퓨터에서 사용하면 데이터안정성과 데이터 크기를 분산시킬수 있게됩니다.

  * 쿼럼은 분산시스템을 관리하는녀석이라고만 생각하면 됩니다.
   * 도연이가 A  에 1000원을 더 입금하였는데,  B 에 갱신이 안되어서 미나는 2000원으로만 보입니다. 

* 미나한테도 3000원으로 보이려면 즉시 A 는 데이터를 B 에 업데이트 해야합니다.
* 이 업데이트는 LB 라는 녀석이 해줄 수 도 있고, A 랑 B 가 서로 간에 해 줄 수 도 있습니다. 개발하는 사람 맘.
* A 가 B 로 2000원을 3000원으로 업데이트하는 시간이 0 일 수는 없습니다. 그 시간동안 미나를 기다리게 해서
  미나도 3000원으로 보이게하는것을  "일관성있는 시스템이다"  라고 말합니다.
* 근데 real life 에서 미나를 기다리게 하는 시간이 너무 지나치게 된다면  "가용성" 은 떨어 진다고 봅니다.

*  B 라는 노드가 망가져도,  A 노드와 C 노드로 도연이와 미나에게 서비스 해줄 수 있습니다. "가용성 있다" 라고 합니다. 
* 쿼럼은 B 노드를 다시 re-join 시켜 줄 것입니다.
* B 가 다시 re-join 되었을때, A 와 데이터가 다를 수 있고, 그 다른  B의 데이터를 미나가 보게되면 이 시스템은     "일관성" 이 없는 시스템이라 할 수 있습니다. 


*  B 라는 노드가 망가진것이 아니라, A 와 B 를 연결해주는 네트워크가 고장났습니다. 
* A 도 작동하고, B 도 작동합니다. 다만 둘간의 커뮤니케이션이 안되기때문에 둘이 동일한 데이터를 갖기 
 힘들어졌습니다. (다른 노드를 경유해서 공유를 하면 됩니다) 
* 만약 A 와 B 가 네트워크가 분단되어서 서로 다른 데이터가 있을때 (제 3의 노드를 통해서 일관성을 유지하지 못할때)  도연이는 A 를 , 미나는 B 디비를 사용한다면 , 둘은 서로 다른 데이터를 보게 됩니다. 즉 "일관성" 이 없어지게 됩니다. 
* "일관성" 을 지키기위해서 네트워크가 복구될때까지 기다린다면 "가용성" 이 떨어지게 됩니다. 가용성이란건 정확히 규정하는게 정해져있지 않지만 , 즉시 서비스 결과를 도연이와 미나는 받을 수 있는걸 말합니다. (실패여부라도 즉시, 여기서 즉시에 해당하는 시간은 정의에 따라서 달라집니다.)


2. CAP 이론  

 
CAP 이론에 관한 블로그글이나 많은 책에서 말하는것들은 대개 비슷합니다. 이것만 가지고는 CAP이 대략 어떤건지 알게되겠지만 시원하게 이해하기는 어렵긴합니다. (함정에 빠지면 굉장히 난해함) 

먼저 이론을 소개하면 아래와 같습니다. 위에 그림으로 설명한 것과 똑같은 내용입니다.

CAP이론은 간단하게 말하면 

“분산 시스템에서는  3개 속성을 모두 가지는 것이 불가능하다!” 이다. 각 속성은 아래와 같이 3가지이다.

Consistency (일관성)

  • 우선.. CAP이론에서 말하는 Consistency는 ACID의 ‘C’가 아니다! ACID의 ‘C’는 “데이터는 항상 일관성 있는 상태를 유지해야 하고 데이터의 조작 후에도 무결성을 해치지 말아야 한다”는 속성이다.
  • CAP의 ‘C’는 “Single request/response operation sequence”의 속성을 나타낸다. 그 말은 쓰기 동작이 완료된 후 발생하는 읽기 동작은 마지막으로 쓰여진 데이터를 리턴해야 한다는 것을 의미한다.
  • 모든 노드가 같은 시간에 같은 데이터를 보여줘야 한다. (저장된 데이터까지 모두 같을 필요는 없음)

Availability (가용성)

  • 흔히 보는 단어이고 의미도 크게 혼동될 이유가 없어보인다. “특정 노드가 장애가 나도 서비스가 가능해야 한다”라는 의미를 가진다.
  • 데이터 저장소에 대한 모든 동작(read, write 등)은 항상 성공적으로 리턴되어야 한다.
  • 명확해 보이는 단어이기는 하지만 분산 시스템에서의 특징을 말하는 것이기 때문에 “서비스가 가능하다”와 “성공적으로 리턴”이라는 표현이 애매하다. 얼마동안 기다리는 것 까지를 성공적이라고 할 수 있느냐에 대한 문제가 남아있다. “20시간정도 기다렸더니 리턴이 왔어! Availability가 있는 시스템이야!”라고 할 수 없기 때문이다.
  • 다시한번 “성공적으로 리턴”에 대해서 보면 모든 동작에 대해서 시스템이 “Fail!!”이라는 리턴을 성공적으로 보내준다면 그것을 Availability가 있다고 해야 하느냐에 대해서도 애매하다. CAP를 설명하는 문서들 중 “Fail!!”이라고 리턴을 하는 경우도 “성공적인 리턴”이라고 설명하는 것을 보았다.

Partitions Tolerance (분리 내구성)

  • 원래는 Tolerance to network Partitions인데 보통은 Partition-tolerance라고도 한다.
  • 노드간에 통신 문제가 생겨서 메시지를 주고받지 못하는 상황이라도 동작해야 한다.
  • Availablity와의 차이점은 Availability는 특정 노드가 “장애”가 발생한 상황에 대한 것이고 Tolerance to network Partitions는 노드의 상태는 정상이지만 네트워크 등의 문제로 서로간의 연결이 끊어진 상황에 대한 것이다

3가지중 2개만 지원한다는것은 MySQL 을 예로 들면 MySQL 은 CA 이며  P 는 지원안한다는 겁니다.  노드가 하나 망가져도 서비스 할 수 있는데 ,  노드간에 네트워크가 장애가 발생했다고  서비스를 못해? 이게 말이 되나?    이렇게 핵깔려 할 수 도 있는데.. 이런게 함정입니다. 저도 빠졌습니다. 


보통 책이나 블로그들에선 이론 설명 후에 그림으로 현재  DB 들의 지향하는바에  대해서 보여줍니다.


위에 말했다시피  MySQL 가  CA 에 있는것에  빠지면 혼동을 겪게되는데, MySQL 은 CA 이기도 하지만 클러스터링 방법에 따라서 다양하게 변화 할수  있음을 인지하셔야합니다. 그래서 결국 각 제품의 클러스터링 전략 별로 살펴보는게 도움이 됩니다.  3 장에선  각 제품들의 CAP 특성에 대해서 조사한 결과를 나열해 보겠습니다.



3. CAP 실제  


3-1. 카산드라, 카우치DB  (AP)


  가. C가 없기때문에 카산드라는 사용자 A,B의 요청에 다른 대답을 할 가능성이 생긴다.

  나. A 가 있기때문에 카산드라는 하나의 노드에 문제가 생겨도 바로바로 리턴한다. 

      어차피 여러노드간에 데이타가 좀 달라도 상관없기때문에 (C 를 포기했으니..)

  다. P 가 있기때문에 카산드라는 네트워크가 끊어져도 , 서비스에 문제가 없다. 

      어차피 여러노드간에 데이타가 좀 달라도 상관없기도 하며 (C를 포기했으니)

      커넥션에 문제있는 노드는 제거되고, 잘되는 놈으로만 서비스한다. 


3-2. 몽고DB (CP) 


   가.  C 가 있기때문에 몽고DB 는 모든 사람에게 동일한 대답을 해주려고 해야한다.

   나.  동일한 대답을 해줘야하기때문에   A 노드와 B 노드가 같은 값을 가져야하는데 

         그러기위해 동기화 시간이 생기기 마련이다.  좀 느릴수가있다. A 가 없으니 상관없다.

   다.  P 가 있기때문에 네트워크 단절이 되도 서비스는 가능해야한다. 음 어렵네..

        네트워크 단절이되어서 일관성에 문제가 생길듯한데, 제 3의 노드를 통해서 일관성을 지킨다는 뜻인듯?

   라.  결국 A 가 없기때문에 P 네트워크 단절이 고쳐질때까지 대기타야하며, A 가 없으니깐

         대기타도 괜찮음으로 결론 -.-a 

3-3. Percona Cluster (CA)

   가.  이것은 CA 속성을 가지고 있다.
   나.  이중 커밋 프로세스에 따라 일관성을 지원한다. 
   다.  노드간 네트워크가 깨지면 일관성을 유지할수 없기때문에, 그룹 커뮤니케이션은 매우 중요하다.
   라. 온라인 쇼핑몰같은곳에 어울린다. 금액을 지불하는 고객에게 일관성 있는 서비스를 제공해야한다.

3-3. MySQL NDB  (AP)

   가.  이것은 AP 속성을 가지고 있다.
   나.  MySQL 서버들은 각각 독립노드로써 기능을 유지한다. 만약 분리된 노드간의 네트워크가 단절되었을때         데이터는 그 노드들 사이의 일관성을 유지시켜주지 못한다. 
   다.  소셜 싸이트에 어울린다. 쇼셜사이트에서는 수많은 서버들간에 네트워크가 문제가 생겼더라도 즉시 즉         시 서비스를 해야하며, 내가 올린 데이터가 상대방에게 즉시 보여질 필요보다는 서비스가 죽었다는 느낌         을 안주는게 중요하다.

3-4. Galera Cluster for MySQL (CA)

   가.  Galera 클러스터는 데이타 안정성과 일관성에 주력한다. 이 말은 클러스터 가용성(A) 과
        분리내구성(P) 사이에 트래이드오프 작용이 있다는것. 이 말은 불안정한 네트워크를 사용했을때,                   
evs.suspect_timeout  와 evs.inactive_timeout  이러한 값이 높냐? 낮냐에 따라서 오래
        지연시킴으로 해서 가용성이 악화되거나,  빠른 실패를 동반하여 네트워크 내구성을 떨어뜨릴 수           있게된다.
   
   나. '가' 에서의 주요 의미는  evs.suspect_timeout 시간을 잘못된 노드를 탐지하기 위한 최소의
        시간으로 설정하것은  일관성 제약에 기인한  클러스터의 가용성을 높힌다는 뜻이다. 

3-5. Oracle RAC    (CP)

   가.  오라클 RAC 은 일관성 및 분리내구성을 가지고 있다. 만약 SQL node 중 하나를 잃더라도 여전히 잘 동작할 것이며, 데이터를 모든 시스템에 나눌것이다.
   나. 명확하게 CP 다. 라고 말하는 문서는 못찾았음.  

3-6.  Zookeeper   (P)

   가.  엄격하게 얘기하면  디폴트로 일관성도 없고, 가용성도 없다. 단지 P
   나.  만약 옵션으로  sync를 호출하면  CP ,  read only 모드를 사용하면 AP
   다.  주키퍼 잣대로 위의 다른 DB 를 보면 대부분 C 도 아니고 A 도 아니다. 


4. CAP 논란 

https://martin.kleppmann.com/2015/05/11/please-stop-calling-databases-cp-or-ap.html
https://www.percona.com/live/mysql-conference-2013/sessions/cap-theorem-two-out-three-aint-right
https://www.quora.com/Distributed-Databases-What-are-the-best-known-exceptions-for-CAP-theorem
http://www.stucharlton.com/blog/archives/2010/10/confused-cap-arguments.html



P.S 

* 제가 분산이나 DB CAP 이론만 파온 전문가는 아니기때문에 이 글을 100% 신뢰를 하시면 아니 됩니다. 
* CAP 이론에 대한 논란은 꽤 많은거 같습니다. 위의 논란 부분을 읽는거 추천합니다.

'RDBMS (PostgreSQL)' 카테고리의 다른 글

[PostgreSQL] mac 에서 시작하고 멈추기  (0) 2016.08.28
왜 PostgreSQL 을 선택 했나? ( PostgreSQL vs MySQL ) [번역]  (0) 2016.08.02
JDBC 트랜잭션  (0) 2015.07.30
트랜잭션 인사이드  (0) 2015.07.01
PostgreSQL 조인  (0) 2015.05.13

JDBC 기본 트랜잭션 


JDBC API의 Connection 객체는 commit() 메소드와 rollback() 메소드를 제공한다.

기본적으로 Connection 객체에 setAutoCommit 이란 메소드가 있는데 기본값이 true 로 설정이 되어 있다. 

하나의 쿼리당 자동시작~자동커밋이 일어난다는 이야기이다.

그러나  여러 개의 쿼리 문장이 하나의 작업으로 수행되어야 할경우에  각각의 문장이  자동으로 작동되지 못하게 해야한다.

오토커밋이 자동으로 작동되지 못하게 하려면 setAutoCommit(false); 로 지정해야 한다.



자 그럼 begin() 은 어딨느냐?? 


AutoCommit = true 일경우



암시적으로 각각의 액션시 (각각 SQL 문에서)  자동으로 BEGIN()



AutoCommit = false  일경우



이건 좀 희안하다. setAutoCommit(false) 를 호출하여 오토커밋코드를 꺼넣으면 시스템은 이 호출과 동시에 BEGIN 요청을 하게


된다. 이후의 commit() 과 rollback() 호출은 해당 SQL 을 호출한 뒤에 다시 BEGIN() 을 요청하게된다. 




그 밖에 setAutoCommit 을 통해 모드를 바꾸면  동시에 commit() 1회 발생한다.






JDBC Savepoint  

번역: http://www.tutorialspoint.com/jdbc/jdbc-transactions.htm


새로운 JDBC 3.0 Savepoint interface 는 추가적인 트랜잭션기능을 제공하는데 . Oracle's PL/SQL 같은 많은 현대 DBMS 에서는 그들의 환경내에 savepoints 를 지원하고있다.  당신이 세이프포인트를 세팅할때 트랜잭션안에 논리적 롤백 포인트를 정의할수있다.

만약 에러가  세이프포인트뒤에 발생하면 , 모든 변경사항 또는  세이브포인트 후에 만들어진 변화를 undo 를 하기위해  롤백메소드를  사용할수있다. 

Connection 객체는 2가지 새로운 메소드로 세이브포인트를 요리할수있도록 도와준다.

  • setSavepoint(String savepointName):  새로운 세이브포인트를 정의하고 그 객체를  리턴받는다.

  • releaseSavepoint(Savepoint savepointName): 세이브포인트 삭제. 

 rollback (String savepointName) 는  특정 세이브포인트로 작업을 롤백한다.


다음 예를 통해 살펴보자.

try{ conn.setAutoCommit(false); Statement stmt = conn.createStatement(); // Savepoint 설정 Savepoint savepoint1 = conn.setSavepoint("Savepoint1"); String SQL = "INSERT INTO Employees " + "VALUES (106, 20, 'Rita', 'Tez')"; stmt.executeUpdate(SQL); String SQL = "INSERTED IN Employees " + "VALUES (107, 22, 'Sita', 'Tez')"; stmt.executeUpdate(SQL); conn.commit(); }catch(SQLException se){ conn.rollback(savepoint1); }


문제가 생기면 savepoint1 까지 롤백된다. insert 문은 작동안하겠지


좀더 구체적인 예를 살펴봅시다.

//STEP 1. 관련 패키지 임포트 import java.sql.*; public class JDBCExample { static final String JDBC_DRIVER = "com.mysql.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost/EMP"; static final String USER = "username"; static final String PASS = "password"; public static void main(String[] args) { Connection conn = null; Statement stmt = null; try{ //STEP 2: JDBC 드라이버 등록 Class.forName("com.mysql.jdbc.Driver"); //STEP 3: connection 맺고 System.out.println("Connecting to database..."); conn = DriverManager.getConnection(DB_URL,USER,PASS); //STEP 4: 오토 커밋 않하게함 conn.setAutoCommit(false); //STEP 5: statment 생성 System.out.println("Creating statement..."); stmt = conn.createStatement(); //STEP 6: 모든 데이터 가져오기 String sql = "SELECT id, first, last, age FROM Employees"; ResultSet rs = stmt.executeQuery(sql); System.out.println("List result set for reference...."); printRs(rs); // STEP 7: 세이브 포인트1 설정 Savepoint savepoint1 = conn.setSavepoint("ROWS_DELETED_1");  

// STEP 8: ID = 110 삭제

System.out.println("Deleting row...."); String SQL = "DELETE FROM Employees WHERE ID = 110"; stmt.executeUpdate(SQL);  

// 엇.. 잘못된 놈을 지웠네.. //STEP 8: Rollback the changes afetr save point 2. conn.rollback(savepoint1); // STEP 9: 세이브 포인트 2 설정 Savepoint savepoint2 = conn.setSavepoint("ROWS_DELETED_2");  

System.out.println("Deleting row...."); SQL = "DELETE FROM Employees WHERE ID = 95"; stmt.executeUpdate(SQL); //STEP 10: Now list all the available records. sql = "SELECT id, first, last, age FROM Employees"; rs = stmt.executeQuery(sql); System.out.println("List result set for reference...."); printRs(rs); //STEP 10: Clean-up environment rs.close(); stmt.close(); conn.close(); }catch(SQLException se){ //Handle errors for JDBC se.printStackTrace(); // If there is an error then rollback the changes. System.out.println("Rolling back data here...."); try{ if(conn!=null) conn.rollback(); }catch(SQLException se2){ se2.printStackTrace(); }//end try }catch(Exception e){ //Handle errors for Class.forName e.printStackTrace(); }finally{ //finally block used to close resources try{ if(stmt!=null) stmt.close(); }catch(SQLException se2){ }// nothing we can do try{ if(conn!=null) conn.close(); }catch(SQLException se){ se.printStackTrace(); }//end finally try }//end try System.out.println("Goodbye!"); }//end main public static void printRs(ResultSet rs) throws SQLException{ //Ensure we start with first row rs.beforeFirst(); while(rs.next()){ //Retrieve by column name int id = rs.getInt("id"); int age = rs.getInt("age"); String first = rs.getString("first"); String last = rs.getString("last"); //Display values System.out.print("ID: " + id); System.out.print(", Age: " + age); System.out.print(", First: " + first); System.out.println(", Last: " + last); } System.out.println(); }//end printRs() }//end JDBCExample

다음처럼 컴파일하고

C:\>javac JDBCExample.java
C:\>

JDBCExample 실행하면 다음과 같이 나오게된다. 

C:\>java JDBCExample
Connecting to database...
Creating statement...
List result set for reference....
ID: 95, Age: 20, First: Sima, Last: Chug
ID: 100, Age: 18, First: Zara, Last: Ali
ID: 101, Age: 25, First: Mahnaz, Last: Fatma
ID: 102, Age: 30, First: Zaid, Last: Khan
ID: 103, Age: 30, First: Sumit, Last: Mittal
ID: 110, Age: 20, First: Sima, Last: Chug

Deleting row....
Deleting row....
List result set for reference....
ID: 100, Age: 18, First: Zara, Last: Ali
ID: 101, Age: 25, First: Mahnaz, Last: Fatma
ID: 102, Age: 30, First: Zaid, Last: Khan
ID: 103, Age: 30, First: Sumit, Last: Mittal
ID: 110, Age: 20, First: Sima, Last: Chug

Goodbye!
C:\>


ORACLE  Savepoint
펌 : http://reiphiel.tistory.com/entry/jdbc-transaction-savepoints

  

SQL예(오라클)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
INSERT INTO TEST_USER (NAME) VALUES ('JOHN');
 
SAVEPOINT MARK;
INSERT INTO TEST_USER (NAME) VALUES ('MARK');
 
SAVEPOINT JACK;
INSERT INTO TEST_USER (NAME) VALUES ('JACK');
 
SAVEPOINT PAUL;
INSERT INTO TEST_USER (NAME) VALUES ('PAUL');
 
ROLLBACK TO JACK;
 
INSERT INTO TEST_USER (NAME) VALUES ('JANE');
 
COMMIT;


실행 결과




SQL문은 실행한 결과는 위와 같은데 SAVEPOINT로 특정 세이브포인트까지 롤백을 하게되면 롤백실행한 


부분부터지정한 세이브 포인트까지 롤백 되는 것을 알수있다. 



구조적으로 나중에 지정한 세이브포인트는 직전 세이브 포인트에 종속적이라고 보면 되겠다. 


중첩된 트랜잭션이라고 보면 되겠다. 그리고 지정한 세이브포인트 이전과 롤백을 실행한 이후의 SQL에는 


영향을 미치지 않았다는 것을 알 수 있다.


http://helloworld.naver.com/helloworld/textyle/407507 펌


NHN Business Platform 서비스플랫폼개발센터 오이석

트랜잭션 관리는 DBMS가 제공하는 여러 기능 중에서 가장 중요하고 기본적인 것 중의 하나로, DBMS 사용자들에게는 공기와 같은 존재입니다. 이 글에서는 우리가 트랜잭션을 커밋하거나 철회했을 때 어떤 일이 일어나는지, 어떻게 DBMS가 트랜잭션을 복구하는지에 대해서 알아보려고 합니다. 어떤 원리로 트랜잭션 관리라는 매직이 이루어지는지 살펴봅시다.

트랜잭션은 무엇인가?

잘 알려진 내용이라 진부한 측면이 있지만, 먼저 트랜잭션이 무엇인지 정의부터 살펴보자. 하나의 논리적 작업 단위를 구성하는 일련의 연산들의 집합을 트랜잭션이라고 한다. 트랜잭션의 예로 계좌 간의 자금 이체가 많이 언급된다. 한 계좌에서 10만 원을 인출하여 다른 계좌로 10만 원 입금하는 이체 작업은 전체 작업이 정상적으로 완료되거나, 만약 정상적으로 처리될 수 없는 경우에는 아무 것도 실행되지 않은 처음 상태로 되돌려져야 한다. 이러한 트랜잭션은 다양한 데이터 항목들을 액세스하고 갱신하는 프로그램 수행의 단위가 된다. 흔히 트랜잭션은 ACID 성질이라고 하는 다음의 네 가지 성질로 설명된다.

  • Atomicity(원자성): 이체 과정 중에 트랜잭션이 실패하게 되어 예금이 사라지는 경우가 발생해서는 안 되기 때문에 DBMS는 완료되지 않은 트랜잭션의 중간 상태를 데이터베이스에 반영해서는 안 된다. 즉, 트랜잭션의 모든 연산들이 정상적으로 수행 완료되거나 아니면 전혀 어떠한 연산도 수행되지 않은 상태를 보장해야 한다. atomicity는 쉽게 'all or nothing' 특성으로 설명된다.
  • Consistency(일관성): 고립된 트랜잭션의 수행이 데이터베이스의 일관성을 보존해야 한다. 즉, 성공적으로 수행된 트랜잭션은 정당한 데이터들만을 데이터베이스에 반영해야 한다. 트랜잭션의 수행을 데이터베이스 상태 간의 전이(transition)로 봤을 때, 트랜잭션 수행 전후의 데이터베이스 상태는 각각 일관성이 보장되는 서로 다른 상태가 된다. 트랜잭션 수행이 보존해야 할 일관성은 기본 키, 외래 키 제약과 같은 명시적인 무결성 제약 조건들뿐만 아니라, 자금 이체 예에서 두 계좌 잔고의 합은 이체 전후가 같아야 한다는 사항과 같은 비명시적인 일관성 조건들도 있다.
  • Isolation(독립성): 여러 트랜잭션이 동시에 수행되더라도 각각의 트랜잭션은 다른 트랜잭션의 수행에 영향을 받지 않고 독립적으로 수행되어야 한다. 즉, 한 트랜잭션의 중간 결과가 다른 트랜잭션에게는 숨겨져야 한다는 의미인데, 이러한 isolation 성질이 보장되지 않으면 트랜잭션이 원래 상태로 되돌아갈 수 없게 된다. Isolation 성질을 보장할 수 있는 가장 쉬운 방법은 모든 트랜잭션을 순차적으로 수행하는 것이다. 하지만 병렬적 수행의 장점을 얻기 위해서 DBMS는 병렬적으로 수행하면서도 일렬(serial) 수행과 같은 결과를 보장할 수 있는 방식을 제공하고 있다.
  • Durability(지속성): 트랜잭션이 성공적으로 완료되어 커밋되고 나면, 해당 트랜잭션에 의한 모든 변경은 향후에 어떤 소프트웨어나 하드웨어 장애가 발생되더라도 보존되어야 한다.

트랜잭션은 다음의 <표 1>과 같이 세 가지 중 하나의 형태로 종료된다. 문제 없이 정상적으로 수행된 경우에는 커밋을 통해서 종료될 것이고, 잘못된 입력이 주어졌거나 일관성 제약 조건을 위배한다거나 하는 상황이 발생되거나 사용자의 요청에 의하여 철회되는 경우가 있으며, 타임 아웃이나 교착 상태 등과 같이 시스템이 감지하는 문제로 인하여 DBMS가 철회하는 경우가 있다.

표 1 트랜잭션의 세 가지 가능한 결과 형태(Gray 외, 1981)

BEGIN

READ

WRITE

READ

WRITE

COMMIT

BEGIN

READ

WRITE

READ

ABORT

BEGIN

READ

WRITE

READ

ç SYSTEM ABORTS TRANSACTION

이 외에도 트랜잭션은 각종 시스템 고장으로 인하여 영향을 받을 수 있으며, DBMS는 이와 같은 상황에서 트랜잭션을 관리해야 한다.

트랜잭션 관리를 위한 DBMS의 전략

트랜잭션 관리를 위한 DBMS의 전략을 이해하기 위해서는 우선 DBMS의 개략적인 구조와 버퍼 관리자 및 트랜잭션 관리와 연관된 버퍼 관리 정책에 대한 이해가 필요하다.

데이터베이스 시스템은 보통 비휘발성 저장 장치인 디스크에 데이터를 저장하며 전체 데이터베이스의 일부분을 메인 메모리에 유지한다. DBMS는 데이터를 고정 길이의 페이지(page)로 저장하며, 디스크에서 읽거나 쓸 때에 페이지 단위로 입출력이 이루어진다. 메인 메모리에 유지하는 페이지들을 관리하는 모듈을 보통 페이지 버퍼(page buffer) 관리자 또는 버퍼 관리자라고 부르는데, DBMS의 많은 주요 모듈 중에서 매우 중요한 모듈 중의 하나이다. DBMS는 각 제품마다 구조가 다르기는 하지만, <그림 1>과 같이 크게 질의 처리기(Query Processor)와 저장 시스템(Storage System)으로 나눠볼 수 있다. MySQL의 경우에는 InnoDB, MyISAM 등과 같이 여러 하부 저장 시스템을 선택할 수 있는데, 이와 같은 모델은 상부의 질의 처리기와 하부의 저장 시스템 간의 명확하게 구분되는 계층(layered) 구조에 해당한다. CUBRID 역시 질의 처리기와 저장 시스템 두 개의 구성 요소로 이루어져 있으며, 질의 처리기와 저장 시스템이 좀 더 밀접하게 연결되어 있다.

343d976a40006a2bc3d59f9243069fc1.png

그림 1 DBMS의 개략적인 구조

DBMS의 많은 구성 요소 중에서 굳이 버퍼 관리자를 소개한 이유는 버퍼 관리 정책이 트랜잭션 관리에 매우 중요한 결정을 가져오기 때문이다. 버퍼 관리 정책에 따라서 트랜잭션의 UNDO 복구와 REDO 복구가 요구되거나 그렇지 않게 된다. 이 부분에 대해서 하나씩 살펴보자.

UNDO는 왜 필요할까?

오퍼레이션 수행 중에 수정된 페이지들이 버퍼 관리자의 버퍼 교체 알고리즘에 따라서 디스크에 출력될 수 있다. 버퍼 교체는 전적으로 버퍼의 상태에 따라서 결정되며, 일관성 관점에서 봤을 때는 임의의 방식으로 일어나게 된다. 즉 아직 완료되지 않은 트랜잭션이 수정한 페이지들도 디스크에 출력될 수 있으므로, 만약 해당 트랜잭션이 어떤 이유든 정상적으로 종료될 수 없게 되면 트랜잭션이 변경한 페이지들은 원상 복구되어야 한다. 이러한 복구를 UNDO라고 한다. 만약 버퍼 관리자가 트랜잭션 종료 전에는 어떤 경우에도 수정된 페이지들을 디스크에 쓰지 않는다면, UNDO 오퍼레이션은 메모리 버퍼에 대해서만 이루어지면 되는 식으로 매우 간단해질 수 있다. 이 부분은 매력적이지만 이 정책은 매우 큰 크기의 메모리 버퍼가 필요하다는 문제점을 가지고 있다. 수정된 페이지를 디스크에 쓰는 시점을 기준으로 다음과 같은 두 개의 정책으로 나누어 볼 수 있다.

  • STEAL: 수정된 페이지를 언제든지 디스크에 쓸 수 있는 정책
  • ¬STEAL: 수정된 페이지들을 최소한 트랜잭션 종료 시점(EOT, End of Transaction)까지는 버퍼에 유지하는 정책

STEAL 정책은 수정된 페이지가 어떠한 시점에도 디스크에 써질 수 있기 때문에 필연적으로 UNDO 로깅과 복구를 수반하는데, 거의 모든 DBMS가 채택하는 버퍼 관리 정책이다.

REDO는 왜 필요할까?

이제는 UNDO 복구의 반대 개념인 REDO 복구에 대해서 알아볼 것인데, 앞서 설명한 바와 같이 커밋한 트랜잭션의 수정은 어떤 경우에도 유지(durability)되어야 한다. 이미 커밋한 트랜잭션의 수정을 재반영하는 복구 작업을 REDO 복구라고 하는데, REDO 복구 역시 UNDO 복구와 마찬가지로 버퍼 관리 정책에 영향을 받는다. 트랜잭션이 종료되는 시점에 해당 트랜잭션이 수정한 페이지들을 디스크에도 쓸 것인가 여부로 두 가지 정책이 구분된다.

  • FORCE: 수정했던 모든 페이지를 트랜잭션 커밋 시점에 디스크에 반영하는 정책
  • ¬FORCE: 수정했던 페이지를 트랜잭션 커밋 시점에 디스크에 반영하지 않는 정책

여기서 주의 깊게 봐야 할 부분은 ¬FORCE 정책이 수정했던 페이지(데이터)를 디스크에 반영하지 않는다는 점이지 커밋 시점에 어떠한 것도 쓰지 않는다는 것은 아니다. 어떤 일들을 했었다고 하는 로그는 기록하게 되는데 이 부분은 아래에서 자세히 설명한다.

FORCE 정책을 따르면 트랜잭션이 커밋되면 수정되었던 페이지들이 이미 디스크 상의 데이터베이스에 반영되었으므로 REDO 복구가 필요 없게 된다. 반면에 ¬FORCE 정책을 따른다면 커밋한 트랜잭션의 내용이 디스크 상의 데이터베이스 상에 반영되어 있지 않을 수 있기 때문에 반드시 REDO 복구가 필요하게 된다. 사실 FORCE 정책을 따르더라도 데이터베이스 백업으로부터의 복구, 즉 미디어(media) 복구 시에는 REDO 복구가 요구된다. 거의 모든 DBMS가 채택하는 정책은 ¬FORCE 정책이다.

정리해보면 DBMS는 버퍼 관리 정책으로 STEAL과 ¬FORCE 정책을 채택하고 있어, 이로 인해서 UNDO 복구와 REDO 복구가 모두 필요하게 된다. 

트랜잭션 관리

지금까지 설명한 UNDO 복구와 REDO 복구를 위해서 가장 널리 쓰이는 구조는 로그(log)이다. Shadow paging(nilavalagan, 2009)이라고 불리는 복구 기법도 존재하지만, 여기서는 보편적으로 사용되는 로그 기법에 대해서만 설명하기로 한다.

로그

로그는 로그 레코드의 연속이며 데이터베이스의 모든 갱신 작업을 기록한다. 로그는 이론적으로는 안정적 저장 매체(stable storage)에 기록된다고 하는데, 안정적 저장 매체는 어떤 경우에도 절대로 손실이 발생하지 않는 이른바 이상적인 매체이다. 바꿔 말하면 현실 상에서는 존재하지 않는다고 봐야 하는데, RAID 등 인프라 시스템의 도움 외에도 DBMS 자체적으로 여러 벌의 로그를 유지하는 등 안정적 저장 매체처럼 동작하게 하는 기법을 사용하기도 한다. 하지만 대부분 DBMS는 성능 상의 이유로 하나의 로그를 유지한다.

로그는 덧붙이는(append) 방식으로 기록되며, 각 로그 레코드는 고유의 식별자를 가진다. 로그 레코드의 식별자를 LSN(Log Sequence Number) 혹은 LSA(Log Sequence Address)라고 부른다. 로그는 항상 뒤에 덧붙이는 방식으로 쓰이기 때문에, 로그 식별자는 단조 증가하는 성질을 가진다. 로그 데이터는 기록할 오브젝트의 타입에 따라서 물리적/논리적 로깅으로 분류할 수 있고, 데이터베이스의 상태 또는 변화를 야기한 전이를 기록하느냐에 따라서 분류할 수 있다.

표 2 로그 데이터 분류(Haerder & Reuter, 1983)

 

State

Transition

Logical

-

액션(DML문, DDL문)

Physical

이전 이미지

이후 이미지

XOR 차이

물리적인 상태 로깅(physical state logging)

이 방법은 DBMS에서 가장 널리 쓰이는 기본적인 로깅 방법인데 이에 해당하는 로그 레코드는 갱신 이전 이미지와 이후 이미지를 모두 다 가지고 있으며, UNDO 복구 때에는 이전 이미지로 현재 이미지를 대체하며, REDO 복구 때에는 이후 이미지를 반영하는 방식으로 복구가 이루어진다. 결국 이런 복구 작업은 이전 이미지 혹은 이후 이미지로 단순히 대체하는 작업으로 이해하면 된다. 예를 들어, UPDATE 문장에 대한 로깅은 수정 이전 이미지(즉, 수정 전 레코드 이미지)와 이후 이미지(새로 갱신하는 레코드 이미지)를 모두 기록하고, UNDO 시에는 수정 이전 이미지로 대체하는 식으로, REDO가 필요한 경우에는 수정 이후 이미지를 반영하는 식으로 이루어진다. 물리적 상태 로깅은 때로는 페이지 수준(예를 들어, 인덱스나 데이터 파일의 헤더 페이지의 변경 로깅)에서 이루어지기도 하고, 레코드 수준에서 이루어지기도 한다.

물리적인 전이 로깅(physical transition logging)

이 방법은 페이지 혹은 레코드에 대해서 이전 및 이후 이미지를 모두 기록하기 보다는 XOR 차이점을 기록하는 방식으로 이루어진다. 복구 시점에서 로그 레코드에 기록된 XOR 이미지와 레코드 이미지를 이용하여 UNDO 복구와 REDO 복구를 수행하게 된다.

논리적인 전이 로깅(logical transition logging)

이 방법은 오퍼레이션 로깅(operation logging)으로도 불리는데, 물리적인 로깅이 결과 값을 기록하는 방식이라면 논리적인 로깅은 어떤 일을 했었는가를 기록하는 방식이다. 예를 들어, a = a + 1과 같은 연산을 로깅할 때 이전 값 0, 이후 값 1을 물리적으로 기록할 수도 있고, a = a + 1 이라는 연산 그 자체를 기록할 수 있다. 이러한 논리적인 로그에 대한 복구 작업은 REDO를 위하여 로그 레코드에 기록된 오퍼레이션을 재수행하거나, UNDO를 위하여 역 오퍼레이션을 수행하는 방식으로 이루어진다.

이런 논리적인 전이 로깅은 로그 레코드의 크기를 크게 줄여준다는 장점이 있다. 하지만 더 중요한 점은 물리적으로 복구하기 쉽지 않은 자료 구조에 대한 로깅을 쉽게 해준다는 점이다. 예를 들어, 인덱스 구조로 많이 사용되는 B+-tree 또는 B-tree 는 split, merge 와 같은 SMO(Structure Modification Operation)를 통해서 레코드의 위치가 계속 변경되기 때문에 로깅 시점과 복구 시점의 데이터 물리적 위치가 같다는 점이 보장되지 않기 때문에(페이지 내의 위치가 다를 수도 있고, 심지어 다른 페이지에 위치할 수도 있다), 물리적인 로그를 통해서 복구하기가 쉽지 않지만, 논리적인 로그를 통해서 보다 쉽게 복구할 수 있다. 즉, 인덱스에 키 값 k와 포인터 p가 저장되었다는 논리 로그에 대한 REDO 복구는 인덱스에 (k, p)를 다시 삽입하는 작업이면 충분하고, UNDO 복구는 (k, p)를 인덱스에서 제거하는 작업을 수행하면 된다.

DBMS 제품들은 위에서 살펴본 물리적인 상태 로깅, 물리적인 전이 로깅, 논리적인 전이 로깅 방법(그 외에도 물리-논리(physiological) - 수정한 페이지를 물리적으로 식별할 수 있지만 해당 페이지 내에서는 논리적으로 기록되는 - 로깅 기법도 존재한다) 중에 하나만을 선택하여 사용하는 것이 아니라 이들을 적절하게 혼용한다. CUBRID도 각각의 로깅 기법이 유리한 경우에 대해서 물리적인 로깅과 논리적인 로깅을 함께 사용하고 있다. 위에서 설명한 3개의 그룹으로 나눠볼 수 있는 로그 레코드가 DBMS 내에 실제로 몇 종류나 필요할까 하는 궁금증이 드는데, DBMS마다 다르지만 CUBRID의 경우에는 UNDO 로그, REDO 로그, 커밋 로그 같은 것을 포함하여 약 40여 종류의 로그 레코드가 존재한다. 같은 로그 레코드라고 하더라도 자료 구조마다 복구 연산이 다르기 때문에 DBMS가 가지고 있는 복구 연산(함수)은 로그 레코드 종류보다는 훨씬 많이 필요한데, CUBRID는 현재 약 100여 개의 복구 연산을 가지고 있다.

여기서 한 가지 더 얘기할 사항은 로그를 통한 UNDO 복구, REDO 복구는 멱등성(idempotent)을 가져야 한다는 점이다. 멱등성은 여러 번 수행하더라도 한 번 수행한 결과와 같아야 한다는 것을 의미하는데, 물리적인 로그를 통한 복구는 자연스럽게 멱등성이 보장되지만, 논리적인 로그를 통한 복구는 그렇게 간단하지 않다. 예를 들어, a++ 연산을 여러 번 반복해서 복구하게 되면 정확하지 않게 복구될 수 있게 되는데, 이런 일을 방지하기 위해서 한 번 수행한 복구 연산을 또 다시 수행하지 않도록 해야 한다. 이를 어떻게 해결하는지는 이후에 살펴보기로 한다.

로그는 어떻게 쓸까?

로그는 로그 타입에 관계없이 다음의 규칙에 따라 써진다.

  • 해당 업데이트가 데이터베이스에 써지기 전에 먼저 관련된 UNDO 정보가 로그에 써져야 한다. 이 원칙을 WAL(Write Ahead Logging)이라고 부른다. 어떤 경우에도 UNDO 복구가 되기 위해서는 반드시 WAL 규칙이 준수되어야 한다.

  • 트랜잭션이 정상적으로 종료 처리되기 위해서는 먼저 REDO 정보가 로그에 써져야 한다. 역시 어떤 경우에도 REDO 복구를 할 수 있기 위해서는 REDO 로그가 적어도 커밋 시점에는 써져야 한다.

DBMS는 로그 레코드를 위한 별도의 버퍼를 유지하는데, 이를 로그 버퍼라고 한다. 로그 버퍼 관리는 DBMS마다 서로 다른 방식으로 구현하는데 로그 버퍼를 통해서 로그 파일에 입출력한다는 점은 같다. 성능을 위해서 로그 버퍼에 로그 레코드를 모았다가 블록 단위로 로그 파일에 출력한다.

트랜잭션들이 동시에 수행을 하면서 각각의 연산에 대해서 로그 레코드를 생성하게 되는데, 이들은 로그 버퍼에 유지되게 되고 몇몇 시점에 로그 파일에 써지게 된다. 로그 버퍼에 유지된 로그 레코드는 (1) 어떤 트랜잭션이 커밋을 요청한 경우, (2) WAL을 해야 하는 경우, (3) 로그 버퍼가 다 소진된 경우, (4) DBMS가 내부적으로 필요로 하는 경우(예를 들어, 체크 포인트(checkpoint) 연산, 로그 관리 연산 등)에 로그 파일에 출력된다. 대부분의 경우는 (1)과 (2)의 경우로 발생되며 (3), (4)로 인해서 수행되는 경우도 있다. 로그 버퍼는 상대적으로 작기(대개 수MB에서 수십MB 수준) 때문에 긴(long) 트랜잭션이 수행 중인 경우에는 로그 버퍼가 소진될 수 있다.

어떤 트랜잭션이 커밋을 요청하는 경우에는 해당 트랜잭션의 마지막 로그 레코드까지 출력하면 되는데, <그림 2>에서 트랜잭션 T1이 커밋할 때 LSN3까지의 로그 레코드가 로그 파일에 써져야 한다.

5205aed6cb95030dbd71df8b2a05a419.png

그림 2 로그 버퍼의 예

로그를 쓰는 일은 왜 느릴까?

로그 레코드가 손실되는 경우가 발생되면 데이터베이스가 완전히 복구될 수 없기 때문에 로그 레코드를 안전하게 쓰는 것이 필요한데, DBMS는 최대한 안전하게 로그를 쓰기 위해서 write 함수(내지는 writev 함수) 호출 외에 fsync 함수(fsync(2) - Linux man page, 2013)를 호출한다. fsync 함수 호출이 디스크에 물리적으로 써지는 것까지 보장하면 좋겠지만 리눅스를 포함한 대부분의 운영 체제는 그렇게까지 보장하지는 않는다. fsync 함수 호출은 매우 느린 연산이고, 커밋을 위해서는 해당 트랜잭션의 로그가 로그 파일에 써져야 하기 때문에 커밋을 하려는 트랜잭션은 fsync 함수 호출이 종료되기를 대기해야 한다.

더 자세히 보면, 로그 버퍼를 쓸 때에는 로그 헤더 정보와 로그 레코드를 써야 하는데, 로그 레코드를 먼저 쓰고 fsync 함수를 실행하고, 로그 헤더를 업데이트한 후에 다시 fsync 함수를 실행해야 한다. 로그는 끝에 추가되는 방식으로 써지기 때문에 이와 같이 하지 않으면 로그 레코드나 헤더가 온전하지 않은 상태로 기록될 수 있게 된다. fsync를 처리할 때 어떤 버퍼 프레임부터 디스크에 쓰게 될지는 DBMS 입장에서는 알 수 없고 DBMS가 원하는 순서대로 디스크에 반영하도록 할 방법도 없기 때문에, 이와 같이 여러 단계를 거쳐 로그를 쓰게 된다.

하지만 로그 버퍼를 로그 파일에 쓸 때에 한 번에 한 페이지만 쓰는 것이 아니라 여러 페이지를 쓰는 경우가 대부분인데, 이런 경우의 로그 쓰기 작업은 더 복잡하게 이루어진다. 예를 들어, 로그 버퍼 Bi부터 Bk까지 출력하는 경우에, 먼저 Bj부터 Bk까지 로그 파일에 쓰고 fsync 함수를 실행하고, 첫 번째 로그 버퍼인 Bi를 쓴 후 다시 fsync 함수를 실행하고 나서, 비로소 로그 헤더를 업데이트하는 절차로 이루어진다.

2b978007f0168bc97f8568300c1a47c9.png

그림 3 로그 버퍼를 로그 파일에 쓰는 순서

대부분의 커밋 연산이 소모하는 시간은 로그 레코드를 로그 파일에 쓰고, fsync 함수를 실행하는 시간이라고 보면 된다. 정확성을 위해서는 fsync 함수를 여러 차례 호출해야 하는데, 일 초에도 수천, 수만 트랜잭션이 커밋을 요청하는 상황을 생각해보면 로깅을 위해서 얼마나 많은 디스크 출력이 있어야 하는지 쉽게 이해할 수 있을 것이다.

로그 쓰기 작업, 즉 커밋 오퍼레이션의 성능을 높일 방법이 없을까?

성능을 위한 몇 가지 기법이 있는데, 먼저 그룹 커밋(group commit)부터 알아보자. 그룹 커밋은 각각의 트랜잭션의 커밋 요구를 개별적으로 처리하기 보다는 모아서 한꺼번에 처리하는 방식이다. 수천 내지는 수만 TPS 수준의 요청이 있다고 했을 때, 한 트랜잭션이 커밋할 때 잠시만 기다리면 다른 트랜잭션들이 역시 커밋을 요청할 것이고, 이들의 요청을 한 번에 처리하게 되면 디스크 출력 횟수를 줄일 수 있으므로 이로 인해 성능을 높일 수 있게 된다. 즉, 그룹 커밋은 여전히 정확성을 보장하면서 각 트랜잭션의 응답 시간(response time)은 약간 희생시키는 경우가 발생되더라도 시스템 전체의 처리량(throughput)을 높이자는 의도이다. 쉽게 생각해서 개별적으로 승용차를 이용하는 방식과 고속 버스를 이용하는 방식을 연상하면 된다. 고속 버스의 경우 정해진 출발 시각까지 대기해야 하지만, 한 번에 이동하는 승객이 많기 때문에 효율은 높다.

그룹 커밋은 (한계 시점 이전까지는) 동시에 요청되는 커밋 요구가 많을수록 그 효율이 높아지는데, 적절한 그룹 커밋 대기 시간을 정하는 것이 시스템 성능에 매우 중요하다. 너무 짧으면 효율이 떨어지게 되고, 너무 길면 응답 시간이 느려지고 효율은 더 이상 높아지지 못하게 된다. 최적의 값은 워크로드 패턴에 따라서 다르며 대게 짧게는 몇 밀리초(ms)에서 길게는 수백 밀리초(ms)까지 설정하기도 하는데, 시스템의 부하에 따라서 시스템이 적응적(adaptive)으로 자동 조정(Helland 외, 1988)하기도 한다.

성능을 위해서 지속성을 살짝 포기할 수는 없을까?

커밋 성능을 극대화하기 위해서 지속성(durability)을 일부 포기하는 방식도 있다. DBMS 제품에 따라서는 매 커밋마다 정확하게 로그를 쓰고 fsync 함수를 실행하는 것이 아니라 보다 나은 성능을 위해 좀 더 느슨하게 로그를 쓰는 옵션을 제공하기도 한다. 예를 들어, InnoDB는 innodb_flush_log_at_trx_commit 파라미터(InnoDB Startup Options and System Variables, 2013)의 설정을 통해서 로그를 쓰는 방식을 조정할 수 있다.

또한, 응용이나 시스템의 성격에 따라서는 비동기 커밋(asynchronous commit) 방식을 사용하기도 하는데, 비동기 커밋은 로그 버퍼에 로그 레코드를 쓰고 곧바로 커밋을 완료하는 방식이다. 즉, 로그 파일에 로그가 써질 때까지 대기하지 않고 커밋을 하게 된다. 로그 레코드는 로그 쓰기 스레드(내지는 프로세스)가 이후에(대개는 매우 짧은 시간 내에 곧바로) 비동기적으로 쓰게 되는데, 트랜잭션의 로그가 미처 써지기 전에 시스템에 장애가 발생되면 해당 트랜잭션은 이미 커밋을 완료했지만 손실되게 된다. 비동기 커밋 방식으로 인해 발생할 수 있는 데이터의 손실은 커밋한 트랜잭션이 변경한 데이터의 유실(loss)이며, 이 때도 데이터의 일관성은 보장된다. 최근의 몇몇 트랜잭션의 커밋 로그가 유실된 것이기 때문에 복구 시점에 DBMS는 마치 해당 트랜잭션이 커밋을 하지 않은 것으로 간주하여 이를 철회(rollback)시키게 된다. 철회를 위한 UNDO 로그는 이미 트랜잭션 수행 중에 WAL 원칙에 따라서 트랜잭션 로그에 써져 있기 때문에, 커밋되지 않은 데이터가 데이터베이스에 반영되지는 않는다. 비동기 커밋은 성능 향상 효과가 크기는 하지만, 손실이 발생할 수 있으므로 응용의 성격에 따라서 신중하게 선택해야 할 필요가 있다. 동시에 커밋 요청이 매우 많이 요구되며 데이터의 일부 유실을 감내할 수 있는 응용 환경에서는 적용을 고려해볼 수 있을 것이다. CUBRID도 그룹 커밋과 비동기 커밋 방식을 모두 제공하고 있고, 설정을 통해서 쉽게 적용할 수 있다.

어떻게 로그로 복구가 이루어지나?

이제 그러면 로그를 통해서 어떻게 복구가 이루어지는지 알아보자. 복구에는 두 가지 종류가 있는데, 사용자의 요청 또는 오류 발생 등으로 인해서 시스템이 트랜잭션을 철회하는 경우와 소프트웨어 문제나 하드웨어 문제 등으로 인해서 장애가 발생하고 데이터베이스 시스템이 재시작 복구(restart recovery)하는 경우가 있다.

트랜잭션 철회는 어떻게?

트랜잭션을 철회하는 경우는 시스템은 정상적으로 동작하고 있는 중이며 특정 트랜잭션만 철회하는 경우인데, 이 때 트랜잭션의 철회는 다음과 같이 이루어진다. 먼저 로그를 역방향으로 탐색하면서 해당 트랜잭션의 UNDO 복구가 필요한 로그를 찾아서 이에 해당하는 UNDO 연산을 수행한다. 역방향으로 로그를 탐색하면서 트랜잭션 수행 순서의 역순으로 UNDO를 수행해야 정확하게 UNDO가 이루어질 수 있다.

UNDO를 수행하고 나면 해당 UNDO 작업에 대한 보상 로그 레코드(CLR, Compensation Log Record)라고 하는 REDO 전용 로그를 쓰게 되는데, UNDO를 하고 난 이후에 다시 UNDO를 해서 복구가 잘못 이루어지지 않도록 하기 위함이다. CLR은 이전 로그 레코드 위치를 UNDO 로그의 이전 로그를 가리키도록 하여 이후에는 한 번 UNDO된 로그를 다시 접근하여 재차 UNDO하게 되는 일이 발생되지 않도록 해준다. 이전 로그를 계속 탐색하면서 해당 트랜잭션의 시작 로그까지 도달하면 해당 트랜잭션의 철회 복구가 완료된 것이다.

장애로 인해 재시작되면 어떻게 복구가 되나?

장애 발생 이후 데이터베이스가 재시작 복구하는 경우에는 크게 3 단계로 복구가 이루어진다.

1 단계는 로그 분석 단계로, 마지막 체크포인트(checkpoint) 시점부터 최근 로그(EOL, End of Log)까지 로그를 탐색하면서 어디서부터 시스템이 복구를 시작해야 하는지, 어느 트랜잭션들을 복구해야 하는지 등등을 알아내는 단계이다.

2 단계는 REDO 복구 단계로 복구를 시작해야 하는 시점부터 장애 발생 직전 시점까지 REDO가 필요한 모든 로그를 REDO 복구를 하는 단계이다. 이 단계에서는 심지어 실패한 트랜잭션의 REDO 로그조차도 REDO를 하게 되는데, 언뜻 보면 불필요한 것으로 생각되지만 이렇게 하면 이후의 복구 단계를 매우 간단하게 하는 효과를 가져다 준다. 이 단계에서는 모든 트랜잭션에 대해서 REDO 복구만 한다는 점이 중요한데, 이러한 REDO 복구가 완료된 시점의 데이터베이스 상태는 장애 발생 시점의 상태와 같게 된다. 이전 상황을 그대로 재현하여 복원한다는 의미로 이 REDO 복구에서 이루어지는 작업을 repeating history(Mohan 외, 1992)라고 부른다.

마지막 3 단계는 UNDO 복구 단계로 로그를 최신 시점부터 다시 역방향으로 탐색하면서 UNDO 복구가 필요한 로그들에 대해서 UNDO 복구를 수행한다. 여기서 수행하는 UNDO는 결국 위에서 설명한 트랜잭션 철회 시에 수행하는 UNDO와 같은 방식으로, repeating history를 통해 데이터베이스 상태를 장애 시점까지 복원해두고 UNDO 복구를 여러 트랜잭션의 철회로 간단하게 해결할 수 있다. 한 트랜잭션만 철회시키는 것이 아니라 여러 트랜잭션을 철회시킨다는 차이점만 존재한다. 이 단계의 UNDO 복구를 개별 트랜잭션의 UNDO와 구별하여 Global UNDO라고도 부른다.

2880ae634676944afdd10bed6a86954f.png

그림 4 재시작 복구 단계와 로그 접근 방향

로그를 통한 복구 과정 중에 특정 로그가 UNDO 내지는 REDO 복구가 필요한 것인지를 판단해야 할 필요가 있다. 이미 로그가 반영되었다면 그 로그에 대한 복구 연산은 필요치 않은데 이는 어떻게 해결할까? 앞서 설명한 바와 같이 모든 로그에는 LSN이라고 하는 식별자가 있는데, 데이터베이스의 모든 페이지는 page LSN을 가지고 있다. 이 page LSN은 페이지가 갱신될 때마다 해당 로그의 LSN으로 갱신된다. 즉, 모든 페이지는 해당 페이지를 마지막으로 갱신한 로그의 식별자를 포함하고 있으므로, 로그를 적용해야 할지 여부는 해당 로그의 LSN과 page LSN을 비교함으로써 판단할 수 있다. Page LSN이 어떤 로그의 LSN보다 예전 것이라면 해당 페이지는 반드시 해당 로그로 복구되어야 한다는 것을 의미하며, 반대로 page LSN이 해당 로그의 LSN과 같거나 더 최신의 값을 가지고 있다면 이 페이지는 해당 로그보다 나중에 쓰인 로그로 이미 갱신되었다는 것을 의미하므로 복구가 필요치 않다는 것을 의미한다. CUBRID는 page LSN으로 페이지 시작 부분의 8바이트의 공간을 사용하므로, 기본 16KB의 페이지를 사용하는 경우 실제 데이터가 저장되는 공간은 page LSN을 위한 공간을 제외한 16376바이트가 된다.

e9bfe03d5dbbde428c1522181eba2dfc.png

그림 6 데이터베이스의 페이지 구성

백업을 이용한 미디어 복구는 어떻게?

디스크 미디어(media)의 문제가 생겼을 때 수행하는 미디어 복구, 일명 아카이브(archive) 복구가 있는데, 이는 데이터베이스의 백업으로부터 복구를 하는 것을 의미한다. 데이터베이스 백업 기법에는 여러 가지가 있는데, 데이터베이스가 수행 도중에 트랜잭션들의 수행을 방해하지 않고 현재 스냅샷(snapshot)을 그대로 복사하는 퍼지(fuzzy) 백업이 CUBRID를 포함한 상용 DBMS가 사용하는 기법이다.

트랜잭션이 수행하고 있는 도중에 데이터베이스 이미지를 복사하는 것이기 때문에 미처 커밋하지 못한 일부 트랜잭션의 이미지가 복사될 수도 있고, 커밋한 트랜잭션의 데이터가 아직 반영되지 못한 채로 복사가 될 수도 있다. 이렇게 퍼지하게 복사한 데이터베이스 백업으로 어떻게 복원(restore)를 할까? 역시나 답은 로그에 있다. 미디어 복구 시에는 데이터베이스 백업과 (이에 포함된) 로그, 혹시 남아 있다면 장애 시점의 로그까지 활용하여 복구를 하게 되는데, (아주 간략하게 설명하면) 데이터베이스 백업은 데이터베이스 파일을 복사한 것이므로 이를 새로 복사해둔 후 데이터베이스를 재시작한다고 생각하면, 미디어 복구 문제는 위에서 설명한 장애 발생 이후에 재시작 복구 작업과 결국 같은 문제가 된다. 결국 로그를 읽어서 퍼지하게 복사했던 데이터베이스 이미지에서 아직 미처 반영되지 못한 커밋했던 트랜잭션들을 다시 REDO해 주고, 결국 커밋 레코드가 포함되지 않은 트랜잭션들은 UNDO해 주면 된다. 이러한 미디어 복구 시점의 재시작 복구를 특별히 roll-forward 복구라고 부르기도 한다.

미디어 장애가 발생했을 때 마지막 데이터베이스 백업 이후의 모든 로그가 남아 있다면 장애 시점까지 손실 없이 데이터베이스를 복원할 수 있다. 불행히도 백업 이후의 일부 로그가 유실되었다면 최소한 백업 시점의 일관성이 유지되는 데이터베이스 시점까지는 복원이 가능하다. 미디어 복구를 이용하여 특정 시점으로 데이터베이스를 복원하는 것도 가능한데, 이는 roll-forward 과정을 현재 시점까지 전체를 수행하는 것이 아니라 DBA가 원하는 특정 시점까지만 수행하면 된다.

커밋을 하면 어떤 일이 일어나나?

커밋을 하면 어떤 일이?

커밋 트리거 혹은 지연된(deferred) 트리거가 정의되어 있다면 해당 트리거가 수행된다. 또한, 트랜잭션 수행 도중에 생성했던 커서(질의 결과 집합)를 정리하게 된다. SQL 표준은 커서를 선언할 때 트랜잭션 커밋 이후에도 커서를 계속 유지하고 결과를 볼 수 있도록 하는 Holdable Cursor(Cursor (databases), 2013)로 선언한 커서만을 유지하고 그 외의 커서는 모두 해제할 것으로 정의하고 있는데, 반면 JDBC에서는 기본으로 Holdable Cursor로 정의하고 있다. 이는 JDBC의 기본 동작이 자동 커밋이기 때문에 사용자의 편의성을 고려한 결정인데, 바깥쪽(outer) 커서에서 얻어온 결과를 기반으로 중첩 루프의 안쪽(inner)에서 다른 질의를 하는 경우에 전체를 묶어서 트랜잭션 처리를 하지 않으면 안쪽 질의가 커밋되는 순간에 바깥쪽 커서마저 닫히게 되는 상황이 발생된다. Holdable Cursor가 지원되면 안쪽에서 커밋을 하더라도 바깥쪽 커서가 계속 유지되기 때문에 트랜잭션 처리를 하지 않아도 된다.

DBMS는 트랜잭션을 수행하는 과정 중의 일부 내부 연산들(예를 들어, 리소스 반환과 같은 물리적인 연산)을 커밋이나 롤백과 같은 트랜잭션 종료 시점까지 지연시키는 경우가 있는데, 이런 연기(postpone)된 연산들이 포함되어 있었다면 커밋 시점에 수행된다. 장애 대비를 위하여 데이터베이스의 복제(replication)를 적용하고 있다면, 복제를 위한 로그를 쓰는 것과 같은 작업을 수행한다. 복제는 DBMS마다 구현 전략이 상이하기 때문에 일반적으로 설명하기는 어려운 측면이 있는데, CUBRID는 트랜잭션 로그를 기반으로 하는 복제를 사용하며 트랜잭션 커밋 시점에 복제를 위한 로그를 쓴다. 트랜잭션을 수행하는 과정 중에 획득한 모든 락(lock)을 해제하고, 트랜잭션이 최종적으로 커밋했다는 로그를 쓴 후에 마지막으로 트랜잭션이 가지고 있던 메모리, 트랜잭션 식별자 등과 같은 리소스들을 반환하고 비로소 트랜잭션이 종료하게 된다.

커밋을 하다가 오류가 발생되면?

응용 프로그램의 커밋 요청으로 위에서 설명한 단계들을 수행하는 과정 중에 오류가 발생되면 어떻게 될까? "끝나기 전까지는 끝난 것이 아니다."라는 말이 가장 적절한 설명이 될 것 같다. 트랜잭션이 커밋이 완료된 것이 아니라면 그것은 수행되지 않은 것과 같게 취급된다. <그림 7>에서 사용자의 커밋 요청이 오면 일단 해당 트랜잭션의 상태는 'partially committed' 상태가 된다. 문제 없이 커밋할 수 있으면 'committed' 상태로 완료되지만, 그렇지 않은 경우에는 다른 오류 발생과 마찬가지로 'failed' 상태를 거쳐 결국 'aborted' 상태에 다다르게 된다.

9bfdf8875b6a11f4c3274e0677d46596.png

그림 7 트랜잭션 상태 다이어그램(Silberschatz 외, 2010)

 

마치며

지금까지 DBMS가 어떻게 트랜잭션을 관리하는가라는 주제로 트랜잭션 관리의 주요 원리들을 간략하게 살펴보았다. DBMS의 트랜잭션 관리는 워낙 방대하고 깊숙한 주제라 본 글에서는 주요 개념들을 개괄적으로 다루었는데, 이 부분에 대해서 좀 더 관심이 있으면 참고 문헌에 수록된 자료들을 살펴보면 된다. 데이터베이스 시스템 전반에 대한 이해가 필요하다면 "Database System Concepts"(Silberschatz 외, 2010)를, 트랜잭션 복구와 관련하여 좀 더 궁금하면 "Recovery Mechanisms in Database Systems"(Kumar & Hsu, 1998)를, 트랜잭션 처리 전반에 대해 구현 수준까지 깊게 이해하고 싶다면 "Transaction Processing: Concepts and Techniques"(Gray & Reuter, 1993)를 추천한다.

참고 자료

[1] "Cursor (databases)." (2013). http://en.wikipedia.org/wiki/Cursor_(databases)#.22WITH_HOLD.22에서 검색됨

[2] "fsync(2) - Linux man page." (2013). http://linux.die.net/man/2/fsync에서 검색됨

[3] "InnoDB Startup Options and System Variables." (2013). http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit에서 검색됨

[4] Gray, J., & Reuter, A. (1993). "Transaction Processing: Concepts and Techniques." Morgan Kaufmann Publishers.

[5] Gray, J., Mcjones, P., Blasgen, M., Lindsay, B., Lorie, R., Price, T. 외. (June, 1981). The Recovery Manager of the System R Database Manager. "Computing Surveys, Vol. 13, No.2", 228.

[6] Haerder, T., & Reuter, A. (December, 1983). Principles of Transaction-Oriented Database Recovery. "ACM Computing Survey, Vol. 15, No. 4".

[7] Helland, P., Sammer, H., Lyon, J., Carr, R., Garrett, P., & Reuter, A. (1988). "Group Commit Timers and High-Volume Transaction Systems." Tandem Technical Report 88.1.

[8] Kumar, V., & Hsu, M. (1998). "Recovery Mechanisms in Database Systems." Prentice Hall PTR.

[9] Mohan, C., Haderle, D., Lindsay, B., Pirahesh, H., & Schwarz, P. (March, 1992). ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking and Partial Rollbacks Using Write-Ahead Logging. "ACM Transactions on Database Systems, Vol. 17", 94-162.

[10] nilavalagan. (2009). "Shadow Paging Recovery Technique." https://www.classle.net/book/shadow-paging-recovery-technique에서 검색됨

[11] SilberschatzAbraham, KorthF.Henry, & SudarshanS. (2010). "Database System Concepts." McGraw-Hill.

'RDBMS (PostgreSQL)' 카테고리의 다른 글

[DB/분산] 초보자를 위한 CAP 이론  (1) 2016.04.29
JDBC 트랜잭션  (0) 2015.07.30
PostgreSQL 조인  (0) 2015.05.13
Efficient Use of PostgreSQL Indexes  (0) 2015.05.13
PostgreSQL 날짜&시간 사용하기  (0) 2015.05.13

The PostgreSQL Joins clause is used to combine records from two or more tables in a database. A JOIN is a means for combining fields from two tables by using values common to each.

Join Types in PostgreSQL are:

  • The CROSS JOIN

  • The INNER JOIN

  • The LEFT OUTER JOIN

  • The RIGHT OUTER JOIN

  • The FULL OUTER JOIN

Before we proceed, let's consider two tables COMPANY and DEPARTMENT. We already have seen INSERT statements to populate COMPANY table. So just let's assume the list of records available in COMPANY table:

 id | name  | age | address   | salary | join_date
----+-------+-----+-----------+--------+-----------
  1 | Paul  |  32 | California|  20000 | 2001-07-13
  3 | Teddy |  23 | Norway    |  20000 |
  4 | Mark  |  25 | Rich-Mond |  65000 | 2007-12-13
  5 | David |  27 | Texas     |  85000 | 2007-12-13
  2 | Allen |  25 | Texas     |        | 2007-12-13
  8 | Paul  |  24 | Houston   |  20000 | 2005-07-13
  9 | James |  44 | Norway    |   5000 | 2005-07-13
 10 | James |  45 | Texas     |   5000 | 2005-07-13

Another table is DEPARTMENT, has the following definition:

CREATE TABLE DEPARTMENT(
   ID INT PRIMARY KEY      NOT NULL,
   DEPT           CHAR(50) NOT NULL,
   EMP_ID         INT      NOT NULL
);

Here is the list of INSERT statements to populate DEPARTMENT table:

INSERT INTO DEPARTMENT (ID, DEPT, EMP_ID)
VALUES (1, 'IT Billing', 1 );

INSERT INTO DEPARTMENT (ID, DEPT, EMP_ID)
VALUES (2, 'Engineering', 2 );

INSERT INTO DEPARTMENT (ID, DEPT, EMP_ID)
VALUES (3, 'Finance', 7 );

Finally, we have the following list of records available in DEPARTMENT table:

 id | dept        | emp_id
----+-------------+--------
  1 | IT Billing  |  1
  2 | Engineering |  2
  3 | Finance     |  7

The CROSS JOIN

A CROSS JOIN matches every row of the first table with every row of the second table. If the input tables have x and y columns, respectively, the resulting table will have x+y columns. Because CROSS JOINs have the potential to generate extremely large tables, care must be taken to only use them when appropriate.

Following is the syntax of CROSS JOIN:

SELECT ... FROM table1 CROSS JOIN table2 ...

Based on the above tables, we can write a CROSS JOIN as follows:

testdb=# SELECT EMP_ID, NAME, DEPT FROM COMPANY CROSS JOIN DEPARTMENT;

Above query will produce the following result:

emp_id| name  |  dept
------|-------|--------------
    1 | Paul  | IT Billing
    1 | Teddy | IT Billing
    1 | Mark  | IT Billing
    1 | David | IT Billing
    1 | Allen | IT Billing
    1 | Paul  | IT Billing
    1 | James | IT Billing
    1 | James | IT Billing
    2 | Paul  | Engineering
    2 | Teddy | Engineering
    2 | Mark  | Engineering
    2 | David | Engineering
    2 | Allen | Engineering
    2 | Paul  | Engineering
    2 | James | Engineering
    2 | James | Engineering
    7 | Paul  | Finance
    7 | Teddy | Finance
    7 | Mark  | Finance
    7 | David | Finance
    7 | Allen | Finance
    7 | Paul  | Finance
    7 | James | Finance
    7 | James | Finance

The INNER JOIN

A INNER JOIN creates a new result table by combining column values of two tables (table1 and table2) based upon the join-predicate. The query compares each row of table1 with each row of table2 to find all pairs of rows, which satisfy the join-predicate. When the join-predicate is satisfied, column values for each matched pair of rows of table1 and table2 are combined into a result row.

An INNER JOIN is the most common type of join and is the default type of join. You can use INNER keyword optionally.

Following is the syntax of INNER JOIN:

SELECT table1.column1, table2.column2...
FROM table1
INNER JOIN table2
ON table1.common_filed = table2.common_field;

Based on the above tables, we can write an INNER JOIN as follows:

testdb=# SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT
        ON COMPANY.ID = DEPARTMENT.EMP_ID;

Above query will produce the following result:

 emp_id | name  | dept
--------+-------+------------
      1 | Paul  | IT Billing
      2 | Allen | Engineering

The LEFT OUTER JOIN

The OUTER JOIN is an extension of the INNER JOIN. SQL standard defines three types of OUTER JOINs: LEFT, RIGHT, and FULL and PostgreSQL supports all of these.

In case of LEFT OUTER JOIN, an inner join is performed first. Then, for each row in table T1 that does not satisfy the join condition with any row in table T2, a joined row is added with null values in columns of T2. Thus, the joined table always has at least one row for each row in T1.

Following is the syntax of LEFT OUTER JOIN:

SELECT ... FROM table1 LEFT OUTER JOIN table2 ON conditional_expression ...

Based on the above tables, we can write a inner join as follows:

testdb=# SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT
        ON COMPANY.ID = DEPARTMENT.EMP_ID;

Above query will produce the following result:

 emp_id | name  | dept
--------+-------+------------
      1 | Paul  | IT Billing
      2 | Allen | Engineering
        | James |
        | David |
        | Paul  |
        | Mark  |
        | Teddy |
        | James |

The RIGHT OUTER JOIN

First, an inner join is performed. Then, for each row in table T2 that does not satisfy the join condition with any row in table T1, a joined row is added with null values in columns of T1. This is the converse of a left join; the result table will always have a row for each row in T2.

Following is the syntax of LEFT OUTER JOIN:

SELECT ... FROM table1 RIGHT OUTER JOIN table2 ON conditional_expression ...

Based on the above tables, we can write a inner join as follows:

testdb=# SELECT EMP_ID, NAME, DEPT FROM COMPANY RIGHT OUTER JOIN DEPARTMENT
        ON COMPANY.ID = DEPARTMENT.EMP_ID;

Above query will produce the following result:

 emp_id | name  | dept
--------+-------+--------
      1 | Paul  | IT Billing
      2 | Allen | Engineering
      7 |       | Finance

The FULL OUTER JOIN

First, an inner join is performed. Then, for each row in table T1 that does not satisfy the join condition with any row in table T2, a joined row is added with null values in columns of T2. Also, for each row of T2 that does not satisfy the join condition with any row in T1, a joined row with null values in the columns of T1 is added.

Following is the syntax of FULL OUTER JOIN:

SELECT ... FROM table1 FULL OUTER JOIN table2 ON conditional_expression ...

Based on the above tables, we can write a inner join as follows:

testdb=# SELECT EMP_ID, NAME, DEPT FROM COMPANY FULL OUTER JOIN DEPARTMENT
        ON COMPANY.ID = DEPARTMENT.EMP_ID;

Above query will produce the following result:

 emp_id | name  | dept
--------+-------+---------------
      1 | Paul  | IT Billing
      2 | Allen | Engineering
      7 |       | Finance
        | James |
        | David |
        | Paul  |
        | Mark  |
        | Teddy |
        | James |

'RDBMS (PostgreSQL)' 카테고리의 다른 글

JDBC 트랜잭션  (0) 2015.07.30
트랜잭션 인사이드  (0) 2015.07.01
Efficient Use of PostgreSQL Indexes  (0) 2015.05.13
PostgreSQL 날짜&시간 사용하기  (0) 2015.05.13
PostgreSQL 컬럼 타입들  (0) 2015.05.13

+ Recent posts