Redis Eval使用好处
Redis使用eval可以执行lua脚本,使得程序的灵活性更好。 打包命令执行,减少网络开销,evalsha更小的参数数据传输。 原子操作
Redis Eval的c++封装
更新 方案二 (redisCommandArgv)
redisContext* redisCon;
std:: map < std:: string, std:: string> _map_script_sha;
bool ScriptLoad ( const std:: string & luascript, std:: string & sha)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, "SCRIPT Load %s " , luascript. c_str ( ) ) ;
if ( NULL != reply)
{
if ( reply- > type == REDIS_REPLY_STRING) {
sha = reply- > str;
flag = true ;
_map_script_sha[ luascript] = sha;
}
freeReplyObject ( reply) ;
}
return flag;
}
bool RunCommand ( std:: vector< std:: string> cmd)
{
redisReply * r = nullptr ;
bool ret = RunCommand ( cmd, r) ;
freeReplyObject ( r) ;
return ret;
}
bool RunCommand ( std:: vector< std:: string> cmd, redisReply * & r)
{
std:: vector< const char * > argv;
std:: vector< unsigned int > argvlen;
for ( const auto & str : cmd) {
argv. push_back ( str. c_str ( ) ) ;
argvlen. push_back ( str. length ( ) ) ;
}
r = ( redisReply * ) redisCommandArgv ( redisCon, argv. size ( ) , & ( argv[ 0 ] ) , & ( argvlen[ 0 ] ) ) ;
if ( NULL == r) {
return false ;
}
else if ( r- > type == REDIS_REPLY_ERROR) {
std:: cout << r- > str << std:: endl;
}
return true ;
}
bool Eval ( const std:: string& luascript, int numkeys, const std:: vector< std:: string> vec_key, const std:: vector< std:: string> vec_value, redisReply * & r) {
std:: vector< std:: string> cmd;
cmd. push_back ( "EVAL" ) ;
cmd. push_back ( luascript) ;
cmd. push_back ( std:: to_string ( numkeys) ) ;
cmd. insert ( cmd. end ( ) , vec_key. begin ( ) , vec_key. end ( ) ) ;
cmd. insert ( cmd. end ( ) , vec_value. begin ( ) , vec_value. end ( ) ) ;
bool ret = RunCommand ( cmd, r) ;
return ret;
}
bool Eval ( const std:: string& luascript, int numkeys, const std:: vector< std:: string> vec_key, const std:: vector< std:: string> vec_value) {
redisReply * r = nullptr ;
bool ret = Eval ( luascript, numkeys, vec_key, vec_value, r) ;
freeReplyObject ( r) ;
return ret;
}
bool EvalSha ( const std:: string& luascript, int numkeys, const std:: vector< std:: string> vec_key, const std:: vector< std:: string> vec_value, redisReply * & r) {
std:: string sha1 = _map_script_sha[ luascript] ;
if ( sha1 == "" && ! ScriptLoad ( luascript, sha1) )
{
return false ;
}
bool ret = false ;
auto evalfuc = [ & ] ( ) {
std:: vector< std:: string> cmd;
cmd. push_back ( "EVALSHA" ) ;
cmd. push_back ( sha1) ;
cmd. push_back ( std:: to_string ( numkeys) ) ;
cmd. insert ( cmd. end ( ) , vec_key. begin ( ) , vec_key. end ( ) ) ;
cmd. insert ( cmd. end ( ) , vec_value. begin ( ) , vec_value. end ( ) ) ;
ret = RunCommand ( cmd, r) ;
} ;
evalfuc ( ) ;
if ( NULL != r && r- > type == REDIS_REPLY_ERROR
&& std:: string ( r- > str) == "NOSCRIPT No matching script. Please use EVAL." )
{
freeReplyObject ( r) ;
if ( ScriptLoad ( luascript, sha1) ) {
evalfuc ( ) ;
}
else {
return false ;
}
}
return ret;
}
bool EvalSha ( const std:: string& luascript, int numkeys, const std:: vector< std:: string> vec_key, const std:: vector< std:: string> vec_value) {
redisReply * r = nullptr ;
bool ret = EvalSha ( luascript, numkeys, vec_key, vec_value, r) ;
freeReplyObject ( r) ;
return ret;
}
bool ScriptFlush ( )
{
bool flag = false ;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, "SCRIPT FLUSH" ) ;
if ( NULL != reply)
{
if ( std:: string ( reply- > str) == "OK" ) {
flag = true ;
_map_script_sha. clear ( ) ;
}
freeReplyObject ( reply) ;
}
return flag;
}
方案一 (redisCommand)
bool ScriptLoad ( const std:: string & luascript, std:: string & sha)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, "SCRIPT Load %s " , luascript. c_str ( ) ) ;
if ( NULL != reply)
{
if ( reply- > type == REDIS_REPLY_STRING) {
sha = reply- > str;
flag = true ;
map_script_sha[ luascript] = sha;
}
freeReplyObject ( reply) ;
}
return flag;
}
bool ScriptExist ( const std:: string & sha)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, "SCRIPT EXISTS %s " , sha. c_str ( ) ) ;
if ( NULL != reply)
{
if ( reply- > type == REDIS_REPLY_ARRAY && reply- > elements> 0 ) {
redisReply * item1 = reply- > element[ 0 ] ;
if ( item1 && item1- > type == REDIS_REPLY_INTEGER && item1- > integer == 1 )
{
flag = true ;
}
}
freeReplyObject ( reply) ;
}
return flag;
}
std:: map < std:: string, std:: string> map_script_sha;
template < typename . . . T>
bool EvalEx ( const std:: string& luascript, int numkeys, std:: string format, T. . . args)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
std:: string sha1 = map_script_sha[ luascript] ;
if ( sha1== "" && ! ScriptLoad ( luascript, sha1) )
{
return false ;
}
std:: string totalFormat = "evalsha %s %d " + format;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, totalFormat. c_str ( ) , sha1. c_str ( ) , numkeys, args. . . ) ;
if ( NULL != reply && reply- > type == REDIS_REPLY_ERROR
&& std:: string ( reply- > str) == "NOSCRIPT No matching script. Please use EVAL." )
{
freeReplyObject ( reply) ;
if ( ScriptLoad ( luascript, sha1) ) {
reply = ( redisReply * ) redisCommand ( redisCon, totalFormat. c_str ( ) , sha1. c_str ( ) , numkeys, args. . . ) ;
}
else {
return false ;
}
}
if ( NULL != reply) {
if ( reply- > type != REDIS_REPLY_ERROR)
{
flag = true ;
}
freeReplyObject ( reply) ;
}
return flag;
}
template < typename . . . T>
bool EvalEx ( redisReply * & p_reply, const std:: string& luascript, int numkeys, std:: string format, T. . . args)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
std:: string sha1 = map_script_sha[ luascript] ;
if ( sha1 == "" && ! ScriptLoad ( luascript, sha1) )
{
return false ;
}
std:: string totalFormat = "evalsha %s %d " + format;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, totalFormat. c_str ( ) , sha1. c_str ( ) , numkeys, args. . . ) ;
if ( NULL != reply && reply- > type == REDIS_REPLY_ERROR
&& std:: string ( reply- > str) == "NOSCRIPT No matching script. Please use EVAL." )
{
freeReplyObject ( reply) ;
if ( ScriptLoad ( luascript, sha1) ) {
reply = ( redisReply * ) redisCommand ( redisCon, totalFormat. c_str ( ) , sha1. c_str ( ) , numkeys, args. . . ) ;
}
else {
return false ;
}
}
if ( NULL != reply) {
if ( reply- > type == REDIS_REPLY_ERROR)
{
freeReplyObject ( reply) ;
}
else {
flag = true ;
p_reply = reply;
}
}
return flag;
}
template < typename . . . T>
bool Eval ( redisReply * & p_reply, const std:: string& luascript, int numkeys, std:: string format, T. . . args)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
std:: string totalFormat = "eval %s %d " + format;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, totalFormat. c_str ( ) , luascript. c_str ( ) , numkeys, args. . . ) ;
if ( NULL != reply)
{
if ( reply- > type == REDIS_REPLY_ERROR)
{
freeReplyObject ( reply) ;
}
else {
flag = true ;
p_reply = reply;
}
}
return flag;
}
template < typename . . . T>
bool Eval ( const std:: string& luascript, int numkeys, std:: string format, T. . . args)
{
if ( ! redisCon)
{
return false ;
}
bool flag = false ;
std:: string totalFormat = "eval %s %d " + format;
redisReply * reply = ( redisReply * ) redisCommand ( redisCon, totalFormat. c_str ( ) , luascript. c_str ( ) , numkeys, args. . . ) ;
if ( NULL != reply)
{
if ( reply- > type != REDIS_REPLY_ERROR)
{
flag = true ;
}
freeReplyObject ( reply) ;
}
return flag;
}
附带说明
EvalEx
主要是调用的evalsha命令,本来是想用ScriptExist
来判断脚本是否已经加载(存在),但是考虑到如果每次调用都执行ScriptExist的话,确实对于程序的性能是一种浪费,于是改成直接调用evalsha,如果脚本不存在的话,会报一个特定的错误NOSCRIPT No matching script. Please use EVAL.
,以此来提高运行效率。
EvalEx ( "redis.call('set', KEYS[1], ARGV[1])" , 1 , "%s %d" , "string8" , 9 ) ;
redisReply * p_reply = NULL ;
EvalEx ( p_reply, "redis.call('set', KEYS[1], ARGV[1])" , 1 , "%s %d" , "string7" , 3 ) ;
if ( p_reply) {
freeReplyObject ( p_reply) ;
}
Eval ( "redis.call('set', KEYS[1], ARGV[1])" , 1 , "%s %d" , "string8" , 9 ) ;
redisReply * p_reply = NULL ;
Eval ( p_reply, "redis.call('set', KEYS[1], ARGV[1])" , 1 , "%s %d" , "string7" , 3 ) ;
if ( p_reply) {
freeReplyObject ( p_reply) ;
}
Eval 和 EvalEx都有两个版本,一个关心lua脚本的返回值的,一个不关心的。如果需要获取lua脚本返回值的话,需要传入redisReply
指针,但是要注意
需要自己释放销毁指针,不然会发生内存泄漏。 如果想要获取多个lua脚本返回值。需要以table的形式返回。
local a = 1
local b = "str"
return { a, b}