Bootstrap

Redis复习

NoSQL

NoSQL(Not Only SQL)数据库是一类非关系型数据库,它与传统的关系型数据库(如 MySQL、PostgreSQL 等)相比,具有更灵活的数据模型和更好的横向扩展性。NoSQL 数据库不依赖于关系模型和 SQL 查询语言,而是根据不同的需求和场景提供多种不同的数据存储方式。

NoSQL 数据库通常用于需要处理大规模、高并发、低延迟的应用场景,比如社交媒体、电子商务、实时分析、大数据处理等。它们支持结构化、半结构化或非结构化数据,并且在设计上通常具有更好的水平扩展性,适应现代分布式系统的要求。

NoSQL 数据库的特点

  1. 灵活的数据模型: NoSQL 数据库支持多种不同的数据模型,如键值对、文档、列族、图形等,允许存储结构化、半结构化或非结构化数据。

  2. 高扩展性: NoSQL 数据库通常具有良好的水平扩展性,可以通过增加更多的节点来提高性能和存储容量,适应大规模、高并发的应用需求。

  3. 分布式架构: NoSQL 数据库通常采用分布式架构,支持数据在多个节点或服务器上分布存储,提供高可用性和容错能力。

  4. 不强制使用 SQL: NoSQL 数据库不使用传统的 SQL 查询语言,或者即使使用也没有关系数据库那样严格的 SQL 标准。这使得 NoSQL 数据库在处理某些特殊需求时具有灵活性。

  5. 高性能: 由于 NoSQL 数据库去除了很多关系数据库中的复杂性(如数据约束、事务控制等),它们通常能够提供较低的延迟和较高的吞吐量,适合高频访问的数据操作。

NoSQL 数据库的种类

根据数据存储和处理的方式,NoSQL 数据库主要分为以下几种类型:

1. 键值数据库(Key-Value Store)

键值数据库是最简单的 NoSQL 数据库类型,它通过键(key)和对应的值(value)进行数据存储,类似于一个哈希表。每条数据都有一个唯一的键,通过该键可以快速访问对应的值。

  • 代表产品:Redis、Amazon DynamoDB、Riak。
  • 应用场景:缓存、会话管理、临时数据存储等。

优点

  • 存储简单,查询速度快。
  • 支持大规模、高并发的访问。

缺点

  • 数据结构过于简单,不适合复杂查询和分析。

Redis 核心概念

Redis 缓存机制

Redis 的缓存机制:将数据保存在内存中,以加快读取速度;如果 Redis中没有所需数据,则从数据库(比如 mysql)中读取,然后将数据缓存到内存中。

扩展:

  1. 缓存击穿

待查数据 Redis 缓存中没有,导致去查数据库(mysql),称为 “缓存击穿”。

  1. 缓存雪崩

同一时间发生大量 “缓存击穿”,这种现象称为 “缓存雪崩”。

解决方案:

缓存雪崩通常是同时加载的数据设置了相同的有效期导致的(查询时都过期失效了)。所以在给批量缓存的数据设置有效期时添加一个随机数(过期时间就不再相同),大量数据就不会同时失效了。

  1. 缓存穿透

待查数据 Redis 缓存中没有,查数据库(mysql)也没有,称为 “缓存穿透”。

“缓存穿透” 会导致大量请求到访数据库,从而对数据库造成很大压力甚至数据库崩溃。

解决方案:

  1. 针对现有所有数据,生成布隆过滤器

  2. 在业务逻辑层,查询 Redis前先检查这个待查 id 是否在布隆过滤器中

  3. 如果布隆过滤器判断这个 id 不存在,直接返回空即可(拒绝请求)

  4. 如果布隆过滤器判断 id 存在,再进行后续操作

布隆过滤器:

布隆过滤器(Bloom Filter)是一种数据结构,用于快速判断一个元素是否存在于一个集合中。它通过使用多个哈希函数和位数组来实现,查询效率很高 。它可以快速判断出一个元素不在集合中,但不能准确判断一个元素在集合中(有一定概率误判)。

参考:Redis-布隆过滤器(Bloom Filter)详解-CSDN博客

Redis 数据淘汰策略

Redis 如果作为缓存数据库使用,可设置最大使用内存大小。通过配置文件 redis.conf 中的 maxmemory 这个值来开启内存淘汰功能。Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 就会施行数据淘汰策略,淘汰掉一部分数据。

  1. 如果直接运行 redis-server 命令,不带参数redis.windows.conf,则 Redis 默认以缓存数据库的模式运行,数据不会持久化。

  2. 如果运行 redis-server redis.windows.conf ,则默认开启数据持久化(RDB持久化)。如果将配置文件中的快照(SNAPSHOTTING)配置注释或删除(save <seconds> <changes>),即关闭 RDB 持久化,Redis 则以 缓存模式运行。

  3. 如果 Redis 的应用场景是作为持久化数据库使用,这个选项就不要设置,因为数据库是不能容忍丢失数据的。

Redis 提供 8 种数据淘汰策略,通过 maxmemory-policy 设置策略(默认noenviction)

  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  • volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不常用的数据淘汰

  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中选择任意数据淘汰

  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

  • allkeys-lfu:从数据集(server.db[i].dict)中挑选最不常用的数据淘汰

  • allkeys-random:从数据集(server.db[i].dict)中选择任意数据淘汰

  • noenviction:禁止驱逐数据,仅返回一个错误。

LRU:Least Recently Used, 最近最少使用

LFU :Least Frequently Used, 最不常用

volatile-lfuallkeys-lfu 是 reids4.0 之后新增的,4.0 之前只有其它 6 种。

Redis 持久化机制

Redis 持久化(内存数据保存到磁盘)有两种模式:RDBAOF。两种模式各有优劣,同时两种模式可以同时开启。

1. RDB(Redis DataBase)

RDB 持久化机制是将 Redis 在内存中的数据以 快照 的形式写入硬盘。快照文件(dbfilename)包含了Redis在某个时间点上的内存数据。快照文件的优点是备份和恢复速度快,并且不会因为数据多而影响性能。但是,如果Redis 在快照文件生成之后发生故障(比如突然断电),可能会丢失部分数据。

redis[.windows].conf 中配置了快照(类似下面 save 配置),则代表开启了 RDB 持久化,如果没有配置,则代表不开启。通过 dbfilename 可以指定 RDB 持久化文件名(数据保存到哪儿)。

save 900 1 save 300 10 save 60 10000

dbfilename dump.rdb

2. AOF(Append Only File)

AOF 持久化机制是将 Redis 的操作以 日志 的形式追加到文件中。在 Redis 启动时,会将该文件恢复回内存。AOF 持久化机制的优点是数据持久性好,即使 Redis意外停止,也能恢复所有的数据。缺点是因为每个写操作都会追加到文件中,所以文件会很大,恢复的速度较慢。

redis[.windows].conf 中 ,通过 appendonly 可以配置是否开启 AOF 持久化。默认不开启。通过 appendfilename 可以指定 AOF 持久化文件名。通过 appendfsync 可以配置 AOF 持久化 策略。

appendonly no

appendfilename "appendonly.aof"

appendfsync everysec

AOF 持久化策略

  1. no:不执行日志刷新(写磁盘),由操作系统保证数据同步到磁盘,性能最好,但是数据安全性最低

  2. always:每次都执行日志刷新,以保证数据同步到磁盘;这种方式最安全,但是性能最低

  3. everysec:每秒执行一次日志刷新,可能会导致丢失这 1s 数据,这种方式比较安全,性能也比较不错

Redis 主从同步

主从同步是指 Redis 中的主节点(master)将数据复制到从节点( slave)的过程。主从同步可以提供读写分离的能力,即主节点负责处理写请求,从节点负责处理读请求,以分担主节点的负载。

从节点中通过 replicaof 绑定主节点的 IP 和 端口号(port)即可实现主从同步。(如果主从节点在同一台主机上,记得改一下从节点的端口号。)

replicaof masterIP masterPort

Redis 集群

Redis 集群(Cluster)即多个 Redis节点组合在一起对外提供服务。Redis要求集群不能少于 3 个主节点,也就是 6 个节点(每个主节点需有一个从节点)。

Redis 集群通过将数据分片存储在多个节点上来实现数据的分布式存储故障转移。每个节点可以负责存储整个数据集的一部分。当集群中的某个节点故障时,其他节点可以接管故障节点的负载,并在需要的情况下重新分片数据以保持均衡。

集群中的每个节点,通过将 cluster-enabled 配置为 yes即可启用 Redis集群,然后通过执行redis-cli --cluster create 集群节点 --cluster-replicas N 命令,便可创建集群。

cluster-enabled yes

redis-cli --cluster create IP1:port1 IP2:port2 IP3:port3 IP4:port4 IP5:port5 IP6:port6 --cluster-replicas 1

Redis

Redis(Remote Dictionary Server 远程字典服务器),是一个开源的高性能的 NoSQL 类型的 key-value 内存数据库系统。Redis以键值对的形式存储数据,并提供多种数据结构的支持,如字符串、哈希、列表、集合、有序集合等。 默认端口号:6379.

由于 Redis 的数据存储在内存中,所以读写速度非常快,常被用于缓存方向(缓存的数据不会持久化,Redis服务关闭,数据即全部丢失)。同时也支持数据持久化提供 RDB 和 AOF 两种持久化策略),可以将内存中的数据保存在磁盘中,重启 Redis 服务的时候可以再次加载进行使用。

官网:Redis - The Real-time Data Platform

Redis for windows 下载:Releases · tporadowski/redis · GitHub

菜鸟教程:Redis 教程 | 菜鸟教程

Redis-insight GUI:Redis Insight

安装和启动(for windows)

安装

  • 安装版:一路下一步安装即可。

  • 解压缩版:解压即可

    安装版 和 解压缩版 区别:安装版会安装 redis 服务,启动服务器多了一种选择。

启动 Redis 服务

  • 方式 1

    • 通过 CMD命令行,进入 Redis 根目录,运行 redis-server redis.windows.conf 命令,即可启动 Redis 服务器。 redis.windows.conf 可以省略,此时采用的是 Redis 的默认配置。命令行窗口关闭,服务即关闭。

    • 另外新开一个 CMD命令行,进入 Redis 根目录,运行 redis-cli 命令,可打开 Redis客户端。

  • 方式 2

    • 点击运行 Redis 根目录下的 redis-server.exe 程序即可。

    • 点击运行 Redis 根目录下的 redis-cli.exe,可打开 Redis客户端。

  • 方式 3

    对于安装版,可以直接通过 任务管理器 -> 服务 ,找到 Redis 服务启动即可。客户端的启动同上。

Redis 常用命令

命令说明
select db_Index切换数据库。Redis 默认有 16 个库(编号: 0~15)。
set key value设置数据类型为string 的 key-value
get key获取通过set 设置的 key 对应的值。key 不存在返回nil。
randomkey从当前数据库中随机返回一个 key。
keys pattern查找所有符合模式 pattern(正则表达式)的 key。
scan cursor [match pattern] [count count]迭代数据库中的键。其中 cursor 表示游标,0 表示开始第一次新的迭代;[match pattern] 表示匹配的正则;[count count] 表示指定从数据库中返回key的数量,默认值为 10。示例:scan 0 match *a*count 5:表示返回最多5个key中含有a的key。
type key返回 key 所储存的值的类型。none (key 不存在)、string (字符串)、list (列表)、set (集合)、zset (有序集)、hash (哈希表)。
rename key newkey修改 key 的名称。将 key 重命名为 newkey,如果 key 与 newkey 相同,返回一个错误。如果 newkey 已经存在,值将被覆盖。
renamenx key newkey仅当 newkey 不存在时,将 key 改名为 newkey 。返回 1 表示修改成功; 0 表示 newkey 已经存在。
del key [key....]删除指定的一个或多个 key。删除成功返回被删除的 key 的数量。
exists key [key...]判断指定的一个或多个 key 是否存在。若存在,则返回存在 key 的数量。
dump key序列化给定 key的值,并返回被序列化的值。key 不存在返回 nil。
expire key seconds设置 key 的失效时间(秒)。返回 1表示设置成功;返回 0 表示key 不存在或不能设置过期时间。
pexpire key millisecondsexpire key seconds,区别是时间单位为:毫秒
ttl key以秒为单位返回 key 的剩余过期时间。返回值为 -2 表示 key 不存在或已过期;-1 表示 key 存在并且没有设置过期时间(永久有效)。
pttl keyttl key,区别是时间单位为:毫秒
persist key用于移除给定 key 的过期时间,使得 key 永不过期。返回 1 表示生存时间移除成功;0 表示 key 不存在或 key 没有设置生存时间。
touch key [key ...]修改指定的一个或多个 key 的最后访问时间。如果返回为 0,表示 key 不存在,不做操作;否则返回操作的 key 的数量。
move key db_index用于将当前数据库的 key 移动到给定的数据库 db 当中。redis 默认使用数据库 0。当两个库中的 key 相同时,不能移动。

Redis 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

  • string 是 redis 最基本的数据类型,一个 key 对应一个 value。

  • hash 是一个键值对集合, 非常适合用于存储对象,类似 Java 中的 HashMap。 每个 hash 可以存储 2^32 -1 键值对(40多亿)。

  • list 是 string 类型的有序(插入顺序)可重复集合, 基于 Linked List 实现,类似 Java 中的 LinkedList 。可以添加一个元素到列表的头部(左边)或者尾部(右边)。 每个 list可以存储 2^32 -1 键值对(40多亿)。

  • set 是 string 类型的无序不可重复集合,类似Java中的 Hashset,通过哈希表实现,故增、删、查的时间负责度都为 O(1)。

  • zset 和 set 一样也是 string 类型元素的不可重复集合。 不同的是 zset 每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序,分数(score)可以重复。 添加元素到集合,元素在集合中存在则更新对应score

Redis 配置文件

redis[.windows].conf

# --------------------------- INCLUDES 引入外部文件 ---------------------------
# 包含其它配置文件,可以在同一主机上多个 Redis 实例之间使用同一份配置文件(通用配置),而同时各个实例又拥有自己的特定配置文件。
# 将引入的配置文件写在 redis.conf 文件的开头,那么后面的配置会覆盖引入文件的配置。如果引入的配置文件写在 redis.conf 文件的末尾,那么引入配置会覆盖 redis.conf 文件的配置。
include c:\path\to\other.conf
​
# --------------------------- MODULES(加载扩展功能模块)---------------------------
# 加载功能扩展模块(Redis 4.0 新增) 
loadmodule c:\path\to\other_module.dll
​
# --------------------------- NETWORK(网络配置) ---------------------------
# bind 绑定 Redis 服务器IP,默认为本机 127.0.0.1,只接收本机的请求。如果 bind 没有配置,则会接受所有连接请求(不设防,危险)。
bind 192.168.1.100 10.0.0.1
​
# 保护模式:
# 1. 当开启保护模式(yes)时,如果没有配置 bind 和密码,则服务器只接收本机的请求。
# 2. 当关闭保护模式(no)时,服务器接受所有外部主机的连接(危险)。
protected-mode yes      
# Redis 运行端口,默认 6379。因为 Redis 是单线程,所以如果开启多个 Redis 进程需要修改端口。
port 6379
# TCP 连接积压数。在每秒高访问量的环境下,提高 backlog 值,可以避免客户端连接慢的问题。
tcp-backlog 511
# 当一个客户端连接空闲 N 秒后,关闭该连接。 (0:不关闭)
timeout 0
# 每隔 N 秒,对客户端连接进行一次“心跳”检测,检测连接是否仍然活动。
tcp-keepalive 300
​
# --------------------------- GENERAL(常规参数) ---------------------------
# 指定 Redis 是否以守护进程的方式运行(windows 下不支持设置 daemonize)
daemonize no
# 如果从 upstart 或 systemd 运行 Redis,Redis可以与您的监控树进行交互。(windows 下不支持)
supervised no
# 当 redis 作为守护进程运行时,它会把 pid 默认写到 /var/run/redis.pid 文件里(windows 下不支持)
pidfile /var/run/redis.pid
​
# 日志级别:
# debug:记录大量日志信息,适用于开发、测试阶段
# verbose:记录许多很少有用的信息,比 debug 级别记录的日志少
# notice:适量日志信息,使用于生产环境
# warning:只记录一些非常重要关键的信息
loglevel notice
# 配置 log 文件地址,默认在命令行终端输出。
logfile ""
# 是否启用系统记录器的日志记录,并可选择更新其他 syslog 参数以满足你的需求。
syslog-enabled no
# 指定系统日志标识
syslog-ident redis
# 指定系统日志工具。必须是 USER 或介于 LOCAL0-LOCAL7 之间。local0在windows下不支持。 
syslog-facility local0
​
# 设置数据库的数目。默认的数据库是 DB 0,默认有 16 个数据库(0~15)。
databases 16
​
# --------------------------- SNAPSHOTTING(快照:RDB 持久化)---------------------------
# redis 持久化有 RDB 和 AOF 两种方式,快照方式即 RDB 方式。
# save <seconds> <changes>:seconds 秒后,如果数据发生了changes 次改变,则将数据保存到磁盘。
# 如果不设置,则表示禁用保存。
save 900 1
save 300 10
save 60 10000
# 当启用了 redis 的快照功能(设置了 save <seconds> <changes>),那么当最后一次后台保存数据失败,Redis 将停止接收数据(yes).这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难发生了。
stop-writes-on-bgsave-error yes
# 对于存储到磁盘中的快照,设置是否进行压缩存储。如果是的话,redis 会采用 LZF 算法进行压缩。如果不想消耗 CPU 来进行压缩的话,可以关闭此功能,但是存储在磁盘上的快照会比较大。
rdbcompression yes
# 存储快照后,可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10% 的性能消耗。
rdbchecksum yes
# redis db 文件名
dbfilename dump.rdb
# redis 快照文件的存放路径
dir ./
​
# --------------------------- SECURITY(安全) ---------------------------
#设置连接 Redis 服务器的密码。
requirepass 123456
​
# 命令重命名,对于一些危险命令,避免被外部使用,可以进行重命名(内部使用不受影响),比如下面的 config 命令。
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
# 禁用命令
rename-command CONFIG ""
​
# --------------------------- CLIENTS(客户端) ---------------------------
# 设置客户端最大并发连接数,默认值为 10000
maxclients 10000
​
# --------------------------- MEMORY MANAGEMENT(内存管理) ---------------------------
# 设置 Redis db 的最大内存
maxmemory <bytes>
# 当内存使用达到 maxmemory 设置的最大值时,Redis 使用的内存数据淘汰策略
maxmemory-policy noeviction
​
# --------------------------- APPEND ONLY MODE(AOF 相关参数) ---------------------------
# 默认 Redis 使用的是 RDB 方式持久化,这种方式在许多应用中已经足够用了。但是 Redis 如果中途宕机,会导致可能有几分钟的数据丢失(根据 save 策略进行持久化),Append Only File(日志记录) 是另一种持久化方式, 可以提供更好的持久化特性。Redis 会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时 Redis 都会先把这个文件的数据读入内存里(先忽略 RDB 文件)
appendonly no
# AOF 文件名(the name of append only file )
appendfilename "appendonly.aof"
​
# AOF 持久化策略:
# 1. no:不执行 fsync(日志刷新),由操作系统保证数据同步到磁盘,性能最好,但是数据安全性最低
# 2. always:每次都执行 fsync,以保证数据同步到磁盘;这种方式最安全,但是性能最低
# 3. everysec:每秒执行一次 fsync,可能会导致丢失这 1s 数据,这种方式比较安全,性能也比较不错
appendfsync everysec
​
# 在 AOF 重写或者写入 RDB 文件的时候,会执行大量 IO,此时对于 everysec 和 always 的 AOF 模式来说,执行 fsync 会造成阻塞过长时间,no-appendfsync-on-rewrite 默认为 no。如果对延迟要求很高的应用,这个字段可以设置为 yes。 设置为 yes 表示 rewrite 期间对新写操作不执行 fsync,暂时存在内存中等 rewrite 完成后再写入,默认为 no,建议 yes
no-appendfsync-on-rewrite no
# AOF 自动重写配置,当目前 AOF 文件大小超过上一次重写的 AOF 文件大小的百分之多少进行重写,即当 AOF 文件增长到一定大小的时候,Redis 能够调用 bgrewriteaof 对日志文件进行重写。当前 AOF 文件大小是上次日志重写得到 AOF 文件大小的二倍(设置为 100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
# 设置允许重写的最小 AOF 文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写。
auto-aof-rewrite-min-size 64mb
# AOF 文件可能在尾部是不完整的,当 Redis 启动的时候,AOF 文件的数据被载入内存。重启可能发生在 Redis 所在的主机操作系统宕机后,尤其在 ext4 文件系统没有加上 data=ordered 选项,出现这种现象 Redis 宕机或者异常终止不会造成尾部不完整现象,可以选择让 Redis 退出,或者导入尽可能多的数据。如果选择的是 yes,当截断的 AOF 文件被导入的时候,会自动发布一个 log 给客户端然后 load。如果是 no,用户必须手动redis-check-aof 修复 AOF 文件才可以,默认值为 yes。
aof-load-truncated yes
# 当重写 AOF 文件时,Redis 能够在 AOF 文件中使用 RDB 序言,以更快地重写和恢复。当打开这个选项时,重写的 AOF 文件将由两个不同的节组成。当加载时,Redis 识别到 AOF 文件以 “Redis” 字符串开始,并加载带前缀的 RDB 文件,然后继续加载 AOF 尾部。
aof-use-rdb-preamble yes
​
​

Jedis、Lettuce 和 Redisson

Redis 官方推荐的 Java 客户端有 JedisLettuceRedisson

Jedis 官网:GitHub - redis/jedis: Redis Java client

Lettuce 官网:GitHub - redis/lettuce: Advanced Java Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.

Redisson 官网:Redisson: Valkey & Redis Java client. Complete Real-Time Data PlatformGitHub - redisson/redisson: Redisson - Valkey and Redis Java client. Complete Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache..

参考资料:Redis Client 之 Jedis与Lettuce_redis jedis lettuce-CSDN博客

Jedis

优点:老牌的 Redis 客户端,使用很稳定。支持全面的 Redis 操作特性(API 全面,操作简单)。

缺点:使用阻塞的 IO,方法调用都是同步的,不支持异步。Jedis 客户端实例不是线程安全的,所以需要通过连接池来使用 Jedis

  • jedis 依赖

 <!-- jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

Jedis 无连接池

import redis.clients.jedis.Jedis;
 
@Test void testJedis(){
    Jedis client = new Jedis("127.0.0.1",6379);         // 连接本地的 Redis 服务
    System.out.println("服务正在运行: "+client.ping());   // 查看服务是否运行
    client.select(0);                   // 选择 0 号数据库,默认即是 0 号数据库
    
    client.set("test", "Jedis");    // 存储 key-value
    String val = client.get("test");    // 获取 value
    client.close();                     // 释放连接
}

Jedis 连接池

@Test void testJedisPool(){
    /* 连接池配置项 多有默认值
    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(6);
    config.setMaxIdle(6);
    config.setMinIdle(2);
    JedisPool pool = new JedisPool(config,"127.0.0.1",6379);
    */
    JedisPool pool = new JedisPool("127.0.0.1",6379);
    Jedis client = pool.getResource();  // 从 pool 获取连接
    client.set("test2","Jedis pool");
    String val = client.get("test2");
    System.out.println(val);
    client.close();                     // 连接返回 pool
}

Lettuce

Lettuce 是一种可扩展的线程安全的 Redis 客户端,支持异步模式。Lettuce 底层基于 Netty,支持高级的 Redis 特性,比如哨兵,集群,管道,自动重新连接和 Redis 数据模型。

优点:支持同步、异步通信模式;Lettuce 的 API 是线程安全的,如果不是执行阻塞和事务操作,多个线程就可以共享一个连接;

  • 依赖

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
</dependency>

同步模式

@Test void testLettuceSync(){
    RedisClient client= RedisClient.create("redis://localhost:6379");// 创建RedisClient对象
    StatefulRedisConnection<String, String> conn = client.connect(); // 创建连接
    // 创建同步命令对象
    RedisCommands<String, String> cmd = conn.sync();    
    cmd.set("test3", "Lettuce");
    String value = cmd.get("test3");
    // 释放资源
    conn.close();
    client.shutdown();
}

异步模式

@Test void testLettuceAsync(){
    RedisClient client= RedisClient.create("redis://localhost:6379");// 创建RedisClient对象
    StatefulRedisConnection<String, String> conn = client.connect(); // 创建连接
    // 创建异步命令对象
    RedisAsyncCommands<String, String> cmd = conn.async();
    cmd.set("test4", "Lettuce async");
    RedisFuture<String> get = cmd.get("test4");
    String val = get.get();
    // 释放资源
    conn.close();
    client.shutdown();
}

Redisson

Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。

优点:使用者对 Redis 的关注分离,让开发者有更多的时间来关注业务逻辑;提供很多分布式相关操作服务,如分布式锁,分布式集合,可通过Redis支持延迟队列等;支持异步和响应式编程模型。

缺点:Redisson 对字符串的操作支持比较差。

  • 依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.23.2</version>
</dependency>
  • 示例

@Test void testRedisson(){
    // 创建配置对象
    Config config = new Config();
    config.useSingleServer().setAddress("redis://localhost:6379");
​
    // 创建RedissonClient对象
    RedissonClient client = Redisson.create(config);
​
    // 执行Redis命令
    client.getBucket("test5").set("Redisson");
    Object val = client.getBucket("test5").get();
    System.out.println(val);
    // 关闭连接
    client.shutdown();
}

SpringBoot 整合 Redis

添加依赖

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

编写配置文件

application.properties 配置 redis (配置可省略,有默认值)

# redis
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
#......

编写 java 代码

@SpringBootTest
public class RedisTest {
    @Resource
    RedisTemplate redisTemplate;                // 操作对象
    @Resource
    StringRedisTemplate stringRedisTemplate;    // 操作字符串
​
    @Test
    void test(){
        //操作 string
        ValueOperations ops = redisTemplate.opsForValue();
        //操作 hash
        HashOperations oph = redisTemplate.opsForHash();
        //操作 list
        ListOperations opl = redisTemplate.opsForList();
        //操作 set
        SetOperations opz = redisTemplate.opsForSet();
        //操作 zset
        ZSetOperations opzs = redisTemplate.opsForZSet();
​
        // 增
        ops.set("a1","hello,redis");
        ops.set("a2","hello,redis",5, TimeUnit.HOURS);
​
        // 查
        Object s = ops.get("a1");
        System.out.println(s);
    }
​
    @Test
    void test2(){
        HashOperations oph = redisTemplate.opsForHash();
​
        User u1 = new User("001","libai","123456");
        User u2 = new User("002","dufu","666666");
        User u3 = new User("003","wangbo","999999");
​
        // 增
        oph.put("users",u1.getId(),u1);
        oph.put("users",u2.getId(),u2);
        oph.put("users",u3.getId(),u3);
​
        // 查
        Set ids = oph.keys("users");                // 拿到所有 键值对 的键 
        System.out.println(ids);
​
        List users = oph.values("users");           // 拿到所有的 键值对
        System.out.println(users);
​
        User user = (User) oph.get("users","001");  // 获取 键为001的 键值对
        System.out.println(user);
​
        // 改
        user.setPwd("000000");
        oph.put("users","001",user);
​
        // 删
        oph.delete("users","002");
        System.out.println(oph.values("users"));
    }
}

Redis 主从同步

主从同步是指 Redis 中的主节点(master redis)将数据复制到从节点( slave redis)的过程。主节点负责处理写请求,并将写操作的日志传播给从节点,从节点则负责接收并应用这些写操作,以保持与主节点的数据一致性。 ​ 主从同步可以提供读写分离的能力,即主节点负责处理写请求,从节点负责处理读请求,以分担主节点的负载。从节点可以提供高性能的读取操作,并且在主节点故障时能够提供一定程度的故障容忍性。

主从配置

通过主从节点的配置文件 redis[.windows].conf ,我们可以进行主从节点数据同步的配置,实现读写分离。

  • 主节点(master)

bind 127.0.0.1
port 6379

redis.conf 默认即是如上配置,故主节点如果采用默认配置,则不用修改。

  • 从节点1(slave1)

# 由于主从节点目前在同一台主机上,故需修改端口号以示区分;如果主从节点在不同主机上,则无需修改。
port 6380       
# 表示当前从节点的数据从 127.0.0.1 6379 这个服务器拷贝
replicaof 127.0.0.1 6379    
  • 从节点2(slave2)

port 6381
replicaof 127.0.0.1 6379

主从启动

主从节点配置好后,通过redis-server redis.windows.conf 命令,先启动主节点,再启动从节点。主节点可读可写,从节点默认只读(replica-read-only yes)。

  • 测试主从配置

    E:\Redis\redis-master-replica\Redis-master>redis-cli
    127.0.0.1:6379> set a hello     //master 写数据
    OK
    127.0.0.1:6379> get a           //master 读数据
    "hello"
    127.0.0.1:6379> exit
    ​
    E:\Redis\redis-master-replica\Redis-master>redis-cli -h 127.0.0.1 -p 6380
    127.0.0.1:6380> get a           //slave(6380) 读数据
    "hello"
    127.0.0.1:6380> set b hi        //slave 写数据失败(从节点默认只读)
    (error) READONLY You can't write against a read only replica.
    127.0.0.1:6380> exit
    ​
    E:\Redis\redis-master-replica\Redis-master>redis-cli -h 127.0.0.1 -p 6381
    127.0.0.1:6381> get a           //slave(6381) 读数据
    "hello"

Java中实现读写分离

  • 依赖

 <!-- jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

方式 1:无连接池 Jedis

// 主:set(写数据)
try(Jedis master = new Jedis("127.0.0.1",6379)){
    master.set("test1","hello,redis");
}
​
// 从:get(读数据)
try(Jedis slave = new Jedis("127.0.0.1",6380)){
    String val = slave.get("test2");
    System.out.println(val);//输出:hello,redis
}

方式 2:使用连接池 JedisPool

// 主节点连接池
JedisPool masterPool = new JedisPool("127.0.0.1",6379);
// 从节点连接池
JedisPool slavePool = new JedisPool("127.0.0.1",6380);
​
// 主:set(写数据)
try(Jedis master = masterPool.getResource()){
    master.set("test2","Hi,Redis");
}
​
// 从:get(读数据)
try(Jedis slave = slavePool.getResource()){
    String val = slave.get("test2");
    System.out.println(val);//输出:Hi,Redis
}

Redis 集群(Cluster)

Redis 集群(Cluster)是通过将数据分片存储在多个节点上来实现数据的分布式存储和高可用性。每个节点可以负责存储整个数据集的一部分。同时,Redis 集群还使用内部协议进行节点间的通信和数据迁移。

Redis 集群提供了数据自动分片(分布式存储)和故障转移的能力。当集群中的某个节点故障时,其他节点可以接管故障节点的负载,并在需要的情况下重新分片数据以保持均衡。

Redis 集群要求,至少需要 3 个 master 主节点,每个 master 节点至少需分配一个 slave 从节点。所以一个Redis 集群,至少需要 6 个节点。

集群配置

依次修改集群中的每个节点的配置文件(redis[.windows].conf),主要配置以下前 2 项即可(后两项有默认配置):

# 端口(由于目前集群节点全部在本机,故为了区分节点,需为各节点配置不同的端口号;此处6个节点,端口依次配置为:6379、6380、6381、6382、6383、6384)
port 6379
# 开启集群(默认没有开启)
cluster-enabled yes
​
# 节点的超时时间,单位:ms,超时后集群会认为该节点失败。如不配置,默认即为 15 秒。
cluster-node-timeout 15000
# 集群节点 nodes 信息配置文件,是自动生成的,默认文件名:nodes.conf,如果无需修改文件名,则不需要配置。
cluster-config-file nodes.conf

集群创建

启动

通过redis-server redis.windows.conf 命令,依次启动集群中的所有节点即可。

提示: 节点如果之前使用过,那么启动前,检查一下各节点是否有 .rdb 和 .aof 文件以及 nodes.conf 文件,如果有则删除。

创建

创建命令:redis-cli --cluster create 集群节点 --cluster-replicas NN表示为每个 master 节点分配 N 个 slave 节点。

  • 示例1:错误

# 下面节点数只有 4 个,不符合 redis 集群最少节点数(6个)的要求,故创建失败。
E:\Redis\redis-cluster\Redis-1>redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 --cluster-replicas 1
*** ERROR: Invalid configuration for cluster creation.
*** Redis Cluster requires at least 3 master nodes.
*** This is not possible with 4 nodes and 1 replicas per node.
*** At least 6 nodes are required.
  • 示例2:正确

E:\Redis\redis-cluster\Redis-1>redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6383 to 127.0.0.1:6379
Adding replica 127.0.0.1:6384 to 127.0.0.1:6380     
Adding replica 127.0.0.1:6382 to 127.0.0.1:6381     
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 1006251df397d825388d178a71d047176759ed5a 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
M: b9cab7991b2181ff1bd7138249b438f32c234261 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
M: 36d05c5259d92d58caddf13c07494b69f9b5cdbc 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
S: 74a69f3ef6478434bf9a19a4751e9c1b961dcf60 127.0.0.1:6382  //主:6379 -> 从:6382
   replicates 1006251df397d825388d178a71d047176759ed5a      
S: f349aaac855b8f38d999dbb66ed324e71031cbf9 127.0.0.1:6383  //主:6380 -> 从:6383
   replicates b9cab7991b2181ff1bd7138249b438f32c234261      
S: e48a4357f22626a754a690bc47adc123e22aee83 127.0.0.1:6384  //主:6381 -> 从:6384
   replicates 36d05c5259d92d58caddf13c07494b69f9b5cdbc      
Can I set the above configuration? (type 'yes' to accept): yes  // 输入 yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 1006251df397d825388d178a71d047176759ed5a 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: e48a4357f22626a754a690bc47adc123e22aee83 127.0.0.1:6384
   slots: (0 slots) slave
   replicates 36d05c5259d92d58caddf13c07494b69f9b5cdbc
M: b9cab7991b2181ff1bd7138249b438f32c234261 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: f349aaac855b8f38d999dbb66ed324e71031cbf9 127.0.0.1:6383
   slots: (0 slots) slave
   replicates b9cab7991b2181ff1bd7138249b438f32c234261
M: 36d05c5259d92d58caddf13c07494b69f9b5cdbc 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 74a69f3ef6478434bf9a19a4751e9c1b961dcf60 127.0.0.1:6382
   slots: (0 slots) slave
   replicates 1006251df397d825388d178a71d047176759ed5a
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

查看集群状态

  • 命令:redis-cli [-h 节点IP -p 节点端口号] cluster info

E:\Redis\redis-cluster\Redis-1>redis-cli cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:61
cluster_stats_messages_pong_sent:66
cluster_stats_messages_sent:127
cluster_stats_messages_ping_received:61
cluster_stats_messages_pong_received:61
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:127

查看集群节点信息

  • 命令:redis-cli [-h 节点IP -p 节点端口号] cluster nodes

E:\Redis\redis-cluster\Redis-1>redis-cli cluster nodes
e48a4357f22626a754a690bc47adc123e22aee83 127.0.0.1:6384@16384 slave 36d05c5259d92d58caddf13c07494b69f9b5cdbc 0 1691761739000 6 connected
b9cab7991b2181ff1bd7138249b438f32c234261 127.0.0.1:6380@16380 master - 0 1691761738407 2 connected 5461-10922
f349aaac855b8f38d999dbb66ed324e71031cbf9 127.0.0.1:6383@16383 slave b9cab7991b2181ff1bd7138249b438f32c234261 0 1691761739500 5 connected
36d05c5259d92d58caddf13c07494b69f9b5cdbc 127.0.0.1:6381@16381 master - 0 1691761738000 3 connected 10923-16383
74a69f3ef6478434bf9a19a4751e9c1b961dcf60 127.0.0.1:6382@16382 slave 1006251df397d825388d178a71d047176759ed5a 0 1691761740592 4 connected
1006251df397d825388d178a71d047176759ed5a 127.0.0.1:6379@16379 myself,master - 0 1691761737000 1 connected 0-5460

测试

E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set a hello
(error) MOVED 15495 127.0.0.1:6381// key为a的数据应插入15495哈希槽,该编号槽属于 端口为6381的节点
127.0.0.1:6379> exit
​
E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6381
127.0.0.1:6381> set a hello     // 将 a = hello 写入 6381 节点
OK
127.0.0.1:6381> keys *          // 查看:有
1) "a"
127.0.0.1:6381> exit
​
E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6382
127.0.0.1:6382> keys *          // 查看:无
(empty list or set)
127.0.0.1:6382> exit
​
E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6383
127.0.0.1:6383> keys *          // 查看:无
(empty list or set)
127.0.0.1:6383> exit
​
E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6384
127.0.0.1:6384> keys *          // 查看:有。6384 为 6381 的从节点。
1) "a"
127.0.0.1:6384> exit
​
E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> keys *          // 查看:无
(empty list or set)
127.0.0.1:6379> exit
​
E:\Redis\redis-cluster\Redis-1>redis-cli -h 127.0.0.1 -p 6380
127.0.0.1:6380> keys *          // 查看:无
(empty list or set)
127.0.0.1:6380>

Java 实现集群读写

  • 依赖

 <!-- jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>
  • 实现

Set<HostAndPort> set = new HashSet<>();
set.add(new HostAndPort("127.0.0.1",6379));
set.add(new HostAndPort("127.0.0.1",6380));
set.add(new HostAndPort("127.0.0.1",6381));
set.add(new HostAndPort("127.0.0.1",6382));
set.add(new HostAndPort("127.0.0.1",6383));
set.add(new HostAndPort("127.0.0.1",6384));
​
//创建 redis 集群实例
JedisCluster jedisCluster = new JedisCluster(set);
​
//set(写数据)
jedisCluster.set("abc","hello,cluster");
//get(读数据)
String val = jedisCluster.get("abc");
System.out.println(val);//输出:hello,cluster
​
// 关闭集群
jedisCluster.close();
;