1. 程式人生 > >Redis6大基礎資料結構以及在spring中的常用命令

Redis6大基礎資料結構以及在spring中的常用命令

Redis6大基礎資料結構:

1.字串
2.雜湊
3.連結串列
4.集合
5.有序集合
6.基數

字串:

最基礎的資料結構,以鍵值對形式儲存於Redis內部,相當於Map,通過鍵去找值。
如果是操作字串redisTemplate的keySerializer和valueSerializer引數都必須是stringRedisSerializer,如果是jdk序列化器那麼Redis儲存的將不會是數字而是異常。

 <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"></property>
        <property name="keySerializer" ref="stringRedisSerializer"></property>
        <property name="valueSerializer" ref="stringRedisSerializer"></property>
    </bean>

字串分兩種情況:
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的配置項進行修改:

  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"></property>
        <property name="defaultSerializer" ref="stringRedisSerializer"></property>
        <property name="keySerializer" ref="stringRedisSerializer"></property>
        <property name="valueSerializer" ref="stringRedisSerializer"></property>
    </bean>

如果相對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<String,String> map = new HashMap<String,String>();
        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<String,String> 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<String> list = redisTemplate.opsForHash().values(key);相當於hkeys,獲取指定map的values的list集合
        //Set keyList = redisTemplate.opsForHash().keys(key);相當於hkeys,獲取指定map的key值
        /*List<String> list =new ArrayList<String>();
        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<String> nodeList = new ArrayList<String>();
       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<String> list1 = new ArrayList<String>();
        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<String> list2 = new ArrayList<String>();
        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<ZSetOperations.TypedTuple> set1 = new HashSet<ZSetOperations.TypedTuple>();
        Set<ZSetOperations.TypedTuple> set2 = new HashSet<ZSetOperations.TypedTuple>();
        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<ZSetOperations.TypedTuple> 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();
    }
}

還有基數 :

並不是儲存元素,而是給某一個有重複元素的資料集合 評價需要的空間單元數。