Redis6大基础数据结构:
1.字符串
2.哈希
3.链表
4.集合
5.有序集合
6.基数
最基础的数据结构,以键值对形式存储于Redis内部,相当于Map,通过键去找值。
如果是操作字符串redisTemplate的keySerializer和valueSerializer参数都必须是stringRedisSerializer,如果是jdk序列化器那么Redis保存的将不会是数字而是异常。
字符串分两种情况:
1.不是数字 2是数字
在Spring中,redisTemplate.opsForValue()返回的对象可以操作简单键值对。
1.不是数字
redisTemplate.opsForValue().set("key1","111"); //保存键为key1,值为111的键值对
//redisTemplate.delete("key1"); //删除键为key1的键值对
//Long length=redisTemplate.opsForValue().size("key1"); //求长度
//String old_value = (String) redisTemplate.opsForValue().getAndSet("key1","new_111");//设置新值并返回旧值
//String str = (String) redisTemplate.opsForValue().get("key1");//通过key获取值
//String str = (String) redisTemplate.opsForValue().get("key1",0,1);//求子串
//int number = redisTemplate.opsForValue().append("key1","append");追加字符串到末尾,返回新串长度
//System.out.println(old_value);
System.out.println(str);
//System.out.print(length);
2.是数字
注意的是:Redis支持简单的运算,如原字段加上1和整数,或者原子段减去1或者整数,还有一个是原字段加上浮点数,原字段可以是浮点数或者整数。注意关于所有的减法,原有值都必须是整数。注意:无法减去一个double类型的数
public class test {
@Test
public void xx(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
//redisTemplate.opsForValue().set("i","9");
//redisTemplate.opsForValue().increment("i",1);//增加值,可以long或者double,但是减法不行
//redisTemplate.opsForValue().increment("i",1.2);
//redisTemplate.getConnectionFactory().getConnection().decr(redisTemplate.getKeySerializer().serialize("i"));
// 减去1这个可以实现
//redisTemplate.getConnectionFactory().getConnection().decrBy(redisTemplate.getKeySerializer().serialize("i")
// ,1);//redisTemplate并没有支持减法。
printValue(redisTemplate,"i");
}
public void printValue(RedisTemplate redisTemplate, String key){
String i =(String) redisTemplate.opsForValue().get(key);
System.out.print(i);
}
}
是String类型的field和value的映射表,实际在Redis内存中都是一个个字符串,hash的键值对在内存中是无序的状态,我们可以通过键找到对应的值
需要将spring提供的默认序列化器,即defaultSerializer修改为字符串序列化器 因为spring对hash结构的操作中会涉及map等其他类操作,需要明确他的规则。
由于spring对redis进行了封装,所以需要对redistemplate的配置项进行修改:
如果相对hash结构制定序列化器可以使用hashKeySerializer和hashValueSerializer为hash的filed和value制定序列化器。
注意:
hmset前提是map保存了多个键值对
hgetall会返回所有的键值对
hincrby和bincrbyFloat都采用increment方法
redisTemplate.opsForHash().values(key)相当于hvals命令,他会返回所有的值保存到List对象中,而redisTemplate.opsForHash().keys(key)相当于hkeys,获取所有的键,保存到Set中
redisTemplate.opsForHash().putall()相当于执行了hmset命令,使用了map,是根据默认的序列化器(字符串),所以会用字符串来进行转化,这样才可以执行对应的数值加法。
public class test {
@Test
public void xx() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
String key = "hash";
Map map = new HashMap();
map.put("f1","val1");
map.put("f2","11");
//redisTemplate.opsForHash().putAll(key,map);//相当于hmset,添加一个map
//redisTemplate.opsForHash().put(key,"f3","val3");相当于hset,添加指定map的一个键值对
//boolean exists = redisTemplate.opsForHash().hasKey(key,"f3");相当于hexists key filed 查询指定map的键值对是否存在
//Map map1 =redisTemplate.opsForHash().entries(key);相当于hgetall 返回指定map全部键值对
//redisTemplate.opsForHash().increment(key,"f2",2);//相当于hincrby,给指定字段加上一个整数
//redisTemplate.opsForHash().increment(key,"f2",2.2);//相当于hincrbyfloat,给指定字段添加一个浮点数,
//Object value = redisTemplate.opsForHash().get(key,"f2");//获取指定map中的键值对
//List list = redisTemplate.opsForHash().values(key);相当于hkeys,获取指定map的values的list集合
//Set keyList = redisTemplate.opsForHash().keys(key);相当于hkeys,获取指定map的key值
/*List list =new ArrayList();
list.add("f1");
list.add("f2");
List values = redisTemplate.opsForHash().multiGet(key,list);相当于hmget,获取指定map的指定多个key对应的value值*/
//System.out.print(values);
// boolean success= redisTemplate.opsForHash().putIfAbsent(key,"f4","val4");相当于hsetnx,如果指定map不存在对应的键,才设置值
//Long result = redisTemplate.opsForHash().delete(key,"f1","f2");相当于hdel,删除指定map中的指定键值对
System.out.print(result);
}
}
一般存储字符串,是有序的,是双向的,可以存储2的32次方-1节点,redis的链表是双向的。
链表操作有进程安全和不安全的
不安全:
public class test {
@Test
public void xx() throws UnsupportedEncodingException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
redisTemplate.delete("list");//删除链表
redisTemplate.opsForList().leftPush("list","node1");//插入节点,从头插入
redisTemplate.opsForList().leftPush("list","node2");//插入节点
/*List nodeList = new ArrayList();
for(int i=0;i<2;i++){
nodeList.add("node:"+i);
}*/
//redisTemplate.opsForList().leftPushAll("list",nodeList);//从左插入链表
//redisTemplate.opsForList().rightPush("list","node4");//从右边插入一个节点
//String node1 =(String) redisTemplate.opsForList().index("list",0);获取下标为0的节点
//String lpop =(String) redisTemplate.opsForList().rightPop("list");从左边弹出一个节点
//String rpop =(String) redisTemplate.opsForList().leftPop("list");从右边弹出一个节点
/*redisTemplate.getConnectionFactory().getConnection().lInsert("list".getBytes("utf-8"),
RedisListCommands.Position.BEFORE,
"node1".getBytes("utf-8"),
"before_node".getBytes("utf-8")
);//使用linsert命令在node1前插入一个节点
*/
/* redisTemplate.getConnectionFactory().getConnection().lInsert("list".getBytes("utf-8"),
RedisListCommands.Position.AFTER,
"node1".getBytes("utf-8"),
"after_node".getBytes("utf-8")
);//使用linsert命令在node1后插入一个节点*/
//redisTemplate.opsForList().leftPushIfPresent("list","head");//判断list是否存在,如果存在则从左边插入head节点
//redisTemplate.opsForList().rightPushIfPresent("list","end");//判断list是否存在,如果存在则从右边插入end节点
//List values = redisTemplate.opsForList().range("list",0,1);//从左到右获取指定的节点
//redisTemplate.opsForList().remove("list",2,"node1");//从左到右删除至多3个node节点
//redisTemplate.opsForList().set("list",0,"new_node");//给下标为0的节点设置新值
printList(redisTemplate,"list");
}
public void printList(RedisTemplate redisTemplate,String key){
Long size = redisTemplate.opsForList().size(key);//获取指定list节点数
List values = redisTemplate.opsForList().range(key,0,size);//获取指定链表的内容
System.out.print(values);
}
}
以上代表的是redisTemplate对redis链表的操作,left代表左操作,right代表右操作,但是有些命令使用redisTemplate并不支持,比如linsert命令,这个时候需要更底层的命令执行。
如果是多值操作,一般都是使用list封装,然后leftPushAll的方式插入redis
对于链表的阻塞操作,也可以实现,为什么要阻塞呢?因为并发的时候防止多个进程对同一块数据进程读写操作,发生数据安全和一致性的问题。
public class test {
@Test
public void xx() throws UnsupportedEncodingException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
redisTemplate.delete("list1");//删除链表 为了重复数据
redisTemplate.delete("list2");//删除链表 为了重复数据
List list1 = new ArrayList();
for(int i=0;i<5;i++){
list1.add("1node:"+i);
}//初始化
redisTemplate.opsForList().leftPushAll("list1",list1);
/* redisTemplate.opsForList().leftPop("list",1, TimeUnit.SECONDS);
redisTemplate.opsForList().rightPop("list",1, TimeUnit.SECONDS);//使用参数超市时间作为阻塞命令区别*/
List list2 = new ArrayList();
for(int i=0;i<5;i++){
list2.add("2node:"+i);
}//初始化
redisTemplate.opsForList().leftPushAll("list2",list2);
//redisTemplate.opsForList().rightPopAndLeftPush("list1","list2");//弹出list1最右边的节点,插入到list2最左边
//redisTemplate.opsForList().rightPopAndLeftPush("list1","list2",1,TimeUnit.SECONDS);//同上,但是他是阻塞的,意思就是并发
printList(redisTemplate,"list1");
printList(redisTemplate,"list2");
}
public void printList(RedisTemplate redisTemplate,String key){
Long size = redisTemplate.opsForList().size(key);//获取指定list节点数
List values = redisTemplate.opsForList().range(key,0,size);//获取指定链表的内容
System.out.println(values);
}
}
阻塞和非阻塞区别:通过设置超时参数进行区分
不是一个线性结构,而是一个哈希表结构,所以他的插入删除和查找的时间复杂度都是O(1)
1.对于集合而言,他的每一个元素都是不能重复的,插入相同记录会失败
2.无序的
3.每一个元素都是String类型的
集合还有额外的操作,如两个或者以上集合的交集,差集和并集
public class test {
@Test
public void xx() throws UnsupportedEncodingException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
Set set = null;
//redisTemplate.boundSetOps("set1").add("v1","v2","v3");将元素加入列表
//redisTemplate.boundSetOps("set2").add("s1","s2","s3");
//Long length = redisTemplate.opsForSet().size("set1");求集合长度
//set = redisTemplate.opsForSet().difference("set2","set1");求set2中有,但是set1中没有的
//set = redisTemplate.opsForSet().intersect("set1","set2");求并集
//boolean exists = redisTemplate.opsForSet().isMember("set1","v1");判断是否存在
//set = redisTemplate.opsForSet().members("set1");//获取集合所有元素
//String name = (String) redisTemplate.opsForSet().pop("set1");//随机弹出一个元素
//String name = (String) redisTemplate.opsForSet().randomMember("set1");随机获取集合中的元素
//List list = redisTemplate.opsForSet().randomMembers("set1",2L);随机获取2个集合的元素
//redisTemplate.opsForSet().remove("set1","v1");//删除一个集合的元素,参数可以有多个
//set = redisTemplate.opsForSet().union("set1","set2");//两个集合的并集
redisTemplate.opsForSet().differenceAndStore("set1","set2","diff_set");//求差集,保存到diff_set
redisTemplate.opsForSet().intersectAndStore("set1","set2","inter_set");//求交集,保存到inter_set
redisTemplate.opsForSet().unionAndStore("set1","set2","union_set");//求并集,保存到union_set
}
}
他有个分数是浮点数根据这个来排序的。
有序集合操作的时候需要封装,DefaultTypedTuple默认实现类实现TypedTuple接口,将带有分数的有序集合的值和分数封装到这个类,通过这个类对象对去对于的值和分数。
对有序集合的封装操作与通过DefaultTypedTuple还有通过RedisZSetCommands下的内部类Range进行范围限定
public class test {
@Test
public void xx() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
Set set1 = new HashSet();
Set set2 = new HashSet();
int j = 9;
for (int i = 0; i < 9; i++) {
j--;
Double score1 = Double.valueOf(i);
String value1 = "x" + i;
Double score2 = Double.valueOf(j);
String value2 = j % 2 == 1 ? "y" + j : "x" + j;
ZSetOperations.TypedTuple typedTuple1 = new DefaultTypedTuple(value1, score1);
set1.add(typedTuple1);
ZSetOperations.TypedTuple typedTuple2 = new DefaultTypedTuple(value2, score2);
set2.add(typedTuple2);
}
redisTemplate.opsForZSet().add("zset1", set1);//插入集合
redisTemplate.opsForZSet().add("zset2", set2);
Long size = null;
//size = redisTemplate.opsForZSet().zCard("zset1");//统计总数
//size =redisTemplate.opsForZSet().count("zset1",3,6);//求3<=score<=6
Set set = null;
//set = redisTemplate.opsForZSet().range("zset1",1,5);//从下标1开始截取5个元素,但是不返回分数,每一个元素都是string
//set = redisTemplate.opsForZSet().rangeWithScores("zset1",0,-1);
// 截取集合所有元素,并且对集合按分数排序,并返回分数,每一个元素是TypeTuple,-1代表全部元素
//printTypedTule(set);
size = redisTemplate.opsForZSet().intersectAndStore("zset1","zset2","inter_zset");//将zset1和zset2两个集合的交集放入集合inter_zset
//区间:
/*RedisZSetCommands.Range range = RedisZSetCommands.Range.range();
range.lt("x8");//小于
range.gt("x1");//大于
range.lte("x8");//小于等于
range.gte("x1");//大于等于
set = redisTemplate.opsForZSet().rangeByLex("zset1",range);
printSet(set);*/
//限制:
RedisZSetCommands.Limit limit = RedisZSetCommands.Limit.limit();
limit.count(4);//限制返回个数为4
limit.offset(5);//限制从第五个开始截取
//set = redisTemplate.opsForZSet().rangeByLex("zset1",range,limit);//求区间内的元素,并限制返回四条。
//Long rank = redisTemplate.opsForZSet().rank("zset1","x0");//求排名 第一个返回0 第二个返回1
//size = redisTemplate.opsForZSet().remove("zset1","x5","x11");//删除元素,返回删除个数
//size =redisTemplate.opsForZSet().removeRange("zset1",1,2);//按照排名删除从0开始,这里删除第二个和第三个
Double dbl = redisTemplate.opsForZSet().incrementScore("zset1","x1",11);//给集合的一个元素的分数加上11 并且返回这个新的分数
System.out.print(dbl);
}
//打印TypedTuple集合
public void printTypedTule(Set set){
if(set!=null&&set.isEmpty()){
return;
}
Iterator iterator = set.iterator();
while(iterator.hasNext()){
ZSetOperations.TypedTuple val = (ZSetOperations.TypedTuple)iterator.next();
System.out.print("{value="+val.getValue()+",score="+val.getScore()+"}\n");
}
}
public void printSet(Set set){
if(set!=null&&set.isEmpty()){
return;
}
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Object val = iterator.next();
System.out.print(val+"\t");
}
System.out.println();
}
}
并不是存储元素,而是给某一个有重复元素的数据集合 评价需要的空间单元数。