文章目录

  • 1. 概述
    • 1.1 redis的作用
    • 1.2 redis简介
    • 1.3 redis基本操作
      • 1.3.1 功能性命令
        • 1.3.1.1 数据添加
        • 1.3.1.2 数据查询
        • 1.3.1.3 数据删除
      • 1.3.2 清屏
      • 1.3.3 帮助
      • 1.3.4 退出
  • 2. redis数据类型
    • 2.1 redis常用于
    • 2.2 string类型
      • 2.2.1 添加/修改/获取多个数据
      • 2.2.2 获取字符串长度
      • 2.2.3 append
      • 2.2.4 拓展操作
        • 2.2.4.1 分布式ID
        • 2.2.4.2 时效性操作
      • 2.2.5 string类型操作注意事项
      • 2.2.6 string应用场景
    • 2.3 hash : HashMap
      • 2.3.1 hash基本操作
      • 2.3.2 hash拓展操作
      • 2.3.3 hash注意事项
      • 2.3.4 hash应用场景
        • 2.3.4.1 电商网站购物车设计与实现
        • 2.3.4.2 抢购,限购,限量,激活码
    • 2.4 list : LinkedList
    • 2.5 set : HashSet
      • 2.5.1 常见命令有:
    • 2.6 sorted_set : TreeSet
      • 2.6.1 常见命令
  • 3. redis通用指令
    • 3.1 redis选择数据库
  • 4. Redis的Java客户端
    • 4.1 概述
    • 4.2 Jedis
      • 4.2.1 基础开发步骤
      • 4.2.2 优化:工具类与连接池
    • 4.3 Spring Data Redis
      • 4.3.1 使用姿势
      • 4.3.2 SpringDataRedis的序列化方式
      • 4.3.3 其他数据类型的操作

1. 概述

1.1 redis的作用

当存在海量用户与高并发时

关系型数据库:

  • 性能瓶颈:磁盘IO性能低下

  • 扩展瓶颈:数据关系复杂,扩展性差,不便于大规模集群

解决思路:

  • 降低磁盘IO次数,越低越好—— 内存存储

  • 去除数据间关系,越简单越好 —— 不存储关系,仅存储数据

而这就是NoSQL: not only SQL, 泛指非关系型的数据库, 作为关系型数据库的补充

作用:应对基于海量用户和海量数据前提下的数据处理问题。

常见 Nosql 数据库:

  • Redis
  • memcache
  • HBase
  • MongoDB

特征:

  • 可扩容,可伸缩
  • 大数据量下高性能
  • 灵活的数据模型
  • 高可用

示例:

使用redis解决电商场景下的高并发:

1.2 redis简介

概念:Redis (REmote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库

特征

  1. 数据间没有必然的关联关系

  2. 内部采用单线程机制进行工作

  3. 高性能。官方提供的测试数据,50个并发执行100000 个请求,读的速度是110000 次/s,写的速度是81000次/s。

  4. 多数据类型支持

    字符串类型 string

    列表类型 list

    散列类型 hash

    集合类型 set

    有序集合类型 sorted_set

  5. 持久化支持。可以进行数据灾难恢复

应用:

  • 为热点数据加速查询(主要场景),如热点商品、热点新闻、热点资讯、推广类等高访问量信息等

  • 任务队列,如秒杀、抢购、购票排队等

  • 即时信息查询,如各大排行榜、各类网站访问统计、公交到站信息、在线人数信息(聊天室、网站)、设备信号等

  • 时效性信息控制,如验证码控制、投票控制等

  • 分布式数据共享,如分布式集群架构中的 session 分离

  • 消息队列

  • 分布式锁

核心文件:

redis端口:6379

PID:随机生成, 每启动一个redis相当于启动一个redis对象, PID就是这个实例对象的ID, 也就是进程ID

Windows使用redis:

​ 首先打开redis服务端, 相当于启动服务器, 再打开redis客户端, 即命令行窗口相当于连接你的服务端, 接着就可以敲指令了

​ 注意:此时你在redis中存入数据, 只要服务端未关闭则不论客户端开关几次数据都还在, 但是当服务端关闭之后再次打开就没有数据了, 证明redis将数据存到内存中

1.3 redis基本操作

redis是命令行模式的工具, 而命令行模式工具不外乎以下四种命令:

  • 功能性命令
  • 清除屏幕信息
  • 帮助信息查阅
  • 退出命令

1.3.1 功能性命令

1.3.1.1 数据添加

  • 功能:设置 key,value 数据

  • 命令 :

set key value
  • 示例:
set age 20

1.3.1.2 数据查询

  • 功能:根据 key 查询对应的 value,如果不存在,返回空(nil)
  • 命令:
get key
  • 示例:
get age

1.3.1.3 数据删除

  • 功能:根据key删除对应键值对数据
  • 命令:
del key

1.3.2 清屏

  • 功能:清除屏幕中的信息
  • 命令:
clear

1.3.3 帮助

  • 功能:获取命令的帮助文档或者组中所有命令信息名称
  • 命令:
help 命令名称如set
help @组名

1.3.4 退出

  • 功能:退出客户端
  • 命令
quit
exit
<ESC>

2. redis数据类型

2.1 redis常用于

作为缓存使用:

1)原始业务功能设计

  • 秒杀
  • 618活动
  • 双11活动
  • 排队购票

2)运营平台监控到的突发高频访问数据

  • 突发时政要闻,被强势关注围观
  • 文娱新闻

3)高频、复杂的统计数据

  • 在线人数

  • 投票排行榜

附加功能

1)系统功能优化或升级

  • 单服务器升级集群
  • Session 管理
  • Token 管理

因此redis提供了五种数据类型(每种都可以对应于java中的一种类型,但不完全类同):

  • string : String
  • hash : HashMap
  • list : LinkedList
  • set : HashSet
  • sorted_set : TreeSet

2.2 string类型

首先说一下redis存储数据的格式:

  • redis 自身是一个 Map,其中所有的数据都是采用 key : value 的形式存储

  • 数据类型指的是存储的数据的类型,也就是 value 部分的类型,key 部分永远都是字符串

string类型介绍:

  • 存储的数据:单个数据,最简单的数据存储类型,也是最常用的数据存储类型

  • 存储数据的格式:一个存储空间保存一个数据

  • 存储内容:通常使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用

除了上述三个命令之外, string还提供了四个基本命令:

2.2.1 添加/修改/获取多个数据

  • 添加/修改多个:
mset key1 value1 key2 value2 ....
  • 获取多个:
mget key1 key2 ...

注意:

​ 对于选择使用多数据操作和单数据操作只需要衡量执行速度即可, 例如多条数据使用单操作, 不仅执行过程长了, 发请求与响应也变多了, 但如果使用多数据操作, 则只有执行过程变长, 请求和响应只有一次

​ 但也不可以一次执行几十几百万数据的操作, 不太现实, 可以拆分为多次执行多数据操作

2.2.2 获取字符串长度

strlen key

2.2.3 append

  • 追加信息到原始信息后部(如果原始信息存在就追加,否则新建)
append key value

2.2.4 拓展操作

2.2.4.1 分布式ID

大型企业级应用中,分表操作是基本操作,使用多张表存储同类型数据,但是对应的主键 id 必须保证统一性,不能重复。Oracle 数据库具有 sequence 设定,可以解决该问题,但是 MySQL数据库并不具有类似的机制, 此时就可以使用redis来解决这种分布式的ID问题: 使用redis控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性, 并且适用于所有数据库, 且支持数据库集群

解决方案:

  • 设置数值数据增加指定范围的值(默认1, 可以为负数)
incr key
incrby key increment
incrbyfloat key increment
  • 设置数值数据减少指定范围的值
decr key
decrby key increment

string数值操作注意点:

  • string在redis内部存储默认就是一个字符串,当遇到增减类操作incr,decr时会转成数值型进行计算。

  • redis所有的操作都是原子性的(无关联无事务),即采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响,

    也由于原子性, 我们可以使用redis的INCR,INCRBY与DESR等来完成原子计数, 例如三个客户端同时读取一个数的值并进行+1, 那么最后这个值就一定被+3(也就是说不会因为多线程而最终只+1), 可以利用redis的这个特性来实现业务上的统计计数需求

  • 注意:按数值进行操作的数据,如果原始数据不能转成数值,或超越了redis 数值上限范围,将报错(可以在代码中利用这一点做限制,在catch中做处理)。

    上限: 9223372036854775807(java中long型数据最大值,Long.MAX_VALUE)

2.2.4.2 时效性操作

例如投票操作控制每个微信号多久投一次, 控制热门商品,热点新闻时效性等等

redis解决方案

  • 设置数据具有指定的生命周期(秒与毫秒)
setex key seconds value
psetex key milliseconds value
  • redis 控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作

2.2.5 string类型操作注意事项

  1. 当key一致时, 后存储覆盖前

  2. 数据不存在时返回nil, 相当于null

  3. 有时候返回的数字 表示运行是否成功:

    (integer) 0 → false 失败

    (integer) 1 → true 成功

    有时候表示运行影响结果值:

    (integer) 3 → 3 3个

    (integer) 1 → 1 1个

  4. 数据最大存储量 : 512MB

  5. 数值计算最大范围 : 9223372036854775807(java中long型数据最大值,Long.MAX_VALUE)

  6. key的命名规范 : 表名:主键名:主键值:字段名(解决NoSQL没有表级结构, 因此使用键的层级结构)

    or : 项目名:业务名:类型:id (这里的数据类型不是redis的数据类型, 而是Java的, 例如实体类User)

2.2.6 string应用场景

除了上述分布式ID与失效性信息外还可以用于主页高频访问信息显示控制,例如新浪微博大V主页显示粉丝数与微博数量

redis解决方案(规范):

  • 为大V设定信息是, 以表名:主键名:主键值:字段名(如fans)作为key, 接着在后台设定定时刷新策略即可

    eg : user:userId:044124:fans

    user:userId:3506728370:blogs

  • 在redis中以json格式存储大V用户信息,此时的key为表名:主键名:主键值, value通过{id:1241, fans:43414}的写法来写, 定时刷新(也可以使用hash类型)

2.3 hash : HashMap

上面说的存储大V方式, 怎么看怎么笨重, JSON的直接value合一, 一改必须全改, 不是JSON的太多key了

而hash就可以解决这种问题, 他将key-value中的value也变为一个key-value, 可以理解为套娃(前面说过redis的数据类型仅针对value就是这个原因), 但是hash的key不称之为key, 而叫做field

hash:

  • 新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息(例如刚刚上面的JSON)

  • 需要的存储结构:一个存储空间(value)保存多个键值对数据

  • hash类型:底层使用哈希表结构实现数据存储(哈希表:数组+链表+红黑树)

hash存储结构优化

  • 如果field数量较少,存储结构优化为类数组结构(类键值对)

  • 如果field数量较多,存储结构使用HashMap结构(哈希表:数组+链表+红黑树)

2.3.1 hash基本操作

  • 添加/修改数据
hset key field value
  • 获取数据(field和value一起获取, 单数field, 双数value)
hget key field
hgetall key
  • 删除数据
hdel key field1 [field2]
  • 添加/修改多个数据
hmset key field1 value1 field2 value2 …
  • 获取多个数据(只获取value, 不获取field)
hmget key field1 field2 …
  • 获取哈希表中字段的数量
hlen key
  • 获取哈希表中是否存在指定的字段(返回1或0)
hexists key field

2.3.2 hash拓展操作

  • 获取哈希表中所有的字段名或字段值

    与上面的相比,可以专门一次拿全部field或者value

hkeys key
hvals key
  • 设置指定字段的数值数据增加指定范围的值
hincrby key field increment
hincrbyfloat key field increment
  • 当field有值时不覆盖(先做一次判定再覆盖)
hsetnx key field value

2.3.3 hash注意事项

  • hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象。如果数据未获取到,

    对应的值为(nil)

  • 每个 hash 可以存储 2 32 - 1 个键值对

  • hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性。但hash设计初衷不是为了存

    储大量对象而设计的,切记不可滥用,更不可以将hash作为对象列表使用

  • hgetall 操作可以获取全部属性,如果内部field过多,遍历整体数据效率就很会低,有可能成为数据访问

    瓶颈, 建议用几个拿几个

  • string存储json以读为主,讲究整体性,hash存储json以更新为主

2.3.4 hash应用场景

2.3.4.1 电商网站购物车设计与实现

注意:

  1. 仅分析购物车的redis存储模型,包括:

    添加、浏览、更改数量、删除、清空

  2. 购物车于数据库间持久化同步(不讨论)

  3. 购物车于订单间关系(不讨论)

    提交购物车:读取数据生成订单

    商家临时价格调整:隶属于订单级别

  4. 未登录用户购物车信息存储(不讨论)

    cookie存储

  5. 此处仅讨论购物车中的模型设计

    购物车与数据库间持久化同步、购物车与订单间关系、未登录用户购物车信息存储不进行讨论

分析购物车能不能用hash实现:

在购物车中:

以客户id作为key,每位客户创建一个hash存储结构存储对应的购物车信息

将商品编号作为field,购买数量作为value进行存储

添加商品:追加全新的field与value

浏览:遍历hash

更改数量:自增/自减,设置value值

删除商品:删除field

清空:删除key

我们发现, hash简直仿佛专门为了购物车而生,一切都是刚刚好

但是注意:当前仅仅是将数据存储到了redis中,并没有起到加速的作用,因为商品信息还需要二次查询数据库

此时我们可以将每条购物车中的商品记录保存成两条field,

第一个field专用于保存用户购买数量(仅属于一个用户):

  • 命名格式:商品id:nums,
  • 保存数据:数值

而第二个filed专用于保存购物车中显示的信息,包含文字描述,图片地址,所属商家信息等, 并且通过面向对象思想抽取成一个独立公共的hash, 存放公共信息, 不是独属于一个用户的:

  • 命名格式:商品id:info
  • 保存数据:json

这样子私人的hash数据还变少了速度更快了, 相当于购物车和商品信息俩张表关联,购物车只需要id和数量数据

但是由于redis会覆盖,这样子每个人后买时都会覆盖公共区域的数据又浪费效率,因此可以使用hsetnx, 当field有值时不覆盖

2.3.4.2 抢购,限购,限量,激活码

场景 : 双11活动日,销售手机充值卡的商家对移动、联通、电信的30元、50元、100元商品推出抢购活动,每种商品抢购上限1000张

解决方案

  • 以商家id作为key
  • 将参与抢购的商品id作为field
  • 将参与抢购的商品数量作为对应的value
  • 抢购时使用降值的方式控制产品数量
  • 实际业务中还有超卖等实际问题,这里不做讨论

也就是说redis 应用于抢购,限购类、限量发放优惠卷、激活码等业务的数据存储设计

2.4 list : LinkedList

list:

  • 数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分

    主要用于对顺序有要求的数据,如朋友圈点赞顺序

  • list类型:保存多个数据,底层使用双向链表存储结构实现(但其实并不仅仅是双向链表)

    插入删除快,查询慢,有序(元素进入顺序),元素可以重复

  • string讲究单个数据,hash讲究分类,list讲究多数据与有序

  • 由于list是双向链表,因此可以任何一边进任何一边出, 约定右进左出

常用命令:

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

  • LPOP key [count]:移除并返回列表左侧的count个元素,没有则返回nil

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

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

  • LRANGE key star end:返回一段角标范围内的所有元素, 开始数据索引是0)

  • lindex key index : 获取index索引的数据, 哪边数据索引是0?, lrange索引可以是负

  • BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil, 时间到了获取并移除数据(没值nil)

    即会阻塞,开始等数据,规定时间没有数据返回nil,建议等多个key,但最后只拿一个key(可以拿来模拟任务队列,数据队列,消息队列)

    blpop key1 [key2] timeout

可以看到list命令中带着很多L与R,实际上这些命令将链表看成队列,左边队首右边队尾, L就是向队伍左侧插入数据, 如图:

如何利用List结构模拟一个栈
入口和出口在同一边,即左进左出或者右进右出
如何利用List结构模拟一个队列
入口和出口在不同边,即左进右出或者右进左出
如何利用List结构模拟一个阻塞队列
入口和出口在不同边,左进右出或者右进左出
出队时采用BLPOP或BRPOP

2.5 set : HashSet

set:

​ Redis的Set结构与Java中的HashSet类似(可以看做是一个value为null的HashMap, 因为HashSet本质就是HashMap的实现,只取用了其中的Key值)。因为也是一个hash表,因此具备与HashSet类似的特征:

  • 无序, 顺序与插入顺序无关, 由hash算法计算得到
  • 元素不可重复(实现好友列表)
  • 查找快
  • 支持交集、并集、差集等功能(HashSet没有)

2.5.1 常见命令有:

  • SADD key member … :向set中添加一个或多个元素
  • SREM key member … : 移除set中的指定元素
  • SCARD key: 返回set中元素的个数
  • SISMEMBER key member:判断一个元素是否存在于set中
  • SMEMBERS key:获取set中的所有元素
  • SINTER key1 key2 … :求key1与key2的交集
  • SDIFF key1 key2 … :求key1与key2的差集(key1-key2=key1有key2没有)
  • SUNION key1 key2 …:求key1和key2的并集

2.6 sorted_set : TreeSet

SortedSet:

​ SortedSet是一个可排序的set集合,与Java中的TreeSet(红黑树)有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表, 也就是说我们往SortedSet中插入数据时需要带上一个score属性, 例如得分,这样子就会按得分进行排序了(HashMap的key是我们需要存的数据,value是score,与set不同的就是这个value不是null)

SortedSet具备下列特性:

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

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

2.6.1 常见命令

  • ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
  • ZREM key member:删除sorted set中的一个指定元素
  • ZSCORE key member : 获取sorted set中的指定元素的score值
  • ZRANK key member:获取sorted set 中的指定元素的排名
  • ZCARD key:获取sorted set中的元素个数
  • ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
  • ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值, 是对score的操作
  • ZRANGE key min max:按照score排序后,获取指定排名范围内(0开始)的元素
  • ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
  • ZDIFF、ZINTER、ZUNION:求差集、交集、并集

注意:

  • 所有的排名默认都是升序(低到高),如果要降序则在命令的Z后面添加REV即可,例如ZREVCARD

    排行榜一般都是降序,同时注意+1

  • 对于命令, 不需要死记硬背, 只需要查官网或者help @数据类型查看即可

  • 上面的范围, 与寻常语言语法的左闭右开不同,是一个闭区间

3. redis通用指令

通用指令是任何数据类型都可以使用的指令,常见的有:

  • KEYS:查看符合模板的所有key, 模糊查询,效率不高,并且redis是单线程的,搜索时其他什么事都不能做,不建议在生产环境下使用
KEYS pattern(模板)
// 例如:查询所有key:
KEYS *
// 例如:查询以a开头的所有key:
KEYS a*
  • DEL:删除一个指定的key
DEL key [key...]
  • EXISTS:判断key是否存在
EXISTS key [key...]
  • EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除(可以清理/管理内存或做一些具有时效性限定控制的操作)
EXPIRE key seconds
  • TTL:查看一个KEY的剩余有效期(-1表示永久有效,-2表示已经歇逼了)
TTL key

平常使用时可以通过help [command] 可以查看一个命令的具体用法

对于命令不需要记忆, 需要使用时可以上官网查或者通过help @generic来查看所有通用命令

3.1 redis选择数据库

命令:

SELECT index

切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。
默认使用 0 号数据库

在redis中每个数据库都有属于自己的空间,不必担心之间的key冲突

使用redis的flushdb命令可以清除数据,但是只会清除当前的数据库下的数据,不会影响到其他数据库

redis配置文件中下面的参数来控制数据库总数:

database 16 #默认为16个数据库,可以自己更改

4. Redis的Java客户端

4.1 概述

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

其中Redis对于Java提供的客户端常用的有Jedis, Lettuce与Redisson

Jedis:以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用

**Lettuce:**Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持Redis的哨兵模式、集群模式和管道模式。是Spring默认使用的

**Redisson:**Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、 Semaphore、AtomicLong等强大功能

同时Spring整合了Jedis与Lettuce,形成了Spring Data Redis,同时兼任Jedis与Lettuce

4.2 Jedis

Jedis的官网地址: https://github.com/redis/jedis

4.2.1 基础开发步骤

  1. 导依赖:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>
  1. 在初始化操作时建立redis的连接,设置密码以及选择库:
// 建立连接
Jedis jedis = new Jedis("localhost", 6379);
// 设置密码
jedis.auth("password");
// 选择库
jedis.select(0);
  1. 通过Jedis对象的API操作redis
jedis.set("name", "itheima");
jedis.get("name");
  1. 关闭redis连接
if (jedis != null) {jedis.close();
}

4.2.2 优化:工具类与连接池

Jedis提供了连接池技术:JedisPool, 其中的构造方法:

public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password) {this(poolConfig, host, port, count, (String)null, 0, password);
}

构造方法中的参数(池的连接参数):

poolConfig:连接池配置对象

host:redis服务地址

port:redis服务端口号

timeout:超时时间

password:redis密码

null值是String user

连接池配置对象可以设置的参数(池的配置参数):

maxTotal:最大连接数

minIdle:最小空闲连接(一段时间后没人来连接就变成最小空闲连接,配置0表示没人来连时我准备的连接全部释放)

maxIdle:最大空闲连接(没人来连时我会准备着的连接数)

maxWaitMillis:最长等待时间(ms),(当连接时没有连接可用时用户的等待时长,默认-1无限等待)

使用jedis.properties封装连接参数:

jedis.host=localhost
jedis.port=6379
jedis.password=password
jedis.maxTotal=30
jedis.maxIdle=10
jedis.minIdle=0
jedis.maxWaitMillis=1000

工具类模板:

public class RedisUtil() {private static final JedisPool jedisPool;private static final JedisPoolConfig poolConfig;static{//读取配置文件 获得参数值ResourceBundle rb = ResourceBundle.getBundle("jedis");host = rb.getString("jedis.host");port = Integer.parseInt(rb.getString("jedis.port"));password = rb.getString("jedis.password");maxTotal = Integer.parseInt(rb.getString("jedis.maxTotal"));maxIdle = Integer.parseInt(rb.getString("jedis.maxIdle"));minIdle = Integer.parseInt(rb.getString("jedis.minIdle"));maxWaitMillis = Integer.parseInt(rb.getString("jedis.maxWaitMillis"));poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(maxTotal);poolConfig.setMaxIdle(maxIdle);poolConfig.setMaxIdle(minIdle);poolConfig.setMaxWaitMillis(maxWaitMillis);jedisPool = new JedisPool(poolConfig,host,port, 2000, password);}public static Jedis getJedis(){Jedis jedis = jedisPool.getResource();return jedis;}}

4.3 Spring Data Redis

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis

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

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中, 即调用RedisTemplate的一些方法即可获得专门操作某种数据类型的对象(相较于Jedis全部方法丢到一个对象中,会比较轻量级):

API 返回值类型 说明
redisTemplate.opsForValue() ValueOperations 操作String类型数据
redisTemplate.opsForHash() HashOperations 操作Hash类型数据
redisTemplate.opsForList() ListOperations 操作List类型数据
redisTemplate.opsForSet() SetOperations 操作Set类型数据
redisTemplate.opsForZSet() ZSetOperations 操作SortedSet类型数据
redisTemplate 通用的命令直接通过redisTemplate对象调用

4.3.1 使用姿势

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

  1. 引入依赖
<!--Redis依赖-->
<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>
  1. 配置yml文件
spring:redis:host: localhostport: 6379password: 123321database: 0# 配置连接池配置信息:# 这里选择使用lettuce的连接池# 因为boot默认使用lettuce的连接池,如果使用Jedis还要去再导一个jedis的依赖# 注意:必须手动配置连接池才会生效lettuce:pool:max-active: 8 # 最大连接max-idle: 8 # 最大空闲连接min-idle: 0 # 最小空闲连接max-wait: 100ms # 连接等待时间
  1. 在需要使用的类中自动注入RedisTemplate:
@Autowired
private RedisTemplate redisTemplate;
  1. 使用RedisTemplate
@SpringBootTest
public class RedisTest {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid testString() {// 插入一条string类型的数据redisTemplate.opsForValue().set("name",
"李四");// 读取数据Object name =redisTemplate.opsForValue().get("name");System.out.println("name = " + name);}
}

前面Jedis的get/set方法参数必须传入String,而这里的set/get方法参数可以传入Object类型,体现的Spring提供的各种自动序列化操作

SpringDataRedis的使用步骤:

  1. 引入spring-boot-starter-data-redis依赖

  2. 在application.yml配置Redis信息

  3. 注入RedisTemplate

4.3.2 SpringDataRedis的序列化方式

上面的代码运行之后打开redis的命令行取出数据, 会看见键名前跟着一大串\xAC\x00…, 取出来的东西也和我们存进去的不一样变成了\xAC\x00…

这是因为spring默认做了数据序列化,我们传入的参数被当成Object对象进行序列化

同时spring使用的序列化是JDK默认的ObjectOutputStream进行序列化,将对象转为字节数组

缺点:

  • 可读性差

  • 内存占用较大

RedisTemplate部分源码:

public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {private boolean enableTransactionSupport = false;private boolean exposeConnection = false;private boolean initialized = false;private boolean enableDefaultSerializer = true;@Nullableprivate RedisSerializer<?> defaultSerializer;@Nullableprivate ClassLoader classLoader;@Nullableprivate RedisSerializer keySerializer = null;@Nullableprivate RedisSerializer valueSerializer = null;@Nullableprivate RedisSerializer hashKeySerializer = null;@Nullableprivate RedisSerializer hashValueSerializer = null;private RedisSerializer<String> stringSerializer = RedisSerializer.string();

其中的RedisSerializer等就是序列化工具, RedisSerializer是一个接口, 有一系列的实现类,通过实现类来完成序列化操作, 默认的实现类就是JdkSerializationRedisSerializer

为了改善上述缺点我们可以自定义RedisTemplate的序列化方式, 通常我们会改为这俩个实现类:

StringRedisSerializer : 专门用来处理字符串的, 一般key和hashkey都使用这个进行序列化

GenericJackson2JsonRedisSerializer : 转JSON字符串的序列化工具, value和 hashValue采用 JSON序列化

同时查看源码发现RedisTemplate有俩个泛型,分别对应key和value的类型, 当我们使用上述俩个序列化工具之后就可以将返回的RedisTemplate返回的泛型设置为String和Object

代码如下:

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {// 创建TemplateRedisTemplate<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;}
}

设置完bean之后以后注入RedisTemplate时将泛型加上即可

接着key就会是正常的字符串, value也可以实现JSON与Java对象的互转了

但是!

这种方法为了在反序列化时知道Java对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销:

"@class": "com.othin.pojo.user"

因此实际开发中为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化

为此, Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate序列化的过程, 因此我们只需要将自动注入的对象类型改为StringRedisTemplate, 并在代码中手动完成序列化即可:

@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);//写入一条数据到redisstringRedisTemplate.opsForValue().set("user:200", json);//读取数据String val = stringRedisTemplate.opsForValue().get("user:200");//反序列化User user1 = mapper.readValue(val, User.class);System.out.println("user1 = " + user1);
}

总结:

RedisTemplate的两种序列化实践方案:

方案一:

  1. 自定义RedisTemplate

  2. 修改RedisTemplate的序列化器为

    GenericJackson2JsonRedisSerializer

方案二:(推荐使用,封装一个json的工具类出来就会很方便了)

  1. 使用StringRedisTemplate

  2. 写入Redis时,手动把对象序列化为JSON

  3. 读取Redis时,手动把读取到的JSON反序列化为对象

4.3.3 其他数据类型的操作

注意:

​ 操作其他数据类型时使用的API不一定与redis的命令一致, 例如hash的hset命令被Spring改为了put

01Redis基础篇相关推荐

  1. Python Qt GUI设计:信号与槽的使用方法(基础篇—7)

    目录 1.信号与槽的概念 2.信号与槽的基础函数 2.1.创建信号函数 2.2.连接信号函数 2.3.断开信号函数 2.4.发射信号函数 3.信号和槽的使用方法 3.1.内置信号与槽的使用 3.2.自 ...

  2. Python Qt GUI设计:窗口布局管理方法【强化】(基础篇—6)

    目录 1. 水平布局类(QHBoxLayout) 2.垂直布局类(QVBoxLayout) 3.网格布局类(QGridLayout) 3.1.单一的网络布局 3.2.跨越行.列的网络布局 4.表单布局 ...

  3. Python Qt GUI设计:窗口布局管理方法【基础】(基础篇—5)

    目录 1.布局管理器进行布局 2.容器控件进行布局 3.geometry属性:控件绝对布局 4.sizePolicy属性:微调优化控件布局 Qt Designer提供4种窗口布局方式,分别如下: Ve ...

  4. ES6 你可能不知道的事 – 基础篇

    ES6 你可能不知道的事 – 基础篇 转载 作者:淘宝前端团队(FED)- 化辰 链接:taobaofed.org/blog/2016/07/22/es6-basics/ 序 ES6,或许应该叫 ES ...

  5. python多线程并发_Python进阶记录之基础篇(二十四)

    回顾 在Python进阶记录之基础篇(二十三)中,我们介绍了进程的基本概念以及Python中多进程的基本使用方法.其中,需要重点掌握多进程的创建方法.进程池和进程间的通信.今天我们讲一下Python中 ...

  6. 基础篇9-python基本数据结构-列表

    基础篇9-python基本数据结构-列表 一.列表: 1.有序的集合 2.通过偏移来索引,从而读取数据 3.支持内嵌 a =[[1,2,3],[4,5,6]] 4.可变类型 a[0][1] = 7 二 ...

  7. Linq初级班 Linq To XML体验(基础篇)

    LINQ To XML体验(基础) 这两天开始学习LINQ to XML的知识,我会继续把自己的感想和示例发布给初学者们学习的,一样欢迎高手们多多指点,请勿使用过激语言,针锋相对,我是个初学者,自知还 ...

  8. php 爬虫_Scrapy 爬虫完整案例-基础篇

    1 Scrapy 爬虫完整案例-基础篇 1.1 Scrapy 爬虫案例一 Scrapy 爬虫案例:爬取腾讯网招聘信息 案例步骤: 第一步:创建项目. 在 dos下切换到目录 D:爬虫_scriptsc ...

  9. class括号里的object_Python入门 类class 基础篇

    记住一句话:类是模板,而实例则是根据类创建的对象. 我初学时对类的理解是从类的字面上,可以片面的认为它是一个种类,它是相似特征的抽像,也就是相似的东西,可以把相似特征的事务抽象成一个类.(事务可以是具 ...

最新文章

  1. python脚本设置linux环境变量_Linux环境变量export方法与修改文件方法的区别
  2. HTML SVG 如何下载svg文件,png文件,jpge文件
  3. hive数据仓库摘录和总结
  4. oracle创建数据库总结,oracle创建数据库和用户方法总结
  5. PHP 5.6 Apache配置
  6. php的yii简介,yii
  7. 《编程题》找出数组中出现次数超过一半的数(时间复杂度O(n),空间复杂度为O(1))
  8. 前端样板资源概览及总评
  9. scatter python_python数据可视化(matplotlib、scatter)
  10. 双向链表VS单向链表
  11. 一天一个Java基础——序列化
  12. 用Python一键批量将任意结构的CSV文件导入 SQLite_用 Python 使用 Google Colab?岂止是炫酷...
  13. socket 编程入门教程(五)UDP原理:4、“有连接”的UDP
  14. [置顶] Eclipse显示中文 在线安装教程
  15. unity 安装失败:operation not permitted, mkdir......
  16. 中兴代工移动光猫GM620开启telnet
  17. ClickHouse SAMPLE 采样子句介绍
  18. VirtualBox和Docker安装
  19. android 键盘 定义,自定义全键盘-[Android_YangKe]
  20. Java笔记09——常用类

热门文章

  1. android 设置壁纸,在Android中使用WallpaperManager设置壁纸
  2. Deep High-Resolution Representation Learning for Visual Recognition阅读笔记
  3. 大学计算机原理知识点,四川大学计算机组成原理知识点
  4. 四川大学计算机学院男女生比例,2019年四川各大高校男女比例排行,电子科技大学4比1...
  5. Service Mesh的未来将与Knative和Apahce Whisk等技术和谐共存——采访RedHat的Istio产品经理...
  6. 中国荫罩对准器市场深度研究分析报告
  7. 详解ip地址和mac地址即ARP协议
  8. Redis Cluster集群节点间通信
  9. 私域流量社群公司团队管理KPI考核运营方案制度
  10. VC所有版本一键清除缓存垃圾脚本