- 前言:
在遨游了一番 Java Web 的世界之后,發(fā)現(xiàn)了自己的一些缺失脊髓,所以就著一篇深度好文:知名互聯(lián)網(wǎng)公司校招 Java 開發(fā)崗面試知識點解析 蜂科,來好好的對 Java 知識點進行復(fù)習和學習一番曼追,大部分內(nèi)容參照自這一篇文章窍仰,有一些自己補充的,也算是重新學習一下 Java 吧礼殊。
前序文章鏈接:
Java 面試知識點解析(五)——網(wǎng)絡(luò)協(xié)議篇
前排引用說明及好文推薦:面試/筆試第三彈 —— 數(shù)據(jù)庫面試問題集錦驹吮、數(shù)據(jù)庫常見面試題(開發(fā)者篇)
1)什么是存儲過程针史?有哪些優(yōu)缺點?
存儲過程就像是編程語言中的函數(shù)一樣碟狞,封裝了我們的代碼(PLSQL啄枕,T-SQL)
例如:
-------------創(chuàng)建名為GetUserAccount的存儲過程----------------
create Procedure GetUserAccount
as
select * from UserAccount
go
-------------執(zhí)行上面的存儲過程----------------
exec GetUserAccount
存儲過程的優(yōu)點:
- 能夠?qū)⒋a封裝起來
- 保存在數(shù)據(jù)庫之中
- 讓編程語言進行調(diào)用
- 存儲過程是一個預(yù)編譯的代碼塊,執(zhí)行效率比較高
- 一個存儲過程替代大量T_SQL語句 族沃,可以降低網(wǎng)絡(luò)通信量频祝,提高通信速率
存儲過程的缺點:
- 每個數(shù)據(jù)庫的存儲過程語法幾乎都不一樣,十分難以維護(不通用)
- 業(yè)務(wù)邏輯放在數(shù)據(jù)庫上脆淹,難以迭代
2)三大范式
- 思考這樣的一個例子:
我們現(xiàn)在需要建立一個描述學校教務(wù)的數(shù)據(jù)庫智润,該數(shù)據(jù)庫涉及的對象包括學生的學號(Sno)、所在系(Sdept)未辆、系主任姓名(Mname)、課程號(Cno)和成績(Grade)锯玛,假設(shè)我們使用單一的關(guān)系模式 Student 來表示咐柜,那么根據(jù)現(xiàn)實世界已知的信息,會描述成以下這個樣子:
但是攘残,這個關(guān)系模式存在以下問題:
(1) 數(shù)據(jù)冗余
比如拙友,每一個系的系主任姓名重復(fù)出現(xiàn),重復(fù)次數(shù)與該系所有學生的所有課程成績出現(xiàn)次數(shù)相同歼郭,這將浪費大量的存儲空間遗契。
(2)更新異常(update anomalies)
由于數(shù)據(jù)冗余,當更新數(shù)據(jù)庫中的數(shù)據(jù)時病曾,系統(tǒng)要付出很大的代價來維護數(shù)據(jù)庫的完整性牍蜂,否則會面臨數(shù)據(jù)不一致的危險。比如泰涂,某系更換系主任后鲫竞,必須修改與該系學生有關(guān)的每一個元組。
(3)插入異常(insertion anomalies)
如果一個系剛成立逼蒙,尚無學生从绘,則無法把這個系及其系主任的信息存入數(shù)據(jù)庫。
(4)刪除異常(deletion anomalies)
如果某個系的學生全部畢業(yè)了是牢,則在刪除該系學生信息的同時僵井,這個系及其系主任的信息也丟失了。
- 總結(jié): 所以驳棱,我們在設(shè)計數(shù)據(jù)庫的時候批什,就需要滿足一定的規(guī)范要求,而滿足不同程度要求的就是不同的范式社搅。
- 第一范式: 列不可分
1NF(第一范式)是對屬性具有原子性的要求渊季,不可再分朋蔫,例如:
如果認為最后一列還可以再分成出生年,出生月却汉,出生日驯妄,則它就不滿足第一范式的要求。
- 第二范式: 消除非主屬性對碼的部分函數(shù)依賴
2NF(第二范式)是對記錄有唯一性的要求合砂,即實體的唯一性青扔,不存在部分依賴,每一列與主鍵都相關(guān)翩伪,例如:
該表明顯說明了兩個事物:學生信息和課程信息微猖;正常的依賴應(yīng)該是:學分依賴課程號,姓名依賴學號缘屹,但這里存在非主鍵字段對碼的部分依賴凛剥,即與主鍵不相關(guān),不滿足第二范式的要求轻姿。
可能存在的問題:
- 數(shù)據(jù)冗余:每條記錄都含有相同信息犁珠;
- 刪除異常:刪除所有學生成績,就把課程信息全刪除了互亮;
- 插入異常:學生未選課犁享,無法記錄進數(shù)據(jù)庫;
- 更新異常:調(diào)整課程學分豹休,所有行都調(diào)整炊昆。
正確的做法:
- 第三范式: 消除非主屬性對碼的傳遞函數(shù)依賴
3NF(第三范式)對字段有冗余性的要求,任何字段不能由其他字段派生出來威根,它要求字段沒有冗余凤巨,即不存在依賴傳遞,例如:
很明顯洛搀,學院電話是一個冗余字段磅甩,因為存在依賴傳遞:(學號)→(學生)→(學院)→(學院電話)
可能會存在的問題:
- 數(shù)據(jù)冗余:有重復(fù)值;
- 更新異常:有重復(fù)的冗余信息姥卢,修改時需要同時修改多條記錄卷要,否則會出現(xiàn)數(shù)據(jù)不一致的情況 。
正確的做法:
3)數(shù)據(jù)庫索引
- 什么是索引独榴?
索引是對數(shù)據(jù)庫表中一個或多個列的值進行排序的數(shù)據(jù)結(jié)構(gòu)僧叉,以協(xié)助快速查詢、更新數(shù)據(jù)庫表中數(shù)據(jù)棺榔。
你也可以這樣理解:索引就是加快檢索表中數(shù)據(jù)的方法瓶堕。數(shù)據(jù)庫的索引類似于書籍的索引。在書籍中症歇,索引允許用戶不必翻閱完整個書就能迅速地找到所需要的信息郎笆。在數(shù)據(jù)庫中谭梗,索引也允許數(shù)據(jù)庫程序迅速地找到表中的數(shù)據(jù),而不必掃描整個數(shù)據(jù)庫宛蚓。
- 底層數(shù)據(jù)結(jié)構(gòu)是什么激捏,為什么使用這種數(shù)據(jù)結(jié)構(gòu)?
(1)底層數(shù)據(jù)結(jié)構(gòu)是B+樹:
在數(shù)據(jù)結(jié)構(gòu)中凄吏,我們最為常見的搜索結(jié)構(gòu)就是二叉搜索樹和AVL樹(高度平衡的二叉搜索樹远舅,為了提高二叉搜索樹的效率,減少樹的平均搜索長度)了痕钢。然而图柏,無論二叉搜索樹還是AVL樹,當數(shù)據(jù)量比較大時任连,都會由于樹的深度過大而造成I/O讀寫過于頻繁蚤吹,進而導(dǎo)致查詢效率低下,因此對于索引而言随抠,多叉樹結(jié)構(gòu)成為不二選擇裁着。特別地,B-Tree的各種操作能使B樹保持較低的高度暮刃,從而保證高效的查找效率。
(2)使用B+樹的原因:
查找速度快爆土、效率高椭懊,在查找的過程中,每次都能拋棄掉一部分節(jié)點步势,減少遍歷個數(shù)氧猬。(此時,你應(yīng)該在白紙上畫出什么是B+樹)
- 索引的分類坏瘩?
- 唯一索引:唯一索引不允許兩行具有相同的索引值
- 主鍵索引:為表定義一個主鍵將自動創(chuàng)建主鍵索引盅抚,主鍵索引是唯一索引的特殊類型。主鍵索引要求主鍵中的每個值是唯一的倔矾,并且不能為空
- 聚集索引(Clustered):表中各行的物理順序與鍵值的邏輯(索引)順序相同妄均,每個表只能有一個
- 非聚集索引(Non-clustered):非聚集索引指定表的邏輯順序。數(shù)據(jù)存儲在一個位置哪自,索引存儲在另一個位置丰包,索引中包含指向數(shù)據(jù)存儲位置的指針∪老铮可以有多個邑彪,小于249個
- 索引的優(yōu)缺點?
(1)優(yōu)點:
- 大大加快數(shù)據(jù)的檢索速度胧华,這也是創(chuàng)建索引的最主要的原因寄症;
- 加速表和表之間的連接宙彪;
- 在使用分組和排序子句進行數(shù)據(jù)檢索時,同樣可以顯著減少查詢中分組和排序的時間有巧;
- 通過創(chuàng)建唯一性索引释漆,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性;
(2)缺點:
- 時間方面:創(chuàng)建索引和維護索引要耗費時間剪决,具體地灵汪,當對表中的數(shù)據(jù)進行增加、刪除和修改的時候柑潦,索引也要動態(tài)的維護享言,這樣就降低了數(shù)據(jù)的維護速度;
- 空間方面:索引需要占物理空間渗鬼。
- 什么樣的字段適合創(chuàng)建索引览露?
- 經(jīng)常作查詢選擇的字段
- 經(jīng)常作表連接的字段
- 經(jīng)常出現(xiàn)在order by, group by, distinct 后面的字段
- 創(chuàng)建索引時需要注意什么?
非空字段:應(yīng)該指定列為NOT NULL譬胎,除非你想存儲NULL差牛。在mysql中,含有空值的列很難進行查詢優(yōu)化堰乔,因為它們使得索引偏化、索引的統(tǒng)計信息以及比較運算更加復(fù)雜。你應(yīng)該用0镐侯、一個特殊的值或者一個空串代替空值侦讨;
取值離散大的字段:(變量各個取值之間的差異程度)的列放到聯(lián)合索引的前面,可以通過count()函數(shù)查看字段的差異值苟翻,返回值越大說明字段的唯一值越多字段的離散程度高韵卤;
索引字段越小越好:數(shù)據(jù)庫的數(shù)據(jù)存儲以頁為單位一頁存儲的數(shù)據(jù)越多一次IO操作獲取的數(shù)據(jù)越大效率越高。
4)聽說過事務(wù)嗎崇猫?(必考)
事務(wù)簡單來說:一個 Session 中所進行所有的操作沈条,要么同時成功,要么同時失敗诅炉;作為單個邏輯工作單元執(zhí)行的一系列操作蜡歹,滿足四大特性:
- 原子性(Atomicity):事務(wù)作為一個整體被執(zhí)行 ,要么全部執(zhí)行涕烧,要么全部不執(zhí)行
- 一致性(Consistency):保證數(shù)據(jù)庫狀態(tài)從一個一致狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€一致狀態(tài)
- 隔離性(Isolation):多個事務(wù)并發(fā)執(zhí)行時季稳,一個事務(wù)的執(zhí)行不應(yīng)影響其他事務(wù)的執(zhí)行
- 持久性(Durability):一個事務(wù)一旦提交,對數(shù)據(jù)庫的修改應(yīng)該永久保存
- 實例說明:
/*
* 我們來模擬A向B賬號轉(zhuǎn)賬的場景
* A和B賬戶都有1000塊澈魄,現(xiàn)在我讓A賬戶向B賬號轉(zhuǎn)500塊錢
*
**/
//JDBC默認的情況下是關(guān)閉事務(wù)的景鼠,下面我們看看關(guān)閉事務(wù)去操作轉(zhuǎn)賬操作有什么問題
//A賬戶減去500塊
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//B賬戶多了500塊
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
從上面看,我們的確可以發(fā)現(xiàn)A向B轉(zhuǎn)賬,成功了铛漓∷菹悖可是如果A向B轉(zhuǎn)賬的過程中出現(xiàn)了問題呢?下面模擬一下
// A賬戶減去500塊
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
// 這里模擬出現(xiàn)問題
int a = 3 / 0;
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
顯然浓恶,上面代碼是會拋出異常的玫坛,我們再來查詢一下數(shù)據(jù)。A賬戶少了500塊錢包晰,B賬戶的錢沒有增加湿镀。這明顯是不合理的。
我們可以通過事務(wù)來解決上面出現(xiàn)的問題:
// 開啟事務(wù),對數(shù)據(jù)的操作就不會立即生效伐憾。
connection.setAutoCommit(false);
// A賬戶減去500塊
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
// 在轉(zhuǎn)賬過程中出現(xiàn)問題
int a = 3 / 0;
// B賬戶多500塊
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
// 如果程序能執(zhí)行到這里勉痴,沒有拋出異常,我們就提交數(shù)據(jù)
connection.commit();
// 關(guān)閉事務(wù)【自動提交】
connection.setAutoCommit(true);
} catch(SQLException e) {
try {
// 如果出現(xiàn)了異常树肃,就會進到這里來蒸矛,我們就把事務(wù)回滾【將數(shù)據(jù)變成原來那樣】
connection.rollback();
// 關(guān)閉事務(wù)【自動提交】
connection.setAutoCommit(true);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
上面的程序也一樣拋出了異常,A賬戶錢沒有減少胸嘴,B賬戶的錢也沒有增加雏掠。
- 注意:當Connection遇到一個未處理的SQLException時,系統(tǒng)會非正常退出劣像,事務(wù)也會自動回滾乡话,但如果程序捕獲到了異常,是需要在catch中顯式回滾事務(wù)的耳奕。
5)事務(wù)的并發(fā)問題有哪幾種绑青?
- 丟失更新:一個事務(wù)的更新覆蓋了另一個事務(wù)的更新;
- 臟讀:一個事務(wù)讀取了另一個事務(wù)未提交的數(shù)據(jù)吮铭;
- 不可重復(fù)讀:不可重復(fù)讀的重點是修改时迫,同樣條件下兩次讀取結(jié)果不同颅停,也就是說谓晌,被讀取的數(shù)據(jù)可以被其它事務(wù)修改;
- 幻讀:幻讀的重點在于新增或者刪除癞揉,同樣條件下兩次讀出來的記錄數(shù)不一樣纸肉。
6)事務(wù)的隔離級別有哪幾種?
隔離級別決定了一個session中的事務(wù)可能對另一個session中的事務(wù)的影響喊熟。ANSI標準定義了4個隔離級別柏肪,MySQL的InnoDB都支持,分別是:
讀未提交(READ UNCOMMITTED):最低級別的隔離芥牌,通常又稱為dirty read烦味,它允許一個事務(wù)讀取另一個事務(wù)還沒 commit 的數(shù)據(jù),這樣可能會提高性能,但是會導(dǎo)致臟讀問題谬俄;
讀已提交(READ COMMITTED):在一個事務(wù)中只允許對其它事務(wù)已經(jīng) commit 的記錄可見柏靶,該隔離級別不能避免不可重復(fù)讀問題;
可重復(fù)讀(REPEATABLE READ):在一個事務(wù)開始后溃论,其他事務(wù)對數(shù)據(jù)庫的修改在本事務(wù)中不可見屎蜓,直到本事務(wù) commit 或 rollback。但是钥勋,其他事務(wù)的 insert/delete 操作對該事務(wù)是可見的炬转,也就是說,該隔離級別并不能避免幻讀問題算灸。在一個事務(wù)中重復(fù) select 的結(jié)果一樣扼劈,除非本事務(wù)中 update 數(shù)據(jù)庫。
序列化(SERIALIZABLE):最高級別的隔離乎婿,只允許事務(wù)串行執(zhí)行测僵。
MySQL默認的隔離級別是可重復(fù)讀(REPEATABLE READ)
- MySql 的事務(wù)支持
MySQL的事務(wù)支持不是綁定在MySQL服務(wù)器本身,而是與存儲引擎相關(guān):
- MyISAM:不支持事務(wù)谢翎,用于只讀程序提高性能捍靠;
- InnoDB:支持ACID事務(wù)、行級鎖森逮、并發(fā)榨婆;
- Berkeley DB:支持事務(wù)。
7)什么是視圖褒侧?以及視圖的使用場景有哪些良风?
視圖是一種虛擬的表,具有和物理表相同的功能闷供⊙萄耄可以對視圖進行增,改歪脏,查疑俭,操作,試圖通常是有一個表或者多個表的行或列的子集婿失。對視圖的修改不影響基本表钞艇。它使得我們獲取數(shù)據(jù)更容易,相比多表查詢豪硅。
如下兩種場景一般會使用到視圖:
- 不希望訪問者獲取整個表的信息哩照,只暴露部分字段給訪問者,所以就建一個虛表懒浮,就是視圖飘弧。
- 查詢的數(shù)據(jù)來源于不同的表,而查詢者希望以統(tǒng)一的方式查詢,這樣也可以建立一個視圖次伶,把多個表查詢結(jié)果聯(lián)合起來蹋岩,查詢者只需要直接從視圖中獲取數(shù)據(jù),不必考慮數(shù)據(jù)來源于不同表所帶來的差異学少。
注意:這個視圖是在數(shù)據(jù)庫中創(chuàng)建的 而不是用代碼創(chuàng)建的剪个。
8)drop,delete與truncate的區(qū)別?
drop 直接刪除表版确;truncate 刪除表中數(shù)據(jù)扣囊,再插入時自增長id又從1開始 ;delete 刪除表中數(shù)據(jù)绒疗,可以加where字句侵歇。
- drop table:
- 屬于DDL(Data Definition Language,數(shù)據(jù)庫定義語言)
- 不可回滾
- 不可帶 where
- 表內(nèi)容和結(jié)構(gòu)刪除
- 刪除速度快
- truncate table:
- 屬于DDL(Data Definition Language吓蘑,數(shù)據(jù)庫定義語言)
- 不可回滾
- 不可帶 where
- 表內(nèi)容刪除
- 刪除速度快
- delete from:
- 屬于DML
- 可回滾
- 可帶where
- 表結(jié)構(gòu)在惕虑,表內(nèi)容要看where執(zhí)行的情況
- 刪除速度慢,需要逐行刪除
- 使用簡要說明:
- 不再需要一張表的時候,用drop
- 想刪除部分數(shù)據(jù)行時候磨镶,用delete溃蔫,并且?guī)蟱here子句
- 保留表而刪除所有數(shù)據(jù)的時候用truncate
9)觸發(fā)器的作用?
觸發(fā)器是與表相關(guān)的數(shù)據(jù)庫對象琳猫,在滿足定義條件時觸發(fā)伟叛,并執(zhí)行觸發(fā)器中定義的語句集合。觸發(fā)器的這種特性可以協(xié)助應(yīng)用在數(shù)據(jù)庫端確保數(shù)據(jù)庫的完整性脐嫂。
10)數(shù)據(jù)庫的樂觀鎖和悲觀鎖是什么统刮?
數(shù)據(jù)庫管理系統(tǒng)(DBMS)中的并發(fā)控制的任務(wù)是確保在多個事務(wù)同時存取數(shù)據(jù)庫中同一數(shù)據(jù)時不破壞事務(wù)的隔離性和統(tǒng)一性以及數(shù)據(jù)庫的統(tǒng)一性。
樂觀并發(fā)控制(樂觀鎖)和悲觀并發(fā)控制(悲觀鎖)是并發(fā)控制主要采用的技術(shù)手段账千。
- 悲觀鎖:假定會發(fā)生并發(fā)沖突侥蒙,屏蔽一切可能違反數(shù)據(jù)完整性的操作
悲觀鎖是一種利用數(shù)據(jù)庫內(nèi)部機制提供的鎖的方式,也就是對更新的數(shù)據(jù)加鎖匀奏,這樣在并發(fā)期間一旦有一個事務(wù)持有了數(shù)據(jù)庫記錄的鎖鞭衩,其他的線程將不能再對數(shù)據(jù)進行更新了,這就是悲觀鎖的實現(xiàn)方式攒射。
MySQL InnoDB中使用悲觀鎖:
要使用悲觀鎖醋旦,我們必須關(guān)閉mysql數(shù)據(jù)庫的自動提交屬性恒水,因為MySQL默認使用autocommit模式会放,也就是說,當你執(zhí)行一個更新操作后钉凌,MySQL會立刻將結(jié)果進行提交咧最。 set autocommit=0;
//0.開始事務(wù)
begin;/begin work;/start transaction; (三者選一就可以)
//1.查詢出商品信息
select status from t_goods where id=1 for update;
//2.根據(jù)商品信息生成訂單
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status為2
update t_goods set status=2;
//4.提交事務(wù)
commit;/commit work;
上面的查詢語句中,我們使用了 select…for update
的方式,這樣就通過開啟排他鎖的方式實現(xiàn)了悲觀鎖矢沿。此時在t_goods表中滥搭,id為1的 那條數(shù)據(jù)就被我們鎖定了,其它的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行捣鲸。這樣我們可以保證當前的數(shù)據(jù)不會被其它事務(wù)修改瑟匆。
上面我們提到,使用 select…for update
會把數(shù)據(jù)給鎖住栽惶,不過我們需要注意一些鎖的級別愁溜,MySQL InnoDB默認行級鎖。行級鎖都是基于索引的外厂,如果一條SQL語句用不到索引是不會使用行級鎖的冕象,會使用表級鎖把整張表鎖住,這點需要注意汁蝶。
優(yōu)點與不足:
悲觀并發(fā)控制實際上是“先取鎖再訪問”的保守策略渐扮,為數(shù)據(jù)處理的安全提供了保證。但是在效率方面掖棉,處理加鎖的機制會讓數(shù)據(jù)庫產(chǎn)生額外的開銷墓律,還有增加產(chǎn)生死鎖的機會;另外幔亥,在只讀型事務(wù)處理中由于不會產(chǎn)生沖突只锻,也沒必要使用鎖,這樣做只能增加系統(tǒng)負載紫谷;還有會降低了并行性齐饮,一個事務(wù)如果鎖定了某行數(shù)據(jù),其他事務(wù)就必須等待該事務(wù)處理完才可以處理那行數(shù)
- 樂觀鎖:假設(shè)不會發(fā)生并發(fā)沖突笤昨,只在提交操作時檢查是否違反數(shù)據(jù)完整性祖驱。
樂觀鎖是一種不會阻塞其他線程并發(fā)的控制,它不會使用數(shù)據(jù)庫的鎖進行實現(xiàn)瞒窒,它的設(shè)計里面由于不阻塞其他線程捺僻,所以并不會引起線程頻繁掛起和恢復(fù),這樣便能夠提高并發(fā)能力崇裁,所以也有人把它稱為非阻塞鎖匕坯。一般的實現(xiàn)樂觀鎖的方式就是記錄數(shù)據(jù)版本。
數(shù)據(jù)版本,為數(shù)據(jù)增加的一個版本標識拔稳。當讀取數(shù)據(jù)時葛峻,將版本標識的值一同讀出,數(shù)據(jù)每更新一次巴比,同時對版本標識進行更新禁漓。當我們提交更新的時候,判斷數(shù)據(jù)庫表對應(yīng)記錄的當前版本信息與第一次取出來的版本標識進行比對阳谍,如果數(shù)據(jù)庫表當前版本號與第一次取出來的版本標識值相等,則予以更新佣耐,否則認為是過期數(shù)據(jù)。
實現(xiàn)數(shù)據(jù)版本有兩種方式唧龄,第一種是使用版本號兼砖,第二種是使用時間戳。
使用版本號實現(xiàn)樂觀鎖:
使用版本號時既棺,可以在數(shù)據(jù)初始化時指定一個版本號掖鱼,每次對數(shù)據(jù)的更新操作都對版本號執(zhí)行+1操作。并判斷當前版本號是不是該數(shù)據(jù)的最新的版本號援制。
1.查詢出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根據(jù)商品信息生成訂單
3.修改商品status為2
update t_goods
set status=2,version=version+1
where id=#{id} and version=#{version};
優(yōu)點與不足:
樂觀并發(fā)控制相信事務(wù)之間的數(shù)據(jù)競爭(data race)的概率是比較小的戏挡,因此盡可能直接做下去,直到提交的時候才去鎖定晨仑,所以不會產(chǎn)生任何鎖和死鎖褐墅。但如果直接簡單這么做,還是有可能會遇到不可預(yù)期的結(jié)果洪己,例如兩個事務(wù)都讀取了數(shù)據(jù)庫的某一行妥凳,經(jīng)過修改以后寫回數(shù)據(jù)庫,這時就遇到了問題答捕。
參考文章:深入理解樂觀鎖與悲觀鎖
11)超鍵逝钥、候選鍵、主鍵拱镐、外鍵分別是什么艘款?
超鍵:在關(guān)系中能唯一標識元組的屬性集稱為關(guān)系模式的超鍵。一個屬性可以為作為一個超鍵沃琅,多個屬性組合在一起也可以作為一個超鍵哗咆。超鍵包含候選鍵和主鍵。
候選鍵(候選碼):是最小超鍵益眉,即沒有冗余元素的超鍵晌柬。
主鍵(主碼):數(shù)據(jù)庫表中對儲存數(shù)據(jù)對象予以唯一和完整標識的數(shù)據(jù)列或?qū)傩缘慕M合。一個數(shù)據(jù)列只能有一個主鍵郭脂,且主鍵的取值不能缺失年碘,即不能為空值(Null)。
外鍵:在一個表中存在的另一個表的主鍵稱此表的外鍵展鸡。
候選碼和主碼:
例子:郵寄地址(城市名屿衅,街道名,郵政編碼娱颊,單位名傲诵,收件人)
- 它有兩個候選鍵:{城市名,街道名} 和 {街道名箱硕,郵政編碼}
- 如果我選取{城市名拴竹,街道名}作為唯一標識實體的屬性,那么{城市名剧罩,街道名} 就是主碼(主鍵)
12)SQL 約束有哪幾種栓拜?
- NOT NULL: 用于控制字段的內(nèi)容一定不能為空(NULL)。
- UNIQUE: 控件字段內(nèi)容不能重復(fù)惠昔,一個表允許有多個 Unique 約束幕与。
- PRIMARY KEY: 也是用于控件字段內(nèi)容不能重復(fù),但它在一個表只允許出現(xiàn)一個镇防。
- FOREIGN KEY: 用于預(yù)防破壞表之間連接的動作啦鸣,也能防止非法數(shù)據(jù)插入外鍵列,因為它必須是它指向的那個表中的值之一来氧。
- CHECK: 用于控制字段的值范圍诫给。
13)MySQL存儲引擎中的MyISAM和InnoDB區(qū)別詳解
在MySQL 5.5之前,MyISAM是mysql的默認數(shù)據(jù)庫引擎啦扬,其由早期的ISAM(Indexed Sequential Access Method:有索引的順序訪問方法)所改良中狂。雖然MyISAM性能極佳,但卻有一個顯著的缺點: 不支持事務(wù)處理扑毡。不過胃榕,MySQL也導(dǎo)入了另一種數(shù)據(jù)庫引擎InnoDB,以強化參考完整性與并發(fā)違規(guī)處理機制瞄摊,后來就逐漸取代MyISAM勋又。
InnoDB是MySQL的數(shù)據(jù)庫引擎之一,其由Innobase oy公司所開發(fā)换帜,2006年五月由甲骨文公司并購赐写。與傳統(tǒng)的ISAM、MyISAM相比膜赃,InnoDB的最大特色就是支持ACID兼容的事務(wù)功能挺邀,類似于PostgreSQL。目前InnoDB采用雙軌制授權(quán)跳座,一是GPL授權(quán)端铛,另一是專有軟件授權(quán)。具體地疲眷,MyISAM與InnoDB作為MySQL的兩大存儲引擎的差異主要包括:
存儲結(jié)構(gòu):每個MyISAM在磁盤上存儲成三個文件:第一個文件的名字以表的名字開始禾蚕,擴展名指出文件類型。.frm文件存儲表定義狂丝,數(shù)據(jù)文件的擴展名為.MYD (MYData)换淆,索引文件的擴展名是.MYI (MYIndex)哗总。InnoDB所有的表都保存在同一個數(shù)據(jù)文件中(也可能是多個文件,或者是獨立的表空間文件)倍试,InnoDB表的大小只受限于操作系統(tǒng)文件的大小讯屈,一般為2GB。
存儲空間:MyISAM可被壓縮县习,占據(jù)的存儲空間較小涮母,支持靜態(tài)表、動態(tài)表躁愿、壓縮表三種不同的存儲格式叛本。InnoDB需要更多的內(nèi)存和存儲,它會在主內(nèi)存中建立其專用的緩沖池用于高速緩沖數(shù)據(jù)和索引彤钟。
可移植性来候、備份及恢復(fù):MyISAM的數(shù)據(jù)是以文件的形式存儲,所以在跨平臺的數(shù)據(jù)轉(zhuǎn)移中會很方便逸雹,同時在備份和恢復(fù)時也可單獨針對某個表進行操作吠勘。InnoDB免費的方案可以是拷貝數(shù)據(jù)文件、備份 binlog峡眶,或者用 mysqldump剧防,在數(shù)據(jù)量達到幾十G的時候就相對痛苦了。
事務(wù)支持:MyISAM強調(diào)的是性能辫樱,每次查詢具有原子性峭拘,其執(zhí)行數(shù)度比InnoDB類型更快,但是不提供事務(wù)支持狮暑。InnoDB提供事務(wù)鸡挠、外鍵等高級數(shù)據(jù)庫功能,具有事務(wù)提交搬男、回滾和崩潰修復(fù)能力拣展。
AUTO_INCREMENT:在MyISAM中,可以和其他字段一起建立聯(lián)合索引缔逛。引擎的自動增長列必須是索引备埃,如果是組合索引,自動增長可以不是第一列褐奴,它可以根據(jù)前面幾列進行排序后遞增按脚。InnoDB中必須包含只有該字段的索引,并且引擎的自動增長列必須是索引敦冬,如果是組合索引也必須是組合索引的第一列辅搬。
表鎖差異:MyISAM只支持表級鎖,用戶在操作MyISAM表時脖旱,select堪遂、update介蛉、delete和insert語句都會給表自動加鎖,如果加鎖以后的表滿足insert并發(fā)的情況下溶褪,可以在表的尾部插入新的數(shù)據(jù)币旧。InnoDB支持事務(wù)和行級鎖。行鎖大幅度提高了多用戶并發(fā)操作的新能竿滨,但是InnoDB的行鎖佳恬,只是在WHERE的主鍵是有效的捏境,非主鍵的WHERE都會鎖全表的于游。
全文索引:MyISAM支持 FULLTEXT類型的全文索引;InnoDB不支持FULLTEXT類型的全文索引垫言,但是innodb可以使用sphinx插件支持全文索引贰剥,并且效果更好。
表主鍵:MyISAM允許沒有任何索引和主鍵的表存在筷频,索引都是保存行的地址蚌成。對于InnoDB,如果沒有設(shè)定主鍵或者非空唯一索引凛捏,就會自動生成一個6字節(jié)的主鍵(用戶不可見)担忧,數(shù)據(jù)是主索引的一部分,附加索引保存的是主索引的值坯癣。
表的具體行數(shù):MyISAM保存表的總行數(shù)瓶盛,select count() from table;會直接取出出該值;而InnoDB沒有保存表的總行數(shù)示罗,如果使用select count() from table惩猫;就會遍歷整個表,消耗相當大蚜点,但是在加了wehre條件后轧房,myisam和innodb處理的方式都一樣。
CURD操作:在MyISAM中绍绘,如果執(zhí)行大量的SELECT奶镶,MyISAM是更好的選擇。對于InnoDB陪拘,如果你的數(shù)據(jù)執(zhí)行大量的INSERT或UPDATE实辑,出于性能方面的考慮,應(yīng)該使用InnoDB表藻丢。DELETE從性能上InnoDB更優(yōu)剪撬,但DELETE FROM table時,InnoDB不會重新建立表悠反,而是一行一行的刪除残黑,在innodb上如果要清空保存有大量數(shù)據(jù)的表馍佑,最好使用truncate table這個命令。
外鍵:MyISAM不支持外鍵梨水,而InnoDB支持外鍵拭荤。
通過上述的分析,基本上可以考慮使用InnoDB來替代MyISAM引擎了疫诽,原因是InnoDB自身很多良好的特點舅世,比如事務(wù)支持、存儲過程奇徒、視圖雏亚、行級鎖、外鍵等等摩钙。尤其在并發(fā)很多的情況下罢低,相信InnoDB的表現(xiàn)肯定要比MyISAM強很多。另外胖笛,必須需要注意的是网持,任何一種表都不是萬能的,合適的才是最好的长踊,才能最大的發(fā)揮MySQL的性能優(yōu)勢功舀。如果是不復(fù)雜的、非關(guān)鍵的Web應(yīng)用身弊,還是可以繼續(xù)考慮MyISAM的辟汰,這個具體情況具體考慮。
14)MyIASM和Innodb兩種引擎所使用的索引的數(shù)據(jù)結(jié)構(gòu)是什么佑刷?
答案:都是B+樹!
MyIASM引擎莉擒,B+樹的數(shù)據(jù)結(jié)構(gòu)中存儲的內(nèi)容實際上是實際數(shù)據(jù)的地址值。也就是說它的索引和實際數(shù)據(jù)是分開的瘫絮,只不過使用索引指向了實際數(shù)據(jù)涨冀。這種索引的模式被稱為非聚集索引。
Innodb引擎的索引的數(shù)據(jù)結(jié)構(gòu)也是B+樹麦萤,只不過數(shù)據(jù)結(jié)構(gòu)中存儲的都是實際的數(shù)據(jù)鹿鳖,這種索引有被稱為聚集索引。
15)varchar和char的區(qū)別
char是一種固定長度的類型壮莹,varchar是一種可變長度的類型翅帜,例如:
定義一個char[10]和varchar[10],如果存進去的是 'test'命满,那么char所占的長度依然為10涝滴,除了字符 'test' 外,后面跟六個空格,varchar就立馬把長度變?yōu)?了歼疮,取數(shù)據(jù)的時候杂抽,char類型的要用trim()去掉多余的空格,而varchar是不需要的
char的存取速度還是要比varchar要快得多韩脏,因為其長度固定缩麸,方便程序的存儲于查找
char也為此付出的是空間的代價,因為其長度固定赡矢,所以難免會有多余的空格占位符占據(jù)空間杭朱,可謂是以空間換取時間效率。
varchar是以空間效率為首位吹散。
char的存儲方式是:對英文字符(ASCII)占用1個字節(jié)弧械,對一個漢字占用兩個字節(jié)。
varchar的存儲方式是:對每個英文字符占用2個字節(jié)送浊,漢字也占用2個字節(jié)梦谜。
兩者的存儲數(shù)據(jù)都非unicode的字符數(shù)據(jù)丘跌。
16)主鍵袭景、自增主鍵、主鍵索引與唯一索引概念區(qū)別
主鍵:指字段 唯一闭树、不為空值 的列耸棒;
主鍵索引:指的就是主鍵,主鍵是索引的一種报辱,是唯一索引的特殊類型与殃。創(chuàng)建主鍵的時候,數(shù)據(jù)庫默認會為主鍵創(chuàng)建一個唯一索引碍现;
自增主鍵:字段類型為數(shù)字幅疼、自增、并且是主鍵昼接;
唯一索引:索引列的值必須唯一爽篷,但允許有空值。主鍵是唯一索引慢睡,這樣說沒錯逐工;但反過來說,唯一索引也是主鍵就錯誤了漂辐,因為唯一索引允許空值泪喊,主鍵不允許有空值,所以不能說唯一索引也是主鍵髓涯。
17)主鍵就是聚集索引嗎袒啼?主鍵和索引有什么區(qū)別?
主鍵是一種特殊的唯一性索引,其可以是聚集索引蚓再,也可以是非聚集索引灶泵。在SQLServer中,主鍵的創(chuàng)建必須依賴于索引对途,默認創(chuàng)建的是聚集索引赦邻,但也可以顯式指定為非聚集索引。InnoDB作為MySQL存儲引擎時实檀,默認按照主鍵進行聚集惶洲,如果沒有定義主鍵,InnoDB會試著使用唯一的非空索引來代替膳犹。如果沒有這種索引恬吕,InnoDB就會定義隱藏的主鍵然后在上面進行聚集。所以须床,對于聚集索引來說铐料,你創(chuàng)建主鍵的時候,自動就創(chuàng)建了主鍵的聚集索引豺旬。
18)實踐中如何優(yōu)化MySQL
實踐中钠惩,MySQL的優(yōu)化主要涉及SQL語句及索引的優(yōu)化、數(shù)據(jù)表結(jié)構(gòu)的優(yōu)化族阅、系統(tǒng)配置的優(yōu)化和硬件的優(yōu)化四個方面篓跛,如下圖所示:
⑴ SQL語句優(yōu)化:
SQL語句的優(yōu)化主要包括三個問題,即如何發(fā)現(xiàn)有問題的SQL坦刀、如何分析SQL的執(zhí)行計劃以及如何優(yōu)化SQL愧沟,下面將逐一解釋。
① 怎么發(fā)現(xiàn)有問題的SQL?(通過MySQL慢查詢?nèi)罩緦τ行蕟栴}的SQL進行監(jiān)控)
MySQL的慢查詢?nèi)罩臼荕ySQL提供的一種日志記錄鲤遥,它用來記錄在MySQL中響應(yīng)時間超過閥值的語句沐寺,具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢?nèi)罩局懈悄巍ong_query_time的默認值為10混坞,意思是運行10s以上的語句。慢查詢?nèi)罩镜南嚓P(guān)參數(shù)如下所示:
通過MySQL的慢查詢?nèi)罩静防剩覀兛梢圆樵兂鰣?zhí)行的次數(shù)多占用的時間長的SQL拔第、可以通過pt_query_disgest(一種mysql慢日志分析工具)分析Rows examine(MySQL執(zhí)行器需要檢查的行數(shù))項去找出IO大的SQL以及發(fā)現(xiàn)未命中索引的SQL,對于這些SQL场钉,都是我們優(yōu)化的對象蚊俺。
② 通過explain查詢和分析SQL的執(zhí)行計劃:
使用 EXPLAIN 關(guān)鍵字可以知道MySQL是如何處理你的SQL語句的,以便分析查詢語句或是表結(jié)構(gòu)的性能瓶頸逛万。通過explain命令可以得到表的讀取順序泳猬、數(shù)據(jù)讀取操作的操作類型批钠、哪些索引可以使用、哪些索引被實際使用得封、表之間的引用以及每張表有多少行被優(yōu)化器查詢等問題埋心。當擴展列extra出現(xiàn)Using filesort和Using temporay,則往往表示SQL需要優(yōu)化了忙上。
③ SQL語句的優(yōu)化:
⒈優(yōu)化insert語句:一次插入多值拷呆;
⒉應(yīng)盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描疫粥;
⒊應(yīng)盡量避免在 where 子句中對字段進行null值判斷茬斧,否則將導(dǎo)致引擎放棄使用索引而進行全表掃描;
⒋優(yōu)化嵌套查詢:子查詢可以被更有效率的連接(Join)替代梗逮;
⒌很多時候用 exists 代替 in 是一個好的選擇项秉。
⒍選擇最有效率的表名順序:數(shù)據(jù)庫的解析器按照從右到左的順序處理FROM子句中的表名,F(xiàn)ROM子句中寫在最后的表將被最先處理
在FROM子句中包含多個表的情況下:
- 如果三個表是完全無關(guān)系的話慷彤,將記錄和列名最少的表娄蔼,寫在最后,然后依次類推
- 也就是說:選擇記錄條數(shù)最少的表放在最后
如果有3個以上的表連接查詢:
- 如果三個表是有關(guān)系的話底哗,將引用最多的表岁诉,放在最后,然后依次類推艘虎。
- 也就是說:被其他表所引用的表放在最后
⒎用IN代替OR:
select * from emp where sal = 1500 or sal = 3000 or sal = 800;
select * from emp where sal in (1500,3000,800);
⒏SELECT子句中避免使用*號:
我們最開始接觸 SQL 的時候唉侄,“*
” 號是可以獲取表中全部的字段數(shù)據(jù)的咒吐,但是它要通過查詢數(shù)據(jù)字典完成野建,這意味著將消耗更多的時間,而且使用 “*
” 號寫出來的 SQL 語句也不夠直觀恬叹。
⑵ 索引優(yōu)化:
建議在經(jīng)常作查詢選擇的字段候生、經(jīng)常作表連接的字段以及經(jīng)常出現(xiàn)在 order by、group by绽昼、distinct 后面的字段中建立索引唯鸭。但必須注意以下幾種可能會引起索引失效的情形:
以 “%(表示任意0個或多個字符)” 開頭的 LIKE 語句,模糊匹配硅确;
OR語句前后沒有同時使用索引目溉;
數(shù)據(jù)類型出現(xiàn)隱式轉(zhuǎn)化(如varchar不加單引號的話可能會自動轉(zhuǎn)換為int型);
對于多列索引菱农,必須滿足最左匹配原則(eg,多列索引col1缭付、col2和col3,則 索引生效的情形包括col1或col1循未,col2或col1陷猫,col2,col3)。
⑶ 數(shù)據(jù)庫表結(jié)構(gòu)的優(yōu)化:
① 選擇合適數(shù)據(jù)類型:
- 使用較小的數(shù)據(jù)類型解決問題绣檬;
- 使用簡單的數(shù)據(jù)類型(mysql處理int要比varchar容易)足陨;
- 盡可能的使用not null 定義字段;
- 盡量避免使用text類型娇未,非用不可時最好考慮分表墨缘;
② 表的范式的優(yōu)化:
一般情況下,表的設(shè)計應(yīng)該遵循三大范式零抬。
③ 表的垂直拆分:
把含有多個列的表拆分成多個表飒房,解決表寬度問題,具體包括以下幾種拆分手段:
- 把不常用的字段單獨放在同一個表中媚值;
- 把大字段獨立放入一個表中狠毯;
- 把經(jīng)常使用的字段放在一起;
這樣做的好處是非常明顯的褥芒,具體包括:拆分后業(yè)務(wù)清晰嚼松,拆分規(guī)則明確、系統(tǒng)之間整合或擴展容易锰扶、數(shù)據(jù)維護簡單
④ 表的水平拆分:
表的水平拆分用于解決數(shù)據(jù)表中數(shù)據(jù)過大的問題献酗,水平拆分每一個表的結(jié)構(gòu)都是完全一致的。一般地坷牛,將數(shù)據(jù)平分到N張表中的常用方法包括以下兩種:
- 對ID進行hash運算罕偎,如果要拆分成5個表,mod(id,5)取出0~4個值京闰;
- 針對不同的hashID將數(shù)據(jù)存入不同的表中颜及;
表的水平拆分會帶來一些問題和挑戰(zhàn),包括跨分區(qū)表的數(shù)據(jù)查詢蹂楣、統(tǒng)計及后臺報表的操作等問題俏站,但也帶來了一些切實的好處:
- 表分割后可以降低在查詢時需要讀的數(shù)據(jù)和索引的頁數(shù),同時也降低了索引的層數(shù)痊土,提高查詢速度肄扎;
- 表中的數(shù)據(jù)本來就有獨立性,例如表中分別記錄各個地區(qū)的數(shù)據(jù)或不同時期的數(shù)據(jù)赁酝,特別是有些數(shù)據(jù)常用犯祠,而另外一些數(shù)據(jù)不常用。
- 需要把數(shù)據(jù)存放到多個數(shù)據(jù)庫中酌呆,提高系統(tǒng)的總體可用性(分庫衡载,雞蛋不能放在同一個籃子里)。
⑷ 系統(tǒng)配置的優(yōu)化:
操作系統(tǒng)配置的優(yōu)化:增加TCP支持的隊列數(shù)
mysql配置文件優(yōu)化:Innodb緩存池設(shè)置(innodb_buffer_pool_size肪笋,推薦總內(nèi)存的75%)和緩存池的個數(shù)(innodb_buffer_pool_instances)
⑸ 硬件的優(yōu)化:
- CPU:核心數(shù)多并且主頻高的
- 內(nèi)存:增大內(nèi)存
- 磁盤配置和選擇:磁盤性能
歡迎轉(zhuǎn)載月劈,轉(zhuǎn)載請注明出處度迂!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關(guān)注公眾微信號:wmyskxz_javaweb
分享自己的Java Web學習之路以及各種Java學習資料