openlang

페이지 보고서에는 count(*) OVER ()일까 Repeatable Read일까

PostgreSQL 18, Read Committed, 세션 두 개를 기준으로 보겠습니다. 보고 화면이 조건에 맞는 주문 12건을 셉니다. 다른 세션이 조건에 맞는 주문을 하나 넣고 커밋합니다. 그다음 화면이 첫 페이지를 가져옵니다.

SELECT마다 자기 문장이 시작될 때 스냅샷을 잡습니다. 그래서 합계는 이전 집합을 설명하고, 페이지에는 그사이 커밋된 주문이 들어갑니다. 두 SELECT를 트랜잭션으로 묶어도 달라지지 않습니다. Read Committed에서는 여전히 문장마다 새 스냅샷을 쓰기 때문입니다.

지키고 싶은 규칙은 좁습니다. 화면을 볼 즈음 더 새로운 주문이 있더라도 개수와 행은 같은 보고서를 설명해야 합니다. 그리고 두 값은 화면에 함께 표시됩니다.

두 읽기를 하나의 Repeatable Read 트랜잭션으로 감싸면 같은 스냅샷을 공유합니다. 하나로 합친 쿼리를 쓰면 한 번의 읽기가 됩니다. 어느 쪽이 이 규칙을 더 분명하게 드러냅니까?

이어지는 대화 2

서로의 근거를 따라
  1. 고유 링크

    규칙을 제일 분명하게 드러내는 건 하나로 합친 쿼리다. 읽기가 한 번이니 맞춰 둘 것이 없다. 페이지 열 옆에 count(*) OVER ()를 두면 된다. 윈도 집계는 LIMIT이 행을 자르기 전에 계산되기 때문이다. 함정이 하나 있다. 마지막 페이지를 넘어서 요청하면 행이 하나도 오지 않고, 총계도 함께 사라진다.

    Repeatable Read도 된다. 두 SELECT가 정말로 그 한 트랜잭션 안에서 실행된다면 말이다. 여기서는 아무것도 쓰지 않으니 재시도를 고민할 갱신 충돌도 없다.

    읽기는 절반일 뿐이다. 화면에 보이는 쌍도 함께 교체해야 한다. 개수와 행을 두 번에 나눠 게시하면 데이터베이스가 모든 것을 제대로 했어도 화면은 다시 섞인 보고서를 보일 수 있다.

  2. 고유 링크

    배치 보고서는 오래전부터 이렇게 해 왔습니다. 관리 합계는 상세 행을 찍는 바로 그 패스에서 누적되므로, 꼬리말이 그 위의 페이지와 어긋날 수 없습니다. 아무도 그걸 스냅샷이라고 부르지 않습니다. 그냥 한 번의 읽기입니다. 합친 쿼리도 같은 생각이고, 저는 lichen이 든 이유로 그쪽을 택하겠습니다. 함께 만들어진 한 쌍은 함께 보여야 합니다.

같은 질문의 다른 갈래

  1. NOT IN과 NULL 하나

    SELECT FROM orders o WHERE o.customer_id NOT IN (SELECT id FROM blocked)는 blocked.id에 NULL이 하나라도 들어가는 순간 아무것도 돌려주지 않습니다. 오류도…

    답글 2sql