- 工信部備案號 滇ICP備05000110號-1
- 滇公安備案 滇53010302000111
- 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證 B1.B2-20181647、滇B1.B2-20190004
- 云南互聯(lián)網(wǎng)協(xié)會(huì )理事單位
- 安全聯(lián)盟認證網(wǎng)站身份V標記
- 域名注冊服務(wù)機構許可:滇D3-20230001
- 代理域名注冊服務(wù)機構:新網(wǎng)數碼
我們在操作數據庫的時(shí)候,可能會(huì )由于并發(fā)問(wèn)題而引起的數據的不一致性(數據沖突)
樂(lè )觀(guān)鎖
樂(lè )觀(guān)鎖不是數據庫自帶的,需要我們自己去實(shí)現。樂(lè )觀(guān)鎖是指操作數據庫時(shí)(更新操作),想法很樂(lè )觀(guān),認為這次的操作不會(huì )導致沖突,在操作數據時(shí),并不進(jìn)行任何其他的特殊處理(也就是不加鎖),而在進(jìn)行更新后,再去判斷是否有沖突了。
通常實(shí)現是這樣的:在表中的數據進(jìn)行操作時(shí)(更新),先給數據表加一個(gè)版本(version)字段,每操作一次,將那條記錄的版本號加1。也就是先查詢(xún)出那條記錄,獲取出version字段,如果要對那條記錄進(jìn)行操作(更新),則先判斷此刻version的值是否與剛剛查詢(xún)出來(lái)時(shí)的version的值相等,如果相等,則說(shuō)明這段期間,沒(méi)有其他程序對其進(jìn)行操作,則可以執行更新,將version字段的值加1;如果更新時(shí)發(fā)現此刻的version值與剛剛獲取出來(lái)的version的值不相等,則說(shuō)明這段期間已經(jīng)有其他程序對其進(jìn)行操作了,則不進(jìn)行更新操作。
舉例:
下單操作包括3步驟:
1.查詢(xún)出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根據商品信息生成訂單
3.修改商品status為2
update t_goods
set status=2,version=version+1
where id=#{id} and version=#{version};
除了自己手動(dòng)實(shí)現樂(lè )觀(guān)鎖之外,現在網(wǎng)上許多框架已經(jīng)封裝好了樂(lè )觀(guān)鎖的實(shí)現,如hibernate,需要時(shí),可能自行搜索"hiberate 樂(lè )觀(guān)鎖"試試看。
悲觀(guān)鎖
與樂(lè )觀(guān)鎖相對應的就是悲觀(guān)鎖了。悲觀(guān)鎖就是在操作數據時(shí),認為此操作會(huì )出現數據沖突,所以在進(jìn)行每次操作時(shí)都要通過(guò)獲取鎖才能進(jìn)行對相同數據的操作,這點(diǎn)跟java中的synchronized很相似,所以悲觀(guān)鎖需要耗費較多的時(shí)間。另外與樂(lè )觀(guān)鎖相對應的,悲觀(guān)鎖是由數據庫自己實(shí)現了的,要用的時(shí)候,我們直接調用數據庫的相關(guān)語(yǔ)句就可以了。
說(shuō)到這里,由悲觀(guān)鎖涉及到的另外兩個(gè)鎖概念就出來(lái)了,它們就是共享鎖與排它鎖。共享鎖和排它鎖是悲觀(guān)鎖的不同的實(shí)現,它倆都屬于悲觀(guān)鎖的范疇。
共享鎖
共享鎖指的就是對于多個(gè)不同的事務(wù),對同一個(gè)資源共享同一個(gè)鎖。相當于對于同一把門(mén),它擁有多個(gè)鑰匙一樣。就像這樣,你家有一個(gè)大門(mén),大門(mén)的鑰匙有好幾把,你有一把,你女朋友有一把,你們都可能通過(guò)這把鑰匙進(jìn)入你們家,進(jìn)去啪啪啪啥的,一下理解了哈,沒(méi)錯,這個(gè)就是所謂的共享鎖。
剛剛說(shuō)了,對于悲觀(guān)鎖,一般數據庫已經(jīng)實(shí)現了,共享鎖也屬于悲觀(guān)鎖的一種,那么共享鎖在mysql中是通過(guò)什么命令來(lái)調用呢。通過(guò)查詢(xún)資料,了解到通過(guò)在執行語(yǔ)句后面加上lock in share mode就代表對某些資源加上共享鎖了。
比如,我這里通過(guò)mysql打開(kāi)兩個(gè)查詢(xún)編輯器,在其中開(kāi)啟一個(gè)事務(wù),并不執行commit語(yǔ)句
city表DDL如下:
CREATE TABLE `city` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`state` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
begin;
SELECT * from city where id = "1" lock in share mode;
然后在另一個(gè)查詢(xún)窗口中,對id為1的數據進(jìn)行更新
update city set name="666" where id ="1";
此時(shí),操作界面進(jìn)入了卡頓狀態(tài),過(guò)幾秒后,也提示錯誤信息
[SQL]update city set name="666" where id ="1";
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
那么證明,對于id=1的記錄加鎖成功了,在上一條記錄還沒(méi)有commit之前,這條id=1的記錄被鎖住了,只有在上一個(gè)事務(wù)釋放掉鎖后才能進(jìn)行操作,或用共享鎖才能對此數據進(jìn)行操作。
再實(shí)驗一下:
update city set name="666" where id ="1" lock in share mode;
[Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock in share mode' at line 1
加上共享鎖后,也提示錯誤信息了,通過(guò)查詢(xún)資料才知道,對于update,insert,delete語(yǔ)句會(huì )自動(dòng)加排它鎖的原因
于是,我又試了試SELECT * from city where id = "1" lock in share mode;
這下成功了。
排它鎖
排它鎖與共享鎖相對應,就是指對于多個(gè)不同的事務(wù),對同一個(gè)資源只能有一把鎖。
與共享鎖類(lèi)型,在需要執行的語(yǔ)句后面加上for update就可以了
行鎖
行鎖,由字面意思理解,就是給某一行加上鎖,也就是一條記錄加上鎖。
比如之前演示的共享鎖語(yǔ)句
SELECT * from city where id = "1" lock in share mode;
由于對于city表中,id字段為主鍵,就也相當于索引。執行加鎖時(shí),會(huì )將id這個(gè)索引為1的記錄加上鎖,那么這個(gè)鎖就是行鎖。
表鎖
表鎖,和行鎖相對應,給這個(gè)表加上鎖。
售前咨詢(xún)
售后咨詢(xún)
備案咨詢(xún)
二維碼
TOP