MySQL面試知識點追命連環問(二)事務、索引及SQL優化

目錄

1. MySQL事務

2. MySQL索引

3. SQL優化

4.常見問題


​上次我們討論了MySQL的運行流程及原理,字段設計,存儲引擎和查詢緩存。

MySQL面試知識點追命連環問(一)

這次我們繼續來追命連環問關於事務,索引,SQL優化等相關的內容。準備好了嗎?

  • 事務

  • 索引

  • SQL優化

  • 常見問題

1. MySQL事務

面試官:你知道事務嗎?

我:知道。事務(Transaction)是訪問和更新數據庫的程序執行單元;

事務中可能包含一個或多個sql語句,這些語句要麼都執行,要麼都不執行。

事務主要有四大特性。即ACID:原子性,一致性,隔離性和持久性。

原子性:不可分割的操作單元,事務中所有操作,要麼全部成功;要麼回滾到執行事務之前的狀態。

一致性:在事務開始之前和事務結束以後,數據庫的完整性約束沒有被破壞。

隔離性:事務操作之間彼此獨立和透明互不影響。如果一個事務處理後的結果,影響了其他事務,那麼其他事務會撤回。

持久性:事務一旦提交,其結果就是永久的。即便發生系統故障,也能恢復。

面試官:嗯四大特性說的沒錯,那你知道高併發場景下事務可能會出現的問題嗎?

我:事務併發執行的話確實會產生一些問題。比如說:幻讀,髒讀,不可重複讀。因爲隔離性髒寫是不會發生的。

髒讀:一個事務讀取到另一個未提交事務修改的數據。

session A:查詢,得到某條數據
session B:修改某條數據,但是最後回滾掉啦
session A:在sessionB修改某條數據之後,在回滾之前,讀取了該條記錄

對於session A來說,讀到了session回滾之前的髒數據

不可重複讀:多次讀取的數據內容不一樣。

session A:查詢某條記錄
session B : 修改該條記錄,並提交事務
session A : 再次查詢該條記錄,發現前後查詢不一致

幻讀:前後多次讀取,數據總量不一樣。

session A:查詢表內所有記錄
session B : 新增一條記錄,並查詢表內所有記錄
session A : 再次查詢該條記錄,發現前後查詢不一致

面試管:那什麼情況下會出現這些問題呢?

MySQL標準中定義了四種隔離級別,並規定了每種隔離級別下上述幾個問題是否存在。

一般來說,隔離級別越低,系統開銷越低,可支持的併發越高,但隔離性也越差。隔離級別與讀問題的關係如下:

讀未提交:髒讀,不可重複讀,幻讀都有可能發生
讀已提交:不可重複讀,幻讀可能發生
可重複讀:幻讀可能發生
可串行化:都不可能發生

在實際應用中,讀未提交在併發時會導致很多問題,而性能相對於其他隔離級別提高卻很有限,因此使用較少。

可串行化強制事務串行,併發效率很低,只有當對數據一致性要求極高且可以接受沒有併發時使用,因此使用也較少。

因此在大多數數據庫系統中,默認的隔離級別是讀已提交(如Oracle)或可重複讀。

MySQL事務默認的隔離級別是可重複讀,而且MySQL可以解決了幻讀的問題。

面試官:看來你對事務理解的還不錯。那你知道MySQL的另一個重要特性索引嗎?

2. MySQL索引

答:索引就是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。索引的實現通常使用B樹及其變種B+樹。

在數據之外,數據庫維護這些原來快速查找的索引也是要付出代價的。一是增加了數據庫的存儲,二是在插入和修改數據時要花費較多的時間(因爲索引也要隨之變動)。

面試官:那索引是怎樣實現的呢?MyISAM和Innodb的實現方式一樣嗎?

答:不一樣的。MyISAM和Innodb雖然都使用B+樹作爲索引結構,但索引的實現方式還是不一樣的。

MyISAM的葉節點的data域存放的是數據記錄的地址,而Innodb數據文件本身就是索引文件。

MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

 

MyISAM索引

而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。

 

Innodb索引

因爲InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有)。

如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵,如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵。

B+樹是一種B樹的變種,爲有序數組鏈表+平衡多叉樹。基本和B樹類似,只有葉子節點存放數據,而且葉子節點之間通過指針相連。

面試官:那爲什麼索引用B+樹呢,B+樹有什麼優點呢?

1、 B+樹的磁盤讀寫代價更低:B+樹的內部節點並沒有指向關鍵字具體信息的指針,因此其內部節點相對B樹更小,如果把所有同一內部節點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多,一次性讀入內存的需要查找的關鍵字也就越多,相對IO讀寫次數就降低了。

2、由於B+樹的數據都存儲在葉子結點中,分支結點均爲索引,方便掃庫,只需要掃一遍葉子結點即可,但是B樹因爲其分支結點同樣存儲着數據,我們要找到具體的數據,需要進行一次中序遍歷按序來掃,所以B+樹更加適合在區間查詢的情況,所以通常B+樹用於數據庫索引。

 

 

面試官:那什麼是聚簇索引呢?

聚簇索引是一種數據存儲方式,它實際上是在同一個結構中保存了B+樹索引和數據行,InnoDB表是按照聚簇索引組織的。
InnoDB通過主鍵聚簇數據。他使用主鍵值的大小來進行記錄和頁的排序。葉子節點存儲的是完整的用戶記錄。

注:聚簇索引不需要我們顯示的創建,他是由InnoDB存儲引擎自動爲我們創建的。如果沒有主鍵,其也會默認創建一個。

 

但聚簇索引只有在搜索條件爲主鍵是才發揮作用,如果爲其他的字段就不行,這個時候就需要普通索引了。

二級索引的葉子節點不再是完整的數據記錄,而是字段和主鍵值。當需要這條記錄的其他字段時仍然需要根據這個主鍵id去查詢,這個步驟叫做回表。

聚簇索引表最大限度地提高了I/O密集型應用的性能,但它也有以下幾個限制:

  1. 插入速度嚴重依賴於插入順序,按照主鍵的順序插入是最快的方式,否則將會出現頁分裂,嚴重影響性能。因此,對於InnoDB表,我們一般都會定義一個自增的ID列爲主鍵。

  2. 更新主鍵的代價很高,因爲將會導致被更新的行移動。因此,對於InnoDB表,我們一般定義主鍵爲不可更新的。

  3. 二級索引訪問需要兩次索引查找,第一次找到主鍵值,第二次根據主鍵值找到行數據。

面試官:索引有哪些類型?索引越多越好嗎?

除了上面說的主鍵索引和普通索引,還有唯一索引,聯合索引和全文索引。

唯一索引:該列具有唯一性的同時又是索引,不允許重複。
全文索引:主要用於文本的查詢,它的出現是爲了解決WHERE name LIKE “%word%"這類針對文本的模糊查詢效率較低的問題。
聯合索引:對多列值進行一個索引,其效率大於索引合併。需遵循前綴原則。

建索引是有開銷的所以也不是越多越好,只要在需要的字段上建立索引。

第一,創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加。

第二,索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。

第三,當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。

索引的使用需要注意以下幾點:

1.最左前綴原則。一個聯合索引(a,b,c),如果有一個查詢條件有a,有b,那麼他則走索引,如果有一個查詢條件沒有a,那麼他則不走索引。

2.使用唯一索引。具有多個重複值的列,其索引效果最差。

3.不要過度索引。每個額外的索引都要佔用額外的磁盤空間,並降低寫操作的性能。在修改表的內容時,索引必須進行更新,有時可能需要重構,因此,索引越多,所花的時間越長。

4、索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);

5.一定要設置一個主鍵。前面聚簇索引說到如果不指定主鍵,InnoDB會自動爲其指定主鍵,這個我們是看不見的。反正都要生成一個主鍵的,還不如我們設置,以後在某些搜索條件時還能用到主鍵的聚簇索引。

6.主鍵推薦用自增id,而不是uuid。上面的聚簇索引說到每頁數據都是排序的,並且頁之間也是排序的,如果是uuid,那麼其肯定是隨機的,其可能從中間插入,導致頁的分裂,產生很多表碎片。如果是自增的,那麼其有從小到大自增的,有順序,那麼在插入的時候就添加到當前索引的後續位置。當一頁寫滿,就會自動開闢一個新的頁。

索引禁忌:

  • 不在低區分度的列上建⽴索引,例如“性別”

  • 儘量避免%前導的查詢,如like “%ab”

  • 儘量避免負向查詢,如not in/like

  • 避免全表掃描和頻繁的回表操作

面試官:看來你對索引掌握的很不錯啊,那你平常遇到慢查詢是怎麼優化的呢?

3. SQL優化

SQL語句從客戶端經由網絡協議到查詢緩存,如果沒有命中緩存,再經過解析工作,得到準確的SQL然後再來到優化器。

首先,我們知道每一條SQL都有不同的執行方法,要不通過索引,要不通過全表掃描的方式。

影響SQL速度的主要在I/O成本和CPU成本的消耗上。

數據存儲在硬盤上,我們想要進行某個操作需要將其加載到內存中,這個過程的時間被稱爲I/O成本。在內存對結果集進行排序的時間被稱爲CPU成本。

所以進行sql優化首先進行索引優化,讓我們的sql語句儘量走索引而不是走全表掃描的方法。

在平常遇到慢查詢時首先去分析慢查詢日誌,找出慢查詢的sql。然後針對這些sql進行分析。常見慢查詢主要有以下幾種情況:

  1. 索引沒起作用。字段沒建立索引,或者是索引沒有起作用。使用了like關鍵字或使用了多列索引的查詢語句。

     

  2. 數據庫結構不合理。合理的數據庫結構不僅可以使數據庫佔用更小的磁盤,也可以讓sql執行速度更快。一可以將字段很多的表拆解成多個表。二增加中間表。

     

  3. 分解關聯查詢。將大查詢分成多個小查詢。

     

  4. 優化limit分頁。當偏移量非常大時會導致前面查詢到的無用數據都要捨棄掉,如果表非常大,且篩選字段沒有合適的索引,那麼這樣的代價是非常高的。如我們下一次的查詢能從前一次查詢結束後標記的位置開始查找,那將節省很多開銷。

4.常見問題

問題一:嗯現在我們來看看具體問題,那你看這條語句會用到索引嗎?

以下語句是否會應用索引:SELECT FROM users WHERE YEAR(adddate) < 2007;

答:不會,因爲只要列涉及到運算,MySQL就不會使用索引。

 

問題二:那如果列值爲NULL時,查詢是否會用到索引?

在MySQL裏NULL值的列也是走索引的。當然,如果計劃對列進行索引,就要儘量避免把它設置爲可空,MySQL難以優化引用了可空列的查詢,它會使索引、索引統計和值更加複雜。

 

問題三:索引一定會提高速度嗎?

通常,通過索引查詢數據比全表掃描要快。但是我們也必須注意到它的代價。

索引需要空間來存儲,也需要定期維護, 每當有記錄在表中增減或索引列被修改時,索引本身也會被修改. 這意味着每條記錄的INSERT,DELETE,UPDATE將爲此多付出4,5 次的磁盤I/O. 因爲索引需要額外的存儲空間和處理,那些不必要的索引反而會使查詢反應時間變慢。使用索引查詢不一定能提高查詢性能。

 

問題四:如何查詢第n高的工資?

SELECT DISTINCT(salary) from employee ORDER BY salary DESC LIMIT n-1,1

 

問題五:一個6億的表a,一個3億的表b,通過外間tid關聯,你如何最快的查詢出滿足條件的第50000到第50200中的這200條數據記錄。

1、如果A表TID是自增長,並且是連續的,B表的ID爲索引

select * from a,b where a.tid = b.id and a.tid>500000 limit 200;


2、如果A表的TID不是連續的,那麼就需要使用覆蓋索引。TID要麼是主鍵,要麼是輔助索引,B表ID也需要有索引。

select * from b , (select tid from a limit 50000,200) a where b.id = a .tid;

 

好啦,今天的追命連環問就到這裏了,下次繼續,如對文章有疑惑或補充的地方歡迎留言交流(●'◡'●)。原創不易,如果對你有幫助的話歡迎點贊!

 

                                                                                    相關推薦閱讀

                                                                你知道一次完整的HTTP請求過程嗎

                                                 Redis常見面試題連環問,你能回答到第幾問?(上)

                                                 大廠高頻面試題:高併發下接口冪等性的解決方案?

                                                           

 

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