mysql┃多個角度說明sql優化,讓你吊打面試官!

目錄mysql

  • 目錄
  • 前言
  • 正文
    • 1.表結構優化●
    • 1.1拆分字段
    • 1.2字段類型的選擇
    • 1.3字段類型大小的限制
    • 1.4合理的增長冗餘字段
    • 1.5新建字段必定要有默認值
    • 2.索引方面●
    • 2.1索引字段的選擇
    • 2.2利用好mysql支持的索引下推,覆蓋索引等功能
    • 2.3惟一索引和普通索引的選擇
    • 3.查詢語句方面●
    • 3.1避免索引失效
    • 3.2合理的書寫where條件字段順序
    • 3.3小表驅動大表
    • 3.4可使用force index()防止優化器選錯索引
    • 3.5事務語句順序
    • 4.分庫分表●
  • 結語

前言面試

mysql的優化是咱們常常都會提到的一個話題,也是重中之重,在不少大廠中會有專門的DBA來作這件事情,甚至更過度的是連應屆生的招聘崗位要求上都寫了須要懂一點sql優化,最近moon一直在寫關於mysql的文章,包括以前寫的索引相關,其實也都是爲了這篇文章作個鋪墊,因此你懂了嗎,今天我將從表結構、索引、查詢語句、分庫分表這四個維度來和你們聊聊,在工做中,怎麼進行sql優化?算法

正文sql

1.表結構優化●數據庫

優化sql最基本的條件時要有一張表,那麼咱們怎麼經過一張表來達到sql語句優化的目的呢?微信

1.1拆分字段mysql優化

咱們給出一個場景,想象本身是一家包子鋪老闆,天天都要結帳,因而確定會有一張帳戶餘額表,來記錄包子鋪的總資產併發

CREATE TABLE `accout_balance` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
   `account` varchar(64) NOT NULL DEFAULT '' COMMENT '帳戶',
   `balance` decimal(16,2) DEFAULT NULL COMMENT '餘額',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

但是後來包子鋪的生意作的愈來愈好,老闆陸陸續續開了幾百家店,後來竟然作成了全國連鎖店。函數

老闆很開心,可是他發現了一個問題,因爲生意太火爆,因此**每時每刻都會有人結帳,並且他們的系統愈來愈卡了,這是爲何?高併發

咱們來分析下:

每時每刻都會有人結帳,結帳後會修改accout_balance(帳戶餘額表)的balance(餘額)字段,因此這張表是一張熱表,而每一次修改都會開啓一個事務(update語句就至關於一個事務),因此在高併發的狀況下,問題顯而易見,針對同一行數據一個事務必需要等上另外一個事務執行完成以後才能執行本身的更新語句,因此愈來愈慢。(行鎖)

那麼咱們怎麼針對這種狀況來優化呢?moon的思路是控制併發度。

咱們目前的狀況是幾百家分店都會操做這同一行記錄,那麼咱們就能夠把這一行記錄分紅多行,也就是說,把帳戶的餘額分紅N份,這樣每次增長的時候選擇其中的一條記錄增長,衝突的機率也變成了以前的N分之一。

1.2字段類型的選擇

這種優化應該是比較常見的,咱們就長話短說。

好比針對UUID這種數據咱們能夠直接使用char(36)來做爲該字段的類型,或者說在表示boolean這種數據格式的時候,咱們就能夠直接使用tinyint(2)做爲咱們的字段類型,在咱們提早可預知字段的大小的時候,最好在類型上直接限制,避免浪費存儲空間佔用。

1.3字段類型大小的限制

這點在咱們公司的sql建表規範上就會明確寫到。

我這裏簡單的舉個例子,好比varchar,要使用varchar(255),這裏會有幾點考量:

.255恰好會消耗一個字節的存儲單元,可是256會致使消耗兩個字節的存儲單元。(這個針對UTF-8編碼)

.若是你要在varchar上創建索引,255會是一個徹底索引,而266以上只能用到最左前綴(MySQL的每一個單表中所建立的索引長度是有限制的,且對不一樣存儲引擎下的表有不一樣的限制。myisam表,單列索引,最大長度不能超過1000 bytes,不然會報警,可是建立成功,最終建立的是前綴索引。innodb表,單列索引,超過 767 bytes的,給出warning,最終索引建立成功,取前綴索引(取前 255 字符)),最左前綴的弊端就是沒法用到mysql提供的覆蓋索引的加速功能了。

.此外在onlineddl的時候,255如下能夠用inplace的方式,256須要rebuild。(Inplace方式:這是原生MySQL 5.5,以及innodb_plugin中提供的建立索引的方式。所謂Inplace,也就是索引建立在原表上直接進行,不會拷貝臨時表。相對於Copy Table方式,這是一個進步。 Inplace方式建立索引,建立過程當中,原表一樣可讀的,可是不可寫。 )

1.4合理的增長冗餘字段

在咱們剛開始學習mysql的時候,就會了解到數據庫的三範式,而在實際的使用過程當中,爲了性能,咱們也能夠拋棄數據庫的三範式

moon在以前的公司就有這樣的問題,一條sql語句要連5張表,正常一個查詢下來可能要1分多鐘,因此這條sql過重了,而在moon的細心觀察下發現,其中兩張表都只用到了其中一個字段,而後我就和DBA商量下將這兩個字段冗餘到了其它的兩個表中(業務有關聯),結果這條sql語句的執行時間就變成了十幾秒。

1.5新建字段必定要有默認值

好處以下

1.節省空間。

大致看上去,好像設置能夠爲空的時候更節省空間,但實際上,他比NOT NULL要多佔用一個bit的空間,用來判斷該字段是否爲空。

2.索引失效索引分裂

引用到null,索引會失效。還看到一個說法:空更新到非空時,若是空間不足,有可能會引發索引分裂。

3.減小因空值出現的計算錯誤等

count()在遇到null值時,這條記錄不會計算在內。

2.索引方面●

2.1索引字段的選擇

通常狀況下,能夠經過慢查詢日誌選擇出一些熱sql語句,給select條件後以及where條件後的字段加索引

2.2利用好mysql支持的索引下推,覆蓋索引等功能

    select a from user where  b = 5;

此時給a和b字段增長索引,這樣能夠利用mysql的覆蓋索引加速的功能,省去了回表的過程。

    select a from user where c = 5 and  d > 5;

此時給c和d字段增長索引,也能夠在判斷的時候也能利用到索引下推的功能,也就是說mysql在判斷c=5後,發現d也是索引,會直接找到d判斷d>5,若是不給d增長索引此時也是須要回表的。

其次對於組合索引: (a,b)這種索引一旦創建,就不須要再給a創建索引了,mysql的最左前綴原則支持組合索引或者字符串類型的索引最左N個單位的索引創建。反之,若是你此刻創建的是(a,b)索引,可是你的業務卻還須要一個b的單獨索引,那麼就能夠考慮給b單獨新建索引了。 若是如今你的表中只有a索引,可是業務需求須要(a,b)索引,必定要記得,先增長索引,而後再創建索引,否則可能會致使服務掛掉。moon有個朋友的同事,在新增索引的時候,選擇了先刪除,後增長,這樣就致使的在刪除後到新增前的這段空白期,出現了不少慢查詢sql,同時請求量有很大,業務沒法在短期內處理完,只能慢慢等待,最後致使服務掛掉。

2.3惟一索引和普通索引的選擇

若是咱們能在業務意義上保證某個字段是惟一的,而且這張表又是一個常常寫入數據的表,那麼這裏moon推薦你用普通索引,而不是惟一索引,緣由以下:

.在讀取數據的時候,普通索引在查到知足第一個條件的記錄後,會繼續查找下一個記錄,直到第一個不知足條件的記錄。而惟一索引,查找到第一個知足條件的記錄時,就直接中止了。這樣看來其實惟一索引更好,可是實際觀察來看,這種性能的差別微乎其微,何況咱們還能夠在查詢語句上用limit 1來限制。重點是第二點。

.在更新過程當中,普通索引的更新由於不用考慮惟一性,會將此次更新操做直接寫入change buffer中,以後會按期或者再次訪問到這個數據頁的時候持久化到磁盤當中。而惟一索引的更新不能用change bufer,緣由是要在表中判斷是否已經有該條記錄,因此會有一個將數據頁讀入內存的IO操做,而IO操做又是很消耗資源的。

3.查詢語句方面●

3.1避免索引失效

.最佳左前綴法則(帶頭索引不能死,中間索引不能斷

.不要在索引上作任何操做(計算、函數、自動/手動類型轉換),否則會致使索引失效而轉向全表掃描

.不能繼續使用索引中範圍條件(bettween、<、>、in等)右邊的列,如:

    select a from user where c > 5 and b = 4;

.索引字段上使用(!= 或者 < >)判斷時,會致使索引失效而轉向全表掃描

.索引字段上使用 is null / is not null 判斷時,會致使索引失效而轉向全表掃描。

.索引字段使用like以通配符開頭(‘%字符串’)時,會致使索引失效而轉向全表掃描,也是最左前綴原則。

.索引字段是字符串,但查詢時不加單引號,會致使索引失效而轉向全表掃描

3.2合理的書寫where條件字段順序

    這裏其實也是最左前綴原則。在一些後需維護開發工做中,能夠觀察表中的聯合索引,當你新寫的sql有where條件時,儘可能在where條件的書寫順序按照聯合索引的順序

3.3小表驅動大表

join查詢在有索引條件下,驅動表有索引不會使用到索引,被驅動表創建索引會使用到索引

MySQL 表關聯的算法是 Nest Loop Join,是經過驅動表的結果集做爲循環基礎數據,而後一條一條地經過該結果集中的數據做爲過濾條件到下一個表中查詢數據,而後合併結果。若是還有第三個參與Join,則再經過前兩個表的Join結果集做爲循環基礎數據,再一次經過循環查詢條件到第三個表中查詢數據,如此往復。因此,小表驅動大表所創建的鏈接次數也遠比大表驅動小表所創建的鏈接次數要小的多

能夠經過EXPLAIN分析來判斷在sql中誰是驅動表,EXPLAIN語句分析出來的第一行的表便是驅動表。

3.4可使用force index()防止優化器選錯索引

在咱們肯定要使用某個索引的時候可使用force index()強制只用某個索引,避免在某些狀況下優化器選擇錯誤致使查詢效率下降。

優化器選擇索引的目的,是找到一個最優的執行方案,並用最小的代價去執行語句。

優化器會結合是否使用臨時表、是否排序、掃描行數等因素進行綜合判斷。

固然是用force index 也是有弊端的,若是你的索引起生了變化,而你的sql語句沒有即便更改,那麼這裏就會報錯。

3.5事務語句順序

當開啓一個事務有多個語,而且其中有更新語句,要把更新語句放在事務的最後面,由於更新語句會上行鎖,而且釋放鎖的時間是這個事務結束,放在事務最後執行能夠有效縮短上鎖時間。

4.分庫分表●

 在以上你能作到優化的極致條件下,因爲數據量很大,可能仍是會面臨着慢查詢的狀況出現,那麼這時候咱們就要考慮分庫分表了。

moon這裏簡單的和你們舉個例子:

 一家作客服系統的公司,業務量很大,客戶不少,天天可能有上千萬的數據量,若是你將這些數據都放在一張表裏面,毫無疑問,會死的很慘。這時候咱們能夠考慮和業務相關的方式來進行分表,好比說你有10000家客戶,你能夠每一百家客戶放在一張表上,這樣平均下來一天該表可能只能幾十萬條數據,這樣是能夠接受的。可是時間久了,你會發現以前的數據都是沒有用的,客戶關心的都是最新產生的數據,那麼咱們就能夠分庫,將這些客戶不關心的數據放在這個冷庫中,以提升線上熱數據的查詢效率。

結語

mysql優化的路還很長,固然以上這也不是所有的優化方案,可是會基本覆蓋全部你在平常開發中能用到的優化小技巧,對於通常的面試官來講,足以吊打他了,可是我要提醒你的是,面對DBA,仍是能夠儘可能乖一點,嗯,言至於此~

文章首發於個人微信公衆號:moon聊技術,歡迎你們關注

下期見,老鐵們~

相關文章
相關標籤/搜索