bloom.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package bloom
  2. import (
  3. "errors"
  4. "strconv"
  5. "github.com/zeromicro/go-zero/core/hash"
  6. "github.com/zeromicro/go-zero/core/stores/redis"
  7. )
  8. const (
  9. // for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
  10. // maps as k in the error rate table
  11. maps = 14
  12. )
  13. var (
  14. // ErrTooLargeOffset indicates the offset is too large in bitset.
  15. ErrTooLargeOffset = errors.New("too large offset")
  16. setScript = redis.NewScript(`
  17. for _, offset in ipairs(ARGV) do
  18. redis.call("setbit", KEYS[1], offset, 1)
  19. end
  20. `)
  21. testScript = redis.NewScript(`
  22. for _, offset in ipairs(ARGV) do
  23. if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
  24. return false
  25. end
  26. end
  27. return true
  28. `)
  29. )
  30. type (
  31. // A Filter is a bloom filter.
  32. Filter struct {
  33. bits uint
  34. bitSet bitSetProvider
  35. }
  36. bitSetProvider interface {
  37. check([]uint) (bool, error)
  38. set([]uint) error
  39. }
  40. )
  41. // New create a Filter, store is the backed redis, key is the key for the bloom filter,
  42. // bits is how many bits will be used, maps is how many hashes for each addition.
  43. // best practices:
  44. // elements - means how many actual elements
  45. // when maps = 14, formula: 0.7*(bits/maps), bits = 20*elements, the error rate is 0.000067 < 1e-4
  46. // for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
  47. func New(store *redis.Redis, key string, bits uint) *Filter {
  48. return &Filter{
  49. bits: bits,
  50. bitSet: newRedisBitSet(store, key, bits),
  51. }
  52. }
  53. // Add adds data into f.
  54. func (f *Filter) Add(data []byte) error {
  55. locations := f.getLocations(data)
  56. return f.bitSet.set(locations)
  57. }
  58. // Exists checks if data is in f.
  59. func (f *Filter) Exists(data []byte) (bool, error) {
  60. locations := f.getLocations(data)
  61. isSet, err := f.bitSet.check(locations)
  62. if err != nil {
  63. return false, err
  64. }
  65. return isSet, nil
  66. }
  67. func (f *Filter) getLocations(data []byte) []uint {
  68. locations := make([]uint, maps)
  69. for i := uint(0); i < maps; i++ {
  70. hashValue := hash.Hash(append(data, byte(i)))
  71. locations[i] = uint(hashValue % uint64(f.bits))
  72. }
  73. return locations
  74. }
  75. type redisBitSet struct {
  76. store *redis.Redis
  77. key string
  78. bits uint
  79. }
  80. func newRedisBitSet(store *redis.Redis, key string, bits uint) *redisBitSet {
  81. return &redisBitSet{
  82. store: store,
  83. key: key,
  84. bits: bits,
  85. }
  86. }
  87. func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
  88. var args []string
  89. for _, offset := range offsets {
  90. if offset >= r.bits {
  91. return nil, ErrTooLargeOffset
  92. }
  93. args = append(args, strconv.FormatUint(uint64(offset), 10))
  94. }
  95. return args, nil
  96. }
  97. func (r *redisBitSet) check(offsets []uint) (bool, error) {
  98. args, err := r.buildOffsetArgs(offsets)
  99. if err != nil {
  100. return false, err
  101. }
  102. resp, err := r.store.ScriptRun(testScript, []string{r.key}, args)
  103. if err == redis.Nil {
  104. return false, nil
  105. } else if err != nil {
  106. return false, err
  107. }
  108. exists, ok := resp.(int64)
  109. if !ok {
  110. return false, nil
  111. }
  112. return exists == 1, nil
  113. }
  114. func (r *redisBitSet) del() error {
  115. _, err := r.store.Del(r.key)
  116. return err
  117. }
  118. func (r *redisBitSet) expire(seconds int) error {
  119. return r.store.Expire(r.key, seconds)
  120. }
  121. func (r *redisBitSet) set(offsets []uint) error {
  122. args, err := r.buildOffsetArgs(offsets)
  123. if err != nil {
  124. return err
  125. }
  126. _, err = r.store.ScriptRun(setScript, []string{r.key}, args)
  127. if err == redis.Nil {
  128. return nil
  129. }
  130. return err
  131. }