Skip to content

Commit

Permalink
Merge pull request #5 from orca-zhang/buffer_queue
Browse files Browse the repository at this point in the history
支持缓冲队列用法 - support BufferQueue mode
  • Loading branch information
orca-zhang authored Dec 29, 2021
2 parents f6111e4 + ec37fb5 commit 4a088b7
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 167 deletions.
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ c.Del("uid1")
- 不用担心,随意设置一个就好,`orcache`会找一个合适的数字便于后面掩码计算
- 第二个参数是每个桶所能容纳的item个数上限
- 意味着`orcache`全部写满的情况下,应该有`第一个参数 X 第二个参数`个item
- 第三个参数是每个item的过期时间
- \[可选\]第三个参数是每个item的过期时间
- `orcache`使用内部计时器提升性能,默认100ms精度,每秒校准
- 不传或者传`0`,代表永久有效

## 最佳实践

Expand All @@ -114,6 +115,9 @@ c.Del("uid1")
- 如果不想因为类似遍历的请求把热数据刷掉,可以改用[`LRU-2`模式](#LRU-2模式),可能有很少的损耗(💬 [什么是LRU-2](#什么是LRU-2)
- 一个实例可以存储多种类型的对象,试试key格式化的时候加上前缀,用冒号分割
- 并发访问量大的场景,试试`256``1024`个桶,甚至更多
- 可以当作**缓冲队列**用于合并更新以减少刷盘次数(数据可以重建或容忍断电丢失的情况下)
- 具体使用方式是[挂载`Inspector`](#注入监听器)监听驱逐事件
- 终末或定时调用`Walk`将数据刷到存储

## 特别场景

Expand Down Expand Up @@ -158,6 +162,29 @@ copier.Copy(o, v) // 从`v`复制到`o`
o.Status = 1 // 修改副本的字段
```

### 注入监听器

``` go
// inspector - 可以用来做统计或者缓冲队列等
// `action`:PUT, `status`: evicted=-1, updated=0, added=1
// `action`:GET, `status`: miss=0, hit=1
// `action`:DEL, `status`: miss=0, hit=1
// `value`只有在`status`不为0或者`action`为PUT时才有效
type inspector func(action int, key string, value *interface{}, status int)

// Inspect - 注入一个监听器
func (c *Cache) Inspect(insptr inspector)
```

- 使用方式
``` go
c.Inspect(func(action int, key string, value *interface{}, status int) {
// TODO: 实现你想做的事情
// 监听器会根据注入顺序依次执行
// 注意⚠️如果有耗时操作,尽量另开channel保证不阻塞当前协程
})
```

## 统计缓存使用情况

> 实现超级简单,注入inspector后,每个操作只多了一次原子操作,具体看[代码](/stats/stats.go#L26)
Expand All @@ -169,7 +196,9 @@ import (
)
```

#### 绑定缓存实例(名称为自定义的池子名称,内部会按名称聚合)
#### 绑定缓存实例
> 名称为自定义的池子名称,内部会按名称聚合\
> 注意⚠️绑定可以放在全局
``` go
var _ = stats.Bind("user", c)
var _ = stats.Bind("user", c0, c1, c2)
Expand Down Expand Up @@ -282,6 +311,9 @@ dist.OnDel("user", "uid1")
- 可以容忍短暂不一致的数据
- 用户头像、昵称、商品库存(实际下单会在db再次检查)等
- 修改的配置(过期时间10秒,那最多延迟10秒生效)
- 缓冲队列:合并更新以减少刷盘次数
- 通过给查询打补丁来实现强一致(分布式情况下,需要在负载均衡层保证同用户/设备调度到同一节点)
- 可以重建或容忍断电丢失的情况下

## 设计思路

Expand Down
37 changes: 34 additions & 3 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ c.Del("uid1")
- Don't worry, just set as you want, `orcache` will find a suitable number which is convenient for mask calculation later
- Second parameter is the number of items that each bucket can hold
- When `orcache` is full, there should be `first parameter X second parameter` item
- Third parameter is the expiration time of each item
- \[Optional\]Third parameter is the expiration time of each item
- `orcache` uses internal counter to improve performance, default 100ms accuracy, calibration every second
- No parameter or pass `0`, means permanent

## Best Practices

Expand All @@ -114,6 +115,9 @@ c.Del("uid1")
- If you don’t want to erase the hot data due to traversal requests, you can switch to [`LRU-2` mode](#LRU-2-mode), there may be very little loss (💬 [What Is LRU-2](#What-Is-LRU-2))
- One instance can store multiple types of objects, try adding a prefix when formatting the key and separating it with a colon
- For scenes with large concurrent visits, try `256`, `1024` buckets, or even more
- Can be used as a **buffer queue** to merge updates to reduce disk flushes (data can be rebuilt or tolerate loss of power outage)
- [Add an `Inspector`](#inject-an-inspector) to monitor the eviction event
- At the end or intervally call `Walk` to flush the data to storage

## Special Scenarios

Expand Down Expand Up @@ -158,6 +162,29 @@ copier.Copy(o, v) // Copy from `v` to `o`
o.Status = 1 // Modify the field of the copy
```

### Inject an inspector

``` go
// inspector - can be used to do statistics or buffer queues, etc.
// `action`:PUT, `status`: evicted=-1, updated=0, added=1
// `action`:GET, `status`: miss=0, hit=1
// `action`:DEL, `status`: miss=0, hit=1
// `value` is only valid when `status` is not 0 or `action` is PUT
type inspector func(action int, key string, value *interface{}, status int)

// Inspect - inject a inspector
func (c *Cache) Inspect(insptr inspector)
```

- How to use
``` go
c.Inspect(func(action int, key string, value *interface{}, status int) {
// TODO: add what you want to do
// Inspector will be executed in sequence according to the injection order
// Note:⚠️ If there is a operation that takes a long time, try to transfer job to another channel to ensure not blocking current coroutine.
})
```

## Cache Usage Statistics

> The implementation is super simple. After the inspector is injected, only one more atomic operation is added to each operation. See [details](/stats/stats.go#L26).
Expand All @@ -170,7 +197,8 @@ import (
```

#### Bind the cache instance
> The name is a custom pool name, which will be aggregated by name internally.
> The name is a custom pool name, which will be aggregated by name internally.\
> Note:⚠️ The binding can be placed in global scope.
``` go
var _ = stats.Bind("user", c)
var _ = stats.Bind("user", c0, c1, c2)
Expand Down Expand Up @@ -198,7 +226,7 @@ import (

### Bind cache instance
> The name is a custom pool name, which will be aggregated by name internally.\
> Note:⚠️ The binding can be placed in global scope and does not depend on initialization
> Note:⚠️ The binding can be placed in global scope and does not depend on initialization.
``` go
var _ = dist.Bind("user", c)
var _ = dist.Bind("user", c0, c1, c2)
Expand Down Expand Up @@ -283,6 +311,9 @@ dist.OnDel("user", "uid1")
- Inconsistent-tolerated data
- Such as user avatar, nickname, product inventory (the actual order will be checked again in db), etc.
- Modified configuration (expiration time is 10 seconds, then it will take effect with a maximum delay of 10 seconds)
- Buffer queue: merge updates to reduce disk flushes
- Achieve strong consistency through patching query with cache diff (in the case of distributed, it is necessary to ensure that the same user/device is balanced to the same node at the load balancing layer)
- Data can be rebuilt or tolerate loss of power outage

## Design Ideas

Expand Down
Loading

0 comments on commit 4a088b7

Please sign in to comment.