咱們經常使用的關係型數據庫是MySQL,操做數據庫的語言通常爲SQL語句,SQL在執行的時候須要要先編譯,而後執行,而存儲過程(Stored Procedure)是一組爲了完成某種特定功能的SQL語句集,經編譯後存儲在數據庫中,用戶經過指定存儲過程的名字並給定參數(若是該存儲過程帶有參數)來調用執行它。html
一個存儲過程是一個可編程的函數,它在數據庫中建立並保存。它能夠有SQL語句和一些特殊的控制結構組成。當但願在不一樣的應用程序或平臺上執行相同的函數,或者封裝特定功能時,存儲過程是很是有用的。數據庫中的存儲過程能夠看作是對面向對象方法的模擬,它容許控制數據的訪問方式。java
1)存儲過程是一個預編譯的代碼塊,執行效率比較高;mysql
2)一個存儲過程替代大量T_SQL語句 ,能夠下降網絡通訊量,提升通訊速率,即只須要傳存儲過程的名字和參數,不用傳SQL語句;算法
3)能夠必定程度上確保數據安全,存儲過程可被做爲一種安全機制來充分利用:系統管理員經過執行某一存儲過程的權限進行限制,可以實現對相應的數據的訪問權限的限制,避免了非受權用戶對數據的訪問,保證了數據的安全。sql
舉例:數據庫
-------------建立名爲GetUserAccount的存儲過程---------------- create Procedure GetUserAccount as select * from UserAccount go -------------執行上面的存儲過程---------------- exec GetUserAccount
結果:至關於運行 select * from UserAccount 這行代碼,結果爲整個表的數據。編程
更多使用語法實例:http://www.cnblogs.com/knowledgesea/archive/2013/01/02/2841588.html安全
索引(Index)是幫助MySQL高效獲取數據的數據結構;在數據以外,數據庫系統還維護着知足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,能夠在這些數據結構上實現高級查找算法,提升查詢速度,這種數據結構,就是索引。網絡
實現原理:https://www.cnblogs.com/xdyixia/p/9368691.html 數據結構
索引是在MySQL的存儲引擎層中實現的,而不是在服務層實現的。因此各類存儲引擎支持的索引並不相同,MySQL目前提供瞭如下4種索引。
B-Tree 索引:最多見的索引類型,大部分引擎都支持B樹索引。
HASH 索引:只有Memory引擎支持,使用場景簡單。
R-Tree 索引(空間索引):空間索引是MyISAM的一種特殊索引類型,主要用於地理空間數據類型。
Full-text (全文索引):全文索引也是MyISAM的一種特殊索引類型,主要用於全文索引,InnoDB從MySQL5.6版本提供對全文索引的支持。
普通索引
這是最基本的索引類型,並且它沒有惟一性之類的限制,能夠經過如下幾種方式建立:
(1)建立索引: CREATE INDEX 索引名 ON 表名(列名1,列名2,…);
(2)修改表: ALTER TABLE 表名 ADD INDEX 索引名 (列名1,列名2,…);
(3)建立表時指定索引:CREATE TABLE 表名 ( […], INDEX 索引名 (列名1,列名 2,…) );
UNIQUE索引
表示惟一的,不容許重複的索引,若某一字段的信息不能重複(例如身份證號),能夠將該字段的索引設置爲unique:
(1)建立索引:CREATE UNIQUE INDEX 索引名 ON 表名(列名1,列名2,…);
(2)修改表:ALTER TABLE 表名ADD UNIQUE 索引名 (列名1,列名2,…);
(3)建立表時指定索引:CREATE TABLE 表名( […], UNIQUE 索引名 (列名1,列名2,…));
主鍵:PRIMARY KEY索引
主鍵是一種惟一性索引,但它必須指定爲「PRIMARY KEY」。能夠將其理解爲 索引名固定爲 PRIMARY KEY 的 UNIQUE索引。
(1)主鍵通常在建立表的時候指定:「CREATE TABLE 表名( […], PRIMARY KEY (列的列表) ); 」。
(2)可是,咱們也能夠經過修改表的方式加入主鍵:「ALTER TABLE 表名 ADD PRIMARY KEY (列的列表); 」。
每一個表只能有一個主鍵。 (主鍵至關於聚合索引,是查找最快的索引)
注:不能用CREATE INDEX語句建立PRIMARY KEY索引
設置索引
在執行CREATE TABLE語句時能夠建立索引,也能夠單獨用CREATE INDEX或ALTER TABLE來爲數據表增長索引。
1.ALTER TABLE - ALTER TABLE能夠用來建立普通索引、UNIQUE索引或PRIMARY KEY索引。
ALTER TABLE table_name ADD INDEX index_name (column_list) ALTER TABLE table_name ADD UNIQUE index_name (column_list) ALTER TABLE table_name ADD PRIMARY KEY (column_list)
2.CREATE INDEX - CREATE INDEX可對錶增長普通索引或UNIQUE索引。
CREATE INDEX index_name ON table_name (column_list) CREATE UNIQUE INDEX index_name ON table_name (column_list)
刪除索引
可利用ALTER TABLE或DROP INDEX語句來刪除索引。相似於CREATE INDEX語句,DROP INDEX能夠在ALTER TABLE內部做爲一條語句處理,語法以下。
DROP INDEX index_name ON talbe_name ALTER TABLE table_name DROP INDEX index_name ALTER TABLE table_name DROP PRIMARY KEY
其中,前兩條語句是等價的,刪除掉table_name中名爲index_name的索引。
第3條語句只在刪除PRIMARY KEY索引時使用,由於一個表只可能有一個PRIMARY KEY索引,所以不須要指定索引名。若是沒有建立PRIMARY KEY索引,但表具備一個或多個UNIQUE索引,則MySQL將刪除第一個UNIQUE索引。
若是從表中刪除了某列,則索引會受到影響。對於多列組合的索引,若是刪除其中的某列,則該列也會從索引中刪除。若是刪除組成索引的全部列,則整個索引將被刪除。
查看索引
mysql> show index from tblname;
SELECT count(DISTINCT(column_name))/count(*) AS Selectivity FROM table_name;
索引是有代價的:索引文件自己要消耗存儲空間,同時索引會加劇插入、刪除和修改記錄時的負擔,另外,MySQL在運行時也要消耗資源維護索引,所以索引並非越多越好。
如上圖,是一顆b+樹,淺藍色的塊咱們稱之爲一個磁盤塊,能夠看到每一個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數據項17和35,包含指針P一、P二、P3,P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。真實的數據存在於葉子節點即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。非葉子節點不存儲真實的數據,只存儲指引搜索方向的數據項,如1七、35並不真實存在於數據表中。
如圖所示,若是要查找數據項29,那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找肯定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間由於很是短(相比磁盤的IO)能夠忽略不計,經過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,經過指針加載磁盤塊8到內存,發生第三次IO,同時內存中作二分查找找到29,結束查詢,總計三次IO。真實的狀況是,3層的b+樹能夠表示上百萬的數據,若是上百萬的數據查找只須要三次IO,性能提升將是巨大的,若是沒有索引,每一個數據項都要發生一次IO,那麼總共須要百萬次的IO,顯然成本很是很是高。
1.經過上面的分析,咱們知道IO次數取決於b+樹的高度h,假設當前數據表的數據量爲N,每一個磁盤塊的數據項的數量是m,則有h=㏒(m+1)N,當數據量N必定的狀況下,m越大,h越小;而m = 磁盤塊的大小 / 數據項的大小,磁盤塊的大小也就是一個數據頁的大小,是固定的,若是數據項佔的空間越小,數據項的數量越多,樹的高度越低。這就是爲何每一個數據項,即索引字段要儘可能的小,好比int佔4字節,要比bigint8字節少一半。這也是爲何b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度降低,致使樹增高。當數據項等於1時將會退化成線性表。
2.當b+樹的數據項是複合的數據結構的時候,好比(name,age,sex),b+樹是按照從左到右的順序來創建搜索樹的,好比當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來肯定下一步的所搜方向,若是name相同再依次比較age和sex,最後獲得檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪一個節點,由於創建搜索樹的時候name就是第一個比較因子,必需要先根據name來搜索才能知道下一步去哪裏查詢。好比當(張三,F)這樣的數據來檢索時,b+樹能夠用name來指定搜索方向,但下一個字段age的缺失,因此只能把名字等於張三的數據都找到,而後再匹配性別是F的數據了, 這個是很是重要的性質,即索引的最左匹配特性。
事務(Transaction)是併發控制的基本單位。所謂的事務,它是一個操做序列,由一條或者多條sql語句組成,這些操做要麼都執行,要麼都不執行,它是一個不可分割的工做單位。
事務應該具備4個屬性:原子性、一致性、隔離性、持久性。
原子性(Atomicity):指整個數據庫事務是不可分割的工做單位。只有事務中全部的數據庫操做都執行成功,整個事務的執行纔算成功。事務中任何一個sql語句執行失敗,那麼已經執行成功的sql語句也必須撤銷,數據庫狀態應該退回到執行事務前的狀態。
一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變爲另外一個一致狀態。一致狀態的含義是數據庫中的數據應知足完整性約束,也就是說在事務開始以前和事務結束之後,數據庫的完整性約束沒有被破壞 。
隔離性(Isolation):隔離性也叫作併發控制、可串行化或者鎖。事務的隔離性要求每一個讀寫事務的對象與其它事務的操做對象能相互分離,即該事務提交前對其它事務都不可見,這一般使用鎖來實現多個事務併發執行時,一個事務的執行不該影響其餘事務的執行。
持久性(Durability):表示事務一旦提交了,其結果就是永久性的,也就是數據就已經寫入到數據庫了,若是發生了宕機等事故,數據庫也能將數據恢復。
事務分爲一下5類:
1)扁平事務
扁平事務是最簡單的一種,也是實際開發中使用的最多的一種事務。在這種事務中,全部操做都處於同一層次,最多見的方式以下:
BEGIN WORK Operation 1 Operation 2 Operation 3 ... Operation N COMMIT WORK
或者
BEGIN WORK Operation 1 Operation 2 Operation 3 ... Operation N (Error Occured) ROLLBACK WORK
扁平事務很簡單,但有一個主要缺點是不能提交或回滾事務的某一部分,或者分幾個獨立的步驟去提交。好比有這樣的一個例子,我從呼和浩特去深圳,爲了便宜,我可能這麼幹:
BEGIN WORK Operation1:呼和浩特---火車--->北京 Operation2:北京---飛機--->深圳 ROLLBACK WORK
可是,若是在Operation1中,從呼和浩特到北京的火車晚點了,錯過了飛往深圳的航班,怎麼辦?
由於扁平事務的特性,那我就須要回滾,我須要再回到呼和浩特,這樣作的成本過高,因此就有了下面的第二種事務——帶有保存點的扁平事務。
2)帶有保存點的扁平事務
這種事務除了支持扁平事務支持的操做外,容許在事務執行過程當中回滾到同一事務中較早的一個狀態,這是由於可能某些事務在執行過程當中出現的錯誤並不會對全部的操做都無效,放棄整個事務不合乎要求,開銷也太大。保存點用來通知系統應該記住事務當前的狀態,以便之後發生錯誤時,事務能回到該狀態。
3)鏈事務
鏈事務,就是指回滾時,只能恢復到最近一個保存點;而帶有保存點的扁平事務則能夠回滾到任意正確的保存點。
4)嵌套事務
經過下面實例來講明什麼叫嵌套事務
BEGIN WORK SubTransaction1: BEGIN WORK SubOperationX COMMIT WORK SubTransaction2: BEGIN WORK SubOperationY COMMIT WORK ... SubTransactionN: BEGIN WORK SubOperationN COMMIT WORK COMMIT WORK
這就是嵌套事務,在事務中再嵌套事務,位於根節點的事務稱爲頂層事務。事務的前驅稱爲父事務,事務的下一層稱爲子事務。
子事務既能夠提交也能夠回滾,可是它的提交操做並不立刻生效,除非由其父事務提交。所以就能夠肯定,任何子事務都在頂層事務提交後才真正的被提交了。同理,任意一個事務的回滾都會引發它的全部子事務一同回滾。
5)分佈式事務
分佈式事務一般是指在一個分佈式環境下運行的扁平事務,所以須要根據數據所在位置訪問網絡中的不一樣節點,好比:經過建設銀行向招商銀行轉帳,建設銀行和招商銀行確定用的不是同一個數據庫,同時兩者的數據庫也不在一個網絡節點上,那麼當用戶跨行轉帳,就是經過分佈式事務來保證數據的ACID的。
在MySQL命令行的默認設置下,事務都是自動提交的,即執行SQL語句後就會立刻執行COMMIT操做。所以要顯示地開啓一個事務須使用命令BEGIN或START TRANSACTION,或者執行命令SET AUTOCOMMIT=0,用來禁止使用當前會話的自動提交。
來看看咱們可使用哪些事務控制語句。
更新丟失:當有兩個併發執行的事務,更新同一行數據,那麼有可能一個事務會把另外一個事務的更新覆蓋掉。當數據庫沒加任何鎖操做的狀況下會發生。
髒讀:一個事務讀取到了另一個事務沒有提交的數據;
好比:事務T1更新了一行記錄的內容,可是並無提交所作的修改。事務T2讀取到了T1更新後的行,而後T1執行回滾操做,取消了剛纔所作的修改。如今T2所讀取的行就無效了;
不可重複讀:在同一事務中,屢次讀同一數據,讀到的數據不一樣(該數據被另外一個已經提交的事務修改);
好比:事務T1讀取一行記錄,緊接着事務T2修改(update)了T1剛纔讀取的那一行記錄。而後T1又再次讀取這行記錄,發現與剛纔讀取的結果不一樣。這就稱爲「不可重複」讀,由於T1原來讀取的那行記錄已經發生了變化;
幻讀:同一事務中,根據相同的查詢條件查詢,從新執行查詢時,返回的記錄與前一次查詢記錄不一樣;
好比:事務T1讀取一條指定的WHERE子句所返回的結果集。而後事務T2新插入或刪除(insert,delete)一行記錄,這行記錄剛好能夠知足T1所使用的查詢條件中的WHERE子句的條件。而後T1又使用相同的查詢再次對錶進行檢索,可是此時卻看到了事務T2剛纔插入的新行。這個新行就稱爲「幻像」,由於對T1來講這一行就像忽然出現的同樣。
在數據庫操做中,爲了有效保證併發讀取數據的正確性,提出的事務隔離級別。 InnoDB存儲引擎提供的事務的隔離級別有4個,由低到高依次爲Read uncommitted 、Read committed 、Repeatable 、read 、Serializable ,這四個級別能夠逐個解決髒讀 、不可重複讀 、幻讀 這幾類問題。
1. Read uncommitted 讀未提交
在該級別下,一個事務對一行數據修改的過程當中,不容許另外一個事務對該行數據進行修改,但容許另外一個事務對該行數據讀。所以本級別下,不會出現更新丟失,但會出現髒讀、不可重複讀、幻讀。
2. Read committed 讀提交
在該級別下,未提交的寫事務不容許其餘事務訪問該行,所以不會出現髒讀;可是讀取數據的事務容許其餘事務的訪問該行數據,所以會出現不可重複讀的狀況。
3. Repeatable read 重複讀
在該級別下,讀事務禁止寫事務,但容許讀事務,所以不會出現同一事務兩次讀到不一樣的數據的狀況(不可重複讀),且寫事務禁止其餘一切事務。
4. Serializable 序列化
該級別要求全部事務都必須串行執行,所以能避免一切因併發引發的問題,但效率很低。
隔離級別越低,事務請求的鎖越少或保持鎖的時間就越短。InnoDB存儲引擎默認的支持隔離級別是REPEATABLE READ;在這種默認的事務隔離級別下已經能徹底保證事務的隔離性要求,即達到SQL標準的SERIALIZABLE級別隔離。固然咱們也能夠在應用程序中再進行加鎖控制。
開發中必備:
JDBC中設置事務級別,即java中的事務級別,能夠看到第4級別就對應repeatable read;
Connection.setTransactionIsolation(int level);咱們用masql數據庫通常直接用mysql默認隔離級別就行,不用在JDBC中再設
視圖是一種虛擬的表,具備和物理表相同的功能,能夠對視圖進行增,改,查操做,視圖一般是有一個表或者多個表的行或列的子集,對視圖的修改不影響基本表,它使得咱們獲取數據更容易,相比多表查詢。
超鍵:在關係中能惟一標識元組(數據庫中的一條記錄)的屬性集稱爲關係模式的超鍵。一個屬性能夠爲做爲一個超鍵,多個屬性組合在一塊兒也能夠做爲一個超鍵。超鍵包含候選鍵和主鍵。
候選鍵:是最小超鍵,即沒有冗餘元素的超鍵。
主鍵:數據庫表中對儲存數據對象予以惟一和完整標識的數據列或屬性的組合,用戶選做元組標識的一個侯選鍵稱爲主鍵。一個數據列只能有一個主鍵,且主鍵的取值不能缺失,即不能爲空值(Null)。
外鍵:在一個表中存在的另外一個表的主鍵稱此表的外鍵,外鍵主要是用來描述兩個表的關係。
第一範式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。
第二範式(2NF):數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的狀況),也即全部非關鍵字段都徹底依賴於任意一組候選關鍵字。
第三範式(3NF):在第二範式的基礎上,數據表中若是不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三範式。所謂傳遞函數依賴,指的是如 果存在」A → B → C」的決定關係,則C傳遞函數依賴於A。所以,知足第三範式的數據庫表應該不存在以下依賴關係: 關鍵字段 → 非關鍵字段 x → 非關鍵字段y。
E-R圖也稱實體-聯繫圖(Entity Relationship Diagram),提供了表示實體類型、屬性和聯繫的方法,用來描述現實世界的概念模型。
E-R方法是「實體-聯繫方法」(Entity-Relationship Approach)的簡稱。它是描述現實世界概念結構模型的有效方法,是表示概念模型的一種方式,用矩形表示實體型,矩形框內寫明實體名;用橢圓表示實體的屬性,並用無向邊將其與相應的實體型鏈接起來;用菱形表示實體型之間的聯繫,在菱形框內寫明聯繫名,並用無向邊分別與有關實體型鏈接起來,同時在無向邊旁標上聯繫的類型(1:1,1:n或m:n)。
(1)查找表中多餘的重複記錄,重複記錄是根據單個字段(column_name)來判斷。
select * from table_name where column_name in (select column_name from table_name group by column_name having count(column_name) > 1)
(2)刪除表中多餘的重複記錄,重複記錄是根據單個字段(column_name)來判斷,只留有id最小的記錄。
delete from table_name where column_name in (select b.column_name from (select column_name from table_name group by column_name having count(column_name)>1)b);
(3)查找表中多餘的重複記錄(多個字段)。
select * from table_name a where (a.column_name1,a.column_name2) in (select column_name1,column_name2 from vitae group by column_name1,column_name2 having count(*) > 1)
(4)刪除表中多餘的重複記錄(多個字段),只留有rowid最小的記錄 。
delete from table_name a where (a.column_name1,a.column_name2) in (select column_name1,column_name2 from table_name group by column_name1,column_name2 having count(*) > 1) and rowid not in (select min(rowid) from table_name group by column_name1,column_name2 having count(rowid)>1)
MyISAM:每一個MyISAM表在磁盤上存儲成三個文件,文件的名字以表的名字開始,擴展名指出文件類型:.frm文件存儲表定義;數據文件的擴展名爲.MYD (MYData);索引文件的擴展名是.MYI (MYIndex)。
InnoDB:全部的表都保存在同一個數據文件中(也多是多個文件,或者是獨立的表空間文件),InnoDB表的大小隻受限於操做系統文件的大小,通常爲2GB。
MyISAM:可被壓縮,存儲空間較小。支持三種不一樣的存儲格式:靜態表(默認,可是注意數據末尾不能有空格,會被去掉)、動態表、壓縮表。
InnoDB:須要更多的內存和存儲,它會在主內存中創建其專用的緩衝池用於高速緩衝數據和索引。
MyISAM:數據是以文件的形式存儲,因此在跨平臺的數據轉移中會很方便。在備份和恢復時可單獨針對某個表進行操做。
InnoDB:免費的方案能夠是拷貝數據文件、備份 binlog,或者用 mysqldump,在數據量達到幾十G的時候就相對痛苦了。
MyISAM:強調的是性能,每次查詢具備原子性,其執行數度比InnoDB類型更快,可是不提供事務支持。
InnoDB:支持事務,外部鍵等高級數據庫功能。 具備事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。
這一點是很是重要。事務是一種高級的處理方式,如在一些列增刪改中只要哪一個出錯還能夠回滾還原,而MyISAM就不能夠了。
MyISAM:能夠和其餘字段一塊兒創建聯合索引。引擎的自動增加列必須是索引,若是是組合索引,自動增加能夠不是第一列,他能夠根據前面幾列進行排序後遞增。
InnoDB: InnoDB中必須包含只有該字段的索引。引擎的自動增加列必須是索引,若是是組合索引也必須是組合索引的第一列。
MyISAM:只支持表級鎖,用戶在操做myisam表時,select,update,delete,insert語句都會給表自動加鎖,若是加鎖之後的表知足insert併發的狀況下,能夠在表的尾部插入新的數據。
InnoDB:支持事務和行級鎖,是innodb的最大特點。行鎖大幅度提升了多用戶併發操做的性能。可是InnoDB的行鎖,只是在WHERE的主鍵是有效的,非主鍵的WHERE都會鎖全表的
MyISAM:支持 FULLTEXT類型的全文索引。
InnoDB:不支持FULLTEXT類型的全文索引,可是innodb可使用sphinx插件支持全文索引,而且效果更好。
MyISAM:容許沒有任何索引和主鍵的表存在,索引都是保存行的地址。
InnoDB:若是沒有設定主鍵或者非空惟一索引,就會自動生成一個6字節的主鍵(用戶不可見),數據是主索引的一部分,附加索引保存的是主索引的值。
MyISAM:保存有表的總行數,若是select count() from table;會直接取出出該值。
InnoDB:沒有保存表的總行數,若是使用select count() from table;就會遍歷整個表,消耗至關大,可是在加了wehre條件後,myisam和innodb處理的方式都同樣。
MyISAM:若是執行大量的SELECT,MyISAM是更好的選擇。
InnoDB:若是你的數據執行大量的INSERT或UPDATE,出於性能方面的考慮,應該使用InnoDB表。DELETE 從性能上InnoDB更優,但DELETE FROM table時,InnoDB不會從新創建表,而是一行一行的刪除,在innodb上若是要清空保存有大量數據的表,最好使用truncate table這個命令。
MyISAM:不支持
InnoDB:支持
經過上述的分析,基本上能夠考慮使用InnoDB來替代MyISAM引擎了,緣由是InnoDB自身不少良好的特色,好比事務支持、存儲 過程、視圖、行級鎖定等等,在併發不少的狀況下,相信InnoDB的表現確定要比MyISAM強不少。另外,任何一種表都不是萬能的,只用恰當的針對業務類型來選擇合適的表類型,才能最大的發揮MySQL的性能優點。若是不是很複雜的Web應用,非關鍵應用,仍是能夠繼續考慮MyISAM的,這個具體視狀況而定。
數據庫管理系統(DBMS)中的併發控制的任務是確保在多個事務同時存取數據庫中同一數據時不破壞事務的隔離性和一致性以及數據庫的統一性。
樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制採用的主要技術手段。
不管是悲觀鎖仍是樂觀鎖,都是人們定義出來的概念,能夠認爲是一種思想。其實不只僅是關係型數據庫系統中有樂觀鎖和悲觀鎖的概念,像memcache、hibernate、tair等都有相似的概念。像java併發中的CAS操做也是樂觀鎖的一種實現。
針對不一樣的業務場景,應該選用不一樣的併發控制方式。因此,不要把樂觀併發控制和悲觀併發控制狹義的理解爲僅在DBMS中存在的概念,更不要把他們和數據庫中提供的鎖機制(行鎖、表鎖、排他鎖、共享鎖)混爲一談。其實,在DBMS中,悲觀鎖正是利用數據庫自己提供的鎖機制來實現的。
在關係數據庫管理系統裏,悲觀併發控制(又名「悲觀鎖」,Pessimistic Concurrency Control,縮寫「PCC」)是一種併發控制的方法。它能夠阻止一個事務以影響其餘用戶的方式來修改數據。若是一個事務執行的操做在某行數據上應用了鎖,那只有當這個事務把鎖釋放,其餘事務纔可以執行與該鎖衝突的操做。
悲觀併發控制主要用於數據爭用激烈的環境,以及發生併發衝突時使用鎖保護數據的成本要低於回滾事務的成本的環境中。
悲觀鎖:正如其名,它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度(悲觀),所以,在整個數據處理過程當中,將數據處於鎖定狀態。 悲觀鎖的實現,每每依靠數據庫提供的鎖機制 (也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。
悲觀鎖的流程:
1.在對某一記錄進行修改前,先嚐試爲該記錄加上排他鎖(exclusive locking)。
2.若是加鎖失敗,說明該記錄正在被修改,那麼當前操做可能要等待或者拋出異常, 具體響應方式由開發者根據實際狀況決定。
3.若是成功加鎖,那麼就能夠對記錄作修改,事務完成後就會解鎖了。
4.其間若是有其餘事務要對該記錄作修改或加排他鎖,都會等待該事務將該記錄解鎖或直接拋出異常。
MySQL InnoDB中使用悲觀鎖
注意:要使用悲觀鎖,必須先關閉mysql數據庫的自動提交功能,由於MySQL默認使用autocommit模式,也就是說,當你執行一個更新操做後,MySQL會馬上將結果進行提交。
set autocommit=0;
//0.開始事務 begin;/begin work;/start transaction; (三者選一就能夠) //1.查詢出商品信息 select status from t_goods where id=1 for update; //2.根據商品信息生成訂單 insert into t_orders (id,goods_id) values (null,1); //3.修改商品status爲2 update t_goods set status=2; //4.提交事務 commit;/commit work;
上面的查詢語句中,咱們使用了select…for update的方式,這樣就經過開啓排他鎖的方式實現了悲觀鎖。此時在t_goods表中,id爲1的 那條數據就被咱們鎖定了,其它事務必須等本次事務提交以後才能對該記錄進行操做。這樣咱們能夠保證當前的數據不會被其它事務修改。
注意:上面提到,使用select…for update會把數據給鎖住,不過咱們須要注意一下鎖的級別,MySQL InnoDB默認爲行級鎖。行級鎖都是基於索引的,若是一條SQL語句沒有用到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住,這點須要注意。
優勢與不足:
優勢:悲觀併發控制其實是採用「先取鎖再訪問」的保守策略,爲數據處理的安全性提供了保證;
缺點:在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,同時會增長產生死鎖的機率;另外,在只讀型事務中因爲不會產生衝突,也不必使用鎖,這樣作只會增長系統負載;還會下降並行性,一個事務若是鎖定了某行數據,其餘事務就必須等待該事務處理完才能夠處理那行數
在關係數據庫管理系統裏,樂觀併發控制(又名「樂觀鎖」,Optimistic Concurrency Control,縮寫「OCC」)是一種併發控制的方法。它假設多用戶併發的事務在處理數據時不會彼此互相影響,各事務可以在不產生鎖的狀況下處理各自影響的那部分數據。在提交數據更新以前,每一個事務會先檢查在該事務讀取數據後,有沒有其餘事務對該數據作過修改。若是其餘事務更新過該數據的話,正在提交的事務會進行回滾(這正是CAS操做的思想)。樂觀事務控制最先是由孔祥重(H.T.Kung)教授提出。
樂觀鎖( Optimistic Locking )是相對悲觀鎖而言,樂觀鎖假設數據通常狀況下不會形成衝突,因此在事務對數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則返回錯誤信息,讓用戶決定如何去作。
相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制,通常用記錄數據版本的方式實現樂觀鎖。
數據版本:爲數據增長的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,便對版本標識進行一次更新。當事務提交更新的時候,須要判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識是否一致,若是數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,不然認爲是過時數據。
實現數據版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。
使用版本號實現樂觀鎖
使用版本號時,能夠在數據初始化時指定一個版本號,每次對數據的更新操做都對版本號執行+1操做。並判斷當前版本號是否是該數據的最新的版本號。
1.查詢出商品信息 select (status,status,version) from t_goods where id=#{id} 2.根據商品信息生成訂單 3.修改商品status爲2 update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
樂觀併發控制假設事務之間的數據競爭(data race)機率比較小,所以儘量直接作下去,直到提交的時候纔去鎖定,因此不會產生任何鎖和死鎖。但若是直接簡單這麼作,仍是有可能會遇到不可預期的結果,例如兩個事務都讀取了數據庫的某一行,通過修改之後寫回數據庫,這時就遇到了問題。
https://blog.csdn.net/xiaomingdetianxia/article/details/72475924
https://zhuanlan.zhihu.com/p/23713529