MySQL面試知識整理,Java程序員升職漲薪必備,附贈架構師成長圖

1、數據庫引擎

MySQL InnoDB MyISAM
事物支持
外鍵支持
行級 表級
全文索引 是【只支持英文】
崩潰安全恢復支持

選擇:MyISAM相對簡單,因此在效率上要優於InnoDB。若是系統插入和查詢操做多,不須要事務和外鍵,選擇MyISAM,若是須要頻繁的更新、刪除操做,或者須要事務、外鍵、行級鎖的時候,選擇InnoDB。java

2、事務

一、事務特性

  • 原子性(Atomicity):事務做爲一個總體被執行 ,要麼所有執行,要麼所有不執行。事務執行過程當中出錯,會回滾到事務開始前的狀態,全部的操做就像沒有發生同樣。
  • 一致性(Consistency):事務開始前和結束後,數據庫的完整性約束沒有被破壞,執行結果符合預期規則 。好比A向B轉帳,不可能A扣了錢,B卻沒收到。
  • 隔離性(Isolation):同一時間,只容許一個事務請求同一數據,不一樣的事務之間彼此沒有任何干擾。好比A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉帳。
  • 持久性(Durability):一個事務一旦提交,對數據庫的修改應該永久保存,不能回滾。

二、事物的併發問題

  • 髒讀:事務A讀取了事務B已經修改但還沒有提交的數據。若事務B回滾數據,事務A的數據存在不一致性的問題,那麼A讀取到的數據就是髒數據。【一致性】
  • 不可重複讀:事務A在執行過程當中,第一次讀取到的是原始數據,第二次讀取到的是事務B已經提交的修改後的數據。致使兩次讀取同一數據的值不一致。不符合事務的隔離性。【隔離性】
  • 幻讀:事務A根據相同條件第二次查詢到事務B提交的新增或刪除的數據,兩次數據結果集不一致。不符合事務的隔離性。【隔離性】

小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住知足條件的行,解決幻讀須要鎖表。mysql

三、事物的隔離級別

事務隔離級別 讀數據一致性 髒讀 不可重複讀 幻讀
讀未提交(read-uncommitted) 最低級別
讀已提交(read-committed) 語句級
可重複讀(repeatable-read) 事務級
串行化(serializable) 最高級別,事務級

四、總結

  • 事務隔離級別爲讀已提交時,寫數據只會鎖住相應的行。
  • 事務隔離級別爲可重複讀時,若檢索條件有索引(包括主鍵索引),默認加鎖方式是next-key 鎖【間隙鎖】;若檢索條件沒有索引,則更新數據時會鎖住整張表。一個間隙被事務加了鎖,其餘事務是不能在這個間隙插入記錄的,這樣能夠防止幻讀。
  • 事務隔離級別爲串行化時,讀寫數據都會鎖住整張表
  • 隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。
  • MySQL默認隔離級別是可重複讀。
  • 查看當前數據庫的事務隔離級別:show variables like 'tx_isolation';
  • MYSQL MVCC實現機制
  • next-key 鎖【間隙鎖】

3、鎖

​一、行鎖

①. 優點ios

  • 鎖的粒度小;
  • 發生鎖衝突的機率低;
  • 處理併發的能力強。

②. 劣勢程序員

  • 開銷大;
  • 加鎖慢;
  • 會出現死鎖。

③. 加鎖方式面試

自動加鎖。sql

對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及的數據集加排他鎖;對於普通SELECT語句,InnoDB不會加任何鎖;固然咱們也能夠顯示的加鎖: 加共享鎖:select from tableName where ... lock in share mode 加排他鎖:select from tableName where ... for update數據庫

④. 間隙鎖【Next-Key鎖】編程

當咱們用範圍條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫作"間隙(GAP)"。InnoDB也會對這個"間隙"加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。數組

危害(坑):若執行的條件範圍過大,則InnoDB會將整個範圍內全部的索引鍵值所有鎖定,很容易對性能形成影響。緩存

Transaction-A
mysql> update innodb_lock set k=66 where id >=6;
Query OK, 1 row affected (0.63 sec)
mysql> commit;

Transaction-B
mysql> insert into innodb_lock (id,k,v) values(7,'7','7000');
Query OK, 1 row affected (18.99 sec)

⑤. 排他鎖

排他鎖,也稱寫鎖,獨佔鎖,當前寫操做沒有完成前,它會阻斷其餘寫鎖和讀鎖。

# Transaction_A
mysql> set autocommit=0;
mysql> select * from innodb_lock where id=4 for update;
+----+------+------+
| id | k    | v    |
+----+------+------+
|  4 | 4    | 4000 |
+----+------+------+
1 row in set (0.00 sec)

mysql> update innodb_lock set v='4001' where id=4;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.04 sec)
# Transaction_B
mysql> select * from innodb_lock where id=4 for update;
+----+------+------+
| id | k    | v    |
+----+------+------+
|  4 | 4    | 4001 |
+----+------+------+
1 row in set (9.53 sec)

⑥. 共享鎖

共享鎖,也稱讀鎖,多用於判斷數據是否存在,多個讀操做能夠同時進行而不會互相影響。若是事務對讀鎖進行修改操做,極可能會形成死鎖。以下圖所示。

# Transaction_A
mysql> set autocommit=0;
mysql> select * from innodb_lock where id=4 lock in share mode;
+----+------+------+
| id | k    | v    |
+----+------+------+
|  4 | 4    | 4001 |
+----+------+------+
1 row in set (0.00 sec)

mysql> update innodb_lock set v='4002' where id=4;
Query OK, 1 row affected (31.29 sec)
Rows matched: 1  Changed: 1  Warnings: 0
# Transaction_B
mysql> set autocommit=0;
mysql> select * from innodb_lock where id=4 lock in share mode;
+----+------+------+
| id | k    | v    |
+----+------+------+
|  4 | 4    | 4001 |
+----+------+------+
1 row in set (0.00 sec)

mysql> update innodb_lock set v='4002' where id=4;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

⑦. 分析行鎖定

經過檢查InnoDB_row_lock 狀態變量分析系統上的行鎖的爭奪狀況,命令:

show status like 'innodb_row_lock%'
mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 0     |
| Innodb_row_lock_time_avg      | 0     |
| Innodb_row_lock_time_max      | 0     |
| Innodb_row_lock_waits         | 0     |
+-------------------------------+-------+

innodb_row_lock_current_waits: 當前正在等待鎖定的數量 innodb_row_lock_time: 從系統啓動到如今鎖定總時間長度;【很是重要的參數】 innodb_row_lock_time_avg: 每次等待所花平均時間;【很是重要的參數】 innodb_row_lock_time_max: 從系統啓動到如今等待最長的一次所花的時間; innodb_row_lock_waits: 系統啓動後到如今總共等待的次數;很是重要的參數,直接決定優化的方向和策略。

⑧. 行鎖優化

  • 儘量讓全部數據檢索都經過索引來完成,避免無索引行或索引失效致使行鎖升級爲表鎖。
  • 儘量避免間隙鎖帶來的性能降低,減小或使用合理的檢索範圍。
  • 儘量減小事務的粒度,好比控制事務大小,而從減小鎖定資源量和時間長度,從而減小鎖的競爭等,提供性能。
  • 儘量低級別事務隔離,隔離級別越高,併發的處理能力越低。

二、表鎖

①. 優點

  • 開銷小;
  • 加鎖快;
  • 無死鎖。

②. 劣勢

  • 鎖粒度大;
  • 發生鎖衝突的機率高;
  • 併發處理能力低。

③. 加鎖方式

自動加鎖。

查詢操做(SELECT),會自動給涉及的全部表加讀鎖,更新操做(UPDATE、DELETE、INSERT),會自動給涉及的表加寫鎖。也能夠顯示加鎖:

共享讀鎖:lock table tableName read; ​ 獨佔寫鎖:lock table tableName write; ​ 批量解鎖:unlock tables;

④. 共享讀鎖

對MyISAM表的讀操做(加讀鎖),不會阻塞其餘進程對同一表的讀操做,但會阻塞對同一表的寫操做。只有當讀鎖釋放後,才能執行其餘進程的寫操做。

Transaction-A
mysql> lock table myisam_lock read;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from myisam_lock;
9 rows in set (0.00 sec)

mysql> select * from innodb_lock;
ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES

mysql> update myisam_lock set v='1001' where k='1';
ERROR 1099 (HY000): Table 'myisam_lock' was locked with a READ lock and can't be updated

mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
Transaction-B
mysql> select * from myisam_lock;
9 rows in set (0.00 sec)

mysql> select * from innodb_lock;
8 rows in set (0.01 sec)

mysql> update myisam_lock set v='1001' where k='1';
Query OK, 1 row affected (18.67 sec)

⑤. 獨佔寫鎖

對MyISAM表的寫操做(加寫鎖),會阻塞其餘進程對同一表的讀和寫操做,只有當寫鎖釋放後,纔會執行其餘進程的讀寫操做。

Transaction-A
mysql> set autocommit=0;
Query OK, 0 rows affected (0.05 sec)

mysql> lock table myisam_lock write;
Query OK, 0 rows affected (0.03 sec)

mysql> update myisam_lock set v='2001' where k='2';
Query OK, 1 row affected (0.00 sec)

mysql> select * from myisam_lock;
9 rows in set (0.00 sec)

mysql> update innodb_lock set v='1001' where k='1';
ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES

mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
Transaction-B
mysql> select * from myisam_lock;
9 rows in set (42.83 sec)

小結:表鎖,讀鎖會阻塞寫,不會阻塞讀。而寫鎖則會把讀、寫都阻塞。

⑥. 查看加鎖狀況:

show open tables; 1表示加鎖,0表示未加鎖。

mysql> show open tables where in_use > 0;
+----------+-------------+--------+-------------+
| Database | Table       | In_use | Name_locked |
+----------+-------------+--------+-------------+
| lock     | myisam_lock |      1 |           0 |
+----------+-------------+--------+-------------+

⑦. 分析表鎖定

經過檢查table_locks_waited 和 table_locks_immediate 狀態變量分析系統上的表鎖定,命令:

show status like 'table_locks%';
mysql> show status like 'table_locks%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Table_locks_immediate      | 104   |
| Table_locks_waited         | 0     |
+----------------------------+-------+
  • table_locks_immediate: 表示當即釋放表鎖數。
  • table_locks_waited: 表示須要等待的表鎖數。此值越高則說明存在着越嚴重的表級鎖爭用狀況。

此外,MyISAM的讀寫鎖調度是寫優先,這也是MyISAM不適合作寫爲主的存儲引擎。由於寫鎖後,其餘線程不能作任何操做,大量的更新會使查詢很可貴到鎖,從而形成永久阻塞。

三、什麼狀況下用表鎖?

InnoDB默認採用行鎖,在未使用索引字段查詢時升級爲表鎖。MySQL這樣設計並非給你挖坑。它有本身的設計目的。 ​ 即使你在條件中使用了索引字段,MySQL會根據自身的執行計劃,考慮是否使用索引(因此explain命令中會有possible_key 和 key)。若是MySQL認爲全表掃描效率更高,它就不會使用索引,這種狀況下InnoDB將使用表鎖,而不是行鎖。所以,在分析鎖衝突時,別忘了檢查SQL的執行計劃,以確認是否真正使用了索引。

  • 第一種狀況:全表更新。事務須要更新大部分或所有數據,且表又比較大。若使用行鎖,會致使事務執行效率低,從而可能形成其餘事務長時間鎖等待和更多的鎖衝突。
  • 第二種狀況:多表查詢。事務涉及多個表,比較複雜的關聯查詢,極可能引發死鎖,形成大量事務回滾。這種狀況若能一次性鎖定事務涉及的表,從而能夠避免死鎖、減小數據庫因事務回滾帶來的開銷。

四、總結

  • InnoDB 支持表鎖和行鎖,使用索引做爲檢索條件修改數據時採用行鎖,不然採用表鎖。
  • InnoDB 自動給寫操做加鎖,讀操做不自動加鎖。
  • 行鎖可能由於未使用索引而升級爲表鎖,因此除了檢查索引是否建立的同時,也須要經過explain執行計劃查詢索引是否被實際使用。
  • 行鎖相對於表鎖來講,優點在於高併發場景下表現更突出,畢竟鎖的粒度小。
  • 當表的大部分數據須要被修改,或者是多表複雜關聯查詢時,建議使用表鎖優於行鎖。
  • 爲了保證數據的一致完整性,任何一個數據庫都存在鎖定機制。鎖定機制的優劣直接影響到一個數據庫的併發處理能力和性能。

4、索引

一、原理

咱們拿出一本新華字典,它的目錄實際上就是一種索引:非彙集索引。咱們能夠經過目錄迅速定位咱們要查的字。而字典的內容部分通常都是按照拼音排序的,這實際上又是一種索引:彙集索引。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。

二、底層實現【數據結構】

①、mysql主要使用B+樹來構建索引,爲何不用二叉樹和紅黑樹

  • B+樹是多叉的,能夠減小樹的高度。
  • 索引自己較大,不會所有存儲在內存中,會以索引文件的形式存儲在磁盤上,因此索引在查找數據的過程當中會涉及到磁盤I/O操做。
  • 因磁盤I/O效率低下,mysql爲了儘可能減小磁盤IO的存取次數,須要利用了磁盤存取的局部性原理進行磁盤預讀。

    • 局部性原理:爲了減小磁盤IO,磁盤每每會進行數據預讀,會從某位置開始,預先順序向後讀取必定長度的數據放入內存。由於磁盤順序讀取的效率較高,不須要尋道時間,所以能夠提升IO效率。
    • 磁盤預讀長度通常爲頁的整數倍,主存和磁盤以頁做爲單位交換數據。當須要讀取的數據不在內存時,觸發缺頁中斷,系統會向磁盤發出讀取磁盤數據的請求,磁盤找到數據的起始位置並向後連續讀取一頁或幾頁數據載入內存,而後中斷返回,系統繼續運行。
  • mysql將B+數的一個節點的大小設爲一個頁,這樣每一個節點只須要一次I/O就能夠徹底載入內存【因爲節點中有若干個數組,因此地址連續】。
  • 紅黑樹的結構深度更深,不少邏輯上很近的節點(如父子節點)在物理上可能很遠,沒法利用局部性原理。

在InnoDB裏,每一個頁默認16KB,假設索引的是8B的long型數據,每一個key後有個頁號4B,還有6B的其餘數據(參考《MySQL技術內幕:InnoDB存儲引擎》P193的頁面數據),那麼每一個頁的扇出係數爲16KB/(8B+4B+6B)≈1000,即每一個頁能夠索引1000個key。在高度h=3時,s=1000^3=10億!!也就是說,InnoDB經過三次索引頁的I/O,便可索引10億的key。一般來講,索引樹的高度在2~4。

②. B+Tree與B-Tree的區別

M階B-Tree

定義:

  • 樹中每一個結點至多有m個孩子;
  • 除根結點和葉子結點外,其它每一個結點至少有m/2個孩子;
  • 若根結點不是葉子結點,則至少有2個孩子;
  • 全部葉子結點都在同一層;

特性:

  • 關鍵字集合分佈在整顆樹中;
  • 任何一個關鍵字出現且只出如今一個結點中;
  • 搜索有可能在非葉子結點結束;
  • 其搜索性能等價於在關鍵字全集內作一次二分查找;
  • 自動層次控制;

M階B+Tree

定義:

  • 有m個子樹的節點包含有m個元素(B-Tree中是m-1);
  • 非葉子節點不保存數據,只用於索引,全部數據都保存在葉子節點中。
  • 全部分支節點和根節點都同時存在於子節點中,在子節點元素中是最大或者最小的元素。
  • 葉子節點會包含全部的關鍵字,以及指向數據記錄的指針,而且葉子節點自己是根據關鍵字的大小從小到大順序連接。

特性:

  • 全部關鍵字都出如今葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字是有序的;
  • 不可能在非葉子結點命中,由於非葉子節點只有Key,沒有Data;
  • 非葉子結點至關因而葉子結點的索引(稀疏索引),葉子結點至關因而存儲全部關鍵字數據的數據層;
  • 更適合文件索引系統。

③. B+Tree與B-Tree的區別

  • B+Tree有n棵子樹的結點中含有n個關鍵字; (而B樹是n棵子樹有n-1個關鍵字)。
  • B+Tree全部Key(關鍵字)存儲在葉子節點,非葉子節點不存儲真正的data(數據)。
  • B+Tree爲全部葉子節點增長了一個鏈指針,且全部葉子節點的關鍵字按從小到大順序連接,加強了區間訪問性。

④. 爲何mysql的索引使用B+樹而不是B樹呢

  • B+樹更適合外部存儲(通常指磁盤存儲),因爲內節點(非葉子節點)不存data(數據)只存Key(關鍵字),因此B+樹一個節點能夠存儲更多的Key,即每一個節點能索引的範圍更大更精確。也就是說使用B+樹單次磁盤I/O的信息量相比較B樹更大,I/O效率更高。
  • mysql是關係型數據庫,常常會按照區間來訪問某個索引列,B+樹的葉子節點間按Key的順序創建了鏈指針,增強了區間訪問性,因此B+樹對索引列上的區間範圍查詢很友好。而B樹每一個節點的key和data在一塊兒,沒法進行區間查找。

三、索引優勢

  • 快速讀取數據。
  • 惟一性索引能保證數據記錄的惟一性。
  • 實現表與表之間的參照完整性【經過主鍵索引】。

四、索引缺點

  • 索引須要佔用物理空間。
  • 當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,下降了數據的維護速度。

5、mysql性能分析

一、MySQL 自身瓶頸

MySQL自身常見的性能問題有磁盤空間不足,磁盤I/O太大,服務器硬件性能低。

  • CPU瓶頸:CPU 飽和通常發生在數據裝入內存或從磁盤上讀取數據的時候。
  • IO瓶頸:磁盤I/O 瓶頸發生在裝入數據遠大於內存容量的時候。
  • 服務器硬件的性能瓶頸:可經過top,free,iostat 和 vmstat來查看系統的性能狀態。

二、explain 分析sql語句

使用explain關鍵字能夠模擬優化器執行sql查詢語句,從而得知MySQL 是如何處理sql語句。

①. id
select 查詢的序列號,包含一組能夠重複的數字,表示查詢中sql語句的執行順序。通常有三種狀況: 第一種:id所有相同,sql的執行順序是由上至下; 第二種:id所有不一樣,sql的執行順序是根據id大的優先執行(若是是子查詢,id的序號會遞增); 第三種:id既存在相同,又存在不一樣的。先根據id大的優先執行,再根據相同id從上至下的執行。

②. select_type

select 查詢的類型,主要是用於區別普通查詢,聯合查詢,嵌套的複雜查詢:

  • simple:簡單的select 查詢,查詢中不包含子查詢或者union。
  • primary:查詢中若包含任何複雜的子查詢,最外層查詢則被標記爲primary。
  • subquery:在select或where 列表中包含了子查詢。
  • derived:在from列表中包含的子查詢被標記爲derived(衍生),MySQL會遞歸執行這些子查詢,把結果放在臨時表裏。
  • union:若第二個select出如今union以後,則被標記爲union【聯合查詢】,若union包含在from子句的子查詢中,外層select將被標記爲:derived。
  • union result:從union表獲取結果的select。

subquery和union 還能夠被標記爲dependent和uncacheable。 dependent意味着select依賴於外層查詢中發現的數據。 uncacheable意味着select中的某些特性阻止結果被緩存於一個item_cache中。

③. table

查詢結果來自於哪一個表。

④. partitions

表所使用的分區,若是要統計十年公司訂單的金額,能夠把數據分爲十個區,每年表明一個區。這樣能夠大大的提升查詢效率。

⑤. type

這是一個很是重要的參數,鏈接類型,常見的有:all , index , range , ref , eq_ref , const , system , null 八個級別。

性能從最優到最差的排序:null > system > const > eq_ref > ref > range > index > all

對java程序員來講,若保證查詢至少達到range級別或者最好能達到ref則算是一個優秀而又負責的程序員。

  • all:(full table scan)全表掃描無疑是最差,如果百萬千萬級數據量,全表掃描會很是慢。
  • index:(full index scan)全索引文件掃描比all好不少,畢竟從索引樹中找數據,比從全表中找數據要快。
  • range:只檢索給定範圍的行,使用索引來匹配行。範圍縮小了,固然比全表掃描和全索引文件掃描要快。sql語句中通常會有between,>,< 等查詢,IN()和OR列表,也會顯示range。
  • ref:非惟一性索引掃描,本質上也是一種索引訪問,返回全部匹配某個單獨值的行。好比查詢公司全部屬於研發團隊的同事,匹配的結果是多個並不是惟一值。
explain select * from t1 where name='yayun';
  • eq_ref:惟一性索引掃描,對於每一個索引鍵,表中有一條記錄與之匹配。好比用主鍵或惟一字段做爲判斷條件。
explain select t1.name from t1, t2 where t1.id=t2.id;
  • const:表示經過索引一次就能夠找到,const用於比較primary key 或者unique索引。由於只匹配一行數據,因此很快,若將主鍵至於where列表中,MySQL就能將該查詢轉換爲一個常量。
explain select * from t1 where id = 1
  • system:表只有一條記錄(等於系統表),這是const類型的特列,平時不會出現,瞭解便可。
  • NULL:MySQL在優化過程當中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列裏選取最小值能夠經過單獨索引查找完成。
explain select * from t1 where id = (select min(id) from t2);

⑥. possible_keys

顯示查詢語句可能用到的索引(即查詢涉及字段中存在索引的字段,可能爲一個、多個或爲null),不必定被查詢實際使用,僅供參考使用。

⑦. key

顯示查詢語句實際使用的索引字段。若爲null,則表示沒有使用索引。

⑧. key_len

顯示索引中使用的字節數,可經過key_len計算查詢中使用的索引長度。

​ 在不損失精確性的狀況下索引長度越短越好。key_len 顯示的值爲索引字段的最可能長度,並不是實際使用長度,即key_len是根據表定義計算而得,並非經過表內檢索出的。

⑨. ref

表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值。即顯示使用哪一個列或常數與key一塊兒從表中選擇行。

**⑩. rows

根據表統計信息及索引選用狀況,大體估算出找到所需的記錄所須要讀取的行數,值越大越很差。

即根據查詢語句及索引選用狀況,大體估算出要獲得查詢結果,所須要在表中讀取的行數。

⑪. filtered

一個百分比的值,和rows 列的值一塊兒使用,能夠估計出查詢執行計劃(QEP)中的前一個表的結果集,從而肯定join操做的循環次數。小表驅動大表,減輕鏈接的次數。

⑫. extra

包含不適合在其餘列中顯示但又十分重要的額外信息。

  • Using filesort: 「文件排序」,說明MySQL中沒法利用索引完成的排序操做 ,MySQL會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。出現這個就要馬上優化sql。
  • Using temporary: 使用了臨時表保存中間結果,說明MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和 分組查詢 group by時,須要藉助輔助表再進行排序的狀況(這種狀況是多個表都涉及到排序字段纔會引發的)。 出現這個更要馬上優化sql。
  • Using index: 表示相應的select 操做中使用了覆蓋索引(Covering index),避免訪問了表的數據行,效果不錯!若是同時出現Using where,代表索引被用來執行索引鍵值的查找。若是沒有同時出現Using where,表示索引只是用來讀取數據而非執行查找動做。 覆蓋索引(Covering Index) :也叫索引覆蓋,就是select 的數據列只用從索引中就可以取得,沒必要讀取數據行,MySQL能夠利用索引返回select 列表中的字段,而沒必要根據索引再次讀取數據文件。
  • Using index condition: 在5.6版本後加入的新特性,優化器會在索引存在的狀況下,經過符合RANGE範圍的條數 和 總數的比例來選擇是使用索引仍是進行全表遍歷。
  • Using where: 代表使用了where 過濾。
  • Using join buffer: 代表使用了鏈接緩存。
  • impossible where: where 語句的值老是false,不可用,不能用來獲取任何元素。
  • distinct: 優化distinct操做,在找到第一匹配的元組後即中止找一樣值的動做。

三、explain總結

經過explain的參數介紹,咱們能夠得知:

  • sql的查詢順序(根據id,id越大越先執行)。
  • 數據讀取操做的操做類型(type)
  • 哪些索引被實際使用(key)
  • 表之間的引用(ref)
  • 每張表有多少行被優化器查詢(rows)

6、數據庫性能優化

一、優化思路

可從如下幾個方面對數據庫性能進行優化:

  • 優化數據庫與索引的設計。
  • 優化SQL語句。
  • 加緩存【Memcached, Redis】
  • 主從複製,讀寫分離。
  • 垂直拆分,其實就是根據你模塊的耦合度,將一個包含多個字段的表分紅多個小的表,將一個大的系統分爲多個小的系統,也就是分佈式系統。
  • 水平切分,針對數據量大的表,這一步最麻煩,最能考驗技術水平,要選擇一個合理的sharding key,爲了有好的查詢效率,表結構也要改動,作必定的冗餘,應用也要改,sql中儘可能帶sharding key,將數據定位到限定的表上去查,而不是掃描所有的表;

二、數據庫與索引設計

①. 數據庫設計

  • 表字段避免null值出現,null值很難進行查詢優化且佔用額外的索引空間,推薦默認數字0代替null。
  • 儘可能使用INT而非BIGINT,若是非負則加上UNSIGNED(這樣數值容量會擴大一倍),固然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
  • 使用枚舉或整數代替字符串類型。
  • 儘可能使用TIMESTAMP而非DATETIME。
  • 單表不要有太多字段,建議在20之內。
  • 用整型來存IP。【可去搜索IP地址轉爲整型】

等。

②. 索引設計

  • 索引要佔用物理內存,並非越多越好,要根據查詢有針對性的建立,考慮在WHERE和ORDER BY命令上涉及的列創建索引,可根據EXPLAIN來查看是否用了索引仍是全表掃描。
  • 應儘可能避免在WHERE子句中對字段進行NULL值判斷,不然將致使引擎放棄使用索引而進行全表掃描。
  • 值分佈很稀少的字段不適合建索引,例如"性別"這種只有兩三個值的字段。
  • 字符字段只建前綴索引。
  • 字符字段最好不要作主鍵。
  • 不用外鍵,由程序保證約束。
  • 儘可能不用UNIQUE,由程序保證約束。
  • 使用多列索引時主意順序和查詢條件保持一致,同時刪除沒必要要的單列索引。

③. 總結

就一句話:使用合適的數據類型,選擇合適的索引:

  • 使用合適的數據類型

    • 使用可存下數據的最小的數據類型,整型 < date,time < char,varchar < blob
    • 使用簡單的數據類型,整型比字符處理開銷更小,由於字符串的比較更復雜。如,int類型存儲時間類型,bigint類型轉ip函數。
    • 使用合理的字段屬性長度,固定長度的表會更快。使用enum、char而不是varchar。
    • 儘量使用not null定義字段。
    • 儘可能少用text,非用不可最好分表。
  • 選擇合適的索引列(即哪些列適合添加索引)

    • 查詢頻繁的列,在where,group by,order by,on從句中出現的列。
    • where條件中<,<=,=,>,>=,between,in,以及like 字符串+通配符(%)出現的列。
    • 長度小的列,索引字段越小越好,由於數據庫的存儲單位是頁,一頁中能存下的數據越多越好。
    • 離散度大(不一樣的值多)的列,放在聯合索引前面。查看離散度,經過統計不一樣的列值來實現,count越大,離散程度越高。

三、sql優化

  • 使用limit對查詢結果的記錄進行限定。
  • *避免select ,將須要查找的字段列出來。
  • 使用鏈接(join)來代替子查詢。
  • 拆分大的delete或insert語句。
  • 可經過開啓慢查詢日誌來找出較慢的SQL。如何開啓mysql慢查詢日誌?
  • 不作列運算:SELECT id WHERE age + 1 = 10,任何對列的操做都將致使表掃描,它包括數據庫教程函數、計算表達式等等,查詢時要儘量將操做移至等號右邊。
  • sql語句儘量簡單:一條sql只能在一個cpu運算;大語句拆小語句,減小鎖時間;一條大sql能夠堵死整個庫。
  • OR改寫成IN:OR的效率是O(n)級別,IN的效率是O(logn)級別,in的個數建議控制在200之內。
  • 不用函數和觸發器,在應用程序實現。
  • 避免%xxx式查詢。
  • 少用JOIN。
  • 使用同類型進行比較,好比用'123'和'123'比,123和123比。
  • 儘可能避免在WHERE子句中使用 != 或 <> 操做符,不然致使引擎放棄使用索引而進行全表掃描。
  • 對於連續數值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5。
  • 列表數據不要拿全表,要使用LIMIT來分頁,每頁數量也不要太大。

四、分區、分庫、分表

①. 分區

把一張表的數據分紅N個區塊,在邏輯上看最終只是一張表,但底層是由N個物理區塊組成的,經過將不一樣數據按必定規則放到不一樣的區塊中提高表的查詢效率。

②. 分表

  • 水平分表:爲了解決單表數據量過大(數據量達到千萬級別)問題。因此將固定的ID hash以後mod,取若0~N個值,而後將數據劃分到不一樣表中,須要在寫入與查詢的時候進行ID的路由與統計。
  • 垂直分表:爲了解決表的寬度問題,同時還能分別優化每張單表的處理能力。因此將表結構根據數據的活躍度拆分紅多個表,把不經常使用的字段單獨放到一個表、把大字段單獨放到一個表、把常用的字段放到一個表。

③. 分庫

面對高併發的讀寫訪問,當數據庫沒法承載寫操做壓力時,無論如何擴展slave服務器,此時都沒有意義了。所以需對數據庫進行拆分,從而提升數據庫寫入能力,這就是分庫。

④. 問題

  • 事務問題。在執行分庫以後,因爲數據存儲到了不一樣的庫上,數據庫事務管理出現了困難。若是依賴數據庫自己的分佈式事務管理功能去執行事務,將付出高昂的性能代價;若是由應用程序去協助控制,造成程序邏輯上的事務,又會形成編程方面的負擔。
  • 跨庫跨表的join問題。在執行了分庫分表以後,難以免會將本來邏輯關聯性很強的數據劃分到不一樣的表、不一樣的庫上,咱們沒法join位於不一樣分庫的表,也沒法join分表粒度不一樣的表,結果本來一次查詢可以完成的業務,可能須要屢次查詢才能完成。
  • 額外的數據管理負擔和數據運算壓力。額外的數據管理負擔,最顯而易見的就是數據的定位問題和數據的增刪改查的重複執行問題,這些均可以經過應用程序解決,但必然引發額外的邏輯運算。

7、緩存

對於不少的數據庫系統都可以緩存執行計劃,對於徹底相同的sql, 可使用已經已經存在的執行計劃,從而跳過解析和生成執行計劃的過程。MYSQL提供了更爲高級的查詢結果緩存功能,對於徹底相同的SQL (字符串徹底相同且大小寫敏感) 能夠執行返回查詢結果。

MySQL緩存機制簡單的說就是緩存sql文本及查詢結果,若是運行相同的sql,服務器直接從緩存中取到結果,而不須要再去解析和執行sql。若是表更改了,那麼使用這個表的全部緩衝查詢將再也不有效,查詢緩存值的相關條目被清空。更改指的是表中任何數據或是結構的改變,包括INSERT、UPDATE、 DELETE、TRUNCATE(截斷)、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改變了的表的使用MERGE表的查詢。顯然,這對於頻繁更新的表,查詢緩存是不適合的,而對於一些不常改變數據且有 大量相同sql查詢的表,查詢緩存會節約很大的性能。

8、面試真題

問:有個表特別大,字段是姓名、年齡、班級,若是調用select * from table where name = xxx and age = xxx該如何經過創建索引的方式優化查詢速度

答:因爲mysql查詢每次只能使用一個索引,若是在name、age兩列上建立複合索引的話將帶來更高的效率。若是咱們建立了(name, age)的複合索引,那麼其實至關於建立了(name)、(name, age)兩個索引,這被稱爲最佳左前綴特性。所以咱們在建立複合索引時應該將最經常使用做限制條件的列放在最左邊,依次遞減。其次還要考慮該列的數據離散程度,若是有不少不一樣的值的話建議放在左邊,name的離散程度也大於age。

問:max(xxx)如何用索引優化

答:在xxx列上創建索引,由於索引是B+樹順序排列的,鎖在下次查詢的時候就會使用索引來查詢到最大的值是哪一個。

問:如何對分頁進行優化

答:SELECT * FROM big_table order by xx LIMIT 1000000,20,這條語句會查詢出1000020條的全部數據而後丟棄掉前1000000條,爲了不全表掃描的操做,在order by的列上加索引就能經過掃描索引來查詢。可是這條語句會查詢仍是會掃描1000020條,還能改進成select id from big_table where id >= 1000000 order by xx LIMIT 0,20,用ID做爲過濾條件將不須要查詢的數據直接去除。

9、讀者福利

針對Java程序員,筆者最近整理了一份完整的一線互聯網大廠面試真題,包含了Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分佈式、高併發、性能調優、微服務等架構技術

須要的朋友請點擊下方傳送門免費領取

傳送門

如下是部分面試題截圖

10、架構師成長路線圖

相關文章
相關標籤/搜索