mysql中 for update 使用


解釋:

for update是在數據庫中上鎖用的,可以為數據庫中的行上一個排它鎖。當一個事務的操作未完成時候,其他事務可以讀取但是不能寫入或更新。
例子:

比如一張表三個字段 , id(商品id), name(商品名字) , count(數量)
當商品搶購時候會顯示剩余商品件數,如果並發量大的時候,商品自減的值可能不准確。所以當我們在一個事務中對count字段進行修改的時候,其他事務應該只能讀取指定id的count,而不能進行update等操作。這個時候就需要用到for update.
sql語句:

start transaction ;
select * from table_name where id =1 for update ;

update table_name set count = count - 1 where id= 1;
此時如果另一個事務也想執行類似的操作:

start transaction ;
select * from table_name where id =1 for update ;
//下面的這行sql會等待,直到上面的事務回滾或者commit才得到執行。
update table_name set count = count - 1 where id= 1;

*注:當選中某一個行的時候,如果是通過主鍵id選中的。那么這個時候是行級鎖。
其他的行還是可以直接insert 或者update的。如果是通過其他的方式選中行,或者選中的條件不明確包含主鍵。這個時候會鎖表。其他的事務對該表的任意一行記錄都無法進行插入或者更新操作。只能讀取。*
---------------------
作者:宇宙意志
來源:CSDN
原文:https://blog.csdn.net/qq_39632561/article/details/80061364
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

sql的for update

 

歡迎大家吐槽

oracle行級共享鎖

通常是通過select … from for update語句添加的,同時該方法也是我們用來手工鎖定某些記錄的主要方法。比如,當我們在查詢某些記錄的過程中,不希望其他用戶對查詢的記錄進行更新操作,則可以發出這樣的語句。當數據使用完畢以后,直接發出rollback命令將鎖定解除(如果我們查詢到了並且修改了,則commit后就可以釋放了鎖)。當表上添加了RS鎖定以后,不允許其他事務對相同的表添加排他鎖,但是允許其他的事務通過DML語句或lock命令鎖定相同表里的其他數據行。

select for update

示例:

select *from goods where goods_code ='9990300100000030001' for update ;

當select語句中使用了for update,如果當前select發現自己的結果集中有一條或者多條數據正在被修改(如有其他的語句提前for update了等),那么再執行此語句就會一直等待:

此時執行:

Update/delete等語句都出現等待。只有對應的占用鎖的數據被提交commi(或者rollback)t掉時才可以進行新的for update 上鎖或者進行update、delete。

select for update nowait

for update nowait 和for  update都會對查詢到的當前結果集進行加鎖。區別是是for update nowait 發現自己要加鎖的結果集正在被其他操作修改(已經被加鎖),則直接反饋資源占用,不會像for update 一樣等待執行。

這兩個操作等待和彈窗與被那種形式的加鎖sql無關,只要要處理的結果集被加鎖了直接反饋彈窗。(應該是廢話)

select for update wait

與for update nowait的區別是for update wait 可配置等待時間,單位秒。當過了這個時間,被加鎖資源還未被釋放,則和for update nowait一樣直接彈窗反饋資源占用。否則執行此語句。

跳過加鎖行,對滿足條件的其他行進行加鎖

步驟:

窗口1(每個窗口代表一個session)執行

select *from goods where goods_code ='9990300100000030001' for update  ;

窗口2中執行:

由於窗口2的sql集合中有一行被窗口1的集合加鎖了,導致窗口二等待鎖釋放。

窗口2 修改語句如下:

select *from goods for update skip locked ;

關聯表加鎖

1.關聯的表都加鎖鎖

復制代碼
select * from stat_dic_goods sg inner join goods g on sg.goods_code = g.goods_code where g.goods_code = 'qwe002n3' for update;
復制代碼

可以得出結論兩張表中goods_code='qwe002n3'的商品都被加鎖了,所以導致后免得語句對兩張整體加鎖時出現了等待的情況。

 

當在新開的窗口中對其他數據進行加鎖時加鎖成功:

2. 對關聯的表中的某一張表進行加鎖

 

select * from stat_dic_goods sg inner join goods g on sg.goods_code = g.goods_code where g.goods_code = 'qwe002n3' for update of sg.goods_code;

 

Oracle共享鎖

語法:lock table stat_dic_goods in share mode;

執行完lock語句再執行update語句就會出現等待中,因為lock語句已經將這個表鎖了,此時只允許讀取不允許修改。

通過lock table in share mode命令添加該S鎖。在該鎖定模式下,不允許任何用戶更新表。但是允許其他用戶發出select …from for update命令對表添加RS鎖。

 

 

Oracle只有表級別的共享鎖沒有行級別的共享鎖,行級別的是排他鎖,多個用戶可以同時對同一個表加共享鎖。通過下面的語句就可以看出來。

Oracle排他鎖

示例語句:lock table stat_dic_goods in exclusive mode;

同一張表不能同時執行共享鎖和排他鎖。

通過lock table in exclusive mode命令添加X鎖。在該鎖定模式下,其他用戶不能對表進行任何的DML和DDL操作,該表上只能進行查詢。

Oracle行級排他鎖

當我們進行DML時會自動在被更新的表上添加RX鎖,或者也可以通過執行lock命令顯式的在表上添加RX鎖。在該鎖定模式下,允許其他的事務通過DML語句修改相同表里的其他數據行,或通過lock命令對相同表添加RX鎖定,但是不允許其他事務對相同的表添加排他鎖(X鎖)。

參考了這里:http://www.cnblogs.com/hsz1124/p/7409981.html

找了相關資料理解的還不是懂

Oracle共享行級排他鎖

通過lock table in share row exclusive mode命令添加SRX鎖。該鎖定模式比行級排他鎖和共享鎖的級別都要高,這時不能對相同的表進行DML操作,也不能添加共享鎖。

找了相關資料理解的還不是懂

 

TM鎖,表級鎖;

TX(事務)鎖;

lock只會給表上鎖(TM鎖),select for update,insert,delete,update既給表上鎖(TM鎖),也給行上鎖(TX鎖)。select for update給表上行共享鎖,insert,delete,update給表上行排他鎖。

數據庫的隔離級別

回頭看看數據庫的隔離級別:

隔離級別就是對事務並發控制的等級。ANSI/ ISO SQL將其分為串行化(SERIALIZABLE)、可重復讀(REPEATABLE READ)、讀已提交(READ COMMITED)、讀未提交(READ UNCOMMITED)四個等級。為了實現隔離級別通常數據庫采用鎖(Lock)。一般在編程的時候只需要設置隔離等級,至於具體采用什么鎖則由數據庫來設置。

Oracle: Read Commited

MySQL : Repeatable Read

Oracle默認情況下,讀數據不加鎖,而是通過回滾段防止臟讀和保證可重復讀。

Oracle具有死鎖檢查功能,周期性檢查系統是否有死鎖,如果存在死鎖,則撤銷執行更新操作次數最少的事務。

 

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQLOracle不同,后者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味着:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!要是對應的SQL語句沒有走索引,則會鎖掉整張表。

 

Mysql

Mysql只有在InnoDB引擎下才支持行鎖和表鎖。Mysql的默認引擎就是innoDB。

MySQL默認操作模式就是autocommit自動提交模式(值為1)。這就表示除非顯式地開始一個事務,否則每個查詢都被當做一個單獨的事務自動執行。我們可以通過設置autocommit的值改變是否是自動提交autocommit模式。

可以手動設置:set autocommit = 0;則將自動提交關閉了。

示例:

悲觀鎖:

begin;

select * from goods where id = 1 for update;

update goods set stock = stock - 1 where id = 1;

commit;

 

樂觀鎖:(已改造)

#不加鎖獲取 id=1 的商品對象

select * from goods where id = 1

begin;

#更新 stock 值,這里需要注意 where 條件 “stock = cur_stock”,只有程序中獲取到的庫存量與數據庫中的庫存量相等才執行更新

update goods set stock = stock - 1 where id = 1 and stock = cur_stock;

commit;

程序中大多用的是樂觀鎖,悲觀鎖

 

與oracle差異:

Mysql支持:select * from orders for update wait 6 –語法錯誤

                  select * from orders for update nowait 6 ; –語法錯誤

          

 

還沒有總結完,后續補充

 

項目組用的jpa。謝謝jpa的配置吧

復制代碼
@Repository 

public interface JobInfoDao extends JpaRepository<JobInfo, Long> { @Query(value = "select j from JobInfo j where j.jobName = :jobname ") public JobInfo getJobForUpdate(@Param("jobname") String jobname); @Lock(value = LockModeType.PESSIMISTIC_WRITE) @Query(value = "select j from JobInfo j where j.id = :id ") public void getJobByIdForUpdate(@Param("id") Long id); @Lock(value = LockModeType.PESSIMISTIC_WRITE) // TODO 千萬不要用這個哦!  @Query(value = "select j from JobInfo j where j.jobName = :jobname ") public void getJobByNameForUpdate(@Param("jobname") String jobname); } 
復制代碼

 

.jpa如何搞 select for update。

也是醉了,在Dao層的方法上,要配置Lock的注解。並且要加上LockModeType.PESSIMISTIC_WRITE ,這個就相當於for update了。大家也可以在程序運行時,打印出的sql中看到。 

 


ALTER TABLE pay_bill
CHANGE COLUMN `created_time` `created_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;

ALTER TABLE  order
CHANGE COLUMN `created_time` `created_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;

ALTER TABLE refund
CHANGE COLUMN `created_time` `created_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM