当前位置: 首页 > article >正文

我手写了一个 Java 内存数据库(二):B+ 树的插入与分裂

我手写了一个 Java 内存数据库二B 树的插入与分裂上一篇搭好了节点和查询框架。这篇写 B 树最核心的部分——插入和节点分裂。这块我调了最久分裂的边界条件特别多。插入的整体思路B 树插入分两步从根节点一路向下找到应该插入的叶子节点如果叶子节点满了分裂成两个把分裂向上传播给父节点听起来简单但分裂传播到根节点时树要长高边界条件不少。向下路由找目标叶子非叶子节点不存数据只负责路由。我根据 key 的大小选择子树if(isLeaf){// 到叶子了执行插入}else{if(key.compareTo(entries.get(0).getKey())0){children.get(0).insertOrUpdate(key,obj,tree,obligate);}elseif(key.compareTo(entries.get(entries.size()-1).getKey())0){children.get(children.size()-1).insertOrUpdate(key,obj,tree,obligate);}else{for(inti0;ientries.size();i){if(entries.get(i).getKey().compareTo(key)0entries.get(i1).getKey().compareTo(key)0){children.get(i).insertOrUpdate(key,obj,tree,obligate);break;}}}}三种情况比最小还小走最左子树比最大还大走最右子树中间的线性扫描找。中间部分后来我意识到也可以用二分但当时数据量不大就没优化。叶子节点插入要不要分裂到了叶子节点先看还有没有位置if(isLeaf){floatper0;if(obligate){pertree.getPer();// 默认 0.1预留 10%}intorder(int)(tree.getOrder()*(1-per));if(contains(key)||entries.size()order){insertOrUpdate(key,obj);if(parent!null)parent.updateInsert(tree,obligate);}else{// 满了分裂}}预留空间的设计obligate这个参数是我后来加的。批量插入时发现如果节点刚好满到 M后续几乎每次插入都触发分裂性能抖得厉害。于是加了一个 10% 的预留空间实际可用容量 M × (1 - 0.1) M × 0.9提前分裂虽然多占一点空间但减少了后续的分裂频率。后来查资料发现MySQL InnoDB 的页分裂策略也有类似思路算是歪打正着。分裂我调得最久的部分当叶子节点满了把它拆成两个分裂前M4已满要插入 key9 ┌─────────────────┐ │ 3 | 7 | 12 | 18 │ ← 插入 9 后变成 5 个 key └─────────────────┘ 分裂后 左节点 右节点 ┌───────┐ ┌───────────┐ │ 3 | 7 │ │ 9 | 12 | 18 │ └───────┘ └───────────┘ ↑ ↑ └─── 双向链表 ───┘代码NodeleftnewNode(true);NoderightnewNode(true);// 维护双向链表——这块最容易出 bugif(previous!null){previous.setNext(left);left.setPrevious(previous);}if(next!null){next.setPrevious(right);right.setNext(next);}if(previousnull){tree.setHead(left);// 原来是链表头更新头指针}left.setNext(right);right.setPrevious(left);previousnull;nextnull;// 先插入新 key再按大小分配insertOrUpdate(key,obj);intleftSize(order1)/2(order1)%2;intrightSize(order1)/2;for(inti0;ileftSize;i){left.getEntries().add(entries.get(i));}for(inti0;irightSize;i){right.getEntries().add(entries.get(leftSizei));}左右分配是(order 1) / 2向上取整给左边。比如 order4插入后 5 个 key左 3 右 2。链表维护是最容易出 bug 的地方。我当时调试了很久主要问题是分裂后 previous/next 的指向容易搞乱特别是链表头节点的处理。分裂后挂到父节点还是长出新的根分裂完要把新节点挂到父节点上分两种情况有父节点if(parent!null){intindexparent.getChildren().indexOf(this);parent.getChildren().remove(this);left.setParent(parent);right.setParent(parent);parent.getChildren().add(index,left);parent.getChildren().add(index1,right);setEntries(null);setChildren(null);parent.updateInsert(tree,obligate);// 父节点可能也要分裂setParent(null);}没有父节点——说明是根节点在分裂else{isRootfalse;NodeparentnewNode(false,true);// 新根非叶子tree.setRoot(parent);left.setParent(parent);right.setParent(parent);parent.getChildren().add(left);parent.getChildren().add(right);setEntries(null);setChildren(null);parent.updateInsert(tree,obligate);}根节点分裂意味着树长高了一层。B 树就是这样一个节点一个节点地长高的。父节点也可能分裂级联传播updateInsert里判断父节点的子节点数是否超限protectedvoidupdateInsert(BPTreetree,booleanobligate){validate(this,tree);intorder(int)(tree.getOrder()*(1-per));if(children.size()order){// 父节点也满了继续分裂NodeleftnewNode(false);NoderightnewNode(false);intleftSize(order1)/2(order1)%2;intrightSize(order1)/2;for(inti0;ileftSize;i){left.getChildren().add(children.get(i));left.getEntries().add(newSimpleEntry(children.get(i).getEntries().get(0).getKey(),null));children.get(i).setParent(left);}for(inti0;irightSize;i){right.getChildren().add(children.get(leftSizei));right.getEntries().add(newSimpleEntry(children.get(leftSizei).getEntries().get(0).getKey(),null));children.get(leftSizei).setParent(right);}// 然后和叶子分裂一样挂到祖父节点或建新根...}}分裂会从叶子向根传播直到某个父节点能容纳为止。最坏情况下一直传播到根树高 1。validate关键字校准这个函数我踩了一个大坑。分裂之后非叶子节点的关键字必须反映子节点的最小 key不然路由会出错。我一开始忘了同步查了半天发现查询结果不对。protectedstaticvoidvalidate(Nodenode,BPTreetree){if(node.getEntries().size()node.getChildren().size()){for(inti0;inode.getEntries().size();i){Comparablekeynode.getChildren().get(i).getEntries().get(0).getKey();if(node.getEntries().get(i).getKey().compareTo(key)!0){node.getEntries().remove(i);node.getEntries().add(i,newSimpleEntry(key,null));if(!node.isRoot()){validate(node.getParent(),tree);// 递归向上校准}}}}elseif(...){// 关键字数量完全不对重建node.getEntries().clear();for(inti0;inode.getChildren().size();i){Comparablekeynode.getChildren().get(i).getEntries().get(0).getKey();node.getEntries().add(newSimpleEntry(key,null));}if(!node.isRoot())validate(node.getParent(),tree);}}这篇的坑总结链表维护——分裂时 previous/next 指向容易搞错特别是头节点validate 忘调——非叶子节点关键字没同步导致路由错误分裂时先插入再分配——不是先分配再插入顺序搞反会丢数据上一篇上一篇[我手写了一个Java内存数据库一起因与架构]下一篇插入和分裂搞定了。下一篇写删除借节点、合并节点比插入更复杂和范围查询B 树最大的优势所在。下一篇[我手写了一个 Java 内存数据库三删除、合并与范围查询]系列我手写了一个 Java 内存数据库共 4 篇

相关文章:

我手写了一个 Java 内存数据库(二):B+ 树的插入与分裂

我手写了一个 Java 内存数据库(二):B 树的插入与分裂 上一篇搭好了节点和查询框架。这篇写 B 树最核心的部分——插入和节点分裂。这块我调了最久,分裂的边界条件特别多。 插入的整体思路 B 树插入分两步: 从根节点一…...

音频自动分割工具Audio Slicer:快速高效的静音检测分割指南

音频自动分割工具Audio Slicer:快速高效的静音检测分割指南 【免费下载链接】audio-slicer A simple GUI application that slices audio with silence detection 项目地址: https://gitcode.com/gh_mirrors/aud/audio-slicer 你是否经常需要处理长音频文件&…...

基于深度学习的车辆行人距离检测额计算 车距检测 单目测距检测 YOLO11单目测距与深度估计和目标检测项目

文章目录YOLO11单目测距与深度估计和目标检测:结合目标检测与深度学习的高效解决方案1. 引言2. YOLO11简介2.1 核心功能核心代码2.2 YOLO11的改进3. 技术原理与方法3.1 YOLO目标检测模块3.2 深度估计模块3.3 单目测距模块3.4 多任务损失函数4. 实验与结果分析4.1 数…...

如何用Pixelle-Video快速制作专业短视频:AI全自动视频生成工具完全指南

如何用Pixelle-Video快速制作专业短视频:AI全自动视频生成工具完全指南 【免费下载链接】Pixelle-Video 🚀 AI 全自动短视频引擎 | AI Fully Automated Short Video Engine 项目地址: https://gitcode.com/GitHub_Trending/pi/Pixelle-Video Pixe…...

ImageStrike:一站式CTF图像隐写分析工具,18种功能智能解析隐藏信息

ImageStrike:一站式CTF图像隐写分析工具,18种功能智能解析隐藏信息 【免费下载链接】ImageStrike ImageStrike是一款用于CTF中图片隐写的综合利用工具 项目地址: https://gitcode.com/gh_mirrors/im/ImageStrike 在CTF(Capture The Fl…...

3分钟系统大扫除:Win11Debloat让Windows重获新生的终极指南

3分钟系统大扫除:Win11Debloat让Windows重获新生的终极指南 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter a…...

Windows上直接安装APK文件的终极指南:告别笨重模拟器

Windows上直接安装APK文件的终极指南:告别笨重模拟器 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否厌倦了在Windows电脑上使用安卓模拟器时遇到的卡…...

告别网盘限速的终极方案:八大平台直链解析工具LinkSwift深度解析

告别网盘限速的终极方案:八大平台直链解析工具LinkSwift深度解析 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云…...

如何用LibreHardwareMonitor全面掌控电脑硬件健康状态?开源硬件监控神器深度解析

如何用LibreHardwareMonitor全面掌控电脑硬件健康状态?开源硬件监控神器深度解析 【免费下载链接】LibreHardwareMonitor Libre Hardware Monitor is free software that can monitor the temperature sensors, fan speeds, voltages, load and clock speeds of you…...

2026Kyocera京瓷LCD工业液晶屏代理选型与实测指南

① 京瓷系列核心参数解析与规格初筛 在工业显示领域,京瓷(Kyocera)的 LCD 产品一直以“稳”著称。很多工程师在选型初期,容易被分辨率或尺寸吸引,却忽略了决定项目生死的核心参数。根据我们过往对接京瓷原厂及处理大量…...

GPT-SoVITS语音合成实测:仅需1分钟音频,克隆效果超自然

GPT-SoVITS语音合成实测:仅需1分钟音频,克隆效果超自然 1. 引言:声音克隆技术的突破 想象一下,你只需要提供1分钟的语音样本,就能让AI完美模仿你的声音——这不是科幻电影,而是GPT-SoVITS带来的真实能力。…...

森利威尔SL4011 是专门针对单节两节锂电3.7V 5V 7.4V升压恒压9V 12V 16V 内置MOS 峰值10A电流

输入兼容强,扩展超灵活 输入电压 2.7V - 12V,完美覆盖单节锂电池 3.0V - 4.2V 全周期,低至 3V 也能稳出 5V,告别电量低输出中断的尴尬。还支持单双节锂电池输入,智能穿戴、移动电源等便携设备电源架构都能适配。效率高…...

汇总培训学员反馈太慢还不会整理?试试标准化梳理方法

汇总培训学员反馈太慢还理不清,整理面试、OKR面谈记录总是要耗大半天,是很多HR都会遇到的问题。要么重点错漏,要么整理完赶不上汇报进度。2026可以试试标准化梳理方法,能把几小时的工作压缩到十几分钟,接下来给你拆解可…...

企业级Docker WASM边缘网关部署指南,含FaaS函数热加载、OTA差分更新与断网自治策略(仅限头部客户内部流出)

更多请点击: https://intelliparadigm.com 第一章:企业级Docker WASM边缘网关部署指南 WebAssembly(WASM)正迅速成为边缘计算场景中轻量、安全、跨平台函数执行的核心载体。结合 Docker 的标准化分发能力与 WASM 的零成本沙箱特性…...

2026年,沸石转轮厂家光卖设备不够,业主还看重什么?

前些年,工厂只要买环保设备,能达标排放就算交差了。但现在环保检查越来越严,运行成本居高不下,设备三天两头出毛病——业主们渐渐发现:光买一台沸石转轮设备远远不够,后续能不能稳定运行、省不省电、厂家管…...

YOLOv5模型魔改实战:插入SE模块后,我的检测精度提升了多少?(附消融实验对比)

YOLOv5模型魔改实战:插入SE模块后,我的检测精度提升了多少?(附消融实验对比) 当我在VOC数据集上跑完最后一组消融实验时,控制台输出的mAP0.5数值让我停下了手中的咖啡——相比基准模型,添加SE模…...

你的App连不上WiFi?可能是Android 10的隐私权限在搞鬼(附排查指南)

Android 10 WiFi连接失效深度排查指南:隐私权限与API变革解析 最近在调试一个智能家居App时,遇到了一个诡异的问题:在Android 10设备上,WiFi连接功能总是莫名其妙失败,而在旧版本系统却运行良好。这让我意识到&#xf…...

01导论——《大数据平台架构(主编:吕欣 黄宏斌)》读书笔记2

当数据爆炸撞上传统技术,我们如何绝地求生? 问题的诞生:数据洪流与旧船票 过去的企业系统像一艘设计精良的小船,能稳稳载着【结构化数据】在风平浪静的水域航行。但突然之间,社交媒体的评论、监控摄像头的视频、传感器…...

从.imy到.mmf:手把手解析那些‘古老’手机铃声格式,并教你用Python将它们转换为现代音频

从.imy到.mmf:用Python解码复古手机铃声格式的工程实践 还记得功能机时代那些简单却充满个性的手机铃声吗?当诺基亚的《Nokia Tune》以单音旋律成为一代人的记忆符号,背后是IMY、RTTTL这些如今看来颇具"考古"价值的音频格式在支撑。…...

用FPGA和XDMA从零打造一个百兆网卡:我的踩坑记录与性能调优心得

用FPGA和XDMA从零打造一个百兆网卡:我的踩坑记录与性能调优心得 去年夏天,当我第一次将自制的FPGA网卡插入RK3399开发板时,满心期待能在iperf测试中看到接近百兆的传输速率。然而现实给了我一记重拳——发送速度卡在33.5Mbps就再也上不去了。…...

游戏装备交易验真程序,装备唯一标识上链,确认归属,防止盗号,假货交易。

⚠️ 说明:这是本地模拟区块链思路的演示程序,用于展示“装备唯一标识上链 归属确认”的核心机制,不是可直接上线运营的金融级系统。一、实际应用场景描述某中小型游戏工作室希望解决以下问题:- 玩家之间交易装备时,无…...

办公用品领用程序,领用归还记录上链,减少浪费,丢失,虚报领用。

办公用品领用上链管理系统设计方案 一、实际应用场景描述 某中型互联网公司(约200人)行政部门管理着包含笔记本电脑、投影仪、绘图板等高价值设备,以及硒鼓、墨盒、A4纸等高频消耗品。当前采用纸质登记表Excel台账的方式管理,每月…...

旅行拼团信用程序,团员爽约记录上链,降低组团风险,方便筛选靠谱伙伴。

旅行拼团信用上链系统设计方案一、实际应用场景描述户外徒步俱乐部“山野行者”定期组织跨省长线徒步(如川西环线、冈仁波齐转山),需提前30天统计人数并预订包车、高山协作及住宿。近一年出现多次“临出发前48小时内无故退团”事件&#xff0…...

别再折腾官方SDK了!手把手教你用这个优化版WPS Web Office V3 SDK快速集成(附Java/Solon Demo)

告别官方SDK的繁琐:高效集成WPS Web Office V3的实战指南 如果你正在寻找一种更简单、更高效的方式来集成WPS Web Office V3,那么你来对地方了。本文将带你深入了解如何利用优化版SDK快速完成集成,避开官方SDK的种种坑点,节省宝贵…...

员工绩效考核上链程序,指标数据不可篡改,公平公开,减少职场不公,暗箱操作。

员工绩效考核上链系统设计方案一、实际应用场景描述某科技公司研发团队采用OKR考核制度,存在跨部门评分标准不统一、绩效数据被HR私下修改、员工无法追溯历史评分记录等问题。本方案通过Python构建基于区块链的绩效存证系统,实现考核指标从录入到公示的全…...

SD-PPP架构方案:解决Photoshop与AI绘图平台无缝集成的技术挑战

SD-PPP架构方案:解决Photoshop与AI绘图平台无缝集成的技术挑战 【免费下载链接】sd-ppp A Photoshop AI plugin 项目地址: https://gitcode.com/gh_mirrors/sd/sd-ppp 传统AI绘图工作流中,设计师需要在Photoshop与ComfyUI/Stable Diffusion等AI平…...

Demucs-GUI:AI音乐分离工具的图形界面解决方案

Demucs-GUI:AI音乐分离工具的图形界面解决方案 【免费下载链接】Demucs-Gui A GUI for music separation AI demucs 项目地址: https://gitcode.com/gh_mirrors/de/Demucs-Gui 音乐制作和音频处理领域迎来了一次革命性的变化——AI音乐分离技术让任何人都能轻…...

FastGithub深度实战:5步打造GitHub极速访问的智能DNS加速方案

FastGithub深度实战:5步打造GitHub极速访问的智能DNS加速方案 【免费下载链接】FastGithub github定制版的dns服务,解析访问github最快的ip 项目地址: https://gitcode.com/gh_mirrors/fa/FastGithub FastGithub是一款专为GitHub优化的智能DNS加速…...

DxWrapper技术架构深度解析:Windows老游戏兼容性修复的底层实现机制

DxWrapper技术架构深度解析:Windows老游戏兼容性修复的底层实现机制 【免费下载链接】dxwrapper Fixes compatibility issues with older games running on Windows 10/11 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension…...

深入IgH EtherCAT DC同步:从‘主站参考’到‘从站参考’的时钟优化实践

深入IgH EtherCAT DC同步:从‘主站参考’到‘从站参考’的时钟优化实践 在工业自动化领域,EtherCAT因其卓越的实时性能而广受欢迎,而分布式时钟(DC)同步机制则是实现高精度控制的核心。传统的IgH主站实现默认采用主站时…...