• No results found

Bulk Insertion and Bulk Deletion

The BUB-Tree

6.9 Bulk Insertion and Bulk Deletion

In Section 4.3 and 4.4 we have addressed bulk operations on UB-Trees. Due to the different structure of the BUB-Tree they cannot be reused without modification. This section discusses the modification required to make them work for the BUB-Tree. All algorithms retain their original I/O complexity.

6.9.1 Initial Bulk Loading

Initial bulk loading is the creation of a new BUB-Tree from a data set. As for the UB-Tree, the data set is sorted according to address order and then loading starts. The UB-Tree bulk loading algorithm ensures a desired page utilization U .

The BUB-Tree goal of minimizing the covered dead space is orthogonal to this. The solution addressing both problems are the rules triggering a best split as listed in Section 6.6.

The UB-Tree writes a page if its utilization reaches U . For the BUB-Tree the page is filled completely including the tuple causing an overflow. If a best split is triggered according to the rules listed in Section 6.6, it is performed at the designated position.

Search for the best split position is performed in the range of positions [Umin, 100% − Umin] of the page. If no best split is triggered, a split producing a page utilization of U is performed.

The first page w.r. to address order is written to disk and the other one is kept in main memory for further filling. Whenever a page is written its bounding interval is inserted into the index. As for the UB-Tree, only the path to the current page needs to be kept in main memory and writing of index pages is postponed until they are not subject to modification anymore, i.e., when descending into a new path.

This process continues until there is no data left.

Bookkeeping of the average page utilization allows for disabling the best split when it differs to much from the designated page utilization U .

6.9.2 Incremental Bulk Loading

Incremental bulk loading is the insertion of new data to an existing BUB-Tree. The core of the algorithm is the same as for the UB-Tree. When searching for a page to insert new data, the insertion algorithm of Section 6.5 must be used in order to ensure a data page

is found while keeping index coverage low. Filling and splitting the pages is performed in the same way as described in the last section on initial loading.

No further modifications are required.

6.9.3 Bulk Deletion

Also the bulk deletion for BUB-Trees requires only minor changes to the algorithm pre-sented for UB-Trees. First, the BUB-Tree range query has to be used instead of the UB-Tree range query algorithm. Second, page merges and tuple redistribution should take minimal coverage of dead space into account and an underflow is only triggered when the page utilization drops below Umin.

Further, instead of simply moving tuples to the page with the underflow until a 50%

utilization is restored, again it is sufficient to only restore the Umin requirement.

Another solution to fix an underflow is a merge of both pages followed by a best split resp. a normal 50/50 split, if no best split position was found.

6.10 Reorganization

The index might degenerate w.r. to page utilization. An example for this has been given before. Further, the index might not be optimal when adjustments of index entries had been postponed for deletions or when bounds have been chosen to be not tight in order to avoid frequent adjustments of bounds during insertion.

Thus, it might be necessary to reorganize the index from time to time or on demand in order to enhance query performance and/or page utilization. In the following we discuss different aspects of such a reorganization separately. However, it is possible to combine them and thus avoid multiple passes over the complete index and data.

All operations presented here modify the tree only locally, i.e., they only require to lock modified pages on the currently processed path from the root to the leafs of the index and where necessary its neighboring pages. Further, it is not necessary to run them exclusively as they are not modifying data, but they can be run concurrently with other operations, e.g., queries, deletes, updates.

The operation presented here will reorganize the whole index. If the index only requires a partial reorganization, the operation can be easily restricted to the relevant range. This is done by utilizing the range query algorithm to select the pages that reorganization should be applied to.

When modifying index pages it is important to consider the trade-off between reading and writing them multiple times versus blocking other transactions. As there might be more than one modification of an index page it is a good idea to lock it until no more modifications can occur. However, locking index pages at higher index levels, or even the root, blocks transactions requiring any node in the subtree of the locked page. Therefore, as there are fewer pages on the upper levels and as they will be in cache most of the time

it is advised to unlock them after modifying them and thus do not block too many other transactions.

6.10.1 Tightening Bounds

Optimal bounds can be restored by adjusting them during a depth-first index traversal.

Index entries are adjusted to the minimum and maximum of their child nodes in a bottom up process, i.e., when reaching a data page the index entries on the path to it are corrected and modified index pages marked as dirty1. When following a new path all dirty pages on the old path are written to disk as they are not subject to modifications anymore. The cache needs to hold h pages where h is the height of the index.

All index and data pages are read once and modified pages are written only once. As the depth-first search is done in address order pre-fetching is also utilized when working on a bulk loaded BUB-Tree.

6.10.2 Optimizing the Index Coverage

Minimizing the index coverage is a goal which is not accomplished easily, thus for us optimizing refers to a reduction of the index coverage.

Looking at two regions [σi, i] and [σi+1, i+1] which are neighbors w.r. to address or-der, it is simple to decide if another partitioning would be better. If a search for a best split positions in the range [σi, i+1] finds a position which is different to the current split position, then we have found a better partitioning. Of course, Umin should be taken into account to limit the search on those gaps that will ensure Umin in case of a split. Umin can be changed anytime and thus may differ from the initial Umin of the BUB-Tree.

Applying this to the data pages means they are scanned in sequential order, always considering a pair of consecutive pages and their corresponding regions. Whenever a better split position is found, it is used and the left page, which is the one corresponding to the region with the smaller address, is stored and the index entry referring to it is updated.

The right page is not written immediately as it needs to be considered in the next cycle of the loop which might modify it again. Index pages are marked as dirty and are kept in cache until displaced from it. Again it is only necessary to have a small cache, i.e., a cache of h + 1 pages is sufficient.

If data pages are linked to each other, this algorithm will read all data pages in sequen-tial order, but only those index pages lying on a path to a modified data page. The path to a data page is found simply be performing a point search on one of the points stored on the data page. Modified pages are written only once.

The resulting BUB-Tree has the same number of pages, but if any modifications have been made, reduced coverage allowing for pruning more search paths.

1A page marked as dirty will be written to disk before displacing it from the cache. This is a common strategy to postpone writing to disk until it cannot be avoided anymore.

6.10.3 Increasing the Page Utilization

Performing a best split causes in the worst case the creation of pages all having a page utilization of Umin. Thus, increasing the average page utilization and reducing the number of pages usually has a positive effect on query performance as there are fewer pages which can be accessed.

However, simply merging pages or redistributing tuples might cause the degeneration of the BUB-Tree performance, as possibilities to prune search paths might be reduced.

Thus, only looking at the page utilization of a single page is no solution, but the average page utilization should be increased.

Therefore, there is only one way to increase the page utilization, i.e., by merging two consecutive pages which both have a page utilization below 50%. Further this should only be done if there is no best split position between the tuples on those two pages, i.e., in range [σi, i+1]. When a merge occurs, the index entry referring to the deleted page is removed and the other one is adjusted to its new bounds.

This algorithm will read all data pages once in sequential address order and all index pages lying on a path to modified data pages. It will only write modified pages and all of them just once.