Java 面試知識點解析(六)——數據庫篇


  • 前言:

在遨游了一番 Java Web 的世界之后,發現了自己的一些缺失,所以就着一篇深度好文:知名互聯網公司校招 Java 開發崗面試知識點解析 ,來好好的對 Java 知識點進行復習和學習一番,大部分內容參照自這一篇文章,有一些自己補充的,也算是重新學習一下 Java 吧。

前序文章鏈接:

Java 面試知識點解析(一)——基礎知識篇

Java 面試知識點解析(二)——高並發編程篇

Java 面試知識點解析(三)——JVM篇

Java 面試知識點解析(四)——版本特性篇

Java 面試知識點解析(五)——網絡協議篇


前排引用說明及好文推薦:面試/筆試第三彈 —— 數據庫面試問題集錦數據庫常見面試題(開發者篇)

1)什么是存儲過程?有哪些優缺點?

存儲過程就像是編程語言中的函數一樣,封裝了我們的代碼(PLSQL,T-SQL)

例如:

-------------創建名為GetUserAccount的存儲過程----------------
create Procedure GetUserAccount
as
select * from UserAccount
go

-------------執行上面的存儲過程----------------
exec GetUserAccount

存儲過程的優點:

  • 能夠將代碼封裝起來
  • 保存在數據庫之中
  • 讓編程語言進行調用
  • 存儲過程是一個預編譯的代碼塊,執行效率比較高
  • 一個存儲過程替代大量T_SQL語句 ,可以降低網絡通信量,提高通信速率

存儲過程的缺點:

  • 每個數據庫的存儲過程語法幾乎都不一樣,十分難以維護(不通用)
  • 業務邏輯放在數據庫上,難以迭代

2)三大范式

  • 思考這樣的一個例子:

我們現在需要建立一個描述學校教務的數據庫,該數據庫涉及的對象包括學生的學號(Sno)、所在系(Sdept)、系主任姓名(Mname)、課程號(Cno)和成績(Grade),假設我們使用單一的關系模式 Student 來表示,那么根據現實世界已知的信息,會描述成以下這個樣子:

但是,這個關系模式存在以下問題:

(1) 數據冗余
比如,每一個系的系主任姓名重復出現,重復次數與該系所有學生的所有課程成績出現次數相同,這將浪費大量的存儲空間。
(2)更新異常(update anomalies)
由於數據冗余,當更新數據庫中的數據時,系統要付出很大的代價來維護數據庫的完整性,否則會面臨數據不一致的危險。比如,某系更換系主任后,必須修改與該系學生有關的每一個元組。
(3)插入異常(insertion anomalies)
如果一個系剛成立,尚無學生,則無法把這個系及其系主任的信息存入數據庫。
(4)刪除異常(deletion anomalies)
如果某個系的學生全部畢業了,則在刪除該系學生信息的同時,這個系及其系主任的信息也丟失了。

  • 總結: 所以,我們在設計數據庫的時候,就需要滿足一定的規范要求,而滿足不同程度要求的就是不同的范式。
  • 第一范式: 列不可分

1NF(第一范式)是對屬性具有原子性的要求,不可再分,例如:

如果認為最后一列還可以再分成出生年,出生月,出生日,則它就不滿足第一范式的要求。

  • 第二范式: 消除非主屬性對碼的部分函數依賴

2NF(第二范式)是對記錄有唯一性的要求,即實體的唯一性,不存在部分依賴,每一列與主鍵都相關,例如:

該表明顯說明了兩個事物:學生信息和課程信息;正常的依賴應該是:學分依賴課程號,姓名依賴學號,但這里存在非主鍵字段對碼的部分依賴,即與主鍵不相關,不滿足第二范式的要求。

可能存在的問題:

  • 數據冗余:每條記錄都含有相同信息;
  • 刪除異常:刪除所有學生成績,就把課程信息全刪除了;
  • 插入異常:學生未選課,無法記錄進數據庫;
  • 更新異常:調整課程學分,所有行都調整。

正確的做法:

  • 第三范式: 消除非主屬性對碼的傳遞函數依賴

3NF(第三范式)對字段有冗余性的要求,任何字段不能由其他字段派生出來,它要求字段沒有冗余,即不存在依賴傳遞,例如:

很明顯,學院電話是一個冗余字段,因為存在依賴傳遞:(學號)→(學生)→(學院)→(學院電話)

可能會存在的問題:

  • 數據冗余:有重復值;
  • 更新異常:有重復的冗余信息,修改時需要同時修改多條記錄,否則會出現數據不一致的情況 。

正確的做法:


3)數據庫索引

  • 什么是索引?

索引是對數據庫表中一個或多個列的值進行排序的數據結構,以協助快速查詢、更新數據庫表中數據。

你也可以這樣理解:索引就是加快檢索表中數據的方法。數據庫的索引類似於書籍的索引。在書籍中,索引允許用戶不必翻閱完整個書就能迅速地找到所需要的信息。在數據庫中,索引也允許數據庫程序迅速地找到表中的數據,而不必掃描整個數據庫。

  • 底層數據結構是什么,為什么使用這種數據結構?

(1)底層數據結構是B+樹:
在數據結構中,我們最為常見的搜索結構就是二叉搜索樹和AVL樹(高度平衡的二叉搜索樹,為了提高二叉搜索樹的效率,減少樹的平均搜索長度)了。然而,無論二叉搜索樹還是AVL樹,當數據量比較大時,都會由於樹的深度過大而造成I/O讀寫過於頻繁,進而導致查詢效率低下,因此對於索引而言,多叉樹結構成為不二選擇。特別地,B-Tree的各種操作能使B樹保持較低的高度,從而保證高效的查找效率。

(2)使用B+樹的原因:
查找速度快、效率高,在查找的過程中,每次都能拋棄掉一部分節點,減少遍歷個數。(此時,你應該在白紙上畫出什么是B+樹)

  • 索引的分類?
  • 唯一索引:唯一索引不允許兩行具有相同的索引值
  • 主鍵索引:為表定義一個主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特殊類型。主鍵索引要求主鍵中的每個值是唯一的,並且不能為空
  • 聚集索引(Clustered):表中各行的物理順序與鍵值的邏輯(索引)順序相同,每個表只能有一個
  • 非聚集索引(Non-clustered):非聚集索引指定表的邏輯順序。數據存儲在一個位置,索引存儲在另一個位置,索引中包含指向數據存儲位置的指針。可以有多個,小於249個
  • 索引的優缺點?

(1)優點:

  • 大大加快數據的檢索速度,這也是創建索引的最主要的原因;
  • 加速表和表之間的連接;
  • 在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間;
  • 通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性;

(2)缺點:

  • 時間方面:創建索引和維護索引要耗費時間,具體地,當對表中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度;
  • 空間方面:索引需要占物理空間。
  • 什么樣的字段適合創建索引?
  • 經常作查詢選擇的字段
  • 經常作表連接的字段
  • 經常出現在order by, group by, distinct 后面的字段
  • 創建索引時需要注意什么?
  • 非空字段:應該指定列為NOT NULL,除非你想存儲NULL。在mysql中,含有空值的列很難進行查詢優化,因為它們使得索引、索引的統計信息以及比較運算更加復雜。你應該用0、一個特殊的值或者一個空串代替空值;

  • 取值離散大的字段:(變量各個取值之間的差異程度)的列放到聯合索引的前面,可以通過count()函數查看字段的差異值,返回值越大說明字段的唯一值越多字段的離散程度高;

  • 索引字段越小越好:數據庫的數據存儲以頁為單位一頁存儲的數據越多一次IO操作獲取的數據越大效率越高。


4)聽說過事務嗎?(必考)

事務簡單來說:一個 Session 中所進行所有的操作,要么同時成功,要么同時失敗;作為單個邏輯工作單元執行的一系列操作,滿足四大特性:

  1. 原子性(Atomicity):事務作為一個整體被執行 ,要么全部執行,要么全部不執行
  2. 一致性(Consistency):保證數據庫狀態從一個一致狀態轉變為另一個一致狀態
  3. 隔離性(Isolation):多個事務並發執行時,一個事務的執行不應影響其他事務的執行
  4. 持久性(Durability):一個事務一旦提交,對數據庫的修改應該永久保存
  • 實例說明:
/*
 * 我們來模擬A向B賬號轉賬的場景
 *   A和B賬戶都有1000塊,現在我讓A賬戶向B賬號轉500塊錢
 *
 **/
//JDBC默認的情況下是關閉事務的,下面我們看看關閉事務去操作轉賬操作有什么問題

//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();

從上面看,我們的確可以發現A向B轉賬,成功了。可是如果A向B轉賬的過程中出現了問題呢?下面模擬一下

// A賬戶減去500塊
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();

// 這里模擬出現問題
int a = 3 / 0;

String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();

顯然,上面代碼是會拋出異常的,我們再來查詢一下數據。A賬戶少了500塊錢,B賬戶的錢沒有增加。這明顯是不合理的。

我們可以通過事務來解決上面出現的問題:

	// 開啟事務,對數據的操作就不會立即生效。
	connection.setAutoCommit(false);

	// A賬戶減去500塊
	String sql = "UPDATE a SET money=money-500 ";
	preparedStatement = connection.prepareStatement(sql);
	preparedStatement.executeUpdate();

	// 在轉賬過程中出現問題
	int a = 3 / 0;

	// B賬戶多500塊
	String sql2 = "UPDATE b SET money=money+500";
	preparedStatement = connection.prepareStatement(sql2);
	preparedStatement.executeUpdate();

	// 如果程序能執行到這里,沒有拋出異常,我們就提交數據
	connection.commit();

	// 關閉事務【自動提交】
	connection.setAutoCommit(true);

} catch(SQLException e) {
	try {
		// 如果出現了異常,就會進到這里來,我們就把事務回滾【將數據變成原來那樣】
		connection.rollback();

		// 關閉事務【自動提交】
		connection.setAutoCommit(true);
	} catch (SQLException e1) {
		e1.printStackTrace();
	}
}

上面的程序也一樣拋出了異常,A賬戶錢沒有減少,B賬戶的錢也沒有增加。

  • 注意:當Connection遇到一個未處理的SQLException時,系統會非正常退出,事務也會自動回滾,但如果程序捕獲到了異常,是需要在catch中顯式回滾事務的。

5)事務的並發問題有哪幾種?

  1. 丟失更新:一個事務的更新覆蓋了另一個事務的更新;
  2. 臟讀:一個事務讀取了另一個事務未提交的數據;
  3. 不可重復讀:不可重復讀的重點是修改,同樣條件下兩次讀取結果不同,也就是說,被讀取的數據可以被其它事務修改;
  4. 幻讀:幻讀的重點在於新增或者刪除,同樣條件下兩次讀出來的記錄數不一樣。

6)事務的隔離級別有哪幾種?

隔離級別決定了一個session中的事務可能對另一個session中的事務的影響。ANSI標准定義了4個隔離級別,MySQL的InnoDB都支持,分別是:

  1. 讀未提交(READ UNCOMMITTED):最低級別的隔離,通常又稱為dirty read,它允許一個事務讀取另一個事務還沒 commit 的數據,這樣可能會提高性能,但是會導致臟讀問題;

  2. 讀已提交(READ COMMITTED):在一個事務中只允許對其它事務已經 commit 的記錄可見,該隔離級別不能避免不可重復讀問題;

  3. 可重復讀(REPEATABLE READ):在一個事務開始后,其他事務對數據庫的修改在本事務中不可見,直到本事務 commit 或 rollback。但是,其他事務的 insert/delete 操作對該事務是可見的,也就是說,該隔離級別並不能避免幻讀問題。在一個事務中重復 select 的結果一樣,除非本事務中 update 數據庫。

  4. 序列化(SERIALIZABLE):最高級別的隔離,只允許事務串行執行。

MySQL默認的隔離級別是可重復讀(REPEATABLE READ)

  • MySql 的事務支持

MySQL的事務支持不是綁定在MySQL服務器本身,而是與存儲引擎相關:

  • MyISAM:不支持事務,用於只讀程序提高性能;
  • InnoDB:支持ACID事務、行級鎖、並發;
  • Berkeley DB:支持事務。

7)什么是視圖?以及視圖的使用場景有哪些?

視圖是一種虛擬的表,具有和物理表相同的功能。可以對視圖進行增,改,查,操作,試圖通常是有一個表或者多個表的行或列的子集。對視圖的修改不影響基本表。它使得我們獲取數據更容易,相比多表查詢。

如下兩種場景一般會使用到視圖:

  1. 不希望訪問者獲取整個表的信息,只暴露部分字段給訪問者,所以就建一個虛表,就是視圖。
  2. 查詢的數據來源於不同的表,而查詢者希望以統一的方式查詢,這樣也可以建立一個視圖,把多個表查詢結果聯合起來,查詢者只需要直接從視圖中獲取數據,不必考慮數據來源於不同表所帶來的差異。

注意:這個視圖是在數據庫中創建的 而不是用代碼創建的。


8)drop,delete與truncate的區別?

drop 直接刪除表;truncate 刪除表中數據,再插入時自增長id又從1開始 ;delete 刪除表中數據,可以加where字句。

  • drop table:
  • 屬於DDL(Data Definition Language,數據庫定義語言)
  • 不可回滾
  • 不可帶 where
  • 表內容和結構刪除
  • 刪除速度快
  • truncate table:
  • 屬於DDL(Data Definition Language,數據庫定義語言)
  • 不可回滾
  • 不可帶 where
  • 表內容刪除
  • 刪除速度快
  • delete from:
  • 屬於DML
  • 可回滾
  • 可帶where
  • 表結構在,表內容要看where執行的情況
  • 刪除速度慢,需要逐行刪除
  • 使用簡要說明:
  • 不再需要一張表的時候,用drop
  • 想刪除部分數據行時候,用delete,並且帶上where子句
  • 保留表而刪除所有數據的時候用truncate

9)觸發器的作用?

觸發器是與表相關的數據庫對象,在滿足定義條件時觸發,並執行觸發器中定義的語句集合。觸發器的這種特性可以協助應用在數據庫端確保數據庫的完整性。


10)數據庫的樂觀鎖和悲觀鎖是什么?

數據庫管理系統(DBMS)中的並發控制的任務是確保在多個事務同時存取數據庫中同一數據時不破壞事務的隔離性和統一性以及數據庫的統一性。

樂觀並發控制(樂觀鎖)和悲觀並發控制(悲觀鎖)是並發控制主要采用的技術手段。

  • 悲觀鎖:假定會發生並發沖突,屏蔽一切可能違反數據完整性的操作

悲觀鎖是一種利用數據庫內部機制提供的鎖的方式,也就是對更新的數據加鎖,這樣在並發期間一旦有一個事務持有了數據庫記錄的鎖,其他的線程將不能再對數據進行更新了,這就是悲觀鎖的實現方式。

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語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住,這點需要注意。

優點與不足:

悲觀並發控制實際上是“先取鎖再訪問”的保守策略,為數據處理的安全提供了保證。但是在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增加產生死鎖的機會;另外,在只讀型事務處理中由於不會產生沖突,也沒必要使用鎖,這樣做只能增加系統負載;還有會降低了並行性,一個事務如果鎖定了某行數據,其他事務就必須等待該事務處理完才可以處理那行數

  • 樂觀鎖:假設不會發生並發沖突,只在提交操作時檢查是否違反數據完整性。

樂觀鎖是一種不會阻塞其他線程並發的控制,它不會使用數據庫的鎖進行實現,它的設計里面由於不阻塞其他線程,所以並不會引起線程頻繁掛起和恢復,這樣便能夠提高並發能力,所以也有人把它稱為非阻塞鎖。一般的實現樂觀鎖的方式就是記錄數據版本。

數據版本,為數據增加的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,同時對版本標識進行更新。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,如果數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,否則認為是過期數據。

實現數據版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。

使用版本號實現樂觀鎖:

使用版本號時,可以在數據初始化時指定一個版本號,每次對數據的更新操作都對版本號執行+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)的概率是比較小的,因此盡可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。但如果直接簡單這么做,還是有可能會遇到不可預期的結果,例如兩個事務都讀取了數據庫的某一行,經過修改以后寫回數據庫,這時就遇到了問題。

參考文章:深入理解樂觀鎖與悲觀鎖


11)超鍵、候選鍵、主鍵、外鍵分別是什么?

  • 超鍵:在關系中能唯一標識元組的屬性集稱為關系模式的超鍵。一個屬性可以為作為一個超鍵,多個屬性組合在一起也可以作為一個超鍵。超鍵包含候選鍵和主鍵。

  • 候選鍵(候選碼):是最小超鍵,即沒有冗余元素的超鍵。

  • 主鍵(主碼):數據庫表中對儲存數據對象予以唯一和完整標識的數據列或屬性的組合。一個數據列只能有一個主鍵,且主鍵的取值不能缺失,即不能為空值(Null)。

  • 外鍵:在一個表中存在的另一個表的主鍵稱此表的外鍵。

候選碼和主碼:

例子:郵寄地址(城市名,街道名,郵政編碼,單位名,收件人)

  • 它有兩個候選鍵:{城市名,街道名} 和 {街道名,郵政編碼}
  • 如果我選取{城市名,街道名}作為唯一標識實體的屬性,那么{城市名,街道名} 就是主碼(主鍵)

12)SQL 約束有哪幾種?

  • NOT NULL: 用於控制字段的內容一定不能為空(NULL)。
  • UNIQUE: 控件字段內容不能重復,一個表允許有多個 Unique 約束。
  • PRIMARY KEY: 也是用於控件字段內容不能重復,但它在一個表只允許出現一個。
  • FOREIGN KEY: 用於預防破壞表之間連接的動作,也能防止非法數據插入外鍵列,因為它必須是它指向的那個表中的值之一。
  • CHECK: 用於控制字段的值范圍。

13)MySQL存儲引擎中的MyISAM和InnoDB區別詳解

在MySQL 5.5之前,MyISAM是mysql的默認數據庫引擎,其由早期的ISAM(Indexed Sequential Access Method:有索引的順序訪問方法)所改良。雖然MyISAM性能極佳,但卻有一個顯著的缺點: 不支持事務處理。不過,MySQL也導入了另一種數據庫引擎InnoDB,以強化參考完整性與並發違規處理機制,后來就逐漸取代MyISAM。

InnoDB是MySQL的數據庫引擎之一,其由Innobase oy公司所開發,2006年五月由甲骨文公司並購。與傳統的ISAM、MyISAM相比,InnoDB的最大特色就是支持ACID兼容的事務功能,類似於PostgreSQL。目前InnoDB采用雙軌制授權,一是GPL授權,另一是專有軟件授權。具體地,MyISAM與InnoDB作為MySQL的兩大存儲引擎的差異主要包括:

  • 存儲結構:每個MyISAM在磁盤上存儲成三個文件:第一個文件的名字以表的名字開始,擴展名指出文件類型。.frm文件存儲表定義,數據文件的擴展名為.MYD (MYData),索引文件的擴展名是.MYI (MYIndex)。InnoDB所有的表都保存在同一個數據文件中(也可能是多個文件,或者是獨立的表空間文件),InnoDB表的大小只受限於操作系統文件的大小,一般為2GB。

  • 存儲空間:MyISAM可被壓縮,占據的存儲空間較小,支持靜態表、動態表、壓縮表三種不同的存儲格式。InnoDB需要更多的內存和存儲,它會在主內存中建立其專用的緩沖池用於高速緩沖數據和索引。

  • 可移植性、備份及恢復:MyISAM的數據是以文件的形式存儲,所以在跨平台的數據轉移中會很方便,同時在備份和恢復時也可單獨針對某個表進行操作。InnoDB免費的方案可以是拷貝數據文件、備份 binlog,或者用 mysqldump,在數據量達到幾十G的時候就相對痛苦了。

  • 事務支持:MyISAM強調的是性能,每次查詢具有原子性,其執行數度比InnoDB類型更快,但是不提供事務支持。InnoDB提供事務、外鍵等高級數據庫功能,具有事務提交、回滾和崩潰修復能力。

  • AUTO_INCREMENT:在MyISAM中,可以和其他字段一起建立聯合索引。引擎的自動增長列必須是索引,如果是組合索引,自動增長可以不是第一列,它可以根據前面幾列進行排序后遞增。InnoDB中必須包含只有該字段的索引,並且引擎的自動增長列必須是索引,如果是組合索引也必須是組合索引的第一列。

  • 表鎖差異:MyISAM只支持表級鎖,用戶在操作MyISAM表時,select、update、delete和insert語句都會給表自動加鎖,如果加鎖以后的表滿足insert並發的情況下,可以在表的尾部插入新的數據。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處理的方式都一樣。

  • CURD操作:在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的,這個具體情況具體考慮。


14)MyIASM和Innodb兩種引擎所使用的索引的數據結構是什么?

答案:都是B+樹!

MyIASM引擎,B+樹的數據結構中存儲的內容實際上是實際數據的地址值。也就是說它的索引和實際數據是分開的,只不過使用索引指向了實際數據。這種索引的模式被稱為非聚集索引。

Innodb引擎的索引的數據結構也是B+樹,只不過數據結構中存儲的都是實際的數據,這種索引有被稱為聚集索引。


15)varchar和char的區別

char是一種固定長度的類型,varchar是一種可變長度的類型,例如:

定義一個char[10]和varchar[10],如果存進去的是 'test',那么char所占的長度依然為10,除了字符 'test' 外,后面跟六個空格,varchar就立馬把長度變為4了,取數據的時候,char類型的要用trim()去掉多余的空格,而varchar是不需要的

char的存取速度還是要比varchar要快得多,因為其長度固定,方便程序的存儲於查找

char也為此付出的是空間的代價,因為其長度固定,所以難免會有多余的空格占位符占據空間,可謂是以空間換取時間效率。

varchar是以空間效率為首位。

char的存儲方式是:對英文字符(ASCII)占用1個字節,對一個漢字占用兩個字節。

varchar的存儲方式是:對每個英文字符占用2個字節,漢字也占用2個字節。
兩者的存儲數據都非unicode的字符數據。


16)主鍵、自增主鍵、主鍵索引與唯一索引概念區別

  1. 主鍵:指字段 唯一、不為空值 的列;

  2. 主鍵索引:指的就是主鍵,主鍵是索引的一種,是唯一索引的特殊類型。創建主鍵的時候,數據庫默認會為主鍵創建一個唯一索引;

  3. 自增主鍵:字段類型為數字、自增、並且是主鍵;

  4. 唯一索引:索引列的值必須唯一,但允許有空值。主鍵是唯一索引,這樣說沒錯;但反過來說,唯一索引也是主鍵就錯誤了,因為唯一索引允許空值,主鍵不允許有空值,所以不能說唯一索引也是主鍵。


17)主鍵就是聚集索引嗎?主鍵和索引有什么區別?

主鍵是一種特殊的唯一性索引,其可以是聚集索引,也可以是非聚集索引。在SQLServer中,主鍵的創建必須依賴於索引,默認創建的是聚集索引,但也可以顯式指定為非聚集索引。InnoDB作為MySQL存儲引擎時,默認按照主鍵進行聚集,如果沒有定義主鍵,InnoDB會試着使用唯一的非空索引來代替。如果沒有這種索引,InnoDB就會定義隱藏的主鍵然后在上面進行聚集。所以,對於聚集索引來說,你創建主鍵的時候,自動就創建了主鍵的聚集索引。


18)實踐中如何優化MySQL

實踐中,MySQL的優化主要涉及SQL語句及索引的優化、數據表結構的優化、系統配置的優化和硬件的優化四個方面,如下圖所示:

⑴ SQL語句優化:

SQL語句的優化主要包括三個問題,即如何發現有問題的SQL、如何分析SQL的執行計划以及如何優化SQL,下面將逐一解釋。

① 怎么發現有問題的SQL?(通過MySQL慢查詢日志對有效率問題的SQL進行監控)

MySQL的慢查詢日志是MySQL提供的一種日志記錄,它用來記錄在MySQL中響應時間超過閥值的語句,具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢日志中。long_query_time的默認值為10,意思是運行10s以上的語句。慢查詢日志的相關參數如下所示:

通過MySQL的慢查詢日志,我們可以查詢出執行的次數多占用的時間長的SQL、可以通過pt_query_disgest(一種mysql慢日志分析工具)分析Rows examine(MySQL執行器需要檢查的行數)項去找出IO大的SQL以及發現未命中索引的SQL,對於這些SQL,都是我們優化的對象。

② 通過explain查詢和分析SQL的執行計划:

使用 EXPLAIN 關鍵字可以知道MySQL是如何處理你的SQL語句的,以便分析查詢語句或是表結構的性能瓶頸。通過explain命令可以得到表的讀取順序、數據讀取操作的操作類型、哪些索引可以使用、哪些索引被實際使用、表之間的引用以及每張表有多少行被優化器查詢等問題。當擴展列extra出現Using filesort和Using temporay,則往往表示SQL需要優化了。

③ SQL語句的優化:

⒈優化insert語句:一次插入多值;

⒉應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描;

⒊應盡量避免在 where 子句中對字段進行null值判斷,否則將導致引擎放棄使用索引而進行全表掃描;

⒋優化嵌套查詢:子查詢可以被更有效率的連接(Join)替代;

⒌很多時候用 exists 代替 in 是一個好的選擇。

⒍選擇最有效率的表名順序:數據庫的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最后的表將被最先處理

在FROM子句中包含多個表的情況下:

  • 如果三個表是完全無關系的話,將記錄和列名最少的表,寫在最后,然后依次類推
  • 也就是說:選擇記錄條數最少的表放在最后

如果有3個以上的表連接查詢:

  • 如果三個表是有關系的話,將引用最多的表,放在最后,然后依次類推。
  • 也就是說:被其他表所引用的表放在最后

⒎用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 的時候,“*” 號是可以獲取表中全部的字段數據的,但是它要通過查詢數據字典完成,這意味着將消耗更多的時間,而且使用 “*” 號寫出來的 SQL 語句也不夠直觀。


⑵ 索引優化:

建議在經常作查詢選擇的字段、經常作表連接的字段以及經常出現在 order by、group by、distinct 后面的字段中建立索引。但必須注意以下幾種可能會引起索引失效的情形:

  • 以 “%(表示任意0個或多個字符)” 開頭的 LIKE 語句,模糊匹配;

  • OR語句前后沒有同時使用索引;

  • 數據類型出現隱式轉化(如varchar不加單引號的話可能會自動轉換為int型);

  • 對於多列索引,必須滿足最左匹配原則(eg,多列索引col1、col2和col3,則 索引生效的情形包括col1或col1,col2或col1,col2,col3)。


⑶ 數據庫表結構的優化:

① 選擇合適數據類型:

  • 使用較小的數據類型解決問題;
  • 使用簡單的數據類型(mysql處理int要比varchar容易);
  • 盡可能的使用not null 定義字段;
  • 盡量避免使用text類型,非用不可時最好考慮分表;

② 表的范式的優化:

一般情況下,表的設計應該遵循三大范式。

③ 表的垂直拆分:

把含有多個列的表拆分成多個表,解決表寬度問題,具體包括以下幾種拆分手段:

  • 把不常用的字段單獨放在同一個表中;
  • 把大字段獨立放入一個表中;
  • 把經常使用的字段放在一起;

這樣做的好處是非常明顯的,具體包括:拆分后業務清晰,拆分規則明確、系統之間整合或擴展容易、數據維護簡單

④ 表的水平拆分:

表的水平拆分用於解決數據表中數據過大的問題,水平拆分每一個表的結構都是完全一致的。一般地,將數據平分到N張表中的常用方法包括以下兩種:

  • 對ID進行hash運算,如果要拆分成5個表,mod(id,5)取出0~4個值;
  • 針對不同的hashID將數據存入不同的表中;

表的水平拆分會帶來一些問題和挑戰,包括跨分區表的數據查詢、統計及后台報表的操作等問題,但也帶來了一些切實的好處:

  • 表分割后可以降低在查詢時需要讀的數據和索引的頁數,同時也降低了索引的層數,提高查詢速度;
  • 表中的數據本來就有獨立性,例如表中分別記錄各個地區的數據或不同時期的數據,特別是有些數據常用,而另外一些數據不常用。
  • 需要把數據存放到多個數據庫中,提高系統的總體可用性(分庫,雞蛋不能放在同一個籃子里)。

⑷ 系統配置的優化:

  • 操作系統配置的優化:增加TCP支持的隊列數

  • mysql配置文件優化:Innodb緩存池設置(innodb_buffer_pool_size,推薦總內存的75%)和緩存池的個數(innodb_buffer_pool_instances)


⑸ 硬件的優化:

  • CPU:核心數多並且主頻高的
  • 內存:增大內存
  • 磁盤配置和選擇:磁盤性能

歡迎轉載,轉載請注明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關注公眾微信號:wmyskxz_javaweb
分享自己的Java Web學習之路以及各種Java學習資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM