MySQL MyISAM/InnoDB高併發優化經驗

最近作的一個應用,功能要求很是簡單,就是 key/value 形式的存儲,簡單的 INSERT/SELECT,沒有任何複雜查詢,惟一的問題是量很是大,若是目前投入使用,初期的單表 insert 頻率約 20Hz(次/秒,我喜歡這個單位,讓我想起國內交流電是 50Hz),但我估計之後會有 500Hz+ 的峯值。目前的工做成果,額定功率 200Hz(CPU 佔用 10 – 20,load avg = 2),最大功率 500Hz(這時 load avg > 20,很明顯,只能暫時挺挺,應該在出現這種負載前提早拆表了)php

INSERT DELAYED INTOhtml

從 數據的插入開始提及。若是能夠容忍結果幾秒之後再生效的,能夠用 INSERT DELAYED INTO,由於在個人這個結構中不須要對同一個 key 頻繁的 INSERT/SELECT,由於 SELECT 我是用 Memcached 擋住了,除非 Memcached 掛了,或者數據實在老到過時了,纔會去 SELECT。並且要注意,若是 PHP 不須要關心 MySQL 操做的返回結果,應該使用 unbuffered query,簡單的說,在你提交 query 後,不用等待 MySQL 有返回信息就繼續執行以後的 PHP 指令,具體用法是用 mysql_unbuffered_query 代替 mysql_query,若是用的 MySQLi 類,應該使用 mysqli->query($sQuery, MYSQLI_USE_RESULT);mysql

若是 SHOW PROCESSLIST,能夠看到用戶名爲 DELAYED 的進程,進程數量等於 INSERT DELAYED 的表的數量,由於表級鎖的存在,每一個表一條以上的 DELAYED 進程是沒有意義的sql

關於這個功能的 my.cnf 配置有三條,我定爲以下值數據庫

delayed_insert_limit = 1000
delayed_insert_timeout = 300
delayed_queue_size = 5000緩存

鏈接安全

有 人說,若是報錯鏈接數過大,你把 max_connections 調大就 OK,若是隻這麼說而不講緣由,徹底是句廢話,你調成 1M 確定不會再報 Too many connections(但應該會報內存溢出之類的),但若是是這樣 MySQL 又何須給這個參數?服務器

我看到的一個頗有用的公式架構

key_buffer_size + (read_buffer_size + sort_buffer_size) * max_connections併發

之前只有很模糊的概念,應該設的很大,但又不能太大,具體多大合適,知道這個就明確了。innoDB 的公式比這個複雜點,一併給出

innodb_buffer_pool_size
+ key_buffer_size
+ max_connections * ( sort_buffer_size + read_buffer_size + binlog_cache_size )
+ max_connections * 2MB

還有一個看起來頗有用的參數 back_log,給我一種鏈接池的感受,並且它確實在起做用,我不知道若是設大了會佔用多少內存,但估計不會不少。

 

key_buffer_size

很 多文章都告訴你越大越好,要爲此分配一半左右的物理內存,這麼幹一般不會出問題,但確定不是最優的,甚至能夠說很無理頭——分多少內存應該是根據需求決 定,而不是無論什麼機器,都砍掉一半內存用做 key_buffer_size ——有的時候多是不夠,還有的時候多是浪費。

其實最關 鍵的指標,仍是看 SHOW GLOBAL STATUS 時的 Key_blocks_unused,只要還有剩餘,就說明 key_buffer_size 沒用滿。有人說看 Key_reads 跟 Key_read_requests 的比值,至少要達到 1:100。這能夠做爲一個結果來衡量,但不夠準確,由於在服務器剛啓動的時候,大多數請求都要新建緩存,緩存命中比高不起來,須要運行穩定(幾小時後) 再觀察。我我的建議仍是看 Key_blocks_unused

若是不花很長時間在運行中調試,給出一個簡單的計算方法,把數據庫填滿,達到設計時的最大值,看看這時候索引佔了多大空間,而後把全部表的索引大小加起來,就是 key_buffer_size 可能達到的最大值,固然,還要留些餘地,乘個 2 或 3 之類的。

這是我作測試的時候的 phpMyAdmin 截圖,能夠看到 key_buffer_size 被浪費了太多

OPTIMIZE TABLE

優 化一下有好處,但會鎖住表,是否值得作要權衡一下。拿我如今這個表作例子,有 text 字段,700萬條記錄,1.5G 大小,優化時間約兩分鐘,優化後性能提高了 50%,同時表的大小變爲 1.4G,但隨着表的頻繁改寫,約一天後又恢復到之前的速度,所以在我看來並不值得。

Query Cache

由於每有寫操做 Query Cache 都會被清空,除了極特殊的狀況(大量讀,少許寫,但即便這樣也應該是多用 memcached 纔對)徹底沒有必要使用這個,把 query_cache_size 設爲 0 關閉這個功能吧。

InnoDB和MyISAM是在使用MySQL最經常使用的兩個表類型,各有優缺點,視具體應用而定。基本 的差異爲:MyISAM類型不支持事務處理等高級處理,而InnoDB類型支持。MyISAM類型的表強調的是性能,其執行數度比InnoDB類型更快, 可是不提供事務支持,而InnoDB提供事務支持已經外部鍵等高級數據庫功能。

MyISAM:這個是默認類型,它是基於傳統的ISAM類型,ISAM是Indexed Sequential Access Method (有索引的順序訪問方法) 的縮寫,它是存儲記錄和文件的標準方法.與其餘存儲引擎比較,MyISAM具備檢查和修復表格的大多數工具. MyISAM表格能夠被壓縮,並且它們支持全文搜索.它們不是事務安全的,並且也不支持外鍵。若是事物回滾將形成不徹底回滾,不具備原子性。若是執行大量 的SELECT,MyISAM是更好的選擇。

InnoDB:這種類型是事務安全的.它與BDB類型具備相同的特性,它們還支持外鍵.InnoDB表格速度很快.具備比BDB還豐富的特性,所以若是需 要一個事務安全的存儲引擎,建議使用它.若是你的數據執行大量的INSERT或UPDATE,出於性能方面的考慮,應該使用InnoDB表,

對於支持事物的InnoDB類型的標,影響速度的主要緣由是AUTOCOMMIT默認設置是打開的,並且程序沒有顯式調用BEGIN 開始事務,致使每插入一條都自動Commit,嚴重影響了速度。能夠在執行sql前調用begin,多條sql造成一個事物(即便autocommit打 開也能夠),將大大提升性能。

MyIASM是IASM表的新版本,有以下擴展:

一、二進制層次的可移植性。
二、NULL列索引。
三、對變長行比ISAM表有更少的碎片。
四、支持大文件。
五、更好的索引壓縮。
六、更好的鍵碼統計分佈。
七、更好和更快的auto_increment處理。

InnoDB 是 MySQL 上第一個提供外鍵約束的引擎,除了提供事務處理外,InnoDB 還支持行鎖,提供和 Oracle 同樣的一致性的不加鎖讀取,能增長併發讀的用戶數量並提升性能,不會增長鎖的數量。

InnoDB 的設計目標是處理大容量數據時最大化性能,它的 CPU 利用率是其餘全部基於磁盤的關係數據庫引擎中最有效率的。

InnoDB 是一套放在 MySQL 後臺的完整數據庫系統,InnoDB 有它本身的緩衝池,能緩衝數據和索引,InnoDB 還把數據和索引存放在表空間裏面,可能包含好幾個文件,這和 MyISAM 表徹底不一樣,在 MyISAM 中,表被存放在單獨的文件中,InnoDB 表的大小隻受限於操做系統文件的大小,通常爲 2GB。

InnoDB全部的表都保存在同一個數據文件 ibdata1 中(也多是多個文件,或者是獨立的表空間文件),相對來講比較很差備份,免費的方案能夠是拷貝數據文件、備份 binlog,或者用 mysqldump。

MyISAM 是MySQL缺省存貯引擎 .

每張MyISAM 表被存放在三個文件 。frm 文件存放表格定義。 數據文件是MYD (MYData) 。 索引文件是MYI (MYIndex) 引申。

由於MyISAM相對簡單因此在效率上要優於InnoDB..小型應用使用MyISAM是不錯的選擇。

MyISAM表是保存成文件的形式,在跨平臺的數據轉移中使用MyISAM存儲會省去很多的麻煩。

如下是一些細節和具體實現的差異:

一、InnoDB不支持FULLTEXT類型的索引。
二、InnoDB 中不保存表的具體行數,也就是說,執行select count(*) from table時,InnoDB要掃描一遍整個表來計算有多少行,可是MyISAM只要簡單的讀出保存好的行數便可。注意的是,當count(*)語句包含 where條件時,兩種表的操做是同樣的。
三、對於AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,可是在MyISAM表中,能夠和其餘字段一塊兒創建聯合索引。
四、DELETE FROM table時,InnoDB不會從新創建表,而是一行一行的刪除。
五、LOAD TABLE FROM MASTER操做對InnoDB是不起做用的,解決方法是首先把InnoDB表改爲MyISAM表,導入數據後再改爲InnoDB表,可是對於使用的額外的InnoDB特性(例如外鍵)的表不適用。
六、InnoDB表的行鎖也不是絕對的,若是在執行一個SQL語句時MySQL不能肯定要掃描的範圍,InnoDB表一樣會鎖全表,例如update table set num=1 where name like 「%aaa%」

綜上所述,任何一種表都不是萬能的,只有恰當的針對業務類型來瘍合適的表類型,才能最大的發揮MySQL的性能優點。

兩種類型最主要的差異就是 InnoDB 支持事務處理與外鍵和行級鎖.而MyISAM不支持.因此Myisam每每就容易被人認爲只適合在小項目中使用。

我做爲使用mysql的用戶角度出發,innodb和myisam都是比較喜歡的,可是從我目前運維的數據庫平臺要達到需求:99.9%的穩定性,方便的擴展性和高可用性來講的話,myisam絕對是個人首選。

緣由以下:

1.首先我目前平臺上承載的大部分項目是讀多寫少的項目,而myisam的讀性能是比innodb強很多的。

2.myisam的索引和數據是分開的,而且索引是有壓縮的,內存使用率就對應提升了很多。能加載更多索引,而innodb是索引和數據是緊密捆綁的,沒有使用壓縮從而會形成innodb比myisam體積龐大不校

3.從平臺角度來講,常常隔1,2個月就會發生應用開發人員不當心update一個表where寫的範圍不對,致使這個表無法正經常使用了,這個時候 myisam 的優越性就體現出來了,隨便從當天拷貝的壓縮包取出對應表的文件,隨便放到一個數據庫目錄下,而後dump成sql再導回到主庫,並把對應的binlog 補上。若是是innodb,恐怕不可能有這麼快速度,別和我說讓innodb按期用導出xxx.sql機制備份,由於我平臺上最小的一個數據庫實例的數據 量基本都是幾十G大校

4.從我接觸的應用邏輯來講,select count(*) 和order by 是最頻繁的,大概能佔了整個sql總語句的60%以上的操做,而這種操做innodb其實也是會鎖表的,不少人覺得innodb是行級鎖,那個只是 where對它主鍵是有效,非主鍵的都會鎖全表的。

5.還有就是常常有不少應用部門須要我給他們按期某些表的數據,myisam的話很方便,只要發給他們對應那表的frm.MYD,MYI的文件,讓他們自 己在對應版本的數據庫啓動就行,而innodb就須要導出xxx.sql了,由於光給別人文件,受字典數據文件的影響,對方是沒法使用的。

6.若是和myisam比insert寫操做的話,innodb還達不到myisam的寫性能,若是是針對基於索引的update操做,雖然myisam可能會遜色innodb,可是那麼高併發的寫,從庫可否追的上也是一個問題,還不如經過多實例分庫分表架構來解決。

7.若是是用Myisam的話,merge引擎能夠大大加快應用部門的開發速度,他們只要對這個merge表作一些select count(*)操做,很是適合大項目總量約幾億的rows某一類型(如日誌,調查統計)的業務表。

固然innodb也不是絕對不用,用事務的項目如模擬炒股項目,我就是用innodb的,活躍用戶20多萬時候,也是很輕鬆應付了,所以我我的也是很喜歡Innodb的,只是

若是從數據庫平臺應用出發,我仍是會首選myisam.

PS:可能有人會說你myisam沒法抗太多寫操做,可是我能夠經過架構來彌補,說個我現有用的數據庫平臺容量:主從數據總量在幾百T以上,天天十多億 pv的動態頁面,還有幾個大項目是經過數據接口方式調用未算進pv總數,(其中包括一個大項目由於初期memcached沒部署,致使單臺數據庫天天處理 9千萬的查詢)。而個人總體數據庫服務器平均負載都在0.5-1左右。

MyISAM和InnoDB優化:

key_buffer_size – 這對MyISAM表來講很是重要。若是隻是使用MyISAM表,能夠把它設置爲可用內存的 30-40%。合理的值取決於索引大小、數據量以及負載 — 記住,MyISAM表會使用操做系統的緩存來緩存數據,所以須要留出部份內存給它們,不少狀況下數據比索引大多了。儘管如此,須要老是檢查是否全部的 key_buffer 都被利用了 — .MYI 文件只有 1GB,而 key_buffer 卻設置爲 4GB 的狀況是很是少的。這麼作太浪費了。若是你不多使用MyISAM表,那麼也保留低於 16-32MB 的 key_buffer_size 以適應給予磁盤的臨時表索引所需。

innodb_buffer_pool_size – 這對Innodb表來講很是重要。Innodb相比MyISAM表對緩衝更爲敏感。MyISAM能夠在默認的 key_buffer_size 設置下運行的能夠,然而Innodb在默認的 innodb_buffer_pool_size 設置下卻跟蝸牛似的。因爲Innodb把數據和索引都緩存起來,無需留給操做系統太多的內存,所以若是隻須要用Innodb的話則能夠設置它高達 70-80% 的可用內存。一些應用於 key_buffer 的規則有 — 若是你的數據量不大,而且不會暴增,那麼無需把 innodb_buffer_pool_size 設置的太大了。

innodb_additional_pool_size – 這個選項對性能影響並不太多,至少在有差很少足夠內存可分配的操做系統上是這樣。不過若是你仍然想設置爲 20MB(或者更大),所以就須要看一下Innodb其餘須要分配的內存有多少。

innodb_log_file_size 在高寫入負載尤爲是大數據集的狀況下很重要。這個值越大則性能相對越高,可是要注意到可能會增長恢復時間。我常常設置爲 64-512MB,跟據服務器大小而異。

innodb_log_buffer_size 默 認的設置在中等強度寫入負載以及較短事務的狀況下,服務器性能還可 以。若是存在更新操做峯值或者負載較大,就應該考慮加大它的值了。若是它的值設置過高了,可能會浪費內存 — 它每秒都會刷新一次,所以無需設置超過1秒所需的內存空間。一般 8-16MB 就足夠了。越小的系統它的值越小。

innodb_flush_logs_at_trx_commit 是否爲Innodb比MyISAM慢1000倍而頭大?看來也許你忘了修改這個參數了。默認值是 1,這意味着每次提交的更新事務(或者每一個事務以外的語句)都會刷新到磁盤中,而這至關耗費資源,尤爲是沒有電池備用緩存時。不少應用程序,尤爲是從 MyISAM轉變過來的那些,把它的值設置爲 2 就能夠了,也就是不把日誌刷新到磁盤上,而只刷新到操做系統的緩存上。日誌仍然會每秒刷新到磁盤中去,所以一般不會丟失每秒1-2次更新的消耗。若是設置 爲 0 就快不少了,不過也相對不安全了 — MySQL服務器崩潰時就會丟失一些事務。設置爲 2 指揮丟失刷新到操做系統緩存的那部分事務。

table_cache — 打開一個表的開銷可能很大。例如MyISAM把MYI文件頭標誌該表正在使用中。你確定不但願這種操做太頻繁,因此一般要加大緩存數量,使得足以最大限度 地緩存打開的表。它須要用到操做系統的資源以及內存,對當前的硬件配置來講固然不是什麼問題了。若是你有200多個表的話,那麼設置爲 1024 也許比較合適(每一個線程都須要打開表),若是鏈接數比較大那麼就加大它的值。我曾經見過設置爲 100,000 的狀況。

thread_cache — 線程的建立和銷燬的開銷可能很大,由於每一個線程的鏈接/斷開都須要。我一般至少設置爲 16。若是應用程序中有大量的跳躍併發鏈接而且 Threads_Created 的值也比較大,那麼我就會加大它的值。它的目的是在一般的操做中無需建立新線程。

query_cache — 若是你的應用程序有大量讀,並且沒有應用程序級別的緩存,那麼這頗有用。不要把它設置太大了,由於想要維護它也須要很多開銷,這會致使MySQL變慢。通 常設置爲 32-512Mb。設置完以後最好是跟蹤一段時間,查看是否運行良好。在必定的負載壓力下,若是緩存命中率過低了,就啓用它。

sort_buffer_size –若是你只有一些簡單的查詢,那麼就無需增長它的值了,儘管你有 64GB 的內存。搞很差也許會下降性能。

部份內容來源:https://soulogic.com/archives/347

相關文章
相關標籤/搜索