InnoDB體系架構
上圖簡單顯示了InnoDB存儲引擎的體系架構圖中可見证杭,InnoDB存儲引擎有多個內(nèi)存塊徽职,可以認為這些內(nèi)存塊組成了一個大的內(nèi)存池枝缔,負責如下工作:
維護所有進程/線程需要訪問的多個內(nèi)部數(shù)據(jù)結(jié)構枣抱。
緩存磁盤上的數(shù)據(jù)熔吗,方便快速地讀取,同時在對磁盤文件的數(shù)據(jù)修改之前在這里緩存佳晶。
重做日志(redo log)緩沖
......
后臺線程的主要作用是負責刷新內(nèi)存池中的數(shù)據(jù)桅狠,保證緩沖池中的內(nèi)存緩存是最近的數(shù)據(jù)。此外將已修改的數(shù)據(jù)文件刷新到磁盤文件轿秧,同時保證在數(shù)據(jù)庫發(fā)生異常的情況下InnoDB能恢復到正常運行狀態(tài)中跌。
后臺線程
InnoDB存儲引擎是多線程的模型,因此其后臺有多個不同的后臺線程菇篡,負責處理不同的任務漩符。
1.Master Thread
Master Thread是一個非常核心的后臺線程,主要負責將緩沖池中的數(shù)據(jù)異步刷新到磁盤驱还,保證數(shù)據(jù)的一致性嗜暴,包括臟頁的刷新,合并插入緩沖议蟆、UNDO頁的回收等闷沥。
2.IO Thread
在InnoDB中大量使用了AIO來處理寫IO請求,這樣可以極大提高數(shù)據(jù)庫的性能咐容。而IO Thread的工作主要負責這些IO請求的回調(diào)處理舆逃。
可通過命令SHOW ENGINE INNODB STATUS來觀察InnoDB中的IO Thread。
3.Purge Thread
事務被提交后,其所使用的undo log可能不再需要路狮,因此需要PurgeThread來回收已經(jīng)使用并分配的undo頁鸟雏。用戶可以在MySQL數(shù)據(jù)庫的配置文件中添加如下命令來啟動獨立的Purge Thread:
innodb_purge_threads=1
4.Page Cleaner Thread
InnoDB 1.2版本中引入。其作用是將之前版本中臟頁的刷新操作都放到單獨的線程中來完成览祖,目的是減輕原Master Thread的工作及對于用戶查詢線程的阻塞孝鹊,進一步提高InnoDB存儲引擎的性能。
內(nèi)存
1.緩沖池(Database Buffer Pool)
InnoDB存儲引擎是基于磁盤存儲的展蒂,并將其中的記錄按照頁的方式進行管理又活。在數(shù)據(jù)庫管理系統(tǒng)中,由于CPU速度與磁盤速度之間的鴻溝锰悼,基于磁盤的數(shù)據(jù)庫系統(tǒng)通常使用緩沖池技術來提高數(shù)據(jù)庫的整體性能柳骄。
緩存池簡單來說就是一塊內(nèi)存區(qū)域,通過內(nèi)存的速度來彌補磁盤速度較慢對數(shù)據(jù)庫性能的影響箕般。在數(shù)據(jù)庫中進行讀取頁的操作耐薯,首先將從磁盤讀到的頁存放在緩沖池中,這個過程稱為“FIX”在緩沖池中丝里。下一次再讀相同的頁時曲初,首先判斷該頁是否在緩沖池中。若在緩沖池中杯聚,稱為頁在緩沖池中被命中臼婆,直接讀取該頁。否則幌绍,讀取磁盤上的頁颁褂。
對于數(shù)據(jù)庫中頁的修改操作,則首先修改在緩沖池中的頁傀广,然后再以一定的頻率刷新到磁盤上(并不是每次頁發(fā)生更新時刷新颁独,而是通過一種稱為CheckPoint的機制刷新回磁盤)。
綜上所述伪冰,緩沖池的大小直接影響數(shù)據(jù)庫的整體性能誓酒。對于InnoDB而言,通過參數(shù)innodb_buffer_pool_size來設置糜值。
緩沖池中緩存的數(shù)據(jù)頁類型有:索引頁丰捷、數(shù)據(jù)頁、undo頁寂汇、插入緩沖病往、自適應哈希索引、InnoDB存儲的鎖信息骄瓣、數(shù)據(jù)字典信息等停巷。
InnoDB允許有多個緩沖池實例。每個頁根據(jù)哈希值平均分配到不同的緩沖池實例中,好處是減少數(shù)據(jù)庫內(nèi)部的資源競爭畔勤,增加數(shù)據(jù)庫的并發(fā)能力蕾各。
2.LRU List、Free List和Flush List
通常來說庆揪,數(shù)據(jù)庫中的緩沖池是通過LRU(Latest Recent Used)算法來進行管理的式曲。即最頻繁使用的頁在LRU列表的前端,而最少使用的頁在LRU列表的尾端缸榛。當緩沖池不能存放新讀取到的頁時吝羞,將首先釋放LRU列表中尾端的頁。
在InnoDB中内颗,對傳統(tǒng)的LRU算法做了一些優(yōu)化钧排,添加了midpoint位置。新讀取到的頁不是直接放到LRU列表的首部均澳,而是放到midpoint位置恨溜。在默認配置下,該位置在LRU列表長度的5/8處找前。通過參數(shù)innodb_old_blocks_pct控制糟袁。
在InnoDB中,把midpoint之后的列表稱為old列表纸厉,之前的列表稱為new列表系吭。InnoDB引入另一個參數(shù)innodb_old_blocks_time來管理列表五嫂,用于表示頁讀取到mid位置后需要等待多久才會被加入到LRU列表的熱端颗品。
LRU列表用來管理已經(jīng)讀取的頁,但當數(shù)據(jù)庫剛啟動時沃缘,LRU列表是空的躯枢,即沒有任何的頁。這時頁先存放在Free列表中槐臀。當需要從緩沖池中分頁時锄蹂,首先從Free列表中查找是否有可用的空閑頁若有則將該頁從Free列表中刪除,放入到LRU列表中水慨。否則得糜,根據(jù)LRU算法,淘汰LRU列表末尾的頁晰洒,將該內(nèi)存空間分配給新的頁朝抖。當頁從LRU列表的old部分加入到new部分時,稱此時的操作為page made young谍珊,而因為innodb_old_blocks_time的設置而導致頁沒有從old部分移動到new部分的操作稱為page not made young治宣。
可以通過命令SHOW ENGINE INNODB STATUS來查看LRU列表及FREE列表的使用情況和運行狀態(tài)。
其中Buffer pool size表示緩沖池中頁的數(shù)量。
Free Buffers表示當前Free列表中頁的數(shù)量侮邀。
Database pages表示LRU列表中頁的數(shù)量坏怪。
pages made young顯示了LRU列表中頁移動到前端的次數(shù)。
Buffer pool hit rate表示緩沖池命中率绊茧,通常該值不應該小于95%铝宵,否則需要觀察是否由于全表掃描引起的LRU列表被污染。
Modified db pages 表示臟頁的數(shù)量华畏。
在LRU列表中的頁被修改后捉超,稱該頁為臟頁,即緩沖池中的頁和磁盤上的頁的數(shù)據(jù)產(chǎn)生了不一致唯绍。這時數(shù)據(jù)庫會通過CHECKPOINT機制將臟頁刷新回磁盤拼岳,而Flush列表中的頁即為臟頁列表。需要注意的是况芒,臟頁既存在于LRU列表中惜纸,也存在于Flush列表中,二者互不影響绝骚。
3.重做日志緩沖
InnoDB存儲引擎首先將重做日志信息先放入到這個緩沖區(qū)耐版,然后按一定的頻率將其刷新到重做日志文件。重做日志緩沖一般不需要設置得很大压汪,因為一般每秒就會刷新一次粪牲。
重做日志在下列三種情況下會將緩沖內(nèi)容刷新到外部磁盤的重做日志文件中:
Master Thread每一秒將重做日志緩沖刷新到重做日志文件。
每個事務提交時會將重做日志緩沖刷新到重做日志文件止剖。
當重做日志緩沖池剩余空間小于1/2時腺阳,重做日志緩沖刷新到重做日志文件。
4.額外的內(nèi)存池
在InnoDB存儲引擎中穿香,對內(nèi)存的管理是通過一種稱為內(nèi)存堆的方式進行的亭引。在對一些數(shù)據(jù)結(jié)構本身的內(nèi)存進行分配時,需要從額外的內(nèi)存池中進行申請皮获,當該區(qū)域的內(nèi)存不夠時會從緩沖池中進行申請焙蚓。例如,分配了緩沖池洒宝,但是每個緩沖池中的幀緩沖還有對應的緩沖控制對象购公,這些對象記錄了一些諸如LRU、鎖雁歌、等待等信息宏浩,而這個對象的內(nèi)存需要從額外內(nèi)存池中申請。因此申請了很大的InnoDB緩沖池時将宪,也需要考慮相應增加這個值绘闷。
Checkpoint技術
為了避免每次頁發(fā)送變化就將其刷新至磁盤橡庞,采用了Checkpoint技術統(tǒng)一對刷新進行管理。但是為了防止刷新時發(fā)生宕機引起數(shù)據(jù)丟失的情況印蔗,數(shù)據(jù)庫系統(tǒng)普遍采用了Write Ahead Log策略扒最,即當前事務提交時,先寫重做日志华嘹,再修改頁吧趣。這樣宕機導致數(shù)據(jù)丟失時,可以通過重做日志來完成數(shù)據(jù)的恢復耙厚。
Checkpoint技術是為了解決以下幾個問題:
縮短數(shù)據(jù)庫的恢復時間
緩沖池不夠用時强挫,將臟頁刷新到磁盤
重做日志不可用時,刷新臟頁
當數(shù)據(jù)庫發(fā)生宕機時薛躬,數(shù)據(jù)庫不需要重做所有的日志俯渤,因為Checkpoint之前的頁都已經(jīng)刷新回磁盤诈悍。只需要對Checkpoint后的重做日志進行恢復简珠。這樣就大大縮短了恢復時間。
當緩沖池不夠用時咬腕,根據(jù)LRU算法會溢出最近最少使用的頁趴酣,若此頁為臟頁梨树,那么需要強制執(zhí)行Checkpoint,將臟頁刷回磁盤岖寞。
重做日志出現(xiàn)不可用的情況是因為當前事務數(shù)據(jù)庫系統(tǒng)對重做日志的設計都是循環(huán)使用的抡四,并不是無限增大的。因此為了保證重做日志正常的循環(huán)使用仗谆,需要將臟頁及時刷新到磁盤指巡。
InnoDB存儲引擎內(nèi)部,有兩種CheckPoint胸私,分別為:Sharp Checkpoint厌处、Fuzzy Checkpoint。
Sharp Checkpoint發(fā)生在數(shù)據(jù)庫關閉時將所有的臟頁都刷新回磁盤岁疼,這時默認的工作方式。
但是數(shù)據(jù)庫運行并不會將所有的臟頁同時刷新回磁盤缆娃,在InnoDB內(nèi)部使用Fuzzy Checkpoint進行頁的刷新捷绒,即只刷新一部分臟頁,而不是刷新所有的臟頁回磁盤贯要。
以下幾種情況會發(fā)生Fuzzy Checkpoint:
Master Thread Checkpoint
每秒或者每十秒從緩沖池的臟頁列表中刷新一定比例的頁回磁盤暖侨。這個過程是異步的,即此時InnoDB存儲引擎可以進行其他的操作崇渗,用戶查詢線程不會阻塞字逗。
FLUSH_LRU_LIST Checkpoint
倘若LRU列表中沒有100個空閑頁可用京郑,引擎會將LRU列表尾端的頁移除。如果這些頁中有臟頁葫掉,那么需要進行Checkpoint些举。MySQL5.6版本后,這個檢查放在了一個單獨的Page Cleaner線程中俭厚,并且用戶可以通過參數(shù)innodb_lru_scan_depth控制列表中可用頁的數(shù)量户魏。
Async/Synv Flush Checkpoing
重做日志文件不可用的情況下,這時需要強制將一些頁刷新回磁盤挪挤。MySQL5.6版本后叼丑,這部分刷新操作同樣放入到單獨的Page Cleaner Thread,故不會阻塞用戶查詢進程扛门。
Dirty Page too much Checkpoint
臟頁的數(shù)量太多鸠信,導致InnoDB存儲引擎強制進行Checkpoint,可以通過參數(shù)innodb_max_dirty_pages_pct控制论寨。
Master Thread工作方式
InnoDB 1.0版本之前症副,Master Thread具有最高的線程優(yōu)先級別。其內(nèi)部由多個循環(huán)組成:主循環(huán)政基、后臺循環(huán)贞铣、刷新循環(huán)、暫停循環(huán)沮明。Master Thread會根據(jù)數(shù)據(jù)庫運行的狀態(tài)在其中進行切換辕坝。
主循環(huán)大概每秒一次或每10秒一次,每次的操作包括:
日志緩沖刷新到磁盤荐健,即使這個事務還沒有提交(總是)酱畅;
合并插入緩沖(可能);
至多刷新100個InnoDB的緩沖池中的臟頁到磁盤(可能)江场;
如果當前沒有用戶活動纺酸,切換到backgroud loop(可能)。
當前沒有用戶活動或者數(shù)據(jù)庫關閉時址否,會切換到后臺循環(huán)餐蔬。后臺循環(huán)會執(zhí)行以下操作:
刪除無用的Undo頁(總是);
合并20個插入緩沖(總是)佑附;
跳回到主循環(huán)(總是)樊诺;
不斷刷新100個頁直到符合條件(可能)。
若刷新循環(huán)中也沒什么事情可以做了音同,InnoDB存儲引擎會切換到暫停循環(huán)词爬,將Master Thread掛起。
InnoDB 1.2.x之前版本的Master Thread
在了解了1.0.x版本之前的Master Thread的具體實現(xiàn)過程后权均,細心的讀者會發(fā)現(xiàn)InnoDB存儲引擎對于IO其實是有限制的顿膨,在緩沖池向磁盤刷新時其實都做了一定的硬編碼(hard coding)锅锨。在磁盤技術飛速發(fā)展的今天,當固態(tài)磁盤(SSD)出現(xiàn)時恋沃,這種規(guī)定在很大程度上限制了InnoDB存儲引擎對磁盤IO的性能必搞,尤其是寫入性能。
從前面的偽代碼來看芽唇,無論何時顾画,InnoDB存儲引擎最大只會刷新100個臟頁到磁盤,合并20個插入緩沖匆笤。如果是在寫入密集的應用程序中研侣,每秒可能會產(chǎn)生大于100個的臟頁,如果是產(chǎn)生大于20個插入緩沖的情況炮捧,Master Thread似乎會“忙不過來”庶诡,或者說它總是做得很慢。即使磁盤能在1秒內(nèi)處理多于100個頁的寫入和20個插入緩沖的合并咆课,但是由于hard coding末誓,Master Thread也只會選擇刷新100個臟頁和合并20個插入緩沖。同時书蚪,當發(fā)生宕機需要恢復時喇澡,由于很多數(shù)據(jù)還沒有刷新回磁盤,會導致恢復的時間可能需要很久殊校,尤其是對于insert buffer來說晴玖。
InnoDB Plugin(從InnoDB1.0.x版本開始)提供了參數(shù)innodb_io_capacity,用來表示磁盤IO的吞吐量为流,默認值為200呕屎。對于刷新到磁盤頁的數(shù)量,會按照innodb_io_capacity的百分比來進行控制敬察。規(guī)則如下:
在合并插入緩沖時秀睛,合并插入緩沖的數(shù)量為innodb_io_capacity值的5%;
在從緩沖區(qū)刷新臟頁時莲祸,刷新臟頁的數(shù)量為innodb_io_capacity蹂安。
若用戶使用了SSD類的磁盤,或者將幾塊磁盤做了RAID虫给,當存儲設備擁有更高的IO速度時藤抡,完全可以將innodb_io_capacity的值調(diào)得再高點,直到符合磁盤IO的吞吐量為止抹估。
另一個問題是,參數(shù)innodb_max_dirty_pages_pct默認值的問題弄兜,在InnoDB 1.0.x版本之前药蜻,該值的默認為90瓷式,意味著臟頁占緩沖池的90%。但是該值“太大”了语泽,因為InnoDB存儲引擎在每秒刷新緩沖池和flush loop時會判斷這個值贸典,如果該值大于innodb_max_dirty_pages_pct,才刷新100個臟頁踱卵,如果有很大的內(nèi)存廊驼,或者服務器的壓力很大,這時刷新臟頁的速度反而會降低惋砂。同樣妒挎,在恢復階段可能需要更多的時間。
InnoDB 1.0.x版本帶來的另一個參數(shù)是innodb_adaptive_flushing(自適應地刷新)西饵,該值影響每秒刷新臟頁的數(shù)量酝掩。原來的刷新規(guī)則是:臟頁在緩沖池所占的比例小于innodb_max_dirty_pages_pct時,不刷新臟頁眷柔;大于innodb_max_dirty_pages_pct時期虾,刷新100個臟頁。隨著innodb_adaptive_flushing參數(shù)的引入驯嘱,InnoDB存儲引擎會通過一個名為buf_flush_get_desired_flush_rate的函數(shù)來判斷需要刷新臟頁最合適的數(shù)量镶苞。粗略地翻閱源代碼后發(fā)現(xiàn)buf_flush_get_desired_flush_rate通過判斷產(chǎn)生重做日志(redo log)的速度來決定最合適的刷新臟頁數(shù)量。因此鞠评,當臟頁的比例小于innodb_max_dirty_pages_pct時茂蚓,也會刷新一定量的臟頁。
InnoDB 1.2.x版本的Master Thread
在InnoDB 1.2.x版本中再次對Master Thread進行了優(yōu)化谢澈,由此也可以看出Master Thread對性能所起到的關鍵作用煌贴。在InnoDB 1.2.x版本中,Master Thread的偽代碼如下:
if InnoDB is idle
srv_master_do_idle_tasks();
else
srv_master_do_active_tasks();
其中srv_master_do_idle_tasks()就是之前版本中每10秒的操作锥忿,srv_master_do_active_tasks()處理的是之前每秒中的操作牛郑。同時對于刷新臟頁的操作,從Master Thread線程分離到一個單獨的Page Cleaner Thread敬鬓,從而減輕了Master Thread的工作淹朋,同時進一步提高了系統(tǒng)的并發(fā)性。
InnoDB關鍵特性
InnoDB存儲引擎的關鍵特性包括:
插入緩沖(Insert Buffer)
兩次寫(Double Write)
自適應哈希索引(Adaptive Hash Index)
異步IO(Async IO)
刷新鄰接頁(Flush Neighbor Page)
上述這些特性為InnoDB存儲引擎帶來更好的性能以及更高的可靠性钉答。
插入緩沖
1.Insert Buffer
這個名字可能會讓人認為插入緩沖是緩沖池中的一個組成部分础芍。其實不然,InnoDB緩沖池中有Insert Buffer信息固然不錯数尿,但是Insert Buffer和數(shù)據(jù)頁一樣仑性,也是物理頁的一個組成部分。
在InnoDB存儲引擎中右蹦,主鍵是行唯一的標識符诊杆。通常應用程序中行記錄的插入順序是按照主鍵遞增的順序進行插入的歼捐。因此,插入聚集索引(Primary Key)一般是順序的晨汹,不需要磁盤的隨機讀取豹储。比如按下列SQL定義表:
CREATE TABLE t (
a INT AUTO_INCREMENT,
b VARCHAR(30),
PRIMARY KEY(a));
其中a列是自增長的,若對a列插入NULL值淘这,則由于其具有AUTO_INCREMENT屬性剥扣,其值會自動增長。同時頁中的行記錄按a的值進行順序存放铝穷。在一般情況下钠怯,不需要隨機讀取另一個頁中的記錄。因此氧骤,對于這類情況下的插入操作呻疹,速度是非常快的筹陵。
注意
并不是所有的主鍵插入都是順序的刽锤。若主鍵類是UUID這樣的類,那么插入和輔助索引一樣朦佩,同樣是隨機的并思。即使主鍵是自增類型,但是插入的是指定的值语稠,而不是NULL值宋彼,那么同樣可能導致插入并非連續(xù)的情況。
但是不可能每張表上只有一個聚集索引仙畦,更多情況下输涕,一張表上有多個非聚集的輔助索引(secondary index)。比如慨畸,用戶需要按照b這個字段進行查找莱坎,并且b這個字段不是唯一的。
在這樣的情況下產(chǎn)生了一個非聚集的且不是唯一的索引寸士。在進行插入操作時檐什,數(shù)據(jù)頁的存放還是按主鍵a進行順序存放的,但是對于非聚集索引葉子節(jié)點的插入不再是順序的了弱卡,這時就需要離散地訪問非聚集索引頁乃正,由于隨機讀取的存在而導致了插入操作性能下降。當然這并不是這個b字段上索引的錯誤婶博,而是因為B+樹的特性決定了非聚集索引插入的離散性瓮具。
需要注意的是,在某些情況下,輔助索引的插入依然是順序的搭综,或者說是比較順序的垢箕,比如用戶購買表中的時間字段划栓。在通常情況下兑巾,用戶購買時間是一個輔助索引,用來根據(jù)時間條件進行查詢忠荞。但是在插入時卻是根據(jù)時間的遞增而插入的蒋歌,因此插入也是“較為”順序的。
InnoDB存儲引擎開創(chuàng)性地設計了Insert Buffer委煤,對于非聚集索引的插入或更新操作堂油,不是每一次直接插入到索引頁中,而是先判斷插入的非聚集索引頁是否在緩沖池中碧绞,若在府框,則直接插入;若不在讥邻,則先放入到一個Insert Buffer對象中迫靖。看似非聚集的索引已經(jīng)插到葉子節(jié)點兴使,而實際并沒有系宜,只是存放在另一個位置。然后再以一定的頻率和情況進行Insert Buffer和輔助索引頁子節(jié)點的merge(合并)操作发魄,這時通常能將多個插入合并到一個操作中(因為在一個索引頁中)盹牧,這就大大提高了對于非聚集索引插入的性能。
然而Insert Buffer的使用需要同時滿足以下兩個條件:
索引是輔助索引(secondary index)励幼;
索引不是唯一(unique)的汰寓。
當滿足以上兩個條件時,InnoDB存儲引擎會使用Insert Buffer苹粟,這樣就能提高插入操作的性能了有滑。不過考慮這樣一種情況:應用程序進行大量的插入操作,這些都涉及了不唯一的非聚集索引六水,也就是使用了Insert Buffer俺孙。若此時MySQL發(fā)生了宕機,這時勢必有大量的Insert Buffer并沒有合并到實際的非聚集索引中去掷贾。因此這時恢復可能需要很長的時間睛榄,在極端情況下甚至需要幾個小時。
輔助索引不能是唯一的想帅,因為在插入緩沖時场靴,數(shù)據(jù)庫并不去查找索引頁來判斷插入的記錄的唯一性。如果去查找肯定又會有離散讀取的情況發(fā)生,從而導致Insert Buffer失去了意義旨剥。
用戶可以通過命令SHOW ENGINE INNODB STATUS來查看插入緩沖的信息咧欣。
正如前面所說的,目前Insert Buffer存在一個問題是:在寫密集的情況下轨帜,插入緩沖會占用過多的緩沖池內(nèi)存(innodb_buffer_pool)魄咕,默認最大可以占用到1/2的緩沖池內(nèi)存。
這對于其他的操作可能會帶來一定的影響蚌父,修改IBUF_POOL_SIZE_PER_MAX_SIZE就可以對插入緩沖的大小進行控制哮兰。比如將IBUF_POOL_SIZE_PER_MAX_SIZE改為3,則最大只能使用1/3的緩沖池內(nèi)存苟弛。
2.Change Buffer
InnoDB從1.0.x版本開始引入了Change Buffer喝滞,可將其視為Insert Buffer的升級。從這個版本開始膏秫,InnoDB存儲引擎可以對DML操作——INSERT右遭、DELETE、UPDATE都進行緩沖缤削,他們分別是:Insert Buffer窘哈、Delete Buffer、Purge buffer僻他。
當然和之前Insert Buffer一樣宵距,Change Buffer適用的對象依然是非唯一的輔助索引。
對一條記錄進行UPDATE操作可能分為兩個過程:
將記錄標記為已刪除吨拗;
真正將記錄刪除满哪。
因此Delete Buffer對應UPDATE操作的第一個過程,即將記錄標記為刪除劝篷。Purge Buffer對應UPDATE操作的第二個過程哨鸭,即將記錄真正的刪除。同時娇妓,InnoDB存儲引擎提供了參數(shù)innodb_change_buffering像鸡,用來開啟各種Buffer的選項。該參數(shù)可選的值為:inserts哈恰、deletes只估、purges、changes着绷、all蛔钙、none。inserts荠医、deletes吁脱、purges就是前面討論過的三種情況桑涎。changes表示啟用inserts和deletes,all表示啟用所有兼贡,none表示都不啟用攻冷。該參數(shù)默認值為all。
從InnoDB 1.2.x版本開始遍希,可以通過參數(shù)innodb_change_buffer_max_size來控制Change Buffer最大使用內(nèi)存的數(shù)量等曼。
兩次寫
如果說Insert Buffer帶給InnoDB存儲引擎的是性能上的提升,那么doublewrite(兩次寫)帶給InnoDB存儲引擎的是數(shù)據(jù)頁的可靠性孵班。
當發(fā)生數(shù)據(jù)庫宕機時涉兽,可能InnoDB存儲引擎正在寫入某個頁到表中,而這個頁只寫了一部分篙程,比如16KB的頁,只寫了前4KB别厘,之后就發(fā)生了宕機虱饿,這種情況被稱為部分寫失效(partial page write)。在InnoDB存儲引擎未使用doublewrite技術前触趴,曾經(jīng)出現(xiàn)過因為部分寫失效而導致數(shù)據(jù)丟失的情況氮发。
有經(jīng)驗的DBA也許會想,如果發(fā)生寫失效冗懦,可以通過重做日志進行恢復爽冕。這是一個辦法。但是必須清楚地認識到披蕉,重做日志中記錄的是對頁的物理操作颈畸,如偏移量800,寫'aaaa'記錄没讲。如果這個頁本身已經(jīng)發(fā)生了損壞眯娱,再對其進行重做是沒有意義的。這就是說爬凑,在應用(apply)重做日志前徙缴,用戶需要一個頁的副本,當寫入失效發(fā)生時嘁信,先通過頁的副本來還原該頁于样,再進行重做,這就是doublewrite潘靖。在InnoDB存儲引擎中doublewrite的體系架構如下圖所示穿剖。
doublewrite由兩部分組成,一部分是內(nèi)存中的doublewrite buffer秘豹,大小為2MB携御,另一部分是物理磁盤上共享表空間中連續(xù)的128個頁,即2個區(qū)(extent),大小同樣為2MB啄刹。在對緩沖池的臟頁進行刷新時涮坐,并不直接寫磁盤,而是會通過memcpy函數(shù)將臟頁先復制到內(nèi)存中的doublewrite buffer誓军,之后通過doublewrite buffer再分兩次袱讹,每次1MB順序地寫入共享表空間的物理磁盤上,然后馬上調(diào)用fsync函數(shù)昵时,同步磁盤捷雕,避免緩沖寫帶來的問題。在這個過程中壹甥,因為doublewrite頁是連續(xù)的救巷,因此這個過程是順序?qū)懙模_銷并不是很大句柠。在完成doublewrite頁的寫入后浦译,再將doublewrite buffer中的頁寫入各個表空間文件中,此時的寫入則是離散的溯职。
若查看MySQL官方手冊精盅,會發(fā)現(xiàn)在命令SHOW GLOBAL STATUS中Innodb_buffer_pool_pages_flushed變量表示當前從緩沖池中刷新到磁盤頁的數(shù)量。根據(jù)之前的介紹谜酒,用戶應該了解到叹俏,在默認情況下所有頁的刷新首先都需要放入到doublewrite中,因此該變量應該和Innodb_dblwr_pages_written一致僻族。然而在MySQL 5.5.24版本之前粘驰,Innodb_buffer_pool_pages_flushed總是為Innodb_dblwr_pages_written的2倍,而此Bug直到MySQL5.5.24才被修復鹰贵。因此用戶若需要統(tǒng)計數(shù)據(jù)庫在生產(chǎn)環(huán)境中寫入的量晴氨,最安全的方法還是根據(jù)Innodb_dblwr_pages_written來進行統(tǒng)計,這在所有版本的MySQL數(shù)據(jù)庫中都是正確的碉输。
參數(shù)skip_innodb_doublewrite可以禁止使用doublewrite功能籽前,這時可能會發(fā)生前面提及的寫失效問題。不過如果用戶有多個從服務器(slave server)敷钾,需要提供較快的性能(如在slaves erver上做的是RAID0)枝哄,也許啟用這個參數(shù)是一個辦法。不過對于需要提供數(shù)據(jù)高可靠性的主服務器(master server)阻荒,任何時候用戶都應確保開啟doublewrite功能挠锥。
自適應哈希索引
哈希(hash)是一種非常快的查找方法侨赡,在一般情況下這種查找的時間復雜度為O(1)蓖租,即一般僅需要一次查找就能定位數(shù)據(jù)粱侣。而B+樹的查找次數(shù),取決于B+樹的高度蓖宦,在生產(chǎn)環(huán)境中齐婴,B+樹的高度一般為3~4層,故需要3~4次的查詢稠茂。
InnoDB存儲引擎會監(jiān)控對表上各索引頁的查詢柠偶。如果觀察到建立哈希索引可以帶來速度提升,則建立哈希索引睬关,稱之為自適應哈希索引(Adaptive Hash Index诱担,AHI)。AHI是通過緩沖池的B+樹頁構造而來电爹,因此建立的速度很快蔫仙,而且不需要對整張表構建哈希索引。InnoDB存儲引擎會自動根據(jù)訪問的頻率和模式來自動地為某些熱點頁建立哈希索引藐不。
AHI有一個要求匀哄,即對這個頁的連續(xù)訪問模式必須是一樣的。例如對于(a雏蛮,b)這樣的聯(lián)合索引頁,其訪問模式可以是以下情況:
WHERE a=xxx
WHERE a=xxx and b=xxx
訪問模式一樣指的是查詢的條件一樣阱州,若交替進行上述兩種查詢挑秉,那么InonDB存儲引擎不會對該頁構造AHI。此外AHI還有如下的要求:
以該模式訪問了100次
頁通過該模式訪問了N次苔货,其中N=頁中記錄*1/16
根據(jù)InnoDB存儲引擎官方的文檔顯示歌溉,啟用AHI后野来,讀取和寫入速度可以提高2倍,輔助索引的連接操作性能可以提高5倍。毫無疑問春弥,AHI是非常好的優(yōu)化模式,其設計思想是數(shù)據(jù)庫自優(yōu)化的(self-tuning)泡垃,即無需DBA對數(shù)據(jù)庫進行人為調(diào)整纺念。
值得注意的是,哈希索引只能用來搜索等值的查詢敢会,如SELECT*FROM table WHERE index_col='xxx'曾沈。而對于其他查找類型,如范圍查找鸥昏,是不能使用哈希索引的塞俱。
由于AHI是由InnoDB存儲引擎控制的,因此這里的信息只供用戶參考吏垮。不過用戶可以通過觀察SHOW ENGINE INNODB STATUS的結(jié)果及參數(shù)innodb_adaptive_hash_index來考慮是禁用或啟動此特性障涯,默認AHI為開啟狀態(tài)罐旗。
異步IO
為了提高磁盤操作性能,當前的數(shù)據(jù)庫系統(tǒng)都采用異步IO(Asynchronous IO唯蝶,AIO)的方式來處理磁盤操作九秀。InnoDB存儲引擎亦是如此。
與AIO對應的是Sync IO生棍,即每進行一次IO操作颤霎,需要等待此次操作結(jié)束才能繼續(xù)接下來的操作。但是如果用戶發(fā)出的是一條索引掃描的查詢涂滴,那么這條SQL查詢語句可能需要掃描多個索引頁友酱,也就是需要進行多次的IO操作。在每掃描一個頁并等待其完成后再進行下一次的掃描柔纵,這是沒有必要的缔杉。用戶可以在發(fā)出一個IO請求后立即再發(fā)出另一個IO請求,當全部IO請求發(fā)送完畢后搁料,等待所有IO操作的完成或详,這就是AIO。
AIO的另一個優(yōu)勢是可以進行IO Merge操作郭计,也就是將多個IO合并為1個IO霸琴,這樣可以提高IOPS的性能。例如用戶需要訪問頁的(space昭伸,page_no)為:
(8梧乘,6)、(8庐杨,7)选调,(8,8)
每個頁的大小為16KB灵份,那么同步IO需要進行3次IO操作仁堪。而AIO會判斷到這三個頁是連續(xù)的,因此AIO底層會發(fā)送一個IO請求填渠,從(8弦聂,6)開始,讀取48KB的頁揭蜒。
在InnoDB1.1.x之前横浑,AIO的實現(xiàn)通過InnoDB存儲引擎中的代碼來模擬實現(xiàn)。而從InnoDB 1.1.x開始(InnoDB Plugin不支持)屉更,提供了內(nèi)核級別AIO的支持徙融,稱為Native AIO。因此在編譯或者運行該版本MySQL時瑰谜,需要libaio庫的支持欺冀。
需要注意的是树绩,Native AIO需要操作系統(tǒng)提供支持。Windows系統(tǒng)和Linux系統(tǒng)都提供Native AIO支持隐轩,而Mac OSX系統(tǒng)則未提供饺饭。因此在這些系統(tǒng)下,依舊只能使用原模擬的方式职车。
參數(shù)innodb_use_native_aio用來控制是否啟用Native AIO瘫俊,在Linux操作系統(tǒng)下,默認值為ON:
用戶可以通過開啟和關閉Native AIO功能來比較InnoDB性能的提升悴灵。官方的測試顯示扛芽,啟用Native AIO,恢復速度可以提高75%积瞒。
在InnoDB存儲引擎中川尖,read ahead方式的讀取都是通過AIO完成,臟頁的刷新茫孔,即磁盤的寫入操作則全部由AIO完成叮喳。
刷新鄰接頁
InnoDB存儲引擎還提供了Flush Neighbor Page(刷新鄰接頁)的特性。其工作原理為:當刷新一個臟頁時缰贝,InnoDB存儲引擎會檢測該頁所在區(qū)(extent)的所有頁馍悟,如果是臟頁,那么一起進行刷新剩晴。這樣做的好處顯而易見赋朦,通過AIO可以將多個IO寫入操作合并為一個IO操作,故該工作機制在傳統(tǒng)機械磁盤下有著顯著的優(yōu)勢李破。但是需要考慮到下面兩個問題:
是不是可能將不怎么臟的頁進行了寫入,而該頁之后又會很快變成臟頁壹将?
固態(tài)硬盤有著較高的IOPS嗤攻,是否還需要這個特性?
為此诽俯,InnoDB存儲引擎從1.2.x版本開始提供了參數(shù)innodb_flush_neighbors妇菱,用來控制是否啟用該特性。對于傳統(tǒng)機械硬盤建議啟用該特性暴区,而對于固態(tài)硬盤有著超高IOPS性能的磁盤闯团,則建議將該參數(shù)設置為0,即關閉此特性仙粱。
啟動房交、關閉與恢復
InnoDB是MySQL的存儲引擎之一,因此InnoDB存儲引擎的啟動和關閉伐割,更準確的是指在MySQL實例的啟動過程中對InnoDB存儲引擎的處理過程候味。
在關閉時刃唤,參數(shù)innodb_fast_shutdown影響著表的存儲引擎為InnoDB的行為。該參數(shù)可取值為0白群、1尚胞、2,默認值為1帜慢。
0表示在MySQL數(shù)據(jù)庫關閉時笼裳,InnoDB需要完成所有的full purge和merge insert buffer,并且將所有的臟頁刷新回磁盤粱玲。這需要一些時間躬柬,有時甚至需要幾個小時來完成。如果在進行InnoDB升級時密幔,必須將這個參數(shù)調(diào)為0楔脯,然后再關閉數(shù)據(jù)庫。
1是參數(shù)innodb_fast_shutdown的默認值胯甩,表示不需要完成上述的full purge和merge insert buffer操作昧廷,但是在緩沖池中的一些數(shù)據(jù)臟頁還是會刷新回磁盤。
2表示不完成full purge和merge insert buffer操作偎箫,也不將緩沖池中的數(shù)據(jù)臟頁寫回磁盤木柬,而是將日志都寫入日志文件。這樣不會有任何事務的丟失淹办,但是下次MySQL數(shù)據(jù)庫啟動時眉枕,會進行恢復操作(recovery)。
當正常關閉MySQL數(shù)據(jù)庫時怜森,下次的啟動應該會非乘偬簦“正常”副硅。但是如果沒有正常地關閉數(shù)據(jù)庫姥宝,如用kill命令關閉數(shù)據(jù)庫,在MySQL數(shù)據(jù)庫運行中重啟了服務器恐疲,或者在關閉數(shù)據(jù)庫時腊满,將參數(shù)innodb_fast_shutdown設為了2時,下次MySQL數(shù)據(jù)庫啟動時都會對InnoDB存儲引擎的表進行恢復操作培己。
參數(shù)innodb_force_recovery影響了整個InnoDB存儲引擎恢復的狀況碳蛋。該參數(shù)值默認為0,代表當發(fā)生需要恢復時省咨,進行所有的恢復操作肃弟,當不能進行有效恢復時,如數(shù)據(jù)頁發(fā)生了corruption茸炒,MySQL數(shù)據(jù)庫可能發(fā)生宕機(crash)愕乎,并把錯誤寫入錯誤日志中去阵苇。
但是,在某些情況下感论,可能并不需要進行完整的恢復操作绅项,因為用戶自己知道怎么進行恢復。比如在對一個表進行alter table操作時發(fā)生意外了比肄,數(shù)據(jù)庫重啟時會對InnoDB表進行回滾操作快耿,對于一個大表來說這需要很長時間,可能是幾個小時芳绩。這時用戶可以自行進行恢復掀亥,如可以把表刪除,從備份中重新導入數(shù)據(jù)到表妥色,可能這些操作的速度要遠遠快于回滾操作搪花。
參數(shù)innodb_force_recovery還可以設置為6個非零值:1~6。大的數(shù)字表示包含了前面所有小數(shù)字表示的影響嘹害。具體情況如下:
1(SRV_FORCE_IGNORE_CORRUPT):忽略檢查到的corrupt頁撮竿。
2(SRV_FORCE_NO_BACKGROUND):阻止Master Thread線程的運行,如Master Thread線程需要進行full purge操作笔呀,而這會導致crash幢踏。
3(SRV_FORCE_NO_TRX_UNDO):不進行事務的回滾操作。
4(SRV_FORCE_NO_IBUF_MERGE):不進行插入緩沖的合并操作许师。
5(SRV_FORCE_NO_UNDO_LOG_SCAN):不查看撤銷日志(Undo Log)房蝉,InnoDB存儲引擎會將未提交的事務視為已提交。
6(SRV_FORCE_NO_LOG_REDO):不進行前滾的操作微渠。
需要注意的是搭幻,在設置了參數(shù)innodb_force_recovery大于0后,用戶可以對表進行select逞盆、create和drop操作粗卜,但insert、update和delete這類DML操作是不允許的纳击。
InnoDB表
索引組織表
在InnoDB存儲引擎中,表都是根據(jù)主鍵順序組織存放的攻臀,這種存儲方式的表稱為索引組織表焕数。每個表都有主鍵,如果在創(chuàng)建表時沒有顯示定義主鍵刨啸,則會按照如下方式選擇或者創(chuàng)建主鍵:
a.判定是否有非空的唯一索引堡赔,如果有則該列即為主鍵。若果有多個设联,則選擇建表是第一個定義的非空位于索引為主鍵善已。注意:主鍵的選擇根據(jù)的是定義索引的順序灼捂,而不是建表時的列的順序。
?b.如果不存在唯一索引换团,InnoDB存儲引擎字段創(chuàng)建一個6字節(jié)大小的指針悉稠。
InnoDB邏輯存儲結(jié)構
從InnoDB存儲引擎的邏輯存儲結(jié)構看,所有數(shù)據(jù)都被邏輯地存放在一個空間中艘包,稱為表空間的猛。表空間又由段(segment)、區(qū)(extent)想虎、頁(page)組成卦尊。頁在一些文檔中有時也稱為塊(block),InnoDB存儲引擎的邏輯存儲結(jié)構大致如圖:
表空間
表空間可以看做時InnoDB存儲引擎邏輯結(jié)構的最高層舌厨,所有的數(shù)據(jù)都存放在表空間中岂却。默認情況下InnoDB只有一個共享表空間ibdata1,即所有的數(shù)據(jù)都存放在這個表空間中裙椭。如果用戶啟用了innodb_file_per_table躏哩,則每張表內(nèi)的數(shù)據(jù)可以單獨放到一個表空間內(nèi)。
需要注意的是骇陈,啟用了innodb_file_per_table參數(shù)震庭,每張表的表空間內(nèi)存放的只是數(shù)據(jù)、索引和插入緩沖Bitmap頁你雌,其他類的數(shù)據(jù)器联,如回滾信息,插入緩沖索引頁婿崭、系統(tǒng)事務信息拨拓,二次寫緩沖等還是存放在原來的共享表空間內(nèi)。
段
表空間是由各個段組成的氓栈,常見的段有數(shù)據(jù)段渣磷、索引段、回滾段等授瘦。因為InnoDB引擎表是索引組織的醋界,因此數(shù)據(jù)即索引,索引即數(shù)據(jù)提完。那么數(shù)據(jù)段即為B+樹的葉子結(jié)點形纺,索引段即為B+樹的非葉子結(jié)點⊥叫溃回滾段較為特殊逐样,后面進行介紹。
區(qū)
區(qū)是由連續(xù)頁組成的空間,在任何情況下每個區(qū)的大小都為1MB脂新。為了保證區(qū)中頁的連續(xù)性挪捕,InnoDB一次從磁盤申請4-5個區(qū)。在默認情況下争便,InnoDB存儲引擎頁的大小為16KB级零,即一個區(qū)中一共有64個連續(xù)的頁。
InnoDB 1.0版本開始引入壓縮頁始花,每個頁的大小可以設置為2K妄讯、4K、8K酷宵。
InnoDB 1.2版本新增參數(shù)innodb_page_size亥贸,可將默認頁的大小設置為4K、8K浇垦。
頁
頁是InnoDB磁盤管理的最小單位炕置,在InnoDB存儲引擎中,默認每個頁的大小為16KB男韧。
在InnoDB存儲引擎中朴摊,常見的頁類型有:
數(shù)據(jù)頁
undo頁
系統(tǒng)頁
事務數(shù)據(jù)頁
插入緩沖位圖頁
插入緩沖空閑列表頁
未壓縮的二進制大對象頁
壓縮的二進制大對象頁
行
InnoDB數(shù)據(jù)是按照行進行存放的。每個頁存放的行記錄也是有硬性定義的此虑,最多允許存放16KB/ 2 - 200行的記錄甚纲,即7992行記錄。
InnoDB行記錄格式
InnoDB存儲引擎記錄是以行的形式存儲的朦前。這意味著頁中保存著表中一行行的數(shù)據(jù)介杆。
Compact行記錄格式
compact行記錄是由MySQL5.0引入的,其設計目標是高效地存儲數(shù)據(jù)韭寸。簡單來說一個頁中存放的行數(shù)據(jù)越多春哨,其性能就越高。
compact行記錄格式的首部是一個非Null變長字段長度列表恩伺,并且其是按照列的順序逆序放置的赴背,其長度為:
若列的長度小于255,用一字節(jié)表示晶渠;
若大于255字節(jié)凰荚,用2字節(jié)表示。
所以VARCHAR類型的最大長度限制為65535褒脯。
之后的第二個部分是NULL標志位浇揩,該位指示了該行數(shù)據(jù)中是否有NULL值,有則用1表示憨颠,占用1字節(jié)。
接下來的部分是記錄頭信息,固定5字節(jié)爽彤,具體含義見圖:
最后的部分就是實際存儲每個列的數(shù)據(jù)养盗。需要注意的是,NULL不占該部分任何空間适篙,即NULL除了占有NULL標志位往核,實際存儲不占有任何空間。另外有一點需要注意的是嚷节,每行數(shù)據(jù)除了用戶定義的列外聂儒,還有兩個隱藏列,事務ID列和回滾指針列硫痰,分別為6字節(jié)和7字節(jié)的大小衩婚。若InnoDB表沒有定義主鍵,每行還會增加一個6字節(jié)的rowid列效斑。
行溢出數(shù)據(jù)
InnoDB存儲引擎可以將一條記錄中的某些數(shù)據(jù)存儲在真正的數(shù)據(jù)頁面之外非春。一般默認BLOB、LOB這類的大對象列類型就會存放在數(shù)據(jù)頁面之外缓屠。但他們也可以不將數(shù)據(jù)放在溢出頁面奇昙,即便是VARCHAR列數(shù)據(jù)類型,依然有可能被存放在行溢出數(shù)據(jù)敌完。
在一般情況下储耐,InnoDB存儲引擎的數(shù)據(jù)都是存放在頁類型為B-tree node中。但是當發(fā)生行溢出時滨溉,數(shù)據(jù)存放在頁類型為Uncompress BLOB頁中什湘。
InnoDB表是索引組織的,即B+Tree的結(jié)構业踏,這樣每個頁中至少有兩條行記錄禽炬。因此,如果頁中只能存放下一條記錄勤家,那么InnoDB存儲引擎會自動將行數(shù)據(jù)存放到溢出頁中腹尖。
InnoDB數(shù)據(jù)頁結(jié)構
InnoDB數(shù)據(jù)頁由以下七個部分組成,如圖所示:
File Header(文件頭)
Page Header(頁頭)
Infimun + Supremum Records
User Records(用戶記錄)
Free Space(空閑空間)
Page Directory(頁目錄)
File Trailer(文件結(jié)尾信息)
File Header伐脖、Page Header热幔、File Trailer的大小是固定的,用來標示該頁的一些信息讼庇。其余部分為實際的行存儲空間绎巨,因此大小是動態(tài)的。
Infimun + Supremum Records
Infimun Supremum Records用來限定記錄的邊界蠕啄,Infimun記錄是比該頁中任何主鍵值都小的值场勤,Supremum記錄是比該頁中任何主鍵值都大的值戈锻。
File Trailer
為了保證頁能夠完整地寫入磁盤(如寫入過程中遇到宕機、磁盤損壞等原因)和媳,InnoDB存儲引擎的頁中設置了File Trailer部分格遭。
約束
數(shù)據(jù)完整性
關系型數(shù)據(jù)庫和文件系統(tǒng)的一個不同點是,關系數(shù)據(jù)庫本身能保證存儲數(shù)據(jù)的完整性留瞳,不需要應用程序的控制拒迅,而文件系統(tǒng)一般需要在程序端進行控制。幾乎所有的關系型數(shù)據(jù)庫都提供約束機制她倘,約束提供了一條強大而簡易的途徑來保證數(shù)據(jù)庫中的數(shù)據(jù)完整性璧微,數(shù)據(jù)完整性有三種形式:
實體完整性:保證表中有一個主鍵。在InnoDB中硬梁,我們可以通過定義Primary Key或者Unique Key約束來保證實體的完整性前硫。
域完整性:保證數(shù)據(jù)的值滿足特定的條件。在InnoDB引擎中靶溜,域完整性通過以下幾種途徑來保證:選擇合適的數(shù)據(jù)類型可以確保一個數(shù)據(jù)值滿足特定條件开瞭,外鍵約束,編寫觸發(fā)器罩息,還可以考慮DEFAULT約束作為強制域完整性的一個方面嗤详。
參照完整性:保證兩張表之間的關系。InnoDB引擎支持外鍵允許用戶定義外鍵以強制參照完整性瓷炮。
對于InnoDB存儲引擎葱色,提供了以下幾種約束:
Primary Key
Unique Key
Foreign Key
Default
NOT NULL
約束可以在表建立時就進行定義,也可以在之后使用ALTER TABLE命令來進行創(chuàng)建娘香。
約束和索引的概念有所不同苍狰,約束更是一個邏輯的概念,用來保證數(shù)據(jù)的完整性烘绽,而索引是一個數(shù)據(jù)結(jié)構淋昭,有邏輯上的概念,在數(shù)據(jù)庫中更是一個物理存儲的方式安接。
默認情況下翔忽,MySQL數(shù)據(jù)庫允許非法或者不正確的數(shù)據(jù)插入或更新,或者內(nèi)部將其轉(zhuǎn)化為一個合法的值盏檐,如NOT NULL字段插入一個NULL值歇式,會將其更改為0再進行插入,因此本身沒有對數(shù)據(jù)的正確性進行約束胡野。
MySQL不支持傳統(tǒng)的CHECK約束材失,但是通過ENUM和SET類型可以解決部分這樣的約束需求
觸發(fā)器與約束
前面小結(jié)介紹了,完整性約束通常也可以使用觸發(fā)器來實現(xiàn)硫豆。
觸發(fā)器的作用是在INSERT龙巨、DELETE笼呆、和UPDATE命令之前或之后自動調(diào)用SQL命令或者存儲過程。
創(chuàng)建觸發(fā)器的命令是CREATE TRIGGER旨别,只有具備Super權限的MySQL用戶才可以執(zhí)行這條命令抄邀。
最多可以為一個表建立6個觸發(fā)器,即分別為INSERT昼榛、UPDATE、DELETE的BEFORE和AFTER各定義一個剔难。
外鍵
外鍵用來保證參照完整性胆屿,MySQL的MyISAM引擎本身不支持外鍵,對于外鍵的定義只是起到一個注釋的作用偶宫。InnoDB引擎則支持外鍵約束非迹。
我們可以在建表時就添加外鍵,也可以在之后通過ALTER TABLE命令添加纯趋。
視圖
視圖是一個命名的虛表憎兽,它由一個查詢來定義,可以當做表使用吵冒。與持久表不同的是纯命,視圖中的數(shù)據(jù)沒有物理表現(xiàn)的形式。
視圖的主要用途之一是被用做一個抽象裝置痹栖,特別是對于一些應用程序亿汞,程序本身不需要關系基表的結(jié)構,只需要按照視圖定義來獲取數(shù)據(jù)或者更新數(shù)據(jù)揪阿。
雖然視圖是基于基表的一個虛擬表疗我,但是我們可以對某些視圖進行更新操作,其實就是通過視圖的定義來更新基本表南捂。
分區(qū)表
分區(qū)功能并不是在存儲引擎層完成的吴裤,因此不只有InnoDB存儲引擎支持分區(qū),常見的存儲引擎MyISAM溺健、NDB等都支持麦牺。但也并不是所有的存儲引擎都支持。
MySQL在5.1版本時添加了對于分區(qū)的支持這個過程是將一個表或者索引物分解為多個更小矿瘦、更可管理的部分枕面。就訪問數(shù)據(jù)庫的應用而言,從邏輯上講缚去,只有一個表或者一個索引潮秘,但是在物理上這個表或者索引可能由數(shù)十個物理分區(qū)組成。每個分區(qū)都是獨立的對象易结,可以獨自處理枕荞,也可以作為一個更大的對象的一部分進行處理柜候。
MySQL數(shù)據(jù)庫支持的分區(qū)類型為水平,并不支持垂直分區(qū)躏精。此外渣刷,MySQL數(shù)據(jù)庫的分區(qū)是局部分區(qū)索引,一個分區(qū)中既存放了數(shù)據(jù)有存放了索引矗烛。
分區(qū)對于某些SQL語句性能可能會帶來提高辅柴,但是分區(qū)主要用于高可用性,利于數(shù)據(jù)庫的管理瞭吃。
當前MySQL數(shù)據(jù)庫支持以下幾種類型的分區(qū):
RANGE分區(qū):行數(shù)據(jù)基于屬于一個給定連續(xù)區(qū)間的列值放入分區(qū)碌嘀。
LIST分區(qū):和RANGE分區(qū)類似,只是LIST分區(qū)面向的是離散的值歪架。
HASH分區(qū):根據(jù)用戶自定義的表達式的返回值來進行分區(qū)股冗,返回值不能為負數(shù)。
KEY分區(qū):根據(jù)MySQL數(shù)據(jù)庫提供的哈希函數(shù)來進行分區(qū)和蚪。
不論創(chuàng)建何種類型的分區(qū)止状,如果表中存在主鍵或者是唯一索引時,分區(qū)列必須是唯一索引的一個組成部分攒霹。
Columns分區(qū)
前面介紹的幾種分區(qū)中怯疤,分區(qū)的條件必須是整形。MySQL 5.5版本開始支持Columns分區(qū)剔蹋,可以視為RANGE和LIST分區(qū)的一種進化旅薄。
Columns分區(qū)支持以下的數(shù)據(jù)類型:
所有整形類型
日期類型,如DATE和DATETIME
字符串類型泣崩,如CHAR少梁、VARCHAR、BINARY和VARBINARY矫付。BLOB和TEXT類型不予支持凯沪。
子分區(qū)
子分區(qū)是在分區(qū)的基礎上再進行分區(qū),有時也稱這種分區(qū)為符合分區(qū)买优。MySQL允許RANGE和LIST的分區(qū)上再進行HASH或者是KEY的子分區(qū)妨马。
分區(qū)性能
數(shù)據(jù)庫的應用分為兩類:一類是OLTP(在線事務處理),如博客杀赢、電子商務烘跺、網(wǎng)絡游戲等;另一類是OLAP(在線分析處理)脂崔,如數(shù)據(jù)倉庫滤淳、數(shù)據(jù)集市。
對于OLAP的應用砌左,分區(qū)的確可以很好地提高查詢的性能脖咐,因為OLAP應用的大多數(shù)查詢需要頻繁地掃描一張很大的表铺敌。假設有一張1億行的表,其中有一個時間戳屬性列屁擅。你的查詢需要從這張表中獲取一年的數(shù)據(jù)偿凭。如果按時間戳進行分區(qū),則只需要掃描相應的分區(qū)即可派歌。
對于OLTP的應用弯囊,分區(qū)應該非常小心。在這種應用下胶果,不可能會獲取一張大表中10%的數(shù)據(jù)常挚,大部分都是通過索引返回一條記錄即可』铮可根據(jù)B+樹索引的原理可知,對于一張大表折欠,一般的B+樹需要2-3次磁盤IO贝或。因此B+樹可以很好的完成操作,不需要分區(qū)的幫助锐秦。
參考