• Redis中的Lua脚本(五)


    Lua脚本

    脚本复制

    复制EVALSHA命令

    EVALSHA命令式所有与Lua脚本有关的命令中,复制操作最复杂的一个,因为主服务器与从服务器载入Lua脚本的情况可能有所不同,所以主服务器不能像复制EVAL命令、SCRIPT LOAD命令或者SCRIPT FLUSH命令那样,直接将EVALSHA命令传播给从服务器,对于一个在主服务器被成功执行的EVALSHA命令来说,相同的EVALSHA命令在从服务器执行时可能会出现脚本未找到(not found)错误。
    举个例子。假设现在有一个主服务器master,如果客户端向主服务器发送命令:

    127.0.0.1:6379> SCRIPT LOAD "return 'hello world'"
    "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
    
    • 1
    • 2

    那么在执行这个SCRIPT LOAD命令之后,SHA1值为5332031c6b470dc5a0dd9b4bf2030dea6d65de91的脚本
    就存在于主服务器中了,现在假设一个从服务器slave1开始复制主服务器master,如果master不想办法将脚本:

    "return 'hello world'"
    
    • 1

    传送给slave1载入的话,那么当客户端向主服务器发送命令:

    127.0.0.1:6379> EVALSHA "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" 0
    "hello world"
    
    • 1
    • 2

    的时候,master将成功执行这个EVALSHA命令,而当master将这个命令传播给slave1执行的时候,slave1却会出现脚本未找到错误:

    127.0.0.1:6380> EVALSHA "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" 0
    (error) NOSCRIPT No matching script. Please use EVAL.
    
    • 1
    • 2

    更为复杂的是,因为多个从服务器之间载入Lua脚本的情况也可能各有不同,所以即使一个EVALSHA命令可以在某个从服务器成功执行,也不代表这个EVALSHA命令就一定可以在另一个从服务器成功执行。

    例子
    • 举个例子。假设有主服务器master和从服务器slave1,并且slave1一致复制着master,所以master载入的所有Lua脚本,slave1也有载入(通过传播EVAL命令或者SCRIPT LOAD命令来实现)例如说,如果客户端向master发送命令
    127.0.0.1:6379> SCRIPT LOAD "return 'hello world'"
    "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
    
    • 1
    • 2

    那么这个命令也会被传播到slave1上面,所以master和slave1都会成功载入SHA1校验和为 5332031c6b470dc5a0dd9b4bf2030dea6d65de91
    的Lua脚本。如果这时,一个新的从服务器slave2开始复制主服务器master,如果master不想办法将脚本:

    "return 'hello world'"
    
    • 1

    传送给slave2的话,那么当客户端向主服务器发送命令:

    127.0.0.1:6379> EVALSHA "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" 0
    "hello world"
    
    • 1
    • 2

    的时候,master和slave1都将成功执行这个EVALSHA命令,而slave2却会发生脚本未找到错误。为了防止以上假设的情况出现,Redis要求主服务器在传播EVALSHA命令的时候,必须确保EVALSHA命令要执行的脚本已经被所有从服务器载入过,如果不能确保这一点的话,主服务器会将EVALSHA命令转换成一个等价的EVAL命令,然后通过传播EVAL命令来代替EVALSHA命令。传播EVALSHA命令,或者将EVALSHA命令转换成命令,都需要用到服务器状态的lua_scripts字典和repl_scriptcache_dict字典

    判断传播EVALSHA命令是否安全的方法

    主服务器使用服务器状态的repl_scriptcache_dict字典记录自己已经将哪些脚本传播给了所有从服务器:

    struct redisServer{
    // ...
    
    dict *replc_scriptcache_dict;
    
    // ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    repl_scriptcache_dict字典的键是一个个Lua脚本的SHA1校验和,而字典的值则全部都是NULL,当一个校验和出现在repl_scriptcache_dict字典时,说ing这个校验和对应的Lua脚本已经传播给了所有从服务器,主服务器
    可以直接向从服务器传播包含这个SHA1校验和的EVALSHA命令,而不必担心从服务器会出现脚本未找到错误

    例子
    • 举个例子。如果主服务器repl_scriptcache_dict字典的当前状态如图所示。那么主服务器可以向从服务器传播以下三个EVALSHA命令,并且从服务器在执行这些EVAlSHA命令的时候不会出现脚本未找到错误:
    EVALSHA "2f31ba2bb6d6a0f42cc159d2e2dad55440778de3" ...
    EVALSHA "a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9" ...
    EVALSHA "4475bfb5919b5ad16424cb50f74d4724ae833e72" ...
    
    • 1
    • 2
    • 3

    另一方面,如果一个脚本的SHAR1校验和存在于lua_scripts字典,但是不存在于repl_scriptcache_dict字典,那么说明校验和对应的Lua脚本已经被主服务器载入,但是并没有传播给所有从服务器,如果尝试向从服务器传播包含这个SHA1校验和的EVALSHA命令,那么至少有一个从服务器会出现脚本未找到错误
    在这里插入图片描述

    • 举个例子。对于如图所示的lua_scirpts字典,以及上图的repl_scriptcache_dict字典来说,SHA1校验和为:
    "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
    
    • 1

    的脚本:

    "return 'hello world'"
    
    • 1

    虽然存在于lua_scirpts字典,但是repl_scriptcache_dict字典却并不包含校验和"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
    这说明脚本

    "return 'hello world'"
    
    • 1

    虽然已经载入到主服务器里面,但并未传播给所有从服务器,如果主服务器尝试向从服务器发送命令:

    EVALSHA "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" ...
    
    • 1

    那么至少会有一个从服务器遇上脚本未找到错误
    在这里插入图片描述

  • 相关阅读:
    Python爬虫教程之五大重要库入门使用教程Scrapy、Requests、Urllib、Beautiful Soup、Selenium(教程含源码)
    Navicat操作mysql分区
    创建并运行一个 Spring项目
    【Flutter】Flutter学习-GetX 导航操作
    【业务安全-04】万能用户名及万能密码实验
    Android项目在 app 中通过 WebView 访问 url显示空白,使用浏览器可以打开,Android WebView加载出现空白页面问题解决
    Unity sln 和 csproj 基础
    html网页制作期末大作业成品:基于HTML+CSS+JavaScript简洁汽车网站(7页)
    MindSponge分子动力学模拟——Constraint约束(2023.09)
    电脑怎么录制视频,录制的视频怎么剪辑?
  • 原文地址:https://blog.csdn.net/Cover_sky/article/details/137979711