MySQL查詢優化

在分析性能欠佳的查詢時,應考慮:mysql

        1) 應用程序是否正獲取超過須要的數據,即訪問了過多的行或列。程序員

        2) Mysql服務器是否分析了超過須要的行。算法

 

        若是發現訪問的數據行數很大,而生成的結果中數據行不多,那麼能夠嘗試修改,好比使用覆蓋索引、更改架構或重寫查詢讓優化器能夠以優化的方式執行它。sql

 

         優化最終集中在減小IO,下降CPU,提升查詢速度。數據庫

         通常應用中數據庫一般是IO密集型的,大部分數據庫操做中超過90%的時間是由IO操做所佔用,因此減小IO訪問次數是SQL優化中首要考慮的因素。除了IO外,須要再考慮優化CPU的運算量。一般,ORDER BY、GROUP BY、DISTINCT和一些比較運算都是主要消耗CPU的地方。下降CPU計算也就成爲優化的重要目標。緩存

1. 查詢優化步驟

1)經過 show status和應用特色瞭解各類 SQL的執行頻率

       能夠經過 SHOW STATUS 提供的服務器狀態信息,或使用 mysqladmin extende d-status 命令得到。 SHOW STATUS 能夠根據須要顯示 session 級別的統計結果和 global級別的統計結果。服務器

        爲了近似於實時知道服務器性能,能夠週期性運行SHOW STATUS,而且和前一次的輸出進行比較。如:網絡

             Mysqladmin extended –r –i 10session

        由於輸出不少,能夠把結果導入到grep中,過濾掉本身不想看的變量,也可使用innotop或其餘工具(如mysqlreport)來檢查結果。一些值得監控的變量是:架構

             Bytes_received和Bytes_send:和服務器之間來往的流量。

             Com_*  :       服務器正在執行的命令

             Created_*:    在查詢執行期間建立的臨時表和文件

             Handler_*:    存儲引擎操做

             Select_*:      不一樣類型的聯接執行計劃

             Sort_*:          幾種排序信息

        同時用這種方法還能監視MySQL的內部操做,例如:鍵訪問的次數、爲MyISAM從磁盤上進行的鍵讀取、數據訪問率、爲InnoDB從磁盤上讀取的數據,等等。這有助於定位系統中實際的和潛在的瓶頸,而無需調查每個查詢。
       如下幾個參數是對 MyISAM 和 InnoDB 存儲引擎的計數:

         1. Com_select      執行 select 操做的次數,一次查詢只累加 1 ;

         2. Com_insert      執行 insert 操做的次數,對於批量插入的 insert 操做,只累加一次 ;

         3. Com_update    執行 update 操做的次數;

         4. Com_delete     執行 delete 操做的次數;

        如下幾個參數是針對 Innodb 存儲引擎計數,累加的算法略有不一樣:

         1. Innodb_rows_read select   查詢返回的行數;

         2. Innodb_rows_inserted       執行 Insert 操做插入的行數;

         3. Innodb_rows_updated       執行 update 操做更新的行數;

         4. Innodb_rows_deleted        執行 delete 操做刪除的行數;

        經過以上幾個參數,能夠了解到當前數據庫的應用是以插入更新爲主還 是以查詢操做爲主,以及各類類型的 SQL大體的執行比例。注意對更新操做的計數是針對執行次數的計數,不論提交仍是回滾都會累加。
        對於事務型的應用,經過 Com_commit 和 Com_rollback 能夠了解事務提交和回 滾的狀況,對於回滾操做很是頻繁的數據庫,可能意味着應用編寫存在問題。此外,如下幾個參數便於咱們瞭解數據庫的基本狀況:

         1. Connections       試圖鏈接 Mysql 服務器的次數
         2. Uptime               服務器工做時間
         3. Slow_queries     慢查詢的次數

 

        爲找到耗費了最多時間的工做,能夠將測試分析創建在任何適合的粒度上:能夠總體分析服務器,或者檢查單個查詢或批查詢。獲得信息包括:

         *  MySQL訪問得最多的數據。

         *  MySQL執行得最多的查詢的種類。

         *  MySQL停留時間最長的狀態

         *  MySQL用來執行查詢的使用得最頻繁的子系統

         *  MySQL查詢過程當中訪問的數據種類

         *  MySQL執行了多少種不一樣類型的活動,好比索引掃描

 

2)定位執行效率較低的SQL語句

        一旦肯定查詢只獲取了所須要的數據,那麼接下來不該檢查在生成查詢結果時是否檢查了過多數據,測量指標主要:執行時間、檢查行數、返回行數。

     能夠經過如下兩種方式定位執行效率較低的 SQL 語句:
     1).  能夠經過慢查詢日誌定位那些執行效率較低的 sql 語句

        用 --log-slow-queries[=file_name] 選項啓動時, mysqld 寫一個包含全部執行時間超過long_query_time 秒的 SQL 語句的日誌文件。

 

        測量的三個指標(執行時間、檢查行數、返回行數)都被寫入了慢查詢日誌,故慢查詢日誌是檢索查找過多數據查詢的最佳方式。

        注意:執行時間在不一樣負載下表現是不同的;在理想狀況下,返回的行和檢查的行應該是同樣的,但其實是不可能的,例:使用聯接查詢時就需訪問更多的行來產生一行輸出。一般說來,檢查的行和返回的行之間的比率一般較小,在1:1到10:1之間。


     2).  使用 show processlist查看當前MySQL的線程

         慢查詢日誌在查詢結束之後才紀錄,因此在應用反映執行效率出現問題的時候查詢慢查詢日誌並不能有效定位問題,可使用 show processlist 命令查看當前 MySQL 在進行的線程,包括線程的狀態,是否鎖表等等,它不只能夠顯示哪一種查詢正在執行,也能看到鏈接的狀態。一些因素,好比大量鏈接處於鎖定狀態,是瓶頸的明顯線索。。

 

3)經過EXPLAIN 分析低效 SQL的執行計劃:

       經過以上步驟查詢到效率低的 SQL 後,咱們能夠經過 explain 或者 desc 獲取MySQL 如何執行 SELECT 語句的信息,包括 select 語句執行過程表如何鏈接和鏈接 的次序。

 

        經過EXPLAIN來探知訪問類型,訪問在掃描表、索引、範圍和常量時的速度是不同的。若是沒有獲得好的訪問類型,那麼最好的解決辦法是加一個索引。

        一般說來,MySQL會在3種狀況下使用where子句,從最好到最壞依次是:

        *  對索引查找應用where子句來消除不匹配的行,這發生在存儲引擎層;

        *  使用覆蓋索引來避免訪問行(」Using Index’),而且從索引取得數據後過濾掉不匹配的行。這發生在服務器層

        *  從表中檢索出數據,而後過濾掉不匹配的行(‘Using Where’)。這發生在服務器端而且要求在過濾以前讀取這些行。

 

 

2. MySQL索引

1) mysql如何使用索引    

        索引用於快速找出在某個列中有一特定值的行。對相關列使用索引是提升SELECT 操做性能的最佳途徑。
       查詢要使用索引最主要的條件是查詢條件中須要使用索引關鍵字,若是是多列索引,那麼只有查詢條件使用了多列關鍵字最左邊的前綴時(前綴索引),纔可使用索引,不然將不能使用索引。

       下列狀況下, Mysql 不會使用已有的索引:
      一、若是 mysql 估計使用索引比全表掃描更慢,則不使用索引。例如:若是 key_part 1均勻分佈在 1 和 100 之間,下列查詢中使用索引就不是很好:
               SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90
      二、若是使用 heap 表而且 where 條件中不用=索引列,其餘 > 、 < 、 >= 、 <= 均不使 用索引(MyISAM和innodb表使用索引);

     三、查詢條件裏使用了函數(WHERE DAY(column) = …)或索引字段是表達式的一部分,則不會使用索引。

      四、使用or分割的條件,若是or前的條件中的列有索引,後面的列中沒有索引,那麼涉及到的索引都不會使用。
      五、若是建立複合索引,若是條件中使用的列不是索引列的第一部分;(不是前綴索引)
      六、比較操做符LIKE和REGEXP的搜索模板的第一個字符是通配符,如 like 是以%開始時,不使用索引。
      七、對 where 後邊條件爲字符串的必定要加引號,字符串若是爲數字 mysql 會自動轉爲字符串,可是不使用索引。

 

2)查看索引使用狀況

        經過下面幾個參數來了解索引使用狀況:

              Handler_read_key                    請求數字基於鍵讀行。

              Handler_read_next                   請求讀入基於一個鍵的一行的次數。

        若是索引正在工做, Handler_read_key 的值將很高,這個值表明了一個行被索引值讀的次數,很低的值代表增長索引獲得的性能改善不高,由於索引並不常常使 用。
        Handler_read_rnd_next 的值高則意味着查詢運行低效,而且應該創建索引補救。這個值的含義是在數據文件中讀下一行的請求數。若是你正進行大量的表掃描,該值較高。一般說明表索引不正確或寫入的查詢沒有利用索引。

 

3.查詢語句分析

1)使用SHOW STATUS分析查詢

        咱們能夠經過FLUSH STATUS和SHOW SESSION STATUS相結合來分析查詢或批處理查詢。這是一種優化查詢的好辦法:

        首先,運行FLUSH STATUS把會話狀態變量設置爲零,這樣就能夠知道MySQL執行查詢時作了多少工做:mysql > FLUSH STATUS;

        接下來運行查詢。咱們添上了SQL_NO_CACHE,這樣MySQL不會從查詢緩存中取得查詢結果。

        首先,運行FLUSH STATUS把會話狀態變量設置爲零,這樣就能夠知道MySQL執行查詢時作了多少工做:mysql > FLUSH STATUS;

        接下來運行查詢。咱們添上了SQL_NO_CACHE,這樣MySQL不會從查詢緩存中取得查詢結果:

mysql> select sql_no_cache id, count(*) from tb inner join tb1 using(id) group by id order by count(*) desc;

4 rows in set (0.72 sec)

        如今來分析這條語句的運行?先看看服務器選擇的查詢計劃:

mysql> show session status like 'select%';

+------------------------+-------+

| Variable_name          | Value |

+------------------------+-------+

| Select_full_join       | 0     |

| Select_full_range_join | 0     |

| Select_range           | 0     |

| Select_range_check     | 0     |

| Select_scan            | 2     |

+------------------------+-------+

5 rows in set (0.00 sec)

        看上去MySQL進行了全表掃描。若是查詢涉及了多張表,有幾個變量的值就會大於零。例如,若是MySQL在後續表中進行了範圍掃描,以尋找匹配行,select_full_range_join變會有值,甚至還能夠查看查詢執行的低層次存儲引擎操做:

mysql> show session status like 'Handler%';

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| Handler_commit             | 1     |

| Handler_delete             | 0     |

| Handler_discover           | 0     |

| Handler_prepare            | 0     |

| Handler_read_first         | 1     |                      ---  請求讀入表中第一行的次數。

| Handler_read_key           | 12    |                    ---請求數字基於鍵讀行。

| Handler_read_next          | 0     |                     ---請求讀入基於一個鍵的一行的次數

| Handler_read_prev          | 0     |

| Handler_read_rnd           | 4     |                     ---請求讀入基於一個固定位置的一行的次數

| Handler_read_rnd_next      | 14    |

| Handler_rollback           | 0     |

| Handler_savepoint          | 0     |

| Handler_savepoint_rollback | 0     |

| Handler_update             | 0     |

| Handler_write              | 7     |

+----------------------------+-------+

15 rows in set (0.02 sec)

         「讀(read)」操做的值很高,意味着MySQL須要掃描多個表才能知足查詢須要。一般,若是MySQL只對一個表使用了全表掃描,咱們就會看到Handler_read_rn_next的值較高,而且Handler_read_rnd是零。

         在這個例子中,多個非零值意味着MySQL必須使用臨時表來知足GROUP BY和ORDER BY子句,這是Handler_write和Handler_update不爲零的緣由:MySQL假定寫入臨時表,掃描它並進行排序,而後再次進行掃描,輸出排序後的結果。

        再來看看MySQL爲排序作了些什麼:

mysql> show session status like 'Sort%';

+-------------------+-------+

| Variable_name     | Value |

+-------------------+-------+

| Sort_merge_passes | 0     |

| Sort_range        | 0     |

| Sort_rows         | 4     |

| Sort_scan         | 1     |

+-------------------+-------+

4 rows in set (0.01 sec)

        正如咱們猜想那樣,MySQL經過掃描包含輸出中全部行的臨時表進行排序。若是值多於4行,咱們懷疑它在查詢執行的過程當中在別的地方進行了排序。還能看到MySQL爲查詢建立了多少臨時表:

mysql> show session status like 'created%';

+-------------------------+-------+

| Variable_name           | Value |

+-------------------------+-------+

| Created_tmp_disk_tables | 0     |

| Created_tmp_files       | 0     |

| Created_tmp_tables      | 2     |

+-------------------------+-------+

3 rows in set (0.00 sec)

         Create_tmp_disk_tables爲0表示不須要使用磁盤上的臨時表。

         咱們能夠經過將命令運行兩次,並將第2次結果減去第1次結果來計算開銷,獲得精確的結果。這樣咱們就能夠知曉MySQL在執行查詢的過程當中作了多少工做。這將是咱們進行優化的基礎。

 

2)使用SHOW PROFILE分析查詢

       在默認狀況下,分析是關閉的,可是能夠在會話的層面打開。打開它會讓服務器收集用於執行查詢的資源信息。在開始收集統計信息以前,須要把分析變量設置爲1:

                   Mysql>set profiling = 1;

       如今運行查詢:

mysql> select sql_no_cache id, count(*) from tb inner join tb1 using(id) group by id order by count(*) desc;

+----+----------+

| id | count(*) |

+----+----------+

|  1 |        1 |

|  2 |        1 |

|  3 |        1 |

|  4 |        1 |

+----+----------+

4 rows in set (0.00 sec)

        查詢的分析數據被保存在會話中。使用SHOW PROFILES查看已經被分析事後查詢:

mysql> show profiles\G;

*************************** 1. row ***************************

Query_ID: 1

Duration: 0.00242825

   Query: select sql_no_cache id, count(*) from tb inner join tb1 using(id) group by id order by count(*) desc

1 row in set (0.00 sec)

 

ERROR:

No query specified

 

        可使用SHOW PROFILE命令取得被保存下來的分析數據。若是不加任何參數,它就會顯示最近一個命令的狀態值和運行時間:

mysql> show profile;

+----------------------+----------+

| Status               | Duration |

+----------------------+----------+

| starting             | 0.001015 |

| Opening tables       | 0.000042 |

| System lock          | 0.000013 |

| Table lock           | 0.000024 |

| init                 | 0.000097 |

| optimizing           | 0.000027 |

| statistics           | 0.000092 |

| preparing            | 0.000036 |

| Creating tmp table   | 0.000261 |

| executing            | 0.000010 |

| Copying to tmp table | 0.000288 |

| Sorting result       | 0.000093 |

| Sending data         | 0.000048 |

| end                  | 0.000006 |

| removing tmp table   | 0.000036 |

| end                  | 0.000008 |

| query end            | 0.000010 |

| freeing items        | 0.000202 |

| logging slow query   | 0.000011 |

| logging slow query   | 0.000098 |

| cleaning up          | 0.000013 |

+----------------------+----------+

21 rows in set (0.00 sec)

 

        每一行表明了進程的一種變化,以及在這種狀態停留的時間。Status列和SHOW FULL PROCESSLIST輸出的State列是對應的。它的值來自於thd->proc_info變量,所以能夠直接看到MySQL內部的值。儘管它們的名字都很直觀,也不難理解,可是這些變量仍是能夠從MySQL的手冊中找到。

        能夠從SHOW PROFILES的輸出中獲得特定的Query_ID,而且進行指定的分析,而且還能夠定義輸出的其他列。例如,爲了解執行查詢時用戶的CPU使用率,可使用下面的命令:

               Mysql> SHOW PROFILE CPU FOR QUERY 1;

        SHOW PROFILE很好地揭示了服務器執行查詢時所作的事情,而且有助於理解查詢在操做上花費時間。它的侷限是未實現的特性,不能查看和分析其餘聯接的查詢,以及因爲分析帶來的開銷。

 

4.MySQL查詢優化器的限制

        1)關聯子查詢(Correlated Subqueries):MySQL有時把子查詢優化得不好,最差的就是在Where子句中使用IN。優化語句須要考慮到執行順序和緩存:從裏向外執行查詢是一種優化方式 ,緩存內部查詢的結果是另一種方式。重寫查詢時應考慮兼顧這兩方面。

        但MySQL不會老是把關聯子查詢優化得不好,好比Exists在邏輯上表達「有一個匹配」概念,它不會產生任何重複的行,也可以避免使用GROUP BY和DISTINCT操做,有時子查詢會比聯接快得多。對待子查詢不能有絕對的態度,應該用測試來證實。

 

        2)聯合的限制:MySQL有時不能把UNION外的一些條件「下推」到UNION的內部,而這些外部條件原本用於限制結果或者產生優化。具體應該把LIMIT子句或ORDER BY子句添加到UNION內部的每個子句上。

 

        3)索引合併優化:索引合併算法能夠在查詢中對一個表使用多個索引。查詢能夠同時掃描多個索引,而且合併結果,這種算法有3種變體,分別是:對OR取並集、對AND取交集、對AND和OR的組合並集。但有時這種算法的緩衝、排序和合並操做使用了大量的CPU和內存資源,對於沒有足夠區分性的索引,並行掃描會返回大量須要合併操做的列,這種狀況就更容易發生。

       若是查詢由於優化器的限制而運行得很慢,那麼能夠經過IGNORE INDEX命令禁止一些索引,或使用老的UNION策略。

 

        4)相等傳遞:優化器經過把相應的列拷貝到相關的表裏來共享列表。這一般是有用的,由於它讓優化器和執行引擎有更多的機會選擇執行IN操做的時機。可是若是這個列很是大,它就可能致使較慢的優化和執行。

 

        5)並行執行:MySQL不能在多個CPU上並行執行一個查詢。

 

        6)哈希聯接(Hash Join):MySQL還不能真正執行哈希聯接。

 

        7)鬆散索引掃描(Loose Index Scan):MySQL不支持鬆散索引掃描,即掃描不連續的索引。MySQL索引掃描一般都須要一個肯定的起點和終點,即便查詢只須要其中一些不連續的行,MySQL也會掃描起點到終點範圍內的全部行。

        例:假設某表在列(a,b)上有索引,要執行查詢:select … from tb1 where b between 2 and 3;由於索引是從a開始,可是Where子句中沒有列a,MySQL將會全表掃描而且去掉不匹配的行。Explain中」Using Index for group-by」表示查詢使用了鬆散索引掃描。

 

        8)Min()和Max():MySQL不能很好地優化MIN()和MAX()。

 

        9)對同一個表進行SELECT和UPDATE:MySQL不會讓你在對一個表進行UPDATE的同時運行SELECT。一個變通的方式是衍生表(臨時表),這樣能夠有效地處理兩個查詢:一是在子查詢內部使用SELECT,二是使用表和子查詢的結果進行聯接,而後進行更新:

             mysql> UPDATE tb1 INNER JOIN(

                              SELECT type, count(*) AS cnt FROM tb1 GROUP BY type) AS der USING(type)

                               SET tb1.cnt = der.cnt;

 

MySQL可以處理的一些優化類型是:

         》對聯接中的表從新排序

         》對外聯接轉換成內聯接

         》代數等價法則:如5=5 AND a > 5等價於a>5

         》優化COUNT()、MIN()和MAX()

         》計算和減小常量表達式

         》覆蓋索引

         》子查詢優化

         》儘早中止:一旦知足查詢或某個步驟的條件,MySQL就會當即中止處理該查詢或該步驟

         》相等傳遞:

         》比較IN()裏面的數據:MySQL會對IN()裏面的數據進行排序,而後用二分法查找某個值是否在列表中。這個算法效率是O(logn),而等同的OR 子句的效率是O(N),在列表很大的時候,OR子句會慢得多。

 

5. 具體優化查詢語句

1. 應儘可能避免全表掃描

    對查詢進行優化,應儘可能避免全表掃描,首先應考慮在 where 及 order by 涉及的列上創建索引。相對於使用給定的索引,全表掃描將很是耗時。能夠嘗試下面的技巧以免優化器錯選了表掃描:

       *  使用ANALYZE TABLEtbl_name爲掃描的表更新關鍵字分佈。

       *  對掃描的表使用FORCE INDEX。

        SELECT * FROM t1, t2 FORCE INDEX (index_for_column)  WHERE t1.col_name=t2.col_name;

       *  用--max-seeks-for-key=1000選項啓動MySQL或者使用SET max_seeks_for_key=1000告知優化器掃描不會超過1,000次關鍵字搜索。

 

       雖然對錶創建了索引,但表查詢仍然可能會使用全表索引。

 1)  應儘可能避免在 where 子句中對字段進行 null 值判斷

     任何在where子句中使用is null或is not null的語句優化器都是不容許使用索引,相反會進行全表掃描,如:

            select fa from tb where fb is null

        NULL對於大多數數據庫都須要特殊處理,MySQL也不例外,它須要更多的代碼,更多的檢查和特殊的索引邏輯,只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。因此咱們在數據庫設計時不要讓字段的默認值爲NULL。

 

 2)  應儘可能避免在 where 子句中使用!=或<>操做符

     一般MySQL只對<,<=,=,>,>=,BETWEEN,IN,以及某些時候的LIKE纔會使用索引。若是查詢條件中存在!=或<>符號時,MySQL將沒法使用索引而進行全表掃描。

     對於LIKE語句,只有在不是以通配符(%或者_)開頭的查詢下才會使用索引。例如:
              SELECT fa FROM  tb WHERE fb LIKE 'Mich%'; #  這個查詢將使用索引,
              SELECT fa FROM  tb WHERE fb  LIKE '%ike';   #這個查詢不會使用索引。

      對於TEXT類型,若要提升效率,可考慮全文檢索。

 

 3)  應儘可能避免在 where 子句中使用 or 來鏈接條件

       WHERE語句中使用OR,且沒有使用覆蓋索引,會進行全表掃描。如:

                  select id from t where num=10 or num=20

        能夠 使用UNION合併查詢:

                 select id from t where num=10 union all select id from t where num=20

       在某些狀況下,or條件能夠避免全表掃描的:必須全部的or條件都是獨立索引。

 

 4)  慎用in 和 not in,不然會致使全表掃描

       MySQL索引能很好地匹配匹配範圍值,但這也只會使用索引第一列,同時也不能跳過索引中的列。對於連續的數值,能用 between 就不要用 in 了:

                 select id from t where num between 1 and 3

      使用BETWEEN語句時也需注意:MySQL不支持鬆散索引掃描,即掃描不連續的索引。MySQL索引掃描一般都須要一個肯定的起點和終點,即便查詢只須要其中一些不連續的行,MySQL也會掃描起點到終點範圍內的全部行。

        例:假設某表在列(a,b)上有索引,要執行查詢:select … from tb1 where b between 2 and 3;由於索引是從a開始,可是Where子句中沒有列a,MySQL將會全表掃描而且去掉不匹配的行

 

 5)   若是在 where 子句中使用參數,也會致使全表掃描。

        由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描:

               select id from t where num=@num

       能夠改成強制查詢使用索引: select id from t with(index(索引名)) where num=@num

 

 6)   應儘可能避免在 where 子句中對字段進行表達式操做。

       這將致使MySQL放棄使用索引而進行全表掃描。如:

                  select id from t where num/2=100

       該查詢不會使用索引。MySQL不會幫你求解方程,應該養成簡化WHERE子句的習慣,這樣就會把被索引的列單獨放在比較運算符的一邊,不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算。上句可改成:

                  select id from t where num=100*2 或者selece id from t where num=200;

 

7)  應儘可能避免在where子句中對字段進行函數操做,

       這一樣會致使MySQL放棄使用索引而進行全表掃描。如:

                 SELECT … WHERE TO_DAYS(CURRENT_DATE) – TO_DAYS(date_col) <= 10;

        這個查詢將會查找date_col值離今天不超過10天的全部行,可是它不會使用索引,由於使用了TO_DAYS()函數。下面是一種較好的方式:

                 SELECT … WHERE date_col >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY) ;

       使用CURRENT_DATE將會阻止查詢緩存把結果緩存起來,能夠用常量替換CURRENT_DATE的值對上條語句進行改進:

                 SELECT … WHERE date_col >=DATE_SUB(‘2008-01-17’, INTERVAL 10 DAY);

 

8)  索引字段不是複合索引的前綴索引

         一般MySQL索引對於如下類型的查詢有用(如下針對B-Tree樹索引):

          * 匹配全名:    全鍵值匹配指和索引中的全部列匹配。

          * 匹配最左前綴:這僅僅適用了索引中的第一列。

          * 匹配列前綴:能夠匹配某列的值的開頭部分

         若是查找沒有從索引列的最左邊開始,它就沒有什麼用處。若是該索引是複合索引,那麼必須使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用,而且應儘量的讓字段順序與索引順序相一致。例如在表tb上存在索引(fa, fb, fc),這種索引不能幫助查詢存在fb查詢條件同時卻沒有定義fa條件的數據,即這種查詢不能使用上索引(fa, fb, fc)。

        一樣查詢條件不能跳過索引中的列。一樣以上面表tb的索引(fa, fb, fc),索引也不能幫助查詢指定fa、fc條件同時缺沒有定義fb查詢條件的數據。

        另外,存儲引擎也不能優化訪問任何在第一個範圍條件右邊的列。例如表tb上的索引(fa,fb,fc),若是查詢形如WHERE fa=’….’ AND fb LIKE ‘x%’ AND fc=’….’,那麼訪問就只能使用索引的頭兩列(fa, fb),由於LIKE是範圍條件。

        這些侷限都和列順序有關,因此列順序極端重要了。對於高性能應用程序,也許要針對相同列以不一樣順序建立多個索引,以知足程序要求。

        這就是說,(A,B)上的索引能被當成(A)上的索引。(這種多餘只適合B-Tree索引)。而(B,A)上的索引就不會是多餘的,(B)上的索引也不是,由於列B不是列(A,B)的最左前綴。

 

2. 其餘一些注意優化

1)  不要寫一些沒有意義的查詢,

        如生成一個空表結構:

                 select col1, col2 into #t from t where 1=0

        上面這條語句不會返回任何結果集,可是會消耗系統資源的,應改用: create table #t(...)

 

2)  不少時候用 exists 代替 in 是一個好的選擇:

        EXISTS用於檢查子查詢是否至少會返回一行數據,用於檢測行的存在,該子查詢實際上並不返回任何數據,而是返回值True或False。EXISTS在邏輯上表達「有一個匹配」概念,它不會產生任何重複的行,也可以避免使用GROUP BY和DISTINCT操做:

       select num from a where num in(select num from b)

                用下面的語句替換:

       select num from a where exists(select 1 from b where num=a.num)

       關於EXISTS與IN查詢的效率,能夠網上找‘mysql in和exists性能比較和使用’查看。具體以測試結果爲準。

 

3)  並非全部索引對查詢都有效,

       SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那麼即便在sex上建了索引也對查詢效率起不了做用。一般認爲在這樣字段上不適合於創建索引。

 

4)  索引並非越多越好

       索引當然能夠提升相應的 select 的效率,但同時也下降了 insert 及 update 的效率,由於 insert 或 update 時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

        在任何可能的地方,首先應試着擴展索引,而不是新增索引。一般維護一個多列索引要比維護多個單列索引容易。

 

5)  應儘量的避免更新 clustered 索引數據列

       由於 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新 clustered 索引數據列,那麼須要考慮是否應將該索引建爲 clustered 索引。

 

6)  儘可能使用數字型字段

      若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接時會逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。

      特別是在做關鍵字段的類型選擇上,要同時考慮存儲類型和MySQL如何對它們進行計算和比較。

 

7)  儘量的使用 varchar/nvarchar 代替 char/nchar

         由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。

 

8)   最好不要使用"*"返回全部: select * from t

     若要利用覆蓋索引,則應用具體的字段列表代替「*」,不要返回用不到的任何字段。

 

3. 臨時表的問題

臨時表通常都不多用,通常是程序中動態建立或者由MySQL內部根據SQL執行計劃須要時建立。

內存表則大多數是當Cache用,隨着memcache、NoSQL的流行,內存表也愈來愈少使用了。

1)  避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。

       即使在刪除臨時表數據時,建議使用TRUNCATE TABLE 替代DELETE操做。

2)  在新建臨時表時,若是一次性插入數據量很大,那麼可使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先create table,而後insert。

3)   若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先 truncate table ,而後 drop table 這樣能夠避免系統表的較長時間鎖定。

使用EXPLAIN分析查詢語句時,extra列顯示「using temporary」即便用了內部臨時表。內部臨時表的建立條件:

        *  group by 和 order by中的列不相同

        *  order by的列不是引用from 表列表中 的第一表

        *  group by的列不是引用from 表列表中 的第一表

        *  使用了sql_small_result選項

        *  含有distinct 的 order by語句

 

4. 遊標的問題:

1)  儘可能避免使用遊標,

         由於遊標的效率較差,若是遊標操做的數據超過1萬行,那麼就應該考慮改寫。 Mysql的遊標不適合處理大一點的數據量,僅適合用於操做幾百上千的小數據量。

 

2)  使用基於遊標的方法或臨時表方法以前,應先尋找基於集合的解決方案來解決問題,基於集合的方法一般更有效。

       對小型數據集使用 FAST_FORWARD 遊標一般要優於其餘逐行處理方法,尤爲是在必須引用幾個表才能得到所需的數據時。在結果集中包括「合計」的例程一般要比使用遊標執行的速度快。若是開發時間容許,基於遊標的方法和基於集的方法均可以嘗試一下,看哪種方法的效果更好。

 

5. 事務的問題:

1)  儘可能避免大事務操做,提升系統併發能力。

         innodb在事務下能夠鎖定行也能夠鎖定表(MyISAM僅支持表鎖),固然對於應用而言,鎖定行是最佳性能,當鎖定了表,其餘進程對錶進行write操做時只能隊列等候事務的完成再繼續。

 

6. 數據量的問題

1)  儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

        一般避免向客戶端返回大數據量的方法是採用分頁系統,在分頁系統中使用LIMIT和OFFSET很常見,它們一般也會和ORDER BY一塊兒使用。但分頁系統中(LIMIT和OFFSET)的一個常見問題是偏移量很大時,能夠限制一個分頁裏訪問的頁面數目,或者讓偏移量很大時查詢效率更高。一個提升效率的簡單技巧就是在覆蓋索引上進行偏移,而不是對全行數據進行偏移。能夠將從覆蓋索引上提取出來的數據和全行數據進行聯接,而後取得須要的列。有時能夠把LIMIT轉換爲位置性查詢(bwteen and子句),服務器能夠以索引範圍掃描的方式來執行。

7. COUNT優化:

        COUNT有兩種不一樣的工做方式:統計值的數量統計行的數量。若是在COUNT()的括號中定義了列名或其它表達式,COUNT就會統計這個表達式有值班的次數。COUNT(*)則統計結果中行的數量。

       一般說來,使用了COUNT的查詢很難優化,由於它們一般須要統計不少行。在MySQL內部優化它的惟一其餘選擇就是使用覆蓋索引。若是這還不夠,那麼就須要更改應用程序的架構。能夠考慮使用匯總表,還能夠利用外部緩存系統,好比數據庫緩存服務器(MemCached)。

      1)  InnoDB引擎在統計方面和MyISAM是不一樣的,MyISAM內置了一個計數器,COUNT(*)在沒有查詢條件的狀況下使用 select count(*) from table 的時候,MyISAM直接能夠從計數器中取出數據,而InnoDB必須全表掃描一次方能獲得總的數量。

     2) 可是當有查詢條件的時候,二者的查詢效率一致。

     對主鍵索引做COUNT的時候之因此慢,是由於:

     *  InnoDB引擎:

       [1]     數據文件和索引文件存儲在一個文件中,主鍵索引默認直接指向數據存儲位置。

       [2]     二級索引存儲指定字段的索引,實際的指向位置是主鍵索引。當咱們經過二級索引統計數據的時候,無需掃描數據文件;而經過主鍵索引統計數據時,因爲主鍵索引與數據文件存放在一塊兒,因此每次都會掃描數據文件,因此主鍵索引統計沒有二級索引效率高。

      [3]     因爲主鍵索引直接指向實際數據,因此當咱們經過主鍵id查詢數據時要比經過二級索引查詢數據要快。

     *  MyISAM引擎

      [1]     該引擎把每一個表都分爲幾部分存儲,好比用戶表,包含user.frm,user.MYD和user.MYI。

      [2]     User.frm負責存儲表結構

      [3]     User.MYD負責存儲實際的數據記錄,全部的用戶記錄都存儲在這個文件中

      [4]     User.MYI負責存儲用戶表的全部索引,這裏也包括主鍵索引。

8.優化聯接:

對於JOIN查詢,

  •   首先確保ON或USING使用的列上有索引。這樣,MySQL內部會啓動爲你優化Join的SQL語句的機制。

  •   確保GROUP BY或ORDER BY只引用一個表中的列。這樣MySQL能夠嘗試對這些操做使用索引。

  •   確保被用來Join的字段,應該是相同的類型的。例如:若是你要把 DECIMAL 字段和一個 INT 字段Join在一塊兒,MySQL就沒法使用它們的索引。對於那些STRING類型,還須要有相同的字符集才行。(兩個表的字符集有可能不同)

  •   要謹慎升級MySQL。

 

9.優化子查詢:

        對子查詢重要的建議就是儘量使用聯接。

 

10. 優化order by語句

基於索引的排序
        MySQL的弱點之一是它的排序。雖然MySQL能夠在1秒中查詢大約15,000條記錄,但因爲MySQL在查詢時最多隻能使用一個索引。所以,若是WHERE條件已經佔用了索引,那麼在排序中就不使用索引了,這將大大下降查詢的速度。咱們能夠看看以下的SQL語句:
             SELECT * FROM SALES WHERE NAME = 「name」 ORDER BY SALE_DATE DESC;

        在以上的SQL的WHERE子句中已經使用了NAME字段上的索引,所以,在對SALE_DATE進行排序時將再也不使用索引。爲了解決這個問題,咱們能夠對SALES表創建複合索引:

            ALTER TABLE SALES DROP INDEX NAME, ADD INDEX (NAME, SALE_DATE)

        這樣再使用上述的SELECT語句進行查詢時速度就會大副提高。但要注意,在使用這個方法時,要確保WHERE子句中沒有排序字段,在上例中就是不能用SALE_DATE進行查詢,不然雖然排序快了,可是SALE_DATE字段上沒有單獨的索引,所以查詢又會慢下來。

        在某些狀況中, 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;
    可是如下狀況不使用索引:
       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 :

 

11. 優化GROUP BY和DISTINCT

        一般說來,索引也是優化它們的最重要手段。

        當不能使用索引時,MySQL有兩種優化GROUP BY的策略:使用臨時表或文件排序進行分組。經過使用SQL_SMALL_RESULT強制MySQL選擇臨時表,或者使用SQL_BIG_RESULT強制它使用文件排序。

        若是要對聯接進行分組,經過對錶的ID列進行分組會更加高效。經過配置SQL_MODE參數來禁止SELECT中使用未在GROUP BY中出現的列。

        分組查詢的一個變化就是要求MySQL在結果內部實現超級聚合(Super Aggregation)。能夠在GROUP BY後面加上WITH ROLLUP來實現。但也許它沒有被很好地優化。可使用解釋器檢查執行方法,確認分組是否已經經過文件排序或臨時表完成,而後試着移除WITH ROLLUP,而且查看分組方法是否沒有變化。

        默認狀況下, MySQL 排序全部 GROUP BY col1 , col2 , .... 。查詢的方法如同在查詢中指定 ORDER BY col1 , col2 , ... 。若是顯式包括一個包含相同的列的 ORDER BY子句, MySQL 能夠絕不減速地對它進行優化,儘管仍然進行排序。若是查詢包括 GROUP BY 但你想要避免排序結果的消耗,你能夠指定 ORDER BY NULL禁止排序。

12.優化LIMIT和OFFSET:

        在分頁系統中使用LIMIT和OFFSET很常見,它們一般也會和ORDER BY一塊兒使用。

        一個常見問題是偏移量很大時,能夠限制一個分頁裏訪問的頁面數目,或者讓偏移量很大時查詢效率更高。一個提升效率的簡單技巧就是在覆蓋索引上進行偏移,而不是對全行數據進行偏移。能夠將從覆蓋索引上提取出來的數據和全行數據進行聯接,而後取得須要的列。有時能夠把LIMIT轉換爲位置性查詢(bwteen and子句),服務器能夠以索引範圍掃描的方式來執行。            

        若是確實須要優化分頁系統,也許應該利用預先計算好的彙總數據。

 

13.優化聯合:

        MySQL老是經過建立並填充臨時表方式來執行UNION,它不能對UNION進行太多的優化。重要的是始終要使用UNION ALL,除非須要服務器消除重複的行。若是忽略了ALL關鍵字,MySQL就會向臨時表添加distinct選項,它會利用全部行來決定數據的惟一性,這種操做開銷很大。

 

10. 優化 OR

一般狀況下,在OR子句中的字段都具備索引的狀況下,用UNION子句來替換WHERE子句中的OR將會起到較好的效果。但若是存在沒有索引的列,則不該使用UNION子句。對索引列使用OR將形成全表掃描。

另外,可使用IN子句來替代OR子句。經過給索引添加愈來愈多的列,而且使用IN()列表來覆蓋那些不存WHERE子句的列。

對於OR子句,一般MyISAM存儲索引可使用索引來掃描,而InnoDB則不能。爲了利用索引,OR子句中的全部條件中的字段都具備單獨的索引,不然利用不上索引。

6.查詢優化提示

        若是不滿意MySQL優化器選擇的優化方案,可使用一些優化提示來控制優化器的行爲。

  •   HIGH_PRIORITY和LOW_PRIORITY:決定訪問同一個表的語句相對於其餘語句的優先級。HIGH_PRIORITY告訴MySQL將一個SELECT語句放在其餘語句的前面,以便它修改數據。LOW_PRIORITY則相反,若是有其餘語句須要訪問數據,它就將當前語句放到隊列的最後。能夠將這個選項用於SELECT、INSERT、UPDATE、REPLACE和DELETE。這兩個選項隻影響服務器對訪問表的隊列的處理,並非指在查詢上分配較多或較少的資源。

 

  •   DELAYED:用於INSERT和UPDATE。應用這個提示的語句會當即返回並將待插入的列放入緩衝區中,在表空閒的時候再執行插入。

 

  •   STRAIGHT_JOIN:用於SELECT語句中SELECT關鍵字後,也可用於聯接語句。它可強制MySQL按照查詢中表出現的順序來聯接表,並當它出如今兩個聯接表中間時,強制這兩個表按照順序聯接。

 

  •   SQL_SMALL_RESULT和SQL_BIG_RESULT:用於SELECT語句。告訴MySQL在GROUP BY或DISTINCT查詢中如何並什麼時候使用臨時表。SQL_SMALL_RESULT告訴優化器結果集會比較小,能夠放在索引過的臨時表中,以免對分組後的數據排序。SQL_BIG_RESULT是結果集很大,最好使用磁盤上的臨時表進行排序。

 

  •   SQL_BUFFER_RESULT:將結果放在臨時表中,而且儘快釋放掉表鎖。

 

  •   SQL_CACHE和SQL_NO_CACHE:SQL_CACHE代表查詢已經存在緩存中,而SQL_NO_CACHE正好相反。

 

  •   SQL_CALC_FOUND_ROWS:告訴MySQL在有LIMIT子句時計算完整的結果集。(見上述瞭解爲何不要使用這個提示)

 

  •   FOR UPDATE和LOCK IN SHARE MODE:SELECT語句使用這兩個提示來控制鎖定,但只針對有行級鎖的存會引擎。它可幫助預先鎖定匹配的行。INSERT..SELECT查詢不須要這兩個提示。

 

  •   USE INDEX、IGNORE INDEX和FORCE INDEX:告訴優化器從表中尋找行時使用或忽略索引。

 

  •   Optimizer_search_depth:這個變量告訴優化器檢查執行計劃的深度。若是查詢在「統計」的狀態停留很長時間,就能夠考慮減小這個變量的值。

  •   Optimizer_prune_level:讓優化器根據檢查的行的數量跳過某些查詢計劃

 

 

7. SQL核心語句(很是實用的幾個技巧)

1.優化insert語句

        若是能夠同時從同一客戶插入不少行,使用多個值表的INSERT 語句。多個值表的 INSERT 語句 ,能夠大大縮減客戶端與數據庫之間的鏈接、語法分析等消耗,使得效率比分開執行的單個 INSERT 語句快不少。

       如批量插入:

         INSERT  INTO tb (fa, fb, fc) VALUES ('1', '12', '13'), ('2', '22', '23'), ('3', '32', '33'), 

        若是從不一樣客戶插入不少行,能夠經過使用INSERT DELAYED 語句獲得更高的速度。Delayed 的含義是讓insert 語句立刻執行,其實數據都被放在內存的隊列中,並無真正的寫入磁盤;這比每條語句都分別插入要快的多;LOW_PRIORITY恰好相反,在全部其餘用戶對錶的讀寫完成後才進行插入。

 

將索引文件和數據文件分在不一樣的磁盤上存放(利用建表中的選項);

若是進行批量插入,能夠增長bulk_insert_buffer_size 變量值的方法來提升速度,可是,這隻能對myisam表使用

當從一個文本文件裝載一個表時,使用LOAD DATA INFILE。這一般比使用不少INSERT語句快20倍;

根據應用狀況使用replace 語句代替insert;

根據應用狀況使用ignore 關鍵字忽略重複記錄。

 

2.大批量插入數據

1. 對於Myisam 類型的表,能夠經過如下方式快速的導入大量的數據。

         ALTER TABLE tblname DISABLE KEYS;

          loading the data

          ALTER TABLE tblname ENABLE KEYS;

        這兩個命令用來打開或者關閉MyISAM 表非惟一索引的更新。在導入大量的數據到一個非空的Myisam 表時,經過設置這兩個命令,能夠提升導入的效率。對於導入大量數據到一個空的Myisam 表,默認就是先導入數據而後才建立索引的,因此不用進行設置。

 

2. 而對於Innodb 類型的表,這種方式並不能提升導入數據的效率。對於Innodb 類型的表,咱們有如下幾種方式能夠提升導入的效率:

      a.   由於Innodb 類型的表是按照主鍵的順序保存的,因此將導入的數據按照主鍵的順序排列,能夠有效的提升導入數據的效率。若是Innodb 表沒有主鍵,那麼系統會默認建立一個內部列做爲主鍵,因此若是能夠給表建立一個主鍵,將能夠利用這個優點提升導入數據的效率。
 

       b.   在導入數據前執行SET UNIQUE_CHECKS=0,關閉惟一性校驗,在導入結束後執行SETUNIQUE_CHECKS=1,恢復惟一性校驗,能夠提升導入的效率。

 

       c.   若是應用使用自動提交的方式,建議在導入前執行SET AUTOCOMMIT=0,關閉自動提交,導入結束後再執行SET AUTOCOMMIT=1,打開自動提交,也能夠提升導入的效率。

 

3.清空數據表

            TRUNCATE TABLE  `mytable`

       注意:刪除表中的全部記錄,應使用TRUNCATE TABLE語句。

 

        TRUNCATE TABLE 與沒有 WHERE 子句的 DELETE 語句相似;可是,TRUNCATE TABLE 速度更快,使用的系統資源和事務日誌資源更少。這也意味着TRUNCATE TABLE要比DELETE快得多。

       DELETE 語句每次刪除一行,並在事務日誌中爲所刪除的每行記錄一個項。TRUNCATE TABLE 經過釋放用於存儲表數據的數據頁來刪除數據,而且在事務日誌中只記錄頁釋放。

       當使用行鎖執行 DELETE 語句時,將鎖定表中各行以便刪除。TRUNCATE TABLE 始終鎖定表和頁,而不是鎖定各行。

        執行 DELETE 語句後,表仍會包含空頁。例如,必須至少使用一個排他 (LCK_M_X) 表鎖,才能釋放堆中的空表。若是執行刪除操做時沒有使用表鎖,表(堆)中將包含許多空頁。對於索引,刪除操做會留下一些空頁,儘管這些頁會經過後臺清除進程迅速釋放。

        TRUNCATE TABLE 刪除表中的全部行,但表結構及其列、約束、索引等保持不變。若要刪除表定義及其數據,就需使用 DROP TABLE 語句。

 須要注意的是,在下列狀況下,不能對如下表使用 TRUNCATE TABLE:

     *  由 FOREIGN KEY 約束引用的表。

     *  參與索引視圖的表。

     *  經過使用事務複製或合併複製發佈的表。

     *  對於具備以上一個或多個特徵的表,請使用 DELETE 語句。

     *  TRUNCATE TABLE 不能激活觸發器,由於該操做不記錄各個行刪除。

 

4.拆分大的 DELETE 或 INSERT 語句

        若是你須要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你須要很是當心,要避免你的操做讓你的整個網站中止相應。由於這兩個操做是會鎖表的,表一鎖住了,別的操做都進不來了。

        Apache 會有不少的子進程或線程。因此,其工做起來至關有效率,而咱們的服務器也不但願有太多的子進程,線程和數據庫連接,這是極大的佔服務器資源的事情,尤爲是內存。

        若是你把你的表鎖上一段時間,好比30秒鐘,那麼對於一個有很高訪問量的站點來講,這30秒所積累的訪問進程/線程,數據庫連接,打開的文件數,可能不只僅會讓你泊WEB服務Crash,還可能會讓你的整臺服務器立刻掛了。

        因此,若是你有一個大的處理,你定你必定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個示例:

 

while (1) {

    //每次只作1000條

    mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");

    if (mysql_affected_rows() == 0) {

        // 沒得可刪了,退出!

        break;

    }

    // 每次都要休息一下子

    usleep(50000);

}

 

5.用SELECT建立記錄和表

  INSERT語句與DELETE語句和UPDATE語句有一點不一樣,它一次只操做一個記錄。然而,有一個方法可使INSERT 語句一次添加多個記錄。要做到這一點,你須要把INSERT語句與SELECT語句結合起來,象這樣:

  INSERT mytable(first_column,second_column)

       SELECT another_first,another_second  FROM anothertable WHERE another_first='Copy Me!';

  這個語句從anothertable拷貝記錄到mytable.只有表anothertable中字段another_first的值爲'Copy Me!'的記錄才被拷貝。

  當爲一個表中的記錄創建備份時,這種形式的INSERT語句是很是有用的。在刪除一個表中的記錄以前,你能夠先用這種方法把它們拷貝到另外一個表中。

  若是你須要拷貝整個表,你可使用SELECT INTO語句。例如,下面的語句建立了一個名爲newtable的新表,該表包含表mytable的全部數據:

                SELECT * INTO newtable FROM mytable;

  你也能夠指定只有特定的字段被用來建立這個新表。要作到這一點,只需在字段列表中指定你想要拷貝的字段。另外,你可使用WHERE子句來限制拷貝到新表中的記錄。下面的例子只拷貝字段second_columnd的值等於'Copy Me!'的記錄的first_column字段。

           SELECT first_column INTO newtable   FROM mytable

                   WHERE second_column='Copy Me!';

  使用SQL修改已經創建的表是很困難的。例如,若是你向一個表中添加了一個字段,沒有容易的辦法來去除它。另外,若是你不當心把一個字段的數據類型給錯了,你將沒有辦法改變它。可是,使用本節中講述的SQL語句,你能夠繞過這兩個問題。

  例如,假設你想從一個表中刪除一個字段。使用SELECT INTO語句,你能夠建立該表的一個拷貝,但不包含要刪除的字段。這使你既刪除了該字段,又保留了不想刪除的數據。

  若是你想改變一個字段的數據類型,你能夠建立一個包含正確數據類型字段的新表。建立好該表後,你就能夠結合使用UPDATE語句和SELECT語句,把原來表中的全部數據拷貝到新表中。經過這種方法,你既能夠修改表的結構,又能保存原有的數據。

 

8.重構查詢的方式

        一個重要的查詢設計問題就是是否能夠把一個複雜查詢分解成多個簡單的查詢。注意的是,在應用程序中若是可使用簡單查詢返回結果時仍然須要避免使用太多的查詢。(從網絡開銷與服務器開銷做平衡)。

1) 縮短查詢(分治法):讓查詢每次執行一小部分,以減小受影響的行數。

        例:將一次性大量數據的刪除細化成屢次中等大小的數據刪除,獲得移除相同數據的目的。對一個高效的查詢來講,一次刪除10000行數據的任務已經足夠大了。

 

2)分解聯接:把一個多表聯接分解成多個單表查詢,而後在應用程序端實現聯接操做。

      這種重構方式的優點:

      *  緩存的效率更高:許多程序都直接緩存了表,這樣可直接利用上緩存數據。

      *  對MyISAM表來講,每一個表一個查詢能夠有效地利用表鎖。由於查詢會在短期內鎖住單個表,而不是把全部表長時間鎖住。

      *  查詢自己會更高效。

      * 能夠減小多餘的行訪問。聯接會反覆訪問同一行數據。

      * 在某種意義上能夠認爲這種方式是手工執行了哈希聯接,而不是MySQL內部執行聯接操做時採用的嵌套循環算法。

 

注意:在何時應用程序端進行聯接效率會更高:

     *  能夠緩存早期查詢的大量數據。

     *  使用了多個MyISAM表。

     *  數據分佈在不一樣的服務器上。

     *  對於大表使用IN()替換聯接。

     *  一個聯接引用了同一個表不少次。

 

3)爲查詢緩存優化你的查詢

       大多數的MySQL服務器都開啓了查詢緩存。這是提升性最有效的方法之一,並且這是被MySQL的數據庫引擎處理的。當有不少相同的查詢被執行了屢次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操做表而直接訪問緩存結果了。

      這裏最主要的問題是,對於程序員來講,這個事情是很容易被忽略的。由於,咱們某些查詢語句會讓MySQL不使用緩存。請看下面的示例:

     // 查詢緩存不開啓

      $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");

 

       // 開啓查詢緩存

       $today = date("Y-m-d");

      $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

 

       MySQL還容許改變語句調度的優先級,它可使來自多個客戶端的查詢更好地協做,這樣單個客戶端就不會因爲鎖定而等待很長時間。改變優先級還能夠確保特定類型的查詢被處理得更快。

相關文章
相關標籤/搜索