背景
我们的异æ¥é˜Ÿåˆ—是Tasktiger,一个背åŽæœ‰å•†ä¸šå…¬å¸æ”¯æŒçš„å¼€æºé¡¹ç›®ã€‚
之å‰æ˜¯Pyres,一个Resqueçš„Pythonå®žçŽ°ï¼Œå› ä¸ºGithub在使用Resque,Instagram在使用Pyres,我们应该å¯ä»¥ç”¨ä¸€æ®µæ—¶é—´ã€‚
åŽæ¥Pyresé•¿ä¹…ä¸å†ç»´æŠ¤ï¼Œç§¯ç´¯äº†ä¸€äº›bug以åŠæ€§èƒ½é—®é¢˜è®©æˆ‘们ä¸å¾—ä¸é€‰æ‹©æ›´æ¢ä¸€ä¸ªå¼‚æ¥é˜Ÿåˆ—系统,期åˆè€ƒè™‘过Celery,那是一个很æˆç†Ÿå¾ˆå¼ºå¤§çš„队列系统,以至于我很难一下åæžæ¸…它。
问题
在使用Tasktiger
åŽæœ‰æ—¶ä¼šé‡åˆ°å·²ç»å¤„ç†å®Œæ¯•çš„taskåˆé‡æ–°è¢«enqueue的情况,如果所有task都是幂ç‰çš„(Idempotent),那这也ä¸æ˜¯é—®é¢˜ï¼Œç¬¬äºŒæ¬¡æ‰§è¡Œtaskä¼šå› ä¸ºä¹‹å‰å¤„ç†è¿‡è€Œä¸è‡³äºŽå†åšä¸€æ¬¡ã€‚然而并ä¸æ˜¯æ‰€æœ‰task都完æˆäº†å¹‚ç‰æ”¹é€ ,所以这对于我们æ¥è¯´æ˜¯ä¸ªä¸¥é‡é—®é¢˜ï¼Œä½†å½“时并ä¸èƒ½ç¨³å®šé‡çŽ°ã€‚
探索
怀疑过许多地方,åŽæ¥å‘现在queue redis上执行BGREWRITEAOF
就能é¿å…这个问题,于是把原先一个月执行一次的AOF
é‡å†™æ“作å˜æˆäº†æ¯å¤©æ‰§è¡Œä¸€æ¬¡ï¼Œå¤§æ¦‚在备份数æ®ä¹‹å‰ï¼Œå› 为备份需è¦åœæœºï¼Œå¦‚æžœä¸åœ¨è¿™ä¹‹å‰åšï¼Œå¯èƒ½å¤‡ä»½å¯¼è‡´é‡å¯æœåŠ¡å°±ä¼šè®©å¾ˆå¤šå·²ç»å¤„ç†è¿‡çš„taské‡å¤å¤„ç†ï¼Œä¾‹å¦‚å¯èƒ½ä¼šç»™ä¼šå‘˜å‘多æ¡çŸä¿¡å’Œå¾®ä¿¡æ醒。
å°±åœ¨æ˜¨å¤©ï¼Œå› ä¸ºé‡å¯queue redis导致很多taské‡å¤å¤„ç†ï¼Œè¿™ä¸ªé—®é¢˜ç»ˆç©¶æ²¡æœ‰è¢«è§£å†³ï¼ŒäºŽæ˜¯èŠ±äº†å¤§çº¦2å¤©æ—¶é—´ï¼Œæ‰¾åˆ°äº†å¯¼è‡´é—®é¢˜çš„æ ¹æºã€‚
之å‰çŒœæµ‹æ˜¯ä¸æ˜¯Tasktiger
哪里没写好导致,毕竟这个项目在Github上åªæœ‰ä¸åˆ°500个star,而redisæœ‰å‡ ä¸‡ï¼Œå¹¿å¤§ç”¨æˆ·åŸºç¡€æ›´å®¹æ˜“å‘现奇怪bug,而在Tasktiger
上é‡åˆ°æŸä¸ªbugï¼Œä¹Ÿè®¸ä½ å°±æ˜¯ä¸–ç•Œä¸Šç¬¬ä¸€ä¸ªé‡åˆ°çš„。基于这个å‡è®¾æˆ‘看了很多æºä»£ç ,没有找到å¯é è¯æ®ï¼Œè€Œä¸”基于一个简å•äº‹å®žï¼šä¸€ä¸ªtask从最开始ä¸å˜åœ¨ï¼Œåˆ°å‡ºçŽ°åœ¨Tasktigerä¸ï¼Œåˆ°å¤„ç†å®Œè¢«åˆ 除,如果是Tasktiger
的问题,那它从哪里得到数æ®æž„é€ å‡ºä¸€ä¸ªtask呢?难é“是redis的问题å—?
queue redis一般都会å¯ç”¨AOF
,所有修改数æ®çš„命令和值被记录到AOF
ä¸ï¼Œç‰redisé‡å¯æ—¶ï¼Œä¼šæ‰§è¡ŒAOF
文件ä¸çš„å‘½ä»¤ï¼Œè¿™æ ·redis就有了和é‡å¯å‰ä¸€æ¨¡ä¸€æ ·çš„æ•°æ®ã€‚在AOF
ä¸è®°å½•ç€æŸä¸ªtask从出现到消失的全过程,难é“是redis没完æˆload就接å—æœåŠ¡äº†ï¼Ÿä»Žæ—¥å¿—上看并ä¸æ˜¯è¿™æ ·ï¼Œåœ¨æ²¡load完执行æ“作会得到异常。
看了下生产环境的queue状æ€ï¼ŒåŸºæœ¬æ²¡æœ‰æ£åœ¨å¤„ç†çš„task,åœæŽ‰queue redis,下载AOF
文件,在本地让rediså¯åŠ¨å¹¶load这份AOF
,ä¸å¯åŠ¨Tasktiger
çš„worker,åªé™é™åœ°çœ‹queue的状æ€ï¼Œå‘现load完åŽæœ‰å¾ˆå¤štask被é‡æ–°æ”¾å›žäº†queue,æ¤æ—¶å¦‚果有worker,一定会é‡å¤æ‰§è¡Œã€‚
到这里难é“能确定是redis的问题å—?redisçš„AOF
并ä¸èƒ½è®©redis回到之å‰çš„状æ€ï¼Ÿå¦‚æžœæ˜¯è¿™æ ·çš„è¯redis怎么会被广泛应用?
于是找了一个task,从AOF
ä¸çœ‹çœ‹Tasktiger
是如何处ç†å®ƒçš„。
处ç†è¿‡ç¨‹æ²¡å‘现异常,åªæ˜¯Tasktiger
用了很多lua
脚本æ“作数æ®ï¼Œä¾‹å¦‚有个脚本是:当一个keyä¸åœ¨ç»™å®šçš„æŸå‡ 个zsetä¸æ—¶åˆ 除这个key。这个æ“作ä¸å¤æ‚ï¼Œä½†ä½ æ— æ³•åªç”¨redis完æˆï¼Œå› 为redisåªæ˜¯ä¸€ä¸ªæ•°æ®å˜å‚¨ï¼Œæ— 法处ç†å¤æ‚逻辑,所以åªèƒ½ä½¿ç”¨lua
完æˆï¼Œå½“然很多è¯éƒ½å¯ä»¥å®Œæˆè¿™ä¸ªé€»è¾‘,åªä¸è¿‡redis支æŒæ‰§è¡Œlua
脚本,算作是对redisæ“作的补充。
å› ä¸ºæœ‰äº†ä¸€ä¸ªå…·ä½“task,查看和它相关的所有æ“作åŽï¼Œå‘现除éžæ˜¯åœ¨æŸä¸€æ¥lua
脚本未执行时æ‰ä¼šå¯¼è‡´è¿™ä¸ªtask被放在active queueä¸è€Œä¸æ˜¯è¢«ç§»é™¤ã€‚
äºŽæ˜¯æˆ‘æž„é€ äº†ä¸€ä»½AOF
ï¼Œæœ€å¼€å§‹çš„éƒ¨åˆ†æ˜¯ä¸€å †SCRIPT LOAD
指令,把Tasktiger
需è¦çš„脚本都load到redisä¸ï¼Œè¿™æ ·åŽç»æ“作æ‰å¯ä»¥ä½¿ç”¨EVALSHA
ï¼Œè¿™ä»½æž„é€ å¥½çš„AOF
也通过了redis-check-aof
检查,被loadåŽå†æ¬¡æŸ¥çœ‹queue状æ€ï¼Œç«Ÿç„¶æ£å¸¸äº†ï¼Œå’Œåœredis之å‰ä¸€è‡´ï¼çœ‹æ¥é—®é¢˜å‡ºåœ¨æ‰§è¡Œè¿™äº›è„šæœ¬çš„地方。
在redisä¸æ‰§è¡Œlua
脚本有两ç§åšæ³•ï¼Œç¬¬ä¸€ç§æ˜¯ä½¿ç”¨EVAL
,这ç§åšæ³•æ˜¯æŠŠè„šæœ¬å†…容一并贴上,æ¯æ¬¡æ‰§è¡Œéƒ½éœ€è¦è¿™æ ·ï¼Œå¦‚果总是é‡å¤æ‰§è¡ŒæŸä¸ªç‰¹å®šè„šæœ¬å°±ä¼šå› ä¸ºä¼ è¾“äº†å¤§é‡è„šæœ¬å†…容而使得效率ä¸å¤Ÿé«˜ï¼ŒäºŽæ˜¯æœ‰äº†ç¬¬äºŒç§æ‰§è¡Œæ–¹æ³•––EVALSHA
,使用这ç§éœ€è¦è„šæœ¬çš„SHA1
,当执行SCRIPT LOAD
åŽï¼Œredis返回的内容就是脚本的SHA1
,load过的脚本在手工flushå‰æˆ–者redisé‡å¯å‰éƒ½æ˜¯æœ‰æ•ˆçš„。SCRIPT LOAD
在BGREWRITEAOF
åŽå¹¶ä¸ä¼šå˜åœ¨ï¼Œè™½ç„¶ä½ 还是å¯ä»¥é€šè¿‡EVALSHA
调用脚本。
那么这两ç§æ‰§è¡Œæ–¹æ³•å¯¹äºŽAOF
文件记录内容有差别å—?没有,执行EVALSHA
也会被记录æˆEVAL
ï¼Œå¦‚æžœä½ åœ¨AOF
ä¸å‘现有EVALSHA
那就奇怪了。在生产环境下载的AOF
ä¸ï¼Œæœ‰ä¸å°‘EVALSHA
,为何没有被记录æˆEVAL
ï¼Ÿå› ä¸ºè¿™äº›EVALSHA
并ä¸æ˜¯redis执行的,而是Tasktiger
çš„pipeline
å¯¼è‡´å‡ºçŽ°å›žæ»šé—®é¢˜çš„åŽŸå› æ˜¯ï¼šBGREWRITEAOFåŽï¼Œç”±äºŽSCRIPT LOAD被去掉,redisé‡å¯åŽå¦‚果有使用EVALSHAè°ƒç”¨è„šæœ¬çš„åœ°æ–¹ä¼šå‡ºé”™ï¼Œå› ä¸ºé‚£ä¸ªè„šæœ¬å¹¶æ²¡æœ‰è¢«redis load
ä¸æ˜¯è¯´EVALSHA
会被翻译æˆEVAL
放在AOF
ä¸å—?是的,ä¸è¿‡Tasktiger
自己实现了一个redis pipeline
,这也是个lua
脚本,在这个lua
脚本ä¸æ˜¯éœ€è¦è°ƒç”¨å…¶ä»–lua
脚本的。调用方法包å«ä¸¤ç§ï¼Œå¤§å¤šæ•°éƒ½æ˜¯EVALSHA
, 由于redisç¦æ¢åœ¨luaä¸ä½¿ç”¨redis.call执行EVALSHA调用å¦ä¸€ä¸ªè„šæœ¬ï¼ˆå¹¶ä¸æ˜¯æ‰€æœ‰redis命令都å¯ä»¥åœ¨redis.call使用) ,Tasktiger使用了其他方法æ¥è°ƒç”¨ï¼Œç®—是一ç§hack。
由于redisçš„é™åˆ¶åŠ 上Tasktiger
çš„hack,导致 在BGREWRITEAOFåŽï¼Œå¦‚果有pipeline脚本的调用å‘生,下次é‡å¯redis会使得之å‰åšè¿‡çš„task被é‡æ–°æ”¾å›žqueueï¼Œå› ä¸ºç§»é™¤æ“作的脚本还没有load,所以移除失败了,看起æ¥å°±åƒæ˜¯task被é‡æ–°æ”¾å›žqueue
ç›®å‰æˆ‘们针对这个问题的解决方法是:勤快点BGREWRITEAOF,æ¯å°æ—¶ä¸€æ¬¡ï¼Œé‡å¯redis之å‰å†åšä¸€æ¬¡
Top comments (0)