Redis是一个键值数据库,即以key-value格式存储的,这种NoSQL非关系型数据库与MySQL关系型数据库截然相反
SQL中的S指的是structured结构化,那么NOSQL的非结构化,一般NOSQL分为四种
- 键值型,如redis这种key-value一一对应的,
- 文档型,DOcument。存储JSON这种特殊字符串
- 图型,Graph,存储的信息为节点
- 列表型,HBase
第二个是关联性Relational。NOSQL多为嵌套的形式,以JSON的格式在表和表之间完成联系
{
id: 1,
name: "张三",
orders: [
{
id: 1,
item: {
id: 10, title: "荣耀6", price: 4999
}
},
{
id: 2,
item: {
id: 20, title: "小米11", price: 3999
}
}
]
}
第三,SQL查询有一个固定语法,比如SELECT id,name,age FROM tb_user WHERE id=1 而redis只需要 get user:1,相似的MongoDB是db.users.find({_id:1})这样的,类似一个函数,不是统一的SQL语法
第四在于事物的特性,即SQL可以ACID均能满足,相对的无事物或者无法全部满足,仅仅BASE
以下是图中的文字信息:
特性 | SQL | NoSQL |
---|---|---|
数据结构 | 结构化 (Structured) | 非结构化 |
数据关联 | 关联的 (Relational) | 无关联的 |
查询方式 | SQL查询 | 非SQL |
事务特性 | ACID | BASE |
存储方式 | 磁盘 | 内存 |
扩展性 | 垂直 | 水平 |
使用场景 | 1) 数据结构固定 | 1) 数据结构不固定 |
2) 相关业务对数据安全性、一致性要求较高 | 2) 对一致性、安全性要求不高 | |
3) 对性能要求 |
NoSQL数据库类型:
1. 键值类型 (Redis)
2. 文档类型 (MongoDB)
3. 列类型 (HBase)
4. Graph类型 (Neo4j)
RESIS的全称是Remote Dictionary Server,是基于内存的键值型数据库
- 键值(key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、10多路复用、良好的编码)。
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端
安装Redis
首先在centos7上进行,安装Redis依赖
yum install -y gcc tcl
将6.2.6版本拖入其中,通过此命令解压
tar -zxvf redis-6.2.6.tar.gz
进入redis-6.2.6目录
运行编译命令
make && make install
默认的安装路径是在在 /usr/Local/bin目录下:
最简单的 redis-server 即可运行
[root@centos1 redis-6.2.6]# redis-server
7410:C 28 Feb 2025 15:06:59.066 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7410:C 28 Feb 2025 15:06:59.066 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=7410, just started
7410:C 28 Feb 2025 15:06:59.066 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
7410:M 28 Feb 2025 15:06:59.068 * Increased maximum number of open files to 10032 (it was originally set to 1024).
7410:M 28 Feb 2025 15:06:59.068 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 7410
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
7410:M 28 Feb 2025 15:06:59.072 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
7410:M 28 Feb 2025 15:06:59.072 # Server initialized
这种启动属于[前台启动],会阻塞整个会话窗口,窗口关闭或者按下CTRL+C则Redis停止。不推荐使用
cp redis.conf redis.conf.bck
#先拷贝一份
#然后修改conf文件
vi redis.conf
#监听的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,
生产环境不要设置为0.0.0.0
bind 0.0.0.0
#守护进程,修改为yes后即可后台运行
daemonize yes
#密码,设置后访问Redis必须输入密码
requirepass 123321
#其他常见配置
#监听的端口
port 6379
#工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir.
#数据库数量,设五为1,代表只使用1个库,默认有16个库,编号0-15
database1
#设置redis能够使用的最大内存
maxmemory 512mb
#日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"
查找命令为 / 查找内容
#通过redis-server redis.conf即可后台运行
[root@centos1 redis-6.2.6]# redis-server redis.conf
[root@centos1 redis-6.2.6]# ps -ef | grep redis
root 11482 1 0 15:25 ? 00:00:00 redis-server 0.0.0.0:6379 .
root 11496 2831 0 15:26 pts/0 00:00:00 grep --color=auto redis
kill -9 11482
#-9是kill的参数,1重新加载,9杀死程序,15停止,11482是刚才第一行的进程名
当然有开机自启动的方法
#新建文件
vi /etc/systemd/system/redis.service
复制以下文字到vim中
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl start redis
systemctl status redis
systemctl stop redis
systemctl status redis
#开机自启
systemctl enable redis
总结下,就是这样
#启动
systemctl start redis
#停止
systemctl stop redis
#重启
systemctl restart redis
#查看状态
systemctl status redis
安装完成Redis,我们就可以操作Redis,实现数据的CRUD了。这需要用到Redis客户端,包括:
命令行客户端
#Redis安装完成后就自带了命令行客户端:redis-cli,使用方式如下:
redis-cli [options] [commonds]
其中常见的options有:
-h 127.0.0.1 :指定要连接的redis节点的IP地址,默认是127.0.0.1
-p 6379:指定要连接的redis节点的端口,默认是6379
-a 123321:指定redis的访问密码
#其中的commonds就是Redis的操作命令,例如:
ping:与redis服务端做心跳测试,服务端正常会返回pong
不指定commond时,会进入redis-cli的交互控制台:
图形化桌面客户端
Releases · lework/RedisDesktopManager-Windows · GitHub这位大神提供了免费编译的windows版本,下载他的zip文件(不是那个src的)
编辑
永远的16个库,这时我们回到cli,如下即可查找到刚才的
192.168.169.133:6379> SELECT 1
OK
192.168.169.133:6379[1]> get name
"Rose"
编程客户端暂时不予介绍
02 Redis常见命令
Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样,string
Hash ,List,Set,Sortedset(基本类型),GEO,BitMap,HyperLog(特殊类型)
KEYS *
KEYS a* 不建议在生产环境设备上使用
#这是一种基于通配符搜索的,这种通配符查询对于单线程的redis是有巨大负担的
DEL name
#即可删除name
MSET k1 v1 k2 v2 k3 v3
#批量插入
192.168.169.133:6379[1]> MSET k1 v1 k2 v2 k3 v3
OK
192.168.169.133:6379[1]> keys *
1) "k3"
2) "k2"
3) "name"
4) "k1"
#当删除值大于本来的值时,会返回应该删除的数量
DEL k1 k2 k3 k4
(integer) 3
################################################
DEL:删除一个指定的key
EXISTS:判断key是否存在
EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
TTL:查看一个KEY的剩余有效期
################################################
EXPIRE age 20
#可以设定为20s,利用ttl查看的时候,当时间为-2的时候,表明他已经失活
set age 18
TTL age
#那么现在是否失活呢?TTL后是-1
String类型
String类型,也就是字符串类型,是Redis中最简单的存储类型。
其value是字符串,不过根据字符串的格式不同,又可以分为3类:
●string:普通字符串
●int:整数类型,可以做自增、自减操作
●float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m.
String的常见命令有:
SET:添加或者修改已经存在的一个String类型的键值对
GET:根据key获取String类型的value
MSET:批量添加多个String类型的键值对
MGET:根据多个key获取多个String类型的value
INCR:让一个整型的key自增1
INCRBY:让一个整型的key自增并指定步长,例如:incrby num 2让num值自增2
INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
SETEX:添加一个String类型的键值对,并且指定有效期
SETNX 和 SETEX 实质上在 Redis 2.6.12 以及之后的版本已经过时,推荐使用 SET 命令来完成了
set name wangwu nx
key的结构
Redis的key允许有多个单词形成层级结构,多个单词之间用 :隔开,格式如下:
项目名:业务名:类型:id
这个格式并非固定,也可以根据自己的需求来删除或添加词条。
例如我们的项目名称叫heima,有user和product两种不同类型的数据,我们可以这样定义key:
user相关的 key:heima:user:1
product相关的 key:heima:product:1
[项目名]:[业务名]:[类型]:[id]
如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON字符串后存储:
KEY | VALUE |
heima:user:1 | {"id":1,"name":"Jack","age":21} |
heima:product:1 | {"id":1,"name":“小米11","price":4999} |
#分四次注入数据
set heima:user:1 '{"id":1, "name":"Jack", "age": 21}'
set heima:user:2 '{"id":2, "name":"Rose", "age": 18}'
set heima:product:1 '{"id":1, "name:"小米11", "price":4999}'
set heima:product:2 '{"id":2, "name":"荣耀6", "price":2999}'
回到客户端中,黑马的key自然的形成了层级结构
hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:
KEY | VALUE |
heima:user:1 | {name:"Jack",age:21} |
heima:user:2 | {name:"Rose",age:18} |
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
我们可以对照着string的命令进行学习,比如Hash类型的常见命令,即所有命令加个h
Hash的常见命令有:
- HSETkeyfieldvalue:添加或者修改hash类型key的field的值
- HGETkeyfield:获取一个hash类型key的field的值
- HMSET:批量添加多个hash类型key的field的值
- HMGET:批量获取多个hash类型key的field的值
- HGETALL:获取一个hash类型的key中的所有的field和value
- HKEYS:获取一个hash类型的key中的所有的field
- HVALS:获取一个hash类型的key中的所有的value
- HINCRBY:让一个hash类型key的字段值自增并指定步长
- HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
127.0.0.1:6379> HSET heima:user:3 name Lucy
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 21
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 17
(integer) 0
127.0.0.1:6379> HGET heima:user:3 name
"Lucy"
127.0.0.1:6379> HGET heima:user:3 age
"17"
127.0.0.1:6379> HMSET heima:user:4 name HanMeiMei
OK
127.0.0.1:6379> HMSET heima:user:4 name LiLei age 20 sex man
OK
127.0.0.1:6379> HMGET heima:user:4 name age sex
1)"LiLei"
2)"20"
3)"man"
list类型
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
●有序
●元素可以重复
●插入和删除快
●查询速度一般
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
List的常见命令有:
●LPUSH key element...:向列表左侧插入一个或多个元素
●LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
●RPUSH key element...:向列表右侧插入一个或多个元素
●RPOP key:移除并返回列表右侧的第一个元素
●LRANGE key star end:返回一段角标范围内的所有元素
●BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
实际上redis把链表看成一个队列了,现在我LPUSH一个元素,那么链表A <-> B变成 C <-> A <-> B。相应的RPUSH就到了链表右边了
那么,如何用list结构模拟一个阻塞队列呢?
阻塞队列(超详细易懂)-CSDN博客BlockingQueue(阻塞队列)详解 - 一步一个小脚印 - 博客园1.1w字,10图彻底掌握阻塞队列(并发必备) - 知乎
1.出口入口在不同处
2.出队时用BLPOP或BRPOP
set类型
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
●无序
●元素不可重复
●查找快
●支持交集、并集、差集等功能
String的常见命令有:
- SADD key member...:向set中添加一个或多个元素
- SREM key member...:移除set中的指定元素
- SCARD key:返回set中元素的个数
- SISMEMBER key member:判断一个元素是否存在于set中
- SMEMBERS:获取set的所有元素
上面这些命令都是对元素的单一增删改查操作,那么当然就有多个的批量操作
- SINTER key1 key2...:求key1与key2的交集
- SDIFF key1 key2...:求key1与key2的差集
- SUNlON key1 key2..:求key1和key2的并集
Set命令的练习
将下列数据用Redis的Set集合来存储:
张三的好友有:李四、王五、赵六
李四的好友有:王五、麻子、二狗
利用Set的命令实现下列功能:
- 计算张三的好友有几人
- 计算张三和李四有哪些共同好友
- 查询哪些人是张三的好友却不是李四的好友
- 查询张三和李四的好友总共有哪些人
- 判断李四是否是张三的好友
- 判断张三是否是李四的好友
- 将李四从张三的好友列表中移除
127.0.0.1:6379> SADD zs lisi wangwu zhaoliu
(integer) 3
127.0.0.1:6379> SADD ls wangwu mazi ergou
(integer) 3
127.0.0.1:6379> SCARD ZS
(integer) 3
127.0.0.1:6379> SINTER ZS lS
1) "wangwu"
127.0.0.1:6379> SDIFF ZS ls
1)"is"
2)"zhaoliu"
127.0.0.1:6379> SUNION ZS lS
1)"isi"
2)"wangwu"
3)"zhaoliu"
4)"mazi"
5) "ergou"
127.0.0.1:6379> SISMEMBER ZS lisi
(integer) 1
127.0.0.1:6379> SISMEMBER ls zhangSan
(integer)
127.0.0.1:6379> SREM ZS lisi
(integer) 1
Sortedset类型
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表。
SortedSet具备下列特性:
●可排序
●元素不重复
●查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
Treeset底部是红黑树实现的,且需要你定义排序序列,添加元素少的时候用的是压缩表ziplist ,数据量多了的时候就是跳表skiplist
SortedSet的常见命令有:
●ZADD key scoremember:添加一个或多个元素到sortedset,如果已经存在则更新其score值
●ZREM key member:删除sorted set中的一个指定元素
●ZScoRE key member:获取sortedset中的指定元素的score值
●ZRANK key member:获取sortedset中的指定元素的排名
●ZCARD key:获取sortedset中的元素个数
●ZCOUNT key minmax:统计score值在给定范围内的所有元素的个数
●ZINcRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
●ZRANGE key minmax:按照score排序后,获取指定排名范围内的元素
●ZRANGEBYSCORE key minmax:按照score排序后,获取指定score范围内的元素
●ZDIFF、ZINTER、ZUNION:求差集、交集、并集
其实命令远不于此 ,你可以通过help 在redis中查看更多的命令
SortedSet命令练习
将班级的下列学生得分存入Redis的SortedSet中:
Jack85,Lucy89,Rose82,Tom95,Jerry78,Amy92,Miles76
并实现下列功能:
删除Tom同学
获取Amy同学的分数
获取Rose同学的排名
查询80分以下有几个学生
给Amy同学加2分
查出成绩前3名的同学
查出成绩80分以下的所有同学
127.0.0.1:6379> ZADD stus 85 Jack 89 Lucy 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles
(integer)7
127.0.0.1:6379> ZREM stus Tom
(integer) 1
127.0.0.1:6379> ZRANK stus RoSe
(integer) 2
127.0.0.1:6379> ZREVRANK stus RoSe
(integer) 3
127.0.0.1:6379> ZCARD stus
(integer) 6
127.0.0.1:6379> ZCOUNT stus 0 80
(integer) 2
127.0.0.1:6379> ZINCRBY stus 2 Amy
"94"
127.0.0.1:6379> ZRANGE stus 0 2
1) "Miles"
2) "Jerry"
3) "Rose"
127.0.0.1:6379> ZREVRANGE stus 02
1) "Amy"
2) "Lucy"
3)"Jack"
127.0.0.1:6379> ZRANGEBYSC0RE stus 0 80
1) "Miles"
2) "Jerry"
03 Redis的Java客户端
Netty是一个高性能的网络框架Netty实战入门详解——让你彻底记住什么是Netty(看不懂你来找我) - AI_爱码小士 - 博客园
spring官方默认是兼容的lettuce,实际上这也体现了spring最擅长的某部分,即整合。因此,spring Data Redis做了进一步整合,即Jedis和lettuce
Jedis
Jedis的官网地址:https://github.com/redis/jedis,我们先来个快速入门:
1. 引入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
2. 建立连接
private Jedis jedis;
@BeforeEach
void setUp() {
// 建立连接
jedis = new Jedis("192.168.150.101", 6379);
// 设置密码
jedis.auth("123321");
// 选择库
jedis.select(0);
}
先来创建一个maven工程Jedis-demo,GroupId改为com.heima
然后四步:
1.在pom.xml中引入依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
2.建立连接,在src.test.java中新建com.heima.test下的JedisTest
package com.heima.test;
import org.junit.jupiter.api.BeforeEach;
import redis.clients.jedis.Jedis;
public class JedisTest {
private Jedis jedis;
@BeforeEach
void setUp() {
// 1. 建立连接
jedis = new Jedis("192.168.150.101", 6379);
// 2. 设置密码
jedis.auth("123321");
// 3. 选择库
jedis.select(0);
}
@Test
void testString() {
// 存入数据
String result = jedis.set("name", "虎哥");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}
}
3.测试操作
4.释放资源在testString后添加下面代码
@Test
void testHash() {
// 插入hash数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");
// 获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
key-value组成了一个数据库,idea自动为我们写作map
Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大连接
jedisPoolConfig.setMaxTotal(8);
// 最大空闲连接
jedisPoolConfig.setMaxIdle(8);
// 最小空闲连接
jedisPoolConfig.setMinIdle(0);
// 设置最长等待时间,ms
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool = new JedisPool(jedisPoolConfig, "192.168.150.101", 6379, 1000, "123321");
}
// 获取Jedis对象
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
Jedis非线程安全测试以及原因分析_jedis为什么不是线程安全的-CSDN博客
使用jedis面临的非线程安全问题-腾讯云开发者社区-腾讯云
jedis是什么,为什么是线程不安全的_jedis线程安全吗-CSDN博客
下面创建连接池实例
创建java.com.heima.jedis.util.TedisConnectionFactory
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
// 配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
// 创建连接池对象
jedisPool = new JedisPool(poolConfig,
host: "192.168.150.101", port: 6379, timeout: 1000, password: "123321");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
回到JedisTest里修改
@BeforeEach
void setUp() {
// 1. 建立连接
// jedis = new Jedis("192.168.150.101", 6379);
jedis = JedisConnectionFactory.getJedis();
// 2. 设置密码
jedis.auth("123321");
// 3. 选择库
jedis.select(0);
}
你看jedis 的源代码就能意识到
SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)(类似于JBDC,实际上这里是list和get封装实现的)
- 提供了RedisTemplate统一APl来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
我们看一下set的类型,key和value,都是字符串类型,要么是字节,如果说我现在有一个java对象需要存,那么set和hset显然不行,如果你要存的是一个复杂的java对象时,你就必须得手动的对它做序列化,即变成字符串或者变成字节
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作APl封装到了不同的类型中:对set中引用redis臃肿的类减轻了一定的负担。
API | 返回值 | 类型说明 |
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
redisTemplate.opsForZSet() | ZSetOperations | 操作SortedSet类型数据 |
redisTemplate | 通用的命令 |
SpringDataRedis快速入门
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单:
1.引入依赖
名称:redis-demq
语言:Java
类型:Maven
组:com.heima
工件:redis-demo
软件包名称:com.heima
JDK: openjdk-23 Oracle OpenJDK 23
Java:17
打包:Jar
Developer Tool选择Lombok,NoSQL选择Spring data redis
删除没用的文件 即:gitignore,HELP.md,mvnw,mvnw.cmd,.mvn
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2.配置application.yaml文件
spring:
data:
redis:
host: 192.168.150.101
port: 6379
password: 123321
lettuce:
pool:
max-active:8
max-idle:8
min-idle:0
max-wait:100
package com.heima;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class RedisDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
redisTemplate.opsForValue().set("name","虎哥");
//获取string数据
Object name=redisTemplate.opsForValue().get("name");
System.out.println("name ="+name);
}
}
我们set一个rose,自然get到一个rose,然后重新运行java程序,发现get 仍然是rose不是虎哥。这就是序列化。
我们RedisTemplate,它的set方法接收的参数并不是字符串,而是object。这就是spring data redis的一个特殊功能,它可以接受任何形式的对象,帮我们把他转成redis可以处理的java对象了,而redistk的底层默认对这些对象的处理方式就是利用gdp的序列化工具object output stream。
此事在RedisTemplate.java中已有记载
第98行
/rawtypes/private@NullableRedisSerializerkeySerializer = null;
/rawtypes/ private @Nullable RedisSerializer valueSerializer = null;
/rawtypes/ private @Nullable RedisSerializer hashKeySerializer = null;
/rawtypes/ private @Nullable RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
第134行
defaultSerializer =new JdkSerializationRedisSerializer(
classLoader != null ? classLoader:this.getClass().getClassLoader());
它会给它创建一个默认的序列化器而默认序列化器就是jdk的序列化器
那么在你没有给这几个值进行定义的情况下
它就会走这个默认的gdp序列化器, 我们可以回到这个set方法
那么我们自然想到修改那个98行.首先gdk这个我们就看过了啊,我们不希望用它,这里边还有两个是我们可以使用的,一个是StringRedisSerializer:专门用来处理字符串。因为我们知道字符串要想转字节写入redis,其实只需要简单的getby是不是就行了。那么老师认为,如果你的key和你的哈希key都是字符串的情况下,那么就用它。当然我们的value如果有可能是对象啊,那么建议你使用这个GenericJackson2JsonRedisSerializer,一个转json字符串的这样一种序列化工具
SpringDataRedis的序列化方式
我们可以自定义RedisTemplate的序列化方式,在java.com.heima.redis.config新建RedisConfig代码如下:
packagecom.heima.redis.config;
import org.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig{
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
// 创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
// key和hashKey采用string序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// value和hashValue采用JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
我们把这个RedisDemoApplicationTests.java的时候加个泛型string object 第11行
@Autowired
private RedisTemplate<String,Object>redisTemplate;
在xml中补充Jackson依赖
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
那么如果我写的一个对象,他能不能帮我们也去做序列化呢,答案是ok的(这里省略了一部分)
序列化失败的同学看下导入的@Configuration包是否正确 import org.springframework.context.annotation.Configuration;
StringRedisTemplate
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。
StringRedisTemplate
为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String 类型的key和value。当需要存储lava对象时,手动完成对象的序列化和反序列化。
既然说了我们要统一使用string,也就是说我们啊原来的那个redis template,是不是需要统一的定义成用string serializer才可以啊,也就是说还需要重新的定义redis template
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testStringTemplate() throws JsonProcessingException {
// 准备对象
User user = new User("虎哥", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入一条数据到redis
stringRedisTemplate.opsForValue().set("user:200", json);
// 读取数据
String val = stringRedisTemplate.opsForValue().get("user:200");
// 反序列化
User user1 = mapper.readValue(val, User.class);
System.out.println("user1 = " + user1);
}
新建RedisStringTests.java
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testString(){
//写入一条String数据
stringRedisTemplate.opsForValue().set("name","虎哥");
//获取string数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name ="+ name);
}
//由于你换了所以无法自动序列化
private static final objectMappermapper=new objectMapper();
@Test
void testSaveUser() throws JsonProcessingException{
//创建对象
User user = new User(name:"虎哥",age:21);
//手动序列化
String json = mapper.writeValueAsString(user) ;
//写入数据
stringRedisTemplate.opsForValue().set("user:200",json);
//获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:100");
//手动反序列化
User userl =mapper.readValue(jsonUser,User.class);
System.out.println("user1 ="+user1);
}
}
RedisTemplate的两种序列化实践方案:
方案一:
1.自定义RedisTemplate
2.修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
方案二:
1.使用StringRedisTemplate
2.写入Redis时,手动把对象序列化为JSON3.读取Redis时,手动把读取到的JSON反序列化为对象
Hash结构
hash可以干什么呢?就是一个key对应多个字段和值字段值,但是在spring里面,它并不是以命令名作为方法名的。而是put
@Test
void testHash(){
stringRedisTemplate.opsForHash().put(key:"user:40o",hashKey:"name",value:"虎哥");
stringRedisTemplate.opsForHash().put(key:"user:40o",hashKey:"age",value:"21");
Map<Object,Object> entries = stringRedisTemplate.opsForHash().entries(key:"user:400");
//得到的就是这个所有的键值对形成的map了
System.out.println("entries ="+entries);
}
Comments NOTHING