/// <summary> /// B+树节点 /// </summary> /// <param name="tree">包含该节点的树</param> /// <param name="parent">节点的父节点</param> /// <param name="indexInParent">在父节点中的索引</param> /// <param name="isLeaf">是否为叶子节点</param> public BPlusTreeNode(BPlusTreeLong tree, BPlusTreeNode parent, int indexInParent, bool isLeaf) { if(tree == null) throw new ArgumentNullException("tree"); this.Tree = tree; this.Parent = parent; this.IsLeaf = isLeaf; this.IndexInParent = -1; this.BlockNumber = StoredConstants.NullBlockNumber; this.Capacity = tree.NodeCapacity; this.Dirty = true; this.Clear(); // 存在父节点 if (parent != null && indexInParent >= 0) { // B+树 父节点中键值数与子节点数量相等 if (indexInParent > this.Capacity) { throw new BPlusTreeException("parent index too large"); } // 建立与父节点关系 this.Parent.ChildNodes[indexInParent] = this; this.BlockNumber = this.Parent.ChildValues[indexInParent]; this.IndexInParent = indexInParent; } }
/// <summary> /// 加载指定插入的索引点的节点对象 /// </summary> /// <param name="insertPosition">指定插入的索引点</param> /// <returns>节点对象</returns> private BPlusTreeNode LoadNodeAtIndex(int insertPosition) { if (this.IsLeaf) // 只对中间节点应用有效 { throw new BPlusTreeException("cannot materialize child for leaf"); } // 获取指定位置对应子节点的块序号 long childBlockNumber = this.ChildValues[insertPosition]; if (childBlockNumber == StoredConstants.NullBlockNumber) { throw new BPlusTreeException("can't search null subtree at position " + insertPosition + " in " + this.BlockNumber); } // 节点对象已经存在吗 BPlusTreeNode node = this.ChildNodes[insertPosition]; if (node != null) { return node; } // 如果不存在则从块中加载 node = new BPlusTreeNode(this.Tree, this, insertPosition, true); // dummy isLeaf item node.LoadFromBlock(childBlockNumber); this.ChildNodes[insertPosition] = node; // 该节点不在是终端节点 this.Tree.ForgetTerminalNode(this); // 我已经有了一个具体化的子节点,我不再是终端节点 return node; }
/// <summary> /// 查找指定索引处的下一个键值,如果没有则遍历右子树,如果仍没找到则返回空 /// </summary> /// <param name="atIndex">开始查找的索引</param> /// <param name="foundInLeaf">找到键值的叶子节点</param> /// <param name="foundKey">找到的键值</param> private void TraverseToFollowingKey(int atIndex, out BPlusTreeNode foundInLeaf, out string foundKey) { foundInLeaf = null; foundKey = null; bool lookInParent = false; if (this.IsLeaf) { lookInParent = (atIndex >= this.Capacity) || (this.ChildKeys[atIndex] == null); } else { lookInParent = (atIndex > this.Capacity) || (atIndex > 0 && this.ChildKeys[atIndex - 1] == null); } if (lookInParent) { // if it's anywhere it's in the next child of parent if (this.Parent != null && this.IndexInParent >= 0) { this.Parent.TraverseToFollowingKey(this.IndexInParent + 1, out foundInLeaf, out foundKey); return; } else { return; // no such following key } } if (this.IsLeaf) { // leaf, we found it. foundInLeaf = this; foundKey = this.ChildKeys[atIndex]; return; } else { // nonleaf, look in child (if there is one) if (atIndex == 0 || this.ChildKeys[atIndex - 1] != null) { BPlusTreeNode child = this.LoadNodeAtIndex(atIndex); child.TraverseToFollowingKey(0, out foundInLeaf, out foundKey); } } }
/// <summary> /// 在节点中插入键值对并作为叶子节点 /// </summary> /// <param name="key">叶子节点的键</param> /// <param name="value">键对应的值</param> /// <param name="splitFirstKey">if not null then the smallest key in the new split leaf</param> /// <param name="splitNode">if not null then the node was split and this is the leaf to the right.</param> /// <returns>smallest key item in keys, or null if no change</returns> public string InsertLeaf(string key, long value, out string splitFirstKey, out BPlusTreeNode splitNode) { splitFirstKey = null; splitNode = null; if (!this.IsLeaf) { throw new BPlusTreeException("bad call to insert leaf, this is not a leaf"); } // 标示节点已被更改 this.Soil(); // 查找新键的位置 键可能已经存在 int insertPosition = this.FindAtOrNextPosition(key, false); bool doSplit = false; // 节点未满 if (insertPosition < this.Capacity) { // 如果键已存在,则更改其对应值及位置,不支持重复的条目 if (this.ChildKeys[insertPosition] == null || this.Tree.Compare(this.ChildKeys[insertPosition], key) == 0) { this.ChildKeys[insertPosition] = key; this.ChildValues[insertPosition] = value; // 返回键序列中的最小值,如果无更改则返回空 if (insertPosition == 0) { return key; } else { return null; } } // 插入点为比指定键稍大的键 } else { // 节点已满,准备分割节点 doSplit = true; } // 查看是否还有空位置 int nullIndex = insertPosition; while (nullIndex < this.ChildKeys.Length && this.ChildKeys[nullIndex] != null) { nullIndex++; } if (nullIndex >= this.ChildKeys.Length) { doSplit = true; } // 做分割的准备 数组增加1 if (doSplit) { this.PrepareBeforeSplit(); } // 将新数据插入至数组中,将已存在的值向右移动 string nextKey = this.ChildKeys[insertPosition]; long nextValue = this.ChildValues[insertPosition]; this.ChildKeys[insertPosition] = key; this.ChildValues[insertPosition] = value; while (nextKey != null) { key = nextKey; value = nextValue; insertPosition++; nextKey = this.ChildKeys[insertPosition]; nextValue = this.ChildValues[insertPosition]; this.ChildKeys[insertPosition] = key; this.ChildValues[insertPosition] = value; } // 如果需要分割 if (doSplit) { // 从中间开始分割 折半 int splitPoint = this.ChildKeys.Length / 2; int splitLength = this.ChildKeys.Length - splitPoint; // 新创建的分割出的节点,始终是右节点 splitNode = new BPlusTreeNode(this.Tree, this.Parent, -1, this.IsLeaf); // 将指定分割点左侧的数据拷贝至新的节点 Array.Copy(this.ChildKeys, splitPoint, splitNode.ChildKeys, 0, splitLength); Array.Copy(this.ChildValues, splitPoint, splitNode.ChildValues, 0, splitLength); Array.Copy(this.ChildNodes, splitPoint, splitNode.ChildNodes, 0, splitLength); // 记录分割节点的第一个键,右节点的第一个键 splitFirstKey = splitNode.ChildKeys[0]; // 存储新节点至块文件 splitNode.DumpToNewBlock(); // 分割完毕 恢复之前的准备 处理分割点右侧数据,保留左侧数据,删除右侧数据 this.RepairAfterSplit(splitPoint); // 记录新的节点 this.Tree.RecordTerminalNode(splitNode); // InsertLeaf // 新节点及其父节点需要处理 splitNode.Soil(); } if (insertPosition == 0) { return key; // smallest key changed. } else { return null; // no change in smallest key } }
/// <summary> /// 重新组织分割树,新的根节点将有两个子节点 /// </summary> /// <param name="oldRoot">原根节点</param> /// <param name="splitFirstKey">新根节点的第一个Key</param> /// <param name="splitNode">新分割出的节点</param> /// <param name="tree">指定的树</param> /// <returns>新根节点</returns> public static BPlusTreeNode BinaryRoot(BPlusTreeNode oldRoot, string splitFirstKey, BPlusTreeNode splitNode, BPlusTreeLong tree) { if (oldRoot == null) throw new ArgumentNullException("oldRoot"); if (splitNode == null) throw new ArgumentNullException("splitNode"); // 已不是叶子节点 BPlusTreeNode newRoot = MakeRoot(tree, false); // 新的跟记录分割节点的第一个Key newRoot.ChildKeys[0] = splitFirstKey; // 新旧节点分别为新的根节点的索引 0 1 位置 oldRoot.ResetParent(newRoot, 0); splitNode.ResetParent(newRoot, 1); // new root is stored elsewhere return newRoot; }
/// <summary> /// 重置节点的父节点 /// </summary> /// <param name="newParent">父节点</param> /// <param name="indexInParent">在父节点中的索引</param> private void ResetParent(BPlusTreeNode newParent, int indexInParent) { // keys and existing parent structure must be updated elsewhere. this.Parent = newParent; this.IndexInParent = indexInParent; newParent.ChildValues[indexInParent] = this.BlockNumber; // 中间节点存储的值为子节点的块序号 newParent.ChildNodes[indexInParent] = this; // parent is no longer terminal this.Tree.ForgetTerminalNode(this.Parent); // 父节点已经有了一个具体化的子节点,父节点不再是终端节点 }
/// <summary> /// 合并叶子节点,当节点的使用率不足50%时,则需要合并 /// </summary> /// <param name="left">左节点</param> /// <param name="right">右节点</param> /// <param name="canDeleteRightNode">是否可以删除右节点</param> public static void MergeLeaves(BPlusTreeNode left, BPlusTreeNode right, out bool canDeleteRightNode) { if (left == null) throw new ArgumentNullException("left"); if (right == null) throw new ArgumentNullException("right"); canDeleteRightNode = false; string[] allKeys = new string[left.Capacity * 2]; long[] allValues = new long[left.Capacity * 2]; int index = 0; for (int i = 0; i < left.Capacity; i++) { if (left.ChildKeys[i] == null) { break; } allKeys[index] = left.ChildKeys[i]; allValues[index] = left.ChildValues[i]; index++; } for (int i = 0; i < right.Capacity; i++) { if (right.ChildKeys[i] == null) { break; } allKeys[index] = right.ChildKeys[i]; allValues[index] = right.ChildValues[i]; index++; } // 如果左节点的容量足够,则可删除右节点 if (index <= left.Capacity) { canDeleteRightNode = true; left.Clear(); for (int i = 0; i < index; i++) { left.ChildKeys[i] = allKeys[i]; left.ChildValues[i] = allValues[i]; } left.Soil(); right.Free(); return; } // 左节点的容量不够了 left.Clear(); right.Clear(); left.Soil(); right.Soil(); int rightContent = index / 2; int leftContent = index - rightContent; int newIndex = 0; for (int i = 0; i < leftContent; i++) { left.ChildKeys[i] = allKeys[newIndex]; left.ChildValues[i] = allValues[newIndex]; newIndex++; } for (int i = 0; i < rightContent; i++) { right.ChildKeys[i] = allKeys[newIndex]; right.ChildValues[i] = allValues[newIndex]; newIndex++; } }
/// <summary> /// 在节点中插入键值对 /// </summary> /// <param name="key">叶子节点的键</param> /// <param name="value">键对应的值</param> /// <param name="splitFirstKey">if not null then the smallest key in the new split leaf</param> /// <param name="splitNode">if not null then the node was split and this is the leaf to the right.</param> /// <returns> /// null unless the smallest key under this node has changed, in which case it returns the smallest key. /// </returns> public string Insert(string key, long value, out string splitFirstKey, out BPlusTreeNode splitNode) { if (this.IsLeaf) { return this.InsertLeaf(key, value, out splitFirstKey, out splitNode); } // 我不是叶子 我是中间节点 找到Key对应的位置 在子节点中插入键值对 splitFirstKey = null; splitNode = null; // 查找新键的位置 由于是中间节点 则新键的位置必须存在 int insertPosition = this.FindAtOrNextPosition(key, false); // 非叶子节点中的值数组存储叶子节点的块序号 long insertValue = this.ChildValues[insertPosition]; if (insertValue == StoredConstants.NullBlockNumber) { throw new BPlusTreeException("key not followed by block number in non-leaf"); } // 加载子节点 BPlusTreeNode insertChild = this.LoadNodeAtIndex(insertPosition); string childSplitFirstKey; BPlusTreeNode childSplitNode; // 在子节点中插入新的键值对 string childInsert = insertChild.Insert(key, value, out childSplitFirstKey, out childSplitNode); // 发现子节点已满,也需要分割 if (childSplitNode != null) { // 我即将被更改 this.Soil(); // redundant -- a child will have a change so this node will need to be copied // 为新的子节点创建位置索引,即下一个 int newChildPosition = insertPosition + 1; // 自己是否已满 bool doSplit = false; // 如果我作为中间节点容量也满了,则中间节点也需要被分割 if (this.ChildValues[this.Capacity] != StoredConstants.NullBlockNumber) { doSplit = true; } if (doSplit) { // 做分割准备 this.PrepareBeforeSplit(); } // bubble over the current values to make space for new child // 新节点位置上及其右侧内容全部向右移动1位,为新节点空出位置 for (int i = this.ChildKeys.Length - 2; i >= newChildPosition - 1; i--) { int iPlus1 = i + 1; int iPlus2 = iPlus1 + 1; this.ChildKeys[iPlus1] = this.ChildKeys[i]; this.ChildValues[iPlus2] = this.ChildValues[iPlus1]; this.ChildNodes[iPlus2] = this.ChildNodes[iPlus1]; } // record the new child // 新节点的位置存放新节点的第一个键 this.ChildKeys[newChildPosition - 1] = childSplitFirstKey; // 被分割出的子节点的父节点为自己 childSplitNode.ResetParent(this, newChildPosition); // 如果我作为中间节点容量也满了,则中间节点也需要被分割 if (doSplit) { // 从中间开始分割 折半 int splitPoint = this.ChildNodes.Length / 2 - 1; // 分割出的新节点的第一个Key splitFirstKey = this.ChildKeys[splitPoint]; // 新建节点 包含分割点右侧所有数据 splitNode = new BPlusTreeNode(this.Tree, this.Parent, -1, this.IsLeaf); splitNode.Clear(); // redundant. // 记录已经扩充的数据结构 long[] values = this.ChildValues; string[] keys = this.ChildKeys; BPlusTreeNode[] nodes = this.ChildNodes; // 重置和清空数据 this.Clear(); // 将分割点左侧的数据拷贝至此节点 Array.Copy(keys, 0, this.ChildKeys, 0, splitPoint); Array.Copy(values, 0, this.ChildValues, 0, splitPoint + 1); Array.Copy(nodes, 0, this.ChildNodes, 0, splitPoint + 1); // 将分割点右侧的数据拷贝至新的分割节点 int remainingKeys = this.Capacity - splitPoint; Array.Copy(keys, splitPoint + 1, splitNode.ChildKeys, 0, remainingKeys); Array.Copy(values, splitPoint + 1, splitNode.ChildValues, 0, remainingKeys + 1); Array.Copy(nodes, splitPoint + 1, splitNode.ChildNodes, 0, remainingKeys + 1); // 重置新节点中所有的子节点的父节点 splitNode.ResetAllChildrenParent(); // 存储新节点 splitNode.DumpToNewBlock(); splitNode.CheckIfTerminal(); splitNode.Soil(); this.CheckIfTerminal(); } // end do split // 重置节点中所有的子节点的父节点 this.ResetAllChildrenParent(); } // 返回最小的那个键 if (insertPosition == 0) { return childInsert; // the smallest key may have changed } else { return null; // no change in smallest key } }
/// <summary> /// 合并节点,当节点的使用率不足50%时,则需要合并 /// </summary> /// <param name="left">左节点</param> /// <param name="keyBetween">左右节点的中间键</param> /// <param name="right">右节点</param> /// <param name="rightLeastKey">合并后的键的最小值</param> /// <param name="canDeleteRightNode">是否可以删除右节点</param> public static void Merge(BPlusTreeNode left, string keyBetween, BPlusTreeNode right, out string rightLeastKey, out bool canDeleteRightNode) { if (left == null) throw new ArgumentNullException("left"); if (right == null) throw new ArgumentNullException("right"); rightLeastKey = null; // only if DeleteRight // 合并叶子节点 if (left.IsLeaf || right.IsLeaf) { if (!(left.IsLeaf && right.IsLeaf)) { throw new BPlusTreeException("can't merge leaf with non-leaf"); } // 合并子节点 MergeLeaves(left, right, out canDeleteRightNode); rightLeastKey = right.ChildKeys[0]; return; } // 合并非叶子节点 canDeleteRightNode = false; if (left.ChildValues[0] == StoredConstants.NullBlockNumber || right.ChildValues[0] == StoredConstants.NullBlockNumber) { throw new BPlusTreeException("cannot merge empty non-leaf with non-leaf"); } string[] allKeys = new string[left.Capacity * 2 + 1]; long[] allValues = new long[left.Capacity * 2 + 2]; BPlusTreeNode[] allNodes = new BPlusTreeNode[left.Capacity * 2 + 2]; // 拷贝左节点的数据 int index = 0; allValues[0] = left.ChildValues[0]; allNodes[0] = left.ChildNodes[0]; for (int i = 0; i < left.Capacity; i++) { if (left.ChildKeys[i] == null) { break; } allKeys[index] = left.ChildKeys[i]; allValues[index + 1] = left.ChildValues[i + 1]; allNodes[index + 1] = left.ChildNodes[i + 1]; index++; } // 拷贝中间键 allKeys[index] = keyBetween; index++; // 拷贝右节点的数据 allValues[index] = right.ChildValues[0]; allNodes[index] = right.ChildNodes[0]; int rightCount = 0; for (int i = 0; i < right.Capacity; i++) { if (right.ChildKeys[i] == null) { break; } allKeys[index] = right.ChildKeys[i]; allValues[index + 1] = right.ChildValues[i + 1]; allNodes[index + 1] = right.ChildNodes[i + 1]; index++; rightCount++; } // 如果数量小于左节点的能力,则右节点可以删除掉 if (index <= left.Capacity) { // it will all fit in one node canDeleteRightNode = true; for (int i = 0; i < index; i++) { left.ChildKeys[i] = allKeys[i]; left.ChildValues[i] = allValues[i]; left.ChildNodes[i] = allNodes[i]; } left.ChildValues[index] = allValues[index]; left.ChildNodes[index] = allNodes[index]; left.ResetAllChildrenParent(); left.Soil(); right.Free(); return; } // otherwise split the content between the nodes left.Clear(); right.Clear(); left.Soil(); right.Soil(); int leftContent = index / 2; int rightContent = index - leftContent - 1; rightLeastKey = allKeys[leftContent]; int outputIndex = 0; for (int i = 0; i < leftContent; i++) { left.ChildKeys[i] = allKeys[outputIndex]; left.ChildValues[i] = allValues[outputIndex]; left.ChildNodes[i] = allNodes[outputIndex]; outputIndex++; } rightLeastKey = allKeys[outputIndex]; left.ChildValues[outputIndex] = allValues[outputIndex]; left.ChildNodes[outputIndex] = allNodes[outputIndex]; outputIndex++; rightCount = 0; for (int i = 0; i < rightContent; i++) { right.ChildKeys[i] = allKeys[outputIndex]; right.ChildValues[i] = allValues[outputIndex]; right.ChildNodes[i] = allNodes[outputIndex]; outputIndex++; rightCount++; } right.ChildValues[rightCount] = allValues[outputIndex]; right.ChildNodes[rightCount] = allNodes[outputIndex]; left.ResetAllChildrenParent(); right.ResetAllChildrenParent(); }
/// <summary> /// 将当前节点的容量扩大(+1),为插入和分割做准备 /// </summary> private void PrepareBeforeSplit() { int superSize = this.Capacity + 1; string[] keys = new string[superSize]; long[] positions = new long[superSize + 1]; BPlusTreeNode[] materialized = new BPlusTreeNode[superSize + 1]; Array.Copy(this.ChildKeys, 0, keys, 0, this.Capacity); keys[this.Capacity] = null; Array.Copy(this.ChildValues, 0, positions, 0, this.Capacity + 1); positions[this.Capacity + 1] = StoredConstants.NullBlockNumber; Array.Copy(this.ChildNodes, 0, materialized, 0, this.Capacity + 1); materialized[this.Capacity + 1] = null; this.ChildValues = positions; this.ChildKeys = keys; this.ChildNodes = materialized; }
/// <summary> /// Find near-index of comparekey in leaf under this node. /// </summary> /// <param name="compareKey">the key to look for</param> /// <param name="inLeaf">the leaf where found</param> /// <param name="lookPastOnly">If true then only look for a greater item, not an exact match.</param> /// <returns>index of match in leaf</returns> private int FindAtOrNextPositionInLeaf(string compareKey, out BPlusTreeNode inLeaf, bool lookPastOnly) { int keyPosition = this.FindAtOrNextPosition(compareKey, lookPastOnly); // 如果自己即是叶子节点 if (this.IsLeaf) { inLeaf = this; return keyPosition; } // 尝试在子节点中查找 BPlusTreeNode child = this.LoadNodeAtIndex(keyPosition); return child.FindAtOrNextPositionInLeaf(compareKey, out inLeaf, lookPastOnly); }
/// <summary> /// 由树记录节点是否为终端节点,终端节点没有子节点,而根或中间节点有子节点 /// </summary> /// <param name="nonTerminalNode">这个节点已经不是终端节点</param> public void ForgetTerminalNode(BPlusTreeNode nonTerminalNode) { if (!this._TerminalNodeToId.ContainsKey(nonTerminalNode)) { // silently ignore (?) return; } int id = (int)this._TerminalNodeToId[nonTerminalNode]; if (id == this._LowerTerminalNodeCount) { this._LowerTerminalNodeCount++; } this._IdToTerminalNode.Remove(id); this._TerminalNodeToId.Remove(nonTerminalNode); }
/// <summary> /// 由树记录节点是否为终端节点,终端节点没有子节点,而根或中间节点有子节点 /// </summary> /// <param name="terminalNode">这个节点是终端节点</param> public void RecordTerminalNode(BPlusTreeNode terminalNode) { if (terminalNode == this.RootNode) { return; // never record the root node } if (this._TerminalNodeToId.ContainsKey(terminalNode)) { return; // don't record it again } int id = this._TerminalNodeCount; this._TerminalNodeCount++; this._TerminalNodeToId[terminalNode] = id; this._IdToTerminalNode[id] = terminalNode; }