Mysql 사무 와 데이터 의 일치 성 처리 문 제 를 분석 합 니 다.

이 글 은 안전성,용법,병행 처리 등 을 통 해 Mysql 사무 와 데이터 의 일치 성 처리 문 제 를 상세 하 게 분석 했다.다음은 모든 내용 이다.
업무 중 에 우 리 는 자주 이런 문 제 를 만 날 수 있 습 니 다.재 고 를 업데이트 해 야 합 니 다.우리 가 사용 가능 한 재 고 를 조회 하여 수정 을 준비 할 때 다른 사용자 들 이 이 재고 데 이 터 를 수정 해서 우리 가 조회 한 데이터 에 문제 가 있 을 수 있 습 니 다.다음은 해결 방법 을 살 펴 보 겠 습 니 다.
MySQL 의 InnoDB 에서 미리 설 정 된 Tansaction isolation level 은 REPEATABLE READ(다시 읽 을 수 있 음)입 니 다.
SELECT 뒤에 UPDATE 같은 폼 을 원한 다 면 SELECT..UPDATE 를 사용 하 는 것 이 좋 습 니 다.
예 를 들 어:
상품 표 products 에 상품 수량 을 저장 하 는 quantity 가 있다 고 가정 하면 주문 이 성립 되 기 전에 quantity 상품 수량 이 충분 한 지(quantity>0)를 확인 한 다음 에 수량 을 1 로 업데이트 해 야 합 니 다.코드 는 다음 과 같 습 니 다:

SELECT quantity FROM products WHERE id=3; UPDATE products SET quantity = 1 WHERE id=3;
왜 안전 하지 않 습 니까?
소량의 상황 에서 문제 가 없 을 수도 있 지만 대량의 데이터 액세스 에 문제 가 생 길 수 있 습 니 다.만약 에 우리 가 quantity>0 의 상황 에서 재 고 를 공제 할 수 있다 면 프로그램 이 첫 번 째 줄 에서 SELECT 에서 읽 은 quantity 가 2 라 고 가정 하면 숫자 가 틀 리 지 않 은 것 처럼 보이 지만 MySQL 이 UPDATE 를 준비 하고 있 을 때 누군가가 재 고 를 0 으로 잠 갔 을 수도 있 지만 프로그램 은 전혀 모 르 고 틀 리 는 UPDATE 를 내 려 갔다.따라서 읽 기와 제출 한 데이터 가 모두 정확 하 다 는 것 을 확보 해 야 한다.
그래서 우 리 는 MySQL 에서 이렇게 테스트 할 수 있 습 니 다.코드 는 다음 과 같 습 니 다.

SET AUTOCOMMIT=0; BEGIN WORK; SELECT quantity FROM products WHERE id=3 FOR UPDATE;
이 때 products 데이터 에서 id=3 의 데이터 가 잠 겨 있 습 니 다(주 3).다른 사 무 는 이번 업무 가 제출 되 기 를 기 다 려 야 실행 할 수 있 습 니 다.

SELECT * FROM products WHERE id=3 FOR UPDATE 
이렇게 하면 quantity 가 다른 일 에서 읽 은 숫자 가 정확 하 다 는 것 을 확보 할 수 있다.

UPDATE products SET quantity = '1' WHERE id=3 ; COMMIT WORK;
데이터베이스 에 기록 하고 products 잠 금 을 해제 합 니 다.
주 1:BEGIN/COMMIT 는 업무 의 시작 과 끝 점 으로 두 개 이상 의 MySQL Command 창 을 사용 하여 잠 긴 상 태 를 서로 관찰 할 수 있 습 니 다.
주 2:업무 진행 중SELECT ... FOR UPDATE또는 LOCK IN SHARE MODE 와 같은 데이터 가 있 을 때 다른 업무 가 끝 난 후에 야 실 행 됩 니 다.보통 SELECT...은 이 영향 을 받 지 않 습 니 다.
주 3:InnoDB 가 Row-level Lock 으로 미리 설정 되 어 있 기 때문에 데이터 열의 잠 금 은 이 편 을 참고 할 수 있 습 니 다.
주 4:InnoDB 폼 은 LOCK TABLES 명령 을 사용 하지 마 십시오.만약 에 부득이 하 게 사용 하려 면 먼저 InnoDB 가 LOCK TABLES 를 사용 하 는 것 에 대한 공식 설명 을 보고 시스템 이 자주 잠 겨 있 지 않도록 하 십시오.
고급 용법
만약 우리 가 먼저 조회 한 후에 데 이 터 를 업데이트 해 야 한다 면,이렇게 문장 을 사용 하 는 것 이 가장 좋다.

UPDATE products SET quantity = '1' WHERE id=3 AND quantity > 0;
이렇게 하면 사물 을 첨가 하지 않 아 도 처리 할 수 있다.
mysql 처리 높 은 합병,재고 초과 판매 방지
아주 좋 은 문장 을 보고 특별히 이것 을 배 웠 다.
오늘 왕 사장 은 또 우리 에 게 수업 을 해 주 었 습 니 다.사실은 my sql 은 높 은 합병 을 처리 하여 재고 의 초과 판 매 를 방지 하 는 문 제 를 제 기 했 습 니 다.작년 에 왕 사장 은 이미 말 했 습 니 다.하지만 안 타 깝 게 도 모두 가 알 아 들 었 지만 현실 개발 에 서 는 그런 의식 이 없 었 다.오늘 은 제 이해 에 대해 서 이 문 제 를 정리 하고 앞으로 이런 과정 이 많 았 으 면 좋 겠 습 니 다.
먼저 재고 초과 판매 문제 에 대해 설명 한다.일반 전자상거래 사 이 트 는 단체 구 매,스톱워치,특가 와 같은 활동 을 만 날 수 있 는데 이런 활동 은 방 문 량 이 급증 하고 수천 명 에서 수천 명 이 한 상품 을 앞 다 투어 구 매 하 는 것 이 공 통 된 특징 이다.그러나 행사 상품 으로서 재 고 는 분명 한계 가 있 을 것 이다.재 고 를 어떻게 통제 하여 초과 구 매 를 하지 않도록 하 는 지,불필요 한 손실 을 방지 하 는 것 은 많은 전자상거래 사이트 프로그래머 들 이 골 치 아 픈 문제 이자 가장 기본 적 인 문제 이다.
기술적 으로 분석 하면 많은 사람들 이 사 무 를 생각 할 것 이다.그러나 사 무 는 재고 의 초과 판 매 를 통제 하 는 필수 조건 이지 만 충분 한 필요조건 은 아니다.
예:
총 재고:4 개 상품
청구 인:a,1 개 상품 b,2 개 상품 c,3 개 상품
프로그램 은 다음 과 같 습 니 다:

beginTranse(    )
try{
 $result = $dbca->query('select amount from s_store where postID = 12345');
 if(result->amount > 0){
  //quantity          
  $dbca->query('update s_store set amount = amount - quantity where postID = 12345');
 }
}catch($e Exception){
 rollBack(  )
}
commit(    )
이상 의 코드 는 우리 가 평소에 재 고 를 통제 하고 쓴 코드 입 니 다.대부분 사람들 이 이렇게 씁 니 다.문제 가 크 지 않 은 것 처럼 보이 지만 사실은 큰 구멍 이 숨 어 있 습 니 다.데이터 베 이 스 를 방문 하 는 것 은 디스크 파일 에 대한 접근 입 니 다.데이터 베이스 에 있 는 시 계 는 디스크 에 저 장 된 파일 하나,심지어 한 파일 에 여러 장의 시 계 를 포함 하고 있 습 니 다.예 를 들 어 높 은 병발 로 인해 현재 세 명의 사용자 a,b,c 세 명의 사용자 가 이 업무 에 들 어 왔 습 니 다.이때 공유 자물쇠 가 생 길 수 있 기 때문에 select 를 할 때 이 세 명의 사용자 가 찾 은 재고 수량 은 모두 4 개 입 니 다.또한 my sql innodb 가 찾 은 결 과 는 버 전 관리 가 있 습 니 다.다른 사용자 가 commt 를 업데이트 하기 전에(즉,새로운 버 전이 나 오기 전에)현재 사용자 가 찾 은 결 과 는 여전히 버 전 입 니 다.
그 다음 에 update 입 니 다.만약 에 이 세 사용자 가 동시에 update 에 도착 하면 이 때 update 업데이트 문 구 는 병렬 직렬 화 됩 니 다.즉,동시에 도착 하 는 세 사용자 에 게 순 서 를 정 하고 하 나 를 실행 하 며 배타 자 물 쇠 를 생 성 합 니 다.현재 이 update 문 구 는 commt 전에 다른 사용자 가 실행 을 기다 리 고 commt 후에 새로운 버 전 을 생 성 합 니 다.이렇게 집행 한 후에 재 고 는 틀림없이 마이너스 가 될 것 이다.그러나 상기 설명 에 따 르 면 우 리 는 코드 를 수정 하면 초과 구 매 현상 이 나타 나 지 않 을 것 이다.코드 는 다음 과 같다.

beginTranse(    )
try{
 //quantity          
 $dbca->query('update s_store set amount = amount - quantity where postID = 12345');
 $result = $dbca->query('select amount from s_store where postID = 12345');
 if(result->amount < 0){
  throw new Exception('    ');
 }
}catch($e Exception){
 rollBack(  )
}
commit(    )
또한 더욱 간결 한 방법:

beginTranse(    )
try{
 //quantity          
 $dbca->query('update s_store set amount = amount - quantity where amount>=quantity and postID = 12345');
}catch($e Exception){
 rollBack(  )
}
commit(    )
1.초 에 죽 이 는 상황 에서 이렇게 고주파 로 데이터 베 이 스 를 읽 고 쓰 지 못 하면 성능 문제 가 심각 할 것 입 니 다.
캐 시 를 사용 해 야 합 니 다.초 살 이 필요 한 상품 을 캐 시 에 넣 고 잠 금 으로 병행 상황 을 처리 해 야 합 니 다.사용자 의 스톱워치 를 받 고 주문 서 를 제출 한 상황 에서 먼저 상품 수량 을 감소(잠 금/잠 금 해제)한 다음 에 다른 방면 의 처 리 를 한다.처리 실 패 는 데 이 터 를 1(잠 금/잠 금 해제)증가 시 키 고 그렇지 않 으 면 거래 성공 을 나타 낸다.
상품 수량 이 0 으로 점차 줄 어 들 면 상품 이 순식간에 죽 었 음 을 나타 내 고 다른 사용자 의 요 구 를 거절 합 니 다.
2.이것 은 데이터 베 이 스 를 직접 조작 할 수 없 을 것 입 니 다.끊 을 것 입 니 다.라 이브 러 리 를 직접 읽 고 라 이브 러 리 를 쓰 는 것 은 데이터베이스 에 대한 압력 이 너무 커서 캐 시 를 사용 해 야 한다.
당신 이 팔 려 고 하 는 상품,예 를 들 어 10 개의 상품 을 캐 시 에 넣 으 세 요.그리고 memcache 에 계산 기 를 설치 하여 요청 수 를 기록 합 니 다.이 요청 서 는 당신 이 판매 하고 자 하 는 상품 수 를 기수 로 할 수 있 습 니 다.예 를 들 어 10 개의 상품 을 팔 고 싶 으 면 100 개의 요청 만 들 어 올 수 있 습 니 다.그러면 카운터 가 100 에 이 르 렀 을 때 뒤에 들 어 오 는 사람 은 스톱워치 가 끝나 면 서버 의 압력 을 줄 일 수 있 습 니 다.그리고 이 100 개의 요구 에 따라 먼저 돈 을 지불 하 는 선 득 후 지불 의 제시 상품 은 순식간에 끝 냅 니 다.
3.우선,다 중 사용자 가 같은 기록 을 수정 할 때 나중에 제출 한 사용자 가 전자 가 제출 한 결 과 를 덮어 쓸 것 입 니 다.
이것 은 바로 잠 금 메커니즘 을 사용 하여 해결 할 수 있다.낙관적 인 잠 금 이나 비관 적 인 잠 금 이다.
낙관 자물쇠:데이터베이스 에 버 전 번호 의 필드 를 설계 하고 매번 수정 할 때마다+1 을 하 는 것 입 니 다.이렇게 제출 할 때 제출 전의 버 전 번호 보다 동시 제출 한 것 인지 아 닌 지 를 알 수 있 습 니 다.그러나 한 가지 단점 은 응용 프로그램 에서 만 제어 할 수 있 습 니 다.만약 에 크로스 응용 프로그램 이 같은 데이터 낙관 자 물 쇠 를 수정 하면 어 쩔 수 없습니다.이 럴 때 비관 자 물 쇠 를 고려 할 수 있 습 니 다.
비관 적 잠 금:데이터베이스 차원 에서 데 이 터 를 직접 잠 그 는 것 입 니 다.Oaclece 에서 사용 하 는 것 과 같 습 니 다select xxxxx from xxxx where xx=xx for update그러면 다른 스 레 드 는 데 이 터 를 제출 할 수 없습니다.
잠 금 방식 을 제외 하고 수신 잠 금 방식 도 사용 할 수 있다.사고방식 은 데이터베이스 에 상태 표지 위 치 를 설계 하 는 것 이다.사용자 가 데 이 터 를 수정 하기 전에 상태 표지 위 치 를 편집 중인 상태 로 표시 하면 다른 사용자 가 이 기록 을 편집 하려 고 할 때 시스템 은 기 존의 다른 사용자 가 편집 하고 있 으 면 편집 요청 을 거부한다.운영 체제 에서 어떤 파일 이 실행 되 고 있 는 것 과 같 습 니 다.그리고 이 파일 을 수정 하려 고 할 때 시스템 은 이 파일 을 편집 하거나 삭제 할 수 없다 는 것 을 알려 줍 니 다.
4.데이터베이스 차원 에서 자 물 쇠 를 추가 하 는 것 을 권장 하지 않 습 니 다.서버 의 메모리 잠 금(메 인 키 잠 금)을 사용 하 는 것 을 권장 합 니 다.한 사용자 가 어떤 id 의 데 이 터 를 수정 하려 고 할 때 수정 할 id 를 memcache 에 저장 합 니 다.다른 사용자 가 이 id 의 데 이 터 를 수정 하려 고 할 때 memcache 에 이 id 의 값 이 있 을 때 그 사용자 의 수정 을 막 습 니 다.
5.실제 응용 에서 my sql 에 게 직접 읽 고 쓰 라 고 하 는 것 이 아니 라'외부의 힘'을 빌려 캐 시,메 인 라 이브 러 리 를 이용 하여 읽 기와 쓰기 분리,분 표,대기 열 쓰기 등 방법 으로 읽 기와 쓰 기 를 낮 출 수 있 습 니 다.
비관 자물쇠 와 낙관 자물쇠
우선,다 중 사용자 가 같은 기록 을 수정 할 때 나중에 제출 한 사용자 가 전자 가 제출 한 결 과 를 덮어 쓸 것 입 니 다.이것 은 바로 잠 금 메커니즘 을 사용 하여 해결 할 수 있다.낙관적 인 잠 금 이나 비관 적 인 잠 금 이다.
비관 적 인 자물쇠(Pessimstic Lock)는 말 그대로 비관 적 이다.데 이 터 를 가 지 러 갈 때마다 다른 사람 이 수정 할 것 이 라 고 생각 하기 때문에 데 이 터 를 가 져 올 때마다 자 물 쇠 를 잠 그 면 다른 사람 이 이 데 이 터 를 가 지 려 고 할 때 까지 block 을 한다.전통 적 인 관계 형 데이터 베이스 에 이런 자물쇠 체 제 를 많이 사용 했다.예 를 들 어 줄 자물쇠,시계 자물쇠 등,읽 기 자물쇠,쓰기 자물쇠 등 은 모두 조작 하기 전에 먼저 자 물 쇠 를 잠 그 는 것 이다.
낙관 잠 금(Optimistic Lock)은 말 그대로 낙 천적 이어서 데 이 터 를 가 지 러 갈 때마다 다른 사람 이 수정 하지 않 을 것 이 라 고 생각 하기 때문에 잠 그 지 않 지만 업데이트 할 때 이 기간 에 다른 사람 이 이 데 이 터 를 업데이트 하 러 갔 는 지,버 전 번호 등 메커니즘 을 사용 할 수 있 는 지 판단 한다.낙관적 잠 금 은 다 독 응용 유형 에 적합 합 니 다.이렇게 하면 스루풋 을 높 일 수 있 습 니 다.예 를 들 어 데이터 베 이 스 를 write 와 유사 하 게 제공 하면condition 메커니즘 의 사실은 모두 제공 하 는 낙관적 인 자물쇠 이다.
두 가지 자 물 쇠 는 각각 장단 점 이 있 기 때문에 어느 것 이 좋 은 지 단순히 정의 해 서 는 안 된다.낙관적 인 자 물 쇠 는 데이터 수정 이 비교적 적 고 빈번 한 장면 을 읽 는 데 적합 하 다.소량의 충돌 이 발생 하 더 라 도 대량의 자물쇠 비용 을 줄 이기 때문에 시스템 의 스루풋 을 높 였 다.그러나 자주 충돌(데 이 터 를 많이 쓰 는 경우)이 발생 하면 상층 부 에 끊임없이 사용 되 지 않 는 retry 가 오히려 성능 을 떨 어 뜨 려 비관 적 인 자 물 쇠 를 사용 하 는 것 이 적절 하 다.
실전

이 표 의 amount 를 수정 하고 명령 행 창 두 개 를 엽 니 다.
첫 번 째 창 A;

SET AUTOCOMMIT=0; BEGIN WORK; SELECT * FROM order_tbl WHERE order_id='124' FOR UPDATE;
두 번 째 창 B:

#     ID 124      
UPDATE `order_tbl` SET amount = 1 WHERE order_id = 124;
우 리 는 창 A 가 사물 을 추가 하고 이 데 이 터 를 잠 그 는 것 을 볼 수 있 습 니 다.창 B 가 실 행 될 때 이런 문제 가 발생 합 니 다.

첫 번 째 창 에서 모든 것 을 제출 합 니 다:

SET AUTOCOMMIT=0; BEGIN WORK; SELECT * FROM order_tbl WHERE order_id='124' FOR UPDATE;
UPDATE `order_tbl` SET amount = 10 WHERE order_id = 124;
COMMIT WORK;
my sql 처리 높 은 동시 다발,재고 초과 판 매 를 방지 합 니 다.이상 은 본 글 의 전체 내용 입 니 다.만약 에 모 르 시 는 것 이 있 으 면 아래 에 댓 글 을 달 아 토론 할 수 있 습 니 다.

좋은 웹페이지 즐겨찾기