table_crud.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package core
  2. import (
  3. "errors"
  4. "fmt"
  5. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  6. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  7. )
  8. // ======================== 高层 CRUD ========================
  9. // InsertRow 插入一行数据, pkID 由引擎按自增游标分配 (只增不减, 见 AllocPKID)。
  10. func (table *Table) InsertRow(values []types.FieldValue) (uint64, error) {
  11. if err := table.errIfReadOnly(); err != nil {
  12. return 0, err
  13. }
  14. defer table.db.lockOp()()
  15. return table.InsertRowWithID(0, values)
  16. }
  17. // InsertRowWithID 插入一行数据, pkID == 0 表示由引擎自增分配。
  18. //
  19. // 允许调用方指定主键: 上层 (ORM/导入) 往往已经拿到一个明确的行标识, 旧实现
  20. // 无条件用 TotalRows+1 覆盖, 结果是"调用方以为按 _id 写进去了, 之后按 _id 却
  21. // 查不到" —— 行确实存在, 只是不可达 (数据静默错位的一类)。
  22. //
  23. // 指定的主键若已存在则报 ErrDuplicatePK, 避免同一 pkID 在主键树里出现两条记录。
  24. // 例外: 引擎刚分配并"预留"的号 (AllocPKID) 必然唯一, 跳过这次查重 ——
  25. // 查重走的是回表 ResolveByPK, 会重建 O(页数) 的定位缓存, 而每次插入又使其失效;
  26. // 逐行插入将因此退化成 O(N²) (千万级写入从分钟级变成不可完成)。
  27. func (table *Table) InsertRowWithID(pkID uint64, values []types.FieldValue) (uint64, error) {
  28. if err := table.errIfReadOnly(); err != nil {
  29. return 0, err
  30. }
  31. defer table.db.lockOp()()
  32. defer table.db.beginOp()() // 插入 = 数据页+各级索引的多页写, 中途禁止阈值提交截断
  33. if pkID == 0 {
  34. pkID = table.AllocPKID()
  35. }
  36. // 游标状态 (预留消费 + 顶起) 统一在 pkMu 内读写, 与 AllocPKID 的
  37. // 取号/预留登记互斥: 并发写者交错时避免游标回退发重号或预留错位。
  38. table.pkMu.Lock()
  39. reserved := table.hasPendingPK && table.pendingPK == pkID
  40. table.hasPendingPK = false
  41. // 查重短路: nextPK 是"历史发号上界" (只增不减, 删除行不回退), 初始化自
  42. // 当前最大存活 pkID。pkID >= nextPK ⇒ 这个号从未发放过 ⇒ 必然不存在,
  43. // 跳过 rowStoreHasPK 回表查重。时序行存储按 pkID 取行是 O(段数+页内行扫),
  44. // 顺序写入时这笔开销占写入 CPU 约 36% (bench_ts profile); 短路必须在
  45. // 游标顶起之前判定 —— 顶起后 pkID < nextPK 恒成立, 上界信息就被覆盖了。
  46. possiblyExists := true
  47. if !reserved {
  48. table.ensureNextPK()
  49. possiblyExists = pkID < table.nextPK
  50. }
  51. // 显式指定更大的主键时把游标顶到它之后, 避免后续自增再发一次同样的号
  52. if pkID+1 > table.nextPK {
  53. table.nextPK = pkID + 1
  54. }
  55. table.pkMu.Unlock()
  56. if !reserved && possiblyExists {
  57. if table.indexMeta.rowStoreHasPK(pkID) {
  58. return 0, fmt.Errorf("%w: pkID=%d", ErrDuplicatePK, pkID)
  59. }
  60. }
  61. rowData, err := table.serializeRow(values, nil)
  62. if err != nil {
  63. return 0, err
  64. }
  65. if err := table.indexMeta.InsertRow(table.Field, pkID, rowData, table.activeVersion, table.versions); err != nil {
  66. // 唯一冲突的预检先于任何索引写入 (index_meta_page.InsertRow): 此时行没落进
  67. // 任何索引, 序列化时已提交的 blob 链成为孤儿, 必须还掉。其它错误 (页满 / IO)
  68. // 可能已部分写入, 不能动链 —— 那种半成品只能靠事务回滚兜底。
  69. if errors.Is(err, ErrDuplicatedKey) {
  70. _ = table.releaseRowBlobRefs(rowData)
  71. }
  72. return 0, err
  73. }
  74. table.info.TotalRows++
  75. table.saveTableInfo()
  76. if table.onWrite != nil {
  77. table.onWrite()
  78. }
  79. return pkID, nil
  80. }
  81. // InsertRowsWithIDs 批量插入。pkIDs[i]==0 的行由引擎自增分配 (号先拿到、写进
  82. // _id 列后再序列化, 与 ORM 单行路径同一约定); pkIDs[i]!=0 为显式主键, 逐个走
  83. // 查重。返回实际使用的 pkID 列表 (与入参等长)。
  84. //
  85. // 与逐行 InsertRowWithID 的语义差异只在"摊销", 不在"语义":
  86. // - 自增号来自只增不减的游标, 整批天然唯一, 跳过逐行查重 (单行路径靠
  87. // pendingPK 预留跳过同一件事; 批量下预留槽只有一格, 改为按"号段"整批豁免);
  88. // - 显式主键照旧 rowStoreHasPK 查重, 另补批内查重 (单行路径里批内冲突会
  89. // 在第 i+1 行的查重中被发现, 这里一次性比出来);
  90. // - 序列化 (含动态堆写入) 仍逐行做 —— 堆页的写入粒度是页级的, 堆块追加
  91. // 本身已经近似批量; saveTableInfo / onWrite 摊到批末一次。
  92. //
  93. // 失败语义: 序列化阶段报错时, 先前行写入的堆块成为无主垃圾 (泄漏而非错乱,
  94. // 行数据并未落任何索引, 不会被读到); 各先行行的 blob 链在报错时会被 serializeRow
  95. // 的兜底回收 (见 serializeRowInternal 的 newBlobRefs), 不随堆块一起泄漏。
  96. // 索引阶段报错时部分索引已写入 —— 与单行路径"写了一半报错"一致, 唯一冲突
  97. // (预检先于任何写入) 时整批 blob 链会回收, 其余错误靠事务回滚兜底。
  98. func (table *Table) InsertRowsWithIDs(pkIDs []uint64, values [][]types.FieldValue) ([]uint64, error) {
  99. if err := table.errIfReadOnly(); err != nil {
  100. return nil, err
  101. }
  102. defer table.db.lockOp()()
  103. return table.insertRows(pkIDs, values, false)
  104. }
  105. // InsertRowsReserved 批量插入"引擎预留号"的行: pkIDs 全部来自 AllocPKID
  106. // (ORM 先要号填进对象、再连同行数据一起送回来), 唯一性由只增不减的游标保证,
  107. // 逐行查重整个跳过 —— 语义同单行路径的 pendingPK 预留豁免, 只是预留号不止
  108. // 一格, 按号段整批豁免。批内查重仍保留 (号段自身不可能重复, 这层是防御)。
  109. //
  110. // 注意: 走这个入口的前提是调用方【没有】夹带任何非预留号 —— 夹带了请用
  111. // InsertRowsWithIDs (显式号走逐行查重)。
  112. func (table *Table) InsertRowsReserved(pkIDs []uint64, values [][]types.FieldValue) ([]uint64, error) {
  113. if err := table.errIfReadOnly(); err != nil {
  114. return nil, err
  115. }
  116. defer table.db.lockOp()()
  117. return table.insertRows(pkIDs, values, true)
  118. }
  119. func (table *Table) insertRows(pkIDs []uint64, values [][]types.FieldValue, allReserved bool) ([]uint64, error) {
  120. defer table.db.beginOp()() // 整批是一个操作: 批内任何一点都不许阈值提交
  121. n := len(values)
  122. if n == 0 {
  123. return nil, nil
  124. }
  125. if len(pkIDs) != n {
  126. return nil, fmt.Errorf("tsdb: 批量插入: pkIDs (%d) 与 values (%d) 数量不一致", len(pkIDs), n)
  127. }
  128. // 1) 补齐自增号
  129. auto := make([]bool, n)
  130. for i := 0; i < n; i++ {
  131. if pkIDs[i] == 0 {
  132. pkIDs[i] = table.AllocPKID()
  133. auto[i] = true
  134. }
  135. }
  136. // 预留槽消费与游标顶起都在 pkMu 内, 与 AllocPKID/单行路径互斥
  137. // skipDup[i]=true 表示"号从未发放" (pkID >= 下界), 第 3) 步免回表查重 —
  138. // 判定必须在游标顶起之前做 (与单行路径同一短路, 见 InsertRowWithID)。
  139. table.pkMu.Lock()
  140. table.hasPendingPK = false // 预留号已被本批整体消费
  141. skipDup := make([]bool, n)
  142. if !allReserved {
  143. table.ensureNextPK()
  144. for i := 0; i < n; i++ {
  145. skipDup[i] = !auto[i] && pkIDs[i] >= table.nextPK
  146. }
  147. for i := 0; i < n; i++ {
  148. if !auto[i] && pkIDs[i]+1 > table.nextPK {
  149. table.nextPK = pkIDs[i] + 1
  150. }
  151. }
  152. }
  153. table.pkMu.Unlock()
  154. // 2) 批内主键查重 (显式 vs 显式, 以及显式撞上自增号段)
  155. {
  156. seen := make(map[uint64]struct{}, n)
  157. for i := 0; i < n; i++ {
  158. if _, dup := seen[pkIDs[i]]; dup {
  159. return nil, fmt.Errorf("%w: pkID=%d (批内重复)", ErrDuplicatePK, pkIDs[i])
  160. }
  161. seen[pkIDs[i]] = struct{}{}
  162. }
  163. }
  164. // 3) 显式主键对既有行查重 (自增/预留号必然唯一; 号从未发放的免查)
  165. if !allReserved {
  166. for i := 0; i < n; i++ {
  167. if auto[i] || skipDup[i] {
  168. continue
  169. }
  170. if table.indexMeta.rowStoreHasPK(pkIDs[i]) {
  171. return nil, fmt.Errorf("%w: pkID=%d", ErrDuplicatePK, pkIDs[i])
  172. }
  173. }
  174. }
  175. // 4) 逐行序列化 (动态堆写入在这一步发生)
  176. rowDatas := make([][]byte, n)
  177. for i := 0; i < n; i++ {
  178. rowData, err := table.serializeRow(values[i], nil)
  179. if err != nil {
  180. return nil, err
  181. }
  182. rowDatas[i] = rowData
  183. }
  184. // 6) 索引层批量写入
  185. if err := table.indexMeta.InsertRows(table.Field, pkIDs, rowDatas, table.activeVersion, table.versions); err != nil {
  186. // 唯一预检是"全部查完才动第一笔写入" (index_meta_page.InsertRows):
  187. // 唯一冲突意味着零写入, 每行序列化时已落的 blob 链整批还掉。
  188. // 非唯一错误可能已部分写入, 链不能动 (同单行路径)。
  189. if errors.Is(err, ErrDuplicatedKey) {
  190. for _, rd := range rowDatas {
  191. _ = table.releaseRowBlobRefs(rd)
  192. }
  193. }
  194. return nil, err
  195. }
  196. table.info.TotalRows += uint64(n)
  197. table.saveTableInfo()
  198. if table.onWrite != nil {
  199. table.onWrite()
  200. }
  201. return pkIDs, nil
  202. }
  203. // ======================== 按行号 (pkID) 直接读写 ========================
  204. //
  205. // 索引键只决定"落在哪个分段/哪一批", 行号才唯一确定"是哪一行"。
  206. // 时序表里同一个时间戳允许存在多行 (同一时刻的多个测点/多次事件), 而上面那组
  207. // "按索引键取第一条"的入口只能碰到第一行 —— 其余行存得进、读不到、改不了、删不掉。
  208. // 下面这组入口按行号直达, 供 ORM (与任何需要精确定位一行的上层) 使用。
  209. // FindByPK 按行号读取一行的字段值。
  210. func (table *Table) FindByPK(pkID uint64) ([]types.FieldValue, error) {
  211. defer table.db.lockOp()()
  212. storeRow, found := table.indexMeta.ResolveByPK(pkID)
  213. if !found || len(storeRow) < 8 {
  214. return nil, page.ErrNotFound
  215. }
  216. return table.deserializeRow(storeRow[8:]), nil
  217. }
  218. // DeleteRowByPK 按行号删除一行, 并级联清掉它在全部索引里的条目。
  219. func (table *Table) DeleteRowByPK(pkID uint64) error {
  220. if err := table.errIfReadOnly(); err != nil {
  221. return err
  222. }
  223. defer table.db.lockOp()()
  224. defer table.db.beginOp()() // 删除 = 数据页+全部索引的多页写, 中途禁止阈值提交
  225. // 一次回表: 既拿到行数据用于释放动态字段的堆块, 也下传给索引层定位 pkID。
  226. storeRow, found := table.indexMeta.ResolveByPK(pkID)
  227. if !found || len(storeRow) < 8 {
  228. return page.ErrNotFound
  229. }
  230. name, _, ok := table.indexMeta.rowStoreMeta()
  231. if !ok {
  232. return ErrFieldNotFound
  233. }
  234. // indexKey 传 nil: 行数据在手, 每个索引都能自己从行里推出键 (时序键/主键列)。
  235. if err := table.indexMeta.DeleteByKey(name.Name(), nil, storeRow, table.Field, table.activeVersion, table.versions); err != nil {
  236. return err
  237. }
  238. table.heapReleaseRow(storeRow[8:])
  239. table.info.TotalRows--
  240. table.saveTableInfo()
  241. if table.onWrite != nil {
  242. table.onWrite()
  243. }
  244. return nil
  245. }
  246. // UpdateRowByPK 按行号更新一行, 并级联更新它在全部索引里的条目。
  247. func (table *Table) UpdateRowByPK(pkID uint64, values []types.FieldValue) error {
  248. if err := table.errIfReadOnly(); err != nil {
  249. return err
  250. }
  251. defer table.db.lockOp()()
  252. defer table.db.beginOp()() // 更新 = 数据页+全部索引的多页写, 中途禁止阈值提交
  253. // 旧行既用于复用动态字段堆块, 也用于让索引层提取"旧键" (时序键变了要搬分段)。
  254. storeRow, found := table.indexMeta.ResolveByPK(pkID)
  255. if !found || len(storeRow) < 8 {
  256. return ErrFieldNotFound
  257. }
  258. // 唯一性预检放在序列化之前: 见 checkUniqueUpdate。
  259. if err := table.checkUniqueUpdate(values, storeRow[8:], pkID); err != nil {
  260. return err
  261. }
  262. newRowData, err := table.serializeRow(values, storeRow[8:])
  263. if err != nil {
  264. return err
  265. }
  266. if err := table.indexMeta.UpdateByKey(nil, newRowData, pkID, storeRow, table.Field, table.activeVersion, table.versions); err != nil {
  267. return err
  268. }
  269. if table.onWrite != nil {
  270. table.onWrite()
  271. }
  272. return nil
  273. }