`
DiaoCow
  • 浏览: 244858 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

《Redis源码学习笔记》事务

阅读更多
《Redis源码学习笔记》文章列表

Redis中的事务,提供了一种“将多个命令打包并且一次执行”的方式;

当用户输入MULTI命令时,就打开了客户端REDIS_MULTI选项,客户端从“非事务状态”切换到“事务状态”



之后客户端执行的所有命令都不会被Redis立即执行,而是放到客户端的“命令队列”里去(服务器返回QUEUED字样,表示命令已经入队),当客户端发出EXEC命令(表示客户端需要执行事务),这时候Redis从客户端的“命令队列”里依次取出命令执行,eg:
redis 127.0.0.1:6379> multi  # 客户端进入事务状态
OK
redis 127.0.0.1:6379> set name diaocow	
QUEUED					     # 命令已经入队
redis 127.0.0.1:6379> set age  25
QUEUED
redis 127.0.0.1:6379> get name
QUEUED
redis 127.0.0.1:6379> get country
QUEUED
redis 127.0.0.1:6379> exec	# 执行事务
1) OK
2) OK
3) "diaocow"
4) (nil)

用一幅图来总结客户端的命令执行流程就是:



刚才例子中所对应的命令队列:


关于命令队列的更多细节,请看:multi.c/queueMultiCommand函数

前面说到,当客户端处于“事务”状态,所以命令都不会立即执行,而是被放到“命令队列”缓存起来,其实不准确!当客户端处于“事务”状态下,如果遇到下面几个命令,依然会立即执行:

MULTIRedis返回错误,不允许事务嵌套
DISCARD丢弃当前事务(该命令只能在事务状态下使用,否则Redis返回错误)
EXEC执行事务, 把“命令队列”里的命令依次取出、执行,并且把执行结果放入到一个“回答队列”中,当所有命令执行完后,Redis把这个“回答队列”发送给客户端(该命令只能在事务状态下使用,否则Redis返回错误)
WATCH监视某些键,如果在事务执行期间,有一个“监视”的键被修改,那么事务执行失败(该命令必须在MULTI命令之前执行,否则Redis返回错误)

命令实现伪代码:
def multiCommand(client):
    # 不允许事务嵌套
	if client.flag & REDIS_MULTI:
		return "error"
	client.flag &= REDIS_MULTI

def discardCommand(client):
	# discard命令只能在事务状态下使用
	if not (client.flags & REDIS_MULTI):
		return "error"
	# 重置客户端的事务状态(1.释放命令队列;2.重置客户端为非事务状态;3.取消之前监视的所有键)
    resetClientMultiState(client)

def execCommand(client):
	# exec命令只能在事务状态下使用
	if not (client.flags & REDIS_MULTI):
		return "error"
	# 若某些监视的键被修改或者命令入队错误(命令不存在?参数错误?),则事务执行失败
    if client.flag & (REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC):
        resetClientMultiState()
        return "error" 
    # 依次执行命令队列里的命令
    for cmd, argc, argv in client.multiState.commands:
        result.add(call(cmd, argc, argv))
    resetClientMultiState(client)
    return result

DISCARD和EXEC命令比较容易理解,下面我们重点看下WATCH命令的作用以及实现原理:

WATCH命令用来在事务开始之前,监视任意数量的键,当调用EXEC命令执行事务时,如果其中任意一个键被其他客户端修改,那么整个事务将不再执行,直接返回失败,eg:



那这一切Redis是怎么实现的呢?

原理:RedisDb中维护了一个watched_keys字典,字典的键就是这个数据库中被“监视”的键,键值就是所有“监视”该键的客户端列表(list类型)


(上图只是字典的简化画法,若严格按照字典结构画不仅较为麻烦并且不利于阐述主要思想;关于redis字典详情,请参看 字典章节)

当一个客户端执行watch命令时:
a. Redis会把它加入到被“监视”键的客户端列表中;
b. 同时,客户端自己也会维护一个watched_keys列表,用来保存自己所有监视的键(这个属性有什么用?是不是和RedisDb中的watched_keys属性有重叠?我们稍后会说)

当任何一个会触发键内容变更的命令执行后(譬如set),touchWatchedKey函数会被调用:它检查数据库的watched_keys字典,看该键是否正在被“监视”,如果有,那么该键所关联的所有客户端列表都将打开REDIS_DIRTY_CAS选项(事务被破坏),然后当客户端执行EXEC命令时,如果发现REDIS_DIRTY_CAS选项打开,则事务执行失败,整个过程用伪代码表示就是:

def touchWatchedKey(redisDb, key):
	# 获取监视该键的客户端列表
    client_list = redisDb.watched_keys.get(key)
    if client_list is None: return
    # 打开客户端的REDIS_DIRTY_CAS选项(告诉它们事务已经被破坏)
    for client in client_list:
        client.flag &= REDIS_DIRTY_CAS 

更多细节请看:multi.c/touchWatchedKey函数

刚才我们还提到过,客户端自己也维护了一个watched_keys属性——用来保存自己所监视的键,那么这个属性有什么用呢? 我自己觉得是出于效率考虑的:

1. 防止监视相同的键,当Redis发现该客户端已经监视了某个键,则跳过该键,不做任何处理;
2. 当某个客户端(譬如A)执行完事务(或成功或失败),客户端需要清除自己之前所有监视的键,如果客户端自己没有维护自己监视了哪些键,那么Redis就必须遍历整个RedisDb.watched_keys字典,然后在每一个客户端列表中查找A并删除,效率非常低下,但若客户端自己维护了所监视键的列表,那么就不在需要遍历整个字典做清除,eg伪代码:
def unwatchAllKeys(client):
	# 若客户端没有监视任何键,则立即返回
    if len(client.watched_keys) == 0 : return
    # 遍历客户端监视的键
    for wk in client.watched_keys.copy():
    	# 取出被监视键关联的客户端列表
        client_list = redisDb.watched_keys.get(wk)
        # 删除客户端
        client_list.del(client)
        client.watched_keys.del(wk)

更多细节请看:multi.c/unwatchAllKeys函数

总结:
1. 熟悉事务相关命令:multi discard exec watch
2. 了解事务执行原理
3. 了解watch命令作用以及实现原理



  • 大小: 3.9 KB
  • 大小: 2.4 KB
  • 大小: 6.4 KB
  • 大小: 3.8 KB
  • 大小: 6.7 KB
分享到:
评论

相关推荐

    狂神redis源码笔记.rar

    在“狂神redis源码笔记”中,我们可以期待学习到Redis的源码分析、内部机制以及如何通过Java进行高效操作。源码分析是深入理解Redis工作原理的关键,有助于开发者优化使用方式或进行定制化开发。 Redis的核心知识点...

    7.Redis学习笔记.pdf

    在安装Redis时,通常需要从官方下载Redis的源码包,并通过解压缩(例如使用tar命令)进行安装。安装过程中,需要编译源码并执行安装。在这个过程中,可能需要先安装编译所需的工具,如gcc和tcl。安装完成后,需要...

    redis入门学习笔记

    Redis是一个开源的、基于内存的数据...总的来说,Redis入门学习笔记详细介绍了Redis的特性、安装、数据类型、使用场景、备份与恢复、性能测试、Python客户端使用等基础知识和技巧,非常适合新手作为入门学习的资料。

    redis学习笔记.pdf

    Redis学习笔记 Redis是一个开源的基于键值对(Key-Value)NoSQL数据库,使用ANSI C语言编写、支持网络、基于内存但支持持久化。性能优秀,并提供多种语言的API。Redis可以被称为KV数据库,键值对数据库,内部存储...

    Redis全套学习笔记

    以下是关于Redis学习笔记的详细内容: 1. **Redis概述和安装** Redis是一个开源的内存数据结构存储系统,可以作为数据库、缓存和消息代理使用。它支持多种数据结构,且性能极高。Redis的安装通常涉及下载源码、...

    redis学习笔记

    ### Redis 学习笔记知识点概览 #### 一、Redis 简介及特性 - **Redis**(Remote Dictionary Server)是一种开源的、基于内存的数据结构存储系统,它支持多种数据结构,如字符串(strings)、散列(hashes)、列表...

    Redis的学习笔记

    Redis 的环境搭建相对简单,可以从官方网站或源码仓库下载最新稳定版本的源码包,例如 Redis 2.0.4。解压后通过 `make` 命令编译,编译完成后会有 `redis-server` 和 `redis-cli` 两个可执行文件。直接运行 `./redis...

    一份精简的redis入门学习笔记

    首先,从官方下载地址获取最新稳定版的 Redis 源码包,如 `redis-2.0.4.tar.gz`。解压后,进入目录并执行 `make` 命令进行编译。编译完成后,有两个主要的可执行文件:`redis-server`(服务端)和 `redis-cli`...

    Redis学习笔记整理

    ### Redis学习笔记整理 #### 一、Redis环境搭建 ##### 1.1 简介 Redis是一款开源的键值(Key-Value)型数据库系统,因其高性能和丰富的数据结构而广受欢迎。它不仅可以作为数据库使用,还可以作为一种数据结构服务器...

    2022年redis学习笔记

    这份2022年的Redis学习笔记涵盖了Redis的基础概念、核心特性、使用场景以及最佳实践。 一、Redis简介 Redis是一个开源(BSD许可)的,非关系型、内存中的数据结构存储系统,可以用作数据库、缓存和消息中间件。它...

    燕十八redis视频教程笔记资料

    首先,从官方网站下载最新稳定版的Redis源码或二进制包。在Linux环境下,可以使用编译源码的方式进行安装,涉及到`make`和`make install`等命令。对于Windows用户,可直接下载预编译的二进制包,解压后配置环境变量...

    redis学习笔记,redis详解,Java源码.zip

    Redis是一款高性能的键值对数据库,它以内存存储为主,数据持久...通过阅读本学习笔记和源码分析,你可以深入了解Redis的工作原理,掌握如何在实际项目中高效地使用Redis。不断实践和探索,你将成为Redis的熟练驾驭者。

    Redis.zip学习笔记

    在"Redis.zip学习笔记"中,我们可以预期包含以下几个主要的知识点: 1. Redis简介:Redis是一个开源(BSD许可),内存中的数据结构存储系统,它可以用作数据库、缓存和消息代理。它支持多种数据结构,如字符串、...

    redis学习笔记详细整理手册

    本手册详细整理了Redis的学习过程,包括基础概念、安装部署和实战应用。 一、Redis基础知识 Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它的数据模型包括字符...

    redis初级入门笔记

    Redis 是一个高性能的键值对数据库,常被用于数据缓存、消息队列以及数据库功能。...同时,不断学习和实践 Redis 的高级特性,如事务、持久化策略、集群等,将有助于提升你的数据库管理和应用开发能力。

    Redis笔记.md

    1. **下载安装包**:通常是从Redis官方网站获取最新的源码包。 2. **解压**:使用命令行工具进行解压。 3. **编译安装**:利用`make`和`make install`完成编译安装过程。 4. **环境配置**:通过`yum`安装必要的依赖...

    Redis全面深入学习笔记(强烈推荐).docx

    在深入学习Redis时,还需要掌握更多高级特性,如持久化(RDB和AOF)、事务、发布订阅、主从复制、哨兵系统和Cluster集群等。理解这些特性将有助于在实际应用中更好地利用Redis的优势。此外,了解Redis的数据结构(如...

Global site tag (gtag.js) - Google Analytics