2017-12-13 15 views
0
book: 
    id: primary key, integer 
    title: varchar 
    borrowed: boolean 
    borrowed_by_user_id: foreign key user.id 

user: 
    id: primary key, integer 
    name: varchar 
    blocked: boolean 

에 제약을 존중 테이블의 행.업데이트는 PostgreSQL의 기본적 수준 (이 요구 사항은 내게되지 않음) 때문에, 다른 테이블 분리 레벨이 최선을 다하고을 읽어

도서를 SELECT FOR UPDATE에 데이터베이스 트랜잭션 하나를 사용하고 책을 아직 대여하지 않은 경우 모든 사용자에게 빌려주고 있습니다. 이 책은 FOR UPDATE으로 선택되었으므로 동시에 빌릴 수 없습니다.

그러나 또 다른 문제가 있습니다. 차단 된 사용자에게 책을 대여하도록 허용 할 수 없습니다. 어떻게 확인할 수 있습니까? 사용자가 차단되지 않았는지 처음에 확인해도 동시 트랜잭션으로 인해 사용자가 차단 될 수 있으므로 체크를하면 결과가 올바르지 않을 수 있습니다.

예를 들어 관리자 패널의 동시 트랜잭션으로 사용자를 차단할 수 있습니다.

어떻게 문제를 해결할 수 있습니까? 내가 SERIALIZABLE를 사용할 수있는


  1. 나는 참조하십시오. 처리 오류가 필요해, 네?
  2. 어떻게 CHECK이 작동하는지 잘 모르겠습니다. 그것에 대해 더 많이 말해 줄 수 있습니까?

답변

0

이들은 실제로 두 가지 질문입니다. 책 소개

: 당신이 바로 그것을 밖으로 대출을 고려으로 SELECT ... FOR UPDATE와 함께 책을 잠그면

,이 “ 비관적 잠금 ”의 예이며 모든 동시 활동에 대한 책을 차단합니다.

트랜잭션이 매우 짧으면 –입니다. 특히 트랜잭션의 잠금과 트랜잭션 사이에 사용자 상호 작용이 없으면 좋습니다.

그렇지 않으면 “ 낙관적 잠금 ”을 사용해야합니다. 이것은 여러 가지 방법으로 수행 할 수 있습니다

  1. 사용 REPEATABLE READ 트랜잭션 격리를. 그런 다음 데이터를 읽은 후에 수정 된 책을 업데이트하면 직렬화 오류가 발생합니다 (마지막주의 사항 참조).

  2. 도서를 선택할 때 시스템 열 ctidxmin의 값을 기억하십시오. 어떤 행이 업데이트되지됩니다

    UPDATE books SET ... 
    WHERE id = ... 
        AND ctid = original_ctid AND xmin = original_xmin; 
    

    경우 그것을보고 있기 때문에, 누군가가 책을 수정해야합니다 나서 다음과 같이 업데이트합니다.사용자에 대한

:

세 가지 아이디어 : 당신은 SERIALIZABLE 트랜잭션 격리를 사용

  1. (끝의 참고 참조).

  2. 사용자가 빌린 책 수가 포함 된 카운터를 사용자가 유지 관리합니다.

    은 그럼 당신은 TRUE를 얻을 수

    ALTER TABLE users ADD CHECK (NOT blocked OR books_borrowed = 0); 
    

    이러한 점검 제한 조건은 각 문장의 마지막에 평가가되는 등의 점검 제한 조건, 다른 오류가 발생합니다을 가질 수 있습니다.

    그래서 책을 빌린 거래 또는 사용자를 차단하는 거래가 실패해야합니다 (두 거래가 모두 사용자를 수정해야합니다).

  3. 오른쪽 사용자에게 책을 대출하기 전에 다른 책을 빌려, 당신이 TRUE 얻을 경우, 당신은 트랜잭션을 중단

    SELECT blocked FROM users WHERE id = ... FOR UPDATE; 
    

    실행합니다.

    사용자를 차단하려는 동시 트랜잭션의 사용자도 SELECT ... FOR UPDATE이고 이면 해당 사용자에게 대여 한 책이 있는지 확인하십시오.

    그런 식으로 불일치가 발생할 수 있습니다. 사용자를 차단하려면 사용자에게 책을 빌려주고 싶은 모든 동시 트랜잭션을 완료해야 효과를 볼 수 있습니다. 그렇지 않으면 사용자가 사용자 차단이 완료되면 실패합니다. 높은 격리 수준에 대한

참고 : REPEATABLE READ 또는 SERIALIZABLE의 격리 수준에서 트랜잭션을 실행하면

, 당신은직렬화 오류가 발생할 수 있습니다. 이것들은 프로그램의 버그가 아니며 정상적인 것으로 예상됩니다. 직렬화 오류가 발생하면 롤백하고 동일한 트랜잭션을 다시 시도해야합니다. 그것은 경주 조건에 대해 걱정할 필요가 없다는 이유로 지불하는 가격입니다.

+0

약 어떨까요? 'SELECT * FROM book INNER JOIN 사용자가 book.borrowed_by_user_id = user.id user.blocked FOR UPDATE하지 않은 곳은 어디입니까? – asdas

+0

차단되지 않은 모든 사용자를 잠그고 그러한 책을 모든 사용자에게 대여합니다. 그게 무슨 소용입니까? –

+0

'SELECT * FROM book INNER JOIN 사용자가 book.borrowed_by_user_id = user.id user.blocked가 아닌 곳과 book.id = 100 FOR UPDATE' 이제 내 게시물의 의미에서 안전한가요? – asdas