数据类型
String
Redis字符串存储字节序列,包括文本、序列化对象和二进制数组。 默认情况下 单个Redis字符串最大值不能超过512m
常用命令
- SETNX仅当键不存在时才存储字符串值。对于实现锁很有用。
- MGET在一次操作中检索多个字符串值。
- INCRBY原子地增加(传递负数时减少)存储在给定键的计数器。
- INCRBYFLOAT 浮点计数器
性能
大多数字符串操作都是 O(1),这意味着它们非常高效。但是,请谨慎使用SUBSTR、GETRANGE和SETRANGE命令,因为它们的复杂度可能为 O(n)。这些随机访问字符串命令在处理大型字符串时可能会导致性能问题。
Hash
基本命令
- HSET:设置哈希表中一个或多个字段的值。
- HGET:返回给定字段的值。
- HMGET:返回一个或多个给定字段的值。
- HINCRBY:将给定字段的值增加提供的整数。
Redis7.4开始支持为各个哈希字段指定过期时间
- HEXPIRE:设置剩余TTL(秒数)。
- HPEXPIRE:设置剩余TTL,单位为毫秒。
- HEXPIREAT:将过期时间设置为以秒为单位指定的时间戳1。
- HPEXPIREAT:将过期时间设置为以毫秒为单位指定的时间戳。
- HPERSIST:删除有效期。
大多数 Redis 哈希命令都是 O(1)。一些命令(例如HKEYS、、HVALS)HGETALL以及大多数与到期相关的命令都是 O(n),其中n是字段值对的数量。
List
Redis 列表是通过双向链表实现的(意味着访问列表头、尾速度很快O(1),操作中间元素比较慢O(n)),常用于实现堆栈和队列。
Redis列表的最大长度为2^32-1(4,294,967,295)个元素。
基本命令
-
LPUSH 在列表的头部添加一个新元素;RPUSH添加到尾部。
-
LPOP从列表头部移除并返回一个元素;RPOP执行相同操作,但从列表尾部移除。
-
LLEN O(1)返回列表的长度。
-
LMOVE原子地将元素从一个列表移动到另一个列表。
-
LRANGE从列表中提取一定范围的元素。
-
LTRIM将列表缩减为指定的元素范围。
-
BLOP(阻塞)从列表头部移除并返回一个元素。如果列表为空,则命令将阻塞,直到有元素可用或达到指定的超时时间。
-
BLMOVE(阻塞)原子地将元素从源列表移动到目标列表。如果源列表为空,则命令将阻塞,直到有新元素可用。
Set
Set可以执行常见的集合运算,例如交集、并集和差集。
基本命令
- SADD向集合中添加新成员。
- SREM从集合中删除指定成员。
- SISMEMBER测试字符串是否为集合成员。
- SINTER返回两个或多个集合共同拥有的成员集合(即交集)。
- SCARD O(1)返回集合的大小(又称基数)。
- SDIFF 返回 第一个集合 与其他所有集合的差集,即在 key1 集合中存在,但在 key2 … keyN 集合中不存在的元素。时间复杂度为 O(N),其中 N 是所有集合元素总数的大小。Redis 会遍历所有集合来计算差集。
Redis 集合的最大大小为 2^32 - 1(4,294,967,295)个成员。
大多数集合操作(包括添加、删除和检查某个项是否为集合成员)都是 O(1)。这意味着它们非常高效。
对于大key的集合,应谨慎SMEMBERS。此命令是 O(n) 并在单个响应中返回整个集合。应用SSCAN代替,它允许迭代检索集合的所有成员。
Sorted Set
Sorted Set是按相关分数排序的唯一字符串(成员)的集合,有序集合中的每个元素都与一个浮点值相关联,称为分数 。分数越大排越前,当多个字符串具有相同的分数时,这些字符串将按字典顺序排序。
有序集合底层使用了跳跃列表和哈希表
基本命令
- ZADD O(log(N))将新成员和相关分数添加到排序集中。如果该成员已存在,则更新分数。
- ZRANGE返回在给定范围内排序的排序集的成员。
- ZRANK返回所提供成员的排名,假设排序按升序排列。
- ZREVRANK返回所提供成员的排名,假设排序集按降序排列。
Stream
Redis流是一种数据结构作用类似仅追加日志。Redis为每个流条目生成一个唯一ID。
Streams使用的radix trees实现
基本命令
- XADD向流中添加新条目。
- XREAD读取一个或多个条目,从给定位置开始并随时间向前移动。
- XRANGE返回两个提供的条目 ID 之间的条目范围。
- XLEN返回流的长度。
- XGROUP 创建消费组,可以让多个消费者并行处理流中的消息
Bitmap
位图不是实际的数据类型,而是一组定义在字符串类型上的面向位的操作,该字符串被视为位向量.可以对字符串按位运算
- SETBIT将提供的偏移量处的位设置为 0 或 1。
- GETBIT返回给定偏移量的位的值。
- BITCOUNT 用于计算字符串中所有位(bit)为 1 的数量
- BITOP 对不同的字符串执行按位运算。提供的运算有 AND、OR、XOR 和 NOT。
- BITPOS 查找第一个具有指定值 0 或 1 的位。
SETBIT和GETBIT都是 O(1)。 BITOP是 O(n),其中n是比较中最长的字符串的长度。
BitField
BitField 是一种更灵活的位操作命令,您设置、递增和获取任意位长度的整数值。
Geospatial
Redis 地理空间索引对于查找给定地理半径或边界框内的位置非常有用
- GEOADD将位置添加到给定的地理空间索引(请注意,在此命令中经度位于纬度之前)。
- GEOSEARCH返回具有给定半径或边界框的位置。
扩展类型(Redis Stack和Redis Enterprise)
- JSON
- Probabilistic data types
- Time series
高级特性
脚本
在运行脚本或函数时,Redis 保证其原子执行。脚本的执行会在整个过程中阻止所有服务器活动。
只读脚本是仅执行不修改 Redis 中任何键的命令的脚本。Redis支持如下两种方式
-
Redis Eval Scripts
通过EVAL/EVAL SHA命令在服务器上执行Lua脚本,运行在服务器端执行复杂的逻辑操作,不支持持久化,调用Script flush或重启Redis缓存会丢失。 -
Redis Functions
Redis Functions 是 Redis 7.0 引入的新特性,作为对 Eval Scripts 的增强。通过 FUNCTION 命令定义和管理 Redis 函数,支持持久化和复制。
事务
Redis 通过 MULTI、EXEC、WATCH 和 DISCARD 命令来实现事务。
WATCH mykey # 监视一个或多个见如果在执行EXEC前键被修改,事务不会执行
val = GET mykey
val = val + 1
MULTI # 开始事务
SET mykey $val
EXEC # 执行事务
Redis事务不支持回滚
客户端缓存
对某些很少变动,对性能要求比较高的,可以使用客户端缓存,即一、二级缓存。毕竟访问本地计算机内存所需的时间与访问Redis等网络服务相比要小几个数量级
Redis6.0开始支持客户端缓存,并提供了两种模式
- 跟踪模式
- 广播模式
Redis 分布式锁
RedLock:RedLock 是一种基于 Redis 的分布式锁算法,旨在解决在分布式环境中确保多个节点之间互斥访问共享资源的问题。RedLock 提出了在多个 Redis 实例上同时获取锁的方案,从而增加容错性。
Redis Modules
Redis 模块使得使用外部模块扩展 Redis 功能成为可能,Redis 模块是动态库,可以在启动时或使用MODULE LOAD命令加载到 Redis 中。
比如 Redis-Cell 令牌桶限流
客户端
RESP协议
RESP(Redis serialization protocol specification)仅用于客户端-服务器通信,Redis Cluster使用不同的二进制协议在节点之间交换消息。
RESP 本质上是一个支持多种数据类型的序列化协议。在 RESP 中,数据的第一个字节决定了其类型。
Redis 通常使用 RESP 作为请求-响应协议,方式如下:
\r\n (CRLF) 是协议的终止符,始终分隔协议的各个部分。
RESP 序列化有效负载中的第一个字节始终标识其类型,后续字节构成类型的内容。
以简单字符串为例
简单字符串被编码为一个加号 ( +) 字符,后面跟着一个字符串。
当 Redis 使用简单字符串回复时,客户端库应该向调用者返回一个由+字符串末尾的第一个字符组成的字符串值,不包括最后的 CRLF 字节。
+OK\r\n
Redis6.0引入了新的握手机制,客户端可以在连接建立后通过发送HELLO命令来开始于服务器通信,这一机制允许客户端和服务器协商使用不同版本的协议(RESP2 或 RESP3),并为客户端提供一些连接信息。
客户端工具
CLIENT LIST用于获取已连接客户端及其状态的列表
CLIENT KILL 关闭客户端的连接
从3.2开始,Redis默认开启了TCP keepalive(SO_KEEPALIVE OPTION),大约300秒。
Redis官方为Java提供两种连接工具
- Jedis,用于同步应用程序
- Lettuce,用于同步、异步、响应式应用
Redis Key
Redis关于Key的使用规则
1、key不能太长,也不必过短,需要在易读性和使用空间上找到平衡,user:1000:followers 比 u1000flow更好。
2、尽量使用object-type:id,点或破折号通常用于多个字段,如:“comment:4321:reply.to”或“comment:4321:reply-to”
3、允许的最大key为512m
4、使用scan来代替smmebers、keys,smember、keys会返回所有的keys,这将阻塞Redis服务器
key淘汰策略
redis.conf
maxmemory 100mb 设置Redis实例可以使用的最大内存量。不设置或者为0表示不限制。
当maxmemory达到限制时,有以下的的淘汰策略
- noeviction:达到内存限制时不会保存新值。当数据库使用复制时,这适用于主数据库
- allkeys-lru:保留最近使用的键;删除最近最少使用的 (LRU) 键
- allkeys-lfu:保留最常用的键;删除最不常用(LFU)的键
- volatile-lru :从设置了过期时间的键中选择最近最少使用的键进行淘汰。
- volatile-lfu :从设置了过期时间的键中随机选择键进行淘汰。
- allkeys-random:随机删除键以便为新添加的数据腾出空间。
- volatile-random:从设置了过期时间的键中随机选择键进行淘汰。
- volatile-ttl :从设置了过期时间的键中选择剩余时间最短的键进行淘汰
key过期策略
- 被动方式:当客户端尝试访问密钥并且该密钥已超时时,该密钥将被动过期。
- 主动方式:Redis 会定期在具有过期时间的键集中随机测试几个键。所有已过期的键都会从键空间中删除。
Redis Cluster
Redis节点间使用gossip协议传播集群信息。
Redis集群节点间使用异步复制,
集群密钥空间被分成16384个槽,键和槽的映射算法
HASH_SLOT = CRC16(key) mod 16384
节点握手
一个节点仅通过两种方式接受另一个节点作为集群的一部分:
-
如果节点向自己发送MEET消息(CLUSTER MEET命令)。 meet 消息与消息完全相同PING,但会强制接收方接受该节点作为集群的一部分。仅当系统管理员通过以下命令请求时,节点才会MEET向其他节点发送消息:
CLUSTER MEET IP 端口
-
如果一个已经受信任的节点会传播有关该节点的信息,则节点还会将另一个节点注册为集群的一部分。因此,如果 A 认识 B,而 B 认识 C,则最终 B 会向 A 发送有关 C 的gossip消息。发生这种情况时,A 会将 C 注册为网络的一部分,并尝试与 C 建立连接。
MOVED重定向
Redis 客户端可以自由地向集群中的每个节点(包括副本节点)发送查询。节点将分析查询,如果可以接受(即查询中仅提到一个键,或者提到的多个键都属于同一个哈希槽),它将查找哪个节点负责该键所属的哈希槽。
如果哈希槽由节点提供服务,则只需处理查询,否则节点将检查其内部哈希槽到节点映射,并使用 MOVED 错误回复客户端,如下例所示:
GET x
-MOVED 3999 127.0.0.1:6381
错误包括密钥的哈希槽 (3999) 和可以为查询提供服务的实例的端点:端口。客户端需要向指定节点的端点地址和端口重新发出查询。虽然没有强制要求,但是客户端应该尝试哈希槽和节点的映射关系,当集群稳定时,最终所有客户端可以直接寻址正确的节点,而无需重定向。
ASKING 重定向
当请求的键当前位于另一个节点,但该节点正在进行临时的主从切换,或数据正在被迁移。
和MOVED区别:
- MOVED:明确重定向,客户端需直接向新节点发送请求。
- ASKING:临时重定向,客户端仍然可以向原节点发送请求,并在稍后迁移到新节点。
心跳和gossip
Redis Cluster节点间不断交换ping和pong数据包,ping和pong数据包的总和成为心跳数据包。ping和pong都携带重要的配置信息。节点可以只发送 pong 数据包,向其他节点发送有关其配置的信息,而不会触发回复。
ping/pong数据包内容:
一、header部分包含以下信息
- 节点ID,一个 160 位伪随机字符串,在第一次创建节点时分配,并且在 Redis Cluster 节点的整个生命周期内保持不变。
- currentEpoch (集群版本,类似Raft term概念)
- 节点标志,指示节点是否是副本、主节点以及其他单比特节点信息。
- 发送节点所服务的哈希槽的位图,或者,如果该节点是副本,则为它的主节点所服务的槽的位图。
- 发送方 TCP 基本端口,即 Redis 用于接受客户端命令的端口
- 集群端口是 Redis 用于节点到节点通信的端口。
- 从发送方的角度来看集群的状态(关闭或正常)
- 如果发送节点是副本,则为发送节点的主节点 ID
二、gossip部分
- 节点ID
- 节点IP和端口
- 节点标志
哈希槽配置传播
哈希槽配置传播的方式有两种
- 心跳消息。ping 或 pong 数据包的发送方始终会添加有关其所服务的哈希槽集(或其主节点,如果是副本)的信息。
- UPDATE消息。由于每个心跳包中都有关于发送方configEpoch和服务的哈希槽集的信息,因此如果心跳包的接收方发现发送方信息已过时,它将发送一个包含新信息的数据包,从而迫使过时的节点更新其信息。
Redis持久化
RDB
RDB 持久性会按照指定的时间间隔对数据集执行时间点快照。
- RDB 是 Redis 数据的一个非常紧凑的单文件时间点表示。RDB 文件非常适合备份。例如,您可能希望每小时存档最近 24 小时内的 RDB 文件,并每天保存 30 天的 RDB 快照。这样,您就可以在发生灾难时轻松恢复数据集的不同版本。
- RDB 非常适合灾难恢复,它是一个单一的紧凑文件,可以传输到远程数据中心,或者传输到 Amazon S3(可能加密)。
- RDB 可最大程度地提高 Redis 的性能,因为 Redis 父进程为了持久化所需要做的唯一工作就是派生一个子进程,然后由子进程来完成其余所有工作。父进程永远不会执行磁盘 I/O 或类似操作。
- 与 AOF 相比,RDB 允许使用大数据集更快地重启。
- 在副本上,RDB 支持重启和故障转移后的部分重新同步。
AOF
AOF 持久性记录服务器收到的每个写入操作。然后可以在服务器启动时再次重放这些操作,重建原始数据集。命令使用与 Redis 协议本身相同的格式进行记录。
- 使用 AOF Redis 的持久性更强:您可以采用不同的 fsync 策略:完全不进行 fsync、每秒进行 fsync、每次查询时进行 fsync。使用每秒进行 fsync 的默认策略,写入性能仍然很好。fsync 使用后台线程执行,当没有正在进行 fsync 时,主线程将努力执行写入,因此您只会丢失一秒钟的写入。
- AOF 日志是仅附加日志,因此不会出现寻道,也不会在断电时出现损坏问题。即使日志因某种原因(磁盘已满或其他原因)以半写命令结束,redis-check-aof 工具也能够轻松修复它。
- 当 AOF 太大时,Redis 能够在后台自动重写。重写是完全安全的,因为 Redis 在继续向旧文件追加内容的同时,会使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备就绪,Redis 就会切换这两个文件并开始向新文件追加内容。
- AOF 以易于理解和解析的格式逐一记录所有操作的日志。您甚至可以轻松导出 AOF 文件。例如,即使您不小心使用该FLUSHALL命令刷新了所有内容,只要在此期间没有执行日志重写,您仍然可以通过停止服务器、删除最新命令并重新启动 Redis 来保存数据集。