MySQL相關問題匯總(更新中)

關于各種 DBMS 的介紹
答疑 1
文章中有句話不太理解,“列式數據庫是將數據按照列存儲到數據庫中,這樣做的好處是可以大量降低系統的 I/O”,可以解釋一些“降低系統 I/O”是什么意思嗎?

解答
行式存儲是把一行的數據都串起來進行存儲,然后再存儲下一行。同樣,列式存儲是把一列的數據都串起來進行存儲,然后再存儲下一列。這樣做的話,相鄰數據的數據類型都是一樣的,更容易壓縮,壓縮之后就自然降低了 I/O。

我們還需要從數據處理的需求出發,去理解行式存儲和列式存儲。數據處理可以分為 OLTP(聯機事務處理)和 OLAP(聯機分析處理)兩大類。

OLTP 一般用于處理客戶的事務和進行查詢,需要隨時對數據表中的記錄進行增刪改查,對實時性要求高。

OLAP 一般用于市場的數據分析,通常數據量大,需要進行復雜的分析操作,可以對大量歷史數據進行匯總和分析,對實時性要求不高。

那么對于 OLTP 來說,由于隨時需要對數據記錄進行增刪改查,更適合采用行式存儲,因為一行數據的寫入會同時修改多個列。傳統的 RDBMS 都屬于行式存儲,比如 Oracle、SQL Server 和 MySQL 等。

對于 OLAP 來說,由于需要對大量歷史數據進行匯總和分析,則適合采用列式存儲,這樣的話匯總數據會非常快,但是對于插入(INSERT)和更新(UPDATE)會比較麻煩,相比于行式存儲性能會差不少。

所以說列式存儲適合大批量數據查詢,可以降低 I/O,但如果對實時性要求高,則更適合行式存儲。

關于查詢優化
答疑 1
在 MySQL 中統計數據表的行數,可以使用三種方式:SELECT COUNT()、SELECT COUNT(1)和SELECT COUNT(具體字段),使用這三者之間的查詢效率是怎樣的?之前看到說是:SELECT COUNT()> SELECT COUNT(1)> SELECT COUNT(具體字段)。

解答
在 MySQL InnoDB 存儲引擎中,COUNT(*)和COUNT(1)都是對所有結果進行COUNT。如果有 WHERE 子句,則是對所有符合篩選條件的數據行進行統計;如果沒有 WHERE 子句,則是對數據表的數據行數進行統計。

因此COUNT(*)和COUNT(1)本質上并沒有區別,執行的復雜度都是O(N),也就是采用全表掃描,進行循環 + 計數的方式進行統計。

如果是 MySQL MyISAM 存儲引擎,統計數據表的行數只需要O(1)的復雜度,這是因為每張 MyISAM 的數據表都有一個 meta 信息存儲了row_count值,而一致性則由表級鎖來保證。因為 InnoDB 支持事務,采用行級鎖和 MVCC 機制,所以無法像 MyISAM 一樣,只維護一個row_count變量,因此需要采用掃描全表,進行循環 + 計數的方式來完成統計。

需要注意的是,在實際執行中,COUNT(*)和COUNT(1)的執行時間可能略有差別,不過你還是可以把它倆的執行效率看成是相等的。

另外在 InnoDB 引擎中,如果采用COUNT()和COUNT(1)來統計數據行數,要盡量采用二級索引。因為主鍵采用的索引是聚簇索引,聚簇索引包含的信息多,明顯會大于二級索引(非聚簇索引)。對于COUNT()和COUNT(1)來說,它們不需要查找具體的行,只是統計行數,系統會自動采用占用空間更小的二級索引來進行統計。

然而如果想要查找具體的行,那么采用主鍵索引的效率更高。如果有多個二級索引,會使用 key_len 小的二級索引進行掃描。當沒有二級索引的時候,才會采用主鍵索引來進行統計。

這里我總結一下:

一般情況下,三者執行的效率為 COUNT()= COUNT(1)> COUNT(字段)。我們盡量使用COUNT(),當然如果你要統計的是某個字段的非空數據行數,則另當別論,畢竟比較執行效率的前提是結果一樣才可以。
如果要統計COUNT(),盡量在數據表上建立二級索引,系統會自動采用key_len小的二級索引進行掃描,這樣當我們使用SELECT COUNT()的時候效率就會提升,有時候可以提升幾倍甚至更高。
答疑 2
在 MySQL 中,LIMIT關鍵詞是最后執行的,如果可以確定只有一條結果,那么就起不到查詢優化的效果了吧,因為LIMIT是對最后的結果集過濾,如果結果集本來就只有一條,那就沒有什么用了。

解答
如果你可以確定結果集只有一條,那么加上LIMIT 1的時候,當找到一條結果的時候就不會繼續掃描了,這樣會加快查詢速度。這里指的查詢優化針對的是會掃描全表的 SQL 語句,如果數據表已經對字段建立了唯一索引,那么可以通過索引進行查詢,不會全表掃描的話,就不需要加上LIMIT 1了。

關于通配符的解釋
關于查詢語句中通配符的使用理解,我舉了一個查詢英雄名除了第一個字以外,包含“太”字的英雄都有誰的例子,使用的 SQL 語句是:

SQL> SELECT name FROM heros WHERE name LIKE '% 太 %'
)匹配任意一個字符,(%) 匹配大于等于 0 個任意字符。

所以通配符'_%太%'說明在第一個字符之后需要有“太”字,這里就不能匹配上“太乙真人”,但是可以匹配上“東皇太一”。如果數據表中有“太乙真人太太”,那么結果集中也可以匹配到。

另外,單獨的LIKE '%'無法查出 NULL 值,比如:SELECT * FROM heros WHERE role_assist LIKE '%'。

答疑 4
可以理解在 WHERE 條件字段上加索引,但是為什么在 ORDER BY 字段上還要加索引呢?這個時候已經通過 WHERE 條件過濾得到了數據,已經不需要再篩選過濾數據了,只需要根據字段排序就好了。

解答
在 MySQL 中,支持兩種排序方式,分別是 FileSort 和 Index 排序。在 Index 排序中,索引可以保證數據的有序性,不需要再進行排序,效率更高。而 FileSort 排序則一般在內存中進行排序,占用 CPU 較多。如果待排結果較大,會產生臨時文件 I/O 到磁盤進行排序的情況,效率較低。

所以使用 ORDER BY 子句時,應該盡量使用 Index 排序,避免使用 FileSort 排序。當然你可以使用 explain 來查看執行計劃,看下優化器是否采用索引進行排序。

優化建議:

SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中避免全表掃描,在 ORDER BY 子句避免使用 FileSort 排序。當然,某些情況下全表掃描,或者 FileSort 排序不一定比索引慢。但總的來說,我們還是要避免,以提高查詢效率。一般情況下,優化器會幫我們進行更好的選擇,當然我們也需要建立合理的索引。
盡量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用單索引列;如果不同就使用聯合索引。
無法使用 Index 時,需要對 FileSort 方式進行調優。
答疑 5
ORDER BY 是對分的組排序還是對分組中的記錄排序呢?

解答
ORDER BY 就是對記錄進行排序。如果你在 ORDER BY 前面用到了 GROUP BY,實際上這是一種分組的聚合方式,已經把一組的數據聚合成為了一條記錄,再進行排序的時候,相當于對分的組進行了排序。

答疑 6
請問下關于 SELECT 語句內部的執行步驟。

解答
一條完整的 SELECT 語句內部的執行順序是這樣的:

FROM 子句組裝數據(包括通過 ON 進行連接);
WHERE 子句進行條件篩選;
GROUP BY 分組 ;
使用聚集函數進行計算;
HAVING 篩選分組;
計算所有的表達式;
SELECT 的字段;
ORDER BY 排序;
LIMIT 篩選。
答疑 7
不太理解哪種情況下應該使用 EXISTS,哪種情況應該用 IN。選擇的標準是看能否使用表的索引嗎?

解答
索引是個前提,其實選擇與否還是要看表的大小。你可以將選擇的標準理解為小表驅動大表。在這種方式下效率是最高的。

比如下面這樣:

SELECT * FROM A WHERE cc IN (SELECT cc FROM B)
SELECT * FROM A WHERE EXISTS (SELECT cc FROM B WHERE B.cc=A.cc)
當 A 小于 B 時,用 EXISTS。因為 EXISTS 的實現,相當于外表循環,實現的邏輯類似于:

for i in A
for j in B
if j.cc == i.cc then ...
當 B 小于 A 時用 IN,因為實現的邏輯類似于:

for i in B
for j in A
if j.cc == i.cc then ...
哪個表小就用哪個表來驅動,A 表小就用 EXISTS,B 表小就用 IN。

關于存儲過程
答疑 1
在使用存儲過程聲明變量時,都支持哪些數據類型呢?

解答
不同的 DBMS 對數據類型的定義不同,你需要查詢相關的 DBMS 文檔。以 MySQL 為例,常見的數據類型可以分成三類,分別是數值類型、字符串類型和日期/時間類型。

答疑 2
“IN 參數必須在調用存儲過程時指定”的含義是什么?我查詢了 MySQL 的存儲過程定義,可以不包含 IN 參數。當存儲過程的定義語句里有 IN 參數時,存儲過程的語句中必須用到這個參數嗎?

解答
如果存儲過程定義了 IN 參數,就需要在調用的時候傳入。當然在定義存儲過程的時候,如果不指定參數類型,就默認是 IN 類型的參數。因為 IN 參數在存儲過程中是默認值,可以省略不寫。比如下面兩種定義方式都是一樣的:

CREATE PROCEDURE add_num(IN n INT)
CREATE PROCEDURE add_num(n INT)
在存儲過程中的語句里,不一定要用到 IN 參數,只是在調用的時候需要傳入這個。另外 IN 參數在存儲過程中進行了修改,也不會進行返回的。如果想要返回參數,需要使用 OUT,或者 INOUT 參數類型。

關于事務處理
答疑 1
如果INSERT INTO test SELECT '關羽';之后沒有執行 COMMIT,結果應該是空。但是我執行出來的結果是'關羽',為什么 ROLLBACK 沒有全部回滾?

代碼如下:

CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '關羽';
BEGIN;
INSERT INTO test SELECT '張飛';
INSERT INTO test SELECT '張飛';
ROLLBACK;
SELECT * FROM test;
解答
先解釋下連續 BEGIN 的情況。

在 MySQL 中 BEGIN 用于開啟事務,如果是連續 BEGIN,當開啟了第一個事務,還沒有進行 COMMIT 提交時,會直接進行第二個事務的 BEGIN,這時數據庫會隱式地 COMMIT 第一個事務,然后再進入到第二個事務。

為什么 ROLLBACK 沒有全部回滾呢?

因為 ROLLBACK 是針對當前事務的,在 BEGIN 之后已經開啟了第二個事務,當遇到 ROLLBACK 的時候,第二個事務都進行了回滾,也就得到了第一個事務執行之后的結果即“關羽”。

關于事務的 ACID,以及我們使用 COMMIT 和 ROLLBACK 來控制事務的時候,有一個容易出錯的地方。

在一個事務的執行過程中可能會失敗。遇到失敗的時候是進行回滾,還是將事務執行過程中已經成功操作的來進行提交,這個邏輯是需要開發者自己來控制的。

這里開發者可以決定,如果遇到了小錯誤是直接忽略,提交事務,還是遇到任何錯誤都進行回滾。如果我們強行進行 COMMIT,數據庫會將這個事務中成功的操作進行提交,它會認為你覺得已經是 ACID 了(就是你認為可以做 COMMIT 了,即使遇到了一些小問題也是可以忽略的)。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容