讀操作 - 共享鎖(InnoDB悲觀鎖)
因?yàn)閙ysql是自動(dòng)提交,但是我們要實(shí)驗(yàn)不讓自動(dòng)提交使用命令set autocommit=0;
共享鎖結(jié)論:
多個(gè)會(huì)話共享同一把鎖。
| 共享鎖/會(huì)話session | 讀操作 | 寫操作 | 鎖操作 |
|---|---|---|---|
| 當(dāng)前session | 可以 | 可以 | 可以 |
| 其他session | 可以 | 阻塞等待操作session的commit(超時(shí)報(bào)錯(cuò)) | 可以 |
加鎖操作:多個(gè)會(huì)話都可以加鎖。
讀操作:多個(gè)會(huì)話共享同一把鎖,每個(gè)會(huì)話都可以去讀數(shù)據(jù)。
寫操作:但是對(duì)于寫數(shù)據(jù),如果一個(gè)會(huì)話寫數(shù)據(jù)了,那么其他會(huì)話都要阻塞等待。
- 如果當(dāng)前session修改了值,但是還沒有commit提交,那么其他session無法修改表中的所有記錄,只能等待超時(shí)。
- 其他session可以select查詢數(shù)據(jù),但是查詢的結(jié)果是舊數(shù)據(jù)。
語(yǔ)句:
select XXXXXXX lock in share mode;
實(shí)驗(yàn):
| session1 | session2 |
|---|---|
| set autocommit =0; | |
| select * from edu_user lock in share mode; | |
| select * from edu_user; | |
| update edu_user set password="456789" where username = "王五"; | |
| update edu_user set password="012345" where username = "王五"; | |
| 等待:1205 - Lock wait timeout exceeded; try restarting transaction | |
| commit; |
結(jié)果:
mysql> select password from edu_user where username="王五";
+----------+
| password |
+----------+
| 456789 |
+----------+
1 row in set (0.29 sec)
說明:
- 當(dāng)前session1獲得共享鎖,session2也可以繼續(xù)添加共享鎖查詢記錄。
- 但是session2想要寫記錄,將會(huì)處于阻塞狀態(tài)。超過了指定時(shí)間(50s)將報(bào)錯(cuò)。雖然session1寫記錄,但是必須要提交事務(wù),session2才可見。
- 所以select * from t lock in share mode;一般配合事務(wù)commit命令使用。
- 查詢共享鎖等待時(shí)間
mysql> show variables like '%innodb_lock_wait_timeout%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set (0.00 sec)
讀操作 - 排他鎖(InnoDB悲觀鎖 )
排他鎖結(jié)論:
當(dāng)前事務(wù)加了排他鎖之后,其他事務(wù)什么鎖都不能加。
- 鎖操作:select * from 表名 where 條件 for update
- 寫操作:insert / update
- 讀操作:select * from 表名 where 條件
| 排他鎖/會(huì)話session | 讀操作 | 寫操作 | 鎖操作 |
|---|---|---|---|
| 當(dāng)前session | 可以 | 可以 | 可以 |
| 其他session | 可以 | 阻塞等待(超時(shí)報(bào)錯(cuò)) | 阻塞等待(超時(shí)報(bào)錯(cuò)) |
- 加鎖操作:如果多個(gè)session的其中一個(gè)session加鎖了,那么其他session只能阻塞。
- 讀操作:多個(gè)session可以讀。
- 寫操作:如果存在一個(gè)session加鎖了,那么其他session寫操作都會(huì)阻塞,需要等待已上鎖的session提交commit操作。
語(yǔ)句:
select XXXXXXX for update;
# 自動(dòng):delete / update / insert 默認(rèn)加上排他鎖
# 手動(dòng):select * from table for update
實(shí)驗(yàn):
| session1 | session2 |
|---|---|
| set autocommit =0; | |
| select * from edu_user for update; | set autocommit =0; |
| 成功:select * from edu_user; | |
| 阻塞:select * from edu_user for update; | |
| update edu_user set password="456789" where username = "王五"; | |
| update edu_user set password = "13579" where username="王五"; | |
| 等待:1205 - Lock wait timeout exceeded; try restarting transaction | |
| commit; |
說明:
當(dāng)前事務(wù)添加了排他鎖后,其他事務(wù)想要添加鎖都將被阻塞。
寫操作 - 排他鎖(InnoDB悲觀鎖 )
delete / update / insert 默認(rèn)會(huì)自動(dòng)加上排他鎖。
update操作:
獲取需要更新的一條記錄的位置,使用排他鎖鎖定該記錄。
| session1 | session2 |
|---|---|
| set autocommit =0; | |
| update edu_user set username = "李保國(guó)" where id = '5045dfba5f5b4cb5b805c379fc123456'; | set autocommit =0; |
| 阻塞:update edu_user set username = "李大嘴" where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
| commit; 字段改變?yōu)槔畋?guó) | |
| 執(zhí)行:update edu_user set username = "李大嘴" where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
| commit; 字段改變?yōu)槔畲笞?/td> |
注意:
當(dāng)一個(gè)session在update操作過程中,其他session只能讀操作,只能讀到原來數(shù)據(jù),寫操作,會(huì)阻塞。
delete操作:
獲取刪除記錄的位置,然后使用排他鎖鎖定該記錄。
| session1 | session2 |
|---|---|
| set autocommit =0; | |
| delete from edu_user where id = '5045dfba5f5b4cb5b805c379fc123456'; | set autocommit =0; |
| 阻塞:delete from edu_user where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
| commit; 指定記錄刪除 | |
| 執(zhí)行:delete from edu_user where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
| commit; |
insert操作:
不需要加鎖,通過隱式鎖來保護(hù)事務(wù)全過程。
insert操作檢查:
- 情況1:如果記錄之間存在有間隙鎖,那么為了避免幻讀情況,是不能插入記錄。
- 情況2:如果插入記錄主鍵沖突,也不能插入記錄。
- 其他情況:插入隱式鎖。
| session1 | session2 |
|---|---|
| set autocommit =0; | |
| insert into edu_user(id, username, password, role_id) values('1234df4446cb4cb6bc2f639830b12345','張三','123456',3); | set autocommit =0; |
| 非阻塞:update edu_user set username= '孫中山' where id = '5045dfba5f5b4cb5b805c379fc538bcb'; 老記錄 | |
| 阻塞:update edu_user set username= '孫中山' where id = '1234df4446cb4cb6bc2f639830b12345'; | |
| commit; 新增一條張三記錄 | |
| commit; 更新記錄 |