0
点赞
收藏
分享

微信扫一扫

Redis(二)


文章目录

  • ​​5、三种特殊的数据类型​​
  • ​​5.1、geospatial地理位置​​
  • ​​5.2、Hyperloglog基数统计​​
  • ​​5.3、Bitmap位存储​​
  • ​​6、事务​​
  • ​​6.1、事务基本操作​​
  • ​​6.2、Redis实现乐观锁​​
  • ​​7、Jedis​​
  • ​​7.1、简介​​
  • ​​7.2、入门案例​​
  • ​​7.3、key的基本操作​​
  • ​​7.4、String的基本操作​​
  • ​​7.5、List的基本操作​​
  • ​​7.6、Set的基本操作​​
  • ​​7.7、Hash的基本操作​​
  • ​​8、整合SpringBoot​​
  • ​​8.1、整合测试​​
  • ​​8.2、自定义Redis配置类​​
  • ​​8.3、RedisUtil​​


5、三种特殊的数据类型

5.1、geospatial地理位置

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增

​​win版本3.2下载地址​​

主要分为6个指令

  1. geoadd(将指定的地理空间位置空添加到指定的key中)
  2. geodist(返回两个给定位置之间的距离)
  3. geohash(返回一个或多个位置对象的 geohash 值)
  4. geopos(返回指定地理名称的经纬度)
  5. georadius(返回指定经纬度范围内的城市信息)
  6. georadiusbymember(返回已存在城市指定范围内的城市信息)


GEOADD:将指定的地理空间位置空添加到指定的key中

具体的限制,由EPSG:900913 / EPSG:3785 / OSGEO:41001 规定如下:

  • 有效的经度从-180度到180度。
  • 有效的纬度从-85.05112878度到85.05112878度。
  • 添加格式为——经度、纬度、名称

当坐标位置超出上述指定范围时,该命令将会返回一个错误。

# 使用geoadd添加五个城市的地理位置数据

127.0.0.1:6379> geoadd city 106.54 29.40 chongqing
(integer) 1
127.0.0.1:6379> geoadd city 104.64 28.75 sichuan
(integer) 1
127.0.0.1:6379> geoadd city 120.21 30.25 hangzhou
(integer) 1
127.0.0.1:6379> geoadd city 125.15 42.93 xian
(integer) 1
127.0.0.1:6379> geoadd city 114.06 22.54 shenzhen
(integer) 1


GEODIST:返回两个给定位置之间的距离

如果两个位置之间的其中一个不存在, 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位

"># 计算两个城市之间的距离

127.0.0.1:6379> GEODIST city chongqing sichuan
"198341.1791"

127.0.0.1:6379> GEODIST city hangzhou chongqing
"1321660.7039"

127.0.0.1:6379> GEODIST city xian hangzhou km # 默认的单位是m,可以显示的指定距离单位为km
"1477.0109"

127.0.0.1:6379> GEODIST city xian hangzhou
"1477010.8848"


GEOHASH:返回一个或多个位置对象的 geohash 值

# 返回指定地理位置的geohash值

127.0.0.1:6379> GEOHASH city chongqing sichuan
1) "wm5z22h53v0"
2) "wm47t1u8bs0"

127.0.0.1:6379> GEOHASH city chongqing sichuan xian
1) "wm5z22h53v0"
2) "wm47t1u8bs0"
3) "wz8upz5mqk0"
127.0.0.1:6379>


GEOPOS:返回一个数组,数组中每一项都由两个元素组成(精度、维度),如果元素不存在,对应的数组项为空制

# 返回指定地理名称的地理经纬度值

127.0.0.1:6379> GEOPOS city chongqing shanghai xian
1) 1) "106.54000014066696"
2) "29.399998800186417"
2) (nil)
3) 1) "125.14999777078629"
2) "42.930000696102383"

127.0.0.1:6379> GEOPOS city hangzhou
1) 1) "120.21000176668167"
2) "30.249999797922619"


GEORADIUS:返回指定经纬度范围内的城市信息

GEORADIUSBYMEMBER:返回已存在城市信息范围内的其他城市信息

参数说明

  • m :米,默认单位。
  • km :千米。
  • mi :英里。
  • ft :英尺。
  • WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。
  • WITHCOORD: 将位置元素的经度和维度也一并返回。
  • WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
  • COUNT 限定返回的记录数。
  • ASC: 查找结果根据距离从近到远排序。
  • DESC: 查找结果根据从远到近排序。

# georadius 返回以经度为100 纬度为25的为原点,100km范围内的城市信息

127.0.0.1:6379> georadius city 100 25 100 km
(empty list or set)
127.0.0.1:6379> georadius city 100 25 1000 km
1) "sichuan"
2) "chongqing"

127.0.0.1:6379> georadius city 100 25 1000 km withcoord
1) 1) "sichuan"
2) 1) "104.63999837636948"
2) "28.749999975527849"
2) 1) "chongqing"
2) 1) "106.54000014066696"
2) "29.399998800186417"

# 位置元素与中心之间的距离也一并返回
127.0.0.1:6379> georadius city 100 25 1000 km withdist
1) 1) "sichuan"
2) "621.0759"
2) 1) "chongqing"
2) "810.9416"

# 将位置元素的经度和维度也一并返回
127.0.0.1:6379> georadius city 100 25 1000 km withcoord withdist
1) 1) "sichuan"
2) "621.0759"
3) 1) "104.63999837636948"
2) "28.749999975527849"
2) 1) "chongqing"
2) "810.9416"
3) 1) "106.54000014066696"
2) "29.399998800186417"

# georadiusbymember 以chongqing为原点,范围1000km以内的城市信息

127.0.0.1:6379> georadiusbymember city chongqing 1000 km
1) "sichuan"
2) "chongqing"

# 将位置元素的经度和维度也一并返回
127.0.0.1:6379> georadiusbymember city chongqing 1000 km withcoord
1) 1) "sichuan"
2) 1) "104.63999837636948"
2) "28.749999975527849"
2) 1) "chongqing"
2) 1) "106.54000014066696"
2) "29.399998800186417"

# 将位置元素的经度和维度也一并返回,由远及近排列
127.0.0.1:6379> georadiusbymember city chongqing 1000 km withcoord desc
1) 1) "sichuan"
2) 1) "104.63999837636948"
2) "28.749999975527849"
2) 1) "chongqing"
2) 1) "106.54000014066696"
2) "29.399998800186417"

127.0.0.1:6379> georadiusbymember city chongqing 1000 km withcoord asc
1) 1) "chongqing"
2) 1) "106.54000014066696"
2) "29.399998800186417"
2) 1) "sichuan"
2) 1) "104.63999837636948"
2) "28.749999975527849"

127.0.0.1:6379> georadiusbymember city chongqing 1000 km withcoord withhash
1) 1) "sichuan"
2) (integer) 4025121096646710
3) 1) "104.63999837636948"
2) "28.749999975527849"
2) 1) "chongqing"
2) (integer) 4026043655521786
3) 1) "106.54000014066696"
2) "29.399998800186417"

# 返回一条结果集
127.0.0.1:6379> georadiusbymember city chongqing 1000 km withcoord count 1
1) 1) "chongqing"
2) 1) "106.54000014066696"
2) "29.399998800186417"
127.0.0.1:6379>


5.2、Hyperloglog基数统计

以下解释来源菜鸟教程:

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。Redis 在 2.8.9 版本添加了 HyperLogLog 结构

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素

什么是基数:数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

# 基数统计

127.0.0.1:6379> PFADD myset 1 2 3 3 1 2 22 123 323 11
(integer) 1
127.0.0.1:6379> PFCOUNT myset
(integer) 7
127.0.0.1:6379> PFADD myset2 2 1 22 333 45 7 7
(integer) 1
127.0.0.1:6379> PFCOUNT myset2
(integer) 6

# 将myset和myset2进行合并
127.0.0.1:6379> PFMERGE myset myset2
OK
# 可以对合并以后的集合进行基数统计,去掉两个集合中重复的元素以后总数为10个
127.0.0.1:6379> PFCOUNT myset
(integer) 10

注意:如果统计的数据不允许容错,那么就不能使用Hyperloglog进行基数的统计


5.3、Bitmap位存储

从Redis从2.2.0版本开始,Redis新增了setbit、getbit、bitcount等几个与bitmap相关的命令。因为这些命令都是在set命令的基础上扩展而来,所以并没有新增新的数据类型。

Bitmap就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。且其能够极大的节省储存空间

# 模拟一周内的上线情况,1表示上线了,0表示未上线
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0

# 获取指定的星期日子的上线情况
127.0.0.1:6379> getbit sign 4
(integer) 0
127.0.0.1:6379> getbit sign 6
(integer) 1

# 统计一周内总的上线天数,即sign为1的数量
127.0.0.1:6379> bitcount sign
(integer) 5


6、事务

6.1、事务基本操作

Redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程种,会按照顺序执行

Redis事务的特点:一次性、顺序性、排它性。可以将它抽象的理解为一个队列

------ 开启  set set set  执行 ------

Redis事务没有隔离级别概念,Redis单条命令是保证原子性的,但是Redis的事务不保证原子性

Redis事务开启方式:

  1. 开启事务(multi)
  2. 命令入队
  3. 执行事务(exec)

正常执行事务:exec

127.0.0.1:6379> multi   # 开启事务
OK
127.0.0.1:6379> set name mobian
QUEUED
127.0.0.1:6379> set age 11
QUEUED
127.0.0.1:6379> set sex 1
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec # 结束事务,一次执行进入队列中的命令
1) OK
2) OK
3) OK
4) "11"
5) "mobian"


放弃事务:discard

127.0.0.1:6379> multi   # 开启事务
OK
127.0.0.1:6379> set name mobian
QUEUED
127.0.0.1:6379> discard # 放弃事务
OK
127.0.0.1:6379> get name
(nil)


编译型异常:代码语法有问题,命令有误。事务中所有的命令都不会被执行

127.0.0.1:6379> multi   # 开启事务
OK
127.0.0.1:6379> set name1 mobian1
QUEUED
127.0.0.1:6379> set name2 mobian2
QUEUED
127.0.0.1:6379> setset name3 mobian3 # 代码语法有误,出现编译型异常
(error) ERR unknown command 'setset'
127.0.0.1:6379> set name4 mobian4
QUEUED
127.0.0.1:6379> exec # 执行事务,报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get name1 # 获取由于出现编译型异常,导致事务中的所有命令都不会被执行,所以获取不到数据
(nil)


执行时异常:代码正确,但执行会报异常,如对字符串+1。事务中的其他命令不受影响

127.0.0.1:6379> multi   # 开启事务
OK
127.0.0.1:6379> set name1 mobian1
QUEUED
127.0.0.1:6379> incr name1 # 给一个非数字的key执行+1操作
QUEUED
127.0.0.1:6379> set name2 mobian2
QUEUED
127.0.0.1:6379> exec # 执行事务,第二步报异常
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get name2 # 由于前面是执行时异常并非编译型异常,所以其他的set操作不受影响
"mobian2"


6.2、Redis实现乐观锁

关键词:watch

使用watch关键词,对变量进行监控,以达到常规实现乐观锁操作的版本字段控制

使用Redis测试乐观锁,步骤如下:

  1. 分别打开两个Redis客户端
  2. 客户端1设置money变量的值为100
  3. 客户端2获取设置的money变量值
  4. 客户端1使用watch给money变量添加监控(对比理解乐观锁的实现,更行money变量时添加一个version条件控制)
  5. 客户端1开启事务
  6. 客户端1修money的值
  7. 客户端2修改money变量的值
  8. 客户端1执行事务(由于客户端2修改了money的值,即监控的money字段其version字段发生了改变,所以执行事务失败)
  9. 客户端1获取money变量的值(由于客户端1的事务操作失败,所以money值为客户端2中修改后的值)
  10. 客户端2获取money变量的值(由于客户端1的事务操作失败,所以money值为客户端2中修改后的值)

Redis(二)_Redis


如果修改失败,解锁后重新加锁再次操作即可。这里优点类似于CAS锁的操作。

加锁…解锁 。。 加锁…解锁 。。。。。。

127.0.0.1:6379> unwatch
OK


7、Jedis

7.1、简介

Java语言与Redis服务进行连接


7.2、入门案例

1、创建一个maven项目

2、添加对应的maven依赖

<!--对应的jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>

3、启动后台的Redis服务

4、编写一个测试类

public class Test {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);

System.out.println(jedis.ping());
}
}

测试结果:

PONG


7.3、key的基本操作

public class Key {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);

System.out.println("判断对应的key键是否存在:" + jedis.exists("name"));
System.out.println("添加数据:" + jedis.set("name", "mobian"));
System.out.println("添加数据:" + jedis.set("city", "chongqing"));
System.out.println("获取系统所有的键:" + jedis.keys("*"));

System.out.println("根据key删除对应的数据:" + jedis.del("name"));
System.out.println("判断对应的key是否存在:" + jedis.exists("name"));
System.out.println("判断key的类型:" + jedis.type("city"));

System.out.println("随机返回一个key:" + jedis.randomKey());//需要添加大于一个的key才能完成测试

System.out.println("重新命名对应的key" + jedis.rename("city", "newcity"));
System.out.println(jedis.get("newcity"));

System.out.println("选择使用1号数据库" + jedis.select(1));
System.out.println("获取系统所有的键:" + jedis.keys("*"));

System.out.println("选择使用0号数据库" + jedis.select(0));
System.out.println("获取系统所有的键:" + jedis.keys("*"));

System.out.println("获取当前数据库key的数目" + jedis.dbSize());
System.out.println("清空当前数据库" + jedis.flushDB());
System.out.println("清空所有数据库" + jedis.flushAll());

System.out.println("获取系统所有的键:" + jedis.keys("*"));

}
}

测试结果:

判断对应的key键是否存在:false
添加数据:OK
添加数据:OK
获取系统所有的键:[name, city]
根据key删除对应的数据:1
判断对应的key是否存在:false
判断key的类型:string
随机返回一个key:city
重新命名对应的keyOK
chongqing
选择使用1号数据库OK
获取系统所有的键:[]
选择使用0号数据库OK
获取系统所有的键:[newcity]
获取当前数据库key的数目1
清空当前数据库OK
清空所有数据库OK
获取系统所有的键:[]


7.4、String的基本操作

public class String {
public static void main(java.lang.String[] args) throws InterruptedException {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");

System.out.println("删除键k1:" + jedis.del("k1"));
System.out.println("获取键k2:" + jedis.get("k2"));
System.out.println("修改键k3的值:" + jedis.set("k3", "newV3"));

System.out.println("k3键的值后添加信息:" + jedis.append("k3", "_appendV3"));
System.out.println("获取键k2:" + jedis.get("k3"));

System.out.println("=================测试批量操作======================");
System.out.println("批量添加对应的数据:" + jedis.mset("k4", "v4", "k5", "v5"));
System.out.println("批量获取对应的数据:" + jedis.mget("k1", "k2", "k3", "k4", "k5"));
System.out.println("对应的数据:" + jedis.del("k5"));
System.out.println("批量获取对应的数据:" + jedis.mget("k1", "k2", "k3", "k4", "k5"));

jedis.flushDB();//清除当前的数据库

System.out.println("=================测试setnx======================");
System.out.println(jedis.setnx("key1", "value1"));
System.out.println(jedis.setnx("key2", "value2"));
System.out.println(jedis.setnx("key2", "new_value2"));//如果已存在key2键,则不操作

System.out.println(jedis.get("key1"));
System.out.println(jedis.get("key2"));


System.out.println("=============设置键值对,并设置有效时间============");
System.out.println(jedis.setex("key3", 2, "value3"));
System.out.println("获取三秒前key3的值:"+jedis.get("key3"));
TimeUnit.SECONDS.sleep(3); // 线程停止三秒钟
System.out.println("获取三秒后key3的值:"+jedis.get("key3"));


System.out.println("================根据对应的键,重新设置值============");
System.out.println("获取键key1后重新设置值:"+jedis.getSet("key1", "newSetKey1"));
System.out.println("获取key1的值:"+jedis.get("key1"));
System.out.println("获取key1的值,并且切割出索引位置为2-4的字符串:"+jedis.getrange("key1", 2, 4));


}
}

测试结果:

删除键k1:1
获取键k2:v2
修改键k3的值:OK
k3键的值后添加信息:14
获取键k2:newV3_appendV3
=================测试批量操作======================
批量添加对应的数据:OK
批量获取对应的数据:[null, v2, newV3_appendV3, v4, v5]
对应的数据:1
批量获取对应的数据:[null, v2, newV3_appendV3, v4, null]
=================测试setnx======================
1
1
0
value1
value2
=============设置键值对,并设置有效时间============
OK
获取三秒前key3的值:value3
获取三秒后key3的值:null
================根据对应的键,重新设置值============
获取键key1后重新设置值:value1
获取key1的值:newSetKey1
获取key1的值,并且切割出索引位置为2-4的字符串:wSe


7.5、List的基本操作

public class ListTest {

public static void main(String[] args) {

Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushAll();//清除整个数据库的数据

System.out.println(jedis.lpush("city", "shenzhen"));
System.out.println(jedis.lpush("city", "shanghai", "beijing"));
System.out.println(jedis.lpush("city", "shenzhen"));
System.out.println(jedis.lpush("city", "chongqing"));
System.out.println("打印list中的数据:" + jedis.lrange("city", 0, -1)); //获取集合中的所有数据
System.out.println("获取指定下标范围的数据:" + jedis.lrange("city", 0, 1)); //获取集合中下标0-1的数据


System.out.println("============对list集合中的数据进行删除==========");
//删除list中指定的值,第二个参数为删除的个数(有重复时),后push的值会先被删除,类似于出栈操作
//即该例中删除的shenzhen为beijing和chongqing中间的
System.out.println("删除指定位置的数据:" + jedis.lrem("city", 1, "shenzhen"));
System.out.println("打印list中的数据:" + jedis.lrange("city", 0, -1)); //获取集合中的所有数据

//切割指定索引的数据
System.out.println(jedis.ltrim("city", 0, 2));
System.out.println("打印list中的数据:" + jedis.lrange("city", 0, -1)); //获取集合中的所有数据


System.out.println("============对list集合中的数据进行弹出==========");
// 从左端弹出元素
System.out.println("弹出左边元素:" + jedis.lpop("city"));
System.out.println("左端弹出数据后,打印list中的数据:" + jedis.lrange("city", 0, -1)); //获取集合中的所有数据
// 从左端加入数据
jedis.lpush("city", "hangzhou");
System.out.println("左端加入数据后,打印list中的数据:" + jedis.lrange("city", 0, -1));

// 从右端弹出元素
System.out.println("弹出右边元素:" + jedis.rpop("city"));
System.out.println("右端弹出数据后,打印list中的数据:" + jedis.lrange("city", 0, -1)); //获取集合中的所有数据
// 从右端加入数据
jedis.rpush("city", "chengdu");
System.out.println("右端加入数据后,打印list中的数据:" + jedis.lrange("city", 0, -1));


jedis.lset("city", 1, "newValue");
System.out.println("修改值后,打印list中的数据:" + jedis.lrange("city", 0, -1));
System.out.println("list集合的长度:" + jedis.llen("city"));


System.out.println("============对list集合中的数据进行排序==========");
jedis.lpush("sortList", "2", "33", "2", "7", "34", "11", "12", "36", "66");
System.out.println("list排序前数据:" + jedis.lrange("sortList", 0, -1));
System.out.println("list排序前数据:" + jedis.sort("sortList"));
}
}

测试结果:

1
3
4
5
打印list中的数据:[chongqing, shenzhen, beijing, shanghai, shenzhen]
获取指定下标范围的数据:[chongqing, shenzhen]
============对list集合中的数据进行删除==========
删除指定位置的数据:1
打印list中的数据:[chongqing, beijing, shanghai, shenzhen]
OK
打印list中的数据:[chongqing, beijing, shanghai]
============对list集合中的数据进行弹出==========
弹出左边元素:chongqing
左端弹出数据后,打印list中的数据:[beijing, shanghai]
左端加入数据后,打印list中的数据:[hangzhou, beijing, shanghai]
弹出右边元素:shanghai
右端弹出数据后,打印list中的数据:[hangzhou, beijing]
右端加入数据后,打印list中的数据:[hangzhou, beijing, chengdu]
修改值后,打印list中的数据:[hangzhou, newValue, chengdu]
list集合的长度:3
============对list集合中的数据进行排序==========
list排序前数据:[66, 36, 12, 11, 34, 7, 2, 33, 2]
list排序前数据:[2, 2, 7, 11, 12, 33, 34, 36, 66]


7.6、Set的基本操作

public class SetTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();

// 由于set集合是一个不可重复的集合,所以添加重复数据会失败
System.out.println(jedis.sadd("set", "s1", "s2", "s3", "s4", "s5", "s6"));
System.out.println(jedis.sadd("set", "s7"));
System.out.println(jedis.sadd("set", "s7", "s1"));

System.out.println("打印set集合中的所有数据:" + jedis.smembers("set"));


System.out.println("============集合的删除操作==========");

System.out.println("删除set集合中的数据:" + jedis.srem("set", "s1"));
System.out.println("打印set集合中的所有数据:" + jedis.smembers("set"));

System.out.println("批量删除set集合中的数据:" + jedis.srem("set", "s2", "s3"));
System.out.println("打印set集合中的所有数据:" + jedis.smembers("set"));


System.out.println("随机移出set集合中的数据:" + jedis.spop("set"));
System.out.println("打印set集合中的所有数据:" + jedis.smembers("set"));

System.out.println("集合中的个数为:" + jedis.scard("set"));

//前面有随机移出一个元素,所以该测试结果,可以为false也可以为true
System.out.println(jedis.sismember("set", "s4"));


System.out.println("============合并集合操作==========");
jedis.flushDB();
System.out.println(jedis.sadd("set", "s1", "s2", "s3", "s4", "shanghai", "chongqing"));

//将set集合中的s1元素,移入到newSet集合中
System.out.println(jedis.smove("set", "newSet", "s1"));
System.out.println(jedis.smove("set", "newSet", "chongqing"));
System.out.println("被移入元素的对象集合:" + jedis.smembers("newSet"));
System.out.println("移出元素的对象集合:" + jedis.smembers("set"));


System.out.println("============集合运算==========");
jedis.flushDB();
System.out.println(jedis.sadd("set1", "s1", "s4", "s5", "s6"));
System.out.println(jedis.sadd("set2", "s1", "s3", "s5", "s7"));
System.out.println("两个集合的交集" + jedis.sinter("set1", "set2"));
System.out.println("两个集合的并集" + jedis.sunion("set1", "set2"));
System.out.println("两个集合的差集" + jedis.sdiff("set1", "set2"));

// 将两个集合的交集保存到新的集合中
jedis.sinterstore("set3", "set1", "set2");
System.out.println("打印新的交集集合:" + jedis.smembers("set3"));

// 将两个集合的并集保存到新的集合中
jedis.sunionstore("set4", "set1", "set2");
System.out.println("打印新的并集集合:" + jedis.smembers("set4"));
}
}

测试结果:

6
1
0
打印set集合中的所有数据:[s6, s4, s3, s7, s5, s2, s1]
============集合的删除操作==========
删除set集合中的数据:1
打印set集合中的所有数据:[s6, s4, s3, s7, s5, s2]
批量删除set集合中的数据:2
打印set集合中的所有数据:[s6, s4, s7, s5]
随机移出set集合中的数据:s5
打印set集合中的所有数据:[s6, s4, s7]
集合中的个数为:3
true
============合并集合操作==========
6
1
1
被移入元素的对象集合:[s1, chongqing]
移出元素的对象集合:[s4, s3, shanghai, s2]
============集合运算==========
4
4
两个集合的交集[s1, s5]
两个集合的并集[s6, s4, s3, s5, s7, s1]
两个集合的差集[s4, s6]
打印新的交集集合:[s1, s5]
打印新的并集集合:[s6, s4, s3, s5, s7, s1]


7.7、Hash的基本操作

public class HashTest {
public static void main(String[] args) {

Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();


HashMap<String, String> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "3");
map.put("k3", "v3");
map.put("k4", "v4");

System.out.println("将map类型的数据添加到hash中:" + jedis.hmset("hash", map));
System.out.println("打印hash中的数据:" + jedis.hgetAll("hash"));
System.out.println("打印hash中的所有键:" + jedis.hkeys("hash"));
System.out.println("打印hash中的所有值:" + jedis.hvals("hash"));


System.out.println("=============对hash中指定key的value进行数字修改============");
// 给hash中指定的key添加对应的数值(整数、小数)
// 如果对应的key存在,则累加,原value不为数字类型会报错
// 如果对应的key不存在,则新添加一个key-value键值对
System.out.println(jedis.hincrBy("hash", "k5", 9));
System.out.println(jedis.hincrBy("hash", "k2", 9));
System.out.println("打印hash中的数据:" + jedis.hgetAll("hash"));
System.out.println(jedis.hincrByFloat("hash", "k6", 2.5));
System.out.println(jedis.hincrByFloat("hash", "k2", 2.5));
System.out.println("打印hash中的数据:" + jedis.hgetAll("hash"));


System.out.println("=============对hash中指定key的value进行数字修改============");
System.out.println("打印hash中的所有键:" + jedis.hkeys("hash"));
System.out.println("打印hash中的所有值:" + jedis.hvals("hash"));


System.out.println("删除指定的key:" + jedis.hdel("hash", "k1"));
System.out.println("打印hash中的数据:" + jedis.hgetAll("hash"));

System.out.println("获取hash的长度:" + jedis.hlen("hash"));


System.out.println("=============判断hash中是否存在对应的key============");
System.out.println("判断hash中是否存在对应的key:" + jedis.hexists("hash", "k2"));
System.out.println("判断hash中是否存在对应的key:" + jedis.hexists("hash", "k8"));
System.out.println("获取hash中指定key的value值:" + jedis.hmget("hash", "k4"));
}
}

测试结果:

将map类型的数据添加到hash中:OK
打印hash中的数据:{k3=v3, k4=v4, k1=v1, k2=3}
打印hash中的所有键:[k3, k4, k1, k2]
打印hash中的所有值:[v1, v4, v3, 3]
=============对hash中指定key的value进行数字修改============
9
12
打印hash中的数据:{k3=v3, k4=v4, k5=9, k1=v1, k2=12}
2.5
14.5
打印hash中的数据:{k3=v3, k4=v4, k5=9, k6=2.5, k1=v1, k2=14.5}
=============对hash中指定key的value进行数字修改============
打印hash中的所有键:[k3, k4, k5, k6, k1, k2]
打印hash中的所有值:[v1, v4, v3, 14.5, 9, 2.5]
删除指定的key:1
打印hash中的数据:{k3=v3, k4=v4, k5=9, k6=2.5, k2=14.5}
获取hash的长度:5
=============判断hash中是否存在对应的key============
判断hash中是否存在对应的key:true
判断hash中是否存在对应的key:false
获取hash中指定key的value值:[v4]


8、整合SpringBoot

在Spring Boot 2.x以后的版本,jedis被替换为了lettuce

jedis:采用直连的方式,多线程环境下是不安全的。如果想要避免不安全,需要使用jedis pool进行连接,类比于我们的BIO模式。

lettuce:底层使用netty,实例在多个线程中可以共享,是线程安全的,该方式可以减少线程数据。类比于我们的NOI模式。



分析Spring Boot默认的配置类

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

@Bean
// 如果没有redisTemplate这个bean,那么这个类就生效
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}


@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
// 由于String类型是比较常用的,所以单独为其添加一个bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}


根据@EnableConfigurationProperties(RedisProperties.class)注解,可以查看我们能够完成的配置项

Redis能够完成的配置项

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
...
}


8.1、整合测试

1、创建Spring Boot项目,并导入对应的依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--由于我们需要测试,所以需要引入测试的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

2、配置对应的连接信息

#Redis 配置
spring.redis.port=6379
spring.redis.host=127.0.0.1
spring.redis.lettuce.pool.max-active=7

3、编写测试类

@SpringBootTest
public class TestRedis {

@Autowired
private RedisTemplate redisTemplate;

@Test
public void test() {

// 成对操作相关的数据
redisTemplate.opsForValue().set("name","mobian");
System.out.println(redisTemplate.opsForValue().get("name"));

RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
System.out.println(connection.ping());
}
}

分别对应Redis中的数据类型:

redisTemplate.opsForHash – > hash

redisTemplate.opsForList – > list

redisTemplate.opsForSet – > set

redisTemplate.opsForValue – > string

redisTemplate.opsForZSet – > zset

redisTemplate.opsForGeo – > geospatial


8.2、自定义Redis配置类

我们将序列化的方式修改为使用json进行,所以需要引入对应的maven依赖

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.3</version>
</dependency>



编写配置类

@Configuration
public class RedisTemplateConfig {


@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 自定义一个key为String类型,value为Object的Redis配置类
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);

// JSON序列化配置
Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

objectJackson2JsonRedisSerializer.setObjectMapper(mapper);

// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

// Key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);

// Hash的Key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// Hash的Value采用jackson的序列化方式
template.setHashValueSerializer(objectJackson2JsonRedisSerializer);

// Value采用jackson的序列化方式
template.setValueSerializer(objectJackson2JsonRedisSerializer);

return template;
}
}

编写该测试类解决的问题:

// 编写如下的测试代码
@Test
public void test02(){

redisTemplate.opsForValue().set("user",new User("默辨",2));
System.out.println(redisTemplate.opsForValue().get("user"));

}


如果我们的User对象没有进行序列化是会报错的。默认的config配置类中并没有去配置序列化相关的操作,解决办法:

  1. 我们每次将对象数据放入redis中前,进行序列化
  2. 创建实体类实现Serializable接口
  3. 重新修改配置类

Redis(二)_System_02

方式一:过于繁琐

方式二:对应的对象会添加转义字符

Redis(二)_System_03

方式三:重写Redis的配置类,修改其序列化的方式。

Redis(二)_ci_04


8.3、RedisUtil

编写一个工具类,直接封装对应的set、get以及其他常见的操作

​​Redis操作工具类——RedisUtil​​


举报

相关推荐

0 条评论