MySQL優化總結

1.存儲引擎的選擇(MyISAM和Innodb)

存儲引擎:MySQL中的數據、索引以及其餘對象是如何存儲的,是一套文件系統的實現。mysql

5.1以前默認存儲引擎是MyISAM,5.1以後默認存儲引擎是Innodb。web

功能差別
區別 MyISAM Innodb
文件格式 數據和索引是分別存儲的,數據.MYD,索引.MYI 數據和索引是集中存儲的,.ibd
文件可否移動 能,一張表就對應.frm、MYD、MYI3個文件 否,由於關聯的還有data下的其它文件
記錄存儲順序 按記錄插入順序保存 按主鍵大小有序插入
空間碎片(刪除記錄並flush table 表名以後,表文件大小不變) 產生。定時整理:使用命令optimize table 表名實現 不產生
事務 不支持 支持
外鍵 不支持 支持
鎖支持 表級鎖定 行級鎖定、表級鎖定,鎖定力度小併發能力高
選擇依據

MyISAM引擎設計簡單,數據以緊密格式存儲,因此某些讀取場景下性能很好。sql

若是沒有特別的需求,使用默認的Innodb便可。數據庫

MyISAM:以讀寫插入爲主的應用程序,好比博客系統、新聞門戶網站。併發

Innodb:更新(刪除)操做頻率也高,或者要保證數據的完整性;併發量高,支持事務和外鍵保證數據完整性。好比OA自動化辦公系統。數據庫設計

官網建議

官方建議使用Innodb,上面只是告訴你們,數據引擎是能夠選擇,不過大多數狀況仍是不要選爲妙svg

2.字段設計

數據庫設計3大範式
  • 第一範式(確保每列保持原子性)函數

  • 第二範式(確保表中的每列都和主鍵相關)性能

  • 第三範式(確保每列都和主鍵列直接相關,而不是間接相關)學習

一般建議使用範式化設計,由於範式化一般會使得執行操做更快。但這並非絕對的,範式化也是有缺點的,一般須要關聯查詢,不只代價昂貴,也可能使一些索引策略無效。

因此,咱們有時須要混同範式化和反範式化,好比一個更新頻率低的字段能夠冗餘在表中,避免關聯查詢

單表字段不宜過多

建議最多30個之內
字段越多,會致使性能降低,而且增長開發難度(一眼望不盡的字段,咱們這些開發仔會頓時傻掉的)

使用小而簡單的合適數據類型

a.字符串類型

固定長度使用char,非定長使用varchar,並分配合適且足夠的空間

char在查詢時,會把末尾的空格去掉;

b.小數類型

通常狀況可使用float或double,佔用空間小,但存儲可能會損失精度

decimal可存儲精確小數,存儲財務數據或經度要求高時使用decimal

c.時間日期

datetime:

  • 範圍:1001年~9999年

  • 存儲:8個字節存儲,以YYYYMMDDHHMMSS的格式存儲

  • 時區:與時區無關

timestamp:

  • 範圍:1970年~2038年

  • 存儲:4個字節存儲,存儲以UTC格式保存,與UNIX時間戳相同

  • 時區:存儲時對當前的時區進行轉換,檢索時再轉換回當前的時區

1.一般儘可能使用timestamp,由於它佔用空間小,而且會自動進行時區轉換,無需關心地區時差

2.datetime和timestamp只能存儲最小顆粒度是秒,可使用BIGINT類型存儲微秒級別的時間戳

d.大數據 blob和text
blob和text是爲存儲很大的數據的而設計的字符串數據類型,但一般建議避免使用

MySQL會把每一個blob和text當作獨立的對象處理,存儲引擎存儲時會作特殊處理,當值太大,innoDB使用專門的外部存儲區域進行存儲,行內存儲指針,而後在外部存儲實際的值。這些都會致使嚴重的性能開銷

儘可能將列設置爲NOT NULL

a.可爲NULL的列佔用更多的存儲空間
b.可爲NULL的列,在使用索引和值比較時,mySQL須要作特殊的處理,損耗必定的性能

建議:一般最好指定列爲NOT NULL,除非真的須要存儲NULL值

儘可能使用整型作主鍵

a.整數類型一般是標識列最好的選擇,由於它們很快而且可使用AUTO_INCREMENT

b.應該避免使用字符串類型做爲標識列,由於它們很消耗空間,而且一般比數字類型慢

c.對於徹底"隨機"的字符串也須要多加註意。例如:MD5(),SHAI()或者UUID()產生的字符串。這些函數生成的新值也任意分佈在很大空間內,這會致使INSERT和一些SELECT語句很緩慢

索引

使用索引爲何快
  • 索引相對於數據自己,數據量小

  • 索引是有序的,能夠快速肯定數據位置

  • InnoDB的表示索引組織表,表數據的分佈按照主鍵排序

就比如書的目錄,想要找到某一個內容,直接看目錄即可找到對應的頁

索引的存儲結構

a.B+樹(具體的結構就不說了,本身去了解)
b.哈希(鍵值對的結構)

MySQL中的主鍵索引用的是B+樹結構,非主鍵索引能夠選擇B+樹或者哈希

一般建議使用B+樹索引
由於哈希索引缺點比較多:
1.沒法用於排序
2.沒法用於範圍查詢
3.數據量大時,可能會出現大量哈希碰撞,致使效率低下

索引的類型

按做用分類:

1.主鍵索引:不解釋,都知道

2.普通索引:沒有特殊限制,容許重複的值

3.惟一索引:不容許有重複的值,速度比普通索引略快

4.全文索引:用做全文搜索匹配,但基本用不上,只能索引英文單詞,並且操做代價很大

按數據存儲結構分類:

1.聚簇索引

定義:數據行的物理順序與列值(通常是主鍵的那一列)的邏輯順序相同,一個表中只能擁有一個彙集索引。

主鍵索引是聚簇索引,數據的存儲順序是和主鍵的順序相同的

2.非聚簇索引

定義:該索引中索引的邏輯順序與磁盤上行的物理存儲順序不一樣,一個表中能夠擁有多個非彙集索引。

聚簇索引之外的索引都是非彙集索引,細分爲普通索引、惟一索引、全文索引,它們也被稱爲二級索引。

以下圖<高性能MySQL> Innodb存儲數據和索引的關係

image

主鍵索引的葉子節點存儲的是"行指針",直接指向物理文件的數據行。
二級索引的葉子結點存儲的是主鍵值

覆蓋索引:可直接從非主鍵索引直接獲取數據無需回表的索引
好比:
假設t表有一個(clo1,clo2)的多列索引

select clo1,clo2 from t where clo = 1

那麼,使用這條sql查詢,可直接從(clo1,clo2)索引樹中獲取數據,無需回表查詢
所以咱們須要儘量的在select後只寫必要的查詢字段,以增長索引覆蓋的概率。

多列索引:使用多個列做爲索引,好比(clo1,clo2)
使用場景:當查詢中常用clo1和clo2做爲查詢條件時,可使用組合索引,這種索引會比單列索引更快

須要注意的是,多列索引的使用遵循最左索引原則
假設建立了多列索引index(A,B,C),那麼其實至關於建立了以下三個組合索引:
1.index(A,B,C)
2.index(A,B)
3.index(A)

這就是最左索引原則,就是從最左側開始組合。

索引優化

1.索引不是越多越好,索引是須要維護成本的

2.在鏈接字段上應該創建索引

3.儘可能選擇區分度高的列做爲索引,區分度count(distinct col)/count(*)表示字段不重複的比例,比例越大掃描的記錄數越少,狀態值、性別字段等區分度低的字段不適合建索引

4.幾個字段常常同時以AND方式出如今Where子句中,能夠創建複合索引,不然考慮單字段索引

5.把計算放到業務層而不是數據庫層

6.若是有 order by、group by 的場景,請注意利用索引的有序性。

  • order by 最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現     file_sort 的狀況,影響查詢性能。

例如對於語句 where a=? and b=? order by     c,能夠創建聯合索引(a,b,c)。

order     by 最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現     file_sort(外部排序) 的狀況,影響查詢性能。

  • 例如對於語句 where a=? and b=? order by     c,能夠創建聯合索引(a,b,c)。

  • 若是索引中有範圍查找,那麼索引有序性沒法利用,如 WHERE     a>10 ORDER BY b;索引(a,b)沒法排序。

可能致使沒法使用索引的狀況

1.is null 和 is not null

2.!= 和 <> (可用in代替)

3.「非獨立列」:索引列爲表達式的一部分或是函數的參數
例如:
表達式的一部分:select id from t where id +1 = 5
函數參數:select id from t where to_days(date_clo) >= 10

4.like查詢以%開頭

5.or (or兩邊的列都創建了索引則可使用索引)

6.類型不一致
若是列是字符串類型,傳入條件是必須用引號引發來,否則沒法使用索引
select * from tb1 where email = 999;

3.Sql優化建議

1.首先了解一下sql的執行順序,使咱們更好的優化

(1)FROM:數據從硬盤加載到數據緩衝區,方便對接下來的數據進行操做

(2)ON:join on實現多表鏈接查詢,先篩選on的條件,再鏈接表

(3)JOIN:將join兩邊的表根據on的條件鏈接

(4)WHERE:從基表或視圖中選擇知足條件的元組

(5)GROUP BY:分組,通常和聚合函數一塊兒使用

(6)HAVING:在元組的基礎上進行篩選,選出符合條件的元組(必須與GROUP BY連用)

(7)SELECT:查詢到得全部元組須要羅列的哪些列

(8)DISTINCT:去重

(9)UNION:將多個查詢結果合併

(10)ORDER BY:進行相應的排序

(11)LIMIT:顯示輸出一條數據記錄

  • join on實現多表鏈接查詢,推薦該種方式進行多表查詢,不使用子查詢(子查詢會建立臨時表,損耗性能)。

  • 避免使用HAVING篩選數據,而是使用where

  • ORDER BY後面的字段創建索引,利用索引的有序性排序,避免外部排序

  • 若是明確知道只有一條結果返回,limit 1 可以提升效率

2.超過三個表最好不要 join

3.避免 SELECT *,從數據庫裏讀出越多的數據,那麼查詢就會變得越慢

4.儘量的使用 NOT NULL列,可爲NULL的列佔用額外的空間,且在值比較和使用索引時須要特殊處理,影響性能

5.用exists、not exists和in、not in相互替代

原則是哪一個的子查詢產生的結果集小,就選哪一個

select * from t1 where x in (select y from t2)select * from t1 where exists (select null from t2 where y =x)

IN適合於外表大而內表小的狀況;exists適合於外表小而內表大的狀況

六、使用exists替代distinct

當提交一個包含一對多表信息(好比部門表和僱員表)的查詢時,避免在select子句中使用distinct,通常能夠考慮使用exists代替,exists使查詢更爲迅速,由於子查詢的條件一旦知足,立馬返回結果。

低效寫法:

select distinct dept_no,dept_name from dept d,emp e where d.dept_no=e.dept_no

高效寫法:

select dept_no,dept_name from dept d where  exists (select 'x' from emp e where e.dept_no=d.dept_no)

備註:其中x的意思是:由於exists只是看子查詢是否有結果返回,而不關心返回的什麼內容,所以建議寫一個常量,性能較高!

用exists的確能夠替代distinct,不過以上方案僅適用dept_no爲惟一主鍵的狀況,若是要去掉重複記錄,須要參照如下寫法:

select * from emp  where dept_no exists (select Max(dept_no)) from dept d, emp e where e.dept_no=d.dept_no group by d.dept_no)

七、避免隱式數據類型轉換

隱式數據類型轉換不能適用索引,致使全表掃描!t_tablename表的phonenumber字段爲varchar類型

如下代碼不符合規範:

select column1 into i_l_variable1 from t_tablename where phonenumber=18519722169;

應編寫以下:

select column1 into i_lvariable1 from t_tablename where phonenumber='18519722169';

8.分段查詢

在一些查詢頁面中,當用戶選擇的時間範圍過大,形成查詢緩慢。主要的緣由是掃描行數過多。這個時候能夠經過程序,分段進行查詢,循環遍歷,將結果合併處理進行展現。

4.Expalin 分析執行計劃

explain顯示了mysql如何使用索引來處理select語句以及鏈接表。能夠幫助選擇更好的索引和寫出更優化的查詢語句。
例:

explain SELECT user_name from sys_user where user_id <10

image

該語句鏈接類型爲range,使用主鍵索引進行了範圍查詢,估計掃描了100行數據
更多含義詳看下面表格從上可看出

標識符 含義
id SELECT識別符。這是SELECT的查詢序列號
select_type SELECT類型,能夠爲如下任何一種:
  • SIMPLE:簡單SELECT(不使用UNION或子查詢)

  • PRIMARY:最外面的SELECT

  • UNION:UNION中的第二個或後面的SELECT語句

  • DEPENDENT UNION:UNION中的第二個或後面的SELECT語句,取決於外面的查詢

  • UNION RESULT:UNION 的結果

  • SUBQUERY:子查詢中的第一個SELECT

  • DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決於外面的查詢

  • DERIVED:導出表的SELECT(FROM子句的子查詢)

|
| table | 輸出的行所引用的表 |
| type | 聯接類型。下面給出各類聯接類型,按照從最佳類型到最壞類型進行排序:

  • system:表僅有一行(=系統表)。這是const聯接類型的一個特例。

  • const:表最多有一個匹配行,它將在查詢開始時被讀取。由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數。const表很快,由於它們只讀取一次!

  • eq_ref:對於每一個來自於前面的表的行組合,從該表中讀取一行。這多是最好的聯接類型,除了const類型。

  • ref:對於每一個來自於前面的表的行組合,全部有匹配索引值的行將從這張表中讀取。

  • ref_or_null:該聯接類型如同ref,可是添加了MySQL能夠專門搜索包含NULL值的行。

  • index_merge:該聯接類型表示使用了索引合併優化方法。

  • unique_subquery:該類型替換了下面形式的IN子查詢的ref: value IN (SELECT primary_key FROM single_table WHERE some_expr)

  • unique_subquery是一個索引查找函數,能夠徹底替換子查詢,效率更高。

  • index_subquery:該聯接類型相似於unique_subquery。能夠替換IN子查詢,但只適合下列形式的子查詢中的非惟一索引: value IN (SELECT key_column FROM single_table WHERE some_expr)

  • range:只檢索給定範圍的行,使用一個索引來選擇行。

  • index:該聯接類型與ALL相同,除了只有索引樹被掃描。這一般比ALL快,由於索引文件一般比數據文件小。

  • ALL:對於每一個來自於先前的表的行組合,進行完整的表掃描。

    經常使用的類型有:ALL->index->range->ref->eq_ref->const->system(從左到右,性能從差到好,至少得達到range)

|
| possible_keys | 指出MySQL能使用哪一個索引在該表中找到行 |
| key | 顯示MySQL實際決定使用的鍵(索引)。若是沒有選擇索引,鍵是NULL。 |
| key_len | 顯示MySQL決定使用的鍵長度。若是鍵是NULL,則長度爲NULL。 |
| ref | 顯示使用哪一個列或常數與key一塊兒從表中選擇行。 |
| rows | 顯示MySQL認爲它執行查詢時必須檢查的行數。多行之間的數據相乘能夠估算要處理的行數。 |
| filtered | 顯示了經過條件過濾出的行數的百分比估計值。 |
| Extra | 該列包含MySQL解決查詢的詳細信息

  • Distinct:MySQL發現第1個匹配行後,中止爲當前的行組合搜索更多的行。

  • Not exists:MySQL可以對查詢進行LEFT JOIN優化,發現1個匹配LEFT JOIN標準的行後,再也不爲前面的的行組合在該表內檢查更多的行。

  • range checked for each record (index map: #):MySQL沒有發現好的可使用的索引,但發現若是來自前面的表的列值已知,可能部分索引可使用。

  • Using filesort:MySQL須要額外的一次傳遞,以找出如何按排序順序檢索行。

  • Using index:從只使用索引樹中的信息而不須要進一步搜索讀取實際的行來檢索表中的列信息。

  • Using temporary:爲了解決查詢,MySQL須要建立一個臨時表來容納結果。

  • Using where:WHERE 子句用於限制哪個行匹配下一個表或發送到客戶。

  • Using sort_union(…), Using union(…), Using intersect(…):這些函數說明如何爲index_merge聯接類型合併索引掃描。

  • Using index for group-by:相似於訪問表的Using index方式,Using index for group-by表示MySQL發現了一個索引,能夠用來查 詢GROUP BY或DISTINCT查詢的全部列,而不要額外搜索硬盤訪問實際的表。

若是以爲不錯,分享給你的朋友!

一個立志成大腿而天天努力奮鬥的年輕人

伴學習伴成長,成長之路你並不孤單!

 掃描二維碼,關注公衆號

相關文章
相關標籤/搜索