MySQL MyISAM/InnoDB高并发优化经验
最近做的一個(gè)應(yīng)用,功能要求非常簡單,就是 key/value 形式的存儲,簡單的 INSERT/SELECT,沒有任何復(fù)雜查詢,唯一的問題是量非常大,如果目前投入使用,初期的單表 insert 頻率約 20Hz(次/秒,我喜歡這個(gè)單位,讓我想起國內(nèi)交流電是 50Hz),但我估計(jì)以后會有 500Hz+ 的峰值。目前的工作成果,額定功率 200Hz(CPU 占用 10 – 20,load avg = 2),最大功率 500Hz(這時(shí) load avg > 20,很明顯,只能暫時(shí)挺挺,應(yīng)該在出現(xiàn)這種負(fù)載前提前拆表了)
從 數(shù)據(jù)的插入開始說起。如果可以容忍結(jié)果幾秒以后再生效的,可以用 INSERT DELAYED INTO,因?yàn)樵谖业倪@個(gè)結(jié)構(gòu)中不需要對同一個(gè) key 頻繁的 INSERT/SELECT,因?yàn)?SELECT 我是用 Memcached 擋住了,除非 Memcached 掛了,或者數(shù)據(jù)實(shí)在老到過期了,才會去 SELECT。而且要注意,如果 PHP 不需要關(guān)心 MySQL 操作的返回結(jié)果,應(yīng)該使用 unbuffered query,簡單的說,在你提交 query 后,不用等待 MySQL 有返回信息就繼續(xù)執(zhí)行之后的 PHP 指令,具體用法是用 mysql_unbuffered_query 代替 mysql_query,如果用的 MySQLi 類,應(yīng)該使用 mysqli->query($sQuery, MYSQLI_USE_RESULT);
如果 SHOW PROCESSLIST,可以看到用戶名為 DELAYED 的進(jìn)程,進(jìn)程數(shù)量等于 INSERT DELAYED 的表的數(shù)量,因?yàn)楸砑夋i的存在,每個(gè)表一條以上的 DELAYED 進(jìn)程是沒有意義的
關(guān)于這個(gè)功能的 my.cnf 配置有三條,我定為如下值
連接
有 人說,如果報(bào)錯(cuò)連接數(shù)過大,你把 max_connections 調(diào)大就 OK,如果只這么說而不講原因,完全是句廢話,你調(diào)成 1M 肯定不會再報(bào) Too many connections(但應(yīng)該會報(bào)內(nèi)存溢出之類的),但如果是這樣 MySQL 又何必給這個(gè)參數(shù)?
我看到的一個(gè)很有用的公式
以前只有很模糊的概念,應(yīng)該設(shè)的很大,但又不能太大,具體多大合適,知道這個(gè)就明確了。innoDB 的公式比這個(gè)復(fù)雜點(diǎn),一并給出
還有一個(gè)看起來很有用的參數(shù) back_log,給我一種連接池的感覺,而且它確實(shí)在起作用,我不知道如果設(shè)大了會占用多少內(nèi)存,但估計(jì)不會很多。
key_buffer_size
很 多文章都告訴你越大越好,要為此分配一半左右的物理內(nèi)存,這么干通常不會出問題,但肯定不是最優(yōu)的,甚至可以說很無理頭——分多少內(nèi)存應(yīng)該是根據(jù)需求決 定,而不是不管什么機(jī)器,都砍掉一半內(nèi)存用作 key_buffer_size ——有的時(shí)候可能是不夠,還有的時(shí)候可能是浪費(fèi)。
其實(shí)最關(guān) 鍵的指標(biāo),還是看 SHOW GLOBAL STATUS 時(shí)的 Key_blocks_unused,只要還有剩余,就說明 key_buffer_size 沒用滿。有人說看 Key_reads 跟 Key_read_requests 的比值,至少要達(dá)到 1:100。這可以作為一個(gè)結(jié)果來衡量,但不夠準(zhǔn)確,因?yàn)樵诜?wù)器剛啟動(dòng)的時(shí)候,大多數(shù)請求都要新建緩存,緩存命中比高不起來,需要運(yùn)行穩(wěn)定(幾小時(shí)后) 再觀察。我個(gè)人建議還是看 Key_blocks_unused
如果不花很長時(shí)間在運(yùn)行中調(diào)試,給出一個(gè)簡單的計(jì)算方法,把數(shù)據(jù)庫填滿,達(dá)到設(shè)計(jì)時(shí)的最大值,看看這時(shí)候索引占了多大空間,然后把所有表的索引大小加起來,就是 key_buffer_size 可能達(dá)到的最大值,當(dāng)然,還要留些余地,乘個(gè) 2 或 3 之類的。
這是我做測試的時(shí)候的 phpMyAdmin 截圖,可以看到 key_buffer_size 被浪費(fèi)了太多。
OPTIMIZE TABLE
優(yōu) 化一下有好處,但會鎖住表,是否值得做要權(quán)衡一下。拿我現(xiàn)在這個(gè)表做例子,有 text 字段,700萬條記錄,1.5G 大小,優(yōu)化時(shí)間約兩分鐘,優(yōu)化后性能提升了 50%,同時(shí)表的大小變?yōu)?1.4G,但隨著表的頻繁改寫,約一天后又恢復(fù)到以前的速度,因此在我看來并不值得。
Query Cache
因?yàn)槊坑袑懖僮?Query Cache 都會被清空,除了極特殊的情況(大量讀,少量寫,但即使這樣也應(yīng)該是多用 memcached 才對)完全沒有必要使用這個(gè),把 query_cache_size 設(shè)為 0 關(guān)閉這個(gè)功能吧。
InnoDB和MyISAM是在使用MySQL最常用的兩個(gè)表類型,各有優(yōu)缺點(diǎn),視具體應(yīng)用而定。基本 的差別為:MyISAM類型不支持事務(wù)處理等高級處理,而InnoDB類型支持。MyISAM類型的表強(qiáng)調(diào)的是性能,其執(zhí)行數(shù)度比InnoDB類型更快, 但是不提供事務(wù)支持,而InnoDB提供事務(wù)支持已經(jīng)外部鍵等高級數(shù)據(jù)庫功能。
MyISAM:這個(gè)是默認(rèn)類型,它是基于傳統(tǒng)的ISAM類型,ISAM是Indexed Sequential Access Method (有索引的順序訪問方法) 的縮寫,它是存儲記錄和文件的標(biāo)準(zhǔn)方法.與其他存儲引擎比較,MyISAM具有檢查和修復(fù)表格的大多數(shù)工具. MyISAM表格可以被壓縮,而且它們支持全文搜索.它們不是事務(wù)安全的,而且也不支持外鍵。如果事物回滾將造成不完全回滾,不具有原子性。如果執(zhí)行大量 的SELECT,MyISAM是更好的選擇。
InnoDB:這種類型是事務(wù)安全的.它與BDB類型具有相同的特性,它們還支持外鍵.InnoDB表格速度很快.具有比BDB還豐富的特性,因此如果需 要一個(gè)事務(wù)安全的存儲引擎,建議使用它.如果你的數(shù)據(jù)執(zhí)行大量的INSERT或UPDATE,出于性能方面的考慮,應(yīng)該使用InnoDB表,
對于支持事物的InnoDB類型的標(biāo),影響速度的主要原因是AUTOCOMMIT默認(rèn)設(shè)置是打開的,而且程序沒有顯式調(diào)用BEGIN 開始事務(wù),導(dǎo)致每插入一條都自動(dòng)Commit,嚴(yán)重影響了速度。可以在執(zhí)行sql前調(diào)用begin,多條sql形成一個(gè)事物(即使autocommit打 開也可以),將大大提高性能。
MyIASM是IASM表的新版本,有如下擴(kuò)展:
- 1、二進(jìn)制層次的可移植性。
- 2、NULL列索引。
- 3、對變長行比ISAM表有更少的碎片。
- 4、支持大文件。
- 5、更好的索引壓縮。
- 6、更好的鍵碼統(tǒng)計(jì)分布。
- 7、更好和更快的auto_increment處理。
InnoDB 是 MySQL 上第一個(gè)提供外鍵約束的引擎,除了提供事務(wù)處理外,InnoDB 還支持行鎖,提供和 Oracle 一樣的一致性的不加鎖讀取,能增加并發(fā)讀的用戶數(shù)量并提高性能,不會增加鎖的數(shù)量。
InnoDB 的設(shè)計(jì)目標(biāo)是處理大容量數(shù)據(jù)時(shí)最大化性能,它的 CPU 利用率是其他所有基于磁盤的關(guān)系數(shù)據(jù)庫引擎中最有效率的。
InnoDB 是一套放在 MySQL 后臺的完整數(shù)據(jù)庫系統(tǒng),InnoDB 有它自己的緩沖池,能緩沖數(shù)據(jù)和索引,InnoDB 還把數(shù)據(jù)和索引存放在表空間里面,可能包含好幾個(gè)文件,這和 MyISAM 表完全不同,在 MyISAM 中,表被存放在單獨(dú)的文件中,InnoDB 表的大小只受限于操作系統(tǒng)文件的大小,一般為 2GB。
InnoDB所有的表都保存在同一個(gè)數(shù)據(jù)文件 ibdata1 中(也可能是多個(gè)文件,或者是獨(dú)立的表空間文件),相對來說比較不好備份,免費(fèi)的方案可以是拷貝數(shù)據(jù)文件、備份 binlog,或者用 mysqldump。
MyISAM 是MySQL缺省存貯引擎 .
每張MyISAM 表被存放在三個(gè)文件 。frm 文件存放表格定義。 數(shù)據(jù)文件是MYD (MYData) 。 索引文件是MYI (MYIndex) 引伸。
因?yàn)镸yISAM相對簡單所以在效率上要優(yōu)于InnoDB..小型應(yīng)用使用MyISAM是不錯(cuò)的選擇。
MyISAM表是保存成文件的形式,在跨平臺的數(shù)據(jù)轉(zhuǎn)移中使用MyISAM存儲會省去不少的麻煩。
以下是一些細(xì)節(jié)和具體實(shí)現(xiàn)的差別:
- 1、InnoDB不支持FULLTEXT類型的索引。
- 2、InnoDB 中不保存表的具體行數(shù),也就是說,執(zhí)行select count(*) from table時(shí),InnoDB要掃描一遍整個(gè)表來計(jì)算有多少行,但是MyISAM只要簡單的讀出保存好的行數(shù)即可。注意的是,當(dāng)count(*)語句包含 where條件時(shí),兩種表的操作是一樣的。
- 3、對于AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,但是在MyISAM表中,可以和其他字段一起建立聯(lián)合索引。
- 4、DELETE FROM table時(shí),InnoDB不會重新建立表,而是一行一行的刪除。
- 5、LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,導(dǎo)入數(shù)據(jù)后再改成InnoDB表,但是對于使用的額外的InnoDB特性(例如外鍵)的表不適用。
- 6、InnoDB表的行鎖也不是絕對的,如果在執(zhí)行一個(gè)SQL語句時(shí)MySQL不能確定要掃描的范圍,InnoDB表同樣會鎖全表,例如update table set num=1 where name like “%aaa%”
綜上所述,任何一種表都不是萬能的,只有恰當(dāng)?shù)尼槍I(yè)務(wù)類型來瘍合適的表類型,才能最大的發(fā)揮MySQL的性能優(yōu)勢。
兩種類型最主要的差別就是 InnoDB 支持事務(wù)處理與外鍵和行級鎖.而MyISAM不支持.所以Myisam往往就容易被人認(rèn)為只適合在小項(xiàng)目中使用。
我作為使用mysql的用戶角度出發(fā),innodb和myisam都是比較喜歡的,但是從我目前運(yùn)維的數(shù)據(jù)庫平臺要達(dá)到需求:99.9%的穩(wěn)定性,方便的擴(kuò)展性和高可用性來說的話,myisam絕對是我的首選。
原因如下:
- 1.首先我目前平臺上承載的大部分項(xiàng)目是讀多寫少的項(xiàng)目,而myisam的讀性能是比innodb強(qiáng)不少的。
- 2.myisam的索引和數(shù)據(jù)是分開的,并且索引是有壓縮的,內(nèi)存使用率就對應(yīng)提高了不少。能加載更多索引,而innodb是索引和數(shù)據(jù)是緊密捆綁的,沒有使用壓縮從而會造成innodb比myisam體積龐大不校
- 3.從平臺角度來說,經(jīng)常隔1,2個(gè)月就會發(fā)生應(yīng)用開發(fā)人員不小心update一個(gè)表where寫的范圍不對,導(dǎo)致這個(gè)表沒法正常用了,這個(gè)時(shí)候 myisam 的優(yōu)越性就體現(xiàn)出來了,隨便從當(dāng)天拷貝的壓縮包取出對應(yīng)表的文件,隨便放到一個(gè)數(shù)據(jù)庫目錄下,然后dump成sql再導(dǎo)回到主庫,并把對應(yīng)的binlog 補(bǔ)上。如果是innodb,恐怕不可能有這么快速度,別和我說讓innodb定期用導(dǎo)出xxx.sql機(jī)制備份,因?yàn)槲移脚_上最小的一個(gè)數(shù)據(jù)庫實(shí)例的數(shù)據(jù) 量基本都是幾十G大校
- 4.從我接觸的應(yīng)用邏輯來說,select count(*) 和order by 是最頻繁的,大概能占了整個(gè)sql總語句的60%以上的操作,而這種操作innodb其實(shí)也是會鎖表的,很多人以為innodb是行級鎖,那個(gè)只是 where對它主鍵是有效,非主鍵的都會鎖全表的。
- 5.還有就是經(jīng)常有很多應(yīng)用部門需要我給他們定期某些表的數(shù)據(jù),myisam的話很方便,只要發(fā)給他們對應(yīng)那表的frm.MYD,MYI的文件,讓他們自 己在對應(yīng)版本的數(shù)據(jù)庫啟動(dòng)就行,而innodb就需要導(dǎo)出xxx.sql了,因?yàn)楣饨o別人文件,受字典數(shù)據(jù)文件的影響,對方是無法使用的。
- 6.如果和myisam比insert寫操作的話,innodb還達(dá)不到myisam的寫性能,如果是針對基于索引的update操作,雖然myisam可能會遜色innodb,但是那么高并發(fā)的寫,從庫能否追的上也是一個(gè)問題,還不如通過多實(shí)例分庫分表架構(gòu)來解決。
- 7.如果是用Myisam的話,merge引擎可以大大加快應(yīng)用部門的開發(fā)速度,他們只要對這個(gè)merge表做一些select count(*)操作,非常適合大項(xiàng)目總量約幾億的rows某一類型(如日志,調(diào)查統(tǒng)計(jì))的業(yè)務(wù)表。
當(dāng)然innodb也不是絕對不用,用事務(wù)的項(xiàng)目如模擬炒股項(xiàng)目,我就是用innodb的,活躍用戶20多萬時(shí)候,也是很輕松應(yīng)付了,因此我個(gè)人也是很喜歡Innodb的,只是
如果從數(shù)據(jù)庫平臺應(yīng)用出發(fā),我還是會首選myisam.
PS:可能有人會說你myisam無法抗太多寫操作,但是我可以通過架構(gòu)來彌補(bǔ),說個(gè)我現(xiàn)有用的數(shù)據(jù)庫平臺容量:主從數(shù)據(jù)總量在幾百T以上,每天十多億 pv的動(dòng)態(tài)頁面,還有幾個(gè)大項(xiàng)目是通過數(shù)據(jù)接口方式調(diào)用未算進(jìn)pv總數(shù),(其中包括一個(gè)大項(xiàng)目因?yàn)槌跗趍emcached沒部署,導(dǎo)致單臺數(shù)據(jù)庫每天處理 9千萬的查詢)。而我的整體數(shù)據(jù)庫服務(wù)器平均負(fù)載都在0.5-1左右。
MyISAM和InnoDB優(yōu)化:
key_buffer_size – 這對MyISAM表來說非常重要。如果只是使用MyISAM表,可以把它設(shè)置為可用內(nèi)存的 30-40%。合理的值取決于索引大小、數(shù)據(jù)量以及負(fù)載 — 記住,MyISAM表會使用操作系統(tǒng)的緩存來緩存數(shù)據(jù),因此需要留出部分內(nèi)存給它們,很多情況下數(shù)據(jù)比索引大多了。盡管如此,需要總是檢查是否所有的 key_buffer 都被利用了 — .MYI 文件只有 1GB,而 key_buffer 卻設(shè)置為 4GB 的情況是非常少的。這么做太浪費(fèi)了。如果你很少使用MyISAM表,那么也保留低于 16-32MB 的 key_buffer_size 以適應(yīng)給予磁盤的臨時(shí)表索引所需。
innodb_buffer_pool_size – 這對Innodb表來說非常重要。Innodb相比MyISAM表對緩沖更為敏感。MyISAM可以在默認(rèn)的 key_buffer_size 設(shè)置下運(yùn)行的可以,然而Innodb在默認(rèn)的 innodb_buffer_pool_size 設(shè)置下卻跟蝸牛似的。由于Innodb把數(shù)據(jù)和索引都緩存起來,無需留給操作系統(tǒng)太多的內(nèi)存,因此如果只需要用Innodb的話則可以設(shè)置它高達(dá) 70-80% 的可用內(nèi)存。一些應(yīng)用于 key_buffer 的規(guī)則有 — 如果你的數(shù)據(jù)量不大,并且不會暴增,那么無需把 innodb_buffer_pool_size 設(shè)置的太大了。
innodb_additional_pool_size – 這個(gè)選項(xiàng)對性能影響并不太多,至少在有差不多足夠內(nèi)存可分配的操作系統(tǒng)上是這樣。不過如果你仍然想設(shè)置為 20MB(或者更大),因此就需要看一下Innodb其他需要分配的內(nèi)存有多少。
innodb_log_file_size 在高寫入負(fù)載尤其是大數(shù)據(jù)集的情況下很重要。這個(gè)值越大則性能相對越高,但是要注意到可能會增加恢復(fù)時(shí)間。我經(jīng)常設(shè)置為 64-512MB,跟據(jù)服務(wù)器大小而異。
innodb_log_buffer_size 默 認(rèn)的設(shè)置在中等強(qiáng)度寫入負(fù)載以及較短事務(wù)的情況下,服務(wù)器性能還可 以。如果存在更新操作峰值或者負(fù)載較大,就應(yīng)該考慮加大它的值了。如果它的值設(shè)置太高了,可能會浪費(fèi)內(nèi)存 — 它每秒都會刷新一次,因此無需設(shè)置超過1秒所需的內(nèi)存空間。通常 8-16MB 就足夠了。越小的系統(tǒng)它的值越小。
innodb_flush_logs_at_trx_commit 是否為Innodb比MyISAM慢1000倍而頭大?看來也許你忘了修改這個(gè)參數(shù)了。默認(rèn)值是 1,這意味著每次提交的更新事務(wù)(或者每個(gè)事務(wù)之外的語句)都會刷新到磁盤中,而這相當(dāng)耗費(fèi)資源,尤其是沒有電池備用緩存時(shí)。很多應(yīng)用程序,尤其是從 MyISAM轉(zhuǎn)變過來的那些,把它的值設(shè)置為 2 就可以了,也就是不把日志刷新到磁盤上,而只刷新到操作系統(tǒng)的緩存上。日志仍然會每秒刷新到磁盤中去,因此通常不會丟失每秒1-2次更新的消耗。如果設(shè)置 為 0 就快很多了,不過也相對不安全了 — MySQL服務(wù)器崩潰時(shí)就會丟失一些事務(wù)。設(shè)置為 2 指揮丟失刷新到操作系統(tǒng)緩存的那部分事務(wù)。
table_cache — 打開一個(gè)表的開銷可能很大。例如MyISAM把MYI文件頭標(biāo)志該表正在使用中。你肯定不希望這種操作太頻繁,所以通常要加大緩存數(shù)量,使得足以最大限度 地緩存打開的表。它需要用到操作系統(tǒng)的資源以及內(nèi)存,對當(dāng)前的硬件配置來說當(dāng)然不是什么問題了。如果你有200多個(gè)表的話,那么設(shè)置為 1024 也許比較合適(每個(gè)線程都需要打開表),如果連接數(shù)比較大那么就加大它的值。我曾經(jīng)見過設(shè)置為 100,000 的情況。
thread_cache — 線程的創(chuàng)建和銷毀的開銷可能很大,因?yàn)槊總€(gè)線程的連接/斷開都需要。我通常至少設(shè)置為 16。如果應(yīng)用程序中有大量的跳躍并發(fā)連接并且 Threads_Created 的值也比較大,那么我就會加大它的值。它的目的是在通常的操作中無需創(chuàng)建新線程。
query_cache — 如果你的應(yīng)用程序有大量讀,而且沒有應(yīng)用程序級別的緩存,那么這很有用。不要把它設(shè)置太大了,因?yàn)橄胍S護(hù)它也需要不少開銷,這會導(dǎo)致MySQL變慢。通 常設(shè)置為 32-512Mb。設(shè)置完之后最好是跟蹤一段時(shí)間,查看是否運(yùn)行良好。在一定的負(fù)載壓力下,如果緩存命中率太低了,就啟用它。
sort_buffer_size –如果你只有一些簡單的查詢,那么就無需增加它的值了,盡管你有 64GB 的內(nèi)存。搞不好也許會降低性能。
文章來源:https://soulogic.com/archives/347
http://www.ha97.com/4170.html
轉(zhuǎn)載于:https://www.cnblogs.com/zsmynl/p/3538603.html
總結(jié)
以上是生活随笔為你收集整理的MySQL MyISAM/InnoDB高并发优化经验的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 各种OS间文件传输
- 下一篇: 使用证书创建数据库镜像