1. 程式人生 > 實用技巧 >Mysql--調優

Mysql--調優

1 SQL以及索引的優化

 根據需求寫出結構良好的SQL,然後根據SQL在表中建立合適的索引,一般在主鍵列或經常用於查詢條件的列建立索引

 開啟慢查詢日誌定位到具體的出問題的SQL,然後使用explain、profile等工具來逐步調優,最後經過測試達到效果後上線

 注:如果索引太多,或在頻繁修改的表上建立索引反而會增大資料庫的開銷,降低資料庫的效能

2 資料表結構

 資料庫三正規化:

  第一正規化  資料表中每個欄位都必須是不可拆分的最小單元,也就是確保每一列的原子性

  第二正規化  在滿足第一正規化的基礎上,表中每一條記錄必須可以被唯一地區分

  第三正規化  在2NF基礎上,任何非主屬性不依賴於其它非主屬性(在2NF基礎上消除傳遞依賴)

 注:一個完全正規化化設計的資料庫經常會面臨“查詢緩慢”這個問題,資料庫越正規化化,就需要Join越多的表

 可以根據場景合理地部分反規範化:

  欄位過多時分割表

  保留部分冗餘欄位,當兩個或多個表在查詢中經常需要連線時,可以在其中一個表上增加若干冗餘的欄位,以避免表之間的連線過於頻繁,一般在冗餘列的資料不經常變動的情況下使用

  增加派生列,派生列是由表中的其它多個列的計算所得,增加派生列可以減少統計運算,在資料彙總時可以大大縮短運算時間

 欄位型別選擇:

  儘量使用TINYINT、SMALLINT、MEDIUM_INT作為整數型別而非INT,如果非負則加上UNSIGNED

  VARCHAR的長度只分配真正需要的空間

  使用列舉或整數代替字串型別

  儘量使用TIMESTAMP而非DATETIME

  避免使用NULL欄位,很難查詢優化且佔用額外索引空間

 約束:

  設定合理約束(非空、唯一、自增、預設、主鍵、外來鍵)

3系統配置

 服務狀態檢視:

show status; 檢視伺服器狀態
show status like 'Connections'; 查詢連線mysql伺服器次數
show status like 'Uptime'; 查詢當前MySQL本次啟動後的執行統計時間
show status like 'slow_queries' 查詢慢查詢次數
show global status like 'com_select'; 查詢自當前MySQL啟動後所有連線執行的SELECT語句總數
show status like 'Thread_%'; 檢視MySQL伺服器的執行緒資訊
show[GLOBAL] status like 'com_insert'; 檢視insert語句的執行數
show status like 'com_select'; 查詢本次MySQL啟動後執行的SELECT語句的次數
show variables; 檢視變數(設定)
show warnings; 檢視最近一個sql語句產生的錯誤警告,看其他的需要看.err日誌
show [full] processlist; 顯示伺服器中正在執行的連線程序
show errors; 顯示錯誤

 

 

 引數配置優化(my.cnf):

max_connections=2000       #MySQL允許最大的程序連線數,如果經常出現TooManyConnections的錯誤提示,則需要增大此值
max_connect_errors=6000     #設定每個主機的連線請求異常中斷的最大次數,當超過該次數,MYSQL伺服器將禁止host的連線請求,直到mysql伺服器重啟或通過flushhosts命令清空此host的相關資訊
table_open_cache=2048       #指示表調整緩衝區大小,table_open_cache引數設定表快取記憶體的數目
                     #每個連線進來,都會至少開啟一個表快取。因此,table_open_cache的大小應與max_connections的設定有關
                     #當Mysql訪問一個表時,如果該表在快取中已經被開啟,則可以直接訪問快取
                     #如果還沒有被快取,但是在Mysql表緩衝區中還有空間,那麼這個表就被開啟並放入表緩衝區
                     #如果表快取滿了,則會按照一定的規則將當前未用的表釋放,或者臨時擴大表快取來存放
                     #使用表快取的好處是可以更快速地訪問表中的內容,執行flushtables會清空快取的內容
                     #一般來說,可以通過檢視資料庫執行峰值時間的狀態值Open_tables和Opened_tables,判斷是否需要增加table_open_cache的值
                     #其中open_tables是當前開啟的表的數量,Opened_tables則是已經開啟的表的數量
                     #即如果open_tables接近table_open_cache的時候,並且Opened_tables這個值在逐步增加,那就要考慮增加這個值的大小了
                     #當Table_locks_waited比較高的時候,也需要增加table_open_cache
                     #注:不能盲目地把table_open_cache設定成很大的值,設定太大超過了shell的檔案描述符(通過ulimit -n檢視),造成檔案描述符不足,從而造成效能不穩定或者連線失敗
                     #較為合適的值:Open_tables / Opened_tables >= 0.85 Open_tables / table_open_cache <= 0.95
external-locking=FALSE      #使用–skip-external-lockingMySQL選項以避免外部鎖定,該選項預設開啟
max_allowed_packet
=32M      #設定在網路傳輸中一次訊息傳輸量的最大值,系統預設值為1MB,最大值是1GB,必須設定1024的倍數
sort_buffer_size
=1M        #Sort_Buffer_Size是一個connection級引數,在每個connection(session)第一次需要使用這個buffer的時候,一次性分配設定的記憶體
                    #Sort_Buffer_Size並不是越大越好,由於是connection級的引數,過大的設定+高併發可能會耗盡系統記憶體資源
                    #在Linux上,有256KB和2MB的閾值,較大的值可能會顯著降低記憶體分配
join_buffer_size
=1M        #用於表間關聯快取的大小,和sort_buffer_size一樣,該引數對應的分配記憶體也是每個連線獨享
                    #如果join語句不多,不用調整此引數, 如果join語句較多的話, 可將此引數調整到1M, 如果記憶體充足,還可增大到2M
thread_cache_size
=300       #伺服器執行緒快取,這個值表示可以重新利用儲存在快取中執行緒的數量
                     #當斷開連線時如果快取中還有空間,那麼客戶端的執行緒將被放到快取中,如果執行緒重新被請求,那麼請求將從快取中讀取
                     #如果快取中是空的或者是新的請求,那麼這個執行緒將被重新建立,如果有很多新的執行緒,增加這個值可以改善系統性能
                     #通過比較Connections和Threads_created狀態的變數,可以看到這個變數的作用
                     #設定規則如下:1GB記憶體配置為8,2GB配置為16,3GB配置為32,4GB或更高記憶體,可配置更大
query_cache_size=64M       #一個SELECT查詢在DB中工作後,DB會把該語句快取下來,當同樣的一個SQL再次來到DB裡呼叫時,DB在該表沒發生變化的情況下把結果從快取中返回給Client
                    #注:DB在利用Query_cache工作時,要求該語句涉及的表在這段時間內沒有發生變更
                    #  如果有變化,首先要把Query_cache和該表相關的語句全部置為失效,然後再寫入更新
                    #  如果Query_cache非常大,該表的查詢結構又比較多,查詢語句失效也慢,一個更新或是Insert就會很慢,這樣看到的就是Update或是Insert怎麼這麼慢了
                    #所以在資料庫寫入量或是更新量也比較大的系統,該引數不適合分配過大,而且在高併發,寫入量大的系統,建議把該功能禁掉
query_cache_limit
=4M       #指定單個查詢能夠使用的緩衝區大小,預設為1M
query_cache_min_res_unit
=2k   #預設是4KB,設定值大對大資料查詢有好處,但如果查詢都是小資料查詢,就容易造成記憶體碎片和浪費
                    #查詢快取碎片率=Qcache_free_blocks/Qcache_total_blocks*100%
                    #如果查詢快取碎片率超過20%,可以用FLUSHQUERYCACHE整理快取碎片,或者試試減小query_cache_min_res_unit,如果查詢都是小資料量的話
                    #查詢快取利用率=(query_cache_size–Qcache_free_memory)/query_cache_size*100%
                    #查詢快取利用率在25%以下的話說明query_cache_size設定的過大,可適當減小
                    #查詢快取利用率在80%以上而且Qcache_lowmem_prunes>50的話說明query_cache_size可能有點小,要不就是碎片太多
                    #查詢快取命中率=(Qcache_hits–Qcache_inserts)/Qcache_hits*100%
default-storage-engine=MyISAM  #修改預設儲存引擎(預設為default_table_type=InnoDB)
thread_stack
=192K         #設定MYSQL每個執行緒的堆疊大小,可設定範圍為128K至4GB,預設為192KB
tmp_table_size
=256M        #預設32M,如果一張臨時表超出該大小,MySQL產生一個Thetabletbl_nameisfull形式的錯誤
                    #如果做很多高階GROUPBY查詢,增加tmp_table_size值,如果超過該值,則會將臨時表寫入磁碟
key_buffer_size
=2048M       #批定用於索引的緩衝區大小,增加它可以得到更好的索引處理效能,對於記憶體在4GB左右的伺服器來說,該引數可設定為256MB或384MB
read_buffer_size
=1M        #MySql讀入緩衝區大小。對錶進行順序掃描的請求將分配一個讀入緩衝區,MySql會為它分配一段記憶體緩衝區,read_buffer_size變數控制這一緩衝區的大小
                     #如果對錶的順序掃描請求非常頻繁,並且頻繁掃描進行得太慢,可以通過增加該變數值以及記憶體緩衝區大小提高其效能
                     #和sort_buffer_size一樣,該引數對應的分配記憶體也是每個連線獨享
read_rnd_buffer_size
=16M     #MySql的隨機讀(查詢操作)緩衝區大小。當按任意順序讀取行時(例如,按照排序順序),將分配一個隨機讀快取區
                    #進行排序查詢時,MySql會首先掃描一遍該緩衝,以避免磁碟搜尋,提高查詢速度,如果需要排序大量資料,可適當調高該值
                    #但MySql會為每個客戶連線發放該緩衝空間,所以應儘量適當設定該值,以避免記憶體開銷過大
bulk_insert_buffer_size
=64M   #批量插入資料快取大小,可以有效提高插入效率,預設為8M
innodb_additional_mem_pool_size
=16M    #設定InnoDB儲存的資料目錄資訊和其它內部資料結構的記憶體池大小,類似於Oracle的librarycache
innodb_buffer_pool_size
=2048M        #Innodb相比MyISAM表對緩衝更為敏感
                          #MyISAM可以在預設的key_buffer_size設定下可以正常執行,然而Innodb在預設的innodb_buffer_pool_size設定下卻執行非常緩慢
                          #由於Innodb把資料和索引都快取起來,無需留給作業系統太多的記憶體,因此如果只需要用Innodb的話則可以設定它高達70-80%的可用記憶體
                          #一些應用於key_buffer的規則有—如果你的資料量不大,並且不會暴增,那麼無需把innodb_buffer_pool_size設定的太大
innodb_data_file_path
=ibdata1:1024M:autoextend  #表空間檔案重要資料, 可以設定多個
innodb_file_io_threads
=4          #檔案IO的執行緒數,一般為4,但是在Windows下,可以設定得較大
innodb_thread_concurrency=8        #伺服器有幾個CPU就設定為幾
innodb_flush_log_at_trx_commit=2     #如果將此引數設定為1,將在每次提交事務後將日誌寫入磁碟,為提供效能,可以設定為0或2,但要承擔在發生故障時丟失資料的風險
                          #0表示事務日誌寫入日誌檔案,而日誌檔案每秒重新整理到磁碟一次
                          #2表示事務日誌將在提交時寫入日誌,但日誌檔案每次重新整理到磁碟一次
innodb_log_buffer_size=16M         #日誌檔案所用的記憶體大小,以M為單位,緩衝區更大能提高效能,但意外的故障將會丟失資料.MySQL開發人員建議設定為1-8M之間
innodb_log_file_size
=128M         #資料日誌檔案的大小,以M為單位,更大的設定可以提高效能,但也會增加恢復故障資料庫所需的時間
innodb_log_files_in_group
=3        #為提高效能,MySQL可以以迴圈方式將日誌檔案寫到多個檔案。推薦設定為3M
innodb_max_dirty_pages_pct=90       #Buffer_Pool中Dirty_Page所佔的數量,直接影響InnoDB的關閉時間
                          #此引數可以直接控制Dirty_Page在Buffer_Pool中所佔的比率,而且innodb_max_dirty_pages_pct是可以動態改變的
                          #所以,在關閉InnoDB之前先將innodb_max_dirty_pages_pct調小,強制資料塊Flush一段時間,則能夠大大縮短MySQL關閉的時間
innodb_lock_wait_timeout=120       #InnoDB有其內建的死鎖檢測機制,能導致未完成的事務回滾
                         #但是,如果結合InnoDB使用MyISAM的locktables語句或第三方事務引擎,則InnoDB無法識別死鎖
                         #為消除這種可能性,可以將此引數設定為一個整數值,指示MySQL在允許其他事務修改那些最終受事務回滾的資料之前要等待多長時間(秒數)
innodb_file_per_table=0          #獨享表空間(關閉)
wait_timeout=10               #指定一個請求的最大連線時間,對於4GB左右的記憶體伺服器來說,可以將其設定為5-10

4快取

 快取作用:

  減輕資料庫的壓力,減少訪問時間

 使用場景:

  短時間內相同資料重複查詢多次且資料更新不頻繁,這個時候可以選擇先從快取查詢,查詢不到再從資料庫載入並回設到快取的方式

  高併發查詢熱點資料,後端資料庫不堪重負,可以用快取來扛

 快取選擇:

  如果資料量小,並且不會頻繁地增長又清空(這會導致頻繁地垃圾回收),那麼可以選擇本地快取

  其他情況,可以考慮快取服務,如Redis/Tair/Memcache等

 快取穿透:

  一般的快取系統,都是按照key去快取查詢,如果不存在對應的value,就應該去後端系統查詢

  如果key對應的value是一定不存在的,並且對該key併發請求量很大,就會對後端系統造成很大的壓力

  可以對查詢結果為空的情況也進行快取,快取時間設定短點,或者該key對應的資料insert了之後清理快取

 快取併發:

  有時候如果網站併發訪問高,一個快取如果失效,可能出現多個程序同時查詢DB,同時設定快取的情況

  如果併發確實很大,這也可能造成DB壓力過大,還有快取頻繁更新的問題

  可以對快取查詢加鎖,如果KEY不存在,就加鎖,然後查DB入快取,然後解鎖;其他程序如果發現有鎖就等待,然後等解鎖後返回資料或者進入DB查詢

 快取雪崩(失效):

  當快取伺服器重啟或者大量快取集中在某一個時間段失效,這樣在失效的時候,也會給後端系統造成很大壓力

  對於不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻

 防止快取空間不夠用:

  給快取服務,選擇合適的快取逐出演算法,比如最常見的LRU

  針對當前設定的容量,設定適當的警戒值,比如10G的快取,當快取資料達到8G的時候,就開始發出報警,提前排查問題或者擴容

  給一些沒有必要長期儲存的key,儘量設定過期時間