首页 > 数据库 >Redis之pipeline与事务

Redis之pipeline与事务

时间:2024-09-14 11:23:28浏览次数:3  
标签:Pipeline 事务 Redis pipeline 命令 执行 客户端

前言

        Redis使用的是单reactor网络模型,也就是io多路复用+非阻塞io的异步处理流程(注册事件,在事件循环callback处理事件)。我们可以将每个连接抽象看成一个pipe,哪个pipe中的数据先满就先处理。注意,单reactor指的是acceptor只有一个,而工作线程在6.0版本之前只有一个,也就是单reactor单线程,6.0版本之后就是单reactor多线程。下图为6.0版本之后的reactor模型。但之后的讨论我们基于6.0版本之前,也就是单reactor单线程。

pipeline

        Redis的Pipeline是一种客户端的批处理技术,它允许客户端一次性发送多个命令给Redis服务器,然后服务器端再依次执行这些命令,并返回结果。这种技术可以显著减少网络延迟,提高操作效率。

        使用Pipeline之前,每次执行命令都需要客户端与服务器之间进行一次完整的网络往返(Round Trip Time,RTT),这在执行大量命令时会导致显著的性能问题。Pipeline通过将多个命令打包在一起发送,然后服务器端集中处理后返回结果,从而减少了网络往返的次数,提高了执行效率。Pipeline的工作流程大致如下:

  1. 客户端创建一个Pipeline对象,并向其中添加需要执行的命令。
  2. 客户端将所有命令一次性发送到Redis服务器。
  3. Redis服务器接收到命令后,依次执行这些命令,并将每个命令的结果存储起来。
  4. 客户端等待所有命令执行完成后,从服务器获取结果并按照命令发送的顺序进行处理。

需要注意的是,虽然Pipeline可以提高性能,但它也有一些限制和注意事项:

  • Pipeline中的命令不具备原子性,即如果其中一个命令失败,不会影响到其他命令的执行。
  • 使用Pipeline时,应该避免一次性发送过多命令,以免造成服务器处理阻塞,影响其他客户端的请求。
  • 在Redis集群环境中使用Pipeline时,需要确保所有命令都在同一个节点上执行,避免跨节点操作导致的性能问题。

#include <cpp_redis/cpp_redis>
#include <iostream>

int main() {
    // 连接到 Redis 服务器
    cpp_redis::client client;

    // 尝试连接到 Redis
    client.connect("127.0.0.1", 6379, [](const std::string& host, std::size_t port, cpp_redis::connect_state status) {
        if (status == cpp_redis::connect_state::ok) {
            std::cout << "Successfully connected to redis instance at " << host << ":" << port << std::endl;
        } else {
            std::cerr << "Failed to connect to redis at " << host << ":" << port << std::endl;
        }
    });

    // 等待连接确认
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 开启 Pipeline
    auto sync_commit = client.sync_commit();

    // 获取当前键的值
    sync_commit.get("zg", [](const cpp_redis::reply& reply) {
        if (reply.is_error()) {
            std::cerr << "Error: " << reply.error() << std::endl;
        } else {
            // 将值翻倍
            std::string value = std::to_string(std::stoll(reply.as_string()) * 2);
            // 设置新的值
            cpp_redis::client::commit("zg", value);
        }
    });

    // 执行 Pipeline 并等待回复
    std::vector<cpp_redis::reply> replies = sync_commit.exec();

    // 检查回复并输出结果
    for (const auto& reply : replies) {
        if (reply.is_error()) {
            std::cerr << "Error: " << reply.error() << std::endl;
        } else {
            std::cout << "Reply: " << reply.as_string() << std::endl;
        }
    }

    return 0;
}

Redis事务

        事务概念可以参考我写的MySQL事务原理,从Redis的角度简单来说就是用户定义的一系列操作被视为一个完整的逻辑处理工作单元,这些单元要么全部执行,要么全部不执行。Redis是非关系型数据库,它是不使用SQL语句的,所以那些数据库操作就是命令。 在多线程操作的情况下就需要考虑事务,因为单线程的命令本身就是一条条执行没有干扰的,多线程会有交叉操作,这种情况可以参考我写的原子操作与锁。所以事务就是用来解决并发场景下的冲突。

        涉及到事务我们就要了解ACID,同样可以参考MySQL事务原理。ACID分别指的是原子性、一致性、隔离性、持久性。MySQL中有完整性约束保持数据类型的一致性,而Redis作为一个基于内存的非关系型数据库,它提供的是最终一致性模型。在 Redis 事务中,所有命令都会被序列化并按顺序执行,但与 MySQL 不同的是,如果事务中的某个命令执行失败,Redis 的事务并不会回滚,而是继续执行后续命令。Redis 事务不是完全符合 ACID 属性的。它只保证原子性和隔离性,但它们不保证持久性(Durability),因为 Redis 的持久化机制(如 RDB 或 AOF)与事务机制是分开的。而且,由于 Redis 是单线程的,它不需要处理多个并发事务之间的复杂交互,这简化了它的事务模型。

WATCH--监视一个或多个键,如果在执行 EXEC 之前,这些键被其他命令修改,则事务会被丢弃。
MULTI--开始一个新的事务,将后续的命令放入事务队列中。
--命令
EXEC--执行事务队列中的所有命令。如果事务成功,它会返回一个数组,其中包含每个命令的回复。如果事务因为之前的错误而被丢弃,它会返回 null。
DISCARD--取消事务,清空事务队列,并退出事务状态。
WATCH key
MULTI
INCR counter
SET key "value"
HSET hashfield field "value"
DISCARD  # 假设在这个时候,我们决定取消事务

        其实在实际上我们使用并非使用这些命令,而是使用lua脚本,因为乐观锁watch失败需要重试,操作起来复杂度就高。Redis中嵌入了一个lua虚拟机,所以可以通过lua操作redis的数据,lua脚本执行命令是一个语句(EVAL script numkeys key [key ... ] arg [arg ... ])作为一个数据包执行,所以其他语句根本插不进去,这样就天生具备原子性。我们通常先使用script load将lua脚本发送至Redis,Redis会将lua脚本使用hash算法生成40bit字符串sha1,而后我们使用evalsha sha1 来执行脚本,通过hash简化命令,使数据包更小,减少带宽压力。 

标签:Pipeline,事务,Redis,pipeline,命令,执行,客户端
From: https://blog.csdn.net/oxygen3000/article/details/142248825

相关文章

  • Redis解决"Warning: Using a password with .....may not be safe"
    Redis的命令行工具redis-cli可以连接、访问Redis数据库,在没有设置密码验证的情况下,我们可以无需密码直接访问Redis数据库,如下所示./redis-cli127.0.0.1:6379>127.0.0.1:6379>pingPONG127.0.0.1:6379>我们连接到本地的Redis服务并执行ping命令,该命令用于检测Redis服务是否启动,......
  • redis集群的搭建
    一、创建节点文件夹因为集群至少要6个节点,所有创建6个文件夹mkdir700{0,1,2,3,4,5}二、复制配置文件到每个创建的文件夹cpredis.conf700{0,1,2,3,4,5}三、修改每个配置文件的端口等信息绑定服务器的IP:bind0.0.0.0或者注释掉#bind0.0.0.0关闭保护模式用于公网访......
  • Redis 分布式锁的正确实现原理演化历程与 Redission的源码
    ......
  • ZooKeeper面试必备:ZooKeeper4种数据节点类型、了解事务ID
    1.ZooKeeper数据模型1.1ZooKeeper数据节点ZooKeeper的数据模型是一颗树结构,每一个树节点是一个数据节点,我们称它为ZNode。而每一个ZNode的节点路径标识使用斜杠/作为分隔符,我们可以在ZNode节点下写入数据、创建节点,这种斜杠/作为路径分隔符的方式和Unix文件系统路径非常相......
  • Redis集群:构建高性能和高可用的键值存储系统
    引言Redis,即RemoteDictionaryServer,是一种开源的高性能键值数据库。它支持多种类型的数据结构,如字符串、哈希、列表、集合、有序集合等。随着业务的发展,单个Redis实例可能无法满足大规模数据存储和高并发访问的需求。Redis集群提供了一种解决方案,通过分布式存储和自动分片来......
  • 2024Mysql And Redis基础与进阶操作系列(7)作者——LJS[含MySQL 聚合、数学、字符创、日
    目录MySQL函数1.聚合函数 格式补充 示例将所有员工的名字合并成一行指定分隔符合并指定排序方式和分隔符2.数学函数(即用即查,重在融会贯通与运用)3.字符串函数(即用即查,重在融会贯通与运用)4.日期函数(即用即查,重在融会贯通与运用)5.控制流函数(即用即查,重在融会贯通与运用)if逻辑判......
  • 2024Mysql And Redis基础与进阶操作系列(1)作者——LJS[含MySQL的下载、安装、配置详解
    目录1.数据库与数据库管理系统1.1数据库的相关概念1.2数据库与数据库管理系统的关系 1.3 常见的数据库简介Oracle1. 核心功能2. 架构和组件3. 数据存储和管理4. 高可用性和性能优化5. 安全性6. 版本和产品7. 工具和接口 SQLServer1. 核心功能2. 架构和组件3. 数据......
  • 深入理解Redis线程模型
    Reids6.0之前版本的线程模型在讨论最新版本的Redis的线程模型之前呢,我们先来聊聊原来的Redis的线程模型:有人说,在6.0之前呢,Redis是单线程的,这么说其实也不太准确,为什么呢?因为Redis在4.0之后,就引入了多线程,比如说除了处理用户命令的主线程之外,还会起异步的线程去做一些资源释......
  • Redis的IO模型
    RedisIO模型RedisIO模型使用的是基于Reactor模式的I/O多路复用模型。这个模型通过单线程事件循环来处理所有的客户端请求和响应。基本模式1.Reactor模式Reactor模式是一种用于处理并发I/O操作的设计模式。它包含以下几个组件:多路复用器(Multiplexer):负责监听多......
  • 保姆级,手把手教你物理机搭建Redis-sentinel(哨兵)集群
    集群介绍        Redis,作为一种开源的、基于内存的数据结构存储系统,被广泛应用于各种场景,包括缓存、消息队列、短期存储等。单一实例的工作模式通常无法保证Redis的可用性和拓展性,Redis提供了三种分布式方案:主从模式哨兵模式集群模式      主从模式    ......