hash类型
Redis自身已经是键值对结构了。Redis自身的键值对就是通过哈希的方式来组织的。
把key这一层组织完成之后,到了value这一层。value的其中一种类型还可以再是哈希。哈希类型中的映射关系通常称为field-value,用于区分Redis整体的键值对(key-value)。注意这里的 value 是指 field 对应的值,不是键(key)对应的值,请注意 value 在不同上下文的作用。
查询命令:
hset hget hexists hdel
hset hget:
hset:设置hash中指定的字段(field)的值(value)。
语法:HSET key field value [field value …]
时间复杂度:插入一组field为0(1),插入N组field为O(N)
返回值:添加的字段的个数。
hget:获取hash中指定字段的值。
语法:HGET key field
时间复杂度:0(1)
返回值:字段对应的值或者nil。
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> hset key f1 111
(integer) 1
127.0.0.1:6379> hset key f2 222 f3 333 f4 444
(integer) 3
127.0.0.1:6379> hget key f1
"111"
127.0.0.1:6379> hget key f2
"222"
127.0.0.1:6379> hget key f100
(nil)
127.0.0.1:6379> hget key2 f1
(nil)
hexists hdel:
hexists:判断hash中是否有指定的字段。
语法:HEXISTS key field
时间复杂度:0(1)
返回值:1表示存在,0表示不存在。
hdel:删除hash中指定的字段。
时间复杂度:删除一个元素为O(1).删除N个元素为O(N).
返回值:本次操作删除的字段个数。
-- hexists 命令
127.0.0.1:6379> hexists key f1
(integer) 1
127.0.0.1:6379> hexists key f2
(integer) 1
127.0.0.1:6379> hexists key f100
(integer) 0
127.0.0.1:6379> hexists key2 f1
(integer) 0
-- hdel命令
127.0.0.1:6379> hdel key f1
(integer) 1
127.0.0.1:6379> hexists key f1
(integer) 0
127.0.0.1:6379> hdel key f2 f3
(integer) 2
127.0.0.1:6379> hget key f4
"444"
127.0.0.1:6379> hget key f3
(nil)
127.0.0.1:6379> hget key f2
(nil)
127.0.0.1:6379> hdel key f1
(integer) 0
注意区分hdel和del.
del是删除整个键值对。hdel只是删除一个键值中值是hash类型的一个键值对。
hkeys hvals
hkeys:获取hash中的所有字段。
语法:HKEYS key
时间复杂度:O(N),N为field的个数.
返回值:字段列表。
hvals:和hkeys相对。能够获取到hash中的所有value
语法:HVALS key
时间复杂度:O(N),N为field的个数.
返回值:所有的值。
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> hset key f1 111 f2 222 f3 333 f4 444
(integer) 4
127.0.0.1:6379> hkeys key
1) "f1"
2) "f2"
3) "f3"
4) "f4"
127.0.0.1:6379> hvals key
1) "111"
2) "222"
3) "333"
4) "444"
hgetall hmget
hgetall:获取hash中的所有字段以及对应的值。
语法:HGETALL key
时间复杂度:O(N),N为field的个数.
返回值:字段和对应的值。
hmget:一次获取hash中多个字段的值。
语法:HMGET key field [field …]
时间复杂度:只查询一个元素为0(1),查询多个元素为O(N),N为了查询元素个数。
返回值:字段对应的值或者nil。
127.0.0.1:6379> hgetall key
1) "f1"
2) "111"
3) "f2"
4) "222"
5) "f3"
6) "333"
7) "f4"
8) "444"
127.0.0.1:6379> hmget key f1 f2 f3
1) "111"
2) "222"
3) "333"
有没有hmset,一次设置多个field和value呢?答案是又的,但是,并不需要使用.hset已经支持一次设置多个field和value了。
上述hkeys,hvals,hgetall都是存在一定风险的.hash的元素个数太多,执行的耗时会比较长,从而阻塞redis。可以采用,hscan遍历redis的hash.但是它属于"渐进式遍历"。
hlen hsetnx
hlen
语法:hlen key
获取 hash 的元素个数,不需要遍历的
返回值:hash 的元素个数
hsetnx
语法:hsetnx key field value
类似于setnx.不存在的时候,才能设置成功.如果存在,则失败.
返回值:1 - 成功 0 - 失败
127.0.0.1:6379> hlen key
(integer) 4
127.0.0.1:6379> hgetall key
1) "f1"
2) "111"
3) "f2"
4) "222"
5) "f3"
6) "333"
7) "f4"
8) "444"
-- hsetnx.不存在的时候,才能设置成功.如果存在,则失败.
127.0.0.1:6379> hsetnx key f5 555
(integer) 1
127.0.0.1:6379> hsetnx key f5 666
(integer) 0
127.0.0.1:6379> hget key f5
"555"
hincrby hincrbyfloat
hash 这里的 value,也可以当做数字来处理,hincrby 就可以加减整数。hincrbyfloat 就可以加减小数。使用频率不算很高,redis 没有提供类似于 incr decr。
127.0.0.1:6379> hincrby key f1 10
(integer) 121
127.0.0.1:6379> hincrby key f1 -20
(integer) 101
127.0.0.1:6379> hget key f1
"101"
127.0.0.1:6379> hincrbyfloat key f1 0.5
"101.5"
127.0.0.1:6379> hincrbyfloat key f1 -2.5
"99"
hash编码
- 哈希中的元素个数比较少,使用ziplist表示.元素个数比较多,使使用hashtable来表示
- 每个value的值长度都比较短,使用ziplist表示.如果某个value的长度太长了,也会转换成hashtable
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> hset key f1 111
(integer) 1
127.0.0.1:6379> object encoding key
"ziplist"
127.0.0.1:6379> hset key f2 222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
(integer) 1
127.0.0.1:6379> object encoding key
"hashtable"
list 列表类型
列表 (List) 相当于 数组 或者 顺序表。
注意,list内部的结果(编码方式)。并非是一个简单的数组。而是更接近于"双端队列"(deque)。
约定最左侧元素下标是0,redis的下标支持负数下标。支持头插头删,尾插尾删。
列表中的元素是有序的,"有序"的含义,要根据上下文区分。有的时候,谈到有序,指的是**“升序”,“降序”,有的时候,谈到的有序指的是,顺序很关键**。redis的list类型指的就是后者,如果把元素位置颠倒,顺序调换。此时得到的新的List和之前的List是不等价的。
列表中的元素是允许元素重复的。为当前的 List,头和尾都能高效的插入删除元素,就可以把这个 List 当做一个 栈/队列来使用了。Redis 有一个典型的应用场景,就是作为消息队列最早的时候, 就是通过 List 类型。
类型后来 Redis 又提供了一个stream类型来作为消息列。
lpush 和 lrange
lpush:头插,一次可以插入一个元素,也可以插入多个元素。
语法:LPUSH key element [element …]
时间复杂度O(1)。返回值是list的长度.
注意事项:
如果key已经存在,并且key对应的value类型,不是list,此时Ipush命令就要报错。redis中所有的这些各种数据类型的操作,都是类似的效果。
Irange命令查看list中指定范围的元素.
LRANGE key start stop
此处描述的区间也是闭区间,下标支持负数。
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> lpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> lpush key 5 6 7 8
(integer) 8
127.0.0.1:6379> lrange key 0 -1
1) "8"
2) "7"
3) "6"
4) "5"
5) "4"
6) "3"
7) "2"
8) "1"
-- 数组越界的情况:
-- Redis 的做法, 是直接尽可能的获取到给定区间的元素,
-- 如果给定区间非法,比如超出下标。就会尽可能的获取对应的内容。
127.0.0.1:6379> lrange key 0 100
1) "8"
2) "7"
3) "6"
4) "5"
5) "4"
6) "3"
7) "2"
8) "1"
127.0.0.1:6379> lrange key 10 100
(empty array)
"鲁棒性“(你对我越粗鲁,我就表现的越棒)。程序的容错能力更强。
lpushx
lpushx和lpush的操作基本一致,不同之处在于,lpushx在key存在时,将一个或者多个元素从左侧放入(头插)到list中。不存在,直接返回。
127.0.0.1:6379> lpushx key 9 10 11 12
(integer) 12
127.0.0.1:6379> lrange key 0 -1
1) "12"
2) "11"
3) "10"
4) "9"
5) "8"
6) "7"
7) "6"
8) "5"
9) "4"
10) "3"
11) "2"
12) "1"
127.0.0.1:6379> lpushx key2 1 2 3 4
(integer) 0
127.0.0.1:6379> lrange key2 0 -1
(empty array)
127.0.0.1:6379> exists key2
(integer) 0
rpush 和 rpushx
rpush 和 rpushx 这一组命令和lpush 和 lpushx用法基本一致,但是表示的并不是头插了,表示的是尾插。
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> rpush key 5 6 7 8
(integer) 8
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
-- rpushx 有则尾插,无则不操作
127.0.0.1:6379> rpushx key 9 10 11 12
(integer) 12
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"
10) "10"
11) "11"
12) "12"
127.0.0.1:6379> rpushx key2 1 2 3 4
(integer) 0
127.0.0.1:6379> lrange key2 0 -1
(empty array)
127.0.0.1:6379> exists key2
(integer) 0
lpop 和 rpop
lpop : 从list左侧取出元素(即头删)
rpop : 从list右侧取出元素(即尾删)
操作:LPOP key
操作:RPOP key [count]
LPOP 和 RPOP。从redis6.2版本,新增了一个count参数。count就描述这次要删几个元素。
返回值:取出的元素或者nil
时间复杂度:O(1)
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> lpop key
"1"
127.0.0.1:6379> lpop key
"2"
127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "4"
127.0.0.1:6379> lpop key
"3"
127.0.0.1:6379> lpop key
"4"
127.0.0.1:6379> lrange key 0 -1
(empty array)
127.0.0.1:6379> lpop key
(nil)
-- rpop
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> rpop key
"4"
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> rpop key
"3"
127.0.0.1:6379> rpop key
"2"
127.0.0.1:6379> rpop key
"1"
127.0.0.1:6379> lrange key 0 -1
(empty array)
127.0.0.1:6379> rpop key
(nil)
lindex 和 linsert ,llen
lindex:给定下标,获取到对应的元素.
语法:LINDEX key index
返回值:取出的元素。如果下标非法,返回的是nil
linsert:在特定位置插入元素
语法:LINSERT key <BEFORE/AFTER> pivot element
BEFORE:指定位置之前
AFTER:指定位置之后
返回值是插入之后,得到的新的list的长度。
时间复杂度:O(N) 此处N指的是list中的元素个数。
如果要插入的列表中,基准值存在多个,怎么办?
linsert进行插入的时候,要根据基准值,找到对应的位置.从左往右找,找到第一个符合基准值的位置即可.
llen 命令:获取list⻓度
127.0.0.1:6379> rpush key 1 2 3 4 5 6 7 8
(integer) 8
127.0.0.1:6379> lindex key 3
"4"
127.0.0.1:6379> lindex key -1
"8"
127.0.0.1:6379> lindex key 100
(nil)
-- linsert命令
127.0.0.1:6379> linsert key before 4 100
(integer) 9
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "100"
5) "4"
6) "5"
7) "6"
8) "7"
9) "8"
127.0.0.1:6379> linsert key after 4 200
(integer) 10
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "100"
5) "4"
6) "200"
7) "5"
8) "6"
9) "7"
10) "8"
-- linsert进行插入的时候,要根据基准值,找到对应的位置.从左往右找,找到第一个符合基准值的位置即可.
127.0.0.1:6379> rpush key 4
(integer) 11
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "100"
5) "4"
6) "200"
7) "5"
8) "6"
9) "7"
10) "8"
11) "4"
127.0.0.1:6379> linsert key before 4 300
(integer) 12
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "100"
5) "300"
6) "4"
7) "200"
8) "5"
9) "6"
10) "7"
11) "8"
12) "4"
-- llen命令
127.0.0.1:6379> llen key
(integer) 12
127.0.0.1:6379> llen key2
(integer) 0
lrem
Irem: 删除列表中的元素
rem => remove
语法:LREM key count element
- count :要删除的个数
- element : 要删除的值
- count > 0 从左往右删,
- count < 0 从右往左删
- count = 0 删除全部
准备工作:
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 8
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 12
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 16
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "1"
6) "2"
7) "3"
8) "4"
9) "1"
10) "2"
11) "3"
12) "4"
13) "1"
14) "2"
15) "3"
16) "4"
代码案例:
删除左侧两个元素。
127.0.0.1:6379> lrem key 2 1
(integer) 2
127.0.0.1:6379> lrange key 0 -1
1) "2"
2) "3"
3) "4"
4) "2"
5) "3"
6) "4"
7) "1"
8) "2"
9) "3"
10) "4"
11) "1"
12) "2"
13) "3"
14) "4"
删除右侧两个元素。
127.0.0.1:6379> lrem key -2 1
(integer) 2
127.0.0.1:6379> lrange key 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "1"
6) "2"
7) "3"
8) "4"
9) "2"
10) "3"
11) "4"
12) "2"
13) "3"
14) "4"
删除全部:
127.0.0.1:6379> lrem key 0 1
(integer) 4
127.0.0.1:6379> lrange key 0 -1
1) "2"
2) "3"
3) "4"
4) "2"
5) "3"
6) "4"
7) "2"
8) "3"
9) "4"
10) "2"
11) "3"
12) "4"
ltrim 和 lset
ltrim
语法:LTRIM key start stop
含义:保留start和stop之间区间内的元素(区间外面两边的元素就直接玻删除了)
lset
语法:LSET key index element
含义:根据下标,修改元素
如果输入的index非法,会直接报错。lindex可以很好从处理下标越界的情况,直接返回nil。lset来说则会报错,不会像js那样,直接在10这个下标这里搞出个元素来。
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> rpush key 1 2 3 4 5 6 7 8
(integer) 8
127.0.0.1:6379> ltrim key 2 5
OK
127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "4"
3) "5"
4) "6"
-- lset 命令
127.0.0.1:6379> lset key 2 100
OK
127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "4"
3) "100"
4) "6"
-- 如果越界就会报错
127.0.0.1:6379> lset key 10 200
(error) ERR index out of range
阻塞版本的命令brpop和blpop
阻塞:当前的线程,不走了,代码不继续执行了.会在满足一定的条件之后,被唤醒。
如果list中存在元素,blpop和brpop就和lpop 以及rpop作用完全相同。如果list中为空,blpop和brpop就会产生阻塞.一直阻塞到队列不空为止。
但阻塞版本会根据timeout,阻塞一段时间。期间Redis可以执行其他命令。此处的blpop和brpop看起来
好像耗时很久,但是实际上并不会对redis服务器产生负面影响。
- 命令中如果设置了多个键,那么会从左向右进行遍历键,一旦有一个键对应的列表中可以弹出元素,命令立即返回。
- blpop和brpop都是可以同时去尝试获取多个key的列表的元素的。
- 多个key对应多个list。这多个list哪个有元素了,就会返回哪个元素。
- 如果多个客户端同时针对同一个键执行pop,则最先执行命令的客户端会得到弹出的元素。
语法:BLPOP key [key …] timeout
- 针对一个非空的列表进行操作
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> blpop key 0
1) "key"
2) "1"
返回的结果相当于一个pair(二元组),一方面是告诉我们当前的数据来自于哪个key。一方面告诉我们取到的数据是什么。
- 针对一个空的列表进行操作
-- 客户端1:
127.0.0.1:6379> del key
(integer) 1
127.0.0.1:6379> keys *
(empty array)
-- 针对一个空表进行取元素,等待时间为100s,进行阻塞
-- 等另一个客户端进行添加元素的时候,就会停止阻塞,取出元素
127.0.0.1:6379> blpop key 100
1) "key"
2) "1"
(53.95s)
-- 客户端2
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
- 针对多个key进行操作.
-- 客户端1:
127.0.0.1:6379> flushall
OK
-- 取出多个key中任意一个key的元素,此时所有key均为空
-- 当任意一个key有值的时候,就取出元素。
127.0.0.1:6379> blpop key key2 key3 key4 500
1) "key2"
2) "1"
(41.50s)
-- 客户端2:
127.0.0.1:6379> lrange key 0 -1
(empty array)
127.0.0.1:6379> lrange key2 0 -1
(empty array)
127.0.0.1:6379> lrange key3 0 -1
(empty array)
127.0.0.1:6379> lrange key4 0 -1
(empty array)
-- 四个key均为空,在key2中放入key值
127.0.0.1:6379> rpush key2 1
(integer) 1
brpop效果和blpop完全一样(这里是尾删了),就不再赘述了。
list类型的编码
在老版本的redis中编码分为ziplist(压缩列表)和linkedlist(链表)。把数据按照更紧凑的压缩形式进行表示的节省空间。当元素个数多了,操作起来效率会降低。在redis5及其之后,使用的是quicklist相当于是链表和压缩列表的结合.整体还是一个链表,链表的每个节点,是一个压缩列表.每个压缩列表,都不让它太大。同时再把多个压缩列表通过链式结构连起来
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> lpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding key
"quicklist"