java架構之路-(mysql底層原理)Mysql之讓咱們再深擼一次mysql

  讓我再深擼一次mysql吧,此次主要以應對面試來講說mysql,大概幾個方向,索引結構,查詢引擎,索引優化,explain的詳解和trace工具的使用。mysql

索引:面試

咱們先來看一下mysql的B+tree,本文幾乎都在圍繞這個圖來講的。sql

mysql的底層是使用B+tree來存儲數據的,和B+tree有一點點不一樣的是葉子節點是雙向鏈表的結構,並非圖內的單向指針的。且null值放置在葉子節點的最前面。這個是主鍵索引。json

下面我來看一下聯合索引,好比咱們如今有Student表,將name,age,address三個字段設置成聯合索引,這時存儲的節點變爲先按照name排序,name一致按照age排序的B+tree,攜帶數據爲主鍵ID,並不攜帶總體數據的。session

查詢引擎:分佈式

  咱們常見的查詢引擎主要是InnoDB還有MyISAM,區別主要是,MyISAM存儲B+tree的索引攜帶數據都是內存地址,咱們在查詢的時候須要拿到hash計算後的內存地址,而後回行獲得數據,而InnoDB直接攜帶數據,不須要回行,MyISAM不支持事物,不支持外鍵,也不支持行級鎖,對於數據的非範圍查詢效率可能要高於InnoDB,且在底層有維護count總條數的內存,可是MyISAM的範圍查詢是不能用到索引的。咱們大部分使用的都是InnoDB查詢引擎,順便提一下,MyISAM在磁盤上的文件爲三個,一個是表的結構,一個是索引文件,一個是真正的數據文件,InnoDB在磁盤上存的是兩個文件,一個是表結構文件,兩一個是索引和數據文件。函數

explain的詳解:工具

咱們執行性能

explain select * from student;

這時會有十列數據,分別的是id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra。咱們來逐個說一下他們都是幹啥的。深刻理解explain和B+tree的使用,mysql面試也就有救了。優化

id:就是一個編號,同時也表明了select的執行順序,通常來講,咱們有幾個select就有幾行數據,他們可能擁有相同或者不一樣的ID,執行順序爲ID大的優先執行,id相同,從上到下執行。id爲null的最後執行。

select_type:表明咱們的執行是一個什麼樣子的SQL,是簡單查詢啊,仍是連表查詢,大體能夠分爲

simple:簡單查詢,好比explain select * from table;

primary:複雜查詢的最外層查詢,(嵌套查詢的最外層)好比explain select 1 from (select * from table) t;

subquery:子查詢的select,但不表示在from後面的查詢,好比explain select (select 1 from table) from table;或者explain select * from table where id = (select 1 from table where id = 2)

derived:和上面的subquery是相對的,表示在外層from後面的子查詢。好比explain select * from (select * from table) t;

union:聯合查詢,explain select * from table union select * from table;

union result:表示聯合查詢後的組合,並不表明實際的select。

table:表明你查詢的是哪一張表,若是表你給予了別名,這裏會顯示別名,會顯示<derivedn>,標紅色的n爲執行計劃裏的id列。還有某些時候會顯示<union1,2>,也就是說,咱們合併id爲1和2的結果虛擬表。有時候還會顯示null,例如EXPLAIN SELECT 1 

type(超級重要)判斷你SQL的運行效率的參數, 依次從最優到最差分別爲:system > const > eq_ref > ref > range > index > ALL

system通常是表爲空,或者表裏只有一行數據。 性能也是最好的,用左腳腳指頭想一想,數據都爲空,或者只要一行,查詢必定不會慢到哪裏去。

const:用到了主鍵索引的查詢,效率依然給力。主鍵索引葉子節點直接帶着數據呢,不須要再去掃描第二顆樹,效果必定給力了。

eq_ref:eq比較啊。因此簡單的sql不會出現這個玩意。例如explain select * from table t innter join table2 t2 on t.pid = t2.id;也就是說該select下關聯的必定是主鍵id,效率也是很OK的,後面會說一下innter join的查詢機制。在trace的使用會說的,別慌,乾貨面試還沒到來。

ref:相比eq_ref來講比較好記憶的,仍是比較,也就是非主鍵索引的比較。例如explain select * from table t innter join table2 t2 on t.pid = t2.name;仍是走索引的性能仍是能夠的。說明一下,這個type爲ref,簡單查詢也能夠出現,不必定是兩張表關聯纔會出現的。

range:範圍查詢,也能夠理解爲索引範圍查詢。

index:掃描全表索引,比All強一點,說完All會舉例。

All:垃圾了,全表掃描。

例如:explain SELECT classNum from  student; 就是一個index查詢,由於classNum是一個非主鍵索引,因此在咱們的節點上存儲的,不須要攜帶id再次去第二個上掃描。

   explain SELECT classNum,create_time from  student;就是一個all查詢,由於classNum雖然是一個非主鍵索引,能夠拿到classNum的數據,可是咱們卻得不到create_time的數據,其實也能夠經過classNum的索引樹獲得id,而後再拿着id去主鍵索引樹上找create_time,須要查找兩顆樹,代價太大了,mysql底層幫咱們優化了,在這裏說一下啊。sql具體走不走索引和表內數據量一點關係都沒有,不是說表裏的數據大,就必定走索引或者不走索引,後面咱們在trace會具體分析。

possible_keys:可能用到的索引。好比主鍵索引,非主鍵聯合索引。

key:實際用到的索引列。
key_len:實際使用的索引長度,在聯合索引用處仍是比較大的,根據長度能夠判斷出來到底走了聯合索引裏面的幾個字段。計算公式以下,
char(n):3n字節長度,
varchar(n):2字節存儲字符串長度,若是是utf-8,則長度 3n +2,
tinyint:1字節
smallint:2字節
int:4字節
date:3字節
timestamp:4字節
datetime:8字節
Mysql版本不一樣計算會有所不一樣,可是都差很少的。
ref:這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常量),或者字段名。
rows:mysql預估的檢測行數,不是最終查詢到的行數,也不是表裏一共有多少數據。只是一個預估值。
Extra(超級重要):這個字段可能性太多太多了,你們能夠去閱讀官方文檔,在這裏我簡單說幾個最多見的。
Using index:使用覆蓋索引,好比:explain SELECT id  from  student where id  = 2
Using where:使用 where 語句來處理結果,查詢的列未被索引覆蓋,好比:explain SELECT *  from  student where name  = '2'
Using index condition:查詢的列不徹底被索引覆蓋,where條件中是一個前導列的範圍;好比:explain SELECT *  from  student where name  = '2' and stuNum=2
Using temporary:mysql須要建立一張臨時表來處理查詢。出現這種狀況通常是要進行 優化的,首先是想到用索引來優化。例如:explain SELECT DISTINCT create_time  from student t;
Using filesort:將用外部排序而不是索引排序,數據較小時從內存排序,不然須要在磁盤 完成排序。這種狀況下通常也是要考慮使用索引來優化的。例如:explain SELECT  create_time  from student t order by create_time ;
Using filesort這個詳細記錄一下,後面經過題目來講明具體細節實現的。
  explain的工具大概就這麼多東西了,經過執行計劃咱們能夠獲得一大部分sql的執行過程,咱們還可使用trace來具體看一下是否須要走索引,掃描一個樹,和掃描兩個樹對查詢的影響。
trace
首先咱們先打開trace。
set session optimizer_trace="enabled=on",end_markers_in_json=on

直接在mysql控制檯運行就OK的,平時沒事別開這個玩意,會對性能有影響的。

而後咱們運行sql;在後面加上SELECT * FROM information_schema.OPTIMIZER_TRACE;例如:

select * from student WHERE name > '張三';
SELECT * FROM information_schema.OPTIMIZER_TRACE;

這時咱們看結果2中會有有這樣一個數據

咱們來主要看第二列,TRACE,複製出來弄到json解析器內。

而後咱們查找一下cost這個參數,cost就是咱們使用各個索引的一個指標,越大表示越差,只在一個sql內比較,不要在兩個不一樣的sql比較啊。咱們來看一下個人cost

這個是全表掃描大概是4.1。而後我繼續向下看。

這有還有一個使用name_num_address這個聯合索引的cost爲3.41要比前面的4.1好,那麼mysql選擇走name_num_address聯合索引。我這還有一個事例。EXPLAIN select * from student WHERE name > 'a';

正常來講,name是一個聯合索引,咱們拿按照name去範圍查找,type列應該爲range,其實否則,mysql並無選擇走任何索引。能夠本身嘗試用trace去看看執行過程。

因爲我愛動mysql的默認配置,這裏簡單說一下using filesort排序,底層分爲兩中排序方式,一種是單路排序,也能夠理解爲一次排序或者叫非回溯排序,就是你的查詢結果足夠小(小於1024字節),mysql有一個

max_length_for_sort_data 的參數默認爲1024字節,咱們就將咱們要排序的結果集拿到sort buffer中進行排序,若是大於1024字節,放不下啦,也就是雙路排序,也能夠叫回溯排序,就是咱們只拿着須要排序的字段和惟一標識的id到sort buffer中進行排序,排序之後再回去找他們對應的數據,

這個就是雙路排序,使用trace工具能夠看到不一樣的using filesort,能夠本身嘗試。輸入set max_length_for_sort_data = 字節數,能夠本身更改這個參數,最好沒事別動這個玩意。讓DBA調,咱們只是知道這麼回事,太底層的還沒深刻研究。

  貌似說了這麼多能夠出幾個面試題來聊聊了。

一、咱們InnoDB的主鍵用數字自增好,仍是UUID好?

答:固然是數字的好,仍是回到咱們的B+tree,這顆樹是按照由小到大,由左向右來排列的。咱們的數字便於咱們去比較,UUID比較起來是很耗力耗時的。並且UUID比較佔地方,mysql的B+tree的每一個節點16KB大小,咱們用數字類型,可在有限的空間內,有限的層級,存儲更多的數據(其實沒啥用了,三層的B+tree就能夠存2000萬的數據了)

並且最好是自動增加的,由於中間有間隙時,當咱們插入的數據正好須要排列在間隙位置,可能會形成樹的從新排列,影響效率。

二、爲何要設置is not null字段。

答:mysql對於null是不友好的,官方文檔也是這樣來講的,不建議使用null,null都放在B+tree最左側,對於比較大小是很不利的。在sql語句中where ** is null 會直接不使用索引,與其null還不如給予其一個默認值。

三、什麼是最左前綴原則。

答:咱們在使用複合索引時必需要按照其順序充分的使用,好比咱們的聯合索引爲ABC三列,那咱們想用C就必定先使用B,想使用B必定要使用A。範圍查詢之後的索引再也不繼續使用,而且不要作任何函數計算處理,也會再也不走索引查詢了。

再就是好比有一個字段varchar類型,咱們在比較數字的時候必定要加「」,否則mysql底層會執行一個強轉函數,從而形成不在走索引。

四、說說對於mysql的優化措施。我的總結。

答: 使用int類型做爲主鍵,且自動增加。(必定要設置一個主鍵),設置索引字段is not null,索引字段要選擇區分度高使用頻率高的字段。

貨幣字段使用DECIMAL來存儲。 不少事還要對應實際的業務須要來肯定的。

  mysql的索引我的以爲差很少就這麼多吧。關於索引的使用優化並無說太多,這個仍是須要靠我的經驗的,心中有索引的存儲模型,熟練使用explain我相信優化sql不成問題的。

下一期咱們再來講說mysql的鎖,事務,分佈式還有日誌。 還有MVCC

有一個點忘記說了,select count(name) from table 非主鍵索引是最快的。別不信,本身琢磨琢磨。本身去試(InnoDB)。MyISAM引擎有維持count的內存。

最進弄了一個公衆號,小菜技術,歡迎你們的加入

相關文章
相關標籤/搜索