Oracle之事务和锁
生活随笔
收集整理的這篇文章主要介紹了
Oracle之事务和锁
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
那我們現在講事務的東西,無論我們是什么數據庫,只要是關系型數據庫,都有ACID,那ACID原子性,一致性,隔離性,持久性分別的場合在這里說1. 首先原子性,一個事務必須是一個完整的操作,事務的各個部分的操作是不可分割的,原子的特性,要么執行,要么都不執行很簡單的場景就是銀行轉賬,一個減100,一個加100,現在好像都不用這種方式了,看到支付寶和余額寶轉賬的問題,比如A叫支付寶,然后B就是叫余額寶,他們之間要有一個轉賬的策略,其實支付寶肯定是一個系統,他們是異構的系統,編碼的時候可能不是一個時間點去開發的,比如舉個例子,支付寶是05年開發的,余額寶是07年開發的,如果異構的系統如果想做異構的分布式,這種分布式事務,就好像銀行轉賬一樣,一個是交通銀行,一個是其他的銀行,比如說招商銀行,可以這么理解,傳統的做法呢比如你要靠關系型數據庫古老的做法,采用二次提交事務,這種方式,這個我們以后講MQ的時候會說,消息中間件,消息中間件去解決這種分布式事務,其實事務要解決就一個問題,就是保證事務的一致性,其實分布式事務要考慮的就是一個問題,就是數據的一致性,你必須保證你這邊減了1千塊錢,那邊得有1千塊錢,如果沒有的話,你必須得回滾掉,現在是兩個系統之間,你應該怎么去做,這個事情以后講MQ的時候再說吧,就是關于原子性,如果是本地事務的話,肯定滿足原子性了,一個Spring切面,里面有很多的Service,讓他去怎么怎么樣,做一個DAO,讓他去保證事務的原子性2. 一致性呢是一個查詢的結果必須沖數據庫查詢的開始,就是你開始想數據庫發起一條SELECT的時候,從那一點開始記錄,讀不等待寫,寫不等待讀,這是我們非常關鍵的一個特性,就是一致性,在我們講并發編程的時候,比如我又一張表,這張表可能有很多數據,我用戶A9點這一刻,9點00分00秒的時候,去做了一條SELECT的查詢,到一張數據庫里面去查一張大表,100多萬條數據,查的數據在90多萬的時候,然后中間有一個client端,把這個改了,可能他查的時候需要10分鐘,10分鐘才能查完,這是9點開始查,9點10分把數據返回,然后我這邊9點05的時候,去把這個數據改了,這個數據原先可能是100,然后我直接執行update和commit,我把它改成200了,那么最終9點10分的時候,A用戶看到的一定是100,是這個老的值,就是沒改之前100的這個值,他一定看不到這個新的100的這個值,這個就體現了我們數據庫的一致性,當你9點這一刻這個時間點發起的SELECT語句,那么你看到的數據一定是9點這一點上所有的數據的變化,9點01分的你都看不到,這就是一致性讀的概念,或者還有一致性寫,然后繼續往下看吧,場景剛開始已經描述好了,ORACLE事務有一些undo的概念,這個以后再說吧,再往下看3. 隔離性無非就是兩個SESSION,比如我當前用了一個SCOTT,登陸了我們的用戶,我要操作SCOTT里面的一張表,比如我又有一個SCOTT2,他又是一個用戶,他也去登陸了ORACLE服務器,那很明顯現在是兩個SESSION,你無論說你在里面玩出什么來,SELECT,UPDATE,只要你不COMMIT的話,他們兩個之間是兩個SESSION,所以嚴格保持數據的隔離特性,只要你不COMMIT,你隨便改隨便刪,這都沒問題,一旦COMMIT了以后,他們兩個的數據就可見了,不COMMIT的話,你在你自己的SESSION中隨便去玩,這個就是隔離的特性,沒有任何影響4. 然后還有持久性,事務一旦被提交,數據庫就不可以丟失這個事務的結果,數據庫通過日志能夠保持事務持久的特性,然后場合就是說,事務提交是一個不可逆的操作,提交數據是由內存數據刷到磁盤上的,這個過程的快慢和性能有關,和你的機器的性能有關,那么ORACLE用的是redo,用redo日志來記錄到磁盤上的,事務一旦你commit以后,我一commit的瞬間,有一個redo先記錄懂啊我這個日志,然后記錄到日志以后,開始做insert,開始去做了,你commit的一瞬間,你的這個redo日志肯定記錄好了,哪怕你下一秒,下一刻,整個機器掛了,都斷點了都沒關系,ORACLE重啟再恢復的時候,找到之前你操作的數據,然后把沒干完的事給干完,這個可以去做到的,那這個就是事務的4個特性,咱們簡單的講一下,這個都很簡單吧,然后繼續往下看
這個事務的開始和結束,事務采用隱式的方式,然后起始于SESSION的第一條DML語句,就是我們執行UPDATE,DELETE的時候,執行的就是DML語句的,那就是直接開啟了事務了,你可以通過這個表去查看事務,當前是SCOTT,我們現在create一個table,不在原表上去操作,create table emp1 as select * from emp;然后select * from emp1;
剛才我們新建了一張表,叫emp1,emp1表里是有這么多數據的,我這邊還是用SCOTT去操作的,update emp1這個表,set這個sal吧,這個字段,現在等于這個888,where條件就是empno,等于這個7369,然后這個是在我自己的SESSION,update emp1 set sal = 888 where empno = 7369,我就這么去做了,現在我肯定沒有去敲COMMIT,rollback就回滾了,在我執行這種DML操作的時候,我用這個v$transaction去做,select * from v$transaction,咱們conn system/tiger,然后我去select * from v$transaction,應該是有事務的,v$transaction正常來講是能看到事務的,用戶不對,應該是用誰啊,scott沒有查$v的權限,我說的是什么事啊,剛才update語句一旦寫了以后,這個事務還是相當于開啟的,這個事務開啟了之后,你就看v$v$transaction,其實這張表里是有一條記錄的,有事務的記錄的,把dba的權限賦給SCOTT,用cmd能不能看到,就是用sysdba最大的用戶,sqlplus / as sysdba:select * from v$transaction,他這里面也不行啊,用一下這個,conn / as sysdba,用這個system登陸一下,select * from v$transaction,還是不行,先不管他了,是不是想告訴你一件事,可能是用戶不對,我重新再連一下,直接用Command Window,可能是我連的時候不是sysdba,select * from emp1看一看,update emp1 set sal = 888 where empno = 7369,然后這邊是事務已經開啟了,這個時候select * from v$transaction就ok了
我重新登陸了,默認是普通用戶,我們要選sys as dba,才能看到這個結果,也就是說用戶剛才不對了,這一塊就產生一個transaction了,就是這個事務了,這個事務有事務ID號,事務一直存活著,只要是你做了這件事情了,就會有一個事務,事務也就必然會產生一個鎖
這個鎖我們可以重置去查
這個鎖就是在這兒,他必然會產生表鎖和行鎖,TM和TX,只要產生事務,肯定會有這個鎖,這個一會再說,我先再把Command Window開起來,我再用conn as sysdba,選擇system,Connect as 不是普通的角色,而是SYSDBA,然后密碼,然后你選擇的數據庫實例
我應該這么連,這就OK了,然后我們再看那個語句,查看事務的語句,select * from v$transaction,事務還是一直存在的
因為我這塊沒有提交,如果我這塊commit,commit就是成功了唄
就是事務已經結束了,我們再看一下
這回這個事務就已經不存在了,那么也就是有一個問題就是,一旦我們執行DML操作,肯定會有事務,commit是提交,rollback是回滾,這個都很簡單,還有一些產生事務的,DDL語句的執行,DCL語句的執行和被提交,都會產生事務,然后用戶退出sqlplus,正常退出的時候是提交事務的,非正常退出的時候是回滾的,機器故障了是回滾的,shutdown immediate也是回滾的,這可能是相當于簡單的操作了,好長時間不用我都有點忘了,這是關于事務的問題,其實你只要記住一件事,就是執行DML語句的時候,必定會產生一個事務,產生事務了,一定會有一個鎖的概念,就是lock鎖,在這兒還是說啊,鎖大體上分很多種,大體上分兩大種一種是共享,一種是排他,共享鎖和排他鎖有什么區別呢1. 排它鎖就是獨占,就是占掉了誰也進不來,就好像是一個多線程的時候,一個人進到線程里執行了,前面加一個sycnhronized,其他的人都進不來了,排斥其他排斥和共享鎖,比如舉個例子,我現在有一把鎖,這把鎖的顏色是紅色的,這把鎖的顏色是綠色的,紅色就是排它鎖,綠色是共享鎖,當我現在執行一個語句的時候,上了這把鎖了,如果這把鎖是排他鎖的話,他就是排斥了其他的排他鎖和共享鎖,都是排斥的,如果共享鎖了呢,只是排斥其他的排它鎖,但是不排斥其他的共享鎖,其實這個概念跟咱們這個叫之前我們學線程的時候,就是Thread的時候有一個概念,讀寫共享鎖的,還記得吧,就是讀讀共享,寫寫互斥,就是我可能用ReentrantLock他有一個東西叫readLock,還有一個叫writeLock,這兩把鎖嗎,一把是讀鎖,一把是寫鎖,就是讀和讀是共享的,但是讀和寫肯定是互斥的然后寫和寫也是互斥的,其實對于ORACLE里面,也是一個通用的一個概念,就是排它鎖和共享鎖,共享鎖就是讀鎖,排它鎖就是寫鎖,可以簡單的這么理解一下,然后鎖的類型大體上分很多種,比如TX行級鎖,TM表級鎖,我們一般來講,日常所用的DML操作,都會產生事務和鎖,有事務必有鎖,查看事務的語句,select * from v$transaction,查看鎖的語句,select * from v$lock,然后還有一些DDL鎖,就是dictionary locks,就是數據字典鎖,他用于保護數據對象的結構,比如表,索引,等等這些結構的,比如SYSTEM鎖,鎖的用途,只有有事務才會產生鎖,保證數據的完整性,一致性,還有以及正確性,鎖的作用是做這個事情的,其實你可以和JAVA里的鎖做對比一下,然后鎖里面有很多種
行共享,行排他,還有什么共享鎖,還有什么共享排他鎖,還有排他鎖,很多啊,就是這里面分這么多種,RS,RX,S,SRX,X如果你要做ORACLE DBA的話,沒有這個東西,在OCA的時候,最基礎入門的時候,這些都得要講的,而且你都得要會的,就是對于鎖的類型,每個鎖是干什么用的,就是簡單知道這個事就行了,我不會說講太多,然后這里還有一個加鎖的模式
然后這里還有一個加鎖的模式,可能會用到吧,咱們可以使用select * from 表,where條件加上for update,去給這個東西上鎖,select * from emp1 where deptno = 10 for update;去給你的SQL去上一把鎖,這個語句自動加鎖,在做DML的時候,這些語句都會自動加鎖,剛才其實咱們都看到了,你只要是執行一個DML語句的,在事務里面咱們看到一個記錄,在鎖里面咱們也看到一個TX和TM,一個行鎖,一個表鎖,但是在SELECT的時候,其實也是可以去加鎖的,就是你去加一個for update做這個事情,其實別人就不能夠碰了,也就是整個意思,另外我做一個這樣的事情,當前這個用戶是SCOTT,這個用戶是DBA,咱們拿DBA去觀察數據信息,拿SCOTT去做操作,當前我肯定是emp1這張表,我一查詢的時候
你會發現當前這個事務已經開啟了,for update,修改其部門為10的記錄會被鎖定,就是你已經加了一把鎖了,你還想去修改部門為10的記錄,那就會被鎖定,我們可以嘗試的去修改記錄,是否被加鎖,你可以去做一些試探,可以有這三種方式做試探,舉個例子吧,其實有的時候你使用ORACLE的時候,你總會覺得什么死鎖啊,其實在ORACLE里面是不存在死鎖的問題,在ORACLE里面如果真的發生死鎖的話,ORACLE會給你解決的,其實以前工作的時候也總是發現什么啊,這邊就是這個表,就是誰也進不來了,就是鎖上了,然后你就發現你的SESSION就卡到這兒了,然后初級的程序員總覺得是怎么,是不是咱兩操作產生沖突了,是不是產生死鎖了,其實在ORACLE里面是不存在死鎖的概念的,那為什么會有這種情況呢,一定是人家做了一個吧第10號部門的這幾條記錄都給我進行for update了,就是我要更新,鎖已經占著了,然后其他那個人的客戶端想改10號部門的其中一條記錄,然后他就發現那個SESSION就一直卡到那兒,就一直等著了,因為這個哥們for update還沒有做完事,然后另外那個人只能在那里等著了,等你這邊把事務提交了,你做完這個操作了,然后他才能真正的去修改,所以這個等待的過程就相當于什么啊,就是那個排隊的過程,沒有死鎖的概念啊,咱們做一下測試你就知道了,那剛才我還是要查這個system
我們發現這里面有一個TM和TX了,比如我提交
咱們再次看一下
看一下我這個操作,就是select * from v$lock;
最后幾塊,是沒有任何TX,TM字樣的,TX表示行鎖,TM表示表鎖,是沒有的,如果我剛才要執行這個語句的時候,for update的時候
你會發現我在看一次
它會產生后面兩個東西,一個是TM,一個是TX,會產生這兩個東西,那既然產生這個東西了,那我還有個哥們,我們再來一個Command Window,也是SCOTT,他現在就像做這個事情,我現在想做update,update emp1表,set sal,咱們還是還原回來,也還原不回來了,等于10000,4個0,update emp1 set sal=10000,where條件就是7782,就是empno=7782,update emp1 set sal = 10000 where empno = 7782,回車
去執行ORACLE的時候,有的人這塊怎么就卡死了呢
是不是我死鎖了啊,或者怎么怎么樣,其實不是,因為你這個SQL語句,在那邊排隊等著呢,你必須等這哥們for update完事之后,你才能訪問
我這里一個commit
緊接著他就row updated,一條記錄進行update了,其實就是這個過程,其實為了避免鎖一直占用,其實我們寫SQL的時候,尤其你用ORACLE的時候,用一些手段避免這個事,本來你想去數據庫做一條查詢,然后查詢以后,那邊的人,包括銀行的一些業務的時候,他肯定是要鎖表的,for updated,執行了這么一個操作之后,for update,下面一個client端,他想操作同一條記錄的時候,他只能在外面等著,那就意味著什么啊,可能用戶的頁面要點更新,點修改,這個操作的時候,頁面可能有按鈕,點的時候就一直卡到這兒了,卡死到這兒了,那這個界面肯定是不行的,原因不是咱們程序的事情,其實核心的原因是因為那邊for update了,這邊只能在這邊等著,其實是由于這個事,可以解決的方案其實有很多,有什么方案呢,可以去觸碰他一下
簡單的說怎么去觸碰,咱們還是以這條語句為例,以后你是做一些比較嚴謹的業務的時候,更新的時候是不允許其他的人去修改,也就是避免并發,我一定要在后面加for update,我數據是安全的,把這三條記錄鎖住
如果其他的client端非得要訪問這三條,那你可以這么去做,怎么去做呢,試探他一下,試探的方式寫了三種,第一種叫做不等待,三面這三條記錄已經被我鎖住了,7782這條記錄已經被我鎖住了,我就select * from emp1 where empno = 10 for update nowait;這一塊我是可以這樣去做的,就是不等待
還有一個是等待幾秒,還有一個是跳過已經鎖定的東西了,我可以做這個事情,應該不是在這兒,就是我再去查他的時候,我再次觸碰去查的時候,他告訴我,not wait,什么意思,就是不等待嗎,我再去查的時候,那個哥們已經把這三條記錄占著呢那怎么辦,其他人想操作查這個數據的時候,他想查的時候是不等待,那這樣的話,那個客戶端,一進來,進來的時候,他馬上就能返回去,告訴你這個用戶說,對不起系統在忙,請稍后再試,可以用這種策略,還有什么啊
還有這種,讓他等幾秒,最大你可以容忍幾秒,wait 5秒鐘,你可以數吧,5秒鐘之后,肯定也會出現這個錯誤就是wait超時,你可以指定一個時間
還有一種是跳過,我看一下,這個語法你看一下,首先select * from 條件,現在條件變了,變成什么了,變成job等于銷售,CLEAR銷售這個職位,然后for update 然后skip locked,就是跳過被鎖定的記錄,select * from emp1 where job='CLEAR'for update skip locked,跳過了MILLER這條記錄了,7934的這條記錄,因為7934的這個job就是這個,他在我的一個用戶已經update了,正常來講我能夠查到4條記錄,因為有4條記錄是CLEAR這個職位的,因為已經有個用戶已經占著這條記錄了,所以說你可以去做這件事情,skip locked,就是跳過的記錄就不查出來,要查沒被鎖的記錄,其實在操作的時候呢,有一種概念就是,叫做表霸,就是DBA一上來就把所有的表給霸占了,其他人誰也別想玩了,我一上來就select * from 表 for update,誰也別想玩我里面的數據反正我們的那個DBA一上來就做這個事,這都是一些簡單的特性吧,反正你在做一些銀行核心業務的時候,都可能會用到for update 這些參數,為了嚴謹
然后這塊后面還可以做什么事啊,如果這鎖占用的時間太長了,你如果可以做什么事,你有管理員用戶的話,你可以把那個表霸for update那個進程給他干掉,SESSION就給他kill掉,可以去把它的SESSION殺掉,你能不能別玩了,你都占用一個多小時了,我可以把它kill掉,那這個kill也是很簡單的,就是利用這個語句,可以去kill掉select sid,serial# from v$session where sid = 170,alter system kill session 'sid,serial',做一下測試吧,比如說,還是他,咱們還是要做for update
那現在肯定是有一個SESSION產生了,有一個事務產生了,然后我現在就像把這個kill掉怎么辦,那我就利用管理員用戶,我去做這個事情,首先我要查這個,我要select,首先我要看哪個sid占用的時間太長了,要去看v$lock這個表,到這個表里面去找到這個sid,然后執行select sid,serial# from v$session where sid = 100這個SQL語句,然后再把serial這個東西查出來,然后再kill掉,這個挺麻煩的,咱們來做一下操作吧,首先是v$lock這張表里面,這個哥們肯定是一直占著這幾條數據的,我如果用管理員登陸的話
我就看到了這141這個,這個SID,它是一直鎖著表的,它是一直占著SESSION的,這個是SID,這141這個哥們我就想把它刪掉,那怎么辦,就這樣去做,首先就是select sid, serial# from v$session where sid = 141,首先我要做的事情是查詢v$session這個sid,sid是141唄,查詢出來
找到這兩條記錄,一個叫做SID,一個叫SERIAL#,然后我去執行系統的alter權限,我去做什么啊,我去把這個kill掉,sid是141,然后這個serial是40,然后我一回車,altered
然后你會發現,其實這個時候這個用戶的SESSION就已經掉了,比如我commit一下,其實就已經不好用了,你的connection就已經lost,就是已經是掉線的狀態了,因為我已經用系統管理員已經把你當前的SESSION給kill掉了,所以你會發現他是這種情況的,只要把這個SESSION給kill掉了,其他的用戶都可以去訪問了,都是可以正常的去訪問了,這個就是你在工作中可能會碰到這種問題,有的時候你必須得用for update,下面顯示commit completed,其實這塊已經掉線了,我操作的時候他已經掉線了,你放心吧,我用管理員去kill掉的時候,已經是掉線了,因為已經把SESSION強殺掉了,kill掉了
?
總結
以上是生活随笔為你收集整理的Oracle之事务和锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle之用户操作
- 下一篇: Oracle之索引和索引碎片问题解决