2012-05-01 4 views
1

불행히도 MyISAM 테이블 (bleagh ...)에 레거시 mysql_ * 함수를 사용하는 응용 프로그램이있어 트랜잭션을 사용할 수 없습니다. 현재 잔액을 얻는 코드를 가지고 있습니다.이 잔액이 맞는지 확인합니다. 그렇다면 양을 빼고 새 잔액을 저장합니다.이전 값을 반환하는 MySQL SELECT

문제는 최근에 두 개의 쿼리가 동일한 시작 밸런스를 가져 와서 수량을 뺀 다음 새 밸런스를 기록하는 경우를 보았습니다. 그들은 둘 다 같은 시작 균형을 잡았으므로 두 업데이트 이후의 끝 균형은 잘못되었습니다.

100 - 10 = 90 
100 - 15 = 85 

그것이 있어야 은 ...

100 - 10 = 90 
90 - 15 = 75 

는 이러한 요청은 떨어져 몇 분을 실행, 그래서 나는 불일치 조건을 경주 할 예정이다 믿지 않는다. 필자의 초기지만, MySQL 캐시는 균형을 잡는 동일한 초기 쿼리의 결과를 저장하고 있습니다. 그러나 관련 테이블이 수정되면이 유형의 캐시는 삭제됩니다.

나는 모든 것을 하나의 쿼리에 넣는 것으로 해결할 것이지만 여전히 이것을 파악하고 싶습니다. 그것은 나를 신비화합니다. 테이블이 수정 될 때 캐시가 삭제되면 일어난 일이 없어야합니다. 아무도이 같은 것을 듣지 못했거나 그것이 일어난 이유에 대한 아이디어가 있습니까?

+1

이 문제를 재현하는 코드의 단순화 된 버전을 게시 할 수 있습니까? –

+0

mysql_ *로 트랜잭션을 사용할 수 없습니까? innoDB 테이블을 사용한다면 mysql_query ("START TRANSACTION") 등을 수행하는 것만으로도 충분하다고 생각한다. – bfavaretto

+0

@bfavaretto 불행히도 그들은 MyISAM입니다. – dqhendricks

답변

1

가 문제의 해결책, 당신의 응용 프로그램이 오래된 데이터가 가장 가능성

2

쿼리 캐시가 될 가능성은 거의 없습니다. MySQL은 기본 데이터 집합이 다른 쿼리에 의해 수정 된 경우 캐시 항목을 무효화 할만큼 똑똑합니다. 쿼리 캐시가 오래된 만료 값을 오랫동안 만료 한 경우 MySQL은 전혀 쓸모가 없습니다.

아마도 커밋되지 않은 커밋 된 트랜잭션이 발생 했습니까? 관련 레코드에 적절한 잠금이 없으면 두 번째 쿼리가 오래된 데이터를 아주 쉽게 잡을 수 있습니다. 테이블을 잠금

+0

동의합니다. 캐싱 문제가 아닙니다. 응용 프로그램에 오래된 데이터가있을 가능성이 큽니다.OP는 그것이 MYISAM (거래가 아니라)이었다라고 말했다. –

+0

이제 모든 데이터를 읽거나 쓰기 전에 모든 테이블을 잠그고 읽을 데이터에는 캐시를 지정하지 않습니다. 문제가 다시 발생했습니다. 여기서 일어나는 일을 상상할 수 없습니다. 마커스가 말했듯이, 그것은 MYISAM입니다. 애플리케이션의 다른 영역에서 편집을 모두 사용 중지했습니다. 관련 변수에 대한 코드를 검색했는데 예기치 않은 할당이 없습니다. 299/300 번이 코드는 정상적으로 작동하며, 한 달에 한 번이 문제가 발생합니다. – dqhendricks

2

:) 생각입니다.

UPDATE account 
SET balance = :current_balance - 10 
WHERE account_id = 1 

이 비슷한 더 많은 일을 수행해야합니다 :

UPDATE account 
SET balance = balance - 10 
WHERE account_id = 1 

그 방법을 이렇게 많은 데이터베이스 응용 프로그램이 작동하는 방법,하지만 대신이 같은 일을, 당신의 업데이트를 수행 할 때, 괜찮습니다 부실 응용 프로그램 데이터에 의존하는 대신 누군가가 데이터베이스를 변경 한 경우에도 데이터베이스의 현재 잔액을 사용합니다. 다른 사람이 그것을 수정되지 않은 경우에만 값을 변경하려면

, 당신은 같은 것을 할 :

UPDATE account 
SET balance = balance - 10 
WHERE account_id = 1 
    AND balance = :current_balance 

영향을받는 행의 수가 1 인 경우에, 당신은, 기록을 한게 성공 다른 사람이 바꿨습니다. 그러나 영향을받은 행 수가 0이면 누군가 다른 사람이 레코드를 변경했습니다. 그런 다음 거기에서 무엇을하고 싶은지 결정할 수 있습니다.

+0

나는 먼저 새로운 잔액을 만들고 잔액을 업데이트하기 전에 잔액에 대한 몇 가지 점검을해야한다는 것을 제외하고는 이것을 할 것입니다. 나는 테이블 잠금을 구현할 것이다. – dqhendricks

+0

@dqhendricks, 동시성이 문제가 아니기 때문에이 플러스 잠금을 구현해야합니다. 당신은 그 요청이 수분 남았다 고했습니다. –

+0

글쎄, 나는 초기 SELECT에서 SQL_NO_CACHE를 잠그는 테이블을 수행했다. 이렇게하면 내가 바라는 부실 캐싱 데이터 문제를 해결할 수 있습니다. 당신의 솔루션은 가능하다면 제가 피하고 싶은 리팩토링을 필요로합니다. 그러나 나는 그것을 다시 볼 수 있다면 ... 나는 돌아올 것이다. – dqhendricks

관련 문제