세션 간 통신 DBMSALERT
41249 단어 alert
Inter-Session Communication DBMS_ALERT 세션 간 통신 DBMSALERT
Inter-Session Communication DBMS_ALERT
세션 간 통신 DBMSALERT
질문: 어떻게 프로그램이 데이터가 바뀌었다는 것을 알 수 있습니까?
대답:
1. 조회표, 조회count(*) 등 기록이 증가했는지 확인
2. 감사표(audit table)를 조회하고count(*) 또는 타임 스탬프 필드를 조회하여 표가 업데이트되었는지 확인한다.
3.DBMS_ALERT
4.DBMS_AQ ...
dbms_alert는 Oracle 버전 7에 처음 등장하여 데이터베이스 세션 간의 통신을 실현하는 방식이다.
다중 사용자에게 데이터베이스 이벤트(database 이벤트) 즉 경보(alerts)를 방송하는 메커니즘을 제공하였다.
의존dbmspipe 및dbmslock은 실현했다
사용 가능, 때로는 데이터베이스 테이블의 변화를 감시하려면 응용 프로그램이 반복적으로 조회해야 하기 때문에 비용이 많이 든다
dbms_alert의 메커니즘은 변화가 발생할 때 데이터베이스가 주동적으로 응용 프로그램에 통지할 수 있도록 한다
참조 문서:
13 DBMS_ALERT :
Alerts are transaction-based. This means that the waiting session is not alerted until the transaction signalling the alert commits. There can be any number of concurrent signalers of a given alert, and there can be any number of concurrent waiters on a given alert.
경보는 사무에 기초한 것이다.이것은 경보를 발동한 세션이 업무를 제출할 때까지 경보를 기다리는 세션이 경보를 받았다는 것을 의미한다.지정된 경보는 임의의 발기자가 동시에 있을 수도 있고, 임의의 수용자가 있을 수도 있다.
A waiting application is blocked in the database and cannot do any other work.
대기 중인 프로그램이 막혀서 다른 동작을 할 수 없습니다
An application can register for multiple events and can then wait for any of them to occur using the WAITANY procedure.
하나의 응용 프로그램은 여러 개의 이벤트를 등록한 다음에 WAITANY 저장 프로세스를 사용하여 그들 중 임의의 이벤트를 기다릴 수 있다(여러 개의 관계, 한 개의 경보는 여러 개의 수신자, 한 개의 수신자는 여러 개의 경보를 받을 수 있다)
An application can also supply an optional timeout parameter to the WAITONE or WAITANY procedures. A timeout of 0 returns immediately if there is no pending alert.
하나의 응용 프로그램도 WAITONE이나 WAITANY 저장 프로세스에 시간 초과 파라미터를 지정할 수 있습니다.만약 결정되지 않은 경보가 없다면, 시간 초과 파라미터가 0이면 즉시 되돌아옵니다.
The signalling session can optionally pass a message that is received by the waiting session.
신호를 보내는 세션은 대기 세션에 메시지를 전달할 수 있다.
Alerts can be signalled more often than the corresponding application wait calls. In such cases, the older alerts are discarded. The application always gets the latest alert (based on transaction commit times).
프로그램의 대기 호출보다 경보가 더 많이 울릴 수 있습니다.이런 상황에서 낡은 경보는 버려졌다.응용 프로그램은 항상 최신 경보를 받는다.
If the application does not require transaction-based alerts, the DBMS_PIPE package may provide a useful alternative.
애플리케이션에 트랜잭션 기반 경고가 필요하지 않으면 DBMSPIPE 패키지를 선택할 수 있습니다.
If the transaction is rolled back after the call to SIGNAL, no alert occurs.
SIGNAL 호출 후 트랜잭션이 롤백된 경우 경고가 발생하지 않습니다.
It is possible to receive an alert, read the data, and find that no data has changed. This is because the data changed after the prior alert, but before the data was read for that prior alert.
경보를 받고 데이터를 읽으면 데이터가 바뀌지 않을 수도 있다.이것은 데이터가 경보를 발령한 후 데이터를 읽기 전에 바뀌었기 때문이다(이게 어떻게 가능하지? 경보를 먼저 발령한 다음에 데이터를 바꿉니까? 경보를 발령하는 것이 사무 제출보다 빠를 수 있습니까?)
Usually, Oracle is event-driven; this means that there are no polling loops. There are two cases where polling loops can occur:
일반적으로 Oracle은 이벤트로 구동됩니다.이것은 윤문 순환이 없다는 것을 의미한다.교대 훈련 순환에는 두 가지 상황이 있다.
Shared mode. If your database is running in shared mode, a polling loop is required to check for alerts from another instance. The polling loop defaults to one second and can be set by the SET_DEFAULTS procedure.
공유 모드.만약 데이터베이스가 공유 모드에서 실행된다면, 다른 실례의 경보를 순환해서 검사해야 한다.기본 폴링 주기는 SETDEFAULT 스토리지 프로시저 설정
WAITANY procedure. If you use the WAITANY procedure, and if a signalling session does a signal but does not commit within one second of the signal, a polling loop is required so that this uncommitted alert does not camouflage other alerts. The polling loop begins at a one second interval and exponentially backs off to 30-second intervals.
WAITANY 프로세스.WAITANY 프로세스를 사용하고 경보가 발령되어 1초 안에 업무를 제출하지 않으면, 이 미제출 경보가 다른 경보를 막지 못하도록 윤문이 필요합니다.폴링 간격은 1초로 시작한 후 지수로 30초까지 증가한다.
테스트:
1. 권한 부여
conn / as sysdba
grant execute on dbms_alert to a;
2. 경고를 받는 세션을 새로 엽니다.
conn a/a
set pages 50000 line 130
set serveroutput on size unlimited
경고 등록
exec dbms_alert.register('alert_test');
3. 경고 정보 보기 SYS 사용자 실행
SQL> select * from dbms_alert_info;
NAME SID C
------------------------------ ------------------------------ -
MESSAGE
--------------------------------------------------------------------------------
ALERT_TEST 065C00C00001 N
SQL> desc dbms_alert_info
Name Null? Type
----------------------------------------- -------- ----------------------------
NAME NOT NULL VARCHAR2(30)
SID NOT NULL VARCHAR2(30)
CHANGED VARCHAR2(1)
MESSAGE VARCHAR2(1800)
SQL>
DBMS_ALERT_INFO NAME: 경고 이름.같은 이름의 세션이 있을 수 있습니다. 즉, 여러 세션이 같은 경보 SID를 등록하고 받을 수 있습니다. DBMS 와 같습니다.SESSION.UNIQUE_SESSION_ID. 세 부분으로 나뉘는데 SID+SERIAL#+InstanceNumber How Is SID In DBMS_ALERT_INFO Related To SID In V$Session
select name
, to_number(substr(sid,1,4),'xxxx') sid
, to_number(substr(sid,5,4),'xxxx') serial#
, to_number(substr(sid,9,4),'xxxx') instance#
from dbms_alert_info;
NAME SID SERIAL# INSTANCE#
------------------------------ ---------- ---------- ----------
ALERT_TEST 1628 192 1
CHANGED: N: 경보가 발령되지 않았거나 경보가 수신되었습니다. Y: 경보가 발령되었습니다. 아직 수신되지 않았습니다. MESSAGE: 경보 메시지4.등록 후 경고 수신 대기
var v_messge varchar2(1000)
var v_status number
exec dbms_alert.waitone('alert_test', :v_messge, :v_status);
print :v_status :v_messge
调用waitone后没有返回, 一直在等待警报
5.
打开另一个会话, 发出警报
conn a/a set pages 50000 line 130 set serveroutput on size unlimited exec dbms_alert.signal('alert_test', 'hello world!'); commit;
事务提交后才发出警报
6.
等待接收警报的会话接收到了警报, 打印出信息
SQL> print :v_status :v_messge V_STATUS ---------- 0 V_MESSGE ---------------------------------------------------------------------------------------------------------------------------------- hello world!
조회dbmsalert_info 정보SQL> select * from dbms_alert_info; NAME SID C ------------------------------ ------------------------------ - MESSAGE -------------------------------------------------------------------------------- ALERT_TEST 065C00C00001 N hello world!
여기는 CHANGED인지 N인지 경보가 울리자마자 바로 들어왔기 때문에 경보가 안 울리면 CHANGED는 Y7.질문: 등록 시 끊기 등록 경보가dbms 호출되면alert.register 때 멈췄습니다. 설명을 되돌려 주지 않기 전에 다른 세션에서 경고를 보냈지만 업무를 제출하지 않았습니다. 그러면 등록 과정이 막혔습니다. 이 업무를 제출하거나 굴러가면 해결할 수 있습니다. 참고: PL/SQL Session Hangs When Executing "Dbms_alert.Register(''Varchar'')" Statement DBMS_ALERT.WAITONE HANGS WAITING ON DBMS_ALERT.SIGNAL 108.여러 번 경고exec dbms_alert.signal('alert_test', 'hello one') commit; exec dbms_alert.signal('alert_test', 'hello two') commit;
SQL> select * from dbms_alert_info; NAME SID C ------------------------------ ------------------------------ - MESSAGE -------------------------------------------------------------------------------- ALERT_TEST 065C00C00001 Y hello two
최신 경보만 발효되어 경보를 받는 것을 보았다SQL> exec dbms_alert.waitone('alert_test', :v_messge, :v_status); PL/SQL procedure successfully completed. SQL> print :v_status :v_messge V_STATUS ---------- 0 V_MESSGE ---------------------------------------------------------------------------------------------------------------------------------- hello two
또한 최신 수신 후 CHANGED 필드는 N9.삭제 경고 삭제 전에 정의된 경고는 등록된 세션에서 실행되어야 합니다.exec dbms_alert.remove('alert_test')
경보를 등록한 세션이 종료되면 다른 세션으로 삭제할 수 없습니다.dbmsalert_info표에 이 기록이 존재합니다SQL> select * from dbms_alert_info; NAME SID C ------------------------------ ------------------------------ - MESSAGE -------------------------------------------------------------------------------- ALERT_TEST 065C00C00001 N hello 2
참고 문서 How To Remove Alerts From DBMS_ALERTS_INFO Table에서 다시 등록하면 삭제할 수 있다고 했습니다. 이전 세션이 종료되었기 때문입니다. 이 때 다시 등록oracle는 자동으로 원래 기록을 삭제하고 새로운 (또는 덮어쓰기)를 추가하기 때문에 10.파이프를 삭제하면 경보가 삭제되지만dbmsalert에서 만든 파이프는 아직 시스템에 있으며 삭제되지 않았습니다.v$db 보기pipes:SQL> col name for a40 SQL> select * from v$db_pipes; OWNERID NAME TYPE PIPE_SIZE ---------- ---------------------------------------- ------- ---------- ORA$ALERT$065C00C20001 PUBLIC 1687 ORA$ALERT$066300730001 PUBLIC 1687 ORA$ALERT$066C021C0001 PUBLIC 1687 ORA$ALERT$066C021A0001 PUBLIC 1687 ORA$ALERT$065D00670001 PUBLIC 1687 SQL>
DBMSALERT는 DBMS 를 사용하는 암시적 파이핑을 사용합니다.PIPE.PURGE가 비워지고 직접 삭제되지 않으며 시스템이 사용 가능한dbms 를 자동으로 삭제할 때까지 기다립니다.pipe.remove_pipe 지우기declare v int; begin for x in (select name from v$db_pipes where name like 'ORA$ALERT$%') loop v := dbms_pipe.remove_pipe(x.name); end loop; end; /
11. 소스 코드package dbms_alert is ------------ -- OVERVIEW -- -- This package provides support for the asynchronous (as opposed to -- polling) notification of database events. By appropriate use of -- this package and database triggers, an application can cause itself -- to be notified whenever values of interest in the database are -- changed. -- -- For example, suppose a graphics tool is displaying a graph of some -- data from a database table. The graphics tool can, after reading and -- graphing the data, wait on a database alert ('dbms_alert.waitone') -- covering the data just read. The tool will automatically wake up when -- the data is changed by any other user. All that is required is that a -- trigger be placed on the database table which then performs a signal -- ('dbms_alert.signal') whenever the trigger is fired. -- -- Alerts are transaction based. This means that the waiting session -- does not get alerted until the transaction signalling the alert commits. -- -- There can be any number of concurrent signallers of a given alert, and -- there can be any number of concurrent waiters on a given alert. -- -- A waiting application will be blocked in the database and cannot do -- any other work. -- -- Most of the calls in the package, except for 'signal', do commits. -- ----------- -- EXAMPLE -- -- Suppose the application wishes to graph average salaries, say by -- department, for all employees. So the application needs to know -- whenever 'emp' is changed. The application would look like this: -- -- dbms_alert.register('emp_table_alert'); -- readagain: -- -- dbms_alert.waitone('emp_table_alert', :message, :status); -- if status = 0 then goto readagain; else -- -- The 'emp' table would have a trigger similar to the following: -- -- create trigger emptrig after insert or update or delete on emp -- begin -- dbms_alert.signal('emp_table_alert', 'message_text'); -- end; -- -- When the application is no longer interested in the alert, it does -- dbms_alert.remove('emp_table_alert'); -- This is important since it reduces the amount of work required by -- the alert signaller. -- -- If a session exits (or dies) while there exist registered alerts, -- they will eventually be cleaned up by future users of this package. -- -- The above example guarantees that the application will always see -- the latest data, although it may not see every intermediate value. -------------- -- VARIATIONS -- -- The application can register for multiple events and can then wait for -- any of them to occur using the 'waitany' call. -- -- An application can also supply an optional 'timeout' parameter to the -- 'waitone' or 'waitany' calls. A 'timeout' of 0 returns immediately -- if there is no pending alert. -- -- The signalling session can optionally pass a message which will be -- received by the waiting session. -- -- Alerts may be signalled more often than the corresponding application -- 'wait' calls. In such cases the older alerts are discaded. The -- application always gets the latest alert (based on transaction commit -- times). -- -- If the application does not require transaction based alerts, then the -- 'dbms_pipe' package may provide a useful alternative -- -- If the transaction is rolled back after the call to 'dbms_alert.signal', -- no alert will occur. -- -- It is possible to receive an alert, read the data, and find that no -- data has changed. This is because the data changed after the *prior* -- alert, but before the data was read for that *prior* alert. -------------------------- -- IMPLEMENTATION DETAILS -- -- In most cases the implementation is event-driven, i.e., there are no -- polling loops. There are two cases where polling loops can occur: -- -- 1) Parallel mode. If your database is running parallel mode then -- a polling loop is required to check for alerts from another -- instance. The polling loop defaults to one second and is settable -- by the 'set_defaults' call. -- 2) Waitany call. If you use the 'waitany' call, and a signalling -- session does a signal but does not commit within one second of the -- signal, then a polling loop is required so that this uncommitted -- alert does not camouflage other alerts. The polling loop begins -- at a one second interval and exponentially backs off to 30 second -- intervals. -- -- This package uses the dbms_lock package (for synchronization between -- signallers and waiters) and the dbms_pipe package (for asynchronous -- event dispatching). ------------------------------------------------------- -- INTERACTION WITH MULTI-THREADED AND PARALLEL SERVER -- -- When running with the parallel server AND multi-threaded server, a -- multi-threaded (dispatcher) "shared server" will be bound to a -- session (and therefore not shareable) during the time a session has -- any alerts "registered", OR from the time a session "signals" an -- alert until the time the session commits. Therefore, applications -- which register for alerts should use "dedicated servers" rather than -- connecting through the dispatcher (to a "shared server") since -- registration typically lasts for a long time, and applications which -- cause "signals" should have relatively short transactions so as not -- to tie up "shared servers" for too long. ------------ -- SECURITY -- -- Security on this package may be controlled by granting execute on -- this package to just those users or roles that you trust. You may -- wish to write a cover package on top of this one which restricts -- the alertnames used. Execute privilege on this cover package can -- then be granted rather than on this package. ------------- -- RESOURCES -- -- This package uses one database pipe and two locks for each alert a -- session has registered. --------------------- -- SPECIAL CONSTANTS -- maxwait constant integer := 86400000; -- 1000 days -- The maximum time to wait for an alert (essentially forever). ---------------------------- -- PROCEDURES AND FUNCTIONS -- procedure set_defaults(sensitivity in number); -- Set various defaults for this package. -- Input parameters: -- sensitivity -- In case a polling loop is required (see "Implementation Details" -- above), this is the time to sleep between polls. Deafult is 5 sec. -- procedure register(name in varchar2); -- Register interest in an alert. A session may register interest in -- an unlimited number of alerts. Alerts should be de-registered when -- the session no longer has any interest (see 'remove'). This call -- always performs a 'commit'. -- Input parameters: -- name -- The name of the alert in which this session is interested. -- WARNING: Alert names beginning with 'ORA$' are reserved for use for -- products provided by Oracle Corporation. Name must be 30 bytes -- or less. The name is case-insensitive. -- procedure remove(name in varchar2); -- Remove alert from registration list. Do this when the session is no -- longer interested in an alert. Removing an alert is important -- since it will reduce the amount of work done by signalers of the alert. -- If a session dies without removing the alert, that alert will -- eventually (but not immediately) be cleaned up. This call always -- performs a commit. -- Input parameters: -- name -- The name of the alert to be removed from registration list. The -- name is case-insensitive. -- procedure removeall; -- Remove all alerts for this session from registration list. Do this -- when the session is no longer interested in any alerts. Removing -- alerts is important since it will reduce the amount of work done -- by signalers of the alert. If a session dies without removing all -- of its alerts, the alerts will eventually (but not immediately) -- be cleaned up. This call always performs a commit. -- -- This procedure is called automatically upon first reference to this -- package during a session. Therefore no alerts from prior sessions -- which may have terminated abnormally can affect this session. procedure waitany(name out varchar2, message out varchar2, status out integer, timeout in number default maxwait); -- Wait for an alert to occur for any of the alerts for which this -- session is registered. Although probably unusual, the same session -- that waits for the alert may also first signal the alert. In this -- case remember to commit after the signal and prior to the wait. -- Otherwise a lock request exception (status 4) will occur. This -- call always performs a commit. -- Input parameters: -- timeout -- The maximum time to wait for an alert. If no alert occurs before -- timeout seconds, then this call will return with status of 1. -- Output parameters: -- name -- The name of the alert that occurred, in uppercase. -- message -- The message associated with the alert. This is the message -- provided by the 'signal' call. Note that if multiple signals -- on this alert occurred before the waitany call, then the message -- will correspond to the most recent signal call. Messages from -- prior signal calls will be discarded. -- status -- 0 - alert occurred -- 1 - timeout occurred -- Errors raised: -- -20000, ORU-10024: there are no alerts registered. -- Cause: You must register an alert before waiting. -- procedure waitone(name in varchar2, message out varchar2, status out integer, timeout in number default maxwait); -- Wait for specified alert to occur. If the alert was signalled since -- the register or last waitone/waitany, then this call will return -- immediately. The same session that waits for the alert may also -- first signal the alert. In this case remember to commit after the -- signal and prior to the wait. Otherwise a lock request exception -- (status 4) will occur. This call always performs a commit. -- Input parameters: -- name -- The name of the alert to wait for. The name is case-insensitive. -- timeout -- The maximum time to wait for this alert. If no alert occurs before -- timeout seconds, then this call will return with status of 1. -- If the named alert has not been registered then the this call -- will return after the timeout period expires. -- Output parameters: -- message -- The message associated with the alert. This is the message -- provided by the 'signal' call. Note that if multiple signals -- on this alert occurred before the waitone call, then the message -- will correspond to the most recent signal call. Messages from -- prior signal calls will be discarded. The message may be up to -- 1800 bytes. -- status -- 0 - alert occurred -- 1 - timeout occurred -- procedure signal(name in varchar2, message in varchar2); -- Signal an alert. -- Input parameters: -- name -- Name of the alert to signal. The effect of the signal call only -- occurs when the transaction in which it is made commits. If the -- transaction rolls back, then the effect of the signal call is as -- if it had never occurred. All sessions that have registered -- interest in this alert will be notified. If the interested sessions -- are currently waiting, they will be awakened. If the interested -- sessions are not currently waiting, then they will be notified the -- next time they do a wait call. Multiple sessions may concurrently -- perform signals on the same alert. However the first session -- will block concurrent sessions until the first session commits. -- Name must be 30 bytes or less. It is case-insensitive. This call -- does not perform a commit. -- message -- Message to associate with this alert. This will be passed to -- the waiting session. The waiting session may be able to avoid -- reading the database after the alert occurs by using the -- information in this message. The message must be 1800 bytes or less. end;
PACKAGE BODY dbms_alert IS P_INT NUMBER := 5; THIS_SESSION_ID VARCHAR2(30) := DBMS_SESSION.UNIQUE_SESSION_ID; PARALLEL BOOLEAN := DBMS_UTILITY.IS_CLUSTER_DATABASE; SIGPIPE VARCHAR2(30) := 'ORA$ALERT$' || THIS_SESSION_ID; MSGSEQ BINARY_INTEGER := 0; FIRSTREGISTER BOOLEAN := TRUE; INSTANTIATING_PKG BOOLEAN := TRUE; FUNCTION MINIMUM(V1 NUMBER, V2 NUMBER) RETURN NUMBER IS BEGIN IF V1 < V2 THEN RETURN V1; ELSE RETURN V2; END IF; END; PROCEDURE SET_DEFAULTS(SENSITIVITY IN NUMBER) IS BEGIN IF SENSITIVITY >= 0 THEN P_INT := SENSITIVITY; END IF; END; PROCEDURE REGISTER(NAME IN VARCHAR2) IS STATUS INTEGER; LSTATUS INTEGER; LOCKID INTEGER; CURSOR C1 IS SELECT DISTINCT SUBSTR(KGLNAOBJ,11) SID FROM X$KGLOB WHERE KGLHDNSP = 7 AND KGLNAOBJ LIKE 'ORA$ALERT$%' AND BITAND(KGLHDFLG,128)!=0 UNION SELECT DISTINCT SID FROM DBMS_ALERT_INFO; BEGIN IF INSTANTIATING_PKG THEN REMOVEALL; INSTANTIATING_PKG := FALSE; END IF; IF (FIRSTREGISTER) THEN IF DBMS_UTILITY.IS_CLUSTER_DATABASE THEN FOR REC IN C1 LOOP LOCKID := DBMS_UTILITY.GET_HASH_VALUE(REC.SID, 2000002048, 2048); LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.X_MODE, TIMEOUT => 0, RELEASE_ON_COMMIT => TRUE); IF LSTATUS = 0 THEN DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID); DELETE DBMS_ALERT_INFO WHERE SID = REC.SID; COMMIT; ELSIF LSTATUS NOT IN (1,2,4) THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10025: lock request error, status: ' || TO_CHAR(LSTATUS)); END IF; END LOOP; LSTATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(THIS_SESSION_ID, 2000002048, 2048), DBMS_LOCK.S_MODE, TIMEOUT => 60); IF LSTATUS != 0 AND LSTATUS != 4 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10021: lock request error, status: ' || TO_CHAR(LSTATUS)); END IF; ELSE FOR REC IN C1 LOOP IF NOT DBMS_SESSION.IS_SESSION_ALIVE(REC.SID) THEN DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID); DELETE DBMS_ALERT_INFO WHERE SID = REC.SID; COMMIT; END IF; END LOOP; END IF; FIRSTREGISTER := FALSE; END IF; STATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(UPPER(NAME), 2000000000, 2048), DBMS_LOCK.X_MODE, DBMS_LOCK.MAXWAIT, RELEASE_ON_COMMIT => TRUE); IF STATUS != 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10002: lock request error, status: ' || TO_CHAR(STATUS)); END IF; INSERT INTO DBMS_ALERT_INFO VALUES (UPPER(REGISTER.NAME), THIS_SESSION_ID, 'N', NULL); COMMIT; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN COMMIT; END; PROCEDURE REMOVE(NAME IN VARCHAR2) IS BEGIN IF INSTANTIATING_PKG THEN REMOVEALL; INSTANTIATING_PKG := FALSE; END IF; DELETE FROM DBMS_ALERT_INFO WHERE NAME = UPPER(REMOVE.NAME) AND SID = THIS_SESSION_ID; COMMIT; END; PROCEDURE PIPE_WAIT(MAXTIME NUMBER, CUMTIME IN OUT NUMBER) IS STATUS INTEGER; TMO NUMBER := MAXTIME; BEGIN IF PARALLEL THEN TMO := MINIMUM(TMO, P_INT); END IF; IF TMO = MAXWAIT THEN TMO := DBMS_PIPE.MAXWAIT; END IF; STATUS := DBMS_PIPE.RECEIVE_MESSAGE(SIGPIPE, TMO); IF STATUS = 1 THEN CUMTIME := CUMTIME + TMO; RETURN; END IF; IF STATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10015: error:' || TO_CHAR(STATUS) || ' waiting for pipe message.'); END IF; RETURN; END; PROCEDURE OPTIMISTIC( NAME OUT VARCHAR2, MESSAGE OUT VARCHAR2, STATUS OUT INTEGER) IS LOCKID INTEGER; LSTATUS INTEGER; CURSOR C1 IS SELECT NAME FROM DBMS_ALERT_INFO WHERE SID = THIS_SESSION_ID AND CHANGED = 'Y'; BEGIN STATUS := 1; FOR REC IN C1 LOOP LOCKID := DBMS_UTILITY.GET_HASH_VALUE(REC.NAME, 2000000000, 2048); LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.SX_MODE, TIMEOUT => 0, RELEASE_ON_COMMIT => TRUE); IF LSTATUS <> 1 THEN IF LSTATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10019: error ' || TO_CHAR(LSTATUS) || ' on lock request.'); END IF; UPDATE DBMS_ALERT_INFO SET CHANGED = 'N' WHERE SID = THIS_SESSION_ID AND NAME = REC.NAME; SELECT MESSAGE INTO MESSAGE FROM DBMS_ALERT_INFO WHERE SID = THIS_SESSION_ID AND NAME = REC.NAME; COMMIT; DBMS_PIPE.PURGE(SIGPIPE); NAME := REC.NAME; STATUS := 0; RETURN; END IF; END LOOP; RETURN; END; PROCEDURE WAITANY( NAME OUT VARCHAR2, MESSAGE OUT VARCHAR2, STATUS OUT INTEGER, TIMEOUT IN NUMBER DEFAULT MAXWAIT) IS WAITIME NUMBER := 0; CUMTIME NUMBER := 0; LOCKID INTEGER; ST INTEGER; LSTATUS INTEGER; TIMEDOUT BOOLEAN; CHANGED VARCHAR2(1); FOUNDONE BOOLEAN; CURSOR C1 IS SELECT NAME FROM DBMS_ALERT_INFO WHERE SID = THIS_SESSION_ID; BEGIN IF INSTANTIATING_PKG THEN REMOVEALL; INSTANTIATING_PKG := FALSE; END IF; OPTIMISTIC(NAME, MESSAGE, ST); IF ST = 0 THEN STATUS := ST; RETURN; END IF; WAITIME := 1; CUMTIME := 0; LOOP TIMEDOUT := FALSE; FOUNDONE := FALSE; FOR REC IN C1 LOOP FOUNDONE := TRUE; LOCKID := DBMS_UTILITY.GET_HASH_VALUE(REC.NAME, 2000000000, 2048); LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.SX_MODE, WAITIME, RELEASE_ON_COMMIT => TRUE); IF LSTATUS = 1 THEN OPTIMISTIC(NAME, MESSAGE, ST); IF ST = 0 THEN STATUS := 0; RETURN; END IF; CUMTIME := CUMTIME + WAITIME; IF CUMTIME >= TIMEOUT THEN STATUS := 1; RETURN; END IF; TIMEDOUT := TRUE; GOTO CONTINUE; ELSIF LSTATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10020: error ' || TO_CHAR(LSTATUS) || ' on lock request.'); ELSE SELECT CHANGED, MESSAGE INTO CHANGED, MESSAGE FROM DBMS_ALERT_INFO WHERE SID = THIS_SESSION_ID AND NAME = REC.NAME; IF CHANGED = 'Y' THEN UPDATE DBMS_ALERT_INFO SET CHANGED = 'N' WHERE SID = THIS_SESSION_ID AND NAME = REC.NAME; COMMIT; NAME := REC.NAME; STATUS := 0; DBMS_PIPE.PURGE(SIGPIPE); RETURN; END IF; LSTATUS := DBMS_LOCK.RELEASE(LOCKID); END IF; <<continue>> NULL; END LOOP; IF NOT FOUNDONE THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10024: there are no alerts registered.'); END IF; IF TIMEDOUT THEN WAITIME := MINIMUM(WAITIME*2, 32); WAITIME := MINIMUM(WAITIME, TIMEOUT-CUMTIME); ELSE PIPE_WAIT(TIMEOUT-CUMTIME, CUMTIME); END IF; IF CUMTIME >= TIMEOUT THEN STATUS := 1; RETURN; END IF; END LOOP; END; PROCEDURE WAITONE( NAME IN VARCHAR2, MESSAGE OUT VARCHAR2, STATUS OUT INTEGER, TIMEOUT IN NUMBER DEFAULT MAXWAIT) IS CUMTIME NUMBER := 0; LOCKID INTEGER := DBMS_UTILITY.GET_HASH_VALUE(UPPER(NAME), 2000000000, 2048); LSTATUS INTEGER; BEGIN IF INSTANTIATING_PKG THEN REMOVEALL; INSTANTIATING_PKG := FALSE; END IF; LOOP LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.SX_MODE, TIMEOUT-CUMTIME, RELEASE_ON_COMMIT => TRUE); IF LSTATUS = 1 THEN STATUS := 1; RETURN; END IF; IF LSTATUS = 4 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10037: attempting to wait on uncommitted signal from same session'); END IF; IF LSTATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10023: error ' || TO_CHAR(LSTATUS) || ' on lock request.'); END IF; UPDATE DBMS_ALERT_INFO SET CHANGED = 'N' WHERE NAME = UPPER(WAITONE.NAME) AND SID = THIS_SESSION_ID AND CHANGED = 'Y'; IF SQL%ROWCOUNT != 0 THEN SELECT MESSAGE INTO MESSAGE FROM DBMS_ALERT_INFO WHERE NAME = UPPER(WAITONE.NAME) AND SID = THIS_SESSION_ID; COMMIT; DBMS_PIPE.PURGE(SIGPIPE); STATUS := 0; RETURN; END IF; LSTATUS := DBMS_LOCK.RELEASE(LOCKID); PIPE_WAIT(TIMEOUT, CUMTIME); IF CUMTIME >= TIMEOUT THEN STATUS := 1; RETURN; END IF; END LOOP; END; PROCEDURE SIGNAL_PIPE(PIPENAME VARCHAR2) IS MSGID VARCHAR2(40); TMPMSGID VARCHAR2(40); STATUS INTEGER; BEGIN MSGID := THIS_SESSION_ID || ':' || TO_CHAR(MSGSEQ); MSGSEQ := MSGSEQ + 1; DBMS_PIPE.PACK_MESSAGE(MSGID); STATUS := DBMS_PIPE.SEND_MESSAGE(PIPENAME); IF STATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10016: error:' || TO_CHAR(STATUS) || ' sending on pipe ' || PIPENAME); END IF; STATUS := DBMS_PIPE.RECEIVE_MESSAGE(PIPENAME, 0); IF STATUS = 1 THEN RETURN; END IF; IF STATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10017: error:' || TO_CHAR(STATUS) || ' receiving on pipe ' || PIPENAME); END IF; DBMS_PIPE.UNPACK_MESSAGE(TMPMSGID); IF TMPMSGID = MSGID THEN DBMS_PIPE.PACK_MESSAGE(MSGID); STATUS := DBMS_PIPE.SEND_MESSAGE(PIPENAME); IF STATUS <> 0 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10018: error:' || TO_CHAR(STATUS) || ' sending on pipe ' || PIPENAME); END IF; END IF; END; PROCEDURE SIGNAL(NAME IN VARCHAR2, MESSAGE IN VARCHAR2) IS STATUS INTEGER; CURSOR C2(ALERTNAME VARCHAR2) IS SELECT SID FROM DBMS_ALERT_INFO WHERE NAME = UPPER(ALERTNAME); BEGIN STATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(UPPER(NAME), 2000000000, 2048), DBMS_LOCK.S_MODE, DBMS_LOCK.MAXWAIT, RELEASE_ON_COMMIT => TRUE); IF STATUS != 0 AND STATUS != 4 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10001: lock request error, status: ' || TO_CHAR(STATUS)); END IF; UPDATE DBMS_ALERT_INFO SET CHANGED = 'Y', MESSAGE = SIGNAL.MESSAGE WHERE NAME = UPPER(SIGNAL.NAME); IF DBMS_UTILITY.IS_CLUSTER_DATABASE THEN FOR REC IN C2(NAME) LOOP STATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(REC.SID, 2000002048, 2048), DBMS_LOCK.SX_MODE, TIMEOUT => 0, RELEASE_ON_COMMIT => TRUE); IF STATUS = 0 THEN DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID); STATUS := DBMS_LOCK.RELEASE(DBMS_UTILITY.GET_HASH_VALUE(REC.SID, 2000002048, 2048)); ELSE IF STATUS != 1 AND STATUS != 4 THEN RAISE_APPLICATION_ERROR(-20000, 'ORU-10022: lock request error, status: ' || TO_CHAR(STATUS)); END IF; SIGNAL_PIPE('ORA$ALERT$' || REC.SID); END IF; END LOOP; ELSE FOR REC IN C2(NAME) LOOP IF NOT DBMS_SESSION.IS_SESSION_ALIVE(REC.SID) THEN DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID); ELSE SIGNAL_PIPE('ORA$ALERT$' || REC.SID); END IF; END LOOP; END IF; END; PROCEDURE REMOVEALL IS BEGIN DELETE FROM DBMS_ALERT_INFO WHERE SID = THIS_SESSION_ID; DBMS_PIPE.PURGE(SIGPIPE); COMMIT; END; END;
12. 동시성 소스에서 볼 수 있듯이 DBMSALERT는 데이터베이스 테이블(DBMS ALERT INFO) 및 DBMS 기반LOCK, DBMS_PIPE가 구현한 테이블 DBMSALERT_INFO 작업 전에 자물쇠를 추가했고 경고의 이름에 따라 자물쇠를 신청했다. 업무가 제출된 후에야 풀렸기 때문에 경고에 대한 작업은 직렬이다. 예를 들어 알림 데이터가 수정된 트리거가 하나 있는데 만약에 여러 세션이 동시에 데이터를 수정하면 동시에 트리거하고 경보를 보낸다.이 중 한 세션만 잠금 신청을 할 수 있고 다른 세션은 막혔습니다.자물쇠를 신청한 세션은 경보를 보내고 자물쇠를 풀면 다른 세션 중 한 세션은 자물쇠를 신청하는데 성공하고 나머지 세션은 막혀서...이런 식으로 추론하면 DBMS 를 사용할 수 있다JOB 이 문제 해결, DBMSJOB는 흔히 볼 수 있는 조작을 병행화하는 작은 기교이다. 그러나 직렬화 문제를 해결하고 대량으로 병행하는 상황에서 DBMS를 빈번하게 조작한다ALERT_INFO 테이블은 성능상의 문제를 가져올 수 있습니다. 외부 링크: dbms_alert DBMS_ALERT: Broadcasting Alerts to Users Using DBMS_ALERT To Notify Sessions Of Database Events PACKAGE DBMS_ALERT Specification Telling a Forms Application that a change has been made on the database.은 프로그램이 데이터를 어떻게 바꾸는지 알려주는 방법을 소개했습니다. 이것은 Enterprise DB를 말합니다. 어떤 데이터베이스인지 모르겠습니다. Postgres 같은 종류인 것 같습니다. Oracle과 DBMS_PIPE & DBMS_ALERT In EnterpriseDB을 닮았습니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JS 다시 쓰기alert, 탄창 오류 우호성 보장텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.