Redis 中的 5 种数据结构精讲(1)
扫描二维码
随时随地手机看文章
Redis 中有 5 种数据结构,分别是字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set),因为使用 Redis 场景的开发中肯定是无法避开这些基础结构的,所以熟练掌握它们也就成了一项必不可少的能力。
字符串类型
字符串是 Red is 中的最基础的数据结构,我们保存到 Redis 中的 key,也就是键,就是字符串结构的。除此之外,Redis 中其它数据结构也是在字符串的基础上设计的,可见字符串结构对于 Redis 是多么重要。
Redis 中的字符串结构可以保存多种数据类型,如:简单的字符串、JSON、XML、二进制等,但有一点要特别注意:在 Redis 中字符串类型的值最大只能保存 512 MB。
命令
下面通过命令了解一下对字符串类型的操作:
1.设置值
1 | set key value [EX seconds] [PX milliseconds] [NX|XX] |
set 命令有几个非必须的选项,下面我们看一下它们的具体说明:
EX seconds:为键设置秒级过期时间
PX milliseconds:为键设置毫秒级过期时间
NX:键必须不存在,才可以设置成功,用于添加
XX:键必须存在,才可以设置成功,用于更新
set 命令带上可选参数 NX 和 XX 在实际开发中的作用与 setnx 和 setxx 命令相同。我们知道 setnx 命令只有当 key 不存在的时候才能设置成功,换句话说,也就是同一个 key 在执行 setnx 命令时,只能成功一次,并且由于 Redis 的单线程命令处理机制,即使多个客户端同时执行 setnx 命令,也只有一个客户端执行成功。所以,基于 setnx 这种特性,setnx 命令可以作为分布式锁的一种解决方案。
而 setxx 命令则可以在安全性比较高的场景中使用,因为 set 命令执行时,会执行覆盖的操作,而 setxx 在更新 key 时可以确保该 key 已经存在了,所以为了保证 key 中数据类型的正确性,可以使用 setxx 命令。
2.获取值
1 | get key |
3.批量设置值
1 | mset key value |
4.批量获取值
1 | mget key |
如果有些键不存在,那么它的值将为 nil,也就是空,并且返回结果的顺序与传入时相同。
5.计数
1 | incr key |
incr 命令用于对值做自增操作,返回的结果分为 3 种情况:
如果值不是整数,那么返回的一定是错误
如果值是整数,那么返回自增后的结果
如果键不存在,那么就会创建此键,然后按照值为 0 自增, 就是返回 1
除了有 incr 自增命令外,Redis 中还提供了其它对数字处理的命令。例如:
1 2 3 4 | decr key 自减 incrby kek increment 自增指定数字 decrby key decrement 自减指定数字 incrbyfloat key increment 自增浮点数 |
6.追加值
1 | append key value |
append 命令可以向字符串尾部追加值。
7.字符串长度
1 | strlen key |
由于每个中文占用 3 个字节,所以 jilinwula 这个键,返回是字符串长度为 12,而不是 4。
8.设置并返回原值
1 | getset key value |
9.设置指定位置的字符
1 | setrange key offeset value |
10.获取部分字符串
1 | getrange key start end |
时间复杂度
在 Redis 中执行任何命令时,都有相应的时间复杂度,复杂度越高也就越费时间,所以在执行 Redis 中的命令时,如果要执行的命令复杂度越高,就越要慎重。下面是字符串命令时间复杂度类型表:
命令 | 时间复杂度 |
set key value | O(1) |
get key | O(1) |
del key | O(k) k是键的个数 |
mset key value | O(k) k是键的个数 |
mget key | O(k) k是键的个数 |
incr key | O(1) |
decr key | O(1) |
incrby key increment | O(1) |
decrby keky increment | O(1) |
incrbyfloat key iincrement | O(1) |
append key value | O(1) |
strlen key | O(1) |
setrange key offset value | O(1) |
getrange key start end | O(n) n是字符串长度 |
内部编码
在 Redis 中字符串类型的内部编码有 3 种:
int:8 个字节的长整型
embstr:小于等于 39 个字节的字符串
raw:大于 39 个字节的字符串
哈希类型
大部分语言基本都提供了哈希类型,如 Java 语言中的 Map 类型及 Python 语言中的字典类型等等。虽然语言不同,但它们基本使用都是一样的,也就是都是键值对结构的。例如:
1 | value={{field1, value1} |
通过下图可以直观感受一下字符串类型和哈希类型的区别:
Redis 中哈希类型都是键值对结构的,所以要特别注意这里的 value 并不是指 Redis 中 key 的 value,而是哈希类型中的 field 所对应的 value。
命令
下面我们还是和介绍字符串类型一样,了解一下 Redis 中哈希类型的相关命令。
1.设置值
1 | hset key field value |
我们看上图执行的命令知道,hset 命令也是有返回值的。如果 hset 命令设置成功,则返回 1,否则返回 0。除此之外 Redis 也为哈希类型提供了 hsetnx 命令。在前文对字符串的介绍中,我们知道 nx 命令只有当 key 不存在的时候,才能设置成功,同样的,hsetnx 命令在 field 不存在的时候,才能设置成功。
2.获取值
1 | hget key field |
我们看 hget 命令和 get 有很大的不同,get 命令在获取的时候,只要写一个名字就可以了,而 hget 命令则要写两个名字,第一个名字是 key,第二个名字是 field。当然 key 或者 field 不存在时,返回的结果都是 nil。
3.删除 field
1 | hdel key field [field ...] |
hdel 命令删除的时候,也会有返回值,并且这个返回就是成功删除 field 的个数。当 field 不存在时,并不会报错,而是直接返回 0。
4.计算 field 个数
1 | hlen key |
hlen 命令返回的就是当前 key 中 field 的个数,如果 key 不存在,则返回 0。
5.批量设置或获取 field-value
1 2 | hmget key field [field ...] hmset key field value [field value ...] |
hmset 命令和 hmget 命令分别是批量设置和获取值的,hmset 命令没有什么要注意的,但 hmget 命令要特别注意,当我们获取一个不存在的 key 或者不存在的 field 时,Redis 并不会报错,而是返回 nil。并且有几个 field 不存在,则 Redis 返回几个 nil。
6.判断 field 是否存在
1 | hexists key field |
当执行 hexists 命令时,如果当前 key 包括 field,则返回 1,否则返回 0。
7.获取所有 field
1 | hkeys key |
8.获取所有 value
1 | hvals key |
9.获取所有的 field-value
1 | hgetall key |
hgetall 命令会返回当前 key 中的所有 field-value,并按照顺序依次返回。
10.计数
1 2 | hincrby key field increment hincrbyfloat key field increment |
hincrby 命令和 incrby 命令的使用功能基本一样,都是对值进行增量操作的,唯一不同的就是 incrby 命令的作用域是 key,而 hincrby 命令的作用域则是 field。
11.计算 value 的字符串长度
1 | hstrlen key field |
hstrlen 命令返回的是当前 key 中 field 中字符串的长度,如果当前 key 中没有 field 则返回 0。
时间复杂度
命令 | 时间复杂度 |
hset key field value | O(1) |
hget key field | O(1) |
hdel key field [field …] | O(k) ,k是field个数 |
hlen key | O(1) |
hgetall key | O(n) ,n是field总数 |
hmget key field [field …] | O(k) ,k是field个数 |
hmset key field value [field value …] | O(k) ,k是field个数 |
hexists key field | O(1) |
hkeys key | O(n) ,n是field总数 |
hvals key | O(n) ,n是field总数 |
hsetnx key field value | O(1) |
hincrby key field increment | O(1) |
hincrbyfloat key field increment | O(1) |
hstrlen key field | O(1) |
内部编码
Redis 哈希类型的内部编码有两种,它们分别是:
ziplist(压缩列表):当哈希类型中元素个数小于 hash-max-ziplist-entries 配置(默认 512 个),同时所有值都小于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使用 ziplist 作为哈希的内部实现。
hashtable(哈希表):当上述条件不满足时,Redis 则会采用 hashtable 作为哈希的内部实现。
下面我们通过以下命令来演示一下 ziplist 和 hashtable 这两种内部编码。
当 field 个数比较少并且 value 也不是很大时候 Redis 哈希类型的内部编码为 ziplist:
当 value 中的字节数大于 64 字节时(可以通过 hash-max-ziplist-value 设置),内部编码会由 ziplist 变成 hashtable。
当 field 个数超过 512(可以通过 hash-max-ziplist-entries 参数设置),内部编码也会由 ziplist 变成 hashtable。
由于直接手动创建 512 个 field 不方便,为了更好的验证该功能,我将用程序的方式,动态创建 512 个 field 来验证此功能,下面为具体的代码:
1 2 3 4 5 6 7 8 | import redis r = redis.Redis(host='127.0.0.1', port=6379) print('Key为【userinfo】的字节编码为【%s】' % r.object('encoding', 'userinfo').decode('utf-8')) for i in range(1,513): r.hset('userinfo', i, '吉林乌拉') print('Key为【userinfo】的字节编码为【%s】' % r.object('encoding', 'userinfo').decode('utf-8')) Key为【userinfo】的字节编码为【ziplist】 Key为【userinfo】的字节编码为【hashtable】 |
列表类型
Redis 中列表类型可以简单地理解为存储多个有序字符串的一种新类型,这种类型除了字符串类型中已有的功能外,还提供了其它功能,如可以对列表的两端插入和弹出元素(在列表中的字符串都可以称之为元素),除此之外还可以获取指定的元素列表,并且还可以通过索引下标获取指定元素等等。下面我们通过下图来看一下 Redis 中列表类型的插入和弹出操作:
下面我们看一下 Redis 中列表类型的获取与删除操作:
Redis 列表类型的特点如下:
列表中所有的元素都是有序的,所以它们是可以通过索引获取的,也就是上图中的 lindex 命令。并且在 Redis 中列表类型的索引是从 0 开始的。
列表中的元素是可以重复的,也就是说在 Redis 列表类型中,可以保存同名元素,如下图所示: