redis学习之基本数据类型字符串,哈希,列表
关系型数据库和非关系型数据库
数据是一个程序的核心,因此,需要有专门存储数据的地方,数据库就就是存储数据的仓库,他本质上也是一个软件,安装在某个电脑,像服务器一样可以通过ip和端口访问,因此也称为数据库服务器。数据库,可分为关系型数据库和非关系型数据库,关系型数据库指采用了关系模型来组织数据的数据库**关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。非关系型数据库指非关系型的,分布式的,且一般不保证遵循ACID
原则的数据存储系统,非关系型数据库以键值对存储,且结构不固定,每一个元组可以有不一样的字段,每个元组可以根据需要增加一些自己的键值对,不局限于固定的结构,可以减少一些时间和空间的开销。例如传统的mysql,oracle等属于关系型数据库,而redis,MongoDB等属于非关系数据库。可以用来做缓存,存储文件等。
redis主要用来做什么
1.热点数据的缓存
由于redis访问速度块、支持的数据类型比较丰富,所以redis很适合用来存储热点数据,另外结合expire,我们可以设置过期时间然后再进行缓存更新操作,这个功能最为常见,我们几乎所有的项目都有所运用。
redis主要数据类型
redis键(key)
127.0.0.1:6379> keys * //查看所有key
1) "test"
127.0.0.1:6379> dbsize //查看key的总数
(integer) 1
127.0.0.1:6379> exists test //查看key是否存在,存在返回1,不存在返回0
(integer) 1
127.0.0.1:6379> exists test2
(integer) 0
127.0.0.1:6379> type test //查看key的类型
string
127.0.0.1:6379> ttl test //查看key的过期时间,-1代表永不过期
(integer) -1
127.0.0.1:6379> rename test test2 //给key重命名
OK
127.0.0.1:6379> keys *
1) "test2"
127.0.0.1:6379> expire test2 10 //设置key的过期时间
(integer) 1
127.0.0.1:6379> del test //删除key
(integer) 1
127.0.0.1:6379> move test 2 //移动key到2号库
(integer) 1
127.0.0.1:6379> select 2 //选择2号库
OK
127.0.0.1:6379[2]> flushall //清空整个 Redis 服务器的数据
OK
127.0.0.1:6379[2]> flushdb //清空当前库中的所有 key
OK
字符串String
set
127.0.0.1:6379[2]> set key "value" //对不存在的键进行设置
OK
127.0.0.1:6379[2]> get key //获取值
"value"
127.0.0.1:6379[2]> SET key "new-value" //对已存在的键进行设置新值
OK
127.0.0.1:6379[2]> get key
"new-value"
127.0.0.1:6379[2]> SET hello "hello" EX 10 //设置值时并指定过期时间为10秒
OK
setnx
127.0.0.1:6379[2]> setnx b 123 //设值成功
(integer) 1
127.0.0.1:6379[2]> setnx b 456 //重新覆盖值失败
(integer) 0
127.0.0.1:6379[2]> get b
"123"
setex
127.0.0.1:6379[2]> SETNX CD TIME
(integer) 1
127.0.0.1:6379[2]> SETEX CD 15 TIMES2 //
OK
127.0.0.1:6379[2]> TTL CD
(integer) 8
127.0.0.1:6379[2]> GET CD
"TIMES2"
SETEX
命令的效果和
SET key value
EXPIRE key seconds
的效果类似,SETEX
和这两个命令的不同之处在于 SETEX
是一个原子(atomic)操作, 它可以在同一时间内完成设置值和设置过期时间这两个操作, 因此 SETEX
命令在储存缓存的时候非常实用。
get
- 返回值
- 如果键
key
不存在, 那么返回特殊值nil
; 否则, 返回键key
的值。 - 如果键
key
的值并非字符串类型, 那么返回一个错误, 因为GET
命令只能用于字符串值
127.0.0.1:6379[2]> get db
(nil)
127.0.0.1:6379[2]> set db redis
OK
127.0.0.1:6379[2]> get db
"redis"
GETSET
- 返回给定键
key
的旧值。 - 如果键
key
没有旧值, 也即是说, 键key
在被设置之前并不存在, 那么命令返回nil
。 - 当键
key
存在但不是字符串类型时, 命令返回一个错误。
127.0.0.1:6379[2]> getset db redis //没有旧值,返回nil
(nil)
127.0.0.1:6379[2]> get db
"redis"
127.0.0.1:6379[2]> getset db mysql //返回旧值redis
"redis"
127.0.0.1:6379[2]> get db
"mysql"
STRLEN
STRLEN
命令返回字符串值的长度。- 当键
key
不存在时, 命令返回0
。 - 当
key
储存的不是字符串值时, 返回一个错误。
127.0.0.1:6379[2]> set mykey "hello world"
OK
127.0.0.1:6379[2]> strlen mykey
(integer) 11
127.0.0.1:6379[2]> strlen haha //不存在的key,返回0
(integer) 0
APPEND
- 如果键
key
已经存在并且它的值是一个字符串,APPEND
命令将把value
追加到键key
现有值的末尾。 - 如果
key
不存在,APPEND
就简单地将键key
的值设为value
, 就像执行SET key value
一样。
127.0.0.1:6379[2]> append d1 apple //d1不存在,相当于set d1 apple
(integer) 5
127.0.0.1:6379[2]> append d1 123 //d1后面追加123
(integer) 8
127.0.0.1:6379[2]> get d1
"apple123"
SETRANGE
- 从偏移量
offset
开始, 用value
参数覆写(overwrite)键key
储存的字符串值。 - 不存在的键
key
当作空白字符串处理。 SETRANGE
命令会确保字符串足够长以便将value
设置到指定的偏移量上, 如果键key
原来储存的字符串长度比偏移量小(比如字符串只有5
个字符长,但你设置的offset
是10
), 那么原字符和偏移量之间的空白将用零字节(zerobytes,"\x00"
)进行填充。
127.0.0.1:6379[2]> set d2 "hello world"
OK
127.0.0.1:6379[2]> setrange d2 6 "redis"
(integer) 11
127.0.0.1:6379[2]> get d2
"hello redis"
127.0.0.1:6379[2]> setrange d1 5 redis //d1不存在,所以第5个位置前面的用\x00填充
(integer) 10
127.0.0.1:6379[2]> get d1
"\x00\x00\x00\x00\x00redis"
GETRANGE
- 返回键
key
储存的字符串值的指定部分, 字符串的截取范围由start
和end
两个偏移量决定 (包括start
和end
在内)。 - 负数偏移量表示从字符串的末尾开始计数,
-1
表示最后一个字符,-2
表示倒数第二个字符, 以此类推。 GETRANGE
通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。
127.0.0.1:6379[2]> set d3 "hello my friend"
OK
127.0.0.1:6379[2]> getrange d3 0 4 //返回索引0-4的字符,包括4。
"hello"
127.0.0.1:6379[2]> getrange d3 0 -1 //从第一个到最后一个
"hello my friend"
127.0.0.1:6379[2]> getrange d3 0 10086 //值域范围不超过实际字符串,超过部分自动被忽略
"hello my friend"
127.0.0.1:6379[2]> getrange d3 -1 -5// 不支持回绕操作
""
127.0.0.1:6379[2]> getrange d3 -5 -1 //负数索引
"riend"
INCR
为键 key
储存的数字值加上一。
如果键 key
不存在, 那么它的值会先被初始化为 0
, 然后再执行 INCR
命令。
如果键 key
储存的值不能被解释为数字, 那么 INCR
命令将返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。
127.0.0.1:6379[2]> set init 20
OK
127.0.0.1:6379[2]> incr init
(integer) 21
127.0.0.1:6379[2]> get init
"21"
INCRBY
在加上增量 increment
之后, 键 key
当前的值。
127.0.0.1:6379[2]> get init
"21"
127.0.0.1:6379[2]> INCRBY rank 20
(integer) 40
127.0.0.1:6379[2]> get init
"21"
DECR 和DECRBY
DECR
命令会返回键 key
在执行减一操作之后的值。
DECRBY 在减上增量 increment
之后, 键 key
当前的值。
127.0.0.1:6379[2]> set init2 30
OK
127.0.0.1:6379[2]> decr init2
(integer) 29
127.0.0.1:6379[2]> get init2
"29"
127.0.0.1:6379[2]> DECRBY init2 20
(integer) 9
127.0.0.1:6379[2]> get init2
"9"
MSET
-
同时为多个键设置值。
-
如果某个给定键已经存在, 那么
MSET
将使用新值去覆盖旧值, 如果这不是你所希望的效果, 请考虑使用MSETNX
命令, 这个命令只会在所有给定键都不存在的情况下进行设置。 -
MSET
是一个原子性(atomic)操作, 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。127.0.0.1:6379[2]> mset k1 a k2 b //设置k1和k2的值 OK 127.0.0.1:6379[2]> mget k1 k2 1) "a" 2) "b" 127.0.0.1:6379[2]> mset k1 c k2 d //覆盖k1和k2的值 OK 127.0.0.1:6379[2]> mget k1 k2 1) "c" 2) "d"
MSETNX
-
当且仅当所有给定键都不存在时, 为所有给定键设置值。
-
即使只有一个给定键已经存在,
MSETNX
命令也会拒绝执行对所有键的设置操作。 -
MSETNX
是一个原子性(atomic)操作, 所有给定键要么就全部都被设置, 要么就全部都不设置, 不可能出现第三种状态。
127.0.0.1:6379[2]> msetnx k3 f k4 g
(integer) 1
127.0.0.1:6379[2]> mget k3 k4
1) "f"
2) "g"
127.0.0.1:6379[2]> msetnx k3 h k4 u //重新设置失败
(integer) 0
127.0.0.1:6379[2]> mget k3 k4
1) "f"
2) "g"
哈希表
redis指的是数据也是以k,v的形式存储,只不过v是以k,v形式的hash存储的。
HSET
- 将哈希表
hash
中域field
的值设置为value
。 - 如果给定的哈希表并不存在, 那么一个新的哈希表将被创建并执行
HSET
操作。 - 如果域
field
已经存在于哈希表中, 那么它的旧值将被新值value
覆盖。
127.0.0.1:6379[2]> hset a a1 1 //a里面存了a1,1
(integer) 1
127.0.0.1:6379[2]> hget a a1
"1"
127.0.0.1:6379[2]> hset a a1 2 //重新设值
(integer) 0
127.0.0.1:6379[2]> hget a a1
"2"
HSETNX
- 当且仅当域
field
尚未存在于哈希表的情况下, 将它的值设置为value
。 - 如果给定域已经存在于哈希表当中, 那么命令将放弃执行设置操作。
- 如果哈希表
hash
不存在, 那么一个新的哈希表将被创建并执行HSETNX
命令。
127.0.0.1:6379[2]> hsetnx k2 k2 2
(integer) 1
127.0.0.1:6379[2]> hget k2 k2
"2"
127.0.0.1:6379[2]> hsetnx k2 k2 3 //已经存在,设值无法成功
(integer) 0
127.0.0.1:6379[2]> hget k2 k2
"2"
HEXISTS
检查给定域 field
是否存在于哈希表 hash
当中,HEXISTS命令在给定域存在时返回
1, 在给定域不存在时返回
0
127.0.0.1:6379[2]> hexists phone myphone
(integer) 0
127.0.0.1:6379[2]> hset phone myhone xiaomi
(integer) 1
127.0.0.1:6379[2]> hexists phone myhone
(integer) 1
HMSET
同时将多个 field-value
(域-值)对设置到哈希表 key
中。
此命令会覆盖哈希表中已存在的域。
如果 key
不存在,一个空哈希表被创建并执行 HMSET 操作。
127.0.0.1:6379[2]> hmset phones phone1 xiaomi phone2 huawei phone3 luojiya
OK
127.0.0.1:6379[2]> hget phones phone1
"xiaomi"
127.0.0.1:6379[2]> hget phones phone2
"huawei"
HMGET
返回哈希表 key
中,一个或多个给定域的值。
如果给定的域不存在于哈希表,那么返回一个 nil
值。
因为不存在的 key
被当作一个空哈希表来处理,所以对一个不存在的 key
进行 HMGET 操作将返回一个只带有 nil
值的表。
127.0.0.1:6379[2]> hmget phones phone1 phone2 phone3 phone4
1) "xiaomi"
2) "huawei"
3) "luojiya"
4) (nil)
HKEYS
返回哈希表 key
中的所有域。
127.0.0.1:6379[2]> hkeys phones
1) "phone1"
2) "phone2"
3) "phone3"
HVALS
返回哈希表 key
中所有域的值。
127.0.0.1:6379[2]> hvals phones
1) "xiaomi"
2) "huawei"
3) "luojiya"
HGETALL
返回哈希表 key
中,所有的域和值。
在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
127.0.0.1:6379[2]> hgetall phones
1) "phone1"
2) "xiaomi"
3) "phone2"
4) "huawei"
5) "phone3"
6) "luojiya"
HDEL
删除哈希表 key
中的一个或多个指定域,不存在的域将被忽略。返回值为被成功移除的域的数量,不包括被忽略的域。
127.0.0.1:6379[2]> hgetall phones
1) "phone1"
2) "xiaomi"
3) "phone2"
4) "huawei"
5) "phone3"
6) "luojiya"
127.0.0.1:6379[2]> hdel phones phone1
(integer) 1
127.0.0.1:6379[2]> hgetall phones
1) "phone2"
2) "huawei"
3) "phone3"
4) "luojiya"
127.0.0.1:6379[2]>
HLEN
返回哈希表 key
中域的数量。
127.0.0.1:6379[2]> hgetall phones
1) "phone2"
2) "huawei"
3) "phone3"
4) "luojiya"
127.0.0.1:6379[2]> hlen phones
(integer) 2
HSTRLEN
- 返回哈希表
key
中, 与给定域field
相关联的值的字符串长度(string length)。 - 如果给定的键或者域不存在, 那么命令返回
0
。
127.0.0.1:6379[2]> hgetall phones
1) "phone2"
2) "huawei"
3) "phone3"
4) "luojiya"
127.0.0.1:6379[2]> hstrlen phones phone2
(integer) 6
127.0.0.1:6379[2]> hstrlen phones phone3
(integer) 7
HINCRBY
- 为哈希表
key
中的域field
的值加上增量increment
。 - 增量也可以为负数,相当于对给定域进行减法操作。
- 如果
key
不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 - 如果域
field
不存在,那么在执行命令前,域的值被初始化为0
。 - 对一个储存字符串值的域
field
执行 HINCRBY 命令将造成一个错误。 - 本操作的值被限制在 64 位(bit)有符号数字表示之内。
127.0.0.1:6379[2]> hincrby counter init 200 //counter不存在,新创建
(integer) 200
127.0.0.1:6379[2]> hget counter init
"200"
127.0.0.1:6379[2]> hincrby counter init -50 //减50
(integer) 150
127.0.0.1:6379[2]> hset counter2 inint2 "hello,world"
(integer) 1
127.0.0.1:6379[2]> hget counter2 inint2
"hello,world"
127.0.0.1:6379[2]> hincrby counter2 inint2 50 //字符串无法执行此操作
(error) ERR hash value is not an integer
127.0.0.1:6379[2]>
列表
LPUSH
- 将一个或多个值
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
存在但不是列表类型时,返回一个错误。
127.0.0.1:6379[2]> lpush mylist a b c //从左边入队
(integer) 3
127.0.0.1:6379[2]> lrange mylist 0 -1 //从左边开始取值
1) "c"
2) "b"
3) "a"
LPUSHX
- 将值
value
插入到列表key
的表头,当且仅当key
存在并且是一个列表。 - 和 [LPUSH key value value …] 命令相反,当
key
不存在时, LPUSHX 命令什么也不做。
127.0.0.1:6379[2]> llen list1 //创建一个空列表
(integer) 0
127.0.0.1:6379[2]> lpushx list1 a //失败,因为list1是空的
(integer) 0
127.0.0.1:6379[2]> lpush list1 c
(integer) 1
127.0.0.1:6379[2]> lpushx list1 d //成功
(integer) 2
127.0.0.1:6379[2]> lrange list1 0 -1
1) "d"
2) "c"
RPUSH
- 将一个或多个值
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
存在但不是列表类型时,返回一个错误。
127.0.0.1:6379[2]> rpush list2 a b c
(integer) 3
127.0.0.1:6379[2]> lrange list2 0 -1
1) "a"
2) "b"
3) "c"
RPUSHX
- 将值
value
插入到列表key
的表尾,当且仅当key
存在并且是一个列表。 - 和 [RPUSH key value value …] 命令相反,当
key
不存在时, RPUSHX 命令什么也不做。
127.0.0.1:6379[2]> llen list3
(integer) 0
127.0.0.1:6379[2]> rpushx list3 a
(integer) 0
127.0.0.1:6379[2]> rpush list3 c
(integer) 1
127.0.0.1:6379[2]> rpushx list3 d
(integer) 2
127.0.0.1:6379[2]> lrange list3 0 -1
1) "c"
2) "d"
LPOP
移除并返回列表 key
的头元素。
127.0.0.1:6379[2]> lpop list3
"c"
RPOP
移除并返回列表 key
的尾元素。
127.0.0.1:6379[2]> rpop list3
"d"
RPOPLPUSH
命令 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)操作。
127.0.0.1:6379[2]> lpush mylist a b c d
(integer) 4
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379[2]> rpoplpush mylist mylist2 //将mylist的队尾元素a取出从左边放到mylist2
"a"
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "d"
2) "c"
3) "b"
127.0.0.1:6379[2]> lrange mylist2 0 -1
1) "a"
127.0.0.1:6379[2]> rpoplpush mylist mylist //把自己的队尾元素取出放到队首
"b"
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "b"
2) "d"
3) "c"
LREM
根据参数 count
的值,移除列表中与参数 value
相等的元素。
count
的值可以是以下几种:
count > 0
: 从表头开始向表尾搜索,移除与value
相等的元素,数量为count
。count < 0
: 从表尾开始向表头搜索,移除与value
相等的元素,数量为count
的绝对值。count = 0
: 移除表中所有与value
相等的值。
127.0.0.1:6379[2]> rpush mylist3 a b a c d
(integer) 5
127.0.0.1:6379[2]> lrange mylist3 0 -1
1) "a"
2) "b"
3) "a"
4) "c"
5) "d"
127.0.0.1:6379[2]> LREM mylist3 2 a //从队首开始移除两个一样的a
(integer) 2
127.0.0.1:6379[2]> lrange mylist3 0 -1
1) "b"
2) "c"
3) "d"
127.0.0.1:6379[2]> LREM mylist3 -1 c //移除从表尾到表头,第一个c
(integer) 1
127.0.0.1:6379[2]> lrange mylist3 0 -1
1) "b"
2) "d"
127.0.0.1:6379[2]> LREM mylist3 0 d // 移除表中所有d
(integer) 1
127.0.0.1:6379[2]> lrange mylist3 0 -1
1) "b"
LLEN
- 返回列表
key
的长度。 - 如果
key
不存在,则key
被解释为一个空列表,返回0
. - 如果
key
不是列表类型,返回一个错误。
127.0.0.1:6379[2]> LLEN JOB
(integer) 0
127.0.0.1:6379[2]> LPUSH JOB A
(integer) 1
127.0.0.1:6379[2]> LPUSH JOB B
(integer) 2
127.0.0.1:6379[2]> LLEN JOB
(integer) 2
LINDEX
- 返回列表
key
中,下标为index
的元素。 - 下标(index)参数
start
和stop
都以0
为底,也就是说,以0
表示列表的第一个元素,以1
表示列表的第二个元素,以此类推。 - 你也可以使用负数下标,以
-1
表示列表的最后一个元素,-2
表示列表的倒数第二个元素,以此类推。 - 如果
key
不是列表类型,返回一个错误。
127.0.0.1:6379[2]> lpush list4 a b c
(integer) 3
127.0.0.1:6379[2]> lindex list4 0
"c"
127.0.0.1:6379[2]> lindex list4 -1
"a"
127.0.0.1:6379[2]> lindex list4 -3
"c"
127.0.0.1:6379[2]> lindex list4 -4
(nil)
LINSERT
- 将值
value
插入到列表key
当中,位于值pivot
之前或之后。 - 当
pivot
不存在于列表key
时,不执行任何操作。 - 当
key
不存在时,key
被视为空列表,不执行任何操作。 - 如果
key
不是列表类型,返回一个错误。
127.0.0.1:6379[2]> lpush list6 a b c d f
(integer) 5
127.0.0.1:6379[2]> linsert list6 before d e //d之前插入e
(integer) 6
127.0.0.1:6379[2]> lrange list6 0 -1
1) "f"
2) "e"
3) "d"
4) "c"
5) "b"
6) "a"
127.0.0.1:6379[2]> linsert list6 before g m //在不存在的元素g之前插入不成功
(integer) -1
127.0.0.1:6379[2]> linsert list7 before a v //list7不存在插入不成功
(integer) 0
LSET
- 将列表
key
下标为index
的元素的值设置为value
。 - 当
index
参数超出范围,或对一个空列表(key
不存在)进行 LSET 时,返回一个错误。
127.0.0.1:6379[2]> lpush list a b
(integer) 2
127.0.0.1:6379[2]> lrange list 0 -1
1) "b"
2) "a"
127.0.0.1:6379[2]> lset list 1 c //1号位设值为c
OK
127.0.0.1:6379[2]> lrange list 0 -1
1) "b"
2) "c"
127.0.0.1:6379[2]> lset list 3 d //index 超出范围
(error) ERR index out of range
127.0.0.1:6379[2]> lset list2 1 n //空列表list2
(error) ERR no such key
127.0.0.1:6379[2]>
LRANGE
- 返回列表
key
中指定区间内的元素,区间以偏移量start
和stop
指定。 - 下标(index)参数
start
和stop
都以0
为底,也就是说,以0
表示列表的第一个元素,以1
表示列表的第二个元素,以此类推。 - 你也可以使用负数下标,以
-1
表示列表的最后一个元素,-2
表示列表的倒数第二个元素,以此类推。
127.0.0.1:6379[2]> lrange list 0 -1
1) "b"
2) "c"
127.0.0.1:6379[2]> lrange list 0 0
1) "b"
LTRIM
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
127.0.0.1:6379[2]> lpush list2 a b c d f
(integer) 5
127.0.0.1:6379[2]> lrange list2 0 -1
1) "f"
2) "d"
3) "c"
4) "b"
5) "a"
127.0.0.1:6379[2]> ltrim list2 1 -1
OK
127.0.0.1:6379[2]> lrange list2 0 -1
1) "d"
2) "c"
3) "b"
4) "a"