회사에서 이슈 진행을 하다가, 트랜잭션를 이용하여 처리해야하는 부분을 발견하였다.
따라서 SQLAlchemy에서는 트랜잭션을 어떻게 사용하는지 정리해보도록 하겟다.
트랜잭션 관리하기

- 새롭게 생성된 세션은
begin()상태이다. begin()상태에서Session은 아직 어떠한Connection및Transactional과 연관되지 않았다.- 그러한 다음에,
Session은 데이터베이스 커넥션 요청을 수신한다. - 일반적으로, 이것은
Engine을 이용하여, 특정SQL문을 수행해야하는 것을 의미한다. - 특정
SQL문의 수행은Session.query(),Session.execute()통해 이루어지고Session.commit(),Session.flush()를 할 때 보류된 변경 사항을 비우고 커밋하면서 발생합니다. - 이러한 요청이 수신되면, 새로운 엔진 각각이 세션에서 유지되고 관리하는 트랜잭션 상태와 연결이 된다.
- 첫 번째, 엔진이 작동하면 세션은
begin상태를 떠나서,transactional상태로 전환되었다고 할 수 있다. - 각각의 엔진에 대해서 연결이 되어 있으며, 이러한 연결은
Engine.contextual_connect()메서드를 통해서 획득된다. - 롤백 또는 커밋 후에 트랜잭션 상태가 완료되면 모든 트랜잭션 및 연결 리소스를 해제하고,
begin상태로 돌아간다. begin상태로 돌아가면, 다시 새로운SQL문을 내보내는 새 요청이 수신될 때 새로운Connection및Transaction객체를 다시 호출합니다.
다음은 위의 라이프 사이클을 보여주는 예제이다.
engine = create_engine("...")
Session = sessionmaker(bind=engine)
# 새로운 세션, 어떤 커넥션도 사용중이지 않다.
session = Session()
try:
# 첫 번째 쿼리를 수행하면, 커넥션을 엔진으로 부터 획득하고
# 트랜잭션이 시작된다.
item1 = session.query(Item).get(1)
# 두 번째 쿼리이다. 같은 커넥션과 트랜젝션이 사용된다.
item2 = session.query(Item).get(2)
# 아직 반영되지 않은 변경 사항이 생성된다.
item1.foo = 'bar'
item2.bar = 'foo'
# 커밋을 수행한다.
# 아직 반영되지 않은 변경사항이 모두 `flush` 된다.
# 트랜잭션이 커밋되고, 연결 객체가 닫치고 사라진다.
# DBAPI 연결이 커넥션 풀로 반환된다.
session.commit()
except:
# 롤백시에도, 커미과 동일한 상태 종료가 진행된다.
session.rollback()
raise
finally:
# 세션을 닫는다, 이렇게 하면 남아 있는 모든 객체가 영구적으로 삭제되며, 기존 SessionTransaction 상태가 재설정 된다.
# 일반적으로 이러한 단계는 필수는 아니지만,
# commit() 또는 rollback() 자체에 예기치 않은 내부 오류가
# 발생한 경우 close()는 유효하지 않은 상태가 제거되도록 한다.
session.close()
SAVEPOINT 사용하기
기본 엔진에서 지원하는 경우, SAVEPOINT 트랜잭션은 Session.begin_nested() 메서드를 사용할 수 있다.
Session = sessionmaker()
session = Session()
session.add(u1)
session.add(u2)
session.begin_nested() # establish a savepoint
session.add(u3)
session.rollback() # rolls back u3, keeps u1 and u2
session.commit() # commits u1 and u2
-
Session.begin_nested()는 여러 번 호출 될 수 있으며, 각 호출에 대해서 고유한 식별자가 있는 새로운 SAVEPOINT를 발행합니다. -
각각의
Session.begin_nested()호출에 대해서,Session.rollback()또는,Session.commit()을 수행 해야 합니다. -
그러나 반환 값이 컨텍스트 매니저에 의해서 사용되는 경우 예를 들어서
with문에서rollback및commit은 컨텍스트를 종료할 때, 컨텍스트 관리자가 실행하므로 명시적으로 추가해서는 안된다. -
Session.begin_nested()메서드는Session.begin()을 덜 사용하기 위해서 사용하며 컨텍스트 매니저로 작동하는SessionTransaction을 반환한다. -
개발 레코드를 삽입할 때, 유용하게 사용할 수 있다.
for record in records:
try:
with session.begin_nested():
session.merge(record)
except:
print("Skipped record %s" % record)
session.commit()
트랜잭션 격리 수준 설정 / DBAPI AUTOCOMMIT

- 대부분의
DBAPI는 설정할 수 있는 트랜잭션 격리 수준의 개념을 지원한다. - 전통적으로
READ UNCOMMITED,READ COMMITED,REPEATABLE READ, 및SERIALIZABLE의 네 가지 수준이 있다. - 이는 일반적으로 새로운 트랜잭션을 사용하기 전에,
DBAPI연결에 적용되며 대부분의 DBAPI는 SQL문이 처음 생성될 때, 트랜잭션을 암시적으로 시작한다. SQLAlchemy는create_engine수준과,Connection.execution_options()수준 모두에서 플래그를 사용하여 엔진 별 또는 연결 별로 설정 가능한 격리 모드를 지원한다.
세션 메이커 및 엔진 전체에 대한 고립 수준 설정
전역 적으로 특정 고립 수준으로 Session 또는 Session Maker를 설정하려면 첫 번째 기술은 모든 경우에 특정 격리 수준에 대해서 엔진을 설정한 다음에, 세션 메이커의 연결 소스로 사용하는 것이다.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
eng = create_engine(
"postgresql://scott:tiger@localhost/test",
isolation_level='REPEATABLE READ'
)
Session = sessionmaker(eng)
서로 다른 격리 수준을 가진 두 개의 엔진이 있는 경우 유용한 다른 옵션은 Engine.execution_options() 메서드를 사용하는 것입니다.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
eng = create_engine("postgresql://scott:tiger@localhost/test")
autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT")
transactional_session = sessionmaker(eng)
autocommit_session = sessionmaker(autocommit_engine)
- 위의
eng과autocommit_engine은 동일한 연결 풀을 공유합니다. AUTOCOMMIT모드는autocommit_engine에서 연결할 때 따로 설정된다.
각각의 세션에 대한, 고립 수준 설정
생성자를 직접 사용하거나, 세션 메이커가 생성한 호출 가능한 객체를 호출 할 때, bind 인자로 바로 전달 할 수 있다.
예를 들어, transactional_session에서 세션을 만들고, autocommit_engine을 전달할 수 있다.
session = transactional_session(bind=autocommit_engine)
# work with session
session.close()
각각의 트랜잭션에 대한 고립 수준 설정
격리 수준과 관련된 주요한 주의 사항은 트랜잭션이 이미 시작된 연결에서 설정을 안전하게 수정할 수 없다는 것이다.
데이터베이스는 진행중인 트랜잭션의 격리 수준을 변경할 수 없으며 일부 DBAPI 및 SQLALchemy 언어에서는 이러한 영역에서 일관적이지 않은 동작이 있다.
따라서 원하는 격리 수준을 가진 엔진 앞쪽에 바인딩 된 세션을 사용하는 것이 좋다.
그러나 연결 단위의 격리 수준은 트랜잭션이 시작될 때 Session.commection() 메서드를 사용하여 영향 받을 수 있다.
from sqlalchemy.orm import Session
sess = Session(bind=engine)
with sess.begin():
sess.connection(execution_options={'isolation_level': 'SERIALIZABLE'})
# commits transaction. the connection is released
# and reverted to its previous isolation level.
위에서 먼저, 생성자 또는 세션 메이커를 사용하여 세션을 생성한다. 그런 다음에 트랜잭션이 시작되기 전에 연결에 전달되는 실행 옵션을 제공하는 Session.connection()을 호출하여 트랜잭션 시작을 명시적으로 설정한다.