GoLang实现基于本地文件的海量数据存储和读取
基于 MMAP(Memory mapping)技术,将硬盘映射到内存,轻松映射超过 100G 硬盘空间至内存(不影响实际内存占用),相当于无限内存。
开源项目:https://github.com/vito-go/msearch/
本文添加个人理解的注释,未修改代码
README main
package main
import (
"fmt"
"github.com/vito-go/msearch"
)
func main() {
fileName := "test.msearch" // 底层文件名
ms, err := msearch.NewMsearch(fileName, 0)
if err != nil {
panic(err)
}
user := "example@example.com"
userLi := "userLi@example.com"
values := []string{"abc429298@example.com", "abc429179@example.com", "abc429178@example.com", "abc429177@example.com", "abc429176@example.com", "kadhx11@example.com", "kadhx1@example.com", "1101010022@example.com"}
err = ms.Add(user, values...) // 添加一组values
if err != nil {
panic(err)
}
fmt.Println(ms.Get(user)) // 获取一个key对应的一组values
// 添加一个value
if err=ms.Add(userLi, "aaa@163.com|nickname1");err!=nil{
panic(err)
}
ms.Del(user, values[:len(values)-2]...) // 删除多个value
fmt.Println(ms.Get(user)) // 获取一组values
fmt.Println(ms.Get(`userNil`)) // 获取一组values,如果没有values返回空数组。
}主要方法
部分注释个人添加,方便理解
//go:build !windows
// Package msearch 基于mmap技术的,以本地文件为基础的搜索技术。提供增加、删、查(简单的替代mysql。)
// 单个 value 长度不能超过255. // TODO if needed?
// [_8(total) _1 key _1(len) xxx _1(len) xxx _8(overflow offset)]
package msearch
import (
"encoding/binary"
"errors"
"os"
"strings"
"sync"
"syscall"
)
// notExist 标记不存在的key. // TODO 好像这个标记没什么用
const notExist = -1
// DefaultLength 默认映射空间大小 64 GB,不影响实际内存大小。
const DefaultLength = 64 << 30
type MSearcher interface {
Add(key string, values ...string) error
Del(key string, values ...string)
Get(key string) []string
DelByPrefix(key string, values ...string)
Update(key string, values ...string) error
Exist(key string) bool
}
// Msearch It's safe for concurrent use by multiple goroutines.
type Msearch struct {
mu sync.RWMutex // mu to protect the follow fields
f *os.File // After the syscall.Mmap() call has returned, the file descriptor, fd, can be closed immediately
// without invalidating the mapping. But after f.Close(), we can't write any data to the file.
// So, the f should not call Close().
offset int // last offset of the f
keyMap map[string]int // store all keys, value is the offset in bytesAddr of every key
bytesAddr []byte // bytesAddr is the virtual address space of the process
}
// NewMsearch create a new Msearch by file and length。
// file is the path of the underlying file.
// the length argument specifies the length of the mapping (which must be greater than 0)
// it has no impact on the real memory. the default value is 64GB.
func NewMsearch(file string, length int) (*Msearch, error) {
f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, err
}
if length <= DefaultLength {
length = DefaultLength
}
// 追加用f.Write 读取和修改用MMap
bytesAddr, err := syscall.Mmap(int(f.Fd()), 0, length, syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return nil, err
}
return &Msearch{
mu: sync.RWMutex{},
f: f,
offset: 0,
keyMap: make(map[string]int, 1<<10),
bytesAddr: bytesAddr,
}, nil
}
// Get one or more value.
func (s *Msearch) Get(key string) []string {
s.mu.RLock()
defer s.mu.RUnlock()
return s.gets(key)
}
// Add one or more value.
func (s *Msearch) Add(key string, values ...string) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.adds(key, values...)
}
// Del one or more value.
func (s *Msearch) Del(key string, values ...string) {
s.mu.Lock()
defer s.mu.Unlock()
s.dels(key, values...)
}
// DelByPrefix delete values by prefix.
func (s *Msearch) DelByPrefix(key string, values ...string) {
s.mu.Lock()
defer s.mu.Unlock()
s.delsPrefix(key, values...)
}
// Update will delete all the old values of key and set it to the newValues.
func (s *Msearch) Update(key string, newValues ...string) error {
s.mu.Lock()
defer s.mu.Unlock()
oldValues := s.gets(key)
s.dels(key, oldValues...)
err := s.adds(key, newValues...)
return err
}
// Exist check the key whether exists.
func (s *Msearch) Exist(key string) bool {
s.mu.Lock()
defer s.mu.Unlock()
if offset, ok := s.keyMap[key]; ok && offset != notExist {
return true
}
s.keyMap[key] = notExist
return false
}
func (s *Msearch) delsPrefix(key string, values ...string) {
offset, ok := s.keyMap[key]
if !ok {
return
}
if len(values) == 0 {
return
}
for {
d := s.delPrefix(offset, values...)
if d == 0 {
break
}
offset = d
}
}
func (s *Msearch) dels(key string, values ...string) {
offset, ok := s.keyMap[key]
if !ok {
return
}
valueMap := make(map[string]struct{}, len(values))
for _, value := range values {
valueMap[value] = struct{}{}
}
if len(valueMap) == 0 {
return
}
for {
d := s.del(offset, valueMap)
if d == 0 {
break
}
offset = d
}
}
func (s *Msearch) gets(key string) []string {
offset, ok := s.keyMap[key]
if !ok || offset == notExist {
return nil
}
var lists []string
var d int
for {
var list []string
list, d = s.get(offset)
lists = append(lists, list...)
if d == 0 {
break
}
offset = d
}
return lists
}
// empty 插入判断是否有空位,以及空位的长度.
func (s *Msearch) empty(offset int) (o int, start int, end int, t bool) {
var lastDec int
for {
o, lastDec, start, end, t = s.empty1(offset)
if lastDec == 0 || t {
break
}
offset = lastDec
}
return
}
// getB8byOffset 这个offset是每个value的起始offset 得到最后的一个8位 offset只能通过s.keyMap 获得。
func (s *Msearch) getB8byOffset(offset int) (b8 []byte) {
var lastDec int
for {
lastDec, b8 = s.b8(offset)
if lastDec == 0 {
break
}
offset = lastDec
}
return
}
// empty1 是否有空位,以及空位的长度.
func (s *Msearch) empty1(offset int) (o int, lastDec int, start int, end int, t bool) {
// t为false的时候 也就是没有空位 有b8
var first bool
// 获取kv的前8位,得到数据的总长度
total := bigUint64(s.bytesAddr[offset : offset+8])
// 获取kv的全部数据
b := s.bytesAddr[offset : offset+total]
o = offset
for i := int(b[8] + 1 + 8); i < len(b[:len(b)-8]); {
// value长度为0
if b[i] == 0 {
if !first {
// 第一次遇到空值时候
first = true
t = true
start = i
}
i++
continue
}
if t {
end = i
return
}
i += int(b[i]) + 1
}
if t && end == 0 {
end = total - 8
}
lastDec = bigUint64(b[total-8 : total])
return
}
func (s *Msearch) b8(offset int) (lastDec int, b8 []byte) {
// t为false的时候 也就是没有空位 有b8
if offset >= s.offset {
// 当前的偏移量已超过总的数据长度,说明没有位置了,返回nil,后面会追加
return 0, nil
}
// 拿到当前offset的后面(具体是后面8是下一次kv的总长度)的值,即next的lens
// 获取当前kv在磁盘的总长度
total := bigUint64(s.bytesAddr[offset : offset+8])
// 获取数据的最后8位,
b8 = s.bytesAddr[offset+total-8 : offset+total]
// 获取全部数据
b := s.bytesAddr[offset : offset+total]
// 截取最后8位数据
lastDec = bigUint64(b[total-8 : total])
return
}
// data = {
// [0 - 7] : 当前kv的总长度
// [8] : key字符串的长度
// [9 - len(key)] : 字符串key内容
// [9+len(key)+n*len(value) - 9+len(key)+(n+1)*len(next_value)] : 每个value的内容
// [ -8 : ] : 最后8位是kv链表的下一链
// }
func (s *Msearch) add(b8 []byte, key string, values ...string) (int, error) {
// 初始化一块地方
var b = make([]byte, 1<<10)
// 保存key字符串的长度
b[8] = byte(len(key))
// 保存在[9 - len(key)+8 ]的key字符串内容
n := copy(b[9:], key)
// 计算idx的位置,做为存储values的开始
idx := n + 1 + 8
for _, value := range values {
if len(b) < idx+len(value)+2 {
// 容量不足就扩容 扩容一定要覆盖下面的copy
b = append(b, make([]byte, 1<<10)...)
}
// todo len(value)大于255??
if len(value) > 255 {
return 0, errors.New("value exceed max length 255")
}
// 保存这个value字符串的长度
b[idx] = byte(len(value))
// 保存value字符串内容
// 一定要注意copy的地方
copy(b[idx+1:], value)
idx += 1 + len(value)
}
// 总数据长度+8字节保留
total := idx + 8
// binary.BigEndian.PutUint64(b[idx:], uint64(total+s.offset)) // todo 是否有必要??
// 切片所需数据的完整长度
b = b[:total]
// 设置total到前8位,作为当前kv的总长度
binary.BigEndian.PutUint64(b[:8], uint64(total))
// 写到文件
_, err := s.f.Write(b)
if err != nil {
return 0, err
}
if i, ok := s.keyMap[key]; !ok || i == notExist {
// 如果key不存在,就将添加key 的map中,value为当前数据的在总位置上的偏移量(第一个是0)
s.keyMap[key] = s.offset
}
if len(b8) > 0 {
// b8是上一链的结尾
// 末尾的
binary.BigEndian.PutUint64(b8, uint64(s.offset))
}
// 计算为下一个的kv的开始偏移量
s.offset += total
return total, err
}
func (s *Msearch) adds(key string, values ...string) error {
if len(values) == 0 {
return nil
}
offset, ok := s.keyMap[key]
// 不存在
if !ok || offset == notExist {
// 不存在时候,需要直接追加写入磁盘
_, err := s.add(nil, key, values...)
return err
}
// t 是否能插空 插空进入
// s.bytesAddr[offset:offset+8]
if len(values) == 1 {
value := values[0]
o, start, end, t := s.empty(offset)
if t && len(value) < (end-start) {
total := bigUint64(s.bytesAddr[offset : offset+8])
b := s.bytesAddr[o : o+total]
b[start] = byte(len(value))
copy(b[start+1:], value)
return nil
}
}
b8 := s.getB8byOffset(offset)
// 存在但是有多个value值 或者 没有空的地方插入时候,需要直接追加写入磁盘
_, err := s.add(b8, key, values...)
return err
}
func (s *Msearch) del(offset int, valueMap map[string]struct{}) int {
total := bigUint64(s.bytesAddr[offset : offset+8])
if total == 0 {
return 0
}
// 全部数据
b := s.bytesAddr[offset : offset+total]
// 遍历value数据字节
for i := int(b[8] + 1 + 8); i < len(b[:len(b)-8]); {
bi := int(b[i])
if bi == 0 {
// 跳过空数据
i++
continue
}
// 构建value值
value := string(b[i+1 : i+1+int(b[i])])
// value在需要删除的列表里
// 这里为什么使用map保存数据,因为这边要遍历所有value,map自带的算法更快
if _, ok := valueMap[value]; ok {
// 将value数据copy位0值
copy(b[i+1:i+1+int(b[i])], make([]byte, int(b[i])))
// 将长度设置为0值
b[i] = 0
}
// 跳到下一个value位置
i += bi + 1
}
// 返回结尾的8位数据
return bigUint64(b[total-8 : total])
}
func (s *Msearch) delPrefix(offset int, values ...string) int {
total := bigUint64(s.bytesAddr[offset : offset+8])
if total == 0 {
return 0
}
b := s.bytesAddr[offset : offset+total]
for i := int(b[8] + 1 + 8); i < len(b[:len(b)-8]); {
bi := int(b[i])
if bi == 0 {
i++
continue
}
value := string(b[i+1 : i+1+int(b[i])])
// 这里为什么手动遍历,因为自己遍历有更多的判断和选择
for _, v := range values {
// 字符串是否以前缀开头
if strings.HasPrefix(value, v) {
copy(b[i+1:i+1+int(b[i])], make([]byte, int(b[i])))
b[i] = 0
}
}
i += bi + 1
}
return bigUint64(b[total-8 : total])
}
// 获取这一链的全部数据,并返回value list和下一链offset
func (s *Msearch) get(offset int) ([]string, int) {
// 数据长度
total := bigUint64(s.bytesAddr[offset : offset+8])
// 全部数据
b := s.bytesAddr[offset : offset+total]
var list []string
for i := int(b[8] + 1 + 8); i < len(b[:len(b)-8]); {
if b[i] == 0 {
// 0是表示这个空间无意义嘛?是被删除了设置为0值嘛?
i++
continue
}
list = append(list, string(b[i+1:i+1+int(b[i])]))
i += int(b[i]) + 1
}
lastDec := bigUint64(b[total-8 : total])
return list, lastDec
}
// bigUint64 对大数字进行解码 长度为0-8位的字节切片. binary.BigEndian.PutUint64 是编码.
// Deprecated: please use binary.BigEndian.Uint64
func bigUint64(buf []byte) int {
if len(buf) > 8 {
return 0
}
var x int
for _, b := range buf {
x = x<<8 | int(b)
}
return x
}个人修改版本
//go:build !windows
// Package msearch 基于mmap技术的,以本地文件为基础的搜索技术。提供增加、删、查(简单的替代mysql。)
// 单个 value 长度不能超过255. // TODO if needed?
// [_8(total) _1 key _1(len) xxx _1(len) xxx _8(overflow offset)]
package main
import (
"encoding/binary"
"errors"
"fmt"
"os"
"strings"
"sync"
"syscall"
"time"
)
// notExist 标记不存在的key. // TODO 好像这个标记没什么用
const notExist = -1
// DefaultLength 默认映射空间大小 64 GB,不影响实际内存大小。
const DefaultLength = 64 << 30
type MSearcher interface {
Add(key string, values ...string) error
Del(key string, values ...string)
Get(key string) []string
DelByPrefix(key string, values ...string)
Update(key string, values ...string) error
Exist(key string) bool
}
// Msearch It's safe for concurrent use by multiple goroutines.
type Msearch struct {
mu sync.RWMutex // mu to protect the follow fields
f *os.File // After the syscall.Mmap() call has returned, the file descriptor, fd, can be closed immediately
// without invalidating the mapping. But after f.Close(), we can't write any data to the file.
// So, the f should not call Close().
offset int // last offset of the f
keyMap map[string]int // store all keys, value is the offset in bytesAddr of every key
bytesAddr []byte // bytesAddr is the virtual address space of the process
}
func openMsearch(file string, length int) (*Msearch, error) {
f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, err
}
fInfo, err := f.Stat()
if err != nil {
return nil, err
}
if length <= DefaultLength {
length = DefaultLength
}
// 追加用f.Write 读取和修改用MMap
bytesAddr, err := syscall.Mmap(int(f.Fd()), 0, length, syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return nil, err
}
var keyMap map[string]int = make(map[string]int, 1<<10)
if length > 0 {
// _ = fInfo
keyMap = restart(int(fInfo.Size()), bytesAddr)
}
a := &Msearch{
mu: sync.RWMutex{},
f: f,
offset: 0,
keyMap: keyMap,
bytesAddr: bytesAddr,
}
// a.empty1(782944)
return a, nil
}
// NewMsearch create a new Msearch by file and length。
// file is the path of the underlying file.
// the length argument specifies the length of the mapping (which must be greater than 0)
// it has no impact on the real memory. the default value is 64GB.
func NewMsearch(file string, length int) (*Msearch, error) {
bakFile := file + "." + time.Now().Format("2006_01_02_15_04_05")
os.Rename(file, bakFile)
m, err := openMsearch(bakFile, length)
if err != nil {
return nil, err
}
relM, err := openMsearch(file, length)
if err != nil {
return nil, err
}
for k, _ := range m.keyMap {
values := m.Get(k)
relM.Add(k, values...)
}
m.f.Close()
return relM, err
}
func restart(fileSize int, bytesAddr []byte) map[string]int {
var keyMap map[string]int = make(map[string]int, 1<<10)
// return keyMap
var offset int
for offset < fileSize {
// 读取一个数据长度
total := bigUint64(bytesAddr[offset : offset+8])
keyLen := bigUint64(bytesAddr[offset+8 : offset+12])
if keyLen != 0 {
key := bytesAddr[offset+12 : offset+12+keyLen]
// valueLen := bigUint64(bytesAddr[offset+12+keyLen : offset+12+keyLen+4])
// value := bytesAddr[offset+12+keyLen+4 : offset+12+keyLen+4+valueLen]
// _ = value
// // 读取一个数据
// b := bytesAddr[offset : offset+total]
// var list []string
// key := b[ 9 : 9+int(b[8])]
// value := bytesAddr[ 9+int(b[8]) : ]
// for i := int(b[8] + 1 + 8); i < len(b[:len(b)-8]); {
// if b[i] == 0 {
// // 0是表示这个空间无意义嘛?是被删除了设置为0值嘛?
// i++
// continue
// }
// list = append(list, string(b[i+1:i+1+int(b[i])]))
// i += int(b[i]) + 1
// }
// lastDec := bigUint64(b[total-8 : total])
// return list, lastDec
// fmt.Printf("key = %#v \n",string(key))
// fmt.Printf(" value = %#v \n",string(value))
// fmt.Printf("offset = %#v \n",offset)
// return nil
// if i, ok := s.keyMap[key]; !ok || i == notExist {
_, ok := keyMap[string(key)]
if !ok {
keyMap[string(key)] = offset
}
}
offset += total
}
return keyMap
}
func (s *Msearch) Print() {
s.mu.RLock()
defer s.mu.RUnlock()
fmt.Printf("会话数据:%#v\n", s.keyMap)
}
// Get one or more value.
func (s *Msearch) Get(key string) []string {
s.mu.RLock()
defer s.mu.RUnlock()
return s.gets(key)
}
// Add one or more value.
func (s *Msearch) Add(key string, values ...string) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.adds(key, values...)
}
// Del one or more value.
func (s *Msearch) Del(key string, values ...string) {
s.mu.Lock()
defer s.mu.Unlock()
s.dels(key, values...)
}
// DelByPrefix delete values by prefix.
func (s *Msearch) DelByPrefix(key string, values ...string) {
s.mu.Lock()
defer s.mu.Unlock()
s.delsPrefix(key, values...)
}
// Update will delete all the old values of key and set it to the newValues.
func (s *Msearch) Update(key string, newValues ...string) error {
s.mu.Lock()
defer s.mu.Unlock()
oldValues := s.gets(key)
s.dels(key, oldValues...)
err := s.adds(key, newValues...)
return err
}
// Exist check the key whether exists.
func (s *Msearch) Exist(key string) bool {
s.mu.Lock()
defer s.mu.Unlock()
if offset, ok := s.keyMap[key]; ok && offset != notExist {
return true
}
s.keyMap[key] = notExist
return false
}
func (s *Msearch) delsPrefix(key string, values ...string) {
offset, ok := s.keyMap[key]
if !ok {
return
}
if len(values) == 0 {
return
}
for {
d := s.delPrefix(offset, values...)
if d == 0 {
break
}
offset = d
}
}
func (s *Msearch) dels(key string, values ...string) {
offset, ok := s.keyMap[key]
if !ok {
return
}
valueMap := make(map[string]struct{}, len(values))
for _, value := range values {
valueMap[value] = struct{}{}
}
if len(valueMap) == 0 {
return
}
for {
d := s.del(offset, valueMap)
if d == 0 {
break
}
offset = d
}
}
func (s *Msearch) gets(key string) []string {
offset, ok := s.keyMap[key]
if !ok || offset == notExist {
return nil
}
var lists []string
var d int
for {
var list []string
list, d = s.get(offset)
lists = append(lists, list...)
if d == 0 {
break
}
offset = d
}
return lists
}
// getB8byOffset 这个offset是每个value的起始offset 得到最后的一个8位 offset只能通过s.keyMap 获得。
func (s *Msearch) getB8byOffset(offset int) (b8 []byte) {
var lastDec int
for {
lastDec, b8 = s.b8(offset)
if lastDec == 0 {
break
}
offset = lastDec
}
return
}
func (s *Msearch) b8(offset int) (lastDec int, b8 []byte) {
// t为false的时候 也就是没有空位 有b8
if offset >= s.offset {
// 当前的偏移量已超过总的数据长度,说明没有位置了,返回nil,后面会追加
return 0, nil
}
// 拿到当前offset的后面(具体是后面8是下一次kv的总长度)的值,即next的lens
// 获取当前kv在磁盘的总长度
total := bigUint64(s.bytesAddr[offset : offset+8])
// 获取数据的最后8位,
b8 = s.bytesAddr[offset+total-8 : offset+total]
lastDec = bigUint64(b8)
return
}
// data = {
// [0 - 7] : 当前kv的总长度
// [8] : key字符串的长度
// [9 - len(key)] : 字符串key内容
// [9+len(key)+n*len(value) - 9+len(key)+(n+1)*len(next_value)] : 每个value的内容
// [ -8 : ] : 最后8位是kv链表的下一链
// }
func (s *Msearch) add(b8 []byte, key string, value string) (int, []byte, error) {
// 初始化一块地方
var b = make([]byte, 1<<10)
// 保存key字符串的长度
{
keyLen := len(key)
// b[8] = uint8(keyLen)
// b[9] = uint8(keyLen >> 8)
// b[10] = uint8(keyLen >> 16)
// b[11] = uint8(keyLen >> 24)
b[8] = uint8(keyLen >> 24)
b[9] = uint8(keyLen >> 16)
b[10] = uint8(keyLen >> 8)
b[11] = uint8(keyLen)
}
// 保存在[9 - len(key)+11 ]的key字符串内容
n := copy(b[12:], key)
// 计算idx的位置,做为存储values的开始
idx := n + 12
// fmt.Printf("1 %#v, %#v, %#v\n", cap(b), len(b), idx)
// for _, value := range values {
for true {
if len(b) < idx+len(value)+12 {
// 容量不足就扩容 扩容一定要覆盖下面的copy
b = append(b, make([]byte, 1<<10)...)
// fmt.Printf("2 %#v, %#v, %#v , %#v\n", cap(b), len(b), idx, len(value))
} else {
break
}
}
// 因为字符串长度为1个字节保存 len(value)不能大于255
// if len(value) > 255 {
// return 0, errors.New("value exceed max length 255")
// }
// 保存这个value字符串的长度
valueLen := len(value)
// b[idx] = byte(len(value))
b[idx] = uint8(valueLen >> 24)
b[idx+1] = uint8(valueLen >> 16)
b[idx+2] = uint8(valueLen >> 8)
b[idx+3] = uint8(valueLen)
idx += 3
// 保存value字符串内容
// 一定要注意copy的地方
copy(b[idx+1:], value)
idx += 1 + len(value)
// fmt.Printf("3 %#v, %#v, %#v\n", cap(b), len(b), idx)
// }
// fmt.Printf("4 %#v, %#v, %#v\n", cap(b), len(b), idx)
// 总数据长度+8字节保留
total := idx + 8
// binary.BigEndian.PutUint64(b[idx:], uint64(total+s.offset)) // todo 是否有必要??
// 切片所需数据的完整长度
b = b[:total]
// 设置total到前8位,作为当前kv的总长度
binary.BigEndian.PutUint64(b[:8], uint64(total))
// 写到文件
_, err := s.f.Write(b)
if err != nil {
return 0, nil, err
}
if i, ok := s.keyMap[key]; !ok || i == notExist {
// 如果key不存在,就将添加key 的map中,value为当前数据的在总位置上的偏移量(第一个是0)
s.keyMap[key] = s.offset
}
if len(b8) > 0 {
// b8是上一链的结尾
// 末尾的
binary.BigEndian.PutUint64(b8, uint64(s.offset))
}
// 计算为下一个的kv的开始偏移量
s.offset += total
return total, b[total-8:], err
}
func (s *Msearch) adds(key string, values ...string) error {
if len(values) == 0 {
return nil
}
for _, v := range values {
if len(v) >= 1024*1024*1024*4 {
return errors.New("value exceed max length 4294967295 ")
}
}
offset, ok := s.keyMap[key]
var err error
// 不存在
if !ok || offset == notExist {
// 不存在时候,需要直接追加写入磁盘
var b8 []byte
for _, v := range values {
_, b8, err = s.add(b8, key, v)
if err != nil {
return err
}
}
}
// t 是否能插空 插空进入
// s.bytesAddr[offset:offset+8]
// if len(values) == 1 {
// value := values[0]
// o, start, end, t := s.empty(offset)
// if t && len(value) < (end-start) {
// total := bigUint64(s.bytesAddr[offset : offset+8])
// b := s.bytesAddr[o : o+total]
// b[start] = byte(len(value))
// copy(b[start+1:], value)
// return nil
// }
// }
b8 := s.getB8byOffset(offset)
// 存在但是有多个value值 或者 没有空的地方插入时候,需要直接追加写入磁盘
for _, v := range values {
_, b8, err = s.add(b8, key, v)
if err != nil {
return err
}
}
return err
}
func (s *Msearch) del(offset int, valueMap map[string]struct{}) int {
total := bigUint64(s.bytesAddr[offset : offset+8])
if total == 0 {
return 0
}
// 全部数据
b := s.bytesAddr[offset : offset+total]
// 遍历value数据字节
// bytesBuffer := bytes.NewBuffer(b[8:12])
// var i32 int32
// binary.Read(bytesBuffer, binary.BigEndian, &i32)
i := bigUint64(b[8:12])
if total < i+12 {
// ????
panic("数据长度超过总长度了")
}
valueLen := bigUint64(b[8+4+i : 8+4+i+4])
if valueLen == 0 {
// 跳过空数据
return 0
}
// 构建value值
value := string(b[8+4+i+4 : 8+4+i+4+valueLen])
// value在需要删除的列表里
// 这里为什么使用map保存数据,因为这边要遍历所有value,map自带的算法更快
if _, ok := valueMap[value]; ok {
// 将value数据copy位0值
copy(b[8:total-8], make([]byte, total-8))
// 将长度设置为0值
// copy(b[8:12], make([]byte, 4))
}
// 返回结尾的8位数据
return bigUint64(b[total-8 : total])
}
func (s *Msearch) delPrefix(offset int, values ...string) int {
total := bigUint64(s.bytesAddr[offset : offset+8])
if total == 0 {
return 0
}
b := s.bytesAddr[offset : offset+total]
i := bigUint64(b[8:12])
if total < i+12 {
// ????
panic("数据长度超过总长度了")
}
valueLen := bigUint64(b[8+4+i : 8+4+i+4])
if valueLen == 0 {
// 跳过空数据
return 0
}
// 构建value值
value := string(b[8+4+i+4 : 8+4+i+4+valueLen])
for _, v := range values {
// 字符串是否以前缀开头
if strings.HasPrefix(value, v) {
copy(b[8:total-8], make([]byte, total-8))
}
}
// 返回结尾的8位数据
return bigUint64(b[total-8 : total])
}
// 获取这一链的全部数据,并返回value list和下一链offset
func (s *Msearch) get(offset int) ([]string, int) {
// 数据长度
total := bigUint64(s.bytesAddr[offset : offset+8])
// 全部数据
b := s.bytesAddr[offset : offset+total]
keyLen := bigUint64(b[8:12])
if keyLen == 0 {
return nil, 0
}
valueLen := bigUint64(b[12+keyLen : 12+keyLen+4])
value := b[12+keyLen+4 : 12+keyLen+4+valueLen]
return []string{string(value)}, bigUint64(b[total-8 : total])
// 之前写for是因为,这个链里面,可以有多个value值
// 现在改成了一个链里面只有一个value值
// for i := int(b[8] + 1 + 8); i < len(b[:len(b)-8]); {
// fmt.Printf("%#v , %#v\n", i, int(b[i]))
// l := i + 1 + int(b[i])
// if len(b) < l {
// fmt.Printf("%#v , %#v , %#v\n", l, len(b), b)
// panic("ssss")
// }
// list = append(list, string(b[i+1:l]))
// i += int(b[i]) + 1
// }
}
// bigUint64 对大数字进行解码 长度为0-8位的字节切片. binary.BigEndian.PutUint64 是编码.
// Deprecated: please use binary.BigEndian.Uint64
func bigUint64(buf []byte) int {
if len(buf) > 8 {
return 0
}
var x int
for _, b := range buf {
x = x<<8 | int(b)
}
return x
}
最后更新于