acl 不支持redis 集群 事务操作
原创
©著作权归作者所有:来自51CTO博客作者wx637304bacd051的原创作品,请联系作者获取转载授权,否则将追究法律责任
切记ACL不支持Redis 集群事务,并且千万不要使用事务,否则各种连接数据错误问题,如果一定要使用请单独连接连接,在该连接上使用事务
首先简单描述一下我出现的问题:使用的acl的redis连接池,然后模块1从连接池里面取连接,使用redis事务更新数据,模块2从连接池里面取连接,获取redis的数据。但是发现模块2在使用redis get数据的时候有时候会返回”+QUEUED”即:REDIS_RESULT_STATUS,”+QUEUED”是使用事务才应该发生的事情,为什么会出现在get接口上面呢?get接口明明没有使用事务啊?只有模块一才会使用事务。
“+QUEUED”了解redis协议:redis协议
“Redis集群事务’+QUEUED’现象”:Redis集群事务
通过上述代码,感觉问题应该出现在acl的redis连接池,然后调试代码发现 redis_command.cpp的
const redis_result* redis_command::run(redis_client_cluster* cluster,
size_t nchild, int* timeout /* = NULL */)
{
省掉上半部分
.................................
// 如果出错信息为重定向指令,则执行重定向过程
if (EQ(ptr, "MOVED"))
{
// 将旧连接对象归还给连接池对象
conn->get_pool()->put(conn, true);
const char* addr = get_addr(ptr);
if (addr == NULL)
{
logger_warn("MOVED invalid, ptr: %s", ptr);
return result_;
}
conn = redirect(cluster, addr);
if (conn == NULL)
{
logger_error("redirect NULL, addr: %s", addr);
return result_;
}
ptr = conn->get_pool()->get_addr();
set_client_addr(ptr);
if (n >= 2 && redirect_sleep_ > 0
&& strcmp(ptr, addr) != 0)
{
logger("redirect %d, curr %s, waiting %s ...",
n, ptr, addr);
acl_doze(redirect_sleep_);
}
last_moved = true;
// 需要保存哈希槽值
clear(true);
}
...........................省略下半部分.......................................
}
上述代码发现:如果发生重定向则会redirect取出或者创建重定向的连接来执行该指令,从而导致原本执行事务的连接被替换为非事务连接,而被标记为事务的连接则被放入连接池,导致该事务连接上以后从连接池上取出来使用的时候,所有操作都被默认为事务操作然后返回+QUEUED。
然后继续跟踪acl事务操作接口发现:
bool redis_transaction::multi()
{
cmds_.clear();
const char* argv[1];
size_t lens[1];
argv[0] = "MULTI";
lens[0] = sizeof("MULTI") - 1;
build_request(1, argv, lens);
return check_status();
}
bool redis_transaction::run_cmd(const char* cmd,
const std::vector<string>& args)
{
build(cmd, NULL, args);
if (check_status("QUEUED") == false)
return false;
cmds_.push_back(cmd);
return true;
}
bool redis_transaction::exec()
{
const char* argv[1];
size_t lens[1];
argv[0] = "EXEC";
lens[0] = sizeof("EXEC") - 1;
build_request(1, argv, lens);
const redis_result* result = run();
if(result == NULL || result->get_type() != REDIS_RESULT_ARRAY)
return false;
size_t size = result->get_size();
if (size != cmds_.size())
return false;
return true;
}
redis事务操作并没有计算槽值,通过peek()代码发现,事务的指令连接都是在连接池里面轮询取连接池里面的连接的,所以redis事务操作前后并不保证在同一个连接上。因此综上所述redis 的acl库并不支持事务操作,如果使用事务操作将放生各种问题。