github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/kv/kv-badger-db.go (about)

     1  package kv
     2  
     3  import (
     4  	"github.com/angenalZZZ/gofunc/f"
     5  	"github.com/dgraph-io/badger/v2"
     6  	"github.com/dgraph-io/badger/v2/options"
     7  	"strconv"
     8  	"time"
     9  )
    10  
    11  /**
    12   * Feature							键值功能设计	https://github.com/dgraph-io/badger
    13   * Design							数据结构设计	LSM tree with value log, it's not a B+ tree
    14   * High Read throughput				高读取吞吐量	Yes
    15   * High Write throughput			高写入吞吐量	Yes
    16   * Designed for SSDs				专为固态硬盘设计	Yes (with latest research 1)
    17   * Embeddable						可嵌入		Yes
    18   * Sorted KV access					排序KV访问	Yes
    19   * Transactions						事务管理		Yes, concurrent with SSI3, ACID: 原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Durability
    20   * Snapshots						快照		Yes
    21   * TTL support						过期支持		Yes
    22   * 3D access (key-value-version)	3D访问(键值版本)Yes
    23   */
    24  type BadgerDB struct {
    25  	DB     *badger.DB
    26  	locker *f.Locker
    27  }
    28  
    29  // Open BadgerDB represents a badger db implementation,
    30  // or no path, db save in memory.
    31  func (db *BadgerDB) Open(path ...string) error {
    32  	var opt badger.Options
    33  	if len(path) == 1 {
    34  		opt = badger.DefaultOptions(path[0])
    35  		opt.Truncate = true
    36  		opt.SyncWrites = false
    37  		opt.TableLoadingMode = options.MemoryMap
    38  		opt.ValueLogLoadingMode = options.FileIO
    39  		//opt.ValueThreshold = 1 << 20 // 阈值 1 MB
    40  		opt.ValueThreshold = 1 // 阈值 默认 32
    41  		opt.NumMemtables = 2
    42  		opt.NumLevelZeroTables = 2
    43  		opt.MaxTableSize = 16 << 20
    44  	} else {
    45  		opt = badger.DefaultOptions("").WithInMemory(true)
    46  	}
    47  
    48  	_db, err := badger.Open(opt)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	db.DB = _db
    54  	db.locker = f.NewLocker()
    55  	if opt.InMemory {
    56  		return nil
    57  	}
    58  
    59  	go (func() {
    60  		for db.DB.RunValueLogGC(0.5) == nil {
    61  			// cleaning ...
    62  		}
    63  	})()
    64  	return nil
    65  }
    66  
    67  // Size gets the size of the database (LSM + ValueLog) in bytes.
    68  func (db *BadgerDB) Size() int64 {
    69  	lsm, vLog := db.DB.Size()
    70  	return lsm + vLog
    71  }
    72  
    73  // Incr - increment the key by the specified value.
    74  func (db *BadgerDB) Incr(k string, by int64) (int64, error) {
    75  	db.locker.Lock(k)
    76  	defer db.locker.Unlock(k)
    77  
    78  	val, err := db.Get(k)
    79  	if err != nil || val == "" {
    80  		val = "0"
    81  	}
    82  
    83  	valFloat, _ := strconv.ParseInt(val, 10, 64)
    84  	valFloat += by
    85  
    86  	err = db.Set(k, strconv.FormatInt(valFloat, 10), -1)
    87  	if err != nil {
    88  		return 0, err
    89  	}
    90  
    91  	return valFloat, nil
    92  }
    93  
    94  // Set sets a key with the specified value and optional ttl.seconds
    95  func (db *BadgerDB) Set(k, v string, ttl int) error {
    96  	return db.SetBytes(f.Bytes(k), f.Bytes(v), ttl)
    97  }
    98  
    99  // SetBytes sets a key with the specified value and optional ttl.seconds
   100  func (db *BadgerDB) SetBytes(k, v []byte, ttl int) error {
   101  	return db.DB.Update(func(txn *badger.Txn) (err error) {
   102  		if ttl <= 0 {
   103  			err = txn.Set(k, v)
   104  		} else {
   105  			err = txn.SetEntry(&badger.Entry{
   106  				Key:       k,
   107  				Value:     v,
   108  				ExpiresAt: uint64(time.Now().Add(time.Duration(ttl) * time.Second).Unix()),
   109  			})
   110  		}
   111  		return err
   112  	})
   113  }
   114  
   115  // MSet sets multiple key-value pairs.
   116  func (db *BadgerDB) MSet(data map[string]string) error {
   117  	return db.DB.Update(func(txn *badger.Txn) error {
   118  		for k, v := range data {
   119  			if err := txn.Set(f.Bytes(k), f.Bytes(v)); err != nil {
   120  				return err
   121  			}
   122  		}
   123  		return nil
   124  	})
   125  }
   126  
   127  // Get fetches the value of the specified k.
   128  func (db *BadgerDB) Get(k string) (string, error) {
   129  	data, err := db.GetBytes(f.Bytes(k))
   130  	if err != nil {
   131  		return "", err
   132  	}
   133  	return f.String(data), nil
   134  }
   135  
   136  // GetBytes fetches the value of the specified k.
   137  func (db *BadgerDB) GetBytes(k []byte) ([]byte, error) {
   138  	var data []byte
   139  	err := db.DB.View(func(txn *badger.Txn) error {
   140  		item, err := txn.Get(k)
   141  		if err != nil {
   142  			return err
   143  		}
   144  
   145  		data, err = item.ValueCopy(nil)
   146  		return err
   147  	})
   148  	return data, err
   149  }
   150  
   151  // MGet fetch multiple values of the specified keys.
   152  func (db *BadgerDB) MGet(keys []string) (data []string) {
   153  	data = make([]string, 0, len(keys))
   154  	_ = db.DB.View(func(txn *badger.Txn) error {
   155  		for _, key := range keys {
   156  			item, err := txn.Get(f.Bytes(key))
   157  			if err != nil {
   158  				data = append(data, "")
   159  				continue
   160  			}
   161  			val, err := item.ValueCopy(nil)
   162  			if err != nil {
   163  				data = append(data, "")
   164  				continue
   165  			}
   166  			data = append(data, f.String(val))
   167  		}
   168  		return nil
   169  	})
   170  	return data
   171  }
   172  
   173  // TTL gets the time.seconds to live of the specified key's value.
   174  func (db *BadgerDB) TTL(key string) int64 {
   175  	var expires int64
   176  
   177  	_ = db.DB.View(func(txn *badger.Txn) error {
   178  		item, err := txn.Get(f.Bytes(key))
   179  		if err != nil {
   180  			expires = -2
   181  			return nil
   182  		}
   183  
   184  		exp := item.ExpiresAt()
   185  		if exp == 0 {
   186  			expires = -1
   187  			return nil
   188  		}
   189  
   190  		expires = int64(exp)
   191  		return nil
   192  	})
   193  
   194  	if expires == -2 {
   195  		return -2
   196  	}
   197  
   198  	if expires == -1 {
   199  		return -1
   200  	}
   201  
   202  	now := time.Now().Unix()
   203  
   204  	if now >= expires {
   205  		return -2
   206  	}
   207  
   208  	return (expires - now) / int64(time.Second)
   209  }
   210  
   211  // Del removes key(s) from the store.
   212  func (db *BadgerDB) Del(keys []string) error {
   213  	return db.DB.Update(func(txn *badger.Txn) error {
   214  		for _, key := range keys {
   215  			if err := txn.Delete(f.Bytes(key)); err != nil {
   216  				return err
   217  			}
   218  		}
   219  		return nil
   220  	})
   221  }
   222  
   223  // Close ...
   224  func (db *BadgerDB) Close() error {
   225  	return db.DB.Close()
   226  }
   227  
   228  // Keys gets matched keys.
   229  func (db *BadgerDB) Keys(prefix ...string) []string {
   230  	opts := badger.DefaultIteratorOptions
   231  	opts.PrefetchValues = false
   232  	var prefixBytes []byte
   233  	var keys []string
   234  	l := len(prefix)
   235  	if l > 0 {
   236  		prefixBytes = f.Bytes(prefix[0])
   237  	}
   238  	_ = db.DB.View(func(txn *badger.Txn) error {
   239  		it := txn.NewIterator(opts)
   240  		defer it.Close()
   241  		if l == 0 {
   242  			for it.Rewind(); it.Valid(); it.Next() {
   243  				keys = append(keys, f.String(it.Item().Key()))
   244  			}
   245  		} else {
   246  			for it.Seek(prefixBytes); it.ValidForPrefix(prefixBytes); it.Next() {
   247  				keys = append(keys, f.String(it.Item().Key()))
   248  			}
   249  		}
   250  		return nil
   251  	})
   252  	return keys
   253  }
   254  
   255  // GC runs the garbage collector, not in memory.
   256  func (db *BadgerDB) GC() error {
   257  	var err error
   258  	for {
   259  		err = db.DB.RunValueLogGC(0.5)
   260  		if err != nil {
   261  			break
   262  		}
   263  	}
   264  	return err
   265  }