Mysql學習筆記九,優化篇,SQL優化

  • 優化SQL的一般步奏:
    • 查看當前數據庫狀態
1 show [session|global] status  --顯示統計結果
   mysqladmin extended-status --顯示統計結果
2 其中session: 當前會話,  global自數據庫上次啓動至今。 默認session。
3 Com_xxx 表示每個 xxx 語句執行的次數,我們通常比較關心的是以下幾個統計參數。

Com_select
執行 select 操作的次數,一次查詢只累加 1。
Com_insert
執行 INSERT 操作的次數, 對於批量插入的 INSERT 操作, 只累加一次。
Com_update
執行 UPDATE 操作的次數。
Com_delete
執行 DELETE 操作的次數。

上面這些參數對於所有存儲引擎的表操作都會進行累計。下面這幾個參數只是針對 InnoDB 存儲引擎的,累加的算法也略有不同。

Innodb_rows_
read
select 查詢返回的行數。
Innodb_rows_
inserted
執行 INSERT 操作插入的行數。
Innodb_rows_
updated
執行 UPDATE 操作更新的行數。
Innodb_rows_
deleted
執行 DELETE 操作刪除的行數。

通過以上幾個參數, 可以很容易地瞭解當前數據庫的應用是以插入更新爲主還是以查詢 操作爲主,以及各種類型的 SQL 大致的執行比例是多少。對於更新操作的計數,是對執行 次數的計數,不論提交還是回滾都會進行累加。 對於事務型的應用, 通過 Com_commit 和 Com_rollback 可以瞭解事務提交和回滾的情況, 對於回滾操作非常頻繁的數據庫,可能意味着應用編寫存在問題。
此外,以下幾個參數便於用戶瞭解數據庫的基本情況。

Connections
試圖連接 MySQL 服務器的次數。
Uptime
服務器工作時間。
Slow_queries
慢查詢的次數。

  • 定位執行效率低的sql語句
1 通過慢查詢日誌定位那些執行效率較低的 SQL 語句,用--log-slow-queries[=file_name]選 項啓動時, mysqld 寫一個包含所有執行時間超過 long_query_time 秒的 SQL 語句的日誌 文件
2 可以通過show processlist命令查看實時的sql執行情況。
mysql -udb2nor -p -e "show processlist">c.out --可以將查詢結果dump到文件中。
  • 通過explain分析低效sql的執行計劃
explain select * from t1 ,t2 where t1.a <> t2.id \G;  --打印執行計劃。

select_type
select的類型, 常見的取值有 SIMPLE (簡單表, 即不使用表連接 或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION 中的第二個或 者後面的查詢語句)、SUBQUERY(子查詢中的第一個 SELECT)
table
輸出結果集的表
type
表的連接類型,性能由好到差:
  1. system, 表中僅有一行,即常量表。
  2. const, 單表中最多有一個匹配行,例如 primary key 或者 unique index。
  3. eq_ref,對於前面的每一行,在此表中只查詢一條記錄,簡單來說,就是多表連接 中使用 primary key 或者 unique index。
  4. ref(與 eq_ref 類似, 區別在於不是使用 primary key 或者 unique index,而是使用普通的索引)
  5. ref_or_null(與 ref 類似,區別在於 條件中包含對 NULL 的查詢)
  6. index_merge(索引合併優化)
  7. unique_subquery(in 的後面是一個查詢主鍵字段的子查詢)
  8.  index_subquery (與 unique_subquery 類似, 區別在於 in 的後面是查詢非唯一索引字段的子查詢)
  9.  range (單表中的範圍查詢)
  10. index (對於前面的每一行, 都通過查詢索引來得到數據)
  11.  all (對於前面的每一行, 都通過全表掃描來得到數據)
possible_keys
表示查詢時,可能使用的索引
key
表示實際使用的索引
key_len
索引字段的長度
rows
掃描行的數量
Extra
執行情況的說明和描述
  • 確定問題並採取優化。
  1. 通過上面的步奏,定位到哪個SQL, 這個SQL出現了什麼問題。

  • 索引問題
 一. 使用索引:
  1. 查詢要使用索引最主要的條件是查詢條件中需要使用索引關鍵字,如果是多列索引,那 麼只有查詢條件使用了多列關鍵字最左邊的前綴時, 纔可以使用索引, 否則將不能使用索引。
  2. 對於創建的多列索引,只要查詢的條件中用到了最左邊的列,索引一般就會被使用。
  3. 對於使用 like 的查詢,後面如果是常量並且只有%號不在第一個字符,索引纔可能會 被使用。如果 like 後面跟的是一個列的名字,那麼索引也不會被使用。
  4. 如果對大的文本進行搜索,使用全文索引而不用使用 like ‘%…%’。
  5. 如果列名是索引,使用 column_name  is  null 將使用索引。

二.存在索引但不使用索引
  1. 如果 MySQL 估計使用索引比全表掃描更慢,則不使用索引。例如如果列 key_part1 均勻分佈在 1 和 100 之間,下列查詢中使用索引就不是很好:SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90;
  2. 如果使用 MEMORY/HEAP 表並且 where 條件中不使用“=”進行索引列,那麼 不會用到索引。heap 表只有在“=”的條件下才會使用索引。
  3. 用 or 分割開的條件, 如果 or 前的條件中的列有索引, 而後面的列中沒有索引, 那麼涉及到的索引都不會被用到。
  4. 不是索引列的第一部分。
  5. 如果 like 是以%開始。
  6. 如果列類型是字符串,那麼一定記得在 where 條件中把字符常量值用引號引 起來,否則的話即便這個列上有索引。

三.查看索引使用情況
  1. 通過命令: show status like 'Handler_read%' 查看

Handler_read_key
代表了一個行被索引值讀的 次數,很低的值表明增加索引得到的性能改善不高,因爲索引並不經常使用。
Handler_read_rnd_next
值高則意味着查詢運行低效,並且應該建立索引補救。這個值 的含義是在數據文件中讀下一行的請求數。如果正進行大量的表掃描, Handler_read_rnd_next 的值較高, 則通常說明表索引不正確或寫入的查詢沒有利用索引
 
  • 實用的優化技巧
    • 定期分析表和檢查表 : 
分析表
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
本語句用於分析和存儲表的關鍵字分佈, 分析的結果將可以使得系統得到準確的統計信 息,使得 SQL 能夠生成正確的執行計劃。如果用戶感覺實際執行計劃並不是預期的執行計 劃, 執行一次分析表可能會解決問題。 在分析期間, 使用一個讀取鎖定對錶進行鎖定。 這對 於 MyISAM, BDB 和 InnoDB 表有作用。對於MyISAM 表,本語句與使用 myisamchk -a 相當。
檢查表
CHECK TABLE tbl_name [, tbl_name] ... [option] ... option = {QUICK | FAST | MEDIUM | EXTENDED
| CHANGED}
1 檢查表的作用是檢查一個或多個表是否有錯誤。 CHECK TABLE 對 MyISAM 和 InnoDB 表有作用。
對於 MyISAM 表,關鍵字統計數據被更新。
2  可以用於檢查視圖。

    • 定期優化表

OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
如果已經刪除了表的一大部分,或者如果已經對含有可變長度行的表(含有 VARCHAR、 BLOB 或 TEXT 列的表) 進行了很多更改, 則應使用 OPTIMIZE TABLE 命令來進行表優化。 這個 命令可以將表中的空間碎片進行合併, 並且可以消除由於刪除或者更新造成的空間浪費, 但 OPTIMIZE TABLE 命令只對 MyISAM、BDB 和 InnoDB 表起作用
*  注意:ANALYZE、CHECK、OPTIMIZE 執行期間將對錶進行鎖定,因此一定注意要在數據庫不
繁忙的時候執行相關的操作。

  • 常用SQL的優化
    • 大批量插入數據 
MyISAM存儲引擎的表,可以使用alter table xx disable keys, alter table xx enable keys,打開關閉myisam表非唯一性索引的更新。
innodb優化方式:

導入的數據按照主鍵順序排列
 因爲 InnoDB 類型的表是按照主鍵的順序保存的, 所以將導入的數據按照主鍵的順 序排列,可以有效地提高導入數據的效率。
 SET UNIQUE_CHECKS
在導入數據前執行 SET UNIQUE_CHECKS=0,關閉唯一性校驗,在導入結束後執行 SET UNIQUE_CHECKS=1,恢復唯一性校驗,可以提高導入的效率。
 SET AUTOCOMMIT
如果應用使用自動提交的方式,建議在導入前執行 SET AUTOCOMMIT=0,關閉自 動提交,導入結束後再執行 SET AUTOCOMMIT=1,打開自動提交,也可以提高導入的效率。
    • 優化insert語句

使用多個值表的 INSERT 語句
insert into test values(1,2),(1,3),(1,4)…
INSERT DELAYED
如果從不同客戶插入很多行,能通過使用 INSERT DELAYED 語句得到更高的速度。 DELAYED 的含義是讓 INSERT 語句馬上執行,其實數據都被放在內存的隊列中,並沒有 真正寫入磁盤,這比每條語句分別插入要快的多;LOW_PRIORITY 剛好相反,在所有其 他用戶對錶的讀寫完後才進行插入;
數據存放
將索引文件和數據文件分在不同的磁盤上存放(利用建表中的選項)
bulk_insert_buffer_size
如果進行批量插入,可以增加 bulk_insert_buffer_size 變量值的方法來提高速度,但是, 這隻能對 MyISAM 表使用
LOAD DATA INFILE
當從一個文本文件裝載一個表時, 使用 LOAD DATA INFILE。 這通常比使用很多 INSERT 語 句快 20 倍。
    • 優化group by 
默認情況下, MySQL 對所有 GROUP BY col1, col2....的字段進行排序用戶想要避免排序結果的消耗,則可以指定 ORDER BY NULL  禁止排序:
select id from t2 group by id order by null;

    • 優化order by
MySQL 可以使用一個索引來滿足 ORDER BY 子句, 而不需要額外的排序。
WHERE 條件和 ORDER BY 使用相同的索引,並且 ORDER BY 的順序和索引順序相同,並且
ORDER BY 的字段都是升序或者都是降序:

這些SQL的排序可以使用索引
SELECT * FROM t1 ORDER BY key_part1,key_part2,... ;
SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
這些SQL的排序不能使用索引
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--order by 的字段混合 ASC 和 DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
--用於查詢行的關鍵字與 ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2;
--對不同的關鍵字使用 ORDER BY:
    • 優化or條件
對於含有 OR 的查詢子句, 如果要利用索引, 則 OR 之間的每個條件列都必須用到索引; 如果沒有索引,則應該考慮增加索引。
explain select * from t1 where t1.var = 'test' or t1.id = 4;--在var上沒有索引,在id上有索引。

    • 使用sql提示
SQL 提示(SQL HINT)是優化數據庫的一個重要手段,簡單來說就是在 SQL 語句中加入一些 人爲的提示來達到優化操作的目的。
如: SELECT SQL_BUFFER_RESULTS * FROM... 這個語句將強制 MySQL 生成一個臨時結果集。只要臨時結果集生成後,所有表上的鎖 定均被釋放。 這能在遇到表鎖定問題時或要花很長時間將結果傳給客戶端時有所幫助, 因爲
可以儘快釋放鎖資源。

USE INDEX
在查詢語句中表名的後面, 添加 USE INDEX 來提供希望 MySQL 去參考的索引列表, 就可 以讓 MySQL 不再考慮其他可用的索引。
 explain select * from t1 use index(t1_idx) where id =4; --建議mysql使用索引t1_idx
IGNORE INDEX
如果用戶只是單純地想讓 MySQL 忽略一個或者多個索引,則可以使用 IGNORE INDEX 作 爲 HINT。
explain select * from t1 ignore index(t1_idx) where id =4; --建議mysql忽略索引t1_idx
FORCE INDEX
爲強制 MySQL 使用一個特定的索引,可在查詢中使用 FORCE INDEX 作爲 HINT。
explain select * from t1 force index(t1_idx) where id =4;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章