Redis——基础篇
本文最后更新于:2 个月前
引言
Redis,作为一种高性能的键值数据库,自2009年诞生以来,因其出色的性能和丰富的数据结构支持,已成为现代数据处理中不可或缺的工具。它的设计为处理高速数据操作和实时应用提供了理想的解决方案。本文将介绍Redis的主要特性、安装过程、基本命令、以及如何通过Java客户端与之交互,帮助读者快速掌握Redis的使用和应用。
简介
Redis
诞生于2009年,全称为Remote Dictionary Server
,即远程词典服务器,是一个开源的高性能键值数据库。因其支持字符串、哈希、列表、集合、有序集合、位图、地理空间索引等多种类型的数据结构,又被称为数据结构服务器。
特征
- 高性能: Redis将其数据主要存储在内存中,同时大部分操作都是单线程的、避免了不必要的上下文切换和竞态条件,加上搞笑的数据结构和算法,使得它提供极高的性能,能够支持每秒高达数十万次的读写操作。
- 持久化: 尽管是基于内存的,Redis也提供了灵活的持久化选项,如
RDB
(Redis 数据库文件)快照和AOF(Append Only File)
日志,确保数据安全。 - 数据结构丰富: Redis支持多种数据结构,使得它在应对不同场景时非常灵活,比如缓存、消息队列系统、应用排行榜、计数器等。
- 发布/订阅消息系统: Redis支持发布/订阅模式,能够构建实时的消息系统。
- 原子操作: Redis中的大多数操作都是原子性的,这意味着在并发环境中执行时,Redis可以保证这些命令的安全执行。
- 事务支持: Redis支持事务功能,允许通过一系列命令的批量执行来进行复合操作。
- 高可用和分布式: 通过
Redis Sentinel
提供高可用性,并且通过Redis Cluster
提供自动分区的支持。 - 多语言客户端:广泛的客户端支持,几乎所有的编程语言都有成熟的Redis客户端库,使得Redis可以轻松集成到各种应用程序和开发环境中。
应用场景
- 缓存系统: 利用Redis的高速数据访问能力,常用作应用的缓存层,减轻后端数据库的压力,提高数据访问速度。
- 会话缓存(Session Cache): 在Web应用中广泛用于存储用户会话。
- 消息队列系统: 利用Redis的发布/订阅功能以及列表操作,实现消息队列的功能,支持高并发消息传递。
- 实时计数系统: 如网站访问计数、在线用户统计等,Redis的原子操作非常适合。
- 排行榜或计数器: 利用Redis的有序集合,可以非常方便地实现各类排行榜功能。
源码安装
安装编译Redis所需的基础工具和开发库
1
sudo yum install -y gcc make
下载源码压缩包
1
wget http://download.redis.io/releases/redis-6.2.6.tar.gz
解压源码压缩包
1
tar -zxvf redis-6.2.6.tar.gz
编译及测试
1
2
3make
make test
make install创建配置文件
1
2sudo mkdir /etc/redis
sudo cp redis.conf /etc/redis/redis.conf可选:编辑配置文件,设置外网访问及后台运行
1
2
3
4
5bind 0.0.0.0
requirepass [redis连接密码]
daemonize yes可选:创建systemd服务文件,确保redis在系统重启后自动启动
1
sudo vim /etc/systemd/system/redis.service
粘贴以下内容
1
2
3
4
5
6
7
8
9
10
11
12
13[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
User=root
Group=root
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always
[Install]
WantedBy=multi-user.target保存并退出,重新加载systemd管理器配置并启动redis服务
1
2
3sudo systemctl daemon-reload
sudo systemctl enable redis
sudo systemctl start redis需要注意:如果使用systemd来管理Redis服务,应该让systemd完全控制服务的启动、停止和重启,此时redis配置文件中的daemonize应设置为no。如果Redis配置为守护进程模式(即
daemonize yes
),这会与systemd的操作发生冲突。因为Redis尝试自己分离出一个守护进程,systemd可能会认为主进程已经退出,这导致systemd认为服务已经“停止”,然后根据配置尝试重新启动它,从而形成一个不断的重启循环。验证
1
2
3sudo systemctl status redis
redis-cli
系统级命令
redis-server
命令说明:用于启动Redis服务器实例的命令行工具,通过多种方式配置和启动Redis服务。
常用选项:
/path/redis.config
:通过指定的配置文件启动。--port <port>
:设置Redis侦听的端口号。--bind <address>
:绑定到一个或多个接口,如果需要多个地址,可以重复这个选项。--conf <file>
:指定配置文件的路径。--loglevel <level>
:设置日志级别(debug、verbose、notice、warning)。--logfile <filename>
:指定日志文件路径。如果设置为stdout
,日志将输出到标准输出。--daemonize yes
:指示Redis以守护进程模式运行。--pidfile <file>
:设置PID文件的路径,这在以守护进程形式运行时非常有用。--protected-mode [yes|no]
:设置保护模式,当Redis没有设置密码,并且绑定到公共接口时,拒绝客户端的连接。--dir <directory>
:指定持久化文件存储的目录。--dbfilename <filename>
:指定持久化文件的文件名。--appendonly [yes|no]
:是否开启AOF (Append Only File)
持久化模式。
redis-cli
命令说明:Redis的命令行接口客户端,它用于与Redis服务器进行交互。通过redis-cli
可以执行各种 Redis 命令,查看服务器状态,进行调试,以及管理数据。
常用选项:
-h
:指定服务器的主机名。-p
:指定服务器的端口。-a
:指定连接密码。--raw
:以原始格式输出(例如,不转义字符串中的特殊字符)。-c
:启用集群模式,允许redis-cli
在Redis集群节点间自动重定向。--scan
:以迭代方式列出所有的键,可以与-p
和--pattern
参数一起使用。--pipe
:使用管道模式,可以通过标准输入批量执行命令。--stat
:提供Redis服务器的统计信息,如每秒命令执行数等。--bigkeys
:找出并报告数据库中的大键。--help
:显示帮助信息,列出所有可用的命令和选项。--version
:显示redis-cli
的版本信息。
redis-benchmark
命令说明:Redis提供的一个性能测试工具,用于通过运行特定的命令序列来测试Redis服务器的性能。它可以快速生成大量请求,测量Redis实例在不同负载下的响应速度和处理能力。这个工具对于评估Redis配置的性能、比较不同硬件或软件设置的影响,或者进行压力测试等都非常有用。
常用选项:
-n <requests>
:设置要执行的请求总数,默认是 100000。-c <clients>
:设置并发客户端的数量,默认是 50。-t <tests>
:指定要运行的测试类型,例如set
、get
、incr
、lpush
等等。-d <size>
:数据大小(以字节为单位),用于测试SET/GET
等命令,默认是 3 字节。--csv
:输出结果以CSV
格式,适合导入到表格或数据库中分析。-k <0/1>
:1(默认)表示保持连接开启(pipelining)
,0 表示每个请求后关闭连接。-P <number>
:使用pipelining
的方式来增加每个请求的响应时间,默认不启用。-r
:对set
、get
、incr
、lpush
、rpop
、sadd
、spop
、lrem
、lpop
、rpop
等命令使用随机 key,避免对同一个 key 的重复操作。
通用命令
1 |
|
数据结构及常用命令
字符串 (Strings)
概述:字符串是Redis中最基本的类型,它可以存储任何形式的字符串(包括二进制数据),最大可以支持存储
512MB
。用途:常用于存储文本或二进制数据,如缓存用户的邮箱、序列化的对象、图片或小文件等。
操作:提供设置(SET)、获取(GET)、追加(APPEND)、多键获取(MGET)等操作。
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
41
42
43
44
45
46
47
48
49
50# 设置键key的值为value
set key value
# 获取键key对应的值
get key
# 删除键key
del key
# 若键key存在,则对值后面拼接字符串value,若键key不存在,设置键key的值为value
append key value
# 获取键key对应的值的字符串长度
strlen key
# 将键key对应的值自增(只针对Integer类型的值)
incr key
# 将键key对应的值自减(只针对Integer类型的值)
decr key
# 将键key对应的值增加incrment(只针对Integer类型的值)
incrby key incrment
# 将键key对应的值减少decrment(只针对Integer类型的值)
decrby key decrment
# 获取键key对应的值特定范围的内容,下标区间为[start,end],end为-1代表到字符串结束
getrange key start end
# 设置键key指定下标的内容为value,替换区间为[offset, offset + value.length]
setrange key offset value
# 设置键key的值为value,且过期时间为seconds(秒)
setex key seconds value
# 如果键key不存在,则设置键key对应的值为value,键key存在不进行设置
setnx key value
# 设置多个键值对,如果命令后面出现前面已存在的键key,则会覆盖前面设置的值
mset key value [key value...]
# 获取多个值
mget key [key]
# 如果键key不存在(于数据库中),则设置键key对应的值为value,键key存在不进行设置
msetnx key value [key value...]
# 设置键key对应的值为value,返回上一次设置的值
getset key value
哈希 (Hashes)
概述:哈希是键值对集合,适用于存储对象。
用途:非常适合存储和表示对象(类似于编程语言中的字典)。例如,可以用哈希存储用户的属性,如name、age等。
操作:可以一次性设置或获取多个字段(HSET, HGET, HMSET, HMGET),删除字段(HDEL),检查字段是否存在(HEXISTS)等。
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# 设置键值对key,对应的值为键值对<field,value>
hset key field value
# 获取键值对key中键field的值
hget key field
# 设置键值对key的多个键及其对应值
hmset key filed value [field value ...]
# 获取键值对key的多个键的对应值
hmget key field [filed ...]
# 获取键值对key中所有的键及其对应值
hgetall key
# 删除键值对key中的键field
hdel key field [field ...]
# 获取键值对key中键的数量
hlen key
# 查询键值对key中是否存在键field
hexists key field
# 获取键值对key中的所有键
hkeys key
# 获取键值对key中的所有值
hvals key
# 将键值对key对应的键field对应值增加incrment
hincrby key field incrment
# 如果键值对key中不存在键field,则设置键field的值为value,存在则不进行设置
hsetnx key field value
列表 (Lists)
概述:列表是简单的字符串列表,按插入顺序排序。你可以在列表的头部或尾部添加元素。
用途:适合实现消息队列,通过将元素从列表头部插入,并从尾部移除来模拟队列的操作。
操作:添加元素到头部(LPUSH)或尾部(RPUSH),移除并获取头部(LPOP)或尾部元素(RPOP),修剪(LTRIM)等。
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# 往列表key头部添加值value
lpush key value [value...]
# 往列表key尾部添加值value
rpush key value [value...]
# 获取列表key下表范围为[start,stop]的值,stop为-1代表到列表结尾
lrange key start stop
# 弹出列表key的第一个值
lpop key
# 弹出列表key的最后一个值
rpop key
# 获取列表key下标为index的值
lindex key index
# 获取列表key的长度
llen key
# (从头开始)移除列表key中count个与value相等的值
lrem key count value
# 移除列表key中下标[start,stop]以外的值
ltrim key start stop
# 将列表source的最后一个值弹出并添加到列表destination头部
rpoplpush source destination
# 将列表key下标为index处的值设置为value
lset key index value
# 在列表元素pivot前面|后面插入value
linsert key before|after pivot value
集合 (Sets)
概述:集合是字符串的无序集合,它保证内部存储的元素是唯一的。
用途:适合存储没有重复的元素,例如,存储一个用户所有喜欢的标签,集合可以自动去重。
操作:添加(SADD)、移除(SREM)、检查存在性(SISMEMBER)、集合间的运算如交集(SINTER)、并集(SUNION)和差集(SDIFF)等。
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# 往集合key中添加元素member
sadd key member [member...]
# 获取集合key中的所有元素
smenbers key
# 查询集合key中是否含有元素member的成员之一
sismember key member
# 获取集合key里面的元素个数
scard key
# 移除集合key中的元素member
srem key member [member...]
# 随机获取集合key中的count个元素,count为空则代表1
srandmember key [count]
# 随机弹出集合key中的count个元素,count为空则代表1
spop key [count]
# 将集合source中的元素member转移到集合destination中
smove source destination member
# 获取存在于集合key1但不存在于集合key2的所有元素
sdiff key1 [key2...]
# 获取集合key1和集合key2的交集
sinter key1 key2
# 获取集合key1和集合key2的并集
sunion key1 key2
有序集合 (Sorted Sets)
概述:有序集合类似于集合,但每个元素都会关联一个浮点数分数,Redis正是通过分数来为集合中的元素提供了顺序。
用途:非常适合需要按顺序访问数据的场景,如排行榜、带权重的队列等。
操作:添加元素(ZADD)、删除元素(ZREM)、修改元素的分数(ZINCRBY)、获取元素的排名(ZRANK)等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# 往有序集合key中添加分数为score的元素member,集合中的元素会根据分数从小到大排序,分数相同则按首字母排序
zadd key score member [score member ...]
# 获取有序集合key中排名为[start,stop]的元素,withscores不为空则可显示对应分数
zrange key start stop [withscores]
# 获取有序集合key中分数为[min,max]的元素,withscores不为空则可显示对应分数,limit可指定从offset处开始截取count个元素,min为-inf表示负无穷,+inf表示正无穷
zrangebyscore key min max [withscores] [limit offset count]
# 移除有序集合key中元素member
zrem key member [member...]
# 获取有序集合key的元素数量
zcard key
# 获取有序集合key中元素member的排位(从小到大顺序,起始为0)
zrank key member
# 获取有序集合key中元素member的排位(从大到小顺序,起始为0)
zrevrank key member
位图 (Bitmaps)
概述:位图本质上不是一种独立的数据结构,它是字符串的一种特殊操作方式,通过位来表示二进制数据。
用途:适合进行大规模的位运算,常用于统计和分析,如用户登录、签到等。
操作:设置位(SETBIT)、获取位(GETBIT)、统计位(BITCOUNT)、位运算(BITOP)等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14# (以字节为单位分配字符串)设置位图key下标为offset的位的值为value,value默认为0,可设置为1
setbit key offset value
# 获取位图key下标为offset的位的值
getbit key offset
# 获取位图key上位为1的个数,可指定下标范围为[start, end]
bitcount key [start end]
# 对位图key进行操作(operation为and、or、xor、not运算),将结果存储到destkey
bitop operation destkey key [key ...]
# 查询位图key中第一个为bit(0或1)的下标位置,可指定字节的开始与结束范围为[start,end]
bitpos key bit [start] [end]
HyperLogLogs
概述:HyperLogLog是一种概率型数据结构,用于高效地估计数据集中唯一元素的数量。
用途:非常适合需要统计大量数据的去重数量,而对精确值要求不是非常严格的场景。
操作:添加元素(PFADD)、计数(PFCOUNT)、合并多个HyperLogLog(PFMERGE)等。
1
2
3
4
5
6
7
8# 往集合key中添加元素element
pfadd key element [element...]
# 获取多个集合key的技术估算值
pfcount key [key...]
# 将多个集合sourcekey合并为一个集合destkey
pfmerge destkey sourcekey [sourcekey...]
地理空间索引 (Geospatial Indexes)
概述:Redis的地理空间索引是通过有序集合实现的,允许你存储经度和纬度坐标作为元素。
用途:适用于地理位置的存储、查询和半径搜索,如查询某范围内的位置点。
操作:添加地理空间坐标(GEOADD)、查询半径内的元素(GEORADIUS)、获取两点之间的距离(GEODIST)等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# 往集合key中添加经度为longitudu、纬度为latitude的元素member
geoadd key longitudu latitude member [longitudu latitude member ...]
# 获取集合key中指定元素member的经纬度信息
geopos key member [member...]
# 获取集合key中元素member1和元素member2的距离,单位为空时代表“m”
geodist key member1 member2 [m|km|ft|mi]
# 获取集合key中的元素经纬度字符串
geohash key member [member...]
# 获取集合key中在经度longitude、维度latitude、半径radius[m|km|ft|mi]内的元素,withdist可显示距离,withcoord可显示经纬度,count n可限制数量为n,asc升序或desc降序
georadius key longitude latitude radius [m|km|ft|mi] [withcoord] [withdist] [withhash] [asc|desc] [count n]
# 获取集合key中在元素member半径radius[m|km|ft|mi]内的元素(含自身)
georadiusbymember key member radius [m|km|ft|mi] [withcoord] [withdist] [withhash] [asc|desc] [count n]
# 删除集合key中的元素member(geo并没有删除,底层通过zset实现,因此用zrem)
zrem key member [member...]
Java客户端
Jedis
简介
Jedis
是一个比较直接和轻量级的Redis客户端实现,提供了简单直接的API来与Redis进行交互。它支持几乎所有的Redis特性,并且由于其简洁性,是学习和实现Redis操作的好选择。 官方文档地址:Jedis guide | Docs (redis.io)
优点
- 轻量级和易于使用。
- 支持连接池。
缺点
- 不支持异步和非阻塞 I/O。
- 多线程环境下管理连接池较为复杂。
- API较为基础,没有分布式护具结构和高级事务处理等功能。
示例
单个连接
1 |
|
运行程序,控制台输出如下:
1 |
|
Jedis也支持连接池,示例
1 |
|
运行程序,控制台输出如下:
1 |
|
lettuce
简介
Lettuce
是一个高级Redis客户端,提供异步、基于事件的API,并且内部使用Netty实现非阻塞通信。Lettuce可以在多个线程间共享单个连接,而不是使用传统的连接池。
优点
- 支持同步、异步和反应式编程模型。
- 内部使用Netty,实现了高效的非阻塞I/O。
- 允许连接多路复用,大大减少线程和连接的使用。
- 支持自动重连和命令重试。
缺点
- 使用了异步网络应用框架Netty,在处理大量并发连接时可能会消耗更多系统资源。
- API和配置相对复杂,尤其是高级功能的配置和优化。
- 要求开发者在错误处理和回调管理上更加小心,否则可能导致难以追踪的错误和内存泄漏。
Redisson
简介
Redisson
是Redis的高级客户端之一,提供了丰富的Redis对象和服务,如分布式锁、集合、映射、有序集合、发布/订阅、Bloom filter 等高级功能。
优点
- 提供了许多Jedis和Lettuce中不包含的高级分布式Java对象和服务。
- 支持同步、异步和反应式接口。
- 内置Redis命令的高级抽象。
- 非常适合需要利用Redis提供分布式数据结构和服务的应用程序。
缺点
- 由于提供了大量高级功能和抽象,Redisson可能会比其他更轻量级的客户端使用更多的内存。
- 一些高级特性,如分布式锁或数据结构,可能引入额外的性能开销,特别是在高负载或大规模部署情况下。
三者对比
性能和并发处理
Lettuce
和Redisson
提供异步处理能力,对于需要处理高并发请求的应用更加合适。Jedis
由于其同步的实现方式,适合简单的应用场景,对于复杂的或高并发的场景则可能成为瓶颈。
API丰富度
Redisson
提供的API最为丰富,支持大量的分布式数据结构和服务。Lettuce
和Jedis
提供的API较为基础,主要围绕Redis的核心功能。
连接管理
Lettuce
提供连接多路复用,减少了连接总数的需求。Jedis
依赖于传统的连接池来管理多个连接。Redisson
支持连接池和单个连接的多路复用。
SpringDataRedis
Spring Data Redis
是Spring框架为Redis数据库提供的一个高级抽象模块,它简化了在Java应用中使用Redis的方式。通过Spring Data Redis,可以利用Spring框架提供的各种便利功能,如声明式事务支持、简化的数据访问模式和数据存取的透明化,从而使得与Redis的交互更加简单和高效。官方文档地址:Spring Data Redis
核心特性
- 简化配置:Spring Data Redis提供了易于使用的配置方式,可以快速集成Redis到Spring应用中。
- 模板 API:
RedisTemplate
和StringRedisTemplate
提供了丰富的方法来操作Redis,包括对keys
、strings
、lists
、sets
、hashes
等数据结构的操作。 - 存储库支持:类似于其他Spring Data模块,Spring Data Redis支持通过创建接口来定义存储库,用于访问Redis keys`和进行复杂查询。
- 对象映射:通过Spring Data的对象映射特性,可以将对象透明地存储在Redis中,而无需手动转换数据格式。
- 事务支持:支持Redis事务,并能与Spring的声明式事务管理无缝整合。
- 发布订阅:支持Redis的发布/订阅功能,可以在应用中实现消息通信模式。
- 高级序列化:支持多种序列化和反序列化机制,如JDK序列化、JSON序列化等。
使用场景
- 缓存实现:利用Redis强大的性能作为应用的缓存,减少数据库访问,提高响应速度。
- 会话存储:在分布式系统中,使用Redis存储会话信息,确保各个节点之间会话的一致性。
- 全局锁:使用Redis实现分布式锁,确保分布式环境下资源同步访问。
- 实时消息系统:利用Redis的发布/订阅功能实现实时消息系统。
与Jedis、Lettuce的关系
客户端库作为依赖
- Jedis:在Spring Data Redis的早期版本中,Jedis是主要使用的客户端。它是一个简单且直接的客户端库,提供了同步的API来与Redis进行交互。Spring Data Redis通过封装Jedis提供了一套更为简洁和Spring风格的操作方式。
- Lettuce:随着对响应式编程的支持和需求的增加,Lettuce成为了Spring Data Redis的首选客户端。Lettuce提供非阻塞和异步的API,且基于Netty实现、支持连接多路复用,适合需要高并发处理的场景。Spring Data Redis利用Lettuce提供了包括响应式编程在内的高级特性。
配置与抽象层
- 在Spring Data Redis中,可以自由选择使用Jedis或Lettuce作为底层连接库。Spring提供了统一的配置接口和操作模板(如
RedisTemplate
和响应式的ReactiveRedisTemplate
),这些模板抽象出了底层客户端的具体实现细节,使得开发者可以不必关心底层连接库是使用的Jedis还是Lettuce。 - 开发者在使用Spring Data Redis时,可以通过简单的配置更改所使用的客户端库,而不需要修改业务代码,这提供了极大的灵活性和便利性。
实际开发
在选择RedisTemplate
的底层实现时,可以根据以下几个方面来决定:
- 性能需求:
- 如果应用需要高性能的阻塞操作,且并发需求不高,可以选择Jedis。
- 对于需要高并发访问的应用,Lettuce的非阻塞和多路复用特性可能更适合。
- 并发模型:
- 在微服务架构中,服务通常需要处理大量并发请求,因此选择支持异步处理的客户端(如Lettuce)会更合适。
- 功能支持:
- 根据应用需求选择支持特定功能的客户端,例如,如果需要详细的连接管理、自动重连等高级功能,Lettuce 可能是更好的选择。
- 资源利用:
- 考虑到资源利用效率,Lettuce的连接多路复用可以减少连接总数,从而降低资源消耗。
示例:项目中Redis配置类(使用Lettuce)
1 |
|
1 |
|
常用API
不同于Jedis将所有的方法都封装到一个类中,Spring Data Redis提供了RedisTemplate
工具类,这个类中封装了各种对Redis的操作,把不同数据类型的操作API封装到不同的Operations
对象中
API | 返回值类型 | 说明 |
---|---|---|
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
redisTemplate.opsForZSet() | ZSetOperations | 操作SortedSet类型数据 |
redisTemplate | 通用命令 |
Demo:注入一个RedisTemplate
对象,先设置键值对,再获取
1 |
|
运行程序,控制台输出如下:
1 |
|
由控制台输出可知,RedisTemplate已正确设置键值对。
序列化
序列化器
上一个DEMO中设置了一个key为name
的键值对,但是通过命令行却看不到这个key
1 |
|
可以看到设置的键并不是name
,这是因为RedisTemplate
默认的序列化器为JDK的序列化器(通过org.springframework.data.redis.core.AbstractOperations
的keySerializer()
和valueSerializer()
方法可以查看)
我们可以将key的序列化器设置为更加常用的字符串序列化器StringRedisSerializer
,而对于value通常设置为JSON序列化器GenericJackson2JsonRedisSerializer
,Redis的配置类添加配置后如下
1 |
|
再次运行测试程序,这次RedisTemplate
会使用字符换序列化器和JSON序列化器在redis中存入一个键值对,通过Redis控制台查看如下:
1 |
|
类标识
在使用RedisTemplate
配置为JSON序列化器存储数据时,存入Redis的数据中包含类的标识信息(@class
属性),如运行以下程序后,往Redis中存入一个HashMap对象
1 |
|
查看Redis数据库中键user1对应的值
1 |
|
从结果可以看到值中包含了对象的类标识java.util.HashMap
,这是因为JSON序列化器GenericJackson2JsonRedisSerializer
被配置为存储类型信息,这种配置便于反序列化过程中能够精确地恢复出原始对象的类型。
注意事项:通过匿名内部类创建的HashMap
对象,创建的是HashMap
的一个匿名子类的实例,因此这个对象存入Redis后,值标识的类名是外部类信息的名称及其匿名类,如运行以下程序
1 |
|
运行出错,对应的错误日志为:
1 |
|
可以看到程序报错的原因为space.yangtao.client.RedisTemplateTest$1
(RedisTemplateTest
的第一个匿名类)缺少默认的构造器,从Redis中查看对应的值为
1 |
|
可以看到值中类标识并不是HashMap
,而是RedisTemplateTest
的第一个匿名类。
因此,在使用RedisTemplate
操作对象时,要尽量避免使用匿名类创建对象。
当然,这种问题也有解决方案,那就是类型擦除或手动序列化。
类型擦除
配置RedisTemplate
的序列化器为Jackson2JsonRedisSerializer
,或对GenericJackson2JsonRedisSerializer
的ObjectMapper
对象进行设置,使其序列化以后不包含类型信息
1 |
|
类型擦除以后,存入Redis中的值没有类型,程序的反序列化便可成功
1 |
|
1 |
|
注意事项:不存储类型信息可能会在反序列化时导致问题,特别是在处理多态和复杂对象结构时。如果序列化的数据结构比较复杂或涉及继承,不包含类型信息可能会导致反序列化失败或数据不正确。
手动序列化
在操作RedisTemplate
往Redis存入对象时,可以先将其进行序列化,从Redis取出对象时,再将其反序列化,如
1 |
|
相比于自动序列化,这种方法具有
优点:
- 灵活性:开发者可以自由选择什么时候以及如何转换数据,适合特定的需求,如减小数据大小或处理复杂的数据结构。
- 减少数据冗余:手动转换可以避免存储不必要的类型信息,使存储在Redis中的数据更加精简。
- 依赖性:不会依赖序列化库(如Jackson)的更新对存取和更新有兼容性和稳定性有影响。
缺点:
- 代码复杂性:需要手动管理数据的序列化和反序列化,增加了代码的复杂度和出错概率。
总结
通过本文的学习,读者应能够对Redis有一个全面的认识,从其基本概念到复杂的应用实现。Redis不仅支持多种数据结构,如字符串、列表、集合、哈希表、有序集合等,还提供了事务、消息订阅与发布和持久化等高级功能,满足现代应用的各种需求。此外,文章还探讨了如何在Java环境中利用Jedis、Lettuce和Redisson等客户端与Redis交互,以及如何通过Spring Data Redis简化代码和提高开发效率。掌握这些知识将极大地增强开发者在数据处理和应用开发中的能力。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!