MySQL之架构与历史(二)
多版本并發(fā)控制
MySQL的大多數(shù)事務(wù)型存儲(chǔ)引擎實(shí)現(xiàn)的都不是簡(jiǎn)單的行級(jí)鎖。基于提升并發(fā)性能的考慮,它們一般都同時(shí)實(shí)現(xiàn)了多版本并發(fā)控制(MVCC)。不僅是MySQL,包括Oracle、PostgreSQL等其他數(shù)據(jù)庫(kù)系統(tǒng)也都實(shí)現(xiàn)了MVCC,但各自的實(shí)現(xiàn)機(jī)制不盡相同,因?yàn)镸VCC沒(méi)有一個(gè)統(tǒng)一的實(shí)習(xí)標(biāo)準(zhǔn)。
可以認(rèn)為MVCC是行級(jí)鎖的一個(gè)變種,但是它在很多情況下避免了加鎖操作,因此開(kāi)銷更低。雖然實(shí)現(xiàn)機(jī)制不同,但大都實(shí)現(xiàn)了非阻塞的讀操作,寫操作也只鎖定了必要的行。
MVCC的實(shí)現(xiàn),是通過(guò)保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來(lái)實(shí)現(xiàn)的。也就是說(shuō),不管需要執(zhí)行多長(zhǎng)時(shí)間,每個(gè)事務(wù)看到的數(shù)據(jù)都是一致的。根據(jù)事務(wù)開(kāi)始的時(shí)間不同,每個(gè)事務(wù)對(duì)同一張表,同一時(shí)刻看到的數(shù)據(jù)可能是不一樣的。
前面說(shuō)到不同存儲(chǔ)引擎的MVCC實(shí)現(xiàn)是不同的,典型的有樂(lè)觀(optimistic)并發(fā)控制和悲觀(pessimistic)并發(fā)控制。下面我們通過(guò)InnoDB的簡(jiǎn)化版行為來(lái)說(shuō)明MVCC是如何工作的。
InnoDB的MVVC,是通過(guò)在每行記錄后面保存兩個(gè)隱藏列來(lái)實(shí)現(xiàn)的。一個(gè)保存了行的創(chuàng)建時(shí)間,一個(gè)保存了行的過(guò)期時(shí)間(或刪除時(shí)間)。當(dāng)然存儲(chǔ)的并不是實(shí)際的時(shí)間值,而是系統(tǒng)版本號(hào)(system version number)。每開(kāi)啟一個(gè)新的事務(wù),系統(tǒng)版本號(hào)都會(huì)自動(dòng)遞增。事務(wù)開(kāi)始時(shí)刻的系統(tǒng)版本號(hào)會(huì)作為當(dāng)前事務(wù)的版本號(hào),用來(lái)和查詢到的每行記錄的版本號(hào)進(jìn)行比較。下面看一下在REPEATABLE READ 隔離級(jí)別下,MVCC具體是如何操作的。
SELECT
InnoDB會(huì)根據(jù)一下兩個(gè)條件檢查每行記錄:
- InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是,行的系統(tǒng)版本號(hào)小于等于當(dāng)前事務(wù)的系統(tǒng)版本號(hào)),這樣可以確保事務(wù)讀取的行,要么實(shí)在事務(wù)開(kāi)始前就已經(jīng)存在的,要么是事務(wù)自身插入或修改過(guò)的。
-
行的刪除版本要么未定義,要么大于當(dāng)前事務(wù)版本號(hào)。這可以確保事務(wù)讀取到的行,在事務(wù)開(kāi)始之前未被刪除。
只有符合上述兩個(gè)條件的記錄,才能作為返回結(jié)果。
INSERT
InnoDB為新插入的每一行保存當(dāng)前的系統(tǒng)版本號(hào)作為行版本號(hào)。
DELETE
InnoDB為刪除的每一行保存當(dāng)前系統(tǒng)版本號(hào)作為行刪除標(biāo)識(shí)。只有commit的時(shí)候才會(huì)真正刪除。
UPDATE
InnoDB為插入一行新記錄,保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào),同時(shí)保存當(dāng)前系統(tǒng)版本號(hào)到原來(lái)的行作為行刪除標(biāo)識(shí)。
保存這兩個(gè)額外系統(tǒng)版本號(hào),使大多數(shù)讀操作都可以不用加鎖。這樣設(shè)計(jì)使得讀數(shù)據(jù)操作很簡(jiǎn)單,性能很好,并且也能保證只會(huì)讀取到符合標(biāo)準(zhǔn)的行。不足之處是每行記錄都需要額外的存儲(chǔ)空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作。
MVCC只在REPEATABLE READ和READ COMMITTED 兩個(gè)隔離級(jí)別下工作。其他兩個(gè)隔離級(jí)別都和MVCC不兼容 ,因?yàn)镽EAD UNCOMMITTED 總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行。而SERIALIZABLE 則會(huì)對(duì)所有讀取的行都加鎖。
MySQL的存儲(chǔ)引擎
在文件系統(tǒng)中,MySQL將每個(gè)數(shù)據(jù)庫(kù)(也稱為schema)保存為數(shù)據(jù)目錄下的一個(gè)子目錄。創(chuàng)建表時(shí),MySQL會(huì)在數(shù)據(jù)庫(kù)子目錄下創(chuàng)建一個(gè)和表同名的.frm文件保存表的定義。因?yàn)镸ySQL使用文件系統(tǒng)的目錄和文件來(lái)保存數(shù)據(jù)庫(kù)和表的定義,大小寫敏感和具體的平臺(tái)密切相關(guān)。在Windows中,大小寫是不敏感的;而在類Unix中則是敏感的。不同的存儲(chǔ)引擎保存的數(shù)據(jù)和索引的方式是不同的,但表的定義則是在MySQL服務(wù)層統(tǒng)一處理的。
可以使用SHOW TABLE STATUS命令查看表的相關(guān)信息。例如,對(duì)于mysql數(shù)據(jù)庫(kù)中的user表:
mysql> use mysql; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -ADatabase changed mysql> SHOW TABLE STATUS LIKE 'user'\G; *************************** 1. row ***************************Name: userEngine: InnoDBVersion: 10Row_format: DynamicRows: 5Avg_row_length: 3276Data_length: 16384 Max_data_length: 0Index_length: 0Data_free: 4194304Auto_increment: NULLCreate_time: 2018-10-05 22:37:53Update_time: NULLCheck_time: NULLCollation: utf8_binChecksum: NULLCreate_options: stats_persistent=0Comment: Users and global privileges 1 row in set (0.04 sec)
- Name:表名。
- Engine:表的存儲(chǔ)引擎。在舊版本中,該列的名字叫Type,而不是Engine。
- Version:版本。
- Row_format:行格式。對(duì)于MyISAM引擎,這可能是Dynamic,Fixed或Compressed。Dynamic(動(dòng)態(tài)行)的行長(zhǎng)度可變,一般包含可變長(zhǎng)的字段,如Varchar或Blob 類型字段。Fixed(固定行)是指行長(zhǎng)度是不變的,只包含固定的長(zhǎng)度列,例如Char和Integer類型字段。Compressed的行旨在壓縮表中存在。
- Rows:表中的行數(shù)。對(duì)于非事務(wù)性表(MyISAM),這個(gè)值是精確的,對(duì)于事務(wù)性引擎(InnoDB),這個(gè)值通常是估算的。
- Avg_row_length:平均每行包含的字節(jié)數(shù)(單位:字節(jié))
- Data_length:整個(gè)表的數(shù)據(jù)量(單位:字節(jié))
- Max_data_length:表可以容納的最大數(shù)據(jù)量,該值和存儲(chǔ)引擎有關(guān)。
- Index_length:索引占用磁盤的空間大小(單位:字節(jié))
- Data_free:對(duì)于MyISAM表,表示已分配,但現(xiàn)在未使用的空間,并且包含了已被刪除行的空間(行),以及后續(xù)可以被INSERT利用到的空間。
- Auto_increment:下一個(gè)Auto_increment的值
- Create_time:表的創(chuàng)建時(shí)間。
- Update_time:表的最近修改時(shí)間(更新時(shí)間)。
- Check_time:使用check table命令或myisamchk工具最近一次檢查表時(shí)間。
- Collation:表的默認(rèn)字符集和字符排序規(guī)則。
- Checksum:如果啟用,保存的是整個(gè)表的實(shí)時(shí)校驗(yàn)和。
- Create_options:指表創(chuàng)建時(shí)的其他所有選項(xiàng)。
- Comment:包含了其他額外信息,MyISAM引擎(表),保存的是表在創(chuàng)建時(shí)已有的注釋;innodb引擎(表),保存的是InnoDB表空間的剩余空間信息;如果是一個(gè)視圖,該列(注釋)里面包含了"VIEW"字樣。
InnoDB存儲(chǔ)引擎
InnoDB是MySQL的默認(rèn)事務(wù)型引擎,也是最重要、使用最廣泛的存儲(chǔ)引擎。他被設(shè)計(jì)出來(lái)處理大量的短期事務(wù),短期事務(wù)大部分情況是正常提交的,很少會(huì)被回滾。InnoDB的性能和自動(dòng)崩潰恢復(fù)特性,使得他在非事務(wù)型存儲(chǔ)需求中也很流行。除非要特別重要的原因需要使用其他存儲(chǔ)引擎,否則應(yīng)該優(yōu)先考慮InnoDB。
InnoDB概覽
InnoDB的數(shù)據(jù)存儲(chǔ)在表空間中,表空間是由InnoDB管理的一個(gè)黑盒子,由一系列的數(shù)據(jù)文件組成。。在MySQL4.1以后的版本中,InnoDB可以將每個(gè)表的數(shù)據(jù)和索引存放在單獨(dú)的文件中。InnoDB也可以使用裸設(shè)備作為表空間的存儲(chǔ)介質(zhì),但現(xiàn)代的文件系統(tǒng)使得裸設(shè)備不再是必要選擇。
InnoDB采用MVCC來(lái)支持高并發(fā),并且實(shí)現(xiàn)了四個(gè)標(biāo)準(zhǔn)的隔離級(jí)別。其默認(rèn)級(jí)別是REPEATABLE READ(可重復(fù)讀),并且通過(guò)間隙鎖策略防止幻讀的出現(xiàn)。間隙鎖使得InnoDB不僅僅鎖定查詢涉及的行,還會(huì)對(duì)索引中的間隙進(jìn)行鎖定,以防止幻影行的插入。
InnoDB表是基于聚簇索引建立的,InnoDB的索引結(jié)構(gòu)和MySQL的其他存儲(chǔ)引擎有很大不同,聚簇索引對(duì)主鍵查詢有很高的性能。不過(guò)它的二級(jí)索引(非主鍵索引)中必須包含主鍵列,所以如果主鍵列很大的話,其他的索引都會(huì)很大。因此,若表上的索引較多的話,主鍵應(yīng)當(dāng)盡可能的小。
InnoDB內(nèi)部做了很多優(yōu)化,包括從磁盤讀取數(shù)據(jù)時(shí)采用的可預(yù)測(cè)性預(yù)讀,能夠自動(dòng)在內(nèi)存中創(chuàng)建hash索引以加速讀操作的自適應(yīng)哈希索引(adaptive hash index),以及能夠加速插入操作的插入緩沖區(qū)(insert buffer)等。
MyISAM存儲(chǔ)引擎
在MySQL 5.1及之前的版本,MyISAM是默認(rèn)的存儲(chǔ)引擎。MyISAM提供了大量的特性,包括全文索引,壓縮,空間函數(shù)(GIS)等,但MyISAM不支持事務(wù)和行級(jí)鎖且有一個(gè)毫無(wú)疑問(wèn)的缺陷就是崩潰后無(wú)法安全恢復(fù)。正是由于MyISAM引擎的緣故,即使MySQL支持事務(wù)已經(jīng)很長(zhǎng)時(shí)間了,在很多人的概念中MySQL還是非事務(wù)性的數(shù)據(jù)庫(kù)。盡管MyISAM引擎不支持事務(wù),不支持崩潰后的安全回復(fù),但它絕不是一無(wú)是處的。對(duì)于只讀的數(shù)據(jù),或者表比較小,可以忍受修復(fù)操作,則依然可以繼續(xù)使用MyISAM。
存儲(chǔ)
MyISAM會(huì)將表存儲(chǔ)在兩個(gè)文件中:數(shù)據(jù)文件和索引文件,分別以.myd和.myi為拓展名。MyISAM表可以包含動(dòng)態(tài)或者靜態(tài)(長(zhǎng)度固定)行。MySQL會(huì)根據(jù)表的定義來(lái)決定采用何種行格式。MyISAM表可以存儲(chǔ)的行記錄數(shù),一般受限于可用磁盤控件,或者操作系統(tǒng)中單個(gè)文件的最大尺寸。
MyISAM特性
加鎖與并發(fā):MyISAM對(duì)整張表進(jìn)行加鎖,而不是針對(duì)行。讀取時(shí)會(huì)對(duì)需要讀到的所有表加共享鎖,寫入時(shí)則對(duì)表加排他鎖。但是在表有讀取查詢的時(shí)候,也可以往表中插入新的記錄(這被稱為并發(fā)插入)。
修復(fù):對(duì)與MyISAM表,MySQL可以手工或者自動(dòng)執(zhí)行檢查和修復(fù)操作,但這里說(shuō)的修復(fù)和事務(wù)回復(fù)以及崩潰恢復(fù)是不同的概念。執(zhí)行表的修復(fù)可能導(dǎo)致一些數(shù)據(jù)丟失,而且修復(fù)操作是非常慢的。可以通過(guò)CHECK TABLE mytable檢查表的錯(cuò)誤,如果有錯(cuò)誤可以通過(guò)執(zhí)行REPAIR TABLE mytable進(jìn)行修復(fù)。另外,如果MySQL服務(wù)器已經(jīng)關(guān)閉,可以通過(guò)myisamchk命令行工具進(jìn)行檢查和修復(fù)操作。
索引特性:對(duì)于MyISAM表,即使是BLOB和TEXT等長(zhǎng)字段,也可以基于其前500個(gè)字符創(chuàng)建索引,MyISAM也支持全文索引,這是一種基于分詞創(chuàng)建的索引,可以支持復(fù)雜的查詢。
延遲更新索引:創(chuàng)建MyISAM表的時(shí)候,如果指定了DELAY_KEY_WRITE選項(xiàng),在每次修改執(zhí)行完成時(shí),不會(huì)立刻將修改的索引數(shù)據(jù)寫入磁盤,而是會(huì)寫到內(nèi)存中的鍵緩沖區(qū),只有在清理鍵緩沖區(qū)或者關(guān)閉表的時(shí)候才會(huì)將對(duì)應(yīng)的索引塊寫入到磁盤。這種方式可以極大的提升寫入性能,但是在數(shù)據(jù)或者主機(jī)崩潰時(shí)會(huì)造成索引損壞,需要執(zhí)行修復(fù)操作。延遲更新索引的特性,可以在全局設(shè)置,也可以為單個(gè)表設(shè)置。
MyISAM壓縮表
如果表在創(chuàng)建并導(dǎo)入后,不會(huì)再進(jìn)行修改操作,那么這樣的表或許適合采用MyISAM壓縮表。?
可以使用myisampack對(duì)MyISAM表進(jìn)行壓縮(也叫打包pack)。壓縮表是不能進(jìn)行修改的(除非先將表解除壓縮,修改數(shù)據(jù),然后再次壓縮)。壓縮表可以極大地減少磁盤空間占用,因此也可以減少磁盤I/O,從而提升查詢性能。壓縮表也支持索引,但索引也是只讀的。
以現(xiàn)在的硬件能力,對(duì)大多數(shù)應(yīng)用場(chǎng)景,讀取壓縮表數(shù)據(jù)時(shí)的解壓帶來(lái)的開(kāi)銷影響并不大,而減少I/O帶來(lái)的好處則要大得多。壓縮時(shí)表中的記錄是獨(dú)立壓縮的,所以讀取單行的時(shí)候不需要去解壓整個(gè)表(甚至也不解壓行所在的整個(gè)頁(yè)面)。
MyISAM性能
MyISAM引擎設(shè)計(jì)簡(jiǎn)單,數(shù)據(jù)以緊密格式存儲(chǔ),所以在某些場(chǎng)景下的性能很好。MyISAM有一些服務(wù)器級(jí)別的性能擴(kuò)展限制,比如對(duì)索引鍵緩沖區(qū)(key cache)的Mutex鎖,MariaDB基于段(segment)的索引緩沖區(qū)機(jī)制來(lái)避免該問(wèn)題。但MyISAM最典型的性能問(wèn)題還是表鎖的問(wèn)題,如果你發(fā)現(xiàn)所有的查詢都長(zhǎng)期處于“Locked”狀態(tài),那么毫無(wú)疑問(wèn)表鎖就是罪魁禍?zhǔn)住?
MySQL內(nèi)建的其他存儲(chǔ)引擎
Archive引擎
Archive引擎只支持INSERT和SELECT操作,在MySQL5.1之前也不支持索引。Archive引擎會(huì)緩存所有的寫并利用zlib對(duì)插入的行進(jìn)行壓縮,所以比MyISAM表的磁盤I/O更少。但是每次SELECT查詢都需要全表掃描。所以Archive表適合日志和數(shù)據(jù)采集類的應(yīng)用,這類應(yīng)用鎖數(shù)據(jù)分析時(shí)往往需要全表掃描,或者在一些需要快速INSERT操作的場(chǎng)合下也可以使用。
Archive引擎支持行級(jí)鎖和專用的緩沖區(qū),所以可以實(shí)現(xiàn)高并發(fā)的插入。在一個(gè)查詢的開(kāi)始直到返回表中存在所有行數(shù)之前,Archive引擎會(huì)阻止其他SELECT的執(zhí)行,以實(shí)現(xiàn)一致性讀。另外,也實(shí)現(xiàn)了批量插入在完成之前對(duì)讀操作是不可見(jiàn)的。這種機(jī)制模仿了MVCC的一些特性。但Archive不是一個(gè)事務(wù)型引擎,而是一個(gè)針對(duì)高速插入和壓縮做了優(yōu)化的簡(jiǎn)單引擎。
Blackhole引擎
Blackhole引擎沒(méi)有實(shí)現(xiàn)任何的存儲(chǔ)機(jī)制,他會(huì)舍棄所有插入的數(shù)據(jù),不做任何保存。但是服務(wù)器會(huì)記錄Blackhole表的日志,所以可以用于復(fù)制數(shù)據(jù)到備庫(kù),或者只是簡(jiǎn)單的記錄到日志。這種特殊的存儲(chǔ)引擎可以在一些特殊的復(fù)制框架和日志審核時(shí)發(fā)揮作用,但是也會(huì)存在很多問(wèn)題,所以不推薦使用。
CSV引擎
CSV引擎可以將普通的CSV文件(逗號(hào)分割值的文件)作為MySQL的表來(lái)處理,但是這種表不支持索引。CSV引擎可以在數(shù)據(jù)庫(kù)運(yùn)行時(shí)拷入或者拷出文件。可以將Excel等電子表格軟件中的數(shù)據(jù)存儲(chǔ)為CSV文件,然后復(fù)制到MySQL數(shù)據(jù)目錄下,就能在MySQL中打開(kāi)使用。同樣,如果將數(shù)據(jù)寫入到一個(gè)CSV引擎表,其他外部程序也能立即從表的數(shù)據(jù)文件中讀取CSV格式的數(shù)據(jù)。因此CSV引擎可以作為一個(gè)數(shù)據(jù)交換的機(jī)制非常有用。
Federated引擎
Federated引擎是訪問(wèn)其他MySQL服務(wù)器的一個(gè)代理,他會(huì)創(chuàng)建一個(gè)到遠(yuǎn)程MySQL服務(wù)器的客戶端連接,并將查詢傳輸?shù)竭h(yuǎn)程服務(wù)器執(zhí)行,然后提取或者發(fā)送需要的數(shù)據(jù)。盡管該引擎看起來(lái)提供了一個(gè)很好的跨服務(wù)器靈活性,但是經(jīng)常會(huì)帶來(lái)問(wèn)題,因此默認(rèn)是禁用的,后續(xù)改良版本FederatedX.
Memory引擎
如果需要快速訪問(wèn)數(shù)據(jù),并且這些數(shù)據(jù)不會(huì)被修改,重啟后丟失也沒(méi)關(guān)系,那么可以使用Memory表(以前也叫HEAP表)是非常有用的,Memory比MyISAM至少要快一個(gè)數(shù)量級(jí),因?yàn)樗袛?shù)據(jù)都保存在內(nèi)存中,不需要進(jìn)行磁盤I/O。Memory表在重啟后結(jié)構(gòu)會(huì)保留,但是數(shù)據(jù)會(huì)丟失。
Memory表可以在很多場(chǎng)景發(fā)揮好的作用:
- 用于查找(lookup)或者映射(mapping)表,如將郵編和州名映射的表。
- 用于緩存周期性聚合數(shù)據(jù)(periodically aggregated data)的數(shù)據(jù)。
- 用于保存數(shù)據(jù)分析中產(chǎn)生的中間數(shù)據(jù)。
Memory表支持Hash索引,因此查找操作非常快。雖然Memory速度非常快,但是還是無(wú)法替代基于磁盤的表。Memory是表級(jí)鎖,因此并發(fā)寫入的性能較低。他不支持BLOB以及TEXT類型的列,并且每行長(zhǎng)度是固定的,所以即使指定了VARCHAR列,實(shí)際存儲(chǔ)的時(shí)候也會(huì)轉(zhuǎn)換成CHAR,這可能導(dǎo)致部分內(nèi)存的浪費(fèi)(一些限制基本在Percona版本基本解決)。?
如果MySQL在執(zhí)行查詢的過(guò)程中需要使用臨時(shí)表來(lái)保存中間結(jié)果,內(nèi)部使用的臨時(shí)表就是Memory表。如果中間結(jié)果太大超出了Memory限制,或者含有BLOB或TEXT字段,則臨時(shí)表會(huì)轉(zhuǎn)換成MyISAM表。
人們經(jīng)常混淆Memory表或者臨時(shí)表,臨時(shí)表是指CREATE TEMPOARY TABLE語(yǔ)句創(chuàng)建的表,他可以使用任何存儲(chǔ)引擎,因此和Memory表不是一回事。臨時(shí)表只在單個(gè)連接可見(jiàn),當(dāng)連接斷開(kāi)時(shí),臨時(shí)表也將不復(fù)存在。
Merge引擎
Merge引擎是MyISAM引擎的一個(gè)變種。Merge表是由多個(gè)MyISAM表合成的虛擬表。如果將MySQL用于日志或者數(shù)據(jù)倉(cāng)庫(kù)類應(yīng)用,該引擎可以發(fā)揮作用。但是引入分區(qū)后,該引擎被放棄。
NDB集群引擎
MySQL服務(wù)器、NDB集群存儲(chǔ)引擎,以及分布式的、share-nothing的、容災(zāi)的、高可用的NDB數(shù)據(jù)庫(kù)的組合,被稱為MySQL集群(MySQL-Cluster)。
選擇合適的引擎
大部分情況下InnoDB都是正確的選擇,所以O(shè)racle在MySQL5.5版本時(shí),將InnoDB作為默認(rèn)的存儲(chǔ)引擎。除非使用到了某些InnoDB不具備的特性,并且沒(méi)有其他辦法可以替代,否則都應(yīng)該優(yōu)先考慮InnoDB引擎。例如需要使用全文索引,建議優(yōu)先考慮InnoDB+Sphinx組合,而不是使用支持全文索引的MyISAM。當(dāng)然如果不需要使用InnoDB的特性,同時(shí)其他特性能夠更好地滿足需求,可以考慮使用其他存儲(chǔ)引擎。
除非萬(wàn)不得已,否則建議不要混合使用多種存儲(chǔ)引擎,否則可能會(huì)帶來(lái)一系列復(fù)雜的問(wèn)題,以及一些潛在的bug和邊界問(wèn)題。存儲(chǔ)引擎層和服務(wù)層的交互已經(jīng)比較復(fù)雜,更不用說(shuō)混合多個(gè)存儲(chǔ)引擎了。至少,混合存儲(chǔ)對(duì)一致性備份和服務(wù)器參數(shù)配置都帶來(lái)了困難。
如果應(yīng)用需要不同的存儲(chǔ)引擎,請(qǐng)考慮以下幾個(gè)因素:
事務(wù):如果需要事務(wù)支持,那么InnoDB(或者XtraDB)是目前最穩(wěn)定并且經(jīng)過(guò)驗(yàn)證的選擇。如果不需要使用事務(wù),并且主要是使用SELECT和INSERT操作,那么MyISAM是一個(gè)不錯(cuò)的選擇。一般日志型的應(yīng)用比較符合這一特性。
備份:備份的需求也會(huì)影響存儲(chǔ)引擎的選擇。如果可以定期的關(guān)閉服務(wù)器進(jìn)行備份,那么備份的因素可以忽略。反之,如果需要在線熱備份,那么選擇InnoDB就是基本要求。
崩潰恢復(fù):數(shù)據(jù)量比較大的時(shí)候,系統(tǒng)崩潰后如何快速恢復(fù)是一個(gè)需要考慮的問(wèn)題。相對(duì)而言,MyISAM崩潰后發(fā)送損壞的概率比InnoDB要高很多,而且恢復(fù)速度也要慢。因此,即使不需要事務(wù)支持,很多人也選擇InnoDB引擎,這是一個(gè)非常重要的因素。
特有的特性:最后有些應(yīng)用可能依賴一些存儲(chǔ)引擎所獨(dú)有的特性或者優(yōu)化,比如很多應(yīng)用依賴聚簇索引的優(yōu)化。另外,MySQL中也只有MyISAM支持地理空間搜索。如果一個(gè)存儲(chǔ)引擎擁有一些關(guān)鍵的特性,同時(shí)又缺乏一些必要的特性,那么有時(shí)候就不得不做折中的考慮,或者在架構(gòu)設(shè)計(jì)上做一些取舍。某些存儲(chǔ)引擎無(wú)法直接支持的特性,有時(shí)候可以通過(guò)變通也可以滿足需要。
日志型應(yīng)用
假設(shè)你需要實(shí)時(shí)地記錄一臺(tái)中心電話交換機(jī)的每一通電話的日志到MySQL中。或者通過(guò)Apache的mod_log_sql模塊將網(wǎng)站的所有訪問(wèn)信息自己記錄到表中。這一類的應(yīng)用插入速度有很高的要求,數(shù)據(jù)庫(kù)不應(yīng)該成為瓶頸。MyISMAM或者Archive存儲(chǔ)引擎對(duì)這類應(yīng)用比較合適,因?yàn)樗鼈冮_(kāi)銷低,并且插入速度非常快。
如果需要對(duì)記錄的日志做分析報(bào)表,則事情就會(huì)變得有趣了。生成報(bào)表的SQL很有可能導(dǎo)致插入效率明顯降低,該怎么做呢?
一種解決方法,是利用MySQL內(nèi)置的復(fù)制方案將數(shù)據(jù)復(fù)制一份到備庫(kù),然后備份庫(kù)上執(zhí)行比較消耗時(shí)間和CPU的查詢。這樣主庫(kù)只用于高效的插入工作,而備庫(kù)的查詢無(wú)須擔(dān)心影響到日志的插入性能。當(dāng)然也可以在系統(tǒng)負(fù)載較低的時(shí)候執(zhí)行報(bào)表查詢操作。但應(yīng)用在不斷變化,如果依賴這個(gè)策略可能以后會(huì)導(dǎo)致問(wèn)題。
另一種方法,在日志記錄表的名字中包含年和月的信息,比如web_log_2012_01或者web_log_2012_jan。這樣可以在已經(jīng)沒(méi)有插入操作的歷史表上做頻繁的查詢操作,而不會(huì)干擾到最新的當(dāng)前表上的插入操作。
只讀或者大部分情況下只讀的表
有些表的數(shù)據(jù)用于編制類目或者分列清單(如工作崗位、競(jìng)拍、不動(dòng)產(chǎn)等),這種應(yīng)用的場(chǎng)景是典型的讀多寫少的業(yè)務(wù)。如果不介意MyISAM的崩潰問(wèn)題,選用MyISAM引擎是合適的。不過(guò)不要低估崩潰恢復(fù)問(wèn)題的重要性。有些存儲(chǔ)引擎不會(huì)保證數(shù)據(jù)完全寫入到磁盤中,而許多用戶實(shí)際上并不清楚這樣有多大風(fēng)險(xiǎn)(MyISAM只將數(shù)據(jù)寫到內(nèi)存中,然后等待操作系統(tǒng)定期將數(shù)據(jù)刷出到磁盤上)。
一個(gè)值得推薦的方式,是在性能測(cè)試環(huán)境模擬真實(shí)的環(huán)境,運(yùn)行應(yīng)用,然后拔下電源模擬崩潰測(cè)試。對(duì)于崩潰恢復(fù)的第一手測(cè)試經(jīng)驗(yàn)是無(wú)價(jià)之寶,可以避免碰到真正的崩潰手足無(wú)措。?
不要輕易相信“MyISAM比InnoDB快”之類的經(jīng)驗(yàn)之談,這個(gè)結(jié)論往往不是絕對(duì)的。在很多已知的場(chǎng)景中,InnoDB的速度都可以讓MyISAM望塵莫及,尤其是使用到了聚簇索引,或者需要訪問(wèn)的數(shù)據(jù)都可以放入到內(nèi)存的應(yīng)用。后面還會(huì)介紹更多影響存儲(chǔ)引擎性能的因素(如數(shù)據(jù)大小、I/O請(qǐng)求量,主鍵還是二級(jí)索引等)以及這些因素對(duì)應(yīng)用的影響。
當(dāng)設(shè)計(jì)上述類型的應(yīng)用時(shí),建議采用InnoDB,MyISAM引擎一開(kāi)始可能沒(méi)有任何問(wèn)題,但是隨著應(yīng)用壓力的上升,則可能迅速惡化。各種鎖的爭(zhēng)用、崩潰后數(shù)據(jù)丟失的問(wèn)題也都會(huì)隨之而來(lái)。
訂單處理
如果涉及訂單處理,那么支持事務(wù)就是必要選項(xiàng)。另外一個(gè)重要考慮點(diǎn)就是存儲(chǔ)引擎對(duì)外鍵的支持情況。InnoDB是訂單處理類應(yīng)用最佳選擇。
電子公告牌和主題討論論壇
對(duì)于MySQL用戶,主題討論群是一個(gè)很有意思的話題。當(dāng)前有成百上千的基于PHP或者Perl的免費(fèi)系統(tǒng)可以支持主題討論。其中大部分的數(shù)據(jù)庫(kù)效率并不高,因?yàn)樗麄兌啻髢A向于在一次請(qǐng)求中執(zhí)行盡可能多的查詢語(yǔ)句。另外還有部分系統(tǒng)設(shè)計(jì)為不采用數(shù)據(jù)庫(kù),當(dāng)然也就無(wú)法利用到數(shù)據(jù)庫(kù)提供的一些方便的特性。主題討論區(qū)一般都有更新計(jì)數(shù)器,并且為各個(gè)主題計(jì)算訪問(wèn)統(tǒng)計(jì)信息。多數(shù)應(yīng)用只設(shè)計(jì)了幾張表保存所有的數(shù)據(jù),所以核心表的讀寫壓力可能非常大。為保證這些核心表的數(shù)據(jù)一致性,鎖成為資源競(jìng)爭(zhēng)的主要因素。
盡管有這些設(shè)計(jì)缺陷,但大多數(shù)應(yīng)用在中低負(fù)載時(shí)可以工作的很好。如果web站點(diǎn)的規(guī)模迅速擴(kuò)張,流量隨之猛增,則數(shù)據(jù)庫(kù)訪問(wèn)可能變得非常慢。此時(shí)一個(gè)典型的解決方案是更改為更高讀寫的存儲(chǔ)引擎,但有時(shí)用戶會(huì)發(fā)現(xiàn)這么做反而導(dǎo)致系統(tǒng)變慢了。
用戶可能沒(méi)有意識(shí)到這是由于某些查詢的緣故,典型的如:
mysql> SELECT COUNT(*) FROM table;?
問(wèn)題就在于,不是所有的存儲(chǔ)引擎運(yùn)行上述查詢都非常快:對(duì)于MyISAM確實(shí)會(huì)很快,其他的可能不行。每種存儲(chǔ)引擎都能找出類似對(duì)自己有利的例子,下一章將幫助用戶分析這些狀況,演示如何發(fā)現(xiàn)和解決存在的這類問(wèn)題。
CD-ROM應(yīng)用
如果要發(fā)布一個(gè)基于CD-ROM或者DVD-ROM并且使用MySQL數(shù)據(jù)文件的應(yīng)用,可以考慮使用MyISAM表或者M(jìn)yISAM壓縮表,這樣的表之間可以隔離并且可以在不同介質(zhì)上相互拷貝。MyISAM壓縮表比未壓縮的表要節(jié)約很多空間,但壓縮表是只讀的。在某些應(yīng)用中可能是一個(gè)大問(wèn)題。但是如果數(shù)據(jù)放到只讀介質(zhì)的場(chǎng)景下,壓縮表的只讀特性就不是問(wèn)題,就沒(méi)有理由不采用壓縮表的。
大數(shù)據(jù)量
什么樣的數(shù)據(jù)量算大?我們創(chuàng)建或者管理的很多InnoDB數(shù)據(jù)庫(kù)的數(shù)據(jù)量在3~5TB之間或者更大,這是單臺(tái)機(jī)器上的量,不是一個(gè)分片(shard)的量。這些系統(tǒng)運(yùn)行還不錯(cuò),要做到這一點(diǎn)需要合理的選擇硬件,做好物理設(shè)計(jì),并為服務(wù)器的I/O瓶頸做好規(guī)劃。在這樣的數(shù)據(jù)量下,如果采用MyISAM,崩潰后的恢復(fù)就是一個(gè)噩夢(mèng)。
如果數(shù)據(jù)量繼續(xù)增長(zhǎng)到10TB以上的級(jí)別,可能就需要建立數(shù)據(jù)倉(cāng)庫(kù)。Infobright是MySQL數(shù)據(jù)倉(cāng)庫(kù)最成功的解決方案。也有一些大數(shù)據(jù)庫(kù)不適合Infobright,卻可能適合TokuDB。
轉(zhuǎn)換表的引擎
有很多種方法可以將表的存儲(chǔ)引擎轉(zhuǎn)換成另外一種引擎。每種方法都有其優(yōu)點(diǎn)和缺點(diǎn)。下面介紹其中的三種方法:?
ALTER TABLE
將表從一個(gè)引擎修改為另一個(gè)引擎最簡(jiǎn)單的版本是使用ALTER TABLE語(yǔ)句。下面的語(yǔ)句將mytable的引擎修改為InnoDB:
mysql> create table mytable(name varchar(32)) ENGINE = MyISAM; Query OK, 0 rows affected (0.05 sec)mysql> SHOW TABLE STATUS LIKE 'mytable'\G; *************************** 1. row ***************************Name: mytableEngine: MyISAMVersion: 10Row_format: DynamicRows: 0Avg_row_length: 0Data_length: 0 Max_data_length: 281474976710655Index_length: 1024Data_free: 0Auto_increment: 1Create_time: 2018-10-07 03:58:03Update_time: 2018-10-07 03:58:03Check_time: NULLCollation: utf8mb4_0900_ai_ciChecksum: NULLCreate_options: Comment: 1 row in set (0.11 sec)mysql> ALTER TABLE mytable ENGINE=InnoDB; Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0mysql> SHOW TABLE STATUS LIKE 'mytable'\G; *************************** 1. row ***************************Name: mytableEngine: InnoDBVersion: 10Row_format: DynamicRows: 0Avg_row_length: 0Data_length: 0 Max_data_length: 281474976710655Index_length: 1024Data_free: 0Auto_increment: 1Create_time: 2018-10-07 03:59:16Update_time: 2018-10-07 03:58:03Check_time: NULLCollation: utf8mb4_0900_ai_ciChecksum: NULLCreate_options: Comment: 1 row in set (0.00 sec)
上述語(yǔ)法可以適用于任何存儲(chǔ)引擎。但是有一個(gè)問(wèn)題:需要執(zhí)行很長(zhǎng)的時(shí)間。MySQL會(huì)按行將數(shù)據(jù)從原表復(fù)制到一張新的表中,在復(fù)制期間可能會(huì)消耗系統(tǒng)所有的I/O能力,同時(shí)原表上會(huì)加上讀鎖。所以,在繁忙的表上執(zhí)行此操作需要特別小心。一個(gè)替代方案是采用接下來(lái)將要討論的導(dǎo)出導(dǎo)入的方法,手工進(jìn)行表的復(fù)制。
如果轉(zhuǎn)換表的存引擎,將會(huì)失去原引擎相關(guān)的所有特性。例如,將InnoDB表轉(zhuǎn)為MyISAM表,再轉(zhuǎn)換為InnoDB,原InnoDB表上所有的外鍵將丟失。
導(dǎo)出與導(dǎo)入
為了更好控制轉(zhuǎn)換的過(guò)程,可以使用mysqldump工具將數(shù)據(jù)導(dǎo)出到文件,然后修改文件中的CREATE TABLE語(yǔ)句的存儲(chǔ)引擎選項(xiàng),注意同時(shí)修改表名,因?yàn)橥粋€(gè)數(shù)據(jù)庫(kù)中不能存在相同的表名,即使他們使用的是不同的存儲(chǔ)引擎。同時(shí)還要注意mysqldump會(huì)默認(rèn)在CREATE TABLE前加上DROP TABLE語(yǔ)句,不注意這一點(diǎn)可能會(huì)導(dǎo)致數(shù)據(jù)丟失。
創(chuàng)建與查詢(CREATE和SELECT)
第三種轉(zhuǎn)換的技術(shù)綜合了第一種方法的高效和第二種方法的安全。不需要導(dǎo)出整個(gè)表的數(shù)據(jù),而是先創(chuàng)建一個(gè)新的存儲(chǔ)引擎表,然后利用INSERT ...SELECT語(yǔ)法來(lái)導(dǎo)數(shù)據(jù):
創(chuàng)建存儲(chǔ)引擎為MyISAM的myisam_table表,并插入測(cè)試數(shù)據(jù)
mysql> create table myisam_table(name varchar(32)) ENGINE = MyISAM; Query OK, 0 rows affected (0.12 sec)mysql> insert into `myisam_table`(`name`) values('Amy'); Query OK, 1 row affected (0.01 sec)mysql> insert into `myisam_table`(`name`) values('John'); Query OK, 1 row affected (0.00 sec)mysql> insert into `myisam_table`(`name`) values('Rose'); Query OK, 1 row affected (0.01 sec)mysql> select * from myisam_table; +------+ | name | +------+ | Amy | | John | | Rose | +------+ 3 rows in set (0.00 sec)
創(chuàng)建和myisam_table存儲(chǔ)引擎一樣的innodb_table,并修改其存儲(chǔ)引擎
mysql> create table innodb_table like myisam_table; Query OK, 0 rows affected (0.07 sec)mysql> SHOW TABLE STATUS LIKE 'innodb_table'\G; *************************** 1. row ***************************Name: innodb_tableEngine: MyISAMVersion: 10Row_format: DynamicRows: 0Avg_row_length: 0Data_length: 0 Max_data_length: 281474976710655Index_length: 1024Data_free: 0Auto_increment: 1Create_time: 2018-10-07 04:09:37Update_time: 2018-10-07 04:09:37Check_time: NULLCollation: utf8mb4_0900_ai_ciChecksum: NULLCreate_options: Comment: 1 row in set (0.02 sec)mysql> select * from innodb_table; Empty set (0.00 sec)mysql> alter table innodb_table ENGINE=InnoDB; Query OK, 0 rows affected (0.07 sec) Records: 0 Duplicates: 0 Warnings: 0mysql> SHOW TABLE STATUS LIKE 'innodb_table'\G; *************************** 1. row ***************************Name: innodb_tableEngine: InnoDBVersion: 10Row_format: DynamicRows: 0Avg_row_length: 0Data_length: 0 Max_data_length: 281474976710655Index_length: 1024Data_free: 0Auto_increment: 1Create_time: 2018-10-07 04:10:03Update_time: 2018-10-07 04:09:37Check_time: NULLCollation: utf8mb4_0900_ai_ciChecksum: NULLCreate_options: Comment: 1 row in set (0.00 sec)
使用INSERT ...SELECT語(yǔ)句將myisam_table的數(shù)據(jù)插入到innodb_table中
mysql> insert into innodb_table select * from myisam_table; Query OK, 3 rows affected (0.06 sec) Records: 3 Duplicates: 0 Warnings: 0mysql> select * from innodb_table; +------+ | name | +------+ | Amy | | John | | Rose | +------+ 3 rows in set (0.00 sec)
如果數(shù)據(jù)量不大的話,這樣工作很好。如果數(shù)據(jù)量很大,則可以考慮分批處理針對(duì)每一段數(shù)據(jù)執(zhí)行事務(wù)提交操作,以避免大事務(wù)產(chǎn)生過(guò)多的undo。假設(shè)主鍵字段id,重復(fù)執(zhí)行以下語(yǔ)句(最小值x和最大值y的進(jìn)行相應(yīng)替換)將數(shù)據(jù)導(dǎo)入到新表:
mysql> START TRANSACTION;mysql> INSERT INTO innodb_table SELECT * FROM myisam_table WHERE id BETWEEN x AND y;mysql> COMMIT;
這樣操作完成后,新表是原表的一個(gè)全量復(fù)制,原表還在,如果需要可以刪除原表。如果有必要可以在執(zhí)行過(guò)程中對(duì)原表加鎖,以確保新表和原表的一致性。
轉(zhuǎn)載于:https://www.cnblogs.com/beiluowuzheng/p/9749315.html
總結(jié)
以上是生活随笔為你收集整理的MySQL之架构与历史(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux入门_韩顺平_复习版_文件目录
- 下一篇: 忘记mysql数据库密码时进行修改方法