开发工具:
- Redis3.2;
- IDEA;
- JDK11;
- Gradle4.8;
使用数据结构常用命令:
1.创建项目,开启Redis服务端,导入相关坐标;
重要坐标如下:
implementation('org.springframework.boot:spring-boot-starter-data-redis')
// Redis客户端,用于执行数据结构常用命令
implementation('redis.clients:jedis:2.9.0')
2.添加配置类(SpringBoot默认自动配置,没有特殊需求则可以省略);
配置类代码如下:
package com.lsm1998.redis.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
/**
* 作者:刘时明
* 日期:2018/11/12
* 时间:21:13
* 说明:
*/
@Configuration
public class RedisConfig
{
@Bean
public RedisTemplate initRedisTemplate()
{
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大空闲数
poolConfig.setMaxIdle(5);
// 最大连接数
poolConfig.setMaxTotal(30);
// 最大等待毫秒数
poolConfig.setMaxWaitMillis(1000 * 20);
// 连接工厂
JedisConnectionFactory factory=new JedisConnectionFactory(poolConfig);
// 后初始化方法
factory.afterPropertiesSet();
// 序列化器
//RedisSerializer jdkSerializer=new JdkSerializationRedisSerializer();
RedisSerializer stringSerializer=new StringRedisSerializer();
// 定义RedisTemplate,设置连接工厂
RedisTemplate redisTemplate=new RedisTemplate();
redisTemplate.setConnectionFactory(factory);
// 设置序列化器
redisTemplate.setDefaultSerializer(stringSerializer);
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
return redisTemplate;
}
}
3.具体程序如下:
package com.lsm1998.redis.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.BinaryClient;
import redis.clients.jedis.Jedis;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 作者:刘时明
* 日期:2018/11/12
* 时间:21:49
* 说明:Redis基本数据类型
*/
@RestController
public class TestController
{
@Autowired
private RedisTemplate redisTemplate;
/**
* 字符数据的常用命令
*
* @return
*/
@GetMapping("test1")
public Object test1()
{
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 设置键值对
jedis.set("str_1", "新恒结衣");
// 返回值的字符长度
System.out.println("值的长度=" + jedis.strlen("str_1"));
// 获取值
System.out.println("str_1=" + jedis.get("str_1"));
// 删除键值对
//jedis.del("str_1");
// 修改值,并返回新值
String str = jedis.getSet("str_1", "泰勒斯威夫特");
System.out.println("str_1旧值=" + str);
System.out.println("str_1新值=" + jedis.get("str_1"));
// 获取子串
System.out.println("str_1子串=" + jedis.getrange("str_1", 0, jedis.strlen("str_1") / 2));
// 追加字符,只支持末尾追加
jedis.append("str_1", "-Red");
System.out.println("str_1=" + jedis.get("str_1"));
// 字节数组组成的键值对
jedis.getSet("str_2".getBytes(), "新恒结衣".getBytes());
System.out.println("str_2=" + jedis.get("str_2".getBytes()));
// 关闭连接
jedis.close();
return "ok";
}
/**
* 字符数据的计算命令
*
* @return
*/
@GetMapping("test2")
public Object test2()
{
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("num", "0");
System.out.println("num=" + jedis.get("num"));
// 指定key值自增
jedis.incr("num");
System.out.println("num=" + jedis.get("num"));
// 指定增加
jedis.incrBy("num", 10L);
System.out.println("num=" + jedis.get("num"));
// 指定key值自减
jedis.decr("num");
System.out.println("num=" + jedis.get("num"));
// 指定减少
jedis.decrBy("num", 5L);
System.out.println("num=" + jedis.get("num"));
// 操作浮点数,此方法没有对应的decrByFloat方法,只需加一个负号即可取得相同效果
jedis.incrByFloat("num", 3.14);
System.out.println("num=" + jedis.get("num"));
jedis.incrByFloat("num", -3.14);
System.out.println("num=" + jedis.get("num"));
// 关闭连接
jedis.close();
return "ok";
}
/**
* 哈希结构的常用命令
*
* @return
*/
@GetMapping("test3")
public Object test3()
{
HashOperations hashOperations = redisTemplate.opsForHash();
// 指定一个key
String key = "hash";
// 一个key下保存多个键值对
hashOperations.put(key, "k1", "新恒结衣");
hashOperations.put(key, "k2", new byte[1024]);
try
{
// 此处会抛出异常
hashOperations.put(key, "k3", new Object());
} catch (Exception e)
{
System.err.println("该对象不可以被序列化");
}
// Jedis操作哈希数据结构,与HashOperations有一定区别
Jedis jedis = new Jedis("127.0.0.1", 6379);
// jedis.hset保存的是field+value,而与HashOperations则是hashKey+value
jedis.hset(key, "j1", "泰勒斯威夫特");
// 编译错误,只支持字符串类型
//jedis.hset(key, "j2", new ArrayList<>());
System.out.println("当前大小=" + hashOperations.size(key));
// 通过遍历key来输出所有的键值对
hashOperations.keys(key).forEach(k -> System.out.println("key=" + k + ",value=" + hashOperations.get(key, k)));
return hashOperations.get(key, "k1");
}
/**
* 有序集合结构的常用命令
*
* @return
*/
@GetMapping("test4")
public Object test4()
{
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
String key = "key";
// 添加元素,key-value-score形式,score代表分数,用于排序
zSetOperations.add(key, 59, 2.5);
zSetOperations.add(key, new String("新恒结衣"), 3.0);
ZSetOperations.TypedTuple<Object> typedTuple1 = new DefaultTypedTuple<>("牛顿", 100.0);
ZSetOperations.TypedTuple<Object> typedTuple2 = new DefaultTypedTuple<>("伯努利", 98.0);
Set<ZSetOperations.TypedTuple<Object>> typedTupleSet = new HashSet<>();
typedTupleSet.add(typedTuple1);
typedTupleSet.add(typedTuple2);
// 添加元素,key-Set形式
zSetOperations.add(key, typedTupleSet);
long size = zSetOperations.zCard(key);
// 此时应该有4个元素,因为key-Set其底层会拆为key-value-score形式
System.out.println("元素个数=" + size);
// 获取所有元素
Set temp = zSetOperations.range(key, 0, size - 1);
long count = zSetOperations.count(key, 80, 100);
System.out.println("分数80到100之间元素个数=" + count);
// 返回分数80到100之间的元素集合
// Set temp = zSetOperations.rangeByScore(key, 80, 100);
// 删除元素,可以同时传入多个值
zSetOperations.remove(key, 59);
// 根据分数范围进行删除
// zSetOperations.removeRangeByScore(key,80,100);
temp.forEach(e -> System.out.println(e));
// Jedis操作ZSet类型
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.zadd(key, -10.0, "hello");
Set temp2 = jedis.zrevrange(key, 0, jedis.zcard(key) - 1);
System.out.println("**********华丽的分隔线**********");
// 此时长度为1
// 与HashOperations相似,Jedis操作的ZSet和ZSetOperations操作的ZSet是有区别的,二者不能混用
System.out.println("长度=" + jedis.zcard(key));
temp2.forEach(e -> System.out.println(e));
// 计算多个有序集合的并集
String destKey = "dest_key";
// 添加一个同value同score的元素
redisTemplate.opsForZSet().add(destKey, "新恒结衣", 3.0);
// 添加一个同value但不同score的元素
redisTemplate.opsForZSet().add(destKey, "新恒结衣", 3.1);
// 此处结果与unionAndStore(key, key, destKey)一致,结果返回3,代表有3个value相同的元素
long l = zSetOperations.unionAndStore(key, destKey, destKey);
System.out.println("相同value元素数量=" + l);
return "ok";
}
/**
* 链表结构
*
* @return
*/
@GetMapping("test5")
public Object test5()
{
/**
* Jedis操作链表
*/
Jedis jedis = new Jedis("127.0.0.1", 6379);
String key = "j_key";
// 左端插入
jedis.lpush(key, "左一", "左二", "左三");
// 右端插入
jedis.rpush(key, "右一", "右二", "右三");
// 下标获取
System.out.println("链表头部元素=" + jedis.lindex(key, 0));
// 最右侧删除
jedis.rpop(key);
// 最左侧删除
jedis.lpop(key);
// 更新元素
jedis.lset(key, 1, "新值");
// 尝试最左侧插入,如果key不存在则失败,返回0
long result = jedis.lpushx(key + "007", "尝试");
System.out.println("result=" + result);
// 从左到右删除1个value为‘右二’的元素,0则代表所有
jedis.lrem(key, 1, "右二");
// 元素插入,指定BEFORE或者AFTER和元素
jedis.linsert(key, BinaryClient.LIST_POSITION.AFTER, "新值", "插队元素");
// 链表裁剪,只保留下标0到5的,裁剪后长度必为end-strrt+1,如果少了则循环补值
jedis.ltrim(key, 0, 5);
// 打印0到5的节点值,此时数据为[左二, 新值, 插队元素, 新值, 插队元素, 新值],奇怪的是头元素不会补值
System.out.println(jedis.lrange(key, 0, 5));
System.out.println("jedis链表长度=" + jedis.llen(key));
// 阻塞命令,试图删除最左侧元素,如果没有则等待,直到删除成功或者超时,并返回key和被删除的元素
List<String> list = jedis.blpop(10 * 1000, key);
System.out.println("list=" + list);
/**
* ListOperations操作链表
* 写多了就会发现redisTemplate包装类和jedis操作的功能大体一致,但效果不互通
*/
ListOperations listOperations = redisTemplate.opsForList();
// 删除前两个‘hello’
listOperations.remove(key, 2, "hello");
System.out.println("redisTemplate包装类链表长度=" + listOperations.size(key));
return "ok";
}
/**
* Set结构
*
* @return
*/
@GetMapping("test6")
public Object test6()
{
Jedis jedis = new Jedis("127.0.0.1", 6379);
String key = "j_key";
// 插入元素,由于是散列结构,重复元素会覆盖
jedis.sadd(key, "新恒结衣", "Aragaki", "新恒结衣");
// 统计个数
System.out.println("元素个数=" + jedis.scard(key));
// 判断元素是否存在
System.out.println("一个元素是否存在?" + jedis.sismember(key, "新恒结衣"));
// 获取所有键
Set<String> keys = jedis.keys(key);
keys.forEach(e -> System.out.println(e));
// 获取所有值
jedis.smembers(key).forEach(e -> System.out.println(e));
// 随机弹出一个元素
System.out.println(jedis.spop(key));
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("key_1", "e1", "e2", "e3");
setOperations.add("key_2", "e0", "e1", "e2");
// 求差集
setOperations.difference("key_1", "key_2").forEach(e -> System.out.println(e));
// 求并集
setOperations.intersect("key_1", "key_2").forEach(e -> System.out.println(e));
return "ok";
}
/**
* 基数结构
*
* @return
*/
@GetMapping("test7")
public Object test7()
{
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 添加一个元素
jedis.pfadd("key", "新恒结衣");
// 返回基数值
System.out.println("基数值=" + jedis.pfcount("key"));
// 保存多个基数值
jedis.pfmerge("key2", "新恒结衣", "Aragaki");
HyperLogLogOperations hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
hyperLogLogOperations.add("key", "新恒结衣");
hyperLogLogOperations.size("key");
hyperLogLogOperations.delete("key");
return "ok";
}
}
Redis的6种数据结构总结:
- 有两种不同的方式可以操作数据,Jedis客户端执行与redisTemplate包装类均有类似的操作方法,但操作的数据不互通;
- 都可以实现基本的增删改查;
- 除了Hash结构和链表结构外,其余结构都具有一定的运算支持,自增或者求并集等;
- 基数类型是最晦涩难懂的,他的原理是压缩数据,将重复的数据转为不重复的基数,减少内存占用;