NoSQL----Redis 2.4--List
一、概述:
1、List:允许出现重复的元素,list可以作为栈(先进后出)或者队列(先进先出)。
头元素和尾元素:头元素指的是列表左端/前端第一个元素;尾元素指的是列表右端/后端第一个元素。
举个例子,列表list包含三个元素:x, y, z,其中x是头元素,而z则是尾元素。
空列表:指不包含任何元素的列表,Redis将不存在的key也视为空列表。
2、链表结构的小技巧:
针对链表结构的Value,Redis在其官方文档中给出了一些实用技巧,如RPOPLPUSH命令,下面给出具体的解释。
Redis链表经常会被用于消息队列的服务,以完成多程序之间的消息交换。假设一个应用程序正在执行LPUSH操作向链表中添加新的元素,我们通常将这样的程序称之为"生产者(Producer)",而另外一个应用程序正在执行RPOP操作从链表中取出元素,我们称这样的程序为"消费者(Consumer)"。如果此时,消费者程序在取出消息元素后立刻崩溃,由于该消息已经被取出且没有被正常处理,那么我们就可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态的不一致等现象的发生。然而通过使用RPOPLPUSH命令,消费者程序在从主消息队列中取出消息之后再将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时我们还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。
二、应用场景:
获得最新的10个登录用户信息:
1、使用数据库方式需执行如下sql语句:
select * from loginuser order by logintime desc limit 10;
但是数据多的时候,全部数据都要受到影响,对数据库的负载比较高,
必要情况还要需要对关键字段logintime设置索引,索引也比较耗费系统资源。
2、如果通过redis的list链表实现以上功能,就非常简单。可以在list链表中只保留最新的10个登录用户,每进来一个新数据就删除一个旧数据,每次就可以从链表中直接获得需要的资源,极大节省各方面的资源。
3、redis的list还能缓存一些不经常修改的系统配置信息。
如缓存地址信息(国家、省份、城市、街道)、缓存用户信息等等。
三、常用操作:
1、lpush(left push)方法:
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存在但不是列表类型时,返回一个错误。
例如:# 加入单个元素redis> LPUSH languages python(integer) 1# 加入重复元素redis> LPUSH languages python(integer) 2redis> LRANGE languages 0 -1 # 列表允许重复元素1) "python"2) "python"# 加入多个元素redis> LPUSH mylist a b c(integer) 3redis> LRANGE mylist 0 -11) "c"2) "b"3) "a"
2、lpushx(left pushx)方法:
LPUSHX key value
将值value插入到列表key的表头,当且仅当key存在并且是一个列表。
和LPUSH命令相反,当key不存在时,LPUSHX命令什么也不做。
例如:# 情况1:对空列表执行LPUSHXredis> LLEN greet # greet是一个空列表(integer) 0redis> LPUSHX greet "hello" # 尝试LPUSHX,失败,因为列表为空(integer) 0# 情况2:对非空列表执行LPUSHXredis> LPUSH greet "hello" # 先用LPUSH创建一个有一个元素的列表(integer) 1redis> LPUSHX greet "good morning" # 这次LPUSHX执行成功(integer) 2redis> LRANGE greet 0 -11) "good morning"2) "hello"
3、rpush(right push)方法:
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存在但不是列表类型时,返回一个错误。
备注:在Redis 2.4版本以前的RPUSH命令,都只接受单个value值。
# 添加单个元素redis> RPUSH languages c(integer) 1# 添加重复元素redis> RPUSH languages c(integer) 2redis> LRANGE languages 0 -1 # 列表允许重复元素1) "c"2) "c"# 添加多个元素redis> RPUSH mylist a b c(integer) 3redis> LRANGE mylist 0 -11) "a"2) "b"3) "c"
4、rpushx(right pushx)方法:
RPUSHX key value
将值value插入到列表key的表尾,当且仅当key存在并且是一个列表。
和RPUSH命令相反,当key不存在时,RPUSHX命令什么也不做。
例如:# 情况1:key不存在redis> LLEN greet(integer) 0redis> RPUSHX greet "hello" # 对不存在的key进行RPUSHX,PUSH失败。(integer) 0# 情况2:key存在且是一个非空列表redis> RPUSH greet "hi" # 先用RPUSH插入一个元素(integer) 1redis> RPUSHX greet "hello" # greet现在是一个列表类型,RPUSHX操作成功。(integer) 2redis> LRANGE greet 0 -11) "hi"2) "hello"
5、lpop(left pop)方法:
LPOP key
移除并返回列表key的头元素。
redis> LLEN course(integer) 0redis> RPUSH course algorithm001(integer) 1redis> RPUSH course c++101(integer) 2redis> LPOP course # 移除头元素"algorithm001"
6、rpop(right pop)方法:
RPOP key
移除并返回列表key的尾元素。
redis> RPUSH mylist "one"(integer) 1redis> RPUSH mylist "two"(integer) 2redis> RPUSH mylist "three"(integer) 3redis> RPOP mylist # 返回被弹出的元素"three"redis> LRANGE mylist 0 -1 # 列表剩下的元素1) "one"2) "two"
7、llen方法:
LLEN key
返回列表key的长度。
如果key不存在,则key被解释为一个空列表,返回0.
如果key不是列表类型,返回一个错误。
# 情况1:空列表redis> LLEN job(integer) 0# 情况2:非空列表redis> LPUSH job "cook food"(integer) 1redis> LPUSH job "have lunch"(integer) 2redis> LLEN job(integer) 2
8、lrange方法:
LRANGE key start end
返回列表key中指定区间内的元素,区间以偏移量start和end指定。
下标(index)参数start和end都以0为底,也就是说,以0表示列表的第一个元素,以1表示列表的第二个元素,以此类推。
你也可以使用负数下标,以-1表示列表的最后一个元素,-2表示列表的倒数第二个元素,以此类推。
超出范围的下标
超出范围的下标值不会引起错误。
如果start下标比列表的最大下标end(LLEN list减去1)还要大,或者start > stop,LRANGE返回一个空列表。
如果stop下标比end下标还要大,Redis将stop的值设置为end。
redis> RPUSH fp-language lisp # 插入一个值到列表fp-language(integer) 1redis> LRANGE fp-language 0 01) "lisp"redis> RPUSH fp-language scheme(integer) 2redis> LRANGE fp-language 0 11) "lisp"2) "scheme"
9、linsert方法:
LINSERT key BEFORE|AFTER pivot value
将值value插入到列表key当中,位于值pivot之前或之后。
当pivot不存在于列表key时,不执行任何操作。
当key不存在时,key被视为空列表,不执行任何操作。
如果key不是列表类型,返回一个错误。
redis> RPUSH mylist "Hello"(integer) 1redis> RPUSH mylist "World"(integer) 2redis> LINSERT mylist BEFORE "World" "There"(integer) 3redis> LRANGE mylist 0 -11) "Hello"2) "There"3) "World"redis> LINSERT mylist BEFORE "go" "let's" # 对一个非空列表插入,查找一个不存在的pivot(integer) -1 # 失败redis> EXISTS fake_list # 对一个空列表执行LINSERT命令(integer) 0redis> LINSERT fake_list BEFORE "nono" "gogogog"(integer) 0 # 失败
10、lset方法:
LSET key index value
将列表key下标为index的元素的值替换为value。
更多信息请参考LINDEX操作。
当index参数超出范围,或对一个空列表(key不存在)进行LSET时,返回一个错误。
# 情况1:对空列表(key不存在)进行LSETredis> EXISTS list(integer) 0redis> LSET list 0 item(error) ERR no such key# 情况2:对非空列表进行LSETredis> LPUSH job "cook food"(integer) 1redis> LRANGE job 0 01) "cook food"redis> LSET job 0 "play game"OKredis> LRANGE job 0 01) "play game"# 情况3:index超出范围redis> LLEN list # 列表长度为1(integer) 1redis> LSET list 3 'out of range'(error) ERR index out of range
11、lrem方法:
LREM key count value
根据参数count的值,移除列表中与参数value相等的元素。
count的值可以是以下几种:
count > 0: 从表头开始向表尾搜索,移除与value相等的元素,数量为count。
count < 0: 从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值。
count = 0: 移除表中所有与value相等的值。
# 先创建一个表,内容排列是# morning hello morning helllo morningredis> LPUSH greet "morning"(integer) 1redis> LPUSH greet "hello"(integer) 2redis> LPUSH greet "morning"(integer) 3redis> LPUSH greet "hello"(integer) 4redis> LPUSH greet "morning"(integer) 5redis> 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) 3redis> LRANGE greet 0 21) "hello"2) "hello"3) "morning"redis> LREM greet -1 morning # 移除从表尾到表头,第一个morning(integer) 1redis> LLEN greet(integer) 2redis> LRANGE greet 0 11) "hello"2) "hello"redis> LREM greet 0 hello # 移除表中所有hello(integer) 2 # 两个hello被移除redis> 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不是列表类型时,返回一个错误。
# 情况1:一般情况下标redis> LRANGE alpha 0 -1 # 建立一个5元素的列表1) "h"2) "e"3) "l"4) "l"5) "o"redis> LTRIM alpha 1 -1 # 删除索引为0的元素OKredis> LRANGE alpha 0 -1 # "h"被删除1) "e"2) "l"3) "l"4) "o"# 情况2:stop下标比元素的最大下标要大redis> LTRIM alpha 1 10086OKredis> LRANGE alpha 0 -11) "l"2) "l"3) "o"# 情况3:start和stop下标都比最大下标要大,且start < sotpredis> LTRIM alpha 10086 200000OKredis> LRANGE alpha 0 -1 # 整个列表被清空,等同于DEL alpha(empty list or set)# 情况4:start > stopredis> LRANGE alpha 0 -1 # 在新建一个列表1) "h"2) "u"3) "a"4) "n"5) "g"6) "z"redis> LTRIM alpha 10086 4OKredis> LRANGE alpha 0 -1 # 列表同样被清空(empty list or set)
13、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)操作。
# 相关数据redis> RPUSH alpha a(integer) 1redis> RPUSH alpha b(integer) 2redis> RPUSH alpha c(integer) 3redis> RPUSH alpha d(integer) 4# 情况1:source和destination不同redis> LRANGE alpha 0 -1 # 查看所有元素1) "a"2) "b"3) "c"4) "d"redis> RPOPLPUSH alpha reciver # 执行一次RPOPLPUSH看看"d"redis> LRANGE alpha 0 -11) "a"2) "b"3) "c"redis> LRANGE reciver 0 -11) "d"redis> RPOPLPUSH alpha reciver # 再执行一次,确保rpop和lpush的位置正确"c"redis> LRANGE alpha 0 -11) "a"2) "b"redis> LRANGE reciver 0 -11) "c"2) "d"# 情况2:source和destination相同redis> RPOPLPUSH alpha alpha"c"redis> LRANGE alpha 0 -1 # 原来的尾元素"c"被放到了头部1) "c"2) "a"3) "b"
14、lindex方法:
LINDEX key index
返回列表key中,下标为index的元素。
下标(index)参数start和stop都以0为底,也就是说,以0表示列表的第一个元素,以1表示列表的第二个元素,以此类推。
你也可以使用负数下标,以-1表示列表的最后一个元素,-2表示列表的倒数第二个元素,以此类推。
如果key不是列表类型,返回一个错误。
redis> LPUSH mylist "World"(integer) 1redis> LPUSH mylist "Hello"(integer) 2redis> LINDEX mylist 0"Hello"redis> LINDEX mylist -1"World"redis> LINDEX mylist 3 # index不在mylist的区间范围内(nil)