国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > Oracle > 正文

Oracle數據庫數據鎖定機制全面解析

2024-08-29 13:33:27
字體:
來源:轉載
供稿:網友
為了得到最大的性能,一般數據庫都有并發機制,不過帶來的問題就是數據訪問的沖突。為了解決這個問題,大多數數據庫用的方法就是數據的鎖定。 數據的鎖定分為兩種方法,第一種叫做悲觀鎖,第二種叫做樂觀鎖。什么叫悲觀鎖呢,悲觀鎖顧名思義,就是對數據的沖突采取一種悲觀的態度,也就是說假設數據肯定會沖突,所以在數據開始讀取的時候就把數據鎖定住。而樂觀鎖就是認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,假如發現沖突了,則讓用戶返回錯誤的信息,讓用戶決定如何去做。 先從悲觀鎖開始說。在SQL Server等其余很多數據庫中,數據的鎖定通常采用頁級鎖的方式,也就是說對一張表內的數據是一種串行化的更新插入機制,在任何時間同一張表只會插1條數據,別的想插入的數據要等到這一條數據插完以后才能依次插入。帶來的后果就是性能的降低,在多用戶并發訪問的時候,當對一張表進行頻繁操作時,會發現響應效率很低,數據庫經常處于一種假死狀態。而Oracle用的是行級鎖,只是對想鎖定的數據才進行鎖定,其余的數據不相干,所以在對Oracle表中并發插數據的時候,基本上不會有任何影響。 Oracle的悲觀鎖需要利用一條現有的連接,分成兩種方式,從SQL語句的區別來看,就是一種是for update,一種是for update nowait的形式。比如我們看一個例子。首先建立測試用的數據庫表。
CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))AS SELECT deptno, dname, loc, 1 FROM scott.dept
這里我們利用了Oracle的Sample的scott用戶的表,把數據copy到我們的test表中。首先我們看一下for update鎖定方式。首先我們執行如下的select for update語句。select * from test where id = 10 for update。 通過這條檢索語句鎖定以后,再開另外一個sql*plus窗口進行操作,再把上面這條sql語句執行一便,你會發現sqlplus似乎死在那里了,似乎檢索不到數據的樣子,但是也不返回任何結果,就屬于卡在那里的感覺。這個時候是什么原因呢,就是一開始的第一個session中的select for update語句把數據鎖定住了。由于這里鎖定的機制是wait的狀態(只要不表示nowait那就是wait),所以第二個Session(也就是卡住的那個sql*plus)中當前這個檢索就處于等待狀態。當第一個session最后commit或者rollback之后,第二個session中的檢索結果就是自動跳出來,并且也把數據鎖定住。不過假如你第二個session中你的檢索語句如下所示。select * from test where id = 10。 也就是沒有for update這種鎖定數據的語句的話,就不會造成阻塞了。另外一種情況,就是當數據庫數據被鎖定的時候,也就是執行剛才for update那條sql以后,我們在另外一個session中執行for update nowait后又是什么樣呢。比如如下的sql語句。 由于這條語句中是制定采用nowait方式來進行檢索,所以當發現數據被別的session鎖定中的時候,就會迅速返回ORA-00054錯誤,內容是資源正忙, 但指定以 NOWAIT 方式獲取資源。所以在程序中我們可以采用nowait方式迅速判定當前數據是否被鎖定中,假如鎖定中的話,就要采取相應的業務措施進行處理。select * from test where id = 10 for update nowait。 那這里另外一個問題,就是當我們鎖定住數據的時候,我們對數據進行更新和刪除的話會是什么樣呢。比如同樣,我們讓第一個Session鎖定住id=10的那條數據,我們在第二個session中執行如下語句。update test set value=2 where id = 10。 這個時候我們發現update語句就似乎select for update語句一樣也停住卡在這里,當你第一個session放開鎖定以后update才能正常運行。當你update運行后,數據又被你update語句鎖定住了,這個時候只要你update后還沒有commit,別的session照樣不能對數據進行鎖定更新等等。 總之,Oracle中的悲觀鎖就是利用Oracle的Connection對數據進行鎖定。在Oracle中,用這種行級鎖帶來的性能損失是很小的,只是要注重程序邏輯,不要給你一不小心搞成死鎖了就好。而且由于數據的及時鎖定,在數據提交時候就不呼出現沖突,可以省去很多惱人的數據沖突處理。缺點就是你必須要始終有一條數據庫連接,就是說在整個鎖定到最后放開鎖的過程中,你的數據庫聯接要始終保持住。 與悲觀鎖相對的,我們有了樂觀鎖。樂觀鎖一開始也說了,就是一開始假設不會造成數據沖突,在最后提交的時候再進行數據沖突檢測。在樂觀鎖中,我們有5種常用的做法來實現。 第一種就是在數據取得的時候把整個數據都copy到應用中,在進行提交的時候比對當前數據庫中的數據和開始的時候更新前取得的數據。當發現兩個數據一模一樣以后,就表示沒有沖突可以提交,否則則是并發沖突,需要去用業務邏輯進行解決。 第二種樂觀鎖的做法就是采用版本戳,這個在Hibernate中得到了使用。采用版本戳的話,首先需要在你有樂觀鎖的數據庫table上建立一個新的column,比如為number型,當你數據每更新一次的時候,版本數就會往上增加1。比如同樣有2個session同樣對某條數據進行操作。兩者都取到當前的數據的版本號為1,當第一個session進行數據更新后,在提交的時候查看到當前數據的版本還為1,和自己一開始取到的版本相同。就正式提交,然后把版本號增加1,這個時候當前數據的版本為2。當第二個session也更新了數據提交的時候,發現數據庫中版本為2,和一開始這個session取到的版本號不一致,就知道別人更新過此條數據,這個時候再進行業務處理,比如整個Transaction都Rollback等等操作。在用版本戳的時候,可以在應用程序側使用版本戳的驗證,也可以在數據庫側采用Trigger(觸發器)來進行驗證。不過數據庫的Trigger的性能開銷還是比較的大,所以能在應用側進行驗證的話還是推薦不用Trigger。
第三種做法和第二種做法有點類似,就是也新增一個Table的Column,不過這次這個column是采用timestamp型,存儲數據最后更新的時間。在Oracle9i以后可以采用新的數據類型,也就是timestamp with time zone類型來做時間戳。這種Timestamp的數據精度在Oracle的時間類型中是最高的,精確到微秒(還沒與到納秒的級別),一般來說,加上數據庫處理時間和人的思考動作時間,微秒級別是非常非常夠了,其實只要精確到毫秒甚至秒都應該沒有什么問題。和剛才的版本戳類似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和自己更新前取到的時間戳進行對比,假如一致則OK,否則就是版本沖突。假如不想把代碼寫在程序中或者由于別的原因無法把代碼寫在現有的程序中,也可以把這個時間戳樂觀鎖邏輯寫在Trigger或者存儲過程中。 第四種做法就是采用散列算法。這種做法和第一種做法有點類似,就是把當前數據內容進行傳遞,最后在提交的時候進行比較。但是區別還是比較大的,第一種做法把整個更新前的數據都copy了下來進行傳遞,而這種做法只是把數據做了一個散列碼,所以傳遞的數據要小的多,網絡負荷也會因此小很多。這種采用散列算法的做法假如在Oracle側生成散列的話,有三種方法可以生成。第一種就是利用Oracle8i 8.1.5以后的OWA_OPT_LOCK.CHECKSUM函數來進行數據的散列生成比如下面的例子。 select owa_opt_lock.checksum (to_char(sysdate,'YYYYMMDDHHMISS') ) from dual 在這里我用這個OWA_OPT_LOCK.CHECKSUM函數對當前的系統時間進行了散列,用這個方法生成的出土的可能性是65536,因為生成的散列算法是一個16位置的值,所以值范圍也就是65536個值而已。 第二種散列算法采用Oracle8i 8.1.7以后提供的DBMS_OBFUSCATION_TOOLKIT.md5方法,不過這個方法不能直接在SQL語句里面使用,需要在存儲過程中調用。所以可以先寫一個共通的存儲過程的Function,然后利用這個function去進行加密。 第三種散列方法就是用Oracle 10g Release1種提供的DBMS_CRYPTO.HASH方法。這個方法可以計算一個SHA-1或者是MD5摘要,所以假如實際情況下使用Oracle 10g的數據庫,建議采用這個方法。不過和第二種類似,這個方法也是需要采用存儲過程才能夠編寫,不能直接應用在SQL語句中的。 最后一種做法就是采用Oracle 10g所帶有的ORA_ROWSCN函數來進行樂觀鎖。ORA_ROWSCN是根據系統最后更新時間來進行計算。這個ORA_ROWSCN在默認情況下是采用數據塊為單位的,也就是一個數據庫塊(block)上共享一個ORA_ROWSCN,當數據更新的時候,這個block快的ORA_ROWSCN就會自動更新。所以在默認情況下的話,有可能出現假沖突的情況。比如A,B,C,D四條數據都在一個block上,這個時候A數據更新了,ORA_ROWSCN也會更新,這個時候因為ABCD四條數據存儲在一個block上,所以BCD的ORA_ROWSCN也更新過了,其實BCD三條數據并沒有更新過,這個就造成了假更新的情況出現。見下面的SQL語句:select id, name, location, value, ora_rowscn from test。 檢索結果如下:
Oracle數據庫數據鎖定機制全面解析(圖一)
然后執行如下的更新語句:update test set value=2 where id = 20。 在數據commit過后,會發現ORA_ROWSCN發生了變化,如下所示:
Oracle數據庫數據鎖定機制全面解析(圖二)
對于這種情況還是可以有辦法解決的,就是利用Oracle 9i提供的ROWDEPENDENCIES建表要害字,這個要害字在Oracle9i中是為了增加行依靠性跟蹤特性的,支持推進復制。在Oracle10g中有可以用來做行級別的ORA_ROWSCN用了。用這個要害字建表以后,在每行會增加一個隱藏的COLUMN,所以每行會增加6個byte的開銷。建表語句如下所示:
CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))ROWDEPENDENCIESAS SELECT deptno, dname, loc, 1 FROM scott.dept
同樣再執行:update test set value=2 where id = 20。 語句后,我們再看一下commit過后檢索的結果:
Oracle數據庫數據鎖定機制全面解析(圖三)
可以看到這次只有id=20的數據的ORA_ROWSCN發生了更新。所以這個時候我們就可以利用ORA_ROWSCN作為樂觀鎖,用不著專門再增加一個額外的域了。而且這個域是Oracle負責維護的,開銷也不是很大,不過需要Oracle 10g才可以使用。 總之,介紹了Oracle的鎖定機制,究竟是悲觀鎖好還是樂觀鎖好,其實也是不一定的。
Oracle中悲觀鎖還是很不錯的,而且從開始的時候就把數據鎖定。免除了后面的很多沖突處理。不過悲觀鎖需要保持一個Oracle連接,在我們常見的B/S應用中,非凡是數據先取得,然后讓用戶再更新,再返回提交這種流程來說,悲觀鎖是不大可能的。首先是因為B/S應用中,一般是利用一個連接池,在兩次Http Request請求都是不同的數據庫Connection。而且也不能鎖定一個數據太長時間,否則人人都這么鎖定,應用很輕易進入死鎖狀態,這個時候就要采用樂觀鎖了。而在樂觀鎖中,雖然采用散列的效果不錯,網絡傳輸數據會減少很多,而且不用在數據庫中增加COLUMN,不過由于散列計算比較占用CPU,所以還是需要仔細考慮一下的。ORA_ROWSCN可以說是比較好的選擇,不過第一需要重新建表,第二需要在Oracle10g下才有這個特性,所以也不是到處都可以使用,當一個萬金油的。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 偏关县| 彭山县| 高清| 彭阳县| 桂阳县| 高尔夫| 萍乡市| 壶关县| 孝昌县| 岐山县| 湟源县| 盱眙县| 乳山市| 乐东| 顺平县| 沙雅县| 家居| 岳阳市| 尚志市| 邳州市| 乌鲁木齐县| 枞阳县| 青岛市| 辽源市| 易门县| 北宁市| 个旧市| 楚雄市| 通江县| 宜阳县| 南溪县| 邹城市| 香格里拉县| 新乡市| 广宁县| 淮北市| 新宁县| 资阳市| 五峰| 阿拉善左旗| 赣榆县|