mongoDB原子操作事务

news2024/11/30 6:35:45

原子操作

  1. 原子操作(atomic operation)指的是由多步操作组成的一个操作。如果该操作不能原子地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。
  2. 不可中断的一个或者一系列操作, 也就是不会被线程调度机制打断的操作, 运行期间不会有任何的上下文切换(context switch).

事务

  1. 事务(Transaction)是访问并可能更新数据库中各项数据项的一个程序执行单元(unit)。 事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
  2. 事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。
  3. 事务结束有两种,事务中的步骤全部成功执行时,提交事务。如果其中一个失败,那么将会发生回滚操作,并且撤销之前的所有操作。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
  4. 事务是恢复和并发控制的基本单位。
  5. 事务具有四个特征:原子性、一致性、隔离性和持久性。这四个特征通常称为ACID。

官方文档:

事务(GO)
在这里插入图片描述

支持版本

  1. MongoDB从 3.0版本引入WiredTiger存储引擎之后开始支持事务。
  2. MongoDB 3.6之前的版本只能支持单文档的事务。
  3. MongoDB 4.0版本开始支持复制集部署模式下的事务。
  4. MongoDB 4.2版本开始支持分片集群中的事务。

要获取事务支持则需要安装对应的或高版本的mongodb和驱动(pymongo(python),mongo-driver(go))

内置的一些原子操作

在这里插入图片描述
在这里插入图片描述

事务支持

go

func (s *sessionImpl) WithTransaction(ctx context.Context, fn func(sessCtx SessionContext) (interface{}, error),
	opts ...*options.TransactionOptions) (interface{}, error) {
	// 超时时间为:120 * time.Second
	timeout := time.NewTimer(withTransactionTimeout)
	defer timeout.Stop()
	var err error
	for {
		// 开启事务
		err = s.StartTransaction(opts...)
		if err != nil {
			return nil, err
		}
		// 回调函数(会话上下文)
		res, err := fn(NewSessionContext(ctx, s))
		// 执行失败终止事务
		if err != nil {
			if s.clientSession.TransactionRunning() {
				// 终止事务
				_ = s.AbortTransaction(internal.NewBackgroundContext(ctx))
			}
			select {
			case <-timeout.C:
				return nil, err
			default:
			}
			if errorHasLabel(err, driver.TransientTransactionError) {
				continue
			}
			return res, err
		}

		// 判断在回调函数里面是否直接通过会话上下文终止事务了
		err = s.clientSession.CheckAbortTransaction()
		if err != nil {
			return res, nil
		}
		
		if ctx.Err() != nil {
			_ = s.AbortTransaction(internal.NewBackgroundContext(ctx))
			return nil, ctx.Err()
		}
	
	// CommitLoop提交事务循环,还在上面那个for里面
	CommitLoop:
		for {
			// 提交
			err = s.CommitTransaction(ctx)
			if err == nil {
				// 返回成功结果 res, err := fn(NewSessionContext(ctx, s))
				return res, nil
			}
			// 超时判断
			select {
			case <-timeout.C:
				return res, err
			default:
			}
			
			if cerr, ok := err.(CommandError); ok {
				// UnknownTransactionCommitResult = "UnknownTransactionCommitResult"
				if cerr.HasErrorLabel(driver.UnknownTransactionCommitResult) && !cerr.IsMaxTimeMSExpiredError() {
					continue
				}
				// errorHasLabel:包含规定的错误信息,返回true
				// TransientTransactionError = "TransientTransactionError"
				if cerr.HasErrorLabel(driver.TransientTransactionError) {
					break CommitLoop
				}
			}
			return res, err
		}
	}
}

python

def with_transaction(
        self,
        callback: Callable[["ClientSession"], _T],
        read_concern: Optional[ReadConcern] = None,
        write_concern: Optional[WriteConcern] = None,
        read_preference: Optional[_ServerMode] = None,
        max_commit_time_ms: Optional[int] = None,
    ) -> _T:
    start_time = time.monotonic()
        while True:
            self.start_transaction(read_concern, write_concern, read_preference, max_commit_time_ms)
            try:
                ret = callback(self)
            except Exception as exc:
                if self.in_transaction:
                    self.abort_transaction()
                if (
                    isinstance(exc, PyMongoError)
                    and exc.has_error_label("TransientTransactionError")
                    and _within_time_limit(start_time)
                ):
                    # Retry the entire transaction.
                    continue
                raise

            if not self.in_transaction:
                # Assume callback intentionally ended the transaction.
                return ret

            while True:
                try:
                    self.commit_transaction()
                except PyMongoError as exc:
                    if (
                        exc.has_error_label("UnknownTransactionCommitResult")
                        and _within_time_limit(start_time)
                        and not _max_time_expired_error(exc)
                    ):
                        # Retry the commit.
                        continue

                    if exc.has_error_label("TransientTransactionError") and _within_time_limit(
                        start_time
                    ):
                        # Retry the entire transaction.
                        break
                    raise

                # Commit succeeded.
                return ret

跑通代码

使用更加简便的with方法

go

func main() {
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
	client, err := mongo.Connect(context.TODO(), clientOptions)
	collection:=client.Database().Collection()
	if err != nil {
		log.Fatal(err)
	}
	wc := writeconcern.New(writeconcern.WMajority())
	txnOptions := options.Transaction().SetWriteConcern(wc)
	session,_:=client.StartSession()
	result,_:=session.WithTransaction(context.TODO(), func(sessCtx mongo.SessionContext) (interface{}, error) {
		result,err:=collection.Find(context.TODO(),bson.M{})
		return result,err
	},txnOptions)

	cur,ok:=result.(*mongo.Cursor)
	if ok{
		fmt.Println("断言成功")
		for cur.Next(context.TODO()){
			m:=make(map[string]interface{})
			cur.Decode(m)
			fmt.Println(m)
		}
	}else{
		fmt.Println("断言失败")
	}
}

python

import pymongo

client = pymongo.MongoClient()
collection = client.get_database().get_collection()

def call_back(ctx: pymongo.ContextManager):
    print(collection.find_one())

with client.start_session() as session:
    session.with_transaction(call_back)

其余补充

高版本的mongoApi废弃了count()方法,如果需要统计数量可以使用:

  1. python的collection.estimated_document_count(),collection.count_documents(filter)
  2. go的collection.CountDocuments(ctx,filter),collection.EstimatedDocumentCount(ctx)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152405.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3——第三章(生命周期钩子)

一、setup() Vue3在组合式 API中去掉了在Vue3中的beforeCreate、created两个生命周期&#xff0c;使用setup()来顶替这两个生命周期。 二、onBeforeMount() 注册一个钩子&#xff0c;在组件被挂载之前被调用。当这个钩子被调用时&#xff0c;组件已经完成了其响应式状态的设…

国产电源芯片DP4054 软硬件兼容TP4054 规格书资料

DP4054 是一款完整的采用恒定电流/恒定电压单 节锂离子电池充电管理芯片。其SOT小封装和较少的外部元件数目使其成为便携式应用的理想器件&#xff0c;DP4054 可以适合USB 电源和适配器电源工作。跟进口的TP4054完全兼容&#xff0c;软硬件无需更改直接替换。 管脚配置 功能框…

(十六)一篇文章学会Java的常用API

目录 前言: 一、Object:toStringequals 二、StringBuilder 三、Math 四、System 五、BigDecimal 前言: API的主要目的是提供应用程序与开发人员以访问一组例程的能力&#xff0c;而又无需访问源码&#xff0c;或理解内部工作机制的细节。提供API所定义的功能的软件称作此API的实…

The Sandbox Game Maker 全新版本即将推出,一览可用于娱乐、社交和音乐会的新功能!

0.8 版本的新功能将包括多人游戏功能、光影和视觉效果升级&#xff0c;以及音频和视频流&#xff0c;满足社区的更新需求。 简要概括 0.8 版本包括了 The Sandbox 社区的主要升级需求&#xff0c;具体有&#xff1a; 全新多人游戏功能 全新光影和视觉效果 视频和音频流 支援…

OKhttp-基本工作流程责任链模式原理

OKhttp工作的大致流程 整体流程 &#xff08;1&#xff09;、当我们通过OkhttpClient创立一个okHttpClient 、Request 、Call&#xff0c;并发起同步或者异步请求时&#xff1b; &#xff08;2&#xff09;、okhttp会通过Dispatcher对我们所有的Call&#xff08;RealCall实现…

微服务的版本号要怎么设计?

今天我们来聊一下微服务项目中的版本号要怎么设计。 小伙伴们平时看到的项目版本号&#xff0c;基本上都是分为了三部分 X.Y.Z&#xff0c;版本升级的时候版本号都会变&#xff0c;那么版本号怎么变&#xff0c;这可不是拍脑门决定的&#xff0c;今天我们就一起来探讨一下这个…

Live800:客户服务的三重境界,你做到了吗?

毋庸置疑&#xff0c;赢得客户的青睐是维系自身经济长青的基础。想要客户满意&#xff0c;得到最佳的客户评价&#xff0c;企业就需要为客户提供超出他们期望的服务。有人将客户服务分为三重境界:第一重境界&#xff0c;把分内的服务做精&#xff1b;第二重境界&#xff0c;把额…

libtorch c++复现cycle gan网络

目录 1. 原论文论文&#xff1a;https://arxiv.org/abs/1703.10593 2. 代码 2.1 下采样 2.2 残差块 2.3 上采样模块 2.4 生成器代码 3. 判别器 3.1 判别器组件 3. 2 判别器 4. 训练 4.1 输入数据 4.2 生成器loss函数结构图 4.3 判别器loss结构图 1. 原论文 论文&…

【java查漏补缺】网络编程

网络编程实际上就是通过套接字进行连接后进行通信&#xff0c;本质还是程序进行IO操作。 所谓套接字&#xff0c;实际上就是IP地址加上端口号的组合&#xff0c;通过套接字&#xff0c;可以连接到网络中某一台计算机的某一个进程。 下面就是客户端和服务器的简单例子&#xf…

vue3-ElmentPlus封装通用表格-含单元格操作-多选-分页器

Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 **&#x1f431;‍&#x1f409;&#x1f431;‍&#x1f409;恭喜你&#xff0c;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c;求收藏&#xff0c;求评论&#xff0c;求一个大大…

【测试】测试分类

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录测试分类&#xff08;大框架&#xff09;一、按照测试对象划分一&#xff09;可靠性测试二&#xff09;容错性测试三&#xff09;安装卸载测试&#xff08;万能公式中可以加上&#xff09;四&#xff09;内存泄漏测试…

合芯科技携手新享科技联合打造国产化项目管理系统解决方案

北京新享科技有限公司 北京新享科技有限公司&#xff0c;是上海合见工业软件集团控股的子公司。上海合见工业软件集团有限公司是自主创新的高性能工业软件及解决方案提供商&#xff0c;以EDA&#xff08;电子设计自动化&#xff0c;Electronic Design Automation&#xff09;领…

【实际开发03】- dto + vo - 先处理 dto , 后处理 vo ( 通常少一注解 )

目录 0. 建议 : 多用组合 , 少用继承 1. EqualsAndHashCode(callSuper true) - 解决允许调用父类 2. 序列化 ID : private static final long serialVersionUID 1L; 1. serialVersionUID 作用 : 序列化时为了保持版本的兼容性 3. 数据概览 ( 统计 ) : XxxxProfileVO 1.…

CAD常用命令:对象选择过滤器(FILTER)

CAD软件中为了方便绘图&#xff0c;有效地提升绘图效率&#xff0c;提供了很多CAD命令快捷键&#xff0c;而CAD对象选择过滤器作为CAD常见命令之一&#xff0c;在日常的CAD绘图过程中经常能用到&#xff0c;你知道CAD对象选择过滤器怎么用吗&#xff1f;本文小编就来给大家分享…

Qt解析Json数据

目录前言1.下载 jsoncpp 源码2.编译3.JSON数据读写示例4.jsoncpp核心类详解前言 本文主要介绍了使用Qt框架编程时如何解析JSON数据的一种方法。JSON是英文JavaScript Object Notation 的缩写&#xff0c;它是一种轻量级的数据交换格式&#xff0c;具有方便阅读和编写的优点&am…

Jenkins 项目的 gpg: signing failed: Bad passphrase 错误

因为我们项目需要使用 Jenkins 对文件进行签名。但是我们遇到了gpg: signing failed: Bad passphrase错误。原因和解决通常这个问题的原因是 Key 已经配置成功并且已经被命令行找到了。主要原因是你的秘钥密码配置的问题。这个配置有 2 个地方&#xff0c;第一个地方是项目的 P…

2022年度牛奶乳品行业数据:十大热门品牌销量排行榜

当前&#xff0c;随着经济的发展及人民生活水平的提高&#xff0c;牛奶乳品已经日趋成为人们在日常饮食中不可缺少的食物之一&#xff0c;市面上的产品种类也越来越多。并且&#xff0c;随着人们消费习惯的转变&#xff0c;牛奶乳品的消费场景也日益多元化。未来&#xff0c;预…

jdk1.8之函数式接口

l[TOC] 函数式接口概述 jdk1.8 引入了一个核心概念&#xff1a;函数式接口&#xff08;Functional Interface&#xff09;。如果一个接口有且只有一个未实现的方法&#xff0c;那这个接口就称为函数式接口。并且引入了一个新的注解&#xff1a;FunctionalInterface &#xff0…

一、Gradle入门

文章目录一、Gradle入门1.1 Gradle 简介1.2 常见的项目构建工具1.3 Gradle 安装1.3.1 Gradle 安装说明1.3.2 安装 JDK1.3.3 下载并解压到指定目录1.3.4 配置环境变量1.3.5 检测是否安装成功1.4 Gradle 项目目录结构1.5 Gradle 创建第一个项目1.5.1 Gradle 中的常用命令1.5.2 修…

【MySQL进阶教程】视图/存储过程/触发器

前言 本文为 【MySQL进阶教程】视图/存储过程/触发器 相关知识&#xff0c;下边将对视图&#xff0c;存储过程&#xff0c;存储函数&#xff0c;触发器等进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &#x1f449;Java全栈学习路线可参考&#xff1a;【…