01Redis基础篇
文章目录
- 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)数据库
特征:
数据间没有必然的关联关系
内部采用单线程机制进行工作
高性能。官方提供的测试数据,50个并发执行100000 个请求,读的速度是110000 次/s,写的速度是81000次/s。
多数据类型支持
字符串类型 string
列表类型 list
散列类型 hash
集合类型 set
有序集合类型 sorted_set
持久化支持。可以进行数据灾难恢复
应用:
为热点数据加速查询(主要场景),如热点商品、热点新闻、热点资讯、推广类等高访问量信息等
任务队列,如秒杀、抢购、购票排队等
即时信息查询,如各大排行榜、各类网站访问统计、公交到站信息、在线人数信息(聊天室、网站)、设备信号等
时效性信息控制,如验证码控制、投票控制等
分布式数据共享,如分布式集群架构中的 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类型操作注意事项
当key一致时, 后存储覆盖前
数据不存在时返回nil, 相当于null
有时候返回的数字 表示运行是否成功:
(integer) 0 → false 失败
(integer) 1 → true 成功
有时候表示运行影响结果值:
(integer) 3 → 3 3个
(integer) 1 → 1 1个
数据最大存储量 : 512MB
数值计算最大范围 : 9223372036854775807(java中long型数据最大值,Long.MAX_VALUE)
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 电商网站购物车设计与实现
注意:
仅分析购物车的redis存储模型,包括:
添加、浏览、更改数量、删除、清空
购物车于数据库间持久化同步(不讨论)
购物车于订单间关系(不讨论)
提交购物车:读取数据生成订单
商家临时价格调整:隶属于订单级别
未登录用户购物车信息存储(不讨论)
cookie存储
此处仅讨论购物车中的模型设计
购物车与数据库间持久化同步、购物车与订单间关系、未登录用户购物车信息存储不进行讨论
分析购物车能不能用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 基础开发步骤
- 导依赖:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>
- 在初始化操作时建立redis的连接,设置密码以及选择库:
// 建立连接
Jedis jedis = new Jedis("localhost", 6379);
// 设置密码
jedis.auth("password");
// 选择库
jedis.select(0);
- 通过Jedis对象的API操作redis
jedis.set("name", "itheima");
jedis.get("name");
- 关闭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的支持,使用非常简单:
- 引入依赖
<!--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>
- 配置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 # 连接等待时间
- 在需要使用的类中自动注入RedisTemplate:
@Autowired
private RedisTemplate redisTemplate;
- 使用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的使用步骤:
引入spring-boot-starter-data-redis依赖
在application.yml配置Redis信息
注入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的两种序列化实践方案:
方案一:
自定义RedisTemplate
修改RedisTemplate的序列化器为
GenericJackson2JsonRedisSerializer
方案二:(推荐使用,封装一个json的工具类出来就会很方便了)
使用StringRedisTemplate
写入Redis时,手动把对象序列化为JSON
读取Redis时,手动把读取到的JSON反序列化为对象
4.3.3 其他数据类型的操作
注意:
操作其他数据类型时使用的API不一定与redis的命令一致, 例如hash的hset命令被Spring改为了put
01Redis基础篇相关推荐
- Python Qt GUI设计:信号与槽的使用方法(基础篇—7)
目录 1.信号与槽的概念 2.信号与槽的基础函数 2.1.创建信号函数 2.2.连接信号函数 2.3.断开信号函数 2.4.发射信号函数 3.信号和槽的使用方法 3.1.内置信号与槽的使用 3.2.自 ...
- Python Qt GUI设计:窗口布局管理方法【强化】(基础篇—6)
目录 1. 水平布局类(QHBoxLayout) 2.垂直布局类(QVBoxLayout) 3.网格布局类(QGridLayout) 3.1.单一的网络布局 3.2.跨越行.列的网络布局 4.表单布局 ...
- Python Qt GUI设计:窗口布局管理方法【基础】(基础篇—5)
目录 1.布局管理器进行布局 2.容器控件进行布局 3.geometry属性:控件绝对布局 4.sizePolicy属性:微调优化控件布局 Qt Designer提供4种窗口布局方式,分别如下: Ve ...
- ES6 你可能不知道的事 – 基础篇
ES6 你可能不知道的事 – 基础篇 转载 作者:淘宝前端团队(FED)- 化辰 链接:taobaofed.org/blog/2016/07/22/es6-basics/ 序 ES6,或许应该叫 ES ...
- python多线程并发_Python进阶记录之基础篇(二十四)
回顾 在Python进阶记录之基础篇(二十三)中,我们介绍了进程的基本概念以及Python中多进程的基本使用方法.其中,需要重点掌握多进程的创建方法.进程池和进程间的通信.今天我们讲一下Python中 ...
- 基础篇9-python基本数据结构-列表
基础篇9-python基本数据结构-列表 一.列表: 1.有序的集合 2.通过偏移来索引,从而读取数据 3.支持内嵌 a =[[1,2,3],[4,5,6]] 4.可变类型 a[0][1] = 7 二 ...
- Linq初级班 Linq To XML体验(基础篇)
LINQ To XML体验(基础) 这两天开始学习LINQ to XML的知识,我会继续把自己的感想和示例发布给初学者们学习的,一样欢迎高手们多多指点,请勿使用过激语言,针锋相对,我是个初学者,自知还 ...
- php 爬虫_Scrapy 爬虫完整案例-基础篇
1 Scrapy 爬虫完整案例-基础篇 1.1 Scrapy 爬虫案例一 Scrapy 爬虫案例:爬取腾讯网招聘信息 案例步骤: 第一步:创建项目. 在 dos下切换到目录 D:爬虫_scriptsc ...
- class括号里的object_Python入门 类class 基础篇
记住一句话:类是模板,而实例则是根据类创建的对象. 我初学时对类的理解是从类的字面上,可以片面的认为它是一个种类,它是相似特征的抽像,也就是相似的东西,可以把相似特征的事务抽象成一个类.(事务可以是具 ...
最新文章
- python脚本设置linux环境变量_Linux环境变量export方法与修改文件方法的区别
- HTML SVG 如何下载svg文件,png文件,jpge文件
- hive数据仓库摘录和总结
- oracle创建数据库总结,oracle创建数据库和用户方法总结
- PHP 5.6 Apache配置
- php的yii简介,yii
- 《编程题》找出数组中出现次数超过一半的数(时间复杂度O(n),空间复杂度为O(1))
- 前端样板资源概览及总评
- scatter python_python数据可视化(matplotlib、scatter)
- 双向链表VS单向链表
- 一天一个Java基础——序列化
- 用Python一键批量将任意结构的CSV文件导入 SQLite_用 Python 使用 Google Colab?岂止是炫酷...
- socket 编程入门教程(五)UDP原理:4、“有连接”的UDP
- [置顶] Eclipse显示中文 在线安装教程
- unity 安装失败:operation not permitted, mkdir......
- 中兴代工移动光猫GM620开启telnet
- ClickHouse SAMPLE 采样子句介绍
- VirtualBox和Docker安装
- android 键盘 定义,自定义全键盘-[Android_YangKe]
- Java笔记09——常用类
热门文章
- android 设置壁纸,在Android中使用WallpaperManager设置壁纸
- Deep High-Resolution Representation Learning for Visual Recognition阅读笔记
- 大学计算机原理知识点,四川大学计算机组成原理知识点
- 四川大学计算机学院男女生比例,2019年四川各大高校男女比例排行,电子科技大学4比1...
- Service Mesh的未来将与Knative和Apahce Whisk等技术和谐共存——采访RedHat的Istio产品经理...
- 中国荫罩对准器市场深度研究分析报告
- 详解ip地址和mac地址即ARP协议
- Redis Cluster集群节点间通信
- 私域流量社群公司团队管理KPI考核运营方案制度
- VC所有版本一键清除缓存垃圾脚本