Mysql 面試題(一網打盡,收藏版)


文章很長,而且持續更新,建議收藏起來,慢慢讀! 高並發 發燒友社群:瘋狂創客圈(總入口) 奉上以下珍貴的學習資源:


推薦:入大廠 、做架構、大力提升Java 內功 的 精彩博文

入大廠 、做架構、大力提升Java 內功 必備的精彩博文 2021 秋招漲薪1W + 必備的精彩博文
1:Redis 分布式鎖 (圖解-秒懂-史上最全) 2:Zookeeper 分布式鎖 (圖解-秒懂-史上最全)
3: Redis與MySQL雙寫一致性如何保證? (面試必備) 4: 面試必備:秒殺超賣 解決方案 (史上最全)
5:面試必備之:Reactor模式 6: 10分鍾看懂, Java NIO 底層原理
7:TCP/IP(圖解+秒懂+史上最全) 8:Feign原理 (圖解)

Java 面試題 30個專題 , 史上最全 , 面試必刷 阿里、京東、美團... 隨意挑、橫着走!!!
1: JVM面試題(史上最強、持續更新、吐血推薦) 2:Java基礎面試題(史上最全、持續更新、吐血推薦
3:架構設計面試題 (史上最全、持續更新、吐血推薦) 4:設計模式面試題 (史上最全、持續更新、吐血推薦)
17、分布式事務面試題 (史上最全、持續更新、吐血推薦) 一致性協議 (史上最全)
29、多線程面試題(史上最全) 30、HR面經,過五關斬六將后,小心陰溝翻船!
9.網絡協議面試題(史上最全、持續更新、吐血推薦) 更多專題, 請參見【 瘋狂創客圈 高並發 總目錄

SpringCloud 精彩博文
nacos 實戰(史上最全) sentinel (史上最全+入門教程)
SpringCloud gateway (史上最全) 更多專題, 請參見【 瘋狂創客圈 高並發 總目錄

社群交流的面試真題

社群面試真題1:MySQL單個實例buffer數據和磁盤數據是如何保證強一致性的

具體問題: 由於MySQL底層有buffer存在,MySQL單個實例buffer數據和磁盤數據是如何保證強一致性的。

社群小伙伴說明:
我的答案是不能保證強一致性,然后如果宕機了只能通過redolog和binlog進行數據恢復。但是好像面試官說這個是錯的,然后底層有機制保證。請問下大家知道是什么機制嗎?我查了下書本,好像INNODB有個double write機制,但是也保證不了強一致性

參考答案:

innodb三大特性的之一,雙寫緩沖區(double write)。 另外, redolog解決不了部分頁寫入問題,因為mysql的頁大小是16k,操作系統是4k,寫了一半就斷電,redolog沒法恢復。

雙寫緩沖區是InnoDB的三大特性之一,還有兩個是 Buffer Pool簡稱BP、自適應Hash索引。doublewrite緩沖區是一個存儲區,在該存儲區中,InnoDB將頁面寫入InnoDB數據文件中的適當位置之前,先從緩沖池中刷新頁面 。如果在頁面寫入過程中存在操作系統,存儲子系統或意外的mysqld進程退出,則InnoDB可以在崩潰恢復期間從doublewrite緩沖區中找到頁面的良好副本。注意:系統恢復后,MySQL 可以根據redolog 進行恢復,而mysql在恢復的過程中是檢查page的checksum,checksum就是pgae的最后事務號,發生partial page write 問題時,page已經損壞,找不到該page中的事務號,就無法恢復。

​ 為什么需要雙寫?個人理解宏觀上還是與InnoDB需要支持事務(ACID)特性有關,而底層的原因是為了解決Partial Write Page問題。

​ MYsql 為了實現事務InnoDB引入了比較多的組件,設計的特別復雜,InnoDB級別包括:(行鎖、臨建鎖、間隙鎖)鎖和加鎖規則、MVCC、redo log、undo log、視圖(Read View)。而官方文檔也在隔離型和持久性上面明確指向了數據雙寫機制,如下圖

img

​ InnoDB的頁大小默認為16K,可以使用參數innodb_page_size設置, 可設置的值有: 64KB,32KB,16KB(默認),8KB和4KB。並且在數據校驗時也針對頁進行計算,即他們是一個整個對待,包括把數據持久化到磁盤的操作。而計算機的硬件和操作系統在極端情況下(比如斷電、系統崩潰)時,剛寫入了4K或8K數據,那么就不能保證該操作的原子性,稱為部分頁面寫問題(Partial Write Page)

​ 此時就引入了雙寫緩存區的機制,當發生極端情況時,可以從系統表空間的Double Write Buffer【磁盤上】進行恢復,下面是InnoDB的架構圖、雙寫和恢復流程圖。為了方便對比,將組件放在了相同的位置:

img

img

​ 這樣在極端情況下也能解決 Partial Write page問題了,但是如果我自己的系統本身數據要求沒有那么高(比如日志數據庫),這樣的話畢竟雙寫是有一定的性能開銷的。可以通過參數innodb_doublewrite = 0進行關閉,設置為1表示開啟。官方認為,盡管需要寫入兩次數據,但是寫緩沖區不需要兩次的 io開銷或操作,因為只需要調用一次操作系統的fsync() 就可以將批量數據順序寫入磁盤 -> 系統表空間的Double Write Buffer(如上圖),這里是順序寫而不是隨機寫(性能可以保證),當然前提是配置刷盤策略參數innodb_flush_method為默認的O_DIRECT。其實還有一點就是真正提交的時候會使用組提交,我們可以用參數控制:binlog_group_commit_sync_delay:組提交執行fsync() 延遲的微妙數,延遲時間越長批量數據越多,磁盤io越少性能越高。binlog_group_commit_sync_no_delay_count:組提交執行fsync的批個數。

注:更多的 面試真題,請來瘋狂創客圈 微信群交流, 入群請加尼恩 微信,具體請參見 瘋狂創客圈 社群 總入口。

史上最全 Java 面試題:Mysql 篇

1、MySQL 中有哪幾種鎖?

(1)表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最 高,並發度最低。

(2)行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最 低,並發度也最高。

(3)頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表 鎖和行鎖之間,並發度一般。

2、MySQL 中有哪些不同的表?

共有 5 種類型的表:

(1)MyISAM

(2)Heap

(3)Merge

(4)INNODB

(5)ISAM

3、簡述在 MySQL 數據庫中 MyISAM 和 InnoDB 的區別

MyISAM:

(1)不支持事務,但是每次查詢都是原子的;

(2)支持表級鎖,即每次操作是對整個表加鎖;

(3)存儲表的總行數;

(4)一個 MYISAM 表有三個文件:索引文件、表結構文件、數據文件;

(5)采用菲聚集索引,索引文件的數據域存儲指向數據文件的指針。輔索引與主索引基本一致,但是輔索引不用保證唯一性。

InnoDb:

(1)支持 ACID 的事務,支持事務的四種隔離級別;

(2)支持行級鎖及外鍵約束:因此可以支持寫並發;

(3)不存儲總行數:

(4)一個 InnoDb 引擎存儲在一個文件空間(共享表空間,表大小不受操作系統控制,一個表可能分布在多個文件里),也有可能為多個(設置為獨立表空,表大小受操作系統文件大小限制,一般為 2G),受操作系統文件大小的限制;

(5)主鍵索引采用聚集索引(索引的數據域存儲數據文件本身),輔索引的數據域存儲主鍵的值;因此從輔索引查找數據,需要先通過輔索引找到主鍵值,再訪問輔索引;最好使用自增主鍵,防止插入數據時,為維持 B+樹結構,文件的大調整。

4、MySQL 中 InnoDB 支持的四種事務隔離級別名稱,以及逐級之間的區別

SQL 標准定義的四個隔離級別為:

(1)read uncommited :讀到未提交數據

(2)read committed:臟讀,不可重復讀

(3)repeatable read:可重讀

(4)serializable :串行事物

5、CHAR 和 VARCHAR 的區別

(1)CHAR 和 VARCHAR 類型在存儲和檢索方面有所不同

(2)CHAR 列長度固定為創建表時聲明的長度,長度值范圍是 1 到 255 當 CHAR值被存儲時,它們被用空格填充到特定長度,檢索 CHAR 值時需刪除尾隨空格。

6、主鍵和候選鍵有什么區別?

表格的每一行都由主鍵唯一標識,一個表只有一個主鍵。

主鍵也是候選鍵。按照慣例,候選鍵可以被指定為主鍵,並且可以用於任何外鍵引用。

7、myisamchk 是用來做什么的?

它用來壓縮 MyISAM 表,這減少了磁盤或內存使用。

MyISAM Static 和 MyISAM Dynamic 有什么區別?

在 MyISAM Static 上的所有字段有固定寬度。動態 MyISAM 表將具有像 TEXT,BLOB 等字段,以適應不同長度的數據類型。

MyISAM Static 在受損情況下更容易恢復。

8、如果一個表有一列定義為 TIMESTAMP,將發生什么?

每當行被更改時,時間戳字段將獲取當前時間戳。

列設置為 AUTO INCREMENT 時,如果在表中達到最大值,會發生什么情況?

它會停止遞增,任何進一步的插入都將產生錯誤,因為密鑰已被使用。

怎樣才能找出最后一次插入時分配了哪個自動增量?

LAST_INSERT_ID 將返回由 Auto_increment 分配的最后一個值,並且不需要指定表名稱。

9、你怎么看到為表格定義的所有索引?

索引是通過以下方式為表格定義的:

SHOW INDEX FROM ;

10、LIKE 聲明中的%和_是什么意思?

%對應於 0 個或更多字符,_只是 LIKE 語句中的一個字符。

如何在 Unix 和 MySQL 時間戳之間進行轉換?

UNIX_TIMESTAMP 是從 MySQL 時間戳轉換為 Unix 時間戳的命令

FROM_UNIXTIME 是從 Unix 時間戳轉換為 MySQL 時間戳的命令

11、列對比運算符是什么?

在 SELECT 語句的列比較中使用=,<>,<=,<,> =,>,<<,>>,<=>,AND,OR 或 LIKE 運算符。

12、BLOB 和 TEXT 有什么區別?

BLOB 是一個二進制對象,可以容納可變數量的數據。TEXT 是一個不區分大小寫的 BLOB。

BLOB 和 TEXT 類型之間的唯一區別在於對 BLOB 值進行排序和比較時區分大小寫,對 TEXT 值不區分大小寫。

13、MySQL_fetch_array 和 MySQL_fetch_object 的區別是什么?

以下是 MySQL_fetch_array 和 MySQL_fetch_object 的區別:

MySQL_fetch_array() – 將結果行作為關聯數組或來自數據庫的常規數組返回。

MySQL_fetch_object – 從數據庫返回結果行作為對象。

14、MyISAM 表格將在哪里存儲,並且還提供其存儲格式?

每個 MyISAM 表格以三種格式存儲在磁盤上:

(1)·“.frm”文件存儲表定義

(2)·數據文件具有“.MYD”(MYData)擴展名

(3)索引文件具有“.MYI”(MYIndex)擴展名

15、MySQL 如何優化 DISTINCT?

DISTINCT 在所有列上轉換為 GROUP BY,並與 ORDER BY 子句結合使用。

SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a;

16、如何顯示前 50 行?

在 MySQL 中,使用以下代碼查詢顯示前 50 行:

SELECT*FROM

LIMIT 0,50;

17、可以使用多少列創建索引?

任何標准表最多可以創建 16 個索引列。

18、NOW()和 CURRENT_DATE()有什么區別?

NOW()命令用於顯示當前年份,月份,日期,小時,分鍾和秒。

CURRENT_DATE()僅顯示當前年份,月份和日期。

19、什么是非標准字符串類型?

(1)TINYTEXT

(2)TEXT

(3)MEDIUMTEXT

(4)LONGTEXT

20、什么是通用 SQL 函數?

(1)CONCAT(A, B) – 連接兩個字符串值以創建單個字符串輸出。通常用於將兩個或多個字段合並為一個字段。

(2)FORMAT(X, D)- 格式化數字 X 到 D 有效數字。

(3)CURRDATE(), CURRTIME()- 返回當前日期或時間。

(4)NOW() – 將當前日期和時間作為一個值返回。

(5)MONTH(),DAY(),YEAR(),WEEK(),WEEKDAY() – 從日期值中提取給定數據。

(6)HOUR(),MINUTE(),SECOND() – 從時間值中提取給定數據。

(7)DATEDIFF(A,B) – 確定兩個日期之間的差異,通常用於計算年齡

(8)SUBTIMES(A,B) – 確定兩次之間的差異。

(9)FROMDAYS(INT) – 將整數天數轉換為日期值。

21、MySQL 支持事務嗎?

在缺省模式下,MySQL 是 autocommit 模式的,所有的數據庫更新操作都會即時提交,所以在缺省情況下,MySQL 是不支持事務的。

但是如果你的 MySQL 表類型是使用 InnoDB Tables 或 BDB tables 的話,你的MySQL 就可以使用事務處理,使用 SETAUTOCOMMIT=0 就可以使 MySQL 允許在非 autocommit 模式,在非autocommit 模式下,你必須使用 COMMIT 來提交你的更改,或者用 ROLLBACK來回滾你的更改。

22、MySQL 里記錄貨幣用什么字段類型好

NUMERIC 和 DECIMAL 類型被MySQL 實現為同樣的類型,這在 SQL92 標准允許。他們被用於保存值,該值的准確精度是極其重要的值,例如與金錢有關的數據。當聲明一個類是這些類型之一時,精度和規模的能被(並且通常是)指定。

例如:

salary DECIMAL(9,2)

在這個例子中,9(precision)代表將被用於存儲值的總的小數位數,而 2(scale)代 表將被用於存儲小數點后的位數。

因此,在這種情況下,能被存儲在 salary 列中的值的范圍是從-9999999.99 到9999999.99。

23、MySQL 有關權限的表都有哪幾個?

MySQL 服務器通過權限表來控制用戶對數據庫的訪問,權限表存放在 MySQL 數據庫里,由 MySQL_install_db 腳本初始化。這些權限表分別 user,db,table_priv,columns_priv 和 host。

24、列的字符串類型可以是什么?

字符串類型是:

(1)SET2

(2)BLOB

(3)ENUM

(4)CHAR

(5)TEXT

25、MySQL 數據庫作發布系統的存儲,一天五萬條以上的增量,預計運維三年,怎么優化?

(1)設計良好的數據庫結構,允許部分數據冗余,盡量避免 join 查詢,提高效率。

(2)選擇合適的表字段數據類型和存儲引擎,適當的添加索引。

(3)MySQL 庫主從讀寫分離。

(4)找規律分表,減少單表中的數據量提高查詢速度。

(5)添加緩存機制,比如 memcached,apc 等。

(6)不經常改動的頁面,生成靜態頁面。

(7)書寫高效率的 SQL。比如 SELECT * FROM TABEL 改為 SELECT field_1, field_2, field_3 FROM TABLE.

26、鎖的優化策略

(1)讀寫分離

(2)分段加鎖

(3)減少鎖持有的時間

(4)多個線程盡量以相同的順序去獲取資源

不能將鎖的粒度過於細化,不然可能會出現線程的加鎖和釋放次數過多,反而效率不如一次加一把大鎖。

27、索引的底層實現原理和優化

B+樹,經過優化的 B+樹

主要是在所有的葉子結點中增加了指向下一個葉子節點的指針,因此 InnoDB 建議為大部分表使用默認自增的主鍵作為主索引。

28、什么情況下設置了索引但無法使用

(1)以“%”開頭的 LIKE 語句,模糊匹配

(2)OR 語句前后沒有同時使用索引

(3)數據類型出現隱式轉化(如 varchar 不加單引號的話可能會自動轉換為 int 型)

29、實踐中如何優化 MySQL

最好是按照以下順序優化:

(1)SQL 語句及索引的優化

(2)數據庫表結構的優化

(3)系統配置的優化

(4)硬件的優化

30、優化數據庫的方法

(1)選取最適用的字段屬性,盡可能減少定義字段寬度,盡量把字段設置 NOTNULL,例如’省份’、’性別’最好適用 ENUM

(2)使用連接(JOIN)來代替子查詢

(3)適用聯合(UNION)來代替手動創建的臨時表

(4)事務處理

(5)鎖定表、優化事務處理

(6)適用外鍵,優化鎖定表

(7)建立索引

(8)優化查詢語句

31、簡單描述 MySQL 中,索引,主鍵,唯一索引,聯合索引的區別,對數據庫的性能有什么影響(從讀寫兩方面)

索引是一種特殊的文件(InnoDB 數據表上的索引是表空間的一個組成部分),它們包含着對數據表里所有記錄的引用指針。

普通索引(由關鍵字 KEY 或 INDEX 定義的索引)的唯一任務是加快對數據的訪問速度。

普通索引允許被索引的數據列包含重復的值。如果能確定某個數據列將只包含彼此各不相同的值,在為這個數據列創建索引的時候就應該用關鍵字 UNIQUE 把它定義為一個唯一索引。也就是說,唯一索引可以保證數據記錄的唯一性。

主鍵,是一種特殊的唯一索引,在一張表中只能定義一個主鍵索引,主鍵用於唯一標識一條記錄,使用關鍵字 PRIMARY KEY 來創建。

索引可以覆蓋多個數據列,如像 INDEX(columnA, columnB)索引,這就是聯合索引。

索引可以極大的提高數據的查詢速度,但是會降低插入、刪除、更新表的速度,因為在執行這些寫操作時,還要操作索引文件。

32、數據庫中的事務是什么?

事務(transaction)是作為一個單元的一組有序的數據庫操作。如果組中的所有操作都成功,則認為事務成功,即使只有一個操作失敗,事務也不成功。如果所有操作完成,事務則提交,其修改將作用於所有其他數據庫進程。如果一個操作失敗,則事務將回滾,該事務所有操作的影響都將取消。

事務特性:

(1)原子性:即不可分割性,事務要么全部被執行,要么就全部不被執行。

(2)一致性或可串性。事務的執行使得數據庫從一種正確狀態轉換成另一種正確狀態。

(3)隔離性。在事務正確提交之前,不允許把該事務對數據的任何改變提供給任何其他事務。

(4)持久性。事務正確提交后,其結果將永久保存在數據庫中,即使在事務提交后有了其他故障,事務的處理結果也會得到保存。

或者這樣理解:

事務就是被綁定在一起作為一個邏輯工作單元的 SQL 語句分組,如果任何一個語句操作失敗那么整個操作就被失敗,以后操作就會回滾到操作前狀態,或者是上有個節點。為了確保要么執行,要么不執行,就可以使用事務。要將有組語句作為事務考慮,就需要通過 ACID 測試,即原子性,一致性,隔離性和持久性。

33、SQL 注入漏洞產生的原因?如何防止?

SQL 注入產生的原因:程序開發過程中不注意規范書寫 sql 語句和對特殊字符進行過濾,導致客戶端可以通過全局變量 POST 和 GET 提交一些 sql 語句正常執行。

防止 SQL 注入的方式:

開啟配置文件中的 magic_quotes_gpc 和 magic_quotes_runtime 設置

執行 sql 語句時使用 addslashes 進行 sql 語句轉換

Sql 語句書寫盡量不要省略雙引號和單引號。

過濾掉 sql 語句中的一些關鍵詞:update、insert、delete、select、 * 。

提高數據庫表和字段的命名技巧,對一些重要的字段根據程序的特點命名,取不易被猜到的。

34、為表中得字段選擇合適得數據類型

字段類型優先級: 整形>date,time>enum,char>varchar>blob,text

優先考慮數字類型,其次是日期或者二進制類型,最后是字符串類型,同級別得數據類型,應該優先選擇占用空間小的數據類型

35、存儲時期

Datatime:以 YYYY-MM-DD HH:MM:SS 格式存儲時期時間,精確到秒,占用 8 個字節得存儲空間,datatime 類型與時區無關Timestamp:以時間戳格式存儲,占用 4 個字節,范圍小 1970-1-1 到 2038-1-19,顯示依賴於所指定得時區,默認在第一個列行的數據修改時可以自動得修改timestamp 列得值

Date:(生日)占用得字節數比使用字符串.datatime.int 儲存要少,使用 date 只需要 3 個字節,存儲日期月份,還可以利用日期時間函數進行日期間得計算

Time:存儲時間部分得數據

注意:不要使用字符串類型來存儲日期時間數據(通常比字符串占用得儲存空間小,在進行查找過濾可以利用日期得函數)

使用 int 存儲日期時間不如使用 timestamp 類型

36、對於關系型數據庫而言,索引是相當重要的概念,請回答有關索引的幾個問題:

(1)索引的目的是什么?

快速訪問數據表中的特定信息,提高檢索速度

創建唯一性索引,保證數據庫表中每一行數據的唯一性。

加速表和表之間的連接

使用分組和排序子句進行數據檢索時,可以顯著減少查詢中分組和排序的時間

(2)索引對數據庫系統的負面影響是什么?

負面影響:

創建索引和維護索引需要耗費時間,這個時間隨着數據量的增加而增加;索引需要占用物理空間,不光是表需要占用數據空間,每個索引也需要占用物理空間;當對表進行增、刪、改、的時候索引也要動態維護,這樣就降低了數據的維護速度。

(3)為數據表建立索引的原則有哪些?

在最頻繁使用的、用以縮小查詢范圍的字段上建立索引。

在頻繁使用的、需要排序的字段上建立索引

(4)什么情況下不宜建立索引?

對於查詢中很少涉及的列或者重復值比較多的列,不宜建立索引。

對於一些特殊的數據類型,不宜建立索引,比如文本字段(text)等

37、解釋 MySQL 外連接、內連接與自連接的區別

先說什么是交叉連接: 交叉連接又叫笛卡爾積,它是指不使用任何條件,直接將一個表的所有記錄和另一個表中的所有記錄一一匹配。

內連接 則是只有條件的交叉連接,根據某個條件篩選出符合條件的記錄,不符合條件的記錄不會出現在結果集中,即內連接只連接匹配的行。

外連接 其結果集中不僅包含符合連接條件的行,而且還會包括左表、右表或兩個表中的所有數據行,這三種情況依次稱之為左外連接,右外連接,和全外連接。

左外連接,也稱左連接,左表為主表,左表中的所有記錄都會出現在結果集中,對於那些在右表中並沒有匹配的記錄,仍然要顯示,右邊對應的那些字段值以NULL 來填充。右外連接,也稱右連接,右表為主表,右表中的所有記錄都會出現在結果集中。左連接和右連接可以互換,MySQL 目前還不支持全外連接。

38、Myql 中的事務回滾機制概述

事務是用戶定義的一個數據庫操作序列,這些操作要么全做要么全不做,是一個不可分割的工作單位,事務回滾是指將該事務已經完成的對數據庫的更新操作撤銷。

要同時修改數據庫中兩個不同表時,如果它們不是一個事務的話,當第一個表修改完,可能第二個表修改過程中出現了異常而沒能修改,此時就只有第二個表依舊是未修改之前的狀態,而第一個表已經被修改完畢。而當你把它們設定為一個事務的時候,當第一個表修改完,第二表修改出現異常而沒能修改,第一個表和第二個表都要回到未修改的狀態,這就是所謂的事務回滾

39、SQL 語言包括哪幾部分?每部分都有哪些操作關鍵字?

SQL 語言包括數據定義(DDL)、數據操縱(DML),數據控制(DCL)和數據查詢(DQL) 四個部分。

數據定義:Create Table,Alter Table,Drop Table, Craete/Drop Index 等

數據操縱:Select ,insert,update,delete,

數據控制:grant,revoke

數據查詢:select

40、完整性約束包括哪些?

數據完整性(Data Integrity)是指數據的精確(Accuracy)和可靠性(Reliability)。

分為以下四類:

(1)實體完整性:規定表的每一行在表中是惟一的實體。

(2)域完整性:是指表中的列必須滿足某種特定的數據類型約束,其中約束又包括取值范圍、精度等規定。

(3)參照完整性:是指兩個表的主關鍵字和外關鍵字的數據應一致,保證了表之間的數據的一致性,防止了數據丟失或無意義的數據在數據庫中擴散。

(4)用戶定義的完整性:不同的關系數據庫系統根據其應用環境的不同,往往還需要一些特殊的約束條件。用戶定義的完整性即是針對某個特定關系數據庫的約束條件,它反映某一具體應用必須滿足的語義要求。

與表有關的約束:包括列約束(NOT NULL(非空約束))和表約束(PRIMARY KEY、foreign key、check、UNIQUE) 。

41、什么是鎖?

數據庫是一個多用戶使用的共享資源。當多個用戶並發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對並發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。

加鎖是實現數據庫並發控制的一個非常重要的技術。當事務在對某個數據對象進行操作前,先向系統發出請求,對其加鎖。加鎖后事務就對該數據對象有了一定的控制,在該事務釋放鎖之前,其他的事務不能對此數據對象進行更新操作。

基本鎖類型:鎖包括行級鎖和表級鎖

42、什么叫視圖?游標是什么?

視圖是一種虛擬的表,具有和物理表相同的功能。可以對視圖進行增,改,查,操作,視圖通常是有一個表或者多個表的行或列的子集。對視圖的修改不影響基本表。它使得我們獲取數據更容易,相比多表查詢。

游標:是對查詢出來的結果集作為一個單元來有效的處理。游標可以定在該單元中的特定行,從結果集的當前行檢索一行或多行。可以對結果集當前行做修改。一般不使用游標,但是需要逐條處理數據的時候,游標顯得十分重要。

43、什么是存儲過程?用什么來調用?

存儲過程是一個預編譯的 SQL 語句,優點是允許模塊化的設計,就是說只需創建一次,以后在該程序中就可以調用多次。如果某次操作需要執行多次 SQL,使用存儲過程比單純 SQL 語句執行要快。可以用一個命令對象來調用存儲過程。

44、如何通俗地理解三個范式?

第一范式:1NF 是對屬性的原子性約束,要求屬性具有原子性,不可再分解;

第二范式:2NF 是對記錄的惟一性約束,要求記錄有惟一標識,即實體的惟一性;

第三范式:3NF 是對字段冗余性的約束,即任何字段不能由其他字段派生出來,它要求字段沒有冗余。。

范式化設計優缺點:

優點:可以盡量得減少數據冗余,使得更新快,體積小

缺點:對於查詢需要多個表進行關聯,減少寫得效率增加讀得效率,更難進行索引優化

反范式化:

優點:可以減少表得關聯,可以更好得進行索引優化

缺點:數據冗余以及數據異常,數據得修改需要更多的成本

45、什么是基本表?什么是視圖?

基本表是本身獨立存在的表,在 SQL 中一個關系就對應一個表。視圖是從一個或幾個基本表導出的表。視圖本身不獨立存儲在數據庫中,是一個虛表

46、試述視圖的優點?

(1) 視圖能夠簡化用戶的操作

(2) 視圖使用戶能以多種角度看待同一數據;

(3) 視圖為數據庫提供了一定程度的邏輯獨立性;

(4)視圖能夠對機密數據提供安全保護。

47、 NULL 是什么意思

NULL 這個值表示 UNKNOWN(未知):它不表示“”(空字符串)。對 NULL 這個值的任何比較都會生產一個 NULL 值。您不能把任何值與一個 NULL 值進行比較,並在邏輯上希望獲得一個答案。

使用 IS NULL 來進行 NULL 判斷

48、主鍵、外鍵和索引的區別?

主鍵、外鍵和索引的區別

定義:

主鍵——唯一標識一條記錄,不能有重復的,不允許為空

外鍵——表的外鍵是另一表的主鍵, 外鍵可以有重復的, 可以是空值

索引——該字段沒有重復值,但可以有一個空值

作用:

主鍵——用來保證數據完整性

外鍵——用來和其他表建立聯系用的

索引——是提高查詢排序的速度

個數:

主鍵—— 主鍵只能有一個

外鍵—— 一個表可以有多個外鍵

索引—— 一個表可以有多個唯一索引

49、你可以用什么來確保表格里的字段只接受特定范圍里的值?

Check 限制,它在數據庫表格里被定義,用來限制輸入該列的值。

觸發器也可以被用來限制數據庫表格里的字段能夠接受的值,但是這種辦法要求觸發器在表格里被定義,這可能會在某些情況下影響到性能。

50、說說對 SQL 語句優化有哪些方法?(選擇幾條)

(1)Where 子句中:where 表之間的連接必須寫在其他 Where 條件之前,那些可以過濾掉最大數量記錄的條件必須寫在 Where 子句的末尾.HAVING 最后。

(2)用 EXISTS 替代 IN、用 NOT EXISTS 替代 NOT IN。

(3) 避免在索引列上使用計算

(4)避免在索引列上使用 IS NULL 和 IS NOT NULL

(5)對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

(6)應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描

(7)應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描


51 面試官:聽說你sql寫的挺溜的,你說一說查詢sql的執行過程

當希望Mysql能夠高效的執行的時候,最好的辦法就是清楚的了解Mysql是如何執行查詢的,只有更加全面的了解SQL執行的每一個過程,才能更好的進行SQl的優化。

當執行一條查詢的SQl的時候大概發生了一下的步驟:

  1. 客戶端發送查詢語句給服務器。

  2. 服務器首先檢查緩存中是否存在該查詢,若存在,返回緩存中存在的結果。若是不存在就進行下一步。

  3. 服務器進行SQl的解析、語法檢測和預處理,再由優化器生成對應的執行計划。

  4. Mysql的執行器根據優化器生成的執行計划執行,調用存儲引擎的接口進行查詢。

  5. 服務器將查詢的結果返回客戶端。

Mysql的執行的流程

Mysql的執行的流程圖如下圖所示:
img

這里以一個實例進行說明Mysql的的執行過程,新建一個User表,如下:

// 新建一個表
DROP TABLE IF EXISTS User;
CREATE TABLE `User` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `age` int DEFAULT 0,
  `address` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `dept` int,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;

// 並初始化數據,如下
INSERT INTO User(name,age,address,phone,dept)VALUES('張三',24,'北京','13265543552',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('張三三',20,'北京','13265543557',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('李四',23,'上海','13265543553',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('李四四',21,'上海','13265543556',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('王五',27,'廣州','13265543558',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('王五五',26,'廣州','13265543559',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('趙六',25,'深圳','13265543550',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('趙六六',28,'廣州','13265543561',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('七七',29,'廣州','13265543562',4);
INSERT INTO User(name,age,address,phone,dept)VALUES('八八',23,'廣州','13265543563',4);
INSERT INTO User(name,age,address,phone,dept)VALUES('九九',24,'廣州','13265543564',4);

現在針對這個表發出一條SQl查詢:查詢每個部門中25歲以下的員工個數大於3的員工個數和部門編號,並按照人工個數降序排序和部門編號升序排序的前兩個部門。

SELECT dept,COUNT(phone) AS num FROM User WHERE age< 25 GROUP BY dept HAVING num >= 3 ORDER BY num DESC,dept ASC LIMIT 0,2;

執行連接器

開始執行這條sql時,會檢查該語句是否有權限,若是沒有權限就直接返回錯誤信息,有權限會進行下一步,校驗權限的這一步是在圖一的連接器進行的,對連接用戶權限的校驗。

執行檢索內存

相連建立之后,履行查詢語句的時候,會先行檢索內存,Mysql會先行冗余這個sql與否履行過,以此Key-Value的形式平緩適用內存中,Key是檢索預定,Value是結果集

假如內存key遭擊中,便會間接回到給客戶端,假如沒命中,便會履行后續的操作,完工之后亦會將結果內存上去,當下一次進行查詢的時候也是如此的循環操作。

執行分析器

分析器主要有兩步:(1)詞法分析(2)語法分析

詞法分析主要執行提煉關鍵性字,比如select,提交檢索的表提交字段名提交檢索條件

語法分析主要執行辨別你輸出的sql與否准確,是否合乎mysql的語法

當Mysql沒有命中內存的時候,接着執行的是 FROM student 負責把數據庫的表文件加載到內存中去,WHERE age< 60,會把所示表中的數據進行過濾,取出符合條件的記錄行,生成一張臨時表,如下圖所示。
img
GROUP BY dept 會把上圖的臨時表分成若干臨時表,切分的過程如下圖所示:
img
img

查詢的結果只有部門2和部門3才有符合條件的值,生成如上兩圖的臨時表。接着執行SELECT后面的字段,SELECT后面可以是表字段也可以是聚合函數

這里SELECT的情況與是否存在GROUP BY有關,若是不存在Mysql直接按照上圖內存中整列讀取。若是存在分別SELECT臨時表的數據。

最后生成的臨時表如下圖所示:
img

緊接着執行HAVING num>2過濾員工數小於等於2的部門,對於WHEREHAVING都是進行過濾,那么這兩者有什么不同呢?

第一點是WHERE后面只能對表字段進行過濾,不能使用聚合函數,而HAVING可以過濾表字段也可以使用聚合函數進行過濾。

第二點是WHERE是對執行from USer操作后,加載表數據到內存后,WHERE是對原生表的字段進行過濾,而HAVING是對SELECT后的字段進行過濾,也就是WHERE不能使用別名進行過濾

因為執行WHERE的時候,還沒有SELECT,還沒有給字段賦予別名。接着生成的臨時表如下圖所示:
img
最后在執行ORDER BY后面的排序以及limit0,2取得前兩個數據,因為這里數據比較少,沒有體現出來。最后生成得結果也是如上圖所示。接着判斷這個sql語句是否有語法錯誤關鍵性詞與否准確等等。

執行優化器

查詢優化器會將解析樹轉化成執行計划。一條查詢可以有多種執行方法,最后都是返回相同結果。優化器的作用就是找到這其中最好的執行計划

生成執行計划的過程會消耗較多的時間,特別是存在許多可選的執行計划時。如果在一條SQL語句執行的過程中將該語句對應的最終執行計划進行緩存。

相似的語句再次被輸入服務器時,就可以直接使用已緩存的執行計划,從而跳過SQL語句生成執行計划的整個過程,進而可以提高語句的執行速度。

img
MySQL使用基於成本的查詢優化器。它會嘗試預測一個查詢使用某種執行計划時的成本,並選擇其中成本最少的一個。

執行執行器

由優化器生成得執行計划,交由執行器進行執行,執行器調用存儲引擎得接口,存儲引擎獲取數據並返回,結束整個查詢得過程。

這里之講解了select的過程,對於update這些修改數據或者刪除數據的操作,會涉及到事務,會使用兩個日志模塊,redo log和binlog日志。具體對這兩個日志的介紹請看着一篇文章。

以前的Mysql的默認存儲引擎MyISAM引擎是沒redo log的,而現在的默認存儲引擎InnoDB引擎便是透過redo 復雜度來擁護事務的,保證事務能夠准確的回滾或者提交,保證事務的ACID。

52:必備:從千萬級數據查詢來聊一聊索引結構和數據庫原理

在日常工作中我們不可避免地會遇到慢SQL問題,比如筆者在之前的公司時會定期收到DBA彪哥發來的Oracle AWR報告,並特別提示我某條sql近階段執行明顯很慢,可能要優化一下等。對於這樣的問題通常大家的第一反應就是看看sql是不是寫的不合理啊諸如:“避免使用in和not in,否則可能會導致全表掃描”“ 避免在where子句中對字段進行函數操作”等等,還有一種常見的反應就是這個表有沒有加索引?絕大部分情況下,加了個索引基本上就搞定了。

既然題目是《從千萬級數據查詢來聊一聊索引結構和數據庫原理》,首先就來構造一個千萬級的表直觀感受下。我們創建了一張user表,然后插入了1000萬條數據,查詢一下:

img

用了近30秒的時間,這還是單表查詢,關聯查詢明顯會更讓人無法忍受。接下來,我們只是對id增加一個索引,再來驗證一把:

img

從30s到0.02s,提升了足足1500倍。為什么加了索引之后,速度嗖地一下子就上去了呢?我們從【索引數據結構】、【Mysql原理】兩個方面入手。

一、索引數據結構

我們先來看下 MySQL官方對索引的定義:

索引(Index)是幫助MySQL高效獲取數據的數據結構。

這里面有2個關鍵詞:高效查找、數據結構。對於數據庫來說,查詢是我們最主要的使用功能,查詢速度肯定是越快越好。最基本的查找是順序查找,更高效的查找我們很自然會想到二叉樹、紅黑樹、Hash表、BTree等等。

1.1 二叉樹

這個大家很熟悉了,他有一個很重要的特點:左邊節點的鍵值小於根的鍵值,右邊節點的鍵值大於根的鍵值。比如圖1,它確實能明顯提高我們的搜索性能。但如果用來作為數據庫的索引,明顯存在很大的缺陷,但對於圖2這種遞增的id,存儲后索引近似於變成了單邊的鏈表,肯定是不合適的。

imgimg

1.2 紅黑樹

也稱之為平衡二叉樹。在JDK1.8后,HashMap對底層的鏈表也優化成了紅黑樹(后續文章我們可以講講Hashmap1.8之后的調整)。平衡二叉樹的結構使樹的結構較好,明顯提高查找運算的速度。但是缺陷也同樣很明顯,插入和刪除運算變得復雜化,從而降低了他們的運算速度。對大數據量的支撐很不好,當數據量很大時,樹的高度太高,如果查找的數據是葉子節點,依然會超級慢。

img

1.3 BTree

B-Tree是為磁盤等外存儲設備設計的一種平衡查找樹。系統從磁盤讀取數據到內存時是以磁盤塊(block)為基本單位的,位於同一個磁盤塊中的數據會被一次性讀取到內存中。在Mysql存儲引擎中有頁(Page)的概念,頁是其磁盤管理的最小單位。Mysql存儲引擎中默認每個頁的大小為16KB,查看方式:

mysql> show variables like 'innodb_page_size';

img

我們也可以將它修改為4K、8K、16K。系統一個磁盤塊的存儲空間往往沒有16K,因此Mysql每次申請磁盤空間時都會將若干地址連續磁盤塊來達到頁的大小16KB。Mysql在把磁盤數據讀入到磁盤時會以頁為基本單位,在查詢數據時如果一個頁中的每條數據都能有助於定位數據記錄的位置,這將會減少磁盤I/O次數,提高查詢效率。

在這里插入圖片描述

如上圖所示,一棵B樹包含有鍵值、存儲子節點的指針信息、及除主鍵外的數據。相對於普通的樹BTree將橫向節點的容量變大,從而存儲更多的索引。

1.4 B+Tree

在B-Tree的基礎上大牛們又研究出了許多變種,其中最常見的是B+Tree,MySQL就普遍使用B+Tree實現其索引結構。

在這里插入圖片描述

與B-Tree相比,B+Tree做了以下一些改進:
1、非葉子節點,只存儲鍵值信息,這樣極大增加了存放索引的數據量。
2、 所有葉子節點之間都有一個鏈指針。對於區間查詢時,不需要再從根節點開始,可直接定位到數據。
3、 數據記錄都存放在葉子節點中。根據二叉樹的特點,這個是順序訪問指針,提升了區間訪問的性能。
通過這樣的設計,一張千萬級的表最多只需要3次磁盤交互就可以找出數據。

二、Mysql部分原理說明

這一部分我們選舉幾個日常面試過程中或者使用過程中比較常見的問題通過問答的形式來進行講解。

2.1、數據庫引擎MyISAM和InnoDB有什么區別

  • MyISAM:
    在Mysql8之前,默認引擎是MyISAM,其目標是快速讀取。
    特點:
    1、讀取非常快,如果頻繁插入和更新的話,因為涉及到數據全表鎖,效率並不高
    2、保存了數據庫行數,執行count時,不需要掃描全表;
    3、不支持數據庫事務;
    4、不支持行級鎖和外鍵;
    5、不支持故障恢復。
    6、支持全文檢索FullText,壓縮索引。
    建議使用場景:
    1、做很多count計算的,(如果count計算后面有where還是會全表掃描)
    2、插入和更新較少,查詢比較頻繁的
  • InnoDB:
    在Mysql8里,默認存儲引擎改成了InnoDB。
    特點
    1、支持事務處理、ACID事務特性
    2、實現了SQL標准的四種隔離級別
    3、支持行級鎖和外鍵約束
    4、可以利用事務日志進行數據恢復
    5、不支持FullText類型的索引,沒有保存數據庫行數,計算count(*)需要全局掃描
    6、支持自動增加列屬性auto_increment
    7、最后也是非常重要的一點:InnerDB是為了處理大量數據時的最大性能設計,其CPU效率可能是其他基於磁盤的關系型數據庫所不能匹敵的。
    建議使用場景
    1、可靠性高或者必須要求事務處理
    2、表更新和查詢相當的頻繁,並且表鎖定的機會比較大的情況下,指定InnerDB存儲引擎。

2.2 表和數據等在Mysql中是如何存儲的

我們新建一個數據庫mds_demo,里面有兩張表:order_info,user

img

我們找到mysql存放數據的data目錄,存在一個mds_demo的文件夾,同時我們也找到了order_info和user的文件。

img

為什么兩張表產生了不同的文件呢?原因很簡單,因為創建這兩張表時使用了不同的引擎

img

img

  • MyISAM引擎在創建表的時候,會創建三個文件
    .MYD文件:存放表里的數據
    .MYI文件:存放索引數據
    .sdi文件: Serialized Dictionary Information的縮寫。在Mysql5里沒有sdi文件,但會有一個FRM文件,用戶存放表結構信息。在MySQL8.0中重新設計了數據字典,改為sdi。
    MyISAM的索引和數據是分開的,並且索引是有壓縮的,所以存儲文件就會小很多,MyISAM應對錯誤碼導致的數據恢復的速度很快。
  • InnerDB引擎在創建表的時候,只有1個文件.ibd,即存放了索引又存放了文件,參見B+Tree。所以它也被稱之為聚集索引,即葉子節點包含完整的索引和數據,對應的MyISAM為非聚集索引。
    補充說明一下:存儲引擎是針對表的,而不是針對數據庫,同一個庫的不同的表可以使用不同的引擎。

2.3 為什么InnoDB必須要有主鍵,並且推薦使用整型的自增主鍵?

通過上面的講解這個問題其實已經很清楚了,為了滿足MySQL的索引數據結構B+樹的特性,必須要有索引作為主鍵,可以有效提高查詢效率。有的童鞋可能會說我創建表的時候可以沒有主鍵啊,這個其實和Oracle的rownum一樣,如果不指定主鍵,InnoDB會從插入的數據中找出不重復的一列作為主鍵索引,如果沒找到不重復的一列,InnoDB會在后台增加一列rowId做為主鍵索引。所以不如我們自己創建一個主鍵。

將索引的數據類型是設置為整型,一來占有的磁盤空間或內存空間更少,另一方面整型相對於字符串比較更快速,而字符串需要先轉換為ASCII碼然后再一個個進行比較的。

參見B+樹的圖它本質上是多路多叉樹,如果主鍵索引不是自增的,那么后續插入的索引就會引起B+樹的其他節點的分裂和重新平衡,影響數據插入的效率,如果是自增主鍵,只用在尾節點做增加就可以。

最后特別強調一點:不管當前是否有性能要求或者數據量多大,千萬不要使用UUID作為索引。

2.4 為什么Mysql存儲引擎中默認每個頁的大小為16KB?

假設我們一行數據大小為1K,那么一頁就能存16條數據,包含指針+數據+索引。假設一行數據大小為1K,那么一頁(1個葉子節點)就能存16條數據;對於非葉子節點,假設ID為bigint類型那么長度為8B,指針大小在Innodb源碼中為6B,一共就是14B,那么一頁里就可以存儲16K/14=1170個(主鍵+指針),這樣一顆高度為3的B+樹能存儲的數據為:1170117016=2千萬級別。所以我們前面1000萬的數據只有0.02s。

2.5 HASH算法的使用場景

img

Hash算法是一種散列算法,就是計算出某個字段的hash,然后存放在對應的地址中,查找數據時只需要1次定位而不像BTree那樣從根節點找到葉子節點經過多次IO操作,所以查詢效率非常地高。但同樣也有很多的弊端,講一下最重要的兩條。

1、很明顯hash只支持=、IN等查詢,而不支持范圍查詢
2、 Hash 索引在任何時候都不能避免表掃描。

所以使用時務必注意。


53: 非關系型數據庫和關系型數據庫區別,優勢比較

img

非關系型數據庫(感覺翻譯不是很准確)稱為 NoSQL,也就是 Not Only SQL,不僅僅是 SQL。非關系型數據庫不需要寫一些復雜的 SQL 語句,其內部存儲方式是以 key-value 的形式存在可以把它想象成電話本的形式,每個人名(key)對應電話(value)。常見的非關系型數據庫主要有 Hbase、Redis、MongoDB 等。非關系型數據庫不需要經過 SQL 的重重解析,所以性能很高;非關系型數據庫的可擴展性比較強,數據之間沒有耦合性,遇見需要新加字段的需求,就直接增加一個 key-value 鍵值對即可。

img

關系型數據庫以表格的形式存在,以行和列的形式存取數據,關系型數據庫這一系列的行和列被稱為表,無數張表組成了數據庫,常見的關系型數據庫有 Oracle、DB2、Microsoft SQL Server、MySQL等。關系型數據庫能夠支持復雜的 SQL 查詢,能夠體現出數據之間、表之間的關聯關系;關系型數據庫也支持事務,便於提交或者回滾。

它們之間的劣勢都是基於對方的優勢來滿足的。


54: MySQL 事務四大特性

一說到 MySQL 事務,你肯定能想起來四大特性:原子性一致性隔離性持久性,下面再對這事務的四大特性做一個描述

  • 原子性(Atomicity): 原子性指的就是 MySQL 中的包含事務的操作要么全部成功、要么全部失敗回滾,因此事務的操作如果成功就必須要全部應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

這里涉及到一個概念,什么是 MySQL 中的事務?

事務是一組操作,組成這組操作的各個單元,要不全都成功要不全都失敗,這個特性就是事務。

在 MySQL 中,事務是在引擎層實現的,只有使用 innodb 引擎的數據庫或表才支持事務。

  • 一致性(Consistency):一致性指的是一個事務在執行前后其狀態一致。比如 A 和 B 加起來的錢一共是 1000 元,那么不管 A 和 B 之間如何轉賬,轉多少次,事務結束后兩個用戶的錢加起來還得是 1000,這就是事務的一致性。
  • 持久性(Durability): 持久性指的是一旦事務提交,那么發生的改變就是永久性的,即使數據庫遇到特殊情況比如故障的時候也不會產生干擾。
  • 隔離性(Isolation):隔離性需要重點說一下,當多個事務同時進行時,就有可能出現臟讀(dirty read)不可重復讀(non-repeatable read)幻讀(phantom read) 的情況,為了解決這些並發問題,提出了隔離性的概念。

臟讀:事務 A 讀取了事務 B 更新后的數據,但是事務 B 沒有提交,然后事務 B 執行回滾操作,那么事務 A 讀到的數據就是臟數據

不可重復讀:事務 A 進行多次讀取操作,事務 B 在事務 A 多次讀取的過程中執行更新操作並提交,提交后事務 A 讀到的數據不一致。

幻讀:事務 A 將數據庫中所有學生的成績由 A -> B,此時事務 B 手動插入了一條成績為 A 的記錄,在事務 A 更改完畢后,發現還有一條記錄沒有修改,那么這種情況就叫做出現了幻讀。

SQL的隔離級別有四種,它們分別是讀未提交(read uncommitted)讀已提交(read committed)可重復讀(repetable read)串行化(serializable)。下面分別來解釋一下。

讀未提交:讀未提交指的是一個事務在提交之前,它所做的修改就能夠被其他事務所看到。

讀已提交:讀已提交指的是一個事務在提交之后,它所做的變更才能夠讓其他事務看到。

可重復讀:可重復讀指的是一個事務在執行的過程中,看到的數據是和啟動時看到的數據是一致的。未提交的變更對其他事務不可見。

串行化:顧名思義是對於同一行記錄,會加寫鎖會加讀鎖。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。

這四個隔離級別可以解決臟讀、不可重復讀、幻象讀這三類問題。總結如下

事務隔離級別 臟讀 不可重復讀 幻讀
讀未提交 允許 允許 允許
讀已提交 不允許 允許 允許
可重復讀 不允許 不允許 允許
串行化 不允許 不允許 不允許

其中隔離級別由低到高是:讀未提交 < 讀已提交 < 可重復讀 < 串行化

隔離級別越高,越能夠保證數據的完整性和一致性,但是對並發的性能影響越大。大多數數據庫的默認級別是讀已提交(Read committed),比如 Sql Server、Oracle ,但是 MySQL 的默認隔離級別是 可重復讀(repeatable-read)

55: MySQL 常見存儲引擎的區別

MySQL 常見的存儲引擎,可以使用

SHOW ENGINES

命令,來列出所有的存儲引擎

img

可以看到,InnoDB 是 MySQL 默認支持的存儲引擎,支持事務、行級鎖定和外鍵

MyISAM 存儲引擎的特點

在 5.1 版本之前,MyISAM 是 MySQL 的默認存儲引擎,MyISAM 並發性比較差,使用的場景比較少,主要特點是

  • 不支持事務操作,ACID 的特性也就不存在了,這一設計是為了性能和效率考慮的。

  • 不支持外鍵操作,如果強行增加外鍵,MySQL 不會報錯,只不過外鍵不起作用。

  • MyISAM 默認的鎖粒度是表級鎖,所以並發性能比較差,加鎖比較快,鎖沖突比較少,不太容易發生死鎖的情況。

  • MyISAM 會在磁盤上存儲三個文件,文件名和表名相同,擴展名分別是 .frm(存儲表定義).MYD(MYData,存儲數據)MYI(MyIndex,存儲索引)。這里需要特別注意的是 MyISAM 只緩存索引文件,並不緩存數據文件。

  • MyISAM 支持的索引類型有 全局索引(Full-Text)B-Tree 索引R-Tree 索引

    Full-Text 索引:它的出現是為了解決針對文本的模糊查詢效率較低的問題。

    B-Tree 索引:所有的索引節點都按照平衡樹的數據結構來存儲,所有的索引數據節點都在葉節點

    R-Tree索引:它的存儲方式和 B-Tree 索引有一些區別,主要設計用於存儲空間和多維數據的字段做索引,目前的 MySQL 版本僅支持 geometry 類型的字段作索引,相對於 BTREE,RTREE 的優勢在於范圍查找。

  • 數據庫所在主機如果宕機,MyISAM 的數據文件容易損壞,而且難以恢復。

  • 增刪改查性能方面:SELECT 性能較高,適用於查詢較多的情況

InnoDB 存儲引擎的特點

自從 MySQL 5.1 之后,默認的存儲引擎變成了 InnoDB 存儲引擎,相對於 MyISAM,InnoDB 存儲引擎有了較大的改變,它的主要特點是

  • 支持事務操作,具有事務 ACID 隔離特性,默認的隔離級別是可重復讀(repetable-read)、通過MVCC(並發版本控制)來實現的。能夠解決臟讀不可重復讀的問題。
  • InnoDB 支持外鍵操作。
  • InnoDB 默認的鎖粒度行級鎖,並發性能比較好,會發生死鎖的情況。
  • 和 MyISAM 一樣的是,InnoDB 存儲引擎也有 .frm文件存儲表結構 定義,但是不同的是,InnoDB 的表數據與索引數據是存儲在一起的,都位於 B+ 數的葉子節點上,而 MyISAM 的表數據和索引數據是分開的。
  • InnoDB 有安全的日志文件,這個日志文件用於恢復因數據庫崩潰或其他情況導致的數據丟失問題,保證數據的一致性。
  • InnoDB 和 MyISAM 支持的索引類型相同,但具體實現因為文件結構的不同有很大差異。
  • 增刪改查性能方面,果執行大量的增刪改操作,推薦使用 InnoDB 存儲引擎,它在刪除操作時是對行刪除,不會重建表。

MyISAM 和 InnoDB 存儲引擎的對比

  • 鎖粒度方面:由於鎖粒度不同,InnoDB 比 MyISAM 支持更高的並發;InnoDB 的鎖粒度為行鎖、MyISAM 的鎖粒度為表鎖、行鎖需要對每一行進行加鎖,所以鎖的開銷更大,但是能解決臟讀和不可重復讀的問題,相對來說也更容易發生死鎖
  • 可恢復性上:由於 InnoDB 是有事務日志的,所以在產生由於數據庫崩潰等條件后,可以根據日志文件進行恢復。而 MyISAM 則沒有事務日志。
  • 查詢性能上:MyISAM 要優於 InnoDB,因為 InnoDB 在查詢過程中,是需要維護數據緩存,而且查詢過程是先定位到行所在的數據塊,然后在從數據塊中定位到要查找的行;而 MyISAM 可以直接定位到數據所在的內存地址,可以直接找到數據。
  • 表結構文件上: MyISAM 的表結構文件包括:.frm(表結構定義),.MYI(索引),.MYD(數據);而 InnoDB 的表數據文件為:.ibd和.frm(表結構定義);

56:MySQL 基礎架構

這道題應該從 MySQL 架構來理解,我們可以把 MySQL 拆解成幾個零件,如下圖所示

img

大致上來說,MySQL 可以分為 Server層和 存儲引擎層。

Server 層包括連接器、查詢緩存、分析器、優化器、執行器,包括大多數 MySQL 中的核心功能,所有跨存儲引擎的功能也在這一層實現,包括 存儲過程、觸發器、視圖等

存儲引擎層包括 MySQL 常見的存儲引擎,包括 MyISAM、InnoDB 和 Memory 等,最常用的是 InnoDB,也是現在 MySQL 的默認存儲引擎。存儲引擎也可以在創建表的時候手動指定,比如下面

CREATE TABLE t (i INT) ENGINE = <Storage Engine>; 

然后我們就可以探討 MySQL 的執行過程了

連接器

首先需要在 MySQL 客戶端登陸才能使用,所以需要一個連接器來連接用戶和 MySQL 數據庫,我們一般是使用

mysql -u 用戶名 -p 密碼

來進行 MySQL 登陸,和服務端建立連接。在完成 TCP 握手 后,連接器會根據你輸入的用戶名和密碼驗證你的登錄身份。如果用戶名或者密碼錯誤,MySQL 就會提示 Access denied for user,來結束執行。如果登錄成功后,MySQL 會根據權限表中的記錄來判定你的權限。

查詢緩存

連接完成后,你就可以執行 SQL 語句了,這行邏輯就會來到第二步:查詢緩存。

MySQL 在得到一個執行請求后,會首先去 查詢緩存 中查找,是否執行過這條 SQL 語句,之前執行過的語句以及結果會以 key-value 對的形式,被直接放在內存中。key 是查詢語句,value 是查詢的結果。如果通過 key 能夠查找到這條 SQL 語句,就直接返回 SQL 的執行結果。

如果語句不在查詢緩存中,就會繼續后面的執行階段。執行完成后,執行結果就會被放入查詢緩存中。可以看到,如果查詢命中緩存,MySQL 不需要執行后面的復雜操作,就可以直接返回結果,效率會很高。

img

但是查詢緩存不建議使用

為什么呢?因為只要在 MySQL 中對某一張表執行了更新操作,那么所有的查詢緩存就會失效,對於更新頻繁的數據庫來說,查詢緩存的命中率很低。

分析器

如果沒有命中查詢,就開始執行真正的 SQL 語句。

  • 首先,MySQL 會根據你寫的 SQL 語句進行解析,分析器會先做 詞法分析,你寫的 SQL 就是由多個字符串和空格組成的一條 SQL 語句,MySQL 需要識別出里面的字符串是什么,代表什么。
  • 然后進行 語法分析,根據詞法分析的結果, 語法分析器會根據語法規則,判斷你輸入的這個 SQL 語句是否滿足 MySQL 語法。如果 SQL 語句不正確,就會提示 You have an error in your SQL syntax

優化器

經過分析器的詞法分析和語法分析后,你這條 SQL 就合法了,MySQL 就知道你要做什么了。但是在執行前,還需要進行優化器的處理,優化器會判斷你使用了哪種索引,使用了何種連接,優化器的作用就是確定效率最高的執行方案。

執行器

MySQL 通過分析器知道了你的 SQL 語句是否合法,你想要做什么操作,通過優化器知道了該怎么做效率最高,然后就進入了執行階段,開始執行這條 SQL 語句

在執行階段,MySQL 首先會判斷你有沒有執行這條語句的權限,沒有權限的話,就會返回沒有權限的錯誤。如果有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。對於有索引的表,執行的邏輯也差不多。

至此,MySQL 對於一條語句的執行過程也就完成了。

57: 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 連接

首先,對 SELECT 語句執行查詢時,對FROM 關鍵字兩邊的表執行連接,會形成笛卡爾積,這時候會產生一個虛表VT1(virtual table)

首先先來解釋一下什么是笛卡爾積

現在我們有兩個集合 A = {0,1} , B = {2,3,4}

那么,集合 A * B 得到的結果就是

A * B = {(0,2)、(1,2)、(0,3)、(1,3)、(0,4)、(1,4)};

B * A = {(2,0)、{2,1}、{3,0}、{3,1}、{4,0}、(4,1)};

上面 A * B 和 B * A 的結果就可以稱為兩個集合相乘的 笛卡爾積

我們可以得出結論,A 集合和 B 集合相乘,包含了集合 A 中的元素和集合 B 中元素之和,也就是 A 元素的個數 * B 元素的個數

再來解釋一下什么是虛表

在 MySQL 中,有三種類型的表

一種是永久表,永久表就是創建以后用來長期保存數據的表

一種是臨時表,臨時表也有兩類,一種是和永久表一樣,只保存臨時數據,但是能夠長久存在的;還有一種是臨時創建的,SQL 語句執行完成就會刪除。

一種是虛表,虛表其實就是視圖,數據可能會來自多張表的執行結果。

ON 過濾

然后對 FROM 連接的結果進行 ON 篩選,創建 VT2,把符合記錄的條件存在 VT2 中。

JOIN 連接

第三步,如果是 OUTER JOIN(left join、right join) ,那么這一步就將添加外部行,如果是 left join 就把 ON 過濾條件的左表添加進來,如果是 right join ,就把右表添加進來,從而生成新的虛擬表 VT3。

WHERE 過濾

第四步,是執行 WHERE 過濾器,對上一步生產的虛擬表引用 WHERE 篩選,生成虛擬表 VT4。

WHERE 和 ON 的區別

  • 如果有外部列,ON 針對過濾的是關聯表,主表(保留表)會返回所有的列;
  • 如果沒有添加外部列,兩者的效果是一樣的;

應用

  • 對主表的過濾應該使用 WHERE;
  • 對於關聯表,先條件查詢后連接則用 ON,先連接后條件查詢則用 WHERE;

GROUP BY

根據 group by 字句中的列,會對 VT4 中的記錄進行分組操作,產生虛擬機表 VT5。果應用了group by,那么后面的所有步驟都只能得到的 VT5 的列或者是聚合函數(count、sum、avg等)。

HAVING

緊跟着 GROUP BY 字句后面的是 HAVING,使用 HAVING 過濾,會把符合條件的放在 VT6

SELECT

第七步才會執行 SELECT 語句,將 VT6 中的結果按照 SELECT 進行刷選,生成 VT7

DISTINCT

在第八步中,會對 TV7 生成的記錄進行去重操作,生成 VT8。事實上如果應用了 group by 子句那么 distinct 是多余的,原因同樣在於,分組的時候是將列中唯一的值分成一組,同時只為每一組返回一行記錄,那么所以的記錄都將是不相同的。

ORDER BY

應用 order by 子句。按照 order_by_condition 排序 VT8,此時返回的一個游標,而不是虛擬表。sql 是基於集合的理論的,集合不會預先對他的行排序,它只是成員的邏輯集合,成員的順序是無關緊要的。

SQL 語句執行的過程如下

img

58:什么是臨時表,何時刪除臨時表

什么是臨時表?MySQL 在執行 SQL 語句的過程中,通常會臨時創建一些存儲中間結果集的表,臨時表只對當前連接可見,在連接關閉時,臨時表會被刪除並釋放所有表空間。

臨時表分為兩種:一種是內存臨時表,一種是磁盤臨時表,什么區別呢?內存臨時表使用的是 MEMORY 存儲引擎,而臨時表采用的是 MyISAM 存儲引擎。

MEMORY 存儲引擎:memory 是 MySQL 中一類特殊的存儲引擎,它使用存儲在內容中的內容來創建表,而且數據全部放在內存中。每個基於 MEMORY 存儲引擎的表實際對應一個磁盤文件。該文件的文件名與表名相同,類型為 frm 類型。而其數據文件,都是存儲在內存中,這樣有利於數據的快速處理,提高整個表的效率。MEMORY 用到的很少,因為它是把數據存到內存中,如果內存出現異常就會影響數據。如果重啟或者關機,所有數據都會消失。因此,基於 MEMORY 的表的生命周期很短,一般是一次性的。

MySQL 會在下面這幾種情況產生臨時表

  • 使用 UNION 查詢:UNION 有兩種,一種是UNION ,一種是 UNION ALL ,它們都用於聯合查詢;區別是 使用 UNION 會去掉兩個表中的重復數據,相當於對結果集做了一下去重(distinct)。使用 UNION ALL,則不會排重,返回所有的行。使用 UNION 查詢會產生臨時表。
  • 使用 TEMPTABLE 算法或者是 UNION 查詢中的視圖。TEMPTABLE 算法是一種創建臨時表的算法,它是將結果放置到臨時表中,意味這要 MySQL 要先創建好一個臨時表,然后將結果放到臨時表中去,然后再使用這個臨時表進行相應的查詢。
  • ORDER BY 和 GROUP BY 的子句不一樣時也會產生臨時表。
  • DISTINCT 查詢並且加上 ORDER BY 時;
  • SQL中用到 SQL_SMALL_RESULT 選項時;如果查詢結果比較小的時候,可以加上 SQL_SMALL_RESULT 來優化,產生臨時表
  • FROM 中的子查詢;
  • EXPLAIN 查看執行計划結果的 Extra 列中,如果使用 Using Temporary 就表示會用到臨時表。

59:MySQL 常見索引類型

索引是存儲在一張表中特定列上的數據結構,索引是在列上創建的。並且,索引是一種數據結構。

在 MySQL 中,主要有下面這幾種索引

  • 全局索引(FULLTEXT):全局索引,目前只有 MyISAM 引擎支持全局索引,它的出現是為了解決針對文本的模糊查詢效率較低的問題。
  • 哈希索引(HASH):哈希索引是 MySQL 中用到的唯一 key-value 鍵值對的數據結構,很適合作為索引。HASH 索引具有一次定位的好處,不需要像樹那樣逐個節點查找,但是這種查找適合應用於查找單個鍵的情況,對於范圍查找,HASH 索引的性能就會很低。
  • B-Tree 索引:B 就是 Balance 的意思,BTree 是一種平衡樹,它有很多變種,最常見的就是 B+ Tree,它被 MySQL 廣泛使用。
  • R-Tree 索引:R-Tree 在 MySQL 很少使用,僅支持 geometry 數據類型,支持該類型的存儲引擎只有MyISAM、BDb、InnoDb、NDb、Archive幾種,相對於 B-Tree 來說,R-Tree 的優勢在於范圍查找。

60:varchar 和 char 的區別和使用場景

MySQL 中沒有 nvarchar 數據類型,所以直接比較的是 varchar 和 char 的區別

char :表示的是定長的字符串,當你輸入小於指定的數目,比如你指定的數目是 char(6),當你輸入小於 6 個字符的時候,char 會在你最后一個字符后面補空值。當你輸入超過指定允許最大長度后,MySQL 會報錯

img

varchar: varchar 指的是長度為 n 個字節的可變長度,並且是非Unicode的字符數據。n 的值是介於 1 - 8000 之間的數值。存儲大小為實際大小。

Unicode 是一種字符編碼方案,它為每種語言中的每個字符都設定了統一唯一的二進制編碼,以實現跨語言、跨平台進行文本轉換、處理的要求

使用 char 存儲定長的數據非常方便、char 檢索效率高,無論你存儲的數據是否到了 10 個字節,都要去占用 10 字節的空間

使用 varchar 可以存儲變長的數據,但存儲效率沒有 char 高。

60:什么是 內連接、外連接、交叉連接、笛卡爾積

連接的方式主要有三種:外連接、內鏈接、交叉連接

  • 外連接(OUTER JOIN):外連接分為三種,分別是左外連接(LEFT OUTER JOIN 或 LEFT JOIN)右外連接(RIGHT OUTER JOIN 或 RIGHT JOIN)全外連接(FULL OUTER JOIN 或 FULL JOIN)

    左外連接:又稱為左連接,這種連接方式會顯示左表不符合條件的數據行,右邊不符合條件的數據行直接顯示 NULL

img

右外連接:也被稱為右連接,他與左連接相對,這種連接方式會顯示右表不符合條件的數據行,左表不符合條件的數據行直接顯示 NULL

img

MySQL 暫不支持全外連接

  • 內連接(INNER JOIN):結合兩個表中相同的字段,返回關聯字段相符的記錄。

img

  • 笛卡爾積(Cartesian product): 我在上面提到了笛卡爾積,為了方便,下面再列出來一下。

現在我們有兩個集合 A = {0,1} , B = {2,3,4}

那么,集合 A * B 得到的結果就是

A * B = {(0,2)、(1,2)、(0,3)、(1,3)、(0,4)、(1,4)};

B * A = {(2,0)、{2,1}、{3,0}、{3,1}、{4,0}、(4,1)};

上面 A * B 和 B * A 的結果就可以稱為兩個集合相乘的 笛卡爾積

我們可以得出結論,A 集合和 B 集合相乘,包含了集合 A 中的元素和集合 B 中元素之和,也就是 A 元素的個數 * B 元素的個數

  • 交叉連接的原文是Cross join ,就是笛卡爾積在 SQL 中的實現,SQL中使用關鍵字CROSS JOIN來表示交叉連接,在交叉連接中,隨便增加一個表的字段,都會對結果造成很大的影響。

    SELECT * FROM t_Class a CROSS JOIN t_Student b WHERE a.classid=b.classid
    

    或者不用 CROSS JOIN,直接用 FROM 也能表示交叉連接的效果

    SELECT * FROM t_Class a ,t_Student b WHERE a.classid=b.classid
    

    如果表中字段比較多,不適宜用交叉連接,交叉連接的效率比較差。

  • 全連接:全連接也就是 full join,MySQL 中不支持全連接,但是可以使用其他連接查詢來模擬全連接,可以使用 UNIONUNION ALL 進行模擬。例如

    (select colum1,colum2...columN from tableA ) union (select colum1,colum2...columN from tableB )
    
    
    或 
    (select colum1,colum2...columN from tableA ) union all (select colum1,colum2...columN from tableB );
    

    使用 UNION 和 UNION ALL 的注意事項

    通過 union 連接的 SQL 分別單獨取出的列數必須相同

    使用 union 時,多個相等的行將會被合並,由於合並比較耗時,一般不直接使用 union 進行合並,而是通常采用 union all 進行合並

61:談談 SQL 優化的經驗

  • 查詢語句無論是使用哪種判斷條件 等於、小於、大於WHERE 左側的條件查詢字段不要使用函數或者表達式
  • 使用 EXPLAIN 命令優化你的 SELECT 查詢,對於復雜、效率低的 sql 語句,我們通常是使用 explain sql 來分析這條 sql 語句,這樣方便我們分析,進行優化。
  • 當你的 SELECT 查詢語句只需要使用一條記錄時,要使用 LIMIT 1
  • 不要直接使用 SELECT *,而應該使用具體需要查詢的表字段,因為使用 EXPLAIN 進行分析時,SELECT * 使用的是全表掃描,也就是 type = all
  • 為每一張表設置一個 ID 屬性
  • 避免在 WHERE 字句中對字段進行 NULL 判斷
  • 避免在 WHERE 中使用 !=<> 操作符
  • 使用 BETWEEN AND 替代 IN
  • 為搜索字段創建索引
  • 選擇正確的存儲引擎,InnoDB 、MyISAM 、MEMORY 等
  • 使用 LIKE %abc% 不會走索引,而使用 LIKE abc% 會走索引
  • 對於枚舉類型的字段(即有固定羅列值的字段),建議使用ENUM而不是VARCHAR,如性別、星期、類型、類別等
  • 拆分大的 DELETE 或 INSERT 語句
  • 選擇合適的字段類型,選擇標准是 盡可能小、盡可能定長、盡可能使用整數
  • 字段設計盡可能使用 NOT NULL
  • 進行水平切割或者垂直分割

水平分割:通過建立結構相同的幾張表分別存儲數據

垂直分割:將經常一起使用的字段放在一個單獨的表中,分割后的表記錄之間是一一對應關系。

63:說幾個常見的 影響MYSQL性能 的案例

參考文獻 :https://www.cnblogs.com/zhiqian-ali/p/6336521.html

大規模數據導出功能

相信很多業務都遇到過數據導出,明細展示這方面的需求,sql基本上都是先求一個數據的總和然后,limit n,m分頁查詢,這樣的問題就在於,在掃描前面的數據時是不會有性能問題的,當n值越大,偏移量越多,掃描的數據就越多,這個時候就會產生問題,一個本來不的sql就會變成慢sql,導致DB性能下降。針對這種問題DBA都會建議開發將limit n,m改為id范圍的查詢,或者進行業務改造對於一些不必要的場景只展示前幾百條,只需要進行一次分頁即可。

類似sql模式:

   select count(*) from table_name_1;
   select * from table_name_1 limit n,m;(n值越大性能越差)
   建議改造成:
   select * from table_name_1 where id>? and id<?
ERP類系統使用聚合函數或者分組排序

類似倉庫內管理系統會需要展示很多統計信息,很多開發會選擇在DB端計算出結果直接展示,問題在於sum,max,min類的聚合函數在DB端執行會消耗到CPU資源,如果這個時候還遇到索引不合理的情況,往往會帶來災難性的后果。這種情況DB端除了增加索引,對CPU的消耗是無法優化的,所以DB性能必然下降。一般這種情況DBA會建議能在程序端計算的就不要放在DB端,或者直接接搜索引擎。

類似sql模式:

    select sum(column_name) as column_1 from table_name_1;
    or
    select distinct cloumn_name  from table_name_1 group by column_name_1 order by column_name_1;
錯誤使用子查詢

在DB端執行去重,join以及子查詢等操作的時候,mysql會自動創建臨時表

DB自動創建臨時表的情況有如下幾種

1. Evaluation of UNION statements.
2.  Evaluation of some views, such those that use the TEMPTABLE algorithm, UNION, or aggregation.
3. Evaluation of derived tables (subqueries in the FROM clause).(這個是本節關注的重點)
4. Tables created for subquery or semi-join materialization (see Section 8.2.1.18, “Subquery Optimization”).
5. Evaluation of statements that contain an ORDER BY clause and a different GROUP BY clause, or for which the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue.
6. Evaluation of DISTINCT combined with ORDER BY may require a temporary table.
7. For queries that use the SQL_SMALL_RESULT option, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.
8. Evaluation of multiple-table UPDATE statements.
9. Evaluation of GROUP_CONCAT() or COUNT(DISTINCT) expressions.

在mysql中,對於子查詢,外層每執行一次,內層子查詢要重復執行一次,所以一般建議用join代替子查詢。

下面舉一個子查詢引起DB性能問題的例子

Query1:select count(*) from wd_order_late_reason_send wrs left join wd_order_detail_late_send wds on wrs.store_code = wds.store_code;

下面是執行計划:

*************************<strong> 1. row </strong>***********************<strong>
       id: 1
       select_type: SIMPLE
       table: wrs
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 836846
       Extra: NULL
 </strong>***********************<strong> 2. row </strong>*************************
       id: 1
       select_type: SIMPLE
       table: wds
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 670612
       Extra: Using where; Using join buffer (Block Nested Loop)

Query2:select count(*) from (select wrs.store_code from wd_order_late_reason_send wrs left join wd_order_detail_late_send wds on wrs.store_code = wds.store_code) tb;

執行計划如下

  *************************<strong> 1. row </strong>***********************<strong>
       id: 1
       select_type: PRIMARY
       table: <derived2>
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 561198969752
       Extra: NULL
 </strong>***********************<strong> 2. row </strong>***********************<strong>
       id: 2
       select_type: DERIVED
       table: wrs
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 836846
       Extra: NULL
</strong>***********************<strong> 3. row </strong>*************************
       id: 2
       select_type: DERIVED
       table: wds
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 670612
       Extra: Using where; Using join buffer (Block Nested Loop)

這兩個sql結果相同,唯一不同的是第二條sql使用了子查詢。通過執行計划可以看出(排除沒有索引部分)兩個sql最大的差別就是第二個sql有derived table並且rows是561198969752,出現這個數值是因為在select count(*)****每次計數的時候子查詢的sql都會執行一遍,所以最后是子查詢join的笛卡爾積。因為內存中用於進行join操作的空間有限,這個時候就會使用磁盤空間來創建臨時表,所以當第二種sql頻繁執行的時候會有磁盤被撐爆的風險。 想要了解更多關於子查詢的優化可以參考下面這個鏈接link

慢sql

這里我們所說的慢sql主要指那些由於索引使用不正確或沒有使用索引產生的,一般可以通過增加索引。一個合理的索引對一條sql性能的影響是非常巨大的。索引的主要目的是為了減少讀取的數據塊,也就是我們常說的邏輯讀,讀取的數據塊越少,sql效率越高。另外索引在一定程度上也可以減少CPU的消耗,例如排序,分組,因為索引本來就是有序的。

說到邏輯讀,對應的就會有物理讀,在mysql服務端是有buffer pool來緩存硬盤中的數據,但是這個buffer pool的大小跟磁盤中數據文件的大小是不等的,往往buffer pool會遠遠小於磁盤中數據的大小。buffer pool會有一個LRU鏈表,當從磁盤中加載數據塊到內存中(這個就是物理讀)發現沒有空間的時候會優先覆蓋LRU鏈表中的數據塊。當一條sql沒有合理的索引需要掃描大量的數據的時候,不光要掃描內存中的許多數據塊,還可能需要從磁盤中加載不同不存在的數據塊到內存中進行判斷,當這種情況頻繁發生的時候,sql性能就會急劇下降,因而也影響了DB實例的性能。

以下表格是訪問不同存儲設備的rt,由此可見一個合理的索引的重要性。

類別 吞吐量 響應時間
訪問L1 Cache 0.5ns
訪問L2 Cache 7ns
內存訪問 800M/s 100ns
機械盤 300M/s 10ms
SSD 300M/s 0.1~0.2ms
日志刷盤策略不合理

目前集團mysql大部分使用的都是innodb存儲引擎,因此在每條DML語句執行時不光會記如binlog還有記錄innodb特有的redo log和undo log。這些日志文件都是先寫入內存中然后在刷新到磁盤中。在server端有兩個參數分別控制他們的寫入速度。innodb_flush_log_at_trx_commit控制redo log寫入模式,sync_binlog控制binlog寫入模式。

img

通過以上表格可以了解到,在使用線上默認配置的情況下每次commit都會刷redo log到磁盤,也就是說每次寫入都會伴隨着日志刷盤的操作,需要消耗磁盤IO,所以在高TPS或者類似業務大促情況下,DBA可以調整這個參數,來提升DB支撐TPS的能力。

BP設置過小

前面已經提到sql在讀寫數據的時候不會直接跟磁盤交互,而是先讀寫內存數據,因為這樣最快。但是考慮到成本問題BP(buffer pool)大小是有限的,不可能跟數據文件同等大小,所以如果BP設置不合理就會導致DB的QPS TPS始終上不去。下面我們具體分析一下。

mysql buffer pool中包含undo page,insert buffer page,adaptive hash index,index page,lock info,data dictionary等等DB相關信息,但是這些page都可以歸為三類free page,clean page,dirty page.buffer pool中維護了三個鏈表:free list,dirty list,lru list

  • free page:此page未被使用,此種類型page位於free鏈表中
  • clean page:此page被使用,對應數據文件中的一個頁面,但是頁面沒有被修改,此種類型page位於lru鏈表中
  • dirty page:此page被使用,對應數據文件中的一個頁面,但是頁面被修改過,此種類型page位於lru鏈表和flush鏈表中

當BP設置過小的時候,比如BP 10g 數據文件有200g 這個時候有大量的select或者dml語句,mysql就會頻繁的刷新lru list或者dirty list 到磁盤,大部分時間消耗在刷磁盤上,而不是業務sql處理上,這個時候就會導致業務TPS QPS始終上不去,伴隨着DB內存命中率降低。通常這個時候的解決辦法是需要DBA調整一下實例BP的大小。

硬件問題

就像生活中會有意外一樣,在排除了之前那些因素之后,還會存在因為硬件故障或者參數設置不合理導致DB性能抖動的情況,如果不能立即修復,DBA一般只能通過遷移實例的方式來消除影響。


Myql面試問題交流:瘋狂創客圈社群


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM