1. 程式人生 > >資料庫的索引與鎖()

資料庫的索引與鎖()

一、索引

索引是幫助MYSQL高效獲取資料的資料結構,可以得到索引的本質,索引是資料結構,有一列或多列欄位。

1.1索引的基礎知識

首先知道索引可以加快資料庫的檢索速度,表經常進行INSERT/UPDATE/DELETE操作就不要建立索引,索引會降低插入刪除修改等維護任務的速度。 2、索引需要佔物理和資料空間 3、索引具有最左匹配原則  4、索引的聚集索引和非聚集索引 5、Mysql支援Hash索引和B+樹索引兩種。

首先了解Mysql的基本儲存結構是頁(所有的記錄都存在頁裡面),各個資料頁可以組成一個雙向連結串列,而每個資料頁中的記錄又可以組成單向連結串列,每個資料頁中的記錄又可以組成單向連結串列。每個資料頁都會儲存在裡面的記錄生成一個頁目錄,通過主鍵查詢某條記錄的時候可以再頁目錄中使用二分快速定位到對應的槽,然後再遍歷該槽對應分組中的記錄即可以快速找到指定的記錄。   從最小記錄開始依次遍歷單鏈表中的每條記錄。

1.2索引提高檢索的速度

索引做了些什麼可以讓我們查詢加快速度呢?實際上就是將無序的資料變為有序的資料。

如果沒有索引我們就需要遍歷雙向連結串列來定位對應的頁,現在通過“”目錄”就可以很快定位到對應的頁上,其實底層就是B+樹,B+樹作為樹的一種實現,能夠讓我們很快查找出對應的記錄。

1.3索引降低增刪改的速度

B+樹是平衡樹的一種,所謂平衡樹就是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。如果一棵普通的樹在極端的情況下,是退化成連結串列的,樹的優點就不存在了。

B+樹是平衡樹的一種,是不會退化成連結串列的,樹的高度都是相對比較低的(類似又矮又胖的那種)這樣一來我們檢索的時間複雜度是O(logn)。B+樹是一顆平衡樹,如果我們對這顆樹刪改的話,那肯定破壞他的原有結構。要維持平衡樹,就必須做額外的工作。正因為這些額外的工作開銷,呆滯索引會降低增刪改的速度。(Innodb資料檔案是一顆B+樹,非單調的書劍會造成插入新紀錄時的資料檔案為了維持B+樹的特性而頻繁撕裂,這樣非常低效)

1.4雜湊索引

除了B+樹之外,還有一種常見的雜湊索引。雜湊索引就是採用一定的雜湊演算法,把鍵值換算成新的雜湊值,檢索時不需要類似B+樹那樣從根節點到葉子節點的逐級尋找,只需要一次hash演算法like定位到響應位置,速度非常快。

本質上就是把鍵值對換算成新的hash值,來根據這個hash值來定位。

hash的缺點1、hash索引也沒辦法利用索引完成排序,2、不支援最左匹配原則 3、在大量重複鍵值的情況下,hash索引的效率也是最低的,存在hash碰撞的問題。 4、不支援範圍查詢

1.5聚集與非聚集索引

聚集索引就是以主鍵建立的索引,非聚集索引就是以非主鍵建立的索引。

區別:聚集索引在葉子節點儲存都是表中資料,非聚集索引在葉子節點儲存的是主鍵與索引列

使用非聚集索引查詢出資料時,拿到葉子上的主鍵再去查到想要查詢的資料。(拿到主鍵再查詢這個過程回表)

非聚集索引也叫作二級索引,不用糾結那麼多名詞,將其等價即可~

非聚集索在建立的時候未必是單列的,可以多個列來建立索引。

建立多個單列(非聚集)索引的時候,會生成多個索引樹(所以過多的建立索引會佔用磁碟空間)。

在建立多列索引中也涉及到了一種特殊的索引——》覆蓋索引

1.6索引的最左匹配原則

最有匹配原則:索引可以簡單如一個列a,也可以複雜如(a,b,c,d),即聯合索引。  如果是聯合索引,那麼key也由多個列組成,同時,索引只能用查詢key是否存在(相等),遇到範圍查詢(<>between like左匹配)等就不進一步匹配了,後續退化成線性查詢。(因此,列的排列順序決定了可命中索引列數

如索引(a,b,c,d)查詢條件a=1 and b=2 and c=3 and d<4,則會在每個節點依次命中a,b,c,無法命中d,因為索引只能命中相等,而不能是範圍匹配。

1.7 = in 自動優化順序

不需要考慮=,in等順序,mysql會自動優化這些條件的順序,以匹配儘可能多的索引列。

如有索引(a, b, c, d),查詢條件c > 3 and b = 2 and a = 1 and d < 4與a = 1 and c > 3 and b = 2 and d < 4等順序都是可以的,MySQL會自動優化為a = 1 and b = 2 and c > 3 and d < 4,依次命中a、b、c。

索引總結:

1、最左匹配原則,非常中亞哦,Mysql會 一直向右匹配直到遇到範圍查詢(> < Between like)就停止匹配。

2、儘量選擇區分度高的列作為索引。別用性別這種

3、索引列 不能參與計算,儘量保持乾淨。比如FROM UNIXTIME(create_time) = '2016-06-06'就不能使用索引,原因很簡單,B+樹中儲存的都是資料表中欄位值,但是進行索引時,需要把所有的元素都應用函式才能比較,所以時間寫成UNIX_TIMESTAMP(‘2016-06-26’)

5、儘可能的去擴充套件索引而不是建立索引,比如表中有了a索引,現在要加上變成a b索引,那麼只需要修改原來的索引即可。

6、單個多列組合索引和多個單索引影的查詢效果不同,因為執行時只能使用一個索引,會從多個單列索引中選擇一個限制最為嚴格的索引。

在mysql中有一大堆鎖名詞,排它鎖,共享鎖,表鎖,頁鎖,間隙所,行鎖,讀鎖,寫鎖,樂觀鎖、悲觀鎖、死鎖。等等。。。。

2.1鎖的知識

首先很多時候這些鎖並不是我們去規定的,在一般情況下還是可以跑的好好的。

對於UPDATE、DELETE、INSERT語句,InnoDB會自動給設計資料集加上排他鎖。

對於MyISAM在執行查詢語句SELECT前,會自動給涉及的所有表加讀鎖,在執行更新操作(UPDATE DELETE INSERT)前,會自動給涉及的表加寫鎖,這個過程並需要使用者干預。

2.2表鎖簡單介紹

首先從鎖的粒度,可以分為兩大類 表鎖與行鎖

表鎖:開銷小、加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突概率高,併發度最低。

行鎖:開銷大、加鎖慢、會出現死鎖、鎖定粒度小,發生鎖衝突的概率低,併發度高。

不同的儲存引擎支援的鎖粒度是不一樣的。InnoDB行鎖與表鎖都支援,MyISAM只支援表鎖。

InnoDB只有通過索引條件檢索資料才能使用行鎖,否則,InnoDB將使用表鎖,也就是說InnoDB的行鎖是基於索引的。

表鎖又分為兩種模式:表讀鎖與表寫鎖,從下圖中可以清晰看到,在表讀鎖和表寫鎖的環境下,讀讀不會阻塞,讀寫阻塞,寫寫阻塞。

讀讀不阻塞:當前使用者在讀資料,其他的使用者也在讀資料,不會加鎖。

讀寫阻塞:當前使用者在讀資料,其他使用者不能修改當前使用者的資料,會加鎖!

寫寫阻塞,當前使用者在修改資料,其他使用者不能修改當前使用者修改的資料,會加鎖。(這個是對錶鎖的兩種模式)

備註如果有一個執行緒想要獲取讀鎖,同時另一個執行緒想要獲取寫鎖,在mysql裡面,寫鎖是優先與讀鎖的。

這裡注意個區別:MyISAM可以查詢與插入操作一起併發執行,通過指定一種模式,MyISAM允許一個程序讀表的同時,一個程序從表尾插入記錄。但是InnoDB儲存引擎是不支援的。

2.3樂觀鎖與悲觀鎖

無論是Read committed 還是Repeatable read隔離級別,都是為了解決讀寫衝突的問題。 例如在可重複讀的條件下,使用者李四的操作丟失掉了。丟失更新:一個事務的更新覆蓋了其他事務的更新結果。

解決辦法就是 使用Serializable隔離級別,事務是序列執行的。

1、樂觀鎖是一種思想,具體就是表中有一個版本欄位,第一次讀的時候,獲取到這個欄位,處理完業務邏輯開始更新的時候,需要再次檢視該欄位是否和第一次的一樣。如果一樣更新,反之拒絕。之所以叫樂觀,因為這個模式沒有從資料庫加鎖,等到更新的時候再判斷是否可以更新。

2、悲觀鎖是從資料庫層加鎖,都會阻塞去等待鎖。

舉例說明:

悲觀鎖就是  select * from for update   在select語句後面加了for update相當於加了排它鎖,加了寫鎖以後,其他的事務就不能對它修改了,需要等待當前事務修改完之後才可以修改。 例如張三執行了這個操作,李四就無法對這個記錄修改。

樂觀鎖:不是資料庫層面上的鎖,是需要自己手動去加的鎖,一般我們新增一個版本欄位來實現,name sex  string  +version,用來判斷。版本不一致就回滾

2.4間隙鎖

當我們用範圍條件檢索資料而不是相等條件檢索資料,並請求共享或排它鎖時,InnoDB會給符範圍條件的已有資料記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫做間隙“GAP”,InnoDB對這個間隙加鎖的機制就是間隙鎖。(間隙鎖只在Repeatable read隔離級別下使用)

例如emp上只有101條記錄,其empid的值分別是1,2,,,,100,101 

Select * from emp where empid >100 for update

上面一個範圍查詢,InnoDB不僅會對符合條件的empid為101的加鎖,同時對大於101的間隙都會加鎖。

2.5死鎖

併發的問題少不了死鎖,在Mysql中同樣會存在死鎖的問題,但一般來說MYSQL通過回滾幫我們解決了很多死鎖的問題,但死鎖是無法完全避免的。

我們應該注意

1、以固定的順序訪問表和行,比如對兩個job批量更新的情形,簡單方法是對id列表先排序後執行,這樣就避免了交叉等待鎖的情形,將兩個事務sql順序調整一致,也能避免死鎖 。

2、大事務拆小。大事務更傾向於死鎖,如果業務允許,將大事務拆小。

3、在同一事務中,儘可能做到一次鎖定所需要的所有資源,減少死鎖概率。

4、降低隔離級別,如果業務允許,將隔離級別調低也是較好的選擇,比如將隔離級別從RR調整為RC,可以避免掉很多因為GAP鎖造成死鎖。

5、為表新增合理的索引,可以看到如果不走索引將會為表的每一行記錄新增上鎖,死鎖的概率大大增大。

2.6鎖總結

在MyISAM儲存引擎中,當執行SQL語句的時候是自動加的。

InnoDB儲存引擎,如果沒有使用索引,表鎖也是自動加的。現在我們大多是使用MYSQL的InnoDB,InnoDB支援行鎖

樂觀鎖其實是一種思想,正如其名,認為不會鎖定的情況下更新資料,如果發現不對勁,才不會更新。在資料庫中往往新增一個version欄位來實現。

悲觀鎖用的就是資料庫的行鎖,認為資料庫會發生併發衝突,直接上來吧資料鎖住,其他事務不能修改,直至提交當前事務。