Redis 常用命令
Redis是什么基于内存的支持持久化k:v NOSQL 数据库用途缓存、数据库、消息中间件RedisDesktopManger功能持久化功能发布订阅功能事务功能过期键功能复制集群RedisRedis 是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库、缓存服务或消息服务使用。Redis支持多种数据结构,包括字符串、哈希表、链表、集合、有序集合、...
Redis
Redis简介
- Redis 是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库、缓存服务或消息服务使用。
- Redis支持多种数据结构,包括字符串、哈希表、链表、集合、有序集合、位图、Hyperloglogs 等。
- Redis具备LRU淘汰、事务实现、以及不同级别的硬盘持久化等能力,并且支持副本集和通RedisSentinel实现的高可用方案,同时还支持通过RedisCluster实现的数据自动分片能力。
- Redis:持久化功能、发布订阅功能、事务功能、过期键功能、复制、集群
Redis 的主要功能都基于单线程模型实现,也就是说 Redis 使用一个线程来服务所有的客户端请求,同时 Redis 采用了非阻塞式 IO,并精细地优化各种命令的算法时间复杂度,这些信息意味着:
- Redis 是线程安全的(因为只有一个线程),其所有操作都是原子的,不会因并发产生数据异常
- Redis 的速度非常快(因为使用非阻塞式 IO,且大部分命令的算法时间复杂度都是 O(1))
使用高耗时的 Redis 命令是很危险的,会占用唯一的一个线程的大量处理时间,导致所有的请求都被拖慢。(例如时间复杂度为 O(N) 的 KEYS 命令,严格禁止在生产环境中使用)
本文只对 Redis 命令进行扼要的介绍,且只列出了较常用的命令。如果想要了解完整的 Redis 命令集,或了解某个命令的详细使用方法,请参考官方文档:https://redis.io/commands
使用redis有哪些好处
- 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
- 支持丰富数据类型,支持string,list,set,sorted set,hash
### Redis 基本数据类型
Redis类型 | 含义 |
---|---|
String | 字符串 |
Hash | 字典 |
List | 列表 |
Set | 集合 |
Sorted set | 有序集合 |
Pub/Sub | 订阅 |
Transactions | 事物 |
字符串
Redis 字符串是一个字节序列。 在 Redis 中字符串是二进制安全的,这意味着它们没有任何
特殊终端字符来确定长度,所以可以存储任何长度为 512 兆的字符串。String 是 Redis 的基础数据类型,Redis 没有 Int、Float、Boolean 等数据类型的概念,所有的基本类型在 Redis 中都以 String 体现。
127.0.0.1:6379> set key1 key1_test
OK
127.0.0.1:6379> get key1
"key1_test"
应用场景
String是最常用的一种数据类型,普通的key/value存储都可以归为此类;
实现方式
String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。
与 String 相关的常用命令
- SET:为一个 key 设置 value,可以配合 EX/PX 参数指定 key 的有效期,通过 NX/XX 参数针对 key 是否存在的情况进行区别操作,时间复杂度 O(1)
- GET:获取某个 key 对应的 value,时间复杂度 O(1)
- GETSET:为一个 key 设置 value,并返回该 key 的原 value,时间复杂度 O(1)
- MSET:为多个 key 设置 value,时间复杂度 O(N)
- MSETNX:同 MSET,如果指定的 key 中有任意一个已存在,则不进行任何操作,时间复杂度 O(N)
- MGET:获取多个 key 对应的 value,时间复杂度 O(N)
上文提到过,Redis 的基本数据类型只有 String,但 Redis 可以把 String 作为整型或浮点型数字来使用,主要体现在 INCR、DECR 类的命令上:
- INCR:将 key 对应的 value 值自增 1,并返回自增后的值。只对可以转换为整型的 String 数据起作用。时间复杂度 O(1)
- INCRBY:将 key 对应的 value 值自增指定的整型数值,并返回自增后的值。只对可以转换为整型的 String 数据起作用。时间复杂度 O(1)
- DECR/DECRBY:同 INCR/INCRBY,自增改为自减。
- INCR/DECR 系列命令要求操作的 value 类型为 String,并可以转换为 64 位带符号的整型数字,否则会返回错误。
也就是说,进行 INCR/DECR 系列命令的 value,必须在 [-2^63 ~ 2^63 - 1] 范围内。
前文提到过,Redis 采用单线程模型,天然是线程安全的,这使得 INCR/DECR 命令可以非常便利的实现高并发场景下的精确控制。
哈希
Redis哈希是键值对的集合。Redis哈希是字符串字段和字符串值之间的映射,所以它们用来表示对象。
Hash 的优点包括:
- 可以实现二元查找,如” 查找 ID 为 1000 的用户的年龄”
- 比起将整个对象序列化后作为 String 存储的方法,Hash 能够有效地减少网络传输的消耗
- 当使用 Hash 维护一个集合时,提供了比 List 效率高得多的随机访问命令
应用场景
我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日
实现方式
Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。Key是用户ID, value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据。
与 Hash 相关的常用命令
- HSET:将 key 对应的 Hash 中的 field 设置为 value。如果该 Hash 不存在,会自动创建一个。时间复杂度 O(1)
- HGET:返回指定 Hash 中 field 字段的值,时间复杂度 O(1)
- HMSET/HMGET:同 HSET 和 HGET,可以批量操作同一个 key 下的多个 field,时间复杂度:O(N),N 为一次操作的 field 数量
- HSETNX:同 HSET,但如 field 已经存在,HSETNX 不会进行任何操作,时间复杂度 O(1)
- HEXISTS:判断指定 Hash 中 field 是否存在,存在返回 1,不存在返回 0,时间复杂度 O(1)
- HDEL:删除指定 Hash 中的 field(1 个或多个),时间复杂度:O(N),N 为操作的 field 数量
- HINCRBY:同 INCRBY 命令,对指定 Hash 中的一个 field 进行 INCRBY,时间复杂度 O(1)
127.0.0.1:6379> hset h1 name name_test
(integer) 0
127.0.0.1:6379> hset h1 age 18
(integer) 0
127.0.0.1:6379> hget h1 name
"name_test"
127.0.0.1:6379> hgetall h1
1) "name"
2) "name_test"
3) "age"
4) "18"
应谨慎使用的 Hash 相关命令
- HGETALL:返回指定 Hash 中所有的 field-value 对。返回结果为数组,数组中 field 和 value 交替出现。时间复杂度 O(N)
- HKEYS/HVALS:返回指定 Hash 中所有的 field/value,时间复杂度 O(N)
上述三个命令都会对 Hash 进行完整遍历,Hash 中的 field 数量与命令的耗时线性相关,对于尺寸不可预知的 Hash,应严格避免使用上面三个命令,而改为使用 HSCAN 命令进行游标式的遍历, 具体请见 官方文档
列表
Redis 列表是简单的字符串列表,通过插入顺序排序。可以使用 LPUSH/RPUSH/LPOP/RPOP
等命令添加一个元素到 Redis 列表的头部或尾部. 其时间复杂度较高(O(N))
应用场景
Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;
实现方式
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
与 List 相关的常用命令
- LPUSH:向指定 List 的左侧(即头部)插入 1 个或多个元素,返回插入后的 List 长度。时间复杂度 O(N),N 为插入元素的数量
- RPUSH:同 LPUSH,向指定 List 的右侧(即尾部)插入 1 或多个元素
- LPOP:从指定 List 的左侧(即头部)移除一个元素并返回,时间复杂度 O(1)
- RPOP:同 LPOP,从指定 List 的右侧(即尾部)移除 1 个元素并返回
- LPUSHX/RPUSHX:与 LPUSH/RPUSH 类似,区别在于,LPUSHX/RPUSHX 操作的 key 如果不存在,则不会进行任何操作
- LLEN:返回指定 List 的长度,时间复杂度 O(1)
- LRANGE:返回指定 List 中指定范围的元素(双端包含,即 LRANGE key 0 10 会返回 11 个元素),时间复杂度 O(N)。应尽可能控制一次获取的元素数量,一次获取过大范围的 List 元素会导致延迟,同时对长度不可预知的 List,避免使用 LRANGE key 0 -1 这样的完整遍历操作。
应谨慎使用的 List 相关命令
- LINDEX:返回指定 List 指定 index 上的元素,如果 index 越界,返回 nil。index 数值是回环的,即 - 1 代表 List 最后一个位置,-2 代表 List 倒数第二个位置。时间复杂度 O(N)
- LSET:将指定 List 指定 index 上的元素设置为 value,如果 index 越界则返回错误,时间复杂度 O(N),如果操作的是头 / 尾部的元素,则时间复杂度为 O(1)
- LINSERT:向指定 List 中指定元素之前 / 之后插入一个新元素,并返回操作后的 List 长度。如果指定的元素不存在,返回 - 1。如果指定 key 不存在,不会进行任何操作,时间复杂度 O(N)
由于 Redis 的 List 是链表结构的,上述的三个命令的算法效率较低,需要对 List 进行遍历,命令的耗时无法预估,在 List 长度大的情况下耗时会明显增加,应谨慎使用。
换句话说,Redis 的 List 实际是设计来用于实现队列,而不是用于实现类似 ArrayList 这样的列表的。如果你不是想要实现一个双端出入的队列,那么请尽量不要使用 Redis 的 List 数据结构。
为了更好支持队列的特性,Redis 还提供了一系列阻塞式的操作命令,如 BLPOP/BRPOP 等,能够实现类似于 BlockingQueue 的能力,即在 List 为空时,阻塞该连接,直到 List 中有对象可以出队时再返回。针对阻塞类的命令,此处不做详细探讨,请参考官方文档 中”Blocking operations on lists” 一节。
集合
Redis Set 是无序的,不可重复的 String 集合。
应用场景
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;
实现方式
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
与 Set 相关的常用命令
- SADD:向指定 Set 中添加 1 个或多个 member,如果指定 Set 不存在,会自动创建一个。时间复杂度 O(N),N 为添加的 member 个数
- SREM:从指定 Set 中移除 1 个或多个 member,时间复杂度 O(N),N 为移除的 member 个数
- SRANDMEMBER:从指定 Set 中随机返回 1 个或多个 member,时间复杂度 O(N),N 为返回的 member 个数
- SPOP:从指定 Set 中随机移除并返回 count 个 member,时间复杂度 O(N),N 为移除的 member 个数
- SCARD:返回指定 Set 中的 member 个数,时间复杂度 O(1)
- SISMEMBER:判断指定的 value 是否存在于指定 Set 中,时间复杂度 O(1)
- SMOVE:将指定 member 从一个 Set 移至另一个 Set
慎用的 Set 相关命令
- SMEMBERS:返回指定 Hash 中所有的 member,时间复杂度 O(N)
- SUNION/SUNIONSTORE:计算多个 Set 的并集并返回 / 存储至另一个 Set 中,时间复杂度 O(N),N 为参与计算的所有集合的总 member 数
- SINTER/SINTERSTORE:计算多个 Set 的交集并返回 / 存储至另一个 Set 中,时间复杂度 O(N),N 为参与计算的所有集合的总 member 数
- SDIFF/SDIFFSTORE:计算 1 个 Set 与 1 或多个 Set 的差集并返回 / 存储至另一个 Set 中,时间复杂度 O(N),N 为参与计算的所有集合的总 member 数。
上述几个命令涉及的计算量大,应谨慎使用,特别是在参与计算的 Set 尺寸不可知的情况下,应严格避免使用。可以考虑通过 SSCAN 命令遍历获取相关 Set 的全部 member, 具体请见 官方文档,如果需要做并集 / 交集 / 差集计算,可以在客户端进行,或在不服务实时查询请求的 Slave 上进行。
排序集合
Redis Sorted Set 是有序的、不可重复的 String 集合。Sorted Set 中的每个元素都需要指派一个分数 (score),Sorted Set 会根据 score 对元素进行升序排序。如果多个 member 拥有相同的 score,则以字典序进行升序排序。Sorted Set 非常适合用于实现排名。
应用场景
Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
实现方式
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
Sorted Set 的主要命令
- ZADD:向指定 Sorted Set 中添加 1 个或多个 member,时间复杂度 O(Mlog(N)),M 为添加的 member 数量,N 为 Sorted Set 中的 member 数量
- ZREM:从指定 Sorted Set 中删除 1 个或多个 member,时间复杂度 O(Mlog(N)),M 为删除的 member 数量,N 为 Sorted Set 中的 member 数量
- ZCOUNT:返回指定 Sorted Set 中指定 score 范围内的 member 数量,时间复杂度:O(log(N))
- ZCARD:返回指定 Sorted Set 中的 member 数量,时间复杂度 O(1)
- ZSCORE:返回指定 Sorted Set 中指定 member 的 score,时间复杂度 O(1)
- ZRANK/ZREVRANK:返回指定 member 在 Sorted Set 中的排名,ZRANK 返回按升序排序的排名,ZREVRANK 则返回按降序排序的排名。时间复杂度 O(log(N))
- ZINCRBY:同 INCRBY,对指定 Sorted Set 中的指定 member 的 score 进行自增,时间复杂度 O(log(N))
慎用的 Sorted Set 相关命令
- ZRANGE/ZREVRANGE:返回指定 Sorted Set 中指定排名范围内的所有 member,ZRANGE 为按 score 升序排序,ZREVRANGE 为按 score 降序排序,时间复杂度 O(log(N)+M),M 为本次返回的 member 数
- ZRANGEBYSCORE/ZREVRANGEBYSCORE:返回指定 Sorted Set 中指定 score 范围内的所有 member,返回结果以升序 / 降序排序,min 和 max 可以指定为 - inf 和 + inf,代表返回所有的 member。时间复杂度 O(log(N)+M)
- ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除 Sorted Set 中指定排名范围 / 指定 score 范围内的所有 member。时间复杂度 O(log(N)+M)
上述几个命令,应尽量避免传递 [0 -1] 或 [-inf +inf] 这样的参数,来对 Sorted Set 做一次性的完整遍历,特别是在 Sorted Set 的尺寸不可预知的情况下。可以通过 ZSCAN 命令来进行游标式的遍历, 具体请见 官方文档,或通过 LIMIT 参数来限制返回 member 的数量(适用于 ZRANGEBYSCORE 和 ZREVRANGEBYSCORE 命令),以实现游标式的遍历。
Redis订阅发布模式
Redis订阅和发布实现了通讯系统,发件人(在 Redis 中的术语称为发布者)发送邮件,而接
收器(订户)接收它们。信息传输的链路称为通道。Redis 一个客户端可以订阅任意数量的通
道
订阅发布主要命令:
- SUBSCRIBE: 订阅一个主题
- PUBLISH:向主题发布消息
127.0.0.1:6379> SUBSCRIBE redis
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redis"
3) (integer) 1
1) "message"
2) "redis"
3) "okokokok"
127.0.0.1:6379> PUBLISH redis okokokok
(integer) 1
Redis事务
Redis事务允许一组命令在单一步骤中执行。事务有两个属性,说明如下:
- 在一个事务中的所有命令作为单个独立的操作顺序执行。在Redis事务中的执行过程中而
另一客户机发出的请求,这是不可以的; - Redis事务是原子的。原子意味着要么所有的命令都执行,要么都不执行;
Redis 事务由指令 MULTI 发起的,之后传递需要在事务中和整个事务中,最后由 EXEC 命令
执行所有命令的列表
127.0.0.1:6379> WATCH name
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set name name1
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379>
127.0.0.1:6379> EXEC
(nil)
Redis的持久化
常见面试题
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)