Redis数据结构——list

目录

列表命令

lpush

lrange

lpushx

rpush

rpushx

lpop

rpop

lindex

linsert

llen

lrem

ltrim

lset

[blpop / brpop](#blpop / brpop)

命令总结

编码方式


list相当于数组或者顺序表,但并不是简单的数组,更接近于C++中的"双端队列"(deque)。

最左侧的下标是0,redis的下标支持负数下标。

从左侧删除叫lpop,右侧删除叫rpop,左侧插入叫lpush,右侧删除叫做rpush。

列表的特点

1.列表中的元素是有序的。

这里的有序指的是顺序很关键,而不是"升序"、"降序",如果把元素位置调换,那我们得到新的list和之前的list是不等价的。

2.列表中的元素是允许重复的

hash这样的类型,field是不允许重复的,列表这里是可以重复的。

3.区分获取和删除的区别

lindex可以获取到元素的值,但不会将改值从列表中删除掉。

lrem也能返回被删除元素的值,但是该元素会从列表中删除。

4.列表的头和尾都能高效插入删除元素

因此,可以把List当做一个栈/队列来使用。

列表命令

lpush

头插,一次可以插入一个元素,也可以插入多个元素。如果写的是1 2 3 4,是先头插1在头插2、3、4,全都插入完毕后列表元素是4、3、2、1。

语法:

lpush key element [element ...]

时间复杂度:O(1)

返回值:list的长度

注意:命令必须和数据结构相匹配。

lrange

查看指定区间内的元素 ,此处的区间也是闭区间。这里的下标也是可以支持负数的。l是list的意思。

语法:

lrange key start end

如果要获取list所有元素,可以把start设成0,end设置成-1, -1可以理解成list的长度-1的下标。list长度的减去1刚好就是最后一个元素下标。

我们可以看到最后插入的8是第一个元素,此处这里前面的1)、2)是给结果集使用的序号,和下标没关系。

谈到下标,往往会关注超出范围的情况。

在C++中如果超出范围,一般可能会导致程序崩溃,也可能得到一个不合法的数据,也可能得到一个看起来合法,但是错误的数据,也有可能得到一个符合要求的数据.....。

redis中是如何处理超出范围的情况呢?如果是合法的区间,把数据拿到返回给你,如果给定区间非法,比如超出下标,会尽可能获取对应的内容。

key里面只有8个元素,合法的下标是0-7,但是我们给0-100, 虽然没有100个元素,但是你有多少元素我给你多少元素。就好比土匪拦路,土匪要100块钱,你没有100,那你就有多少钱给我多少钱。

lpushx

如果key存在,将一个或者多个元素头插到list中,不存在,直接返回

语法:

lpushx key element [element ...]

返回值:插入后list的长度

时间复杂度:O(1)

上面我们已经设置了一个key,因此直接插入成功了。

key存在

key不存在

现在不存在key3,因此返回的就是0,表示列表长度为0,我们使用lrange可以看到是空的列表,我们在用exists判断key3是否存在,结果是不存在。

rpush

上述的lpush其实就是left push,这里的rpush就是right push。

rpush可以将一个或多个元素插入到list中。

语法:

rpush key element [element]

时间复杂度:O(1)

返回值:list的长度

rpushx

如果key存在进行尾插一个或者多个元素,不存在直接返回。

语法:

rpushx key element [element]

时间复杂度和返回值与lpush一样。

key存在

key不存在

lpop

从list左侧取出元素(头删)。

语法:

lpop key

时间复杂度:O(1)

返回值:取出的元素或者nil

示例:

rpop

从list右侧取出元素(尾删)。

语法:

rpop key

时间复杂度:O(1)

返回值:取出的元素或者nil。

示例:

搭配使用rpush和lpop,就相当于一个队列。

搭配使用rpush和rpop,就相当于一个栈。

lindex

给定一个下标,获取到对应的元素。

语法:

lindex key index

返回值:下标对应的元素,如果下标非法,返回nil

下标是从0开始的,并且可以支持负数。

linsert

在某个元素前面或者后面插入一个元素

语法:

linsert key before | after pivot element

before和after只能出现一个,before是往基准元素(pivot)的前面插入一个元素element。

往基准元素前插入一个元素

往基准元素后插入一个元素:

如果给一个不存在的基准值会发生什么情况?会返回-1,表示插入失败。

下面例子中明明不存在77,非要往77前面插入99,当然失败了。

如果基准值存在多个,怎么处理呢?很简单,插入操作相当于遍历列表,如果找到了第一次出现的基准元素那就插入在它的前/后面。

返回值:插入后的列表元素个数

llen

返回列表元素个数,如果列表不存在返回0

语法:

llen key

lrem

rem => remov,删除的意思。

我们用lrem对下面这组数据进行操作。下面的列表中就是插入了4组1、2、3、4。

语法:

lrem key count element

count:要删除的个数。

element:要删除的值。

如果count大于0,从左往右删除element,删除count个。

下面图我们可以看到头部的1没了,第二组的1也没了。lrem key 2 1的意思是删除2个元素1,从左往右删。

如果count小于0,从右往左删除element,删除count个。

我们使用flushall命令清除所有的key,然后在rpush四组1、2、3、4,恢复一下数据。

此时我们使用lrem key -2 1命令,意思是删除元素1,删除2个,从右往左删。

如果count等于0,删除所有的element。

下面尾插四组1234,然后lrem key 0 1,表示列表中所有的1都被删了。

返回值:删除了多少个元素

ltrim

保留start到stop之间内的元素,区间外面两边的都被删了。区间也是闭区间。start和stop是下标。

语法:

ltrim key start stop

可以看到,下标2-5之间的元素多存在,其它的元素都没了。

lset

根据下标修改元素。

语法:

lset key index element

下面是将下标为2的元素5修改为99。

此处如果给的index超出范围,会直接越界报错。

blpop / brpop

阻塞:当前线程不走了,代码不继续执行了,会在满足一定的条件之后,被唤醒。

blpop和brpop的b就是block阻塞。

redis中的list的阻塞,只支持"队列为空"的情况。

阻塞的特性

  • 如果list中存在元素,blpop和brpop就和lpop以及rpop作用相同。
  • 但如果list中元素为空,blpop和brpop就会产生阻塞,一直阻塞到list不空为止。
  • 在使用blpop和brpop的时候,是可以显示设置阻塞时间的,不一定是无休止的等待。
  • 阻塞期间redis可以执行其它的命令。
  • blpop和brpop可以同时尝试获取多个key列表的元素,多个key对应的多个list,这多个list哪个有元素了,就会返回哪个元素。
  • 如果多个客户端同时对一个key执行pop,则最先执行命令的客户端会得到返回的结果。

语法:

blpop key [key ...] timeout

此处可以指定一个key或者多个key,每个key都对应一个list,如果这些list有任意一个非空,那么blpop都能把这里的元素给获取到从而立即返回,如果这些list都是空,那么就阻塞等待,等待其它客户端往这些list中插入元素。此处可以指定超时时间,这里timeout的超时时间单位是秒。reids6中允许设定成小数,redis5中,超时时间只能是整数。

1)针对非列表进行操作

返回的结果相当于一个pair(二元组),一方面是告诉我们当前的数据来自哪个key,一方面告诉我们取到的数据是什么。lpop本来就是头删,因此返回1。

2)针对空列表进行操作

我使用del将key清除了,然后在对key进行lpop操作。

我们可以看到被阻塞了。这里阻塞时间设置了100秒。

此时我们在开启一个客户端,当我右边的客户端往key里插入数据一敲回车之后,左边客户端立刻返回了结果,告诉我们得到的结果1来自于key,它等待了96.99秒。

3)针对多个key进行操作,只要任意一个key有数据就立刻返回。

brpop和blpop效果完全一致 ,此处就不介绍了,只不过一个是头删一个是尾删。

这两组命令有啥用呢?此处这两阻塞命令用途主要是用来作为"消息队列"。但是实际用到的场景几乎很少,了解它们的作用用途即可。

命令总结

|------|-----------------------------------------|---------------------------------|
| 操作类型 | 命令 | 时间复杂度 |
| 添加 | rpush key value [value...] | O(k),k是元素个数 |
| 添加 | lpush key value [value...] | O(k),k是元素个数 |
| 添加 | linsert key before | after pivot value | O(n), n是pivot距离头尾的距离 |
| 查找 | lrange key start end | O(s + n),s是start偏移量 n是start到end |
| 查找 | lindex key index | O(n), n是索引的偏移量 |
| 查找 | len key | O(1) |
| 删除 | lpop key | O(1) |
| 删除 | rpop key | O(1) |
| 删除 | lrem key count value | O(k),k是元素个数 |
| 删除 | ltrim key start end | O(k),k是元素个数 |
| 修改 | lset key index value | O(n), n是索引的偏移量 |
| 阻塞操作 | blpop brpop | O(1) |

编码方式

ziplist(压缩列表):把数据按照更紧凑的压缩形式进行表示的,节省空间,当元素个数多了,操作起来效率会降低。

linkedlist(链表):如果元素多了会使用链表来应对数据多的时候,虽然空间比ziplist开销更多,但效率更高。

quicklist:相当于是链表和压缩链表的结合,整体还是一个链表,链表的每个节点是一个压缩列表,每个压缩列表都不让它太大,同时再把多个压缩列表通过链式结果连起来。

redis3之后都是用的quicklist,而之前的版本使用的是ziplist和linkedlist。

object encoding 可以查看key的编码方式。

相关推荐
obboda4 分钟前
web高可用集群项目(数据库主从同步、文件共享存储、nginx动静分离+负载均衡+高可用)
数据库·nginx·负载均衡
m0_748246358 分钟前
MySQL Workbench安装教程以及菜单汉化
android·数据库·mysql
编程修仙27 分钟前
mysql的管理
数据库·mysql·oracle
CodeJourney.42 分钟前
Deepseek助力思维导图与流程图制作:高效出图新选择
数据库·人工智能·算法
2的n次方_44 分钟前
Redis 中如何保证缓存与数据库的数据一致性
数据库·redis·缓存
m0_7482475544 分钟前
数据库系统架构与DBMS功能探微:现代信息时代数据管理的关键
java·开发语言·数据库
Elastic 中国社区官方博客1 小时前
通过计费集成和警报监控 Elasticsearch Service 成本
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
Jelena157795857921 小时前
爬虫与翻译API接口的完美结合:开启跨语言数据处理新纪元
开发语言·数据库·爬虫
冠位观测者2 小时前
【Leetcode 每日一题】1328. 破坏回文串
数据结构·算法·leetcode
A仔不会笑2 小时前
Redis面试常见问题——集群方案
数据库·redis·面试