ORM 과 트랜잭션

ORM Object-relational mapping 은 객체와 데이터베이스 시스템을 연결(맵핑) 해주는 라이브러리다.


ORM과 장단점

ORM(객체 관계형 매핑)은 관계형 데이터베이스와 객체 지향 프로그래밍 언어 간에 데이터를 변환하는 기술입니다. ORM을 사용하면 개발자는 SQL 쿼리를 작성하거나 기본 데이터베이스 구조를 이해하지 않고도 원하는 프로그래밍 언어로 데이터 작업을 할 수 있습니다. 왜 쓰지 말아야 하는지에 대해 논의하기전에, 먼저 ORM의 장점을 살펴보자.

  • 중복 코드 방지?
  • 다른 데이터베이스로 쉽게 교체 가능
  • 여러 테이블에 쉽게 쿼리를 날릴 수 있음
  • 인터페이스를 작성하는 시간을 아껴 비즈니스 로직에 집중할 수 있음

하지만 ORM을 사용할 때 발생할 수 있는 몇 가지 문제가 있습니다. 가장 일반적인 문제는 다음과 같습니다:

  • 프로젝트의 복잡성이 크면 구현하는 난이도가 상승
  • 성능: 특히 복잡하거나 큰 데이터 집합의 경우, ORM은 네이티브 SQL 쿼리를 사용하는 것보다 느릴 수 있습니다. 이는 데이터베이스와 프로그래밍 언어 간에 데이터를 변환하는 데 드는 오버헤드 또는 ORM 자체의 한계 때문일 수 있습니다. ORM은 데이터베이스와 프로그래밍 언어 사이에 추가하는 추가 계층으로 인해 오버헤드가 발생가능 성능 패널티는 최적이 아닌 쿼리를 생성하는 ORM 또는 데이터베이스와 프로그래밍 언어의 데이터 구조 간에 데이터를 변환하는 데 걸리는 시간으로 인해 발생할 수 있음
  • 매핑 복잡성: ORM을 사용하려면 데이터베이스와 프로그래밍 언어 간의 매핑을 정의해야 하는데, 이는 복잡하고 시간이 많이 소요될 수 있습니다. 특히 스키마나 관계가 복잡한 데이터베이스의 경우, 데이터베이스나 코드베이스가 변경되면 매핑을 유지 관리하기가 어려울 수 있습니다.
  • 데이터 무결성: ORM은 때때로 외래 키 또는 고유 인덱스와 같은 데이터 무결성 제약 조건을 적용하기 어렵게 만들 수 있습니다. 이로 인해 데이터 불일치 또는 오류가 발생할 수 있으며, 데이터가 정확하고 일관성 있게 유지되도록 하기 위해 추가 코드가 필요할 수 있습니다. 데이터 정확성과 일관성을 보장하기 위해 개발자는 추가 코드, 유효성 검사 또는 데이터베이스 트리거를 구현해야하므로 애플리케이션의 복잡성이 증가할 수 있음.

전반적으로 ORM은 데이터 작업을 위한 강력한 도구이지만 문제가 없는 것은 아닙니다. 이러한 문제를 방지하려면 요구 사항을 신중하게 고려하고 프로젝트에 적합한 ORM을 선택하는 것이 중요합니다.

N+1 problem

django ORM은 Lazy Loading 방식. ORM에서 명령을 실행할 때마다 데이터베이스에서 데이터를 가져오는 것이 아니라 모든 명령 처리가 끝나고 실제로 데이터를 불러와야 할 시점이 왔을 때 데이터베이스에 쿼리를 실행하는 방식.

N+1 problem 은 쿼리 1번으로 N건의 데이터를 가져왔는데 원하는 데이터를 얻기 위해 이 N건의 데이터를 데이터 수 만큼 반복해서 2차적으로 쿼리를 수행하는 문제

이 문제 해결방식으로 Eager-Loading 방식이 있다.

사전에 쓸 데이터를 포함하여 쿼리를 날리기 떄문에 비효율적으로 늘어나는 쿼리 요청을 방지할수 있다.

  • select_related : fk, one-to-one 처럼 single - valued relationship 에서만 사용가능. SQL join을 사용하는 방법
  • prefetch_relatd: many-to-many, many-to-one 등 모든 relationship에서 사용 가능 SQL where in 구문을 사용하는 방법

ORM에서 트랜잭션, 추가되는 쿼리가 무엇인지? 어떤 용도인지?

먼저 트랜잭션은 현대의 웹 보안에 있어서 매우 중요한 역할을 차지하며 DB와 프로그래밍 언어가 데이터를 주고 받는 과정에

원자성을 부여하는 수단을 일컫는다.

단일 쿼리로 해결할 수 없는 로직을 처리할 때 필요한 기술이 바로 트랜잭션이다.

트랜잭션이란 2개 이상의 쿼리를 하나의 커넥션으로 묶어 DB에 전송하고 이 과정에서 어떠한 에러가 발생할 경우 자동으로 모든 과정을 원래 상태로 돌려놓는다. All or Nothing 전략.

트랜잭션은 하나 이상의 쿼리에서 동일한 Connection 객체를 공유하는 것을 뜻한다. 트랜잭션을 이용하려면 이 자동커밋을 false 로 바꾸고 수동커밋으로 변경해야만한다. 즉 이용자가 직접 커넥션에 대해 커밋과 롤백을 해준다는 뜻이다.

ORM(객체 관계형 맵핑)에서 트랜잭션을 사용하는 경우 트랜잭션을 관리하고 데이터베이스 작업의 일관성 및 격리를 보장하기 위해 추가 쿼리가 발생할 수 있습니다.

  1. 트랜잭션 시작: 트랜잭션이 시작되면 일반적으로 트랜잭션을 시작하기 위해 데이터베이스에 추가 쿼리가 전송됩니다. 이 쿼리는 데이터베이스 관리 시스템에 따라 “시작” 또는 “트랜잭션 시작”과 같은 쿼리인 경우가 많습니다.

  2. 세이브포인트: ORM을 사용하면 전체 트랜잭션을 롤백하지 않고도 트랜잭션에서 롤백할 수 있는 지점을 표시하는 방법인 세이브포인트를 트랜잭션 내에 만들 수 있습니다. 데이터베이스에 추가 쿼리를 전송하여 세이브포인트를 생성하면 이 작업을 수행할 수 있습니다.

  3. 롤백: 롤백이 필요한 경우 트랜잭션이 시작된 이후 또는 마지막 저장 시점 이후의 모든 변경 사항을 취소하기 위해 데이터베이스에 추가 쿼리가 전송됩니다. 이 쿼리는 대개 “롤백” 또는 “세이브포인트로 롤백”과 같은 형식입니다.

  4. 커밋: 트랜잭션이 성공적으로 완료되면 변경 사항을 커밋하고 영구적으로 적용하기 위해 추가 쿼리가 데이터베이스로 전송됩니다. 이 쿼리는 보통 “COMMIT”과 같은 형식입니다.

  5. 잠금: ORM은 동시 업데이트를 방지하기 위해 특정 행이나 테이블에 잠금을 설정할 수도 있으며, 이러한 잠금은 트랜잭션이 커밋되거나 롤백될 때 해제됩니다.

트랜잭션에 사용되는 정확한 쿼리는 사용 중인 특정 ORM 및 데이터베이스 관리 시스템에 따라 달라질 수 있다는 점에 유의하세요.

참고자료

http://egloos.zum.com/springmvc/v/495798

https://blog.sentry.io/2020/09/14/finding-and-fixing-django-n-1-problems/