在Web應用程序體系架構中,數據持久層(一般是一個關係數據庫)是關鍵的核心部分,它對系統的性能有很是重要的影響。MySQL是目前使用最多的開源數據庫,可是MySQL數據庫的默認設置性能很是的差,僅僅是一個玩具數據庫。所以在產品中使用MySQL數據庫必須進行必要的優化。
優化是一個複雜的任務,本文描述MySQL相關的數據庫設計和查詢優化,服務器端優化,存儲引擎優化。java
在MySQL Server性能調優中,首先要考慮的就是Database Schema設計,這一點是很是重要的。一個糟糕的Schema設計即便在性能調優的MySQL Server上運行,也會表現出不好的性能;和Schema類似,查詢語句的設計也會影響MySQL的性能,應該避免寫出低效的SQL查詢。這一節將詳細討論這兩方面的優化。mysql
Schema的優化取決於將要運行什麼樣的query,不一樣的query會有不一樣的Schema優化方案。2.2節將介紹Query Design的優化。Schema設計一樣受到預期數據集大小的影響。Schema設計時主要考慮:標準化,數據類型,索引。c++
標準化是在數據庫中組織數據的過程。其中包括,根據設計規則建立表並在這些表間創建關係;經過取消冗餘度與不一致相關性,該設計規則能夠同時保護數據並提升數據的靈活性。一般數據庫標準化是讓數據庫設計符合某一級別的範式,一般知足第三範式便可。也有第四範式(也稱爲 Boyce Codd範式,BCNF))與第五範式存在,可是在實際設計中不多考慮。忽視這些規則可能使得數據庫的設計不太完美,但這不該影響功能。
標準化的特色:sql
1) 全部的「對象」都在它本身的table中,沒有冗餘。
2) 數據庫一般由E-R圖生成。
3) 簡潔,更新屬性一般只須要更新不多的記錄。
4) Join操做比較耗時。
5) Select,sort優化措施比較少。
6) 適用於OLTP應用。
非標準化的特色:數據庫
1) 在一張表中存儲不少數據,數據冗餘。
2) 更新數據開銷很大,更新一個屬性可能會更新不少表,不少記錄。
3) 在刪除數據是有可能丟失數據。
4) Select,order有不少優化的選擇。
5) 適用於DSS應用。
標準化和非標準化都有各自的優缺點,一般在一個數據庫設計中能夠混合使用,一部分表格標準化,一部分表格保留一些冗餘數據:緩存
1) 對OLTP使用標準化,對DSS使用非標準化
2) 使用物化視圖。MySQL不直接支持該數據庫特性,可是能夠用MyISAM表代替。
3) 冗餘一些數據在表格中,例如將ref_id和name存在同一張表中。可是要注意更新問題。
4) 對於一些簡單的對象,直接使用value做爲建。例如IP address等
5) Reference by PRIMARY/UNIQUE KEY。MySQL能夠優化這種操做,例如:
java 代碼
- select city_name
- from city,state
- where state_id=state.id and state.code=‘CA’」 converted to 「select city_name from city where state_id=12
最基本的優化之一就是使表在磁盤上佔據的空間儘量小。這能帶來性能很是大的提高,由於數據小,磁盤讀入較快,而且在查詢過程當中表內容被處理所佔用的內存更少。同時,在更小的列上建索引,索引也會佔用更少的資源。
可使用下面的技術可使表的性能更好而且使存儲空間最小:安全
1) 使用正確合適的類型,不要將數字存儲爲字符串。
2) 儘量地使用最有效(最小)的數據類型。MySQL有不少節省磁盤空間和內存的專業化類型。
3) 儘量使用較小的整數類型使表更小。例如,MEDIUMINT常常比INT好一些,由於MEDIUMINT列使用的空間要少25%。
4) 若是可能,聲明列爲NOT NULL。它使任何事情更快並且每列能夠節省一位。注意若是在應用程序中確實須要NULL,應該毫無疑問使用它,只是避免 默認地在全部列上有它。
5) 對於MyISAM表,若是沒有任何變長列(VARCHAR、TEXT或BLOB列),使用固定尺寸的記錄格式。這比較快可是不幸地可能會浪費一些空間。即便你已經用CREATE選項讓VARCHAR列ROW_FORMAT=fixed,也能夠提示想使用固定長度的行。
6) 使用sample character set,例如latin1。儘可能少使用utf-8,由於utf-8佔用的空間是latin1的3倍。能夠在不須要使用utf-8的字段上面使用latin1,例如mail,url等。
全部MySQL列類型能夠被索引。對相關列使用索引是提升SELECT操做性能的最佳途徑。使用索引應該注意如下幾點:服務器
1) MySQL只會使用前綴,例如key(a, b) …where b=5 將使用不到索引。
2) 要選擇性的使用索引。在變化不多的列上使用索引並非很好,例如性別列。
3) 在Unique列上定義Unique index。
4) 避免創建使用不到的索引。
5) 在Btree index中(InnoDB使用Btree),能夠在須要排序的列上創建索引。
6) 避免重複的索引。
7) 避免在已有索引的前綴上創建索引。例如:若是存在index(a,b)則去掉index(a)。
8) 控制單個索引的長度。使用key(name(8))在數據的前面幾個字符創建索引。
9) 越是短的鍵值越好,最好使用integer。
10) 在查詢中要使用到索引(使用explain查看),能夠減小讀磁盤的次數,加速讀取數據。
11) 相近的鍵值比隨機好。Auto_increment就比uuid好。
12) Optimize table能夠壓縮和排序index,注意不要頻繁運行。
13) Analyze table能夠更新數據。
查詢語句的優化是一個Case by case的問題,不一樣的sql有不一樣的優化方案,在這裏我只列出一些通用的技巧。架構
1) 在有index的狀況下,儘可能保證查詢使用了正確的index。可使用EXPLAIN select …查看結果,分析查詢。
2) 查詢時使用匹配的類型。例如select * from a where id=5, 若是這裏id是字符類型,同時有index,這條查詢則使用不到index,會作全表掃描,速度會很慢。正確的應該是 … where id=」5」 ,加上引號代表類型是字符。
3) 使用--log-slow-queries –long-query-time=2查看查詢比較慢的語句。而後使用explain分析查詢,作出優化。
MySQL有不少發行版本,最好使用MySQL AB發佈的二進制版本。也能夠下載源代碼進行編譯安裝,可是編譯器和類庫的一些bug可能會使編譯完成的MySQL存在潛在的問題。
若是安裝MySQL的服務器使用的是Intel公司的處理器,可使用intel c++編譯的版本,在Linux World2005的一篇PPT中提到,使用intel C++編譯器編譯的MySQL查詢速度比正常版本快30%左右。Intel c++編譯版本能夠在MySQL官方網站下載。併發
MySQL默認的設置性能不好,因此要作一些參數的調整。這一節介紹一些通用的參數調整,不涉及具體的存儲引擎(主要指MyISAM,InnoDB,相關優化在4中介紹)。
--character-set:若是是單一語言使用簡單的character set例如latin1。儘可能少用Utf-8,utf-8佔用空間較多。
--memlock:鎖定MySQL只能運行在內存中,避免swapping,可是若是內存不夠時有可能出現錯誤。
--max_allowed_packet:要足夠大,以適應比較大的SQL查詢,對性能沒有太大影響,主要是避免出現packet錯誤。
--max_connections:server容許的最大鏈接。太大的話會出現out of memory。
--table_cache:MySQL在同一時間保持打開的table的數量。打開table開銷比較大。通常設置爲512。
--query_cache_size: 用於緩存查詢的內存大小。
--datadir:mysql存放數據的根目錄,和安裝文件分開在不一樣的磁盤能夠提升一點性能。
MySQL支持不一樣的存儲引擎,主要使用的有MyISAM和InnoDB。
MyISAM管理非事務表。它提供高速存儲和檢索,以及全文搜索能力。MyISAM在全部MySQL配置裏被支持,它是默認的存儲引擎,除非配置MySQL默認使用另一個引擎。
1) 不支持事務,宕機會破壞表
2) 使用較小的內存和磁盤空間
3) 基於表的鎖,併發更新數據會出現嚴重性能問題
4) MySQL只緩存Index,數據由OS緩存
1) 日誌系統
2) 只讀或者絕大部分是讀操做的應用
3) 全表掃描
4) 批量導入數據
5) 沒有事務的低併發讀/寫
1) 聲明列爲NOT NULL,能夠減小磁盤存儲。
2) 使用optimize table作碎片整理,回收空閒空間。注意僅僅在很是大的數據變化後運行。
3) Deleting/updating/adding大量數據的時候禁止使用index。使用ALTER TABLE t DISABLE KEYS。
4) 設置myisam_max_[extra]_sort_file_size足夠大,能夠顯著提升repair table的速度。
1) 避免併發insert,update。
2) 可使用insert delayed,可是有可能丟失數據。
3) 優化查詢語句。
4) 水平分區。
5) 垂直分區。
6) 若是都不起做用,使用InnoDB。
1) 設置key_buffer_size variable。MyISAN最主要的cache設置,用於緩存MyISAM表格的index數據,該參數只對MyISAM有影響。一般在只使用MyISAM的Server中設置25-33%的內存大小。
2) 可使用幾個不一樣的Key Caches(對一些hot data)。
a) SET GLOBAL test.key_buffer_size=512*1024;2) Preload index到Cache中能夠提升查詢速度。由於preloading index是順序的,因此很是快。
b) CACHE INDEX t1.i1, t2.i1, t3 IN test;
a) LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES;
InnoDB給MySQL提供了具備提交,回滾和崩潰恢復能力的事務安全(ACID兼容)存儲引擎。InnoDB提供row level lock,而且也在SELECT語句提供一個Oracle風格一致的非鎖定讀。這些特點增長了多用戶部署和性能。沒有在InnoDB中擴大鎖定的須要,由於在InnoDB中row level lock適合很是小的空間。InnoDB也支持FOREIGN KEY約束。在SQL查詢中,你能夠自由地將InnoDB類型的表與其它MySQL的表的類型混合起來,甚至在同一個查詢中也能夠混合。
InnoDB是爲在處理巨大數據量時得到最大性能而設計的。它的CPU使用效率很是高。
InnoDB存儲引擎已經徹底與MySQL服務器整合,InnoDB存儲引擎爲在內存中緩存數據和索引而維持它本身的緩衝池。 InnoDB存儲它的表&索引在一個表空間中,表空間能夠包含數個文件(或原始磁盤分區)。這與MyISAM表不一樣,好比在MyISAM表中每一個表被存在分離的文件中。InnoDB 表能夠是任何大小,即便在文件尺寸被限制爲2GB的操做系統上。
許多須要高性能的大型數據庫站點上使用了InnoDB引擎。著名的Internet新聞站點Slashdot.org運行在InnoDB上。 Mytrix, Inc.在InnoDB上存儲超過1TB的數據,還有一些其它站點在InnoDB上處理平均每秒800次插入/更新的負荷。
1) 支持事務,ACID,外鍵。
2) Row level locks。
3) 支持不一樣的隔離級別。
4) 和MyISAM相比須要較多的內存和磁盤空間。
5) 沒有鍵壓縮。
6) 數據和索引都緩存在內存hash表中。
1) 須要事務的應用。
2) 高併發的應用。
3) 自動恢復。
4) 較快速的基於主鍵的操做。
1) 儘可能使用short,integer的主鍵。
2) Load/Insert數據時按主鍵順序。若是數據沒有按主鍵排序,先排序而後再進行數據庫操做。
3) 在Load數據是爲設置SET UNIQUE_CHECKS=0,SET FOREIGN_KEY_CHECKS=0,能夠避免外鍵和惟一性約束檢查的開銷。
4) 使用prefix keys。由於InnoDB沒有key壓縮功能。
innodb_buffer_pool_size:這是InnoDB最重要的設置,對InnoDB性能有決定性的影響。默認的設置只有8M,因此默認的數據庫設置下面InnoDB性能不好。在只有InnoDB存儲引擎的數據庫服務器上面,能夠設置60-80%的內存。更精確一點,在內存容量容許的狀況下面設置比InnoDB tablespaces大10%的內存大小。
innodb_data_file_path:指定表數據和索引存儲的空間,能夠是一個或者多個文件。最後一個數據文件必須是自動擴充的,也只有最後一個文件容許自動擴充。這樣,當空間用完後,自動擴充數據文件就會自動增加(以8MB爲單位)以容納額外的數據。例如: innodb_data_file_path=/disk1/ibdata1:900M;/disk2/ibdata2:50M:autoextend兩個數據文件放在不一樣的磁盤上。數據首先放在ibdata1中,當達到900M之後,數據就放在ibdata2中。一旦達到50MB,ibdata2將以8MB爲單位自動增加。若是磁盤滿了,須要在另外的磁盤上面增長一個數據文件。
innodb_autoextend_increment: 默認是8M, 若是一次insert數據量比較多的話, 能夠適當增長.
innodb_data_home_dir:放置表空間數據的目錄,默認在mysql的數據目錄,設置到和MySQL安裝文件不一樣的分區能夠提升性能。
innodb_log_file_size:該參數決定了recovery speed。太大的話recovery就會比較慢,過小了影響查詢性能,通常取256M能夠兼顧性能和recovery的速度
。
innodb_log_buffer_size:磁盤速度是很慢的,直接將log寫道磁盤會影響InnoDB的性能,該參數設定了log buffer的大小,通常4M。若是有大的blob操做,能夠適當增大。
innodb_flush_logs_at_trx_commit=2: 該參數設定了事務提交時內存中log信息的處理。
1) =1時,在每一個事務提交時,日誌緩衝被寫到日誌文件,對日誌文件作到磁盤操做的刷新。Truly ACID。速度慢。innodb_file_per_table:能夠存儲每一個InnoDB表和它的索引在它本身的文件中。
2) =2時,在每一個事務提交時,日誌緩衝被寫到文件,但不對日誌文件作到磁盤操做的刷新。只有操做系統崩潰或掉電纔會刪除最後一秒的事務,否則不會丟失事務。
3) =0時, 日誌緩衝每秒一次地被寫到日誌文件,而且對日誌文件作到磁盤操做的刷新。任何mysqld進程的崩潰會刪除崩潰前最後一秒的事務
transaction-isolation=READ-COMITTED: 若是應用程序能夠運行在READ-COMMITED隔離級別,作此設定會有必定的性能提高。
innodb_flush_method: 設置InnoDB同步IO的方式:
1) Default – 使用fsync()。innodb_thread_concurrency: InnoDB kernel最大的線程數。
2) O_SYNC 以sync模式打開文件,一般比較慢。
3) O_DIRECT,在Linux上使用Direct IO。能夠顯著提升速度,特別是在RAID系統上。避免額外的數據複製和double buffering(mysql buffering 和OS buffering)。
1) 最少設置爲(num_disks+num_cpus)*2。
2) 能夠經過設置成1000來禁止這個限制
緩存有不少種,爲應用程序加上適當的緩存策略會顯著提升應用程序的性能。因爲應用緩存是一個比較大的話題,因此這一部分還須要進一步調研。
1) http://www.mysqlperformanceblog.com/2) Advanced MySQL Performance Optimization, Peter Zaitsev, Tobias Asplund, MySQL Users Conference 20053) Improving MySQL Server Performance with Intel C++ Compiler,Peter Zaitsev,Linux World 20054) MySQL Performance Optimization, Peter Zaitsev, Percona Ltd, OPEN SOURCE DATABASE CONFERENCE 20065) MySQL Server Settings Tuning, Peter Zaitsev, co-founder, Percona Ltd, 20076) MySQL Reference Manual