可能是全網最好的MySQL重要知識點、面試題總結

什麼是MySQL?

MySQL是一種關係型數據庫,在Java企業級開發中非常常用,因爲MySQL是開源免費的,並且方便擴展。阿里巴巴數據庫系統也大量用到了MySQL,因此它的穩定性是有保障的。MySQL是開放源代碼的,因此任何人都可以在GPL(通用公共許可證)的許可下下載並根據個性化的需要進行修改。MySQL的交替端口號是3306

事務相關

什麼是事務?

事務是邏輯上的一組操作,要麼都執行,或者都不執行。

如果小小要給小紅轉賬1000元,這個轉賬會涉及到兩個關鍵操作就是:將小明的餘額減少1000元,將小紅的餘額增加1000元。萬一在這兩個操作之間突然出現錯誤範例銀行系統崩潰,導致小明餘額減少而小紅的餘額沒有增加,這樣就不對了。要失敗。

事物的四大特性(ACID)介紹一下?

事物的特性

  1. 原子性:  事務是最小的執行單位,可以分割。事務的原子性確保動作完全完成,從而完全不起作用;

  2. 一致性:  執行事務前後,數據保持一致,多個事務對同一個數據讀取的結果是相同的;

  3. 隔離性:  併發訪問數據庫時,一個用戶的事務不被其他事務所幹擾,各併發事務之間數據庫是獨立的;

  4. 持久性:  一個事務被提交之後。它對數據庫中數據的改變是持久的,即使數據庫發生故障也不應該發生任何影響。

併發事務帶來什麼問題?

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

  • 髒讀(Dirty read):  當一個事務正在訪問數據和對數據進行了修改,而這種修改還沒有提交到數據庫中,這時另外一個事務也訪問了這個數據,然後使用了這個數據。因爲這個數據是還沒有提交的數據,那麼另外一個事務讀到的這個數據是“髒數據”,依據“髒數據”進行的操作可能是不正確的。

  • 丟失修改(丟失):  指在一個事務讀取一個數據時,另外一個事務也訪問了該數據,然後在第一個事務中修改了這個數據後,第二個事務也修改了這個數據。這樣的第一個事務內部的修改結果就被丟失,因此稱爲丟失修改。例如:事務1讀取某表中的數據A = 20,事務2也讀取A = 20,事務1修改A = A- 1,事務2也修改A = A-1,最終結果A = 19,事務1的修改被丟失。

  • 不可重複讀(Unrepeatableread):  指在一個事務內部多次讀同一數據。在這個事務還沒有結束時,另一個事務也訪問該數據。那麼,在第一個事務中的多個讀數據之間,由於第二個事務的修改導致第一個事務重新插入的數據可能不太相同。然後發生了在一個事務內兩次讀到的數據是不一樣的情況,因此稱爲不可重複讀。

  • 幻讀(Phantom read):  幻讀與不可重複讀類似。它發生在一個事務(T1)讀取了幾行數據,接着另一個併發事務(T2)插入了一些數據時。在隨後的查詢中,第一個事務(T1)就會發現多了一些原本不存在的記錄,就好像發生了幻覺一樣,所以稱爲幻讀。

不可重複度和幻讀區別:

不可重複讀的重點是修改,幻讀的重點在於添加或刪除。

例1(同樣的條件,你讀取過的數據,再次重新出來發現值不一樣了):事務1中的A先生讀取自己的工資爲1000的操作還沒完成,事務2中的B先生就修改了A的工資爲2000,導致A再讀自己的工資時工資換算2000;這就是不可重複讀。

例2(同樣的條件,第1次和第2次糾正來的記錄數不一樣):假某工資單表中工資大於3000的有4人,事務1讀取了所有工資大於3000的人,共查到4條記錄,這時事務2又插入了一條工資大於3000的記錄,事務1再讀時查到的記錄就變成了5條,這樣就導致了幻讀。

事務隔離等級有什麼?MySQL的最小隔離等級是?

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

  • READ-UNCOMMITTED(讀取未提交):  最低的隔離級別,允許重新讀取未提交的數據變更,可能會導致髒讀,幻讀或不可重複讀

  • READ-COMMITTED(讀取已提交):  允許讀取併發事務已經提交的數據,可以阻止髒讀,但是幻讀或不可重複讀卻可能發生

  • REPEATABLE-READ(可重複讀):  對同一片段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止髒讀和不可重複讀,但幻讀仍有可能發生

  • SERIALIZABLE(可串行化):  最高的隔離等級,完全服從ACID的隔離等級。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,從而,該等級可以防止髒讀,不可重複讀以及幻讀

6914_101_18uv5wMruvcDkILz

MySQL InnoDB存儲引擎的默認支持的隔離級別是REPEATABLE-READ(可重讀)。我們可以通過SELECT @@tx_isolation;命令來查看

mysql> SELECT @@ tx_isolation;

這裏需要注意的是:與SQL標準不同的地方在於InnoDB存儲引擎在REPEATABLE-READ(可重讀)事務隔離級別下使用的是Next-Key Lock鎖算法,因此可以避免幻讀的產生,這與其他數據庫所以說InnoDB存儲引擎的替換支持的隔離級別是REPEATABLE-READ(可重讀)已經可以完全保證事務的隔離性要求,即達到了SQL標準的SERIALIZABLE(可串行)。化)隔離等級。

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

InnoDB存儲引擎在分佈式事務的情況下一般將用到SERIALIZABLE(可串行化)隔離等級

索引相關

爲什麼索引能提高查詢速度

先從MySQL的基本存儲結構說起

MySQL的基本存儲結構是頁(記錄都存在頁裏邊):

webp


webp

圖片

  • 各個數據頁可以組成一個雙向鏈表

  • 每個數據頁中的記錄又可以組成一個單向鏈表

-每個數據頁都會爲存儲在其裏邊兒的記錄生成一個頁面目錄,在通過主鍵查找某條記錄的時候可以在頁目錄中使用二分法快速定位到對應的插槽,然後再遍歷該插槽對應分組中的記錄即可快速找到指定的記錄

所以說,如果我們寫select * from user where indexname ='xxx'這樣沒有進行任何優化的sql語句,替換會生成:

  1. 定位到記錄所在的頁:需要遍歷雙向鏈表,找到所在的頁

  2. 從所在的頁面內中查找相應的記錄:由於不是根據主鍵查詢,只能遍歷所在頁面的單鏈表了

很明顯,在數據量很大的情況下這樣查找會很慢!這樣的時間複雜度爲O(n)。

索引就是些什麼什麼可以讓我們查詢加快速度呢?****其實就是將無序的數據變成有序(相對):

webp


要找到id爲8的記錄簡要步驟:

webp


很明顯的是:沒有用索引我們是需要遍歷雙向鏈表來定位對應的頁,現在通過“目錄”就可以很快地定位到對應的頁面上了!(二分查找,時間複雜度近似爲O(logn ))

其實實質上結構就是B +樹,B +樹作爲樹的一種實現,能夠讓我們很快地尋找出對應的記錄。

什麼是最左預設原則?

MySQL中的索引可以以一定順序引用多列,這種索引叫作聯合索引。如用戶表的名稱和城市加聯合索引就是(name,city),而最左上方原則指的是,如果查詢的時候查詢條件精確匹配索引的左邊連續一列或幾列,則此列就可以被用到。如下:

從name = xx和city = xx的用戶中選擇*;//可以命中索引

這裏需要注意的是,查詢的時候如果兩個條件都用上了,但是順序不同,如city= xx and name =xx,那麼現在的查詢引擎會自動優化爲匹配聯合索引的順序,這樣是能夠命中索引的。

由於最左上端原則,在創建聯合索引時,索引分段的順序需要考慮分段值去重之後的個數,而不是放的前面。ORDERBY子句也遵循此規則。

請注意避免索引

冗餘索引指的是索引的功能相同,能夠命中中肯定肯定能命中,然後就是冗餘索引如(name,city)和(name)這兩個索引就是多餘索引,能夠命中另外的查詢肯定是能夠命中中前者的在大多數情況下,都應該儘量擴展現有的索引而不是創建新索引。

MySQLS.7版本後,可以通過查詢sys庫的schema_redundant_indexes表來查看冗餘索引

Mysql如何爲表前綴添加索引?

1.添加PRIMARY KEY(主鍵索引)

ALTER TABLE`table_name`添加主鍵(`column`)

2.添加UNIQUE(唯一索引)

ALTER TABLE`table_name` ADD UNIQUE(`column`)

3.添加INDEX(普通索引)

ALTER TABLE`table_name`添加索引index_name(`column`)

4.添加FULLTEXT(全文索引)

ALTER TABLE`table_name` ADD FULLTEXT(`column`)

5.添加多列索引

ALTER TABLE`table_name`添加索引index_name(`column1`,`column2`,`column3`)

存儲引擎

一些常用命令

查看MySQL提供的所有存儲引擎

mysql>顯示引擎;

webp

圖片

從上圖我們可以查看出MySQL當前默認的存儲引擎是InnoDB,並且在5.7版本所有的存儲引擎中只有InnoDB是事務性存儲引擎,而只有InnoDB支持事務。

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

我們也可以通過下面的命令查看默認的存儲引擎。

mysql>顯示類似'%storage_engine%'的變量;

查看錶的存儲引擎

顯示錶狀態,例如“ table_name”;

webp

圖片

MyISAM和InnoDB區別

MyISAM是MySQL的默認數據庫引擎(5.5版之前)。雖然性能極佳,而且提供相對的特性,包括全文索引,壓縮,空間函數等,但MyISAM不支持事務和行級鎖,而且最大的缺陷就是崩潰,無法安全恢復。不過,5.5版本之後,MySQL日期已過InnoDB(事務性數據庫引擎),MySQL 5.5版本後更改的存儲引擎爲InnoDB。

大多數時候我們使用的都是InnoDB存儲引擎,但是在某些情況下使用MyISAM也是合適的那種讀讀密集的情況下。(如果你不介意MyISAM崩潰回覆問題的話)。

兩者的對比:

  1. 是否支持行級鎖:MyISAM只有表級鎖(表級鎖),而InnoDB支持行級鎖(行級鎖)和表級鎖,可以爲行級鎖。

  2. 是否確實支持事務和崩潰後的安全恢復:**** MyISAM註釋的是性能,每次查詢具有原子性,其執行比InnoDB類型轉換,但是不提供事務支持。但是InnoDB提供事務支持事務,外部鍵等高級數據庫功能。具有事務(提交),回滾(回滾)和崩潰恢復能力(崩潰恢復功能)的事務安全(事務安全(符合ACID))型表。

  3. 是否支持外鍵: MyISAM不支持,而InnoDB支持。

  4. 解決高併發事務,MVCC比單純的加鎖更高效; MVCC只在READ COMMITTEDREPEATABLE READ兩個隔離等級下工作; MVCC可以使用樂觀(樂觀)鎖和悲觀(悲觀)鎖來實現;各數據庫中MVCC實現並不統一。

  5. ......

《 MySQL高級》上面有些句話這樣寫到:

在很多我們已知場景中,InnoDB的速度都可以讓MyISAM望塵莫及,尤其是用到了聚簇索引,或者需要訪問的數據都可以加入內存的應用。

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

樂觀鎖與悲觀鎖的區別

悲觀鎖

總是假設最壞的情況,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會變成直到直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完後再把資源轉讓給其它線程)。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖, Java中synchronizedReentrantLock等獨佔鎖就是悲觀鎖思想的實現。寫鎖等,都是在做操作之前先上鎖

樂觀鎖

總是假設最好的情況,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似於write_condition機制,其實都是提供的樂觀鎖。在Java的中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。

兩種鎖的使用場景

從上面對兩種鎖的介紹,我們知道兩種鎖各有優缺點,不可認爲一種好於另一種,像樂觀鎖適用於寫比較少的情況下(多讀場景),即衝突真但如果是多寫的情況,一般會經常產生衝突,這就會導致上層應用會不斷的進行重試,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適。

樂觀鎖常見的兩種實現方式

樂觀鎖一般會使用版本號機制或CAS算法實現。

1.版本號機制

一般是在數據表中加上一個數據版本號版本劃分,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取版本值,在提交更新時,若先前重新到的版本轉換到當前數據庫中的版本值替代時才更新,否則重試更新操作,直到更新成功。

舉一個簡單的例子:假設數據庫中帳戶信息表中有一個版本,則是初始值1;而當前帳戶餘額分配(餘額)爲$ 100。

  1. 操作員此時將其糾正(version = 1),並從其帳戶餘額中扣除50(100- $ 50)。

  2. 在操作員A操作的過程中,操作員B也讀入此用戶信息(version = 1),並從其帳戶餘額中扣除20(100- $ 20)。

  3. 操作員A完成了修改工作,將數據版本號加一(version = 2),加上帳戶替換後餘額(balance = $ 50),提交至數據庫更新,此時提交數據版本大於數據庫記錄當前版本,數據被更新,數據庫記錄版本更新爲2。

  4. 操作員B完成了操作,也將版本號加一(version = 2)試圖向數據庫提交數據(balance = $ 80),但此時比對數據庫記錄版本時發現,操作員B提交的數據版本號爲2 ,數據庫記錄當前版本也爲2,不滿足“提交版本必須大於記錄當前版本才能執行更新”的樂觀鎖策略,因此,操作員B的提交被駁回。

這樣,就避免了操作員B用基於version = 1的舊數據修改的結果覆蓋操作員A的操作結果的可能。

2. CAS算法

比較和交換(比較與交換),是一種有名的無鎖算法。無鎖編程,即不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被大量的情況下實現變量的同步,所以也叫非雙重同步(Non-blocking Synchronization)。CAS算法涉及到三個操作數

  • 需要讀寫的內存值V

  • 進行比較的值A

  • 擬寫入的新值B

當且僅當V的值等於A時,CAS通過原子方式用新值B來更新V的值,否則不會執行任何操作(比較和替換是一個原子操作)。一般情況下是一個自旋操作,即不斷的重試

樂觀鎖的缺點

ABA問題是樂觀鎖一個常見的問題

1 ABA

如果一個變量V初次讀取的時候是A值,並且在準備賦值的時候檢查到它仍然是A值,那我們可以說明它的值沒有被其他線程修改過了嗎?很明顯是不能的,因爲在這段時間它的值可能被替換爲其他值,然後又改回A,那CAS操作就會誤認爲它從來沒有被修改過。這個問題被稱爲CAS操作的“ ABA”問題。

JDK 1.5以後的AtomicStampedReference 類就提供提供了能力,其中的compareAndSet 方法就是首先檢查當前引用是否等於預期引用,並且內部標誌是否等於預期標誌,如果全部替換,則以原子方式引用和該標誌的值設置爲給定的更新值。

2循環時間長開支大

自旋CAS(也就是不成功就一直循環執行直到成功)如果持續不成功,會給CPU帶來非常大的執行開銷。如果JVM能支持處理器提供的暫停指令那麼效率會有一定的提升,暫停指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間至少具體實現的版本,在某些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序衝突(內存順序衝突)而引起CPU流水線被清空(CPU管道刷新),從而提高CPU的執行效率。

3只能保證一個共享變量的原子操作

但是從JDK 1.5開始,提供了AtomicReference類保證引用引用對象之間的原子性,你可以把多個變量放在一個對象裏來進行CAS操作。所以我們可以使用鎖或利用AtomicReference類把多個共享變量合併成一個共享變量來操作。

鎖機制與InnoDB鎖算法

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

  • MyISAM採用表級鎖(表級鎖定)。

  • InnoDB支持行級鎖(行級鎖)和表級鎖,替代爲行級鎖

表級鎖和行級鎖對比:

  • 表級鎖: Mysql中鎖定粒度最大的一種鎖,對當前操作的整張表加鎖,實現簡單,資源消耗也比較少,加鎖快,不會出現死鎖。其鎖定粒度最大,觸發鎖衝突的概率最高,併發度最低,MyISAM和InnoDB引擎都支持表級鎖。

  • 行級鎖: Mysql中鎖定粒度最小的一種鎖,僅針對當前操作的行進行加鎖。行級鎖能大大減少數據庫操作的衝突。其加鎖粒度最小,併發度高,但加鎖的開銷也最大,加鎖慢,會出現死鎖。

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

  • 記錄鎖:單個行記錄上的鎖

  • 間隙鎖:間隙鎖,鎖定一個範圍,不包括記錄本身

  • 下一鍵鎖定:記錄+間隙鎖定一個範圍,包含記錄本身

相關知識點:

  • innodb對於行的查詢使用下鍵鎖定

  • 下一步鎖定鍵解決幻影問題幻讀問題

  • 當查詢的索引包含唯一屬性時,將下一鍵鎖定降級爲記錄鍵

  • Gap鎖設計的目的是爲了阻止多個事務將記錄插入到同一範圍內,而這會導致幻讀問題的產生

  • 有兩種方式顯式關閉間隙鎖:(除了外鍵約束和唯一性檢查外,其餘情況僅使用記錄鎖)A.將事務隔離級別設置爲RC B.將參數innodb_locks_unsafe_for_binlog設置爲1

大表優化

當MySQL單表記錄數過大時,數據庫的CRUD性能會明顯下降,一些常見的優化措施如下:

1.限定數據的範圍

最小:我們當用戶在查詢訂單歷史的時候,我們可以控制在一個月的範圍內;

2.讀/寫分離

經典的數據庫分解方案,主庫負責寫,從庫負責讀;

3.垂直分區

根據數據庫裏面數據表的相關性進行拆分例如,用戶表中既有用戶的登錄信息又有用戶的基本信息,可以將用戶表拆分成兩個單獨的表,甚至放到單獨的庫做分庫。

簡單來說垂直細分是指數據表列的分解,把一張列比較多的表分開爲多張表。如下圖所示,這樣來說大家應該就更容易理解了。

webp

圖片

  • 垂直細分的優點:可以使列數據變小,在查詢時減少重新讀取的塊數,減少I / O次數。體積,垂直分區可以簡化表的結構,便於維護。

  • 垂直分割的缺陷:主鍵會出現冗餘,需要管理冗餘列,並會引起Join操作,可以通過在應用層進行Join來解決。

4.水平分區

****這樣的每片數據分散到不同的表或者庫中,達到了分散的目的。****水平隔開可以支撐非常大的數據量。

水平分割是指數據錶行的分解,表的行數超過200萬行時,就會變慢,這時可以把一張表的數據拆成多張表來放置。舉個例子:我們可以將用戶信息表分解成多個用戶信息表,這樣就可以避免單個表數據量過大對性能造成影響。

webp


需要注意的一點是:分表選定解決了單個表數據過大的問題,但由於表的數據還是在同一臺機器上,實際上對於提升MySQL併發能力沒有什麼意義,所以水平拆分最好分庫。拆分水平能夠請立即獲取iTunes非常大的數據量存儲,應用端改造也少,但分片事務難以解決,跨節點加入性能較差,邏輯複雜。“Java的工程師修煉之道》的作者推薦儘量不要對數據進行分片,因爲拆分會帶來邏輯,部署,運維的各種複雜度,一般的數據表在優化得當的情況下支撐千萬以下的數據量是沒有太大問題的。如果實在要分片,請選擇客戶端分片架構,這樣可以減少一次和中間件的網絡I / O。下面補充一下數據庫分片的兩種常見方案:


  • 客戶端代理: 分片邏輯在應用端,封裝在jar包中,通過修改或封裝JDBC層來實現。噹噹網的Shading -JDBC,阿里的TDDL是兩種比較常用的實現。

  • 中間件代理: 。在應用和數據中間加了一個代理層****片分邏輯統一維護在中間件服務中我們現在談的Mycat,360的阿特拉斯,網易的DDB等等都是這種架構的實現。

話不多說,讀者福利!

二,面試合集精選

關注我加微信“ haolagui521”註釋脈脈領取以上架構視頻,一些電子書籍,面試文檔。

2020到來!一到五年Java工程師想跳槽,大環境不好,怎麼破?


三,p6〜p7面試合集

關注我加微信“ haolagui521”註釋脈脈領取以上架構視頻,一些電子書籍,面試文檔。

2020到來!一到五年Java工程師想跳槽,大環境不好,怎麼破?


四,架構師進階合集

關注我加微信“ haolagui521”註釋脈脈領取以上架構視頻,一些電子書籍,面試文檔。

2020到來!一到五年Java工程師想跳槽,大環境不好,怎麼破?


五,在線技術書籍

關注我加微信“ haolagui521”註釋脈脈領取以上架構視頻,一些電子書籍,面試文檔。

2020到來!一到五年Java工程師想跳槽,大環境不好,怎麼破?


關注我加微信“ haolagui521”註釋脈脈領取以上架構視頻,一些電子書籍,面試文檔。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章