(python) SQLAlchemy 알아보기 (3) Connection과 Session

  ·   3 min read

지금까지 sqlalchemy의 core layer에 대해 공부했고, 이제는 orm layer에 대해 알아보자. SQLAlchemy를 이용한 application을 다루게 된다면 가장 많이 접할 것이다.

Session #

session은 sqlalchemy 객체로, DBMS와 ORM수준으로 상호작용하고 트랜잭션을 관리하기 위한 객체이다.

생성하는 방법으로는 두가지가 있는데, 모두 Engine 인스턴스가 필요하다.

  1. Session(Engine)

  2. sessionmaker(bind=Engine)()

    sessionmaker는 Session을 생성하기 위한 factory이며 호출시 session을 생성해준다. Engine처럼 보통 DB당 하나씩만 있으면 된다.

주의할 점으로는 Session 객체가 생성되었다고 해서 connection이 할당된 것은 아니라는 점이다. 다음 코드를 interactive mode로 실행해보자!

# test.py
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker

engine = create_engine("mysql+pymysql://root:password@localhost/test_db", echo=True)

sessionmaker = sessionmaker(bind=engine)
session = sessionmaker()

 python -i test.py
>>> (아무것도 출력되지 않는다)
>>> session
<sqlalchemy.orm.session.Session object at 0x106472710>
>>> engine.pool.status()
'Pool size: 5  Connections in pool: 0 Current Overflow: -5 Current Checked out connections: 0'

session이 생겼다고 해서 connection을 가져오지(connect) 않은 것을 확인할 수 있다. 실제 DB 실행시에 connection 객체를 가져올 것이다.

쿼리 실행 & 객체 매핑 #

execute

query

scalars/scalar

위 3개의 차이점 설명 필요

add

flush

refresh

delete

위 메서드들은 session과 연결된 ORM 객체들을 관리하기 위해 사용한다.

트랜잭션 관리 #

with으로 열 수도 있고, begin() ~ close()로 할 수도 있다. Engine과 거의 유사하며, ORM을 사용하기 위해 Session이 필요하다고 봐도 무방하다.

추가로, rollback과 commit 메서드도 존재한다.

begin()시에는 트랜잭션이 열리며, close()시에 rollback()/commit()이 실행된다.

begin() 역시 engine과 마찬가지로 쿼리가 나감(+connection을 가져옴)을 보장하지 않는다!

역으로, close() 메서드도 connection이 할당되지 않았다면 굳이 쿼리가 나가지 않을 것이다.

 python -i test.py
>>> session.begin()
<sqlalchemy.orm.session.SessionTransaction object at 0x1046085f0>
>>> engine.pool.status()
'Pool size: 5  Connections in pool: 0 Current Overflow: -5 Current Checked out connections: 0'

ORM과 트랜잭션의 관계 & expire_on_commit #

inspect(obj).expired를 통해 해당 orm 객체가 expire되었는지 확인할 수 있다.

생성시에 줄 수 있는 옵션으로 expire_on_commit이 있는데, 이는 commit 시에 기존에 session에 묶여있던 ORM 객체들을

engine connection session #

이제, engine, connection, session에 대해 알아봤는데, 차이점을 알 필요가 있다.

connection은 DB API에 있는 connection을 래핑한 객체이다.

engine은 connection들을 관리하기 위한 객체이다.

session은 트랜잭션을 수행하고 ORM 객체와 매핑&객체의 영속화를 수행하기 위한 객체이다. engine으로부터 만들어진다.

정확한 관계 정리: #

  1. **Connection**과 **Connection Pool**은 SQLAlchemy가 사용하는 DBAPI 드라이버와 연관이 있습니다. DBAPI 드라이버는 데이터베이스와의 연결을 관리하며, 이는 PEP 249에 정의된 표준을 따릅니다.
  2. **Session**은 SQLAlchemy의 ORM 레이어에서 데이터베이스와 상호작용하는 객체로, 데이터베이스 연결을 직접 관리하지 않고 Connection Pool을 통해 필요할 때만 Connection을 가져와 사용합니다.
  3. Connection은 데이터베이스와의 직접적인 연결을 나타내며, DBAPI 드라이버(예: psycopg2, pymysql)를 통해 데이터베이스와 연결됩니다.
  4. Connection Pool은 SQLAlchemy가 여러 Connection 객체를 재사용하여 성능을 최적화하기 위한 연결 관리 메커니즘입니다.

따라서 Connection과 Connection Pool은 DB 스펙이라고 보기보다는, DBAPI 드라이버와 SQLAlchemy의 연결 관리 메커니즘에 해당합니다. Session은 그 위에서 동작하는 SQLAlchemy ORM의 트랜잭션 관리 도구라고 할 수 있습니다.

https://cotak.tistory.com/310

JPA의 OSIV : https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94

암묵적 begin #

세션을 통한 쿼리 실행은 암묵적으로 트랜잭션을 열어준다.

session은 스레드 세이프한가? #

https://docs.sqlalchemy.org/en/20/orm/session_basics.html#session-faq-threadsafe

Scoped Session (Thread safe?!) #

하지만 session은 threadlocal하지 않음. 즉 여러 스레드가 동시에 접근시 문제가 생길 수 있음 (도대체 어떤 문제?!)

이에 scoped_session이 나옴. scoped_session은 여러 세션을 key ~ value로 저장할 수 있도록 해주는 함수이다. 따라서 요청마다 고유한 값을 key로 하면 요청마다 session이 공유되게 할 수 있는 것이다!

만약, async session의 경우에는 싱글스레드에서 돌아가므로 여러 스레드가 접근하지 않으므로 문제가 생기지 않는 것인가? 오류 전파 등의 이슈가 없는 건지..

https://jay-ji.tistory.com/115

참고 : https://docs.sqlalchemy.org/en/20/orm/session_basics.html#