百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

从0到1掌握缓存:Redis与Spring Cache实战指南

nanshan 2025-03-01 14:50 20 浏览 0 评论

一、引言

在当今互联网应用飞速发展的时代,高并发场景已成为常态。无论是电商平台的促销活动,还是社交网络的热门话题讨论,大量用户的同时访问对系统的性能提出了极高的挑战。在高并发的重压之下,数据库往往成为性能瓶颈。频繁的数据库查询操作,不仅耗时较长,还会占用大量的系统资源,导致系统响应速度变慢,甚至出现卡顿、崩溃等情况。为了应对这一挑战,缓存技术应运而生,成为提升系统性能的关键解决方案。

Redis 作为一款高性能的内存数据库,在缓存领域占据着举足轻重的地位。它以其卓越的读写速度、丰富的数据结构以及强大的功能特性,为高并发场景下的数据缓存提供了高效的支持。而 Spring Cache 则是 Spring 框架提供的一个优秀的缓存抽象层,它极大地简化了在 Spring 应用中使用缓存的过程,使得开发者能够轻松地将缓存集成到业务逻辑中,提升应用的整体性能。

接下来,让我们深入探索 Redis 和 Spring Cache 的世界,了解它们的工作原理、核心特性以及在实际项目中的应用技巧。

二、Redis 基础入门

(一)什么是 Redis

Redis,全称为 Remote Dictionary Server,是一个开源的内存数据存储系统,它可以用作数据库、缓存和消息中间件 。Redis 基于内存存储,这使得它的读写速度极快,能轻松达到每秒数十万次的操作,性能远超传统的磁盘存储数据库。同时,Redis 支持持久化,可以将数据存储到磁盘上,保证数据的安全性,即便服务器断电重启,数据也不会丢失。

Redis 支持多种丰富的数据结构,如 String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合)等,这些数据结构能满足不同的应用需求。例如,String 类型可用于缓存简单的键值对数据;Hash 类型适合存储对象,将对象的各个属性作为字段存储;List 类型可实现消息队列,按照插入顺序存储数据;Set 类型可用于去重和集合运算;Zset 类型则常用于实现排行榜,根据元素的分数进行排序。

此外,Redis 还支持事务操作,能将一系列命令打包成一个事务,保证这些命令的原子性,要么全部执行成功,要么全部执行失败。它的发布订阅机制也很实用,可实现简单的消息队列功能,将消息发送到指定频道,让订阅了该频道的客户端接收消息。在高可用性方面,Redis 支持主从复制和哨兵机制,能实现数据的备份和故障转移,提高系统的可用性。而且,Redis 的命令简单易懂,学习成本低,还有丰富的客户端库和工具可供使用,这使得开发者能轻松上手,将其应用到各种项目中。

(二)Redis 数据结构探秘

Redis 之所以能在众多缓存技术中脱颖而出,很大程度上得益于其丰富且强大的数据结构。这些数据结构不仅为开发者提供了灵活的数据存储和处理方式,还能根据不同的业务场景进行优化,以达到最佳的性能表现。下面,我们就来深入探索 Redis 的五种基本数据结构。

  1. String(字符串)
    • 原理:String 是 Redis 最基本的数据类型,一个键对应一个值,值可以是字符串、整数或浮点数,最大可存储 512MB。在 Redis 内部,String 类型通过 int、SDS(Simple Dynamic String,简单动态字符串)作为结构存储,int 用来存放整型数据,SDS 存放字节 / 字符串和浮点型数据 。与 C 字符串不同,SDS 记录了字符串的长度,获取字符串长度的时间复杂度为 O (1),而 C 字符串获取长度需要遍历整个字符串,时间复杂度为 O (N)。同时,SDS 通过预分配和惰性释放策略,有效减少了内存重分配的次数,并且能杜绝缓冲区溢出问题,还具备二进制安全特性,可存储任意格式的二进制数据。
    • 应用场景:String 类型的应用场景非常广泛。在缓存数据方面,它可以存储用户登录状态、Token、配置信息等,比如将用户的登录 Token 存储在 Redis 中,每次用户请求时,先从 Redis 中验证 Token 的有效性,减少数据库的查询压力。在计数器场景中,通过 INCR、DECR 命令可以实现简单的计数器,例如统计网站的访问量,每次有用户访问时,对相应的计数器键执行 INCR 操作,就能轻松统计出访问量。在分布式锁的实现中,结合 SETNX 命令(SET if Not eXists,当键不存在时设置键值),可以用字符串来实现简单的分布式锁。例如,在分布式系统中,多个节点可能同时尝试访问共享资源,通过 SETNX 命令设置一个锁键,只有设置成功的节点才能获取到锁,访问共享资源,操作完成后删除锁键,释放锁。
  1. List(列表)
    • 原理:List 是一个按照插入顺序排序的字符串元素集合,支持在头部(LPUSH)和尾部(RPUSH)插入元素,也支持从头部(LPOP)和尾部(RPOP)弹出元素。它的底层数据结构是双向链表或压缩列表。当列表的元素个数小于 512 个(默认值,可由 list - max - ziplist - entries 配置),且每个元素的值都小于 64 字节(默认值,可由 list - max - ziplist - value 配置)时,Redis 会使用压缩列表作为底层数据结构,以节省内存空间;当不满足这些条件时,则使用双向链表。不过在 Redis 3.2 版本之后,List 数据类型底层数据结构由 quicklist 实现,它结合了双向链表和压缩列表的优点,既能在元素较少时节省内存,又能在元素较多时保证操作效率。
    • 应用场景:List 常用于实现消息队列。生产者使用 LPUSH 命令将消息插入到队列的头部,消费者使用 RPOP 命令从队列的尾部读取消息,实现先进先出的消息处理。例如,在一个电商系统中,订单消息可以通过 List 进行传递,订单生成后,将订单信息作为消息插入到 List 中,后台的订单处理服务从 List 中读取订单消息进行处理。此外,List 还可以用于实现简单的任务队列,将任务按照顺序添加到 List 中,工作线程从 List 中获取任务并执行。
  1. Hash(哈希)
    • 原理:Hash 是由键值对组成的无序散列,适合存储对象。它的每个键可以有多个字段,每个字段都有一个值,操作包括 HSET(设置字段值)、HGET(获取字段值)、HDEL(删除字段)等。Hash 使用了两种底层数据结构,在小数据量时使用压缩列表(ziplist),大数据量时使用哈希表(hashtable)。压缩列表在元素较少时能有效节省内存,随着元素的增加,当达到一定阈值时,会自动转换为哈希表,以保证查询效率。
    • 应用场景:在存储用户信息时,Hash 非常实用。以用户 ID 作为键,用户的属性(如姓名、年龄、性别等)作为字段,将用户信息存储在 Hash 中,避免了将整个用户对象序列化成字符串存储的繁琐操作,并且可以方便地对单个属性进行更新。例如,要更新用户的年龄,只需使用 HSET 命令即可。在配置项管理方面,也可以将配置项存储在 Hash 中,方便根据字段名快速访问和更新某个配置。
  1. Set(集合)
    • 原理:Set 是无序、唯一的字符串集合,提供类似于数学集合的操作,如 SADD(添加元素)、SREM(删除元素)、SISMEMBER(判断元素是否存在)、SMEMBERS(获取所有元素)、SINTER(求交集)、SUNION(求并集)、SDIFF(求差集)等。Set 的底层实现,在小集合时使用整数集合(intset),大集合时使用哈希表(hashtable)。通过哈希表的快速查找特性,可以实现 O (1) 的时间复杂度来判断元素是否存在,保证了集合操作的高效性。
    • 应用场景:在标签系统中,Set 可用于存储用户标签。每个用户的标签组成一个 Set,通过集合运算可以方便地找出具有相同标签的用户群体。例如,找出同时关注了 “科技” 和 “美食” 标签的用户,只需对这两个标签对应的 Set 执行 SINTER 命令即可。在去重功能方面,Set 也发挥着重要作用,比如在处理热门搜索词、访问日志时,利用 Set 的唯一性特性可以避免重复数据的存储。
  1. Zset(有序集合)
    • 原理:Zset 类似于 Set,但每个元素关联一个分数(score),集合中的元素会按分数排序。支持的操作包括 ZADD(添加元素及分数)、ZRANGE(按分数范围获取元素)、ZREM(删除元素)、ZREVRANGE(按分数逆序获取元素)、ZCOUNT(统计指定分数范围内的元素个数)等。Zset 的底层使用跳表(Skiplist)和哈希表相结合的数据结构。跳表使得 Zset 支持快速的范围查询和插入操作,时间复杂度为 O (logN),哈希表则保证了元素的快速定位,通过这两种数据结构的结合,Zset 能高效地实现有序集合的功能。
    • 应用场景:Zset 最典型的应用场景是排行榜。比如在游戏中的积分榜,将玩家的 ID 作为元素,玩家的分数作为 score,通过 ZADD 命令添加玩家及其分数,使用 ZRANGE 或 ZREVRANGE 命令可以获取排名靠前或靠后的玩家。在延迟任务的实现中,也可以通过设置任务的执行时间作为分数,将任务添加到 Zset 中,按时间从集合中取出需要执行的任务,实现任务的延迟执行。

三、Redis 持久化机制

在实际应用中,数据的安全性和持久性至关重要。尽管 Redis 以内存存储数据,能提供极快的读写速度,但内存数据具有易失性,一旦服务器断电、崩溃或重启,内存中的数据就会丢失。为了解决这个问题,Redis 提供了强大的持久化机制,确保数据在各种情况下都能得到有效保存和恢复 。

(一)为什么需要持久化

Redis 作为内存数据库,其数据存储在内存中,这使得它在读写操作上具有极高的性能。然而,内存的易失性也带来了数据丢失的风险。如果没有持久化机制,一旦服务器出现故障,如断电、硬件损坏或软件崩溃,内存中的所有数据都将瞬间消失。这对于许多应用场景来说是无法接受的,例如电商系统中的订单数据、用户信息,金融系统中的交易记录等,这些数据的丢失可能会给企业带来巨大的损失。

持久化的主要目的就是将 Redis 内存中的数据保存到磁盘上,形成一个持久化文件。这样,当 Redis 服务器重启时,可以从磁盘上的持久化文件中读取数据,重新加载到内存中,从而恢复到故障前的状态,保证数据的安全性和完整性。同时,持久化文件也可以用于数据备份、数据迁移以及灾难恢复等场景,为系统的稳定运行提供了有力的保障。

(二)RDB 持久化

  1. 原理剖析

RDB(Redis Database)持久化是将 Redis 在某一时刻的内存数据以快照的形式保存到磁盘上的二进制文件(默认名为 dump.rdb)。当需要进行 RDB 持久化时,Redis 会调用 fork 函数创建一个子进程,这个子进程是主进程的副本,它与主进程共享内存数据。子进程负责将内存数据写入到 RDB 文件中,在写入过程中,采用了 Copy - on - Write(写时复制)机制。

写时复制机制的工作原理是:在子进程创建时,子进程和主进程共享相同的内存页面,这些页面的权限被设置为只读。当主进程对内存数据进行修改时,操作系统会为被修改的数据页创建一个新的副本,主进程在新副本上进行修改,而子进程仍然使用原来的内存页面进行 RDB 文件的写入。这样就保证了在 RDB 持久化过程中,主进程可以继续处理客户端的请求,而不会影响子进程的快照操作,同时也避免了不必要的内存复制,提高了持久化的效率 。

2. 触发方式

  • 手动触发
    • save 命令:执行 save 命令时,Redis 会在主线程中进行 RDB 文件的创建,这会导致主线程阻塞,直到 RDB 文件创建完成。在阻塞期间,Redis 无法处理客户端发送的任何请求,因此在生产环境中一般不建议使用 save 命令。例如,在一个高并发的电商系统中,如果执行 save 命令,可能会导致大量用户请求超时,严重影响用户体验。
    • bgsave 命令:bgsave 命令会让 Redis 创建一个子进程,由子进程负责 RDB 文件的生成,而主进程继续处理客户端请求。这样就避免了主线程的阻塞,保证了 Redis 的正常服务。子进程在生成 RDB 文件时,先将数据写入到一个临时文件,完成后再将临时文件重命名为 dump.rdb,替换原来的 RDB 文件。在一个社交平台中,使用 bgsave 命令进行 RDB 持久化,即使在持久化过程中,用户的点赞、评论等操作也能正常进行,不会受到影响。
  • 自动触发
    • 配置 save 规则:在 Redis 的配置文件 redis.conf 中,可以通过配置 save 参数来设置自动触发 RDB 持久化的条件。例如,配置 “save 900 1” 表示在 900 秒内,如果 Redis 数据发生了至少 1 次修改,就会自动执行 bgsave 命令;“save 300 10” 表示在 300 秒内,数据发生至少 10 次修改时触发;“save 60 10000” 表示 60 秒内数据发生至少 10000 次修改时触发 。只要满足其中一个条件,就会自动触发 RDB 持久化。
    • shutdown 命令:当执行 shutdown 命令关闭 Redis 服务器时,如果没有开启 AOF 持久化,Redis 会自动执行 bgsave 命令,将内存数据保存到 RDB 文件中,确保数据不会丢失。
    • flushall 命令:执行 flushall 命令清空 Redis 数据库时,也会触发 RDB 持久化,不过此时生成的 RDB 文件中不包含任何数据,因为数据库已经被清空。
  1. 优缺点分析
    • 优点
      • 数据恢复速度快:RDB 文件是一个紧凑的二进制文件,在恢复数据时,Redis 只需将 RDB 文件中的数据一次性加载到内存中,速度非常快。对于一些对数据恢复时间要求较高的场景,如电商系统在大促活动前的预热阶段,需要快速恢复缓存数据,RDB 持久化就非常适用。
      • 文件体积小:RDB 文件采用了压缩算法对数据进行压缩,使得文件体积相对较小,便于存储和传输。在进行数据备份时,较小的文件体积可以减少存储空间的占用,同时也能加快备份和恢复的速度。
      • 对 Redis 性能影响小:使用 bgsave 命令进行 RDB 持久化时,主进程不会被阻塞,仍然可以正常处理客户端请求,对 Redis 的性能影响较小。在高并发的应用场景中,这一优点尤为重要,能保证系统的稳定运行。
    • 缺点
      • 数据丢失风险:由于 RDB 是定期进行快照,在两次快照之间如果发生服务器故障,这段时间内的数据将会丢失。例如,如果配置的快照间隔是 5 分钟,那么在最坏情况下可能会丢失 5 分钟的数据。对于一些对数据实时性要求较高的业务,如金融交易系统,这种数据丢失的风险是不可接受的。
      • fork 子进程开销大:在执行 bgsave 命令时,Redis 需要 fork 一个子进程来进行 RDB 文件的生成。fork 子进程会消耗一定的系统资源,包括内存和 CPU 等。当 Redis 数据量较大时,fork 子进程的过程可能会比较耗时,并且在子进程创建初期,由于父子进程共享内存,可能会导致内存使用量瞬间增加,对系统性能产生一定的压力。

(三)AOF 持久化

  1. 原理详解

AOF(Append - Only File)持久化是通过记录 Redis 服务器执行的写命令来实现数据持久化的。当 Redis 执行一个写命令(如 SET、DEL、INCR 等)后,会将该命令以协议格式追加到 AOF 文件的末尾。AOF 文件采用文本格式,内容是一个个的 Redis 命令,例如 “*3\r\n\(3\r\nSET\r\n\)3\r\nkey\r\n$5\r\nvalue\r\n” 表示执行了 “SET key value” 命令。

当 Redis 服务器重启时,会读取 AOF 文件中的命令,并按照顺序重新执行这些命令,从而将数据库恢复到故障前的状态。这种方式就像是一个日志记录,记录了数据库的所有写操作历史,通过重放这些操作来恢复数据 。

2. 写入机制

  • 写后日志:Redis 先执行写命令,然后再将命令追加到 AOF 文件中。这样做的好处是可以避免在命令执行前记录错误的命令,同时也不会阻塞当前写操作命令的执行。如果先记录命令再执行,当命令语法错误时,记录到 AOF 文件中的错误命令会导致在恢复数据时出错。
  • 缓冲区写入:Redis 并不会每次执行写命令后就立即将命令写入磁盘,而是先将命令追加到 aof_buf 缓冲区中。当缓冲区达到一定条件时,才会将缓冲区中的内容写入到 AOF 文件中。这样可以减少磁盘 I/O 操作的次数,提高写入效率。
  • fsync 策略
    • always:每次执行写命令后,都立即调用 fsync 函数将 aof_buf 缓冲区中的内容同步到磁盘。这种策略可以保证数据的安全性,即使服务器发生故障,也不会丢失数据。但由于每次都进行磁盘 I/O 操作,性能较低,适用于对数据安全性要求极高,且数据量较小的场景,如金融交易系统。
    • everysec:每秒调用一次 fsync 函数,将 aof_buf 缓冲区中的内容同步到磁盘。这种策略在性能和数据安全性之间取得了较好的平衡,是 AOF 的默认配置。在大多数应用场景中,即使丢失 1 秒内的数据也是可以接受的,同时每秒一次的磁盘 I/O 操作对性能的影响也相对较小。
    • no:由操作系统决定何时将 aof_buf 缓冲区中的内容同步到磁盘,Redis 不主动进行 fsync 操作。这种策略性能最高,但数据安全性最差,在服务器发生故障时,可能会丢失大量未同步到磁盘的数据,适用于对数据实时性要求不高,且能容忍一定数据丢失的场景,如一些日志记录系统。
  1. AOF 重写
    • 原因:随着 Redis 的运行,AOF 文件会不断增大,因为它记录了所有的写命令。如果 AOF 文件过大,会带来一些问题,如占用过多的磁盘空间、在恢复数据时重放命令的时间过长,导致 Redis 启动时间增加,影响系统的可用性。
    • 机制:AOF 重写的目的是为了压缩 AOF 文件的大小。Redis 会创建一个子进程,子进程读取当前数据库中的数据,然后根据数据的当前状态生成一系列的写命令,这些命令是经过优化的,能够用最少的命令达到和原 AOF 文件相同的数据状态。例如,原 AOF 文件中可能记录了多次对同一个键的修改命令,在重写时,会将这些命令合并为一个最终状态的命令。

在重写过程中,主进程仍然可以继续处理客户端的请求。主进程在执行写命令时,除了将命令追加到 AOF 文件中,还会将命令追加到 AOF 重写缓冲区中。当子进程完成 AOF 重写后,会向主进程发送一个信号,主进程收到信号后,将 AOF 重写缓冲区中的内容写入到新的 AOF 文件中,然后将新的 AOF 文件重命名为原 AOF 文件,完成 AOF 文件的重写。

  • 影响:AOF 重写可以有效减小 AOF 文件的体积,提高数据恢复的效率。在恢复数据时,较小的 AOF 文件重放命令的时间更短,Redis 能够更快地启动并提供服务。同时,AOF 重写也可以减少磁盘空间的占用,提高系统的整体性能 。

(四)混合持久化(Redis 4.0+)

从 Redis 4.0 开始,引入了混合持久化的方式,它结合了 RDB 和 AOF 的优点。在进行 AOF 重写时,Redis 会先将当前内存中的数据以 RDB 快照的形式写入 AOF 文件的开头,然后再将重写期间发生的写命令以 AOF 的格式追加到文件末尾 。

在恢复数据时,Redis 首先加载 AOF 文件开头的 RDB 快照部分,这部分数据是二进制格式,加载速度非常快,可以快速将大部分数据恢复到内存中。然后再重放 AOF 文件中追加的写命令部分,将 RDB 快照之后的数据变化进行恢复,从而完整地恢复出故障前的数据状态。

混合持久化的优势在于,它既利用了 RDB 恢复速度快的特点,又结合了 AOF 数据完整性高的优势。在保证数据安全性的同时,大大提高了数据恢复的效率,减少了 Redis 重启的时间。对于一些对数据恢复速度和完整性都有较高要求的应用场景,如大型电商系统、社交平台等,混合持久化是一种非常理想的选择。

四、Spring Cache 深度解析

(一)Spring Cache 是什么

在 Spring 框架的生态体系中,Spring Cache 是一个重要的模块,它为开发者提供了一种统一且便捷的方式来使用缓存功能。Spring Cache 本质上是一个缓存抽象层,它并不直接实现具体的缓存功能,而是定义了一系列的接口和规范,通过这些接口,可以将不同的缓存实现(如 Redis、Ehcache、Caffeine 等)整合到 Spring 应用中,使得开发者能够在不深入了解底层缓存细节的情况下,轻松地使用缓存来提升应用的性能 。

Spring Cache 的核心优势在于它的注解驱动和抽象层设计。通过简单的注解,如 @Cacheable、@CachePut、@CacheEvict 等,开发者可以将缓存逻辑无缝地融入到业务方法中,无需编写大量的缓存操作代码,极大地提高了开发效率。同时,抽象层的设计使得应用程序与具体的缓存实现解耦,当需要更换缓存技术时,只需要在配置文件中进行简单的修改,而无需对业务代码进行大规模的调整,增强了应用的可维护性和扩展性。

(二)核心接口与原理

  1. Cache 接口

Cache 接口是 Spring Cache 中定义缓存操作的核心接口,它提供了一系列方法来操作缓存数据。

    • String getName():该方法用于获取缓存的名称,每个缓存都有一个唯一的名称,通过名称可以在 CacheManager 中获取对应的缓存实例。例如,在一个电商应用中,可能会有 “productCache”“userCache” 等不同名称的缓存,分别用于存储商品信息和用户信息。
    • Object getNativeCache():返回底层实际使用的缓存对象,这在需要直接访问底层缓存的一些特殊操作时非常有用。比如,当使用 Redis 作为缓存时,通过这个方法可以获取到 Redis 的客户端对象,从而执行一些 Redis 特有的命令。
    • Cache.ValueWrapper get(Object key):根据指定的键从缓存中获取值,并将其包装在 ValueWrapper 中返回。如果缓存中不存在该键对应的值,则返回 null。例如,在获取用户信息时,可以使用用户 ID 作为键,通过该方法从缓存中获取用户信息。
    • T get(Object key, Class type):与上一个方法类似,但它会将获取到的值转换为指定的类型后返回。如果缓存中不存在该键对应的值,或者值的类型无法转换为指定类型,则返回 null。比如,在获取商品对象时,可以指定返回类型为 Product 类,该方法会自动将缓存中的值转换为 Product 对象返回。
    • T get(Object key, Callable valueLoader):先尝试从缓存中获取指定键的值,如果缓存中不存在,则调用 valueLoader 回调函数来加载值,并将加载的值存入缓存,最后返回该值。在查询数据库中数据时,如果缓存中没有该数据,就可以通过这个方法从数据库中查询数据,并将数据存入缓存,下次查询时就可以直接从缓存中获取。
    • void put(Object key, Object value):将指定的键值对存入缓存。在更新用户信息后,可以使用该方法将更新后的用户信息存入缓存,保证缓存数据的一致性。
    • boolean evict(Object key):从缓存中删除指定键的缓存项。当用户数据发生删除操作时,就可以通过这个方法删除缓存中对应的用户信息,避免读取到过期数据。
    • void clear():清空缓存中的所有数据。在系统进行数据初始化或数据迁移等操作时,可能需要使用该方法清空缓存,以便重新加载最新的数据。
  1. CacheManager 接口

CacheManager 接口负责管理多个 Cache 实例,它是 Spring Cache 中缓存管理的核心接口。

    • Cache getCache(String name):根据缓存名称获取对应的 Cache 实例。在一个复杂的 Spring 应用中,可能会有多个不同用途的缓存,通过 CacheManager 的这个方法,可以方便地获取到需要的缓存实例。例如,在一个社交平台应用中,通过 “userCache” 名称可以获取到用于存储用户信息的缓存实例,通过 “postCache” 名称可以获取到用于存储帖子信息的缓存实例。
    • Collection getCacheNames():返回当前 CacheManager 管理的所有缓存的名称集合。通过这个方法,可以方便地了解当前系统中定义了哪些缓存,以便进行统一的管理和维护。在系统监控或缓存配置调整时,这个方法能提供有用的信息。

在 Spring 框架中,有多种实现了 CacheManager 接口的类,如 ConcurrentMapCacheManager、RedisCacheManager、EhCacheCacheManager 等,分别对应不同的缓存实现方式。例如,ConcurrentMapCacheManager 使用 Java 的 ConcurrentMap 作为缓存存储,适用于单应用场景;RedisCacheManager 则将 Redis 作为缓存存储,适用于分布式场景。

  1. 原理剖析

Spring Cache 的实现原理主要基于 AOP(面向切面编程)。当在 Spring 应用中启用缓存功能(通过 @EnableCaching 注解)后,Spring 会自动创建一个切面,这个切面会在方法执行前后进行拦截。

    • 方法执行前:如果方法上标注了 @Cacheable 注解,Spring 会首先检查缓存中是否存在该方法的执行结果。它会根据 @Cacheable 注解中指定的缓存名称和键生成策略,在对应的缓存中查找是否有与当前方法参数匹配的缓存值。如果缓存中存在,则直接返回缓存中的值,不再执行方法体,从而提高了系统的响应速度。
    • 方法执行后:如果方法上标注了 @Cacheable 注解,且缓存中不存在该方法的执行结果,或者方法上标注了 @CachePut 注解,Spring 会在方法执行完毕后,将方法的返回值存入缓存中。对于 @CachePut 注解,无论缓存中是否存在旧值,都会将新的返回值存入缓存,以保证缓存数据的及时更新。
    • 缓存删除:当方法上标注了 @CacheEvict 注解时,Spring 会在方法执行前后(根据 beforeInvocation 属性的值决定),按照注解中指定的缓存名称和键,从缓存中删除相应的缓存项。在删除用户数据时,通过 @CacheEvict 注解可以同时删除缓存中与该用户相关的所有信息,保证缓存与数据库数据的一致性。

通过这种 AOP 的方式,Spring Cache 将缓存操作与业务逻辑解耦,使得开发者可以专注于业务代码的编写,而无需过多关注缓存的具体实现细节,极大地提高了开发效率和代码的可维护性。

(三)常用注解及使用

  1. @Cacheable

@Cacheable 注解是 Spring Cache 中最常用的注解之一,主要用于标记方法的返回值可以被缓存。当一个方法被 @Cacheable 注解修饰后,Spring 在调用该方法时,会首先检查指定的缓存中是否已经存在该方法的执行结果。如果存在,Spring 会直接从缓存中获取结果并返回,而不会执行方法体中的代码;如果缓存中不存在,则执行方法体,获取方法的返回值,并将返回值存入缓存中,以便下次调用时直接使用 。

    • 基本用法
@Service

public class ProductService {

@Autowired

private ProductRepository productRepository;

@Cacheable(value = "products", key = "#productId")

public Product getProductById(Long productId) {

return productRepository.findById(productId);

}

}

在上述代码中,@Cacheable 注解的 value 属性指定了缓存的名称为 “products”,表示将该方法的返回值缓存到名为 “products” 的缓存中。key 属性指定了缓存的键为 “#productId”,其中 “#productId” 是 Spring EL 表达式,表示使用方法的参数 productId 作为缓存的键。这样,当多次调用getProductById方法,传入相同的 productId 时,只有第一次会执行数据库查询操作,后续调用都会直接从缓存中获取结果,大大提高了查询效率。

  • 常用参数
    • cacheNames/value:这两个属性是等价的,用于指定缓存的名称,可以是一个字符串数组,表示可以将方法的返回值缓存到多个缓存中。例如,@Cacheable(cacheNames = {"products", "productCache"}),表示将结果同时缓存到 “products” 和 “productCache” 两个缓存中。
    • key:用于指定缓存的键,支持 Spring EL 表达式。除了使用方法参数作为键外,还可以使用更复杂的表达式。例如,@Cacheable(value = "products", key = "#root.methodName + '_' + #productId"),表示使用方法名和 productId 拼接作为缓存的键。
    • condition:用于指定缓存的条件,只有当条件表达式为 true 时,才会进行缓存操作。例如,@Cacheable(value = "products", key = "#productId", condition = "#productId > 0"),表示只有当 productId 大于 0 时,才会对方法的返回值进行缓存。
    • unless:与 condition 相反,它用于指定不进行缓存的条件,当 unless 表达式为 true 时,不会进行缓存操作。例如,@Cacheable(value = "products", key = "#productId", unless = "#result == null"),表示如果方法的返回值为 null,则不进行缓存。
  1. @CachePut

@CachePut 注解主要用于更新缓存,它的特点是无论缓存中是否存在旧值,都会将方法的返回值存入缓存中,以保证缓存数据与方法执行结果的一致性。@CachePut 注解通常用于方法执行后需要更新缓存的场景,如数据的更新、插入操作。

    • 基本用法
@Service

public class ProductService {

@Autowired

private ProductRepository productRepository;

@CachePut(value = "products", key = "#product.id")

public Product updateProduct(Product product) {

Product updatedProduct = productRepository.save(product);

return updatedProduct;

}

}

在上述代码中,当调用updateProduct方法时,方法会首先执行数据库的更新操作,然后将更新后的 Product 对象返回。由于方法上标注了 @CachePut 注解,Spring 会将返回的更新后的 Product 对象存入名为 “products” 的缓存中,缓存的键为#product.id,即 Product 对象的 id 属性。这样,下次查询该 Product 对象时,就可以从缓存中获取到最新的数据。

  • 注意事项
    • @CachePut 注解不会影响方法的正常执行,它只是在方法执行后将返回值存入缓存。因此,即使缓存操作失败,方法的执行结果仍然有效,不会回滚方法的执行。
    • 在使用 @CachePut 注解时,需要确保 key 属性的一致性。即更新缓存时使用的 key 要与查询缓存时使用的 key 一致,这样才能保证缓存数据的正确更新和查询。
  1. @CacheEvict

@CacheEvict 注解用于从缓存中删除数据,它可以在方法执行前或执行后删除指定的缓存项,支持单个或多个缓存删除操作,常用于数据删除或更新后需要清理缓存的场景,以保证缓存数据的一致性。

    • 删除单个缓存
@Service

public class ProductService {

@Autowired

private ProductRepository productRepository;

@CacheEvict(value = "products", key = "#productId")

public void deleteProduct(Long productId) {

productRepository.deleteById(productId);

}

}

在上述代码中,当调用deleteProduct方法时,方法会首先执行数据库的删除操作,然后根据 @CacheEvict 注解的配置,从名为 “products” 的缓存中删除键为#productId的缓存项。这样,下次查询该 productId 对应的产品时,缓存中就不会存在过期的数据。

  • 删除多个缓存
@Service

public class ProductService {

@Autowired

private ProductRepository productRepository;

@Caching(evict = {

@CacheEvict(value = "products", key = "#productId1"),

@CacheEvict(value = "products", key = "#productId2")

})

public void deleteProducts(Long productId1, Long productId2) {

productRepository.deleteByIdIn(Arrays.asList(productId1, productId2));

}

}

在上述代码中,通过 @Caching 注解结合多个 @CacheEvict 注解,实现了同时删除多个缓存项的功能。在执行deleteProducts方法时,会从名为 “products” 的缓存中删除键为#productId1和#productId2的两个缓存项。

  • 常用参数
    • allEntries:当该参数为 true 时,表示删除指定缓存中的所有缓存项。例如,@CacheEvict(value = "products", allEntries = true),会删除 “products” 缓存中的所有数据。
    • beforeInvocation:当该参数为 true 时,表示在方法执行前删除缓存;为 false 时(默认值),表示在方法执行后删除缓存。在某些情况下,如方法执行可能会失败,且失败时也需要删除缓存的场景下,将 beforeInvocation 设置为 true 可以确保缓存的及时清理。
  1. @Caching

@Caching 注解用于组合多个缓存注解,实现复杂的缓存操作。当一个方法需要同时应用多个缓存注解时,由于 Java 不允许在同一个方法上重复使用相同类型的注解,这时就可以使用 @Caching 注解来组合多个缓存注解 。

    • 基本用法
@Service

public class ProductService {

@Autowired

private ProductRepository productRepository;

@Caching(

cacheable = @Cacheable(value = "products", key = "#productId"),

put = @CachePut(value = "products", key = "#product.id")

)

public Product getOrUpdateProduct(Long productId, Product product) {

Product existingProduct = productRepository.findById(productId);

if (existingProduct!= null) {

return existingProduct;

} else {

Product savedProduct = productRepository.save(product);

return savedProduct;

}

}

}

在上述代码中,getOrUpdateProduct方法上使用了 @Caching 注解,它组合了 @Cacheable 和 @CachePut 注解。当调用该方法时,如果缓存中存在键为#productId的缓存项,则直接返回缓存中的值;如果不存在,则执行方法体,从数据库中查询或保存产品数据。如果是保存操作,方法执行后会将返回的 Product 对象存入缓存中,缓存的键为#product.id。通过 @Caching 注解,实现了在一个方法中同时进行缓存查询和缓存更新的功能。

五、Redis 与 Spring Cache 整合实战

(一)整合步骤

  1. 添加依赖

在 Spring Boot 项目中,使用 Maven 或 Gradle 构建工具来添加 Redis 和 Spring Cache 的依赖。如果使用 Maven,在pom.xml文件中添加以下依赖:

org.springframework.boot

spring-boot-starter-cache

org.springframework.boot

spring-boot-starter-data-redis

如果使用 Gradle,在build.gradle文件中添加:

dependencies {

implementation 'org.springframework.boot:spring-boot-starter-cache'

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

}

这些依赖将引入 Spring Cache 和 Redis 相关的类库,为后续的整合工作奠定基础。

  1. 配置 Redis 连接

在 Spring Boot 的配置文件application.properties或application.yml中配置 Redis 的连接信息。以application.yml为例,配置如下:

spring:

redis:

host: localhost # Redis服务器地址

port: 6379 # Redis服务器端口

password: # 如果Redis设置了密码,在此处填写

database: 0 # Redis数据库索引,默认为0

通过这些配置,Spring Boot 能够与 Redis 服务器建立连接,实现数据的读写操作。

  1. 配置缓存管理器

在 Spring Boot 的配置类中,配置 RedisCacheManager,设置缓存过期时间、序列化方式等。以下是一个配置示例:

import org.springframework.cache.CacheManager;

import org.springframework.cache.annotation.EnableCaching;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheConfiguration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;

@Configuration

@EnableCaching

public class RedisConfig {

@Bean

public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()

.entryTtl(Duration.ofMinutes(5)) // 设置缓存过期时间为5分钟

.disableCachingNullValues() // 禁止缓存null值

.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(new
GenericJackson2JsonRedisSerializer())); // 使用
Jackson2JsonRedisSerializer进行值的序列化

return RedisCacheManager.builder(redisConnectionFactory)

.cacheDefaults(cacheConfiguration)

.build();

}

}

在上述代码中,首先创建了一个RedisCacheConfiguration对象,设置了缓存过期时间为 5 分钟,禁止缓存 null 值,并指定了值的序列化方式为
GenericJackson2JsonRedisSerializer。然后,使用RedisCacheManager.builder构建RedisCacheManager,并将配置好的RedisCacheConfiguration作为默认配置传入。通过这样的配置,Spring Cache 将使用 Redis 作为缓存存储,并按照设定的规则进行缓存操作。

(二)实战案例

  1. 创建 Service 和 Controller

假设我们有一个简单的用户管理系统,需要对用户信息进行缓存。首先创建一个UserService类,在方法上使用 Spring Cache 注解:

import org.springframework.cache.annotation.Cacheable;

import org.springframework.stereotype.Service;

@Service

public class UserService {

// 模拟从数据库中获取用户信息

public String getUserById(String id) {

// 实际应用中这里会是数据库查询操作

return "User: " + id;

}

@Cacheable(cacheNames = "users", key = "#id")

public String getUserByIdFromCache(String id) {

return getUserById(id);

}

}

在上述代码中,getUserById方法模拟从数据库中获取用户信息,getUserByIdFromCache方法使用了@Cacheable注解,将方法的返回值缓存到名为users的缓存中,缓存的键为方法的参数id。

接着创建一个UserController类,用于调用UserService的方法:

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class UserController {

@Autowired

private UserService userService;

@GetMapping("/users/{id}")

public String getUser(@PathVariable String id) {

return userService.getUserByIdFromCache(id);

}

}

在UserController中,通过@Autowired注入UserService,并在getUser方法中调用
userService.getUserByIdFromCache方法,根据用户 ID 获取用户信息。

  1. 测试与验证

启动 Spring Boot 应用后,可以使用 Postman 等工具进行测试。首先发送一个请求GET /users/1,此时会调用UserService的getUserByIdFromCache方法,由于缓存中没有数据,会调用getUserById方法从数据库(这里是模拟获取)获取用户信息,并将结果存入缓存。再次发送相同的请求GET /users/1,可以发现请求响应速度明显加快,并且不会调用getUserById方法,说明数据是从缓存中获取的,验证了缓存的效果。通过这种方式,我们可以直观地看到 Redis 与 Spring Cache 整合后,对系统性能的提升作用。

六、常见问题与解决方案

在使用 Redis 和 Spring Cache 构建缓存体系时,虽然它们能显著提升系统性能,但也可能会遇到一些常见问题,如缓存穿透、缓存击穿、缓存雪崩以及缓存一致性问题。这些问题如果不妥善解决,可能会导致系统性能下降、数据不一致等严重后果。下面我们将详细探讨这些问题及其解决方案。

(一)缓存穿透

  1. 问题描述

缓存穿透是指查询一个在缓存和数据库中都不存在的数据,由于缓存中没有命中,请求会直接穿透到数据库。如果大量的这类请求同时到达,会给数据库带来巨大的压力,甚至可能导致数据库崩溃。例如,在一个电商系统中,如果有恶意用户频繁请求一个不存在的商品 ID,每次请求都需要查询数据库,数据库的负载会急剧增加。

  1. 解决方案
    • 布隆过滤器:布隆过滤器是一种基于概率的数据结构,它可以快速判断一个元素是否可能存在于一个集合中。在缓存穿透场景中,我们可以在查询缓存之前,先通过布隆过滤器判断请求的数据是否存在。如果布隆过滤器判断数据不存在,那么可以直接返回,避免查询数据库。布隆过滤器的原理是通过多个哈希函数将数据映射到一个位数组中,当要判断一个数据是否存在时,通过哈希函数计算出对应的位数组位置,如果这些位置的值都为 1,则认为数据可能存在;如果有任何一个位置的值为 0,则可以确定数据一定不存在 。虽然布隆过滤器存在一定的误判率,但可以通过调整参数(如位数组大小、哈希函数个数)来降低误判率。在一个用户管理系统中,可以在系统启动时,将所有已存在的用户 ID 通过布隆过滤器进行处理,当有新的用户 ID 查询请求时,先通过布隆过滤器判断该 ID 是否可能存在,从而避免对不存在的用户 ID 进行数据库查询。
    • 空值缓存:对于查询结果为空的数据,我们可以将其缓存起来,并设置一个较短的过期时间。这样,当后续有相同的请求到来时,直接从缓存中获取空值,而无需查询数据库。在一个新闻资讯系统中,当查询一篇不存在的新闻时,将空值缓存起来,下次再有相同的查询请求时,直接返回空值,减少对数据库的访问。但需要注意的是,空值缓存可能会占用一定的缓存空间,并且在数据更新时需要及时清理空值缓存,以保证数据的一致性。

(二)缓存击穿

  1. 问题描述

缓存击穿是指缓存中某个热点数据在过期或被删除的瞬间,大量请求同时到达,由于缓存中没有该数据,这些请求会直接访问数据库,导致数据库的负载急剧增加。比如在电商促销活动中,某个热门商品的缓存过期了,而此时大量用户同时访问该商品的详情页,所有请求都需要从数据库中查询商品信息,数据库可能会因为承受不住这么大的压力而出现性能问题,甚至崩溃。

  1. 解决方案
    • 互斥锁:在缓存失效时,使用互斥锁(如 Redis 的 SETNX 命令实现分布式锁)来保证只有一个线程能够查询数据库并更新缓存,其他线程等待该线程完成操作后,再从缓存中获取数据。在一个秒杀系统中,当秒杀商品的缓存失效时,只有获取到互斥锁的线程可以去数据库查询商品库存并更新缓存,其他线程在等待期间可以进行适当的重试,直到获取到缓存中的数据。通过这种方式,可以避免大量请求同时查询数据库,有效减轻数据库的压力。
    • 热点数据永不过期:对于一些热点数据,我们可以设置其缓存永不过期,这样就不会出现缓存过期导致的缓存击穿问题。在社交平台中,对于热门话题的相关数据,可以设置为永不过期,同时通过后台线程或定时任务来定期更新这些数据,保证数据的实时性。不过,这种方法需要注意在数据更新时,要及时更新缓存中的数据,以确保数据的一致性 。

(三)缓存雪崩

  1. 问题描述

缓存雪崩是指在某一时刻,缓存中大量的数据同时过期失效,导致大量请求直接访问数据库,数据库无法承受如此大的压力,从而可能引发系统崩溃。例如,在一个电商平台的促销活动中,为了节省缓存空间,可能会将大量商品的缓存设置为相同的过期时间,当这个过期时间到达时,所有商品的缓存同时失效,大量用户的请求会瞬间涌向数据库,数据库可能会因为过载而无法正常响应。

  1. 解决方案
    • 设置随机过期时间:在设置缓存过期时间时,为每个缓存项添加一个随机的时间偏移,使缓存的过期时间分散开来,避免大量缓存同时过期。在缓存商品信息时,可以在原本的过期时间基础上,随机增加 1 - 5 分钟的时间偏移,这样可以大大降低缓存同时失效的概率,减轻数据库的压力。
    • 多级缓存:采用多级缓存架构,如本地缓存(如 Guava Cache)和分布式缓存(如 Redis)相结合。当分布式缓存失效时,请求可以先从本地缓存中获取数据,如果本地缓存中也没有数据,再去查询数据库。在一个内容管理系统中,首先在本地缓存中存储一些常用的文章内容,当用户请求文章时,先从本地缓存中查询,如果没有命中,再从分布式缓存 Redis 中查询,若 Redis 中也没有,则查询数据库,并将结果依次存入本地缓存和 Redis 中。通过这种方式,即使分布式缓存出现问题,本地缓存也能在一定程度上缓解数据库的压力,提高系统的容错性。

(四)缓存一致性

  1. 问题描述

在分布式环境下,当数据发生更新时,可能会出现缓存与数据库数据不一致的情况。因为在分布式系统中,多个节点可能同时对数据进行读写操作,并且缓存和数据库的更新操作不是原子性的,这就容易导致缓存中的数据和数据库中的数据不一致。在一个分布式电商系统中,当一个用户在节点 A 上修改了自己的收货地址,节点 A 在更新数据库后,由于网络延迟等原因,未能及时更新缓存,此时另一个用户在节点 B 上查询该用户的收货地址,就会从缓存中获取到旧的地址,导致数据不一致。

  1. 解决方案
    • 分布式锁:在进行数据更新操作时,先获取分布式锁,确保同一时间只有一个节点能够更新数据和缓存。获取到锁的节点在更新数据库成功后,立即更新缓存,然后释放锁。在一个分布式订单系统中,当有订单状态更新时,先通过 Redis 获取分布式锁,只有获取到锁的节点才能更新订单状态到数据库,并同时更新缓存中的订单状态信息,其他节点在获取锁失败时,等待一段时间后重试,这样可以保证在分布式环境下,数据和缓存的一致性。
    • 读写锁:使用读写锁(如 Redis 的 Redisson 提供的读写锁)来控制对数据的读写操作。读操作可以同时进行,而写操作需要获取写锁,在写操作期间,禁止其他读写操作。在一个分布式文件系统中,当多个节点需要读取文件元数据时,可以同时获取读锁进行读取;当有节点需要更新文件元数据时,先获取写锁,更新数据库和缓存后,再释放写锁,这样可以保证在高并发读写情况下,数据的一致性。
    • 缓存更新策略:采用合理的缓存更新策略,如 “先更新数据库,再删除缓存” 或 “先删除缓存,再更新数据库”。先更新数据库,再删除缓存策略,在更新数据库成功后,立即删除对应的缓存,下次读取数据时,会从数据库中获取最新数据并重新存入缓存。但这种策略在高并发情况下,可能会出现写操作删除缓存后,读操作先读取到数据库中的旧数据并写入缓存,导致缓存数据不一致的问题。先删除缓存,再更新数据库策略,在更新操作前先删除缓存,然后更新数据库。但在并发情况下,可能会出现读操作在删除缓存后,更新数据库前读取数据,由于缓存已删除,会从数据库中读取旧数据并写入缓存,导致数据不一致。为了避免这些问题,可以结合分布式锁或其他机制来保证缓存更新的原子性和一致性 。

七、总结与展望

在高并发的互联网应用场景中,缓存技术无疑是提升系统性能的关键法宝。Redis 凭借其丰富的数据结构、高效的持久化机制以及卓越的性能表现,成为了缓存领域的佼佼者。它的五种基本数据结构 ——String、List、Hash、Set 和 Zset,各自适用于不同的业务场景,为开发者提供了极大的灵活性。无论是缓存简单的键值对数据,还是实现复杂的消息队列、排行榜等功能,Redis 都能轻松胜任。

而 Spring Cache 作为 Spring 框架提供的缓存抽象层,极大地简化了在 Spring 应用中使用缓存的过程。通过简单的注解,如 @Cacheable、@CachePut、@CacheEvict 等,开发者可以将缓存逻辑无缝地融入到业务方法中,实现对缓存的高效管理。同时,Spring Cache 还支持多种缓存实现,如 Redis、Ehcache、Caffeine 等,使得开发者可以根据项目的实际需求选择最合适的缓存方案。

随着技术的不断发展,缓存技术也在持续演进。未来,我们可以期待缓存技术在以下几个方面取得更大的突破:一是智能化缓存管理,借助人工智能和机器学习技术,缓存系统能够根据应用的实际运行情况和用户行为,自动调整缓存策略,实现更精准的数据缓存和淘汰,进一步提高缓存的命中率和系统性能;二是与其他新兴技术的深度融合,如随着云计算、大数据、区块链等技术的广泛应用,缓存技术将与这些技术紧密结合,为分布式系统、数据处理和安全存储等场景提供更强大的支持;三是在缓存一致性和高可用性方面的持续优化,确保在复杂的分布式环境中,缓存数据的一致性和系统的高可用性,为应用的稳定运行提供坚实保障 。

缓存技术的发展为我们构建高性能、高可用的应用系统提供了有力支持。Redis 和 Spring Cache 作为其中的杰出代表,在实际项目中发挥着重要作用。我们应不断学习和探索缓存技术的应用,充分发挥它们的优势,为用户带来更优质的服务体验。

相关推荐

详解 HTTPS、TLS、SSL、HTTP区别和关系

一、什么是HTTPS、TLS、SSLHTTPS,也称作HTTPoverTLS。TLS的前身是SSL,TLS1.0通常被标示为SSL3.1,TLS1.1为SSL3.2,TLS1.2为SSL...

锐安信SSL证书自动化运维系统:灵活管理SSL/TLS证书全生命周期

点击上方关注“锐成云分销”,云建站解决方案专家!域名、SSL证书、DNS、主机一站选齐在SSL/TLS证书的生命周期管理中,证书的各种操作方式是基础且核心的部分之一,更是保障用户数据传输加密的关键。这...

宝塔免费的 SSL/TLS 证书如何续签

申请之前,请确保域名已解析,如未解析会导致审核失败(包括根域名)宝塔SSL申请的是免费版TrustAsiaDVSSLCA-G5证书,仅支持单个域名申请有效期1年,不支持续签,到期后需要重新申...

HTTPS、HTTP、TLS/SSL工作及握手原理、PKI/CA密钥体系

一、HTTPS与HTTP介绍二、TLS/SSL工作原理三、TSL/SSL握手过程四、HTTPS性能优化五、PKI体系一、HTTPS与HTTP介绍1.Https(SecureHypetextTran...

什么是SSL证书卸载 SSL证书卸载有什么作用

SSL证书是数字证书的一种,安装部署的话可以对网站起到身份验证和数据加密的作用。网站部署SSL证书,相对就必然会有SSL证书卸载,那么SSL证书卸载是什么呢?SSL证书卸载有什么作用?随着SSL通信量...

让SSL/TLS协议流行起来:深度解读SSL/TLS实现1

一前言SSL/TLS协议是网络安全通信的重要基石,本系列将简单介绍SSL/TLS协议,主要关注SSL/TLS协议的安全性,特别是SSL规范的正确实现。本系列的文章大体分为3个部分:SSL/TLS协...

苹果、谷歌、微软等一致同意!SSL/TLS证书最长有效期锐减至47天

快科技4月14日消息,苹果此前向CA/B论坛(负责管理SSL/TLS证书的行业组织)提议,将所有证书有效期缩短至45天。日前CA/B论坛服务器证书工作组投票通过SC-081v3提案,最终决定将SSL/...

Android怎么设置端口转发,将访问本设备的端口转到另外一台设备

一、Android系统怎么设置端口转发,将访问本设备的端口转到另外一台设备?要设置端口转发,您需要先在Android设备上安装一个支持端口转发的应用程序。其中一个常用的应用是"Termux&#...

大神级产品:手机装 Linux 运行 Docker 如此简单

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:灵昱Termux作为一个强大的Android终端模拟器,能够运行多种Linux环境。然而,直接在Termux上运行Docker并不可行,需要...

关于H3C交换机的SSH功能配置方法(华三交换机ssh配置)

对于交换机的初步学习,作为初学者的我,还望诸位不吝赐教。若存在不足之处,烦请大家多提宝贵意见。同样身为初学者的我们,亦可携手共进,相互分享技术经验。一、本地用户配置(核心步骤)1.创建用户并设置密码...

Linux常用操作ssh(linux中的ssh命令)

ssh#p是小写ssh-p22user@hostsftp#连接sftp-P22root@host#将文件上传到服务器上:put[本地文件的地址][服务器上文件存储的位置]#将...

小白心得,如何使用SSH连接飞牛系统(fnos)?

一、背景作为一个刚接触飞牛系统的小白,在研究飞牛os的时候,发现很多功能都需要连接ssh,但是如何使用SSH连接飞牛系统成为入门飞牛os的一道坎。下面以自己的学习经历详细记录下过程吧。二、系统设置1、...

如何在 Windows 11 或 10 上使用 Winget 安装 OpenSSH

SSH(SecureShell)是大多数开发人员和系统管理员用来通过Linux远程连接托管服务器或任何云服务的工具,因为SSH在Linux中是内置的。然而,对于Windows呢?是的...

linux文件之ssh配置文件的含义与作用

ssh远程登录命令是操作系统(包括linux和window系统)下常用的操作命令,可以帮助用户,远程登录服务器系统,查看,操作系统相关信息。linux系统对于ssh命令有专门保存其相关配置的目录和文件...

害怕Linux SSH不安全?这几个小妙招安排上!

ssh是访问远程服务器最常用的方法之一,同时,其也是Linux服务器受到攻击的最常见的原因之一。不过别误会...我们并不是说ssh有什么安全漏洞,相反,它在设计上是一个非常安全的协议。但是安...

取消回复欢迎 发表评论: