八、并發控制
數據庫是一個共享資源,可以為多個應用程序所共享,這些程序可以串行執行(排隊執行),但是很多情況下,可能出現多個程序或一個程序中的多個進程并行的執行,這就是數據庫的并行操作(并發控制)。
在多用戶數據庫環境中,多個用戶可以并行的存取數據庫,如果不對并發操作進行控制,那么可能存取不正確的數據,或破壞數據庫數據的一致性。
1. 并行的例子:其中T代表事務
(1)數據庫并發過程中可能出現的問題
(2)并發過程中可能出現的問題的類型
1)臟讀(dirty read):一個事務讀取了另一個事務修改但是沒有提交事務的數據,如果稍后這個修改被回滾,那么這個事務讀取到的數據就是無效的臟數據。
2)不可重復讀(nonrepeated read):一個事務執行了兩次或兩次以上的查詢,但是每次查詢的結果都不一樣,原因通常是另一個事務在兩次查詢之間修改了數據。
3)幻讀(phantom read):當一個事務讀取幾行記錄后,另一個并發事務插入了一些記錄,這些插入的記錄滿足第一個事務讀取數據的條件,此時幻讀就發生了,即后面查詢的數據出現了原來沒有的額外的記錄。
2. 事務
事務是不可分割的原子單元,事務保證SQL語句要么全部執行,要么全部不執行,數據庫中的事務往往是并發的。Oracle數據庫默認情況下事務是自動提交的,這意味著每執行一條SQL語句,事務都會提交一次,可以改為手動提交:SET AUTOCOMMIT OFF,此時需要顯式執行COMMIT來提交事務,ROLLBACK來回滾事務,當然也可以SET AUTOCOMMIT ON恢復成自動提交事務。
(1)事務的特性(ACID)
1)原子性(A):事務將多個對數據庫的操作當成一個原子單元,要么全部執行成功,要門全部失敗。
2)一致性(C):系統的狀態要從一種一致性狀態轉到另一種一致性狀態,不能違反完整性約束,即兩次從同一個地方獲取的數據要一致,不能不一致。
3)隔離性(I):事務必須是相互隔離的,多個用戶操作同一個數據的時候,一個用戶的操作不能去影響其他用戶的操作。
4)持久性(D):已經提交的事務對數據庫的修改是永久的,事務一旦提交,再回滾是沒用的。
(2)事務的隔離級別:
1)序列化:Serializable,最嚴格的級別,事務串行執行(排隊執行),沒有并發問題,但是效率最低。
2)可重復讀,Repeatable read,保證事務不會修改由其他一個事務讀取但未提交的數據,可以避免臟讀和不可重復讀的并發問題,存在幻讀問題,效率一般。
3)讀已提交,Read Committed,是大多數數據庫默認的隔離級別,保證事務不會讀取另一個并行事務修改但未提交的數據,可以避免臟讀,存在不可重復讀和幻讀問題,效率比較高。
其中Oracle數據庫默認的隔離級別是讀已提交,MySQL數據庫默認的隔離級別是可重復讀。
(3)鎖機制
數據的不一致總是由兩個因素造成:一是對數據的修改,二是并發操作的發生,因此為了保證數據的一致性,必須對并發操作進行控制,最常見的方式就是加鎖,數據庫中常見的鎖有兩種:排他鎖(X)和共享鎖(S)
1)排他鎖(X)
如果事務T對數據D加上了X鎖,則其他事務都不能再對數據D添加任何類型的鎖,直到T釋放D上的X鎖(提交或回滾事務時)。一般要求在修改數據前向數據添加排他鎖,所以排他鎖也稱為寫鎖,一個資源,比如一張表只能加一個排他鎖,但是可以加多個共享鎖。
2)共享鎖(S)
如果事務T對數據D加上S鎖,則其他事務也只能對D加S鎖,不能加X鎖,直到T釋放D上的S鎖,一般要求讀取數據前向該數據加共享鎖,所以共享鎖也稱為讀鎖,所以共享鎖具有更高的并行性。
(4)Oracle數據庫將鎖分為以下幾類:
1)數據鎖:為了保護表,在多個用戶并行存取數據時保證數據的完整性,DML操作可以在兩個級別上獲取數據鎖:行鎖和表鎖,當某行要被修改的時候,事務會自動在該行加上排他鎖。
2)DDL鎖:DDL鎖保護模式對象結構的定義,DDL操作將影響對象,一個DDL語句會隱式的提交事務(會釋放鎖),比如創建表格時會提交事務并釋放鎖。
3)內部鎖:保護內部數據庫和內存結構
(5)數據庫中加鎖的兩種方式:獨占方式和共享方式
1)獨占方式:不允許其他用戶以任何方式共享鎖定的資源,當進行數據庫修改的時候就可以使用該模式,即加上排他鎖。
2)共享模式:允許在數據訪問時,并發的共享訪問,但是當修改數據庫時上升為獨占模式,指共享鎖。
(6)鎖分為行級鎖和表級鎖,行級鎖用于鎖定某些記錄,表級鎖用于鎖定整張表的所有記錄。
1)行級鎖
a.?? ?當執行INSERT、UPDATE、DELETE等DML操作時,會隱式的加上行鎖(排他鎖),這意味其他事務只能查詢這幾行的數據,但是不能修改這些行的數據,直到排他鎖被釋放。
b.?? ?顯式通過”SELECT......FOR UPDATE”來加行鎖,會將查詢出來的這幾行記錄加上排他鎖,其他事務也只能查詢這幾行,不能修改這幾行。
示例:行鎖
開啟一個sqlplus(一個會話),即可開啟了一個事務,將事務設置為手動提交
事務1:
此時,empno=7369的這一行就添加了行鎖(排他鎖),那么其他事務就只能查詢不能修改這一行的記錄,當然,其他非7369的行就沒有加鎖,其他事務就可以修改。
再開啟一個sqlplus,即開啟另一個事務:
事務2:
事務2去查詢empno=7369這一行沒有問題,但是不能去修改,試圖修改empno=7369的事務會被阻塞,直到事務1提交事務釋放鎖之后,事務2才能修改成功:
事務2被阻塞了,原因是事務1對empno=7369這一行加了排他鎖,其他事務就只能阻塞
然后事務1提交事務釋放鎖:
事務2就更新成功了:
注意:此時由于事務2執行了UPDATE語句,會隱式的給empno=7369這一行加上行鎖,事務1也只能查詢不能修改,修改的動作也會被阻塞,直到事務2提交事務釋放鎖才可以:
事務1:
事務1可以查詢,但是更新會被阻塞
然后事務2提交事務:
事務1更新成功:
注意:行鎖只對加鎖的這幾行數據有效,其他沒有加行鎖的數據其他事務還是可以修改的。
示例2:假如有其他用戶要鎖定同一個資源,可以使用wait子句對鎖等待的時間(秒級)進行控制,例如:SELECT * FROM emp WHERE empno=7369 FOR UPDATE WAIT 3; 表示等待3秒,如果發現其他事務還沒有釋放鎖,系統給出出錯提示。
我們來操作一下,先將兩個事務都COMMIT一次,保證兩個事務的鎖都被釋放。
事務1:對7369加行鎖
事務2:也試圖對7369加行鎖,但是由于事務1已經對7369加了行鎖,其他事務不能再對7369加任何類型的鎖,等到時間到了,系統給出提示:
2)表級鎖:鎖定整張表的所有記錄,分為三種模式:
??? ?共享模式:IN SHARE MODE
??? ?共享更新模式:IN SHARE UPDATE MODE
??? ?排他鎖模式:IN EXCLUSIVE MODE
??? ?表級鎖的語法:LOCK TABLE 表名 鎖模式;
a. 共享模式:不允許其他用戶插入、更新和刪除行,多個用戶可以同時在一個表上設置共享鎖,這樣大家都只能查詢,不能修改。
示例:先把事務1和事務2都COMMIT一次,全部釋放鎖
事務1:
事務2:可以查詢,但是不能修改數據,直到事務1提交事務釋放鎖之后才行
事務1提交事務釋放鎖:
事務2更新成功:
如果事務1和事務2都加上表級共享鎖,那么兩個事務都只能查詢,不能修改:
此時如果兩個事務都去修改數據,就會出現死鎖,因為兩個事務都在等待對方釋放鎖,就會陷入死鎖狀態,死鎖是我們要避免的一個問題。
b. 共享更新模式:允許多個用戶同時鎖定表的不同行,允許其他用戶執行DML操作,除了已鎖定的行。
示例:
LOCK TABLE emp IN SHARE UPDATE MODE;
SELECT * FROM emp WHERE deptno=30 FROM UPDATE; -- 鎖定特定行,其他用戶不能修改
SELECT * FROM emp WHERE deptno=30; -- 但是其他用戶可以查詢
示例:
事務1加了共享更新模式,對empno=8888加了行鎖,那么事務2可以對其他行進行更新,但是不能對empno=8888進行更新,除非事務1提交事務釋放鎖:
直到事務1提交事務釋放鎖:
c. 排他鎖模式
不允許其他用戶插入、更新和刪除行,允許查詢數據,如果一個事務對表加了排他鎖,其他事務不能再對這張表加任何類型的鎖,包括共享鎖。
示例1:還是兩邊都COMMIT一次
事務1:
事務2:也試圖給emp加排他鎖,會被阻塞
直到事務1提交或回滾事務釋放鎖之后,事務2加鎖才能成功:
目前事務2加上了排他鎖,事務1不能加任何類型的鎖,包括共享鎖:
事務1:
總結:一旦一個事務加了排他鎖,其他事務只能查詢,不能修改,例如:
兩邊都COMMIT一次:
事務1:
事務2:可以查詢,但是修改emp表的任何一條記錄都會被阻塞,直到事務1提交事務釋放鎖后才行:
事務1提交事務釋放鎖,事務2更新才能成功:
總結:共享鎖的并發性比排他鎖的好,對于排他鎖,行鎖的并發性比表鎖的并發性好。
九、其他重要模式對象
1.其他重要模式對象,主要包含:索引、序列、視圖
2.索引
(1)索引概述
1)索引(index)是為了提高數據庫查詢性能而建立的,利用它可以快速的確定指定的信息,索引就像書的目錄。
2)索引可以建立在表的一列或多列上,一旦建立,由Oracle數據庫自動維護和使用,但是當表中建立的索引過多的時候,修改、插入、刪除的性能會下降,因為修改數據的時候,Oracle會去維護索引,帶來額外的開銷,所以索引并不是越多越好。
3)索引分為唯一索引和非唯一索引,唯一索引保證表中的索引字段不能重復,Oracle自動在UNIQUE字段上創建唯一索引。
4)Oracle自動在PRIMARY KEY 字段上創建主鍵索引,所以建議創建表的時候,一定要給出主鍵約束,以便通過主鍵來查詢數據時利用主鍵索引提高查詢的速度。
5)當WHERE子句中包含索引字段時,就會使用索引來檢索數據,性能會大大增強,例如:SELECT * FROM emp WHERE empno=7369;
(2)創建索引的語法:
CREATE [UNIQUE] INDEX 索引名 ON 表名(索引列1,索引列2,...);
其中 UNIQUE 表示創建唯一索引,可以省略
示例:
(3)刪除索引
DROP INDEX 索引名;
3.序列
(1)序列概述
序列(SEQUENCE)用于產生序列號(流水號),在多用戶環境下使用序列特別有用,可以生成整數類型,最多可有38位的數字。
(2)創建序列的語法:
CREATE SEQUENCE 序列名 -- 創建序列
[START WITH 初始值] -- 序列的初始值,默認是1
[INCREMENT BY 遞增值] -- 每次遞增的值,默認是1
[MAXVALUE 最大值 | NOMAXVALUE] -- 能達到的最大值或沒有限制
[MINVALUE 最小值 | NOMINVALUE] -- 最小值或沒有限制
[CYCLE | NOCYCLE] -- 循環生成序列或不循環
[CACHE 緩沖數據個數 | NOCACHE] -- 保留在緩沖區中的序列值個數,緩沖區是為了提高讀取速度,默認20個
[ORDER | NOORDER] -- 按照順序生成序列值或不強調按照順序生成
其中[]里面內容可以省略,”|”表示或者的意思
(3)序列中的兩個偽列:currval和nextval,其中currval表示序列的當前值,nextval表示序列的下一個值
示例:
序列最大的用途是生成遞增的主鍵值,例如:
創建users表,插入5條記錄,使用序列生成遞增的主鍵值:
(4)刪除序列
DROP SEQUENCE 序列名;
4.視圖:一張虛擬的數據庫表
視圖是從一個或多個基本表或視圖中查詢出來的記錄形成的一張虛擬表,可以簡單認為:
視圖就是查詢表或視圖形成的一張虛擬表
視圖只存放視圖的定義,不會存放數據,視圖的數據任然在原來的數據庫表中。
(1)視圖的作用:
1)視圖能簡化用戶的操作
2)使用戶能夠以多種角度來看待同一個數據
3)對重構數據庫提供了一定程度的邏輯獨立性
4)能夠對機密數據提供安全保護,比如只公開部分字段
(2)創建視圖的語法
CREATE [OR REPLACE] VIEW 視圖名 AS SELECT語句;
其中:
OR REPLACE:如果視圖名已經存在,則替換,否則就創建新的視圖,可以省略
SELECT語句:查詢語句,可以是單表、多表或子查詢
注意:創建視圖必須具有CREATE VIEW或CREATE ANY VIEW的系統權限
示例:
示例2:
(3)刪除視圖
DROP VIEW 視圖名;