MySQL數據庫材料

https://www.jianshu.com/p/c189439fb32emysql

1、MySQL架構ios

和其它數據庫相比,MySQL有點不同凡響,它的架構能夠在多種不一樣場景中應用併發揮良好做用。主要體如今存儲引擎的架構上,插件式的存儲引擎架構將查詢處理和其它的系統任務以及數據的存儲提取相分離。這種架構能夠根據業務的需求和實際須要選擇合適的存儲引擎。程序員

 
image
  • 鏈接層:最上層是一些客戶端和鏈接服務。主要完成一些相似於鏈接處理、受權認證、及相關的安全方案。在該層上引入了線程池的概念,爲經過認證安全接入的客戶端提供線程。一樣在該層上能夠實現基於SSL的安全連接。服務器也會爲安全接入的每一個客戶端驗證它所具備的操做權限。
  • 服務層:第二層服務層,主要完成大部分的核心服務功能, 包括查詢解析、分析、優化、緩存、以及全部的內置函數,全部跨存儲引擎的功能也都在這一層實現,包括觸發器、存儲過程、視圖等
  • 引擎層:第三層存儲引擎層,存儲引擎真正的負責了MySQL中數據的存儲和提取,服務器經過API與存儲引擎進行通訊。不一樣的存儲引擎具備的功能不一樣,這樣咱們能夠根據本身的實際須要進行選取
  • 存儲層:第四層爲數據存儲層,主要是將數據存儲在運行於該設備的文件系統之上,並完成與存儲引擎的交互

面試

畫出 MySQL 架構圖,這種變態問題都能問的出來算法

MySQL 的查詢流程具體是?or 一條SQL語句在MySQL中如何執行的?sql

客戶端請求 ---> 鏈接器(驗證用戶身份,給予權限) ---> 查詢緩存(存在緩存則直接返回,不存在則執行後續操做) ---> 分析器(對SQL進行詞法分析和語法分析操做) ---> 優化器(主要對執行的sql優化選擇最優的執行方案方法) ---> 執行器(執行時會先看用戶是否有執行權限,有才去使用這個引擎提供的接口) ---> 去引擎層獲取數據返回(若是開啓查詢緩存則會緩存查詢結果)圖:極客時間數據庫

 
image

緩存

說說MySQL有哪些存儲引擎?都有哪些區別?安全

2、存儲引擎

存儲引擎是MySQL的組件,用於處理不一樣表類型的SQL操做。不一樣的存儲引擎提供不一樣的存儲機制、索引技巧、鎖定水平等功能,使用不一樣的存儲引擎,還能夠得到特定的功能。性能優化

使用哪種引擎能夠靈活選擇,一個數據庫中多個表能夠使用不一樣引擎以知足各類性能和實際需求,使用合適的存儲引擎,將會提升整個數據庫的性能 。

MySQL服務器使用可插拔的存儲引擎體系結構,能夠從運行中的 MySQL 服務器加載或卸載存儲引擎 。

查看存儲引擎

-- 查看支持的存儲引擎 SHOW ENGINES -- 查看默認存儲引擎 SHOW VARIABLES LIKE 'storage_engine' --查看具體某一個表所使用的存儲引擎,這個默認存儲引擎被修改了! show create table tablename --準確查看某個數據庫中的某一表所使用的存儲引擎 show table status like 'tablename' show table status from database where name="tablename" 

設置存儲引擎

-- 建表時指定存儲引擎。默認的就是INNODB,不須要設置 CREATE TABLE t1 (i INT) ENGINE = INNODB; CREATE TABLE t2 (i INT) ENGINE = CSV; CREATE TABLE t3 (i INT) ENGINE = MEMORY; -- 修改存儲引擎 ALTER TABLE t ENGINE = InnoDB; -- 修改默認存儲引擎,也能夠在配置文件my.cnf中修改默認引擎 SET default_storage_engine=NDBCLUSTER; 

默認狀況下,每當 CREATE TABLE 或 ALTER TABLE 不能使用默認存儲引擎時,都會生成一個警告。爲了防止在所需的引擎不可用時出現使人困惑的意外行爲,能夠啓用 NO_ENGINE_SUBSTITUTION SQL 模式。若是所需的引擎不可用,則此設置將產生錯誤而不是警告,而且不會建立或更改表

存儲引擎對比

常見的存儲引擎就 InnoDB、MyISAM、Memory、NDB。

InnoDB 如今是 MySQL 默認的存儲引擎,支持事務、行級鎖定和外鍵

文件存儲結構對比

在 MySQL中創建任何一張數據表,在其數據目錄對應的數據庫目錄下都有對應表的 .frm 文件,.frm 文件是用來保存每一個數據表的元數據(meta)信息,包括表結構的定義等,與數據庫存儲引擎無關,也就是任何存儲引擎的數據表都必須有.frm文件,命名方式爲 數據表名.frm,如user.frm。

查看MySQL 數據保存在哪裏:show variables like 'data%'

MyISAM 物理文件結構爲:

  • .frm文件:與表相關的元數據信息都存放在frm文件,包括表結構的定義信息等
  • .MYD (MYData) 文件:MyISAM 存儲引擎專用,用於存儲MyISAM 表的數據
  • .MYI (MYIndex)文件:MyISAM 存儲引擎專用,用於存儲MyISAM 表的索引相關信息

InnoDB 物理文件結構爲:

  • .frm 文件:與表相關的元數據信息都存放在frm文件,包括表結構的定義信息等
  • .ibd 文件或 .ibdata 文件:這兩種文件都是存放 InnoDB 數據的文件,之因此有兩種文件形式存放 InnoDB 的數據,是由於 InnoDB 的數據存儲方式可以經過配置來決定是使用共享表空間存放存儲數據,仍是用獨享表空間存放存儲數據。獨享表空間存儲方式使用.ibd文件,而且每一個表一個.ibd文件 共享表空間存儲方式使用.ibdata文件,全部表共同使用一個.ibdata文件(或多個,可本身配置)

ps:正經公司,這些都有專業運維去作,數據備份、恢復啥的,讓我一個 Javaer 搞這的話,加錢不?

面試這麼回答

  1. InnoDB 支持事務,MyISAM 不支持事務。這是 MySQL 將默認存儲引擎從 MyISAM 變成 InnoDB 的重要緣由之一;
  2. InnoDB 支持外鍵,而 MyISAM 不支持。對一個包含外鍵的 InnoDB 錶轉爲 MYISAM 會失敗;
  3. InnoDB 是聚簇索引,MyISAM 是非聚簇索引。聚簇索引的文件存放在主鍵索引的葉子節點上,所以 InnoDB 必需要有主鍵,經過主鍵索引效率很高。可是輔助索引須要兩次查詢,先查詢到主鍵,而後再經過主鍵查詢到數據。所以,主鍵不該該過大,由於主鍵太大,其餘索引也都會很大。而 MyISAM 是非彙集索引,數據文件是分離的,索引保存的是數據文件的指針。主鍵索引和輔助索引是獨立的。
  4. InnoDB 不保存表的具體行數,執行select count(*) from table 時須要全表掃描。而 MyISAM 用一個變量保存了整個表的行數,執行上述語句時只須要讀出該變量便可,速度很快;
  5. InnoDB 最小的鎖粒度是行鎖,MyISAM 最小的鎖粒度是表鎖。一個更新語句會鎖住整張表,致使其餘查詢和更新都會被阻塞,所以併發訪問受限。這也是 MySQL 將默認存儲引擎從 MyISAM 變成 InnoDB 的重要緣由之一;

對比項MyISAMInnoDB主外鍵不支持支持事務不支持支持行表鎖表鎖,即便操做一條記錄也會鎖住整個表,不適合高併發的操做行鎖,操做時只鎖某一行,不對其它行有影響,適合高併發的操做緩存只緩存索引,不緩存真實數據不只緩存索引還要緩存真實數據,對內存要求較高,並且內存大小對性能有決定性的影響表空間小大關注點性能事務默認安裝是是

一張表,裏面有ID自增主鍵,當insert了17條記錄以後,刪除了第15,16,17條記錄,再把Mysql重啓,再insert一條記錄,這條記錄的ID是18仍是15 ?

若是表的類型是MyISAM,那麼是18。由於MyISAM表會把自增主鍵的最大ID 記錄到數據文件中,重啓MySQL自增主鍵的最大ID也不會丟失;

若是表的類型是InnoDB,那麼是15。由於InnoDB 表只是把自增主鍵的最大ID記錄到內存中,因此重啓數據庫或對錶進行OPTION操做,都會致使最大ID丟失。

哪一個存儲引擎執行 select count(*) 更快,爲何?

MyISAM更快,由於MyISAM內部維護了一個計數器,能夠直接調取。

  • 在 MyISAM 存儲引擎中,把表的總行數存儲在磁盤上,當執行 select count(*) from t 時,直接返回總數據。
  • 在 InnoDB 存儲引擎中,跟 MyISAM 不同,沒有將總行數存儲在磁盤上,當執行 select count(*) from t 時,會先把數據讀出來,一行一行的累加,最後返回總數量。

InnoDB 中 count(*) 語句是在執行的時候,全表掃描統計總數量,因此當數據愈來愈大時,語句就愈來愈耗時了,爲何 InnoDB 引擎不像 MyISAM 引擎同樣,將總行數存儲到磁盤上?這跟 InnoDB 的事務特性有關,因爲多版本併發控制(MVCC)的緣由,InnoDB 表「應該返回多少行」也是不肯定的。

3、數據類型

主要包括如下五大類:

  • 整數類型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT
  • 浮點數類型:FLOAT、DOUBLE、DECIMAL
  • 字符串類型:CHAR、VARCHAR、TINY TEXT、TEXT、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB
  • 日期類型:Date、DateTime、TimeStamp、Time、Year
  • 其餘數據類型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等
 
image
 
image
 
image

CHAR 和 VARCHAR 的區別?

char是固定長度,varchar長度可變:

char(n) 和 varchar(n) 中括號中 n 表明字符的個數,並不表明字節個數,好比 CHAR(30) 就能夠存儲 30 個字符。

存儲時,前者無論實際存儲數據的長度,直接按 char 規定的長度分配存儲空間;然後者會根據實際存儲的數據分配最終的存儲空間

相同點:

  1. char(n),varchar(n)中的n都表明字符的個數
  2. 超過char,varchar最大長度n的限制後,字符串會被截斷。

不一樣點:

  1. char不論實際存儲的字符數都會佔用n個字符的空間,而varchar只會佔用實際字符應該佔用的字節空間加1(實際長度length,0<=length<255)或加2(length>255)。由於varchar保存數據時除了要保存字符串以外還會加一個字節來記錄長度(若是列聲明長度大於255則使用兩個字節來保存長度)。
  2. 能存儲的最大空間限制不同:char的存儲上限爲255字節。
  3. char在存儲時會截斷尾部的空格,而varchar不會。

char是適合存儲很短的、通常固定長度的字符串。例如,char很是適合存儲密碼的MD5值,由於這是一個定長的值。對於很是短的列,char比varchar在存儲空間上也更有效率。

列的字符串類型能夠是什麼?

字符串類型是:SET、BLOB、ENUM、CHAR、CHAR、TEXT、VARCHAR

BLOB和TEXT有什麼區別?

BLOB是一個二進制對象,能夠容納可變數量的數據。有四種類型的BLOB:TINYBLOB、BLOB、MEDIUMBLO和 LONGBLOB

TEXT是一個不區分大小寫的BLOB。四種TEXT類型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。

BLOB 保存二進制數據,TEXT 保存字符數據。


4、索引

說說你對 MySQL 索引的理解?

數據庫索引的原理,爲何要用 B+樹,爲何不用二叉樹?

彙集索引與非彙集索引的區別?

InnoDB引擎中的索引策略,瞭解過嗎?

建立索引的方式有哪些?

聚簇索引/非聚簇索引,mysql索引底層實現,爲何不用B-tree,爲何不用hash,葉子結點存放的是數據仍是指向數據的內存地址,使用索引須要注意的幾個地方?

  • MYSQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構,因此說索引的本質是:數據結構
  • 索引的目的在於提升查詢效率,能夠類比字典、 火車站的車次表、圖書的目錄等 。
  • 能夠簡單的理解爲「排好序的快速查找數據結構」,數據自己以外,數據庫還維護者一個知足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就能夠在這些數據結構上實現高級查找算法。這種數據結構,就是索引。下圖是一種可能的索引方式示例。左邊的數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址。爲了加快Col2的查找,能夠維護一個右邊所示的二叉查找樹,每一個節點分別包含索引鍵值,和一個指向對應數據記錄物理地址的指針,這樣就能夠運用二叉查找在必定的複雜度內獲取到對應的數據,從而快速檢索出符合條件的記錄。
  • 索引自己也很大,不可能所有存儲在內存中,通常以索引文件的形式存儲在磁盤上
  • 日常說的索引,沒有特別指明的話,就是B+樹(多路搜索樹,不必定是二叉樹)結構組織的索引。其中彙集索引,次要索引,覆蓋索引,符合索引,前綴索引,惟一索引默認都是使用B+樹索引,統稱索引。此外還有哈希索引等。

基本語法:

  • 建立:

  • 建立索引:CREATE [UNIQUE] INDEX indexName ON mytable(username(length));若是是CHAR,VARCHAR類型,length能夠小於字段實際長度;若是是BLOB和TEXT類型,必須指定 length。

  • 修改表結構(添加索引):ALTER table tableName ADD [UNIQUE] INDEX indexName(columnName)

  • 刪除:DROP INDEX [indexName] ON mytable;

  • 查看:SHOW INDEX FROM table_name\G --能夠經過添加 \G 來格式化輸出信息。

  • 使用ALERT命令

  • ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 該語句添加一個主鍵,這意味着索引值必須是惟一的,且不能爲NULL。

  • ALTER TABLE tbl_name ADD UNIQUE index_name (column_list 這條語句建立索引的值必須是惟一的(除了NULL外,NULL可能會出現屢次)。

  • ALTER TABLE tbl_name ADD INDEX index_name (column_list) 添加普通索引,索引值可出現屢次。

  • ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list)該語句指定了索引爲 FULLTEXT ,用於全文索引。

優點

  • 提升數據檢索效率,下降數據庫IO成本
  • 下降數據排序的成本,下降CPU的消耗

劣勢

  • 索引也是一張表,保存了主鍵和索引字段,並指向實體表的記錄,因此也須要佔用內存
  • 雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行INSERT、UPDATE和DELETE。由於更新表時,MySQL不只要保存數據,還要保存一下索引文件每次更新添加了索引列的字段, 都會調整由於更新所帶來的鍵值變化後的索引信息

MySQL索引分類

數據結構角度

  • B+樹索引
  • Hash索引
  • Full-Text全文索引
  • R-Tree索引

從物理存儲角度

  • 彙集索引(clustered index)
  • 非彙集索引(non-clustered index),也叫輔助索引(secondary index)彙集索引和非彙集索引都是B+樹結構

從邏輯角度

  • 主鍵索引:主鍵索引是一種特殊的惟一索引,不容許有空值
  • 普通索引或者單列索引:每一個索引只包含單個列,一個表能夠有多個單列索引
  • 多列索引(複合索引、聯合索引):複合索引指多個字段上建立的索引,只有在查詢條件中使用了建立索引時的第一個字段,索引纔會被使用。使用複合索引時遵循最左前綴集合
  • 惟一索引或者非惟一索引
  • 空間索引:空間索引是對空間數據類型的字段創建的索引,MYSQL中的空間數據類型有4種,分別是GEOMETRY、POINT、LINESTRING、POLYGON。MYSQL使用SPATIAL關鍵字進行擴展,使得可以用於建立正規索引類型的語法建立空間索引。建立空間索引的列,必須將其聲明爲NOT NULL,空間索引只能在存儲引擎爲MYISAM的表中建立

爲何MySQL 索引中用B+tree,不用B-tree 或者其餘樹,爲何不用 Hash 索引

聚簇索引/非聚簇索引,MySQL 索引底層實現,葉子結點存放的是數據仍是指向數據的內存地址,使用索引須要注意的幾個地方?

使用索引查詢必定能提升查詢的性能嗎?爲何?

MySQL索引結構

首先要明白索引(index)是在存儲引擎(storage engine)層面實現的,而不是server層面。不是全部的存儲引擎都支持全部的索引類型。即便多個存儲引擎支持某一索引類型,它們的實現和行爲也可能有所差異。

B+Tree索引

MyISAM 和 InnoDB 存儲引擎,都使用 B+Tree的數據結構,它相對與 B-Tree結構,全部的數據都存放在葉子節點上,且把葉子節點經過指針鏈接到一塊兒,造成了一條數據鏈表,以加快相鄰數據的檢索效率。

先了解下 B-Tree 和 B+Tree 的區別

B-Tree

B-Tree是爲磁盤等外存儲設備設計的一種平衡查找樹。

系統從磁盤讀取數據到內存時是以磁盤塊(block)爲基本單位的,位於同一個磁盤塊中的數據會被一次性讀取出來,而不是須要什麼取什麼。

InnoDB 存儲引擎中有頁(Page)的概念,頁是其磁盤管理的最小單位。InnoDB 存儲引擎中默認每一個頁的大小爲16KB,可經過參數 innodb_page_size 將頁的大小設置爲 4K、8K、16K,在 MySQL 中可經過以下命令查看頁的大小:show variables like 'innodb_page_size';

而系統一個磁盤塊的存儲空間每每沒有這麼大,所以 InnoDB 每次申請磁盤空間時都會是若干地址連續磁盤塊來達到頁的大小 16KB。InnoDB 在把磁盤數據讀入到磁盤時會以頁爲基本單位,在查詢數據時若是一個頁中的每條數據都能有助於定位數據記錄的位置,這將會減小磁盤I/O次數,提升查詢效率。

B-Tree 結構的數據可讓系統高效的找到數據所在的磁盤塊。爲了描述 B-Tree,首先定義一條記錄爲一個二元組[key, data] ,key爲記錄的鍵值,對應表中的主鍵值,data 爲一行記錄中除主鍵外的數據。對於不一樣的記錄,key值互不相同。

一棵m階的B-Tree有以下特性:

  1. 每一個節點最多有m個孩子
  2. 除了根節點和葉子節點外,其它每一個節點至少有Ceil(m/2)個孩子。
  3. 若根節點不是葉子節點,則至少有2個孩子
  4. 全部葉子節點都在同一層,且不包含其它關鍵字信息
  5. 每一個非終端節點包含n個關鍵字信息(P0,P1,…Pn, k1,…kn)
  6. 關鍵字的個數n知足:ceil(m/2)-1 <= n <= m-1
  7. ki(i=1,…n)爲關鍵字,且關鍵字升序排序
  8. Pi(i=1,…n)爲指向子樹根節點的指針。P(i-1)指向的子樹的全部節點關鍵字均小於ki,但都大於k(i-1)

B-Tree 中的每一個節點根據實際狀況能夠包含大量的關鍵字信息和分支,以下圖所示爲一個 3 階的 B-Tree:

 
image

圖片:DobbinSoong

每一個節點佔用一個盤塊的磁盤空間,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指針,指針存儲的是子節點所在磁盤塊的地址。兩個關鍵詞劃分紅的三個範圍域對應三個指針指向的子樹的數據的範圍域。以根節點爲例,關鍵字爲17和35,P1指針指向的子樹的數據範圍爲小於17,P2指針指向的子樹的數據範圍爲17~35,P3指針指向的子樹的數據範圍爲大於35。

模擬查找關鍵字29個過程:

  1. 根據根節點找到磁盤塊1,讀入內存。【磁盤I/O操做第1次】
  2. 比較關鍵字29在區間(17,35),找到磁盤塊1的指針P2。
  3. 根據P2指針找到磁盤塊3,讀入內存。【磁盤I/O操做第2次】
  4. 比較關鍵字29在區間(26,30),找到磁盤塊3的指針P2。
  5. 根據P2指針找到磁盤塊8,讀入內存。【磁盤I/O操做第3次】
  6. 在磁盤塊8中的關鍵字列表中找到關鍵字29。

分析上面過程,發現須要3次磁盤I/O操做,和3次內存查找操做。因爲內存中的關鍵字是一個有序表結構,能夠利用二分法查找提升效率。而3次磁盤I/O操做是影響整個B-Tree查找效率的決定因素。B-Tree相對於AVLTree縮減了節點個數,使每次磁盤I/O取到內存的數據都發揮了做用,從而提升了查詢效率。

B+Tree

B+Tree 是在 B-Tree 基礎上的一種優化,使其更適合實現外存儲索引結構,InnoDB 存儲引擎就是用 B+Tree 實現其索引結構。

從上一節中的B-Tree結構圖中能夠看到每一個節點中不只包含數據的key值,還有data值。而每個頁的存儲空間是有限的,若是data數據較大時將會致使每一個節點(即一個頁)能存儲的key的數量很小,當存儲的數據量很大時一樣會致使B-Tree的深度較大,增大查詢時的磁盤I/O次數,進而影響查詢效率。在B+Tree中,全部數據記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只存儲key值信息,這樣能夠大大加大每一個節點存儲的key值數量,下降B+Tree的高度。

B+Tree相對於B-Tree有幾點不一樣:

  1. 非葉子節點只存儲鍵值信息;
  2. 全部葉子節點之間都有一個鏈指針;
  3. 數據記錄都存放在葉子節點中

將上一節中的B-Tree優化,因爲B+Tree的非葉子節點只存儲鍵值信息,假設每一個磁盤塊能存儲4個鍵值及指針信息,則變成B+Tree後其結構以下圖所示:

 
image

一般在B+Tree上有兩個頭指針,一個指向根節點,另外一個指向關鍵字最小的葉子節點,並且全部葉子節點(即數據節點)之間是一種鏈式環結構。所以能夠對B+Tree進行兩種查找運算:一種是對於主鍵的範圍查找和分頁查找,另外一種是從根節點開始,進行隨機查找。

可能上面例子中只有22條數據記錄,看不出B+Tree的優勢,下面作一個推算:

InnoDB存儲引擎中頁的大小爲16KB,通常表的主鍵類型爲INT(佔用4個字節)或BIGINT(佔用8個字節),指針類型也通常爲4或8個字節,也就是說一個頁(B+Tree中的一個節點)中大概存儲16KB/(8B+8B)=1K個鍵值(由於是估值,爲方便計算,這裏的K取值爲103)。也就是說一個深度爲3的B+Tree索引能夠維護103 * 10^3 * 10^3 = 10億 條記錄。

實際狀況中每一個節點可能不能填充滿,所以在數據庫中,B+Tree的高度通常都在2-4層。MySQL的InnoDB存儲引擎在設計時是將根節點常駐內存的,也就是說查找某一鍵值的行記錄時最多隻須要1~3次磁盤I/O操做。

B+Tree性質

  1. 經過上面的分析,咱們知道IO次數取決於b+數的高度h,假設當前數據表的數據爲N,每一個磁盤塊的數據項的數量是m,則有h=㏒(m+1)N,當數據量N必定的狀況下,m越大,h越小;而m = 磁盤塊的大小 / 數據項的大小,磁盤塊的大小也就是一個數據頁的大小,是固定的,若是數據項佔的空間越小,數據項的數量越多,樹的高度越低。這就是爲何每一個數據項,即索引字段要儘可能的小,好比int佔4字節,要比bigint8字節少一半。這也是爲何b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度降低,致使樹增高。當數據項等於1時將會退化成線性表。
  2. 當b+樹的數據項是複合的數據結構,好比(name,age,sex)的時候,b+數是按照從左到右的順序來創建搜索樹的,好比當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來肯定下一步的所搜方向,若是name相同再依次比較age和sex,最後獲得檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪一個節點,由於創建搜索樹的時候name就是第一個比較因子,必需要先根據name來搜索才能知道下一步去哪裏查詢。好比當(張三,F)這樣的數據來檢索時,b+樹能夠用name來指定搜索方向,但下一個字段age的缺失,因此只能把名字等於張三的數據都找到,而後再匹配性別是F的數據了, 這個是很是重要的性質,即索引的最左匹配特性
MyISAM主鍵索引與輔助索引的結構

MyISAM引擎的索引文件和數據文件是分離的。MyISAM引擎索引結構的葉子節點的數據域,存放的並非實際的數據記錄,而是數據記錄的地址。索引文件與數據文件分離,這樣的索引稱爲"非聚簇索引"。MyISAM的主索引與輔助索引區別並不大,只是主鍵索引不能有重複的關鍵字。

 
image

在MyISAM中,索引(含葉子節點)存放在單獨的.myi文件中,葉子節點存放的是數據的物理地址偏移量(經過偏移量訪問就是隨機訪問,速度很快)。

主索引是指主鍵索引,鍵值不可能重複;輔助索引則是普通索引,鍵值可能重複。

經過索引查找數據的流程:先從索引文件中查找到索引節點,從中拿到數據的文件指針,再到數據文件中經過文件指針定位了具體的數據。輔助索引相似。

InnoDB主鍵索引與輔助索引的結構

InnoDB引擎索引結構的葉子節點的數據域,存放的就是實際的數據記錄(對於主索引,此處會存放表中全部的數據記錄;對於輔助索引此處會引用主鍵,檢索的時候經過主鍵到主鍵索引中找到對應數據行),或者說,InnoDB的數據文件自己就是主鍵索引文件,這樣的索引被稱爲「聚簇索引」,一個表只能有一個聚簇索引。

主鍵索引:

咱們知道InnoDB索引是彙集索引,它的索引和數據是存入同一個.idb文件中的,所以它的索引結構是在同一個樹節點中同時存放索引和數據,以下圖中最底層的葉子節點有三行數據,對應於數據表中的id、stu_id、name數據項。

 
image

在Innodb中,索引分葉子節點和非葉子節點,非葉子節點就像新華字典的目錄,單獨存放在索引段中,葉子節點則是順序排列的,在數據段中。Innodb的數據文件能夠按照表來切分(只須要開啓innodb_file_per_table),切分後存放在xxx.ibd中,默認不切分,存放在xxx.ibdata中。

輔助(非主鍵)索引:

此次咱們以示例中學生表中的name列創建輔助索引,它的索引結構跟主鍵索引的結構有很大差異,在最底層的葉子結點有兩行數據,第一行的字符串是輔助索引,按照ASCII碼進行排序,第二行的整數是主鍵的值。

這就意味着,對name列進行條件搜索,須要兩個步驟:

① 在輔助索引上檢索name,到達其葉子節點獲取對應的主鍵;

② 使用主鍵在主索引上再進行對應的檢索操做

這也就是所謂的「回表查詢

 
image

InnoDB 索引結構須要注意的點

  1. 數據文件自己就是索引文件
  2. 表數據文件自己就是按 B+Tree 組織的一個索引結構文件
  3. 彙集索引中葉節點包含了完整的數據記錄
  4. InnoDB 表必需要有主鍵,而且推薦使用整型自增主鍵

正如咱們上面介紹 InnoDB 存儲結構,索引與數據是共同存儲的,無論是主鍵索引仍是輔助索引,在查找時都是經過先查找到索引節點才能拿到相對應的數據,若是咱們在設計表結構時沒有顯式指定索引列的話,MySQL 會從表中選擇數據不重複的列創建索引,若是沒有符合的列,則 MySQL 自動爲 InnoDB 表生成一個隱含字段做爲主鍵,而且這個字段長度爲6個字節,類型爲整型。

那爲何推薦使用整型自增主鍵而不是選擇UUID?

  • UUID是字符串,比整型消耗更多的存儲空間;
  • 在B+樹中進行查找時須要跟通過的節點值比較大小,整型數據的比較運算比字符串更快速;
  • 自增的整型索引在磁盤中會連續存儲,在讀取一頁數據時也是連續;UUID是隨機產生的,讀取的上下兩行數據存儲是分散的,不適合執行where id > 5 && id < 20的條件查詢語句。
  • 在插入或刪除數據時,整型自增主鍵會在葉子結點的末尾創建新的葉子節點,不會破壞左側子樹的結構;UUID主鍵很容易出現這樣的狀況,B+樹爲了維持自身的特性,有可能會進行結構的重構,消耗更多的時間。

爲何非主鍵索引結構葉子節點存儲的是主鍵值?

保證數據一致性和節省存儲空間,能夠這麼理解:商城系統訂單表會存儲一個用戶ID做爲關聯外鍵,而不推薦存儲完整的用戶信息,由於當咱們用戶表中的信息(真實名稱、手機號、收貨地址···)修改後,不須要再次維護訂單表的用戶數據,同時也節省了存儲空間。

Hash索引

  • 主要就是經過Hash算法(常見的Hash算法有直接定址法、平方取中法、摺疊法、除數取餘法、隨機數法),將數據庫字段數據轉換成定長的Hash值,與這條數據的行指針一併存入Hash表的對應位置;若是發生Hash碰撞(兩個不一樣關鍵字的Hash值相同),則在對應Hash鍵下以鏈表形式存儲。檢索算法:在檢索查詢時,就再次對待查關鍵字再次執行相同的Hash算法,獲得Hash值,到對應Hash表對應位置取出數據便可,若是發生Hash碰撞,則須要在取值時進行篩選。目前使用Hash索引的數據庫並很少,主要有Memory等。MySQL目前有Memory引擎和NDB引擎支持Hash索引。

full-text全文索引

  • 全文索引也是MyISAM的一種特殊索引類型,主要用於全文索引,InnoDB從MYSQL5.6版本提供對全文索引的支持。
  • 它用於替代效率較低的LIKE模糊匹配操做,並且能夠經過多字段組合的全文索引一次性全模糊匹配多個字段。
  • 一樣使用B-Tree存放索引數據,但使用的是特定的算法,將字段數據分割後再進行索引(通常每4個字節一次分割),索引文件存儲的是分割前的索引字符串集合,與分割後的索引信息,對應Btree結構的節點存儲的是分割後的詞信息以及它在分割前的索引字符串集合中的位置。

R-Tree空間索引

空間索引是MyISAM的一種特殊索引類型,主要用於地理空間數據類型

爲何Mysql索引要用B+樹不是B樹?

用B+樹不用B樹考慮的是IO對性能的影響,B樹的每一個節點都存儲數據,而B+樹只有葉子節點才存儲數據,因此查找相同數據量的狀況下,B樹的高度更高,IO更頻繁。數據庫索引是存儲在磁盤上的,當數據量大時,就不能把整個索引所有加載到內存了,只能逐一加載每個磁盤頁(對應索引樹的節點)。其中在MySQL底層對B+樹進行進一步優化:在葉子節點中是雙向鏈表,且在鏈表的頭結點和尾節點也是循環指向的。

面試官:爲什麼不採用Hash方式?

由於Hash索引底層是哈希表,哈希表是一種以key-value存儲數據的結構,因此多個數據在存儲關係上是徹底沒有任何順序關係的,因此,對於區間查詢是沒法直接經過索引查詢的,就須要全表掃描。因此,哈希索引只適用於等值查詢的場景。而B+ Tree是一種多路平衡查詢樹,因此他的節點是自然有序的(左子節點小於父節點、父節點小於右子節點),因此對於範圍查詢的時候不須要作全表掃描。

哈希索引不支持多列聯合索引的最左匹配規則,若是有大量重複鍵值得狀況下,哈希索引的效率會很低,由於存在哈希碰撞問題。

哪些狀況須要建立索引

  1. 主鍵自動創建惟一索引
  2. 頻繁做爲查詢條件的字段
  3. 查詢中與其餘表關聯的字段,外鍵關係創建索引
  4. 單鍵/組合索引的選擇問題,高併發下傾向建立組合索引
  5. 查詢中排序的字段,排序字段經過索引訪問大幅提升排序速度
  6. 查詢中統計或分組字段

哪些狀況不要建立索引

  1. 表記錄太少
  2. 常常增刪改的表
  3. 數據重複且分佈均勻的表字段,只應該爲最常常查詢和最常常排序的數據列創建索引(若是某個數據類包含太多的重複數據,創建索引沒有太大意義)
  4. 頻繁更新的字段不適合建立索引(會加劇IO負擔)
  5. where條件裏用不到的字段不建立索引

MySQL高效索引

覆蓋索引(Covering Index),或者叫索引覆蓋, 也就是平時所說的不須要回表操做

  • 就是select的數據列只用從索引中就可以取得,沒必要讀取數據行,MySQL能夠利用索引返回select列表中的字段,而沒必要根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋
  • 索引是高效找到行的一個方法,可是通常數據庫也能使用索引找到一個列的數據,所以它沒必要讀取整個行。畢竟索引葉子節點存儲了它們索引的數據,當能經過讀取索引就能夠獲得想要的數據,那就不須要讀取行了。一個索引包含(覆蓋)知足查詢結果的數據就叫作覆蓋索引。
  • 判斷標準使用explain,能夠經過輸出的extra列來判斷,對於一個索引覆蓋查詢,顯示爲using index,MySQL查詢優化器在執行查詢前會決定是否有索引覆蓋查詢

5、MySQL查詢

count(*) 和 count(1)和count(列名)區別 ps:這道題說法有點多

執行效果上:

  • count(*)包括了全部的列,至關於行數,在統計結果的時候,不會忽略列值爲NULL
  • count(1)包括了全部列,用1表明代碼行,在統計結果的時候,不會忽略列值爲NULL
  • count(列名)只包括列名那一列,在統計結果的時候,會忽略列值爲空(這裏的空不是隻空字符串或者0,而是表示null)的計數,即某個字段值爲NULL時,不統計。

執行效率上:

  • 列名爲主鍵,count(列名)會比count(1)快
  • 列名不爲主鍵,count(1)會比count(列名)快
  • 若是表多個列而且沒有主鍵,則 count(1) 的執行效率優於 count(*)
  • 若是有主鍵,則 select count(主鍵)的執行效率是最優的
  • 若是表只有一個字段,則 select count(*) 最優。

MySQL中 in和 exists 的區別?

  • exists:exists對外表用loop逐條查詢,每次查詢都會查看exists的條件語句,當exists裏的條件語句可以返回記錄行時(不管記錄行是的多少,只要能返回),條件就爲真,返回當前loop到的這條記錄;反之,若是exists裏的條件語句不能返回記錄行,則當前loop到的這條記錄被丟棄,exists的條件就像一個bool條件,當能返回結果集則爲true,不能返回結果集則爲false
  • in:in查詢至關於多個or條件的疊加
SELECT * FROM A WHERE A.id IN (SELECT id FROM B); SELECT * FROM A WHERE EXISTS (SELECT * from B WHERE B.id = A.id); 

若是查詢的兩個表大小至關,那麼用in和exists差異不大

若是兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in:

UNION和UNION ALL的區別?

UNION和UNION ALL都是將兩個結果集合併爲一個,兩個要聯合的SQL語句 字段個數必須同樣,並且字段類型要「相容」(一致);

  • UNION在進行錶鏈接後會篩選掉重複的數據記錄(效率較低),而UNION ALL則不會去掉重複的數據記錄;
  • UNION會按照字段的順序進行排序,而UNION ALL只是簡單的將兩個結果合併就返回;

SQL執行順序

  • 手寫SELECT DISTINCT <select_list>
    FROM <left_table> <join_type>
    JOIN <right_table> ON <join_condition>
    WHERE <where_condition>
    GROUP BY <group_by_list>
    HAVING <having_condition>
    ORDER BY <order_by_condition>
    LIMIT <limit_number>
  • 機讀FROM <left_table>
    ON <join_condition>
    <join_type> JOIN <right_table>
    WHERE <where_condition>
    GROUP BY <group_by_list>
    HAVING <having_condition>
    SELECT
    DISTINCT <select_list>
    ORDER BY <order_by_condition>
    LIMIT <limit_number>
  • 總結
 
image

mysql 的內鏈接、左鏈接、右鏈接有什麼區別?

什麼是內鏈接、外鏈接、交叉鏈接、笛卡爾積呢?

Join圖

 
image

6、MySQL 事務

事務的隔離級別有哪些?MySQL的默認隔離級別是什麼?

什麼是幻讀,髒讀,不可重複讀呢?

MySQL事務的四大特性以及實現原理

MVCC熟悉嗎,它的底層原理?

MySQL 事務主要用於處理操做量大,複雜度高的數據。好比說,在人員管理系統中,你刪除一我的員,你即須要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操做語句就構成一個事務!

ACID — 事務基本要素

事務是由一組SQL語句組成的邏輯處理單元,具備4個屬性,一般簡稱爲事務的ACID屬性。

  • A (Atomicity) 原子性:整個事務中的全部操做,要麼所有完成,要麼所有不完成,不可能停滯在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣
  • C (Consistency) 一致性:在事務開始以前和事務結束之後,數據庫的完整性約束沒有被破壞
  • I (Isolation)隔離性:一個事務的執行不能其它事務干擾。即一個事務內部的操做及使用的數據對其它併發事務是隔離的,併發執行的各個事務之間不能互相干擾
  • D (Durability) 持久性:在事務完成之後,該事務所對數據庫所做的更改便持久的保存在數據庫之中,並不會被回滾

併發事務處理帶來的問題

  • 更新丟失(Lost Update):事務A和事務B選擇同一行,而後基於最初選定的值更新該行時,因爲兩個事務都不知道彼此的存在,就會發生丟失更新問題
  • 髒讀(Dirty Reads):事務A讀取了事務B更新的數據,而後B回滾操做,那麼A讀取到的數據是髒數據
  • 不可重複讀(Non-Repeatable Reads):事務 A 屢次讀取同一數據,事務B在事務A屢次讀取的過程當中,對數據做了更新並提交,致使事務A屢次讀取同一數據時,結果不一致。
  • 幻讀(Phantom Reads):幻讀與不可重複讀相似。它發生在一個事務A讀取了幾行數據,接着另外一個併發事務B插入了一些數據時。在隨後的查詢中,事務A就會發現多了一些本來不存在的記錄,就好像發生了幻覺同樣,因此稱爲幻讀。

幻讀和不可重複讀的區別:

  • 不可重複讀的重點是修改:在同一事務中,一樣的條件,第一次讀的數據和第二次讀的數據不同。(由於中間有其餘事務提交了修改)
  • 幻讀的重點在於新增或者刪除:在同一事務中,一樣的條件,,第一次和第二次讀出來的記錄數不同。(由於中間有其餘事務提交了插入/刪除)

併發事務處理帶來的問題的解決辦法:

  • 「更新丟失」一般是應該徹底避免的。但防止更新丟失,並不能單靠數據庫事務控制器來解決,須要應用程序對要更新的數據加必要的鎖來解決,所以,防止更新丟失應該是應用的責任。

  • 「髒讀」 、 「不可重複讀」和「幻讀」 ,其實都是數據庫讀一致性問題,必須由數據庫提供必定的事務隔離機制來解決:

  • 一種是加鎖:在讀取數據前,對其加鎖,阻止其餘事務對數據進行修改。

  • 另外一種是數據多版本併發控制(MultiVersion Concurrency Control,簡稱 MVCC 或 MCC),也稱爲多版本數據庫:不用加任何鎖, 經過必定機制生成一個數據請求時間點的一致性數據快照 (Snapshot), 並用這個快照來提供必定級別 (語句級或事務級) 的一致性讀取。從用戶的角度來看,好象是數據庫能夠提供同一數據的多個版本。

事務隔離級別

數據庫事務的隔離級別有4種,由低到高分別爲

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

查看當前數據庫的事務隔離級別:

show variables like 'tx_isolation' 

下面經過事例一一闡述在事務的併發操做中可能會出現髒讀,不可重複讀,幻讀和事務隔離級別的聯繫。

數據庫的事務隔離越嚴格,併發反作用越小,但付出的代價就越大,由於事務隔離實質上就是使事務在必定程度上「串行化」進行,這顯然與「併發」是矛盾的。同時,不一樣的應用對讀一致性和事務隔離程度的要求也是不一樣的,好比許多應用對「不可重複讀」和「幻讀」並不敏感,可能更關心數據併發訪問的能力。

Read uncommitted

讀未提交,就是一個事務能夠讀取另外一個未提交事務的數據。

事例:老闆要給程序員發工資,程序員的工資是3.6萬/月。可是發工資時老闆不當心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,可是事務尚未提交,就在這時,程序員去查看本身這個月的工資,發現比往常多了3千元,覺得漲工資了很是高興。可是老闆及時發現了不對,立刻回滾差點就提交了的事務,將數字改爲3.6萬再提交。

分析:實際程序員這個月的工資仍是3.6萬,可是程序員看到的是3.9萬。他看到的是老闆還沒提交事務時的數據。這就是髒讀。

那怎麼解決髒讀呢?Read committed!讀提交,能解決髒讀問題。

Read committed

讀提交,顧名思義,就是一個事務要等另外一個事務提交後才能讀取數據。

事例:程序員拿着信用卡去享受生活(卡里固然是隻有3.6萬),當他埋單時(程序員事務開啓),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢所有轉出充當家用,並提交。當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額固然要等待妻子轉出金額事務提交完)。程序員就會很鬱悶,明明卡里是有錢的…

分析:這就是讀提交,如有事務對數據進行更新(UPDATE)操做時,讀操做事務要等待這個更新操做事務提交後才能讀取數據,能夠解決髒讀問題。但在這個事例中,出現了一個事務範圍內兩個相同的查詢卻返回了不一樣數據,這就是不可重複讀

那怎麼解決可能的不可重複讀問題?Repeatable read !

Repeatable read

重複讀,就是在開始讀取數據(事務開啓)時,再也不容許修改操做。MySQL的默認事務隔離級別

事例:程序員拿着信用卡去享受生活(卡里固然是隻有3.6萬),當他埋單時(事務開啓,不容許其餘事務的UPDATE修改操做),收費系統事先檢測到他的卡里有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就能夠扣款了。

分析:重複讀能夠解決不可重複讀問題。寫到這裏,應該明白的一點就是,不可重複讀對應的是修改,即UPDATE操做。可是可能還會有幻讀問題。由於幻讀問題對應的是插入INSERT操做,而不是UPDATE操做

何時會出現幻讀?

事例:程序員某一天去消費,花了2千元,而後他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啓),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,彷佛出現了幻覺,這就是幻讀。

那怎麼解決幻讀問題?Serializable!

Serializable 序列化

Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,能夠避免髒讀、不可重複讀與幻讀。簡單來講,Serializable會在讀取的每一行數據上都加鎖,因此可能致使大量的超時和鎖爭用問題。這種事務隔離級別效率低下,比較耗數據庫性能,通常不使用。

比較

事務隔離級別讀數據一致性髒讀不可重複讀幻讀讀未提交(read-uncommitted)最低級被,只能保證不讀取物理上損壞的數據是是是讀已提交(read-committed)語句級否是是可重複讀(repeatable-read)事務級否否是串行化(serializable)最高級別,事務級否否否

須要說明的是,事務隔離級別和數據訪問的併發性是對立的,事務隔離級別越高併發性就越差。因此要根據具體的應用來肯定合適的事務隔離級別,這個地方沒有萬能的原則。

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

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

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

MVCC 多版本併發控制

MySQL的大多數事務型存儲引擎實現都不是簡單的行級鎖。基於提高併發性考慮,通常都同時實現了多版本併發控制(MVCC),包括Oracle、PostgreSQL。只是實現機制各不相同。

能夠認爲 MVCC 是行級鎖的一個變種,但它在不少狀況下避免了加鎖操做,所以開銷更低。雖然實現機制有所不一樣,但大都實現了非阻塞的讀操做,寫操做也只是鎖定必要的行。

MVCC 的實現是經過保存數據在某個時間點的快照來實現的。也就是說無論須要執行多長時間,每一個事物看到的數據都是一致的。

典型的MVCC實現方式,分爲樂觀(optimistic)併發控制和悲觀(pressimistic)併發控制。下邊經過 InnoDB的簡化版行爲來講明 MVCC 是如何工做的。

InnoDB 的 MVCC,是經過在每行記錄後面保存兩個隱藏的列來實現。這兩個列,一個保存了行的建立時間,一個保存行的過時時間(刪除時間)。固然存儲的並非真實的時間,而是系統版本號(system version number)。每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會做爲事務的版本號,用來和查詢到的每行記錄的版本號進行比較。

REPEATABLE READ(可重讀)隔離級別下MVCC如何工做:

  • SELECTInnoDB會根據如下兩個條件檢查每行記錄:只有符合上述兩個條件的纔會被查詢出來

  • InnoDB只查找版本早於當前事務版本的數據行,這樣能夠確保事務讀取的行,要麼是在開始事務以前已經存在要麼是事務自身插入或者修改過的

  • 行的刪除版本號要麼未定義,要麼大於當前事務版本號,這樣能夠確保事務讀取到的行在事務開始以前未被刪除

  • INSERT:InnoDB爲新插入的每一行保存當前系統版本號做爲行版本號

  • DELETE:InnoDB爲刪除的每一行保存當前系統版本號做爲行刪除標識

  • UPDATE:InnoDB爲插入的一行新紀錄保存當前系統版本號做爲行版本號,同時保存當前系統版本號到原來的行做爲刪除標識

保存這兩個額外系統版本號,使大多數操做都不用加鎖。使數據操做簡單,性能很好,而且也能保證只會讀取到符合要求的行。不足之處是每行記錄都須要額外的存儲空間,須要作更多的行檢查工做和一些額外的維護工做。

MVCC 只在 COMMITTED READ(讀提交)和REPEATABLE READ(可重複讀)兩種隔離級別下工做。

事務日誌

InnoDB 使用日誌來減小提交事務時的開銷。由於日誌中已經記錄了事務,就無須在每一個事務提交時把緩衝池的髒塊刷新(flush)到磁盤中。

事務修改的數據和索引一般會映射到表空間的隨機位置,因此刷新這些變動到磁盤須要不少隨機 IO。

InnoDB 假設使用常規磁盤,隨機IO比順序IO昂貴得多,由於一個IO請求須要時間把磁頭移到正確的位置,而後等待磁盤上讀出須要的部分,再轉到開始位置。

InnoDB 用日誌把隨機IO變成順序IO。一旦日誌安全寫到磁盤,事務就持久化了,即便斷電了,InnoDB能夠重放日誌而且恢復已經提交的事務。

InnoDB 使用一個後臺線程智能地刷新這些變動到數據文件。這個線程能夠批量組合寫入,使得數據寫入更順序,以提升效率。

事務日誌能夠幫助提升事務效率:

  • 使用事務日誌,存儲引擎在修改表的數據時只須要修改其內存拷貝,再把該修改行爲記錄到持久在硬盤上的事務日誌中,而不用每次都將修改的數據自己持久到磁盤。
  • 事務日誌採用的是追加的方式,所以寫日誌的操做是磁盤上一小塊區域內的順序I/O,而不像隨機I/O須要在磁盤的多個地方移動磁頭,因此採用事務日誌的方式相對來講要快得多。
  • 事務日誌持久之後,內存中被修改的數據在後臺能夠慢慢刷回到磁盤。
  • 若是數據的修改已經記錄到事務日誌並持久化,但數據自己沒有寫回到磁盤,此時系統崩潰,存儲引擎在重啓時可以自動恢復這一部分修改的數據。

目前來講,大多數存儲引擎都是這樣實現的,咱們一般稱之爲預寫式日誌(Write-Ahead Logging),修改數據須要寫兩次磁盤。

事務的實現

事務的實現是基於數據庫的存儲引擎。不一樣的存儲引擎對事務的支持程度不同。MySQL 中支持事務的存儲引擎有 InnoDB 和 NDB。

事務的實現就是如何實現ACID特性。

事務的隔離性是經過鎖實現,而事務的原子性、一致性和持久性則是經過事務日誌實現 。

事務是如何經過日誌來實現的,說得越深刻越好。

事務日誌包括:重作日誌redo回滾日誌undo

  • redo log(重作日誌) 實現持久化和原子性在innoDB的存儲引擎中,事務日誌經過重作(redo)日誌和innoDB存儲引擎的日誌緩衝(InnoDB Log Buffer)實現。事務開啓時,事務中的操做,都會先寫入存儲引擎的日誌緩衝中,在事務提交以前,這些緩衝的日誌都須要提早刷新到磁盤上持久化,這就是DBA們口中常說的「日誌先行」(Write-Ahead Logging)。當事務提交以後,在Buffer Pool中映射的數據文件纔會慢慢刷新到磁盤。此時若是數據庫崩潰或者宕機,那麼當系統重啓進行恢復時,就能夠根據redo log中記錄的日誌,把數據庫恢復到崩潰前的一個狀態。未完成的事務,能夠繼續提交,也能夠選擇回滾,這基於恢復的策略而定。在系統啓動的時候,就已經爲redo log分配了一塊連續的存儲空間,以順序追加的方式記錄Redo Log,經過順序IO來改善性能。全部的事務共享redo log的存儲空間,它們的Redo Log按語句的執行順序,依次交替的記錄在一塊兒。
  • undo log(回滾日誌) 實現一致性undo log 主要爲事務的回滾服務。在事務執行的過程當中,除了記錄redo log,還會記錄必定量的undo log。undo log記錄了數據在每一個操做前的狀態,若是事務執行過程當中須要回滾,就能夠根據undo log進行回滾操做。單個事務的回滾,只會回滾當前事務作的操做,並不會影響到其餘的事務作的操做。Undo記錄的是已部分完成而且寫入硬盤的未完成的事務,默認狀況下回滾日誌是記錄下表空間中的(共享表空間或者獨享表空間)

二種日誌都可以視爲一種恢復操做,redo_log是恢復提交事務修改的頁操做,而undo_log是回滾行記錄到特定版本。兩者記錄的內容也不一樣,redo_log是物理日誌,記錄頁的物理修改操做,而undo_log是邏輯日誌,根據每行記錄進行記錄。

又引出個問題:你知道MySQL 有多少種日誌嗎?

  • 錯誤日誌:記錄出錯信息,也記錄一些警告信息或者正確的信息。
  • 查詢日誌:記錄全部對數據庫請求的信息,不論這些請求是否獲得了正確的執行。
  • 慢查詢日誌:設置一個閾值,將運行時間超過該值的全部SQL語句都記錄到慢查詢的日誌文件中。
  • 二進制日誌:記錄對數據庫執行更改的全部操做。
  • 中繼日誌:中繼日誌也是二進制日誌,用來給slave 庫恢復
  • 事務日誌:重作日誌redo和回滾日誌undo

分佈式事務相關問題,可能還會問到 2PC、3PC,,,

MySQL對分佈式事務的支持

分佈式事務的實現方式有不少,既能夠採用 InnoDB 提供的原生的事務支持,也能夠採用消息隊列來實現分佈式事務的最終一致性。這裏咱們主要聊一下 InnoDB 對分佈式事務的支持。

MySQL 從 5.0.3 InnoDB 存儲引擎開始支持XA協議的分佈式事務。一個分佈式事務會涉及多個行動,這些行動自己是事務性的。全部行動都必須一塊兒成功完成,或者一塊兒被回滾。

在MySQL中,使用分佈式事務涉及一個或多個資源管理器和一個事務管理器。

 
image

如圖,MySQL 的分佈式事務模型。模型中分三塊:應用程序(AP)、資源管理器(RM)、事務管理器(TM):

  • 應用程序:定義了事務的邊界,指定須要作哪些事務;
  • 資源管理器:提供了訪問事務的方法,一般一個數據庫就是一個資源管理器;
  • 事務管理器:協調參與了全局事務中的各個事務。

分佈式事務採用兩段式提交(two-phase commit)的方式:

  • 第一階段全部的事務節點開始準備,告訴事務管理器ready。
  • 第二階段事務管理器告訴每一個節點是commit仍是rollback。若是有一個節點失敗,就須要全局的節點所有rollback,以此保障事務的原子性。

7、MySQL鎖機制

數據庫的樂觀鎖和悲觀鎖?

MySQL 中有哪幾種鎖,列舉一下?

MySQL中InnoDB引擎的行鎖是怎麼實現的?

MySQL 間隙鎖有沒有了解,死鎖有沒有了解,寫一段會形成死鎖的 sql 語句,死鎖發生瞭如何解決,MySQL 有沒有提供什麼機制去解決死鎖

鎖是計算機協調多個進程或線程併發訪問某一資源的機制。

在數據庫中,除傳統的計算資源(如CPU、RAM、I/O等)的爭用之外,數據也是一種供許多用戶共享的資源。數據庫鎖定機制簡單來講,就是數據庫爲了保證數據的一致性,而使各類共享資源在被併發訪問變得有序所設計的一種規則。

打個比方,咱們到淘寶上買一件商品,商品只有一件庫存,這個時候若是還有另外一我的買,那麼如何解決是你買到仍是另外一我的買到的問題?這裏確定要用到事物,咱們先從庫存表中取出物品數量,而後插入訂單,付款後插入付款表信息,而後更新商品數量。在這個過程當中,使用鎖能夠對有限的資源進行保護,解決隔離和併發的矛盾。

鎖的分類

從對數據操做的類型分類

  • 讀鎖(共享鎖):針對同一份數據,多個讀操做能夠同時進行,不會互相影響
  • 寫鎖(排他鎖):當前寫操做沒有完成前,它會阻斷其餘寫鎖和讀鎖

從對數據操做的粒度分類

爲了儘量提升數據庫的併發度,每次鎖定的數據範圍越小越好,理論上每次只鎖定當前操做的數據的方案會獲得最大的併發度,可是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動做),所以數據庫系統須要在高併發響應和系統性能兩方面進行平衡,這樣就產生了「鎖粒度(Lock granularity)」的概念。

  • 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低(MyISAM 和 MEMORY 存儲引擎採用的是表級鎖);
  • 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高(InnoDB 存儲引擎既支持行級鎖也支持表級鎖,但默認狀況下是採用行級鎖);
  • 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。

適用:從鎖的角度來講,表級鎖更適合於以查詢爲主,只有少許按索引條件更新數據的應用,如Web應用;而行級鎖則更適合於有大量按索引條件併發更新少許不一樣數據,同時又有併發查詢的應用,如一些在線事務處理(OLTP)系統。

行鎖表鎖頁鎖MyISAM

BDB

√√InnoDB√√

Memory

MyISAM 表鎖

MyISAM 的表鎖有兩種模式:

  • 表共享讀鎖 (Table Read Lock):不會阻塞其餘用戶對同一表的讀請求,但會阻塞對同一表的寫請求;
  • 表獨佔寫鎖 (Table Write Lock):會阻塞其餘用戶對同一表的讀和寫操做;

MyISAM 表的讀操做與寫操做之間,以及寫操做之間是串行的。當一個線程得到對一個表的寫鎖後, 只有持有鎖的線程能夠對錶進行更新操做。其餘線程的讀、 寫操做都會等待,直到鎖被釋放爲止。

默認狀況下,寫鎖比讀鎖具備更高的優先級:當一個鎖釋放時,這個鎖會優先給寫鎖隊列中等候的獲取鎖請求,而後再給讀鎖隊列中等候的獲取鎖請求。

InnoDB 行鎖

InnoDB 實現瞭如下兩種類型的行鎖

  • 共享鎖(S):容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。
  • 排他鎖(X):容許得到排他鎖的事務更新數據,阻止其餘事務取得相同數據集的共享讀鎖和排他寫鎖。

爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB 還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖

  • 意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的 IS 鎖。
  • 意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的 IX 鎖。

索引失效會致使行鎖變表鎖。好比 vchar 查詢不寫單引號的狀況。

加鎖機制

樂觀鎖與悲觀鎖是兩種併發控制的思想,可用於解決丟失更新問題

樂觀鎖會「樂觀地」假定大機率不會發生併發更新衝突,訪問、處理數據過程當中不加鎖,只在更新數據時再根據版本號或時間戳判斷是否有衝突,有則處理,無則提交事務。用數據版本(Version)記錄機制實現,這是樂觀鎖最經常使用的一種實現方式

悲觀鎖會「悲觀地」假定大機率會發生併發更新衝突,訪問、處理數據前就加排他鎖,在整個數據處理過程當中鎖定數據,事務提交或回滾後才釋放鎖。另外與樂觀鎖相對應的,悲觀鎖是由數據庫本身實現了的,要用的時候,咱們直接調用數據庫的相關語句就能夠了。

鎖模式(InnoDB有三種行鎖的算法)

  • 記錄鎖(Record Locks):單個行記錄上的鎖。對索引項加鎖,鎖定符合條件的行。其餘事務不能修改和刪除加鎖項;SELECT * FROM table WHERE id = 1 FOR UPDATE;它會在 id=1 的記錄上加上記錄鎖,以阻止其餘事務插入,更新,刪除 id=1 這一行在經過 主鍵索引 與 惟一索引 對數據行進行 UPDATE 操做時,也會對該行數據加記錄鎖:-- id 列爲主鍵列或惟一索引列
    UPDATE SET age = 50 WHERE id = 1;
  • 間隙鎖(Gap Locks):當咱們使用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖。對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙」。InnoDB 也會對這個「間隙」加鎖,這種鎖機制就是所謂的間隙鎖。對索引項之間的「間隙」加鎖,鎖定記錄的範圍(對第一條記錄前的間隙或最後一條將記錄後的間隙加鎖),不包含索引項自己。其餘事務不能在鎖範圍內插入數據,這樣就防止了別的事務新增幻影行。間隙鎖基於非惟一索引,它鎖定一段範圍內的索引記錄。間隙鎖基於下面將會提到的Next-Key Locking 算法,請務必牢記:使用間隙鎖鎖住的是一個區間,而不只僅是這個區間中的每一條數據。SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;即全部在(1,10)區間內的記錄行都會被鎖住,全部id 爲 二、三、四、五、六、七、八、9 的數據行的插入會被阻塞,可是 1 和 10 兩條記錄行並不會被鎖住。GAP鎖的目的,是爲了防止同一事務的兩次當前讀,出現幻讀的狀況
  • 臨鍵鎖(Next-key Locks)臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖範圍,既包含索引記錄,又包含索引區間。(臨鍵鎖的主要目的,也是爲了不幻讀(Phantom Read)。若是把事務的隔離級別降級爲RC,臨鍵鎖則也會失效。)Next-Key 能夠理解爲一種特殊的間隙鎖,也能夠理解爲一種特殊的算法。經過臨建鎖能夠解決幻讀的問題。每一個數據行上的非惟一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。須要強調的一點是,InnoDB 中行級鎖是基於索引實現的,臨鍵鎖只與非惟一索引列有關,在惟一索引列(包括主鍵列)上不存在臨鍵鎖。對於行的查詢,都是採用該方法,主要目的是解決幻讀的問題。

select for update有什麼含義,會鎖表仍是鎖行仍是其餘

for update 僅適用於InnoDB,且必須在事務塊(BEGIN/COMMIT)中才能生效。在進行事務操做時,經過「for update」語句,MySQL會對查詢結果集中每行數據都添加排他鎖,其餘線程對該記錄的更新與刪除操做都會阻塞。排他鎖包含行鎖、表鎖。

InnoDB這種行鎖實現特色意味着:只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖!假設有個表單 products ,裏面有id跟name二個欄位,id是主鍵。

  • 明確指定主鍵,而且有此筆資料,row lock
SELECT * FROM products WHERE id='3' FOR UPDATE; SELECT * FROM products WHERE id='3' and type=1 FOR UPDATE; 
  • 明確指定主鍵,若查無此筆資料,無lock
SELECT * FROM products WHERE id='-1' FOR UPDATE;
  • 無主鍵,table lock
SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
  • 主鍵不明確,table lock
SELECT * FROM products WHERE id<>'3' FOR UPDATE; 
  • 主鍵不明確,table lock
SELECT * FROM products WHERE id LIKE '3' FOR UPDATE; 

注1: FOR UPDATE僅適用於InnoDB,且必須在交易區塊(BEGIN/COMMIT)中才能生效。注2: 要測試鎖定的情況,能夠利用MySQL的Command Mode ,開二個視窗來作測試。

MySQL 遇到過死鎖問題嗎,你是如何解決的?

死鎖

死鎖產生

  • 死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方佔用的資源,從而致使惡性循環
  • 當事務試圖以不一樣的順序鎖定資源時,就可能產生死鎖。多個事務同時鎖定同一個資源時也可能會產生死鎖
  • 鎖的行爲和順序和存儲引擎相關。以一樣的順序執行語句,有些存儲引擎會產生死鎖有些不會——死鎖有雙重緣由:真正的數據衝突;存儲引擎的實現方式。

檢測死鎖:數據庫系統實現了各類死鎖檢測和死鎖超時的機制。InnoDB存儲引擎能檢測到死鎖的循環依賴並當即返回一個錯誤。

死鎖恢復:死鎖發生之後,只有部分或徹底回滾其中一個事務,才能打破死鎖,InnoDB目前處理死鎖的方法是,將持有最少行級排他鎖的事務進行回滾。因此事務型應用程序在設計時必須考慮如何處理死鎖,多數狀況下只須要從新執行因死鎖回滾的事務便可。

外部鎖的死鎖檢測:發生死鎖後,InnoDB 通常都能自動檢測到,並使一個事務釋放鎖並回退,另外一個事務得到鎖,繼續完成事務。但在涉及外部鎖,或涉及表鎖的狀況下,InnoDB 並不能徹底自動檢測到死鎖, 這須要經過設置鎖等待超時參數 innodb_lock_wait_timeout 來解決

死鎖影響性能:死鎖會影響性能而不是會產生嚴重錯誤,由於InnoDB會自動檢測死鎖情況並回滾其中一個受影響的事務。在高併發系統上,當許多線程等待同一個鎖時,死鎖檢測可能致使速度變慢。有時當發生死鎖時,禁用死鎖檢測(使用innodb_deadlock_detect配置選項)可能會更有效,這時能夠依賴innodb_lock_wait_timeout設置進行事務回滾。

MyISAM避免死鎖

  • 在自動加鎖的狀況下,MyISAM 老是一次得到 SQL 語句所須要的所有鎖,因此 MyISAM 表不會出現死鎖。

InnoDB避免死鎖

  • 爲了在單個InnoDB表上執行多個併發寫入操做時避免死鎖,能夠在事務開始時經過爲預期要修改的每一個元祖(行)使用SELECT ... FOR UPDATE語句來獲取必要的鎖,即便這些行的更改語句是在以後才執行的。
  • 在事務中,若是要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,而不該先申請共享鎖、更新時再申請排他鎖,由於這時候當用戶再申請排他鎖時,其餘事務可能又已經得到了相同記錄的共享鎖,從而形成鎖衝突,甚至死鎖
  • 若是事務須要修改或鎖定多個表,則應在每一個事務中以相同的順序使用加鎖語句。在應用中,若是不一樣的程序會併發存取多個表,應儘可能約定以相同的順序來訪問表,這樣能夠大大下降產生死鎖的機會
  • 經過SELECT ... LOCK IN SHARE MODE獲取行的讀鎖後,若是當前事務再須要對該記錄進行更新操做,則頗有可能形成死鎖。
  • 改變事務隔離級別

若是出現死鎖,能夠用 show engine innodb status;命令來肯定最後一個死鎖產生的緣由。返回結果中包括死鎖相關事務的詳細信息,如引起死鎖的 SQL 語句,事務已經得到的鎖,正在等待什麼鎖,以及被回滾的事務等。據此能夠分析死鎖產生的緣由和改進措施。


8、MySQL調優

平常工做中你是怎麼優化SQL的?

SQL優化的通常步驟是什麼,怎麼看執行計劃(explain),如何理解其中各個字段的含義?

如何寫sql可以有效的使用到複合索引?

一條sql執行過長的時間,你如何優化,從哪些方面入手?

什麼是最左前綴原則?什麼是最左匹配原則?

影響mysql的性能因素

  • 業務需求對MySQL的影響(合適合度)

  • 存儲定位對MySQL的影響

  • 系統各類配置及規則數據

  • 活躍用戶的基本信息數據

  • 活躍用戶的個性化定製信息數據

  • 準實時的統計信息數據

  • 其餘一些訪問頻繁但變動較少的數據

  • 二進制多媒體數據

  • 流水隊列數據

  • 超大文本數據

  • 不適合放進MySQL的數據

  • 須要放進緩存的數據

  • Schema設計對系統的性能影響

  • 儘可能減小對數據庫訪問的請求

  • 儘可能減小無用數據的查詢請求

  • 硬件環境對系統性能的影響

性能分析

MySQL Query Optimizer

  1. MySQL 中有專門負責優化 SELECT 語句的優化器模塊,主要功能:經過計算分析系統中收集到的統計信息,爲客戶端請求的 Query 提供他認爲最優的執行計劃(他認爲最優的數據檢索方式,但不見得是 DBA 認爲是最優的,這部分最耗費時間)
  2. 當客戶端向 MySQL 請求一條 Query,命令解析器模塊完成請求分類,區別出是 SELECT 並轉發給 MySQL Query Optimize r時,MySQL Query Optimizer 首先會對整條 Query 進行優化,處理掉一些常量表達式的預算,直接換算成常量值。並對 Query 中的查詢條件進行簡化和轉換,如去掉一些無用或顯而易見的條件、結構調整等。而後分析 Query 中的 Hint 信息(若是有),看顯示 Hint 信息是否能夠徹底肯定該 Query 的執行計劃。若是沒有 Hint 或 Hint 信息還不足以徹底肯定執行計劃,則會讀取所涉及對象的統計信息,根據 Query 進行寫相應的計算分析,而後再得出最後的執行計劃。

MySQL常見瓶頸

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

性能降低SQL慢 執行時間長 等待時間長 緣由分析

  • 查詢語句寫的爛
  • 索引失效(單值、複合)
  • 關聯查詢太多join(設計缺陷或不得已的需求)
  • 服務器調優及各個參數設置(緩衝、線程數等)

MySQL常見性能分析手段

在優化MySQL時,一般須要對數據庫進行分析,常見的分析手段有慢查詢日誌EXPLAIN 分析查詢profiling分析以及show命令查詢系統狀態及系統變量,經過定位分析性能的瓶頸,才能更好的優化數據庫系統的性能。

性能瓶頸定位

咱們能夠經過 show 命令查看 MySQL 狀態及變量,找到系統的瓶頸:

Mysql> show status ——顯示狀態信息(擴展show status like ‘XXX’) Mysql> show variables ——顯示系統變量(擴展show variables like ‘XXX’) Mysql> show innodb status ——顯示InnoDB存儲引擎的狀態 Mysql> show processlist ——查看當前SQL執行,包括執行狀態、是否鎖表等 Shell> mysqladmin variables -u username -p password——顯示系統變量 Shell> mysqladmin extended-status -u username -p password——顯示狀態信息 
Explain(執行計劃)

是什麼:使用 Explain 關鍵字能夠模擬優化器執行SQL查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的。分析你的查詢語句或是表結構的性能瓶頸

能幹嘛:

  • 表的讀取順序
  • 數據讀取操做的操做類型
  • 哪些索引能夠使用
  • 哪些索引被實際使用
  • 表之間的引用
  • 每張表有多少行被優化器查詢

怎麼玩:

  • Explain + SQL語句
  • 執行計劃包含的信息(若是有分區表的話還會有partitions
 
image

expalin

各字段解釋

  • id(select 查詢的序列號,包含一組數字,表示查詢中執行select子句或操做表的順序)

  • id相同,執行順序從上往下

  • id全不一樣,若是是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行

  • id部分相同,執行順序是先按照數字大的先執行,而後數字相同的按照從上往下的順序執行

  • select_type(查詢類型,用於區別普通查詢、聯合查詢、子查詢等複雜查詢)

  • SIMPLE :簡單的select查詢,查詢中不包含子查詢或UNION

  • PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢被標記爲PRIMARY

  • SUBQUERY:在select或where列表中包含了子查詢

  • DERIVED:在from列表中包含的子查詢被標記爲DERIVED,MySQL會遞歸執行這些子查詢,把結果放在臨時表裏

  • UNION:若第二個select出如今UNION以後,則被標記爲UNION,若UNION包含在from子句的子查詢中,外層select將被標記爲DERIVED

  • UNION RESULT:從UNION表獲取結果的select

  • table(顯示這一行的數據是關於哪張表的)

  • type(顯示查詢使用了那種類型,從最好到最差依次排列 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL )tip: 通常來講,得保證查詢至少達到range級別,最好到達ref

  • system:表只有一行記錄(等於系統表),是 const 類型的特例,平時不會出現

  • const:表示經過索引一次就找到了,const 用於比較 primary key 或 unique 索引,由於只要匹配一行數據,因此很快,如將主鍵置於 where 列表中,mysql 就能將該查詢轉換爲一個常量

  • eq_ref:惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配,常見於主鍵或惟一索引掃描

  • ref:非惟一性索引掃描,範圍匹配某個單獨值得全部行。本質上也是一種索引訪問,他返回全部匹配某個單獨值的行,然而,它可能也會找到多個符合條件的行,多以他應該屬於查找和掃描的混合體

  • range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引,通常就是在你的where語句中出現了between、<、>、in等的查詢,這種範圍掃描索引比全表掃描要好,由於它只需開始於索引的某一點,而結束於另外一點,不用掃描所有索引

  • index:Full Index Scan,index於ALL區別爲index類型只遍歷索引樹。一般比ALL快,由於索引文件一般比數據文件小。(也就是說雖然all和index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的

  • ALL:Full Table Scan,將遍歷全表找到匹配的行

  • possible_keys(顯示可能應用在這張表中的索引,一個或多個,查詢涉及到的字段若存在索引,則該索引將被列出,但不必定被查詢實際使用)

  • key

  • 實際使用的索引,若是爲NULL,則沒有使用索引

  • 查詢中若使用了覆蓋索引,則該索引和查詢的 select 字段重疊,僅出如今key列表中

 
image

explain-key

  • key_len

  • 表示索引中使用的字節數,可經過該列計算查詢中使用的索引的長度。在不損失精確性的狀況下,長度越短越好

  • key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度,即key_len是根據表定義計算而得,不是經過表內檢索出的

  • ref(顯示索引的哪一列被使用了,若是可能的話,是一個常數。哪些列或常量被用於查找索引列上的值)

  • rows(根據表統計信息及索引選用狀況,大體估算找到所需的記錄所須要讀取的行數)

  • Extra(包含不適合在其餘列中顯示但十分重要的額外信息)

  • using filesort: 說明mysql會對數據使用一個外部的索引排序,不是按照表內的索引順序進行讀取。mysql中沒法利用索引完成的排序操做稱爲「文件排序」。常見於order by和group by語句中

  • Using temporary:使用了臨時表保存中間結果,mysql在對查詢結果排序時使用臨時表。常見於排序order by和分組查詢group by。

  • using index:表示相應的select操做中使用了覆蓋索引,避免訪問了表的數據行,效率不錯,若是同時出現using where,代表索引被用來執行索引鍵值的查找;不然索引被用來讀取數據而非執行查找操做

  • using where:使用了where過濾

  • using join buffer:使用了鏈接緩存

  • impossible where:where子句的值老是false,不能用來獲取任何元祖

  • select tables optimized away:在沒有group by子句的狀況下,基於索引優化操做或對於MyISAM存儲引擎優化COUNT(*)操做,沒必要等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化

  • distinct:優化distinct操做,在找到第一匹配的元祖後即中止找一樣值的動做

case:

 
image

explain-demo

  1. 第一行(執行順序4):id列爲1,表示是union裏的第一個select,select_type列的primary表示該查詢爲外層查詢,table列被標記爲,表示查詢結果來自一個衍生表,其中derived3中3表明該查詢衍生自第三個select查詢,即id爲3的select。【select d1.name......】
  2. 第二行(執行順序2):id爲3,是整個查詢中第三個select的一部分。因查詢包含在from中,因此爲derived。【select id,name from t1 where other_column=''】
  3. 第三行(執行順序3):select列表中的子查詢select_type爲subquery,爲整個查詢中的第二個select。【select id from t3】
  4. 第四行(執行順序1):select_type爲union,說明第四個select是union裏的第二個select,最早執行【select name,id from t2】
  5. 第五行(執行順序5):表明從union的臨時表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結果進行union操做。【兩個結果union操做】
慢查詢日誌

MySQL 的慢查詢日誌是 MySQL 提供的一種日誌記錄,它用來記錄在 MySQL 中響應時間超過閾值的語句,具體指運行時間超過 long_query_time 值的 SQL,則會被記錄到慢查詢日誌中。

  • long_query_time 的默認值爲10,意思是運行10秒以上的語句
  • 默認狀況下,MySQL數據庫沒有開啓慢查詢日誌,須要手動設置參數開啓

查看開啓狀態

SHOW VARIABLES LIKE '%slow_query_log%'

開啓慢查詢日誌

  • 臨時配置:
mysql> set global slow_query_log='ON'; mysql> set global slow_query_log_file='/var/lib/mysql/hostname-slow.log'; mysql> set global long_query_time=2; 

也可set文件位置,系統會默認給一個缺省文件host_name-slow.log

使用set操做開啓慢查詢日誌只對當前數據庫生效,若是MySQL重啓則會失效。

  • 永久配置修改配置文件my.cnf或my.ini,在[mysqld]一行下面加入兩個配置參數
[mysqld] slow_query_log = ON slow_query_log_file = /var/lib/mysql/hostname-slow.log long_query_time = 3 

注:log-slow-queries 參數爲慢查詢日誌存放的位置,通常這個目錄要有 MySQL 的運行賬號的可寫權限,通常都將這個目錄設置爲 MySQL 的數據存放目錄;long_query_time=2 中的 2 表示查詢超過兩秒才記錄;在my.cnf或者 my.ini 中添加 log-queries-not-using-indexes 參數,表示記錄下沒有使用索引的查詢。

能夠用 select sleep(4) 驗證是否成功開啓。

在生產環境中,若是手工分析日誌,查找、分析SQL,仍是比較費勁的,因此MySQL提供了日誌分析工具mysqldumpslow

經過 mysqldumpslow --help 查看操做幫助信息

  • 獲得返回記錄集最多的10個SQLmysqldumpslow -s r -t 10 /var/lib/mysql/hostname-slow.log
  • 獲得訪問次數最多的10個SQLmysqldumpslow -s c -t 10 /var/lib/mysql/hostname-slow.log
  • 獲得按照時間排序的前10條裏面含有左鏈接的查詢語句mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/hostname-slow.log
  • 也能夠和管道配合使用mysqldumpslow -s r -t 10 /var/lib/mysql/hostname-slow.log | more

也可以使用 pt-query-digest 分析 RDS MySQL 慢查詢日誌

Show Profile 分析查詢

經過慢日誌查詢能夠知道哪些 SQL 語句執行效率低下,經過 explain 咱們能夠得知 SQL 語句的具體執行狀況,索引使用等,還能夠結合Show Profile命令查看執行狀態。

  • Show Profile 是 MySQL 提供能夠用來分析當前會話中語句執行的資源消耗狀況。能夠用於SQL的調優的測量

  • 默認狀況下,參數處於關閉狀態,並保存最近15次的運行結果

  • 分析步驟mysql> show profiles; +----------+------------+---------------------------------+ | Query_ID | Duration | Query | +----------+------------+---------------------------------+ | 1 | 0.00385450 | show variables like "profiling" | | 2 | 0.00170050 | show variables like "profiling" | | 3 | 0.00038025 | select * from t_base_user | +----------+------------+---------------------------------+

  • converting HEAP to MyISAM 查詢結果太大,內存都不夠用了往磁盤上搬了。

  • create tmp table 建立臨時表,這個要注意

  • Copying to tmp table on disk 把內存臨時表複製到磁盤

  • locked

  • 診斷SQL,show profile cpu,block io for query id(上一步前面的問題SQL數字號碼)

  • 平常開發須要注意的結論

  • 是否支持,看看當前的mysql版本是否支持mysql>Show variables like 'profiling'; --默認是關閉,使用前須要開啓

  • 開啓功能,默認是關閉,使用前須要開啓mysql>set profiling=1;

  • 運行SQL

  • 查看結果

查詢中哪些狀況不會使用索引?

性能優化

索引優化

  1. 全值匹配我最愛
  2. 最佳左前綴法則,好比創建了一個聯合索引(a,b,c),那麼其實咱們可利用的索引就有(a), (a,b), (a,b,c)
  3. 不在索引列上作任何操做(計算、函數、(自動or手動)類型轉換),會致使索引失效而轉向全表掃描
  4. 存儲引擎不能使用索引中範圍條件右邊的列
  5. 儘可能使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減小select
  6. is null ,is not null 也沒法使用索引
  7. like "xxxx%" 是能夠用到索引的,like "%xxxx" 則不行(like "%xxx%" 同理)。like以通配符開頭('%abc...')索引失效會變成全表掃描的操做,
  8. 字符串不加單引號索引失效
  9. 少用or,用它來鏈接時會索引失效
  10. <,<=,=,>,>=,BETWEEN,IN 可用到索引,<>,not in ,!= 則不行,會致使全表掃描

通常性建議

  • 對於單鍵索引,儘可能選擇針對當前query過濾性更好的索引
  • 在選擇組合索引的時候,當前Query中過濾性最好的字段在索引字段順序中,位置越靠前越好。
  • 在選擇組合索引的時候,儘可能選擇能夠可以包含當前query中的where字句中更多字段的索引
  • 儘量經過分析統計信息和調整query的寫法來達到選擇合適索引的目的
  • 少用Hint強制索引

查詢優化

永遠小標驅動大表(小的數據集驅動大的數據集)

slect * from A where id in (select id from B)`等價於 #等價於 select id from B select * from A where A.id=B.id 

當 B 表的數據集必須小於 A 表的數據集時,用 in 優於 exists

select * from A where exists (select 1 from B where B.id=A.id) #等價於 select * from A select * from B where B.id = A.id` 

當 A 表的數據集小於B表的數據集時,用 exists優於用 in

注意:A表與B表的ID字段應創建索引。

order by關鍵字優化

  • order by子句,儘可能使用 Index 方式排序,避免使用 FileSort 方式排序

  • MySQL 支持兩種方式的排序,FileSort 和 Index,Index效率高,它指 MySQL 掃描索引自己完成排序,FileSort 效率較低;

  • ORDER BY 知足兩種狀況,會使用Index方式排序;①ORDER BY語句使用索引最左前列 ②使用where子句與ORDER BY子句條件列組合知足索引最左前列

  • 儘量在索引列上完成排序操做,遵守索引建的最佳最前綴

  • 若是不在索引列上,filesort 有兩種算法,mysql就要啓動雙路排序和單路排序

  • 雙路排序:MySQL 4.1以前是使用雙路排序,字面意思就是兩次掃描磁盤,最終獲得數據

  • 單路排序:從磁盤讀取查詢須要的全部列,按照order by 列在 buffer對它們進行排序,而後掃描排序後的列表進行輸出,效率高於雙路排序

  • 優化策略

  • 增大sort_buffer_size參數的設置

  • 增大max_lencth_for_sort_data參數的設置

GROUP BY關鍵字優化

  • group by實質是先排序後進行分組,遵守索引建的最佳左前綴
  • 當沒法使用索引列,增大 max_length_for_sort_data 參數的設置,增大sort_buffer_size參數的設置
  • where高於having,能寫在where限定的條件就不要去having限定了

數據類型優化

MySQL 支持的數據類型很是多,選擇正確的數據類型對於獲取高性能相當重要。無論存儲哪一種類型的數據,下面幾個簡單的原則都有助於作出更好的選擇。

  • 更小的一般更好:通常狀況下,應該儘可能使用能夠正確存儲數據的最小數據類型。簡單就好:簡單的數據類型一般須要更少的CPU週期。例如,整數比字符操做代價更低,由於字符集和校對規則(排序規則)使字符比較比整型比較複雜。
  • 儘可能避免NULL:一般狀況下最好指定列爲NOT NULL

9、分區、分表、分庫

MySQL分區

通常狀況下咱們建立的表對應一組存儲文件,使用MyISAM存儲引擎時是一個.MYI和.MYD文件,使用Innodb存儲引擎時是一個.ibd和.frm(表結構)文件。

當數據量較大時(通常千萬條記錄級別以上),MySQL的性能就會開始降低,這時咱們就須要將數據分散到多組存儲文件,保證其單個文件的執行效率

能幹嗎

  • 邏輯數據分割
  • 提升單一的寫和讀應用速度
  • 提升分區範圍讀查詢的速度
  • 分割數據可以有多個不一樣的物理文件路徑
  • 高效的保存歷史數據

怎麼玩

首先查看當前數據庫是否支持分區

  • MySQL5.6以及以前版本:SHOW VARIABLES LIKE '%partition%';
  • MySQL5.6:show plugins;

分區類型及操做

  • RANGE分區:基於屬於一個給定連續區間的列值,把多行分配給分區。mysql將會根據指定的拆分策略,,把數據放在不一樣的表文件上。至關於在文件上,被拆成了小塊.可是,對外給客戶的感受仍是一張表,透明的。按照 range 來分,就是每一個庫一段連續的數據,這個通常是按好比時間範圍來的,好比交易表啊,銷售表啊等,能夠根據年月來存放數據。可能會產生熱點問題,大量的流量都打在最新的數據上了。range 來分,好處在於說,擴容的時候很簡單。
  • LIST分區:相似於按RANGE分區,每一個分區必須明肯定義。它們的主要區別在於,LIST分區中每一個分區的定義和選擇是基於某列的值從屬於一個值列表集中的一個值,而RANGE分區是從屬於一個連續區間值的集合。
  • HASH分區:基於用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數能夠包含MySQL 中有效的、產生非負整數值的任何表達式。hash 分發,好處在於說,能夠平均分配每一個庫的數據量和請求壓力;壞處在於說擴容起來比較麻煩,會有一個數據遷移的過程,以前的數據須要從新計算 hash 值從新分配到不一樣的庫或表
  • KEY分區:相似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL服務器提供其自身的哈希函數。必須有一列或多列包含整數值。

看上去分區表很帥氣,爲何大部分互聯網仍是更多的選擇本身分庫分表來水平擴展咧?

  • 分區表,分區鍵設計不太靈活,若是不走分區鍵,很容易出現全表鎖
  • 一旦數據併發量上來,若是在分區表實施關聯,就是一個災難
  • 本身分庫分表,本身掌控業務場景與訪問模式,可控。分區表,研發寫了一個sql,都不肯定mysql是怎麼玩的,不太可控

隨着業務的發展,業務愈來愈複雜,應用的模塊愈來愈多,總的數據量很大,高併發讀寫操做均超過單個數據庫服務器的處理能力怎麼辦?

這個時候就出現了數據分片,數據分片指按照某個維度將存放在單一數據庫中的數據分散地存放至多個數據庫或表中。數據分片的有效手段就是對關係型數據庫進行分庫和分表。

區別於分區的是,分區通常都是放在單機裏的,用的比較多的是時間範圍分區,方便歸檔。只不過度庫分表須要代碼實現,分區則是mysql內部實現。分庫分表和分區並不衝突,能夠結合使用。

因爲篇幅緣由,在這就不作所有展現了,這些題我已經整理成pdf文檔免費分享給那些有須要的朋友,同時整理也花費了蠻多時間,有須要的朋友幫忙點贊而後私信關鍵詞【面試資料】便可獲取免費領取方式

 
image.png
 
image.png
 
image.png
做者:愛碼士人員 連接:https://www.jianshu.com/p/c189439fb32e 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索