Comparison of B, B+, red-black tree, and AVL tree

Mainly from Kobayashi coding https://mp.weixin.qq.com/s/AoPq8poENF9T4mVS1fDFPw#
insert image description here

What kind of index data structure is good?

MySQL's data is persistent, which means that the data (index + record) is saved to the disk, because even if the device is powered off, the data will not be lost.

Disk is a ridiculously slow storage device, how ridiculous is it?

People's memory access speed is at the nanosecond level, while the disk access speed is at the millisecond level, that is to say, when reading the same size of data, the speed of reading from the disk is tens of thousands of times slower than that from the memory. , or even hundreds of thousands of times.

The smallest unit of disk reading and writing is a sector, and the size of a sector is only 512B. The operating system will read and write multiple sectors at a time, so the smallest reading and writing unit of the operating system is a block (Block). The block size in Linux is 4KB, that is, one disk I/O operation will directly read and write 8 sectors.

Since the index of the database is saved on the disk, when we look up a row of data through the index, we need to read the index from the disk to the memory first, then find a row of data from the disk through the index, and then read it into the memory , that is to say, multiple disk I/Os will occur during the query process, and the more disk I/Os, the greater the time consumed.

Therefore, we hope that the data structure of the index can complete the query work in as few disk I/O operations as possible, because the fewer disk I/O operations, the smaller the time consumed.

In addition, MySQL supports range search, so the data structure of the index must not only be able to efficiently query a certain record, but also be able to efficiently perform range search.

Therefore, to design a data structure suitable for MySQL index, at least the following requirements should be met:

  • Can complete the query work in as few disk I/O operations as possible;
  • To be able to efficiently query a certain record, it is also necessary to be able to efficiently perform range searches;

After analyzing the requirements, let's analyze each data structure.

binary search array

insert image description here
It can be seen that the binary search method halves the scope of the query every time, so that the time complexity is reduced to O(logn), but each search needs to continuously calculate the middle position.

binary search tree

Although it is simple and easy to use arrays to implement linearly sorted data, the performance is too low when inserting new elements.

Because to insert an element, all the elements after this element need to be shifted back by one bit. What if this operation occurs on the disk? This must be disastrous. Because disks are hundreds of thousands of times slower than memory, we cannot sort disks in a linear structure.

Secondly, when binary search is used for an ordered array, the middle position must be continuously calculated for each search.

Then can we design a data structure that is nonlinear and naturally suitable for binary search?

Yes, please look at the magical operation in the figure below, find all the intermediate nodes used in all binary searches, connect them with pointers, and use the most intermediate node as the root node.
insert image description here

How about it? Has it become a binary tree, but it is not an ordinary binary tree, it is a binary search tree.

The characteristic of a binary search tree is that all the nodes in the left subtree of a node are smaller than this node, and all the nodes in the right subtree are larger than this node. In this way, when we query data, we do not need to calculate the position of the intermediate node. Compare the lookup data with the node's data.

Suppose, we look for the node whose index value is key:

  1. If the key is greater than the root node, search in the right subtree;

  2. If the key is smaller than the root node, search in the left subtree;

  3. If the key is equal to the root node, that is, the node is found, just return the root node.

The dynamic diagram of a binary search tree searching for a node is as follows, for example, to find node 3.
insert image description here
In addition, the binary search tree solves the problem of inserting new nodes, because the binary search tree is a jump structure and does not need to be arranged in a row. In this way, when inserting, the new node can be placed at any position, instead of inserting an element like a linear structure, all elements need to be arranged backwards.

The following is an animation demonstration of a binary search tree inserting a node:
insert image description here
Therefore, a binary search tree solves the problem of high cost of inserting new elements into a continuous structure, while maintaining a natural binary structure.

Is that a binary search tree that can be used as an index data structure?

No, no, there is an edge case in the binary search tree, which will cause it to become a lame!

When the element inserted each time is the largest element in the binary search tree, the binary search tree will degenerate into a linked list, and the time complexity of finding data becomes O(n), as shown in the following animation
insert image description here
: It is stored in the disk, and accessing each node corresponds to a disk I/O operation (assuming that the size of a node is "less than" the size of the smallest read-write unit block of the operating system), that is to say, the height of the tree is equal to each The number of disk IO operations when querying data, so the higher the height of the tree, the query performance will be affected.

Due to the possibility of the binary search tree degenerating into a linked list, the time complexity of the query operation will be reduced from O(logn) to O(n).

Moreover, as more elements are inserted, the height of the tree becomes higher, which means that more disk IO operations are required, which leads to a serious decline in query performance. In addition, range queries cannot be performed, so it is not suitable as an index for databases . structure.

What is an AVL self-balancing binary tree?

In order to solve the problem that the binary search tree will degenerate into a linked list in extreme cases, someone later proposed a balanced binary search tree (AVL tree).

Mainly, some conditional constraints are added on the basis of the binary search tree: the height difference between the left subtree and the right subtree of each node cannot exceed 1. That is to say, the left subtree and right subtree of the node are still balanced binary trees, so the time complexity of the query operation will always be maintained at O(logn).

The figure below shows that the element inserted each time is the largest element in the balanced binary search tree. It can be seen that it will maintain self-balancing:
insert image description here
In addition to the balanced binary search tree, there are many self-balancing binary trees, such as red-black trees. It also achieves self-balancing through some constraints, but the constraints of red-black trees are more complicated, which is not the focus of this article. You can read books related to "Data Structure" to understand the constraints of red-black trees.

The following is the process of inserting a node into a red-black tree. This left-handed and right-handed operation is for self-balancing.
insert image description here
Regardless of the balanced binary search tree or the red-black tree, the height of the tree will increase as the number of inserted elements increases, which means that the number of disk I/O operations will increase, which will affect the efficiency of the overall data query.

For example, the following balanced binary search tree has a height of 5, so when accessing the bottommost node, 5 disk I/O operations are required.

insert image description here
The fundamental reason is that they are all binary trees, that is, each node can only save 2 child nodes. What if we change the binary tree into an M-ary tree (M>2)?

For example, when M=3, under the same number of nodes, the tree height of the ternary tree is shorter than that of the binary tree.

insert image description here

Therefore, when there are more nodes in the tree and the number of forks M of the tree is larger, the height of the M-ary tree will be much smaller than the height of the binary tree.

What is a B-tree

Although the self-balancing binary tree can keep the time complexity of the query operation at O(logn), but because it is essentially a binary tree, each node can only have 2 child nodes, so when the number of nodes increases, the height of the tree It will also become higher accordingly, which will increase the number of disk I/Os, thus affecting the efficiency of data query.

In order to solve the problem of reducing the height of the tree, B-tree came out later. It no longer restricts a node to have only 2 child nodes, but allows M child nodes (M>2), thereby reducing the height of the tree.

Each node of the B-tree can include at most M sub-nodes, and M is called the order of the B-tree, so the B-tree is a multi-fork tree.

Assuming M = 3, then it is a 3rd-order B-tree, which is characterized by each node having at most 2 (M-1) data and at most 3 (M) child nodes. If these requirements are exceeded, it will Split nodes, such as the animation below:
insert image description here
Let's take a look at the query process of a 3rd-order B-tree?

insert image description here

Suppose we want to find the record whose index value is 9 in a 3rd-order B-tree in the above figure, then the steps can be divided into the following steps:

  1. Compare with the index (4, 8) of the root node, if 9 is greater than 8, then go to the child node on the right;
  2. Then the index of the child node is (10, 12), because 9 is less than 10, so it will go to the left child node of the node;
  3. Going to the node with index 9, then we found the node with index value 9.

It can be seen that when a 3rd-order B-tree queries data in leaf nodes, since the height of the tree is 3, 3 disk I/O operations will occur during the query process.

And if the same number of nodes is in a balanced binary tree scenario, the height of the tree will be high, which means more disk I/O operations. Therefore, B-tree is more efficient than balanced binary tree in data query.

However, each node of the B-tree contains data (index + record), and the size of the user's record data is likely to far exceed the index data, which requires more disk I/O operations to read " useful index data".

Moreover, during the process of querying a certain node (such as A record) at the bottom layer, the record data in the "non-A record node" will be loaded from disk to memory, but these record data are useless, we just want to read The index data of these nodes is used for comparison and query, and the record data in "non-A record nodes" is useless to us, which not only increases the number of disk I/O operations, but also occupies memory resources.

In addition, if you use B-tree for range query, you need to use in-order traversal, which will involve disk I/O problems of multiple nodes, resulting in a decrease in overall speed.

What is a B+ tree?

The B+ tree is an upgrade of the B tree. The data structure of the index in MySQL uses the B+ tree. The B+ tree structure is as follows:

insert image description here

The difference between B+ tree and B tree is mainly the following points:

  • Leaf nodes (bottommost nodes) will store actual data (index + records), and non-leaf nodes will only store indexes;
  • All indexes will appear in the leaf nodes, and an ordered linked list is formed between the leaf nodes;
  • The index of the non-leaf node also exists in the child node at the same time, and is the largest (or smallest) of all indexes in the child node.

There are as many indexes as there are child nodes in a non-leaf node;

In the following three aspects, compare the performance difference between B+ and B-tree.

1. Single point query

When the B-tree performs a single index query, it can be found within the time cost of O(1) at the fastest, and from the average time cost, it will be slightly faster than the B+ tree.

However, the query fluctuation of the B-tree will be relatively large, because each node stores both the index and the record, so sometimes the index can be found after visiting the non-leaf node, and sometimes the index can be found only after visiting the leaf node.

The non-leaf nodes of the B+ tree do not store the actual record data, but only store the index. Therefore, in the case of the same amount of data, compared with the B-tree that stores both indexes and records, the non-leaf nodes of the B+ tree can store more indexes. , so the B+ tree can be more "chunky" than the B tree, and the number of disk I/Os to query the underlying nodes will be less.

2. Insertion and deletion efficiency

The B+ tree has a large number of redundant nodes, so that when deleting a node, it can be deleted directly from the leaf node, and even non-leaf nodes can not be moved, so the deletion is very fast.

For example, the animation below is the process of deleting a leaf node of a B+ tree:
insert image description here
Note: B+ trees may have different definitions for the number of sub-nodes and indexes of non-leaf nodes, some of which refer to sub-nodes of non-leaf nodes The number of the index is M-order, and the number of indexes is M-1 (this is the definition in Wikipedia), so the animations of the B+ tree in this article are based on this. But when I introduced the difference between B+ tree and B+ tree earlier, I said "there are as many indexes as there are child nodes in non-leaf nodes", mainly because the B+ tree used by MySQL is this feature.

Even, when the B+ tree deletes the root node, due to the existence of redundant nodes, complex tree deformation will not occur. For example, the following animation is the process of deleting the B+ tree root node:

insert image description here
The B-tree is different. The B-tree has no redundant nodes. It is very complicated to delete nodes. For example, deleting the data in the root node may involve complex tree deformation. For example, the following animation is the process of deleting the B-tree root node:
insert image description here

The same is true for the insertion of the B+ tree. There are redundant nodes, and there may be node splits in the insertion (if the node is saturated), but only one path of the tree is involved at most. Moreover, the B+ tree will be automatically balanced, and there is no need for more complicated algorithms, such as rotation operations such as red-black trees.

Therefore, the insertion and deletion of B+ tree is more efficient.

3. Range query

The principle of equivalent query between B-tree and B+ tree is basically the same. First search from the root node, then compare the scope of the target data, and finally enter the sub-node search recursively.

Because there is a linked list between all leaf nodes of the B+ tree, this design is very helpful for range search. For example, if we want to know the order between December 1 and December 12, we can first search for 12 at this time. The leaf node where the 1st of December is located, and then use the linked list to traverse to the right until the node of the 12th of December is found, so that there is no need to query from the root node, further saving the time required for the query.

The B-tree does not have a structure that connects all leaf nodes with a linked list, so the range query can only be completed by tree traversal, which involves disk I/O operations of multiple nodes, and the range query efficiency is not as good as that of the B+ tree.

Therefore, there are a large number of range retrieval scenarios, which are suitable for using B+ trees, such as databases. For a large number of single index query scenarios, B-trees, such as NoSQL's MongoDB, can be considered.

B+ tree in MySQL

The storage method of MySQL varies according to the storage engine. The most commonly used one is the Innodb storage engine, which uses the B+ tree as the index data structure.

The following figure is the B+ tree in Innodb:
insert image description here
but the B+ tree used by Innodb has some special points, such as:

The leaf nodes of the B+ tree are connected by a "doubly linked list", which has the advantage of being able to traverse both to the right and to the left.

The content of the B+ tree node is a data page, which stores user records and various information. The default size of each data page is 16 KB.

Innodb is divided into aggregated and secondary indexes according to different index types. The difference between them is that the leaf nodes of the clustered index store the actual data, and all complete user records are stored in the leaf nodes of the clustered index, while the leaf nodes of the secondary index store the primary key value instead of the actual data.

Because the data of the table is stored in the leaf nodes of the clustered index, the InnoDB storage engine will definitely create a clustered index for the table, and since the data will only be saved physically, there can only be one clustered index, and Multiple secondary indexes can be created.

For more about Innodb's B+ tree, you can read this article I wrote before: B+ tree from the perspective of data pages.

To sum up
, MySQL will persist data on the hard disk, and the storage function is implemented by the MySQL storage engine, so discussing which data structure MySQL uses as an index is actually discussing which data structure is used as an index for storage references, and InnoDB is MySQL's default storage engine is a data structure that uses a B+ tree as an index.

To design a MySQL index data structure, not only consider the time complexity of adding, deleting, and modifying the data structure, but more importantly, consider the number of disk I/0 operations. Because indexes and records are stored on the hard disk, which is a very slow storage device, when we query data, it is best to complete it within as few disk I/0 operations as possible.

Although the binary search tree is a natural binary structure, it can make good use of binary search to quickly locate data, but it has an extreme situation. Whenever the inserted element is the largest element in the tree, it will lead to a binary search tree. Degenerate into a linked list, and the query complexity will be reduced from O(logn) to O(n).

In order to solve the problem that the binary search tree degenerates into a linked list, a self-balancing binary tree appears, which ensures that the time complexity of the query operation will always be maintained at O(logn). But it is still a binary tree in essence, and each node can only have 2 child nodes. As the number of elements increases, the height of the tree will become higher and higher.

The height of the tree depends on the number of disk I/O operations, because the tree is stored on the disk, and each node is visited, corresponding to a disk I/O operation, that is to say, the height of the tree is equal to each time the data is queried. The number of disk IO operations, so the higher the height of the tree, it will affect the query performance.

Both B-tree and B+ use multi-fork trees to shorten the height of the tree, so these two data structures are very suitable for retrieving data stored on disk.

However, MySQL's default storage engine, InnoDB, uses B+ as the index data structure for the following reasons:

The non-leaf nodes of the B+ tree do not store the actual record data, but only store the index. Therefore, in the case of the same amount of data, compared with the B-tree that stores both indexes and records, the non-leaf nodes of the B+ tree can store more indexes. , so the B+ tree can be more "chunky" than the B tree, and the number of disk I/Os to query the underlying nodes will be less.

The B+ tree has a large number of redundant nodes (all non-leaf nodes are redundant indexes). These redundant indexes make the insertion and deletion of the B+ tree more efficient. For example, when deleting the root node, it will not be like the B tree. Complex tree changes can occur;

The leaf nodes of the B+ tree are connected by a linked list, which is conducive to range query, and the B-tree needs to implement range query, so the range query can only be completed through tree traversal, which involves disk I/O operations of multiple nodes. Range queries are not as efficient as B+ trees.

over!

Comparison of B+ tree and red-black tree

Balanced trees such as red-black trees can also be used to implement indexes, but file systems and database systems generally use B+ Tree as the index structure, mainly for the following two reasons:

(1) Disk IO times

A node of a B+ tree can store multiple elements. Compared with the red-black tree, the tree height is lower and the number of disk IOs is less.

(2) Disk read-ahead feature

In order to reduce disk I/O operations, the disk is often not strictly read on demand, but read ahead every time. During the read-ahead process, the disk performs sequential reads, and sequential reads do not require disk seeks. Integer multiples of pages are read each time.

The operating system generally divides memory and disk into blocks of fixed size, each block is called a page, and memory and disk exchange data in units of pages. The database system sets the size of a node of the index to the size of a page, so that one node can be fully loaded in one I/O.

B + tree and B tree comparison
B + tree disk IO is lower

The internal nodes of the B+ tree do not have pointers to specific information about keywords. Therefore, its internal nodes are smaller than B-trees. If all the keywords of the same internal node are stored in the same disk block, the number of keywords that the disk block can hold is also larger. The more keywords that need to be searched are read into the memory at one time. Relatively speaking, the number of IO reads and writes is also reduced.

The query efficiency of B+ tree is more stable

Because non-leaf nodes are not nodes that ultimately point to file content, but are just indexes of keywords in leaf nodes. So any keyword search must take a path from the root node to the leaf node. All keyword queries have the same path length, resulting in the same query efficiency for each data.

B+ tree element traversal is efficient

While improving disk IO performance, the B-tree does not solve the problem of low efficiency of element traversal. It is to solve this problem that the B+ tree came into being. The B+ tree can realize the traversal of the whole tree as long as the leaf nodes are traversed. Moreover, range-based queries in the database are very frequent, and B-trees do not support such operations (or the efficiency is too low).

Guess you like

Origin blog.csdn.net/S_ZaiJiangHu/article/details/130028126