分別是原子性、一致性、隔離性、持久性。
創(chuàng)新互聯(lián)客戶idc服務(wù)中心,提供成都機(jī)柜租用、成都服務(wù)器、成都主機(jī)托管、成都雙線服務(wù)器等業(yè)務(wù)的一站式服務(wù)。通過各地的服務(wù)中心,我們向成都用戶提供優(yōu)質(zhì)廉價(jià)的產(chǎn)品以及開放、透明、穩(wěn)定、高性價(jià)比的服務(wù),資深網(wǎng)絡(luò)工程師在機(jī)房提供7*24小時(shí)標(biāo)準(zhǔn)級技術(shù)保障。
原子性是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾,因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫,如果操作失敗則不能對數(shù)據(jù)庫有任何影響。
一致性是指事務(wù)必須使數(shù)據(jù)庫從一個一致性狀態(tài)變換到另一個一致性狀態(tài),也就是說一個事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。舉例來說,假設(shè)用戶A和用戶B兩者的錢加起來一共是1000,那么不管A和B之間如何轉(zhuǎn)賬、轉(zhuǎn)幾次賬,事務(wù)結(jié)束后兩個用戶的錢相加起來應(yīng)該還得是1000,這就是事務(wù)的一致性。
隔離性是當(dāng)多個用戶并發(fā)訪問數(shù)據(jù)庫時(shí),比如同時(shí)操作同一張表時(shí),數(shù)據(jù)庫為每一個用戶開啟的事務(wù),不能被其他事務(wù)的操作所干擾,多個并發(fā)事務(wù)之間要相互隔離。關(guān)于事務(wù)的隔離性數(shù)據(jù)庫提供了多種隔離級別,稍后會介紹到。
持久性是指一個事務(wù)一旦被提交了,那么對數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會丟失提交事務(wù)的操作。例如我們在使用JDBC操作數(shù)據(jù)庫時(shí),在提交事務(wù)方法后,提示用戶事務(wù)操作完成,當(dāng)我們程序執(zhí)行完成直到看到提示后,就可以認(rèn)定事務(wù)已經(jīng)正確提交,即使這時(shí)候數(shù)據(jù)庫出現(xiàn)了問題,也必須要將我們的事務(wù)完全執(zhí)行完成。否則的話就會造成我們雖然看到提示事務(wù)處理完畢,但是數(shù)據(jù)庫因?yàn)楣收隙鴽]有執(zhí)行事務(wù)的重大錯誤。這是不允許的。
在數(shù)據(jù)庫操作中,在并發(fā)的情況下可能出現(xiàn)如下問題:
正是為了解決以上情況,數(shù)據(jù)庫提供了幾種隔離級別。
數(shù)據(jù)庫事務(wù)的隔離級別有4個,由低到高依次為Read uncommitted(未授權(quán)讀取、讀未提交)、Read committed(授權(quán)讀取、讀提交)、Repeatable read(可重復(fù)讀?。erializable(序列化),這四個級別可以逐個解決臟讀、不可重復(fù)讀、幻象讀這幾類問題。
雖然數(shù)據(jù)庫的隔離級別可以解決大多數(shù)問題,但是靈活度較差,為此又提出了悲觀鎖和樂觀鎖的概念。
悲觀鎖,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度。因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制。也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)的數(shù)據(jù)訪問層中實(shí)現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù)。
商品t_items表中有一個字段status,status為1代表商品未被下單,status為2代表商品已經(jīng)被下單(此時(shí)該商品無法再次下單),那么我們對某個商品下單時(shí)必須確保該商品status為1。假設(shè)商品的id為1。
如果不采用鎖,那么操作方法如下:
但是上面這種場景在高并發(fā)訪問的情況下很可能會出現(xiàn)問題。例如當(dāng)?shù)谝徊讲僮髦?,查詢出來的商品status為1。但是當(dāng)我們執(zhí)行第三步Update操作的時(shí)候,有可能出現(xiàn)其他人先一步對商品下單把t_items中的status修改為2了,但是我們并不知道數(shù)據(jù)已經(jīng)被修改了,這樣就可能造成同一個商品被下單2次,使得數(shù)據(jù)不一致。所以說這種方式是不安全的。
在上面的場景中,商品信息從查詢出來到修改,中間有一個處理訂單的過程,使用悲觀鎖的原理就是,當(dāng)我們在查詢出t_items信息后就把當(dāng)前的數(shù)據(jù)鎖定,直到我們修改完畢后再解鎖。那么在這個過程中,因?yàn)閠_items被鎖定了,就不會出現(xiàn)有第三者來對其進(jìn)行修改了。需要注意的是,要使用悲觀鎖,我們必須關(guān)閉mysql數(shù)據(jù)庫的自動提交屬性,因?yàn)镸ySQL默認(rèn)使用autocommit模式,也就是說,當(dāng)你執(zhí)行一個更新操作后,MySQL會立刻將結(jié)果進(jìn)行提交。我們可以使用命令設(shè)置MySQL為非autocommit模式: set autocommit=0;
設(shè)置完autocommit后,我們就可以執(zhí)行我們的正常業(yè)務(wù)了。具體如下:
上面的begin/commit為事務(wù)的開始和結(jié)束,因?yàn)樵谇耙徊轿覀冴P(guān)閉了mysql的autocommit,所以需要手動控制事務(wù)的提交。
上面的第一步我們執(zhí)行了一次查詢操作: select status from t_items where id=1 for update; 與普通查詢不一樣的是,我們使用了 select…for update 的方式,這樣就通過數(shù)據(jù)庫實(shí)現(xiàn)了悲觀鎖。此時(shí)在t_items表中,id為1的那條數(shù)據(jù)就被我們鎖定了,其它的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行。這樣我們可以保證當(dāng)前的數(shù)據(jù)不會被其它事務(wù)修改。需要注意的是,在事務(wù)中,只有 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE 操作同一個數(shù)據(jù)時(shí)才會等待其它事務(wù)結(jié)束后才執(zhí)行,一般 SELECT ... 則不受此影響。拿上面的實(shí)例來說,當(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; 則第二個事務(wù)會一直等待第一個事務(wù)的提交,此時(shí)第二個查詢處于阻塞的狀態(tài),但是如果我是在第二個事務(wù)中執(zhí)行 select status from t_items where id=1; 則能正常查詢出數(shù)據(jù),不會受第一個事務(wù)的影響。
使用 select…for update 會把數(shù)據(jù)給鎖住,不過我們需要注意一些鎖的級別,MySQL InnoDB默認(rèn)Row-Level Lock,所以只有「明確」地指定主鍵或者索引,MySQL 才會執(zhí)行Row lock (只鎖住被選取的數(shù)據(jù)) ,否則MySQL 將會執(zhí)行Table Lock (將整個數(shù)據(jù)表單給鎖住)。舉例如下:
1、 select * from t_items where id=1 for update;
這條語句明確指定主鍵(id=1),并且有此數(shù)據(jù)(id=1的數(shù)據(jù)存在),則采用row lock。只鎖定當(dāng)前這條數(shù)據(jù)。
2、 select * from t_items where id=3 for update;
這條語句明確指定主鍵,但是卻查無此數(shù)據(jù),此時(shí)不會產(chǎn)生lock(沒有元數(shù)據(jù),又去lock誰呢?)。
3、 select * from t_items where name='手機(jī)' for update;
這條語句沒有指定數(shù)據(jù)的主鍵,那么此時(shí)產(chǎn)生table lock,即在當(dāng)前事務(wù)提交前整張數(shù)據(jù)表的所有字段將無法被查詢。
4、 select * from t_items where id0 for update; 或者 select * from t_items where id1 for update; (注:在SQL中表示不等于)
上述兩條語句的主鍵都不明確,也會產(chǎn)生table lock。
5、 select * from t_items where status=1 for update; (假設(shè)為status字段添加了索引)
這條語句明確指定了索引,并且有此數(shù)據(jù),則產(chǎn)生row lock。
6、 select * from t_items where status=3 for update; (假設(shè)為status字段添加了索引)
這條語句明確指定索引,但是根據(jù)索引查無此數(shù)據(jù),也就不會產(chǎn)生lock。
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會造成沖突,所以只會在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會正式對數(shù)據(jù)的沖突與否進(jìn)行檢測,如果發(fā)現(xiàn)沖突了,則返回用戶錯誤的信息,讓用戶決定如何去做。實(shí)現(xiàn)樂觀鎖一般來說有以下2種方式:
MySQL 中事務(wù)的隔離級別一共分為四種,分別如下:
序列化(SERIALIZABLE):如果隔離級別為序列化,則用戶之間通過一個接一個順序地執(zhí)行當(dāng)前的事務(wù),這種隔離級別提供了事務(wù)之間最大限度的隔離。
可重復(fù)讀(REPEATABLE READ):在可重復(fù)讀在這一隔離級別上,事務(wù)不會被看成是一個序列。不過,當(dāng)前正在執(zhí)行事務(wù)的變化仍然不能被外部看到,也就是說,如果用戶在另外一個事務(wù)中執(zhí)行同條 SELECT 語句數(shù)次,結(jié)果總是相同的。(因?yàn)檎趫?zhí)行的事務(wù)所產(chǎn)生的數(shù)據(jù)變化不能被外部看到)。
提交讀(READ COMMITTED):READ COMMITTED 隔離級別的安全性比 REPEATABLE READ 隔離級別的安全性要差。處于 READ COMMITTED 級別的事務(wù)可以看到其他事務(wù)對數(shù)據(jù)的修改。也就是說,在事務(wù)處理期間,如果其他事務(wù)修改了相應(yīng)的表,那么同一個事務(wù)的多個 SELECT 語句可能返回不同的結(jié)果。
未提交讀(READ UNCOMMITTED):READ UNCOMMITTED 提供了事務(wù)之間最小限度的隔離。除了容易產(chǎn)生虛幻的讀操作和不能重復(fù)的讀操作外,處于這個隔離級的事務(wù)可以讀到其他事務(wù)還沒有提交的數(shù)據(jù),如果這個事務(wù)使用其他事務(wù)不提交的變化作為計(jì)算的基礎(chǔ),然后那些未提交的變化被它們的父事務(wù)撤銷,這就導(dǎo)致了大量的數(shù)據(jù)變化。
應(yīng)用環(huán)境
與其他的大型數(shù)據(jù)庫例如?Oracle、DB2、SQL Server等相比,MySQL自有它的不足之處,但是這絲毫也沒有減少它受歡迎的程度。對于一般的個人使用者和中小型企業(yè)來說,MySQL提供的功能已經(jīng)綽綽有余,而且由于 MySQL是開放源碼軟件,因此可以大大降低總體擁有成本。
修改方法
有兩種方法可以對配置了 systemd 的程序進(jìn)行資源隔離:1. 命令行修改:通過執(zhí)行?systemctl set-property?命令實(shí)現(xiàn),形式為?systemctl set-property?name parameter=value;修改默認(rèn)即時(shí)生效。2. 手工修改文件:直接編輯程序的 systemd unit file 文件,完成之后需手工執(zhí)行?systemctl?daemon-reload?更新配置,并重啟服務(wù)?systemctl restart name.service。
systemd unit file 里支持的資源隔離配置項(xiàng),如常見的:
CPUQuota=value
該參數(shù)表示服務(wù)可以獲取的最大 CPU 時(shí)間,value 為百分?jǐn)?shù)形式,高于 100% 表示可使用?1 核以上的?CPU。與 cgroup cpu 控制器?cpu.cfs_quota_us?配置項(xiàng)對應(yīng)。
MemoryLimit=value
該參數(shù)表示服務(wù)可以使用的最大內(nèi)存量,value 可以使用 K, M, G, T 等后綴表示值的大小。與 cgroup?memory 控制器?memory.limit_in_bytes?配置項(xiàng)對應(yīng)。
事務(wù)的4種隔離級別
READ UNCOMMITTED ? ? ? 未提交讀,可以讀取未提交的數(shù)據(jù)。
READ COMMITTED ? ? ? ? 已提交讀,對于鎖定讀(select with for update 或者 for share)、update 和 delete 語句,InnoDB 僅鎖定索引記錄,而不鎖定它們之間的間隙,因此允許在鎖定的記錄旁邊自由插入新記錄。 ? ? ? ? ? ? ? ? ? ?
Gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查。
REPEATABLE READ ? ? ? ?可重復(fù)讀,事務(wù)中的一致性讀取讀取的是事務(wù)第一次讀取所建立的快照。
SERIALIZABLE ? ? ? ? ? 序列化在了解了 4 種隔離級別的需求后,在采用鎖控制隔離級別的基礎(chǔ)上,我們需要了解加鎖的對象(數(shù)據(jù)本身間隙),以及了解整個數(shù)據(jù)范圍的全集組成。
數(shù)據(jù)范圍全集組成
SQL 語句根據(jù)條件判斷不需要掃描的數(shù)據(jù)范圍(不加鎖);
SQL 語句根據(jù)條件掃描到的可能需要加鎖的數(shù)據(jù)范圍;
以單個數(shù)據(jù)范圍為例,數(shù)據(jù)范圍全集包含:(數(shù)據(jù)范圍不一定是連續(xù)的值,也可能是間隔的值組成)
mysql事務(wù)隔離級別如下:
1.讀取未提交(READ-UNCOMMITTED):最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更,可能造成臟讀、不可重復(fù)讀、幻讀。
2.讀取已提交(READ-COMMITTED):允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以避免臟讀,但是可能造成不可重復(fù)、幻讀。
3.可重復(fù)讀(REPEATABLE-READ):對同一字段多次讀取的結(jié)果都是一致的,除非本身事務(wù)修改,可以避免臟讀和不可重復(fù)讀,但是可能造成幻讀。
4.可串行化(SERIALIZABLE):最高的隔離級別,完全服從ACID的隔離級別,所以的事務(wù)依次執(zhí)行,可以避免臟讀、不可重復(fù)讀、幻讀。
事務(wù)的特性:
1.原子性:事務(wù)最小的執(zhí)行單位,不允許分割。事務(wù)的原子性確保動作要么全部執(zhí)行,要么全部不執(zhí)行。
2.一致性:執(zhí)行事務(wù)的前后,數(shù)據(jù)保持一致。例如轉(zhuǎn)賬的業(yè)務(wù)中,無論事務(wù)是否成功,轉(zhuǎn)賬者和收款人的總額應(yīng)該是不變的。
3.隔離性:并發(fā)訪問數(shù)據(jù)庫時(shí),一個用戶的事務(wù)不應(yīng)該被其他事務(wù)所影響,各并發(fā)事務(wù)之間數(shù)據(jù)庫是獨(dú)立的。
4.持久性:一個事務(wù)被提交后,它對數(shù)據(jù)庫中數(shù)據(jù)的改變是持久的,即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有影響。