數據庫使用1--注意事項

書寫SQL須要注意的若干問題(MySQL版)  

1、基本問題html

1,  在系統中運行的SQL查詢,先考慮一下能不能在Slave上檢索,目前各個項目中Master上的不可避免的查詢量是其餘全部的Slave總和還多。java

但也不是一味的都是在Slave上查詢。mysql

系統上出過一次查詢數據的狀況:在一個先後順序執行的邏輯代碼中,先更新Master的數據,再在Slave上查更新後的數據,這樣的操做不少時候因服務器和網絡環境而出現查詢結果不一致的狀況。這樣的就不能在Slave上查詢了。程序員

2,  儘可能不要輸出沒有用的列,也不要輸出已經明確的列,增長了無用的數據傳輸量也是影響性能的。算法

3,  儘可能在每一個查詢中返回本身須要的那些行,無關的不要返回。對簡單查詢是這樣,對複雜的包含不少子查詢中的每一個子查詢更是這樣,儘可能讓每一個子查詢的結果集保留到最小再進行關聯,避免出現先關聯後再取Distinct 這樣的操做。sql

4,  儘可能不要在程序裏面有 Select * 這樣的寫法,之後表字段的順序變更均可能形成程序的問題。數據庫

5,  對多表之間的鏈接必須用索引來做爲鏈接列,不然這樣的查詢就是一個全表掃描,兩邊的關聯字段必定要類型一致,避免強制轉換。緩存

     

  1. mysql> explain select count(*) From JHF_ALIVE_EXECUTION E , JHF_ALIVE_CONTRACT C where C.Trade_ID=E.Trade_ID ;

  2. +----+-------------+-------+------+-------------------+-------------------+---------+--------------------+------+-------------+

  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

  4. +----+-------------+-------+------+-------------------+-------------------+---------+--------------------+------+-------------+

  5. | 1 | SIMPLE | E | ALL | NULL | NULL | NULL | NULL | 866 | |

  6. | 1 | SIMPLE | C | ref | ALIVE_CONTRACT_02 | ALIVE_CONTRACT_02 | 42 | CFDMAIN.E.TRADE_ID | 1 | Using index |

  7. +----+-------------+-------+------+-------------------+-------------------+---------+--------------------+------+-------------+

  8. rows in set (0.00 sec)

 

6,  不要在Where 字句中對列使用函數,那樣會致使索引失效,服務器

  1. mysql> show index from JHF_ALIVE_CONTRACT ;

  2. +--------------------+------------+-------------------+--------------+-------------+-----------+-------------+-+------------

  3. | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | | Index_type

  4. +--------------------+------------+-------------------+--------------+-------------+-----------+-------------+-+------------

  5. | JHF_ALIVE_CONTRACT | 0 | PRIMARY | 1 | CONTRACT_ID | A | 157 | | BTREE 

  6. | JHF_ALIVE_CONTRACT | 1 | ALIVE_CONTRACT_01 | 1 | ORDER_ID | A | 157 | | BTREE 

  7. | JHF_ALIVE_CONTRACT | 1 | ALIVE_CONTRACT_02 | 1 | TRADE_ID | A | 157 | | BTREE 

  8. | JHF_ALIVE_CONTRACT | 1 | ALIVE_CONTRACT_03 | 1 | CUSTOMER_ID | A | 19 | | BTREE 

  9. +--------------------+------------+-------------------+--------------+-------------+-----------+-------------+-+------------

  10. rows in set (0.00 sec)

  11. mysql>

  12.  

  13.  

  14. mysql> explain select * From JHF_ALIVE_CONTRACT where Order_ID='20090930CONT00002005' ;

  15. +----+-------------+--------------------+------+-------------------+-------------------+---------+-------+------+-------------+

  16. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

  17. +----+-------------+--------------------+------+-------------------+-------------------+---------+-------+------+-------------+

  18. | 1 | SIMPLE | JHF_ALIVE_CONTRACT | ref | ALIVE_CONTRACT_01 | ALIVE_CONTRACT_01 | 82 | const | 1 | Using where |

  19. +----+-------------+--------------------+------+-------------------+-------------------+---------+-------+------+-------------+

  20. row in set (0.00 sec)

  21. 主鍵檢索,最快的那種了。

  22.  

  23. mysql> explain select * From JHF_ALIVE_CONTRACT where substr(Order_ID,1,17) ='20090930ORD000115' ;

  24. +----+-------------+--------------------+------+---------------+------+---------+------+------+-------------+

  25. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

  26. +----+-------------+--------------------+------+---------------+------+---------+------+------+-------------+

  27. | 1 | SIMPLE | JHF_ALIVE_CONTRACT | ALL | NULL | NULL | NULL | NULL | 94 | Using where |

  28. +----+-------------+--------------------+------+---------------+------+---------+------+------+-------------+

  29. row in set (0.00 sec)

  30. 什麼索引都沒用上,全表掃描。

  31.  

  32.  

  33. mysql> explain select * From JHF_ALIVE_CONTRACT where Order_ID like '20090930ORD000115%' ;

  34. +----+-------------+--------------------+-------+-------------------+-------------------+---------+------+------+-------------+

  35. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

  36. +----+-------------+--------------------+-------+-------------------+-------------------+---------+------+------+-------------+

  37. | 1 | SIMPLE | JHF_ALIVE_CONTRACT | range | ALIVE_CONTRACT_01 | ALIVE_CONTRACT_01 | 82 | NULL | 6 | Using where |

  38. +----+-------------+--------------------+-------+-------------------+-------------------+---------+------+------+-------------+

  39. row in set (0.00 sec)

  40. like 也能發揮索引的效果。

 

 

7,  使用like 語句時,對 「C%」是能利用索引的,但對 「%C」是無效的。並且在前面這個固定字符串越多時效率越好,也就儘可能多匹配。網絡

見上例。

8,  Not in 是個危險的用法,在程序中慎用,必要時能夠用 left outer join 來改寫。

9,  少用點 or ,它極可能會使一個查詢索引失效,必要時能夠用 union all 或者 union 來替代。

10,  注意一下 Union all 與 union 的區別。前者是兩個結果集的不會通過任何處理進行相加,然後者是要通過合併之後的內容。

對兩個絕不相關的集合合併時,儘可能用UNION ALL,避免沒必要要的排序浪費系統資源。

11,  在大表上不作Group by 操做,若是須要的話,能夠用大表的總結表。

對一些避免不了的實時檢索,能夠考慮用索引覆蓋的方式來對所用到的字段所有創建索引的方式來加快查詢速度。

12,    對 Group by ,distinct 出來的結果已是有序的了,不須要再排序,儘可能使用已經排好序的數據,省得再排序浪費資源,若是要排序,不要在Order by 裏面的使用表達式。

13,    在java中儘可能使用 prepareStatement 來替代 Statement,  一個SQL提交給MYSQL後經歷詞義檢查、語義檢查、對象檢查、獲取存取路徑、造成最終執行計劃、生成執行代碼,可是若是是兩個同樣的SQL(如出一轍,多個空格都不行)這個過程就所有省了,使用綁定變量(有的地方可能稱主機變量,就是用?來替代值,而後再設置這個值)能達到如出一轍的效果,DBMS在算存取路徑的時候會估算一個值來替代,這樣能達到一個很好的效果。(若是不注意這一點,那麼你的系統離崩潰就不遠了,這點對程序員特別重要!!)可是也不是全部的狀況都是這樣,對一個SQL「長時間固定不變的環境中」,那麼每次執行都是相同的SQL,這時靜態變量和綁定變量方式惟一的差異是獲取存取路徑方式的不一樣,綁定方式是估算,而寫成變量的方式是精確的路徑。實際中到底使用哪一種?1)通常都按照綁定變量來寫。2)若是在設計的時候就能明確該句在使用執行的環境,再換成靜態方式。

其實 都用綁定變量這種方式來寫,沒有什麼壞處!

14,  不要輕易利用MySQL 的自動類型轉換,看起來挺好使,但用起來危害很是大,由於它極可能會讓看起來好端端的索引失效。

15,  在數據庫上常常在容許爲NULL的字段上創建了索引,注意想查詢此字段上的 is null 或者 is not null 可能會使索引失效。

16,  避免出現 跨庫操做這樣的SQL語句,例如:

     Use MAIN ;

     Insert into JHF_ORDER select * From HISTORY.JHF_ORDER where id=’33’ ;

         這樣的SQL在 Master 上能正常運行的,可是由於Slave 的結構各類各樣,對不存在 HISTORY 庫的SLAVE,這個SQL 就會致使同步中斷,而通常須要人工干預才能繼續同步。

     

17,  現有的數據庫結構中各個Slave所忽略的表是不同的,對相似這樣的SQL:

     Insert into TA select * From TB where Code=’ABC’ ,在Master 上執行沒問題,但若是某個Slave忽略了TB 表的同步,那麼在這個Slave上的TA表的數據將也不會正常,在程序中避免出現一個Insert/Update/Delete中關聯多個表的狀況,很容易由於Slave同步部分表的緣由而致使數據不一致。

 

18,    對一個大的結果結進行排序是個很是費系統資源的操做。但也不能由於這點而不排序。對一個未使用任何排序操做的結果集的默認順序是按照主鍵的順序進行默認排序的,沒有主鍵或者自增加主鍵的是按照記錄的插入前後順序進行輸出,某些時候是知足需求的,可是這樣的排序是不可靠的,在數據庫進行過數據重整和索引重建或者後插入的數據的主鍵值不是按照一個固定的順序來的時候,就極可能打亂原始的順序而出現不用時間的不用的檢索結果。

19,    關於批處理的SQL,編寫時要考慮SQL更新的速率和數據量的大小。 這主要是考慮到咱們如今所使用的M/S同步機制。更新速度過快可能使數據庫壓力增大,而且出現數據庫同步延遲。更新太慢沒有效率。總之,必定要經過測試綜合進行考慮,找到平衡點以達到最好的效果。

 

20,  不要在正式系統裏面運行沒有試過的SQL 語句,即便是Select 語句。

A)、不恰當關聯,形成笛卡兒結果集很是龐大,讓系統忙死在寫入臨時文件的操做中,這個會發生在兩個大表間關聯的時候,關聯的條件是多對多的關係,形成結果集很是龐大,一時半會都執行不完,這時不要慌,關閉終端是解決不了問題的,進入MySQL或者在客戶端終端,按照如下命令:

   show processlist ;  --à 找到State 處於 Copy to tmp ..這樣的SQL對應的Id號

   kill XXXX ;

 纔算真正控制了這個SQL的執行。

B)、要清楚「的確」存在能把數據庫整死的純查詢SQL,看起來不起眼,但威力很大,有些是由於MySQL自己的BUG,例如:

 我遇到的兩個:

  1. SELECT COUNT(*) FROM (

  2.    SELECT Customer_ID FROM JHF_DEPOSIT

  3.  UNION ALL

  4.  SELECT Customer_ID

  5.      FROM JHF_WITHDRAWAL ORDER BY CustomerID

  6.  ) A ;

(在 MySQL 5.0.33 中)

由於在子查詢中Order by 了一個不存在的字段,不是報語句的錯誤,而是直接將MySQL數據庫重啓了。


  1. select A.CC,A.bid,A.ask,A.rateTime,

  2.        (select ratetime From wxlTemp B where B.CC=A.CC and B.bid <> A.bid and B.ask <> A.bid and B.ratetime > A.ratetime Order by ratetime limit 1) as LastTime 

  3. From wxlTemp A

  4. where A.RateTime <'2009-07-03 06:03:00'

  5. Order by A.CC,A.ratetime

這個SQL也會致使MySQL數據庫重啓。

 

 

2、有關分頁相關的:


1. 分頁查詢時,一般一頁記錄爲幾十條,每次只須要查詢當頁的記錄。當有複雜的查詢sql時,咱們要將sql分解,提煉出必要的影響結果集的sql,用於分頁查詢,這個sql只包含一部分主要的表;在分頁查詢執行後,再查詢這一頁記錄對應的其它表的記錄。由於記錄數只有一頁了,那麼其它表的查詢的性能將會很好,這部分是須要在java程序中處理的。

2. 若是僅僅統計表記錄數量,那麼就不要使用order by。


 

3. 對於分頁查詢,一般須要顯示符合條件的總記錄數、頁碼、當頁條數。這樣就須要執行兩次數據庫查詢,一次是計算總記錄數,一次是檢索當頁所有記錄。對於複雜sql,建議將這兩次查詢使用的sql分開。這麼作的緣由是,好比在FX項目中,分頁方法通常都是將sql 直接進行解析,根據from來拆分紅統計記錄數和返回結果集的sql。對於返回當頁記錄的sql來講,一些where條件和表關聯是必要的,由於可能其中一些只是爲了在select中包含部分表的字段;可是對於統計記錄數的sql來講,只須要那些影響結果記錄數的必要條件和關聯的表便可。
好比:
select * from tableA inner join tableB on(tableA.c1=tableB.c1)
left outer join tableC on (tableC.c2=tableA.c2)
tableA 和 tableB的記錄是一對一的關係
通用分頁方法會將統計記錄數的sql分解爲相似下面這樣:
select count(*) from tableA inner join tableB on(tableA.c1=tableB.c1)
left outer join tableC on (tableC.c2=tableA.c2)

可是 tableB 是不須要關聯的,由於不影響記錄數。那麼咱們單獨寫一個統計記錄
數的sql:
select count(*) from tableA inner join tableB on(tableA.c1=tableB.c1)

  

 

2、如何避免出現鎖衝突及死鎖

1,  對一個象Fx這樣的分佈系統,同時操做註文約定邏輯幾個表這樣的模塊有不少,必定要在一個事務中確保全部的模塊對操做相同的幾個表的順序都一致,避免多個進程間對錶產生死鎖。

2,  對由不一樣的模塊更新相同的一批記錄也可能存在記錄間出現死鎖的狀況,因此對事務操做比較密集的地方,儘可能對操做的記錄進行按照一個統一的順序進行,好比升序或者降序。

3,  對更新比較頻繁的表必定要使用INNODB的表而不要使用MyISAM ,由於MyISAM 的每一次更新都將是鎖住整個的表,而大大下降了更新的併發性能。

4,  在現有的系統中,咱們使用的事務隔離級別是:READ_COMMITED ,在一個事務中它會對更新的記錄進行加鎖,這裏的「更新的記錄」比較微妙,它鎖定的範圍是和更新的語句的where 條件密切相關,想要達到行鎖的效果,Update語句的條件必定要加上索引,最好是主鍵或者惟一鍵,

由於這樣的鎖會很本分,肯定的記錄比較明確。

    5,要儘可能保證事務不要過大,小事務發生鎖衝突的概率較小。

 

3、如何優化

對每一個SQL語句在執行以前,作一下 EXPLAIN 檢查,查看是否都使用了索引,是否使用了有效的索引,看是否掃描了不少行數據。

http://dev.mysql.com/doc/refman/5.1/zh/optimization.html#explain

對索引的建立也要把握精而不濫的原則,對特殊的表,能夠考慮只在Slave上創建。

1,索引的創建對提升檢索能力頗有用,可是數據庫維護它很費資源。

2,索引只使用開頭的部分。

  key (a,b) .... where b=5 will not use index.

3,創建一個對檢索有用的索引,

  index on gender is not a good idea,例如在性別上建索引不是頗有用。

4,對惟一建的索引,加上 UNIQUE 。

5,避免出現無用的索引。(歷來沒被調用)

6,索引的順序很重要。

7,不要在同列(s)上創建兩個索引。

8,充分用別的組合索引的前面部分,是個至關好的主意。

9,能夠只對一個字段的前幾個字段創建索引。

10,短一些的索引比較好,整數最好。(Short keys are better, Integer best)

11,有規律的值 比 沒有規律的隨機的數要好。

  – access locality is much better

  – auto_increment better than uuid()

12,記得常常 優化表,這樣能壓縮和排序索引項。

    OPTIMIZE TABLE compact and sort indexes

13,分析表,能更新表的統計信息,這樣對檢索頗有好處。

   ANALYZE TABLE - update statistics

14.利用索引並不必定能提升性能,若是返回結果集數量很大,甚至接近全表記錄數時,那麼全表掃描的效率更高。經過索引再定位到物理記錄,這個過程會比較耗費時間。


附錄:

MySQL中經過 show status 對獲得的值進行計算獲得後的值,你們能夠參考。

  1,鏈接失敗的監控.

   ■監視點   : 鏈接失敗的百分比。

   ■公式     : Aborted_connects*100 / Connections

   ■正常範圍 : 小於 10%.

   ■含義     : 應用程序鏈接服務器失敗的比例,通常緣由有:未受權訪問數據庫/密碼錯誤/鏈接超時 等.

 

 

  2,最大狀況下的鏈接使用百分比.

   ■監視點   : 最大狀況下的鏈接使用百分比。

   ■公式     : Max_used_connections / max_connections

   ■正常範圍 : 小於 75%.

   ■含義     : 從開機到如今的最大鏈接狀況,表示的是這段時間的峯值,對繁忙的系統這個頗有參考意義.

 

  3,MyISAM索引緩存命中率

   ■監視點   : key_buffer_size的設置是否適當。

   ■公式     : 1-(Key_reads / Key_read_requests)

   ■正常範圍 : 95%以上.

   ■含義     : 增大key_buffer_size而且監控緩存利用率。當命中率到達了一個可接收的水平,保存key_buffer_size值到MySQL配置文件中。

                須要MySQL運行一個合理時間後,查看命中率纔有意義。

 

  4,InnoDB緩存命中率

   ■監視點   : innodb_buffer_pool_size的設置是否適當。

   ■公式     : 100* (1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests))

   ■正常範圍 : 95%以上.

   ■含義     : 數據和索引在緩存中讀取的比率。從內存讀取要比磁盤讀取塊不少,所以要儘可能保持物理I/O最少。

                當使用InnoDB大多數的訪問應該在內存中,所以這個值要很高。

 

  5,InnoDB緩存寫入等待率

   ■監視點   : innodb_buffer_pool_size的設置是否適當。

   ■公式     : 100* (Innodb_buffer_pool_wait_free / Innodb_buffer_pool_write_requests)

   ■正常範圍 : 1% 如下.

   ■含義     : 爲了最佳性能,InnoDB不該等待頁寫入到InnoDB緩衝池中。

 

 

  6,InnoDB 回滾日誌寫入的等待比率

   ■監視點   : innodb_log_buffer_size的設置是否適當。

   ■公式     : 100* (Innodb_log_waits / Innodb_log_writes)

   ■正常範圍 : 1% 如下.

   ■含義     : 爲了最佳性能,InnoDB不該等待SQL操做寫入到日誌。

 

  7,線程緩存大小設定值是否合適.

   ■監視點   : thread_cache_size的設置是否適當。

   ■公式     : (1-Threads_created/Connections ) *100%

   ■正常範圍 : 95% 以上.

   ■含義     : 每一個MySQL鏈接都運行在它特有的線程中。線程創建比較耗時,所以每一個鏈接關閉的時候不是殺死線程。

                服務起能保存線程在線程緩存中稍後爲新的鏈接使用,線程緩存命中率。若是這個值過小那麼就要考慮增長線程緩存的大小。

 

  8,表打開操做是否頻繁..

   ■監視點   : table_cache的設置是否適當。

   ■公式     : Opened_tables 的值,服務器運行一段時間後的值,要是一直在增加,那麼就是有問題.

   ■正常範圍 : 0 .

   ■含義     : MySQL每次訪問表就把它放在表緩存中。如何系統訪問不少表那麼在緩存中會更快一些。

                Opened_tables就是沒有經過表緩存中打開表的數量,若是這個數值高或者增加的很快那麼就須要增長table_cache的值。

 

  9,查詢緩存碎片狀況 .

   ■監視點   : 查詢緩存中各個使用塊的狀況,若是單個塊中有空閒的,那麼此項監控就高.

   ■公式     : 100 * Qcache_free_blocks / Qcache_total_blocks

   ■正常範圍 : 低於 70% .

   ■含義     : 若是你有不少小的查詢結果,這個值可能會很高,請考慮下面的選項:一、減小query_cache_min_res_unit值

                二、執行FLUSH QUERY CACHE對查詢緩存進行碎片整理。

 

  10,查詢修剪(從緩存中刪除,由於內存不夠)與插入查詢緩存的比率。

   ■監視點   : 從查詢緩存中刪除的整體量和插入的的比例.

   ■公式     : Qcache_lowmem_prunes / Qcache_inserts

   ■正常範圍 :

   ■含義     : 放入緩存的數量 與 被迫從緩存中擠出去的數量的比值.

                被擠的狀況有某個查詢結果集過久沒有複用,來了新的結果集,緩存中沒有空了.

                          也多是,緩存的結果集涉及到的表更新比較頻繁,在下次利用的時候,

                          發現已是髒的數據了,因而就擠出來,在從新裝載.

                 這個值能反映出 查詢緩存是否是一個穩定的查詢緩存,有沒有必要使用查詢緩存.

 

 

  11,查詢緩存命中率(從緩存中刪除,由於內存不夠)與插入查詢緩存的比率。

   ■監視點   : 一個查詢的結果能被複用的比例.

   ■公式     : Qcache_hits / (Qcache_inserts + Qcache_hits)

   ■正常範圍 :

   ■含義     :  查詢緩存應該爲此一個高的命中率。高命中率表示其餘的鏈接可使用查詢緩存中結果。

                 低命中率說明沒有足夠的內存分配給它,或者查詢沒有在服務器上再三的執行。

 

 

  12, sort_buffer_size 的大小是否合適.

   ■監視點   : 排序算法已經執行的合併的數量。若是這個變量值較大,應考慮增長sort_buffer_size系統變量的值。

   ■公式     : Sort_merge_passes

   ■正常範圍 :

   ■含義     :  

   

  13, read_rnd_buffer_size 的大小是否合適.

   ■監視點   :  暫時無

   ■公式     : 

   ■正常範圍 :

   ■含義     :   當排序後按排序後的順序讀取行時,則經過該緩衝區讀取行,避免搜索硬盤。

                  將該變量設置爲較大的值能夠大大改進ORDER BY的性能。可是,這是爲每一個客戶端分配的緩衝區,

                  所以你不該將全局變量設置爲較大的值。相反,只爲須要運行大查詢的客戶端更改會話變量。

 

14, read_rnd_buffer_size 的大小是否合適.

   ■監視點   :  表鎖的次數.

   ■公式     :  Table_locks_waited / (Table_locks_waited + Table_locks_immediate)

   ■正常範圍 :  10% 之內

   ■含義     :  對 MyISAM 表,所表是發生在讀和寫他們兩兩之間的,是併發性很低的,因此若是這個值高的話,

                 須要拷考慮進行表類型的更改.

 

 

15, Percentage of full table scans .

   ■監視點   :  全表掃描的比率.

   ■公式     :  (Handler_read_rnd_next + Handler_read_rnd) / (Handler_read_rnd_next + Handler_read_rnd + Handler_read_first + Handler_read_next + Handler_read_key + Handler_read_prev)

   ■正常範圍 :  20% 之內

   ■含義     :  要盡力保持很小的值。設法隔離沒使用索引的查詢。使用慢查詢日誌記錄哪些運行時間較長的查詢。

 

16, Select_full_join .

   ■監視點   :  沒有使用索引的聯接的數量.

   ■公式     :  Select_full_join

   ■正常範圍 :  20% 之內

   ■含義     :  沒有使用索引的聯接的數量。若是該值不爲0,你應仔細檢查表的索引。。

 

17, binlog_cache_size 的大小是否合適.

   ■監視點   :  表鎖的次數.

   ■公式     :  Binlog_cache_disk_use / Binlog_cache_use

   ■正常範圍 :  10% 之內

   ■含義     :  增長這個值而且監控這個值。當命中率達到能夠接受的水平將binlog_cache_size參數添加到MySQL配置文件中。

  

18, tmp_table_size,max_heap_table_size  的大小是否合適.

   ■監視點   :  使用臨時表的次數.

   ■公式     :  Created_tmp_disk_tables / Created_tmp_tables

   ■正常範圍 :  50% 之內

   ■含義     :  若是這個值過高了那麼就要考慮增長tmp_table_size和max_heap_table_size大小。

                 臨時表的TEXT or BLOBS 字段要保存在磁盤上,所以設法改變TEXT或者BLOBS字段類型。

(完)
相關文章
相關標籤/搜索