1 Redis 简介

1.1 Redis 是什么?

  • Redis 全称 Remote Dictionary Server(远程字典服务),是一个开源的高性能 Key-Value 数据库;

  • 官网:Redis - The Real-time Data Platform;

  • 引用官网上的⼀个问答,带你重新了解一下 Redis:

    在这里插入图片描述

    Redis 与其他键值存储有何不同?

    • 在键值数据库领域,Redis 有着不同的发展路径,其值可以包含更复杂的数据类型,并且针对这些数据类型定义了原子操作。Redis 的数据类型与基础数据结构紧密相关,会直接向程序员暴露这些数据类型,没有额外的抽象层。
    • Redis 是一种基于内存但可持久化到磁盘的数据库,因此它体现了一种不同的权衡:实现了极高的读写速度,但存在数据集不能超过内存大小的限制。内存数据库的另一个优势是,复杂数据结构在内存中的表示,与磁盘上的相同数据结构相比,操作起来要简单得多,所以 Redis 可以用很少的内部复杂度实现很多功能。同时,两种磁盘存储格式(RDB 和 AOF)不需要支持随机访问,因此它们很紧凑,并且始终以只追加的方式生成(即使是 AOF 日志轮转也是只追加操作,因为新版本是从内存中的数据副本生成的)。然而,与传统的磁盘存储系统相比,这种设计也带来了不同的挑战。由于内存是主要的数据表示形式,Redis 的操作必须谨慎处理,以确保磁盘上始终有数据集的更新版本。
  • 官方定位:Redis 作用被定位为三个方面,即 Cache(缓存)、Database(数据库)、Vector Search(向量搜索)。

1.2 2024年的Redis是什么样的?

  • 2023 年之前 Redis 是纯粹的开源数据库,近两年来,它从单纯的缓存产品,逐渐发展成一整套生态服务;
  • Redis 产品
    • Redis Cloud:基于 AWS、Azure 等公有云的云服务,提供完整企业服务,还包含企业级收费产品 Redis Enterprise;
    • Redis Enterprise Software:企业级软件产品;
    • Redis Enterprise for Kubernetes:面向 Kubernetes 环境的企业级产品;
    • Redis Insight:Redis 官方推出的图形化客户端,用于 Redis 服务的安装与管理,也可在 Redis Cloud 上直接使用,替代了以往的第三方客户端;
    • Redis OSS and Stack:在功能层面,形成了 Redis OSS(更完善的开源服务体系)和 Redis Stack(基于 Redis OSS 打造,用于 Redis Cloud 提供服务,在 Redis OSS 功能基础上提供诸多高级扩展功能)两套服务体系。

2 Redis 是单线程还是多线程?

  • 整体来看,Redis 的整体线程模型可以简单解释为客户端多线程,服务端单线程

  • 客户端多线程:Redis 为了能够与更多的客户端进行连接,使用多线程来维护与客户端的 Socket 连接。在redis.conf中就有⼀个参数maxclients维护了最大客户端连接数;

    # Redis is mostly single threaded, however there are certain threaded
    # operations such as UNLINK, slow I/O accesses and other things that are
    # performed on side threads.
    #
    # Now it is also possible to handle Redis clients socket reads and writes
    # in different I/O threads. Since especially writing is so slow, normally
    # Redis users use pipelining in order to speed up the Redis performances per
    # core, and spawn multiple instances in order to scale more. Using I/O
    # threads it is possible to easily speedup two times Redis without resorting
    # to pipelining nor sharding of the instance.
    #
    # By default threading is disabled, we suggest enabling it only in machines
    # that have at least 4 or more cores, leaving at least one spare core.
    # Using more than 8 threads is unlikely to help much. We also recommend using
    # threaded I/O only if you actually have performance problems, with Redis
    # instances being able to use a quite big percentage of CPU time, otherwise
    # there is no point in using this feature.
    #
    # So for instance if you have a four cores boxes, try to use 2 or 3 I/O
    # threads, if you have a 8 cores, try to use 6 threads. In order to
    # enable I/O threads use the following configuration directive:
    #
    # io-threads 4# Set the max number of connected clients at the same time. By default
    # this limit is set to 10000 clients, however if the Redis server is not
    # able to configure the process file limit to allow for the specified limit
    # the max number of allowed clients is set to the current file limit
    # minus 32 (as Redis reserves a few file descriptors for internal uses).
    #
    # Once the limit is reached Redis will close all the new connections sending
    # an error 'max number of clients reached'.
    #
    # IMPORTANT: When Redis Cluster is used, the max number of connections is also
    # shared with the cluster bus: every node in the cluster will use two
    # connections, one incoming and another outgoing. It is important to size the
    # limit accordingly in case of very large clusters.
    #
    # maxclients 10000
    

    Redis 主要采用单线程架构,但存在某些多线程操作,例如 UNLINK 命令、缓慢的 I/O 访问以及其他在辅助线程中执行的任务。

    现在还可以通过不同的 I/O 线程处理 Redis 客户端套接字的读取与写入。由于写入操作尤其缓慢,通常 Redis 用户会采用管道技术(pipelining)来提升单核的 Redis 性能,并通过启动多个实例来实现扩展。使用 I/O 线程技术,无需借助管道或实例分片即可轻松实现两倍的性能提升。

    默认情况下多线程处于禁用状态,我们建议仅在至少拥有 4 个或更多核心的机器上启用,并确保至少保留一个空闲核心。使用超过 8 个线程通常带来的提升有限。我们同时建议仅在确实遇到性能问题时启用多线程 I/O——即当 Redis 实例已占用较高 CPU 时间占比时,否则启用此功能并无实际意义。

    举例而言:若您的设备为四核架构,可尝试使用 2 至 3 个 I/O 线程;若为八核架构,可尝试使用 6 个线程。如需启用 I/O 线程,请使用以下配置指令:

    io-threads 4

    设置同一时间最大客户端连接数。默认情况下该限制设为 10000 个客户端,但如果 Redis 服务器无法将进程文件限制配置为满足指定数值时,实际允许的最大客户端数将调整为当前文件限制数减 32(因 Redis 需保留部分文件描述符供内部使用)。

    当达到连接数上限后,Redis 将拒绝新连接并返回“达到最大客户端数”错误信息。

    重要提示:当使用 Redis 集群时,最大连接数同样适用于集群总线——集群中的每个节点会使用两个连接:一个入站连接和一个出站连接。在超大规模集群中,请务必据此相应调整连接数限制。

    maxclients 10000

  • 服务端单线程

    • 在服务端,Redis 响应网络 IO 和键值对读写的请求,则是由单个主线程完成的。Redis 基于epoll实现了 I/O 多路复用,这就可以让⼀个主线程同时响应多个客户端 Socket 连接的请求;

    • 在这种线程模型下,Redis 将客户端多个并发的请求转成了串行的执行方式。因此,在 Redis 中,完全不用考虑类似 MySQL 的脏读、幻读、不可重复读等并发问题。同时,串行化线程模型结合 Redis 基于内存工作的极高性能,使其成为解决诸多并发问题的有力工具;

      在这里插入图片描述

  • 版本演进带来的线程模型变化

    • Redis 4.X 以前:采用纯单线程模型;

    • Redis 5.x 及之后:进行了大的核心代码重构,使用一种全新的多线程机制以提升后台工作效率。像持久化(如 RDB、AOF 重写)、集群数据同步等较费时的操作,以及 FLUSHALL(可通过 FLUSHALL [ASYNC | SYNC] 选择异步或同步执行)这类操作,都由额外线程执行,避免了对主线程的影响;

  • Redis 为什么要使用全新的多线程机制呢?

    • 现代 CPU 多为多核架构,若 Redis 一直用单线程,就无法发挥多核 CPU 的性能优势,且耗时操作会影响主线程;
    • 不过,Redis 为保持快速,多线程推进很谨慎,核心线程仍保持单线程模型,因为对于现代 Redis,CPU 通常不是性能瓶颈,性能瓶颈多为内存和网络;
    • 另外,Redis 的这种核心线程以单线程为主的机制,还可减少线程上下文切换的性能消耗,若核心线程改为多线程并发执行,会带来资源竞争,大幅增加业务复杂性,影响执行效率;
  • 总结:

    • 对于现在的 Redis,并不是简单的单线程或多线程,而是一种混合线程模型核心逻辑始终保持单线程,多线程仅用于辅助功能
    • 核心流程始终保持单线程:Redis的键值对读写、命令执行(如GET/SET)等核心操作,无论哪个版本,始终由单个主线程串行执行。这也是 Redis 避免并发安全问题、保持高性能的关键——不需要加锁,也没有线程切换开销;
    • **多线程仅用于辅助功能:**版本演进中引入的多线程,从未触及核心命令执行逻辑,而是负责:
      • 网络I/O的读写(Redis 6.0+可配置I/O多线程);
      • 耗时的后台操作(如RDB生成、AOF重写、UNLINK异步删除);
      • 集群数据同步等非核心流程。

3 Redis 如何保证指令原子性

  • Redis 对于核心的读写键值操作是单线程处理的。当多个客户端同时发起读写请求时,Redis 会让这些请求排队串行执行。但要注意,这种串行执行是针对多个客户端之间的请求而言,Redis 并没有像 MySQL 那样,专门去保证单个客户端自身操作的原子性;

  • 来看一个例子:

    在这里插入图片描述

    • 初始有 set k1 1 操作将 k1 的值设为 1;
    • 然后 Client1Client2 都执行 incr k1incr 是将键值加 1 的操作),之后又都执行 get k1 获取 k1 的值;
    • 由于 Redis 单线程处理多个客户端请求,Client1Client2incr k1 操作会串行执行,但因为没有针对单个客户端操作的原子性保障,最终 get k1 得到的值可能不符合预期(比如可能不是预期的 3,具体结果取决于两个 incr 操作的执行顺序等因素);
  • 如何控制 Redis 指令的原子性是一个需要关注的问题。在不同的业务场景下,Redis 提供了不同的解决思路,我们需要根据项目实际情况灵活选择合适的方式来保证指令执行的原子性,以满足业务需求。

3.1 复合指令

  • Redis 内部提供了诸多复合指令,这类指令看似是一个指令,但实际上能完成多个指令的工作;
  • 例如:
    • MSET(用于同时设置多个键值对)
    • HMSET(用于同时设置哈希表中的多个字段值)
    • GETSET(先获取键的旧值,再设置新值)
    • SETNX(只有键不存在时才设置值)
    • SETEX(设置键值的同时指定过期时间)等
  • 这些复合指令能够很好地保持原子性,也就是说,这些复合指令的执行过程是不可分割的,在执行过程中不会被其他客户端的指令插入或打断,从而保证了操作的完整性和一致性。

3.2 Redis 事务

3.2.1 简介

  • 官网:Transactions | Docs;

  • 基本命令MULTI(开启事务)、EXEC(执行事务)、DISCARD(放弃事务)、WATCH(监听某个或多个 key,若 key 被修改,事务执行会失败)、UNWATCH(去掉监听,仅在当前客户端有效);

  • 例:在 Redis 命令行中执行

    MULTI
    set k2 2
    incr k2
    get k2
    EXEC
    DISCARD
    
  • 但是,Redis 的事务和 MySQL 的事务,是不是同⼀回事呢?看下面这个例子:

    MULTI
    set k2 2
    incr k2
    get k2
    lpop k2 # lpop指令是针对List的操作,此处针对String类型的k2操作,肯定会报错
    incr k2
    get k2
    EXEC
    
    • 结果虽然会报错:WRONGTYPE Operation against a key holding the wrong kind of value
    • 但是这行错误的指令并没有让整个事务回滚,甚至后面的指令都没有受到影响;
    • 所以:Redis 事务并不像数据库事务那样保证事务中的指令一起成功或一起失败。Redis 事务的作用,仅仅只是保证事务中的原子操作是⼀起执行,而不会在执行过程中被其他指令加塞;

    事实上,在执行MULTI开启事务后,后续输入的指令,都会返回 QUEUED,表示 Redis 将这些操作排好了队,等到EXEC后一起执行。

3.2.2 Watch 机制

  • Redis 通过这个机制保证在事务执行前,被监听的 key 不被修改。若执行事务前 key 被修改,事务会执行失败;

  • 看下面的例子:

    • 左侧客户端先获取 k2 的值,然后用 WATCH k2 监听 k2,接着开启事务(MULTI),在事务里对 k2 进行自增(incr k2)和获取操作,最后执行事务(EXEC);
    • 右侧客户端在左侧客户端执行事务前,修改了 k2 的值(set k2 3);
    • 当左侧客户端执行 EXEC 时,因为 k2 被右侧客户端修改了,所以事务执行失败(返回 (nil));

    在这里插入图片描述

  • 可以用UNWATCH命令取消对 key 的监听,但它只在当前客户端有效

    • 左侧客户端同样先获取 k2WATCH k2、开启事务并进行操作;
    • 右侧客户端执行 UNWATCH,然后修改 k2 的值(set k2 3);
    • 左侧客户端执行 EXEC 时,事务还是失败了。这是因为 UNWATCH 是在右侧客户端执行的,而 UNWATCH 只在当前客户端有效,所以右侧的 UNWATCH 无法取消左侧客户端对 k2 的监听,左侧客户端对 k2 的监听仍然存在,k2 被修改后事务就失败了;
    • 只有在左侧客户端步骤 3(MULTI 之前)执行 UNWATCH,才能取消左侧客户端对 k2 的监听,让后续事务可能执行成功;在右侧客户端执行 UNWATCH 是无效的;

    在这里插入图片描述

3.2.3 Redis 事务失败如何回滚

  • Redis 中的事务回滚,不是回滚数据,而是回滚操作;
  • 若事务在 EXEC 执行前失败(如指令错误、参数不对),整个事务的操作都不会执行;
  • 若事务在 EXEC 执行后失败(如操作的 key 类型不对),事务中的其他操作会正常执行,不受影响。

3.3.3 事务执行过程中出现失败了怎么办?

  • 只要客户端执行了 EXEC 指令,即便之后客户端连接断开,事务也会一直进行下去;
  • EXEC 执行后,Redis 先将事务操作记录到 AOF 文件,再执行操作。若 Reids 服务出现异常(如崩溃、被 kill),可能导致 AOF 记录与数据不符。此时需用 redis - check - aof 工具修复 AOF 文件,移除不完整事务操作记录,使服务能正常启动。

3.3 Pipeline

  • 官网:Redis pipelining | Docs;

  • Pipeline 是 Redis 提供的一种机制,能让客户端把多个命令打包,一次性发送给服务器,服务器执行后再将结果批量返回给客户端。这种方式适合大批量数据快速写入 Redis 的场景;

  • 通过 redis - cli -- pipe 相关指令可使用该功能,-- pipe - timeout 还能设置管道模式下的超时时间;

  • 使用案例

    • 先在 Linux 上编辑一个 txt 文件,里面包含一系列 Redis 指令;

    • 然后通过 cat command.txt | redis - cli - a 密码 -- pipe 这样的命令,让 Redis 执行文件里的所有指令;

  • 核心作用:优化 RTT

    • RTT(Round - Trip Time,往返时间)指客户端发送指令到服务器,再到服务器返回结果给客户端的时间消耗;

    • 没有 Pipeline 时,每个命令都要经历一次 RTT,频繁发指令的话,RTT 消耗会很可观;

      在这里插入图片描述

    • 有 Pipeline 时,多个命令打包发送,只需少量的 RTT(甚至一次),大大减少了因多次网络往返带来的时间开销,提升了执行效率;

      在这里插入图片描述

  • 注意点

    • 原子性:Redis 的事务(Transaction)是原子性的,但 Pipeline 不具备原子性。Pipeline 只是把多条命令批量发送导服务端,这些命令最终可能会被其他客户端的指令“加塞”(不过这种概率通常较小),所以不建议在 Pipeline 中进行复杂的数据操作,因为数据一致性难以保证;

    • 客户端阻塞与资源占用

      • Pipeline 执行时,会阻塞当前客户端,直到服务器返回所有结果;
      • 如果 Pipeline 中封装过多指令,一方面客户端阻塞时间会太长;另一方面,服务器要回复这个“繁忙”的客户端,会占用很多内存;
    • 适用场景:Pipeline 适合在非热点时段进行数据调整任务,这样既利用了它提升效率的优势,又能避免在热点时段因客户端阻塞、服务器内存占用过多等问题,对正常业务造成影响。

3.4 Lua 脚本

  • Redis 的事务和 Pipeline 机制在解决指令原子性等问题上有局限,且只能对现有指令拼凑,无法添加更多自定义复杂逻辑。而 Lua 脚本能弥补这些不足,所以 Redis 支持 Lua,且从 2.6.0 版本就开始支持,Redis 7.x 版本支持的 Lua 语言是 5.1 版本。

3.4.1 简介

  • Lua 是一种小巧的脚本语言,具备参数类型、作用域、函数等高级语言特性,语法简单,熟悉 Java 等语言的开发者能轻松上手;

    如果对 Lua 原生的语法感兴趣,推荐⼀个参考⽹站:LuatOS 文档。这个网站可以直接在线调试 Lua;

  • Lua 语言最大的特点是他的线程模型是单线程的模式。这使得 Lua 天生就非常适合⼀些单线程模型的中间件。比如 Redis、Nginx 等都非常适合接入 Lua 语言进行功能定制。所以,在 Redis 中执行一段 Lua 脚本,这个过程天然就是原子性的。

3.4.2 Redis 中执行 Lua 的方式

  • Redis 对 Lua 语言的 API 介绍参考官网:Redis Lua API reference | Docs;

  • 通过 EVAL 指令执行 Lua 脚本,语法为 EVAL script numkeys [key key ...] [arg arg ...]

    • script:要执行的 Lua 脚本程序,会被运行在 Redis 服务器上下文中;

    • numkeys:指定键名参数的个数;

    • key [key ...]:脚本中用到的 Redis 键,可在 Lua 中通过全局变量 KEYS 数组(以 1 为基址)访问,如 KEYS[1]KEYS[2] 等;

    • arg [arg ...]:非键名的附加参数,可在 Lua 中通过全局变量 ARGV 数组访问,如 ARGV[1]ARGV[2] 等;

  • 例:返回一个包含 4 个元素的数组

    EVAL "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}" 2 key1 key2 first second
    
    • KEYS[1]:第一个键名参数
    • KEYS[2]:第二个键名参数
    • ARGV[1]:第一个附加参数
    • ARGV[2]:第二个附加参数
    • 2numkeys 参数,表示后续有 2 个键名参数key1key2);
    • key1 key2:键名参数(通过 Lua 中的 KEYS 数组访问);
    • first second:非键名的附加参数(通过 Lua 中的 ARGV 数组访问);
  • 在 Lua 脚本中可使用 redis.call 函数调用 Redis 命令,例:

    redis.call('get', KEYS[1]) # 获取键值
    redis.call('set', KEYS[1], 10) # 设置键值
    
  • 使用 Lua 的注意点:

    • 避免死循环和耗时运算:若 Lua 脚本中出现死循环或耗时运算,Redis 会阻塞,不再接受其他命令。不过 Redis 有配置参数控制 Lua 脚本的最长执行时间(默认 5 秒),超过时长 Redis 会返回 BUSY 错误,避免一直阻塞;

      ################ NON-DETERMINISTIC LONG BLOCKING COMMANDS ###################### Maximum time in milliseconds for EVAL scripts, functions and in some cases
      # modules' commands before Redis can start processing or rejecting other clients.
      #
      # If the maximum execution time is reached Redis will start to reply to most
      # commands with a BUSY error.
      #
      # In this state Redis will only allow a handful of commands to be executed.
      # For instance, SCRIPT KILL, FUNCTION KILL, SHUTDOWN NOSAVE and possibly some
      # module specific 'allow-busy' commands.
      #
      # SCRIPT KILL and FUNCTION KILL will only be able to stop a script that did not
      # yet call any write commands, so SHUTDOWN NOSAVE may be the only way to stop
      # the server in the case a write command was already issued by the script when
      # the user doesn't want to wait for the natural termination of the script.
      #
      # The default is 5 seconds. It is possible to set it to 0 or a negative value
      # to disable this mechanism (uninterrupted execution). Note that in the past
      # this config had a different name, which is now an alias, so both of these do
      # the same:
      # lua-time-limit 5000
      # busy-reply-threshold 5000
      

      ################ 非确定性长阻塞命令 #####################

      对于 EVAL 脚本、函数以及某些情况下模块命令的最长执行时间(毫秒),超过该时限后 Redis 将开始处理或拒绝其他客户端请求。

      当达到最大执行时间时,Redis 将开始对大多数命令返回 BUSY 错误响应。

      在此状态下,Redis 仅允许执行少量特定命令。

      例如:SCRIPT KILL(终止脚本)、FUNCTION KILL(终止函数)、SHUTDOWN NOSAVE(强制关闭)以及某些模块特定的’allow-busy’(允许繁忙状态执行)命令。

      SCRIPT KILL 和 FUNCTION KILL 仅能终止尚未执行任何写入命令的脚本,因此当脚本已执行写入命令且用户不愿等待脚本自然终止时,SHUTDOWN NOSAVE 可能是停止服务器的唯一方式。

      默认限制为 5 秒。可设置为 0 或负值以禁用此机制(实现无中断执行)。请注意,该配置旧名称现已作为别名保留,因此以下两种配置方式等效:

      lua-time-limit 5000

      busy-reply-threshold 5000

    • 尽量使用只读脚本:只读脚本是 Redis 7 新增的,通过 EVAL_RO 触发,脚本上要加只读标志,且不允许执行修改数据库的操作,还可随时用 SCRIPT_KILL 命令停止。使用只读脚本,一方面能限制部分用户操作;另一方面,只读脚本通常可转移到备份节点执行,减轻 Redis 主节点压力;

    • 热点脚本缓存到服务端:可将热点 Lua 脚本缓存到 Redis 服务端,提升执行效率;

      # 将 Lua 脚本 "return 'Immabe a cached script'" 加载到 Redis 服务器中
      redis> SCRIPT LOAD "return 'Immabe a cached script'"
      # 返回一个 SHA1 哈希值 c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f
      "c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f"
      # 使用之前生成的 SHA1 哈希值来执行缓存的脚本,0 表示脚本不需要任何键(KEY)参数
      redis> EVALSHA c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f 0
      # 返回脚本执行结果
      "Immabe a cached script"
      

3.5 Redis Function

  • 简介:

    • 如果你觉得开发 Lua 脚本有点困难,那么在 Redis 7 之后,提供了另外⼀种让程序员解脱的方法:Redis Function;
    • Redis Function 是 Redis 7 及以后版本提供的功能,能将一些功能声明为统一的函数,提前加载到 Redis 服务端(可由熟悉 Redis 的管理员加载)。客户端可直接调用这些函数,无需再开发函数的具体实现;
    • 它的一大优势是在 Function 中可以嵌套调用其他 Function,更利于代码复用,而 Lua 脚本难以实现这样的复用;
  • 例:

    • 首先在服务器上新增一个 mylib.lua 文件,在文件中定义函数。该文件的第一行必须写: #!lua name=mylib,用于指定函数的命名空间,这不是注释,且这一步不能少。文件内容如下:

      • 定义了 my_hset 函数,该函数接收 keysargs 参数;
      • 获取第一个键名 hash,调用 redis.call('TIME') 获取时间,然后调用 HSET 命令,设置哈希表的 _last_modified_ 字段为当前时间,再设置其他由 args 传递的字段;
      • 最后用 redis.register_function('my_hset', my_hset) 注册函数;
      #!lua name=myliblocal function my_hset(keys, args)local hash = keys[1]local time = redis.call('TIME')[1]return redis.call('HSET', hash, '_last_modified_', time, unpack(args))
      endredis.register_function('my_hset', my_hset)
      
    • 然后使用 Redis 客户端,通过 cat mylib.lua | redis-cli -a 123qweasd -- FUNCTION LOAD REPLACE 命令,将这个函数加载到 Redis 中;

    • 加载后,其他客户端就可以直接调用这个函数,调用方式和 Lua 脚本类似,比如 FCALL my_hset 1 myhash myfield "some value",其中 1 是键名参数的个数,myhash 是键名,myfield"some value" 是参数;

  • Function 注意点

    • 只读调用:Function 同样可以进行只读调用;

    • 集群使用:如果在集群中使用 Function,目前版本需要在各个节点都手动加载一次,Redis 不会在集群中进行 Function 同步;

    • 服务端缓存:Function 是要在服务端缓存的,所以不建议使用太多太大的 Function,否则会占用过多服务端资源;

    • 管理指令:Function 和 Script 一样,也有一系列的管理指令,可使用 help @scripting 指令自行了解。

3.6 总结

  • 以上介绍的各种机制,其实都是 Redis 改变指令执行顺序的方式。在这几种⼯具中,Lua 脚本通常会是项目中用得最多的方式。在很多追求极致性能的高并发场景,Lua 脚本都会担任很重要的角色。但是其他的各种方式也需要有了解,这样在面临真实的业务场景时,才有更多的方案可以选择。

4 Redis 中的 Bigkey 问题

  • Bigkey 指占用空间非常大的键。比如一个 List 类型的键包含 200 万个元素,或者一个 String 类型的键存储了一篇文章这样大量的数据;

  • 基于 Redis 以单线程为主的核心工作机制,Bigkey 非常容易造成 Redis 服务阻塞。因为 Redis 单线程处理命令时,操作 Bigkey(如读取、删除等)会耗费大量时间,在此期间,其他命令会被阻塞,无法得到及时处理,进而影响整个 Redis 服务的性能;

  • 在 Redis 客户端指令中,提供了两个扩展参数来帮助快速发现 Bigkey:

    • --bigkeys:用于抽样 Redis 键,寻找包含大量元素(具有高复杂性)的键;

    • --memkeys:用于抽样 Redis 键,寻找占用大量内存的键;

  • 对于 BigKey 的处理,后续会介绍。

5 总结

  • Redis 整体是多线程的,但后台执行指令的核心线程是单线程,整个线程模型以单线程为主。这就导致不同客户端的各种指令需要依次排队执行,并非并行处理;

  • Redis 这种以单线程为主的线程模型,相比其他中间件,结构非常简单。这使得 Redis 处理线程并发问题时,更加简单高效。在很多复杂业务场景下,Redis 能成为进行线程并发控制的良好工具。

  • 单线程模型的局限性与应对

    • 局限性:单线程模型本身不利于发挥多线程的并发优势,而 Redis 的应用场景又通常和高性能深度绑定;

    • 应对方式:在使用 Redis 时,要时刻思考指令的执行方式,选择合理的指令执行方式,这样才能最大限度发挥 Redis 高性能的优势,避免因线程模型的局限而影响性能。同时,这也说明 Redis 并非没有线程并发问题,合理的指令执行方式至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95936.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95936.shtml
英文地址,请注明出处:http://en.pswp.cn/web/95936.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

simd学习

如何查看cpu是否支持simd?# 检查特定指令集 grep -o avx2 /proc/cpuinfo | head -1 # 检查AVX2 grep -o sse4 /proc/cpuinfo | head -1 # 检查SSE4 grep -o avx512 /proc/cpuinfo | head -1 # 检查AVX512gcc编译选项,增加支持simd-mavx2 -D__AVX2__SS…

LabVIEW汽车发动机振动测试

以某型号四缸汽油发动机为测试对象,借助 LabVIEW 平台与高精度数据采集硬件,开展发动机全工况振动测试。通过实时采集缸体、曲轴箱关键部位振动信号,分析振动特征与故障关联,验证发动机运行稳定性,为后期优化设计提供数…

android 四大组件—Service

启动服务startService//启动服务,通过类名 Intent intent new Intent(this, WiFiAutoLinkService.class); startService(intent); //通过字符串启动 Intent intent new Intent(); intent.setAction("com.launcher.app"); intent.setPackage("com.l…

https + 域名 + 客户端证书访问模式

项目使用金融云部署,对外暴露IP访问,因安全合规要求必须使用域名访问,但公司又不提供域名。故,改为 https 域名 客户端证书双向认证 访问模式,大大提升安全性。 1. 密钥文件类型 .key、.csr、.cer(或 .cr…

ICPC 2023 Nanjing R L 题 Elevator

[ProblemDiscription]\color{blue}{\texttt{[Problem Discription]}}[Problem Discription] 来源:洛谷。侵权则删。 [Analysis]\color{blue}{\texttt{[Analysis]}}[Analysis] 贪心。优先运送楼层高的货物,在能装下的情况下尽量多装。 因为运送货物的代价…

81-dify案例分享-零代码用 Dify 使用梦 AI 3.0 多模态模型,免费生成影视级视频

1.前言 即梦AI作为字节跳动旗下的AI绘画与视频生成平台,近年来不断推出新的模型和功能,以提升用户体验和创作能力。 即梦AI 3.0是即梦AI的最新版本,于2025年4月发布,标志着其在中文生图模型上的重大升级。该版本不仅在中文生图能…

SQL 进阶指南:视图的创建与使用(视图语法 / 作用 / 权限控制)

在 SQL 操作中,你是否遇到过 “频繁查询多表关联的固定结果”“不想让他人看到表中的敏感字段” 这类问题?比如 “每周都要查‘技术部员工的姓名、职位、薪资’”,每次都写多表关联语句很麻烦;又比如 “给实习生开放数据查询权限&…

【全部更新完毕】2025数学建模国赛C题思路代码文章高教社杯全国大学生数学建模-NIPT 的时点选择与胎儿的异常判定

B题全部更新完毕 包含完整的文章全部问题的代码、结果、图表 完整内容请看文末最后的推广群NIPT 的时点选择与胎儿的异常判定 摘要 在问题一中,我们以无创产前检测(NIPT)数据为研究对象,围绕“胎儿 Y 染色体浓度”(记为 (V)) 随孕…

Redis(43)Redis哨兵(Sentinel)是什么?

Redis Sentinel(哨兵)是一种用于管理 Redis 实例的高可用性解决方案。它提供了监控、通知和自动故障转移等功能,确保 Redis 服务在发生故障时能够自动恢复,提供高可用性和可靠性。以下是详细介绍 Redis Sentinel 的功能及其代码示…

蓓韵安禧DHA纯植物藻油纯净安全零添加守护母婴健康

在母婴健康领域,选择合适的营养补充品至关重要。纯植物藻油DHA源自纯净藻类,有效规避了海洋重金属污染的风险,确保安全无隐患。配方坚持零添加香精、色素和防腐剂,避免不必要的化学物质摄入,让妈妈和宝宝更安心。同时&…

钉钉 AI 深度赋能制造业 LTC 全流程:以钉钉宜搭、Teambition 为例

制造业 LTC 流程痛点剖析​在制造业,线索到现金(LTC,Lead to Cash)的全流程包含从潜在客户线索的发现、商机培育、销售转化、订单执行到最终收款的一系列复杂环节。传统制造业在这一流程中面临诸多挑战:客户需求的多样…

理解UE4中C++17的...符号及enable_if_t的用法及SFINAE思想

下面是一段C17的代码&#xff1a;//函数1&#xff1a;template <typename... BufferTypes,std::enable_if_t<std::conjunction<CanAppendBufferType<std::decay_t<BufferTypes>>...>::value> * nullptr> inline explicit FCompositeBuffer(Buff…

安全419正式公布《甲方安全建设精品采购指南》案例首推运营商行业数据安全核心推荐厂商

在数字经济加速渗透与《网络数据安全管理条例》全面实施的双重背景下&#xff0c;运营商作为数据要素流通的核心枢纽&#xff0c;其安全防护体系建设已成为数字基础设施保障的关键环节。近日&#xff0c;安全 419 正式公布《甲方安全建设精品采购指南》&#xff0c;从近 300 个…

基础词根-汇总

ros rus粗糙 ris cos cus cis切lite文字 late面 侧面ven 来 cess走/agdotect 覆盖 covercele 聚集 加速 gre 聚集&#xff0c;accumu聚集gress 抵达 靠近&#xff0c;aggressive侵略性humor humir 大地 土地chron 时间 time&#xff0c;宇宙的宙lumi 光lightviv vil volun vot/…

JVM中常见的GC垃圾收集器

文章目录 目录 1. Serial GC&#xff08;串行收集器&#xff09; 2. Parallel GC&#xff08;并行收集器&#xff09; 3. CMS&#xff08;Concurrent Mark-Sweep&#xff0c;并发标记 - 清除&#xff09; 4. G1&#xff08;Garbage-First&#xff0c;垃圾优先&#xff09; …

嵌入式C语言之链表冒泡排序

链表冒泡排序一是可以交换指针域的值&#xff0c;二是可以交换指针typedef struct st_node{int score;struce st_node *next;}Node,*LinkList;LinkList createList(){Node *head (Node *)malloc(sizeof(Node));if(NULL head){printf("内存分配失败!"):return NULL;…

远场代码学习_FDTD_farfield

项目4.2 farfield3d - Script command在3D模拟中将给定的功率或场剖面监视器或直线数据集投射到远场。返回电场强度|E| 2。语法描述 out farfield3d("mname",f, na, nb, illumination, periodsa, periodsb, index, direction)&#xff1b; 将给定的功率或场分布监…

Adobe Illustrator(Ai) 2022安装教程与下载地址

Adobe Illustrator&#xff08;通常简称 AI&#xff09;是一款由 Adobe 公司开发的、基于矢量图形的专业设计软件。它与 Photoshop&#xff08;基于位图/像素&#xff09;和 InDesign&#xff08;专注于页面排版&#xff09;并称为数字创意领域的“三巨头”&#xff0c;是平面设…

小迪web自用笔记27

框架就是一些封装好的东西*上节课补&#xff1a;JS负责美化框架的&#xff08;发送HTTP请求前端&#xff0c;js相当于前端并且附加上一些连接后端的功能。&#xff09;&#xff0c;JAVA是后端。PHPthink&#xff08;用的最多的框架&#xff09;URL&#xff1a;原&#xff1a;ht…

创建阿里云ECS实例操作(免费试用版)

目录 1、进入阿里云ECS控制台 2、创建ECS实例 3、重置实例密码 4、远程登陆实例 5、查看ECS信息 6、安装apache服务 7、端口规则设置 8、访问测试 9、释放实例 1、进入阿里云ECS控制台 https://www.aliyun.com/ 2、创建ECS实例 3、重置实例密码 4、远程登陆实例 5、查…