Dirty Reader How not to be writor block live?
If the database supports dirty read, all open Dbhandle are configured with db_read_uncommitted;
When the thread gets the write lock and finishes processing (such as Splite one page), it drops to was_write lock. Wwrite Lock and dirty reader will not conflict;
Requests to the dirty read lock will be processed preferentially;
Dirty read Lock is released immediately after the read is completed; Why? Because TXN retains the read lock for Repeatable read.
If the cursor is read_uncommitted, the Lock_mode will be changed to db_lock_read_uncommitted (DB_META.C, Db_lget) when processing a request for a read lock;
Txn abort when the Was_write lock is re-upgraded to the write lock. Because Abort->undo need to write page. (TXN.C, Txn_abort ())
code reading:
Db.h
typedefenum{db_lock_ng=0,/*Not granted.*/Db_lock_read=1,/*Shared/read.*/Db_lock_write=2,/*Exclusive/write.*/db_lock_wait=3,/*Wait for Event*//For queue AMDb_lock_iwrite=4,/*Intent exclusive/write.*///Intent lock for hierarchy lockDb_lock_iread=5,/*Intent to Share/read.*/DB_LOCK_IWR=6,/*Intent to read and write.*/db_lock_read_uncommitted=7,/*degree 1 isolation.*/Db_lock_wwrite=8 /*was written.*/} db_lockmode_t;
LOCK/LOCK_REGION.C:
#defineDb_lock_riw_n 9Static Constu_int8_t db_riw_conflicts[] = {/*N R W WT IW IR riw DR WW*//*N*/ 0,0,0,0,0,0,0,0,0,/*R*/ 0,0,1,0,1,0,1,0,1,/*W*/ 0,1,1,1,1,1,1,1,1,/*WT*/ 0,0,0,0,0,0,0,0,0,/*IW*/ 0,1,1,0,0,0,0,1,1,/*IR*/ 0,0,1,0,0,0,0,0,1,/*Riw*/ 0,1,1,0,0,0,0,1,1,/*DR*/ 0,0,1,0,1,0,1,0,0,//dirty read and Was_write do not conflict/*WW*/ 0,1,1,0,1,1,1,0,1};
LOCK.C, __lock_get_internal (): Dirty read lock priority processing
LP = Sh_tailq_first (&sh_obj->holders, __db_lock); Sh_off= R_offset (<->Reginfo, Sh_locker);
Traversing the holder list for(; lp = NULL; LP =Sh_tailq_next (LP, Links, __db_lock)) { if(Sh_off = = lp->holder) {//has been lockedif(Lp->mode = = Lock_mode && Lp->status = =Db_lstat_held) {LP->refcount++; Lock->off = R_offset (<->reginfo, LP); Lock->gen = lp->Gen; Lock->mode = lp->mode; GotoDone ; } Else{ihold=1; } } Else if(__lock_same_family (LT, R_addr (<->reginfo, lp->holder), Sh_locker) Ihold=1; Else if(CONFLICTS (LT, region, lp->mode, Lock_mode)) Break; Else if(Lp->mode = = Db_lock_read | | lp->mode = =db_lock_wwrite) {Grant_dirty=1;//Holder list only read lock; or a WW lock holder= lp->Holder; } } if(LP! =NULL) {//conflicting holderif(Ihold | | Lf_isset (db_lock_upgrade) | | Lock_mode = =db_lock_read_uncommitted) Action=Head ;//dirty read request first, put waiter queue headerElseAction=TAIL; } Else { if(Lf_isset (db_lock_upgrade)) Action=UPGRADE; Else if(Ihold) Action=GRANT; Else {
Conflict-free holder; Traverse the Waiter List Sh_tailq_foreach (LP,&sh_obj->Waiters, links, __db_lock)if(Lp->holder! = Sh_off && CONFLICTS (LT, region, lp->mode, Lock_mode)) Break; if(LP = =NULL)//conflict-free waiter action=GRANT; Else if(Grant_dirty && Lock_mode = =db_lock_read_uncommitted) {LP= Sh_tailq_first (&sh_obj->waiters, __db_lock); if(Lp->mode = = Db_lock_write && Lp->holder = =holder) Action=SECOND;//Waiter The table header is a upgrade request. The current DR is placed in the secondElseAction=Grant;//approve Dr Lock request. } Else if(Lock_mode = =db_lock_read_uncommitted)
Here is the DR request, but!grant_dirty (no READ/WW holder). There are conflict-free holder;
A conflict of waiter. ?????? Action=SECOND; ElseAction=TAIL; } }
DB_META.C,-write lock downgrade Wwrite Lock:
__db_lget () Dbc->lock.pgno = Pgno; ...Switch (action) {Defaultif (has_timeout)Goto Do_couple; RET = __lock_get (env, Dbc->locker, Lkflags, &DBC->LOCK_DBT, mode, LOCKP);The general treatmentBreakCase lck_downgrade:couple[0].op = Db_lock_get; couple[0].obj =NULL; couple[0].lock = *LOCKP; couple[0].mode = Db_lock_wwrite;Request a new Wwrite lock Umrw_set (couple[0].timeout); i++;/* Fallthrough */Case lck_couple:do_couple:couple[i].op = has_timeout? Db_lock_get_timeout:db_lock_get; Couple[i].obj = &dbc->lock_dbt; Couple[i].mode = mode;A new lock on the incoming Pgno request, Umrw_set (couple[i].timeout); i++;if (has_timeout) couple[0].timeout = F_isset (DBC, dbc_recover)?0:txn->lock_timeout;if (action = = Lck_couple | | action = = lck_downgrade) {couple[i].op = Db_lock_put;Release the original lock (downgrade words, is the original write lock, thereby achieving a lock downgrade) Couple[i].lock = *LOCKP; i++; RET = __lock_vec (env, Dbc->locker, lkflags, couple, I, &REQP);if (ret = =0 | | REQP = = &couple[i-1]) *lockp = i = =1? couple[0].lock:couple[i-2].lock;I! = 1 identifies Lck_downgrade and returns a new Wwrite lockBreak }__db_lput ()if (F_isset (DBC->DBP, db_am_read_uncommitted) &&! F_isset (DBC, dbc_error) && Lockp->mode = = db_lock_write) action = Lck_downgrade;Elseif (Dbc->txn = =NULL) action = lck_couple;The couple here is meant to be released directly. Non-transaction situationElseif (F_isset (DBC, dbc_read_committed | dbc_was_read_committed) && Lockp->mode = = db_lock_read) action = lck_couple;read_commited, and read lockElseif (Lockp->mode = = db_lock_read_uncommitted) action = lck_couple;read_uncommitted, read lock, Direct releaseelse action =0; env = dbc->env;Switch (action) {Case Lck_couple:ret = __lock_put (env, LOCKP);BreakCase lck_downgrade:couple[0].op = Db_lock_get; couple[0].obj =NULL; couple[0].mode = Db_lock_wwrite;//get a WW lock Couple[0].lock = *LOCKP; Umrw_set (Couple[0].timeout); Couple[1].op = DB_LOCK_PUT; Couple[1].lock = *LOCKP; //release the original lock (write lock) ret = __lock_vec (env, Dbc->locker, 0, couple, Span class= "Hljs-number" >2, &REQP); if (ret = 0 | | reqp = = &couple[1] ) *lockp = Couple[0].lock; break; default:ret = 0; //well, here. If the default isolation level, keep read lock, write lock does not downgrade, also here. break;} return (ret);}
Implementation of Dirty read in Berkeley DB