瞭解Redis數據結構——鏈表(linked-list)

鏈表結構是Redis中的一個常用的結構,他可以存儲多個字符串,而且它是有序的,能夠存儲40多億個節點。Redis鏈表是雙向的,因此既可以從左到右,也可以從右到左遍歷它存儲的節點,鏈表結構如下:
在這裏插入圖片描述
由於它是雙向鏈表,所以它的讀性能就會相對喪失,而插入和刪除就會顯得很便利。它的增刪操作與雙向鏈表同。

因爲是雙向鏈表結構,所以Redis鏈表命令分爲左操作和右操作兩種命令,左操作意味着是從左到右,右操作意味着是從右到左。Redis關於鏈表的命令如下:

  • lpush key node1 [node2…]:把節點node1加入到鏈表的最左端,如果是node1,node2…noden這樣的加入,那麼鏈表的開頭從左到右的順序是noden…,node2,node1。
  • rpush key node1 [node2…]:把節點node1加入到鏈表的最右端,如果是node1,node2…noden這樣的加入,那麼鏈表的開頭從左到右的順序是noden1,node2…noden。
  • lindex key index:從左往右讀取下標爲index的節點,返回節點字符串,從0開始算。
  • llen key:求鏈表的長度。返回鏈表節點數。
  • lpop key:刪除左邊第一個節點,並將其返回。
  • rpop key:刪除右邊第一個節點,並將其返回。
  • linsert key before|after pivot node:插入一個節點node,並且可以指定在值爲pivot node的結點的前面或者後面。如果list不存在,則報錯;如果沒有值爲對應pivot node,也會插入失敗。
  • lpushx list node:如果存在key爲list的鏈表,則插入節點node,並且作爲從左到右的第一個節點,如果list不存在,則失敗。
  • rpushx list node:如果存在key爲list的鏈表,則插入節點node,並且作爲從右到左的第一個節點,如果list不存在,則失敗。
  • lrange list start end:獲取鏈表從start下標到end下標的節點數,包含start和end下標的值。
  • lrem list count value:如果count爲0,則刪除所有值爲value的節點;如果count不爲0,則先對count取絕對值,假設極爲abs,然後從左到右刪除不大於abs個等於value的節點。
  • lset key index node:設置下標爲index的節點的值爲node。
  • ltrim key start stop:修建鏈表,只保留從start到stop的區間的節點,其餘的都刪除掉,包含start和end的下標的節點會保留。

但是上述的所有方法都是進程不安全的,因爲當我們操作這些命令的時候,其他的Redis的客戶端也可能操作同一個鏈表,這樣就會造成併發數據安全和一致性的問題。爲了解決這些問題,Redis提供了鏈表的阻塞命令,它們在運行的時候,會給鏈表加鎖,以保證操作鏈表的命令安全性。鏈表的阻塞命令如下:

  • blpop key timeout:移出並獲取列表的第一個元素,如果鏈表沒有元素會阻塞鏈表直到等待超時或發現可彈出元素爲止。相對於lpop命令,它是安全的。
  • brpop key timeout:移出並獲取列表的最後一個元素,如果鏈表沒有元素會阻塞鏈表直到等待超時或發現可彈出元素爲止。相對於rpop命令,它是安全的。
  • rpoplpush key src dest:按從左到右的順序,將一個鏈表的最後一個元素移除,並插入到目標鏈表的最左邊。不能設置超時時間。
  • brpoplpush key src dest timeout:按從左到右的順序,將一個鏈表的最後一個元素移除,並插入到目標鏈表的最左邊。並可以設置超時時間。

我們在用一個簡單的demo來實現這些功能,代碼如下:

redisSpring-cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="50" />
        <property name="maxTotal" value="100" />
        <property name="maxWaitMillis" value="20000" />
    </bean>

    <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />

    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="localhost" />
        <property name="port" value="6379" />
        <property name="password" value="123456" />
        <property name="poolConfig" ref="poolConfig" />
    </bean>

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

testList.java

package com.ssm.redis1.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import static com.sun.xml.internal.messaging.saaj.packaging.mime.util.ASCIIUtility.getBytes;

public class testList {
    public static void main(String[] args){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("redisSpring-cfg.xml");
        RedisTemplate redisTemplate=applicationContext.getBean(RedisTemplate.class);
        try {
            //刪除鏈表,以便我們反覆測試
            redisTemplate.delete("list");
            //把node3插入鏈表list
            redisTemplate.opsForList().leftPush("list","node3");
            List<String> nodeList=new ArrayList<String>();
            for(int i=2;i>=1;i--){
                nodeList.add("node"+1);
            }
            //相當於lpush把多個節點從左插入鏈表
            redisTemplate.opsForList().leftPushAll("list",nodeList);
            //從右邊插入一個節點
            redisTemplate.opsForList().rightPush("list","node4");
            //獲取下標爲0的節點
            String node1=(String)redisTemplate.opsForList().index("list",0);
            //獲取鏈表的長度
            long length=redisTemplate.opsForList().size("list");
            //從左邊彈出一個節點
            String lpop=(String)redisTemplate.opsForList().leftPop("list");
            //從右邊彈出一個節點
            String rpop=(String)redisTemplate.opsForList().rightPop("list");
            //注意,需要使用更爲底層的命令才能操作linsert命令
            //使用linsert命令在node2前插入一個節點
            redisTemplate.getConnectionFactory().getConnection().lInsert(getBytes("utf-8"),
                                                                         RedisListCommands.Position.BEFORE,
                                                                         "node2".getBytes("utf-8"),
                                                                         "before_node".getBytes("utf-8"));

            //使用linsert命令在node2後插入一個節點
            redisTemplate.getConnectionFactory().getConnection().lInsert(getBytes("utf-8"),
                                                                         RedisListCommands.Position.BEFORE,
                                                                         "node2".getBytes("utf-8"),
                                                                         "after_node".getBytes("utf-8"));

            //判斷list是否存在,如果存在則從左邊插入head節點
            redisTemplate.opsForList().leftPushIfPresent("list","head");
            //判斷list是否存在,如果存在則從右邊邊插入end節點
            redisTemplate.opsForList().rightPushIfPresent("list","end");
            //從左到右,或者下襬哦爲0搭配10的節點元素
            List valueList=redisTemplate.opsForList().range("list",0,10);
            nodeList.clear();
            for(int i=1;i<3;i++){
                nodeList.add("node");
            }
            //在鏈表左邊插入三個值爲node的節點
            redisTemplate.opsForList().leftPushAll("list",nodeList);
            //從左到右刪除至多三個node節點
            redisTemplate.opsForList().remove("list",3,"node");
            //給鏈表下標爲0的節點設置新值
            redisTemplate.opsForList().set("list",0,"new_head_node");
        }catch (UnsupportedEncodingException ex){
            ex.printStackTrace();
        }
        //打印鏈表數據
        printList(redisTemplate,"list");
    }

    private static void printList(RedisTemplate redisTemplate, String list) {
        //鏈表長度
        Long size=redisTemplate.opsForList().size(list);
        //獲取整個鏈表的值
        List valueList=redisTemplate.opsForList().range(list,0,size);
        //打印
        System.out.println(valueList);
    }
}

運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章