前言:
本笔记仅做学习与复习使用,不存在刻意抄袭。
给各位学友强烈推荐《遇见狂神说》他的整套Java学习路线使我获益匪浅!!!
点击跳转至遇见狂神说哔哩哔哩首页
点击跳转至原笔记地址
如果你也是狂神的小迷弟,可以加我好友一起探讨学习。
目录
前言:
二、Redis入门
测试
1、单机时代(如MySQL)的瓶颈
- 数据量太大,一个机器存不下
- 数据的索引(MySQL单表300万条数据,一定要建立索引),一个机器内存放不下
- 访问量大(MySQL读写混合 — 性能降低),一个服务器承受不了
2、Memcached缓存(减轻服务器的压力) + MySQL + 垂直拆分(读写分离,多个MySQL服务器,有的值负责读,有的值负责写)
发展过程:①优化数据结构和索引 => ②文件缓存(涉及IO操作)=> ③Memcached
Not Only SQL —> 非关系型数据库
NoSQL特点
-
方便扩展(数据之间没有关系)
-
大数据高性能(Redis一秒写8万次,读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能比较高)
-
数据类型是多样性的(不需要事先设计数据库(因为数据量大),随取随用)
-
传统RDBMS和NoSQL
-
传统RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表中
- 操作,数据定义语言
- 严格的一致性
- 基础的事务
- …
-
NoSQL
- 不仅仅是SQL
- 没有固定的查询语句
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理和BASE
- 高性能,高可用,高可扩
- …
了解:3V + 3高
大数据时代的3V:主要是描述问题的
- 海量Volume
- 多样Variety
- 实时Velocity
大数据时代的3高:主要是对程序的要求
- 高并发
- 高可拓(集群)
- 高性能
电商网站:
# 1、商品的基本信息
名称、价格、商家信息:
关系型数据库: MySQL / Oracle (王坚:阿里去IOE(IBM小型机,Oracle数据库、EMC存储器))
# 2、商品的描述、评论(文字多)
文档型数据库:MongoDB
# 3、图片
分布式文件系统:FastDFS、TFS(淘宝)、GFS(Google)、HDFS(Hadoop)、OSS云存储(阿里云)
# 4、商品的关键字(搜索)
搜索引擎:solr、ElasticSearch、ISearch(阿里:多隆)
# 5、商品热门的波段信息
内存数据库:Redis、Tair、Memcached、...
# 6、商品的交易、外部的接口
三方应用
1.KV键值对
- 新浪:Redis
- 美团:Redis + Tair
- 阿里、百度:Redis + Memcached
2.文档型数据库(bson格式)
- MongoDB(必须掌握)
- 基于分布式文件存储的数据库(C++编写)
- 主要用于处理大量的文档
- 介于关系型数据库和非关系型数据库中的中间产品
- ConthDB
3.列式存储
4.图关系数据库
- 存关系的,不是存图片的(比如,朋友圈社交网络,广告推荐)
- Neo4j、InfoGrid
对比
二、Redis入门
官网:redis.io
中文网:CRUG网站
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储(多样的数据结构)。
- Redis支持数据的备份,即master-slave模式的数据备份。
Redis优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。(但是Redis事务多操作不支持原子性—当我们执行后有语句有错,其他语句仍然可以正常执行,详情看事务)
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
- Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
- 内存存储、持久化(rdb,aof)
- 效率高、可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器(浏览量)
- …
linux 使用的系统是 Centos 7.3
你可使用 虚拟机,也可已使用云服务器(学生机不贵),因为我有服务器,这里就在服务器上安装和学习Redis
①下载安装包
CRUG网站
②加压安装包(提前使用xshell和xftp上传压缩包到服务器上)
压缩包在 /app/目录下
在/usr/local/ 下创建redis文件夹,并进入到文件夹中
解压压缩包到该文件夹下
tar -zxvf /app/redis-6.0.6.tar.gz -C ./
③编译并安装(保证安装了编译环境)
没有安装编译环境,需要 yum install gcc-c++
如果下面操作报错:需要提升GCC的版本(redis6需要5.3+)
//升级gcc到9以上
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
//临时将此时的gcc版本改为9
scl enable devtoolset-9 bash
//或永久改变
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
cd redis-6.0.6
make && make install
编译后,默认安装路径在 /usr/local/bin
④安装系统服务并后台启动
可能会报错:
解决方法:vim install_server.sh,注释如下图内容
安装系统服务(可以指定选项,下面默认)
cd utils
./install_server.sh
默认的配置文件:/etc/redis/6379.conf(安装成功后,默认后台自启)
⑤开启redis服务
可以使用自定义的配置文件开启,这里使用systemctl
/redis安装目录/redis-server /配置文件目录/redis.conf
# 关闭服务
/redis安装目录/redis-cli shutdown
systemctl start redis_6379.service
⑥客户端连接测试
⑦设置远程连接
vim /etc/redis/6379.conf
然后重启服务即可:
systemctl restart redis_6379.service
⑧配置访问密码
vim /etc/redis/6379.conf
重启服务
systemctl restart redis_6379.service
客户端连接测试
关闭连接
使用自带的redis-benchmark
工具测试即可
redis-benchmark [option] [option value]
性能测试工具可选参数:
序号 |
选项 |
描述 |
默认值 |
1 |
-h |
指定服务器主机名 |
127.0.0.1 |
2 |
-p |
指定服务器端口 |
6379 |
3 |
-s |
指定服务器 socket |
|
4 |
-c |
指定并发连接数 |
50 |
5 |
-n |
指定请求数 |
10000 |
6 |
-d |
以字节的形式指定 SET/GET 值的数据大小 |
3 |
7 |
-k |
1=keep alive 0=reconnect |
1 |
8 |
-r |
SET/GET/INCR 使用随机 key, SADD 使用随机值 |
|
9 |
-P |
通过管道传输 \ 请求 |
1 |
10 |
-q |
强制退出 redis。仅显示 query/sec 值 |
|
11 |
—csv |
以 CSV 格式输出 |
|
12 |
-l |
生成循环,永久执行测试 |
|
13 |
-t |
仅运行以逗号分隔的测试命令列表。 |
|
14 |
-I |
Idle 模式。仅打开 N 个 idle 连接并等待。 |
测试
# 测试:100个并发连接 100000请求
redis-benchmark -c 100 -n 100000
如果觉得这里命令不太好看,去这个博客https://www.cnblogs.com/wlandwl/p/redis.html,或者 官网:Redis命令中心(Redis commands) -- Redis中国用户组(CRUG)
注意:以下的所有key都表示对应数据类型的 键的名称
; value表示存储的值
(除非注释中有特殊说明)
1.Redis 有16个数据库(0~15),默认使用第0个
可以使用 select
进行切换
2.查看数据库大小
使用 dbsize
查看数据库大小
3.查看所有的key(当前库)
使用 keys *
4.清除当前数据库
flushdb
5.清空所有数据库
flushall
略
6.Redis是单线程的?
- Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis的性能瓶颈,Redis的性能瓶颈是根据机器的内存和网络的带宽,既然可以使用单线程,就不用使用多线程。(6.0后支持多线程)
7.判断key是否存在
exists
key
8.移除key
move
key 1 // 1表示当前数据库
del
key // 删除当前数据库的key(可以多个)
9.设置key的过期时间
expire
key 时间 // 单位时间为s
ttl
key // 查看剩余存活时间
10.查看当前key的类型
type
key
11.字符串追加(String)
append
key appendValue
- 如果当前 key 不存在,作用相当于 set key
12.获取字符串长度(String)
strlen
key
13.字符串i++操作(可用于阅读量实现)(String)
incr
key
同理,i—
decr
key
步长设置
incrby
key 步长
decrby
key 步长
14.字符串片段 Range (String)
getrange
key startIndex endIndex
- 对应java里的substring(但是这里会endIndex是一个闭区间)
- 特例,endIndex = -1 时,表示从startIndex 到最后
15.字符串替换 (String)
setrange
key index replaceString
- 对应java里的replace
- 注意,如果replaceString是一个字符串,那么会替换源字符串中index后replaceString长度的片段,结果如下
16.特殊set设置 (String)
setex(set with expire)
setex
key 时间 value // 设置值,带过期时间
setnx(set if not exist)
setnx
key value // 如果不存在,则设置
17.批量设置、批量获取(原子性操作) (String)
mset
k1 v1 k2 v2 …
mget
k1 k2 …
18.设置高阶 (String)
1)getset
getset
key value // 先get再set(如果不存在,先返回nil,在设置值;如果存在,就先返回原值,再设置新值)
19.重命名key
rename
key newName
20.返回一个随机key
randomKey
21.手动持久化操作
save
阻塞
- SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
bgsave
非阻塞
- BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。 Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求。
命令 |
save |
bgsave |
IO类型 |
同步 |
异步 |
阻塞? |
是 |
是(阻塞发生在fock(),通常非常快) |
复杂度 |
O(n) |
O(n) |
优点 |
不会消耗额外的内存 |
不阻塞客户端命令 |
缺点 |
阻塞客户端命令 |
需要fock子进程,消耗内存 |
22.获取配置文件中用户密码
config get requirepass
23.设置配置文件中用户密码(临时,重启服务失效)
config set requirepass password
24.密码认证
auth password
设置密码后必须认证才能使用客户端功能
25.关闭redis服务
shutdown
26.查看rdb文件存放的目录
27.查看服务器的信息
info
参数
参数列表:
server
: Redis服务器的一般信息
clients
: 客户端的连接部分
memory
: 内存消耗相关信息
persistence
: RDB和AOF相关信息
stats
: 一般统计
replication
: 主/从复制信息
cpu
: 统计CPU的消耗
commandstats
: Redis命令统计
cluster
: Redis集群信息
keyspace
: 数据库的相关统计
它也可以采取以下值:
all
: 返回所有信息
default
: 值返回默认设置的信息
如果没有使用任何参数时,默认为default
。
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
单点登录、
略,具体看前面
使用场景:
- 计数器
- 统计多单位的数量(uid:122:follow 10)
- 粉丝数
- 对象缓存存储
1.从头部/尾部插入数据,以及数据显示
lpush
key value
rpush
key value
lrange
key 0 -1
2.从头部/尾部移除数据
lpop
key
rpop
key
3.获取指定索引的值
lindex
key index
4.获取列表长度
llen
key
5.移除指定的值
lrem
key count(移除的个数) element
6.列表修剪 trim
ltrim
key startIndex endIndex
7.也可以使用set
lset
key index value // 将列表中 指定index的值替换为对应的value
8.插入指定的值
linsert
key before|after pivot(那个单词后) value
9.复杂操作
1)rpoplpush
rpoplpush
source destination(newList) // 先移除source最后的元素,再将该元素添加到newList中
使用场景:
- 栈(lpush、lpop)
- 队列(lpush、rpop)
- 消息队列
- 阻塞队列
集合中的值不能重复(无序)
1.添加成员到集合中,并查看所有成员
sadd
key member
smembers
key
2.判定成员是否存在
sismember
key member
3.查看集合长度(特别)
scard
key
4.移除指定的成员
srem
key member
5.获取集合中的随机成员
srandmember
key [count]
6.随机移除成员
spop
key [count]
7.移动集合成员到其他集合
smove
source destination member(需要移动的成员)
8.数字集合类:
- 差集
sdiff
key1 ,key2 …
- 交集(共同好友)
sinter
key1,key2 …
- 并集
sunion
key1 ,key2 …
key-Map or key-\,value是一个Map
Hash本质和hash没有区别,只是value变成了Map
用户信息保存,经常变动的信息,适合对象的存储
1.简单存储Map和获取Map
hset
key field value [k1 v1 [k2 v2 …]]
hget
key field
2.获取所有Map字段及值
hgetall
key
3.删除Map中的字段
hdel
key field
4.查看Map中某字段是否存在
hexists
key field
5.获取所有字段或者所有字段对应的值
hkeys
key // 此处的key是Map名
hvals
key
6.增量i++
hincrby
key field value
7.不存在,就添加成功
hsetnx
key field value // field 在map中不存在就添加这个值,否则不做改变
8.适合存储对象
在Set基础上增加了一个值(用于排序的值)
存储班级成绩表,工资表排序,
普通消息 = 1,重要消息 = 2,带权重进行判断
排行榜应用实现
1.添加 和 获取
zadd
key n value
zrange
key startIndex endIndex
2.排序实现(升序和降序)
zrangebyscore
key -inf +inf [withscores] // 升序
zrange
key 0 -1
zrevrangebyscore
key +inf -inf [withscores] // 降序
zrevrange
key 0 -1
zrangebyscore
key -inf 任意值n // 升序 + 显示区间 [-inf,n]
3.移除指定的值
zrem
key value
4.集合的长度
zcard
key
5.指定区间的集合长度
zcount
key startIndex endIndex
朋友的定位,附近的人,打车距离计算
底层是 Zset
,即可以使用Zset的命令操作Geospatial
Redis3.2就支持了
1.添加地理位置
- 两极无法添加
- 经度:-180 ~ 180(度)
- 纬度:-85.05112878 ~ 85.05112878(度)
geoadd
key 经度 纬度 名称
2.获取指定位置的地理位置
geopos
key 名称
3.返回两个给定位置之间的距离(直线距离)
- 单位:
- m :米
- km :千米
- mi : 英里
- ft :英尺
geodist
key
4.以给定值为半径,以经度和维度
为中心,查找
- 附近的人(获得所有附近的人的地址(开启定位))通过半径查询
georadius
key 经度 纬度 半径 单位
5.以给定值为半径,以成员(城市名)为中心,查找
georadiusbymember
key 成员名 半径 单位
6.返回一个或多个位置元素的geohash表示
geohash
key 成员1 成员2
基数统计的算法
- 优点
- 占用内存是固定的,264不同的元素的基数,只需要12KB的内存。(大数据情况下,有0.81%错误率)
基数:集合中元素的个数(先去重),如{1,2,2,3} 其基数为3(集合去重后为1,2,3 有3个元素)
网页的UV(一个人访问访问一个网站多次,但是还是算作一个人)
传统实现UV:Set保存用户的Id,然后统计set中的元素的数量作为标准判断(这种需要保存大量用户的ID)
Redis2.8.9
1.测试
创建一组元素 : pfadd
key ele1 ele2 ele3 …
统计对应key的基数:pfcount
key1 [key2 …] // 多个key 就是统计这些key并集的基数
合并:pfmerge
destkey sourceKey1 sourceKey2 [sourceKey3 …]
位存储,位图(操作二进制)
统计用户信息,活跃,不活跃!登录、未登录!打卡,365打卡!(两个状态都可以使用)
1.案例:一周打卡记录
一周过去
查看单天打卡情况
getbit
key offset
统计所有打卡的天数
bitcount
key
注意:Redis单条命令是保证原子性的;但是事务不保证原子性!
Redis事务没有隔离级别的概念,所有的命令在事务中,并没有直接被执行,只有发起执行命令时才执行
Redis事务本质:一组命令的集合,一个事务中的所有命令都会别序列化,在事务执行过程中,会按照顺序执行!
--- 队列 set set set 执行 ---
一次性、顺序性、排他性!执行一些列的命令
Redis的事务:
- 开启事务(
multi
)
- 命令入队(
其他命令
)
- 执行事务(
exec
)
discard
1.编译时(命令写错)
整个命令队列都不会执行
2.运行时
报错语句,会抛出异常;其他语句照样运行
乐观锁:实现秒杀
- 顾名思义,很乐观,认为什么时候都不会出现问题,所以不会加锁!(更新数据的时候去判断一下,在此期间是否有人修改过这个数据)
悲观锁
- 顾名思义,很悲观,认为什么时候都会出现问题,无论做什么都会加锁!
Redis实现乐观锁
执行成功(单线程没有干扰情况)
测试多线程修改值,使用watch可以当作redis的乐观锁操作
演示‘
①开启俩个,客户端,模拟多线程情况
②左边支出20元(但是不执行事务),然后右边修改money的数值
③左边执行事务,发现执行操作返回nil,查看money和out,发现事务并没有被执行(确实有乐观锁的效果)
如果修改失败获取最新的值就好(exec
、unwatch
、`discard
都可以清除连接时所有的监视)
小结
- 使用Redis实现乐观锁(watch监听某一个key,获取其最新的value)
- 在提交事务时,如果key的value没有发生变化,则成功执行
- 在提交事务时,如果key的value发生了变化,则无法成功执行
Jedis是一个用java写的Redis数据库操作的客户端,通过Jedis,可以很方便的对redis数据库进行操作
①新建空maven项目
②导入依赖
redis.clients
jedis
3.3.0
com.alibaba
fastjson
1.2.70
③编码
下面使用远程连接需要的操作:Jedis远程连接redis详解(密码设置、防火墙端口6379开启、去掉保护)_一起码代码的博客-CSDN博客_jedis连接redis密码
- 连接数据
public class PingTest {
public static void main(String[] args) {
// 1. new Jedis 对象
Jedis jedis = new Jedis("ip地址",6379);
// 如果设置密码 需要认证,没有设置忽略下面这条语句
jedis.auth("密码");
/// jedis 所有的命令(方法)都是之前学的命令
System.out.println(jedis.ping());// 测试连接
}
}
-
操作
清空数据:OK
判断key(name)是否存在:false
设置name的value:1
设置pwd的value:1
打印所有的key:[pwd, name]
获取该name的value:liuyou
删除pwd:1
重命名name为username:OK
打印所有的key:[username]
返回当前数据库中key的数目:1
-
关闭连接
jedis.close()
- 事务
// 连通
Jedis jedis = new Jedis("IP地址", 6379);
jedis.auth("密码");
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","liuyou");
jsonObject.put("pwd","密码");
String s = jsonObject.toJSONString();
jedis.flushAll();
/// 加监听 watch
// jedis.watch("user");
// 开启事务
Transaction multi = jedis.multi();
try {
multi.set("user",s);
// 其他语句
// 执行事务
multi.exec();
} catch (Exception e) {
// 取消事务
multi.discard();
e.printStackTrace();
} finally {
System.out.println(jedis.get("user"));
// 关闭连接
jedis.close();
}
在SpringBoot2.x之后,原来的jedis被替换为lettuce
- jedis
- 采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool连接池!BIO
- lettuce
- 采用netty,实例可以在多个线程**享,不存在线程不安全问题,可以减少线程数据了,性能高,NIO
①新建springboot项目
② RedisAutoConfiguration
源码分析
③整合测试
1、导入依赖
org.springframework.boot
spring-boot-starter-data-redis
2、配置Redis(application.xml)
# SpringBoot 整合Redis
spring.redis.host=xxx
spring.redis.port=6379
spring.redis.password=liuyou
3、编写测试类
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Test
void contextLoads() {
//redisTemplate
// 1.使用redisTemplate.opsForxxx 操作对应的数据结构
// 2.可使用redisTemplate 进行简单的key操作,如multi、move、watch、keys 等操作
/// 3.可使用获取连接,通过连接进行更多操作
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// RedisZSetCommands redisZSetCommands = connection.zSetCommands();
// 这里只使用1.演示
ValueOperations str = redisTemplate.opsForValue();
str.set("name","liuminkai刘民锴");
System.out.println(str.get("name"));
}
}
但是存在问题,没有序列化,存储在Redis中的中文会被转义,如下图
为啥会出现这个情况? RedisTemplate默认序列化使用JDK的,我们需要使用JSON格式
如何解决?我们需要编写自定义Redis配置类,自定义RedisTemplate
④自定义RedisTemplate
@Configuration
public class MyRedisConfig {
// 更改Key :Object ==> String 符合日常使用
// 自己定义了一个 RedisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
// 我们为了自己开发方便,一般直接使用
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
自定义RedisTemplate后,再启动③的测试类,结果Redis中正常显示
⑤RedisUtils工具类
日常开发我们都不会使用原始的RedisTemplate
,都会封装一个RedisUtils工具类便于使用
package com.liuyou.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtils {
@Autowired
private RedisTemplate redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map