CacheSQL(一):手写数据库的工程化重生

CacheSQL(一):手写数据库的工程化重生


写过一套 2000 行的 B+ 树手写数据库,当时觉得"能跑了"。放了一阵子回头看,发现能跑和能交付之间隔了十万八千里。

CacheSQL 就是这段回头路走出来的。同一个 B+ 树内核,但外面长了三层新东西:复制、HTTP、测试。从原型变成了产品。


一、原型长什么样

这是手写数据库那版的核心结构:

复制代码
com.bluepointsoft.database
├── database.java          # 数据库入口,HashMap 管理表
├── praseSql.java          # SQL 解析
├── btree/
│   ├── BPTree.java        # B+ 树封装
│   ├── Node.java          # B+ 树节点(核心,1054 行)
│   └── Tree.java
├── table/
│   ├── Table.java         # 内存表
│   ├── Row.java           # 行数据(软删除)
│   └── rowSet.java        # 行集合
├── container/myList.java  # 自定义 long[] 数组
└── fun/function.java      # SQL 函数桩

这个版本解决了核心问题------B+ 树的插入、分裂、删除、合并、范围查询------但有几个硬伤:

  1. 没有测试。 能跑,但不知道什么情况下会崩。
  2. database 类用 ConcurrentHashMap 是原型遗留思维。 手写数据库那版我开始用 HashMap,后来多线程冲突才换成 ConcurrentHashMap,但当时没有真正理解这意味着什么------不是锁的问题,是一个项目需要从"跑的 Demo"切换到"可交付产品"。
  3. SQL 解析是散装函数,不是引擎。 praseSql.select_items(sql)praseSql.select_table(sql)praseSql.select_where(sql)------每次查都要调三个静态方法,各自解析一次 SQL。
  4. 没有对外 API。 只能内嵌调用 Java,没 HTTP 接口,其他语言用不了。
  5. 没有文档。 别人不知道这玩意怎么用。

二、工程化重生的五个改动

2.1 项目结构从平铺到分层

复制代码
com.browise.database
├── database.java              # 表管理 + 定时刷新
├── SqlQueryEngine.java        # SQL 引擎(独立模块)
├── btree/                     # B+ 树(核心不变)
├── table/                     # Table/Row/rowSet(核心不变)
├── index/CompositeKey.java    # 新增:联合索引
├── server/                    # 新增:HTTP 服务层
├── replication/               # 新增:主从复制
├── core/util/DBUtil.java      # 新增:连接池 + 配置管理
└── core/exception/utilException.java  # 新增:统一异常

核心层(btree + table)基本没动。新东西全加在外面,核心层连复制都不知道------ReplicationManager 对 Table 和 BPTree 完全透明。

2.2 从 HashMap 到 ConcurrentHashMap

手写数据库那版,database.java 用的是 HashMap<String, Table>。AI 审计时指出的第一个问题是"线程不安全"------两个线程同时 load 同一张表会互相覆盖。我换成 ConcurrentHashMap 时,没多想。后来才意识到这个切换意味着什么:

HashMap → ConcurrentHashMap 这个决策本身是"产品意识"的起点------你开始假设这个系统会被并发调用,而不是只在你自己的电脑上跑一个线程。

CacheSQL 里更进一步。tables.putIfAbsent 替代了 tables.put,防止并发 load 时创建重复 Table。refresh 时用 tables.put(name, fresh) 原子替换------先创建新 Table 实例加载最新数据,完成后再替换引用,刷新期间的查询不受影响。

2.3 SQL 解析从散装函数到独立引擎

手写数据库那版 SQL 解析是三个静态函数:

java 复制代码
List<String> items  = praseSql.select_items(sql);
List<String> tables = praseSql.select_table(sql);
String where        = praseSql.select_where(sql);

CacheSQL 拆出了独立的 SqlQueryEngine:SQL 模板化 → 解析 → 缓存执行计划 → 下次同模板直接复用,省掉重复解析。这个设计让后续加 HTTP 接口时天然就支持了 SQL 查询------不需要单独实现一套查询逻辑。

2.4 从零测试到 53 项测试 + 6 项集成

手写数据库那版没有任何测试用例。CacheSQL 加了两层测试:

  • 53 项单元测试:B+ 树 15 项,SQL 引擎 11 项,内存表 17 项,复制模块 8 项,并发 2 项
  • 6 项集成测试:端到端验证主从复制的完整生命周期------写入广播、Slave 转发、幂等性、Master 宕机缓冲、恢复后自动重放

不是说测试越多越好。但一个没有测试的产品,你不敢交付给任何人。

2.5 从零文档到 7 份

功能说明书、部署手册、操作手册、测试报告、性能测试、两版审计报告。加上已有的 4 篇手写数据库博客------这些文档不是给评委看的,是给未来任何一个接手这个系统的人看的。


三、什么没变

B+ 树核心逻辑几乎没变。

  • 256 阶:和原型一样,一次加载 256 个子节点,树高度为 2(100万数据)
  • 软删除 :原型里的 isDelete 标记保留,仍是 O(1),不移动 ArrayList 元素
  • 预留空间:原型里 10% 的预留设计保留,批量插入时减少分裂频率
  • 关键字校准 validate:原型里那个踩了大坑的函数保留------分裂后父节点的 entry key 不更新就会路由出错

不是什么都要重写。核心对了,外围加工程化就好。


四、AI 在这个过程中的角色

手写数据库 4 篇博客提到的 B+ 树算法(分裂、借位合并、二分变体、validate 校准),有一些是我自己写的(软删除、预留都是我的经验),有一些是 AI 辅助生成的(代码框架、文档结构)。

CacheSQL 的工程化升级,AI 承担了大部分"写代码"的工作:项目结构、测试用例、文档框架、异常处理、配置文件解析。我承担的是架构决策:ReplicationManager 对核心层透明、OpLog 用定长环形缓冲区而非 ArrayList、insert 用 upsert 语义保证幂等------这些没有一个是 AI 主动建议的。

用 AI 写代码,用经验做决策。 这就是从原型到产品之间,AI 和人的分工。


下一篇:C[acheSQL(二):主从复制------OpLog 环形缓冲区与故障自动恢复](https://blog.csdn.net/yuhou25/article/details/160674516?spm=1001.2014.3001.5502)


系列:CacheSQL 工程化交付实录(共 5 篇,含桥接篇)

相关推荐
亦暖筑序2 小时前
Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流
java·后端
敲代码的彭于晏3 小时前
Bean 生命周期完全图解:前端同学也能看懂的 Spring 核心机制
java·前端·后端
plainGeekDev4 小时前
ButterKnife → ViewBinding
android·java·kotlin
GBASE18 小时前
G术时刻 |GBase 8s数据库事务并发控制之封锁技术介绍(下)
数据库
像我这样帅的人丶你还20 小时前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩20 小时前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
tntxia21 小时前
Mybatis的日志输入
java
亦暖筑序1 天前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户298698530141 天前
Java 实现 Word 文档加密与权限解除
java·后端
Yeats_Liao1 天前
14:Servlet中的页面跳转-Java Web
java·后端·架构