| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- package core
- import (
- "TsDB2/DB/core/index"
- "TsDB2/DB/core/index/btree"
- "TsDB2/DB/core/index/datapage"
- "TsDB2/DB/core/page"
- "TsDB2/DB/core/types"
- "encoding/binary"
- "runtime/debug"
- "sort"
- "sync"
- )
- // Deleter 删除操作对象
- type Deleter struct {
- table *Table
- where Condition
- }
- // Delete 返回删除操作对象
- func (table *Table) Delete() *Deleter {
- return &Deleter{table: table}
- }
- // Where 设置删除条件
- func (d *Deleter) Where(cond Condition) *Deleter {
- d.where = cond
- return d
- }
- // Exec 执行删除
- // SQLite 风格优化: 先软删除 DataPage 行 (极快), 再重建索引 (流式, 低内存)
- // 避免逐行级联删除 B+ 树的高昂开销
- func (d *Deleter) Exec() (int64, error) {
- // 找到主键索引
- var pkMeta *IndexMeta
- for i := range d.table.indexMeta.metas {
- m := &d.table.indexMeta.metas[i]
- if m.Flag&0x80 != 0 {
- continue
- }
- if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
- pkMeta = &d.table.indexMeta.metas[i]
- break
- }
- }
- if pkMeta == nil {
- return 0, nil
- }
- // 用 Scan 流式收集匹配行的 pkID (避免 Find 反序列化全行)
- pkIDs := make(map[uint64]bool)
- err := d.table.Query().Where(d.where).Scan(func(row []types.FieldValue) bool {
- for _, fv := range row {
- if v, ok := fv.Value.(uint64); ok {
- pkIDs[v] = true
- break
- }
- }
- return true
- })
- if err != nil {
- return 0, err
- }
- if len(pkIDs) == 0 {
- return 0, nil
- }
- // Phase 1: 软删除 DataPage 中的行 (只标记 DeletedFlag, 极快!)
- pkHandler, _ := index.Compose.GetHandler(pkMeta.IndexType)
- dpIDs := pkHandler.GetDataPageIDs(d.table.handle, pkMeta.EntryPage)
- var count int64
- pkKeyBE := make([]byte, 8) // 复用缓冲区
- for _, dpID := range dpIDs {
- dp, err := datapage.Load(d.table.handle, dpID)
- if err != nil {
- continue
- }
- changed := false
- rows := dp.GetAllRows()
- for _, storeRow := range rows {
- if len(storeRow) < 8 {
- continue
- }
- pkID := binary.BigEndian.Uint64(storeRow[:8])
- if pkIDs[pkID] {
- binary.BigEndian.PutUint64(pkKeyBE, pkID)
- if dp.DeleteByPK(pkKeyBE) {
- changed = true
- count++
- }
- }
- }
- if changed {
- dp.Save()
- }
- }
- // 释放 pkIDs 内存 (Phase 2 不需要)
- pkIDs = nil
- // Phase 2: 流式重建所有索引 (逐个索引从 DataPage 读取, 不缓存全部行)
- if err := d.rebuildIndexes(pkMeta, dpIDs); err != nil {
- return count, err
- }
- // 更新行数
- d.table.info.TotalRows -= uint64(count)
- d.table.saveTableInfo()
- // 失效缓存
- d.table.indexMeta.primaryDPs = nil
- return count, nil
- }
- // rebuildIndexes 批量重建所有索引
- // 主键索引: 顺序创建 DataPage + 批量构建 B+ 树 (避免逐行搜索)
- // 二级索引: 收集条目 → 排序 → 批量构建 B+ 树
- func (d *Deleter) rebuildIndexes(pkMeta *IndexMeta, dpIDs []uint32) error {
- fields := d.table.Field
- rowSize := int(d.table.info.RowSize)
- // 内存池: 复用行缓冲区
- rowPool := &sync.Pool{
- New: func() interface{} {
- b := make([]byte, 8+rowSize)
- return &b
- },
- }
- // ===== 重建主键索引: 顺序扫描 + 批量构建 =====
- var leafEntries [][]byte
- var curDP *datapage.DataPage
- var curDPID uint32
- for _, dpID := range dpIDs {
- dp, err := datapage.Load(d.table.handle, dpID)
- if err != nil {
- continue
- }
- rows := dp.GetAllRowsPooled(rowPool)
- for _, storeRow := range rows {
- if len(storeRow) < 8 {
- continue
- }
- rowData := storeRow[8:]
- indexKey := extractIndexKey(rowData, pkMeta, fields, d.table.activeVersion, d.table.versions)
- if indexKey == nil {
- continue
- }
- // 确保有可用的 DataPage
- if curDP == nil {
- curDP = datapage.New(d.table.handle)
- curDPID = curDP.PageID()
- }
- // 当前 DataPage 已满则创建新的
- if curDP.IsFull(uint32(len(storeRow))) {
- curDP = datapage.New(d.table.handle)
- curDPID = curDP.PageID()
- }
- curDP.AppendRow(storeRow)
- curDP.Save()
- // 收集叶条目: [pkID(8B BE) + dpID(4B BE)]
- pkID := binary.BigEndian.Uint64(storeRow[:8])
- pkKeyBE := make([]byte, 8)
- binary.BigEndian.PutUint64(pkKeyBE, pkID)
- entry := make([]byte, 12)
- copy(entry[:8], pkKeyBE)
- binary.BigEndian.PutUint32(entry[8:], curDPID)
- leafEntries = append(leafEntries, entry)
- }
- // 归还缓冲区到池
- for i := range rows {
- rowPool.Put(&rows[i])
- }
- }
- // 批量构建主键 B+ 树 (一次创建所有节点, 避免逐行搜索)
- pkMeta.EntryPage = btree.BuildFromSorted(leafEntries, d.table.handle)
- d.table.indexMeta.saveMeta(pkMeta)
- leafEntries = nil // 释放内存
- debug.FreeOSMemory()
- // ===== 重建每个二级索引: 收集 → 排序 → 批量构建 =====
- for i := range d.table.indexMeta.metas {
- m := &d.table.indexMeta.metas[i]
- if m.Flag&0x80 != 0 {
- continue
- }
- if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
- continue
- }
- // 收集所有二级索引条目
- var secEntries [][]byte
- for _, dpID := range dpIDs {
- dp, err := datapage.Load(d.table.handle, dpID)
- if err != nil {
- continue
- }
- rows := dp.GetAllRowsPooled(rowPool)
- for _, storeRow := range rows {
- if len(storeRow) < 8 {
- continue
- }
- pkID := binary.BigEndian.Uint64(storeRow[:8])
- rowData := storeRow[8:]
- secKey := extractIndexKey(rowData, m, fields, d.table.activeVersion, d.table.versions)
- if secKey == nil {
- continue
- }
- // 构建二级条目: [secKey + pkID(8B BE)]
- secEntry := make([]byte, len(secKey)+8)
- copy(secEntry[:len(secKey)], secKey)
- binary.BigEndian.PutUint64(secEntry[len(secKey):], pkID)
- secEntries = append(secEntries, secEntry)
- }
- for i := range rows {
- rowPool.Put(&rows[i])
- }
- }
- // 按 secKey 排序 (稳定排序保持同 key 的 pkID 顺序)
- sort.SliceStable(secEntries, func(i, j int) bool {
- ki := secEntries[i][:len(secEntries[i])-8]
- kj := secEntries[j][:len(secEntries[j])-8]
- return compareBytes(ki, kj) < 0
- })
- // 批量构建二级 B+ 树
- m.EntryPage = btree.SBTBuildFromSorted(secEntries, d.table.handle)
- d.table.indexMeta.saveMeta(m)
- secEntries = nil
- debug.FreeOSMemory()
- }
- return nil
- }
- // compareBytes 按字典序比较两个字节切片
- func compareBytes(a, b []byte) int {
- ml := len(a)
- if len(b) < ml {
- ml = len(b)
- }
- for i := 0; i < ml; i++ {
- if a[i] < b[i] {
- return -1
- }
- if a[i] > b[i] {
- return 1
- }
- }
- if len(a) < len(b) {
- return -1
- }
- if len(a) > len(b) {
- return 1
- }
- return 0
- }
|