做優化,我在這裏引用淘寶系統分析師蔣江偉的一句話:只有勇於承擔,才能讓人有勇氣,有承擔自己的錯誤的勇氣。有承擔錯誤的勇氣,就有去做事得勇氣。無論做什麼事,只要是對的,就要去做,勇敢去做。出了錯誤,承擔錯誤,改正錯誤,這樣的人沒有人會去責怪。
很久之前就想寫一篇關於mysql優化方面的文章了,忙於工作,在着也比較懶散。現在網上mysql優化方面的帖子很多,也不乏精品。很早聽一DBA前輩說過:mysql的優化,三分配置的優化,七分sql語句的優化。之前不是很理解,但接觸到高併發大數據的時候,這句話的含義尤爲突出,甚至可以說一分配置的優化,九分語句的優化。
mysql的優化,一般分爲配置的優化、sql語句的優化、表結構的優化、索引的優化,這裏着重說前兩點。
一、配置的優化
配置的優化其實包含兩個方面的:操作系統內核的優化和mysql配置文件的優化
1、系統內核的優化對專用的mysql服務器來說,無非是內存實用、連接數、超時處理、TCP處理等方面的優化,根據自己的硬件配置來進行優化,這裏不多講;
2、mysql配置的優化,一般來說包含:IO處理的常用參數、最大連接數設置、緩存使用參數的設置、慢日誌的參數的設置、innodb相關參數的設置等,如果有主從關係在設置主從同步的相關參數即可,網上的相關配置文件很多,大同小異,常用的設置大多修改這些差不多就夠用了。
以下是我的一個mysql配置文件,僅供參考:
###############################################
[mysqld]
skip-name-resolve
server-id = 1
bind-address = 0.0.0.0
port = 3306
datadir = /home/mysql
tmpdir = /tmp
default_storage_engine = InnoDB
character_set_server = utf8
innodb_file_per_table = 1
innodb_log_file_size = 512M
innodb_log_files_in_group = 4
innodb_rollback_on_timeout = 1
slow_query_log = 1
slow_query_log_file =/var/log/mysql/mysql-slow.log
long_query_time = 1
#log-queries-not-using-indexes#這個參數不安全,說是記錄沒有用到索引的語句,其實記錄的全部的日誌,佔用大量的IO,建議不要打開
#relay_log_recovery=1#這個參數在叢庫上一定要加上
query_cache_type = off
query_cache_size = 0
#這兩項是禁用緩存,這個使服務器用途而定:寫比較多的數據庫最好禁用,因爲沒寫一次他要修改緩存中的數據,給數據庫帶來額外的開銷,讀比較的可以開啓,可以提高查詢效率
#一下4個參數是mysql5.6上的新特性
innodb_buffer_pool_dump_at_shutdown = 1 #解釋:在關閉時把熱數據dump到本地磁盤。
innodb_buffer_pool_dump_now = 1 #解釋:採用手工方式把熱數據dump到本地磁盤。
innodb_buffer_pool_load_at_startup = 1 #解釋:在啓動時把熱數據加載到內存。
innodb_buffer_pool_load_now = 1 #解釋:採用手工方式把熱數據加載到內存。
read_buffer_size = 2M
sort_buffer_size = 2M
join_buffer_size = 1M
key_buffer_size = 2G
thread_cache_size = 2048
open_files_limit=65535
innodb_open_files = 8192
max_allowed_packet = 64M
thread_stack = 512k
max_length_for_sort_data = 16k
tmp_table_size = 256M
max_heap_table_size = 256M
max_connections = 4000
max_connect_errors = 30000
innodb_read_io_threads = 8
innodb_write_io_threads = 16
innodb_flush_method = O_DIRECT
innodb_io_capacity = 20000#根據硬盤的情況修改,stat的用100,sas的200,sas做riad10的爲400fision-io的可以設置爲20000
innodb_buffer_pool_size = 72G#內存的80%
innodb_buffer_pool_instances=18
thread_concurrency=0
innodb_thread_concurrency = 0
innodb_log_buffer_size = 16M
innodb_flush_log_at_trx_commit = 2
innodb_lock_wait_timeout = 60
innodb_old_blocks_time=1000
innodb_use_native_aio = 1
innodb_purge_threads=1
innodb_change_buffering=inserts
##############################################
二、sql語句的優化
前期的配置優化做完,其實在很長的一段時間內,基本上不用在去優化了。而一條sql使用不當,致使整個數據庫故障的情況相信做DBA的經常遇到。所以,sql語句的優化和審覈纔是每個DBA的重中之重。
sql語句的優化,一般的基本原則有以下幾個方面:
1、 儘量稍作計算
Mysql的作用是用來存取數據的,不是做計算的,做計算的話可以用其他方法去實現,mysql做計算是很耗資源的。
2.儘量少 join
MySQL 的優勢在於簡單,但這在某些方面其實也是其劣勢。MySQL 優化器效率高,但是由於其統計信息的量有限,優化器工作過程出現偏差的可能性也就更多。對於複雜的多表 Join,一方面由於其優化器受限,再者在 Join 這方面所下的功夫還不夠,所以性能表現離 Oracle 等關係型數據庫前輩還是有一定距離。但如果是簡單的單表查詢,這一差距就會極小甚至在有些場景下要優於這些數據庫前輩。
3.儘量少排序
排序操作會消耗較多的 CPU 資源,所以減少排序可以在緩存命中率高等 IO 能力足夠的場景下會較大影響 SQL 的響應時間。
對於MySQL來說,減少排序有多種辦法,比如:
通過利用索引來排序的方式進行優化
減少參與排序的記錄條數
非必要不對數據進行排序
4.儘量避免 select *
在數據量少並且訪問量不大的情況下,select * 沒有什麼影響,但是量級達到一定級別的時候,在執行效率和IO資源的使用上,還是有很大關係的,用什麼字段取什麼字段,減少不必要的資源浪費。
之前遇到過因爲一個字段存儲的數據比較大,併發高的情況下把網絡帶寬跑滿的情況,造成網站打不開或是打開速度極慢的情況。
5.儘量用 join 代替子查詢
雖然 Join 性能並不佳,但是和 MySQL 的子查詢比起來還是有非常大的性能優勢。MySQL 的子查詢執行計劃一直存在較大的問題,雖然這個問題已經存在多年,但是到目前已經發布的所有穩定版本中都普遍存在,一直沒有太大改善。雖然官方也在很早就承認這一問題,並且承諾儘快解決,但是至少到目前爲止我們還沒有看到哪一個版本較好的解決了這一問題。
6.儘量少 or
當 where 子句中存在多個條件以“或”並存的時候,MySQL 的優化器並沒有很好的解決其執行計劃優化問題,再加上 MySQL 特有的 SQL 與 Storage 分層架構方式,造成了其性能比較低下,很多時候使用 union all 或者是union(必要的時候)的方式來代替“or”會得到更好的效果。
7.儘量用 union all 代替 union
union 和 union all 的差異主要是前者需要將兩個(或者多個)結果集合並後再進行唯一性過濾操作,這就會涉及到排序,增加大量的 CPU 運算,加大資源消耗及延遲。所以當我們可以確認不可能出現重複結果集或者不在乎重複結果集的時候,儘量使用 union all 而不是 union。
8.儘量早過濾
這一優化策略其實最常見於索引的優化設計中(將過濾性更好的字段放得更靠前)。
在 SQL 編寫中同樣可以使用這一原則來優化一些 Join 的 SQL。比如我們在多個表進行分頁數據查詢的時候,我們最好是能夠在一個表上先過濾好數據分好頁,然後再用分好頁的結果集與另外的表 Join,這樣可以儘可能多的減少不必要的 IO 操作,大大節省 IO 操作所消耗的時間。
9.避免類型轉換
這裏所說的“類型轉換”是指 where 子句中出現 column 字段的類型和傳入的參數類型不一致的時候發生的類型轉換:
A:人爲在column_name 上通過轉換函數進行轉換
直接導致 MySQL(實際上其他數據庫也會有同樣的問題)無法使用索引,如果非要轉換,應該在傳入的參數上進行轉換
B:由數據庫自己進行轉換
如果我們傳入的數據類型和字段類型不一致,同時我們又沒有做任何類型轉換處理,MySQL 可能會自己對我們的數據進行類型轉換操作,也可能不進行處理而交由存儲引擎去處理,這樣一來,就會出現索引無法使用的情況而造成執行計劃問題。
以上兩種情況在開發者因爲某種原因經常會有,本來可以用到索引的結果類型不對沒有用到索引,或是因爲類型不對又有越界的情況發生造成無法使用索引的情況,結果造成很嚴重的事故。
10.優先優化高併發的 SQL,而不是執行頻率低某些“大”SQL
對於破壞性來說,高併發的 SQL 總是會比低頻率的來得大,因爲高併發的 SQL 一旦出現問題,甚至不會給我們任何喘息的機會就會將系統壓跨。而對於一些雖然需要消耗大量 IO 而且響應很慢的 SQL,由於頻率低,即使遇到,最多就是讓整個系統響應慢一點,但至少可能撐一會兒,讓我們有緩衝的機會。
11.從全局出發優化,而不是片面調整
SQL 優化不能是單獨針對某一個進行,而應充分考慮系統中所有的 SQL,尤其是在通過調整索引優化 SQL 的執行計劃的時候,千萬不能顧此失彼,因小失大。
12.儘可能對每一條運行在數據庫中的SQL進行 explain
優化 SQL,需要做到心中有數,知道SQL 的執行計劃才能判斷是否有優化餘地,才能判斷是否存在執行計劃問題。在對數據庫中運行的 SQL 進行了一段時間的優化之後,很明顯的問題 SQL 可能已經很少了,大多都需要去發掘,這時候就需要進行大量的 explain 操作收集執行計劃,並判斷是否需要進行優化。
題外話:很多剛接觸mysql的朋友問,有什麼mysql專業書籍推薦?我推薦《MySQL高性能》第三版,是mysql 領域的經典之作,擁有廣泛的影響力,涵蓋的知識面也比較面,一本在手,基礎知識無憂,其他需要在實戰中積累經驗了,本人也是此書的受益者。