Redis是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息中介。
它支持多种类型的数据结构,如字符串、列表、集合、散列表等。
Redis的优势在于其高性能、灵活性、可扩展性和丰富的特性集。它通常用于缓存、会话存储、实时分析、消息队列等场景。
Redis数据类型
Redis的数据类型是其核心特性之一,每种类型都有其特定的用途和优势。
String类型
底层数据结构:SDS(Simple Dynamic String),可以保存文本和二进制数据,提供高效的字符串操作。
操作符 | 命令 | 说明 |
---|---|---|
设置值 | SET key value [EX seconds] [PX milliseconds] [NX] | 设置指定键的值。如果键已经存在则更新其值。 |
获取值 | GET key | 获取指定键的值 |
设置多个值 | MSET key value [key value ...] | 一次性设置多个键值对 |
获取多个值 | MGET key [key ...] | 一次性获取多个键的值 |
追加值 | APPEND key value | 将指定的值追加到键原来的值后面 |
字符串长度 | STRLEN key | 获取键存储的字符串长度 |
设置数值增加 | INCR key | 将键存储的数字值增加1 |
设置数值减少 | DECR key | 将键存储的数字值减少1 |
增加指定数值 | INCRBY key increment | 将键存储的数字值增加指定的整数 |
减少指定数值 | DECRBY key decrement | 将键存储的数字值减少指定的整数 |
增加指定浮点数 | INCRBYFLOAT key increment | 将键存储的数字值增加指定的浮点数 |
获取指定范围的子串 | GETRANGE key start end | 获取键存储的字符串中指定范围的子串 |
设置指定范围的子串 | SETRANGE key offset value | 将键存储的字符串从指定位置开始的子串设置为指定的值 |
设置值并返回原值 | GETSET key value | 设置键的值,并返回键原来的值 |
设置键的过期时间 | SETEX key seconds value | 设置键的值,并设置键的过期时间(秒) |
设置键的过期时间(毫秒) | PSETEX key milliseconds value | 设置键的值,并设置键的过期时间(毫秒) |
List类型
底层数据结构:在Redis 3.2版本之后,List数据类型底层数据结构由quicklist实现,结合了双向链表和压缩列表的优点。
应用场景:适用于消息队列、最新动态等场景。
操作符 | 命令 | 说明 |
---|---|---|
左侧插入 | LPUSH key value [value ...] | 将一个或多个值插入到列表的头部 |
右侧插入 | RPUSH key value [value ...] | 将一个或多个值插入到列表的尾部 |
左侧弹出 | LPOP key | 移除并返回列表头部的一个值 |
右侧弹出 | RPOP key | 移除并返回列表尾部的一个值 |
按索引获取值 | LINDEX key index | 通过索引获取列表中的元素,索引从0开始,-1表示最后一个元素 |
获取列表长度 | LLEN key | 获取列表的长度 |
范围获取值 | LRANGE key start stop | 获取列表在指定范围内的元素 |
在指定值前/后插入 | LINSERT key BEFORE|AFTER pivot value | 在列表中找到等于pivot的元素,在其前或后插入value |
修改指定索引的值 | LSET key index value | 设置列表中指定索引的值为新值 |
移除指定数量的值 | LREM key count value | 从列表中移除前count个值为value的元素,count可以是正数、负数或零 |
移除并获取列表尾部元素,并添加到另一个列表头部 | RPOPLPUSH source destination | 移除列表source的最后一个元素,并将该元素添加到列表destination的头部 |
移除并获取列表尾部元素,并添加到另一个列表头部(阻塞版) | BRPOPLPUSH source destination timeout | 阻塞版本的RPOPLPUSH,如果source为空,阻塞直到有元素可弹出或超时 |
以下是Redis 3.2版本后新增的阻塞弹出操作: | ||
阻塞左弹出 | BLPOP key [key ...] timeout | 从第一个非空列表中弹出位于最左边的元素,或者在timeout时间内阻塞并等待可用的元素出现 |
阻塞右弹出 | BRPOP key [key ...] timeout | 从第一个非空列表中弹出位于最右边的元素,或者在timeout时间内阻塞并等待可用的元素出现 |
Hash类型
底层数据结构:在Redis 7.0中,压缩列表数据结构已经废弃,交由listpack数据结构来实现。
适用场景:适用于存储对象,如用户属性、对象关系等。
操作符 | 命令 | 说明 |
---|---|---|
设置字段值 | HSET key field value | 设置哈希表key中的字段field的值为value |
获取字段值 | HGET key field | 获取哈希表key中字段field的值 |
设置多个字段值 | HMSET key field value [field value ...] | 同时设置哈希表key中的多个字段值 |
获取多个字段值 | HMGET key field [field ...] | 同时获取哈希表key中的多个字段的值 |
判断字段是否存在 | HEXISTS key field | 检查哈希表key中是否存在指定的字段field |
获取所有字段 | HKEYS key | 获取哈希表key中的所有字段 |
获取所有值 | HVALS key | 获取哈希表key中的所有值 |
获取字段数量 | HLEN key | 获取哈希表key中字段的数量 |
获取整个哈希表 | HGETALL key | 获取哈希表key中的所有字段和值 |
删除字段 | HDEL key field [field ...] | 删除哈希表key中的一个或多个字段 |
字段值自增 | HINCRBY key field increment | 将哈希表key中的字段field的值增加increment |
字段值自增(浮点数) | HINCRBYFLOAT key field increment | 将哈希表key中的字段field的值增加浮点数increment |
获取字段值长度 | HSTRLEN key field | 获取哈希表key中字段field的值的字符串长度 |
Set类型
底层数据结构:由哈希表或整数集合实现,取决于元素类型和数量。
应用场景:适用于标签、社交关系等场景。
操作符 | 命令 | 说明 |
---|---|---|
添加成员 | SADD key member [member ...] | 向集合key中添加一个或多个成员 |
获取所有成员 | SMEMBERS key | 返回集合key中的所有成员 |
判断成员是否存在 | SISMEMBER key member | 判断member元素是否是集合key的成员 |
获取集合成员数量 | SCARD key | 获取集合key的成员数量 |
移除成员 | SREM key member [member ...] | 移除集合key中的一个或多个成员 |
随机弹出成员 | SPOP key [count] | 随机移除并返回集合key中的一个或多个成员 |
随机获取成员 | SRANDMEMBER key [count] | 随机返回集合key中的一个或多个成员,但不移除成员 |
将成员从一个集合移动到另一个集合 | SMOVE source destination member | 将member成员从source集合移动到destination集合 |
差集 | SDIFF key [key ...] | 返回一个集合与一个或多个集合的差集 |
差集并存储 | SDIFFSTORE destination key [key ...] | 返回一个集合与一个或多个集合的差集,并将结果存储在destination集合中 |
交集 | SINTER key [key ...] | 返回一个或多个集合的交集 |
交集并存储 | SINTERSTORE destination key [key ...] | 返回一个或多个集合的交集,并将结果存储在destination集合中 |
并集 | SUNION key [key ...] | 返回一个或多个集合的并集 |
并集并存储 | SUNIONSTORE destination key [key ...] | 返回一个或多个集合的并集,并将结果存储在destination集合中 |
Sorted Set类型
底层数据结构:由跳表或压缩列表实现,但在Redis 7.0中,压缩列表已由listpack替代。
应用场景:可以基于分数进行排序,适用于排行榜、优先级队列等场景。
操作符 | 命令 | 说明 |
---|---|---|
添加成员 | ZADD key [NX|XX] [CH] [INCR] score member [score member ...] | 向有序集合key中添加一个或多个成员,或者更新已存在成员的分数 |
获取成员数量 | ZCARD key | 获取有序集合key的成员数量 |
计算分数区间内成员数量 | ZCOUNT key min max | 计算有序集合key中分数在min和max之间的成员数量 |
增加成员分数 | ZINCRBY key increment member | 为有序集合key中的成员member增加increment分数 |
获取分数区间内成员 | ZRANGE key start stop [WITHSCORES] | 返回有序集合key中指定区间内的成员,成员按分数从小到大排序 |
获取分数区间内成员(倒序) | ZREVRANGE key start stop [WITHSCORES] | 返回有序集合key中指定区间内的成员,成员按分数从大到小排序 |
移除成员 | ZREM key member [member ...] | 移除有序集合key中的一个或多个成员 |
移除分数区间内的成员 | ZREMRANGEBYRANK key start stop | 移除有序集合key中指定排名区间内的所有成员 |
移除分数区间内的成员 | ZREMRANGEBYSCORE key min max | 移除有序集合key中分数在min和max之间的所有成员 |
获取成员排名 | ZRANK key member | 返回有序集合key中成员member的排名(从小到大) |
获取成员排名(倒序) | ZREVRANK key member | 返回有序集合key中成员member的排名(从大到小) |
计算交集 | ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] | 计算多个有序集合的交集,并将结果存储在destination中 |
计算并集 | ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] | 计算多个有序集合的并集,并将结果存储在destination中 |
获取分数区间内成员的字典序范围 | ZRANGEBYLEX key min max [LIMIT offset count] | 返回有序集合key中指定字典序区间内的成员 |
移除分数区间内的成员(按字典序) | ZREMRANGEBYLEX key min max | 移除有序集合key中指定字典序区间内的所有成员 |
获取指定分数区间的成员 | ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 返回有序集合key中分数在min和max之间的所有成员 |
获取指定分数区间的成员(倒序) | ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] | 返回有序集合key中分数在min和max之间的所有成员,按分数从大到小排序 |
Bitmap类型
位操作的高级应用:Bitmap是基于String类型提供的一种位操作功能,可以有效地存储和处理大量的布尔值。
应用场景:适用于统计用户签到、活跃用户等场景。
操作符 | 命令 | 说明 |
---|---|---|
设置位 | SETBIT key offset value | 将key对应偏移量上的位设置为value(只能是0或1) |
获取位 | GETBIT key offset | 获取key对应偏移量上的位值 |
获取位图中的第一个设置为1的位 | BITPOS key [start] [end] | 返回key中第一个值为1的位的位置,可选的start和end参数用于指定搜索范围 |
计算位图中设置为1的位的数量 | BITCOUNT key [start end] | 计算key中设置为1的位的数量,可选的start和end参数用于指定计算范围 |
位图逻辑并运算 | BITOP operation destkey key [key ...] | 对一个或多个key执行逻辑并(AND)、逻辑或(OR)、逻辑异或(XOR)和逻辑非(NOT)操作,并将结果保存到destkey中 |
位图偏移量 | BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL] | 对位图进行读取和修改操作,支持多种类型(如u8、i8、u16、i16等) |
HyperLogLog类型
基数统计的实现:HyperLogLog是一种概率性数据结构,用于估算集合的基数。操作包括PFADD
、PFCOUNT
等。
应用场景:适用于统计独立UV、独立IP等场景。
以下是Redis中HyperLogLog类型的操作及其说明,以Markdown表格格式呈现:
操作符 | 命令 | 说明 |
---|---|---|
添加元素 | PFADD key element [element ...] | 将一个或多个元素添加到指定的HyperLogLog结构中 |
计算基数 | PFCOUNT key [key ...] | 返回HyperLogLog结构中不同元素的近似数量(基数) |
合并HyperLogLog | PFMERGE destkey sourcekey [sourcekey ...] | 将一个或多个HyperLogLog结构合并为一个,结果存储在destkey 中 |
它只能提供基数估计值,而不是精确值。 |
无法从HyperLogLog结构中删除特定的元素。
HyperLogLog只适合用于不需要精确计数且数据量非常大的场景。
Geo类型
地理位置信息的存储和操作:Geo是Redis 3.2版本新增的数据类型,用于存储地理位置信息,并支持地理位置距离计算、范围查询等操作。
应用场景:适用于地理位置相关应用,如附近的人、位置签到等。
底层数据结构:使用Sorted Set集合类型,结合GeoHash编码方法实现。
以下是Redis中Geo类型的操作及其说明,以Markdown表格格式呈现:
操作符 | 命令 | 说明 |
---|---|---|
添加地理位置 | GEOADD key longitude latitude member [longitude latitude member ...] | 将给定的空间元素(纬度、经度、名字)添加到指定的键里面 |
获取地理位置 | GEOPOS key member [member ...] | 从键里面返回所有给定位置元素的位置(经度和纬度) |
获取两个位置之间的距离 | GEODIST key member1 member2 [unit] | 返回两个给定位置之间的距离 |
根据位置获取范围内的成员 | GEORADIUS key longitude latitude radius m | GEORADIUS用于查询指定经纬度为中心的指定半径内的成员。可以返回成员的经纬度、距离和哈希值,并且可以排序、计数和存储结果。 |
根据成员获取范围内的位置 | GEORADIUSBYMEMBER key member radius m | GEORADIUSBYMEMBER与GEORADIUS类似,但中心点由指定的成员确定。其余选项与GEORADIUS相同。 |
获取位置的哈希值 | GEOHASH key member [member ...] | 返回一个或多个位置元素的Geohash表示 |
Redis线程模型
Reactor 模式
Reactor 模式是一种在 Java NIO(New I/O)中广泛使用的设计模式,它用于处理高并发的网络请求。
在 Redis 中,Reactor 模式的应用体现在其事件处理器的实现上,特别是文件事件处理器(File Event Handler)。
Reactor
Reactor 是整个模式的核心,负责监听和分配事件。在事件驱动的系统中,Reactor 负责等待事件的到来,然后将事件分发给相应的处理器。
在 Redis 中,Reactor 的角色由主事件循环(event loop)扮演,它负责监听并分配所有的事件。
Redis 服务器启动时,会初始化一个事件循环,这个循环是 Redis 主线程的核心部分。
事件循环使用 I/O 多路复用技术(如 epoll
、kqueue
、select
)来监听多个文件描述符(通常是客户端连接的套接字)。
Handlers
Handlers 是实际执行业务逻辑的地方。每个 Handler 对应一个事件类型,当 Reactor 收到特定类型的事件时,会调用相应的 Handler 来处理。
在 Redis 中,Handlers 的角色由文件事件分派器(File Event Dispatcher)和事件处理器(Event Handlers)扮演:
当 I/O 多路复用程序通知有事件发生时,文件事件分派器会根据事件的类型将事件分发给对应的事件处理器。
Redis 为不同类型的事件定义了不同的处理器,例如:
- 连接应答处理器:处理新的客户端连接请求。
- 命令请求处理器:读取并解析客户端发送的命令。
- 命令回复处理器:将命令的执行结果写回给客户端。
Synchronous Event Demultiplexer
Synchronous Event Demultiplexer 是一个同步的事件分离器,它能够同时监听多个事件源,并在事件发生时通知 Reactor。
在 Redis 中,Synchronous Event Demultiplexer 的角色由 I/O 多路复用程序扮演,
Redis 使用操作系统的 I/O 多路复用机制来同时监听多个文件描述符的可读和可写事件。
当某个文件描述符准备好进行 I/O 操作时,I/O 多路复用程序会通知事件循环。
工作流程
Reactor 模式在 Redis 中的工作流程:
- 初始化:Redis 启动时,初始化事件循环,并将 I/O 多路复用程序设置为监听状态。
- 注册事件:为每个客户端连接的套接字注册读事件和写事件。
- 事件循环:事件循环持续运行,等待 I/O 多路复用程序的通知。
- 事件分发:当 I/O 多路复用程序检测到某个套接字可读或可写时,它将事件通知给文件事件分派器。
- 处理事件:文件事件分派器调用相应的事件处理器来处理事件,例如读取命令、执行命令、回复客户端等。
- 返回事件循环:事件处理器处理完毕后,控制权返回给事件循环,继续监听新的事件。
文件事件处理器
Redis 的文件事件处理器(File Event Handler)基于 Reactor 模式,以下是它的组成:套接字、I/O 多路复用程序、文件事件分派器、事件处理器。
套接字
在 Redis 中,套接字(Sockets)是客户端和 Redis 服务器之间通信的端点。
- 作用:
- 监听套接字:用于接受来自客户端的新连接请求。
- 连接套接字:一旦接受连接,就会为每个客户端创建一个连接套接字,用于后续的命令请求和回复。
- 实现:
- Redis 使用 TCP 套接字来与客户端进行通信。
- 在初始化时,Redis 会创建一个监听套接字,并将其绑定到指定的端口上。
I/O 多路复用程序
I/O 多路复用程序(I/O Multiplexer)负责监听多个套接字上的事件。
- 作用:
- 事件监听:同时监听多个套接字的可读、可写等事件。
- 事件通知:当监听到某个套接字准备好进行 I/O 操作时,通知文件事件分派器。
- 实现:
- 在 Linux 系统上,Redis 使用
epoll
作为 I/O 多路复用程序。 - 在 macOS 系统上,Redis 使用
kqueue
。 - 在其他系统上,Redis 可能使用
select
或poll
。
- 在 Linux 系统上,Redis 使用
- 工作原理:
- I/O 多路复用程序可以阻塞等待,直到至少一个套接字准备好进行 I/O 操作。
- 当有事件发生时,它返回就绪的套接字列表,然后文件事件分派器会根据这些套接字执行相应的操作。
文件事件分派器
文件事件分派器(File Event Dispatcher)是 Redis 事件处理流程中的核心组件,负责将 I/O 多路复用程序检测到的事件分发给相应的事件处理器。
- 作用:
- 事件分发:根据事件的类型和来源,将事件分发给对应的事件处理器。
- 事件注册与注销:管理事件处理器的注册和注销。
- 实现:
- 文件事件分派器内部维护了一个事件到处理器的映射表。
- 当 I/O 多路复用程序通知有事件发生时,文件事件分派器会根据映射表调用相应的事件处理器。
事件处理器
事件处理器(Event Handlers)是实际执行 I/O 操作的地方,每个事件处理器对应一种特定的事件类型。
- 作用:
- 处理读事件:当客户端发送数据到 Redis 时,读事件处理器会被调用,用于读取客户端数据。
- 处理写事件:当 Redis 需要向客户端发送数据时,写事件处理器会被调用,用于将数据写回客户端。
- 处理连接事件:当有新的客户端连接时,连接事件处理器会被调用,用于接受新连接。
- 实现:
- 连接应答处理器:处理
AE_READABLE
事件,用于接受新的客户端连接。 - 命令请求处理器:处理
AE_READABLE
事件,用于读取客户端发送的命令。 - 命令回复处理器:处理
AE_WRITABLE
事件,用于将命令的执行结果写回客户端。
- 连接应答处理器:处理
- 工作原理:
- 当 I/O 多路复用程序检测到某个套接字上有事件发生时,它会通知文件事件分派器。
- 文件事件分派器根据事件的类型和套接字,调用相应的事件处理器。
- 事件处理器执行相应的操作,如读取数据、写入数据或处理连接。
Redis配置
Redis配置文件详解
redis.conf
文件是Redis的主要配置文件,它包含了Redis服务器的各种配置选项。一些重要的配置项包括:
port
:指定Redis服务器监听的端口,默认是6379。bind
:指定Redis服务器绑定的IP地址,默认是127.0.0.1,也可以设置为0.0.0.0以允许所有IP地址访问。timeout
:客户端闲置多少秒后关闭连接,默认是0,表示关闭该功能。save
:指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合。requirepass
:设置Redis密码,默认关闭。maxmemory
:指定Redis最大内存限制,达到限制后会根据驱逐策略删除key。maxmemory-policy
:指定达到最大内存限制后的驱逐策略,例如noeviction
、allkeys-lru
等。appendonly
:指定是否开启AOF持久化,默认是no。appendfsync
:指定AOF同步频率,默认是everysec。
配置Redis网络
- 端口:默认情况下,Redis服务器监听6379端口。如果需要,可以在
redis.conf
文件中修改这个端口。 - 绑定IP:默认情况下,Redis服务器只允许本机访问,即绑定地址为127.0.0.1。如果需要允许其他机器访问,可以将绑定地址设置为0.0.0.0。
- 安全:为了提高安全性,可以通过配置文件设置密码(
requirepass
),并考虑使用SSL加密连接。 - 防火墙设置:如果Redis服务器需要被远程访问,确保防火墙允许相应的端口通信。
Redis持久化
RDB持久化
RDB快照的工作原理
RDB(Redis Database Backup) 快照是Redis提供的一种数据持久化方式。它通过将内存中的所有数据以二进制的形式写入到磁盘上(默认为dump.rdb
二进制文件),来保存当前Redis数据库的状态。RDB快照是一个全量快照,意味着每次执行快照时,都会记录内存中的所有数据。
Redis通过两个命令来生成RDB文件:save
和bgsave
。
save
命令在主线程中生成RDB文件,这可能会阻塞主线程,导致Redis无法处理其他请求。bgsave
命令则会创建一个子进程来生成RDB文件,这样就不会阻塞主线程,Redis仍然可以继续处理请求。
配置RDB的保存策略
Redis允许通过配置文件来设置自动执行bgsave命令的条件。
例如,可以设置在900秒内至少有1次修改、300秒内至少有10次修改或60秒内至少有10000次修改时,自动执行bgsave命令。
save 900 1
save 300 10
save 60 10000
AOF持久化
AOF日志的原理
AOF(Append Only File)日志是Redis提供的另一种数据持久化方式。与RDB快照不同,AOF日志记录的是每个写操作命令,而不是内存中的数据快照。这意味着AOF日志可以提供更细粒度的数据恢复能力。
当Redis执行一个写操作命令后,它会将命令追加到server.aof_buf
缓冲区。然后,通过系统调用write(),将缓冲区中的数据写入到AOF文件。这个过程涉及到内核缓冲区page cache,数据最终由内核决定何时写入硬盘。
AOF 文件格式
AOF 文件中的每一行代表一个 Redis 命令。
格式上,它是一个简单的文本文件,其中的命令按照它们被执行的顺序记录。命令的格式通常如下:
*<参数个数>
$<第一个参数长度>\r\n<第一个参数>\r\n
$<第二个参数长度>\r\n<第二个参数>\r\n
...
这里是一个具体的例子:
假设客户端发送了一个 SET key value
命令,那么在 AOF 文件中会被记录为:
*3
$3\r\nSET\r\n
$3\r\nkey\r\n
$5\r\nvalue\r\n
*3
表示接下来会有三个参数。$3\r\nSET\r\n
表示第一个参数 "SET" 的长度为3,并且紧接着是 "SET" 这个字符串。$3\r\nkey\r\n
表示第二个参数 "key" 的长度为3,并且紧接着是 "key" 这个字符串。$5\r\nvalue\r\n
表示第三个参数 "value" 的长度为5,并且紧接着是 "value" 这个字符串。
这样的格式使得 AOF 文件易于我们人类阅读和维护。
当 Redis 重启时,它可以简单地读取并重新执行 AOF 文件中的所有命令来重建数据集。
配置AOF的同步策略
AOF日志的同步策略可以通过redis.conf
配置文件中的appendfsync
选项来设置,它有三个可选参数:
Always
:每次写操作命令执行后,立即同步将AOF日志数据写回硬盘。Everysec
:每次写操作命令执行后,先将命令写入到AOF文件的内核缓冲区,然后每隔一秒将缓冲区内容写回硬盘。No
:不由Redis控制写回硬盘的时机,转交给操作系统控制。
AOF重写
AOF重写的触发时机和过程
为了避免AOF文件无限增长,Redis提供了AOF重写机制。当AOF文件的大小超过设定的阈值时,Redis会启动AOF重写过程。
AOF重写的目的是压缩AOF文件。它通过分析数据库中的键值对,为每个键值对生成一条最短的命令来代替之前的多条命令。这样,即使一个键值对被多次修改,最终也只需要根据其当前状态生成一条命令。
重写过程会创建一个子进程来执行,以避免阻塞主线程。这个子进程会创建一个新的AOF文件,并将数据库中的所有键值对以最短的命令形式写入到这个新文件中。完成写入后,新文件会替换旧的AOF文件,从而实现AOF文件的大小缩减。
RDB和AOF的区别
特性/方式 | RDB | AOF |
---|---|---|
触发方式 | 手动触发:SAVE、BGSAVE 自动触发:配置文件规则 | 日志记录:写命令执行时追加 |
数据恢复 | 从最新的RDB文件恢复 | 读取并执行AOF文件中的命令恢复 |
优点 | - 快速恢复 - 性能影响小 | - 数据安全性高 - 可维护性好 |
缺点 | - 数据丢失风险高 - 配置灵活性低 | - 恢复速度慢 - 文件体积大 |
数据丢失风险 | 高 | 低 |
恢复速度 | 快 | 慢 |
文件大小 | 小 | 大 |
性能影响 | 创建快照时短暂下降 | 受写入频率和同步策略影响 |
Redis内存管理
内存回收
Redis的内存回收主要涉及过期键的删除策略和内存淘汰机制。这两种机制共同确保了Redis能够有效地管理内存资源。
内存过期处理
惰性删除
客户端访问某个key时,才检查该key是否已过期。如果key已过期,则将其从内存中删除,这一操作由函数expireIfNeeded()
执行。
如果某些key长期未被访问,即使已经过期,它们也会一直占用内存,导致内存浪费。这种情况在缓存场景中可能导致内存泄漏,即无用的过期数据不断积累。
周期删除
Redis按照配置周期性地从设置了过期时间的key集合中随机抽取一部分key,检查是否过期,并对已过期的key进行删除。定期删除的执行频率由Redis配置文件中的hz
参数控制,该值默认为10,表示每秒执行10次检查。每次检查时,Redis会从所有数据库中随机取一部分key作为样本进行检查。
内存淘汰策略
淘汰策略的分类:
- 是否淘汰所有key(allkeys-)还是只淘汰设置了过期时间的key(volatile-)。
- 淘汰策略是基于key的使用时间(LRU)还是基于key的使用频率(LFU)。
- 淘汰策略是随机的(random)还是基于TTL(ttl)。
- 不允许淘汰任何键(noeviction),当内存不足时拒绝写入操作。
类型 | 策略 | 描述 |
---|---|---|
全局淘汰策略 | allkeys-lru | 淘汰最近最少使用的key。 |
allkeys-lfu | 淘汰最少频率使用的key。 | |
allkeys-random | 随机淘汰key。 | |
局部淘汰策略 | volatile-lru | 淘汰TTL已过且最近最少使用的key。 |
volatile-lfu | 淘汰TTL已过且最少频率使用的key。 | |
volatile-random | 随机淘汰TTL已过的key。 | |
volatile-ttl | 淘汰TTL最短的key。 | |
基于时间 | LRU(Least Recently Used) | 通过allkeys-lru和volatile-lru实现,淘汰最近最少使用的key。 |
TTL(Time To Live) | 通过volatile-ttl实现,淘汰剩余生存时间最短的key。 | |
基于频率 | LFU(Least Frequently Used) | 通过allkeys-lfu和volatile-lfu实现,淘汰最少频率使用的key。 |
随机 | Random | 通过allkeys-random和volatile-random实现,随机淘汰key。 |
特殊策略 | noeviction | 不淘汰任何key,达到内存限制时拒绝写入。 |
Redis 的默认策略是noeviction
,如果 Redis 服务器尝试在已满的数据库中执行会增加使用的内存的操作,则 Redis 将返回一个错误(如 OOM command not allowed
)给客户端,并拒绝执行这个操作。
内存监控
监控Redis内存使用情况对于及时发现和解决问题至关重要。以下是一些常用的方法:
- 使用info命令:通过执行
info memory
命令,可以查看Redis的内存使用详情,包括已使用内存、缓存命中率等。 - 监控工具:使用第三方工具如Redis Monitor、RedisInsight等,这些工具提供了更直观的内存使用监控和分析功能。
Redis高级特性
Lua脚本
Lua脚本在Redis中的应用主要是为了实现复杂操作的原子性。在Redis中,执行Lua脚本的原子性是指整个Lua脚本在执行期间不会被其他客户端的命令打断。
Lua脚本的原子性保证得益于Redis的单线程执行模型。当Redis执行Lua脚本时,它会将Lua脚本作为一个整体任务加入到一个队列中,然后按照队列顺序依次执行这些任务。在执行过程中,Lua脚本不会被其他命令或请求打断,从而保证了每个任务的执行都是原子性的。
Lua脚本的使用场景包括:
- 复杂操作的执行:可以执行一系列操作,这些操作要么全部成功,要么全部失败,确保操作的原子性。
- 事务管理:通过Lua脚本,可以实现类似于事务的逻辑,保证操作的连续性和一致性。
- 批处理:可以执行一系列批量操作,提高效率和性能。
Lua脚本的使用方法
- 执行脚本:使用
EVAL
命令执行Lua脚本。 - 脚本编写:Lua脚本可以直接在Redis中编写,也可以在客户端环境中编写后通过
EVAL
命令执行。
Redis事务
在数据库中,事务是指一组操作要么全部成功,要么全部失败,确保数据的一致性。Redis的事务概念与之类似,但实现方式不同。
Redis事务由多个命令组成,这些命令会按照顺序执行,并且事务中的命令在执行过程中不会被其他客户端的命令打断。这意味着事务中的命令可以保证顺序执行,从而实现一定程度的数据一致性。
Redis事务的实现包括:
- WATCH:用于监视一个或多个key,如果在事务执行前这些key被其他命令修改,事务将被打断。
- MULTI:标记事务的开始。
- EXEC:执行事务中的所有命令。
发布订阅
发布订阅(Pub/Sub)是Redis的一种消息通信机制,它允许一个客户端(发布者)向一个或多个频道发送消息,而其他客户端(订阅者)可以订阅一个或多个频道来接收消息。
工作原理:
- 发布者:向一个或多个频道发送消息。
- 订阅者:订阅一个或多个频道,接收这些频道上的消息。
- Redis服务器:负责转发消息,将发布者发送的消息发送给所有订阅该频道的客户端。
应用场景:
- 实时消息系统:如聊天应用、实时通知系统。
- 任务队列:如异步任务处理系统。
- 数据同步:在不同系统之间实现数据同步。