一、事務(wù)四大屬性

          分別是原子性、一致性、隔離性、持久性。

          <>1、原子性(Atomicity)

          原子性是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾,因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫(kù),如果操作失敗則不能對(duì)數(shù)據(jù)庫(kù)有任何影響。

          <>2、一致性(Consistency)


          一致性是指事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài),也就是說(shuō)一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。舉例來(lái)說(shuō),假設(shè)用戶A和用戶B兩者的錢加起來(lái)一共是1000,那么不管A和B之間如何轉(zhuǎn)賬、轉(zhuǎn)幾次賬,事務(wù)結(jié)束后兩個(gè)用戶的錢相加起來(lái)應(yīng)該還得是1000,這就是事務(wù)的一致性。

          <>3、隔離性(Isolation)


          隔離性是當(dāng)多個(gè)用戶并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),比如同時(shí)操作同一張表時(shí),數(shù)據(jù)庫(kù)為每一個(gè)用戶開(kāi)啟的事務(wù),不能被其他事務(wù)的操作所干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。關(guān)于事務(wù)的隔離性數(shù)據(jù)庫(kù)提供了多種隔離級(jí)別,稍后會(huì)介紹到。

          <>4、持久性(Durability)


          持久性是指一個(gè)事務(wù)一旦被提交了,那么對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫(kù)系統(tǒng)遇到故障的情況下也不會(huì)丟失提交事務(wù)的操作。例如我們?cè)谑褂肑DBC操作數(shù)據(jù)庫(kù)時(shí),在提交事務(wù)方法后,提示用戶事務(wù)操作完成,當(dāng)我們程序執(zhí)行完成直到看到提示后,就可以認(rèn)定事務(wù)已經(jīng)正確提交,即使這時(shí)候數(shù)據(jù)庫(kù)出現(xiàn)了問(wèn)題,也必須要將我們的事務(wù)完全執(zhí)行完成。否則的話就會(huì)造成我們雖然看到提示事務(wù)處理完畢,但是數(shù)據(jù)庫(kù)因?yàn)楣收隙鴽](méi)有執(zhí)行事務(wù)的重大錯(cuò)誤。這是不允許的。

          <>二、事務(wù)的隔離級(jí)別

          <>1、為什么要設(shè)置隔離級(jí)別

          在數(shù)據(jù)庫(kù)操作中,在并發(fā)的情況下可能出現(xiàn)如下問(wèn)題:

          *
          更新丟失(Lost update)?

          如果多個(gè)線程操作,基于同一個(gè)查詢結(jié)構(gòu)對(duì)表中的記錄進(jìn)行修改,那么后修改的記錄將會(huì)覆蓋前面修改的記錄,前面的修改就丟失掉了,這就叫做更新丟失。這是因?yàn)橄到y(tǒng)沒(méi)有執(zhí)行任何的鎖操作,因此并發(fā)事務(wù)并沒(méi)有被隔離開(kāi)來(lái)。?
          第1類丟失更新:事務(wù)A撤銷時(shí),把已經(jīng)提交的事務(wù)B的更新數(shù)據(jù)覆蓋了。?
          ?
          第2類丟失更新:事務(wù)A覆蓋事務(wù)B已經(jīng)提交的數(shù)據(jù),造成事務(wù)B所做的操作丟失。?
          ?
          解決方法:對(duì)行加鎖,只允許并發(fā)一個(gè)更新事務(wù)。

          *
          臟讀(Dirty Reads)?
          臟讀(Dirty Read):A事務(wù)讀取B事務(wù)尚未提交的數(shù)據(jù)并在此基礎(chǔ)上操作,而B(niǎo)事務(wù)執(zhí)行回滾,那么A讀取到的數(shù)據(jù)就是臟數(shù)據(jù)。?
          ?
          解決辦法:如果在第一個(gè)事務(wù)提交前,任何其他事務(wù)不可讀取其修改過(guò)的值,則可以避免該問(wèn)題。

          *
          不可重復(fù)讀(Non-repeatable Reads)?
          一個(gè)事務(wù)對(duì)同一行數(shù)據(jù)重復(fù)讀取兩次,但是卻得到了不同的結(jié)果。事務(wù)T1讀取某一數(shù)據(jù)后,事務(wù)T2對(duì)其做了修改,當(dāng)事務(wù)T1再次讀該數(shù)據(jù)時(shí)得到與前一次不同的值。?
          ?
          解決辦法:如果只有在修改事務(wù)完全提交之后才可以讀取數(shù)據(jù),則可以避免該問(wèn)題。

          *
          幻象讀?
          指兩次執(zhí)行同一條 select
          語(yǔ)句會(huì)出現(xiàn)不同的結(jié)果,第二次讀會(huì)增加一數(shù)據(jù)行,并沒(méi)有說(shuō)這兩次執(zhí)行是在同一個(gè)事務(wù)中。一般情況下,幻象讀應(yīng)該正是我們所需要的。但有時(shí)候卻不是,如果打開(kāi)的游標(biāo),在對(duì)游標(biāo)進(jìn)行操作時(shí),并不希望新增的記錄加到游標(biāo)命中的數(shù)據(jù)集中來(lái)。隔離級(jí)別為
          游標(biāo)穩(wěn)定性
          的,可以阻止幻象讀。例如:目前工資為1000的員工有10人。那么事務(wù)1中讀取所有工資為1000的員工,得到了10條記錄;這時(shí)事務(wù)2向員工表插入了一條員工記錄,工資也為1000;那么事務(wù)1再次讀取所有工資為1000的員工共讀取到了11條記錄。?
          ?
          解決辦法:如果在操作事務(wù)完成數(shù)據(jù)處理之前,任何其他事務(wù)都不可以添加新數(shù)據(jù),則可避免該問(wèn)題。

          正是為了解決以上情況,數(shù)據(jù)庫(kù)提供了幾種隔離級(jí)別。

          <>2、事務(wù)的隔離級(jí)別

          數(shù)據(jù)庫(kù)事務(wù)的隔離級(jí)別有4個(gè),由低到高依次為Read uncommitted(未授權(quán)讀取、讀未提交)、Read
          committed(授權(quán)讀取、讀提交)、Repeatable
          read(可重復(fù)讀取)、Serializable(序列化),這四個(gè)級(jí)別可以逐個(gè)解決臟讀、不可重復(fù)讀、幻象讀這幾類問(wèn)題。

          * Read uncommitted(未授權(quán)讀取、讀未提交):?

          如果一個(gè)事務(wù)已經(jīng)開(kāi)始寫數(shù)據(jù),則另外一個(gè)事務(wù)則不允許同時(shí)進(jìn)行寫操作,但允許其他事務(wù)讀此行數(shù)據(jù)。該隔離級(jí)別可以通過(guò)“排他寫鎖”實(shí)現(xiàn)。這樣就避免了更新丟失,卻可能出現(xiàn)臟讀。也就是說(shuō)事務(wù)B讀取到了事務(wù)A未提交的數(shù)據(jù)。
          * Read committed(授權(quán)讀取、讀提交):?

          讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問(wèn)該行數(shù)據(jù),但是未提交的寫事務(wù)將會(huì)禁止其他事務(wù)訪問(wèn)該行。該隔離級(jí)別避免了臟讀,但是卻可能出現(xiàn)不可重復(fù)讀。事務(wù)A事先讀取了數(shù)據(jù),事務(wù)B緊接了更新了數(shù)據(jù),并提交了事務(wù),而事務(wù)A再次讀取該數(shù)據(jù)時(shí),數(shù)據(jù)已經(jīng)發(fā)生了改變。
          * Repeatable read(可重復(fù)讀?。?

          可重復(fù)讀是指在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù)。在這個(gè)事務(wù)還沒(méi)有結(jié)束時(shí),另外一個(gè)事務(wù)也訪問(wèn)該同一數(shù)據(jù)。那么,在第一個(gè)事務(wù)中的兩次讀數(shù)據(jù)之間,即使第二個(gè)事務(wù)對(duì)數(shù)據(jù)進(jìn)行修改,第一個(gè)事務(wù)兩次讀到的的數(shù)據(jù)是一樣的。這樣就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是一樣的,因此稱為是可重復(fù)讀。讀取數(shù)據(jù)的事務(wù)將會(huì)禁止寫事務(wù)(但允許讀事務(wù)),寫事務(wù)則禁止任何其他事務(wù)。這樣避免了不可重復(fù)讀取和臟讀,但是有時(shí)可能出現(xiàn)幻象讀。(讀取數(shù)據(jù)的事務(wù))這可以通過(guò)“共享讀鎖”和“排他寫鎖”實(shí)現(xiàn)。
          * Serializable(序列化):?

          提供嚴(yán)格的事務(wù)隔離。它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個(gè)接著一個(gè)地執(zhí)行,但不能并發(fā)執(zhí)行。如果僅僅通過(guò)“行級(jí)鎖”是無(wú)法實(shí)現(xiàn)事務(wù)序列化的,必須通過(guò)其他機(jī)制保證新插入的數(shù)據(jù)不會(huì)被剛執(zhí)行查詢操作的事務(wù)訪問(wèn)到。序列化是最高的事務(wù)隔離級(jí)別,同時(shí)代價(jià)也花費(fèi)最高,性能很低,一般很少使用,在該級(jí)別下,事務(wù)順序執(zhí)行,不僅可以避免臟讀、不可重復(fù)讀,還避免了幻像讀。?
          ?
          隔離級(jí)別越高,越能保證數(shù)據(jù)的完整性和一致性,但是對(duì)并發(fā)性能的影響也越大。對(duì)于多數(shù)應(yīng)用程序,可以優(yōu)先考慮把數(shù)據(jù)庫(kù)系統(tǒng)的隔離級(jí)別設(shè)為Read
          Committed。它能夠避免臟讀取,而且具有較好的并發(fā)性能。盡管它會(huì)導(dǎo)致不可重復(fù)讀、幻讀和第二類丟失更新這些并發(fā)問(wèn)題,在可能出現(xiàn)這類問(wèn)題的個(gè)別場(chǎng)合,可以由應(yīng)用程序采用悲觀鎖或樂(lè)觀鎖來(lái)控制。大多數(shù)數(shù)據(jù)庫(kù)的默認(rèn)級(jí)別就是Read
          committed,比如Sql Server , Oracle。MySQL的默認(rèn)隔離級(jí)別就是Repeatable read。
          <>三、悲觀鎖和樂(lè)觀鎖

          雖然數(shù)據(jù)庫(kù)的隔離級(jí)別可以解決大多數(shù)問(wèn)題,但是靈活度較差,為此又提出了悲觀鎖和樂(lè)觀鎖的概念。

          <>1、悲觀鎖


          悲觀鎖,它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度。因此,在整個(gè)數(shù)據(jù)處理過(guò)程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制。也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問(wèn)的排他性,否則,即使在本系統(tǒng)的數(shù)據(jù)訪問(wèn)層中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù)。

          * 使用場(chǎng)景舉例:以MySQL InnoDB為例

          商品t_items表中有一個(gè)字段status,status為1代表商品未被下單,status為2代表商品已經(jīng)被下單(此時(shí)該商品無(wú)法再次下單),那么我們對(duì)某個(gè)商品下單時(shí)必須確保該商品status為1。假設(shè)商品的id為1。?
          如果不采用鎖,那么操作方法如下:
          //1.查詢出商品信息 select status from t_items where id=1; //2.根據(jù)商品信息生成訂單,并插入訂單表
          t_ordersinsert into t_orders (id,goods_id) values (null,1); //3.修改商品status為2
          update t_itemsset status=2;
          * 1
          * 2
          * 3
          * 4
          * 5
          * 6

          但是上面這種場(chǎng)景在高并發(fā)訪問(wèn)的情況下很可能會(huì)出現(xiàn)問(wèn)題。例如當(dāng)?shù)谝徊讲僮髦校樵兂鰜?lái)的商品status為1。但是當(dāng)我們執(zhí)行第三步Update操作的時(shí)候,有可能出現(xiàn)其他人先一步對(duì)商品下單把t_items中的status修改為2了,但是我們并不知道數(shù)據(jù)已經(jīng)被修改了,這樣就可能造成同一個(gè)商品被下單2次,使得數(shù)據(jù)不一致。所以說(shuō)這種方式是不安全的。

          * 使用悲觀鎖來(lái)解決問(wèn)題

          在上面的場(chǎng)景中,商品信息從查詢出來(lái)到修改,中間有一個(gè)處理訂單的過(guò)程,使用悲觀鎖的原理就是,當(dāng)我們?cè)诓樵兂鰐_items信息后就把當(dāng)前的數(shù)據(jù)鎖定,直到我們修改完畢后再解鎖。那么在這個(gè)過(guò)程中,因?yàn)閠_items被鎖定了,就不會(huì)出現(xiàn)有第三者來(lái)對(duì)其進(jìn)行修改了。需要注意的是,要使用悲觀鎖,我們必須關(guān)閉mysql數(shù)據(jù)庫(kù)的自動(dòng)提交屬性,因?yàn)镸ySQL默認(rèn)使用autocommit模式,也就是說(shuō),當(dāng)你執(zhí)行一個(gè)更新操作后,MySQL會(huì)立刻將結(jié)果進(jìn)行提交。我們可以使用命令設(shè)置MySQL為非autocommit模式:
          set autocommit=0;?
          設(shè)置完autocommit后,我們就可以執(zhí)行我們的正常業(yè)務(wù)了。具體如下:
          //0.開(kāi)始事務(wù) begin;/begin work;/start transaction; (三者選一就可以) //1.查詢出商品信息 select
          statusfrom t_items where id=1 for update; //2.根據(jù)商品信息生成訂單 insert into t_orders
          (id,goods_id)values (null,1); //3.修改商品status為2 update t_items set status=2;
          //4.提交事務(wù)commit;/commit work;
          * 1
          * 2
          * 3
          * 4
          * 5
          * 6
          * 7
          * 8
          * 9
          * 10
          上面的begin/commit為事務(wù)的開(kāi)始和結(jié)束,因?yàn)樵谇耙徊轿覀冴P(guān)閉了mysql的autocommit,所以需要手動(dòng)控制事務(wù)的提交。?
          上面的第一步我們執(zhí)行了一次查詢操作:select status from t_items where id=1 for update;
          與普通查詢不一樣的是,我們使用了select…for update
          的方式,這樣就通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)了悲觀鎖。此時(shí)在t_items表中,id為1的那條數(shù)據(jù)就被我們鎖定了,其它的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行。這樣我們可以保證當(dāng)前的數(shù)據(jù)不會(huì)被其它事務(wù)修改。需要注意的是,在事務(wù)中,只有
          SELECT ... FOR UPDATE?或LOCK IN SHARE MODE?操作同一個(gè)數(shù)據(jù)時(shí)才會(huì)等待其它事務(wù)結(jié)束后才執(zhí)行,一般SELECT ...
          ?則不受此影響。拿上面的實(shí)例來(lái)說(shuō),當(dāng)我執(zhí)行select status from t_items where id=1 for update;
          后。我在另外的事務(wù)中如果再次執(zhí)行select status from t_items where id=1 for update;
          則第二個(gè)事務(wù)會(huì)一直等待第一個(gè)事務(wù)的提交,此時(shí)第二個(gè)查詢處于阻塞的狀態(tài),但是如果我是在第二個(gè)事務(wù)中執(zhí)行select status from t_items
          where id=1;則能正常查詢出數(shù)據(jù),不會(huì)受第一個(gè)事務(wù)的影響。

          * Row Lock與Table Lock
          使用select…for update會(huì)把數(shù)據(jù)給鎖住,不過(guò)我們需要注意一些鎖的級(jí)別,MySQL InnoDB默認(rèn)Row-Level
          Lock,所以只有「明確」地指定主鍵或者索引,MySQL 才會(huì)執(zhí)行Row lock (只鎖住被選取的數(shù)據(jù)) ,否則MySQL 將會(huì)執(zhí)行Table Lock
          (將整個(gè)數(shù)據(jù)表單給鎖住)。舉例如下:?
          1、select * from t_items where id=1 for update;?
          這條語(yǔ)句明確指定主鍵(id=1),并且有此數(shù)據(jù)(id=1的數(shù)據(jù)存在),則采用row lock。只鎖定當(dāng)前這條數(shù)據(jù)。?
          2、select * from t_items where id=3 for update;?
          這條語(yǔ)句明確指定主鍵,但是卻查無(wú)此數(shù)據(jù),此時(shí)不會(huì)產(chǎn)生lock(沒(méi)有元數(shù)據(jù),又去lock誰(shuí)呢?)。?
          3、select * from t_items where name='手機(jī)' for update;?
          這條語(yǔ)句沒(méi)有指定數(shù)據(jù)的主鍵,那么此時(shí)產(chǎn)生table lock,即在當(dāng)前事務(wù)提交前整張數(shù)據(jù)表的所有字段將無(wú)法被查詢。?
          4、select * from t_items where id>0 for update;?或者select * from t_items where
          id<>1 for update;(注:<>在SQL中表示不等于)?
          上述兩條語(yǔ)句的主鍵都不明確,也會(huì)產(chǎn)生table lock。?
          5、select * from t_items where status=1 for update;(假設(shè)為status字段添加了索引)?
          這條語(yǔ)句明確指定了索引,并且有此數(shù)據(jù),則產(chǎn)生row lock。?
          6、select * from t_items where status=3 for update;(假設(shè)為status字段添加了索引)?
          這條語(yǔ)句明確指定索引,但是根據(jù)索引查無(wú)此數(shù)據(jù),也就不會(huì)產(chǎn)生lock。

          * 悲觀鎖小結(jié)?

          悲觀鎖并不是適用于任何場(chǎng)景,它也有它存在的一些不足,因?yàn)楸^鎖大多數(shù)情況下依靠數(shù)據(jù)庫(kù)的鎖機(jī)制實(shí)現(xiàn),以保證操作最大程度的獨(dú)占性。如果加鎖的時(shí)間過(guò)長(zhǎng),其他用戶長(zhǎng)時(shí)間無(wú)法訪問(wèn),影響了程序的并發(fā)訪問(wèn)性,同時(shí)這樣對(duì)數(shù)據(jù)庫(kù)性能開(kāi)銷影響也很大,特別是對(duì)長(zhǎng)事務(wù)而言,這樣的開(kāi)銷往往無(wú)法承受。所以與悲觀鎖相對(duì)的,我們有了樂(lè)觀鎖。
          <>2、樂(lè)觀鎖

          樂(lè)觀鎖( Optimistic Locking )
          相對(duì)悲觀鎖而言,樂(lè)觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突,所以只會(huì)在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則返回用戶錯(cuò)誤的信息,讓用戶決定如何去做。實(shí)現(xiàn)樂(lè)觀鎖一般來(lái)說(shuō)有以下2種方式:

          * 使用版本號(hào)?
          使用數(shù)據(jù)版本(Version)記錄機(jī)制實(shí)現(xiàn),這是樂(lè)觀鎖最常用的一種實(shí)現(xiàn)方式。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè)數(shù)字類型的
          “version”
          字段來(lái)實(shí)現(xiàn)。當(dāng)讀取數(shù)據(jù)時(shí),將version字段的值一同讀出,數(shù)據(jù)每更新一次,對(duì)此version值加一。當(dāng)我們提交更新的時(shí)候,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的version值進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的version值相等,則予以更新,否則認(rèn)為是過(guò)期數(shù)據(jù)。
          * 使用時(shí)間戳?
          樂(lè)觀鎖定的第二種實(shí)現(xiàn)方式和第一種差不多,同樣是在需要樂(lè)觀鎖控制的table中增加一個(gè)字段,名稱無(wú)所謂,字段類型使用時(shí)間戳(timestamp),
          和上面的version類似,也是在更新提交的時(shí)候檢查當(dāng)前數(shù)據(jù)庫(kù)中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對(duì)比,如果一致則OK,否則就是版本沖突。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問(wèn)題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                少妇性高潮视频 | 黄色福利视频网站 | 亚洲乱妇19p | 中国激情网站 | 久久久精 |