for update 관련 동시성 제어 부문 이슈 문의 0 12 2,876

by 달라쏭 [SQL Query] [2020.01.29 16:37:01]


TEMP_TABLE 의 값이 여러개라 가정했을때

--SESSION.1 에서 실행
SELECT TEMP1 FROM TEMP_TABLE WHERE ROWNUM=1 FOR UPDATE SKIP LOCKED;
(커밋  혹은 롤백 하지 않음)
--SESSION.2 에서 실행
SELECT TEMP1 FROM TEMP_TABLE WHERE ROWNUM=1 FOR UPDATE SKIP LOCKED;

게 실행하면

SESSION.1 에서는 값을 하나 출력하나

SESSION.2 에서는 어떤 값도 출력하지 않게 됩니다.

스킵락은 세션.1 에서 커밋 혹은 롤백 하지 않았을때 2에서는 1에서 락걸린 값을 제외한 나머지 값을 찾는걸로 

알고 있는데 무슨 문제가 있는걸까요? 참고로 오라클은 11.0 이상 버전입니다.

아니면 위와 같은 상황에서 session1 에서 락이걸린것 제외하고 다른 값을 가져오는 방법은 무엇이 있을까요?

문의 드립니다. 좋은 하루 되세요~

 

 

by jkson [2020.01.29 18:08:35]

skip locked는 where절 처리 이후 실행됩니다. rownum으로 가져온 데이터가 lock 상태가 아니어야 가져와서 lock하는데 rownum=1 인 데이터가 동일하므로 결과값으로 가져오지 못 합니다. 해결방법은.. 내일 출근해서 테스트해봐야겠네요ㅎㅎ


by jkson [2020.01.29 18:12:28]

대략적으로는 rownum을 병렬 실행가능한 max값으로 한 번, 다시 rownum=1 하는 방법인데 될지 모르겠네요.


by jkson [2020.01.30 08:26:50]

큭.. one 쿼리로는 안 되네요ㅠ

아무래도 파이프라인 테이블 함수 같은 거 사용해서

lock 되지 않은 건에서 한 건 리턴해주게 해야할 것 같습니다.

pipelined table function 조회하시면 자료가 있으니 활용해보시는 것도 좋을 것 같구요.

구현해야 하는 프로그램이 어떤 것인지 보충설명해주시면 더욱 도움이 되지 않을까 싶네요.

 


by 달라쏭 [2020.01.30 09:22:24]

아 넴 감사합니다.

간략하게 구현해야 하는걸 말씀드리자면요 ^^

temp_table 이라는 테이블에 temp_no, status 가 있을시에

temp_no   status

------------------------

1              00

2              00

3              00

4              99

.

.

이런식으로 데이타가 만건이 있다고 쳤을때

select temp_no from temp_table

where status='00'

and rownum=1 으로 상태가 00인 로우중 하나의 값을 잡아서 99로 업데이트 치는 작업을 해야합니다,

그런데 여기저기서 위와 같은 작업을 해야해서. 동시성에 문제가 있을까 하여

select temp_no from temp_table

where status='00'

and rownum=1

for update;

를 하였눈데 한쪽세션에서 커밋을 안하면 다른쪽 세션에서 동일한 쿼리를 실행했을떼 락이 걸리더라구요

그래서 wait 3 등으로 락을 벗어나는 일을 해결했는데 궁극적으로는

하나의 세션에서 하나를 잡고 있다면 커밋 혹은 롤백 전에 다른 세션에서 다른걸 잡아서 처리할수 있도록

하려는 거여요 ㅎㅎ   구래서 skip locked 라는 옵션을 사용해보았으나 되질않아서요 ㅡㅡ

관심가져주셔서 감사합니다. 좋은 하루 되셔요

 


by jkson [2020.01.30 10:11:42]

왜 한 세션에서 한번에 update하지 않고 rownum으로 1건씩 update 하는 건가요?


by 달라쏭 [2020.01.30 10:24:28]

네.. 어떤 하나의 트랜잭션이 발생할때 마다 임의 테이블에 한건씩 업데이트 처리 해야 하는 이슈가 있어서 그렇습니다. ㅜ그런데 이 작업을 하나의 프로그램에서 처리되는게 아니라 여러군데에 프로그램에서 처리가 되야 하기때문입니다.


by 달라쏭 [2020.01.30 15:15:05]

skip locked 이  안먹는것 같습니다. ㅜㅜ

답변 감사합니다.


by 달라쏭 [2020.01.30 15:31:50]

아 되네요.. ㅜㅜ

문제는 저 남은것중 한개를 어떻게 가져와야 할지가 원쿼리로 안되네요

select * from

(

select * from temp_t

for update skip locked

)

where rownum=1

도 안되는거 같구  ㅎㅎ 암튼 감사합니다


by jkson [2020.01.30 16:44:29]
--테스트

--테이블 생성
CREATE TABLE TMP_20200130
(
  COL1 VARCHAR2(10)
, COL2 VARCHAR2(10)
, COL3 VARCHAR2(10)
);

--데이터 입력
INSERT INTO TMP_20200130
VALUES('A','B','C');

INSERT INTO TMP_20200130
VALUES('D','E','F');

--세션1에서 수행
DECLARE
CURSOR CUR IS 
SELECT ROWID RID, A.* 
FROM TMP_20200130 A 
FOR UPDATE SKIP LOCKED;
TYPE TMPTBL IS TABLE OF CUR%ROWTYPE;
TMPCUR TMPTBL;
BEGIN
    OPEN CUR;
    FETCH CUR BULK COLLECT INTO TMPCUR LIMIT 1;
    CLOSE CUR;
	UPDATE TMP_20200130 SET COL1 = '99'
    WHERE ROWID = TMPCUR(1).RID;
    --COMMIT; --COMMIT하지 않아본다.
END;

SELECT * FROM TMP_20200130

--결과
99	E	F
A	B	C


--세션2에서 수행
DECLARE
CURSOR CUR IS 
SELECT ROWID RID, A.* 
FROM TMP_20200130 A 
FOR UPDATE SKIP LOCKED;
TYPE TMPTBL IS TABLE OF CUR%ROWTYPE;
TMPCUR TMPTBL;
BEGIN
    OPEN CUR;
    FETCH CUR BULK COLLECT INTO TMPCUR LIMIT 1;
    CLOSE CUR;
	UPDATE TMP_20200130 SET COL1 = '99'
    WHERE ROWID = TMPCUR(1).RID;
    --COMMIT; --COMMIT하지 않아본다.
END;

SELECT * FROM TMP_20200130

--결과
D	E	F
99	B	C

-----------------------------------------------

잘 작동합니다.

처음에 제가 설명드린 PIPELINED 함수는

SELECT 만 처리하시려는 줄 알고 말씀드린 건데

목적이 UPDATE라면

위 내용을 프로시저로 만드시고 수행하셔야겠습니다.

어제 퇴근하면서 저도 원쿼리로 되지 않을까 했는데..

어차피 FOR UPDATE문 자체가 쿼리 결과에 대한 LOCK이기 때문에 안 될 것 같더라구요ㅎㅎ

 


by 달라쏭 [2020.01.30 17:48:09]

앗 감사합니다.

한번 해볼께요~ 좋은 하루되시고 즐거운 퇴근길 되시길 바랍니다~ ^^


by 우리집아찌 [2020.01.30 10:14:38]

간단히 99로 업데이트 하시려면 그냥 커서 돌려서 건건히 하시거나 Bulk sql 쓰시면 될거같아여..

그래도 불안하면 db사용빈도가 적은 야간에 하시면 됩니다 

for update 쓸경우는 동시에 업데이트 문이 마구 실행되는 경우나 lob 컬럼에 대량으로 데이터 넣는경우가 대부분 입니다


by 달라쏭 [2020.01.30 10:27:49]

24시간 여기저기서 하나의 트랜잭션이 발생할때마다 한테이블에 업데이트를 해야 하는 상황이라..

감사합니다..

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입