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的事务逻辑和你写的方式不匹配,核心问题在于:
事务命令的添加方式错误:cpp_redis中调用
client.multi()后,不需要在其回调里执行incr,而是直接在multi()之后链式调用事务内的命令,或者直接调用命令即可——这些命令会自动被加入事务队列,直到调用exec()提交。你原来的写法是在multi的回调里发送incr,此时事务队列并未正确捕获该命令,导致exec执行时没有实际运行INCR操作。事务内命令的回调不会单独触发:在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

