企业级Redis开发运维从入门到实践 (6)— list数据结构的内部编码及相关命令详解

列表的内部编码

列表类型的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)
      同时所有值都小于hash-max-ziplist-value配置(默认64个字节)时,Redis会使用ziplist作为哈希的内部实现。
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。

下面演示列表类型的内部编码,以及相应的变化:

当元素个数较少且没有大元素时,内部编码为ziplist:
127.0.0.1:6379> rpush list:2 a b c
(integer) 3
127.0.0.1:6379> object encoding list:2
"ziplist"
当元素个数超过512个,内部编码变为linkedlist:
127.0.0.1:6379>lpush setkey 1 2 3 ... 513
OK
127.0.0.1:6379> object encoding listkey
"linkedlist"
当某个元素超过64个字节,内部编码也会变为linkedlist:
127.0.0.1:6379> rpush list:1 a b "我不再说话,不再思索,但无尽的爱从灵魂中升起,我将远行,走得很远,如同一个吉普塞人,穿过大自然——幸福得如有一位女子同行。"
(integer) 6
127.0.0.1:6379> object encoding list:1
"linkedlist"

只能升级,不能自动变回ziplist类型

列表结构

结构依然还是key-value的结构,key还是字符串类型,value是一个有序的队列。

  • 可以进行lpush、lpop、rpush、rpop。
    这里写图片描述
  • 可以llen(计算列表长度)、lrem 1 b(删除指点元素)、lrange(获取子列表)、lindex 5 (按索引获取列表指定元素)。
    这里写图片描述
特点
  • 有序
  • 可以重复
  • 左右两边插入弹出
列表类型的相关命令:
rpush、lpush
  • rpush key value1 value2 … valueN:从列表右端插入值(1 ~ N个),时间复杂度O(1 ~ n)。
  • lpush key value1 value2 … valueN:从列表左端插入值(1 ~ N个),时间复杂度O(1 ~ n)。
# 加入单个元素
redis> lpush languages python
(integer) 1

# 加入重复元素
redis> lpush languages python
(integer) 2

redis> lpush languages 0 -1     # 列表允许重复元素
1) "python"
2) "python"

# 加入多个元素
redis> lpush mylist a b c
(integer) 3

redis> lpush mylist 0 -1
1) "c"
2) "b"
3) "a"
linsert
  • linsert key before|after value newValue:在list指定的 value 之前|后插入 newValue。
  • 当 value 不存在于列表 key 时,不执行任何操作。
  • 当 key 不存在时, key 被视为空列表,不执行任何操作。
  • 如果 key 不是列表类型,返回一个错误。
  • 时间复杂度O(n) ,N 为寻找 value 过程中经过的元素数量。
redis> rpush mylist "Hello"
(integer) 1

redis> rpush mylist "World"
(integer) 2

redis> linsert mylist before "World" "There"
(integer) 3

redis> lrange mylist 0 -1
1) "Hello"
2) "There"
3) "World"

# 对一个非空列表插入,查找一个不存在的 pivot
redis> linsert mylist before "go" "let's"
(integer) -1                                    # 失败

# 对一个空列表执行 linsert 命令
redis> exists fake_list
(integer) 0

redis> linsert fake_list before "nono" "gogogog"
(integer) 0                                      # 失败
lpop、rpop
  • lpop key:从列表左端弹出一个item,时间复杂度O(1)。
  • rpop key:从列表右端弹出一个item,时间复杂度O(1)。
redis> llen course
(integer) 0

redis> rpush course algorithm001
(integer) 1

redis> rpush course c++101
(integer) 2

redis> lpop course  # 移除头元素
"algorithm001"
lrem
  • lrem key count value:根据count值,从列表中删除所有value相等的项。
  • count>0,从左到右,删除最多count个value相等的项。
  • count<0,从右到左,删除最多Math.abs(count)(绝对值)个value相等的向项。
  • count=0,删除所有value相等的项。
  • 时间复杂度O(n)。
# 先创建一个表,内容排列是
# morning hello morning helllo morning

redis> lpush greet "morning"
(integer) 1
redis> lpush greet "hello"
(integer) 2
redis> lpush greet "morning"
(integer) 3
redis> lpush greet "hello"
(integer) 4
redis> lpush greet "morning"
(integer) 5

redis> lrange greet 0 4         # 查看所有元素
1) "morning"
2) "hello"
3) "morning"
4) "hello"
5) "morning"

redis> lrem greet 2 morning     # 移除从表头到表尾,最先发现的两个 morning
(integer) 2                     # 两个元素被移除

redis> llen greet               # 还剩 3 个元素
(integer) 3

redis> lrange greet 0 2
1) "hello"
2) "hello"
3) "morning"

redis> lrem greet -1 morning    # 移除从表尾到表头,第一个 morning
(integer) 1

redis> llen greet               # 剩下两个元素
(integer) 2

redis> lrange greet 0 1
1) "hello"
2) "hello"

redis> lrem greet 0 hello      # 移除表中所有 hello
(integer) 2                    # 两个 hello 被移除

redis> llen greet
(integer) 0
ltrim
  • ltrim key start end:按照索引范围修剪列表,时间复杂度O(n),n为被移除的元素的数量。
# 情况 1: 常见情况, start 和 stop 都在列表的索引范围之内
redis> lrange alpha 0 -1       # alpha 是一个包含 5 个字符串的列表
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"

redis> ltrim alpha 1 -1        # 删除 alpha 列表索引为 0 的元素
OK

redis> lrange alpha 0 -1       # "h" 被删除了
1) "e"
2) "l"
3) "l"
4) "o"


# 情况 2: stop 比列表的最大下标还要大
redis> ltrim alpha 1 10086     # 保留 alpha 列表索引 1 至索引 10086 上的元素
OK

redis> lrange alpha 0 -1       # 只有索引 0 上的元素 "e" 被删除了,其他元素还在
1) "l"
2) "l"
3) "o"


# 情况 3: start 和 stop 都比列表的最大下标要大,并且 start < stop
redis> ltrim alpha 10086 123321
OK

redis> lrange alpha 0 -1        # 列表被清空
(empty list or set)

# 情况 4: start 和 stop 都比列表的最大下标要大,并且 start > stop
redis> rpush new-alpha "h" "e" "l" "l" "o"     # 重新建立一个新列表
(integer) 5

redis> lrange new-alpha 0 -1
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"

redis> ltrim new-alpha 123321 10086    # 执行 LTRIM
OK

redis> lrange new-alpha 0 -1           # 同样被清空
(empty list or set)
lrange
  • lrange key start end (包含end):获取列表指定索引范围所有item。
  • 时间复杂度O(S+N),S为偏移量 start ,N为指定区间内元素的数量。
redis> rpush fp-language lisp
(integer) 1

redis> lrange fp-language 0 0
1) "lisp"

redis> rpush fp-language scheme
(integer) 2

redis> lrange fp-language 0 1
1) "lisp"
2) "scheme"
lindex
  • lindex key index:获取列表指定索引的item。
  • 时间复杂度O(N), N 为到达下标 index 过程中经过的元素数量。因此,对列表的头元素和尾元素执行 LINDEX 命令,复杂度为O(1)。
redis> lpush mylist "World"
(integer) 1

redis> lpush mylist "Hello"
(integer) 2

redis> lindex mylist 0
"Hello"

redis> lindex mylist -1
"World"

redis> lindex mylist 3        # index不在 mylist 的区间范围内
(nil)
llen
  • llen key:获取列表长度,时间复杂度O(1)。
# 空列表
redis> llen job
(integer) 0

# 非空列表
redis> lpush job "cook food"
(integer) 1

redis> lpush job "have lunch"
(integer) 2

redis> llen job
(integer) 2
lset
  • lset key index newValue:设置列表指定索引值为newValue。
  • 对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
# 对空列表(key 不存在)进行 LSET
redis> exists list
(integer) 0

redis> lset list 0 item
(error) ERR no such key

# 对非空列表进行 LSET
redis> lpush job "cook food"
(integer) 1

redis> lrange job 0 0
1) "cook food"

redis> lset job 0 "play game"
OK

redis> lrange job  0 0
1) "play game"


# index 超出范围
redis> llen list                    # 列表长度为 1
(integer) 1

redis> lset list 3 'out of range'
(error) ERR index out of range
实战
  • 实战-TimeLine
    现在weibo的数据顺序是:weibo10129、weibo998、weibo10194、weibo10158、weibo100;这里写图片描述
    当你关注的人更新微博,lpusp将最新的weibo加入列表的最左边开头。
    这里写图片描述
blpop、brpop
  • blpop key timeout:blpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞,时间复杂度O(1)。
  • brpop key timeout:brpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞,时间复杂度O(1)。
TIPS
  1. lpush + lpop = stack,实现栈的功能。
  2. lpush + rpop = Queue,实现队列的功能。
  3. lpush + ltrim = capped collection,实现由固定数量的列表的功能。
  4. lpush + brpop = message queue,实现消息队列的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值