================================================================== TsDB2 ORM 全量特性验证 主键表(_id) / Hash 二级 / B+ 二级 / 时序表(_ts) ================================================================== ================================================================== 【0. 建表与索引清单 (索引类型由 vrtx tag 自动决定)】 employee.pk_employee IndexType=2 EntryPage=0 employee.idx_age IndexType=6 EntryPage=0 employee.idx_score IndexType=5 EntryPage=0 metric.pk_metric IndexType=1 EntryPage=0 [OK] Age 字段使用 Hash 索引 (IndexType=6) [OK] Score 字段使用 B+ 树索引 (IndexType=5) [OK] 主键 _id 使用 Primary 索引 (IndexType=2) [OK] 时序主键 _ts 使用 Time 索引 (IndexType=1) ================================================================== 【1. 主键表 CRUD (Employee: _id + Hash(Age) + B+(Score))】 [OK] Insert 20000 行 (0.98s, 20410 行/s), Count=20000 [OK] 主键 Find(12345) 命中且字段正确 [OK] Hash 等值查询 Age=25 → 10000 行 (期望 10000), 均摊 43.384183ms [OK] Hash 范围查询 Age∈[60,70] → 2000 行 (期望 2000), 均摊 18.16533ms (全桶扫描) [OK] B+ 范围查询 Score∈[200,299] → 2000 行, 有序=true, 均摊 21.993054ms → 延迟对比: Hash 等值 43.384183ms | Hash 范围 18.16533ms | B+ 范围 21.993054ms [OK] OrderBy(Score desc).Limit(5) 返回 5 行且分数降序 [OK] Offset(10).Limit(5) 分页返回 5 行 [OK] Where(Dept=D1).Count()=4000 (期望 4000, 全表扫描无索引) [OK] First() 返回单条 [OK] Scan 流式遍历 Limit(100) → 100 行 [OK] FindAll() → 20000 行 [INFO] Update 只传 Score 后: Name="" Age=0 Score=777 Dept="" (改前 Name="Emp001001") [OK] Update 为整行覆盖语义 (未赋值字段被清零) [OK] 改 Age(25→123) 100 行后: Age=25 → 9900 (期望 9900), Age=123 → 100 (期望 100) [OK] 改 Score(1→5000): 旧值计数=18, 新值计数=1 [OK] Delete 200 行: Count 20000→19800 (失败 0), Find(1)/(200) 失败, Find(201) 成功 [OK] 删除后 Hash 索引级联清理: Age=123 → 0 (期望 0); B+ 索引级联清理: Score=5000 → 0 (期望 0) ================================================================== 【2. 时序表 CRUD (Metric: _ts + 时序索引)】 [OK] 顺序写入 20000 个时间点 (0.31s, 64148 点/s), Count=20000 [OK] 时序主键 Find(ts) 精确命中 [OK] 时间范围 [_ts∈3.0s~3.999s] → 1000 行 (期望 1000), 有序=true, 在界内=true, 均摊 1.042896ms [OK] OrderBy(_ts desc).Limit(10) 取到最新 10 个点且降序 [OK] Where(Host=h2).Count()=5000 (期望 5000, 无索引全扫描) [OK] 时序行 Update → Find 校验 Value=9999 [OK] 时序行 Delete(ts) 后: Find 失败, Count=19999 (期望 19999) ================================================================== 【3. 时序写入的两个关键特征 (乱序 / 缺省时间戳)】 [OK] LateMetric 顺序写入 1000 点, Count=1000 [INFO] 乱序补写 3 个中间时间点: 插入错误=0, 主键 Find 命中=3/3, 时间范围扫描回读=1003 行 (期望 1003) [OK] 乱序补写的点可被主键 Find 命中 [OK] 时间范围扫描包含乱序补写的点 (1003 行) [INFO] 不设置 _ts 插入后, 结构体回写得到的 TS=1004 [OK] 缺省 _ts 时由 ORM 自动分配 (非真实时间戳, 等于行序号) ================================================================== 【4. 事务 (ORM 增删改 + 提交/回滚)】 [OK] 事务内插入 10 行并提交 → Where(Dept=TX)=10 [OK] 事务内插入后回滚 → Where(Dept=RB)=0 (期望 0) ================================================================== 【5. 关闭重开 (持久化验证)】 [OK] 重开后 employee Count 19810 → 19810 [OK] 重开后 metric Count 19999 → 19999 [OK] 重开后主键 Find(201) 命中 [OK] 重开后时序 Find(ts) 命中 [OK] 重开后 Hash 索引可用: Age=25 → 9900 [OK] 重开后 B+ 索引可用: Score∈[300,399] → 2010 行 数据库文件: 6.61 MB ================================================================== 【6. 四种索引形态特性对比】 主键 _id IndexType = 2 Primary 底层结构 = B+ 树 叶子/条目 = 叶子直接存行数据(DataPage) 查询复杂度 = 等值/范围 O(logN), 天然按 pk 有序 ORM 声明 = 内嵌 DB.BaseModel Hash 二级 IndexType = 6 Hash 底层结构 = 桶目录 + 溢出链 叶子/条目 = 叶子存 pkID, 需回主键取行 查询复杂度 = 等值 O(1); 范围=全桶扫描 O(N) ORM 声明 = vrtx:"index:x,type:hash" B+ 二级 IndexType = 5 BTree 底层结构 = B+ 树 叶子/条目 = 叶子存 pkID, 需回主键取行 查询复杂度 = 等值/范围/有序 均 O(logN) ORM 声明 = vrtx:"index:x" (默认) 时序 _ts IndexType = 1 Time 底层结构 = 扁平时间条目链 叶子/条目 = [Begin,Last,DataPageID] → 分段 DataPage 查询复杂度 = 时间范围只加载命中分段; 等值需扫段内行 ORM 声明 = 内嵌 DB.TimeSeriesModel ================================================================== 总计: 通过 39, 失败 0 ==================================================================