stored procedure안에 if문이 들어갈 때 원자성이 어떻게 보장되는지를 잘 모르겠습니다.
가령
begin
if not exists ( select * from table )
insert into table (...)
...
...
...
end
이런 sp가 있다고 합시다.
이때 if문이랑 insert문이 무조건 같이 실행된다는 보장이 있어야하잖아요.
안 그러면 저 sp가 동시에 실행될때 동시에 if not exists를 만족하고, 동시에 insert를 해버리겠죠.
만약 이게 c코드고 lock을 걸 수 있다면 if문이랑 insert문을 감싸서 lock,free를 했겠죠.
제가 말한 것이 보장이 되나요?
당연히 될거라고 생각은 하는데, 그 규칙이 어떤지가 궁금합니다. begin부터 end까지 다 lock을 걸어버리는 건 아닐거 같고.
아니면 그 spec이 설명되있는 문서같은 걸 링크해주셔도 감사하겠습니다.
Comment 10
-
건우아빠
2013.09.10 12:45
-
질문하신 분의 의도는 그게 아닌거 같습니다.
SP가 두 세션에서 동시에 실행될때 IF NOT EXISTS가 동시에 만족하면 어떡하냐.... 라는 질문인듯
LOCK에 대한 강의가....
-
건우아빠
2013.09.10 13:44
어! 그런가요... 나이가 먹어서 그런가....
IF NOT EXISTS 만족 할려면 LOCK 개념과 에러 처리 부분이 들어가는게 ......
-
코코포도
2013.09.10 14:13
좀 더 자세한 말씀을 듣고 싶습니다. 사실 sql은 명시적으로 lock을 할 필요가 없도록 만들어진 줄 알았는데, 그게 아닌가보네요. 뮤텍스가 되어야 할 부분은 일일 다 명시적으로 lock을 해 줘야하나요?
근데 그러면 insert같은 걸 할때마다 테이블전체에 lock을 걸게 될텐데, 이러면 거의 최악의 시나리오 아닌가요? db 자체적으로 효율적인 lock 정책이 있을 줄 알았는데 말이죠.
-
건우아빠
2013.09.10 15:29
깊이 들어가면 힘드는데요. 이건 초짜해커님이 보충 설명 해주실겁니다.
-
lock은 좀.... ㅠㅠ
-
맨즈밤
2013.09.10 18:08
잘은 모르지만 아는 범위내에서 말씀드리겠습니다..더 좋은 답변은 초짜해커님이 해주실거에요 ^^
에...두개의 프로시저를 동시에 호출해서 생기는 경합을 방지하려면 SP_GETAPPLOCK , SP_RELEASEAPPLOCK 를 이용한 애플리케이션 잠금을 이용하면 됩니다. 다만 한번에 하나의 프로시저만 처리되기에 해당프로시저는 최대한 빨리 끝내도록 해야겠지요.
질문자님께서 말씀하신건 간단한 테스트를 해보면
DROP TABLE TABLE1
CREATE TABLE TABLE1 (A VARCHAR(10))--1번세션에서 실행
IF NOT EXISTS ( SELECT * FROM TABLE1 )
BEGIN
WAITFOR DELAY '00:00:05'
INSERT INTO TABLE1 VALUES('A')
END--2번세션에서 실행 : 1번세션 실행후 1~2초 뒤에 실행
IF NOT EXISTS ( SELECT * FROM TABLE1 )
BEGIN
WAITFOR DELAY '00:00:05'
INSERT INTO TABLE1 VALUES('A')
END결과는..두개 다 들어갑니다. 그럼 BEGIN TRAN COMMIT TRAN 을 두 세션 모두 IF 범위 에 씌워주면,... 역시 두개 들어가지요.
단 WAITFOR 함수를 INSERT 뒤에 넣으면 하나만 들어갑니다. EXISTS 안의 SELECT문은 공유잠금으로 서로 읽을수 있거든요.
이건 SQL 기본 설정인 READCOMMITTED 수준의 격리수준으로는 해결이 안되는걸로 보이구요...SERIALIZABLE 의 격리수준은 되야
할듯합니다.
여튼 결론은 코코포도님이 우려하시는대로 값 두개가 동시에 들어갈 가능성은 있는걸로 보입니다.하지만 일반적으로는 현업에서는 잘 발생안하는데요. 왜냐하면 EXISTS 로 데이터를 찾을때는 PK 로 데이터를 찾고 인서트 할때도 PK가있는 테이블이기에 행여 찰나의 순간으로 IF문을 통과해도 인서트시에 중복키 에러가 나기 때문입니다.
테이블에 PK가 안걸려있고, 동시 수행이 매우 빈번해서 중복수행가능성이 예상될경우 애플리케이션락으로 처리하심이 나을듯합니다.
-
등떠밀려서 아는데까지만 설명하겠습니다. (아는데까지만입니다!!!!!!!!!!!!!!!)
if not exists가 동시에 여러 세션에서 만족할 수 있느냐
그럴 수 있을거 같습니다.
여러 세션에서 nolock으로 읽는다면 충분히 가능하고
공유락을 걸고 읽는다 해도 공유락이니까 충분히 가능하고요.
insert 하는 중에 not exists를 평가한다면
nolock으로 평가한다면 false로 평가될것이고 공유락을 걸고 평가하려면 이미 베타락이 걸려있으니 대기하겠네요.
만약 이렇게 중복으로 not exists가 true로 평가되는 일을막으려면
격리수준을 높여서 SERIALIZABLE로 하던지 해야 하고
중복으로 insert 되는걸 막으려면
not exists가 동시에 true로 평가 될 수 있다는 가정을 하고 적절한 예외처리가 필요할거 같습니다.
-
항해자™
2013.09.10 20:28
동시에 호출 되더라도 하나의 요청만 처리되어야 하는게 목적이라면,,,
아래와 같이 해 주시면 lock, free 효과를 보실 수 있습니다,,
int 컬럼 하나를 포함하는 빈 테이블을 만드시고,,
begin tran
select col
from dbo.tblBlank with(updlock)
where col = 1
if ....
begin
...
end
commit tran -
minsouk
2013.09.10 20:50
if 문에 대한 원자성 지원은 없습니다. ^^;
보통 DB 에서는 PK FK 등의 제약조건을 걸고, 후에 들어온넘은 에러가 발생하고 응용은 에러에 대한 exception 을 처리 하도록 합니다.
if 문 다음에 수행되는 부분은 begin end로 묶지 않을때는 첫번째 행만 if문에 포함 됩니다.
if
begin
...
end
else
begin
...
end
이런식으로 begin end 로 묶어 주시는걸 추천합니다..
lock은
BEGIN TRAN 으로 트랜젝션 시작 위치를 지정 하시면 될듯 합니다.
COMMIT TRAN
@@ERROR ,TRY CATCH 문 혼용해서 쓰시면 원하시는 내용을 구현 하실수 있구요...