Redis入门

课程链接:黑马Redis快速入门,一套搞定Redis,常见数据结构及命令,包含jedis应用与优化、springdataRedis应用与优化_哔哩哔哩_bilibili

声明:该文章为本人的学习笔记,非专业教程

目录

声明:该文章为本人的学习笔记,非专业教程

Redis快速入门

认识NoSQL

差异

认识Redis

安装Redis(Linux)

Redis图形化界面 RedisDesktopManager(RDM)

连接Redis

Redis常见命令

Redis数据结构

Redis通用命令

String类型

String类型常见命令

Key的层级结构

Hash类型

Hash类型常用命令

List类型

List常用命令

Set类型

Set类型常见命令

Set命令练习

SortedSet类型

SortedSet的常见命令

SortedSet命令练习

Redis的Java客户端

各种客户端

Jedis

快速入门

Jedis连接池

SpringDataRedis

SpringDataRedis快速入门

SpringDataRedis的序列化方式

StringRedisTemplate


Redis快速入门

认识NoSQL

差异

SQL(关系型数据库)NoSQL(非关系型数据库)
数据结构结构化(structured)非结构化
数据关联关联的(Relational)无关联的
查询方式SQL查询非SQL
事务特性ACID(事务)BASE(基本一致,无事务)
存储方式磁盘内存
扩展性垂直水平
使用场景1)数据结构固定 2)相关业务对数据安全性、一致性要求较高1)数据结构不固定 2)对一致性、安全性要求不高 3)对性能要求低

认识Redis

Redis诞生于2009年全称Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。

特征

  • 键值(key-value)型,value支持多种不同的数据结构,功能丰富

  • 单线程,每个命令具有原子性

  • 低延迟,速度快(基于内存、IO多路复用、良好的编码)

  • 支持数据持久化(解决断电问题)

  • 支持主从集群、分片集群

  • 支持多语言客户端

安装Redis(Linux)

  • 配置EPEL仓库

    EPEL的全称是 Extra Packages for Enterprise Linux。EPEL是由Fedora 社区打造,为RHEL及其衍生发行版如CentOS、Scientific Linux等提供高质量软件包的项目。装上了EPEL后,相当于添加了一个第三方源。EPEL则为服务器版本提供大量的rpm包(yum程序所使用的程序安装包,类似Windows的exe),而且大多数rpm包在官方repository中是找不到的

    # root执行
    yum install -y epel-release
  • 安装Redis

    # root执行
    yum install -y redis
  • 配置Redis

    # vim 打开 /etc/redis.conf文件(需root)
    # 找到 bind 127.0.0.1 改为
    bind 0.0.0.0                    # 为了方便连接redis,改为任意IP都可以连接Redis
    # 找到 #requirepass foobared
    requirepass 设置你的密码          # 本机测试可选
  • 启动Redis

    # root执行
    # 使用systemctl管控,服务名:redis
    systemctl enable redis          # 开机自启 建议设置
    systemctl disable redis         # 关闭开机自启
    systemctl start redis           # 启动 
    systemctl stop redis            # 关闭
    systemctl status redis          # 查看状态
  • 放行防护墙,redis使用端口6379

    # 方式1(推荐),关闭防火墙
    systemctl stop firewalld        # 关闭
    systemctl disable firewalld     # 关闭开机自启
    ​
    #方式2,放行6379端口
    firewall-cmd --add-port=6379/tcp --permanent #放行tcp规则下的6379端口,永久生效
    firewall-cmd --reload
  • 进入redis服务(命令行客户端)

    # 执行redis-cli
    [womeng@centos ~]$ redis-cli 
    127.0.0.1:6379>AUTH 你的密码 # 没有设置密码请跳过
    OK
    127.0.0.1:6379> ping
    PONG

Redis图形化界面 RedisDesktopManager(RDM)

下载链接:https://github.com/lework/RedisDesktopManager-Windows/releases/download/2022.5/resp-2022.5.zip

安装:双击解压后的文件,除了要修改安装位置,其他一路点击下一步即可

连接Redis

  • 双击运行RDM

  • 在弹出的窗口点击 连接到Redis服务器

  • 点击连接设置

  • 连接名随意,地址填写Linux虚拟机的地址(Linux终端输入ifconfig可知),端口不变,密码跟配置文件的一样

  • 测试连接

  • 成功 —> 点击确定,完成连接

  • 失败 —> 检查安装步骤是否有漏

Redis常见命令

Redis数据结构

Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样

数据类型举例
Stringhello world
Hash{name: "Jack" , age : 21}
List[A -> B -> C -> D]
Set{A , B , C}
SortedSet{A : 1 , B : 2 , C : 3}
GEO{A : (120.3 , 30.5)}
BitMap0110110101110101011
HyperLog0110110101110101011

基本类型

  • String:字符串

  • Hash:哈希表

  • List:有序集合,本质是链表

  • Set:无序集合,不可重复

  • SortedSet:有序集合,不可重复

特殊类型

  • GEO:用于存储地理位置信息

  • BitMap:基于字符串类型,可以将字符串看作是一个由二进制位组成的数组

  • HyperLog:一种概率数据结构,用于估算集合的基数(即集合中不同元素的数量)

不同数据类型有不同的指令

Redis通用命令

通用指令是部分数据类型都可以使用的指令,常见的有

官方文档:Commands | Docs (redis.io)

命令行查看:

127.0.0.1:6379> help @generic

PS使用指令前可以输入 help [command]来查看指令的具体用法,按Tab键可以快速匹配指令

  • KEYS:查看符合模板的所有key(类似MySQL模糊查询),不建议在生产环境设备上使用

    127.0.0.1:6379> help KEYS
    ​
      KEYS pattern
      summary: Find all keys matching the given pattern
      since: 1.0.0
      group: generic
127.0.0.1:6379> KEYS *
1) "mykey"
2) "name"
127.0.0.1:6379> KEYS n*
1) "name"
  • DEL:删除一个或多个指定的key,返回成功删除的键的个数

    127.0.0.1:6379> help DEL
    ​
      DEL key [key ...]
      summary: Delete a key
      since: 1.0.0
      group: generic
    127.0.0.1:6379> KEYS *
    1) "k1"
    2) "k2"
    3) "k3"
    4) "name"
    127.0.0.1:6379> DEL k1 k2 k3 k4
    (integer) 3
  • EXISTS:判断一个或多个key是否存在,返回存在的key的个数

    127.0.0.1:6379> help EXISTS
    ​
      EXISTS key [key ...]
      summary: Determine if a key exists
      since: 1.0.0
      group: generic
    127.0.0.1:6379> KEYS *
    1) "name"
    127.0.0.1:6379> EXISTS name age
    (integer) 1
  • EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除。如果不设置,表示该key永久有效

  • TTL:查看一个key的剩余有效期

之所以有EXPIRE这个命令,是为了节省内存空间的,如果长时间不清理,内存就会被占满,影响Redis的效率

127.0.0.1:6379> KEYS *
1) "name"
127.0.0.1:6379> ttl name
(integer) -1  # -1 表示永久有效
127.0.0.1:6379> EXPIRE name 15
(integer) 1
127.0.0.1:6379> ttl name
(integer) 14
127.0.0.1:6379> ttl name
(integer) 11
127.0.0.1:6379> ttl name
(integer) 6
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2 # -2表示不存在
127.0.0.1:6379> KEYS *
(empty list or set)

String类型

String类型,也就是字符串类型,是Redis中最简单的存储类型。

其value是字符串,不过根据字符串的格式不同,又分为3类:

  • String:普通字符串,

  • int:整数类型,可以自增、自减操作

  • float:浮点类型,可以自增、自减操作

不管是那种格式,底层都是字节数组形式存储,只不过是编码方式不同。

数值类型的字符串,在存储时会转换成二进制形式的数据,这样一个字节就可以存储很大的数值,可以更节省空间;

普通字符串只能是将字符转为对应的字节码,然后进行存储,相对来讲会更加花费空间。

字符类型的最大空间不能超过512m。

KEYVALUE
msghello world
num10
score92.5

String类型常见命令

增改查:删用 DEL

  • 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不存在,否则不执行。等同于 set key value nx推荐使用

  • SETEX:添加一个String类型的键值对,并指定有效期。等同于set key value ex [time]

Key的层级结构

Redis中没有MySQL中的Table的概念,不过Redis允许有多个单词形成层级结构来区分不同的数据(如,用户,订单),多个单词之间用 :隔开,示例格式;

项目名:业务名:类型:id

例如,项目名为test,有user和product两种不同的数据,可以这样定义

  • user相关的key:test:user:1

  • product相关的key:test:product:1

如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON格式的字符串后存储:

KEYVALUE
test:user:1{"id":1,"name":"jack","age":21}
test:product:1{"id":1,"name":"小米","price":4999}

演示

127.0.0.1:6379> MSET test:user:1 '{"id":1,"name":"jack","age":21}' test:product:1 '{"id":1,"name":"小米","price":4999}'
OK
127.0.0.1:6379> MSET test:user:2 '{"id":1,"name":"jack","age":21}' test:product:2 '{"id":1,"name":"小米","price":4999}'
OK
127.0.0.1:6379> KEYS *
1) "k1"
2) "test:user:2"
3) "k2"
4) "test:product:2"
5) "k3"
6) "test:product:1"
7) "test:user:1"

PS:在RDM中可以更好的查看存储效果

Hash类型

Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构

String结构是将对象序列化为JSON字符串后存储,当需要修改对某个字段时很不方便:要么重新赋值,要么删了重建

KEYVALUE
test:user:1{"id":1,"name":"Jack","age":21}
test:user:2{"id":2,"name":"Mike","age":18}

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

Hash类型常用命令

增改查:删用 DEL

  • HSET key field value :添加或修改hash类型key的field值

  • HGET key field:获取一个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 test:user:3 name Tom
(integer) 1
127.0.0.1:6379> HSET test:user:3 age 22
(integer) 1
127.0.0.1:6379> HMSET test:user:4 name Jonh age 21 gender male
OK
127.0.0.1:6379> KEYS *
1) "test:user:3"
2) "k3"
3) "test:product:2"
4) "test:user:4"
5) "k1"
6) "k2"
7) "test:user:2"
8) "test:product:1"
9) "test:user:1"

List类型

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

特征也与LinkedList类似:

  • 有序

  • 元素可重复

  • 插入和删除快

  • 查询速度一般

常用来存储一些有序数据,例如:朋友圈点赞列表,评论列表等

List常用命令

  • LPUSH key element ... :向列表左侧插入一个或多个元素

  • LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil(没错,就是nil)

  • RPUSH key element ... :向列表右侧插入一个或多个元素

  • RPOP key:移除并返回列表右侧的第一个元素,没有则返回nil

  • LRANGE key start end:返回一段角标范围内的所有元素

  • BLPOPBRPOP:与LPOPRPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil

演示

127.0.0.1:6379> LPUSH users 1 2 3 
(integer) 3
127.0.0.1:6379> RPUSH users 4 5 6
(integer) 6

127.0.0.1:6379> LPOP users
"3"
127.0.0.1:6379> RPOP users
"6"

127.0.0.1:6379> LRANGE users 1 3
1) "1"
2) "4"
3) "5"

Set类型

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

  • 无序

  • 元素不可重复

  • 查找快

  • 支持交集、并集、差集等功能

Set类型常见命令

单集合操作

  • SADD key member ...:向set中添加一个或多个元素

  • SREM key member ...:移除set中的在指定元素

  • SCARD key:返回set中元素的个数

  • SISMENBER key member:判断一个元素是否存在于set中

  • SMEMBERS:获取set中的所有元素

多集合操作

  • SINTER key1 key2 ...:求key1于key2的交集

  • SDIFF key1 key2 ...:求key1于key2的差集

  • SUNION key1 key2 ...:求key1合key2的并集

Set命令练习

将下列数据用Redis的Set集合来存储

  • 张三的好友有:李四、王五、赵六

    SADD ZSfs lisi wangwu zhaoliu
  • 李四的好友有:王五、麻子、二狗

    SADD LSfs wangwu mazi ergou

利用Set的命令实现以下功能

  • 计算张三的好友有几个

    127.0.0.1:6379> SCARD ZSfs
    (integer) 3
  • 计算张三和李四有哪些共同好友

    127.0.0.1:6379> SINTER ZSfs LSfs
    1) "wangwu"
  • 查询哪些是张三的好友却不是李四的好友

    127.0.0.1:6379> SDIFF ZSfs LSfs
    1) "zhaoliu"
    2) "lisi"
  • 查询张三和李四的好友总共有哪些人

    127.0.0.1:6379> SUNION ZSfs LSfs
    1) "ergou"
    2) "lisi"
    3) "wangwu"
    4) "zhaoliu"
    5) "mazi"
  • 判断李四是否是张三的好友

    127.0.0.1:6379> SISMEMBER ZSfs lisi
    (integer) 1
  • 判断张三是否是李四的好友

    127.0.0.1:6379> SISMEMBER LSfs zhangsan
    (integer) 0
  • 将李四从张三的好友列表中移除

    127.0.0.1:6379> SREM ZSfs lisi
    (integer) 1

SortedSet类型

Redis的SortedSet是一个可排序的set集合,于Java中的TreeSet类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表。

SortedSet具备下列特性:

  • 可排序

  • 元素不重复

  • 查询速度快

因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。

SortedSet的常见命令

  • ZADD key score member:添加一个或多个元素到sorted set,如果已经存在则更新其score值

  • ZREM key member:删除sorted set中的一个指定元素

  • ZSCORE key member:获取sorted set中指定元素的score值

  • ZRANK key member:获取sorted set中指定元素的排名(升序,降序为 ZREV前缀

  • ZCARD key:获取sorted set中的元素个数

  • ZCOUNT key min max:统计score值在给定范围内的所有元素的个数

  • ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值

  • ZRANGE key min max:按照score排序后,获取指定排名范围内的元素(升序,降序为 ZREV前缀

  • ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素(升序,降序为 ZREV前缀

  • ZDIFF、ZINTER、ZUNION:求差集、交集、并集

SortedSet命令练习

将班级的下列学生得分存入到Redis的SortedSet中

Jack 85 ,Lucy 89 ,Rose 95 ,Tom 78 , Amy 82 ,Miles 76

127.0.0.1:6379> ZADD class1 85 Jack 89 Lucy 95 Rose 78 Tom 82 Amy 76 Miles
(integer) 6

实现下列功能:

  • 删除Tom同学

    127.0.0.1:6379> ZREM class1 Tom
    (integer) 1
  • 获取Amy同学的分数

    127.0.0.1:6379> ZSCORE class1 Amy
    "82"
  • 获取Rose同学的排名

    127.0.0.1:6379> ZRANK class1 Rose # 升序排名 从 0 开始
    (integer) 4
    127.0.0.1:6379> ZREVRANK class1 Rose # 降序排名 
    (integer) 0
  • 查询80分以下有几个同学

    127.0.0.1:6379> ZCOUNT class1 0 80
    (integer) 1
  • 给Amy同学加2分

    127.0.0.1:6379> ZINCRBY class1 2 Amy
    "84"
  • 查出成绩前三名的同学

    127.0.0.1:6379> ZREVRANGE class1 0 2 # 降序排序
    1) "Rose"
    2) "Lucy"
    3) "Jack"
  • 查出成绩80分以下的所有同学

    127.0.0.1:6379> ZRANGEBYSCORE class1 0 80
    1) "Miles"

Redis的Java客户端

各种客户端

在Redis官网中提供了各种语言的额客户端,地址:Connect with Redis clients | Docs

客户端描述
jedis优点:以Redis命令作为方法名称,学习成本低,简单实用。 缺点:jedis实例是线程不安全的,多线程环境下需要基于连接池来使用
lettucelettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且式线程安全的。支持Redis的哨兵模式、集群模式和管道模式
RedissonRedisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、Semaphore、AtomicLong等强大功能

本教程会侧重于jedis和lettuce的学习,以及整合了这两种客户端的Spring Data Redis。

Jedis

快速入门

官网地址:redis/jedis: Redis Java client (github.com)

  1. 新建maven项目,在pom.xml引入以下依赖:

        <dependencies>
            <!--jedis:Redis的Java客户端-->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>5.0.0</version>
            </dependency>
            <!--单元测试-->
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter</artifactId>
                <version>5.7.0</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
  2. 建立连接

        @BeforeEach
        void setUp(){
            //1.建立连接
            jedis = new Jedis("你的Redis服务器地址", 6379);
            //2.设置密码(跟Redis配置文件一致)
            jedis.auth("密码");
            //3.选择库
            jedis.select(0);
        }
  3. 编写测试用例

        //测试字符串类型
        @Test
        void testString(){
            //存入数据
            String result = jedis.set("name", "makabaka");
            System.out.println("result = " + result);
    ​
            //获取数据
            String name = jedis.get("name");
            System.out.println("name = " + name);
        }
  4. 测试完成,断开连接

    @AfterEach
    void tearDown(){
        //测试完成后,断开连接
        if (jedis != null){
            jedis.close();
        }
    }
  5. 运行测试用例,控制台输出

    result = OK
    name = makabaka
  6. 完整测试类代码(在路径src/test/java新建 test 包 )

    package test;
    ​
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import redis.clients.jedis.Jedis;
    ​
    ​
    public class JedisTest {
    ​
        private Jedis jedis;
    ​
        @BeforeEach
        void setUp(){
            //1.建立连接 
            jedis = new Jedis("你的Redis服务器地址", 6379);
            //2.设置密码(跟Redis配置文件一致)
            jedis.auth("你的密码");
            //3.选择库
            jedis.select(0);
        }
    ​
        @AfterEach
        void tearDown(){
            //测试完成后,断开连接
            if (jedis != null){
                jedis.close();
            }
        }
        //测试字符串类型
        @Test
        void testString(){
            //存入数据
            String result = jedis.set("name", "makabaka");
            System.out.println("result = " + result);
    ​
            //获取数据
            String name = jedis.get("name");
            System.out.println("name = " + name);
        }
    }

Jedis连接池

jedis本身时线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用连接池代替jedis的直连方式。

  1. 在 src/main/java 路径下新建 jedis.util包,创建JedisConnectionFactory工具类

    package jedis.util;
    ​
    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);
            // 等待时常 ms
            poolConfig.setMaxWaitMillis(1000);
            // 创建连接池对象
            jedisPool = new JedisPool(poolConfig,"你的Redis服务器地址",6379,1000,"密码");
        }
    ​
        public static Jedis getJedis(){
            return jedisPool.getResource();
        }
    }
  2. 替换jedis的获取方式

    @BeforeEach
    void setUp(){ 
        //1.从JedisConnectionFactory中获取jedis
        jedis = JedisConnectionFactory.getJedis();
        //2.选择库
        jedis.select(0);
    }

SpringDataRedis

SpringData是Spring中数据操作的模板,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis

官网:Spring Data Redis

SpringDataRedis的特点

  • 提供了对不同Redis客户端的整合(Lettuce和jedis)

  • 提供了RedisTemplate统一API来操作Redis

  • 支持Redis的发布定于模型

  • 支持Redis哨兵和Redis集群

  • 支持基于Lettuce的响应式编程

  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化

  • 支持基于Redis的JDKCollection实现

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装在不同的类型中:

API返回值类型说明
redisTemplate.opsForValue()ValueOperations操作String类型数据
redisTemplate.opsForHash()HashOperations操作Hash类型数据
redisTemplate.opsForList()ListOperation操作List类型数据
redisTemplate.opsForSet()SetOperation操作Set类型数据
redisTemplate.opsForZSet()ZSetOperation操作SortedSet类型数据
redisTemplate通用命令

SpringDataRedis快速入门

SpringBoot以及提供了对SpringDataRedis的支持,使用非常简单:

  1. 新建SpringBoot项目,引入依赖

    <!--redis依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--common-pool-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
  2. 在application.yml(默认为.properties后缀,可自行重命名)中配置Redis的信息

    spring:
      # Redis配置
      data:
        redis:
          host: Redis服务器地址
          password: 密码
          lettuce:
            pool:
              max-active: 8
              max-idle: 8
              min-idle: 0
              max-wait: 100ms
  3. 在要使用的地方注入RedisTemplate

        @Autowired
        private RedisTemplate redisTemplate;
  4. 测试类完整代码

    package com.womeng.springbootredisdemo;
    ​
    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 SpringBootRedisDemoApplicationTests {
    ​
        @Autowired
        private RedisTemplate redisTemplate;
    ​
        @Test
        void testString() {
            //写入一条String数据
            redisTemplate.opsForValue().set("name","卧梦");
            //获取String数据
            Object name = redisTemplate.opsForValue().get("name");
            System.out.println("name = " + name);
        }
    ​
    }

SpringDataRedis的序列化方式

RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认采用JDK序列化,得到的结果是这样的:

127.0.0.1:6379> set name Jack
OK
127.0.0.1:6379> get name
"Jack"
127.0.0.1:6379> KEYS *
 1) "test:user:3"
 2) "test:product:2"
 3) "k3"
 4) "test:user:2"
 5) "k2"
 6) "users"
 7) "class1"
 8) "ZSfs"
 9) "test:product:1"
10) "LSfs"
11) "name"
12) "s1"
13) "\xac\xed\x00\x05t\x00\x04name" # 新建的name在这
14) "test:user:4"
15) "k1"
16) "test:user:1"

它没有覆盖原有的name,而是新建了另外一个带前缀的name

缺点:

  • 可读性差

  • 内存占用大

我们可以自定义RedisTemplate的序列化方式,新建配置类,代码如下:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
        // 创建RedisTemplate
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置key的序列化
        template.setKeySerializer(RedisSerializer.string());            // 一般key都是String类型
        template.setHashKeySerializer(RedisSerializer.string());        // 所以设置为专门处理String的序列化工具
        // 设置Value的序列化
        template.setValueSerializer(jsonRedisSerializer);       // value可以是自定义对象
        template.setHashValueSerializer(jsonRedisSerializer);   // 设置为专门json的更合适
        // 返回
        return template;
    }
}

修改注入代码:

@Autowired
private RedisTemplate<String,Object> redisTemplate;

重新运行测试用例,修改成功

PS:如果报错,引入以下依赖:

        <!--Jackson处理依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

这是因为新建SpringBoot项目时,没有引入SpringMVC依赖,缺少了json的处理工具,就需要手动引入json的处理依赖。

另外,也可以自定义实体类来测试,RedisTemplate可以自动完成对象的序列化和反序列化。代码如下:

User.java:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}

在测试类新建测试用例:

@Test
void testSaveUser(){
    // 写入数据
    redisTemplate.opsForValue().set("user:100",new User("womeng",21));
    // 获取数据
    User user = (User) redisTemplate.opsForValue().get("user:100");
    System.out.println("user = " + user);
}

运行测试用例,控制台输出

user = User(name=womeng, age=21)

Redis数据存储:

StringRedisTemplate

在存储实体类的时候,自带json的序列化工具会多存入一条数据,用于自动反序列化,即

"@class": "com.womeng.springbootredisdemo.pojo.User"

这条数据甚至比本身要存数据占用的空间还大,在大量存储数据的时候会造成严重的内存浪费。因此不建议使用默认的JSON序列化工具,推荐统一作String类型处理,手动实现实体类的序列化和反序列化。

Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String。省去了我们自定义RedisTemplate的过程:

@SpringBootTest
class RedisStringTests {
​
    //到如Spring提供的StringRedisTemplate 无需自定义
    @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 ObjectMapper mapper = new ObjectMapper();
​
    @Test
    void testSaveUser() throws JsonProcessingException {
        // 写入数据
        User user = new User("makabaka", 21);
        // 手动序列化
        String json = mapper.writeValueAsString(user);
        stringRedisTemplate.opsForValue().set("user:200",json );
        // 获取数据
        String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
        // 手动反序列化
        User user1 = mapper.readValue(jsonUser, User.class);
        System.out.println("user1 = " + user1);
    }
}

存储结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值