电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

WriteConsistency


发布日期:2023/8/3
 

名词解释ACID :The basic properties of a database transaction: Atomicity Consistency Isolation and Durability All Oracle transactions comply with these properties

其中与本文标题相关的Consistency The transaction takes the resources from one consistent state to another

Consistency is the ACID property that ensures that any changes to values in an instance are consistent with changes to other values in the same instance A consistency constraint is a predicate on data which server as a precondition postcondition and transformation condition on any transaction

根据Consistency 给Oracle带来一个写一致的问题

先看现象

(from asktom write consistency

ops$tkyte@ORA> create sequence s;

Sequence created

ops$tkyte@ORA> create table msg

( seq int primary key sid int

old_id int old_y int

new_id int new_y int );

Table created

ops$tkyte@ORA> create table msg

( seq int primary key sid int

old_id int old_y int

new_id int new_y int );

Table created

ops$tkyte@ORA> create table t

as

select rownum id y

from all_users

where rownum <= ;

Table created

ops$tkyte@ORA> select count(*)

from t;

COUNT(*)

so t is our row table well do concurrent things on msg will be a table

well log the before/after image row by row transactionally msg will be a

table well do the same but using an autonomous transaction:

ops$tkyte@ORA> create or replace procedure log_msg( p_seq in int

p_sid in int

p_old_id in int

p_old_y in int

p_new_id in int

p_new_y in int )

as

pragma autonomous_transaction;

begin

insert into msg

(seq sid old_id old_y new_id new_y )

values

(p_seq p_sid p_old_id p_old_y p_new_id p_new_y );

commit;

end;

/

Procedure created

ops$tkyte@ORA> create or replace trigger t_trigger before update on t for

each row

declare

l_seq number;

begin

select snextval into l_seq from dual;

insert into msg

(seq sid old_id old_y new_id new_y )

values

(l_seq userenv(sessionid) :oldid :oldy :newid :newy );

log_msg

(l_seq userenv(sessionid) :oldid :oldy :newid :newy );

end;

/

Trigger created

that trigger will log entries in both tables well be able to see the atrans entry immediately well only see committed entries in msg however

ops$tkyte@ORA> update t set y = where id = ;

row updated

set the middle row to this locks it as well of course

ops$tkyte@ORA> set echo off

in another session issue:

update t set y = where y = or id = or id = ;

then come back and hit enter

ops$tkyte@ORA> pause

I did that it blocked of course it updated a single row where id= and

blocked on id=

ops$tkyte@ORA> select decode(msgseqnullNOT VISIBLE) msg*

from msg msg

where msgseq = msgseq(+)

order by msgseq;

DECODE(MSG SEQ SID OLD_ID OLD_Y NEW_ID NEW_Y

NOT VISIBLE

we can see it updated row id= the trigger fired It is waiting on id=

ops$tkyte@ORA> set echo off

in another session issue:

update t set y = where id in ( );

commit;

ops$tkyte@ORA> pause

that is your update the first and last row I did that that transaction went right off no problem:

ops$tkyte@ORA> select decode(msgseqnullNOT VISIBLE) msg*

from msg msg

where msgseq = msgseq(+)

order by msgseq;

DECODE(MSG SEQ SID OLD_ID OLD_Y NEW_ID NEW_Y

NOT VISIBLE

we can see now rows in our log table seq= is the first update that we did and is blocking session over there seq= is the one row that blocked session updated so far seq=/ are the entries for the committed first/last row update

ops$tkyte@ORA> commit;

Commit complete

here is where it gets *very* interesting

ops$tkyte@ORA> select decode(msgseqnullNOT VISIBLE) msg*

from msg msg

where msgseq = msgseq(+)

order by msgseq;

DECODE(MSG SEQ SID OLD_ID OLD_Y NEW_ID NEW_Y

NOT VISIBLE

NOT VISIBLE

NOT VISIBLE

NOT VISIBLE

NOT VISIBLE

NOT VISIBLE

rows selected

hmmm when we committed the first thing to happen was the row with id= was

updated we can see that seq= is that update HOWEVER that was actually

rolled back (as was the update to id= as well see below further) and the

update itself was restarted from the beginning It reupdated id= and id=

and added ids and to the mix (y= part of the predicate picked them up)

ops$tkyte@ORA> set echo off

commit in the formerly blocked session

ops$tkyte@ORA> pause

ops$tkyte@ORA> select * from t;

ID Y

so we can clearly see it updated rows and

ops$tkyte@ORA> select decode(msgseqnullUNDONE) msg*

from msg msg

where msgseq = msgseq(+)

order by msgseq;

DECODE SEQ SID OLD_ID OLD_Y NEW_ID NEW_Y

UNDONE

UNDONE

rows selected

now that everything is committed we can see the seq= and seq= were

undone rolled back It was worked performed that was unperformed in

order to allow us to redo it (dont want to give that % raise times or

times or N times do you?)

(from asktom write consistency

如上现象即为Minirollback会导致session的统计信息cleanouts and rollbacks consistent read gets 增加

现说明Oracle执行update的过程

)根据条件作一致读该一致读SCN为update statement开始的时间

)一致读中找到满足条件的记录(this rowwhen we started at updatestatement SCNwas interested by us )

)然后以当前模式(current mode)读取到数据块最新的内容(rowwhen was interested in consistent read)然后比较一致读和当前读的数据集

)如果没有使用触发器则只比较在where中出现的列 (predicate in consistent read) ;如果包括触发器则还需要比较trigger内引用的:old 列因为oracle是在一致读模式得到:old值且在当前模式:old列可能会被改变了

) 如果predicate 发生变化则表示受到其他事务影响则minirollback 否则正常更新

据个例子如果执行update t set x = where y = ;

consistent read中y=为最后一行则通过consistent read找到该纪录然后以current read 读取该块但是发现该纪录已经被其他session将y= update为 y=( Txanother) 表示该session执行的update的状态不一致导致minirollback

update的前部分没有受到Txanother的影响后部分意识到Txanother的影响违背了ACID的C(onsistency):any changes to values in an instance are consistent with changes to other values in the same instance

Minirollback后又如何?

a) Minirollback会释放块上的锁并且回滚(block cleanup and rollback);但被该session所阻塞的session仍然在等待TX Lock

b) 然后切换到SELECT FOR UPDATE 模式 因为

SELECT FOR UPDATE不会产生太多的 undo & redo only the lock byte and the ITL are touched not the other row (or indexes!) bytes不会触发triggersetc

因为有可能再次minirollback所以select for update即能获得锁且相对成本较低降低再次minirollback可能性

c)再执行更新

因此在batch更新之前避免&减少minirollback的方法就是先select for update获得锁再更新

上一篇:找到无用的索引

下一篇:CLOB扩展块满出现的错误