2.3 讀-寫或?qū)?讀情況
<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀-寫</span>或<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">寫-讀</span>统倒,即一個事務進行讀取操作肤晓,另一個進行改動操作 安皱。這種情況下可能發(fā)生<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">臟讀</span>调鬓、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">不可重復讀</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">幻讀</span>的問題酌伊。
各個數(shù)據(jù)庫廠商對<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SQL</span>的支持都不能一樣腾窝。比如MySQL在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">REPEATABLE READ</span>隔離級別上就已經(jīng)解決了<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">幻讀</span>問題。
2.4 并發(fā)問題的解決方案
怎么解決<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">臟讀</span>居砖、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">不可重復讀</span>虹脯、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">幻讀</span>這些問題呢?其實有兩種可選的解決方案:
-
方案一:讀操作利用多版本并發(fā)控制(<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">MVCC</span>)奏候,寫操作進行<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加鎖</span>归形。
所謂的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">MVCC</span>,就是生成一個ReadView鼻由,通過ReadView找到符合條件的記錄版本(歷史版本由undo日志構(gòu)建)暇榴。查詢語句只能<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀</span>到生成ReadView之前<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">已提交事務所做的更改</span>,在生成ReadView之前未提交的事務或者之后才開啟的事務所做的更改是看不到的蕉世。而<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">寫操作</span>肯定針對的是<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">最新版本的記錄</span>蔼紧,讀記錄的歷史版本和改動記錄的最新版本本身并不沖突,也就是采用MVCC時狠轻,<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀-寫</span>操作并不沖突奸例。
普通的SELECT語句在READ COMMITED和REPEATABLE READ隔離級別下會使用到MVCC讀取記錄。 - 在READ COMMITED隔離級別下向楼,一個事務在執(zhí)行過程中每次SELECT操作都會生成一個ReadView查吊,ReadView的存在本身就保證了事務不可以讀取未提交的事務所做的更改,也就是避免了臟讀現(xiàn)象湖蜕; - 在REPEATABLE READ隔離級別下逻卖,一個事務在執(zhí)行過程中只有第一次執(zhí)行SELECT操作才會生成一個ReadView,之后的SELECT操作都復用這個ReadView昭抒,這樣也就避免了不可重復讀和幻讀的問題评也。
-
方案二:讀炼杖、寫操作都采用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加鎖</span>的方式。
如果我們的一些業(yè)務場景不允許讀取記錄的舊版本盗迟,而是每次都必須去<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀取記錄的最新版本</span>坤邪。比如,在銀行存款的事務中罚缕,你需要先把賬戶的余額讀出來艇纺,然后將其加上本次存款的數(shù)額,最后再寫到數(shù)據(jù)庫中邮弹。在將賬戶余額讀取出來后喂饥,就不想讓別的事務再訪問改余額,直到本次存款事務執(zhí)行完成肠鲫,其他事務才可以訪問賬戶的余額。這樣在讀取記錄的時候就需要對其進行<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加鎖</span>操作或粮,這樣也就意味這<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀</span>操作和<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">寫</span>操作也像<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">寫-寫</span>操作那樣<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">排隊執(zhí)行</span>导饲。
<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">臟讀</span>的產(chǎn)生原因是因為當前事務讀取了另外一個未提交事務寫的一條記錄,如果另一個事務在寫記錄的時候就給這條記錄加鎖氯材,那么當前事務就無法繼續(xù)讀取該記錄了渣锦,所以也就不會有臟讀問題的產(chǎn)生了。
<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">不可重復讀</span>的產(chǎn)生是因為當前事務先讀取一條記錄氢哮,另外一個事務對該記錄做了改動并提交之后袋毙,當前事務再次讀取時會獲得不同的值,如果在當前事務讀取記錄時就該該記錄加鎖冗尤,name另一個事務就無法修改該記錄听盖,自然也不會發(fā)生不可重復讀了。
<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">幻寫</span>問題的產(chǎn)生是因為當前事務讀取了一個范圍的記錄裂七,然后另外的事務向該范圍插入了新記錄皆看,當前事務再次讀取該范圍的記錄時發(fā)現(xiàn)了新插入的新紀錄。采用加鎖的方式解決了幻讀問題就有一些麻煩生真,因為當前事務在第一次讀取記錄時幻讀的記錄并不存在嗤形,所以讀取的時候加鎖不知道鎖那個記錄恩伺。
-
對比:
- 采用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">MVCC</span>方式的話,<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀-寫</span>操作批次并不沖突毛雇,<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">性能更高</span>。
- 采用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加鎖</span>方式的話侦镇,<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀-寫</span>操作彼此需要<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">排隊執(zhí)行</span>灵疮,影響性能。
一般情況下我們愿意采用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">MVCC</span>來解決<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">讀-寫</span>操作并發(fā)執(zhí)行的問題壳繁,但是業(yè)務在某些特殊情況下始藕,要求必須在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加鎖</span>的方式執(zhí)行蒲稳。