Tổ chức file chỉ mục tuần tự có một nhược điếm chính là làm giảm hiệu năng khi file lớn lên. Để khắc phục nhược điểm đó đòi hỏi phải tổ chức lại file. Cấu trúc chỉ mục B+-cây là cấu trúc được sử dụng rộng rãi nhất trong các cấu trúc đảm bảo được tính hiệu quả của chúng bất chấp các hoạt động xen, xoá. Chỉ mục B+-cây là một dạng cây cân bằng (mọi đường dẫn từ gốc đến lá có cùng độ dài). Mỗi nút không là lá có số con nằm trong khoảng giữa m/2 và m, trong đó m là một số cố định được gọi là bậc của B+-cây. Ta thấy rằng cấu trúc B+-cây cũng đòi hỏi một tổn phí hiệu năng trên xen và xoá cũng như trên không gian. Tuy nhiên, tổn phí này là chấp nhận được ngay cả đối với các file có tần suất sửa đổi cao.
Cấu trúc của B+-cây
Một chỉ mục B+-cây là một chỉ mục nhiều mức, nhưng có cấu trúc khác với file tuần tự chỉ mục nhiều mức (multilevel index-sequential). Một nút tiêu biểu của B+-cây chứa đến n-1 giá trị khoá tìm kiếm. K1, K2, ..., Kn-1, và n con trỏ P1, P2, ..., Pn, các giá trị khoá trong nút được sắp thứ tự: i < j Ki < Kj.
| P1 |
K1 |
P2 |
K2 |
. . . |
Pn-1 |
Kn-1 |
Pn |
Trước tiên, ta xét cấu trúc của nút lá. Đối với i = 1, 2, ..., n-1, con trỏ Pi trỏ tới hoặc mẩu tin với giá trị khoá Ki hoặc tới một bucket các con trỏ mà mỗi một trong chúng trỏ tới một mẩu tin với
giá trị khoá Ki. Cấu trúc bucket chỉ được sử dụng trong các trường hợp: hoặc khoá tìm kiếm không là khoá sơ cấp hoặc file không được sắp theo khoá tìm kiếm. Con trỏ Pn được dùng vào mục đích đặc biệt: Pn được dùng để móc xích các nút lá lại theo thứ tự khoá tìm kiếm, điều này cho phép xử lý tuần tự file hiệu quả. Bây giờ ta xem các giá trị khoá tìm kiếm được gắn với một nút lá như thế nào. Mỗi nút nút lá có thể chứa đến n-1 giá trị. Khoảng giá trị mà mỗi nút lá chứa là không chồng chéo. Như vậy, nếu Li và Lj là hai nút lá với i < j thì mỗi giá trị khoá trong nút Li nhỏ hơn mọi giá trị khoá trong Lj . Nếu chỉ mục B+-cây là đặc, mỗi giá trị khoá tìm kiếm phải xuất hiện trong một nút lá nào đó.
Các nút không là lá của một B+-cây tạo ra một chỉ mục nhiều mức trên các nút lá. Cấu trúc của các nút không là lá tương tự như cấu trúc nút lá ngoại trừ tất cả các con trỏ đều trỏ đến các nút của cây. Các nút không là lá có thể chứa đến m con trỏ và phải chứa không ít hơn m/2 con trỏ ngoại trừ nút gốc. Nút gốc được phép chứa ít nhất 2 con trỏ. Số con trỏ trong một nút được gọi là số nan (fanout) của nút.
Con trỏ Pi của một nút không là lá (chứa p con trỏ, 1 < i < p) trỏ đến một cây con chứa các giá trị khoá tìm kiếm nhỏ hơn Ki và lớn hơn hoặc bằng Ki-1. Con trỏ P1 trỏ đến cây con chứa các giá trị khoá tìm kiếm nhỏ hơn K1. Con trỏ Pp trỏ tới cây con chứa các khoá tìm kiếm lớn hơn Kp-1.
Cập nhật trên B+-cây
- Xen. Sử dụng cùng kỹ thuật như tìm kiếm, ta tìm nút lá trong đó giá trị khoá tìm kiếm cần xen sẽ xuất hiện. Nếu khoá tìm kiếm đã xuất hiện rồi trong nút lá, xen mẩu tin vào trong file, thêm con trỏ tới mẩu tin vào trong bucket tương ứng. Nếu khoá tìm kiếm chưa hiện diện trong nút lá, ta xen mẩu tin vào trong file rồi xen giá trị khoá tìm kiếm vào trong nút lá ở vị trí đúng (bảo tồn tính thứ tự), tạo một bucket mới với con trỏ tương ứng. Nếu nút lá không còn chỗ cho giá trị khoá mới, Một khối mới được yêu cầu từ hệ điều hành, các giá trị khoá trong nút lá được tách một nửa cho nút mới, giá trị khoá mới được xen vào vị trí đúng của nó vào một trong hai khối này. Điều này kéo theo việc xen giá trị khoá đầu khối mới và con trỏ tới khối mới vào nút cha. Việc xen cặp giá trị khoá và con trỏ vào nút cha này lại có thể dẫn đến việc tách nút ra làm hai. Quá trình này có thể dẫn đến tận nút gốc. Trong trường hợp nút gốc bị tách làm hai, một nút gốc mới được tạo ra và hai con của nó là hai nút được tách ra từ nút gốc cũ, chiều cao cây tăng lên một.
Procedure Insert(value V, pointer P)
Tìm nút lá L sẽ chứa giá trị V
Insert_entry(L, V, P)
end procedure
Procedure Insert_entry(node L, value V, pointer P)
If (L có không gian cho (V, P) then
Xen (V, P) vào L
elsebegin/* tách L */
Tạo nút L'
If ( L là nút lá) then begin
V' là giá trị sao cho m/2 giá trị trong các giá trị L.K1, L.K2, ..., L.Km-1, V nhỏ hơn V'
n là chỉ số nhỏ nhất sao cho L.Kn V'
Di chuyển L.Pn, L.Kn, ..., L.Pm-1, L.Kn-1 sang L'
If (V < V') then xen (V, P) vào trong L else xen (P, V) vào trong L'
end else begin
V' là giá trị sao cho m/2 giá trị trong các giá trị L.K1, L.K2, ..., L.Km-1, V lớn hơn hoặc bằng V'
n là chỉ số nhỏ nhất sao cho L.Kn V'
Thêm Nil, L.Kn, L.Pn+1, L.Kn+1, ..., L.Pm-1, L.Km-1, L.Pm vào L'
Xoá L.Kn, L.Pn+1, L.Kn+1, ..., L.Pm-1, L.Km-1, L.Pm khỏi L
If (V < V') then xen (P, V) vào trong L else xen (P, V) vào trong L'
xoá (Nil, V') khỏi L'
end
If (L không là nút gốc) then Insert_entry(parent(L), V', L')
else begin
Tạo ra nút mới R với các nút con là L và L' với giá trị duy nhất trong nó là V'
Tạo R là gốc của cây
end
If (L) là một nút lá then begin
đặt L'.Pm = L.Pm
đặt L.Pm = L'
end
end
end procedure
- Xoá. Sử dụng kỹ thuật tìm kiếm tìm mẩu tin cần xoá, xoá nó khỏi file, xoá giá trị khoá tìm kiếm khỏi nút lá trong B+-cây nếu không có bucket kết hợp với giá trị khoá tìm kiếm hoặc bucket trở nên rỗng sau khi xoá con trỏ tương ứng trong nó. Việc xoá một giá trị khoá khỏi một nút của B+-cây có thể dẫn đến nút lá trở nên rỗng, phải trả lại, từ đó nút cha của nó có thể có số con nhỏ hơn ngưỡng cho phép, trong trường hợp đó hoặc phải chuyển một con từ nút anh em của nút cha đó sang nút cha nếu điều đó có thể (nút anh em của nút cha này còn số con m/2 sau khi chuyển đi một con). Nếu không, phải gom nút cha này với một nút anh em của nó, điều này dẫn tới xoá một nút trong khỏi cây, rồi xoá khỏi nút cha của nó một hạng, ... quá trình này có thể dẫn đến tận gốc. Trong trường hợp nút gốc chỉ còn một con sau xoá, cây phải thay nút gốc cũ bởi nút con của nó, nút gốc cũ phải trả lại cho hệ thống, chiều cao cây giảm đi một.
Procedure delete(value V, pointer P)
Tìm nút lá chứa (V, P)
delete_entry(L, V, P)
end procedure
Procedure delete_entry(node L, value V, pointer P)
xoá (V, P) khỏi L
If (L là nút gốc and L chỉ còn lại một con) then
Lấy con của L làm nút gốc mới của cây, xoá L
else If (L có quá ít giá trị/ con trỏ) then begin
L' là anh em kề trái hoặc phải của L
V' là giá trị ở giữa hai con trỏ L, L' (trong nút parent(L))
If (các đầu vào của L và L' có thể lấp đầy trong một khối) then begin
If (L là nút trước của L') then wsap_variables(L, L')
If (L không là lá) then nối V' và tất cả con trỏ, giá trị trong L với L'
else begin nối tất cả các cặp (K, P) trong L với L'; L'.Pp = L.Pp end
delete_entry(parent(L), V', L); xoá nút L
end
else begin
If (L' là nút trước của L) then begin
If (L không là nút lá) then begin
p là chỉ số sao cho L'.Pp là con trỏ cuối trong L'
xoá (L'.Kp-1, L'.Pp) khỏi L'
xen (L'.Pp, V') như phần tử đầu tiên trong L (right_shift tất cả các phần tử của L)
thay thế V' trong parent(L) bởi L'.Kp-1
end else begin
p là chỉ số sao cho L'.Pp là con trỏ cuối trong L'
xoá (L'.Pp, L'.Kp) khỏi L'
xen (L'.Pp, L'.Kp) như phần tử đầu tiên trong L (right_shift tất cả các phần tử của L)
thay thế V' trong parent(L) bởi L'.Kp
end
end < đối xứng với trường hợp then >
end
end procedure
Tổ chức file B+-cây
Trong tổ chức file B+-cây, các nút lá của cây lưu trữ các mẩu tin, thay cho các con trỏ tới file. Vì mẩu tin thường lớn hơn con trỏ, số tối đa các mẩu tin được lưu trữ trong một khối lá ít hơn số con trỏ trong một nút không lá. Các nút lá vẫn được yêu cầu được lấp đầy ít nhất là một nửa.
Xen và xoá trong tổ chức file B+-cây tương tự như trong chỉ mục B+-cây.
Khi B+-cây được sử dụng để tổ chức file, việc sử dụng không gian là đặc biệt quan trọng, vì không gian bị chiếm bởi mẩu tin là lớn hơn nhiều so với không gian bị chiếm bởi (khoá,con trỏ). Ta có thể cải tiến sự sử sụng không gian trong B+-cây bằng cách bao hàm nhiều nút anh em hơn khi tái phân phối trong khi tách và trộn. Khi xen, nếu một nút là đầy, ta thử phân phối lại một số đầu vào đến một trong các nút kề để tạo không gian cho đầu vào mới. Nếu việc thử này thất bại, ta mới thực hiện tách nút và phân chia các đầu vào giữa một trong các nút kề và hai nút nhận được do tách nút. Khi xoá, nếu nút chứa ít hơn 2m/3 đầu vào, ta thử mượn một đầu vào từ một trong hai nút anh em kề. Nếu cả hai đều có đúng 2m/3 mẩu tin, ta phân phối lại các đầu vào của nút cho hai nút anh em kề và xoá nút thứ 3. Nếu k nút được sử dụng trong tái phân phối (k-1 nút anh em), mỗi nút đảm bảo chứa ít nhất (k-1)m/k đầu vào. Tuy nhiên, cái giá phải trả cho cập nhật của cách tiếp cận này sẽ cao hơn.