Redis基础

基础理论 #

概况 #

Redis (Remote Dictionary Server) 是一个开源的、支持网络、基于内存、可选持久性的键值对数据库。它支持多种类型的数据结构,如字符串(strings)、列表(lists)、集合(sets)、有序集合(sorted sets)以及散列(hashes)、位图(bitmaps)、超日志(hyperloglogs)和地理空间(geospatial)索引半径查询。

特点包括:

  • 高性能:因为数据主要在内存中进行操作,读写速度非常快。
  • 丰富的数据类型:提供多种数据结构来满足不同场景下的需求。
  • 原子性:所有操作都是原子性的,支持事务(通过MULTI/EXEC命令)。
  • 丰富的功能:支持发布订阅、Lua脚本、事务等。

数据结构 #

Redis提供的数据结构包括: Redis 支持多种数据类型,以下是一些常用的数据类型以及它们的简要描述和使用场景:

数据类型描述使用场景
String二进制安全字符串,最大可以存储 512MB存储文本或二进制数据,如缓存用户个人信息等
List有序集合,按插入顺序排序消息队列、时间线、最新消息列表
Set无序集合,元素不重复标签、社交网络中的朋友关系等
Sorted Set有序集合,元素不重复,并且每个元素都会关联一个浮点数分数排行榜、带权重的集合
Hash键值对集合,适用于存储对象存储、访问和修改对象属性
Bitmaps通过位来表示数据的数据类型,适合做计数统计在线状态、特性标志、统计等
HyperLogLog近似去重计数的数据结构大数据量的计数,如统计独立 IP 访问数量
Geospatial存储地理位置信息,并进行相关地理操作储存经纬度,查询附近的地点
StreamsRedis 5.0 新增的数据类型,是一个可持久化的日志数据结构实现消息队列,发布/订阅模式,日志记录

Redis 之所以快速,主要原因有以下几点: #

  1. 内存存储:Redis 将所有数据存储在内存中,内存的读写速度远高于硬盘。
  2. 数据结构简单:Redis 的数据结构设计简单直观,易于高效操作。
  3. 非阻塞 IO:Redis 使用非阻塞 IO 和多路复用技术,可以处理多个并发连接。
  4. 单线程模型:Redis 大部分操作是单线程执行,避免了多线程的上下文切换开销。

Redis高性能IO模型 #

Redis的IO模型使用的是非阻塞IO复用技术,主要是epoll作为IO多路复用技术的实现方式。它通过单线程事件循环来处理所有客户端请求,确保绝大部分请求都是非阻塞的,并且使用异步编程模式来提高性能。

引用一些数据的话,可以提及以下几点:

  • 内存读写速度:NVMe SSD 的 IOPS 可以达到数十万到数百万,而内存的 IOPS 可以达到千万级别,延迟通常小于100微秒。相比之下,内存的速度通常是硬盘的数千倍甚至更高。
  • 性能表现:根据 Redis 官方给出的数据,在一个标准 Linux 系统上,Redis 可以达到每秒10万级别的读写操作(这个数字可能因配置、数据类型和具体操作而异)。

以上数据只是为了说明 Redis 为什么快速,具体的性能指标会根据使用环境、硬件配置以及具体的工作负载而有所变化。

持久化机制 #

Redis提供两种持久化机制:RDB(Snapshotting)和AOF(Append-only file)。

  • RDB:在指定的时间间隔内生成数据集的时间点快照。
  • AOF:记录服务器接收到的每个写操作指令,并在服务器启动时重新执行这些命令来恢复数据。

缓存淘汰/删 #

当内存不足时,Redis会根据配置的策略来移除一些键,以释放空间。常见的淘汰策略包括:

  • noeviction:不进行淘汰,返回错误。
  • allkeys-lru:从所有键中淘汰最近最少使用的键。
  • volatile-lru:从设置了过期时间的键中淘汰最近最少使用的键。
  • 其他

Redis 缓存淘汰策略是指当内存使用达到一定阈值时,Redis 会按照特定的策略自动移除一些键值对,以释放空间供新的写入操作使用。以下是 Redis 支持的几种缓存淘汰策略:

策略描述
noeviction不进行任何淘汰,尝试写入导致超出内存限制时返回错误信息
allkeys-lru根据最近最少使用算法(Least Recently Used),淘汰长时间未被查询或修改的键
volatile-lru只根据 LRU 算法淘汰设置了过期时间的键
allkeys-random随机移除键
volatile-random随机移除已经设置了过期时间的键
volatile-ttl移除即将过期的键
volatile-lfu (>=4.0)根据最不经常使用算法(Least Frequently Used),淘汰数据访问频率低的键
allkeys-lfu (>=4.0)同上,但是针对所有键,不仅仅是那些设置了过期时间的键

在这些策略中,LFU 和 LRU 是基于数据的访问模式来决定哪个键最有可能被淘汰:

  • LRU(Least Recently Used):这种策略假设如果数据最近很少用到,那么在将来也不太可能被用到。因此,当需要淘汰数据来腾出空间时,它会选取最长时间未被访问的数据。

  • LFU(Least Frequently Used):这个策略则是基于数据被访问的频率,删除那些访问次数最少的数据。相比于 LRU,LFU 在处理“热点数据”上表现更加出色。

上述淘汰策略可以通过在 redis.conf 文件中设置 maxmemory-policy 来选择,例如:

maxmemory-policy allkeys-lru

Redis 常用的显式删除操作,如:

  • DEL 命令直接删除一个或多个键。
  • EXPIRE 命令设置键的过期时间,到期后键自动被删除。
  • SET 命令在设置新值的同时可以设置过期时间。

阻塞点与异步机制 #

Redis 是一个基于事件驱动的服务器,它使用非阻塞 I/O 和事件通知机制来处理并发连接。这意味着 Redis 在执行某些操作时不会被阻塞等待 I/O 操作的完成,从而可以在等待磁盘或网络 I/O 时继续处理其他任务。

阻塞点

尽管 Redis 大部分操作是非阻塞的,但仍有一些情况会引起阻塞:

  • 命令 BLPOPBRPOPBRPOPLPUSH 用于列表操作时,如果列表为空,会导致客户端阻塞直到有新元素插入。
  • 发布订阅 (PUBLISH/SUBSCRIBE 命令) 中,客户端可能会等待新消息的到来。
  • 对于慢查询和大数据量操作(如 KEYS *SMEMBERS),可能会造成临时阻塞。

异步机制

为了减少阻塞时间,Redis 提供了异步处理能力:

  • 使用 AOF(Append Only File)日志时,Redis 支持后台异步进行磁盘写入。
  • 主从同步和 RDB 快照生成也可以异步执行,以避免阻塞主线程。
  • 客户端可以通过管道(pipelining)技术同时发送多个命令,然后一次性读取所有响应,这样可以减少网络延迟带来的影响。

原子操作、管道、事务 #

原子操作

在 Redis 中,单个命令总是原子性执行的。这意味着当一个命令在执行过程中,不会被其他命令打断。例如,INCR 命令增加一个整数值,并返回新值,这个过程是原子的。

管道(Pipelining)

管道是一种技术,可以将多个命令打包后一次性发送给服务器,而无需等待每个命令的响应。这可以显著提高性能,特别是在高延迟网络环境中。管道减少了网络往返次数,使得批量操作更加高效。

事务

Redis 的事务功能通过 MULTIEXECDISCARDWATCH 命令实现。一个事务由一系列命令组成,这些命令会被序列化并按顺序执行。以下是事务的关键点:

  • MULTI 开始一个事务。
  • 连续输入多个命令,这些命令不会立即执行,而是被放入队列中。
  • EXEC 执行所有队列中的命令。
  • 如果在执行 EXEC 之前调用 DISCARD,则可以取消事务。
  • WATCH 可以监视一个或���个键,如果在事务开始之后任何监视的键被修改,则事务将被中断。

内存碎片 #

内存碎片是指由于多次申请和释放内存导致可用内存碎片化,从而降低内存使用效率。在 Redis 中,内存碎片化可以因以下原因产生:

  • 频繁地创建和删除大量的小对象。
  • 分配给字符串或者列表等数据类型的内存大小动态变化。

内存碎片问题可能导致 Redis 占用更多的物理内存,超出实际数据需要的大小。可以通过 info memory 命令检查内存相关的统计信息,其中包括内存碎片率(memory fragmentation ratio),当这个比率超过 1.5 时,就说明存在一定的内存碎片问题。

为了解决内存碎片问题,可以采取以下措施:

  • 定期重启 Redis 服务。
  • 使用 jemalloc 等先进的内存分配器。
  • 适当调整 Redis 中关于对象重用的策略。例如,可以通过调整 maxmemory 策略来控制内存使用,或者使用 lazyfree-lazy-eviction 等参数以更优化的方式释放内存。 优化数据结构和访问模式。比如,尽量避免存储过多的小对象;使用哈希表(hashes)来存储对象集合而非单独的字符串键;对大型列表进行分页处理等。 监控并剔除长时间未使用的键,这可以通过设置键的过期时间(TTL)来实现。

另外,Redis 4.0 及以上版本引入了 active defragmentation 功能,它允许 Redis 在运行时动态地减少内存碎片,而无需重启服务。这个特性可以在 redis.conf 配置文件中开启,并允许你配置不同的参数来控制碎片整理的行为。