cuckoofilter

package module
v0.0.0-...-45a7fbb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2019 License: MIT Imports: 10 Imported by: 1

README

go-cuckoofilter

CuckooFilter用于快速判断一个元素是否属于某个集合, 以少量错误率换取很高的空间利用率.

本项目将CuckooFilter存储部分接口化, 实现了两种存储方式(Table接口):

  • MemTable, 内存存储, 对应Golang中的[]byte类型
  • MMAPTable, 文件存储, 使用MMAP快速读写文件

下载

go get -u github.com/spencer404/go-cuckoofilter

示例

package main

import (
	"fmt"
	"github.com/spencer404/go-cuckoofilter"
)

func main() {
	table := cuckoofilter.NewMemTable(1 << 30) // Size: 1GB
	cf := cuckoofilter.NewCuckooFilter(table)
	
	err := cf.InsertUnique([]byte("Hello"))
	fmt.Println(err) // <nil>

	ok, err := cf.Lookup([]byte("Hello"))
	fmt.Println(ok, err) // true <nil>

	n := cf.Count()
	fmt.Println(n) // 1

	err = cf.Delete([]byte("Hello"))
	fmt.Println(err) // <nil>

	ok, err = cf.Lookup([]byte("Hello"))
	fmt.Println(ok, err) // false <nil>

	n = cf.Count()
	fmt.Println(n) // 0
}

主要操作

在内存中创建过滤器
table := cuckoofilter.NewMemTable(capacity)
cf := cuckoofilter.NewCuckooFilter(table)

Capacity等于占用内存/文件的大小(字节), 项目默认配置fingerprint占用空间为1Byte.

因此, 1 Capacity == 1 Byte空间占用 == 最多容纳1个元素.

业务中具体Capacity的数值, 理应在预计元素数量, 性能, 错误率之间寻找点.

但如果你像我一样懒的话, 直接取预计元素数量 * 2.

如果无法估计元素数量, 可以使用多层的过滤器, 当空间利用率过高时, 再增加一个新的过滤器.

在文件中创建过滤器
table, err := NewMMAPTable(filename, capacity)
cf := cuckoofilter.NewCuckooFilter(table)

注意, 若程序意外退出, 未被刷入文件的数据将会丢失.

插入元素
err := cf.InsertUnique(a)

其返回值err可能为:

  • ErrExist, 元素a已存在(可能是Lookup误报)
  • nil, 元素a已被插入到哈希表的空位中(哈希表接近饱和时, 可能会挪走旧元素以腾出空位, 引起Lookup误报)
  • ErrFull, 多次尝试后, 仍未能在哈希表中找到空位, 此时哈希表已接近饱和
  • 其他情况, 通常是底层存储出现了异常
是否存在
ok, err := cf.Lookup(a)
  • err!=nil, 通常是底层存储出现了异常
  • ok==true时, 可能是误报: 存在已插入的元素b与a具有相同的哈希
  • ok==false时, 可能是误报: InsertUnique(b)时, 由于哈希表已接近饱和, a给b挪出空位后, a无处安放, 被移出了哈希表(其实可增加Rollback流程,使得ok==false是确定的)
删除元素
err := cf.Delete(a)
  • err!=nil, 通常是底层存储出现了异常

  • err==ErrNotExist, 未找到元素a, 无法删除. 可能是误报, 原因见是否存在 > 当ok==false

  • 注意, 只删除已插入过的元素, 若删除未曾插入的元素, 会使Lookup操作出现更多的误报.

    假设元素k未曾插入到过滤器中, a已被插入到过滤器中, a与k具有相同的哈希, 那么Delete(k)时会返回成功, 但实际上把a给删掉了.

性能测试

测试平台
  • Intel Xeon L5640 @ 2.27GHz

  • Ubuntu 16.04

  • 普通SSD

  • 测试方法为插入Capacity条不重复的数据

MemTable:InsertUnique
  • Capacity: 1073741824Byte (=1GB)
  • FingerprintSize: 1Byte
  • SlotSize: 4Byte
区间 已用时(区间用时) 已写入(区间速率) OK(nil) ErrExist ErrFull 空间利用率
0%-12.5% 83.1(83.1) 134217728(1613886/s) 99.80% 0.20% 0.00% 12.48%
12.5%-25.0% 168.3(85.2) 268435456(1575325/s) 99.61% 0.39% 0.00% 24.90%
25.0%-37.5% 253.5(85.2) 402653184(1575325/s) 99.41% 0.59% 0.00% 37.28%
37.5%-50.0% 339.8(86.3) 536870912(1555246/s) 99.22% 0.78% 0.00% 49.61%
50.0%-62.5% 431.5(91.7) 671088640(1463661/s) 99.02% 0.97% 0.01% 61.89%
62.5%-75.0% 586.3(154.8) 805306368(867040/s) 98.69% 1.17% 0.14% 74.02%
75.0%-87.5% 1210.1(623.8) 939524096(215161/s) 97.51% 1.36% 1.13% 85.32%
87.5%-100% 3510.9(2300.8) 1073741824(58335/s) 93.69% 1.54% 4.77% 93.69%
MMAPTable:InsertUnique
  • Capacity: 1073741824Byte (=1GB)
  • FingerprintSize: 1Byte
  • SlotSize: 4Byte
区间 已用时(区间用时) 已写入(区间速率) OK(nil) ErrExist ErrFull 空间利用率
0%-12.5% 247.5(247.5) 134217728(542292/s) 99.80% 0.20% 0.00% 12.48%
12.5%-25.0% 557.0(309.5) 268435456(433668/s) 99.61% 0.39% 0.00% 24.90%
25.0%-37.5% 790.5(233.5) 402653184(574830/s) 99.41% 0.59% 0.00% 37.28%
37.5%-50.0% 1084.8(294.3) 536870912(456097/s) 99.22% 0.78% 0.00% 49.61%
50.0%-62.5% 1535.3(450.6) 671088640(297894/s) 99.02% 0.97% 0.01% 61.89%
62.5%-75.0% 2134.0(598.6) 805306368(224202/s) 98.69% 1.17% 0.14% 74.02%
75.0%-87.5% 3770.3(1636.3) 939524096(82024/s) 97.51% 1.36% 1.13% 85.32%
87.5%-100% 8909.4(5139.1) 1073741824(26117/s) 93.69% 1.54% 4.77% 93.69%

参考资料

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrExist = fmt.Errorf("已存在相同记录")
View Source
var ErrFull = fmt.Errorf("移位次数过多")
View Source
var ErrNotExist = fmt.Errorf("未找到记录")

Functions

This section is empty.

Types

type Bucket

type Bucket [slotSize]byte

func (*Bucket) IndexByte

func (b *Bucket) IndexByte(fp byte) (uint, bool)

func (*Bucket) IndexEmpty

func (b *Bucket) IndexEmpty() (uint, bool)

type CuckooFilter

type CuckooFilter struct {
	// contains filtered or unexported fields
}

func NewCuckooFilter

func NewCuckooFilter(table Table) *CuckooFilter

func (*CuckooFilter) Count

func (c *CuckooFilter) Count() uint

func (*CuckooFilter) Delete

func (c *CuckooFilter) Delete(data []byte) error

func (*CuckooFilter) InsertUnique

func (c *CuckooFilter) InsertUnique(data []byte) error

func (*CuckooFilter) Lookup

func (c *CuckooFilter) Lookup(data []byte) (bool, error)

func (*CuckooFilter) Truncate

func (c *CuckooFilter) Truncate() error

type MMAPTable

type MMAPTable struct {
	// contains filtered or unexported fields
}

func NewMMAPTable

func NewMMAPTable(filename string, capacity uint) (*MMAPTable, error)

func (*MMAPTable) Bucket

func (f *MMAPTable) Bucket(index uint) (*Bucket, error)

func (*MMAPTable) BucketNum

func (f *MMAPTable) BucketNum() uint

func (*MMAPTable) Close

func (f *MMAPTable) Close() error

func (*MMAPTable) Count

func (f *MMAPTable) Count() uint

func (*MMAPTable) DecrCount

func (f *MMAPTable) DecrCount()

func (*MMAPTable) Encode

func (f *MMAPTable) Encode(writer io.Writer) error

func (*MMAPTable) IncrCount

func (f *MMAPTable) IncrCount()

func (*MMAPTable) SetSlot

func (f *MMAPTable) SetSlot(index uint, slot uint, fingerprint byte) error

func (*MMAPTable) Slot

func (f *MMAPTable) Slot(index uint, slot uint) (byte, error)

func (*MMAPTable) Truncate

func (f *MMAPTable) Truncate() error

type MemTable

type MemTable struct {
	// contains filtered or unexported fields
}

func NewMemTable

func NewMemTable(capacity uint) *MemTable

func NewMemTableFromReader

func NewMemTableFromReader(reader io.Reader) (*MemTable, error)

func (*MemTable) Bucket

func (t *MemTable) Bucket(index uint) (*Bucket, error)

func (*MemTable) BucketNum

func (t *MemTable) BucketNum() uint

func (*MemTable) Close

func (t *MemTable) Close() error

func (*MemTable) Count

func (t *MemTable) Count() uint

func (*MemTable) DecrCount

func (t *MemTable) DecrCount()

func (*MemTable) Encode

func (t *MemTable) Encode(writer io.Writer) error

func (*MemTable) IncrCount

func (t *MemTable) IncrCount()

func (*MemTable) SetSlot

func (t *MemTable) SetSlot(index uint, offset uint, fingerprint byte) error

func (*MemTable) Slot

func (t *MemTable) Slot(index uint, offset uint) (byte, error)

func (*MemTable) Truncate

func (t *MemTable) Truncate() error

type Table

type Table interface {
	// 获取指定索引位置的Bucket
	Bucket(index uint) (*Bucket, error)
	BucketNum() uint
	// 获取指定槽
	Slot(index uint, slot uint) (byte, error)
	// 设置槽
	SetSlot(index uint, slot uint, fingerprint byte) error
	// 编码, 可用于保存哈希表
	Encode(io.Writer) error
	// fingerprint数量
	IncrCount()
	DecrCount()
	Count() uint
	// 清空数据
	Truncate() error
	io.Closer
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL