SQL優化
通過show status瞭解各種sql執行的頻率
mysql> show status like 'Innodb_rows_%';
+----------------------+--------------+
| Variable_name | Value |
+----------------------+--------------+
| Innodb_rows_deleted | 163063 |
| Innodb_rows_inserted | 9974483 |
| Innodb_rows_read | 299261731677 |
| Innodb_rows_updated | 19791454 |
+----------------------+--------------+
4 rows in set (0.04 sec)
mysql> show status like 'Com_%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| Com_admin_commands | 0 |
| Com_assign_to_keycache | 0 |
| Com_alter_db | 0 |
| Com_alter_db_upgrade | 0 |
| Com_alter_event | 0 |
| Com_alter_function | 0 |
| Com_alter_procedure | 0 |
| Com_alter_server | 0 |
| Com_alter_table | 0 |
| Com_alter_tablespace | 0 |
| Com_alter_user | 0 |
| Com_analyze | 0 |
| .... | |
+---------------------------------+-------+
159 rows in set (0.18 sec)
- Com_select:執行SELECT操作的次效,一次查詢只累加1
- Com_insert:執行INSERT操作的次數,對於批費插入的INSERT操作,只累加一次
- Com_update:執行UPDATE操作的次數
- Com delete:執行DELETE操作的次數.
- Innodb_rows_read: SELECT i詢返回的行數
- Innodb_rows_mserted:執行INSERT操作插入的行數
- Inoodb_rows_dated:執行UPDATE操作更新的行教
- Innodb_rows_deleted:執行DELETE操作則除的行數
通過以上幾個參數,可以很容易地瞭解當前數據庫的應用是以插入更新爲主還是以査詢操作爲主,以及各種類型的SQL大致的執行比例是多少。對於更新操作的計數,是對執行次數?的計數,不論提交還是回滾都會進行累加。
定位執行效率較低的Sql語句
-
通過慢查詢日誌定位那些執行效率較低的SQL語句,-log-slow-queries[= file_name]選項啓動時,mysqld寫一個包含所有執行時間超過long_query_time秒的SQL語句的日誌文件
-
慢查詢日誌在査詢結束以後才記錄,所以在應用反映執行效率出現問題的時候查詢慢?查詢日誌並不能定位問題,可以使用show processlist命令查看當前MySQL在進行的線程,包?括魏程的狀態、是否鎖表等,可以實時地查看SQL的執行情況,同時對一些鎖表操作進行優化.
通過EXPLAIN分析低效SQL的執行計劃
通過以上步驟査詢到效率低的SQL語句後,可以通過EXPLAIN或者DESC命令獲取?MySQL如何執行SELECT語句的信息,包括在SELECT語句執行過程中表如何連接和連接的順序。
mysql> explain select * from fa_order limit 0,10; \G
+----+-------------+----------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------+
| 1 | SIMPLE | fa_order | ALL | NULL | NULL | NULL | NULL | 5076272 | |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------+
1 row in set (0.03 sec)
-
sdect_type:表示SELECT的類型,常見的取值有SIMPLE (簡單表,即不使用表連?接或者子查詢)、PRIMARY (主查詢,即外層的查詢)、UNION (UNION中的第二個或者後面?的查詢語句),SUBQUERY (子查詢中的第一個SELECT)等。
-
table:輸出詁果集的表.
-
possible_keys:表示查詢時可能使用的索引.
-
key:表示實際使用的索引
-
key_len:使用到索引字段的長度
-
rows:掃描行的數量
-
Extra:執行情況的說明和描迅 包舍不適合在其他列中顯示但是對執行計劃非常重要的額外信息
-
type:表示MySQL在表中找到所需行的方式,或者叫訪問類型。
+------+-------------+----------+------+---------------+---------------+------+
| ALL | index | range | ref | eq_ref | const, system | NULL |
+------+-------------+----------+------+---------------+---------------+------+
從左至右,性能由最差到最好。
① type = ALL,全表掃描,MySQL遍歷全妹找到匹配的行。
② type=index,索引全掃描,MySQL遍歷整個索引來査詢匹配的行。
③ type=range,索引範圍掃描,常見於V、<=^ >、x=、between等操作符
④ type=ref,使用非唯一索引掃描或唯一索引的前綴掃描,返回匹配某個單獨值的記錄行
⑤ type=eq_ref,類似ref,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只?有一條記錄匹配;簡單來說,就是多表連接中使用primary key或者unique index作爲關聯條件。
⑥ type=consVsystem,單表中最多有一個匹配行,査詢起來非常迅速,所以這個匹配行中的其他列的值可以被優化器在當前査詢中當作常量來處理,例如,根據主鍵primary key或者唯一索引unique index進行的査詢。
通過唯一索引uk_email訪問的時候,類型type爲const;而從我們構造的僅有一條記錄的?a表中檢索時,類型type就爲system。
explain extended輸出結果中多了 filtered字段,同時從warning的message字段能夠看到優?化器自動去除了 1=1恆成立的條件,也就是說優化器在改寫SQL時會自動去掉恆成立的條件。?在遇到複雜的SQL時,我們可以利用explain extended的結果來迅速地獲取一個更清晰易讀的SQL。
MySQL 5.1開始支持分區功能,同時explain命令也増加了對分區的支持。可以通過explain partitions命令査看SQL所訪問的分區。
索引優化
索引是數據庫優化中最常用也是最重要的手段之一,通過索引通常可以幫助用戶解決大多數的SQL性能問題。
索引是在MySQL的存儲引華層中實現的,而不是在服務器層實現的。所以每種存儲引擎?的索引都不一定完全相同,也不是所有的存儲引擎都支持所有的索引類型。MySQL目前提供了以下4種索引。
- B-Tree索引:景常見的索引類型,大部分引擎都支持B樹索引。
- HASH索引:只有Memory引擎支持,使用場景簡單。
- R-Tree索引(空間索引):空間索引是MylSAM的一個特珠索引類型,主要用於地理?空間數據類型,通常使用較少,不做特別介紹。
- Full-text (全丈索引):全文索引也是MylSAM的一個特珠索引類型,主要用於全文?索引,InnoDB從MySQL5.6版本開始提供對全文索引的支持。
mysql如何使用索引
B-Tree索引是最常見的索引,構造類似二叉樹,能根據鍵值提供一行或者一個行集的快速訪問,通常只需要很少的讀操作就可以找到正確的行。不過,需要注意&Tree索引中的B不代表二叉樹(binary),而是代表平衡樹(balanced)。B-Tree索引並不是一棵二叉樹。
mysql中能夠使用索引的典型場景
匹配全值(Match the full value),對索引中所有列都指定具體值,即是對索引中的所?有列都有等值匹配的條件。
mysql> explain select * from fa_order where order_no = 'stlad202006281753256428' and user_id = 5140595 ;
+----+-------------+----------+------+-----------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+-----------------+---------+---------+-------+------+-------------+
| 1 | SIMPLE | fa_order | ref | user_id,ordersn | ordersn | 243 | const | 1 | Using where |
+----+-------------+----------+------+-----------------+---------+---------+-------+------+-------------+
1 row in set (0.04 sec)
匹配值的範圍査詢(Match a range of values X對索引的值能夠進行範圍査找。
mysql> explain select * from fa_order where id >= 5277516 ;
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
| 1 | SIMPLE | fa_order | range | PRIMARY | PRIMARY | 8 | NULL | 2538137 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
1 row in set (0.04 sec)
mysql> explain select * from fa_order where user_id >= 5277516 ;
+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | fa_order | ALL | user_id | NULL | NULL | NULL | 5076275 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.04 sec)
注意:user_id 是普通索引,id是主鍵索引,id是range,user_id還是all???答:如果該id返回結果集有較多行,優化器會認爲不如直接掃描聚集索引來得快。畢竟非聚集索引需要再利用聚集索引定位數據獲得全量表,非聚集索引是指向聚集索引的,一般如果需要返回非聚集索引中不包括的字段,就需要再次掃描聚集索引。
匹配最左前綴(Match a leftmost prefix ),僅僅使用索引中的最左邊列進行査找。
僅僅對索引進行査詢(Index only query),當査詢的列都在索引的字段中時,査詢的效率更高
Extra部分變成了 Using index,也就意味着,現在直接訪問索引就足夠獲取到所需要的數據,不需要通過索引回表,Using index也就是平常說的覆蓋索引掃描。只訪問必須訪問的數據,?在一般情況下,減少不必要的數據訪問能夠提升效率。
匹配列前綴(Match a column prefix),僅僅使用索引中的第一列,並且只包含索引第一列的開頭一部分進行査找。
mysql> explain select * from fa_order where order_no like 'ydy_%';
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | fa_order | range | ordersn | ordersn | 243 | NULL | 336 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.07 sec)
Extra 值爲Using where 耘優化器需要通過索引回疝詢數據,Using index 表示査詢使用了覆蓋索引掃描。
如果列名是索引,那麼使用colunm name isnuU就會使用索引。
存在索引但不能使用使用索引的典型場景
① 以%開頭的LIKE査詢不能夠利用B-Tree索引,執行計劃中key的值爲NULL表示沒有使用索引。
② 數據類型出現隱式轉換的時候也不會使用索引,特別是當列類型是字符串,那麼一定記得在where條件中把字符常量值用引號引起來,否則即便這個列上有索引,MySQL也不會用到,因爲MySQL默認把輸入的常量值進行轉換以後才進行檢索。
③ 複合索引的情況下,假如査詢條件不包含索引列最左邊部分,即不滿足最左原則,是不會使用複合索引的。
④ 如果MySQL估計使用索引比全表掃描更慢,則不使用索引。
⑤ 用or分割開的條件,如果or前的條件中的列有索引,而後面的列中沒有索引,那麼涉及的索引都不會被用到。