一、列表(List)
list命令需要知道的是它大致分两个类别L(left)和R(right),顾名思义就是针对list的左边操作命令和右边操作命令从而也可以知道Redis的List数据类型其实是和队列很像的,是不是;
比如队列的先进先出对应List的命令就是先lpush再rpop;后进后出就是先lpush再lpop当然命令顺序可以颠倒,这要看以那个方向为准了。
下面我以左边为准,简单的画一个List的列表操作
1)先从左边(lpush)开始向city列表中添加数据,数据会从左边添加进去,进而beijing会被一步步的向右边挤压;
2)接着开始从左边遍历(lrange)数据 0 到 -1 是遍历所有;
3)可以看出左边最后一个添加的guabgzhou会被第一个遍历出来,第一个添加的beijing会被最后一个遍历出来**(是不是和栈操作一样,先进后出,后进先出)**;
先lpush再lpop = 栈(先进后出,后进先出)
先lpush再rpop = 队列(先进先出,后进后出)
1.1 LPUSH(lpush)
将一个或多个值插入到列表头部
Redis Lpush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。
**注意:**在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。
语法
127.0.0.1:6379> LPUSH KEY_NAME VALUE1.. VALUEN
可以版本:
>= 1.0.0
返回值:
执行 LPUSH 命令后,列表的长度。
案例
127.0.0.1:6379> flushall OK 127.0.0.1:6379> lpush city beijing #从左边开始添加数据,添加一个 (integer) 1 127.0.0.1:6379> lpush city shanghai shenzhen guangzhou #从左边开始添加数据,添加多个 (integer) 4 127.0.0.1:6379> lrange city 0 -1 #从左边开始,遍历所有数据 1) "guangzhou" 2) "shenzhen" 3) "shanghai" 4) "beijing" 127.0.0.1:6379>
1.2 LPOP(lpop)
移出并获取列表的第一个元素
Redis Lpop 命令用于移除并返回列表的第一个元素。
语法
127.0.0.1:6379> LLEN KEY_NAME
可以版本:>= 1.0.0
返回值:列表的第一个元素。 当列表 key 不存在时,返回 nil 。
案例
127.0.0.1:6379> lrange city 0 -1 #遍历列表 1) "guangzhou" 2) "shenzhen" 3) "shanghai" 4) "beijing" 127.0.0.1:6379> lpop city #从左边移除一个元素 "guangzhou" 127.0.0.1:6379> lrange city 0 -1 #遍历列表 1) "shenzhen" 2) "shanghai" 3) "beijing" 127.0.0.1:6379> exists names #判断key是否存在 (integer) 0 127.0.0.1:6379> lpop names #移除不存在列表中的值,输出nil (nil) 127.0.0.1:6379>
1.3 LPUSHX(lpushx)
将一个或多个值插入到已存在的列表头部
Redis Lpushx 将一个或多个值插入到已存在的列表头部,列表不存在时操作无效。
语法
127.0.0.1:6379> LPUSHX KEY_NAME VALUE1.. VALUEN
可以版本:>= 2.2.0
返回值:LPUSHX 命令执行之后,列表的长度。
案例
127.0.0.1:6379> exists city #判断city是否存在 (integer) 1 127.0.0.1:6379> lpushx city nanchang #给存在的list列表添加值 (integer) 4 #成功 127.0.0.1:6379> exists names #判断names是否存在 (integer) 0 127.0.0.1:6379> lpushx names j3_liuliang #给不存在的list列表存在值 (integer) 0 #失败 127.0.0.1:6379> lrange city 0 -1 #遍历存在的列表(city) 1) "nanchang"‘ 2) "shenzhen" 3) "shanghai" 4) "beijing" 127.0.0.1:6379>
1.4 LRANGE(lrange)
获取列表指定范围内的元素(闭区间)
Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
语法
127.0.0.1:6379> LRANGE KEY_NAME START END
可以版本:>= 1.0.0
返回值:一个列表,包含指定区间内的元素。
案例
127.0.0.1:6379> exists city #该列表存在 (integer) 1 127.0.0.1:6379> lrange city 0 0 #遍历一个数据出来,闭区间 1) "nanchang" 127.0.0.1:6379> lrange city 0 1 #遍历两个数据出来,闭区间 1) "nanchang" 2) "shenzhen" 127.0.0.1:6379> lrange city 1 2 #遍历三个数据出来,闭区间 1) "shenzhen" 2) "shanghai" 127.0.0.1:6379> lrange city -2 -1 #遍历倒数第二和第一的数据,闭区间 1) "shanghai" 2) "beijing" 127.0.0.1:6379> lrange city 0 -1 #遍历第一个到最后一个数据,就是遍历全部数据 1) "nanchang" 2) "shenzhen" 3) "shanghai" 4) "beijing" 127.0.0.1:6379>
1.5 LINDEX(lindex)
通过索引获取列表中的元素
Redis Lindex 命令用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
语法
127.0.0.1:6379> LINDEX KEY_NAME INDEX_POSITION
可以版本:>= 1.0.0
返回值:列表中下标为指定索引值的元素。 如果指定索引值不在列表的区间范围内,返回 nil 。
案例
127.0.0.1:6379> exists city #判断列表是否存在 (integer) 1 127.0.0.1:6379> lindex city 2 #获取下标是 2 的数据,就是左边(lindex)数第三个数据 "shanghai" 127.0.0.1:6379> lindex city -1 #获取最后一个下标的数据 "beijing" 127.0.0.1:6379> lindex city 20 #获取不存在的下标数据 (nil) 127.0.0.1:6379>
1.6 LREM(lrem)
移除列表元素
Redis Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。
COUNT 的值可以是以下几种:
count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
count = 0 : 移除表中所有与 VALUE 相等的值。
语法
127.0.0.1:6379> LREM KEY_NAME COUNT VALUE
可以版本:>= 1.0.0
返回值:被移除元素的数量。 列表不存在时返回 0 。
案例
127.0.0.1:6379> lrange city 0 -1 #当前list中的数据 1) "nanchang" 2) "shenzhen" 3) "shanghai" 4) "beijing" 127.0.0.1:6379> lrem city 1 beijing #从表头开始移除元素 (integer) 1 127.0.0.1:6379> lrem city -1 shenzhen #从表尾开始移除元素 (integer) 1 127.0.0.1:6379> lrem city -1 shenzhengggg #移除没有的元素 (integer) 0 127.0.0.1:6379> lrange city 0 -1 #遍历列表 1) "nanchang" 2) "shanghai" 127.0.0.1:6379>
1.7 LLEN(llen)
获取列表长度
Redis Llen 命令用于返回列表的长度。 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 如果 key 不是列表类型,返回一个错误。
语法
127.0.0.1:6379> LLEN KEY_NAME
可以版本:>= 1.0.0
返回值:列表的长度。
案例
127.0.0.1:6379> flushall OK 127.0.0.1:6379> lpush city beijing shanghai shenzhen hangzhou guangzhou #向列表中添加数据 (integer) 5 127.0.0.1:6379> llen city #返回列表长度 (integer) 5 127.0.0.1:6379> exists names #判断key是否存在 (integer) 0 127.0.0.1:6379> llen names #返回不存在key的长度 (integer) 0 127.0.0.1:6379>
1.8 LTRIM(ltrim)
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
Redis Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
语法
127.0.0.1:6379> LTRIM KEY_NAME START STOP
可以版本:>= 1.0.0
返回值:命令执行成功时,返回 ok 。
案例
127.0.0.1:6379> lpush names j3_liuliang xiaohong xiaoma xiaoli xiaohua xiaowang #添加数据 (integer) 6 127.0.0.1:6379> ltrim names 0 -2 #保留第一个到倒数第二个数之间的值 OK 127.0.0.1:6379> lrange names 0 -1 #遍历 1) "xiaowang" 2) "xiaohua" 3) "xiaoli" 4) "xiaoma" 5) "xiaohong" 127.0.0.1:6379> ltrim names 30 -2 #保留从 30 到 倒数第二个数之间的数 OK 127.0.0.1:6379> lrange names 0 -1 #为空了 (empty list or set) 127.0.0.1:6379> lpush names j3_liuliang xiaohong xiaoma xiaoli xiaohua xiaowang #添加数据 (integer) 6 127.0.0.1:6379> ltrim names 0 -20 #保留从第一个,到 倒数第二十个之间的数 OK 127.0.0.1:6379> lrange names 0 -1 #为空了 (empty list or set) 127.0.0.1:6379>
1.9 LINSERT(linsert)
在列表的元素前或者后插入元素
Redis Linsert 命令用于在列表的元素前或者后插入元素。 当指定元素不存在于列表中时,不执行任何操作。 当列表不存在时,被视为空列表,不执行任何操作。 如果 key 不是列表类型,返回一个错误。
语法
LINSERT KEY_NAME BEFORE EXISTING_VALUE NEW_VALUE
可以版本:>= 1.0.0
返回值:如果命令执行成功,返回插入操作完成之后,列表的长度。 如果没有找到指定元素 ,返回 -1 。 如果 key 不存在或为空列表,返回 0 。
案例
127.0.0.1:6379> lrange city 0 -1 #遍历列表 1) "guangzhou" 2) "hangzhou" 3) "shenzhen" 4) "shanghai" 5) "beijing" 127.0.0.1:6379> linsert city before beijing tianjing #在beijing的前面插入tianjing (integer) 6 127.0.0.1:6379> lrange city 0 -1 #遍历列表,查看效果 1) "guangzhou" 2) "hangzhou" 3) "shenzhen" 4) "shanghai" 5) "tianjing" 6) "beijing" 127.0.0.1:6379> linsert city after guangzhou dongwan #在guangzhou的后面插入dongwan (integer) 7 127.0.0.1:6379> lrange city 0 -1 #遍历列表,查看效果 1) "guangzhou" 2) "dongwan" 3) "hangzhou" 4) "shenzhen" 5) "shanghai" 6) "tianjing" 7) "beijing" 127.0.0.1:6379>
1.10 LSET(lset)
通过索引设置列表元素的值
Redis Lset 通过索引来设置元素的值。
当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误。
语法
127.0.0.1:6379> LSET KEY_NAME INDEX VALUE
可以版本:>= 1.0.0
返回值:操作成功返回 ok ,否则返回错误信息。
案例
127.0.0.1:6379> lrange city 0 -1 #当前列表数据 1) "guangzhou" 2) "dongwan" 3) "hangzhou" 4) "shenzhen" 5) "shanghai" 6) "tianjing" 7) "beijing" 127.0.0.1:6379> lset city 0 guangzhou1111 #将下标为 0 的数据 改为 guangzhou1111 OK 127.0.0.1:6379> lrange city 0 -1 #遍历列表查看效果 1) "guangzhou1111" 2) "dongwan" 3) "hangzhou" 4) "shenzhen" 5) "shanghai" 6) "tianjing" 7) "beijing" 127.0.0.1:6379> lset city 3 shenzhen0000000 #将下标为 3 的数据改为 shenzhen0000000 OK 127.0.0.1:6379> lrange city 0 -1 #遍历列表查看效果 1) "guangzhou1111" 2) "dongwan" 3) "hangzhou" 4) "shenzhen0000000" 5) "shanghai" 6) "tianjing" 7) "beijing" 127.0.0.1:6379> lset city 30 shenzhen0000000 #给不存在的下标,设置数据 (error) ERR index out of range #出错,下标越界 127.0.0.1:6379> lrange city 0 -1 #列表数据,没有变化 1) "guangzhou1111" 2) "dongwan" 3) "hangzhou" 4) "shenzhen0000000" 5) "shanghai" 6) "tianjing" 7) "beijing" 127.0.0.1:6379>
1.11 RPUSH(rpush)
在列表中添加一个或多个值
Redis Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。
如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。
**注意:**在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。
语法
127.0.0.1:6379> RPUSH KEY_NAME VALUE1..VALUEN
可以版本:>= 1.0.0
返回值:执行 RPUSH 操作后,列表的长度。
案例
127.0.0.1:6379> flushall OK 127.0.0.1:6379> rpush city beijing shanghai guangzhou shenzhen #从左边开始添加数据,多个 (integer) 4 127.0.0.1:6379> rpush city hangzhou #从左边开始添加数据,单个 (integer) 5 127.0.0.1:6379> lrange city 0 -1 #从右边开始遍历数据 1) "beijing" 2) "shanghai" 3) "guangzhou" 4) "shenzhen" 5) "hangzhou" 127.0.0.1:6379>
1.12 RPOP(rpop)
移除并获取列表最后一个元素
Redis Rpop 命令用于移除并返回列表的最后一个元素。
语法
127.0.0.1:6379> RPOP KEY_NAME
可以版本:>= 1.0.0
返回值:列表的最后一个元素。 当列表不存在时,返回 nil 。
案例
127.0.0.1:6379> lrange city 0 -1 #当前列表数据 1) "beijing" 2) "shanghai" 3) "guangzhou" 4) "shenzhen" 5) "hangzhou" 127.0.0.1:6379> rpop city #从右边移除数据(一个) "hangzhou" 127.0.0.1:6379> exists names #判断key是否存在 (integer) 0 127.0.0.1:6379> rpop names #移除不存在key的值,返回nil (nil) 127.0.0.1:6379>
1.13 RPUSHX(rpushx)
为已存在的列表添加值
Redis Rpushx 命令用于将一个或多个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效。
语法
127.0.0.1:6379> RPUSHX KEY_NAME VALUE1..VALUEN
可以版本:>= 2.2.0
返回值:执行 Rpushx 操作后,列表的长度。
案例
127.0.0.1:6379> lrange city 0 -1 #当前列表中数据 1) "beijing" 2) "shanghai" 3) "guangzhou" 4) "shenzhen" 127.0.0.1:6379> rpushx city tianjin #向存在的列表添加数据(从右边开始) (integer) 5 127.0.0.1:6379> lrange city 0 -1 #当前列表数据 1) "beijing" 2) "shanghai" 3) "guangzhou" 4) "shenzhen" 5) "tianjin" 127.0.0.1:6379> exists names #判断key是否存在 (integer) 0 127.0.0.1:6379> rpushx names j3_liuliang #给不存在的key添加数据 (integer) 0 #失败 127.0.0.1:6379> exists names #没有任何变化,不会创建数据 (integer) 0 127.0.0.1:6379>
1.14 RPOPLPUSH(rpoplpush)
移除列表的最后一个元素,并将该元素添加到另一个列表并返回
Redis Rpoplpush 命令用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回。
语法
127.0.0.1:6379> RPOPLPUSH SOURCE_KEY_NAME DESTINATION_KEY_NAME
可以版本:>= 1.0.0
返回值:被弹出的元素。
案例
127.0.0.1:6379> lrange city 0 -1 #city列表数据 1) "beijing" 2) "shanghai" 3) "guangzhou" 4) "shenzhen" 5) "tianjin" 127.0.0.1:6379> lrange names 0 -1 #names列表数据 1) "j3_liuliang" 127.0.0.1:6379> rpoplpush city names #将city中最后一个数据弹出添加到names中 "tianjin" 127.0.0.1:6379> lrange city 0 -1 #查看全部数据 1) "beijing" 2) "shanghai" 3) "guangzhou" 4) "shenzhen" 127.0.0.1:6379> lrange names 0 -1 #查看全部数据 1) "tianjin" 2) "j3_liuliang" 127.0.0.1:6379>
1.15 BLPOP(blpop)
移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。类比阻塞队列
Redis Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
语法
127.0.0.1:6379> BLPOP LIST1 LIST2 .. LISTN TIMEOUT
可以版本:>= 2.0.0
返回值:如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
案例
127.0.0.1:6379> lrange names 0 -1 #当前列表所有数据 1) "tianjin" 2) "j3_liuliang" 127.0.0.1:6379> blpop names 5 #弹出一个数据从该列表中,如果该列表中没有数据,等待 5 秒,如果该列表没有被其他线程添加数据的化, 就会返回nil,如果有其他的数据添加进来就会立即返回该数据 1) "names" 2) "tianjin" 127.0.0.1:6379> lrange names 0 -1 #当前列表所有数据 1) "j3_liuliang" 127.0.0.1:6379> blpop names 5 #弹出一个数据从该列表中,如果该列表中没有数据,等待 5 秒,如果该列表没有被其他线程添加数据的化, 就会返回nil,如果有其他的数据添加进来就会立即返回该数据 1) "names" 2) "j3_liuliang" 127.0.0.1:6379> blpop names 50 #上面可知,names已经空了,所有在弹出数据时,会等待 50 秒,如果names中没有其他数据添加进来,过时 直接返回nil否者直接返回添加的数据 1) "names" 2) "xiaowang" #这是我开的另一个客户端向names中添加数据的,所以这个客户端会立马被感知弹出数据,用户16.79秒 (16.79s) 127.0.0.1:6379>
1.16 BRPOP(brpop)
移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。类比阻塞队列
Redis Brpop 命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
语法
127.0.0.1:6379> BRPOP LIST1 LIST2 .. LISTN TIMEOUT
可以版本:>= 2.0.0
返回值:假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
案例
# 和blpop的操作一样,只是一个方向问题,案例可以参考blpop命令
1.17 BRPOPLPUSH(brpoplpush)
从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
Redis Brpoplpush 命令从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
语法
127.0.0.1:6379> BRPOPLPUSH LIST1 ANOTHER_LIST TIMEOUT
可以版本:>= 2.0.0
返回值:假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素的值,第二个元素是等待时长。
案例
# brpoplpush 和 rpoplpush的效果一样,不同的就是加了一个等待时间,如果list中没有数据,规定时间内没有添加的话,会返回nil 127.0.0.1:6379> keys * 1) "name" 2) "city" 127.0.0.1:6379> lrange name 0 -1 #name列表中的数据 1) "xiaowang" 127.0.0.1:6379> brpoplpush name city 10 #当name中有数据时,brpoplpush 和 rpoplpush操作效果一样 "xiaowang" 127.0.0.1:6379> brpoplpush name city 100 #当 name 中数据为空时 brpoplpush 会先阻塞100 秒等待name中添加数据,才会执行会须操作,如 果100秒之后还是没有数据,那么返回nil "xiaoliu" (26.60s) #等待26秒执行成功! 127.0.0.1:6379>
二、应用场景
2.1 消息队列
如下图所示,Redis的lpush + brpop命令组合即可实现阻塞队列,生产者客户端使用lpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的争抢列表尾部的元素,多个客户端保证了消费的负载均衡和高可用;
2.2 栈
可以通过(lpush lpop左边进左边出,或rpush rpop 右边进右边出),实现先进后出原则
2.3 最新列表
list类型的lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表,如朋友圈的点赞列表、评论列表。
但是,并不是所有的最新列表都能用list类型实现,因为对于频繁更新的列表,list类型的分页可能导致列表元素重复或漏掉,举个例子,当前列表里由表头到表尾依次有(E,D,C,B,A)五个元素,每页获取3个元素,用户第一次获取到(E,D,C)三个元素,然后表头新增了一个元素F,列表变成了(F,E,D,C,B,A),此时用户取第二页拿到(C,B,A),元素C重复了。只有不需要分页(比如每次都只取列表的前5个元素)或者更新频率低(比如每天凌晨更新一次)的列表才适合用list类型实现。对于需要分页并且会频繁更新的列表,需用使用有序集合sorted set类型实现。另外,需要通过时间范围查找的最新列表,list类型也实现不了,也需要通过有序集合sorted set类型实现,如以成交时间范围作为条件来查询的订单列表。之后在介绍有序集合sorted set类型的应用场景时会详细介绍sorted set类型如何实现最新列表。
2.4 排行榜
list类型的lrange命令可以分页查看队列中的数据。可将每隔一段时间计算一次的排行榜存储在list类型中,如京东每日的手机销量排行、学校每次月考学生的成绩排名、斗鱼年终盛典主播排名等;
但是,并不是所有的排行榜都能用list类型实现,只有定时计算的排行榜才适合使用list类型存储,与定时计算的排行榜相对应的是实时计算的排行榜,list类型不能支持实时计算的排行榜,之后在介绍有序集合sorted set的应用场景时会简单说一下。
结束语
- 本文结合Redis中文网和博主的实践案例所写,下期写Hash类型
- 由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。
- 如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。
- 感谢您的阅读,十分欢迎并感谢您的关注。