You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

cpp_redis::client::multi事务异常,无法递增Redis整数键

问题描述

我需要在Redis中通过事务创建并递增一个整数值,对应的Redis命令是:

MULTI
INCR my_counter
EXEC

现在要在C++中实现该功能,我先后尝试了cpp_redis的两个仓库版本,按Windows安装指南完成了VS2022项目的编译链接。但无论用哪个版本,都无法在MULTI事务内完成整数值递增。

环境说明:

  • 用Docker容器运行Redis,连接本地服务器
  • 非事务场景下,C++程序可以正常递增该值,但必须在事务内完成操作

我的C++代码如下:

#include <cpp_redis/cpp_redis>
#include <tacopie/tacopie>

#ifdef _WIN32
#include <Winsock2.h>
#endif /* _WIN32 */


int main() {

#ifdef _WIN32
    //! Windows netword DLL init
    WORD version = MAKEWORD(2, 2);
    WSADATA data;

    if (WSAStartup(version, &data) != 0) {
        std::cerr << "WSAStartup() failure" << std::endl;
        return -1;
    }
#endif /* _WIN32 */

    cpp_redis::active_logger = std::unique_ptr<cpp_redis::logger>(new cpp_redis::logger);
    cpp_redis::client client;
    
    // Connect to the Redis server
    client.connect("127.0.0.1", 6379, [&](const std::string& host, std::size_t port, cpp_redis::client::connect_state status) {
        if (status == cpp_redis::client::connect_state::ok) {
            std::cout << "Connected to Redis server: " << host << ":" << port << std::endl;
            
            // Start a transaction
            client.multi([&](cpp_redis::reply& reply) {
                if (reply.is_error()) {
                    std::cerr << "Error starting transaction: " << reply.as_string() << std::endl;
                    return;
                }

                // Create a key and increment it by one
                client.incr("my_counter", [&](cpp_redis::reply& reply) {
                    if (reply.is_integer()) {
                        std::cout << "Incremented value: " << reply.as_integer() << std::endl;
                    }
                    else {
                        std::cerr << "Error incrementing value: " << reply.as_string() << std::endl;
                    }
                });
            });
            
            // Commit the transaction
            client.exec([&](cpp_redis::reply& reply) {
                if (reply.is_error()) {
                    std::cerr << "Error committing transaction: " << reply.as_string() << std::endl;
                }
                else {
                    std::cout << "Transaction committed successfully." << std::endl;
                }
            });
        }
        else {
            std::cerr << "Failed to connect to Redis server: " << host << ":" << port << std::endl;
        }
    });

    std::cout << "Before sync_commit..." << std::endl;
    // Run the event loop
    client.sync_commit();
    std::cout << "After sync_commit..." << std::endl;

#ifdef _WIN32
    WSACleanup();
#endif /* _WIN32 */

    return 0;
}

运行结果:

Failed to connect to Redis server: 127.0.0.1:6379
Connected to Redis server: 127.0.0.1:6379
Before sync_commit...
Transaction committed successfully.

注意程序没有输出“exited with code 0”,client.sync_commit()似乎无法完成处理。虽然控制台显示“Transaction committed successfully.”,但没有输出“Incremented value: ...”,且在Docker的Redis容器中执行GET my_counter返回(nil)。

请问是我的代码存在问题,还是库本身的bug?


解决方案

你的代码存在用法错误,并非库的问题。cpp_redis的事务逻辑和你写的方式不匹配,核心问题在于:

  1. 事务命令的添加方式错误:cpp_redis中调用client.multi()后,不需要在其回调里执行incr,而是直接在multi()之后链式调用事务内的命令,或者直接调用命令即可——这些命令会自动被加入事务队列,直到调用exec()提交。你原来的写法是在multi的回调里发送incr,此时事务队列并未正确捕获该命令,导致exec执行时没有实际运行INCR操作。

  2. 事务内命令的回调不会单独触发:在Redis事务中,所有命令的结果会在EXEC执行后一次性返回,所以incr的回调不会单独被调用,你需要在exec的回调里解析所有事务命令的结果。

修正后的代码如下:

#include <cpp_redis/cpp_redis>
#include <tacopie/tacopie>
#include <atomic>

#ifdef _WIN32
#include <Winsock2.h>
#endif /* _WIN32 */

std::atomic<bool> transaction_done = false;

int main() {

#ifdef _WIN32
    WORD version = MAKEWORD(2, 2);
    WSADATA data;
    if (WSAStartup(version, &data) != 0) {
        std::cerr << "WSAStartup() failure" << std::endl;
        return -1;
    }
#endif /* _WIN32 */

    cpp_redis::active_logger = std::unique_ptr<cpp_redis::logger>(new cpp_redis::logger);
    cpp_redis::client client;
    
    client.connect("127.0.0.1", 6379, [&](const std::string& host, std::size_t port, cpp_redis::client::connect_state status) {
        if (status == cpp_redis::client::connect_state::ok) {
            std::cout << "Connected to Redis server: " << host << ":" << port << std::endl;
            
            // 开启事务,直接链式添加命令
            client.multi()
                  .incr("my_counter")
                  .exec([&](cpp_redis::reply& reply) {
                      if (reply.is_array()) {
                          // 事务的结果是数组,每个元素对应事务内命令的结果
                          auto& incr_result = reply.as_array()[0];
                          if (incr_result.is_integer()) {
                              std::cout << "Incremented value: " << incr_result.as_integer() << std::endl;
                          } else {
                              std::cerr << "Error incrementing value: " << incr_result.as_string() << std::endl;
                          }
                          std::cout << "Transaction committed successfully." << std::endl;
                      } else if (reply.is_error()) {
                          std::cerr << "Error committing transaction: " << reply.as_string() << std::endl;
                      }
                      transaction_done = true;
                  });
            // 提交命令到Redis
            client.commit();
        }
        else {
            std::cerr << "Failed to connect to Redis server: " << host << ":" << port << std::endl;
            transaction_done = true;
        }
    });

    std::cout << "Before sync_commit..." << std::endl;
    // 等待事务完成再退出事件循环
    while (!transaction_done) {
        client.sync_commit(std::chrono::milliseconds(100));
    }
    std::cout << "After sync_commit..." << std::endl;

#ifdef _WIN32
    WSACleanup();
#endif /* _WIN32 */

    return 0;
}

关键修正点说明:

  • 链式调用事务命令:client.multi().incr("my_counter").exec(...),这样incr会被正确加入事务队列。
  • 解析exec的结果数组:Redis事务执行后,EXEC的返回值是一个数组,对应每个事务内命令的执行结果,所以需要在exec的回调里遍历数组获取INCR的结果。
  • 添加事务完成标记:用std::atomic<bool>标记事务完成,避免sync_commit无限阻塞,确保程序能正常退出。
  • 手动调用commit:在添加完事务命令后,调用client.commit()将命令发送到Redis。

这样修改后,程序会正确执行事务内的INCR操作,my_counter会被创建并递增,控制台也会输出递增后的值,程序能正常退出。

内容的提问来源于stack exchange,提问作者Powarito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:57:20