不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • MySQL

  • Redis

    • 01.Redis安装
    • 02.Redis基础
    • 03.Redis底层存储 ✅
    • 04.各种数据类型操作
    • 05.Redis连接和基本操作
    • 06.Redis线程模型 ✅
    • 07.Redis持久化 ✅
    • 08.Redis集群 ✅
    • 09.雪崩穿透击穿
    • 10.Redis锁代码
    • 11.Redis缓存
      • 01.缓存读写策略
        • 1、旁路缓存
        • 1、概述
        • 2、缺陷和解决
        • 2、读写穿透
        • 0、概述
        • 1、写穿透
        • 2、读穿透
        • 3、读写穿透code
        • 3、异步缓存写入
      • 02.Redis缓存实现
        • 1、2B青年操作
        • 2、NB青年
    • 12.部署Redis主从
    • 13.Redis的cluster配置
    • 14.Redis哨兵模式
    • 15.Redis淘汰策略 ✅
    • 16.CAP ✅
  • Elasticsearch

  • Kafka

  • Etcd

  • MongoDB

  • TiDB

  • RabbitMQ

  • 数据库
  • Redis
xiaonaiqiang
2021-02-22
目录

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)
}
1
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中,下次查询就有了
1
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()
1
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()
1
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
10.Redis锁代码
12.部署Redis主从

← 10.Redis锁代码 12.部署Redis主从→

最近更新
01
04.数组双指针排序_子数组
03-25
02
08.动态规划
03-25
03
06.回溯算法
03-25
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式