Redis事务和脚本功能

     Redis事务是指将一些命令打包,一次性按顺序执行多个命令的机制,不会出现并发的情况,将命令都执行完以后才会执行其他命令。在一定程度上满足ACID特性。

    1、Redis事务原理

    事务由命令MULTI开始,使执行命令的客户端切换到事务状态,之后当执行EXEC、DISCARD、WATCH、MULTI以外的命令均会将命令放在一个先进先出的队列中。当向客户端发送EXEC命令时,服务器将会遍历事务队列按顺序执行其中的指令。并将返回结果在执行完毕后一起返回。

    由于事务的命令提交是一个过程,在事务命令提交过程中即在EXEC执行之前,存在事务执行的命令被修改的可能,若对事务中的某些key修改敏感,可以使用WATCH命令监听某些key,如果在事务执行之前监听的key被修改,则该事物拒绝执行。其实现原理是在Redis中保存了一个监听key的字典,key为监听的键而value是监听该键的数据库客户端。当key被修改后Redis会查看该字典该key是否被客户端监听,若正在被监听则会打开value对应的客户端的REDIS_DIRTY_CAS标识,当执行EXEC命令时会检测该标识是否打开,打开则拒绝执行该事务。

    注意,WATCH命令的作用只是当被监控键被修改后阻止之后的一个事务执行,而不能保证其他客户端是否修改,同时监听的有效期是EXEC之后,之后所有的监控被取消。

    ACID特性

    原子性,指的是数据库将食物中的多个操作当做一个整体来执行,服务器要么全部执行,要么全不执行。Redis事务具有原子性,但Redis不具有回滚机制,及时某个命令出现错误也会继续执行后面的命令。

    一致性,指的是数据库在事务执行之前是一致的,那么在执行之后也是一致的,没有非法或无效的错误。Redis事务中有三类错误,一是命令不存在或命令格式不正确的错误,在入队时可发现,事务拒绝执行。对数据库键执行错误类型的操作是第二类错误,跳过错误执行继续执行。宕机是第三类错误,但数据仍能保证一致性。

    隔离性,指数据库中多个事务并发执行也互不影响,与顺序执行结果相同。Redis是单线程顺序执行事务,因此满足隔离性。

    持久性,指当执行以后永久保存到存储中。Redis的持久性由Redis的持久化机制来决定,无持久化则没有持久性,RDB机制需要条件触发也不满足,AOF机制当appendfsync为always是满足持久性,而为everysec时会出现数据丢失也不满足,为no时也不具有持久性。

   2、Lua脚本

   与Redis事务类似,Lua脚本功能是将Redis的命令打包成一个lua脚本,在服务器端原子的执行。与Redis的事务特性相比减少了网络开销,一次提交打包的命令;原子性执行避免出现Watch的竞态条件;同时可以被复用,存储以便之后继续使用。

    创建Redis的Lua环境

    调用lua_open创建原始lua环境,载入Lua函数库包括base、table、string、math、debug、struct等库,可以修改redis返回的数据。创建全局表格包括Redis.call和Redis.pcall、Redis.log等执行redis命令的函数。

    为保证相同的脚本在不同的机器上产生相同的结果,Redis要求Lua中运行的函数是无副作用的纯函数。主要涉及随机函数和排序函数。math的随机函数有副作用,Redis使用自己的随机函数,只要种子seed相同结果相同。同时像集合这种无序的类型内容相同但是添加顺序不同也会造成结果不同。因此对于顺序敏感的命令Redis会先做一次排序。

    创建错误处理辅助函数,在脚本中的变量应使用local生命避免污染环境,如果没有加local会报错。

    Redis会将创建的Lua环境在redisServer结构体中与Redis进行关联。

    执行Lua脚本

    Redis执行命令必须有客户端状态,这里为了执行Lua脚本通过伪客户端执行。执行步骤如下图

    Redis创建lua字典用来保存lua脚本,键是检验和值是Lua脚本。RVAL和SCRIPT LOAD载入的脚本都会被保存。EVAL会将脚本定义名为一个f_+校验和的函数,之后将脚本添加到字典中,最后将EVAL传入的键名和参数作为keys数组和ARGV数组作为全局变量传入lua环境,并设置钩子函数,执行脚本会进行垃圾回收。

    EVAL script numkeys key [key ...] arg [arg ...]

    另一个执行lua脚本的命令是EVALSHA,用于之前已经在LUA环境中定义过的脚本,通过SHA1校验和调用。SCRIPT FLUSH用于清除服务器中所有lua脚本的信息,释放字典内容,重新创新新lua环境。SCRIPT EXISTS命令根据输入的检验和检查对应的脚本是否存在。SCRIPT LOAD与EVAL相同。SCRIPT KILL进行超时处理。

    EVALSHA sha1 numkeys key [key ...] arg [arg ...]

    Lua脚本的复制。

    EVAL、SCRIPT FLUSH、SCRIPT LOAD命令直接同步,在主从服务器都会被执行。EVALSHA命令根据校验和执行,但主从服务器可能保存的脚本情况不一,可能出现脚本找不到的情况。因此Redis在复制EVALSHA时会先通过reol_scriptcache_dict字典记录的所有lua脚本中查看是否有该校验和, 如果没有则转换我EVAL命令进行传播。当添加新的从服务器是,会清空reol_scriptcache_dict字典,来确保新的从服务器不会出现脚本找不到的错误。

    其他

    为了防止某个脚本执行时间过长导致Redis无法提供服务(比如陷入死循环),Redis提供了lua-time-limit参数限制脚本的最长运行时间,默认为5秒钟。当脚本运行时间超过这一限制后,Redis将开始接受其他命令但不会执行(以确保脚本的原子性,因为此时脚本并没有被终止),而是会返回“BUSY”错误。通过SHUTDOWN NOSAVE不进行持久化来操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值