Redis基础篇

基础篇

快速入门

初识Redis

NoSQL

image.png

事务:无事务、基本一致不能做到关系性数据库的ACID
NoSQL:数据写入时,就会根据数据的id进行hash运算,从而实现数据拆分,支持水平扩展

认识Redis

特征
  • 键值型,value支持多种不同数据结构,功能丰富
  • 单线程,每个命令具有原子性
    • Redis6.0后,在对网络请求处理的时候是多线程,核心的命令执行仍然单线程
  • 低延迟、速度快(基于内存、IO多路复用、良好编码(基于C语言编写的))
  • 支持数据持久化
  • 支持主从集群、分片集群
  • 支持多语言客户端

安装Redis

Redis官网地址
因为Redis基于C语言编写,因此必须先安装gcc依赖

yum -y install gcc tcl

上传压缩包到服务器
解压压缩包

 tar -zxvf redis-6.2.11.tar.gz

进入Redis的安装目录

cd redis/

运行编译命令

make && make install

查看是否安装成功
进入默认安装目录:

 cd /usr/local/bin/

image.png
启动
可以在任意位置运行,因为已经加入了环境变量。
前台启动:

redis-server

后台启动:
修改Redis配置文件,并指定以配置文件的形式启动。
Redis的配置文件在Redis的安装目录下。
先做备份

cp redis.conf redis.conf.bak

修改配置

#监听地址,127.0.0.1默认只能在本机访问,修改为0.0.0.0可以在任意IP访问
#bind 127.0.0.1 -::1
bind 0.0.0.0

#守护线程,yes表示后台运行
daemonize no
daemonize yes

#设置密码
requirepass 123456

其他常见配置

#监听端口 默认
port 6379

#工作目录
dir .

#数据库设置,默认16个库,0-15,设置为1表示只使用1个库
database 1

#设置Redis的最大内存
maxmemory 512mb

#日志文件,默认为空,可以指定文件名,会产生在工作目录下
logfile "redis.log"

启动Redis时指定配置文件

cd /root/development/redis
redis-server redis.conf

查看启动情况

ps -ef |grep redis

image.png
设置开机自启
先创建一个系统服务文件:

touch /etc/systemd/system/redis.service

文件内容:

[Unit]
Description=redis-server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /root/development/redis/redis.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

重载系统服务:

systemctl daemon-reload

启动Redis

systemctl start redis

查看运行状态

systemctl status redis

正在运行状态:
image.png
停止

systemctl stop redis

重启

systemctl restart redis

设置开机自启

systemctl enable redis

查看进程

ps -ef|grep redis

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 123456:指定Redis的访问密码

其中的commond就是Redis的操作命令,例如:

  • ping:与Redis服务端做心跳检测,服务端正常会返回pong

我们可以先不输入命令,先进入redis-cli控制台:

登录Redis

输入密码

redis-cli -h 127.0.0.1 -p 6379 -a 123456

image.png
明文指定密码比较危险,可以先登录通过命令输入密码

redis-cli -h 127.0.0.1 -p 6379
AUTH 123456
验证是否连接成功
ping

返回pong表示连接成功!

Redis图形化界面

RedisDesktopManage
下载地址
M1不可用,输入以下命令:

sudo spctl --master-disable
sudo xattr -rd com.apple.quarantine /Applications/

连接页面:
image.png
成功连接!
image.png

Redis常见命令

Redis数据结构

Redis是一种key-value类型的数据结构,key一般都是String类型,Value类型多种多样。
image.png
前五种较为常见,也称之为基本类型。
后三种为特殊类型,GEO是一种坐标,BitMap,HyperLog是一种特殊的按位去存储的一种数据结构。
官网:Commands
image.png
通过Redis客户端查看
例如:查看所有的string类型的命令:

help @string

查看通用命令:

help @generic

redis-cli:
image.png

Redis通用命令

KEYS

通用命令指部分数据类型都可以使用的命令,常见的有:
image.png
image.png
可以输入部分前缀,按Tab键补全。

#查看所有的key
keys *

#查看a开头的key
keys a*

#查看a开头,后面有两个字符的key
keys a??

当Redis数据量达到一定的规模时,数百万、数千万,使用模糊匹配查询,效率会很低, 会给服务器造成很大的负担。同时,因为Redis是单线程的,搜索这段时间内,就无法执行其他命令,等于整个Redis的服务就被阻塞了。
所以,不建议在生产环境使用keys去查询。但是,如果Redis是一个主从架构,在从节点使用该命令,倒可以考虑,但是不能在主节点使用该命令。

DEL
#删除指定的key
DEL key

#删除name这个key
del name

image.png

[]表示可以一次删除多个,返回值表示删除的数量。

EXISTS

判断key是否存在。
image.png

EXISTS name
EXPIRE

给一个key设置有效期,有效期到期时该key会被自动删除。
单位为秒
image.png
示例:

#设置name这个key的有效期为20秒
EXPIRE name 20
TTL

查看一个key的剩余有效期。

TTL key

#查看name这个key的剩余时间
TTL name

image.png

注意:
-2:表示已经过期
-1:表示永久有效

String类型

String类型,也就是字符串类型,是Redis中最简单的存储类型。
value字符串,根据字符串的格式不同,又可以分为3类:

  • string:普通字符串
  • int:整数类型,可以做自增、自减操作
  • float:浮点数类型,可以做自增、自减操作

底层都是通过字节数组形式进行存储,只不过是编码方式不同。
会将数字直接转为二进制的形式存储,更节省空间。
字符串只能将字符转为对应的字节码,更占用空间,最大空间不能超过512m。
image.png
常见命令:

  • set:添加或者修改已经存在的一个String类型的键值对
  • get:根据key获取String类型的value
  • mset:批量添加多个string类型的键值对
  • mget:根据多个key获取多个string类型的value

示例:
image.png
针对数值类型的特殊功能:

  • incr:让一个整形的key自增1

image.png

  • increby:让一个整形的key自增,但是我们可以指定步长

image.png

#指定age这个key,自增,每次自增2
incrby age 2

我们可以指定为负数,即为自减

incrby age -2

专门针对浮点数进行自增:

  • incrbyfloat:让一个浮点类型的数字自增并制定步长。没有默认增长,必须指定

image.png

  • setnx:添加一个string类型的键值对,前提是这个key不存在,否则不执行。相当于只有新增功能

image.png
set key value nx命令类似:
image.png

  • setex:添加一个string类型的键值对,并且指定有效期

通用命令中有个指令为expire为设置有效期,expire key seconds,前提是该key已经存在的情况下,必须先set进去才能操作。
setex:相当于是将set和expire命令合二为一了。

#setex key seconds value 

#设置key为name,有效期为30秒
setex name 30 123

image.png
set key value ex seconds功能类似:
image.png

注意:
Redis的key中允许有多个单词形成层级结构,多个单词之间用’:'进行分割,格式项目名:业务名:类型:id

形成的key在RDM中查看会显示树形结构:
image.png

Hash类型

Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String类型是将对象序列化为Json字符串后进行存储,当需要修改对象的某个字段时间,不方便,

  • 要么将整个字符串反序列化为对象,修改完成后,再序列化为Json字符串进行存储
  • 要么将新的Json字符串直接覆盖旧的字符串
    | key | value |
    | — | — |
    | test:user:1 | {“id”: 1,“name”: “Jack”} |
    | test:user:2 | {“id”: 2,“name”: “Rose”} |

Hash结构可以将对象中的每个字段进行独立存储,可以针对单个字段做CRUD:

keyvalue
fieldvalue
test:user:1id1
nameJack
test:user:2id2
nameRose

当我们去修改其中某个字段时,对其他字段没有任何影响。
image.png
示例:HSET

> ping
PONG
> hmset test:user:3 name xxname
OK
> hmset test:user:3 age 20
OK

image.png
对其中一个值进行修改

> hset test:user:3 age 10
OK

image.png

> hmset test:user:4 name aaa age 10 gender 1
OK

image.png

> hget test:user:3 name
xxname
> hmget test:user:4 name age
aaa
10

返回一个key下所有的键值对,参数只需要传key即可。

> hgetall test:user:4
name
aaa
age
10
gender
1

获取一个key下的所有的field

> hkeys test:user:4
name
age
gender

获取一个key下所有的value

> hvals test:user:4
aaa
10
1

HINCRBY:让一个Hash类型的key的字段值自增并指定步长

> HINCRBY test:user:4 age 2
12
> hget test:user:4 age
12

HSETNX:添加一个Hash类型的key的field的值,前提是这个field不存在,否则不执行

> HSETNX test:user:4 age 2

List类型

Redis中的List类型与Java中的LinkedList类似,可以看作是一个双向链表结构。既可以支持正向检索也可以支持反向检索。
特征也与LinkedList类似:

  • 有序
  • 元素可以重复
  • 插入和删除快
  • 查询速度一般

业务场景:
朋友圈点赞(顺序展示),评论…

List常见命令

image.png
image.png
image.png
LPUSH示例:左侧插入元素,可以一次插入一个元素,也可以一次插入多个元素

> LPUSH users 1
1
> LPUSH users 2 3 4
4

查询结果:4 3 2 1
image.png
RPUSH示例:右侧插入元素,可以一次插入一个元素,也可以一次插入多个元素

> RPUSH users 4 5 6
7

查询结果:4 3 2 1 4 5 6
image.png

当前顺序⬆️

LPOP:移除并返回列表左侧的第一个元素,没有则返回nil

> LPOP users 1
4

返回的是4,同时该元素也会被删除。
image.png
元素被移除:
image.png
RPOP:移除并返回列表右侧的第一个元素,没有则返回nil

> RPOP users 1
6

返回元素6,同时该元素也被移除。
image.png
LRANGE:返回一段角标范围内的所有元素

> LRANGE users 1 3
2
1
4

是按照角标顺序来返回的并不是前面的序号:
image.png
BLPOP与BRPOP:与LPOP和RPOP类似,只不过类似于阻塞式的,没有元素时等待指定时间,而不是直接返回nil

> BLPOP users2 1000

1000为timeout,超时时间,单位为:秒

添加元素

> LPUSH users2 1
1

BLPOP命令:阻塞结束

> BLPOP users2 1000
users2
1
List结构模拟栈

插入元素

> LPUSH testZhan 1 2 3 4 5
5

image.png
取出元素:LPOP

> LPOP testZhan
5
> LPOP testZhan
4
> LPOP testZhan
3
> LPOP testZhan
2
> LPOP testZhan
1

另一种方式完全同理!

List结构模拟队列
  • LPUSH添加,RPOP取出
  • RPUSH添加,LPOP取出

两种方式均可。

Set类型

Redis的Set结构与Java中的HashSet类似,可以看作是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:

  • 无序
  • 元素不可重复
  • 查找快
  • 支持交集、并集、差集等功能

可用于好友列表功能!
常见命令:

  • 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之间的差集
  • SUNION key1 key2:求key1和key2之间的并集(重复的元素只会记录一次)

添加多个元素,并查看该Set中的所有元素:

> SADD s1 a b c
3

> SMEMBERS s1
c
a
b

移除指定元素并查看是否存在:

> SREM s1 a
1

> SISMEMBER s1 a
0

查看当前Set中的元素个数:

> SCARD s1
2

SINTER查看两个集合之间的交集:

> SADD s2 a b c
3

> SADD s3 b c d
3

> sinter s2 s3
c
b

image.pngimage.png

SDIFF查看差集:
顺序不同结果不同:

> SDIFF s2 s3
a

> SDIFF s3 s2
d

查看两个集合之间的并集:

> SUNION s2 s3
a
c
d
b

SortedSet类型

Redis的SortedSet是一个排序的Set集合,与Java中的TreeSet较为类似,但是底层数据结构差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表用来排序(SkipList)加Hash表。SortedSet具备以下特性:

  • 可排序
  • 元素不重复
  • 查询速度快

因为SortedSet的可排序特性,经常被用来实现排行榜功能!
常见命令:
image.png
eg:

ZRANGE key min max  
ZREVRANGE key min max

案例练习:
将班级的下列学生得分存入Redis的SortedSet中:
Jack 85,Lucy 89 Rose 82,Tom 95,Jerry 78,Amy 92,Miles 76

> ZADD stu 85 Jack 89 Lucy 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles
7

RDM:
image.png

  • 删除Tom同学
> ZREM stu Tom
1

RDM:
image.png

  • 获取Amy分数
> ZSCORE stu Amy
92
  • 获取Rose同学排名
> ZREVRANK stu Rose
3
> ZRANK stu Rose
2
  • 查询80分以下有几个学生
> ZCOUNT stu 0 80
2
  • Amy加2分
> ZINCRBY stu 2 Amy
94
  • 查询成绩前3名的同学
> ZREVRANGE stu 0 2
Amy
Lucy
Jack
  • 查询成绩80以下的所有同学
> ZRANGEBYSCORE stu 0 80
Miles
Jerry

Redis的Java客户端

在Redis官网中提供了各种语言的客户端,地址:https://redis.io/docs/clients/

Jedis以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用。
LettuceLettuce是基于Netty实现的,支持同步和异步、响应式编程方式,并且是线程安全的。支持Jedis的哨兵模式、集群模式和管道模式,Sping兼容了该方式。

Jedis

Jedis的方法名与Redis命令保持一致,较为方便。

Demo
pom
    <modelVersion>4.0.0</modelVersion>

    <artifactId>JedisDemo</artifactId>

    <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.8.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
单元测试

测试前准备工作

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

/**
 * Jedis单元测试
 *
 * @author zhangzengxiu
 * @date 2023/5/2
 */
public class JedisTest {

    private Jedis jedis;

    /**
     * Jedis主机IP
     */
    private String jedisHost = "172.16.138.100";

    /**
     * Jedis端口
     */
    private int jedisPort = 6379;

    /**
     * Jedis连接密码
     */
    private String jedisPwd = "123456";

    /**
     * Jedis库
     */
    private int db = 0;

    @BeforeEach
    void setUp() {
        //建立连接
        jedis = new Jedis(jedisHost, jedisPort);
        //设置密码
        jedis.auth(jedisPwd);
        //选择库
        jedis.select(db);
    }

测试后处理工作

    @AfterEach
    void tearDown() {
        if (jedis != null) {
            jedis.close();
        }
    }

测试代码:
测试字符串

    /**
     * 单元测试:字符串
     */
    @Test
    public void StringTest() {
        //存数据
        String res = jedis.set("testName", "abc");
        System.out.println("res = " + res);
        //取数据
        String result = jedis.get("testName");
        System.out.println("result = " + result);
    }

测试Hash

    /**
     * 单元测试:Hash类型
     */
    @Test
    public void hashTest() {
        jedis.hset("auth:1", "name", "zhangsan");
        jedis.hset("auth:1", "age", "20");
        String name = jedis.hget("auth:1", "name");
        System.out.println("name = " + name);
        Map<String, String> map = jedis.hgetAll("auth:1");
        System.out.println(map);
    }
Jedis连接池

Jedis本身线程不安全,频繁的创建和销毁连接会有性能损耗,并发环境下,推荐使用Jedis连接池代替Jedis直连的方式。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Jedis连接池工厂
 *
 * @author zhangzengxiu
 * @date 2023/5/2
 */
public class JedisConnectionFactory {

    /**
     * Jedis连接池
     */
    public static final JedisPool jedispool;

    /**
     * Jedis主机IP
     */
    private static String jedisHost = "172.16.138.100";

    /**
     * Jedis端口
     */
    private static int jedisPort = 6379;

    /**
     * Jedis连接密码
     */
    private static String jedisPwd = "123456";

    /**
     * 设置超时时间
     */
    private static int timeout = 1000;

    /**
     * Jedis库
     */
    private static int db = 0;

    static {
        JedisPoolConfig jedispoolConfig = new JedisPoolConfig();
        //最大连接
        jedispoolConfig.setMaxTotal(8);
        //最大空闲连接
        jedispoolConfig.setMaxIdle(8);
        //最小空闲连接(超过一段时间没有人访问,就会清理其他连接)
        jedispoolConfig.setMinIdle(0);
        //最长等待时间(ms) (没有连接时,是否等待,等多久,默认-1 无限制等待)
        jedispoolConfig.setMaxWaitMillis(200);
        jedispool = new JedisPool(jedispoolConfig, jedisHost, jedisPort, timeout, jedisPwd, db);
    }

    /**
     * 获取Jedis实例
     *
     * @return
     */
    public static Jedis getJedis() {
        return jedispool.getResource();
    }

}

之前的获取连接方式修改为:

    @BeforeEach
    void setUp() {
        //获取连接
        jedis = JedisConnectionFactory.getJedis();
        //设置密码
        jedis.auth(jedisPwd);
        //选择库
        jedis.select(db);
    }

但是此时已经不是关闭连接,而是归还连接到连接池中了。
image.png

SpringDataRedis

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis。
SpringDataRedis官网
image.png

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)
  • 提供了RedisTemplate统一API来操作Redis
  • 支持Jedis的发布订阅模型
  • 支持Redis哨兵和Redis集群
  • 支持基于Lettuce的响应式编程
  • 支持基于JDK、Json、字符串、Spring对象的数据序列化与反序列化
  • 支持基于Redis的JDKCollection实现
快速入门

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对于Redis的操作。并且将不同数据类型的操作API封装到不同的类型中。
原生Jedis操作时,命令与方法是实际对应的,但是这样的操作会显的十分臃肿,其实Redis本质是对命令进行了分组的,比如,通用、string、hash等等。
image.png

Demo
依赖坐标
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringdDataRedisDemo</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
        <!--连接池-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.10.0</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
    </dependencies>

</project>
配置文件
spring.redis.host=172.16.138.100
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=0
#连接池配置
#最大连接
spring.redis.lettuce.pool.max-active=8
#最大空闲连接
spring.redis.lettuce.pool.max-idle=8
#最小连接
spring.redis.lettuce.pool.min-idle=0
#连接等待时间
spring.redis.lettuce.pool.max-wait=100

Spring默认引用的是Lettuce,如果需要使用jedis的相关配置,则需要额外引入jedis的坐标。
lettuce必须配置,否则不会生效。
image.png

启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author zhangzengxiu
 * @date 2023/5/2
 */
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
单测
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author zhangzengxiu
 * @date 2023/5/2
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void stringTest() {
        redisTemplate.opsForValue().set("name:a", "qwr");
        Object name = redisTemplate.opsForValue().get("name:a");
        System.out.println("name = " + name);
    }
}
RedisSerializer序列化

上述操作中,操作结果:
image.png
但是,我们去Redis的客户端发现,结果并不是我们预料的

> get name
null

这是由于序列化的问题!

> keys *
\xac\xed\x00\x05t\x00\x04name

这才是我们刚存进去的name这个key。

> get "\xac\xed\x00\x05t\x00\x04name"
\xac\xed\x00\x05t\x00\x03qwr

image.png
redisTemplate的set方法接收的并不是字符串而是一个Object,SpringDataRedis的特殊功能,它可以接受任何类型,帮助我们转为Redis可以处理的字节。RedisTemplate底层默认处理方式就是用JDK的序列化工具。
image.png
默认序列化器:
image.png
默认序列化器处理,存在的问题:

  • 可读性差
  • 内存占用较大

我们程序设置的key并不是我们以为的key,会被序列化,value也会被序列化。
所以,我们需要修改RedisTemplate的序列化方式。
RedisSerializer�序列化器的具体实现:
image.png
正常来说,一般key、hashkey都是字符串,所以我们可以选择StringRedisSerializer�序列化器。
StringRedisSerializer是专门用来处理字符串的。
如果value有可能是对象,建议使用GenericJackson2JsonRedisSerializer�。转Json序列化的工具。
修改方式:

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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

/**
 * @author zhangzengxiu
 * @date 2023/5/3
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置序列化工具
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        //key的序列化器为:StringRedisSerializer
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        return redisTemplate;
    }

}

需要添加依赖,如果引入了SpringMVC的坐标就不要单独引入该坐标了。

        <!--jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>

查看结果:
image.png
小坑:
必须带范型才生效,否则不生效。
因为我们的Bean也是RedisTemplate<String, Object>。

	@Autowired
    private RedisTemplate<String,Object> redisTemplate;

测试序列化对象:

    /**
     * 单测:测试序列化对象
     */
    @Test
    public void jsonTest() {
        redisTemplate.opsForValue().set("user:01", new User(1L, "zhangsan"));
        User user = (User) redisTemplate.opsForValue().get("user:01");
        System.out.println(user);
    }

RDM结果:
image.png
序列化器会根据class自动进行序列化与反序列化。但我们查看结果时,依然是Json:
User(id=1, name=zhangsan)

StringRedisTemplate

上面序列化器,依然存在一些问题,Json串中的@class类型写入Json,存入Redis,会带来额外的内存开销。
字节码本身比我们自身的数据还要大,但是为了实现自动序列化与反序列化,又不能删除。
image.png
为了节省内存空间,我们并不会使用Json序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成序列化和反序列化。
image.png
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化默认就是String。省去了我们自定义RedisTemplate的过程,此时就不需要自定义RedisTemplate了。
示例:

	@Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final ObjectMapper mapper = new ObjectMapper();

	/**
     * 单测:测试序列化对象
     */
    @Test
    public void jsonTest() throws Exception {
        User user = new User(100L, "1000abc");
        String s = mapper.writeValueAsString(user);
        stringRedisTemplate.opsForValue().set("user:001", s);
        String value = stringRedisTemplate.opsForValue().get("user:001");
        User user1 = mapper.readValue(value, User.class);
        System.out.println(user1);
    }

查看结果:省去了复杂的class信息。
image.png
image.png

RedisTemplate操作Hash类型
    /**
     * 单测:测试Hash
     */
    @Test
    public void hashTest() {
        //Hash存值
        stringRedisTemplate.opsForHash().put("user:000", "name", "zhangsan");
        stringRedisTemplate.opsForHash().put("user:000", "age", "100");

        String name = (String) stringRedisTemplate.opsForHash().get("user:000", "name");
        System.out.println(name);

        //取多个值,获取所有的键值对
        Map<Object, Object> entryMap = stringRedisTemplate.opsForHash().entries("user:000");
        System.out.println(entryMap);

        //获取所有的key
        Set<Object> keys = stringRedisTemplate.opsForHash().keys("user:000");
        keys.forEach(System.out::println);

        //获取所有的value
        List<Object> values = stringRedisTemplate.opsForHash().values("user:000");
        values.forEach(System.out::println);
    }

其他类型同理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值