-
B+樹
B+樹是InnoDB底層采用的數(shù)據(jù)結(jié)構(gòu),它是一種多叉平衡搜索樹。InnoDB底層采用的存儲結(jié)構(gòu)就是B+樹窟却。
它的優(yōu)點:-
高度比較低遇汞,減少IO的次數(shù)
因為B+樹采用是多叉樹未妹,所以在節(jié)點相同的情況下,比正常的二叉樹的高度低了很多空入。
對于數(shù)據(jù)庫來說络它,最耗時的操作就是從磁盤加載數(shù)據(jù),因此想要提高數(shù)據(jù)庫的效率歪赢,必須盡量減少從磁盤加載數(shù)據(jù)的次數(shù)化戳。而對于多叉樹,父子節(jié)點往往相距比較遠埋凯,因此在數(shù)據(jù)量比較大的時候点楼,一次加載到父子節(jié)點幾乎是不可能的,最好的情況就是一次加載一個節(jié)點的數(shù)據(jù)白对,然后找到下一個節(jié)點進行加載掠廓,直到最后找到對應(yīng)的信息。
從磁盤加載信息的時候甩恼,最耗時的操作是尋道蟀瞧,而從磁盤讀取數(shù)據(jù)到數(shù)據(jù)總線的耗時幾乎可以忽略,所以磁盤讀取數(shù)據(jù)的時候會有一個預(yù)讀的過程条摸。也就是說悦污,盡管你只要磁盤某個地方的一小段信息,加載的時候也會讀取滿一整個頁的信息钉蒲。所以切端,一個節(jié)點的大小設(shè)置成一個頁大小是最合適的,一次io可以加載進一個節(jié)點的所有信息顷啼,然后再決定下一個節(jié)點帆赢。
葉子節(jié)點有兄弟指針,方便遍歷
有些情況下线梗,會出現(xiàn)需要全表掃描椰于,因為B+樹的所有信息都存在葉子節(jié)點,所以只要找到第一個葉子節(jié)點仪搔,然后依次根據(jù)兄弟節(jié)點指針找到兄弟節(jié)點瘾婿,最后就完成了整個數(shù)據(jù)庫的遍歷。
而B樹等需要前序遍歷這種操作,在內(nèi)存中倒是沒有差別偏陪,但是如果是在磁盤中抢呆,因為父子節(jié)點往往不在一個頁中,遍歷就涉及到非常多的磁盤IO笛谦,效率很低抱虐。
-
索引
InnoDB使用的是聚簇索引表,即一棵以主鍵為索引饥脑、行數(shù)據(jù)存儲在子節(jié)點上的B+樹恳邀。如果沒有主鍵,系統(tǒng)先找一個not null 且unique的列作為主鍵灶轰,如果沒有谣沸,就會默認創(chuàng)建一個隱藏的主鍵作為索引。
其他的索引都是一個獨立的結(jié)構(gòu)笋颤,利用這個結(jié)構(gòu)找到對應(yīng)的主鍵乳附,即通過其他索引找到數(shù)據(jù)需要先到對應(yīng)的索引樹找到主鍵伴澄,然后再到主鍵的索引樹上找到數(shù)據(jù)赋除。
如果where條件中的鍵值沒有索引非凌,會掃描全表。-
鎖
mysql中的鎖有很多種:- 讀寫鎖
讀寫鎖是最常見的兩種鎖清焕。讀鎖也叫共享鎖,允許其他人再獲得讀鎖祭犯,可以同時讀秸妥,但是不能更改數(shù)據(jù)沃粗。寫鎖也叫排他鎖,不允許其他人對數(shù)據(jù)進行讀或者寫最盅。 - Record Lock
Record鎖是一種索引鎖突雪,它加在索引上防止被多次引用 - Gap Lock
Gap鎖的出現(xiàn)主要是用來解決幻讀,鎖住一個區(qū)間涡贱,沒有拿到這個區(qū)間的Gap Lock咏删,就不能向這個區(qū)間插入问词。 - NextKey Lock
NextKey鎖其實就是Record Lock和Gap Lock的組合體督函,同時鎖住一個范圍和其中的記錄 - 意向鎖
意向所用來協(xié)調(diào)表鎖和行鎖。如果一個表有行排他鎖存在锋叨,那么想對表添加排他鎖,就一定要等這個行鎖釋放娃磺。但是想判斷表中是否存在行鎖只能遍歷叫倍,消耗太大偷卧。為了解決這個問題段标,產(chǎn)生了意向鎖。
當(dāng)獲取一個行鎖時逼庞,會先拿到一個表的意向鎖,這樣只要判斷一個表是否有意向鎖赛糟,就可以判斷是否可以添加表鎖了。
- 讀寫鎖
-
事務(wù)隔離級別
- read-uncommited
在這個級別下掌逛,讀寫不會加任何鎖,所以即使事務(wù)沒有提交豆混,互相之間也能讀到彼此的更改动知。因此可能讀到對方更改一半的臟數(shù)據(jù)(臟讀)。 - read-commited(RC)
在這個級別下盒粮,寫的時候會加寫鎖,即在一個事務(wù)在更改一個數(shù)據(jù)的時候丹皱,其他事務(wù)無法獲取這個數(shù)據(jù),直到這個事務(wù)釋放寫鎖之后摊崭。但是如果在那個事務(wù)更改之前讀了一次,更改之后又讀了一次汁讼,這兩次的結(jié)果會不一致(不可重復(fù)讀)。因此InnoDB采用了快照讀解決這個問題嘿架,即在每行數(shù)據(jù)添加一個隱藏字段,最新更改的事務(wù)id耸彪,如果當(dāng)前的事務(wù)id小于這個值, 就回去undo log中找到本事務(wù)對應(yīng)的值蝉娜,從而保證事務(wù)的一致性。
但是如果其他事務(wù)插入了一條數(shù)據(jù)南缓,那么插入前這個事務(wù)是看不到那條數(shù)據(jù)的,但是插入之后汉形,這個事務(wù)就可以select到那個數(shù)據(jù)倍阐。也就是說概疆,同樣的select語句峰搪,前后兩次數(shù)據(jù)的會多或者少(幻讀) - repeatable-read(RR)
在這個級別下,為了解決幻讀使套,添加gap lock。即在select到的數(shù)據(jù)中間添加gap鎖侦高,其他想插入的事務(wù)必須要拿到這個gap鎖才能插入春锋,否則必須等待這個事務(wù)釋放gap鎖矫膨。
4.SERIALIZABLE
在RR的基礎(chǔ)上期奔,轉(zhuǎn)換所有 SELECT 語句為SELECT ... LOCK IN SHARE MODE危尿。即所有的讀和寫都要加鎖。
- read-uncommited
-
死鎖
造成死鎖的原因都是因為加鎖的順序不同谊娇,具體原因可以分為業(yè)務(wù)和索引。-
業(yè)務(wù)導(dǎo)致的死鎖
這種情況比較常見赠堵,也比較容易解決小渊,在代碼中茫叭,兩個線程在加鎖的時候,加鎖順序不同揍愁,導(dǎo)致他們互相需要對方的鎖。
-
索引導(dǎo)致的死鎖
這種情況就比較特殊谬擦,我們在業(yè)務(wù)層幾乎感覺不到這種死鎖朽缎,因為這個涉及到mysql對索引的操作。兩個語句同時需要加鎖饵沧,而且條件是在兩個不同的索引。這樣加鎖順序就取決于主鍵在兩個索引的順序了狼牺。如圖中所示,兩個索引的順序相反是钥,加鎖的順序也就相反,容易死鎖虏冻。
我們需要注意的就是,如果操作一個有多個索引的數(shù)據(jù)表厨相,盡量不要在多個索引上同時操作鸥鹉。
-
-
分析方法
- show engine innodb status
總結(jié)
- InnoDB是通過索引實現(xiàn)的行鎖
所以如果select中條件是一個沒有索引的列,那么會導(dǎo)致表鎖毁渗,如果事務(wù)隔離級別是RR,那么事務(wù)結(jié)束之前都不會釋放灸异。 - 使用Gap鎖
如果select一個不存在的數(shù)據(jù)羔飞,那么也會在對應(yīng)的區(qū)間添加gap鎖檐春。
等于號的使用會影響范圍,不好的話會鎖住gap(next key) - 不使用索引
- 如果MySQL估計使用索引比全表掃描更慢疟暖,則不使用索引。例如誓篱,如果列key均勻分布在1和100之間,下面的查詢使用索引就不是很好:select * from table_name where key>1 and key<90;
- 如果使用MEMORY/HEAP表锦募,并且where條件中不使用“=”進行索引列邻遏,那么不會用到索引糠亩,head表只有在“=”的條件下才會使用索引
- 用or分隔開的條件准验,如果or前的條件中的列有索引,而后面的列沒有索引糊饱,那么涉及到的索引都不會被用到,例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上沒有索引滞项,則該查詢也不會走索引
- 復(fù)合索引夭坪,如果索引列不是復(fù)合索引的第一部分文判,則不使用索引(即不符合最左前綴)室梅,例如,復(fù)合索引為(key1,key2),則查詢select * from table_name where key2='b';將不會使用索引
- 如果like是以‘%’開始的亡鼠,則該列上的索引不會被使用。例如select * from table_name where key1 like '%a'嗓奢;該查詢即使key1上存在索引浑厚,也不會被使用
- 如果列為字符串股耽,則where條件中必須將字符常量值加引號钳幅,否則即使該列上存在索引扮授,也不會被使用。例如,select * from table_name where key1=1;如果key1列保存的是字符串钠导,即使key1上有索引,也不會被使用牡属。
- 重復(fù)加鎖
對不存在的記錄加排他鎖,都會加鎖成功逮栅。即gap鎖的x鎖不互斥。