1、爲什麼要使用索引?
答:如果進行全表掃描,將整個數據表的數據全部或者分批次加載到內存當中,存儲的最小單位是塊或者頁,它們是由多行數據來組成的,將這些塊都加載進來,逐個塊去輪詢,找到我們要的目標並返回,這種方式非常的慢,但是如果數據量小的話,這種方式也非常快的。如果數據量過大,就要避免全表掃描的情況發生,此時就要引入索引了,索引可以快速查詢數據,避免全表掃描查找數據,提升檢索效率。
2、什麼樣的信息能稱爲索引?
答:可以將記錄限制到一定範圍內的字段,主鍵是一個很好的數據切入點,包含主鍵、唯一鍵以及普通鍵等等。主鍵、唯一鍵等,只要是能讓數據具備一定區分性的字段都可以成爲索引。
3、索引的數據結構?
答:查詢高效的數據結構,生成索引,建立二叉查找樹進行二分查找樹,還有紅黑樹、平衡二叉樹。生成索引,建立B-Tree結構進行查找。生成索引,建立B+Tree結構進行查找。生成索引,建立Hash結構進行查找。索引的數據結構主流是B+樹,還有哈希結構,BitMap結構,其中Mysql數據庫不支持BitMap索引,其中基於Innodb、MyISAM引擎的Mysql不顯示支持Hash。
3.1、二分查找樹的使用。二叉查找樹,每個節點最多有兩個孩子,左子樹和右子樹,二叉查找樹的重要性質是對於每個節點x,左子樹的任意節點的值小於x,右子樹的任意節點的值大於x。如果使用二叉查找樹存儲索引,確實可以提升查詢效率,需要注意的是索引的存儲塊和數據庫最小存儲單位,塊或者頁,實際上並非一一對應的,只是爲了發佈理解,先將其一一對應起來,每個存儲塊存儲的是關鍵字和指向子樹的指針。此例中的樹不僅是二叉樹,還是平衡二叉樹,即任意一個節點左右子樹高度差均不超過一。二叉查找樹的查找使用的二分查找,因爲是對半搜索,所以時間複雜度是O(logn),其查詢效率是很高的,但是數據庫的數據面臨的是插入和刪除的。
如果此時將2和6刪除掉了,新增了11、13兩個元素,此時就變成了線性的二叉樹,此時的時間複雜度就變成了O(n),大大降低了查詢的效率,影響程序運行的瓶頸是IO。
3.2、如何降低查詢時間的複雜度,又降低IO的次數呢?就是將樹變得矮一些,每一個節點存儲的數據多一些,這個時候就可以使用B-Tree樹了。
平衡多路查找樹,如果每個節點最多有m個孩子,這樣的樹就是m階B樹,此圖是三階B樹的樣子,當然現實中,索引每個節點的孩子上限肯定遠大於三的,每個存儲塊中主要包含了關鍵字和指向孩子的指針,最多有幾個孩子,取決於每個存儲塊的容量和以及數據庫的相關配置,所以通常情況下,m是很大的。
運用B樹,顯示節點元素和指針的圖示,如下所示:
B樹的特徵與定義。
1)、根節點至少包括兩個孩子。
2)、樹中每個節點最多含有m個孩子(m >= 2),這就是m階B樹的含義了,m取決於節點的容量以及數據庫的相關配置。例子中是3階B樹,所以孩子數目不能大於3,並且孩子數目不能少於ceil(3 / 2) = 1.5,取上限就是2。
3)、除根節點和葉子節點外,其它每個節點至少有ceil(m/2)個孩子,這裏面的ceil是取上限,不是四捨五入的。
4)、所有葉子節點都位於同一層,即葉子節點的高度都是一樣的。
5)、假設每個非終端節點中包含有n個關鍵字信息,其中。
a)、Ki(i=1......n)爲關鍵字,且關鍵字順序升序排序K(i-1) < Ki。假設Ki爲非終端節點的關鍵字,i等於1到n的任意一個正整數,則關鍵字需要按照順序升序排序,即K(i-1) < Ki。比如8小於12,是按照升序排列的。
b)、關鍵字的個數n必須滿足:[ceil(m / 2) -1] <= n <= m-1。即任意節點的關鍵字個數上限比它的孩子數上限少一個,且對於非葉子節點來說,任何一個節點的關鍵字個數比它的指向孩子的指針個數少一個,比如有兩個節點元素就有三個指針的。
c)、非葉子節點的指針:P[1]、P[2]...,P[M]。其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其中P[i]指向關鍵字屬於(K[i-1],K[i])的子樹。其中P[1]指向關鍵字小於K[1]的子樹這句話的意思是某節點最左邊的孩子節點裏面的關鍵字的值均小於該節點最左邊的關鍵字的值,比如3和5小於8。P[M]指向關鍵字大於K[M-1]的子樹這句話的意思是該節點最右邊的孩子節點裏面關鍵字的值均大於該節點裏面所有關鍵字的值,比如13和15大於8和12。其中P[i]指向關鍵字屬於(K[i-1],K[i])的子樹這句話的意思是該節點其餘的孩子節點裏面的關鍵字的值的大小均位於離該孩子節點指針最近的兩個關鍵字之間,是一個開區間的,比如P2這個指針指向的9和10均位於8和12這個開區間的範圍內。
6)、遵守這些約束,目的只有一個,讓每個索引塊儘可能存儲更多的信息,讓樹的高度儘可能減少IO的次數。B-Tree樹和二叉查找樹的效率是一樣高效的O(logn)。當數據發生變動的時候,必須會存在現有結構被打亂的情況,二叉查找樹有可能被打亂成線性的,但是B-Tree樹有五條規則約束,B-Tree有相應的策略,通過合併,分裂,上移,下移節點來保持B-Tree樹的特徵,此樹遠比二叉樹矮的多,並且不會根據數據不斷變動變成線性的這種情況。
3.3、B+樹(B+Tree),B+樹是B樹的變體,其定義基本與B樹相同,除了下面這幾條。
1)、非葉子節點的子樹指針與關鍵字個數相同。B+樹的非葉子節點元素個數和指針的數目是相同的,B樹裏面如果是3階數,非葉子節點元素個數和指針是相同的。表明了B+樹可以存儲更多的關鍵字。
2)、非葉子節點的子樹指針P[i],指向關鍵字值[K[i],K[i + 1])前閉後開的子樹。比如K[i]的值等於10,那麼K[i + 1]的值就是20,10對應的子樹裏面的值10、15、18均小於K[i + 1]這個20的值,均大於等於K[i]這個10的值,注意,大於等於K[i]的條件不是硬性的,也有取子樹裏面的最大值18作爲K[i]的這種情況。但是必須小於K[i + 1]的值。
3)、非葉子節點僅用來索引,數據都保存在葉子節點中。比如要搜索樹10相關的數據的時候,當搜索到第一個10的時候,並不能停止,必須要檢索到葉子節點當中,因爲葉子節點才存儲了我們需要用到的數據,存儲的數據有的可能是指向數據文件的指針,有的也可能是主鍵的值,或者直接將關鍵數據存儲到這個節點上面了,總之會存儲到葉子節點當中的。這就表明了B+樹所有的檢索都是從根部開始的,檢索到葉子節點才能結束,同時非葉子節點僅用來存儲索引,非葉子節點不存儲數據的話,又可以存儲更多的關鍵字了,這也就使得B+樹相對B樹來說更矮,B樹的搜索可能在任意一個非葉子節點就終結掉了,也就是可能把數據文件存儲到非葉子節點上。
4)、所有葉子節點均有一個鏈指針指向下一個葉子節點,並按大小順序鏈接。把這些葉子節點鏈接起來有什麼用呢,B+樹的葉子節點都是按照大小順序來做排列的,鏈接起來的話呢,方便我們直接在葉子節點做範圍統計,比如說要搜索大於等於10的這些數據,定位到葉子節點的10之後呢,會根據鏈接直接向後進行統計,而不會是回到根節點進行搜索了,支持範圍統計,即一旦定位到某個葉子節點,便可以從該葉子節點開始橫向去跨子樹去做統計。
5)、總結,B+樹更適合用來做存儲索引,相比於B樹或者其它樹來說,在文件系統以及數據庫系統當中更有優勢,原因如下所示:
5.1)、B+樹的磁盤讀寫代價更低。B+樹的內部結構並沒有指向關鍵字具體信息的指針,也就是不存放我們的數據,只存放索引信息,因此其內部節點相比於B樹更小,如果把所有統一內部節點的關鍵字存放在同一盤塊中,這個盤塊所能容納的關鍵字數量也越多,一次性讀入內存中需要查找的關鍵字也就越多,相對來說,IO讀寫次數也就降低了。
5.2)、B+樹的查詢效率更加文穩定。由於內部節點並不是最終指向文件內容的節點,而只是葉子節點中關鍵字的索引,所以任何關鍵字的查找,必須走一條從根節點到葉子節點的路,所有關鍵字查詢的長度相同,導致每一個數據的查詢效率也幾乎是相同的,穩定的O(logn)。
5.3)、B+樹更有利於對數據的掃描。B樹在提高了磁盤IO性能的同時,並沒有解決元素遍歷效率低下的問題,而B+樹只需要遍歷葉子節點就可以解決對全部關鍵字信息的掃描。對於數據庫中頻繁使用的範圍查詢,B+樹在範圍查詢的過程中有更高的性能,這也就是把B+樹做主流索引數據結構的原因。
3.4、運用Hash結構存儲索引。
有些數據庫存儲引擎還支持哈希這個數據結構作爲其索引,哈希結構就是根據哈希函數的運算,只需經過一次定位便能找到需要查詢數據所在的頭,B+樹需要從根節點到非葉子節點,再到葉子節點,最後纔可以訪問到我們需要的數據,這樣可能會經過多次的IO訪問,哈希索引的效率理論上要高於B+樹的效率。
哈希索引的查詢效率雖然比較高,但是哈希結構做索引是有缺點。
1)、缺點一如僅僅能滿足"=","IN",不能使用範圍查詢。由於哈希索引比較的是進行哈希運算之後的哈希值,所以只能等值的過濾,不能用於基於範圍的查詢,因爲經過相應的哈希算法處理之後的哈希值的大小,並不能保證和哈希運算前的完全一樣。
2)、缺點二,無法被用來避免數據的排序操作。因爲哈希結構存放的應該哈希運算之後的值,而且哈希值的大小關係並不一定和哈希運算前的鍵值完全一樣,所以數據庫無法使用索引的數據來避免任何排序運算。
3)、缺點三,不能利用部分索引鍵查詢。對於組合索引,哈希索引在計算哈希值的時候是組合鍵,就是將組合索引鍵合併之後,在一起進行運算的哈希的值,而不是單獨計算哈希值的,通過組合索引的前面一個或者幾個索引鍵來查詢的時候,哈希索引也無法被利用,而B+樹是支持利用組合索引裏面的部分索引的。
4)、缺點四,不能避免表掃描。哈希索引是將索引鍵通過哈希運算之後將運算結果的哈希值和所對應的行指針信息存儲到一個桶中,由於不同索引鍵存在相同的哈希值,所以即使取出滿足某個哈希鍵值的那些數據來,也無法從哈希索引中直接完成查詢,還是要通過訪問這個桶中的實際數據進行相應的比較,這是無法避免表掃描的原因的。
5)、缺點五,遇到大量Hash值相等的情況後性能並不一定就會比B+樹索引高。對於選擇性比較低的索引鍵,如果創建哈希索引,那麼將會存在大量記錄指針及其存放同一個桶的情況,從而造成整體性能非常低下,就類比之前的二叉樹,很可能變成線性存儲結構,也有可能在一個極端的情況下,所有的鍵計算出來的哈希值都是相同的,也就是都放到同一個桶中,那我們查詢最後一條數據,就會變成線性的了,所以呢,這也是哈希索引不能成爲主流索引的原因,因爲不穩定,也不支持範圍的查詢。
3.6、BitMap索引,稱爲位圖索引, 當表中的某個字段只有幾種值的時候,比如要表示性別,只有男女兩種性別的時候,如果僅僅是爲了在這個字段上實現高效的統計,此時使用位圖索引是最佳選擇了,需要注意的是,目前很少的數據庫支持位圖索引,比較主流的是Oracle數據庫。位圖索引的結構類似於B+樹。
4、密集索引和稀疏索引的區別?
答:密集索引文件中的每個搜索碼值都對應一個索引值。密集索引,葉子節點保存的不僅僅是鍵值,還保存了位於同一行記錄裏面的其他列的信息,由於密集索引決定了表的物理排列順序,一個表只能有一個物理排列順序,所以一個表只能創建一個密集索引。
稀疏索引文件只爲索引碼的某些值建立索引項。葉子節點僅保存了鍵位類信息,以及該行數據的地址,有的稀疏索引是僅保存了鍵位信息及其主鍵,那麼定位到葉子節點之後,仍然需要通過地址和主鍵信息進一步定位到數據。
5 、對MySql做具體分析,主要有兩種存儲引擎,一種是MyISAM,另外一種是Innodb。對於MyISAM不管是主鍵索引,唯一鍵索引,或者普通索引,其索引均屬於稀疏索引。Innodb是必須有且僅有一個密集索引,密集索引的選取規則。
5.1)、Innodb密集索引的選取規則。
1)、若一個主鍵被定義,該主鍵則作爲密集索引而存在的。
2)、若沒有主鍵被定義,該表的第一個唯一非空索引則作爲密集索引。
3)、若不滿足以上條件,Innodb內部會生成一個隱藏主鍵(密集索引)是一個6字節的列,該列的值會隨着數據的插入而自增,也就是說Innodb必須有一個主鍵,而該主鍵必須作爲唯一的密集索引而存在。
4)、非主鍵索引存儲相關鍵位和其對應的主鍵值,包含兩次查找,這就是爲什麼一定要主鍵索引。非主鍵索引,也就是稀疏索引的葉子節點並不存儲行數據的物理地址,而是存儲的是該行的主鍵值,所以非主鍵索引包含了兩次查找,一次是查找次級索引自身,再查找主鍵。
5.2)、Innodb使用的是密集索引,將主鍵組織到一棵B+樹中,而行數據就存儲在葉子節點中,因爲Innodb的主鍵索引和對應的數據是保存在同一個文件中的,所以檢索的時候,在加載葉子節點的主鍵進入內存的同時,也加載了對應的數據,即若使用where id = 某個值的時候,這樣的條件查詢主鍵,則按照B+樹的檢索算法即刻查找到葉子節點,並獲得對應的行數據,若對稀疏索引進行條件篩選,則需要經歷兩個步驟,第一步在稀疏索引的B+樹中檢索該鍵,然後定位到主鍵信息,獲取到主鍵信息之後呢,還需要精力第二步,第二步就是使用where id =某個值的時候,在B+樹中再執行一遍我們B+樹的檢索操作,最終再到達葉子節點獲取整行的數據。
5.3)MyISAM使用的是均爲稀疏索引,稀疏索引的B+樹看上去沒有什麼不同,節點的結構呢,完全一致,只是存儲的內容不一樣而已,主鍵索引B+樹的節點存儲的主鍵,輔助鍵索引B+樹存儲的輔助鍵,表數據存儲在獨立的地方,就是索引和數據是分開存儲的,這兩棵B+樹的葉子節點呢,都使用一個地址指向真正的表數據,像這裏的映射關係,對於表數據來說,這兩個鍵沒有任何差別,由於索引數是獨立的,通過輔助鍵檢索,無需訪問主鍵的索引數。
6、 如何定位並優化慢查詢Sql?
答:可以先回答,具體場景需要具體分析。
1)、根據慢日誌定位慢查詢sql。慢日誌就是用來記錄我們執行的比較慢的sql。Mysql有很多自帶的系統變量,通過查詢系統變量可以得知配置信息。
使用show variables like '%quer%';
根據查詢出來的變量,需要關注slow_query_log這個是慢日誌,slow_query_log_file這個文件會記錄我們的慢日誌,long_query_time這個是表示的是每次執行這個SQL花費10秒鐘的都會被記錄到慢日誌文件裏面,可以修改是1秒鐘,如果大於1秒的都會被記錄到慢日誌文件裏面。使用set global slow_query_log = on來打開慢日誌查詢,set global long_query_time = 1;這個變量需要重新鏈接客戶端,臨時生效,重啓數據庫恢復原值。
使用show status like '%slow_queries%',查詢系統的狀態,Slow_queries這個是慢查詢的數量,當我們有一次執行比較慢的時候,value值就是1,以此類推,注意,這裏是本次鏈接的條數。
2)、使用explain等工具分析sql。在分析查詢性能的時候,explain非常管用,explain放在select查詢關鍵字的前面,用來描述mysql如何執行查詢操作。以及mysql成功返回結果集執行的行數。explain可以幫助我們分析select語句,讓我們知道查詢效率低下的原因,從而改進我們的查詢,讓查詢優化器可以更好的工作。
注意,extra,可以獲取到更爲詳細的信息,輔助我們l瞭解語句的執行方式,類型有很多,這裏關注Using fIlesort、Using temporary。extra中出現以下2項意味着MYSQL根本不能使用索引,效率會受到重大影響,應儘可能對此進行優化。
extra項 | 說明 |
Using filesort | 表示Mysql會對結果使用一個外部索引排序,而不是從表裏面按照索引次序讀到相關內容。可能在內存或者磁盤上進行排序。Mysql中無法利用索引完成的排序操作稱爲"文件排序"。 |
Using temporary | 表示Mysql在對查詢結果排序的時候使用臨時表,常見於排序Order by和分組查詢Group by。 |
3)、修改sql或者儘量讓sql走索引,即修改自己的sql進行優化或者將需要查詢的字段添加一個索引即可。
可以使用語句來強制使用某個索引,如下所示。mysql的查詢優化器,最重要的目標是儘可能的使用索引,並且使用最嚴格的索引來消除儘可能多的數據行,最終目標是提交select語句查找數據行,而不是排除數據行,優化器試圖排除數據行的原因在於它排除數據行的速度越快,那麼找到與條件匹配的數據行也就越快,因此,查詢優化器會根據一些分析和判斷的標準決定走那個索引。
7、聯合索引的最左匹配原則的成因,聯合索引就是有多列組成的索引。
7.1)、最左匹配原則,假設有兩列A、B,此時對A、B設置一個聯合索引,就是將A、B都設置成索引,設置順序是A、B,在where語句中調用A = ? and B =? ,此時會走A、B的聯合索引,如果使用where A = ? 也會走聯合索引,但是當我們調用where B = ?的時候,此時不會走A、B的組合索引了。
7.2)、最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到查詢(>、<、between、like)就停止匹配,比如a= 3 and b = 4 and c > 5 and d = 6,如果建立(a、b、c、d)順序的索引,d是用不到索引的,如果建立(a、b、d、c)的索引則都可以用到,a、b、d的順序可以任意調整。
7.3)、=和in可以亂序,比如a = 1 and b = 2 and c =3 建立(a、b、c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式。
7.4)、聯合索引的最左匹配原則的成因,mysql創建複合索引的規則是首先會對複合索引的最左邊的,也就是第一個索引字段的數據進行排序,在第一個字段的排序的基礎上呢,在對後面第二個索引字段進行排序,其實就相當於實現了類似Order By字段1,Order By 字段2,這樣的一種排序規則.所以呢,第一個字段是絕對有序的,而第二個字段就是無序的了,因此通常情況下,直接使用第二個字段進行條件判斷是用不到索引的,這就是所謂的Mysql爲什麼要強調聯合索引的最左匹配原則的原因。
8、索引是建立的越多越好嗎?
答:首先數據量小的表不需要建立索引,建立索引會增加額外的索引開銷。數據變更需要維護索引,因此更多的索引意味着更多的維護成本。更多的索引意味着也需要更多的空間。
9、鎖模塊之MyISAM與InooDB關於鎖方面的區別是什麼?
1)、MyISAM默認用的是表級鎖,不支持行級鎖。MyISAM不支持事務,表級鎖的情況不好進行模擬,此時將數據表數據量增大,這樣進行查詢或者更新比較多的數據的時候,會需要一點時間去執行語句,這個時候再開個窗口來去做別的操作,這樣就可以製造出併發訪問的場景,並且可以看到MyISAM會自動的爲我們加上相關的表鎖。
1.1)、第一種情況,下面演示了,先上讀鎖,再上寫鎖或者讀鎖的情況,如下所示:
主鍵索引範圍的查詢,myisam引擎,MyISAM默認用的是表級鎖,不支持行級鎖。
先上讀鎖,再上寫鎖:當讀鎖未被釋放的時候,另外一個窗口想要對同一張表加上寫鎖的時候,就會被阻塞,直到所有的讀鎖都被釋放爲止。
先上讀鎖,再上讀鎖:MyISAM,在進行範圍查詢的時候呢,我們依然可以對錶裏面的數據進行讀操作,這也是讀鎖,又叫爲共享鎖的原因。
1.2)、此時,演示先上寫鎖,後上讀鎖或者寫鎖這種情況。MyISAM默認用的是表級鎖,不支持行級鎖。表級鎖會鎖住整張表,如果先執行1到兩百萬的查詢操作,然後再執行兩百萬開外的數據進行修改操作,數據表同樣是被鎖住的,但是如果是行級鎖的話,只鎖住1到兩百萬行的數據,對兩百萬開外的數據進行更新是可以進行更新的。鎖按照級別分爲共享鎖和排它鎖,上了共享鎖之後呢,依然支持上共享鎖,不支持上排它鎖,要是先上排它鎖,那麼另外的讀或者寫都是不允許的,共享鎖和排它鎖的這種情況,也同樣適合於支持行級鎖的InnoDB引擎。
第二個窗口,模擬併發操作。
先上寫鎖,再上讀鎖:查詢操作需要等待更新操作執行完畢,將寫鎖釋放,纔可以執行查詢操作的。
先上寫鎖,再上寫鎖:當第二次更新語句的時候,需要等待第一此更新的語句執行完畢,將寫鎖釋放,纔可以執行第二個更新操作。
2)、InnoDB默認用的是行級鎖,也支持表級鎖。
2.1)、什麼是二段鎖呢,也就是加鎖和解鎖是分成兩個步驟來進行的,即先對同一個事務裏面的一批操作分別進行加鎖,然後到commit的時候,在對事務裏面加上了鎖呢,進行統一的解鎖,而當前commit是自動提交的,所以看起來和Myisam沒有太大的區別。
第二個窗口,模擬併發操作。
先讀,後寫:顯示的上共享鎖,後面加上lock in share mode就上了讀鎖了。上了讀鎖之後,再執行更新語句就無法執行了。此時需要該session執行commit之後,更新語句纔可以成功。
2.2)、先讀,後讀:此時id等於3這個sql語句並沒有提交,此時,使用另外一個session,將id等於3的數據加上共享鎖,看看是否可以進行查詢。是可以將數據讀出來的。此時,這兩個共享鎖是不會起衝突的。
第二個窗口,模擬併發操作。
3)、InnoDB中行級鎖和索引是否有關呢?
什麼是表級鎖和行級鎖呢,首先需要明白,併發的訪問是有多個語句可能同時操作一張表,或者同一張表裏面的同一條數據。
用到表級鎖,只要操作到表裏面的數據的時候,均會上表鎖,因此表級鎖和索引無關。行級鎖和索引是否有關呢,除了使用id主鍵索引以外的其他鍵,只要sql用到了索引設計到的行都會被上共享鎖或者排它鎖。當不走索引的時候,整張表都會被鎖住,也就是此時的查詢用的是表級鎖,所以InnoDB在sql沒有索引的時候,用的是表級鎖,而sql用到索引之後,用的是行級鎖,以及Get鎖,這個Get鎖是設計到走普通非唯一索引的時候用到的。
無論是表級鎖還是行級鎖,都分爲共享鎖和排斥鎖,共享鎖和排斥鎖的兼容性,它們之間的關係如下所示:
X | S | |
X | 衝突 | 衝突 |
S | 衝突 | 兼容 |
解釋如下所示:
如果session1對某一行數據上了排它鎖X,排它鎖怎麼上的,進行增加、修改、刪除就行了,或者select for update的時候都會給某一行數據上排它鎖,同時呢,如果session2想對這行數據排它鎖,這是不允許的,會衝突,會等待session1的鎖釋放。此時,如果想要上共享鎖S,同樣也會衝突,會等待session1的鎖釋放。
如果session1對某一行數據上了共享鎖S,如果session2想對這行數據上排它鎖的時候,也會是衝突的,也需要等待共享鎖的釋放,纔可以上排它鎖。但是呢,如果用session2對這行數據上共享鎖的時候,它是不衝突的,兩者是兼容的。
行級鎖是不是一定比表級鎖要好,也未必,鎖的粒度越細代價越高,相比表級鎖,在表的頭部直接加鎖來講,行級鎖還要掃描到m某行的時候對其上鎖,這樣代價是比較大的,InnoDB支持事務的同時,也相比MyISAM引擎帶來了更大的開銷,InnoDB有且僅有一個聚集索引的,數據文件是和索引綁到一起的,必須要有主鍵,通過主鍵索效率很高,但是輔助索引呢,需要查詢兩次,先查詢到主鍵,然後再通過主鍵查詢到數據,而MyISAM是非聚集索引,數據文件是分離的,索引保存的是數據文件的指針,主鍵索引和輔助索引是獨立的,因此MyISAM引擎在存檢索系統中,也就是增刪改很少的系統中,其性能要好於InnoDB的。
4)、MyISAM和InnoDB適合的場景。
MyISAM適合的場景,場景一,頻繁執行全表count語句,對於InnoDB來講,它是不保存表的具體行數的,執行count統計的適合需要重新掃描統計,但是MyISAM用一個變量保存了整個表的行數,執行上述語句的時候,只需要讀出該變量即可,速度很快。場景二,對數據進行增刪改的頻率不高,查詢非常頻繁的時候,因爲增刪改會設計到鎖表操作。場景三,適合沒有事務的場景。
InnoDB適合的場景,場景一,適合數據增刪改查都相當頻繁的場景,增刪改只是某些行被鎖,在大多數情況下避免了阻塞,而MyISAM對某行數據的操作都會鎖住整張表。場景二,可靠性要求比較高,要求支持事務的系統。
5)、數據庫鎖的分類。
5.1)、按照鎖的粒度劃分,可以分爲表級鎖、行級鎖、頁級鎖。其中InnoDB默認支持表級鎖、行級鎖,InnoDB對行級上鎖的時候,會先上一種級別的意向鎖。MyISAM僅支持表級鎖。BDB引擎支持頁級鎖,是一種介於表級與頁級之間的鎖。
5.2)、按鎖級別劃分,可分爲共享鎖、排它鎖。
5.3)、按照加鎖方式的劃分,可以分爲自動鎖、顯式鎖。像意向鎖、MyISAM的表級鎖、以及update、insert、delete加上的鎖就是自動鎖,這是mysql自動加上的鎖。而select for update、lock in share mode這些顯式去加的鎖就是顯式鎖。
5.4)、按照操作劃分,可以分爲DML鎖,DDL鎖。對數據進行操作的鎖就是DML鎖,包括對數據的增刪改查,而對錶機構進行變更的如alter table加上的鎖就是DDL鎖。
5.5)、按照使用方式劃分,可以分爲樂觀鎖、悲觀鎖。
樂觀鎖,認爲數據一般情況下不會造成衝突,所以在數據進行提交更新的時候,纔會正式對數據衝突與否進行檢測,如果發現了衝突則返回用戶錯誤的信息,讓用戶決定如何去做,相對於悲觀鎖,在對數據庫進行處理的時候呢,樂觀鎖並不會使用數據庫提供的鎖機制,一般的是實現樂觀鎖的方式,記錄數據的版本,實現數據版本,一種是使用版本號,一種是使用時間戳。
悲觀鎖,指的是數據被外界,外界指的是本系統當前的其它事務,以及來自外部系統的事務的處理,對這些外界的處理持保守態度,因此在整個數據的處理過程中,將數據處於鎖定狀態,悲觀鎖的實現往往依靠數據庫提供的鎖機制,只有數據庫底層提供的鎖機制才能真正保證數據訪問的排他性,否則即使在本系統中,實現了加鎖機制,也無法保證外部系統不會修改數據。全程排它鎖鎖定正是一種悲觀鎖的實現,悲觀併發控制是先取鎖再訪問的保守策略,爲數據的處理安全提供了保證,但是在效率上方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增加死鎖的機會,另外在只讀性數據庫處理中,由於不會產生衝突,也沒有必要使用鎖,如果上鎖會增加系統負擔同時還會降低並行性,如果一個事務鎖定了某行數據,其它事務就必須等待該事務處理完,纔可以去處理。
10、鎖模塊之數據庫事務的四大特性?
答:ACID。
1)、原子性(Atomic),指事務包含的所有操作要麼全部執行,要麼全部失敗回滾,要麼全做,要麼全都不做。
2)、一致性(Consistency),事務應確保數據庫的狀態從一個一致狀態轉變爲另外一個一致的狀態,一致的狀態時指數據庫中的數據應該滿足完整性約束,例如轉賬,A用戶和B用戶兩者的錢加起來是2k,A用戶和B用戶進行轉賬,不論怎麼進行轉賬,最後A用戶和B用戶的錢加到一起還是2k。
3)、隔離性(Isolation),隔離性是多個事務併發執行的時候,一個事務的執行不應該其它事務的執行,重點掌握。
4)、持久性(Durability),一個事務一旦提交,數據庫的修改應該永久保存到數據庫中,持久性意味着當系統發生故障的時候,確保已提交事務的更新不能丟失,即對已提交事務的更新能恢復,一旦一個事務b被提交,DBMS必須保證提供適當的冗餘,使其耐得住系統的故障,所以持久性主要在於DBMS的恢復性能。
11、鎖模塊之事務併發訪問產生的問題以及事務隔離機制?事務隔離級別以及各級別下的併發訪問問題。事務併發訪問引起的問題以及如何避免,就是事務併發訪問可能引起的問題,以及避免這些問題的一類或者幾類。mysql會利用鎖機制創建出來不同的事務隔離級別,從低到高進行學習。
1)、更新丟失lost update,mysql所有事務隔離級別在數據庫層面上均可避免。也就是一個事務的更新會覆蓋另一個事務的更新,現在主流的數據庫都會自動加鎖避免這種更新丟失問題的發生。
2)、髒讀dirty read,read committed事務隔離級別以上可以避免。指的是一個事務讀到另一個事務的未提交的更新數據,而該種問題可以在已提交讀事務隔離級別以上去避免。何避免髒讀帶來的惡果呢,只需要把事務隔離級別成read committed即可。read committed是提交讀,這個是隻能讀到其他事務已提交的內容,該事務隔離級別也是Oracle默認的事務隔離級別。
3)、不可重複讀non repeatable read,REPEATABLE-READ事務隔離級別以上可避免。指的是事務A多次讀取同一數據,而事務B在事務A多次讀取的過程中對數據做了更新並提交,導致事務A多次讀取同一數據的時候結果不一致。解決不可重複讀的問題,是將事務隔離級別再調大一級,設置成innodb默認的事務隔離級別即REPEATABLE-READ(repeatable read)可重複讀,就是支持多次重複讀,能夠讀到相同的結果。
4)、幻讀Phantom read,SERIALIZABLE(serializable)事務隔離級別可避免。指的是事務A讀取與搜索條件相匹配的若干行,事務B以插入或者刪除行等方式來修改事務A的結果集,導致事務A看起來像出現幻覺一樣。如何避免幻讀了,將事務隔離級別設置成最高的隔離seriatizable級別即可避免。
5)、事務併發訪問引起的問題以及如何避免,總結:
5.1)、指的注意的是,我們說的是數據庫層面是如何規避這些併發訪問產生的問題的。在數據庫層面上,數據丟失在各個隔離級別下均是很難復現的,真正的更新丟失問題發生在應用程序中,需要在應用程序中去避免。
5.2)、讀未提交read uncommitted,會發生髒讀,不可重複讀,幻讀這三個問題。
5.3)、讀已提交read committed,避免了髒讀,但是不可避免不可重複讀、幻讀這兩個問題。
5.4)、可重複讀repeatable read,避免了髒讀、不可重複讀,理論上不能避免幻讀,但是通過某種巧妙的方式規避了幻讀。
5.5)、串行化serializable,避免了髒讀、不可重複讀、幻讀。不可重複讀側重於對同一數據的修改,幻讀側重於新增或者刪除。事務隔離級別越高,安全性越高,串行化執行越嚴重,這樣就降低了數據庫的併發度。根據業務需要設置事務隔離級別,mysql默認的可重複讀repeatable read。
事務隔離級別 | 更新丟失 | 髒讀 | 不可重複讀 | 幻讀 |
未提交讀 read uncommitted |
避免 | 發生 | 發生 | 發生 |
已提交讀 read committed |
避免 | 避免 | 發生 | 發生 |
可重複讀 repeatable read |
避免 | 避免 | 避免 | 發生 |
串行化 serializable |
避免 | 避免 | 避免 | 避免 |
12、Group By與Having的學習。
12.1)、Group By是根據給定的數據列的每個成員,對查詢結果進行分組統計,最終得到一個分組彙總表。對於這個含義有兩個解釋,如下所示:
1)、滿足select子句中的列名必須爲分組列或者列函數。如果使用Group By,那麼select語句中選出的列y要是是Group By裏面用到的字段,要麼就是帶有sum、min、max、count、avg等等。注意,該條件只針對同一張表成立,因爲如果兩個表連接的時候,select後面的列可以不是group by後面的列的。
2)、列函數對於group by自居定義的每個組各返回一個結果,可以通過explain查看Extra字段可以看到Using temporary。
12.2)、Having通常與Group By子句一起使用。
1)、Having在和Group By子句使用的時候,我們可以應用他在Group By子句之後l來指定過濾的條件,如果省略了Group By子句,Having子句行爲就像Where子句一樣,Having支持所有Where操作符。
2)、Where過濾行,Having過濾組。
3)、出現在同一sql的順序,where > group by > having。
13、鎖模塊之當前讀和快照讀,InnoDB可重複讀隔離級別下如何避免幻讀。
1)、表象:快照讀(非阻塞讀),僞MVCC。表象是可以看到的現象,但並不是真正的原因,真正的原因是體現在內在的,表象是通過基於僞MVCC機制實現的快照讀,即非阻塞讀,來避免讓我們看到幻行,前提是在repeatable read(RR)級別下。
2)、內在:next-key鎖(行鎖+gap鎖)。
14、當前讀和快照讀。
1)、當前讀,select ...lock in share mode、select ... for update、 update 、 delete 、 insert。當前讀就是加了鎖的增刪改查語句,不管是上的共享鎖還是排它鎖,均爲當前讀,爲什麼叫做當前讀呢,因爲讀取的是記錄最新版本,並且讀取之後還需要保證其它併發事務不能修改當前記錄,對讀取的記錄加鎖,其中除了select ...lock in share mode加的是共享鎖,其它加的都是select ... for update、 update 、 delete 、 insert排它鎖。爲什麼update 、 delete 、 insert也是當前讀呢,我們瞭解到RDBMS主要有兩大部分組成程序實例(mysql server的實例)和存儲(InnoDB引擎)。
2)、快照讀,不加鎖的非阻塞讀,select。快照讀就是簡單的select操作,不加鎖。不加鎖的條件是在事務隔離級別不爲serializable的前提下才成立的。如果是serializable事務隔離級別,由於是串行讀,快照讀也退化成了當前讀即select ...lock in share mode模式。之所以出現快照讀是爲了提升併發性能的考慮,快照讀的實現是基於多版本併發控制即MVCC,可以認爲MVCC是行級鎖的變種,但是它在很多情況下,避免了加鎖操作,因此開銷更低,既然是基於多版本的,也就意味着快照讀有可能讀到的並不是數據的最新版本,可能是之前的歷史版本。
3)、在read committed(RC)事務隔離級別下,當前讀和快照讀結果是一樣的,都是更新過後的數據。
4)、在repeatable read(RR)事務隔離級別下,事務首次調用的地方很關鍵,也就是創建快照的時機決定了數據的版本。如果是先執行快照讀,當前讀和快照讀結果是不一樣的,當前讀的結果是更新後的結果,快照讀是更新前的結果。如果是先執行更新操作,再執行快照讀和當前讀,那麼快照讀和當前讀的結果是一致的。
15、RC、RR級別下的InnoDB的非阻塞讀如何實現?
答:InnoDB要實現在read committed(RC)、repeatable read(RR)級別下的快照讀離不開三個因子。
1)、數據行裏面的DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段。第一個因子是每行數據記錄除了存儲數據以外,還有額外的一些字段,最關鍵是這三個字段。DB_TRX_ID該字段用來標識最近一次對本行記錄做修改,不管是插入還是更新,事務的標識符,即最後一次修改本行數據的事務id,至於刪除操作,在Innodb引擎看來也不過是一次修改操作、DB_ROLL_PTR其中ROLL就是回滾,PTR就是指針的意思,這個是回滾指針,指的是寫入回滾段roll back segment的undo日誌記錄,如果一行記錄被更新,則undo log report包含重建該行記錄b被更新之前的內容所必須的信息、DB_ROW_ID指的是行號,包含一個隨着新行插入而單調遞增的行id,當由innodb自動產生聚集索引的時候,聚集索引會包括這一個行id的值,否則這個行id不會出現在任何索引中。如果innodb引擎表既沒有主鍵也沒有唯一鍵的話,innodb會自動爲我們一次創建一個自增的隱藏主鍵就是這裏的DB_ROW_ID字段。
2)、undo日誌,當我們對記錄做了變更操作的時候,就會產生undo記錄,undo記錄中存儲的是老版本數據,當一箇舊的事務需要讀取數據的時候,爲了可以讀取到老版本的數據,需要順着undo鏈找到滿足的記錄,undo主要分爲兩種,一種是Insert undo log,一種是update undo log。其中Insert undo log表示的是事務對Insert新紀錄產生的undo log,只在事務回滾的時候需要,並且在事務提交的時候可以立即丟棄。update undo log指的是事務對日誌進行delete或者update操作的時候產生的undo log,不僅在事務回滾的時候需要,快照讀也需要,所以不可以隨便刪除,只要當數據庫所使用的快照中不涉及該日誌記錄,對應的回滾日誌纔會被線程刪除掉。
3)、read view。主要是用來做可見性判斷的,即當我們去執行快照讀select的時候會針對我們查詢的數據創建出一個read view,來決定當前事務能看到的是那個版本的數據,有可能是當前最新版本的數據,也有可能是隻允許你看undo Log裏面某個版本的數據。read view遵循一個可見性算法,主要是將要修改的數據的DB_TRX_ID取出來,與系統其它活躍的事務id做比較,如果大於或者等於這些id的話,就通過DB_ROLL_PTR指針去取出undo log,直到小於這些活躍事務id爲止。這樣就保證了當前獲取到的版本是活躍的最穩定的版本。