提升性能,内存缓存是关键,经典LRU根据访问频率更新,不能完全满足需求,通常业务更需要基于时间或事件触发更新。针对要求实时更新(用户信息、配置)和可以延迟更新(评论、动态列表)的数据,需要设计不同的缓存更新策略。
即每个缓存key在put时记录有效期,当有效期到达时清理key(一般语言都有对应的库实现)
当get时,key存在则说明缓存有效,使用该缓存,否则查询db并更新缓存
即每次存储数据被修改时,发出事件通知
所有读数据的服务监听该事件通知(可通过redis或消息队列实现),收到修改事件时更新对应缓存key
在get缓存数据时进行校验,缓存有效即使用,缓存无效查询db并更新
有效检查策略1:
数据添加版本号字段(可选用修改点的时间戳或数据的md5),db和缓存同时记录该版本号。
db更新的同时修改版本号,查询时缓存的版本号和db的做比较(从db拉去版本号效率远高于拉所有数据)
x
package main
// LRUCache 结构体
type LRUCache struct {
size int
capacity int
cache map[int]*DLinkedNode
head, tail *DLinkedNode
}
type DLinkedNode struct {
key, value int
prev, next *DLinkedNode
}
func initDLinkedNode(key, value int) *DLinkedNode {
return &DLinkedNode{
key: key,
value: value,
}
}
func NewLRUCache(capacity int) LRUCache {
l := LRUCache{
cache: map[int]*DLinkedNode{},
head: initDLinkedNode(0, 0),
tail: initDLinkedNode(0, 0),
capacity: capacity,
}
l.head.next = l.tail
l.tail.prev = l.head
return l
}
func (lru *LRUCache) Get(key int) int {
if _, ok := lru.cache[key]; !ok {
return -1
}
node := lru.cache[key]
lru.moveToHead(node)
return node.value
}
func (lru *LRUCache) Put(key int, value int) {
if _, ok := lru.cache[key]; !ok {
node := initDLinkedNode(key, value)
lru.cache[key] = node
lru.addToHead(node)
lru.size++
if lru.size > lru.capacity {
removed := lru.removeTail()
delete(lru.cache, removed.key)
lru.size--
}
} else {
node := lru.cache[key]
node.value = value
lru.moveToHead(node)
}
}
func (lru *LRUCache) addToHead(node *DLinkedNode) {
node.prev = lru.head
node.next = lru.head.next
lru.head.next.prev = node
lru.head.next = node
}
func (lru *LRUCache) removeNode(node *DLinkedNode) {
node.prev.next = node.next
node.next.prev = node.prev
}
func (lru *LRUCache) moveToHead(node *DLinkedNode) {
lru.removeNode(node)
lru.addToHead(node)
}
func (lru *LRUCache) removeTail() *DLinkedNode {
node := lru.tail.prev
lru.removeNode(node)
return node
}
func main() {
lRUCache := NewLRUCache(2)
lRUCache.Put(1, 1) // 缓存是 {1=1}
//spew.Dump(lRUCache.cache)
lRUCache.Put(2, 2) // 缓存是 {1=1, 2=2}
//spew.Dump(lRUCache.cache)
lRUCache.Get(1) // 返回 1
lRUCache.Put(3, 3) // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
//spew.Dump(lRUCache.cache)
lRUCache.Get(2) // 返回 -1 (未找到)
lRUCache.Put(4, 4) // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
//spew.Dump(lRUCache.cache)
lRUCache.Get(1) // 返回 -1 (未找到)
lRUCache.Get(3) // 返回 3
lRUCache.Get(4) // 返回 4
}
选取合适的hash算法,将目标字符串映射到对应的比特位上
比如:
"baidu": 1、4、7
"tencent": 3、4、8
查询 google:
查询app le:
注意: 传统的布隆过滤器并不支持删除操作
缓存和数据库都没有的数据,被大量请求
Filter
缓存突然失效了,这时候如果有大量用户请求该数据
大量的缓存的数据,在同一个时间点过期