2014-10-22 2 views
2

내 레일 앱의 경우 mysql 데이터베이스 테이블에서 중복 레코드를 발견했습니다.동시 발생 원인 중복 레코드가 삽입되었습니다

user_product 테이블

ID | user_id | product_id | 금액 | created_at | update_at

181115 | 811115 | 1800 | 2 | 2014-10-16 03:00:13 | 2014-10-16 03:03:58

181116 | 811115 | 1800 | 2 | 2014-10-16 03:00:13 | 2014-10-16 03:03:58

하지만 사용자 제품 모델에의 validates_uniqueness_of를 추가했습니다.

class UserProduct < ActiveRecord::Base validates_uniqueness_of :product_id, scope: :user_id ...... end 

나는 rails doc에서 유용한 정보를 발견했다. 액티브 ::하는 검증 #과 함께이 검증 방법을 사용

, 중복 레코드 삽입의 부재를 보장하지 않습니다 저장 응용 프로그램 수준에서 고유성 검사 조건을 경주 본질적 경향이 있기 때문이다. 이것은 '직렬화 가능'격리 수준으로 트랜잭션을 사용하는 경우에도 발생할 수 있습니다.

데이터베이스 격리 수준을 검사했는데 "직렬화 가능"이 아니라 "반복 가능 읽기"입니다.

SELECT @@global.tx_isolation; 
REPEATABLE-READ 

이 문제를 해결하기 위해 고유 인덱스를 합성 할 수 있습니다.

add_index :user_product, [:product_id, :user_id], unique: true 

근원을 찾으십시오. 아무도 저를 도울 수 있기를 바랍니다. 고마워요!

+0

은 기본 키가 아닌 ID입니까? –

+0

예, 기본 키가 아닙니다 – pangpang

답변

4

근본 원인은 앱의 경쟁 조건 일 수 있습니다. 당신이 특히

http://1rad.wordpress.com/2008/09/29/0x04-atomic-science/

을 등 다중 스레드, 여러 웹 요청과 같은 경쟁 조건을 가지고 때 데이터베이스 수준의 고유성을 보장 할 수 없기 때문에 정말, 완전하게 작동하지 않습니다 validates_uniqueness_of 레일 예를 들어 순차적 인 ID 번호는 사용자가 두 개의 Ajax 요청 (하나만 사용하는 대신)을 트리거하는 "저장"버튼과 모델 find_or_create을 실행하는 레일스 컨트롤러를 두 번 클릭하여 발생합니다.

REPEATABLE-READ의 데이터베이스 분리는 경쟁 조건에 상관 없습니다. REPEATABLE-READ는 트랜잭션 동안 획득 된 모든 잠금이 트랜잭션 지속 기간 동안 유지된다는 것을 의미합니다. 당신은 여전히 ​​팬텀 읽기를 가질 수 있습니다. Rails 튜토리얼에서 설명하려고했던 것은 validates_uniqueness_of 경쟁 조건이 SERIALIZABLE을 사용하고 있어도 팬텀 읽기를 방지하고 REPEATABLE-READ보다 더 보호된다는 것입니다.

앱에서이를 수정하려면 레일스에서 ​​validates_uniqueness_of에만 독점적으로 의존하지 말고 고유 한 기본 키 또는 고유 한 복합 색인 솔루션과 같이 데이터베이스에 내장 된 고유성 보장을 사용하십시오. 이렇게하면 Rails에 경주가있을 때에도 데이터베이스가 경주를 막을 수 있습니다.

레일스에서 ​​(DB가 아닌) 레이스를 제거 할 수는 있지만 일반적인 Rails 웹 앱에서는 현명한 방법이 아닙니다. 예를 들어, 한 번에 하나의 요청 만 허용하는 웹 서버를 사용하고 한 번에 하나의 Rails 앱만 데이터베이스에 연결하여 Rails에서 경쟁을 제거 할 수 있습니다.

경주가 Ajax 버튼과 같은 것으로 인해 발생하는 경우 두 번 클릭하면 일부 스마트를 버튼에 넣음으로써 사용자에게 매우 유용 할 수 있으므로 빠른 속도로 동일한 데이터를 두 번 보내지는 않습니다 계승. 이렇게하면 많은 일반적인 사용 사례에 대한 Ajax 경주가 제거됩니다.

+0

대단합니다! 나는 질문이있다 : 경쟁 조건을 막을 수있는 방법이 있는가? – pangpang

+0

예. 나는 더 많은 정보를 추가했다. – joelparkerhenderson

+0

매우 좋습니다! 당신의 도움을 주셔서 감사합니다! – pangpang

관련 문제