目录

Redis数据结构list

Redis数据结构——list


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

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

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

https://i-blog.csdnimg.cn/direct/e750e25e40ae47fe9da6138e40e17551.png

列表的特点

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的长度

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

https://i-blog.csdnimg.cn/direct/22a26c56cd0c4665abebbffa78dc4132.png

lrange

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

语法:

lrange key start end

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

https://i-blog.csdnimg.cn/direct/9824aca7707b431ba9248c1b5c7736bb.png

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

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

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

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

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

https://i-blog.csdnimg.cn/direct/eba4e317712c4184bb803f225df1e03e.png

lpushx

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

语法:

lpushx key element [element …]

返回值:插入后list的长度

时间复杂度:O(1)

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

key存在

https://i-blog.csdnimg.cn/direct/e9127e84acf442db9ce760129e876e40.png

key不存在

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

https://i-blog.csdnimg.cn/direct/8818c85f36df43f98ab9e6ae5d93250c.png

rpush

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

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

语法:

rpush key element [element]

时间复杂度:O(1)

返回值:list的长度

https://i-blog.csdnimg.cn/direct/46e31f303d8847fd97b0d13c02432d5c.png

rpushx

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

语法:

rpushx key element [element]

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

key存在

https://i-blog.csdnimg.cn/direct/03e9cdbf877e4628812a7db74df23308.png

key不存在

https://i-blog.csdnimg.cn/direct/c241920d2a824c8ea8c9605af8edc5fb.png

lpop

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

语法:

lpop key

时间复杂度:O(1)

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

示例:

https://i-blog.csdnimg.cn/direct/cb3c7521e12e44f29b21f09ad9257337.png

rpop

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

语法:

rpop key

时间复杂度:O(1)

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

示例:

https://i-blog.csdnimg.cn/direct/ead9fd1c50bd4e128c1aa46aacda9773.png

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

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

lindex

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

语法:

lindex key index

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

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

https://i-blog.csdnimg.cn/direct/5907d7a00875445faeee6add399e419d.png

linsert

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

语法:

linsert key before | after pivot element

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

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

https://i-blog.csdnimg.cn/direct/854000945a21498a82d7830a7f241448.png

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

https://i-blog.csdnimg.cn/direct/f63af3bf9e08419aa6a1fbde2507fdcf.png

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

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

https://i-blog.csdnimg.cn/direct/24d2bea499bd447f99f858afd33d7f8f.png

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

https://i-blog.csdnimg.cn/direct/bda777d624d4441fa263b5573af91330.png

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

llen

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

语法:

llen key

https://i-blog.csdnimg.cn/direct/7c5be7119ca94299bbe214b7db5261fd.png

lrem

rem =>  remov,删除的意思。

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

https://i-blog.csdnimg.cn/direct/1a91b4b7915d4ba89cec0a517668de35.png

语法:

lrem key count element

count:要删除的个数。

element:要删除的值。

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

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

https://i-blog.csdnimg.cn/direct/a4efe2610e254da3869564f4b787299e.png

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

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

https://i-blog.csdnimg.cn/direct/f609555db2404d8d963c13c91739ea18.png

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

https://i-blog.csdnimg.cn/direct/68fd1c418ccd4ce78643f12c7c906254.png

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

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

https://i-blog.csdnimg.cn/direct/a5da37540feb42c5af1c427020cc472d.png

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

ltrim

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

语法:

ltrim key start stop

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

https://i-blog.csdnimg.cn/direct/529de46d41ec445aa63f4ab242ee68d6.png

lset

根据下标修改元素。

语法:

lset key index element

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

https://i-blog.csdnimg.cn/direct/b3a02b7fa06e4fcc96e42111ebd5ed96.png

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

https://i-blog.csdnimg.cn/direct/1839031473a74610bd53f65ffea45c0c.png

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。

https://i-blog.csdnimg.cn/direct/53d21b713b8d4b79887cc8aaf9fd80a1.png

2)针对空列表进行操作

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

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

https://i-blog.csdnimg.cn/direct/581484c38e9d4aa888402564f2b75ca7.png

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

https://i-blog.csdnimg.cn/direct/6b27868f928240c6bc9d119eab0e9d26.png 3)针对多个key进行操作,只要任意一个key有数据就立刻返回。

https://i-blog.csdnimg.cn/direct/4f90ee50142b46678e9b5201cb3fe204.png

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

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

命令总结

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

编码方式

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

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

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

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

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

https://i-blog.csdnimg.cn/direct/eff10150b71e4ebaa00abbaf2d535ad2.png