1. 程式人生 > >Redis資料型別之LIST型別

Redis資料型別之LIST型別

list型別-特點

list 是一個連結串列結構,主要功能是 push、pop、獲取一個範圍的所有值等等,操作中 key理解為連結串列的名字。
Redis 的 list型別其實就是一個每個子元素都是 string 型別的雙向連結串列。連結串列的最大長度是(2的 32 次方)。我們可以通過 push,pop 操作從連結串列的頭部或者尾部新增刪除元素。這使得 list既可以用作棧,也可以用作佇列。
有意思的是 list 的 pop 操作還有阻塞版本的,當我們[lr]pop 一個 list 物件時,如果 list 是空,或者不存在,會立即返回 nil。但是阻塞版本的 b[lr]pop 可以則可以阻塞,當然可以加超時時間,超時後也會返回 nil。為什麼要阻塞版本的 pop 呢,主要是為了避免輪詢。舉個簡單的例子如果我們用 list 來實現一個工作佇列。 執行任務的 thread 可以呼叫阻塞版本的 pop 去獲取任務這樣就可以避免輪詢去檢查是否有任務存在。當任務來時候工作執行緒可以立即返回,也可以避免輪詢帶來的延遲。

list型別-應用場景

Redis list應用場景非常多,也是Redis最重要的資料結構之一,比如微博的關注列表,粉絲列表等都可以用Redis的list結構來實現;部落格實現中,可為每篇日誌設定一個list,在該list中推入進部落格評論;也可以使用Redis list實現訊息佇列。
list中的資料邏輯上存在順序關係(陣列的下表),適合儲存帶有順序特性(空間、時間屬性)的資料。比如,記錄使用者在網站上瀏覽商品id,這種帶時間屬性的資料可以用來分析使用者的購物行為。
list作為queue,一端加入資料,另一端讀取資料,最典型的就是生產者/消費者模型,比如:商品秒殺,從已經初始化佇列中pop出資料,直到佇列為空。
list作為stack,只操作list的一端,資料先進後出。

list常見命令

  1. LPUSH
    LPUSH key value [value …]
    將一個或多個值 value 插入到列表 key 的表頭如果有多個 value 值,那麼各個 value 值按從左到右的順序依次插入到表頭:比如說,對空列表 mylist執行命令 LPUSH mylist a b c ,列表的值將是 c b a ,這等同於原子性地執行 LPUSH mylist a 、LPUSH mylist b 和 LPUSH mylist c 三個命令。
    如果 key 不存在,一個空列表會被建立並執行LPUSH 操作。
    當 key 存在但不是列表型別時,返回一個錯誤。
    時間複雜度: O(1)
    返回值: 執行LPUSH 命令後,列表的長度。

    127.0.0.1:6379[15]> LPUSH languages python
    (integer) 1
    127.0.0.1:6379[15]> LPUSH languages python
    (integer) 2
    127.0.0.1:6379[15]> LRANGE languages 0 -1
    1) "python"
    2) "python"
    127.0.0.1:6379[15]> LPUSH mylist a b c
    (integer) 3
    127.0.0.1:6379[15]> LRANGE mylist 0 -1
    1) "c"
    2) "b"
    3) "a"
  2. LPUSHX
    LPUSHX key value
    將值 value 插入到列表 key 的表頭,當且僅當 key 存在並且是一個列表。
    和LPUSH 命令相反,當 key 不存在時,LPUSHX 命令什麼也不做。
    時間複雜度: O(1)
    返回值: LPUSHX 命令執行之後,表的長度。

    127.0.0.1:6379[15]> LLEN greet
    (integer) 0
    127.0.0.1:6379[15]> LPUSHX greet "hello"
    (integer) 0
    127.0.0.1:6379[15]> LPUSH greet "hello"
    (integer) 1
    127.0.0.1:6379[15]> LPUSHX greet "good morning"
    (integer) 2
    127.0.0.1:6379[15]> LRANGE greet 0 -1
    1) "good morning"
    2) "hello"
  3. RPUSH
    RPUSH key value [value …]
    將一個或多個值 value 插入到列表 key 的表尾 (最右邊)。
    如果有多個 value 值,那麼各個 value 值按從左到右的順序依次插入到表尾:比如對一個空列表 mylist執行 RPUSH mylist a b c ,得出的結果列表為 a b c ,等同於執行命令 RPUSH mylist a 、RPUSH mylist b 、RPUSH mylist c 。
    如果 key 不存在,一個空列表會被建立並執行RPUSH 操作。
    當 key 存在但不是列表型別時,返回一個錯誤。
    時間複雜度: O(1)
    返回值: 執行RPUSH 操作後,表的長度。

    127.0.0.1:6379[15]> RPUSH languages c
    (integer) 1
    127.0.0.1:6379[15]> RPUSH languages c
    (integer) 2
    127.0.0.1:6379[15]> LRANGE languages 0 -1
    1) "c"
    2) "c"
    127.0.0.1:6379[15]> RPUSH mylist a b c
    (integer) 3
    127.0.0.1:6379[15]> LRANGE mylist 0 -1
    1) "a"
    2) "b"
    3) "c"
  4. RPUSHX
    RPUSHX key value
    將值 value 插入到列表 key 的表尾,當且僅當 key 存在並且是一個列表。
    和RPUSH 命令相反,當 key 不存在時,RPUSHX 命令什麼也不做。
    時間複雜度: O(1)
    返回值: RPUSHX 命令執行之後,表的長度。

    127.0.0.1:6379[15]> LLEN greet
    (integer) 0
    127.0.0.1:6379[15]> LPUSHX greet "hello" 
    (integer) 0
    127.0.0.1:6379[15]> LPUSH greet "hello" 
    (integer) 1
    127.0.0.1:6379[15]> LPUSHX greet "good morning"
    (integer) 2
    127.0.0.1:6379[15]> LRANGE greet 0 -1
    1) "good morning"
    2) "hello"
  5. LPOP
    LPOP key
    移除並返回列表 key 的頭元素。
    時間複雜度: O(1)
    返回值:
      列表的頭元素。
      當 key 不存在時,返回 nil 。

    127.0.0.1:6379[15]> LLEN course
    (integer) 0
    127.0.0.1:6379[15]> RPUSH course algorithm001
    (integer) 1
    127.0.0.1:6379[15]> RPUSH course c++101
    (integer) 2
    127.0.0.1:6379[15]> LPOP course
    "algorithm001"
  6. RPOP
    RPOP key
    移除並返回列表 key 的尾元素。
    時間複雜度: O(1)
    返回值:
      列表的尾元素。
      當 key 不存在時,返回 nil 。

    127.0.0.1:6379[15]> RPUSH mylist "one"
    (integer) 1
    127.0.0.1:6379[15]> RPUSH mylist "two"
    (integer) 2
    127.0.0.1:6379[15]> RPUSH mylist "three"
    (integer) 3
    127.0.0.1:6379[15]> RPOP mylist
    "three"
    127.0.0.1:6379[15]> LRANGE mylist 0 -1
    1) "one"
    2) "two"
  7. BLPOP
    BLPOP key [key …] timeout
    BLPOP 是列表的阻塞式 (blocking) 彈出原語。
    它是LPOP 命令的阻塞版本,當給定列表內沒有任何元素可供彈出的時候,連線將被BLPOP 命令阻塞,直到等待超時或發現可彈出元素為止。
    當給定多個 key 引數時,按引數 key 的先後順序依次檢查各個列表,彈出第一個非空列表的頭元素。非阻塞行為當BLPOP 被呼叫時,如果給定 key 內至少有一個非空列表,那麼彈出遇到的第一個非空列表的頭元素,並和被彈出元素所屬的列表的名字一起,組成結果返回給呼叫者。
    當存在多個給定 key 時,BLPOP 按給定 key 引數排列的先後順序,依次檢查各個列表。
    假設現在有 job 、command 和 request 三個列表,其中 job 不存在,command 和 request 都持有非空列表。考慮以下命令:
    BLPOP job command request 0
    BLPOP 保證返回的元素來自 command,因為它是按” 查詢 job -> 查詢 command -> 查詢 request “這樣的順序,第一個找到的非空列表。

    127.0.0.1:6379[15]> DEL job command request
    (integer) 0
    127.0.0.1:6379[15]> LPUSH command "update system..."
    (integer) 1
    127.0.0.1:6379[15]> LPUSH request "visit page" 
    (integer) 1
    127.0.0.1:6379[15]> BLPOP job command request 0
    1) "command"
    2) "update system..."
  8. BRPOP
    BRPOP key [key …] timeout
    BRPOP 是列表的阻塞式 (blocking) 彈出原語。
    它是RPOP 命令的阻塞版本,當給定列表內沒有任何元素可供彈出的時候,連線將被BRPOP 命令阻塞,直 到等待超時或發現可彈出元素為止。
    當給定多個 key 引數時,按引數 key 的先後順序依次檢查各個列表,彈出第一個非空列表的尾部元素。
    關於阻塞操作的更多資訊,請檢視BLPOP 命令,BRPOP 除了彈出元素的位置和BLPOP 不同之外,其他表現一致。
    時間複雜度: O(1)
    返回值:
      假如在指定時間內沒有任何元素被彈出,則返回一個 nil 和等待時長。
      反之,返回一個含有兩個元素的列表,第一個元素是被彈出元素所屬的 key ,第二個元素是被彈出元素的值。

    127.0.0.1:6379[15]> LLEN course
    (integer) 0
    127.0.0.1:6379[15]> RPUSH course algorithm001
    (integer) 1
    127.0.0.1:6379[15]> RPUSH course c++101
    (integer) 2
    127.0.0.1:6379[15]> BRPOP course 30
    1) "course"
    2) "c++101"
  9. LINSERT
    LINSERT key BEFORE|AFTER pivot value
    將值 value 插入到列表 key 當中,位於值 pivot 之前或之後。
    當 pivot 不存在於列表 key 時,不執行任何操作。
    當 key 不存在時,key 被視為空列表,不執行任何操作。
    如果 key 不是列表型別,返回一個錯誤。
    時間複雜度: O(N),N 為尋找 pivot 過程中經過的元素數量。
    返回值:
      如果命令執行成功,返回插入操作完成之後,列表的長度。
      如果沒有找到 pivot ,返回 -1 。
      如果 key 不存在或為空列表,返回 0 。

    127.0.0.1:6379[15]> RPUSH mylist "Hello"
    (integer) 1
    127.0.0.1:6379[15]> RPUSH mylist "World"
    (integer) 2
    127.0.0.1:6379[15]> LINSERT mylist BEFORE "World" "There"
    (integer) 3
    127.0.0.1:6379[15]> LRANGE mylist 0 -1
    1) "Hello"
    2) "There"
    3) "World"
    127.0.0.1:6379[15]> LINSERT mylist BEFORE "go" "let's"
    (integer) -1
    127.0.0.1:6379[15]> EXISTS fake_list
    (integer) 0
    127.0.0.1:6379[15]> LINSERT fake_list BEFORE "nono" "gogogog"
    (integer) 0
  10. LRANGE
    LRANGE key start stop
    返回列表 key 中指定區間內的元素,區間以偏移量 start 和 stop 指定。
    下標 (index) 引數 start 和 stop 都以 0 為底,也就是說,以 0 表示列表的第一個元素,以 1 表示列表的第二個元素,以此類推。
    你也可以使用負數下標,以 -1 表示列表的最後一個元素,-2 表示列表的倒數第二個元素,以此類推。
    注意 LRANGE 命令和程式語言區間函式的區別假如你有一個包含一百個元素的列表,對該列表執行 LRANGE list 0 10 ,結果是一個包含 11 個元素的列表,這表明 stop下標也在LRANGE命令的取值範圍之內 (閉區間),這和某些語言的區間函式可能不一致,比如 Ruby 的 Range.new 、Array#slice 和 Python 的 range() 函式。
    超出範圍的下標值不會引起錯誤。
    如果 start 下標比列表的最大下標 end ( LLEN list 減去 1 ) 還要大,那麼LRANGE 返回一個空列表。
    如果 stop 下標比 end 下標還要大,Redis 將 stop 的值設定為 end 。
    時間複雜度: O(S+N),S 為偏移量 start ,N 為指定區間內元素的數量。
    返回值: 一個列表,包含指定區間內的元素。

    127.0.0.1:6379[15]>  RPUSH fp-language lisp
    (integer) 1
    127.0.0.1:6379[15]>  LRANGE fp-language 0 0
    1) "lisp"
    127.0.0.1:6379[15]>  RPUSH fp-language scheme
    (integer) 2
    127.0.0.1:6379[15]>  LRANGE fp-language 0 1
    1) "lisp"
    2) "scheme"
  11. LREM
    LREM key count value
    根據引數 count 的值,移除列表中與引數 value 相等的元素。
    count 的值可以是以下幾種:
      count > 0 : 從表頭開始向表尾搜尋,移除與 value 相等的元素,數量為 count 。
      count < 0 : 從表尾開始向表頭搜尋,移除與 value 相等的元素,數量為 count 的絕對值。
      count = 0 : 移除表中所有與 value 相等的值。
    時間複雜度: O(N),N 為列表的長度。
    返回值:
      被移除元素的數量。
      因為不存在的 key 被視作空表 (empty list),所以當 key 不存在時,LREM 命令總是返回 0 。

    127.0.0.1:6379[15]> LPUSH greet "morning"
    (integer) 1
    127.0.0.1:6379[15]> LPUSH greet "hello"
    (integer) 2
    127.0.0.1:6379[15]> LPUSH greet "morning"
    (integer) 3
    127.0.0.1:6379[15]> LPUSH greet "hello"
    (integer) 4
    127.0.0.1:6379[15]> LPUSH greet "morning"
    (integer) 5
    127.0.0.1:6379[15]> LRANGE greet 0 4 
    1) "morning"
    2) "hello"
    3) "morning"
    4) "hello"
    5) "morning"
    127.0.0.1:6379[15]> LREM greet 2 morning
    (integer) 2
    127.0.0.1:6379[15]> LLEN greet
    (integer) 3
    127.0.0.1:6379[15]> LRANGE greet 0 2
    1) "hello"
    2) "hello"
    3) "morning"
    127.0.0.1:6379[15]> LREM greet -1 morning 
    (integer) 1
    127.0.0.1:6379[15]> LLEN greet
    (integer) 2
    127.0.0.1:6379[15]> LRANGE greet 0 1
    1) "hello"
    2) "hello"
    127.0.0.1:6379[15]> LREM greet 0 hello
    (integer) 2
    127.0.0.1:6379[15]> LLEN greet
    (integer) 0
  12. LTRIM
    LTRIM key start stop
    對一個列表進行修剪 (trim),就是說,讓列表只保留指定區間內的元素,不在指定區間之內的元素都將被刪除。
    舉個例子,執行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三個元素,其餘元素全部刪除。
    下標 (index) 引數 start 和 stop 都以 0 為底,也就是說,以 0 表示列表的第一個元素,以 1 表示列表的第二個元素,以此類推。
    你也可以使用負數下標,以 -1 表示列表的最後一個元素,-2 表示列表的倒數第二個元素,以此類推。當 key 不是列表型別時,返回一個錯誤。
    LTRIM 命令通常和LPUSH 命令或RPUSH 命令配合使用,舉個例子:
    LPUSH log newest_log
    LTRIM log 0 99
    這個例子模擬了一個日誌程式,每次將最新日誌 newest_log 放到 log 列表中,並且只保留最新的 100 項。注意當這樣使用 LTRIM 命令時,時間複雜度是 O(1),因為平均情況下,每次只有一個元素被移除。
    注意 LTRIM 命令和程式語言區間函式的區別
    假如你有一個包含一百個元素的列表 list ,對該列表執行 LTRIM list 0 10 ,結果是一個包含 11 個元素的列表,這表明 stop 下標也在LTRIM 命令的取值範圍之內 (閉區間),這和某些語言的區間函式可能不一致,比如 Ruby 的 Range.new 、Array#slice 和 Python 的 range() 函式。
    超出範圍的下標值不會引起錯誤。
    如果 start 下標比列表的最大下標 end ( LLEN list 減去 1 ) 還要大,或者 start > stop ,LTRIM 返回一個空列表 (因為LTRIM 已經將整個列表清空)。
    如果 stop 下標比 end 下標還要大,Redis 將 stop 的值設定為 end 。
    時間複雜度: O(N),N 為被移除的元素的數量。
    返回值:命令執行成功時,返回 ok 。

    
    # 情況 1: 常見情況, start 和 stop 都在列表的索引範圍之內
    
    127.0.0.1:6379[15]> LPUSH alpha h e l l o
    (integer) 5
    127.0.0.1:6379[15]> LRANGE alpha 0 -1
    1) "o"
    2) "l"
    3) "l"
    4) "e"
    5) "h"
    127.0.0.1:6379[15]>  LTRIM alpha 1 -1 
    OK
    127.0.0.1:6379[15]> LRANGE alpha 0 -1   # o被刪除了
    1) "l"
    2) "l"
    3) "e"
    4) "h"
    
    # 情況 2stop 比列表的最大下標還要大
    
    127.0.0.1:6379[15]>  LTRIM alpha 1 10086 
    OK
    127.0.0.1:6379[15]>  LRANGE alpha 0 -1 # 只有索引 0 上的元素 "l" 被刪除了,其他元素還在
    1) "l"
    2) "e"
    3) "h"
    127.0.0.1:6379[15]> LTRIM alpha 10086 123321
    OK
    
    # 情況 4: start 和 stop 都比列表的最大下標要大,並且 start > stop
    
    127.0.0.1:6379[15]> LRANGE alpha 0 -1
    (empty list or set)
    127.0.0.1:6379[15]> RPUSH new-alpha "h" "e" "l" "l" "o"
    (integer) 5
    127.0.0.1:6379[15]> LRANGE new-alpha 0 -1
    1) "h"
    2) "e"
    3) "l"
    4) "l"
    5) "o"
    127.0.0.1:6379[15]> LTRIM new-alpha 123321 10086 
    OK
    127.0.0.1:6379[15]> LRANGE new-alpha 0 -1 
    (empty list or set)
  13. LSET
    LSET key index value
    將列表 key 下標為 index 的元素的值設定為 value 。
    當 index 引數超出範圍,或對一個空列表 ( key 不存在) 進行LSET 時,返回一個錯誤。
    時間複雜度:
      對頭元素或尾元素進行LSET 操作,複雜度為 O(1)。
      其他情況下,為 O(N),N 為列表的長度。
    返回值: 操作成功返回 ok ,否則返回錯誤資訊。

    127.0.0.1:6379[15]> EXISTS list
    (integer) 0
    127.0.0.1:6379[15]> LSET list 0 item
    (error) ERR no such key
    127.0.0.1:6379[15]> LPUSH job "cook food"
    (integer) 1
    127.0.0.1:6379[15]> LRANGE job 0 0
    1) "cook food"
    127.0.0.1:6379[15]> LSET job 0 "play game"
    OK
    127.0.0.1:6379[15]> LRANGE job 0 0
    1) "play game"
    127.0.0.1:6379[15]> LPUSH list a
    (integer) 1
    127.0.0.1:6379[15]> llen list
    (integer) 1
    127.0.0.1:6379[15]> LSET list 3 b
    (error) ERR index out of range
  14. LINDEX
    LINDEX key index
    返回列表 key 中,下標為 index 的元素。
    下標 (index) 引數 start 和 stop 都以 0 為底,也就是說,以 0 表示列表的第一個元素,以 1 表示列表的第二個元素,以此類推。
    你也可以使用負數下標,以 -1 表示列表的最後一個元素,-2 表示列表的倒數第二個元素,以此類推。如果 key 不是列表型別,返回一個錯誤。
    時間複雜度:
      O(N),N 為到達下標 index 過程中經過的元素數量。
      因此,對列表的頭元素和尾元素執行LINDEX 命令,複雜度為 O(1)。
    返回值:
      列表中下標為 index 的元素。
      如果 index 引數的值不在列表的區間範圍內 (out of range),返回 nil 。

    127.0.0.1:6379[15]>  LPUSH mylist "World"
    (integer) 1
    127.0.0.1:6379[15]> LPUSH mylist "Hello"
    (integer) 2
    127.0.0.1:6379[15]>  LINDEX mylist 0
    "Hello"
    127.0.0.1:6379[15]>  LINDEX mylist -1
    "World"
    127.0.0.1:6379[15]>  LINDEX mylist 3 
    (nil)
  15. LLEN
    LLEN key
    返回列表 key 的長度。
    如果 key 不存在,則 key 被解釋為一個空列表,返回 0 .
    如果 key 不是列表型別,返回一個錯誤。
    時間複雜度: O(1)
    返回值: 列表 key 的長度。

    127.0.0.1:6379[15]> LLEN job
    (integer) 0
    127.0.0.1:6379[15]> LPUSH job "cook food"
    (integer) 1
    127.0.0.1:6379[15]> LPUSH job "have lunch"
    (integer) 2
    127.0.0.1:6379[15]> LLEN job
    (integer) 2
  16. RPOPLPUSH
    RPOPLPUSH source destination
    命令RPOPLPUSH 在一個原子時間內,執行以下兩個動作:
      將列表 source 中的最後一個元素 (尾元素) 彈出,並返回給客戶端。
      將 source 彈出的元素插入到列表 destination ,作為 destination 列表的的頭元素。
    舉個例子,你有兩個列表 source 和 destination ,source 列表有元素 a, b, c ,destination 列表有元素 x, y, z ,執行 RPOPLPUSH source destination 之後,source 列表包含元素 a, b ,destination 列表包含元素 c, x, y, z ,並且元素 c 會被返回給客戶端。
    如果 source 不存在,值 nil 被返回,並且不執行其他動作。
    如果 source 和 destination 相同,則列表中的表尾元素被移動到表頭,並返回該元素,可以把這種特殊情況視作列表的旋轉 (rotation) 操作。
    時間複雜度: O(1)
    返回值: 被彈出的元素。

    127.0.0.1:6379[15]> LPUSH alpha d c b a
    (integer) 4
    127.0.0.1:6379[15]> LRANGE alpha 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    127.0.0.1:6379[15]> RPOPLPUSH alpha reciver
    "d"
    127.0.0.1:6379[15]> LRANGE alpha 0 -1
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379[15]> LRANGE reciver 0 -1
    1) "d"
    127.0.0.1:6379[15]> RPOPLPUSH alpha reciver
    "c"
    127.0.0.1:6379[15]> LRANGE alpha 0 -1
    1) "a"
    2) "b"
    127.0.0.1:6379[15]> LRANGE reciver 0 -1
    1) "c"
    2) "d"
    source 和 destination 相同
    127.0.0.1:6379[15]> RPUSH number 1 2 3 4
    (integer) 4
    127.0.0.1:6379[15]> LRANGE number 0 -1 
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    127.0.0.1:6379[15]> RPOPLPUSH number number
    "4"
    127.0.0.1:6379[15]> LRANGE number 0 -1 
    1) "4"
    2) "1"
    3) "2"
    4) "3"
    127.0.0.1:6379[15]> RPOPLPUSH number number
    "3"
    127.0.0.1:6379[15]> LRANGE number 0 -1 
    1) "3"
    2) "4"
    3) "1"
    4) "2"
  17. BRPOPLPUSH
    BRPOPLPUSH source destination timeout
    BRPOPLPUSH 是RPOPLPUSH 的阻塞版本,當給定列表 source 不為空時,BRPOPLPUSH 的表現和RPOPLPUSH 一樣。
    當列表 source 為空時,BRPOPLPUSH 命令將阻塞連線,直到等待超時,或有另一個客戶端對 source 執行LPUSH 或RPUSH 命令為止。
    超時引數 timeout 接受一個以秒為單位的數字作為值。超時引數設為 0 表示阻塞時間可以無限期延長(block indefinitely) 。
    時間複雜度: O(1)
    返回值:
      假如在指定時間內沒有任何元素被彈出,則返回一個 nil 和等待時長。
      反之,返回一個含有兩個元素的列表,第一個元素是被彈出元素的值,第二個元素是等待時長。

    127.0.0.1:6379[15]> BRPOPLPUSH msg reciver 500
    "hello"
    (84.71s)
    127.0.0.1:6379[15]> LLEN reciver
    (integer) 1
    127.0.0.1:6379[15]> LRANGE reciver 0 0
    1) "hello"
    127.0.0.1:6379[15]> BRPOPLPUSH msg reciver 1
    (nil)
    (1.82s)