站长工具网站提交网站备案 在那给网站备案
- 作者: 五速梦信息网
- 时间: 2026年03月21日 06:44
当前位置: 首页 > news >正文
站长工具网站提交,网站备案 在那给网站备案,法律咨询网站开发,企业管理平台登录本文主要记录multi/exec、eval、redis执行lua脚本的源码流程 redis在exec之前#xff0c;所有queued的命令是没有执行的#xff0c;#xff01;#xff01;#xff01;在执行时会通过检测client是否被打上CLIENT_DIRTY_CAS标记来判断[watch后,exec时]时间段内是否有key被…本文主要记录multi/exec、eval、redis执行lua脚本的源码流程 redis在exec之前所有queued的命令是没有执行的在执行时会通过检测client是否被打上CLIENT_DIRTY_CAS标记来判断[watch后,exec时]时间段内是否有key被修改如果有因为此时还没有执行事务中的任何命令所以是可以不执行任何命令就取消整个事务的如果是执行过程中有个命令执行是失败此时已经进入事务执行了所以即使当前命令失败redis也会执行完所有的命令即redis事务要么一条都不执行要么就全部执行因为redis是串行执行所有client的所以redis可以执行事务的所有命令而不会被其他任何client的命令中断这也是redis事务原子性的原理 unwatchAllKeys的实现原理八九不离十有一个keylist存放所有的key即每个key都有一个watch-client-list表示所有watch这个key的client然后client watch这个key时就在这个key的watch-client-list中插入一个节点节点的值为当前client并把这个节点的地址保存到client中这样unwatchAllKeys中先通过client-watch_keys获取所有client watch的key然后通过这个key节点然后再通过这个key节点中保存的地址来获取这个key的watch_client_listclient找到watch-client-list后然后删除就是先从keylist中摘去本节点然后再client-watch_keys中摘除这个节点。核心就是client保存了一个指向key对应的节点的指针即redis保存key的时候不是仅仅保存一个key而是保存了一个附加了大量其他状态的节点然后其他地方就直接保留一个指向该节点的指针这样可以非常方便的通过一个指针找到整个链表或者链表中的某个节点进而找到这个链表的其他部分即数据只存一份其他地方保存一个指向这份数据的指针可以看unwatchAllKeys函数的实现很好的体现了这一点。 如果当前客户端的ID是CLIENT_ID_AOF 即这个客户端是在处理AOF的虚拟客户端那么Redis会执行call(c, CMD_CALL_NONE)即调用call()时不进行任何统计、传播或者慢日志记录只是执行命令。这是因为AOF重放期间不需要记录慢日志或传播到其他从节点。在AOF重写或加载期间Redis只是想尽快恢复数据集并不需要执行额外的操作如更新慢日志、传播命令等。因此通过CLIENT_ID_AOF来识别AOF客户端并简化命令的处理逻辑。如果客户端不是CLIENT_ID_AOF 即普通客户端Redis会使用CMD_CALL_FULL来执行命令。这意味着命令执行后会更新慢日志、统计信息并且如果需要还会进行AOF或从节点的传播。 watch、multi/exec、evalredis中eval lua脚本的实现和redis中exec命令的实现原理是一致的只不过区别在于multi/exec中redis在开启multi之后会把exec之前的所有命令都放到命令队列里直到遇到exec命令才会开始把这期间存储的命令一条一条执行即multi/exec有一个命令收集的过程因为有一个收集的过程而收集过程必定不是原子的也就是说在multi开始后exec执行时一个key在此期间是有可能被其他client修改的所以multi/exec事务过程中才需要watch命令,watch是专门与multi搭配的即在watch后exec之前key被其他client修改了那么就取消事务先watch然后multi最后exec注意一旦watch之后就会开启检测也就是说watch检测的是watch之后到exec之前所有对watch_key的修改在此期间一旦有任何其他client修改了key那么redis都会给client打上CLIENT_DIRTY_CAS标记也就是说[watch之后multi之前]任何对key的修改也会取消【multi后exec时]之间命令的执行即会取消整个事务因为redis执行exec时会检测client是否有CLIENT_DIRTY_CAS标记如果有就取消整个事务但是不会影响[watch之后multi之前]区间内命令的执行因为这些命令都是当做普通命令执行执行时不会检测client是否被打上了CLIENT_DIRTY_CAS标记相比之下eval命令中却是一次性提交整个脚本也就是一次性把整个事务过程中所需要的命令都提交给redis这样就不存在提交命令过程中key被其他client修改所以eval之前是不需要watch的eval执行时也不会检测client是否打上了CLIENT_DIRTY_CAS标记也就是说watch只是一个打标记的功能至于打好标记以后怎么办则取决于命令的执行函数如果该函数直接忽略这个CLIENT_DIRTY_CAS标记那么watch就对他没有任何影响。注意exec执行完毕后会unwatchall也就是取消监视所有key也就是说执行下一次multi/exec之前如果需要watch某些key必须重新使用watch redis lua脚本原子执行原理redis启动的时候就会启动一个lua虚拟机用lua变量代表因为lua库对外提供了api可以让外部调用者向其注册函数也就是说外部调用者可以像lua虚拟机注册一个函数x然后这个lua函数x对应调用者的函数y举个例子redis会向这个虚拟机注册“call”,luaRedisCallCommand)函数这表示lua中新增了一个函数call这个函数对应的是外部的luaRedisCallCommand函数这样lua脚本调用redis.call那么lua虚拟机就会跳转到redis的luaRedisCallCommand执行eval命令的时候直接把脚本提交给这个lua虚拟机就行了lua虚拟机是单线程运行的且直接在主线程中运行而不是单独起一个线程。redis lua脚本的执行过程和redis exec命令的执行原理本质上是一样的也是一条一条执行因为redis是单线程顺序执行的所以和事务一样能保证原子性。执行eval时会直接把lua脚本提交给lua虚拟机来解释执行lua虚拟机是单线程而且直接在redis主线程里运行并不是开一个新的线程来运行lua虚拟机lua虚拟机肯定是按脚本一行一行来执行的这每一行都是一个get/set命令这里一行一行执行就相当于exec里一条一条的执行命令队列中的命令所以说lua脚本原子性的实现本质和multi/exec是一样的。redis会向这个lua虚拟机注册一系列函数比如上面说的call这些redis注册的lua api函数可供lua脚本调用这些是redis环境特有的如果lua脚本在非redis环境调用是会报错的lua虚拟机中调用这些函数的时候程序控制权就会转到注册的这些函数。lua脚本举例redis.call(“SET”, “mykey”, “new_value”)redis向lua虚拟机注册了redis.call这个接口lua脚本执行到redis.call的时候代码就会跳转到redis的luaRedisCallCommand这样lua中一行一行执行实际上就是一个一个的调用对应的redis c函数每个redis命令都对应一个执行函数所以lua脚本和redis的exec命令没有本质区别都是一条一条执行因为redis是单线程所以能保证原子性只是lua脚本提供的功能更复杂更强大。 redis命令xxx对应的函数的名字往往都是xxxCommand的格式比如multi命令源码中对应的执行函数就是multiCommand、eval命令对应的执行函数是evalCommand 笔记redis/deps/lua/src/lua.hLUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); LUA_API实际是extern。即第三方库头文件中导出了一个叫做lua_pcall的函数然后redis中直接通过头文件调用这个函数因为是extern的就表明这是外部包所以说编译的时候不会去编译lua_pcall代码而是在链接的时候直接去链接第三方lua包这也是extern的作用。代码编译后因为lua_pcall是直接链接的第三方包(即.o文件)就是说只有lua_pcall的声明而没有lua_pcall的源代码所以我们lua_pcall打断点进去是没用的。但是我们提前向lua中注册了相关函数比如这里我们就向lua注册了一个叫做redis.call的函数绑定的是redis源码中的luaRedisCallCommand函数这样lua_pcall执行lua脚本时遇到redis.call函数时就会转到我们的luaRedisCallCommand函数 笔记lua是栈式虚拟机所以都是push/pop来传递参数和返回值 笔记未经证实lua脚本是如果对key做了修改那么就无法通过script kill来杀掉脚本即此时lua是全部执行和exec全部执行差不多 multi命令源码流程 server.callmulti.multiCommand if (c-flags CLIENT_MULTI) { multi禁止嵌套addReplyError(c,MULTI calls can not be nested);return;}c-flags | CLIENT_MULTI; #multi命令很简单就是标记客户端进入了multi状态然后在此状态时一切非exec等执行事务的命令#他都会存起来放到client.mstate.commands数组中,这就是queued的含义#这里不会重置CLIENT_DIRTY_CAS#也就是说watch开启之后到multi之前任何键的修改都会给client打上CLIENT_DIRTY_CAS的标记#因为multi没有清除CLIENT_DIRTY_CAS标志所以当exec执行时事务会失败#也就是说exec检测的是watch之后到exec之前对watch_key的修改addReply(c,shared.ok); } 事务冲突打标记以set为例 server.call 1执行命令t_string.setCommand t_string.setGenericCommanddb.setKeyWithDictEntry if !SETKEY_NO_SIGNAL: #如果设置了通知更新就通知所有watch这个key的所有对象包括客户端 db.signalModifiedKey #通知key修改了。multi.touchWatchedKey #凡是监视这个key的client都被标记为CLIENT_DIRTY_CAS,如果这个key是在某个事务操作中#那么CLIENT_DIRTY_CAS这就表示该CAS操作失败了即redis的watch是一种乐观锁的方式#watch的时候就会注册client要watch这个key而不是multi时注册#也就是说exec检测的是watch之后到exec之前对watch_key的修改for 所有监视这个key的客户端:c-flags | CLIENT_DIRTY_CAS #这样multi事务执行exec时就会检测到这个标记一旦为真说明这个key被其他人修改了那么就会取消事务exec命令源码流程 server.callmulti.execCommandif (!(c-flags CLIENT_MULTI)) #如果不是multi状态直接返回return tagmulti.isWatchedKeyExpired #遍历client watch的所有key看是否有key过期了for client.watch_keys:db.keyIsExpiredif (tag) {c-flags | (CLIENT_DIRTY_CAS) #key过期和key被修改都是一样的都一样要取消事务的执行}if (c-flags (CLIENT_DIRTY_CAS | CLIENT_DIRTY_EXEC)): #检测CLIENT_DIRTY_CAS标记#一旦为真说明此事务watch的key有些已经被人修改了所以需要放弃事务 #如果有key出了问题或者命令queued的时候出错都会放弃事务networking.addReply #填充响应到发送缓冲区multi.discardTransaction #取消事务就是清空命令队列以及清空client的multi、CLIENT_DIRTY_CAS等标记multi.freeClientMultiState #释放对应的资源这里就是释放指针以及减少引用计数#redis用mstate变量即multiState来存储与multi/exec相关的资源multi.initClientMultiState #重置client.mstate的各个字段就是都设置为0/nullc-flags ~(CLIENT_MULTI|CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC) #清除client的multi相关标记multi.unwatchAllKeys #撤销对key的watch因为multi已经结束而了 #也就是说exec之后就会取消watch#也就是说每次执行multi/exec之前都必须watch一次 #逻辑就是#1key是单独存储的一个key对应一个节点#一个key的节点内保存了一个watch-clients-key#watch-clietnts-list包含了所有watch这个key的clients#2client有一个watch_key链表保存了他watch的所有key#所以先通过client-watch_key找到watch的key对应的节点的指针#然后通过这个指针获取key的watch-clients-key链表然后从中摘去当前client#摘完以后再回过头来在client-watch_key中摘去这个keylistRewind(c-watched_keys,li) #获取client-watch_keys_listwhile lnnext(li)ln!nul: #遍历所有client watch的keywk listNodeValue(ln); #从client节点获取保存的watch_key指针 clients watchedKeyGetClients(wk); #通过watch_key节点获取key对应的watch-client-list即clients listUnlinkNode(clients, watchedKeyGetClientNode(wk)); #从key对应的watch-client-list中摘去当前的clientlistDelNode(c-watched_keys,ln); #从client的watch_key链表中摘去当前key#开始执行事务c-flags | CLIENT_DENY_BLOCKING; #redis multi/exec中禁止执行会导致阻塞的命令multi.unwatchAllKeys #开始执行执行之前unwatch所有key以节约cpufor client-mstate.commands #commands保存了事务的所有命令这里就是遍历数组顺序执行所有命令if (c-id CLIENT_ID_AOF)server.call(c, CMD_CALL_NONE)elseserver.call(commands[i],CMD_CALL_FULL) #redis是串行处理所有client fd上的事件#也就是只有处理完当前client fd后才会处理下一个client fd上的事件#即在执行这个for循环的时候不会并发执行任何其他client fd上的命令#因为在执行这个for循环的时候他也是一条命令接一条命令的执行#执行完一条才会执行下一条#server.call是无返回值的就是说不管当前命令执行是否成功#都会走完整个for循环即执行所有命令而不会中途返回#所以说redis的事务要么就是一条命令也不执行(即执行前检测到冲突就取消整个事务)#要么就是全部执行所以说可以把redis的multi/exec看成是一个原子指令#redis中lua脚本是通过eval实现的#eval之所以能保证原子性的原理和multi/exec是完全一致的 #redis调用lua虚拟机执行脚本直到脚本执行完毕才会运行后面的multi.discardTransaction #所有命令都执行完了即事务已经完成了所以可以重置client的状态了取消事务就是一个重置clint的操作#也就是说exec一次就会取消watch也就是说每次执行multi/exec之前都必须watch一次#而不是一次watch多次multi/exec redis向lua中注册函数 server.mainserver.initServereval.scriptingInitlctx.lua_scripts dictCreate(shaScriptObjectDictType) #创建一个mapkeyscriptSHA1,valuescript每个执行过的脚本都会存放在这里面#evalsha命令的实现基础即根据sha就直接从这个dict找到脚本就省去了重新编译脚本的过程script_lua.luaRegisterRedisAPIlua_pushstring(lua, call) #向lua注册redis.calllua_pushcfunction(lua, luaRedisCallCommand) #将lua中redis.call和redis的luaRedisCallCommand函数绑定起来#这样lua中调用redis.call的时候程序流程就会转到redis源码中的luaRedisCallCommand函数lua_pushstring(lua, pcall) #向lua注册redis.pcalllua_pushcfunction(lua, luaRedisPCallCommand); #同上call和pcall只是对错误的处理不同其他都一样lua_setglobal(lua,redis) #在lua中注册全局表redis代码里会把call/pcall注册到redis表#这样lua中要调用函数call就是redis.call形式即redis表的call函数lua脚本举例 redis.call(SET, k1, v1) local value redis.call(GET, k1) redis.call(SET, k2, v2) return value“busy-reply-threshold”, “lua-time-limit”配置文件中这两个参数是一样的只不过一个是老版本一个是新版本但是最终都设置的同一个变量 笔记阻塞redis evel流程 eval.evalCommandeval.evalGenericCommandlualctx.lua #lctx.lua就是redis中嵌入的lua解释器script_lua.luaCallFunctionif (server.busy_reply_threshold 0 !debug_enabled): #busy_reply_threshold0表示设置了脚本执行的最长时间默认5000ms#如果设置为0那么就表示不设置。#注意这个参数并不会限制脚本的执行时间而是达到阈值后redis就会进入busy状态#一旦进入busy状态除非脚本执行结束或者客户端发来script kill#或者shutdown nosave命令#否则redis不会自动结束脚本执行并且不会响应其他任何命令包括客户端的连接#如果lua脚本修改了任意key那么script kill将不起作用#此时只能执行shutdown nosave#因为如果lua脚本执行时间非常长因为redis是单线程所以会导致redis无法响应客户端命令#busy-reply-threshold和lua-time-limit这两个参数实际是对应同一个变量#前者是新版本中配置项的名称后者是旧版本redis中的配置名称lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000) #这个钩子函数非常非常非常重要是防止redis长时间阻塞于lua脚本的关键#luaMasCountHook即指定的钩子函数#LUA_MASKCOUNT表示lua虚拟机每执行一定条数的指令就调用一次这个钩子函数#100000表示每次执行10w条指令就调用一次钩子函数#redis的逻辑是不管你设置脚本超时时间是多久他都是每隔10w条指令就调用一次钩子函数#然后在钩子函数里检测一下是否超时也就是说redis这个超时时间是不精确的statescript.scriptInterrupt #这个函数非常重要#这个函数的作用是1如果脚本执行时间还没有超时那么就返回继续执行脚本#2如果脚本第一次超时则给脚本打上SCRIPT_TIMEDOUT标记表示超时然后处理其他事件#注意这个SCRIPT_TIMEOUT是所有client都可以看见的相当于给全局变量lua打上的标记#不过此时只能处理script kill/shutdown nosave命令其他的都会被redis拒绝#3如果已经超时则直接处理其他事件#即第一次超时和非第一次超时相比#第一次超时多了一个打超时标记、打印告警日志的动作、protectClient的操作if (run_ctx-flags SCRIPT_TIMEDOUT) { #如果脚本被打上了SCRIPT_TIMEDOUT标记就表明脚本已经超时了#当脚本超时时就代表redis进入了busy状态在eventloop的processCommand中#此时只能执行含有CMD_ALLOW_BUSY的命令执行(script kill/shutdown nosave)networking.processEventsWhileBlocked(); #处理一下网络读写非常重要后续详解#whileBlocked表示当前正阻塞于lua脚本执行#为了避免redis无法响应其他客户端所以才有这个函数#不过此时只能处理script kill强制结束脚本或者shutdown nosave强制结束redis#如果lua脚本修改了任意key那么script kill将不起作用#此时只能执行shutdown nosave其他任何命令都会被redis拒绝return (run_ctx-flags SCRIPT_KILLED) ? SCRIPT_KILL : SCRIPT_CONTINUE; #处理完后如果客户端调用了 #SCRIPT KILL命令就会打上SCRIPT_KILLED的标记}long long elapsed monotonic.elapsedMs(run_ctx-start_time) #计算自启动该lua脚本开始到目前开始的时间if (elapsed server.busy_reply_threshold) { #如果没有超时则返回继续执行lua脚本#即lua脚本第一次执行时最长可以连续执行这么长时间#而不用执行processEventsWhileBlocked#执行processEventsWhileBlocked就相当于打断了lua脚本的连续执行#超时以后每隔10w条就要执行一次processEventsWhileBlocked#注意只是打算lua脚本的连续执行但不会终止#一旦processEventsWhileBlocked执行完毕又会让lua虚拟机执行10w条指令#除非processEventsWhileBlocked处理过程中client执行了script killl#否则一旦陷入死循环这就是个无限循环redis此时就完全阻塞了return SCRIPT_CONTINUE;}serverLog(LL_WARNING,Slow script detected: still in execution after %lld milliseconds. #打印一条警告信息You can try killing the script using the %s command. Script name is: %s.,elapsed, (run_ctx-flags SCRIPT_EVAL_MODE) ? SCRIPT KILL : FUNCTION KILL, run_ctx-funcname);script.enterScriptTimedoutMode #第一次超时给脚本打上超时标记SCRIPT_TIMEDOUT表示脚本执行时间过长run_ctx-flags | SCRIPT_TIMEDOUT; networking.protectClient(client *c) #阻止该client继续发送任何命令直到lua脚本结束或者其他客户端发送script killc-flags | CLIENT_PROTECTED; #给client打上protect标记if (c-conn):connSetReadHandler(c-conn,NULL); #清除read_handler这样redis就不会处理该client fd上的读事件connSetWriteHandler(c-conn,NULL); #清除write_handler这样redis就不会处理该client fd上的写事件#也就是说redis是单线程顺序执行模型#顺序执行的意思是如果上一条命令没有执行完毕或者没有被取消掉#那么redis保证绝不会处理客户端发来的另一条命令#在redis没有执行完当前lua脚本之前redis绝不会执行任何客户端的命令#除了script kill和shutdown nosave命令#即当lua脚本时间超过阈值时甚至陷入死循环时redis就会进入busy状态#redis在busy状态时要么等待redis执行完毕要么由另一个client发送script kill#如果是陷入死循环那么就只能由另一个client发送script kill强制结束#注意是另一个client因为当前client已经被清除read/writehandler了#而且这个其他的客户端必须在redis陷入busy状态前就已经连接了#因为redis处于busy状态时只允许客户端发送script kill/shutdown nosave 命令#如果lua脚本修改了任意key那么script kill将不起作用此时只能执行shutdown nosave#不会接受任何新连接和处理任何其他命令networking.processEventsWhileBlocked #第一次超时多了一个打超时标记、打印告警日志的动作、protectClient的操作#这里就是处理redis其他命令以免redis无法响应其他客户端#不过此时只能处理script kill 命令或者shutdown nosave强制结束redisae.aeProcessEvents……eval.scriptCommandscript.scriptKillcurr_run_ctx-flags | SCRIPT_KILLED; #执行scriptkill时就会给luactx打上SCRIPT_KILLED标记return (run_ctx-flags SCRIPT_KILLED) ? SCRIPT_KILL : SCRIPT_CONTINUE;if stateSCRIPT_KILL: #当redis处于busy状态时只有script kill/shutdown nosave命令可以结束他#一旦检测到SCRIPT_KILL就结束lua脚本执行#这样redis就相当于执行完了这个脚本就可以接着处理其他未处理的描述符了lua_sethook(lua, luaMaskCountHook, LUA_MASKLINE, 0) #防止有人用while true循环pcall阻止redis终止该lua脚本luaError(lua);luaPushError(lua,Script killed by user with SCRIPT KILL) #如果经过scriptInterrupt函数后lua脚本状态为KILL说明要终止当前脚本的执行#lua是栈式虚拟机向lua栈中压入错误等控制器返回lua时lua就会检测到然后结束#如果是pcall则会让用户自己去处理错误来决定下一步是结束lua脚本还是继续往下执行lua.lua_pcall #lua.lua_pcall是redis引用的第三方库lua提供的api#即调用lua解释器执行lua脚本……script_lua.luaRedisCallCommand #redis在启动时就绑定了(redis.callluaRedisCallCommand)#这样lua执行redis.call的时候实际执行的就是luaRedisCallCommand#lua中调用一次redis.call就会执行一次luaRedisCallCommand流程#lua中调用一次redis注册的lua函数就会执行一次对应的redis函数#lua脚本肯定是串行执行的redis exec也是for循环串行执行#都是一条一条命令的执行因为redis是单线程模型#所以lua脚本和exec命令执行期间都不会有任何其他client的命令执行#所以说lua脚本的执行和exec命令的原子性背后的本质是一样的#只是lua能提供更复杂的逻辑和功能script_lua.luaRedisGenericCommandscript.scriptCallclient c run_ctx-c #获取clientredisCommandlookupCommand(c-argv, c-argc) #解析命令最终是一个redisCommand#其中保存了相关信息比如命令对应的执行函数#lua脚本调用redis.call时会传参#这里就是把参数解析成一个redis命令#lua脚本redis.call(SET, k1, v1)#c-argc3#(char)c-argv[0]-ptrSET#(char)c-argv[1]-ptrk1 #(char)c-argv[2]-ptrv1server.lookupCommandLogic(server.commands,argv,argc) #实际就是根据argv和argc查map#server.commands是一个dict即字典c-cmd c-lastcmd c-realcmd redisCommandif (cmd-flags CMD_WRITE): #CMD_WRITE表示这是一个write命令会修改数据状态run_ctx-flags | SCRIPT_WRITE_DIRTY #给lua虚拟机打上SCRIPT_WRITE_DIRTY标记标记数据已经修改了#在数据修改的情况下一旦redis处于busy状态 #client只能执行shutdown nosaveredis会拒绝执行script killserver.call© #!!!这里就跳转到server.call了#下面就是redis具体命令的执行函数了略#至此我们就捋清了redis中lua脚本的执行原理processEventsWhileBlocked源码流程 networking.processEventsWhileBlockediterations4 #只要aeProcessEvents处理的事件不为0就立即break否则至多空转4次 while (iterations–): long long ae_events aeProcessEvents #aeProcessEvents就是处理一次eventLoop#此时相当于在主线程的eventloop中再调用一次eventloop#只不过此时处于脚本timedout状态即redis处于busy状态#和普通的aeProcessEvents相比此时只能处理少数含有CMD_ALLOW_BUSY标记的命令#比如script kill/shutdown now#此时redis会拒绝任何新的连接1:client对应的fd上的read事件fe-rfileProc(fe-clientData) #fe-clientData就是我们保存的conn这里rfileProc实际是connSocketEventHandlersocket.connSocketEventHandler(conn) #当客户端发来请求时描述符可读所以调用的是rfileProc这里rfileProc实际是connSocketEventHandlerconnhelpers.callHandlernetworking.readQueryFromClient(conn)……server.processCommandif (isInsideYieldingLongCommand() !(c-cmd-flags CMD_ALLOW_BUSY)): #当lua脚本超时的时候只允许部分命令执行#这些命令都必须打上CMD_ALLOW_BUSY标记#凡是没有这个标记的此时都会被拒绝#此时只有script kill/shutdown nosave可以执行#一个命令有哪些标记在commands.def文件中都提前定义好了#直接ctrlf查找 CMD_ALLOW_BUSY就可以了server.rejectCommandFormat …如果含有CMD_ALLOW_BUSY就继续往下执行下面以script kill为例……… server.calleval.scriptCommand #redis的xxx命令对应的处理函数为xxxCommand..script kill命令中详解..if ae_events!0:breakserver.whileBlockedCron #在阻塞期间比如rdb/aof/lua脚本超时执行一些定时任务 script kill命令 server.calleval.scriptCommandif argc[1]load: #script load命令略……else if argc[1]kill: #script kill命令script.scriptKillif (curr_run_ctx-flags SCRIPT_WRITE_DIRTY): #lua脚本执行修改redis数据的命令比如redis.call(set,k1,v1)#此时会给lua虚拟机打上SCRIPT_WRITE_DIRTY表示数据有修改#此时redis选择拒绝执行script killaddReplyError(c, -UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.);returncurr_run_ctx-flags | SCRIPT_KILLED #script kill只是给lua打上SCRIPT_KILLED标记#当processEventsWhileBlocked执行完毕后redis会检测到SCRIPT_KILLED标记#所以redis就会主动停止该脚本的执行addReply(c, shared.ok);
- 上一篇: 站长工具权重查询云虚拟主机建设网站一定要域名
- 下一篇: 站长工具域名wordpress搜索功能优化
相关文章
-
站长工具权重查询云虚拟主机建设网站一定要域名
站长工具权重查询云虚拟主机建设网站一定要域名
- 技术栈
- 2026年03月21日
-
站长工具流量统计新零售是什么模式
站长工具流量统计新零售是什么模式
- 技术栈
- 2026年03月21日
-
站长工具浪潮成都网站排名 生客seo
站长工具浪潮成都网站排名 生客seo
- 技术栈
- 2026年03月21日
-
站长工具域名wordpress搜索功能优化
站长工具域名wordpress搜索功能优化
- 技术栈
- 2026年03月21日
-
站长工具怎么关闭好看的免费的小说网站模板
站长工具怎么关闭好看的免费的小说网站模板
- 技术栈
- 2026年03月21日
-
站长素材官网免费品牌建设影响
站长素材官网免费品牌建设影响
- 技术栈
- 2026年03月21日
