CategoryRedis article

Redis 缓存穿透和雪崩 ( 面试高频,工作常用 )

Redis 缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解,如果对数据的一致性要求很高,那么就不能使用缓存。

另外的一些问题就是缓存穿透、缓存雪崩和缓存击穿。

image-20220723172415825

缓存穿透 ( 查不到 )

概念

缓存穿透的概念很简单,用户想要查询一个数据,发现 redis 内存数据库没有,也就是缓存中没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求持久层的数据库,这回给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

解决方案

布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以 hash 形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。

image-20220723172545915

缓存空对象

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。

image-20220723172753779

但这种方法存在两个问题:

1、如果空值能够被缓存起来,这就意味着缓存需要更多的控件存储更多的键,因为这当中可能会有很多的空值的键;

2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

缓存击穿 ( 量太大,缓存过期 )

概念

这里需要注意和缓存穿透的区别,缓存击穿,是指一个 key 非常热点,在不停的扛着大并发,大并发集中对这一点进行访问,当这个 key 在失效的期间,持续的大并发就会穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

当某个 key 在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并回写缓存,会导致数据库瞬间压力过大。

解决方案

设置热点数据永不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。

加互斥锁

分布式锁:使用分布式锁,保证对于每个 key 同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

image-20220723174037017

缓存雪崩

概念

缓存雪崩,指在某一个时间段,缓存集中过期失效。

产生雪崩的原因之一,比如在写文本的时候,马上就要到双十二点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一小时,那么到了凌晨一点的时候,这批商品的缓存就都过期了,而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

image-20220723174503153

其实集中过期,倒不是非常致命,比较致命的是缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的,无非就是对数据库产生周期性的压力而已。而缓存服务结点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案

redia 高可用

这个思想的含义是,既然 redis 有可能挂掉,那我多增设几台 redis,这样一台挂掉之后其他的还可以继续工作,其实就是指搭建的集群。

限流降级

这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。

数据预热

数据预热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生发病发访问前手动触发加载缓存不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

哨兵模式

(自动选举老大的模式)

概念

主从切换技术的方法是:当主服务器宕机后,需要手动把一台服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。

哨兵模式能够后台监控主机是否故障,如果故障了根据投票数==自动将从库转换为主库。==

哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。

测试

1、配置哨兵配置文件 sentinel.conf

# sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1

后面的数字 1,代表主机挂了,slave 投票让谁接替成为主机。

2、启动哨兵

主机宕机后,会选举新的主机,之后如果主机重新连回来,只能归并到新的主机下,当做从机。

哨兵模式

优点:

1、哨兵集群,基于主从复制模式,所有的主从配置的优点,它全有

2、主从可以切换,故障可以转移,系统的可用性就会更好

3、哨兵模式就是主从模式的升级,手动到自动,更加健壮。

缺点:

1、Redis 不好在线扩容,集群容量一旦到达上线,在线扩容就很麻烦

2、实现哨兵模式的配置其实很麻烦,里面有很多选择

哨兵模式的全部配置

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Redis 主从复制

主从复制,是指将一台 Redis 服务器的数据,复制到其他 Redis 服务器,前者称为主节点 ( Master/leader ),后者称为从节点 ( slave/follower );==数据的复制是单向的,只能由主节点到从节点。==Master 以写为主,Slave 以读为主。

==默认情况下,每台 Redis 服务器都是主节点;==且一个主节点可以有多个从节点 ( 或没有从节点 ),但一个从节点只能由一个主节点。

主从复制的作用主要包括:

1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种冗余方式。

2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务 ( 即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点 ),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。

4、高可用(集群)基石:除了上述作用意外,主从复制还是哨兵或集群能够实施的基础,因此说主从复制是 Redis 高可用的基础。

一般来说,要将 Redis 运用于工程项目中,只使用一台 Redis 服务器是万万不能的(宕机,一般都是 一主二从),原因:

1、从结构上,单个 Redis 服务器会发生单点故障,并且一台服务器需要处理所有请求负载,压力较大。

2、从容量上,单个 Redis 服务器内存容量有限,就算一台 Redis 服务器的内存容量为 256G,也不能将左右内存用作 Redis 存储内存,一般来说,单台 Redis 最大使用内存不应该超过 20G。

环境配置

127.0.0.1:6379> info replication    # 查看当前库的信息
# Replication
role:master        # 角色 master
connected_slaves:0    # 0 表示没有从机
master_replid:9bab3da8344f27278c1501d8d11384e0180ce0e7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

复制 3 个配置文件,然后修改对应的信息

1、端口

2、pid 名字(windows 不需要)

3、log 文件名字

4、dump.rdb 名字

修改完毕之后,启动 3 个 redis 服务器

image-20220722172458413

一主二从

==默认情况下,每台 Redis 服务器都是主节点;==

一般情况下,只配置从机就行,一主 ( 79 ) 二从 ( 80, 81 )

127.0.0.1:6380> slaveof 127.0.0.1 6379    # slaveof host port “找谁当自己的老大”
OK
127.0.0.1:6380> info replication
# Replication
role:slave        # 当前角色    从机
master_host:127.0.0.1    # 可以看到主机的信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:0
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:f0e106fbcdc5ebe67df3a711006c8ee663f49453
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:0

# 在主机中查看
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1    # 从机的信息
slave0:ip=127.0.0.1,port=6380,state=online,offset=28,lag=1
master_replid:f0e106fbcdc5ebe67df3a711006c8ee663f49453
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28

如果两个都配置完,就有两个从机了

image-20220722174042083

真实的主从配置应该在配置文件中配置,这样的话是永久的,我们这里使用的是命令,是暂时的。

细节

主机可以写,从机不能写只能读,主机中的所有信息和数据,都会自动被从机保存。

主机写:

image-20220722174721973

从机只能读取内容:

image-20220722174811758

测试结果:主机断开连接,从机依旧能连接到主机上,但是没有写操作,这个时候,如果主机回来了,从机依旧可以直接获取到主机写的信息。

如果是使用命令行来配置的主从,这个时候如果重启了从机,那么该从机就会变回主机。只要变为从机,立马就会从主机中获取值。

复制原理

Slave 启动成功连接到 master 后会发送一个 sync 命令

Master 接到命令,启动后台的存盘进程,同时手机所有接收到的所有用于修改数据集的命令,在后台进程执行完毕之后,==master 将传送整个数据文件到 slave,并完成一次完全同步。==

==全量复制==:而 slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。

==增量复制==:Master 继续将新的所有收集到的修改命令一次传给 Slave,完成同步。

但是只要是重新连接 master,一次完全同步 ( 全量复制 ) 将被自动进行。我们的数据一定可以在从机中看到。

层层链路

上一个主节点连接下一个从节点。

如果没有老大了,这个时候能不能选择一个老大出来?手动

如果主机断开连接我们可以使用 SLAVEOF no one 让自己变成主机,其他的节点就可以手动连接到最新的这个主节点 ( 手动 )。如果这个时候老大修复了,那就重新连接。

Redis 发布订阅

Redis 发布订阅 ( pub/sub ) 是一种消息通信模式,发送者 ( pub ) 发送消息,订阅者 ( sub ) 接收消息。

Redis 客户端可以订阅任意数量的频道。

命令

这些命令被广泛用于构建即时通信应用,比如网络聊天室 ( chatroom ) 和实时广播、实时提醒等。

1、PSUBSCRIBE pattern [pattern]

订阅一个或多个符合给定模式的频道

2、PUBSUB subcommand [argument [argument ...]]

查看订阅与发布系统状态

3、PUBLISH channel message

将信息发送到指定的频道

4、PUNSUBSCRIBE [pattern [pattern ...]]

退订所有给定模式的频道

5、SUBSCRIBE channel [channel ...]

订阅给定的一个或多个频道的信息

6、UNSUBSCRIBE [channel [channel ...]]

退订给定的频道

测试

订阅端:

127.0.0.1:6379> SUBSCRIBE azhang    # 订阅一个频道 azhang
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "azhang"
3) (integer) 1
# 等待读取推送的信息
1) "message"    # 消息
2) "azhang"        # 那个频道的消息
3) "hello,azhang"    # 消息的具体内容

1) "message"
2) "azhang"
3) "hello,redis"

发送端:

127.0.0.1:6379> PUBLISH azhang hello,azhang    # 发布者发布消息到频道
(integer) 1
127.0.0.1:6379> PUBLISH azhang hello,redis    # 发布者发布消息到频道
(integer) 1

使用场景:

1、实时消息系统

2、实时聊天 ( 频道当做聊天室,将信息回显给所有人即可 )

3、订阅,关注系统

稍微复杂的场景就是用消息中间键 MQ ( )

Redis 持久化

Redis 是内存数据库,如果不将内存中的数据库状态保存至硬盘中,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能。

RDB ( Redis DataBase)

在主从复制中,rdb 就是备用的,放在从机上面。

在指定的时间间隔内将内存中的数据集快照写入磁盘,恢复时是将快照文件读到内存里。

Redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个文件替换上次持久化好的文件。在整个过程中,主进程是不进行任何 IO 操作的。这就确保了极高的性能。如果需要大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 要比 AOF 方式更加的高效。RDB 的缺点就是最后一次持久化的数据可能丢失。我们默认的就是 RDB,一般情况下不需要修改这个配置。

==RDB 保存的文件是 dump.rdb==

触发机制

1、save 规则满足的情况下,会自动触发 rdb 规则

2、执行了 flushall 命令也会触发 rdb 规则

3、退出 redis 也会产生 rdb 文件。

备份就自动生成一个 dump.rdb

如何恢复 rdb 文件

1、只需要将 rdb 文件放在 redis 的启动目录下就可以,redis 启动的时候会自动检查 dump.rdb 恢复其中的数据

2、查看需要存在的位置

127.0.0.1:6379> config get dir
1) "dir"
2) "D:\\redis"        # 如果这个目录下存在 dump.rdb 文件,启动就会自动恢复其中的数据

优点:

1、适合大规模的数据恢复

2、对数据的完整性要求不高

缺点:

1、需要一定的时间间隔进程操作,如果 redis 意外宕机了,这个最后一次修改的数据就没有了

2、fork 进程的时候,会占用一定的内存空间

AOF ( Append Only File )

将我们的所有命令都记录下来,恢复的时候就把这个文件全部再执行一遍。

以日志的形式来记录每个写操作,将 Redis 执行过的所有指令记录下来(读操作不记录),只许追加文件但不许改写文件,redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

==AOF保存的是 appendonly.aof==

默认是不开启的,需要手动进行配置。

重启 redis 就可以生效。

如果这个 aof 文件有错,这个时候 redis 是启动不起来的,我们需要修复这个 aof 文件

redis 给我们提供了一个工具 redis-check-aof

image-20220722110704581

重写规则

aof 默认的就是文件的无线追加,文件会越来越大

image-20220722111920442

如果 aof 文件大于 64m,太大了,redis 会 fork 一个新的进程来将我们的文件进行重写。

appendonly no    # 默认是不开启 aof 模式,默认是使用 rdb 方式持久化    在大部分所有的情况下,rdb 完全够用

appendfilename "appendonly.aof"    # 持久化文件的名字

# appendfsync always    # 每次修改都会 sync。消耗性能
appendfsync everysec    # 每秒执行一次 sync同步,可能会丢失这1s的数据
# appendfsync no    # 不执行 sync,这个时候才做系统自己同步数据,速度最快,但一般不用

优点:

1、每一次修改都同步。

2、每秒同步一次,可能会丢失一秒的数据。

3、从不同步,效率最高的。

缺点:

1、相对于数据文件大小来说,aof 远远大于 rdb,修复的速度也比 rdb 慢。

2、AOF 运行效率也要比 rdb 慢,所以 redis 默认的持久化方式是 rdb。

扩展

1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储

2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾,Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大。

3、==只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化。==

4、同时开启两种持久化方式

  • 在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 保存的数据集要比 RDB 文件保存的数据集要完整。
  • RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件,那要不要只使用 AOF 呢?建议不要只使用 AOF,因为 RDB 更适合用于备份数据库 ( AOF 在不断变化不好备份 ) ,快速重启,而且不会有 AOF 可能存在的 BUG,留着作为一个万一的手段。

5、性能建议

  • 因为 RDB 文件只用作后备用途,建议只在 Slave 上持久化 RDB 文件,而且只要 15min 备份一次就够了,只保留 save 900 1这条规则。
  • 如果 Enable AOF,好处就是在最恶劣情况下也只会丢失不超过两秒的数据,启动脚本较简单只 load 自己的 AOF 文件就可以了,代价一是带来了持续的 IO,二是 AOF rewrite 的最后将 rewrite 的过程中产生的新数据写到新文件造成的阻塞是无可避免的。只要硬盘许可,应该尽量减少 AOF rewrite 的频率,AOF 重写的基础大小默认值是 64M 太小,可以设到 5G 以上,默认超过原大小 100% 大小重写可以改到适当的数值。
  • 如果不 Enable AOF,仅靠 Master-Slave Repllcation ( 主从复制 ) 实现高可用性也可以,能省掉一大笔 IO,也减少了 rewrite 时带来的系统波动。代价是如果 Master/Slave 同时倒掉 ( 断电 ),会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB 文件,载入较新的那个,微博就是这种架构。

Redis.conf 详解

启动的时候,就通过配置文件来启动;

单位

image-20220721132156605

1、配置文件 unit 单位对大小写不敏感

包含

image-20220721132424961

就比如 Spring 中的 Import、Include

网络
bind 127.0.0.1  # 绑定的 ip
protected-mode yes    # 保护模式
port 6379    # 端口设置
通用 GENERAL
daemonize yes    # 以守护进程的方式运行,默认是no 我们需要手动开启为 yes    windows 中不支持

pidfile /var/run/redis.pid    # 如果以后台的方式运行,我们就需要指定一个 pid 文件    windows 中不支持

# 日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)    生产环境
# warning (only very important / critical messages are logged)
loglevel notice
logfile ""    # 日志的文件位置名

databases 16    # 数据库的数量 默认是 16 个

always-show-logo yes    # 是否总是显示 LOGO
快照 SNAPSHOTTING

持久化:在规定时间内,执行了多少次操作,则会持久化到文件 .rdb .aof

redis 是内存数据库,如果没有持久化,那么数据断电即失。

# 如果 900s 内,如果至少有一个 key 进行了修改,我们就进行持久化操作
save 900 1
# 如果 300s 内,如果至少有 10 个 key 进行了修改,我们就进行持久化操作
save 300 10
# 如果 60s 内,如果至少有 10000 个 key 进行了修改,我们就进行持久化操作
save 60 10000
# 我们之后学习持久化,会自己定义这个测试

stop-writes-on-bgsave-error yes    # 持久化如果出错,是否还需要继续工作

rdbcompression yes    # 是否压缩 rdb 文件,需要消耗一些 cpu 资源

rdbchecksum yes    # 保存 rdb 文件的时候 是否进行错误的检查校验

dir ./    # 默认保存的目录

dbfilename dump.rdb
REPLICATION 复制
SECURITY 安全

可以在这里设置 redis 的密码,默认是没有密码的。

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> CONFIG GET requirepass    # 获取 redis 的密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass root    # 设置 redis 的密码
OK
127.0.0.1:6379> CONFIG GET requirepass
(error) NOAUTH Authentication required.
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth root    # 使用密码进行登录
OK
127.0.0.1:6379> CONFIG GET requirepass
1) "requirepass"
2) "root"
限制 CLIENTS
maxclients 10000    # 设置能连接上 reids 的最大客户端的数量

maxmemory <bytes>   # redis 配置最大的内存容量

maxmemory-policy noeviction    # 内存到达上限之后的处理策略

1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 
2、allkeys-lru : 删除lru算法的key   
3、volatile-random:随机删除即将过期key   
4、allkeys-random:随机删除   
5、volatile-ttl : 删除即将过期的   
6、noeviction : 永不过期,返回错误
APPEND ONLY MODE aof 配置
appendonly no    # 默认是不开启 aof 模式,默认是使用 rdb 方式持久化    在大部分所有的情况下,rdb 完全够用

appendfilename "appendonly.aof"    # 持久化文件的名字

# appendfsync always    # 每次修改都会 sync。消耗性能
appendfsync everysec    # 每秒执行一次 sync同步,可能会丢失这1s的数据
# appendfsync no    # 不执行 sync,这个时候才做系统自己同步数据,速度最快,但一般不用

SpringBoot 整合 Redis

说明:在 SpringBoot2.x 之后,原来使用的 Jedis 被替换为了 lettuce

jedis:采用的是直连,多个线程操作的话,是不安全的,如果要想避免不安全,则使用 jedis pool 连接池

lettuce:采用 netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量

@Bean
@ConditionalOnMissingBean(
    name = {"redisTemplate"}
)    // 我们可以自定义一个 RedisTemplate 来替换这个默认的
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    // 默认的 RedisTemplate 没有过多的设置,redis 对象都是需要序列化的
    // 两个泛型都是 object,我们后面使用需要强制转换 <String, Object>
    RedisTemplate<Object, Object> template = new RedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

@Bean
@ConditionalOnMissingBean    // 由于 String 是 Redis 中最常使用的类型,所以单独提出来一个 bean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
    return new StringRedisTemplate(redisConnectionFactory);
}
整合测试

1、导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置连接

spring.redis.host=localhost
spring.redis.port=6379

3、测试

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {

        // redisTemplate 操作不同的数据类型,api 和我们的指令是一样的
        // opsForValue  操作字符串   类似 String
        // opsForList   操作 list   类似 list
        // opsForSet
        // opsForHash
        // opsForZSet
        // opsForGeo
        // opsForHyperLogLog

        // 除了基本的操作,我们常用的方法可以直接通过 redisTemplate 来操作,如 事务 和 基本的 CRUD

        // 获取 redis 的连接对象
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.flushDb();
        connection.flushAll();

    }

}

序列化配置:

@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;

默认的序列化方式是 JDK 序列化image-20220720211540968

关于对象的保存,所有的对象都需要序列化

image-20220721110718816

自定义的 RedisTemplate

package com.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 为了开发方便,一般使用 <String, Object> 类型
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        // JSON 序列化配置
        Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        om.activateDefaultTyping()
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key 采用 String 的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash 的 key 采用 String 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value 的序列化方式采用 Jackson
        template.setValueSerializer(objectJackson2JsonRedisSerializer);
        // hash 的 value 序列化方式采用 Jackson
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }

/*    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }*/

}

所有的 Redis 操作,其实对于 java 开发人员来说,十分简单,更重要是要去理解 redis 的思想和每一种数据结构的用处和作用场景。

Jedis

Jedis 是 Redis 官方推荐的 java 连接开发工具,使用 java 操作 Redis 的中间件。

测试

1、导入对应的依赖

<!-- 导入 Jedis 的包 -->
<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.8.0</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.9</version>
    </dependency>
</dependencies>

2、编码测试

  • 连接数据库
  • 操作命令
  • 断开连接
package com;

import redis.clients.jedis.Jedis;

public class TestPing {

    public static void main(String[] args) {

        Jedis jedis = new Jedis("localhost", 6379);
        // Jedis 的所有命令就是 redis 里面的所有指令。
        System.out.println(jedis.ping());    // 输出 PONG

    }

}

常用的 API

String

List

Set

Hash

Zset

所有的 API 命令,就是之前学的 Redis 指令