11.Redis缓存
# 01.缓存读写策略
# 1、旁路缓存
# 1、概述
Cache-Aside
旁路缓存使用
比较多的一个缓存读写模式
,比较适合读请求比较多的场景
服务端需要同时维系 db 和 cache,并且是以 db 的结果为准
- 读策略
- 先读 cache
- cache 找不到从 db 中读取最新数据缓存到 cache(需要应用程序读取 db 写入 cache)
- 写策略
- 先更新 db
- 然后
直接删除 cache
(下次读取更新到 cache)
- 特点
- 实现简单,能够保证数据的强一致性
- 有大量的写操作或者缓存失效的情况,可能会导致数据库压力大
# 2、缺陷和解决
1)缺陷 1:
首次请求
数据一定不在 cache
的问题- 可以将
热点数据
可以提前放入 cache 中
- 可以将
2)缺陷 2:
写操作比较频繁
的话导致 cache 中的数据会被频繁被删除
1、强一致性
- 更新 db 的时候同样更新 cache(加分布式锁)
2、可以短暂地允许数据库和缓存数据不一致的场景
- 更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间
# 2、读写穿透
# 0、概述
- 读写穿透是
同步更新 cache 和 db
- Redis本身并不直接支持
读穿透
策略,这需要通过客户端库或者中间件来实现- 比如,使用go-cache或者一些其他的缓存框架,可以很方便地实现这种策略
- 在 Python 中也可以自己使用装饰器实现
读写穿透中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中
cache 服务负责将此数据读取和写入 db,从而减轻了应用程序的职责
这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见
抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入 db 的功能
# 1、写穿透
1、先查 cache,cache 中不存在,直接更新 db
2、cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(同步更新 cache 和 db)
3、cache 找不到从 db 中读取最新数据缓存到 cache
注:应用只需要读取缓存,未命中缓存系统自己读取 db 写入 cache
# 2、读穿透
从 cache 中读取数据,读取到就直接返回
读取不到的话,先从 db 加载,写入到 cache 后返回响应
# 3、读写穿透code
- 读写穿透和旁路缓存区别
- 旁路缓存如果不存在数据,需要应用在业务逻辑中查询 db 写入 cache
- 读写穿透不是在业务逻辑中实现,而是在Redis 的连接客户端中直接实现了,业务中可以直接使用即可
eg: go-cache库 实现了读写穿透功能
package main
import (
"fmt"
"github.com/patrickmn/go-cache"
"time"
)
func main() {
// 创建一个缓存,缓存项在5分钟后过期,每10分钟清理一次过期的缓存项
c := cache.New(5*time.Minute, 10*time.Minute)
// 假设这是你的数据库查询函数
queryFromDB := func(key string) (interface{}, error) {
fmt.Printf("Fetching data from db for key: %s\n", key)
// 这里返回的数据就是需要从 MySQL 中查询的数据
return fmt.Sprintf("Data for %s", key), nil
}
// 使用cache的Add方法添加数据
data, found := c.Get("product1")
if !found {
data, _ = queryFromDB("product1")
c.Add("product1", data, cache.DefaultExpiration)
}
fmt.Println(data)
// 再次获取数据,这次会从缓存中获取
data, found = c.Get("product1")
fmt.Println(data)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 3、异步缓存写入
异步缓存写入 和 读写穿透 很相似,两者都是由 cache 服务来负责 cache 和 db 的读写
但是,两个又有很大的不同:
读写穿透
是同步更新 cache 和 db
- 而
异步缓存写入
则是只更新缓存,不直接更新 db
,而是改为异步批量的方式来更新 db
这种方式对数据一致性带来了更大的挑战,比如 cache 数据可能还没异步更新 db 的话,cache 服务可能就就挂掉了
这种策略在我们平时开发过程中也非常非常少见,但是不代表它的应用场景少
比如消息队列中消息的异步写入磁盘、MySQL 的 Innodb Buffer Pool 机制都用到了这种策略
异步缓存写入 下 db 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量
# 02.Redis缓存实现
第一步:查询Redis缓存是否存在这个key
第二步:如果存在这个key,不用去MySQL中查询,直接从Redis中取出数据即可(减轻了MySQL压力)
第三步:如果查询的key不存在,先到MySQL中查询数据,让后设置到Redis中,下次查询就有了
2
3
# 1、2B青年操作
2B青年每一个需要使用缓存的数据,我都写一个方法获取数据,再写一个方法处理缓存。
若需要用到缓存的地方越来越多,每一个都需要这么写一套代码,代码冗余繁琐。
# coding:utf-8
from django.core.cache import cache
import time
# 获取readed缓存
def get_readed_cache():
# 判断键是否存在
key = 'readed'
if cache.has_key(key):
data = cache.get(key)
else:
# 不存在,则通过sql语句获取数据,并写入缓存,这里只是一个举例的sql语句
data = "select name from tb"
# 写入缓存
cache.set(key, data, 3600 - int(time.time() % 3600))
return data
def test1():
data = get_readed_cache()
return data
def test2():
data = get_readed_cache()
return data
if __name__ == '__main__':
test1()
test2()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 2、NB青年
- NB青年可以使用三级装饰器,在装饰器中判断key如果存在就从reids中获取,如果不存在就从数据库查询,并设置到reids中
# coding:utf-8
from django.core.cache import cache
# 获取Redis缓存的装饰器
def Redis_cache(key, timeout):
def __Redis_cache(func):
def warpper(*args, **kw):
if cache.has_key(key): # 判断缓存是否存在
data = cache.get(key)
else:
# 若不存在则执行获取数据的方法
# 注意返回数据的类型(字符串,数字,字典,列表均可)
data = func(*args, **kw) # 从数据库查询到数据设置到Redis中
cache.set(key, data, timeout)
return data
return warpper
return __Redis_cache
#键值为test,超时时间为60秒
@Redis_cache('test', 60)
def get_test_data():
# 获取Blog模型随机排序前3条数据
# (Blog模型是我自己的模型,具体代码根据自己需求获取数据)
# values执行结果,将返回一个字典。字典可以直接存入Redis
# data = Blog.objects.values('id', 'caption').order_by('?')[:3]
data = '从数据库查询到了数据'
return data
#键值为test,超时时间为60秒
@Redis_cache('name', 60)
def get_test_name():
# 获取Blog模型随机排序前3条数据
# (Blog模型是我自己的模型,具体代码根据自己需求获取数据)
# values执行结果,将返回一个字典。字典可以直接存入Redis
# data = Blog.objects.values('id', 'caption').order_by('?')[:3]
data = '从数据库查询到了数据'
return data
if __name__ == '__main__':
get_test_data()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40