jvm調優的幾種場景(小結)
Mysql 索引
索引是幫助MySQL高效獲取資料的資料結構,,排好序的快速查詢資料結構
目的:減少磁碟I/O的次數,加快查詢速度
索引主要影響兩個位置:
- 快速查詢(提高資料查詢效率):影響where後面的查詢
- 排好序:order by
索引是在儲存引擎中實現的
優點
1.提高資料檢索的效率,降低資料庫的IO成本
2.建立唯一索引,保證資料庫表中每一行資料的唯一性
3.在使用分組和排序子句進行資料查詢時,可以減少查詢中分組和排序的時間,降低CPU的消耗
缺點
1.建立索引和維護索引要耗費時間,並且隨著資料量增加,所耗費的時間也會增加。
2.索引需要磁碟空間,一般索引本身也很大,不可能全部儲存在記憶體中,因此索引往往以索引檔案的形式儲存在磁碟中。
3.降低更新表的速度,當對錶中的資料進行增加、刪除和修改時,索引也要動態維護,這樣就降低了資料的維護速度
索引可以提高查詢的速度,但是會影響插入記錄的速度。這種情況下,最好的辦法是先刪除表中的索引,然後插入資料,插入完成後再建立索引。
InnoDB的索引模型
一個簡單的設計方案
CREATE TABLE index_demo( c1 INT, c2 INT, c3 CHAR(1), PRIMARY KEY(c1) )ROW_FORMAT = Compact # 行格式表示每個記錄的格式ROW_FORMAT
Compact
record_type:
0 普通記錄
1 目錄項記錄
2 最小記錄
3 最大記錄
案例
假設每個資料頁最多存放3條記錄
INSER INTO index_demo VALUES(1,4,'u'),(5,3,'y'),(3,9,'d')
按主鍵從小到大的順序排列
此時資料頁10已經有三條記錄了,假設現在再插入一條記錄,那麼需要再分配一個數據頁
INSERT INTO index_deomo VALUES(4,4,'a')
假設現在需要查詢一條記錄,由於資料頁的編號可能是不連續的,所以查詢的時候可能需要每個資料頁都進去檢視一遍,效率是非常低的。
給每個頁建立一個目錄項
先在目錄項上查詢,然後再進入具體的資料頁查詢,這樣可以提高查詢效率
迭代一次:目錄項記錄的頁(目錄頁)
上述目錄項採用物理上連續的空間儲存(如陣列)
1.如果當目錄項個數很多時,可能並沒有足夠多的連續空間
2.當發生增刪改目錄項時,為了維持key有序,是非常麻煩的
目錄項也採用單項鍊表上連線,此時目錄項也構成了一個頁,稱為目錄頁
目錄項記錄和普通的使用者記錄的異同點
不同點 | 目錄項記錄 | 使用者記錄 |
---|---|---|
record_type | 1 | 0 |
儲存的內容 | 只有主鍵值和頁標號 | 使用者自定義的 |
min_rec_mask | 只有目錄頁中主鍵最小的目錄項記錄為1,其餘都為0 | 0 |
相同點
都會為主鍵值生成Page Directory頁目錄,目的是為了使用二分法來加快查詢速度。
假設要查詢主鍵為20的記錄
1.先再目錄頁通過二分法查詢頁目錄,12<20<209,定位到記錄在頁9
2.到頁9的頁目錄中採用二分法快速找到主鍵值為20的使用者記錄
只有2次IO操作
迭代2次:目錄項記錄的多個頁
假設一個目錄頁已經記錄完了,就需要再分配一個新的儲存目錄項記錄的頁
如果要查詢某個記錄,就需要先使用二分法在頁30的目錄頁查詢,如果沒找到需要去頁32的頁目錄查詢
迭代3次:目錄項記錄頁的目錄頁
如果需要查詢的記錄在目錄頁的很後面,那需要多次的IO訪問
由於頁是不連續的,為了快速定位,我們需要在套一層目錄。這樣可以穩定IO訪問次數
B+ 樹能夠很好地配合磁碟的讀寫特性,減少的磁碟I/O操作的次數。
這個資料結構就是B+樹
1.只有葉子節點儲存資料
2.每一個父節點的元素都出現在子元素中,是子節點的最大(小)元素
3.葉子節點之間通過連結串列連線,
假設一個數據頁可以存放100條使用者記錄,一個目錄頁可以存放1000條目錄項記錄
B+樹有一層,可以存放100條記錄
B+樹有兩層,可以存放1000*100 = 10,0000條資料
B+樹有三層,可以存放1000*1000*100=1,0000,0000條資料
....
一般情況下,用到的B+樹都不會超過4層
1.已經可以儲存相當多的資料了
2.層數越低,訪問IO的次數越少
聚簇索引
索引分為:
- 聚簇索引:由主鍵構建,聚簇表示記錄和目錄儲存在一起
- 非聚簇索引/二級索引/輔助索引:由非主鍵構建,非聚簇是不儲存真正的資料的
所有完整的使用者記錄(包括隱藏列)都存放在聚簇索引的葉子節點處。InnoDB中不需要顯示的使用INDEX語句去建立聚簇索引,InnoDB儲存引擎會自動為我們建立聚簇索引
優點
1.訪問速度更快
2.聚簇索引對主鍵的排序查詢和範圍查詢速度非常快
3.按照聚簇索引排列順序,查詢一定範圍資料的時候,由於資料都是緊密相連的,資料庫不用從多個數據塊中提取資料,所以節省了IO操作
缺點
1.插入速度嚴重依賴插入順序,對於InnoDB表,我們一般會定義自增的ID列為主鍵
2.更新主鍵的代價很高,因為會導致主鍵被更新後行移動,對於InnoDB,我們一般定義主鍵不可更新
3.二級索引訪問需要兩次索引查詢,第一次查詢主鍵值,第二次根據主鍵值查詢行資料
儘量使用自增主鍵
NOT NULL PRIMARY KEY AUTO_INCREMENT
每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的分裂。
如果使用業務邏輯的欄位做主鍵,則往往不容易保證有序插入,這樣寫資料成本相對較高。
限制
1.MySQL資料庫中只有InnoDB資料引擎支援聚簇索引,而MyISAM並不支援聚簇索引
2.由於資料物理儲存方式只有一種,所以每個MySQL的表只能有一個聚簇索引
3.如果沒有定義主鍵,Innodb會選擇非空的唯一索引代替,如果沒有,Innodb會隱式定義一個索引來作為聚簇索引
二級索引(輔助索引、非聚簇索引)
假設c2列為索引,則葉子節點記錄c2的值和對應的主鍵值
每個索引對應一個B+樹
假設需要找c2=4的記錄,先在這顆B+樹上找到對應的主鍵為1,在去主鍵B+樹去找主鍵為1的記錄,這個過程稱為回表。
聯合索引
聯合索引同時為多個列建立索引,假設我們想讓B+樹按照c2和c3列的大小排序。會先按照c2進行排序,如果c2相同會按照c3列進行排序
InnoDB的B+樹索引的注意事項
- 1 跟頁面位置萬年不動
- 2 內節點中目錄項記錄的唯一性
- 3 一個頁面最少儲存2條記錄
1 跟頁面位置萬年不動
為某個表建立一個B+樹索引(聚簇索引不是人為建立的,預設就有)的流程
1.為這個索引建立一個根節點頁面。最開始表中沒有資料的時候,每個B+樹索引對應的根節點中既沒有使用者記錄,也沒有目錄項記錄
2.向表中插入使用者記錄時,先把使用者記錄儲存到這個根節點中
3.當根節點中的空間用完時,繼續插入記錄,會將根節點的所有記錄複製到一個新分配的頁a,然後對新頁面進行頁分裂操作,得到一個新頁b。新記錄根據鍵值的大小會分配到頁a或頁b中,而根節點便升為目錄頁
4.當跟節點空間用完時,繼續插入記錄,會將跟節點的所有記錄複製到一個新分配的頁C,然後對新頁面進行頁分裂操作,得到一個新頁d,根節點便升為目錄頁的目錄頁
2 內節點中目錄項記錄的唯一性
主要針對二級索引
B+樹中目錄項記錄的內容是索引列+列號
,對於二級索引來說不嚴謹。假設表中的資料是
保證B+樹的同一層內節點的目錄項記錄(除頁號,頁號肯定是不一樣的)是唯一的
所以對於二級索引的內節點的目錄項記錄的內容是由三個部分構成
- 索引列的值
- 主鍵值,保證唯一性
- 頁號
二級索引的內節點的目錄項也保留了主鍵值
MyISAM與InnoDB的對比
主鍵索引和二級索引的結構都一樣
型別 | MyISAM | InnoDB |
---|---|---|
索引方式 | 非聚簇,無聚簇索引 | 包含1個聚簇索引,可以包含非聚簇索引 |
主鍵值查詢 | 相當於都是二級索引,所以會進行一次回表操作 主鍵的樹葉子節點沒有儲存記錄,儲存的是記錄的地址,去地址值找也算一次回表 |
主鍵值對聚簇索引進行一次查詢就能找到對應的記錄 |
資料檔案 | 索引檔案和資料檔案分離 索引檔案僅儲存資料記錄的地址 資料檔案按照新增順序,不需要排序 |
資料檔案本身就是索引檔案 |
回表操作 | 非常的快速,直接根據偏移量去檔案中取資料 | 速度慢一點 |
非聚簇索引data值 | 記錄的是地址 | 引用主鍵作為data域 |
主鍵 | 可以沒有 | 一定有 |
為什麼不適用Hash結構?
1.Hash索引僅滿足(=<>IN)查詢,進行範圍查詢的速度很慢,因為資料的儲存是無序的,如果在ORDER BY的情況下,還需要對資料重新排序
2.對應聯合索引,Hash值是將聯合索引鍵合併一起來計算,無法對單獨的一個鍵或幾個鍵進行查詢
3.對於等值查詢,通常Hash索引效率更高,但是查詢的Hash衝突的值過多時,效率回下降。
InnoDB本身不支援Hash索引,但是提供自適應Hash索引,如果某個資料經常被訪問,就會將這個資料頁的地址放到Hash表中,下次查詢時就可以直接找到這個頁面的所在位置。
採用自適應Hash索引可以根據SQL的查詢條件快速定位到葉子節點,尤其當B+樹比較深的時候,通過自適應Hash索引可以明顯提高資料的檢索效率。
通過innodb_adaptive_hash_index
變數查詢是否開啟自適應Hashshow variables like'%adaptive_hash_index'
,預設時開啟的