當前位置:首頁 » 數據倉庫 » 紅黑樹應用場景資料庫
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

紅黑樹應用場景資料庫

發布時間: 2022-09-25 05:45:30

A. 有沒有一種數據結構,查找,刪除和插入效率都比較高

數據結構需要根據具體應用場景來決定,效率比較高的推薦紅黑樹,查找、刪除、插入的時間復雜度都是O(lgn),紅黑樹是一種平衡的二叉樹,其樹高相比普通排序二叉樹更小,所以紅黑樹效率也比普通排序二叉樹高

B. 紅黑樹,b+樹分別用於什麼場景,為什麼

紅黑樹屬於「黑平衡」的二叉樹,雖然犧牲了一定的平衡性,但是add、remove操作要由優於AVL樹也就是說RB-Tree的「統計性能」更佳!Java中TreeSet,TreeMap的底層都是基於RedBlackTree紅黑樹的;

B+樹主要用在文件系統以及資料庫做索引。比如磁碟存儲、文件系統、MySQL資料庫

C. 資料庫索引的實現原理

資料庫索引的實現原理
一、概述資料庫索引,是資料庫管理系統中一個排序的數據結構,以協助快速查詢、更新資料庫表中數據。索引的實現通常使用B樹及其變種B+樹。在數據之外,資料庫系統還維護著滿足特定查找演算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找演算法。這種數據結構,就是索引。其實說穿了,索引問題就是一個查找問題。二、索引的原理當我們的業務產生了大量的數據時,查找數據的效率問題也就隨之而來,所以我們可以通過為表設置索引,而為表設置索引要付出代價的:一是增加了資料庫的存儲空間,二是在插入和修改數據時要花費較多的時間(因為索引也要隨之變動)。
上圖展示了一種可能的索引方式。左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁碟上也並不是一定物理相鄰的)。為了加快Col2的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在O(log2n)的復雜度內獲取到相應數據。索引是建立在資料庫表中的某些列的上面。在創建索引的時候,應該考慮在哪些列上可以創建索引,在哪些列上不能創建索引。一般來說,應該在這些列上創建索引:在經常需要搜索的列上,可以加快搜索的速度;在作為主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度;在經常需要根據范圍進行搜索的列上創建索引,因為索引已經排序,其指定的范圍是連續的;在經常需要排序的列上創建索引,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度。創建索引可以大大提高系統的性能第一,通過創建唯一性索引,可以保證資料庫表中每一行數據的唯一性。第二,可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。第三,可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。第四,在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。第五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。也許會有人要問:增加索引有如此多的優點,為什麼不對表中的每一個列創建一個索引呢?因為,增加索引也有許多不利的方面。創建索引的弊端第一,創建索引和維護索引要耗費時間,這種時間隨著數據量的增加而增加。第二,索引需要佔物理空間,除了數據表占數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。第三,當對表中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。同樣,對於有些列不應該創建索引。一般來說,不應該創建索引的的這些列具有下列特點:第一,對於那些在查詢中很少使用或者參考的列不應該創建索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。第二,對於那些只有很少數據值的列也不應該增加索引。這是因為,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的數據行佔了表中數據行的很大比例,即需要在表中搜索的數據行的比例很大。增加索引,並不能明顯加快檢索速度。第三,對於那些定義為text, image和bit數據類型的列不應該增加索引。這是因為,這些列的數據量要麼相當大,要麼取值很少。第四,當修改性能遠遠大於檢索性能時,不應該創建索引。這是因為,修改性能和檢索性能是互相矛盾的。當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大於檢索性能時,不應該創建索引。三、索引的類型根據資料庫的功能,可以在資料庫設計器中創建三種索引:唯一索引、主鍵索引和聚集索引。唯一索引唯一索引是不允許其中任何兩行具有相同索引值的索引。當現有數據中存在重復的鍵值時,大多數資料庫不允許將新創建的唯一索引與表一起保存。資料庫還可能防止添加將在表中創建重復鍵值的新數據。例如,如果在employee表中職員的姓(lname)上創建了唯一索引,則任何兩個員工都不能同姓。主鍵索引資料庫表經常有一列或列組合,其值唯一標識表中的每一行。該列稱為表的主鍵。在資料庫關系圖中為表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。聚集索引在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引。如果某索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度。四、局部性原理與磁碟預讀由於存儲介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。程序運行期間所需要的數據通常比較集中。由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有局部性的程序來說,預讀可以提高I/O效率。預讀的長度一般為頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬體及操作系統往往將主存和磁碟存儲區分割為連續的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤信號,磁碟會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,然後異常返回,程序繼續運行。五、B樹和B+樹數據結構1、B樹B樹中每個節點包含了鍵值和鍵值對於的數據對象存放地址指針,所以成功搜索一個對象可以不用到達樹的葉節點。成功搜索包括節點內搜索和沿某一路徑的搜索,成功搜索時間取決於關鍵碼所在的層次以及節點內關鍵碼的數量。在B樹中查找給定關鍵字的方法是:首先把根結點取來,在根結點所包含的關鍵字K1,…,kj查找給定的關鍵字(可用順序查找或二分查找法),若找到等於給定值的關鍵字,則查找成功;否則,一定可以確定要查的關鍵字在某個Ki或Ki+1之間,於是取Pi所指的下一層索引節點塊繼續查找,直到找到,或指針Pi為空時查找失敗。2、B+樹B+樹非葉節點中存放的關鍵碼並不指示數據對象的地址指針,非也節點只是索引部分。所有的葉節點在同一層上,包含了全部關鍵碼和相應數據對象的存放地址指針,且葉節點按關鍵碼從小到大順序鏈接。如果實際數據對象按加入的順序存儲而不是按關鍵碼次數存儲的話,葉節點的索引必須是稠密索引,若實際數據存儲按關鍵碼次序存放的話,葉節點索引時稀疏索引。B+樹有2個頭指針,一個是樹的根節點,一個是最小關鍵碼的葉節點。所以 B+樹有兩種搜索方法:一種是按葉節點自己拉起的鏈表順序搜索。一種是從根節點開始搜索,和B樹類似,不過如果非葉節點的關鍵碼等於給定值,搜索並不停止,而是繼續沿右指針,一直查到葉節點上的關鍵碼。所以無論搜索是否成功,都將走完樹的所有層。B+ 樹中,數據對象的插入和刪除僅在葉節點上進行。這兩種處理索引的數據結構的不同之處:1、B樹中同一鍵值不會出現多次,並且它有可能出現在葉結點,也有可能出現在非葉結點中。而B+樹的鍵一定會出現在葉結點中,並且有可能在非葉結點中也有可能重復出現,以維持B+樹的平衡。2、因為B樹鍵位置不定,且在整個樹結構中只出現一次,雖然可以節省存儲空間,但使得在插入、刪除操作復雜度明顯增加。B+樹相比來說是一種較好的折中。3、B樹的查詢效率與鍵在樹中的位置有關,最大時間復雜度與B+樹相同(在葉結點的時候),最小時間復雜度為1(在根結點的時候)。而B+樹的時候復雜度對某建成的樹是固定的。六、B/+Tree索引的性能分析到這里終於可以分析B-/+Tree索引的性能了。上文說過一般使用磁碟I/O次數評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多需要訪問h個節點。資料庫系統的設計者巧妙利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。B-Tree中一次檢索最多需要h-1次I/O(根節點常駐內存),漸進復雜度為O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進復雜度也為O(h),效率明顯比B-Tree差很多。綜上所述,用B-Tree作為索引結構效率是非常高的。

D. arraylist,linkedlist,hashset等在什麼場景下適用

1、List,Set都是繼承自Collection介面,Map則不是

2、List特點:元素有放入順序,元素可重復 ,Set特點:元素無放入順序,元素不可重復,重復元素會覆蓋掉,(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的,加入Set 的Object必須定義equals()方法 ,另外list支持for循環,也就是通過下標來遍歷,也可以用迭代器,但是set只能用迭代,因為他無序,無法用下標來取得想要的值。)

3.Set和List對比:
Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。
List:和數組類似,List可以動態增長,查找元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。

4.Map適合儲存鍵值對的數據

5.線程安全集合類與非線程安全集合類

LinkedList、ArrayList、HashSet是非線程安全的,Vector是線程安全的;
HashMap是非線程安全的,HashTable是線程安全的;
StringBuilder是非線程安全的,StringBuffer是線程安全的。

下面是具體的使用介紹:

ArrayList與LinkedList的區別和適用場景

Arraylist:

優點:ArrayList是實現了基於動態數組的數據結構,因為地址連續,一旦數據存儲好了,查詢操作效率會比較高(在內存里是連著放的)。

缺點:因為地址連續, ArrayList要移動數據,所以插入和刪除操作效率比較低。

LinkedList:

優點:LinkedList基於鏈表的數據結構,地址是任意的,所以在開辟內存空間的時候不需要等一個連續的地址,對於新增和刪除操作add和remove,LinedList比較占優勢。LinkedList
適用於要頭尾操作或插入指定位置的場景

缺點:因為LinkedList要移動指針,所以查詢操作性能比較低。

適用場景分析:

當需要對數據進行對此訪問的情況下選用ArrayList,當需要對數據進行多次增加刪除修改時採用LinkedList。

ArrayList與Vector的區別和適用場景

ArrayList有三個構造方法:

Java代碼

public ArrayList(int initialCapacity)//構造一個具有指定初始容量的空列表。
public ArrayList()//構造一個初始容量為10的空列表。
public ArrayList(Collection<? extends E> c)//構造一個包含指定 collection 的元素的列表

Vector有四個構造方法:

Java代碼

public Vector()//使用指定的初始容量和等於零的容量增量構造一個空向量。
public Vector(int initialCapacity)//構造一個空向量,使其內部數據數組的大小,其標准容量增量為零。
public Vector(Collection<? extends E> c)//構造一個包含指定 collection 中的元素的向量
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量構造一個空的向量

ArrayList和Vector都是用數組實現的,主要有這么三個區別:

1.Vector是多線程安全的,線程安全就是說多線程訪問同一代碼,不會產生不確定的結果。而ArrayList不是,這個可以從源碼中看出,Vector類中的方法很多有synchronized進行修飾,這樣就導致了Vector在效率上無法與ArrayList相比;

2.兩個都是採用的線性連續空間存儲元素,但是當空間不足的時候,兩個類的增加方式是不同。

3.Vector可以設置增長因子,而ArrayList不可以。

4.Vector是一種老的動態數組,是線程同步的,效率很低,一般不贊成使用。

適用場景分析:

1.Vector是線程同步的,所以它也是線程安全的,而ArrayList是線程非同步的,是不安全的。如果不考慮到線程的安全因素,一般用ArrayList效率比較高。
2.如果集合中的元素的數目大於目前集合數組的長度時,在集合中使用數據量比較大的數據,用Vector有一定的優勢。

HashSet與Treeset的適用場景

1.TreeSet 是二差樹(紅黑樹的樹據結構)實現的,Treeset中的數據是自動排好序的,不允許放入null值

2.HashSet 是哈希表實現的,HashSet中的數據是無序的,可以放入null,但只能放入一個null,兩者中的值都不能重復,就如資料庫中唯一約束

3.HashSet要求放入的對象必須實現HashCode()方法,放入的對象,是以hashcode碼作為標識的,而具有相同內容的String對象,hashcode是一樣,所以放入的內容不能重復。但是同一個類的對象可以放入不同的實例

適用場景分析:HashSet是基於Hash演算法實現的,其性能通常都優於TreeSet。為快速查找而設計的Set,我們通常都應該使用HashSet,在我們需要排序的功能時,我們才使用TreeSet。

HashMap與TreeMap、HashTable的區別及適用場景

HashMap 非線程安全

HashMap:基於哈希表實現。使用HashMap要求添加的鍵類明確定義了hashCode()和equals()[可以重寫hashCode()和equals()],為了優化HashMap空間的使用,您可以調優初始容量和負載因子。

TreeMap:非線程安全基於紅黑樹實現。TreeMap沒有調優選項,因為該樹總處於平衡狀態。

適用場景分析:

HashMap和HashTable:HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允許空鍵值,而HashTable不允許。

HashMap:適用於Map中插入、刪除和定位元素。

Treemap:適用於按自然順序或自定義順序遍歷鍵(key)。

E. 面試 linux 文件系統怎樣io到底層

前言:本文主要講解LinuxIO調度層的三種模式:cfp、deadline和noop,並給出各自的優化和適用場景建議。IO調度發生在Linux內核的IO調度層。這個層次是針對Linux的整體IO層次體系來說的。從read()或者write()系統調用的角度來說,Linux整體IO體系可以分為七層,它們分別是:VFS層:虛擬文件系統層。由於內核要跟多種文件系統打交道,而每一種文件系統所實現的數據結構和相關方法都可能不盡相同,所以,內核抽象了這一層,專門用來適配各種文件系統,並對外提供統一操作介面。文件系統層:不同的文件系統實現自己的操作過程,提供自己特有的特徵,具體不多說了,大家願意的話自己去看代碼即可。頁緩存層:負責真對page的緩存。通用塊層:由於絕大多數情況的io操作是跟塊設備打交道,所以Linux在此提供了一個類似vfs層的塊設備操作抽象層。下層對接各種不同屬性的塊設備,對上提供統一的BlockIO請求標准。IO調度層:因為絕大多數的塊設備都是類似磁碟這樣的設備,所以有必要根據這類設備的特點以及應用的不同特點來設置一些不同的調度演算法和隊列。以便在不同的應用環境下有針對性的提高磁碟的讀寫效率,這里就是大名鼎鼎的Linux電梯所起作用的地方。針對機械硬碟的各種調度方法就是在這實現的。塊設備驅動層:驅動層對外提供相對比較高級的設備操作介面,往往是C語言的,而下層對接設備本身的操作方法和規范。塊設備層:這層就是具體的物理設備了,定義了各種真對設備操作方法和規范。有一個已經整理好的[LinuxIO結構圖],非常經典,一圖勝千言:我們今天要研究的內容主要在IO調度這一層。它要解決的核心問題是,如何提高塊設備IO的整體性能?這一層也主要是針對機械硬碟結構而設計的。眾所周知,機械硬碟的存儲介質是磁碟,磁頭在碟片上移動進行磁軌定址,行為類似播放一張唱片。這種結構的特點是,順序訪問時吞吐量較高,但是如果一旦對碟片有隨機訪問,那麼大量的時間都會浪費在磁頭的移動上,這時候就會導致每次IO的響應時間變長,極大的降低IO的響應速度。磁頭在碟片上尋道的操作,類似電梯調度,實際上在最開始的時期,Linux把這個演算法命名為Linux電梯演算法,即:如果在尋道的過程中,能把順序路過的相關磁軌的數據請求都「順便」處理掉,那麼就可以在比較小影響響應速度的前提下,提高整體IO的吞吐量。這就是我們為什麼要設計IO調度演算法的原因。目前在內核中默認開啟了三種演算法/模式:noop,cfq和deadline。嚴格算應該是兩種:因為第一種叫做noop,就是空操作調度演算法,也就是沒有任何調度操作,並不對io請求進行排序,僅僅做適當的io合並的一個fifo隊列。目前內核中默認的調度演算法應該是cfq,叫做完全公平隊列調度。這個調度演算法人如其名,它試圖給所有進程提供一個完全公平的IO操作環境。註:請大家一定記住這個詞語,cfq,完全公平隊列調度,不然下文就沒法看了。cfq為每個進程創建一個同步IO調度隊列,並默認以時間片和請求數限定的方式分配IO資源,以此保證每個進程的IO資源佔用是公平的,cfq還實現了針對進程級別的優先順序調度,這個我們後面會詳細解釋。查看和修改IO調度演算法的方法是:cfq是通用伺服器比較好的IO調度演算法選擇,對桌面用戶也是比較好的選擇。但是對於很多IO壓力較大的場景就並不是很適應,尤其是IO壓力集中在某些進程上的場景。因為這種場景我們需要的滿足某個或者某幾個進程的IO響應速度,而不是讓所有的進程公平的使用IO,比如資料庫應用。deadline調度(最終期限調度)就是更適合上述場景的解決方案。deadline實現了四個隊列:其中兩個分別處理正常read和write,按扇區號排序,進行正常io的合並處理以提高吞吐量。因為IO請求可能會集中在某些磁碟位置,這樣會導致新來的請求一直被合並,可能會有其他磁碟位置的io請求被餓死。另外兩個處理超時read和write的隊列,按請求創建時間排序,如果有超時的請求出現,就放進這兩個隊列,調度演算法保證超時(達到最終期限時間)的隊列中的請求會優先被處理,防止請求被餓死。不久前,內核還是默認標配四種演算法,還有一種叫做as的演算法(Anticipatoryscheler),預測調度演算法。一個高大上的名字,搞得我一度認為Linux內核都會算命了。結果發現,無非是在基於deadline演算法做io調度的之前等一小會時間,如果這段時間內有可以合並的io請求到來,就可以合並處理,提高deadline調度的在順序讀寫情況下的數據吞吐量。其實這根本不是啥預測,我覺得不如叫撞大運調度演算法,當然這種策略在某些特定場景差效果不錯。但是在大多數場景下,這個調度不僅沒有提高吞吐量,還降低了響應速度,所以內核乾脆把它從默認配置里刪除了。畢竟Linux的宗旨是實用,而我們也就不再這個調度演算法上多費口舌了。1、cfq:完全公平隊列調度cfq是內核默認選擇的IO調度隊列,它在桌面應用場景以及大多數常見應用場景下都是很好的選擇。如何實現一個所謂的完全公平隊列(CompletelyFairQueueing)?首先我們要理解所謂的公平是對誰的公平?從操作系統的角度來說,產生操作行為的主體都是進程,所以這里的公平是針對每個進程而言的,我們要試圖讓進程可以公平的佔用IO資源。那麼如何讓進程公平的佔用IO資源?我們需要先理解什麼是IO資源。當我們衡量一個IO資源的時候,一般喜歡用的是兩個單位,一個是數據讀寫的帶寬,另一個是數據讀寫的IOPS。帶寬就是以時間為單位的讀寫數據量,比如,100Mbyte/s。而IOPS是以時間為單位的讀寫次數。在不同的讀寫情境下,這兩個單位的表現可能不一樣,但是可以確定的是,兩個單位的任何一個達到了性能上限,都會成為IO的瓶頸。從機械硬碟的結構考慮,如果讀寫是順序讀寫,那麼IO的表現是可以通過比較少的IOPS達到較大的帶寬,因為可以合並很多IO,也可以通過預讀等方式加速數據讀取效率。當IO的表現是偏向於隨機讀寫的時候,那麼IOPS就會變得更大,IO的請求的合並可能性下降,當每次io請求數據越少的時候,帶寬表現就會越低。從這里我們可以理解,針對進程的IO資源的主要表現形式有兩個:進程在單位時間內提交的IO請求個數和進程佔用IO的帶寬。其實無論哪個,都是跟進程分配的IO處理時間長度緊密相關的。有時業務可以在較少IOPS的情況下佔用較大帶寬,另外一些則可能在較大IOPS的情況下佔用較少帶寬,所以對進程佔用IO的時間進行調度才是相對最公平的。即,我不管你是IOPS高還是帶寬佔用高,到了時間咱就換下一個進程處理,你愛咋樣咋樣。所以,cfq就是試圖給所有進程分配等同的塊設備使用的時間片,進程在時間片內,可以將產生的IO請求提交給塊設備進行處理,時間片結束,進程的請求將排進它自己的隊列,等待下次調度的時候進行處理。這就是cfq的基本原理。當然,現實生活中不可能有真正的「公平」,常見的應用場景下,我們很肯能需要人為的對進程的IO佔用進行人為指定優先順序,這就像對進程的CPU佔用設置優先順序的概念一樣。所以,除了針對時間片進行公平隊列調度外,cfq還提供了優先順序支持。每個進程都可以設置一個IO優先順序,cfq會根據這個優先順序的設置情況作為調度時的重要參考因素。優先順序首先分成三大類:RT、BE、IDLE,它們分別是實時(RealTime)、最佳效果(BestTry)和閑置(Idle)三個類別,對每個類別的IO,cfq都使用不同的策略進行處理。另外,RT和BE類別中,分別又再劃分了8個子優先順序實現更細節的QOS需求,而IDLE只有一個子優先順序。另外,我們都知道內核默認對存儲的讀寫都是經過緩存(buffer/cache)的,在這種情況下,cfq是無法區分當前處理的請求是來自哪一個進程的。只有在進程使用同步方式(syncread或者syncwirte)或者直接IO(DirectIO)方式進行讀寫的時候,cfq才能區分出IO請求來自哪個進程。所以,除了針對每個進程實現的IO隊列以外,還實現了一個公共的隊列用來處理非同步請求。當前內核已經實現了針對IO資源的cgroup資源隔離,所以在以上體系的基礎上,cfq也實現了針對cgroup的調度支持。總的來說,cfq用了一系列的數據結構實現了以上所有復雜功能的支持,大家可以通過源代碼看到其相關實現,文件在源代碼目錄下的block/cfq-iosched.c。1.1cfq設計原理在此,我們對整體數據結構做一個簡要描述:首先,cfq通過一個叫做cfq_data的數據結構維護了整個調度器流程。在一個支持了cgroup功能的cfq中,全部進程被分成了若干個contralgroup進行管理。每個cgroup在cfq中都有一個cfq_group的結構進行描述,所有的cgroup都被作為一個調度對象放進一個紅黑樹中,並以vdisktime為key進行排序。vdisktime這個時間紀錄的是當前cgroup所佔用的io時間,每次對cgroup進行調度時,總是通過紅黑樹選擇當前vdisktime時間最少的cgroup進行處理,以保證所有cgroups之間的IO資源佔用「公平」。當然我們知道,cgroup是可以對blkio進行資源比例分配的,其作用原理就是,分配比例大的cgroup佔用vdisktime時間增長較慢,分配比例小的vdisktime時間增長較快,快慢與分配比例成正比。這樣就做到了不同的cgroup分配的IO比例不一樣,並且在cfq的角度看來依然是「公平「的。選擇好了需要處理的cgroup(cfq_group)之後,調度器需要決策選擇下一步的service_tree。service_tree這個數據結構對應的都是一系列的紅黑樹,主要目的是用來實現請求優先順序分類的,就是RT、BE、IDLE的分類。每一個cfq_group都維護了7個service_trees,其定義如下:其中service_tree_idle就是用來給IDLE類型的請求進行排隊用的紅黑樹。而上面二維數組,首先第一個維度針對RT和BE分別各實現了一個數組,每一個數組中都維護了三個紅黑樹,分別對應三種不同子類型的請求,分別是:SYNC、SYNC_NOIDLE以及ASYNC。我們可以認為SYNC相當於SYNC_IDLE並與SYNC_NOIDLE對應。idling是cfq在設計上為了盡量合並連續的IO請求以達到提高吞吐量的目的而加入的機制,我們可以理解為是一種「空轉」等待機制。空轉是指,當一個隊列處理一個請求結束後,會在發生調度之前空等一小會時間,如果下一個請求到來,則可以減少磁頭定址,繼續處理順序的IO請求。為了實現這個功能,cfq在service_tree這層數據結構這實現了SYNC隊列,如果請求是同步順序請求,就入隊這個servicetree,如果請求是同步隨機請求,則入隊SYNC_NOIDLE隊列,以判斷下一個請求是否是順序請求。所有的非同步寫操作請求將入隊ASYNC的servicetree,並且針對這個隊列沒有空轉等待機制。此外,cfq還對SSD這樣的硬碟有特殊調整,當cfq發現存儲設備是一個ssd硬碟這樣的隊列深度更大的設備時,所有針對單獨隊列的空轉都將不生效,所有的IO請求都將入隊SYNC_NOIDLE這個servicetree。每一個servicetree都對應了若干個cfq_queue隊列,每個cfq_queue隊列對應一個進程,這個我們後續再詳細說明。cfq_group還維護了一個在cgroup內部所有進程公用的非同步IO請求隊列,其結構如下:非同步請求也分成了RT、BE、IDLE這三類進行處理,每一類對應一個cfq_queue進行排隊。BE和RT也實現了優先順序的支持,每一個類型有IOPRIO_BE_NR這么多個優先順序,這個值定義為8,數組下標為0-7。我們目前分析的內核代碼版本為Linux4.4,可以看出,從cfq的角度來說,已經可以實現非同步IO的cgroup支持了,我們需要定義一下這里所謂非同步IO的含義,它僅僅表示從內存的buffer/cache中的數據同步到硬碟的IO請求,而不是aio(man7aio)或者linux的native非同步io以及lio機制,實際上這些所謂的「非同步」IO機制,在內核中都是同步實現的(本質上馮諾伊曼計算機沒有真正的「非同步」機制)。我們在上面已經說明過,由於進程正常情況下都是將數據先寫入buffer/cache,所以這種非同步IO都是統一由cfq_group中的async請求隊列處理的。那麼為什麼在上面的service_tree中還要實現和一個ASYNC的類型呢?這當然是為了支持區分進程的非同步IO並使之可以「完全公平」做准備嘍。實際上在最新的cgroupv2的blkio體系中,內核已經支持了針對bufferIO的cgroup限速支持,而以上這些可能容易混淆的一堆類型,都是在新的體系下需要用到的類型標記。新體系的復雜度更高了,功能也更加強大,但是大家先不要著急,正式的cgroupv2體系,在Linux4.5發布的時候會正式跟大家見面。我們繼續選擇service_tree的過程,三種優先順序類型的service_tree的選擇就是根據類型的優先順序來做選擇的,RT優先順序最高,BE其次,IDLE最低。就是說,RT里有,就會一直處理RT,RT沒了再處理BE。每個service_tree對應一個元素為cfq_queue排隊的紅黑樹,而每個cfq_queue就是內核為進程(線程)創建的請求隊列。每一個cfq_queue都會維護一個rb_key的變數,這個變數實際上就是這個隊列的IO服務時間(servicetime)。這里還是通過紅黑樹找到servicetime時間最短的那個cfq_queue進行服務,以保證「完全公平」。選擇好了cfq_queue之後,就要開始處理這個隊列里的IO請求了。這里的調度方式基本跟deadline類似。cfq_queue會對進入隊列的每一個請求進行兩次入隊,一個放進fifo中,另一個放進按訪問扇區順序作為key的紅黑樹中。默認從紅黑樹中取請求進行處理,當請求的延時時間達到deadline時,就從紅黑樹中取等待時間最長的進行處理,以保證請求不被餓死。這就是整個cfq的調度流程,當然其中還有很多細枝末節沒有交代,比如合並處理以及順序處理等等。1.2cfq的參數調整理解整個調度流程有助於我們決策如何調整cfq的相關參數。所有cfq的可調參數都可以在/sys/class/block/sda/queue/iosched/目錄下找到,當然,在你的系統上,請將sda替換為相應的磁碟名稱。我們來看一下都有什麼:這些參數部分是跟機械硬碟磁頭尋道方式有關的,如果其說明你看不懂,請先補充相關知識:back_seek_max:磁頭可以向後定址的最大范圍,默認值為16M。back_seek_penalty:向後定址的懲罰系數。這個值是跟向前定址進行比較的。以上兩個是為了防止磁頭尋道發生抖動而導致定址過慢而設置的。基本思路是這樣,一個io請求到來的時候,cfq會根據其定址位置預估一下其磁頭尋道成本。設置一個最大值back_seek_max,對於請求所訪問的扇區號在磁頭後方的請求,只要定址范圍沒有超過這個值,cfq會像向前定址的請求一樣處理它。再設置一個評估成本的系數back_seek_penalty,相對於磁頭向前定址,向後定址的距離為1/2(1/back_seek_penalty)時,cfq認為這兩個請求定址的代價是相同。這兩個參數實際上是cfq判斷請求合並處理的條件限制,凡事復合這個條件的請求,都會盡量在本次請求處理的時候一起合並處理。fifo_expire_async:設置非同步請求的超時時間。同步請求和非同步請求是區分不同隊列處理的,cfq在調度的時候一般情況都會優先處理同步請求,之後再處理非同步請求,除非非同步請求符合上述合並處理的條件限制范圍內。當本進程的隊列被調度時,cfq會優先檢查是否有非同步請求超時,就是超過fifo_expire_async參數的限制。如果有,則優先發送一個超時的請求,其餘請求仍然按照優先順序以及扇區編號大小來處理。fifo_expire_sync:這個參數跟上面的類似,區別是用來設置同步請求的超時時間。slice_idle:參數設置了一個等待時間。這讓cfq在切換cfq_queue或servicetree的時候等待一段時間,目的是提高機械硬碟的吞吐量。一般情況下,來自同一個cfq_queue或者servicetree的IO請求的定址局部性更好,所以這樣可以減少磁碟的定址次數。這個值在機械硬碟上默認為非零。當然在固態硬碟或者硬RAID設備上設置這個值為非零會降低存儲的效率,因為固態硬碟沒有磁頭定址這個概念,所以在這樣的設備上應該設置為0,關閉此功能。group_idle:這個參數也跟上一個參數類似,區別是當cfq要切換cfq_group的時候會等待一段時間。在cgroup的場景下,如果我們沿用slice_idle的方式,那麼空轉等待可能會在cgroup組內每個進程的cfq_queue切換時發生。這樣會如果這個進程一直有請求要處理的話,那麼直到這個cgroup的配額被耗盡,同組中的其它進程也可能無法被調度到。這樣會導致同組中的其它進程餓死而產生IO性能瓶頸。在這種情況下,我們可以將slice_idle=0而group_idle=8。這樣空轉等待就是以cgroup為單位進行的,而不是以cfq_queue的進程為單位進行,以防止上述問題產生。low_latency:這個是用來開啟或關閉cfq的低延時(lowlatency)模式的開關。當這個開關打開時,cfq將會根據target_latency的參數設置來對每一個進程的分片時間(slicetime)進行重新計算。這將有利於對吞吐量的公平(默認是對時間片分配的公平)。關閉這個參數(設置為0)將忽略target_latency的值。這將使系統中的進程完全按照時間片方式進行IO資源分配。這個開關默認是打開的。我們已經知道cfq設計上有「空轉」(idling)這個概念,目的是為了可以讓連續的讀寫操作盡可能多的合並處理,減少磁頭的定址操作以便增大吞吐量。如果有進程總是很快的進行順序讀寫,那麼它將因為cfq的空轉等待命中率很高而導致其它需要處理IO的進程響應速度下降,如果另一個需要調度的進程不會發出大量順序IO行為的話,系統中不同進程IO吞吐量的表現就會很不均衡。就比如,系統內存的cache中有很多臟頁要寫回時,桌面又要打開一個瀏覽器進行操作,這時臟頁寫回的後台行為就很可能會大量命中空轉時間,而導致瀏覽器的小量IO一直等待,讓用戶感覺瀏覽器運行響應速度變慢。這個low_latency主要是對這種情況進行優化的選項,當其打開時,系統會根據target_latency的配置對因為命中空轉而大量佔用IO吞吐量的進程進行限制,以達到不同進程IO佔用的吞吐量的相對均衡。這個開關比較合適在類似桌面應用的場景下打開。target_latency:當low_latency的值為開啟狀態時,cfq將根據這個值重新計算每個進程分配的IO時間片長度。quantum:這個參數用來設置每次從cfq_queue中處理多少個IO請求。在一個隊列處理事件周期中,超過這個數字的IO請求將不會被處理。這個參數只對同步的請求有效。slice_sync:當一個cfq_queue隊列被調度處理時,它可以被分配的處理總時間是通過這個值來作為一個計算參數指定的。公式為:time_slice=slice_sync+(slice_sync/5*(4-prio))。這個參數對同步請求有效。slice_async:這個值跟上一個類似,區別是對非同步請求有效。slice_async_rq:這個參數用來限制在一個slice的時間范圍內,一個隊列最多可以處理的非同步請求個數。請求被處理的最大個數還跟相關進程被設置的io優先順序有關。1.3cfq的IOPS模式我們已經知道,默認情況下cfq是以時間片方式支持的帶優先順序的調度來保證IO資源佔用的公平。高優先順序的進程將得到的時間片長度,而低優先順序的進程時間片相對較小。當我們的存儲是一個高速並且支持NCQ(原生指令隊列)的設備的時候,我們最好可以讓其可以從多個cfq隊列中處理多路的請求,以便提升NCQ的利用率。此時使用時間片的分配方式分配資源就顯得不合時宜了,因為基於時間片的分配,同一時刻最多能處理的請求隊列只有一個。這時,我們需要切換cfq的模式為IOPS模式。切換方式很簡單,就是將slice_idle=0即可。內核會自動檢測你的存儲設備是否支持NCQ,如果支持的話cfq會自動切換為IOPS模式。另外,在默認的基於優先順序的時間片方式下,我們可以使用ionice命令來調整進程的IO優先順序。進程默認分配的IO優先順序是根據進程的nice值計算而來的,計算方法可以在manionice中看到,這里不再廢話。2、deadline:最終期限調度deadline調度演算法相對cfq要簡單很多。其設計目標是:在保證請求按照設備扇區的順序進行訪問的同時,兼顧其它請求不被餓死,要在一個最終期限前被調度到。我們知道磁頭對磁碟的尋道是可以進行順序訪問和隨機訪問的,因為尋道延時時間的關系,順序訪問時IO的吞吐量更大,隨機訪問的吞吐量小。如果我們想為一個機械硬碟進行吞吐量優化的話,那麼就可以讓調度器按照盡量復合順序訪問的IO請求進行排序,之後請求以這樣的順序發送給硬碟,就可以使IO的吞吐量更大。但是這樣做也有另一個問題,就是如果此時出現了一個請求,它要訪問的磁軌離目前磁頭所在磁軌很遠,應用的請求又大量集中在目前磁軌附近。導致大量請求一直會被合並和插隊處理,而那個要訪問比較遠磁軌的請求將因為一直不能被調度而餓死。deadline就是這樣一種調度器,能在保證IO最大吞吐量的情況下,盡量使遠端請求在一個期限內被調度而不被餓死的調度器。

F. 紅黑樹的用途

紅黑樹用在關聯數組、字典的實現上。需要的空間比散列表小。 任何鍵值對應,需要隨機存儲和鍵有序的情況都可以用。

G. 為什麼treeset使用紅黑樹而一些資料庫索引使用b樹和b+樹

為什麼treeset使用紅黑樹而一些資料庫索引使用b樹和b+樹
在C++
STL中,很多部分(目前包括set,
multiset,
map,
multimap)應用了紅黑樹的變體(SGI
STL中的紅黑樹有一些變化,這些修改提供了更好的性能,以及對set操作的支持)。紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。

H. 紅黑樹和平衡二叉樹 區別

紅黑樹和平衡二叉樹的主要區別如下:
平衡二叉樹(AVL)樹是嚴格的平衡二叉樹,平衡條件必須滿足(所有節點的左右子樹高度差不超過1)。不管我們是執行插入還是刪除操作,只要不滿足上面的條件,就要通過旋轉來保持平衡,而的英文旋轉非常耗時的。所以平衡二叉樹(AVL)適合用於插入與刪除次數比較少,但查找多的情況。
紅黑樹在二叉查找樹的基礎上增加了著色和相關的性質使得紅黑樹相對平衡,從而保證了紅黑樹的查找、插入、刪除的時間復雜度最壞為O(log
n)。所以紅黑樹適用於搜索,插入,刪除操作較多的情況。
(8)紅黑樹應用場景資料庫擴展閱讀:
平衡二叉樹在Windows
NT內核中廣泛存在。
紅黑樹的應用:
1、在C
++的STL中,地圖和集都是用紅黑樹實現的;
2、著名的Linux的的進程調度完全公平調度程序,用紅黑樹管理進程式控制制塊,進程的虛擬內存區域都存儲在一顆紅黑樹上,每個虛擬地址區域都對應紅黑樹的一個節點,左指針指向相鄰的地址虛擬存儲區域,右指針指向相鄰的高地址虛擬地址空間;
3、IO多路復用的epoll的的的實現採用紅黑樹組織管理的sockfd,以支持快速的增刪改查;
4、Nginx的的的中用紅黑樹管理定時器,因為紅黑樹是有序的,可以很快的得到距離當前最小的定時器;
5、Java中的TreeMap中的實現。
參考資料:
網路-平衡二叉樹
網路-紅黑樹

I. AVL樹,紅黑樹,B樹,B+樹,Trie樹都分別應用在哪些現實場景中

在C++ STL中,很多部分(目前包括set, multiset, map, multimap)應用了紅黑樹的變體(SGI STL中的紅黑樹有一些變化,這些修改提供了更好的性能,以及對set操作的支持)。紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。在二叉查找樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求: 性質1. 節點是紅色或黑色。 性質2. 根是黑色。 性質3 每個葉節點是黑色的。 性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點) 性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。 An example of a red-black tree 這些約束強制了紅黑樹的關鍵性質: 從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長。結果是這個樹大致上是平衡的。因為操作比如插入、刪除和查找某個值的最壞情況時間都要求與樹的高度成比例,這個在高度上的理論上限允許紅黑樹在最壞情況下都是高效的,而不同於普通的二叉查找樹。 要知道為什麼這些特性確保了這個結果,注意到屬性5導致了路徑不能有兩個毗連的紅色節點就足夠了。最短的可能路徑都是黑色節點,最長的可能路徑有交替的紅色和黑色節點。因為根據屬性4所有最長的路徑都有相同數目的黑色節點,這就表明了沒有路徑能多於任何其他路徑的兩倍長。 在很多樹數據結構的表示中,一個節點有可能只有一個子節點,而葉子節點包含數據。用這種範例表示紅黑樹是可能的,但是這會改變一些屬性並使演算法復雜。為此,本文中我們使用 "nil 葉子" 或"空(null)葉子",如上圖所示,它不包含數據而只充當樹在此結束的指示。這些節點在繪圖中經常被省略,導致了這些樹好象同上述原則相矛盾,而實際上不是這樣。與此有關的結論是所有節點都有兩個子節點,盡管其中的一個或兩個可能是空葉子。

J. java中哪些數據結構使用了紅黑樹

參考資料的網頁上有比較的代碼,你可以仔細看下~~~

java中HashMap,LinkedHashMap,TreeMap,HashTable的區別
java為數據結構中的映射定義了一個介面java.util.Map;它有四個實現類,分別是HashMap Hashtable LinkedHashMap 和TreeMap
Map主要用於存儲健值對,根據鍵得到值,因此不允許鍵重復(重復了覆蓋了),但允許值重復。
Hashmap 是一個最常用的Map,它根據鍵的HashCode 值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度,遍歷時,取得數據的順序是完全隨機的。HashMap最多隻允許一條記錄的鍵為Null;允許多條記錄的值為 Null;HashMap不支持線程的同步,即任一時刻可以有多個線程同時寫HashMap;可能會導致數據的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
Hashtable與 HashMap類似,它繼承自Dictionary類,不同的是:它不允許記錄的鍵或者值為空;它支持線程的同步,即任一時刻只有一個線程能寫Hashtable,因此也導致了 Hashtable在寫入時會比較慢。
LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的.也可以在構造時用帶參數,按照應用次數排序。在遍歷的時候會比HashMap慢,不過有種情況例外,當HashMap容量很大,實際數據較少時,遍歷起來可能會比LinkedHashMap慢,因為LinkedHashMap的遍歷速度只和實際數據有關,和容量無關,而HashMap的遍歷速度和他的容量有關。
TreeMap實現SortMap介面,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator 遍歷TreeMap時,得到的記錄是排過序的。

一般情況下,我們用的最多的是HashMap,HashMap裡面存入的鍵值對在取出的時候是隨機的,它根據鍵的HashCode值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。
TreeMap取出來的是排序後的鍵值對。但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好。
LinkedHashMap 是HashMap的一個子類,如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現,它還可以按讀取順序來排列,像連接池中可以應用。