一、NoSQL概述
1. 技术的发展
技术的分类
- 解决功能性的问题:Java、JSP、RDBMS、Tomcat、HTML、Linux、JDBC、Git等等,随着项目不断升级,普通的技术就很难达到预期(用原生搞太浪费时间了),所以需要考虑扩展性的问题(框架的选型等等)。
- 解决扩展性的问题(框架):Struts、Spring、SpringMVC、Hibernate、MyBatis等等
- 解决性能的问题:NoSQL、Java线程、Hadoop、Nginx、MQ(消息队列)、ElasticSearch(分布式全文检索引擎)、Quartz(定时任务)等等
Web1.0时代
Web1.0的时代,数据访问量很有限,用一台高性能的单点服务器可以解决大部分问题。
缺点:只能通过电脑才能访问。
Web2.0时代
随着Web2.0的时代的到来,用户访问量大幅度提升,同时产生了大量的用户数据。加上后来的智能移动设备的普及,所有的互联网平台都面临了巨大的性能挑战。
- web服务器会产生cpu及内存压力。
- 数据库会产生IO压力。
解决CPU及内存压力
解决CPU及内存压力,使用服务器集群方式(分布式),会出现session共享问题。
解决session共享问题:
-
方案1:存储在客户端 cookie中(每次请求都携带Cookie数据进行访问),缺点:不安全、网络负担效率低。
-
方案2:存在文件服务器或者数据库里,缺点:大量的IO效率问题。
-
方案3:session复制(将session复制到多台服务器上),缺点:数据冗余、节点越多浪费越大。
-
方案4:将用户信息存到缓存数据库(NoSQL),优点:它的数据完全存在内存中,读取速度快、数据结构简单。√
解决IO压力
原始RDBMS方案:水平切分、垂直切分、读写分离,缺点:破坏一定的业务逻辑来换取性能。
方案2:用NoSQL做缓存数据库(把一些频繁查询的数据放到缓存数据库中),优点:减少io的读操作。
2. NoSQL
NoSQL概述
NoSQL (全称:Not only SQL),即 “不仅仅是 SQL ”,泛指非关系型数据库。
NoSQL不依赖业务逻辑方式存储,而以简单的 key-value模式存储。好处:大大的增加了数据库的扩展能力。
- 不遵循SQL标准。
- 不支持ACID。
- 远超于SQL的性能。
NoSQL适用场景
- 对数据做高并发的读写。(如 秒杀)
- 海量数据的读写。
- 对数据的高可扩展性。
NoSQL不适用的场景
- 需要事务支持。
- 基于sql的结构化查询存储,处理复杂的关系。
- (用不着sql的和用了sql也不行的情况,请考虑用NoSQL) 。
常见的NoSQL数据库
1、Memcache(键值型数据库)
- 很早出现的NoSql数据库。
- 数据都在内存中,一般不持久化
- 支持简单的key-value模式,支持类型单一。
- 一般是作为缓存数据库辅助持久化的数据库。
2、Redis(键值型数据库)
- 几乎覆盖了Memcached的绝大部分功能。
- 数据都在内存中,支持持久化,主要用作备份恢复。
- 除了支持简单的key-value模式,还支持多种数据结构的存储,比如list、set、hash、zset等。
- 一般是作为缓存数据库辅助持久化的数据库。
3、MongoDB(文档型数据库)
- 高性能、开源、模式自由(schema free)的文档型数据库。
- 数据都在内存中,如果内存不足,把不常用的数据保存到硬盘中。
- 虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能。
- 支持二进制数据及大型对象。
- 可以根据数据的特点替代RDBMS,成为独立的数据库。或者配合RDBMS,存储特定的数据。
二、Redis概述与安装
1. Redis概述
Redis官网:https://redis.io/
Redis中文官网:http://redis.cn/
- Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,Redis可以达到100000+的QPS(每秒内查询次数)。
- 和Memcached类似,Redis存储value支持的类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set–有序集合)和hash(哈希类型)。
- 这些数据类型都支持 push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
- 在此基础上,Redis支持各种不同方式的排序。
- 与memcached一样,为了保证效率,数据都是缓存在内存中。
- 区别的是Redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。
- 并且在此基础上实现了 master-slave(主从) 同步。(集群)
应用场景
1、配合关系型数据库做高速缓存:
- 存放高频次、热门访问的数据,降低数据库IO。
- 分布式架构,做session共享。
2、多样的数据结构存储持久化数据:
- 最新N个数据:通过list实现按自然时间排序的数据。
- 排行榜,TopN:利用zset(有序集合)
- 时效性的数据,比如手机验证码:Expire过期时间。
- 计数器,秒杀:原子性,自增方法INCR、DECR。
- 去除大量数据中的重复数据:利用set集合。
- 构建队列:利用list集合。
- 发布订阅消息系统:pub/sub模式。
- 分布式锁:SETNX lock abc。
- 分布式ID:INCR userid。
2.Redis的下载与安装
Windows版下载地址:https://github.com/microsoftarchive/redis/releases
Linux版下载地址: https://download.redis.io/releases/
2.1 在Windows系统上安装Redis
Windwx版Redis-x64-3.2.100下载地址:https://github.com/microsoftarchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.zip
Windows安装Redis操作步骤如下:
1、下载Windwx版的Redis安装包。
下载完毕得到压缩包:(大概5M左右)
2、将Redis解压到自己的环境目录即可:
3、启动Redis服务、连接Redis客户端。
windows上除了双击exe可执行程序,也可以通过命令行方式启动Redis服务,以及连接Redis客户端。
(1)启动Redis服务:(启动成功后,这个黑窗口就不要关掉了,保持后台运行)
在Redis目录下,开启cmd窗口执行如下命令:(指定配置文件启动)
redis-server.exe redis.windows.conf
(2)连接Redis客户端
在Redis的bin目录下,开启cmd窗口执行如下命令:
# 连接本地的Redis客户端
redis-cli.exe
# 连接其他电脑上的redis
redis-cli.exe -h ip地址 -p 端口号 -a 密码
# 例如 通过ip和端口号连接本地redis
redis-cli.exe -h ip 127.0.0.1 -p 6379
2.2 在Linux系统上安装Redis
下载地址:https://download.redis.io/releases/redis-4.0.0.tar.gz
1、将下载好的文件上传Linux服务器的 /soft
目录下。
或者也可以使用wget在线下载:
wget https://download.redis.io/releases/redis-4.0.0.tar.gz
2、安装gcc编译器(因为Redis是用C 语言编写的,需要c语言环境)
yum install -y gcc-c++
测试gcc是否安装成功:
gcc --version
3、将Redis文件解压到指定目录下。
cd /soft
tar -xvf redis-4.0.0.tar.gz -C /usr/local/
4、进入/usr/local/redis目录,然后输入make命令进行编译Redis。
cd /usr/local/redis-4.0.0
make
(如果没有准备好C语言编译环境,make会报错—Jemalloc/jemalloc.h
:没有那个文件。
解决方案:先查看gcc是否安装成功,然后 运行 make distclean
(清除编译文件),最后再进行make操作。)
4、安装Redis,设置前缀,把Redis里面的东东统一放到指定目录下:
# 安装到指定路径下
make PREFIX=/usr/local/redis-4.0.0 install
# make install
# 如果直接执行make install命令他会把redis里的文件默认安装到/usr/local/bin目录下
-
redis-benchmark :性能测试工具,可以在自己本子运行,看看自己本子性能如何。
-
redis-check-aof :修复有问题的AOF文件。(持久化里面的问题)
-
redis-check-dump:修复有问题的dump.rdb文件。
-
redis-sentinel :Redis集群使用。
-
redis-server:Redis服务器启动命令。
-
redis-cli :客户端,操作入口。
5、把redis安装目录的redis.conf文件拷贝到bin目录中:(通过这个配置文件让redis后台启动)
cp redis.cong bin/
6、进入bin目录,修改redis.conf文件:
① 设置Redis后台方式启动,只需把daemonize
配置项改为yes即可。(默认是no,霸屏方式启动)
daemonize yes # daemonize 守护进程方式
② Redis的服务默认只允许通过127.0.0.1这个网卡访问,如果允许其他机器也能连接Linux的Redis服务,那么需要修改bind 127.0.0.1 你自己的linux机器的ip地址
, 这样其他电脑就可以通过指定的网卡连接redis啦。🙂
bind 127.0.0.1 192.168.203.104(或者公网ip,多个之间用空格隔开)
③ 可选操作,Reids默认是没有密码的,如果需要设置密码,将配置文件中的 # requirepass foobared
配置项取消注释(默认是注释状态)。默认密码为foobared (食人族),可以根据情况自己指定。
requirepass 123456 # 设置redis密码为123456,默认密码是foobared
:wq保存退出。
7、启动Redis的服务。
./redis-server redis.conf
8、通过客户端连接Redis。(在redis的bin目录下执行)
# 没有密码方式连接
./redis-cli
# 指定ip、端口、密码方式连接
# ./redis-cli -h 127.0.0.1 -p 端口号 -a 密码
./redis-cli -h 127.0.0.1 -p 6379 -a 123456
9、关闭Redis服务。
# 方式1、在redis客户端(redis-cli)输入 shutdown
# 方式2、结束进程 kill -9 进程号
3. Redis快速入门
# Redis快速入门
127.0.0.1:6379> set hello 500 #添加数据 kv键值对
OK
127.0.0.1:6379> get hello #获取key的值
"500"
127.0.0.1:6379> del hello #删除数据
(integer) 1
127.0.0.1:6379> get hello
(nil) # 当key不存在时,返回nil
127.0.0.1:6379>
4. Redis相关知识
1、端口号6379的来源?
- Alessia Merz,Merz对应的9键输入法就是6379。
2、Redis中它默认提供了16个数据库(例如MySQL自带的系统库),类似数组的下标从0开始,初始默认使用0号库,所有库的密码都是相同的。
-
select <dbid>
: 切换数据库。 例如:select 1 切换到1号库。 -
dbsize
:查看当前数据库中key的数量。 -
flushall
:通杀全部库。(清空所有数据库的所有key)
3、Redis是单线程+多路IO复用技术
- 多路复用是指使用一个线程来检查多个文件描述符( Socket )的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)。
4、Memcache: 多线程 + 锁。
5、所谓原子操作是指不会被线程调度机制打断的操作;( 要么都成功才会成功,有一个失败就都失败)
5. Redis可视化工具
Redis可视化工具有:RedisDesktopManager、Another Redis Desktop Manager、Redis Insight、RedisPlus等等。
AnotherRedisDesktopManager下载地址:https://github.com/qishibo/AnotherRedisDesktopManager/releases
Windows版AnotherRedisDesktopManager下载地址:https://github.com/qishibo/AnotherRedisDesktopManager/releases/download/v1.4.8/Another-Redis-Desktop-Manager.1.4.8.exe
下载完毕后会出现这个Another-Redis-Desktop-Manager.1.4.8.exe安装包,安装步骤也非常简单😎😎😎
AnotherRedisDesktopManager使用:
三、Redis数据类型
数据都是以 key-value 的形式存储,其中key是字符串类型,value有5种常用的数据类型:
- 字符串 string、哈希 hash (map) 、列表 list (有序)、集合 set (无序,唯一) 、有序集合zset(sorted set)。
5种常用的数据类型详解:
-
字符串(string):普通字符串,最最常用。
-
哈希(hash):适合存储对象, 常用。
-
列表(list):按照插入顺序排序,可以有重复元素。
-
集合(set):无序集合,没有重复元素。
-
有序集合zset(sorted):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素。
# 面试题:Redis的value有哪几种类型?
有string、hash、list、set、zset这5中类型,常用的是string和hash。
四、Redis常用命令
可以在Redis中文网查看命令:https://www.redis.net.cn/order/
1. 字符串string操作命令
Redis中字符串类型常用命令:
-
set key value
:设置指定key的值。 -
get key
:获取指定key的值。 -
setex key seconds value
:设置指定key的值,并将key的过期时间设为 seconds 秒。 (到指定的秒数这个key就销毁了)- ex,expire:失效、终止。(失效性)
- 应用场景:手机验证码、在有效时间内支付。
-
ttl key
:查看key的剩余时间,以秒为单位(返回值:-2表示不存在、-1表示永不过时的,整数表示剩余时间)。 -
setnx key value
:只有在 key不存在时设置 key 的值。(应用场景:分布式锁)。 -
incr key
:让key的值自增。(让key的value值自增+1)-
incr,ncrement:增长。
-
应用场景:可以作为分布式id,往数据库中新增记录时候,可以让它维护主键。
-
MySQL中每张表存储数据是有限的(最多能存一千万条数据),当数据超过一千万条,就需要进行拆分。
- 将该表拆分成多个表 table_01,table_02,table_03…
- 如果用数据库主键自增,每张表默认都是从1开始,就会出现多张表有相同的id,就不能表示唯一表示。
- 可以使用其他方式维护主键,例如 redis中incr,插入一条数据的时候先获取下redis中指定key的值,获取好了之后再将它的值自增。
-
或者用雪花算法做分布式id。
-
练习:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> setex code 20 1234 # 设置键值对kv,为key设置过期时间20秒,过期自动销毁
OK
127.0.0.1:6379> get code # 获取key的值
"1234"
127.0.0.1:6379> ttl code # 查看key的剩余时间
(integer) 14 #剩余14秒
127.0.0.1:6379> ttl code
(integer) -2 # value失效
127.0.0.1:6379> set hello 403 # 如果有这个key存在就覆盖key的值,没有则创建
OK
127.0.0.1:6379> get hello
"403"
127.0.0.1:6379> setnx lock abc #只有在key不存在时设置key的值
(integer) 1
127.0.0.1:6379> get lock
"abc"
127.0.0.1:6379> setnx lock 123
(integer) 0 # 返回值为0:表示这个key有人在用
127.0.0.1:6379> get lock
"abc"
127.0.0.1:6379> set userid 1
OK
127.0.0.1:6379> get userid
"1"
127.0.0.1:6379> incr userid # 自增,每执行一次,key的值都+1
(integer) 2 #自增后的value值为2
127.0.0.1:6379> get userid
"2"
127.0.0.1:6379> incr userid
(integer) 3
127.0.0.1:6379> get userid
"3"
2. 哈希hash操作命令
Redis中hash是一个string类型的 field 和 value 的映射表(value是一个map集合),hash特别适合用于存储对象。
常用命令有:
hset key field value
:将哈希表 key 中的字段 field 的值设为 value。hget key field
:获取存储在哈希表中指定字段的值。hdel key field
: 删除存储在哈希表中的指定字段。hkeys key
: 获取哈希表中所有字段。hvals key
: 获取哈希表中所有值。hgetall key
: 获取在哈希表中指定 key 的所有字段和值。
练习:
127.0.0.1:6379> hset user1 username tom # 将哈希表user1中的字段username的值设为tom
(integer) 1
127.0.0.1:6379> hset user1 age 20
(integer) 1
127.0.0.1:6379> hget user1 age # 获取存储在哈希表中指定字段的值
"20"
127.0.0.1:6379> hget user1 username
"tom"
127.0.0.1:6379> hkeys user1 # 获取哈希表中所有字段
1) "username"
2) "age"
127.0.0.1:6379> hvals user1 # 获取哈希表中所有值
1) "tom"
2) "20"
127.0.0.1:6379> hdel user1 age # 删除存储在哈希表中的age字段
(integer) 1
127.0.0.1:6379> hgetall user1 # 获取在哈希表中指定key的所有字段和值
1) "username"
2) "tom"
127.0.0.1:6379>
存储key相同的数据。
3. 列表list操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:(push插入、pop弹出)
- LPUSH key value1 [value2] :将一个或多个值插入到列表头部。
- RPUSH key value1 [value2] : 将一个或多个值插入到列表尾部。
- LRANGE key startIndex stopIndex :获取列表指定范围内的元素。
- RPOP key : 移除并获取列表最后一个元素。
- LPOP key : 移除并获取列表第一个元素。
- LLEN key :获取列表长度。
- BRPOP key1 [key2 ] timeout :移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时(秒)或发现可弹出元素为止。
练习:
> 127.0.0.1@6379 connected!
> lpush list1 a b c d # 压栈
4
> lrange list1 0 3 # 获取所有元素
d
c
b
a
> lrange list1 0 -1 # 获取所有元素
d
c
b
a
> rpop list1 # 弹出最后一个
a
> rpop list1
b
> lpush list1 e
3
> lrange list1 0 -1
e
d
c
> brpop list1 5 # 延迟弹出
list1
c
> brpop list1 5
list1
d
> llen list1 # 获取列表长度
1
可以把list当队列使用。从左面插入。(队列:先进先出)
4. 集合set操作命令
Redis中set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:
- SADD key member1 [member2] :向集合添加一个或多个成员。
- SMEMBERS key :返回集合中的所有成员。
- SCARD key :获取集合的长度。
- SINTER key1 [key2] :返回给定所有集合的交集。
- SUNION key1 [key2] :返回所有给定集合的并集。
- SDIFF key1 [key2] :返回给定所有集合的差集。
- SREM key member1 [member2] :移除集合中一个或多个成员。
set集合基础操作:
127.0.0.1:6379> sadd set1 a b c
(integer) 3
127.0.0.1:6379> sadd set1 b c d
(integer) 1 # 成功插入一个
127.0.0.1:6379> scard set1 # 获取集合的长度
(integer) 4
127.0.0.1:6379> smembers set1 # 插入成员是无序的
1) "d"
2) "a"
3) "c"
4) "b"
127.0.0.1:6379> srem set1 c # 从集合中移除成员c
(integer) 1 # 移除成功
127.0.0.1:6379> smembers set1 # 返回集合中的所有成员
1) "d"
2) "a"
3) "b"
set集合运算:(交集、并集、差集)
>sadd set2 a b c # 向set2集合插入多个成员
>sadd set3 b c d # 向set3集合插入多个成员
>sinter set2 set3 # sinter取交集 b c
>sunion set2 set3 # sunion取并集 a b c d
>sdiff set2 set3 # sdiff取差集,在set2集合有 在set3集合中没有的成员: a
>sdiff set3 set2 # sdiff取差集,在set3集合有 在set2集合中没有的成员: d
集合运算是比较强大的: 比如toni购买了升降桌,电竞椅,显示器;rose买了显示器,扫地机器人。
如果用mysql去找他们交集商品的话会非常麻烦,但是用redis的set集合就轻而易举了。
>sadd set2 a b c
>sadd set3 b c d
>sinterstore set4 set2 set3 # 将set2、set3的交集部分 存到set4集合中
>smembers set4 # b c
5. 有序集合zset操作命令
zset(Redis sorted set )有序集合是 string 类型元素的集合,且不允许重复的成员。每个元素都会关联一个double类型的分数(score) 。redis正是通过分数来为集合中的成员进行从小到大排序。有序集合的成员是唯一的,但分数却可以重复。
操作有序集合zset的常用命令:
- ZADD key score1 member1 [score2 member2] :向有序集合添加一个或多个成员,或者更新已存在成员的分数。
- ZRANGE key start stop [WITHSCORES] :通过索引区间返回有序集合中指定区间内的成员。(默认从小到大)
- ZINCRBY key increment member :有序集合中对指定成员的分数加上增量 increment。
- ZREM key member [member …] :移除有序集合中的一个或多个成员。(remove)
练习:
127.0.0.1:6379> zadd java1 80 tom 90 rose 59 toni
(integer) 3
127.0.0.1:6379> zrange java1 0 -1
1) "toni"
2) "tom"
3) "rose"
127.0.0.1:6379> zrange java1 0 -1 withscores # 按照score排序(从小到大)
1) "toni"
2) "59"
3) "tom"
4) "80"
5) "rose"
6) "90"
127.0.0.1:6379> zadd java1 99 toni
(integer) 0
127.0.0.1:6379> zrange java1 0 -1 withscores
1) "tom"
2) "80"
3) "rose"
4) "90"
5) "toni"
6) "99"
127.0.0.1:6379> zincrby java1 1 toni # 给toni成绩加一分
"100"
127.0.0.1:6379> zrange java1 0 -1 withscores #反转
1) "tom"
2) "80"
3) "rose"
4) "90"
5) "toni"
6) "100"
zrevrank java1 0 -1 withscores
应用场景:排名。
6. 通用命令
Redis中的通用命令,主要是针对key进行操作的相关命令:
- KEYS pattern :查找所有符合给定模式( pattern)的 key,支持通配符* 。
- EXISTS key :检查给定 key 是否存在。
- TYPE key :返回 key 所储存的值的类型。(key的value有5种类型)
- TTL key :返回给定 key 的剩余生存时间(TTL,time to live),以秒为单位。
- 返回值-2:不存在。
- 返回值-1:永不过时。
- 正整数:剩余时间。
- DEL key 该命令用于在 key 存在是删除 key。
练习:
# 查看以set开头的key
keys set*
# 查看以k结尾的key
keys *k
# 查看所有key
keys *
# 查看lock存储值的类型
type lock
# 查看java1存储值的类型
type java1
# 检查java1是否存在
exists java1
# 检查java2是否存在
exists java2 # 不存在则返回0
# 删除key
del java1
五、在Java中操作Redis
如果想用Java操作Redis,就需要使用Redis的Java版本客户端,就像我们之前使用JDBC操作MySQL数据库一样。
Redis的Java客户端很多,官方推荐的有三种:
- Jedis
- Lettuce
- Redisson
Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis
,在SpringBoot项目中还提供了对应的Starter,即 spring-boot-starter-data-redis
。
1. Jedis
Jedis是Redis的Java版本客户端实现。
导入Jedis相关依赖:
<dependencies>
<!--jedis:java操作redis的工具-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
使用Jedis操作Redis 的步骤:
- 获取连接
- 执行操作
- 关闭连接
示例代码:
package com.baidou.test;
import org.junit.Test;
import redis.clients.jedis.Jedis;
/**
* 使用Jedis客户端操作Redis
*
* @author 白豆五
* @version 2023/1/9
* @since JDK8
*/
public class JedisDemo {
@Test
public void testHello() {
// 1.获取连接
Jedis jedis = new Jedis("127.0.0.1", 6379); //host,prot
// 2.操作redis
jedis.set("dog", "旺财");
String dog = jedis.get("dog");
System.out.println(dog);
jedis.del("dog");
System.out.println(jedis.exists("dog"));
// 3.释放资源
jedis.close();
}
}
// Jedis里面有数据库连接池
2. Spring Data Redis
Spring Data Redis 是Spring的一部分,提供了在Spring应用中通过简单的配置就可以访问Redis服务,对Redis底层开发包进行了高度封装。在Spring项目中,可以使用Spring Data Redis来简化Redis操作。(上一个东东也挺好用的)
Spring Data Redis官网:https://spring.io/projects/spring-data-redis
SpringDataRedis的maven坐标:🌙
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.4.8</version>
</dependency>
SpringBoot提供了对应的Starter,maven坐标:🚗
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Spring Data Redis中提供了一个高度封装的类:RedisTemplate,针对客户端中大量api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:😊😊😊
- ValueOperations:简单K-V操作。
- SetOperations:set类型数据操作。
- ZSetOperations:zset类型数据操作。
- HashOperations:针对hash类型的数据操作。
- ListOperations:针对list类型的数据操作。
使用SpringBoot操作Redis步骤如下:
1、创建一个maven工程,在pom中导入一些依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baidou</groupId>
<artifactId>springdataredis_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--jackson:配置json序列化器要用这个-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
<!--连接池依赖包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
</project>
2、编写启动类
package com.baidou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RedisApp {
public static void main(String[] args) {
SpringApplication.run(RedisApp.class, args);
}
}
3、配置pplication.yml
spring:
redis:
database: 0 # redis默认有16个数据库, 设置操作数据库的索引
# host: 127.0.0.1
# port: 6379
# password: #若有密码需要配置
# 连接池配置,默认不使用连接池,只有配置了才会使用,且用的是lettuce的连接池
lettuce:
pool:
max-active: 8 #连接池最大的连接数
max-idle: 4 #连接池最大空闲连接数
min-idle: 1 #连接池最小空闲连接数
max-wait: 100ms #获取链接的最大等待时间
测试:
@SpringBootTest
public class DataRedisTest {
@Autowired
RedisTemplate redisTemplate;
/**
* 操作简单的kv
*/
@Test
public void testKeyValue() {
// 1、获取操作对象
ValueOperations valueOperations = redisTemplate.opsForValue();
// 2、调用方法
valueOperations.set("data","redis");
System.out.println(valueOperations.get("data"));
}
}
Spring Boot 框架会自动装配 RedisTemplate 对象,但是默认的key序列化器为JdkSerializationRedisSerializer,导致我们存到Redis中后的数据和原始数据有差别。(不方便阅读)
4、编写配置类(修改序列化器)
SpringDataRedis默认帮我们创建的RedisTemplate对象是使用jdk的序列号器帮我们键与值存储到redis中,而jdk的序列号器对键与值是采用二进制的方式存储的,所以我们会看到乱码的情况。如果我们需要看得懂,那么需要修改redistemplate使用的序列器才行。
StringRedisSerializer
:String的序列化策略,用于将值序列化成字符串类型。GenericJackson2JsonRedisSerializer
:JSON的序列化策略,用于对象与JSON字符串之间的转换.JdkSerializationRedisSerializer
:默认的序列化策略,JDK的序列化策略,以字节的方式进行序列化和反序列化,用于无需类型转换的场景。
package com.baidou.config;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author 白豆五
* @version 2023/1/9
* @since JDK8
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
//设置key的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化方式
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//设置hashkey的序列化方式
redisTemplate.setHashKeySerializer(RedisSerializer.string());
//设置hashvalue的序列化方式
redisTemplate.setHashValueSerializer(RedisSerializer.json());
return redisTemplate;
}
}
测试:
5、操作Redis
@SpringBootTest
public class DataRedisTest {
@Autowired
RedisTemplate redisTemplate;
@Test
public void testValue() {
// 获取操作对象
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("hello","spring data redis");
Object value = valueOperations.get("hello");
System.out.println(value);
HashMap<String, Object> map = new HashMap<>();
map.put("username","tom");
map.put("age",18);
valueOperations.set("mm",map);
value = valueOperations.get("mm");
System.out.println(value);
//设置过期时间
valueOperations.set("hi1","我1分钟过期", Duration.ofMinutes(1));
valueOperations.set("hi2","我1分钟过期", 1, TimeUnit.MINUTES);
//setnx
Boolean flag = valueOperations.setIfAbsent("id", 1);
System.out.println(flag);//true
flag = valueOperations.setIfAbsent("id", 1);
System.out.println(flag);//false
}
@Test
public void testHash() {
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("user1","username","tom");
hashOperations.put("user1","age",18);
Object username = hashOperations.get("user1", "username");
System.out.println(username);
Set keySet = hashOperations.keys("user1");
System.out.println(keySet);
List valueList = hashOperations.values("user1");
System.out.println(valueList);
}
@Test
public void testList() {
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPushAll("list1","a","b","c");
Long size = listOperations.size("list1");
System.out.println(size);//3
List list = listOperations.range("list1", 0, -1);
System.out.println(list);//cba
Object element = listOperations.rightPop("list1");
System.out.println(element);//a
}
@Test
public void testSet() {
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("set1","a","b","c");
Set set = setOperations.members("set1");
System.out.println(set);
Long size = setOperations.size("set1");
System.out.println(size);//3
setOperations.remove("set1", "c");
size = setOperations.size("set1");
System.out.println(size);//2
}
@Test
public void testZset() {
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
zSetOperations.add("java","tom",88);
zSetOperations.add("java","jack",69);
zSetOperations.add("java","rose",100);
//获取某个元素的得分
Double tomScore = zSetOperations.score("java", "tom");
System.out.println(tomScore);//88
//将tom的分数加10分
zSetOperations.incrementScore("java","tom",10);
tomScore = zSetOperations.score("java", "tom");
System.out.println(tomScore);//98
System.out.println("---------");
Set set = zSetOperations.range("java", 0, -1);
for (Object member : set) {
//获取每个元素的得分
System.out.println(member+":"+zSetOperations.score("java", member));
}
}
//通用命令的操作
@Test
public void testCommons() {
//判断元素是否存在
Boolean result = redisTemplate.hasKey("user1");
System.out.println(result);
//判断元素的类型
DataType type = redisTemplate.type("166java");
System.out.println(type);
//删除元素
Boolean flag = redisTemplate.delete("166java");
System.out.println(flag);
//keys 通配符
Set set = redisTemplate.keys("li*");
System.out.println(set);
}
}