redis 进行缓存实战-18

使用 Redis 进行缓存

Redis 通常被认为只是一个数据存储,但它的速度和内存中特性使其成为缓存的绝佳选择。缓存是一种技术,通过将经常访问的数据存储在快速的临时存储位置来提高应用程序性能。通过使用 Redis 作为缓存,您可以显著减少主数据库的负载并缩短用户的响应时间。本课将探讨如何有效地使用 Redis 进行缓存,涵盖关键概念、策略和最佳实践。

了解缓存概念

缓存是软件开发中的一种基本优化技术。它涉及将数据副本存储在缓存中,缓存是一个高速数据存储层,以便将来可以更快地处理对该数据的请求。发出请求时,首先检查缓存。如果在缓存中找到数据(“缓存命中”),则直接从缓存中提供数据。如果未找到数据(“缓存未命中”),则会从原始数据源(例如数据库)中检索数据,将其存储在缓存中,然后提供给用户。

缓存的好处

  • 改进的性能: 缓存通过从更快的存储层提供数据来减少延迟并缩短响应时间。
  • 减少数据库负载: 通过从缓存中提供经常访问的数据,您可以减少主数据库的负载,使其能够处理更复杂的查询和作。
  • 提高可扩展性: 缓存通过减少后端系统的负载,使您的应用程序能够处理更多的并发用户和请求。
  • 节省成本: 通过减少数据库负载和提高资源利用率,缓存可以节省基础设施和运营费用方面的成本。

缓存策略

Redis 可以使用多种缓存策略,每种策略都有自己的优点和缺点:

  • Cache-Aside (延迟加载): 应用程序首先检查缓存中的数据。如果找到数据,则直接返回数据。如果没有,应用程序将从数据库中检索数据,将其存储在缓存中,然后返回它。此策略易于实施,并确保缓存仅包含已请求的数据。
  • 直写: 应用程序同时将数据写入缓存和数据库。这可确保缓存始终是最新的,但会增加写入延迟。
  • 回写 (Write-Behind): 应用程序将数据写入缓存,缓存将数据异步写入数据库。此策略提供最低的写入延迟,但如果缓存在将数据写入数据库之前失败,则可能导致数据丢失。
  • 通读: 应用程序与缓存交互,而缓存又与数据库交互。请求数据时,缓存会检查它是否包含数据。否则,它将从数据库中检索数据,将其存储在缓存中,然后将其返回给应用程序。

对于大多数使用案例,Cache-Aside 策略是 Redis 最实用且最常用的策略,因为它简单高效。

使用 Redis 作为缓存

Redis 非常适合缓存,因为它的速度、内存数据存储和对各种数据结构的支持。以下是将 Redis 用作缓存的方法:

设置和检索数据

您可以使用 SETGET 命令在 Redis 中存储和检索数据。例如:

SET user:123 '{"id": 123, "name": "John Doe", "email": "john.doe@example.com"}'
GET user:123

在此示例中,我们将一个 JSON 字符串存储在 Redis 中,该字符串表示用户对象,其键为 user:123。使用 GET user:123 检索数据时,Redis 返回 JSON 字符串。

设置过期时间 (TTL)

为防止缓存无限增长,您可以使用 EXPIRE 命令或带有 SET 命令的 EX 选项为缓存数据设置过期时间(TTL - 生存时间):

SET user:123 '{"id": 123, "name": "John Doe", "email": "john.doe@example.com"}' EX 3600  # Expires in 3600 seconds (1 hour)
EXPIRE user:123 3600 # Expires in 3600 seconds (1 hour)
TTL user:123 # Check the remaining time to live

这可确保在指定时间段后自动删除缓存的数据,从而防止提供过时的数据。

数据序列化

缓存复杂数据结构时,您需要在将数据存储在 Redis 中之前对其进行序列化,并在检索数据后对其进行反序列化。常见的序列化格式包括 JSON 和 Protocol Buffers。

以下是在 Python 中使用 JSON 的示例:

import redis
import json# Connect to Redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)# Data to cache
user_data = {"id": 123, "name": "John Doe", "email": "john.doe@example.com"}# Serialize the data to JSON
user_data_json = json.dumps(user_data)# Store the JSON string in Redis with an expiration time
redis_client.set('user:123', user_data_json, ex=3600)# Retrieve the data from Redis
cached_user_data_json = redis_client.get('user:123')# Deserialize the JSON string back to a Python dictionary
if cached_user_data_json:cached_user_data = json.loads(cached_user_data_json)print(cached_user_data)
else:print("Data not found in cache")

缓存失效策略

缓存失效是在基础数据更改时删除或更新缓存数据的过程。缓存失效有几种策略:

  • 基于 TTL 的失效: 在指定的 TTL 之后,数据会自动从缓存中删除。这是最简单的策略,但如果基础数据在 TTL 过期之前发生更改,则可能会导致数据过时。
  • 基于事件的失效: 当发生特定事件(例如数据库更新)时,缓存将失效。此策略可确保缓存始终是最新的,但它需要与数据源进行更复杂的集成。
  • 手动失效: 缓存由管理员或应用程序代码手动失效。此策略对缓存失效提供了最大的控制,但它需要仔细监控和管理。

示例:缓存数据库查询

假设您有一个从数据库中检索用户数据的函数:

import redis
import json
import time# Assume this function fetches data from a database
def get_user_from_db(user_id):# Simulate a database query with a delaytime.sleep(1)user_data = {"id": user_id, "name": f"User {user_id}", "email": f"user{user_id}@example.com"}return user_datadef get_user(user_id, redis_client):"""Retrieves user data from cache if available, otherwise fetches from the database,caches it, and returns it."""cache_key = f'user:{user_id}'cached_user_data = redis_client.get(cache_key)if cached_user_data:# Cache hitprint(f"Cache hit for user {user_id}")user_data = json.loads(cached_user_data)else:# Cache missprint(f"Cache miss for user {user_id}. Fetching from DB.")user_data = get_user_from_db(user_id)user_data_json = json.dumps(user_data)redis_client.set(cache_key, user_data_json, ex=3600)  # Cache for 1 hourreturn user_data# Example usage
redis_client = redis.Redis(host='localhost', port=6379, db=0)user1 = get_user(123, redis_client)
print(user1)user1_cached = get_user(123, redis_client) #This time it will be a cache hit
print(user1_cached)user2 = get_user(456, redis_client)
print(user2)

在此示例中,get_user 函数首先检查用户数据在 Redis 缓存中是否可用。如果是,则从缓存中检索数据并返回数据。否则,将从数据库中检索数据,将其存储在缓存中,过期时间为 1 小时,然后返回。

高级缓存技术

缓存防盗

当大量请求同时命中缓存,并且缓存已过期或为空时,就会发生缓存加速。这可能会使数据库过载,因为所有请求都尝试同时检索数据。

要防止缓存踩踏,可以使用以下技术:

  • Probabilistic Early Expiration(概率提前到期): 您可以向过期时间添加一个小的随机延迟,而不是为所有缓存条目设置固定的过期时间。这有助于分配数据库上的负载。
  • 锁定: 当发生缓存未命中时,您可以获取一个锁,以防止其他请求同时尝试从数据库中检索数据。只有第一个请求会检索数据,将其存储在缓存中,然后释放锁。
  • 后台刷新: 您可以在缓存过期之前在后台刷新缓存。这可确保缓存始终是最新的,并降低缓存被踩踏的可能性。

使用 Redis 数据结构进行缓存

Redis 提供了各种可用于缓存不同类型数据的数据结构:

  • Strings: 用于缓存简单的键值对,例如用户 ID 和名称。
  • Hashes: 用于缓存具有多个字段的对象,例如用户配置文件。
  • Lists: 用于缓存有序数据,例如最近的活动源。
  • Sets: 用于缓存唯一数据,例如用户角色。
  • Sorted Sets: 用于缓存排名数据,例如排行榜。

选择正确的数据结构可以提高缓存的效率和性能。

示例:缓存博客文章列表

假设您要缓存最近的博客文章列表。您可以使用 Redis 列表来存储帖子 ID,然后在需要时从数据库中检索完整的帖子数据。

import redis
import json# Connect to Redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)# Assume this function fetches blog posts from a database
def get_blog_posts_from_db():# Simulate a database queryblog_posts = [{"id": 1, "title": "Redis Caching", "content": "..."},{"id": 2, "title": "NoSQL Databases", "content": "..."},{"id": 3, "title": "Python Programming", "content": "..."}]return blog_postsdef get_recent_blog_posts(redis_client, limit=10):"""Retrieves recent blog posts from cache if available, otherwise fetches from the database,caches it, and returns it."""cache_key = 'recent_blog_posts'cached_post_ids = redis_client.lrange(cache_key, 0, limit - 1)if cached_post_ids:# Cache hitprint("Cache hit for recent blog posts")post_ids = [int(post_id) for post_id in cached_post_ids]# In a real application, you would fetch the full post data from the database# based on these IDs.  Here, we just return the IDs.return post_idselse:# Cache missprint("Cache miss for recent blog posts. Fetching from DB.")blog_posts = get_blog_posts_from_db()post_ids = [post['id'] for post in blog_posts]# Store the post IDs in Redisfor post_id in reversed(post_ids):  # Add in reverse order to maintain orderredis_client.lpush(cache_key, post_id)redis_client.expire(cache_key, 3600)  # Cache for 1 hourreturn post_ids[:limit]# Example usage
recent_posts = get_recent_blog_posts(redis_client)
print(recent_posts)recent_posts_cached = get_recent_blog_posts(redis_client) #This time it will be a cache hit
print(recent_posts_cached)

实践练习

  1. 实施 Cache-Aside 策略: 创建一个使用 Redis 缓存 API 调用结果的函数。该函数应首先检查数据在缓存中是否可用。如果是,则返回缓存的数据。如果没有,请进行 API 调用,将结果存储在缓存中并指定过期时间,然后返回结果。
  2. 实施缓存失效: 修改前面的函数,以便在底层数据更改时使缓存失效。您可以通过更新数据库中的值或调用其他 API 终端节点来模拟数据更改。
  3. 使用 Redis 哈希来缓存对象: 创建一个使用 Redis 哈希缓存用户配置文件的函数。该函数应将每个用户配置文件存储为单独的哈希值,其中包含 name、email 和其他相关信息的字段。
  4. 实施缓存踩踏防护: 修改 API 缓存功能,以防止使用概率提前过期或锁定的缓存踩踏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/81063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Nginx学习笔记】:Fastapi服务部署单机Nginx配置说明

服务部署单机Nginx配置说明 服务.conf配置文件: upstream asr_backend {server 127.0.0.1:8010; }server {listen 80;server_name your_domain.com;location / {proxy_pass http://localhost:8000;proxy_set_header Host $host;proxy_set_header X-Real-IP $remot…

Qt网络编程

前言 Qt为了支持跨平台,对系统网络编程的API(socket API)也进行了重新分装。 实际Qt中进行网络编程也不一定使用Qt封装的网络API,也有可能使用的是系统原生API或者其他第三方框架的API。 若要使用Qt中的网络编程的API&#xff…

矩阵短剧系统:如何用1个后台管理100+小程序?技术解析与实战应用

引言:短剧行业的效率革命 2025年,短剧市场规模已突破千亿,但传统多平台运营模式面临重复开发成本高、用户数据分散、内容同步效率低等痛点。行业亟需一种既能降本增效又能聚合流量的解决方案——“矩阵短剧系统”。通过“1个后台管理100小程…

嵌入式STM32学习——ESP8266 01S的基础介绍

简介 ESP8266 系列模组是深圳市安信可科技有限公司开发的一系列基于乐鑫ESP8266EX的低功耗UART-WiFi芯片模组,可以方便地进行二次开发,接入云端服务,实现手机3/4G全球随时随地的控制,加速产品原型设计。 模块核心处理器 ESP8266 在…

form-create-designer中$inject参数的数据结构及各项属性说明

FcDesigner 是一款基于Vue的开源低代码可视化表单设计器工具,通过数据驱动表单渲染。可以通过拖拽的方式快速创建表单,提高开发者对表单的开发效率,节省开发者的时间。并广泛应用于在政务系统、OA系统、ERP系统、电商系统、流程管理等领域。 …

Jasypt概述及整合SpringBoot实现敏感数据加密

前言 在实际开发中,Spring Boot应用的配置文件中经常包含数据库密码、API密钥等敏感信息。如果这些信息以明文形式存储,会带来严重的安全隐患。本文将详细介绍如何使用Jasypt(Java Simplified Encryption)对Spring Boot配置文件中…

Better Faster Large Language Models via Multi-token Prediction 原理

目录 模型结构: Memory-efficient implementation: 实验: 1. 在大规模模型上效果显著: 2. 在不同类型任务上的效果: 为什么MLP对效果有提升的几点猜测: 1. 并非所有token对生成质量的影响相同 2. 关…

git merge解冲突后,add、continue提交

git merge解冲突后,add、continue提交 git merge操作冲突后,需要手动解冲突,解完冲突后,需要: git add . 然后,进入一般的正常git代码提交流程。 git合并‘merge’其他分支的个别文件到当前branch_gitbash 合并branc…

3.8.1 利用RDD实现词频统计

在本次实战中,我们通过Spark的RDD实现了词频统计功能。首先,准备了包含单词的文件并上传至HDFS。接着,采用交互式方式逐步完成词频统计,包括创建RDD、单词拆分、映射为二元组、按键归约以及排序等操作。此外,还通过创建…

应对进行性核上性麻痹,健康护理铸就温暖防线

进行性核上性麻痹(PSP)是一种罕见的神经退行性疾病,主要影响患者的运动、平衡及吞咽等功能。针对这类患者,有效的健康护理对提升其生活质量、延缓病情发展至关重要。 在日常生活护理方面,由于患者存在平衡障碍和肌肉僵…

融合蛋白质语言模型和图像修复模型,麻省理工与哈佛联手提出PUPS ,实现单细胞级蛋白质定位

蛋白质亚细胞定位(subcellular localization of a protein)是指蛋白质在细胞结构中具体的定位情况, 这对蛋白质行使其生物学功能至关重要。举个简单例子,如果把细胞想象成一个庞大的企业,其中细胞核、线粒体、细胞膜等…

lanqiaoOJ 4330:欧拉函数模板

【题目来源】 https://www.lanqiao.cn/problems/4330/learning/ 【问题描述】 这是一道模板题。 首先给出欧拉函数的定义:即 φ(n) 表示的是小于等于 n 的数中和 n 互质的数的个数。 比如说 φ(6)2,当 n 是质数的时候,显然有φ(n)n-1。 【题…

无人机电子防抖技术要点概述!

一、技术要点 1. 传感器数据融合 电子防抖需结合陀螺仪、加速度计、视觉传感器等多源数据,实时检测无人机的姿态变化和振动频率。例如,IMU(惯性测量单元)通过加速度计和陀螺仪测量飞行器的姿态和运动状态,结合视觉感…

Win10 安装单机版ES(elasticsearch),整合IK分词器和安装Kibana

一. 先查看本机windows是否安装了ES(elasticsearch),检查方法如下: 检查进程 按 Ctrl Shift Esc 组合键打开 “任务管理器”。在 “进程” 选项卡中,查看是否有 elasticsearch 相关进程。如果有,说明系统安装了 ES。 检查端口…

BIO、NIO、AIO 的区别与实战应用解析

导语: BIO、NIO 和 AIO 是后端面试中的经典话题,尤其在高并发、高性能场景下更是重中之重。本文将从面试官视角出发,深入剖析三者的区别、典型题目和实战解答,助你掌握答题技巧,轻松拿下这一高频考点! 一、…

电脑风扇转速不正常的原因

一、硬件故障或接触问题 1. 风扇本身损坏 扇叶卡顿或轴承磨损:灰尘堆积、异物缠绕(如头发、线缆)会导致扇叶转动阻力增大,发出异响并转速下降;轴承润滑脂干涸或老化会引起风扇噪音大、转速不稳定。电机故障&#xff…

运维打铁:生产服务器用户权限管理方案全解析

文章目录 一、引言二、方案设计2.1 权限模型选择2.2 角色定义2.3 权限分配2.4 用户与角色关联 三、相关代码注释(以 Linux 系统为例)3.1 用户创建与角色分配脚本3.2 权限设置脚本 四、常见问题解决4.1 用户无法登录4.2 用户权限不足4.3 权限文件修改后不…

在tp6模版中加减法

实际项目中,我们经常需要标签变量加减运算的操作。但是,在ThinkPHP中,并不支持模板变量直接运算的操作。幸运的是,它提供了自定义函数的方法,我们可以利用自定义函数解决:ThinkPHP模板自定义函数语法如下&a…

Fastjson利用链JdbcRowSetImpl分析

首先创建客户端 package com.yq1ng.vul;import com.alibaba.fastjson.JSON;/*** FastJsonTest** author yq1ng* date 2021/12/29 19:45* since 1.0.0*/ public class FastJsonTest {public static void main(String[] args) {String ser "{\"type\":\"co…

基于OAuth2-proxy和Keycloak为comfyui实现SSO

背景 comfyui无认证被漏扫后易被rce挖矿 攻击过程 https://www.oschina.net/news/340226 https://github.com/comfyanonymous/ComfyUI/discussions/5165 阿里云漏洞库关于comfyui的漏洞 https://avd.aliyun.com/search?qcomfyui&timestamp__1384n4%2BxBD0GitGQ0QD8ID%2F…