Redis
一、Redis入门
1.NoSQL
不仅仅指sql,泛指非关系型数据库。
2.NoSQL的四大分类
2.1键值(key-value)存储数据库
1.说明:
- 这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。
2.特点
- Key/value模型对于IT系统来说的优势在于简单、易部署。
- 但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。
3.相关产品
- Tokyo Cabinet/Tyrant,
- Redis
- SSDB
- Voldemort
- Oracle BDB
2.1列存储数据库
# 1.说明
- 这部分数据库通常是用来应对分布式存储的海量数据。
# 2.特点
- 键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。
# 3.相关产品
- Cassandra、HBase、Riak.
2.3文档型数据库
# 1.说明
- 文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似该类型的数据模型是版本化
的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可 以看作是键值数据库的升级版,允许之间嵌
套键值。而且文档型数据库比键值数据库的查询效率更高
# 2.特点
- 以文档形式存储
# 3.相关产品
- MongoDB、CouchDB、 MongoDb(4.x). 国内也有文档型数据库SequoiaDB,已经开源。
2.4图形(Graph)数据库
# 1.说明
- 图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务
器上。
- NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数
据接口或者查询API。
# 2.特点
# 3.相关产品
- Neo4J、InfoGrid、 Infinite Graph、
3.NoSQL常用技术
- Redis
- MongoDB
4.什么是Redis
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,数据是保存在内存里面的
4.1支持的数据类型
- 字符串类型 String(常用)
- 散列类型 hash 相当于map
- 列表类型 list 相当于list
- 集合类型 set 相当于set
- 有序集合类型 sortedset 相当于treeSet
5.redis的应用场景
- 缓存(数据查询、短连接、新闻内容、商品内容等,(缓存主要是针对增删改不频繁的数据))
- 任务队列(秒杀、抢购、12306等)
- 数据过期处理(可以精确到毫秒、短信验证码)
- 分布式集群架构中的session分离 session存在服务器里面的
- 聊天室的在线好友列表
- 应用排行榜
- 网站访问统计
6.Redis安装
1、环境准备
vmware 15.X+
centos 7.x+
2、下载redis源码包
-https://redis/io/
3、下载完整原码包
redis-4.0.14.tar.gz
4、将下载redis资料包上传到Linux中
5、解压文件
tar -zxvf redis-4.0.14.tar.gz
6、安装gcc
yum install -y gcc
gcc:由c语言开发的编译器工具
7.进入解压缩目录执行如下命令进行源文件编译
cd redis-4.0.14
make
8.编译完成后执行如下命令
make install PREFIX=/usr/local/redis
9.进入/usr/local/redis/bin目录启动redis服务
./redis-server
./redis-server & 后台启动
10.Redis服务端口默认是 6379
11.进入bin目录执行客户端连接操作
./redis-cli –p 6379
12.连接成功出现上面界面连接成功
退出客户端命令:exit
7.配置文件设置
1.进入安装好的redis目录,复制配置文件
cd /usr/local/redis/bin
–这句命令的作用是将redis-4.0.14中的redis.conf文件拷贝到bin目录
cp /root/redis-4.0.14/redis.conf /user/local/redis/bin
2.修改配置文件
# 修改配置文件
vi redis.conf
# 修改端口
port xxxx
# Redis后台启动
修改 daemonize 为 yes
# Redis服务器可以跨网络访问
修改 bind 为 0.0.0.0
# 开启aof持久化,这个可以不做
appendonly yes
3.通过配置文件启动redis服务器
./redis-server redis.conf
4.退出redis服务
1.正常关闭本地redis:redis-cli shutdown,默认关闭本地6379的redis
2.如果正常关闭不了,可以通过杀死线程关闭
ps aux |grep redis 查看当前线程
kill - 9 57928 杀死redis线程
ps aux |grep redis 再查看目标线程是否还在
8.redis数据库相关指令
1.数据库操作指令
1.Redis中库说明
- 使用redis的默认配置器动redis服务后,默认会存在16个库,编号从0-15
- 可以使用select 库的编号 来选择一个redis的库
2.Redis中操作库的指令
- 清空当前的库 FLUSHDB
- 清空全部的库 FLUSHALL
3.redis客户端显示中文
- ./redis-cli -p 7000 --raw
2.操作key相关指令
1.DEL指令
- 语法 : DEL key [key …]
- 作用 : 删除给定的一个或多个key 。不存在的key 会被忽略。
- 可用版本: >= 1.0.0
- 返回值: 被删除key 的数量。
2.EXISTS指令
- 语法: EXISTS key
- 作用: 检查给定key 是否存在。
- 可用版本: >= 1.0.0
- 返回值: 若key 存在,返回1 ,否则返回0。
3.EXPIRE
- 语法: EXPIRE key seconds
- 作用: 为给定key 设置生存时间,当key 过期时(生存时间为0 ),它会被自动删除。
- 可用版本: >= 1.0.0
- 时间复杂度: O(1)
- 返回值:设置成功返回1 。
4.KEYS
- 语法 : KEYS pattern
- 作用 : 查找所有符合给定模式pattern 的key 。
- 语法:
KEYS * 匹配数据库中所有key 。
KEYS h?llo 匹配hello ,hallo 和hxllo 等。
KEYS h*llo 匹配hllo 和heeeeello 等。
KEYS h[ae]llo 匹配hello 和hallo ,但不匹配hillo 。特殊符号用 “” 隔开
- 可用版本: >= 1.0.0
- 返回值: 符合给定模式的key 列表。
5.MOVE
- 语法 : MOVE key db
- 作用 : 将当前数据库的key 移动到给定的数据库db 当中。
- 可用版本: >= 1.0.0
- 返回值: 移动成功返回1 ,失败则返回0 。
6.PEXPIRE
- 语法 : PEXPIRE key milliseconds
- 作用 : 这个命令和EXPIRE 命令的作用类似,但是它以毫秒为单位设置key 的生存时间,而不像EXPIRE 命令那样, 以秒为单位。
- 可用版本: >= 2.6.0
- 时间复杂度: O(1)
- 返回值:设置成功,返回1 key 不存在或设置失败,返回0
7.PEXPIREAT
- 语法 : PEXPIREAT key milliseconds-timestamp
- 作用 : 这个命令和EXPIREAT 命令类似,但它以毫秒为单位设置key 的过期unix 时间戳,而不是像EXPIREAT那样, 以秒为单位。
- 可用版本: >= 2.6.0
- 返回值:如果生存时间设置成功,返回1 。当key 不存在或没办法设置生存时间时,返回0 。(查看EXPIRE 命令获取 更多信息)
8.TTL
- 语法 : TTL key
- 作用 : 以秒为单位,返回给定key 的剩余生存时间(TTL, time to live)。
- 可用版本: >= 1.0.0
- 返回值:
当key 不存在时,返回-2 。
当key 存在但没有设置剩余生存时间时,返回-1 。
否则,以秒为单位,返回key 的剩余生存时间。
- Note : 在Redis 2.8 以前,当key 不存在,或者key 没有设置剩余生存时间时,命令都返回-1 。
9.PTTL
- 语法 : PTTL key
- 作用 : 这个命令类似于TTL 命令,但它以毫秒为单位返回key 的剩余生存时间,而不是像TTL 命令那样,以秒为单 位。
- 可用版本: >= 2.6.0
- 返回值: 当key 不存在时,返回-2 。当key 存在但没有设置剩余生存时间时,返回-1 。
- 否则,以毫秒为单位,返回key 的剩余生存时间。
- 注意 : 在Redis 2.8 以前,当key 不存在,或者key 没有设置剩余生存时间时,命令都返回-1 。
10.RANDOMKEY
- 语法 : RANDOMKEY
- 作用 : 从当前数据库中随机返回(不删除) 一个key 。
- 可用版本: >= 1.0.0
- 返回值:当数据库不为空时,返回一个key 。当数据库为空时,返回nil 。
11.RENAME
- 语法 : RENAME key newkey
- 作用 : 将key 改名为newkey 。当key 和newkey 相同,或者key 不存在时,返回一个错误。当newkey 已经存在 时,RENAME 命令将覆盖旧值。
- 可用版本: >= 1.0.0
- 返回值: 改名成功时提示OK ,失败时候返回一个错误。
12.TYPE
- 语法 : TYPE key
- 作用 : 返回key 所储存的值的类型。
- 可用版本: >= 1.0.0
- 返回值:
none (key 不存在)
string (字符串)
list (列表)
set (集合)
zset (有序集)
hash (哈希表)
9.String 类型
1.常用命令操作
命令 | 说明 |
---|---|
set | 设置一个key/value |
get | 根据key获取对应的value |
mset | 一次设置多个key value |
mget | 一次获取多个key的value |
getset | 获取原始key的值,同时设置新值 |
strlen | 获取对应key存储value的长度 |
append | 为对应key的value追加内容 |
getrange索引0开始 | 截取value的内容,索引是以字节为单位 |
setex | 设置一个key存活的有效期(秒) |
psetex | 设置一个key存活的有效期(毫秒) |
setnx | 存在不做任何操作,不存在添加 |
msetnx原子操作(只要有一个存在不做任何操作) | 可以同时设置多个key,只有有一个存在都不保存 |
decr | 进行数据类型的-1操作 |
decrby | 根据提供的数据进行减法操作 |
incr | 进行数值类型的+1操作 |
incrby | 根据提供的数据进行加法操作 |
incrbyfloat | 根据提供的数据加入浮点数 |
10.List类型
list列表相当于java中list集合,特点元素有序且可以重复
1.常用操作指令
命令 | 说明 |
---|---|
lpush | 将某个值加入到一个key列表头部 |
ipushx | 同lpush,但是必须要保证这个key存在 |
rpush | 将某个值加入到一个key列表末尾 |
rpushx | 同rpush,单是必须要保证这个key存在 |
lpop | 返回和移除列表左边的第一个元素 |
rpop | 返回和移除列表右边的第一个元素 |
lrange | 获取某一个下标区间内的元素 区间0代表开始 -1代表最后 |
llen | 获取列表元素个数 |
lset | 设置某一个指定索引的值(索引必须存在) |
lindex | 获取某一个指定位置的元素 |
lrem | 删除重复元素 |
ltrim | 保留列表中特定区间内的元素 |
linsert | 在某一个元素之前,之后插入新元素key BEFORE|AFTER pivot value |
11.Set类型
特点:Set类型 Set集合 元素无序 不可以重复
1.常用命令
命令 | 说明 |
---|---|
sadd | 为集合添加元素 |
smembers | 显示集合中所有元素 无序 |
scard | 返回集合元素中元素的个数 |
spop | 随机返回一个元素,并将元素在集合中删除 |
smove | 从一个集合中向另一个几个移动元素 必须是同一种类型 |
srem | 从集合中删除指定的一个元素 |
sismember | 判断一个几个中是否含有这个元素,有返回1 没有返回0 |
srandmember | 随机返回元素 |
sdiff | 获取元素:去掉第一个集合中其他集合含有的相同元素,批量删除指定元素 |
sinter | 求交集:获取多个集合中都包含的数据 |
sunion | 求和集:将多个集合的元素一起取出,并且会去重 |
12.Zset类型
特点:可排序的set集合 排序 不可重复 相当于java中的treeSet
ZSET 官方 可排序SET sortSEt sorteSet
1.常用命令
命令 | 说明 |
---|---|
zadd | 添加一个有序集合元素 |
zcard | 返回集合的元素个数 |
zrange升序arevrange降序 | 返回一个范围内的元素 |
zrangebyscore | 安装分数查找一个范围内的元素 |
zrank | 返回排名 |
zrevrank | 倒序排名 |
zscore | 显示某一个元素的分数 |
zrem | 移除某一个元素 |
zincrby | 给某个特定元素加分 |
13.hash类型
特点:value 是一个map结构 存在key value key 无序的
1.常用命令
命令 | 说明 |
---|---|
hset | 设置一个key/value队 |
hget | 获取一个key对应的value |
hgetall | 获取所有的key/value对 |
hdel | 删除某一个key/value对 |
hexists | 判断一个key是否存在 |
hkeys | 获取所有的key |
hvals | 获取所有的value |
hmset | 设置多个key/value |
hmget | 获取多个key的value |
hsetnx | 设置一个不存在的key的值 |
hincrby | 为value进行加hset |
hincrbyfloat | 为value加入浮点值 |
二、Redis持久化机制
client redis[内存] ----> 内存数据—>数据持久化—>磁盘
Redis官方提供了两种不同的持久化方法来将数据存储到硬盘里面,分别是:
- 快照(Snapshot)
- AOF(Append Only File) 只追加日志文件
1.快照
1、特点:
这种方式可以将某一时刻的所有数据都写入硬盘中,当然这也是redis的默认开启持久化方式,保存的文件是以.rdb形式结尾的文件,因此这种方式也称之为RDB方式
2、快照生成方式
- 客户端方式:BGSAVE和SAVE指令
- 服务器配置自动触发
1.客户端方式之BGSAVE
- a.客户端可以使用BGSAVE命令来创建一个快照,当接收到客户端的BGSAVE命令时,redis会调用fork¹来创建一个子进程,然后子进程负责将快照写入磁盘中,而父进程则继续处理命令请求。
`名词解释: fork当一个进程创建子进程的时候,底层的操作系统会创建该进程的一个副本,在Linux系统中创建子 进程的操作会进行优化:在刚开始的时候,父子进程共享相同内存,直到父进程或子进程对内存进行了写之后,对被写入的 内存的共享才会结束服务
2.客户端方式之SAVE
- b. 客户端还可以使用SAVE命令来创建一个快照,接收到SAVE命令的redis服务器在快照创建完毕之前将不再响应任何其他的命令
- 注意:SAVE命令并不常用,使用SAVE命令在快照创建完毕之前,redis处于阻塞状态,无法对外提供服务
3.服务器配置方式之满足配置自动触发
- 如果用户在redis.conf中设置了save配置选项,redis会在save选项条件满足之后自动触发一次BGSAVE命令,如果设置多个save配置选项,当任意一个save配置选项条件满足,redis也会触发一次BGSAVE命令
4.服务器接收客户端shutdown指令
- 当redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save命令,阻塞所有的客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器
3、配置生成快照名称和位置
1.修改生成快照名称
dbfilename dump.rdb
2.修改生成位置
dir ./
2.AOF只追加日志文件
1、特点
这种方式可以将所有客户端执行的写命令记录到日志文件中,AOF持久化会将被执行的写命令写到AOF的 文件末尾,以此来记录数据发生的变化,因此只要redis从头到尾执行一次AOF文件所包含的所有写命令,就 可以恢复AOF文件的记录的数据集
2、开启AOF持久化
在redis的默认配置中AOF持久化机制是没有开启的,需要在配置中开启
1.开启AOF持久化机制
a.修改appendonly yes 开启持久化机制
b.修改appendfilename “appendonly.aof” 指定生成文件名称
3、日志追加频率
1.always (一直在开启)【谨慎使用】
- 说明: 每个redis写命令都要同步写入硬盘,严重降低redis速度
- 解释: 如果用户使用了always选项,那么每个redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少;遗憾的是,因为这种同步策略需要对硬盘进行大量的写入操作,所以redis处理命令的速度会受到硬盘性能的限制;
- 注意: 转盘式硬盘在这种频率下200左右个命令/s ; 固态硬盘(SSD) 几百万个命令/s;
- 警告: 使用SSD用户请谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的写入放大问题,导 致将固态硬盘的寿命从原来的几年降低为几个月。
2.everysec (每秒一次)【推荐】
- 说明: 每秒执行一次同步显式的将多个写命令同步到磁盘
- 解释: 为了兼顾数据安全和写入性能,用户可以考虑使用everysec选项,让redis每秒一次的频率对AOF文件进行同步;redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每秒同步一次AOF文件,redis 可以保证,即使系统崩溃,用户最多丢失一秒之内产生的数据。
# 3.no 【不推荐】
- 说明: 由操作系统决定何时同步
- 解释:最后使用no选项,将完全由操作系统决定什么时候同步AOF日志文件,这个选项不会对redis性能带来影响但是系统崩溃时,会丢失不定数量的数据,另外如果用户硬盘处理写入操作不够快的话,当缓冲区被等待写入硬盘数据填满 时,redis会处于阻塞状态,并导致redis的处理命令请求的速度变慢。
4、修改同步频率
1.修改日志同步频率
修改appendfsync everysec|always|no 指定
例:appendfsync everysec
3.AOF文件的重写
1、AOF带来的问题
AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态,其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件Redis提供了AOF重写(ReWriter)机制。
2、AOF重写
用来在一定程度上减小AOF文件的体积
3、触发重写方式
1.客户端方式触发重写
- 执行BGREWRITEAOF命令 不会阻塞redis的服务
2.服务器配置方式自动触发
- 配置redis.conf中的auto-aof-rewrite-percentage选项 参加下图↓↓↓
- 如果设置auto-aof-rewrite-percentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么 当AOF文件体积大于64M,并且AOF文件的体积比上一次重写之后体积大了至少一倍(100%)时,会自动触发,如果重写过于频 繁,用户可以考虑将auto-aof-rewrite-percentage设置为更大
4、重写原理
注意:重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重新写了一个新的aof文件,替换原来的文件,这点跟快照有点类似
重写流程:
- 1. redis调用fork ,现在有父子两个进程,子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 2. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保 证如果子进程重写失败的话并不会出问题。
- 3. 当子进程把快照内容以命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 4. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
4.持久化总结
两种持久化方案既可以同时使用,又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定
无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同的地方)
三、Java操作Redis
Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、
php、Node.js、Go等。 在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC
Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,Jedis同样也是托管在
github上.
说白了Jedis就是使用Java操作Redis的客户端(工具包)
地址:https://github.com/xetorthio/jedis 。
1.环境准备
1.1引入依赖
<!--引入jedis连接依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
1.2设置Linux的防火墙端口
# 查看防火墙释放运行
[root@localhost bin]# firewall-cmd --state
running
# 查看防火墙当前已经放行的端口
[root@localhost bin]# firewall-cmd --list-ports
# 设置防火墙放行redis的端口号 6379
[root@localhost bin]# firewall-cmd --zone=public --add-port=6379/tcp --permanent
success
[root@localhost bin]# firewall-cmd --list-ports
# 重启防火墙服务
[root@localhost bin]# systemctl restart firewalld.service
# 再次查看防火墙放行的端口,已经有6379端口了
[root@localhost bin]# firewall-cmd --list-ports
6379/tcp
1.3创建jedis对象
public static void main(String[] args) {
//1.创建jedis对象
//1.redis服务必须关闭防火墙 2.redis服务必须开启远程连接
Jedis jedis = new Jedis("192.168.40.4", 6379);
jedis.select(0);//选择操作的库默认0号库
//2.执行相关操作
//....
//3.释放资源
jedis.close();
}
2.操作key相关API
private Jedis jedis;
@Before
public void before(){
this.jedis = new Jedis("192.168.202.205", 7000);
}
@After
public void after(){
jedis.close();
}
//测试key相关
@Test
public void testKeys(){
//删除一个key
jedis.del("name");
//删除多个key
jedis.del("name","age");
//判断一个key是否存在exits
Boolean name = jedis.exists("name");
System.out.println(name);
//设置一个key超时时间 expire pexpire
Long age = jedis.expire("age", 100);
System.out.println(age);
//获取一个key超时时间 ttl
Long age1 = jedis.ttl("newage");
System.out.println(age1);
//随机获取一个key
String s = jedis.randomKey();
//修改key名称
jedis.rename("age","newage");
//查看可以对应值的类型
String name1 = jedis.type("name");
System.out.println(name1);
String maps = jedis.type("maps");
System.out.println(maps);
}
3.操作String相关API
//测试String相关
@Test
public void testString(){
//set
jedis.set("name","小陈");
//get
String s = jedis.get("name");
System.out.println(s);
//mset
jedis.mset("content","好人","address","海淀区");
//mget
List<String> mget = jedis.mget("name", "content", "address");
mget.forEach(v-> System.out.println("v = " + v));
//getset
String set = jedis.getSet("name", "小明");
System.out.println(set);
}
4.操作List相关API
//测试List相关
@Test
public void testList(){
//lpush
jedis.lpush("names1","张三","王五","赵柳","win7");
//rpush
jedis.rpush("names1","xiaomingming");
//获取所有数据
List<String> names1 = jedis.lrange("names1", 0, -1);
names1.forEach(name-> System.out.println("name = " + name));
//获取并删除数据
String names11 = jedis.lpop("names1");
System.out.println(names11);
//插入数据
jedis.linsert("names", ListPosition.BEFORE,"123","9999999");
}
5.操作Set的相关API
//测试SET相关
@Test
public void testSet(){
//sadd
jedis.sadd("names","zhangsan","lisi");
//smembers
jedis.smembers("names");
//sismember
jedis.sismember("names","xiaochen");
}
5.操作ZSet相关API
//测试ZSET相关
@Test
public void testZset(){
//zadd
jedis.zadd("names",10,"张三");
//zrange
jedis.zrange("names",0,-1);
//zcard
jedis.zcard("names");
//zrangeByScore
jedis.zrangeByScore("names","0","100",0,5);
}
6、操作Hash相关API
//测试HASH相关
@Test
public void testHash(){
//hset
jedis.hset("maps","name","zhangsan");
//hget
jedis.hget("maps","name");
//hgetall
jedis.hgetAll("mps");
//hkeys
jedis.hkeys("maps");
//hvals
jedis.hvals("maps");
}
四、SpringBoot整合Redis
Spring Boot Data Redis 种提供了RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处在于主要体现在操作数据类型不同,RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串
注意:使用RedisTemplate默认是将对象序列化到Redis中,所以放入的对象必须实现对象序列化接口
springboot2.0后已经将jedis改成了Lettuce
在RedisTemplate中定义了对5种数据结构的操作方法
- opsForvalue():操作字符串
- opsForList():操作列表
- opsForSet():操作集合
- opsForZSet():操作有序集合
- opsForHash():操作hash
1.环境准备
1.1引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1.2配置application.yml
#Spring配置
spring:
#Redis配置
redis:
database: 0 #Redis数据库索引(默认为0)
host: 127.0.0.1 #Redis服务器地址
port: 6379 #Redis服务器连接端口
password: #Redis服务器连接密码(默认为空)
2.使用StringRedisTemplate和RedisTemplate
//对字符串支持比较友好,不能存储对象
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate; //存储对象
@Test
public void testRedisTemplate(){
// 可以手动设置key的序列化规则
red
redisTemplate.opsForValue().set("user",new User("21","小黑",23,new Date()));
User user = (User) redisTemplate.opsForValue().get("user");
System.out.println(user);
Set keys = redisTemplate.keys("*");
keys.forEach(key -> System.out.println(key));
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
Object xiaohei = redisTemplate.opsForValue().get("xiaohei");
System.out.println(xiaohei);
redisTemplate.opsForValue().set("name","xxxx");
Object name = redisTemplate.opsForValue().get("name");
redisTemplate.opsForList().leftPushAll("lists","xxxx","1111");
List lists = redisTemplate.opsForList().range("lists", 0, -1);
lists.forEach(list-> System.out.println(list));
}
//key的绑定操作 如果日后对某一个key的操作及其频繁,可以将这个key绑定到对应redistemplate中,日后基于绑定操作都是操作这个key
//boundValueOps 用来对String值绑定key
//boundListOps 用来对List值绑定key
//boundSetOps 用来对Set值绑定key
//boundZsetOps 用来对Zset值绑定key
//boundHashOps 用来对Hash值绑定key
@Test
public void testBoundKey(){
BoundValueOperations<String, String> nameValueOperations =
stringRedisTemplate.boundValueOps("name");
nameValueOperations.set("1");
//yuew
nameValueOperations.set("2");
String s = nameValueOperations.get();
System.out.println(s);
}
//hash相关操作 opsForHash
@Test
public void testHash(){
stringRedisTemplate.opsForHash().put("maps","name","小黑");
Object o = stringRedisTemplate.opsForHash().get("maps", "name");
System.out.println(o);
}
//zset相关操作 opsForZSet
@Test
public void testZSet(){
stringRedisTemplate.opsForZSet().add("zsets","小黑",10);
Set<String> zsets = stringRedisTemplate.opsForZSet().range("zsets", 0, -1);
zsets.forEach(value-> System.out.println(value));
}
//set相关操作 opsForSet
@Test
public void testSet(){
stringRedisTemplate.opsForSet().add("sets","xiaosan","xiaosi","xiaowu");
Set<String> sets = stringRedisTemplate.opsForSet().members("sets");
sets.forEach(value-> System.out.println(value));
}
//list相关的操作opsForList
@Test
public void testList(){
// stringRedisTemplate.opsForList().leftPushAll("lists","张三","李四","王五");
List<String> lists = stringRedisTemplate.opsForList().range("lists", 0, -1);
lists.forEach(key -> System.out.println(key));
}
//String相关的操作 opsForValue
@Test
public void testString(){
//stringRedisTemplate.opsForValue().set("166","好同学");
String s = stringRedisTemplate.opsForValue().get("166");
System.out.println(s);
Long size = stringRedisTemplate.opsForValue().size("166");
System.out.println(size);
}
//key相关的操作
@Test
public void test(){
Set<String> keys = stringRedisTemplate.keys("*");//查看所有key
Boolean name = stringRedisTemplate.hasKey("name");//判断某个key是否存在
stringRedisTemplate.delete("age");//根据指定key删除
stringRedisTemplate.rename("","");//修改key的名称
stringRedisTemplate.expire("key",10, TimeUnit.HOURS);
//设置key超时时间 参数1:设置key名 参数2:时间 参数3:时间的单位
stringRedisTemplate.move("",1);//移动key
}
五、综合案例
springboot + mybatis-plus + mysql + redis + aop切面实现缓存
1.导入依赖
<?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 https://maven.apache.org/xsd/maven-
4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
</parent>
<groupId>com.example</groupId>
<artifactId>day02-springboot-redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<mysql.version>5.1.49</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.编辑配置文件
server:
port: 8888
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_redis?useSSL=false
username: root
password: root
redis:
host: 192.168.3.157
# 设置mybatis plus的日志显示查询语句
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.beans
@Data
@TableName("province")
public class Province implements Serializable {
//使用RedisTemplate要实现Serializable接口
// 指定当前属性为主键id
@TableId(type = IdType.AUTO)
private Integer pid;
private String pname;
}
4.dao层
@Mapper
public interface ProvinceMapper extends BaseMapper<Province> {
}
5.服务层
public interface ProvinceService extends IService<Province> {
}
@Service
public class ProvinceServiceImpl extends ServiceImpl<ProvinceMapper,Province> implements ProvinceService{
}
6.控制层
@RestController
@RequestMapping("/province")
public class ProvinceController {
@Autowired
private ProvinceService provinceService;
@GetMapping("")
public List<Province> findAll(){
return provinceService.list();
}
@GetMapping("/{id}")
public Province findById(@PathVariable("id") Integer pid){
return provinceService.getById(pid);
}
@GetMapping("/ids")
public List<Province> findAllWithParam(@RequestParam("id") List<Integer> ids){
return provinceService.listByIds(ids);
}
}
7.redis配置文件
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 绑定连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置所有的key都以字符串方式进行序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 设置所有的value都已jdk默认的方式进行序列化
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
}
8.AOP切面类
@Configuration
@Aspect
public class MyAspectConfig {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
// 这里因为用的是mybatis-plus的框架方法,因此要配置框架类表达式
@Around("execution(* com.baomidou.mybatisplus.extension.service.IService.list())")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
// 方法执行前
System.out.println("MyAspectConfig.around---执行前先查询redis缓存");
BoundValueOperations<String, Object> provinceOperation = redisTemplate.boundValueOps("provinces");
Object provinces = provinceOperation.get();
if (provinces != null){
return provinces;
}
// 如果redis中没有数据,就从数据库查询
Object proceed = joinPoint.proceed();
// 方法执行后
System.out.println("MyAspectConfig.around---执行后将数据保存到redis缓存");
// 将从数据库查询的数据缓存到redis中
provinceOperation.set(proceed);
return proceed;
} catch (Throwable e) {
throw e;
}
}
}