文章目录
- 1 :peach:前言:peach:
- 2 :peach:Redis 基本的全局命令:peach:
- 2.1 :apple:keys:apple:
- 2.2 :apple:exists:apple:
- 2.3 :apple:del:apple:
- 2.4 :apple:expire:apple:
- 2.5 :apple:ttl:apple:
- 2.6 :apple:type:apple:
- 3 :peach:单线程架构:peach:
- 4 :peach:Redis 的 5 种常见数据类型:peach:
- 5 :peach:String:peach:
- 5.1 :apple:set:apple:
- 5.2 :apple:get:apple:
- 5.3 :apple:mset:apple:
- 5.4 :apple:mget:apple:
- 5.5 :apple:incr:apple:
- 5.6 :apple:incrby:apple:
- 5.7 :apple:decr:apple:
- 5.8 :apple:decrby:apple:
- 5.9 :apple:incrbyfloat:apple:
- 5.10 :apple:append:apple:
- 5.11 :apple:getrange:apple:
- 5.12 :apple:setrange:apple:
- 5.13 :apple:strlen:apple:
- 5.14 :apple:内部编码:apple:
- 5.15 :apple:典型使用场景:apple:
- 5.15.1 :lemon:缓存功能:lemon:
- 5.15.2 :lemon:计数功能:lemon:
- 5.15.3 :lemon:共享会话:lemon:
- 5.15.4 :lemon:⼿机验证码:lemon:
- 6 :peach:Hash:peach:
- 6.1 :apple:hashset:apple:
- 6.2 :apple:hget:apple:
- 6.3 :apple:hexists:apple:
- 6.4 :apple:hdel:apple:
- 6.5 :apple:hkeys:apple:
- 6.6 :apple:hvals:apple:
- 6.7 :apple:hgetall:apple:
- 6.8 :apple:hmget:apple:
- 6.9 :apple:hlen:apple:
- 6.10 :apple:hsetnx:apple:
- 6.11 :apple:hincrby:apple:
- 6.12 :apple:内部编码:apple:
- 6.13 :apple:使用场景:apple:
- 6.13.1 :lemon:保存用户信息:lemon:
- 6.13.2 :lemon:作为缓存:lemon:
1 🍑前言🍑
在正式介绍 5 种常见的数据结构之前,了解⼀下 Redis 的⼀些全局命令、数据结构和内部编码、单线程命令处理机制是⼗分必要的,它们能为后⾯内容的学习打下⼀个良好的基础。
2 🍑Redis 基本的全局命令🍑
2.1 🍎keys🍎
返回所有满⾜样式(pattern)的 key,⽀持如下统配样式:
- h?llo 匹配 hello , hallo 和 hxllo等;
- h*llo 匹配 hllo 和 heeeello;
- h[ae]llo 匹配 hello 和 hallo 但不匹配 hillo;
- h[^e]llo 匹配 hallo , hbllo , … 但不匹配 hello;
- h[a-b]llo 匹配 hallo 和 hbll。
语法:
KEYS pattern
命令有效版本:1.0.0 之后
时间复杂度:O(N)
返回值:匹配 pattern 的所有 key。
实例:
其实这个规则与正则表达式的规则差不多。
2.2 🍎exists🍎
判断某个 key 是否存在。
语法:
EXISTS key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:key 存在的个数。
⽰例:
由于只有hello和hallo存在,所以返回值为2。
2.3 🍎del🍎
删除指定的 key。
语法:
DEL key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:删除掉的 key 的个数。
⽰例:
2.4 🍎expire🍎
为指定的 key 添加秒级的过期时间(Time To Live TTL)
语法:
EXPIRE key seconds
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:1 表⽰设置成功。0 表⽰设置失败。
⽰例:
等到过了5秒后:
k1就自动被删除了。
2.5 🍎ttl🍎
获取指定 key 的过期时间,秒级。
语法:
TTL key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:剩余过期时间。-1 表⽰没有关联过期时间,-2 表⽰ key 不存在。
⽰例:
注意: EXPIRE 和 TTL 命令都有对应的⽀持毫秒为单位的版本:
PEXPIRE
和PTTL
,详细⽤法与EXPIRE 和 TTL类似。
2.6 🍎type🍎
返回 key 对应的数据类型。
语法:
TYPE key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值: none , string , list , set , zset , hash and stream 等。
⽰例:
本⼩结只是抛砖引⽟,给出⼏个通⽤的命令,为 5 种数据结构的使⽤做⼀个热⾝,后续章节将对键管理做⼀个更为详细的介绍。
3 🍑单线程架构🍑
我们之前介绍了Redis 使⽤了单线程架构来实现⾼性能的内存数据库服务,但是Redis 单线程模型为什么性能如此之⾼?
在回答这个问题之前我们先要弄明白一件事情:Redis 服务器在同时接受多个客户端时是如何处理数据的?
其实Redis 内部实现是采取了类似于任务队列这样的方式来进行组织,将客户端的任务先放进任务队列中,然后服务器再从任务队列中取出命令来执行:
那有人会说如果有两个命令同时到达任务队列应该怎么办呢?其实大家不用想的那么复杂,同时到达随便排序即可,但其实这种场景很少见,大多数命令到达都会有一个时间差。
接下来再来回答为什么单线程还能这么快?
通常来讲,单线程处理能⼒要⽐多线程差,例如有 10000 公⽄货物,每辆⻋的运载能⼒是每次200 公⽄,那么要 50 次才能完成;但是如果有 50 辆⻋,只要安排合理,只需要1 次就可以完成任务。那么为什么 Redis 使⽤单线程模型会达到每秒万级别的处理能⼒呢?可以将其归结为三点:
- 纯内存访问。Redis 将所有数据放在内存中,内存的响应时⻓⼤约为 100 纳秒,这是 Redis 达到每秒万级别访问的重要基础。
- 非阻塞 IO。Redis 使⽤
epoll
作为 I/O 多路复⽤技术的实现,再加上 Redis ⾃⾝的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在⽹络 I/O 上浪费过多的时间。不知道多路复用技术的老哥可以移步博主的这两篇文章: - 单线程避免了线程切换和竞态产⽣的消耗。单线程可以简化数据结构和算法的实现,让程序模型更简单;其次多线程避免了在线程竞争同⼀份共享数据时带来的切换和等待消耗。
虽然单线程给 Redis 带来很多好处,但还是有⼀个致命的问题:对于单个命令的执⾏时间都是有要求的。如果某个命令执⾏过⻓,会导致其他命令全部处于等待队列中,迟迟等不到响应,造成客⼾端的阻塞,对于 Redis 这种⾼性能的服务来说是⾮常严重的,所以 Redis 是⾯向快速执⾏场景的数据库。所以一般我们写一些命令的时候我们不要写的太长。
4 🍑Redis 的 5 种常见数据类型🍑
type 命令实际返回的就是当前键的数据结构类型,它们分别是:String
(字符串)、List
(列表)、Hash
(哈希)、Set
(集合)、Zset
(有序集合),但这些只是 Redis 对外的数据结构,实际上 Redis 针对每种数据结构都有⾃⼰的底层内部编码实现,⽽且是多种实现,这样 Redis 会在合适的场景选择合适的内部编码。
数据结构 | 内部编码 |
---|---|
String | (raw) (int) (embstr) |
Hash | (hashtable) (ziplist) |
List | (linkedlist) (ziplist) (quicklist) |
Set | (hashtable) (intset) |
Zset | (skiplist) (ziplist) |
可以看到每种数据结构都有⾄少两种以上的内部编码实现,可以通过object encoding
命令查询内部编码:
比如:
另外在这里我们注意一下这个nil
,在C++中其实类似于nullptr
,表示空的意思,说明这个数据根本不存在。另外如果我们要清空所有的key值可以使用flushall
命令,不过要慎用,否则就等着律师函吧哈哈~~
Redis 一个数据结构使用多个内部编码设计有两个好处:
- 1)可以改进内部编码,⽽对外的数据结构和命令没有任何影响,这样⼀旦开发出更优秀的内部编码,⽆需改动外部数据结构和命令,例如 Redis 3.2 提供了 quicklist,结合了 ziplist 和 linkedlist 两者的优势,为列表类型提供了⼀种更为优秀的内部编码实现,⽽对⽤⼾来说基本⽆感知。
- 2)多种内部编码实现可以在不同场景下发挥各⾃的优势,例如 ziplist ⽐较节省内存,但是在列表元素⽐较多的情况下,性能会下降,这时候 Redis 会根据配置选项将列表类型的内部实现转换为linkedlist,整个过程⽤⼾同样⽆感知。
至于各个数据结构在什么情况下使用什么编码我们在后面会给出说明。
5 🍑String🍑
字符串类型是 Redis 最基础的数据类型,关于字符串需要特别注意:
- 1)⾸先 Redis 中所有的键的类型都是字符串类型,⽽且其他⼏种数据结构也都是在字符串类似基础上构建的,例如列表和集合的元素类型是字符串类型,所以字符串类型能为其他 4 种数据结构的学习奠定基础。
- 2)其次,字符串类型的值实际可以是字符串,包含⼀般格式的字符串或者类似 JSON、XML 格式的字符串;数字,可以是整型或者浮点型;甚⾄是⼆进制流数据,例如图⽚、⾳频、视频等。不过⼀个字符串的最⼤值不能超过 512 MB。
注意:由于 Redis 内部存储字符串完全是按照⼆进制流的形式保存的,所以 Redis 是不处理字符集编码问题的,客户端传⼊的命令中使⽤的是什么字符集编码,就存储什么字符集编码。
5.1 🍎set🍎
将 string 类型的 value 设置到 key 中。如果 key 之前存在,则覆盖,⽆论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。
语法:
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
命令有效版本:1.0.0 之后
时间复杂度:O(1)
选项:
SET 命令⽀持多种选项来影响它的⾏为:
- EX seconds⸺使⽤秒作为单位设置 key 的过期时间。
- PX milliseconds⸺使⽤毫秒作为单位设置 key 的过期时间。
- NX ⸺只在 key 不存在时才进⾏设置,即如果 key 之前已经存在,设置不执⾏。
- XX ⸺只在 key 存在时才进⾏设置,即如果 key 之前不存在,设置不执⾏。
注意:由于带选项的 SET 命令可以被 SETNX
、 SETEX
、 PSETEX
等命令代替,所以之后的版本中,Redis 可能进⾏合并。
返回值:
- 如果设置成功,返回 OK。
- 如果由于 SET 指定了 NX 或者 XX 但条件不满⾜,SET 不会执⾏,并返回 (nil)。
⽰例:
这里简单的演示一下SETNX
、 SETEX
的用法:
5.2 🍎get🍎
获取 key 对应的 value。如果 key 不存在,返回 nil。如果 value 的数据类型不是 string,会报错。
语法:
GET key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:key 对应的 value,或者 nil 当 key 不存在。
这个很简单就不演示啦。
5.3 🍎mset🍎
⼀次性设置多个 key 的值。
语法:
MSET key value [key value ...]
命令有效版本:1.0.1 之后
时间复杂度:O(N) N 是 key 数量
返回值:永远是 OK
⽰例:
5.4 🍎mget🍎
⼀次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string,返回 nil。
语法:
MGET key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N) N 是 key 数量
返回值:对应 value 的列表
⽰例:
那么此时我们肯定会有一个问题?多次set与一次mset效果一样吗?
效果是一样的,但是效率有所不同,一次set就是一次网络请求,而多次set就是多次网络请求,但是一次mset就只有一个网络请求。所以我们要进行多次set的话使用mset命令效率会更高。get也是同理。
所以学会使⽤批量操作,可以有效提⾼业务处理效率,但是要注意,每次批量操作所发送的键的数量也不是⽆节制的,否则可能造成单⼀命令执⾏时间过⻓,导致 Redis 阻塞。
5.5 🍎incr🍎
将 key 对应的 string 表⽰的数字加⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:
INCR key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:integer 类型的加完后的数值。
⽰例:
5.6 🍎incrby🍎
将 key 对应的 string 表⽰的数字加上对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:
INCRBY key decrement
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:integer 类型的加完后的数值。
⽰例:
5.7 🍎decr🍎
将 key 对应的 string 表⽰的数字减⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:
DECR key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:integer 类型的减完后的数值
用法与incr一致,就不再演示了。
5.8 🍎decrby🍎
将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:
DECRBY key decrement
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:integer 类型的减完后的数值。
用法与incrby一致,就不再演示了。
5.9 🍎incrbyfloat🍎
将 key 对应的 string 表⽰的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的不是 string,或者不是⼀个浮点数,则报错。允许采⽤科学计数法表⽰浮点数。
语法:
INCRBYFLOAT key increment
命令有效版本:2.6.0 之后
时间复杂度:O(1)
返回值:加/减完后的数值。
注意:减去一个浮点数也是用的是 incrbyfloat,后面跟一个负数即可。
5.10 🍎append🍎
如果 key 已经存在并且是⼀个 string,命令会将 value 追加到原有 string 的后边。如果 key 不存在,则效果等同于 SET 命令。
语法:
APPEND KEY VALUE
命令有效版本:2.0.0 之后
时间复杂度:O(1). 追加的字符串⼀般⻓度较短, 可以视为 O(1).
返回值:追加完成之后 string 的⻓度。
⽰例:
5.11 🍎getrange🍎
返回 key 对应的 string 的⼦串,由 start 和 end 确定(左闭右闭)。可以使⽤负数表⽰倒数。-1 代表倒数第⼀个字符,-2 代表倒数第⼆个,其他的与此类似。超过范围的偏移量会根据 string 的⻓度调整成正确的值。
语法:
GETRANGE key start end
命令有效版本:2.4.0 之后
时间复杂度:O(N) N 为 [start, end] 区间的⻓度. 由于 string 通常⽐较短, 可以视为是 O(1)
返回值:string 类型的⼦串
⽰例:
5.12 🍎setrange🍎
覆盖字符串的⼀部分,从指定的偏移开始。
语法:
SETRANGE key offset value
命令有效版本:2.2.0 之后
时间复杂度:O(N), N 为 value 的⻓度. 由于⼀般给的 value ⽐较短, 通常视为 O(1).
返回值:替换后的 string 的⻓度。
⽰例:
5.13 🍎strlen🍎
获取 key 对应的 string 的⻓度。当 key 存放的类似不是 string 时,报错。
语法:
STRLEN key
命令有效版本:2.2.0 之后
时间复杂度:O(1)
返回值:string 的⻓度。或者当 key 不存在时,返回 0。
⽰例:
5.14 🍎内部编码🍎
字符串类型的内部编码有 3 种:
- int:8 个字节的⻓整型。
- embstr:⼩于等于 39 个字节的字符串。
- raw:⼤于 39 个字节的字符串。
5.15 🍎典型使用场景🍎
5.15.1 🍋缓存功能🍋
Redis 作为缓冲层,MySQL 作为存储层,绝⼤部分请求的数据都是从 Redis 中获取。由于 Redis 具有⽀撑⾼并发的特性,所以缓存通常能起到加速读写和降低后端压⼒的作⽤。
5.15.2 🍋计数功能🍋
许多应⽤都会使⽤ Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。例如视频⽹站的视频播放次数可以使⽤Redis 来完成:⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增 1。
5.15.3 🍋共享会话🍋
⼀个分布式 Web 服务将⽤⼾的 Session 信息(例如⽤⼾登录信息)保存在各⾃的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤⼾的访问请求均衡到不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的。
此时我们可以采用Redis管理⽤⼾的 Session 信息,各个服务器可以直接从Redis 中写入以及读取⽤⼾的 Session 信息。
5.15.4 🍋⼿机验证码🍋
很多应⽤出于安全考虑,会在每次进⾏登录时,让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码,然后让⽤⼾再次输⼊收到的验证码并进⾏验证,从⽽确定是否是⽤⼾本⼈。为了短信接⼝不会频繁访问,会限制⽤⼾每分钟获取验证码的频率,例如⼀分钟不能超过 5 次。
6 🍑Hash🍑
⼏乎所有的主流编程语⾔都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组、映射。在 Redis 中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如 key = “key”,value = { {field1, value1 }, …, {fieldN, valueN } }
注意:哈希类型中的映射关系通常称为 field-value
,⽤于区分 Redis 整体的键值对(key-value),注意这⾥的 value 是指 field 对应的值,不是键(key)对应的值。
6.1 🍎hashset🍎
设置 hash 中指定的字段(field)的值(value)。
语法:
HSET key field value [field value ...]
命令有效版本:2.0.0 之后
时间复杂度:插⼊⼀组 field 为 O(1), 插⼊ N 组 field 为 O(N)
返回值:添加的字段的个数。
6.2 🍎hget🍎
获取 hash 中指定字段的值。
语法:
HGET key field
命令有效版本:2.0.0 之后
时间复杂度:O(1)
返回值:字段对应的值或者 nil。
6.3 🍎hexists🍎
判断 hash 中是否有指定的字段。
语法:
HEXISTS key field
命令有效版本:2.0.0 之后
时间复杂度:O(1)
返回值:1 表⽰存在,0 表⽰不存在。
6.4 🍎hdel🍎
删除 hash 中指定的字段。
语法:
HDEL key field [field ...]
命令有效版本:2.0.0 之后
时间复杂度:删除⼀个元素为 O(1). 删除 N 个元素为 O(N).
返回值:本次操作删除的字段个数。
6.5 🍎hkeys🍎
获取 hash 中的所有字段。
语法:
HKEYS key
命令有效版本:2.0.0 之后
时间复杂度:O(N), N 为 field 的个数.
返回值:字段列表。
6.6 🍎hvals🍎
获取 hash 中的所有的值。
语法:
HVALS key
命令有效版本:2.0.0 之后
时间复杂度:O(N), N 为 field 的个数.
返回值:所有的值。
6.7 🍎hgetall🍎
获取 hash 中的所有字段以及对应的值。
语法:
HGETALL key
命令有效版本:2.0.0 之后
时间复杂度:O(N), N 为 field 的个数.
返回值:字段和对应的值。
⽰例:
6.8 🍎hmget🍎
⼀次获取 hash 中多个字段的值。
语法:
HMGET key field [field ...]
命令有效版本:2.0.0 之后
时间复杂度:只查询⼀个元素为 O(1), 查询多个元素为 O(N), N 为查询元素个数.
返回值:字段对应的值或者 nil。
在使⽤ HGETALL 时,如果哈希元素个数⽐较多,会存在阻塞 Redis 的可能。如果开发⼈员只需要获取部分 field,可以使⽤ HMGET,如果⼀定要获取全部 field,可以尝试使⽤HSCAN
命令,该命令采⽤渐进式遍历哈希类型,HSCAN 会在后续章节介绍。
6.9 🍎hlen🍎
获取 hash 中的所有字段的个数。
语法:
HLEN key
命令有效版本:2.0.0 之后
时间复杂度:O(1)
返回值:字段个数。
⽰例:
6.10 🍎hsetnx🍎
在字段不存在的情况下,设置 hash 中的字段和值。
语法:
HSETNX key field value
命令有效版本:2.0.0 之后
时间复杂度:O(1)
返回值:1 表⽰设置成功,0 表⽰失败。
6.11 🍎hincrby🍎
将 hash 中字段对应的数值添加指定的值。
语法:
HINCRBY key field increment
命令有效版本:2.0.0 之后
时间复杂度:O(1)
返回值:该字段变化之后的值。
⽰例:
6.12 🍎内部编码🍎
哈希的内部编码有两种:
- ziplist(压缩列表):当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置(默认 512 个)、同时所有值都⼩于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使⽤ ziplist 作为哈希的内部实现,ziplist 使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐hashtable 更加优秀。
- hashtable(哈希表):当哈希类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,⽽ hashtable 的读写时间复杂度为 O(1)。
1)当 field 个数⽐较少且没有⼤的 value 时,内部编码为 ziplist。
2)当有 value ⼤于 64 字节时,内部编码会转换为 hashtable。
3)当 field 个数超过 512 时,内部编码也会转换为 hashtable。
6.13 🍎使用场景🍎
6.13.1 🍋保存用户信息🍋
我们先来分析下与关系型数据库(比如MySQL)相比,Redis 的优势与缺陷是什么?
- 哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的 field,⽽关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为 null。
- 关系数据库可以做复杂的关系查询,⽽ Redis 去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本⾼。
另外,相⽐于使⽤ JSON 格式的字符串缓存⽤⼾信息,哈希类型变得更加直观,并且在更新操作上变得更灵活。可以将每个⽤⼾的 id 定义为键后缀,多对 field-value 对应⽤⼾的各个属性。
6.13.2 🍋作为缓存🍋
截⾄⽬前为⽌,我们已经能够⽤三种⽅法缓存⽤⼾信息,下⾯给出三种⽅案的实现⽅法和优缺点分析。
- 原⽣字符串类型⸺使⽤字符串类型,每个属性⼀个键。
-
- 优点:实现简单,针对个别属性变更也很灵活。
-
- 缺点:占⽤过多的键,内存占⽤量较⼤,同时⽤⼾信息在 Redis 中⽐较分散,缺少内聚性,所以这种
⽅案基本没有实⽤性。
- 缺点:占⽤过多的键,内存占⽤量较⼤,同时⽤⼾信息在 Redis 中⽐较分散,缺少内聚性,所以这种
- 序列化字符串类型,例如 JSON(PROTOBUF) 格式。
-
- 优点:针对总是以整体作为操作的信息⽐较合适,编程也简单。同时,如果序列化⽅案选择合适,内存的使⽤效率很⾼。
-
- 缺点:本⾝序列化和反序列需要⼀定开销,同时如果总是操作个别属性则⾮常不灵活。
- 哈希类型。
-
- 优点:简单、直观、灵活。尤其是针对信息的局部变更或者获取操作。
-
- 缺点:需要控制哈希在 ziplist 和 hashtable 两种内部编码的转换,可能会造成内存的较⼤消耗。