【Redis九】Redis的事务

 一、首先来说说数据库的事务

   数据库的事务是数据库的操作序列,可以是一句sql、一组sql、整个程序,它们要么一起成功,要么一起失败。

    使用 begin transact(开启事务)、commit(提交事务所有操作,数据库更新到物理磁盘上,事务结束)、rollback (事务执行中出错,事务不能继续执行,系统将事务中对数据库的已完成的所有操作全部撤销)操作事务。

     事务具有A(atomicity)C(Consistency)I(Isolation)D(Durability)原子性、一致性、隔离性、持久性。在多个事务并发执行,不同事务交叉执行和事务在运行过程中被强制停止这两种情况可能使得ACID特性遭到破坏。

     在Mysql中InnoDB引擎是支持事务ACID的存储引擎,而ISAM和MyISAM是不支持事务的。而需要注意的是lock的对象是事务,锁定数据库的对象,如表、页、行,lock的对象一般仅在事务commit或rollback后进行释放。

二、Redis的事务特性

           在redis中事务的原子性(atomicity)、一致性(consistency)、隔离性(Isolation)是有的,而关系型数据库的持久性(Durability),只有在Redis运行在某种特定持久化场景下时会就有持久性。

        在原子性上,对于Redis来说定义好的数据库操作序列就是存在事务队列里面的命令们,事务队列的命令要么全部执行,要么一个都不执行。Redis和关系型数据库的最大区别在于Redis不支持事务回滚机制(rollback),即事务队列中的某个命令在执行的时候出现了错误,这个事务也会执行下去,直到将事务队列的所有命令都执行完毕为止。

       在一致性上,数据库在执行事务之前是保持一致的,那执行事务之后也应该是一致的。对于Redis来说还是有三种地方可能会出现错误:入队错误+执行错误+服务器停机。而Redis发现入队格式错误的命令或者是不存在的命令会拒绝执行?,如果是在执行的过程中发生了错误✖,并不会中断所有的命令的执行而是执行剩余的命令,已经执行的命令不会被错误的命令影响。对数据库执行了错误类型的操作是事务执行期间最常见的错误之一,但是事务在执行过程中,出错的命令会被服务器识别然后被针对处理,从而使这些错误的数据不会对数据库产生影响,保持事务的一致性。而在服务器停机的情况下,Redis会根据不同的持久化模式,使用不同的方式来恢复数据,事务中途发生停机不会影响数据的一致性。而具体的恢复方法会在Redis的持久化的文章里面介绍

      在隔离性上,Redis是单线程来执行事务(以及事务队列中的命令)并且服务器保证咱事务执行事务期间不会对事务进行中断因此Redis是串行的方式运行即执行多个事务的时候,各个事务按照一定的顺序执行,完成一个事务后才能进行下一个,所以总是具有隔离性。

      在持久性上,事务一旦被提交,数据就被保存在永久性的硬件上,Redis在持久性上是依靠使用的不同的持久化模式决定的,而Redis有三种内存模式:无持久化模式+RDB持久化模式+AOF持久化模式,细致的持久化话介绍会单独写一篇文章。

    三、Redis的事务实现

     

关于MUlTI命令是使用REDIS_MULTI来标记的。

def MULTI():
#打开事务标识
client.flags |= REDIS_MULTI
#返回OK回复
replyOK()

  multi 开始事务之后,命令进行入队操作,之后才是执行

而每个Redis客户端都有自己的事务状态,保存在客户端状态mstate中,事务状态包含一个事务队列和一个已入队命令计数器,事务队列是一个multiCmd类型数组,数组中的每一个multiCmd结构都保存了一个已入队的相关信息,是采用FIFO先进先出的方式。

#客户端
typedef struct redisClient {
    // ...
    //事务状态
    multiState mstate;    /* MULTI/EXEC state */
    // ...
} redisClient;

#事务状态
typedef struct multiState {
    //事务队列,FIFO顺序
    multiCmd *commands;
    //已入队命令计数
    int count;
} multiState;

#事务队列
typedef struct multiCmd {
    //参数
    robj **argv;
    //参数数量
    int argc;
    //命令指针
    struct redisCommand *cmd;
} multiCmd;

 EXEC的命令执行伪代码应该如下:

def EXEC():
    #创建空白的回复队列
    reply_queue = []
    #遍历事务队列中的每个项
    #读取命令的参数,参数的个数,以及要执行的命令
    for argv, argc, cmd in client.mstate.commands:
        #执行命令,并取得命令的返回值
        reply = execute_command(cmd, argv, argc)
        #将返回值追加到回复队列末尾
        reply_queue.append(reply)
    #移除REDIS_MULTI标识,让客户端回到非事务状态
    client.flags & = ~REDIS_MULTI
    #清空客户端的事务状态,包括:
    #1)清零入队命令计数器
    #2)释放事务队列
    client.mstate.count = 0
    release_transaction_queue(client.mstate.commands)
    #将事务的执行结果返回给客户端
    send_reply_to_client(client, reply_queue)

四、 Watch的命令的相关知识点

       Redis的事务机制通过在执行命令前使用Watch来监控指定的keys,Watch命令是一个乐观锁(optimistic locking),它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复。   

#每个Redis数据库都保存着一个watched_keys字典,
#这个字典的键是某个被WATCH命令监视的数据库键,
#而字典的值则是一个链表,链表中记录了所有监视相应数据库键的客户端
typedef struct redisDb {
    // ...
    //正在被WATCH命令监视的键
    dict *watched_keys;
    // ...
} redisDb;

所有对数据库进行修改的命令,比如SET、LPUSH、SADD、ZREM、DEL、FLUSHDB等等,在执行之后都会调用multi.c/touchWatchKey函数对watched_keys字典进行检查,查看是否有客户端正在监视刚刚被命令修改过的数据库键,如果有的话,那么touchWatchKey函数会将监视被修改键的客户端的REDIS_DIRTY_CAS标识打开,表示该客户端的事务安全性已经被破坏。

def touchWatchKey(db, key):
    #如果键key存在于数据库的watched_keys字典中
    #那么说明至少有一个客户端在监视这个key
    if key in db.watched_keys:
        #遍历所有监视键key的客户端
        for client in db.watched_keys[key]:
            #打开标识
            client.flags |= REDIS_DIRTY_CAS

标识REDIS_DIRTY_CAS被打开,会引发什么后果呢?他决定了事务是否会被执行,也就是说被打开意味着客户端监控的键中,至少有一个是已经被修改的,客户端提交的事务已经不再安全,所以服务器会拒绝执行客户端提交的事务。只有标识未被打开/keys键未被监控,事务操作处于安全服务器才会提交事务。

OK,就先到这里,如果还有其他事务的知识点  将会继续在这里补充哦!???

附上 Redis的手册:事务(transaction) — Redis 命令参考

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值