github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitree/bithash.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bitree 16 17 import ( 18 "fmt" 19 "time" 20 21 "github.com/zuoyebang/bitalosdb/bithash" 22 "github.com/zuoyebang/bitalosdb/internal/base" 23 "github.com/zuoyebang/bitalosdb/internal/hash" 24 "github.com/zuoyebang/bitalosdb/internal/utils" 25 ) 26 27 func (t *Bitree) bithashGetByHash(key []byte, khash uint32, fn uint32) ([]byte, func(), error) { 28 return t.bhash.Get(key, khash, bithash.FileNum(fn)) 29 } 30 31 func (t *Bitree) bithashGet(key []byte, fn uint32) ([]byte, func(), error) { 32 khash := hash.Crc32(key) 33 return t.bhash.Get(key, khash, bithash.FileNum(fn)) 34 } 35 36 func (t *Bitree) bithashDelete(fn uint32) error { 37 if !t.opts.UseBithash { 38 return nil 39 } 40 41 return t.bhash.Delete(bithash.FileNum(fn)) 42 } 43 44 func (t *Bitree) BithashStats() *bithash.Stats { 45 if t.bhash == nil { 46 return nil 47 } 48 return t.bhash.Stats() 49 } 50 51 func (t *Bitree) BithashDebugInfo(dataType string) string { 52 if t.bhash == nil { 53 return "" 54 } 55 return t.bhash.DebugInfo(dataType) 56 } 57 58 func (t *Bitree) CompactBithash(deletePercent float64) { 59 if t.bhash == nil { 60 return 61 } 62 63 logTag := fmt.Sprintf("[COMPACTBITHASH %d]", t.index) 64 65 delFiles := t.bhash.CheckFilesDelPercent(deletePercent) 66 delFilesNum := len(delFiles) 67 if delFilesNum > 1 { 68 var remainPercent float64 69 var delFileNums []bithash.FileNum 70 for i, file := range delFiles { 71 if file.DelPercent < 1 { 72 remainPercent += 1 - file.DelPercent 73 } 74 delFileNums = append(delFileNums, file.FileNum) 75 if remainPercent > 0.95 || (i == delFilesNum-1 && len(delFileNums) > 1) { 76 t.opts.Logger.Infof("%s compact bithash delPercent files start compactFn:%v remainPercent:%.4f", 77 logTag, delFileNums, remainPercent) 78 err := t.compactBithashFiles(delFileNums, logTag) 79 if err != nil { 80 t.opts.Logger.Errorf("%s compact bithash delPercent files fail err:%s", logTag, err) 81 } 82 remainPercent = 0 83 delFileNums = delFileNums[:0] 84 } 85 } 86 } 87 88 miniFiles := t.bhash.CheckFilesMiniSize() 89 miniFilesNum := len(miniFiles) 90 if miniFilesNum > 1 { 91 var remainSize int64 92 var miniFileNums []bithash.FileNum 93 fileMaxSize := t.bhash.TableMaxSize() - (1 << 20) 94 for i, file := range miniFiles { 95 if file.Size > 0 { 96 remainSize += file.Size 97 } 98 miniFileNums = append(miniFileNums, file.FileNum) 99 if remainSize > fileMaxSize || (i == miniFilesNum-1 && len(miniFileNums) > 1) { 100 t.opts.Logger.Infof("%s compact bithash miniSize files start compactFn:%v remainSize:%s", 101 logTag, miniFileNums, utils.FmtSize(uint64(remainSize))) 102 err := t.compactBithashFiles(miniFileNums, logTag) 103 if err != nil { 104 t.opts.Logger.Errorf("%s compact bithash miniSize files fail err:%s", logTag, err) 105 } 106 remainSize = 0 107 miniFileNums = miniFileNums[:0] 108 } 109 } 110 } 111 } 112 113 func (t *Bitree) compactBithashFiles(fileNums []bithash.FileNum, logTag string) error { 114 t.dbState.LockTask() 115 defer t.dbState.UnlockTask() 116 117 bw, err := t.bhash.NewBithashWriter(true) 118 if err != nil { 119 return err 120 } 121 122 dstFn := bw.GetFileNum() 123 124 defer func() { 125 if err != nil { 126 if err1 := bw.Remove(); err1 != nil { 127 t.opts.Logger.Errorf("%s remove BithashWriter fail err:%s", logTag, err1) 128 } 129 } 130 }() 131 132 var obsoleteFileNums []bithash.FileNum 133 var delKeyTotal int 134 mgFnMap := make(map[bithash.FileNum]bool, 1<<8) 135 delFnMap := make(map[bithash.FileNum]bool, 1<<8) 136 137 compactFile := func(srcFn bithash.FileNum) (e error) { 138 var iter *bithash.TableIterator 139 var mgKeyNum, delKeyNum int 140 141 start := time.Now() 142 143 iter, e = t.bhash.NewTableIter(srcFn) 144 if e != nil { 145 return 146 } 147 defer func() { 148 if iter != nil { 149 e = iter.Close() 150 } 151 152 cost := time.Since(start).Seconds() 153 if e == nil { 154 if delKeyNum > 0 { 155 delKeyTotal += delKeyNum 156 } 157 t.opts.Logger.Infof("%s compactFile %d to %d done mgKeyNum:%d delKeyNum:%d cost:%.4f", 158 logTag, srcFn, dstFn, mgKeyNum, delKeyNum, cost) 159 } else { 160 t.opts.Logger.Errorf("%s compactFile %d to %d fail err:%s", logTag, srcFn, dstFn, e) 161 } 162 }() 163 164 findKey := func(iterKey *base.InternalKey, khash uint32) bool { 165 v, vexist, vcloser := t.getInternal(iterKey.UserKey, khash) 166 if !vexist { 167 return false 168 } 169 defer vcloser() 170 171 dv := base.DecodeInternalValue(v) 172 return iterKey.SeqNum() == dv.SeqNum() 173 } 174 175 for ik, v, fn := iter.First(); iter.Valid(); ik, v, fn = iter.Next() { 176 if _, ok := delFnMap[fn]; !ok { 177 delFnMap[fn] = true 178 } 179 180 khash := hash.Crc32(ik.UserKey) 181 182 if !findKey(ik, khash) { 183 delKeyNum++ 184 continue 185 } 186 187 if e = bw.AddIkey(ik, v, khash, fn); e != nil { 188 t.opts.Logger.Errorf("%s compactFile AddIkey fail ikey:%s keyFn:%d dstFn:%d err:%s", logTag, ik.String(), fn, dstFn, e) 189 return 190 } 191 192 if _, ok := mgFnMap[fn]; !ok { 193 mgFnMap[fn] = true 194 } 195 mgKeyNum++ 196 } 197 198 for k := range delFnMap { 199 if _, ok := mgFnMap[k]; ok { 200 delete(delFnMap, k) 201 } 202 } 203 204 return 205 } 206 207 for _, compactFn := range fileNums { 208 if err = compactFile(compactFn); err != nil { 209 t.opts.Logger.Errorf("%s compactFile fail fn:%d err:%s", logTag, compactFn, err) 210 return err 211 } 212 213 obsoleteFileNums = append(obsoleteFileNums, compactFn) 214 } 215 216 if err = bw.Finish(); err != nil { 217 return err 218 } 219 220 for mgFn := range mgFnMap { 221 t.bhash.SetFileNumMap(dstFn, mgFn) 222 } 223 for delFn := range delFnMap { 224 t.bhash.DeleteFileNumMap(delFn) 225 } 226 227 if len(obsoleteFileNums) > 0 { 228 t.bhash.RemoveTableFiles(obsoleteFileNums) 229 } 230 231 delKeyTotalOld := int(t.bhash.StatsGetDelKeyTotal()) 232 if delKeyTotal > 0 { 233 t.bhash.StatsSubKeyTotal(delKeyTotal) 234 if delKeyTotalOld >= delKeyTotal { 235 t.bhash.StatsSubDelKeyTotal(delKeyTotal) 236 } else { 237 t.bhash.StatsSetDelKeyTotal(0) 238 } 239 } 240 241 t.opts.Logger.Infof("%s compact %v to %d success delKey:%d delKeyTotalOld:%d delKeyTotalNew:%d", 242 logTag, obsoleteFileNums, dstFn, delKeyTotal, delKeyTotalOld, int(t.bhash.StatsGetDelKeyTotal())) 243 244 return nil 245 }