MySQL 常見面試題/知識點總結!(2021 最新版)| JavaGuide

相關閱讀: 2.7w字!Java基礎面試題/知識點總結!(2021 最新版)mysql

這篇文章以前發過,不過,我最近對其進行了重構完善而且修復了不少小問題。因此,在再同步一下!git

內容很硬!強烈建議小夥伴們花 10 分鐘左右閱讀一遍!程序員

MySQL 基礎

關係型數據庫介紹

顧名思義,關係型數據庫就是一種創建在關係模型的基礎上的數據庫。關係模型代表了數據庫中所存儲的數據之間的聯繫(一對1、一對多、多對多)。github

關係型數據庫中,咱們的數據都被存放在了各類表中(好比用戶表),表中的每一行就存放着一條數據(好比一個用戶的信息)。面試

大部分關係型數據庫都使用 SQL 來操做數據庫中的數據。而且,大部分關係型數據庫都支持事務的四大特性(ACID)。算法

有哪些常見的關係型數據庫呢?sql

MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天記錄的存儲就是用的 SQLite) ......。數據庫

MySQL 介紹

MySQL 是一種關係型數據庫,主要用於持久化存儲咱們的系統中的一些數據好比用戶信息。緩存

因爲 MySQL 是開源免費而且比較成熟的數據庫,所以,MySQL 被大量使用在各類系統中。任何人均可以在 GPL(General Public License) 的許可下下載並根據個性化的須要對其進行修改。MySQL 的默認端口號是3306安全

存儲引擎

存儲引擎相關的命令

查看 MySQL 提供的全部存儲引擎

mysql> show engines;
複製代碼

查看MySQL提供的全部存儲引擎

從上圖咱們能夠查看出 MySQL 當前默認的存儲引擎是 InnoDB,而且在 5.7 版本全部的存儲引擎中只有 InnoDB 是事務性存儲引擎,也就是說只有 InnoDB 支持事務。

查看 MySQL 當前默認的存儲引擎

咱們也能夠經過下面的命令查看默認的存儲引擎。

mysql> show variables like '%storage_engine%';
複製代碼

查看錶的存儲引擎

show table status like "table_name" ;
複製代碼

查看錶的存儲引擎

MyISAM 和 InnoDB 的區別

MySQL 5.5 以前,MyISAM 引擎是 MySQL 的默認存儲引擎,可謂是風光一時。

雖然,MyISAM 的性能還行,各類特性也還不錯(好比全文索引、壓縮、空間函數等)。可是,MyISAM 不支持事務和行級鎖,並且最大的缺陷就是崩潰後沒法安全恢復。

5.5 版本以後,MySQL 引入了 InnoDB(事務性數據庫引擎),MySQL 5.5 版本後默認的存儲引擎爲 InnoDB。小夥子,必定要記好這個 InnoDB ,你每次使用 MySQL 數據庫都是用的這個存儲引擎吧?

言歸正傳!我們下面仍是來簡單對比一下二者:

1.是否支持行級鎖

MyISAM 只有表級鎖(table-level locking),而 InnoDB 支持行級鎖(row-level locking)和表級鎖,默認爲行級鎖。

也就說,MyISAM 一鎖就是鎖住了整張表,這在併發寫的狀況下是多麼滴憨憨啊!這也是爲何 InnoDB 在併發寫的時候,性能更牛皮了!

2.是否支持事務

MyISAM 不提供事務支持。

InnoDB 提供事務支持,具備提交(commit)和回滾(rollback)事務的能力。

3.是否支持外鍵

MyISAM 不支持,而 InnoDB 支持。

🌈 拓展一下:

通常咱們也是不建議在數據庫層面使用外鍵的,應用層面能夠解決。不過,這樣會對數據的一致性形成威脅。具體要不要使用外鍵仍是要根據你的項目來決定。

4.是否支持數據庫異常崩潰後的安全恢復

MyISAM 不支持,而 InnoDB 支持。

使用 InnoDB 的數據庫在異常崩潰後,數據庫從新啓動的時候會保證數據庫恢復到崩潰前的狀態。這個恢復的過程依賴於 redo log

🌈 拓展一下:

  • MySQL InnoDB 引擎使用 redo log(重作日誌) 保證事務的持久性,使用 undo log(回滾日誌) 來保證事務的原子性
  • MySQL InnoDB 引擎經過 鎖機制MVCC 等手段來保證事務的隔離性( 默認支持的隔離級別是 REPEATABLE-READ )。
  • 保證了事務的持久性、原子性、隔離性以後,一致性才能獲得保障。

5.是否支持 MVCC

MyISAM 不支持,而 InnoDB 支持。

講真,這個對比有點廢話,畢竟 MyISAM 連行級鎖都不支持。

MVCC 能夠看做是行級鎖的一個升級,能夠有效減小加鎖操做,提供性能。

關於 MyISAM 和 InnoDB 的選擇問題

大多數時候咱們使用的都是 InnoDB 存儲引擎,在某些讀密集的狀況下,使用 MyISAM 也是合適的。不過,前提是你的項目不介意 MyISAM 不支持事務、崩潰恢復等缺點(但是~咱們通常都會介意啊!)。

《MySQL 高性能》上面有一句話這樣寫到:

不要輕易相信「MyISAM 比 InnoDB 快」之類的經驗之談,這個結論每每不是絕對的。在不少咱們已知場景中,InnoDB 的速度均可以讓 MyISAM 可望不可即,尤爲是用到了聚簇索引,或者須要訪問的數據均可以放入內存的應用。

通常狀況下咱們選擇 InnoDB 都是沒有問題的,可是某些狀況下你並不在意可擴展能力和併發能力,也不須要事務支持,也不在意崩潰後的安全恢復問題的話,選擇 MyISAM 也是一個不錯的選擇。可是通常狀況下,咱們都是須要考慮到這些問題的。

所以,對於我們平常開發的業務系統來講,你幾乎找不到什麼理由再使用 MyISAM 做爲本身的 MySQL 數據庫的存儲引擎。

鎖機制與 InnoDB 鎖算法

MyISAM 和 InnoDB 存儲引擎使用的鎖:

  • MyISAM 採用表級鎖(table-level locking)。
  • InnoDB 支持行級鎖(row-level locking)和表級鎖,默認爲行級鎖

表級鎖和行級鎖對比:

  • 表級鎖: MySQL 中鎖定 粒度最大 的一種鎖,對當前操做的整張表加鎖,實現簡單,資源消耗也比較少,加鎖快,不會出現死鎖。其鎖定粒度最大,觸發鎖衝突的機率最高,併發度最低,MyISAM 和 InnoDB 引擎都支持表級鎖。
  • 行級鎖: MySQL 中鎖定 粒度最小 的一種鎖,只針對當前操做的行進行加鎖。 行級鎖能大大減小數據庫操做的衝突。其加鎖粒度最小,併發度高,但加鎖的開銷也最大,加鎖慢,會出現死鎖。

InnoDB 存儲引擎的鎖的算法有三種:

  • Record lock:記錄鎖,單個行記錄上的鎖
  • Gap lock:間隙鎖,鎖定一個範圍,不包括記錄自己
  • Next-key lock:record+gap 臨鍵鎖,鎖定一個範圍,包含記錄自己

查詢緩存

執行查詢語句的時候,會先查詢緩存。不過,MySQL 8.0 版本後移除,由於這個功能不太實用

my.cnf 加入如下配置,重啓 MySQL 開啓查詢緩存

query_cache_type=1
query_cache_size=600000
複製代碼

MySQL 執行如下命令也能夠開啓查詢緩存

set global query_cache_type=1;
set global query_cache_size=600000;
複製代碼

如上,開啓查詢緩存後在一樣的查詢條件以及數據狀況下,會直接在緩存中返回結果。這裏的查詢條件包括查詢自己、當前要查詢的數據庫、客戶端協議版本號等一些可能影響結果的信息。所以任何兩個查詢在任何字符上的不一樣都會致使緩存不命中。此外,若是查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、MySQL 庫中的系統表,其查詢結果也不會被緩存。

緩存創建以後,MySQL 的查詢緩存系統會跟蹤查詢中涉及的每張表,若是這些表(數據或結構)發生變化,那麼和這張表相關的全部緩存數據都將失效。

緩存雖然可以提高數據庫的查詢性能,可是緩存同時也帶來了額外的開銷,每次查詢後都要作一次緩存操做,失效後還要銷燬。 所以,開啓查詢緩存要謹慎,尤爲對於寫密集的應用來講更是如此。若是開啓,要注意合理控制緩存空間大小,通常來講其大小設置爲幾十 MB 比較合適。此外,還能夠經過 sql_cache 和 sql_no_cache 來控制某個查詢語句是否須要緩存:

select sql_no_cache count(*) from usr;
複製代碼

事務

何爲事務?

一言蔽之,事務是邏輯上的一組操做,要麼都執行,要麼都不執行。

能夠簡單舉一個例子不?

事務最經典也常常被拿出來講例子就是轉帳了。假如小明要給小紅轉帳 1000 元,這個轉帳會涉及到兩個關鍵操做就是:

  1. 將小明的餘額減小 1000 元
  2. 將小紅的餘額增長 1000 元。

事務會把這兩個操做就能夠當作邏輯上的一個總體,這個總體包含的操做要麼都成功,要麼都要失敗。

這樣就不會出現小明餘額減小而小紅的餘額卻並無增長的狀況。

何爲數據庫事務?

數據庫事務在咱們平常開發中接觸的最多了。若是你的項目屬於單體架構的話,你接觸到的每每就是數據庫事務了。

平時,咱們在談論事務的時候,若是沒有特指分佈式事務,每每指的就是數據庫事務

那數據庫事務有什麼做用呢?

簡單來講:數據庫事務能夠保證多個對數據庫的操做(也就是 SQL 語句)構成一個邏輯上的總體。構成這個邏輯上的總體的這些數據庫操做遵循:要麼所有執行成功,要麼所有不執行

# 開啓一個事務
START TRANSACTION;
# 多條 SQL 語句
SQL1,SQL2...
## 提交事務
COMMIT;
複製代碼

另外,關係型數據庫(例如:MySQLSQL ServerOracle 等)事務都有 ACID 特性:

事務的特性

何爲 ACID 特性呢?

  1. 原子性Atomicity) : 事務是最小的執行單位,不容許分割。事務的原子性確保動做要麼所有完成,要麼徹底不起做用;
  2. 一致性Consistency): 執行事務先後,數據保持一致,例如轉帳業務中,不管事務是否成功,轉帳者和收款人的總額應該是不變的;
  3. 隔離性Isolation): 併發訪問數據庫時,一個用戶的事務不被其餘事務所幹擾,各併發事務之間數據庫是獨立的;
  4. 持久性Durabilily): 一個事務被提交以後。它對數據庫中數據的改變是持久的,即便數據庫發生故障也不該該對其有任何影響。

數據事務的實現原理呢?

咱們這裏以 MySQL 的 InnoDB 引擎爲例來簡單說一下。

MySQL InnoDB 引擎使用 redo log(重作日誌) 保證事務的持久性,使用 undo log(回滾日誌) 來保證事務的原子性

MySQL InnoDB 引擎經過 鎖機制MVCC 等手段來保證事務的隔離性( 默認支持的隔離級別是 REPEATABLE-READ )。

保證了事務的持久性、原子性、隔離性以後,一致性才能獲得保障。

併發事務帶來哪些問題?

在典型的應用程序中,多個事務併發運行,常常會操做相同的數據來完成各自的任務(多個用戶對同一數據進行操做)。併發雖然是必須的,但可能會致使如下的問題。

  • 髒讀(Dirty read): 當一個事務正在訪問數據而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時另一個事務也訪問了這個數據,而後使用了這個數據。由於這個數據是尚未提交的數據,那麼另一個事務讀到的這個數據是「髒數據」,依據「髒數據」所作的操做多是不正確的。
  • 丟失修改(Lost to modify): 指在一個事務讀取一個數據時,另一個事務也訪問了該數據,那麼在第一個事務中修改了這個數據後,第二個事務也修改了這個數據。這樣第一個事務內的修改結果就被丟失,所以稱爲丟失修改。 例如:事務 1 讀取某表中的數據 A=20,事務 2 也讀取 A=20,事務 1 修改 A=A-1,事務 2 也修改 A=A-1,最終結果 A=19,事務 1 的修改被丟失。
  • 不可重複讀(Unrepeatable read): 指在一個事務內屢次讀同一數據。在這個事務尚未結束時,另外一個事務也訪問該數據。那麼,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改致使第一個事務兩次讀取的數據可能不太同樣。這就發生了在一個事務內兩次讀到的數據是不同的狀況,所以稱爲不可重複讀。
  • 幻讀(Phantom read): 幻讀與不可重複讀相似。它發生在一個事務(T1)讀取了幾行數據,接着另外一個併發事務(T2)插入了一些數據時。在隨後的查詢中,第一個事務(T1)就會發現多了一些本來不存在的記錄,就好像發生了幻覺同樣,因此稱爲幻讀。

不可重複讀和幻讀區別:

不可重複讀的重點是修改好比屢次讀取一條記錄發現其中某些列的值被修改,幻讀的重點在於新增或者刪除好比屢次讀取一條記錄發現記錄增多或減小了。

事務隔離級別有哪些?

SQL 標準定義了四個隔離級別:

  • READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,容許讀取還沒有提交的數據變動,可能會致使髒讀、幻讀或不可重複讀
  • READ-COMMITTED(讀取已提交): 容許讀取併發事務已經提交的數據,能夠阻止髒讀,可是幻讀或不可重複讀仍有可能發生
  • REPEATABLE-READ(可重複讀): 對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改,能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生
  • SERIALIZABLE(可串行化): 最高的隔離級別,徹底服從 ACID 的隔離級別。全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀

隔離級別 髒讀 不可重複讀 幻讀
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL 的默認隔離級別是什麼?

MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀)。咱們能夠經過SELECT @@tx_isolation;命令來查看,MySQL 8.0 該命令改成SELECT @@transaction_isolation;

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
複製代碼

這裏須要注意的是:與 SQL 標準不一樣的地方在於 InnoDB 存儲引擎在 REPEATABLE-READ(可重讀) 事務隔離級別下使用的是 Next-Key Lock 鎖算法,所以能夠避免幻讀的產生,這與其餘數據庫系統(如 SQL Server)是不一樣的。因此說 InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀) 已經能夠徹底保證事務的隔離性要求,即達到了 SQL 標準的 SERIALIZABLE(可串行化) 隔離級別。

🐛 問題更正:MySQL InnoDB 的 REPEATABLE-READ(可重讀)並不保證避免幻讀,須要應用使用加鎖讀來保證。而這個加鎖度使用到的機制就是 Next-Key Locks。

由於隔離級別越低,事務請求的鎖越少,因此大部分數據庫系統的隔離級別都是 READ-COMMITTED(讀取提交內容) ,可是你要知道的是 InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀) 並不會有任何性能損失。

InnoDB 存儲引擎在 分佈式事務 的狀況下通常會用到 SERIALIZABLE(可串行化) 隔離級別。

🌈 拓展一下(如下內容摘自《MySQL 技術內幕:InnoDB 存儲引擎(第 2 版)》7.7 章):

InnoDB 存儲引擎提供了對 XA 事務的支持,並經過 XA 事務來支持分佈式事務的實現。分佈式事務指的是容許多個獨立的事務資源(transactional resources)參與到一個全局的事務中。事務資源一般是關係型數據庫系統,但也能夠是其餘類型的資源。全局事務要求在其中的全部參與的事務要麼都提交,要麼都回滾,這對於事務原有的 ACID 要求又有了提升。另外,在使用分佈式事務時,InnoDB 存儲引擎的事務隔離級別必須設置爲 SERIALIZABLE。

後記

最後再推薦一個很是不錯的 Java 教程類開源項目:JavaGuide 。我在大三開始準備秋招面試的時候,建立了 JavaGuide 這個項目。目前這個項目已經有 100k+的 star,相關閱讀:《1049 天,100K!簡單覆盤!》

對於你學習 Java 以及準備 Java 方向的面試都頗有幫助!正如做者說的那樣,這是一份:涵蓋大部分 Java 程序員所須要掌握的核心知識的 Java 學習+面試指南!

相關推薦:

我是 Guide哥,擁抱開源,喜歡烹飪。開源項目 JavaGuide 做者,Github:Snailclimb - Overview 。將來幾年,但願持續完善 JavaGuide,爭取可以幫助更多學習 Java 的小夥伴!共勉!凎!點擊查看個人2020年工做彙報!

參考

相關文章
相關標籤/搜索