事务是数据库中处理的逻辑单元,每个事务中包括一个或多个数据库操作,既可以是读操作,也可以是写操作。
ACID 是一个“真正”事务所需要具备的一组属性集合,指的是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
原子性指的是,事务中的所有操作要么都被应用,要么都不被应用。
一致性指的是,如果数据库在执行事务之前是一致性状态,那么在事务执行之后,无论事务是否成功,数据库也应该是一致性状态。
隔离性指的是,即使数据库中有多个事务并发地执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全相同。
持久性指的是,在事务成功提交了之后,事务所变更的数据一定会保存起来,而不会因为任何故障导致数据丢失。
当数据库满足所有这些属性,并且只有成功的事务才会被处理时,它就被称为是符合 ACID 的数据库。
MongoDB 提供了两种 API 来使用事务:
start_transaction 和 commit_transaction)核心 API 不为大多数错误提供重试逻辑,它要求开发人员为操作、事务提交函数以及所需的任何重试和错误逻辑手动编写代码。
与核心 API 不同,回调 API 提供了一个单独的函数,该函数封装了大量功能,包括启动与指定逻辑会话关联的事务、执行作为回调函数提供的函数以及提交事务(或在出现错误时中止)。此函数还包含了处理提交错误的重试逻辑。
在 MongoDB 4.2 中添加回调 API 是为了简化使用事务的应用程序开发,也便于添加处理事务错误的应用程序重试逻辑。
| 核心 API | 回调 API |
|---|---|
| 需要显式调用才能启动和提交事务 | 启动事务、执行指定的操作,然后提交(或在发生错误时终止) |
不包含 TransientTransactionError 和 UnknownTransactionCommitResult 的错误处理逻辑,而是提供了为这些错误进行自定义处理的灵活性 |
自动为 TransientTransactionError 和 UnknownTransactionCommitResult 提供错误处理逻辑 |
| 要求为特定事务将显式的逻辑会话传递给 API | 要求为特定事务将显式的逻辑会话传递给 API |
在一个 Python 的例子当中,使用核心 API 的伪代码如下展示:
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, OperationFailure
# 显式开启一个事务会话
with client.start_session() as session:
while True:
try:
with session.start_transaction():
# 执行事务中的多个写操作
pass
# 提交事务
session.commit_transaction()
except (ConnectionFailure, OperationFailure) as e:
# 错误处理
if e.has_error_label("UnknownTransactionCommitResult"):
# 出现暂时性错误,则重试整个事务
continue
else:
raise
使用核心 API 需要注意错误的捕捉和处理,而回调 API 就不需要注意这些,其伪代码如下展示:
from pymongo import MongoClient
def session_callback(session):
# 执行事务中的多个写操作
pass
# 显式开启一个事务会话
with client.start_session() as session:
session.with_transaction(session_callback)
在使用事务时,有几个重要的参数需要注意。可以对它们进行调整,以确保应用程序能够最佳地使用事务。
在 MongoDB 事务中有两类主要的限制:
事务的默认最大运行时间是 1 分钟。
可以通过在 mongod 实例级别上修改 transactionLifetimeLimitSeconds 的限制来增加。对于分片集群,必须在所有分片副本集成员上设置该参数。超过此时间后,事务将被视为已过期,并由定期运行的清理进程中止。清理进程每 60 秒或每 transactionLifetimeLimitSeconds/2 运行一次,以较小的值为准。
要显式设置事务的时间限制,建议在提交事务时指定 maxTimeMS 参数。实际上会使用 maxTimeMS 和 transactionLifetimeLimitSeconds 中的更小值。
事务等待获取其操作所需锁的默认最大时间是 5 毫秒。可以通过修改由 maxTransactionLockRequestTimeoutMillis 参数控制的限制来增加。如果事务在此期间无法获得锁,则该事务会被中止。
当 maxTransactionLockRequestTimeoutMillis 设置为 0 时,意味着如果事务无法立即获得所需的所有锁,则该事务会被中止。设置为 -1 将使用由 maxTimeMS 参数所指定的特定于操作的超时时间。
MongoDB 会创建出与事务中写操作数量相同的 oplog 条目。
但是,每个 oplog 条目必须在 16MB 的 BSON 文档大小限制之内。
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi
我已经按照https://github.com/wayneeseguin/rvm#installation上的说明通过RVM安装了Ruby.有关信息,我有所有文件(readline-5.2.tar.gz、readline-6.2.tar.gz、ruby-1.9.3-p327.tar.bz2、rubygems-1.8.24.tgz、wayneeseguin-rvm-stable.tgz和yaml-0.1.4.tar.gz)在~/.rvm/archives目录中,我不想在任何目录中重新下载它们方式。当我这样做时:sudo/usr/bin/apt-getinstallbuild-essent
我的Ruby-on-Rails项目中有以下文件结构,用于规范:/spec/msd/serviceservice_spec.rb/support/my_modulerequests_stubs.rb我的request_stubs.rb有:moduleMyModule::RequestsStubsmodule_functiondeflist_clientsurl="dummysite.com/clients"stub_request(:get,url).to_return(status:200,body:"clientsbody")endend在我的service_spec.rb我有:re
保存成功后可以回滚吗?让我有一个带有属性名称、电子邮件等的用户模型。例如u=User.newu.name="test_name"u.email="test@email.com"u.save现在记录将成功保存在数据库中,之后我想回滚我的事务(不是销毁或删除)。有什么想法吗? 最佳答案 您可以通过交易来做到这一点,请参阅http://markdaggett.com/blog/2011/12/01/transactions-in-rails/例子:User.transactiondoUser.create(:username=>'Nemu
Ruby是否支持(找不到更好的词)非转义(逐字)字符串?就像在C#中一样:@"c:\ProgramFiles\"...或者在Tcl中:{c:\ProgramFiles\} 最佳答案 是的,您需要在字符串前加上%前缀,然后是描述其类型的单个字符。你想要的是%q{c:\programfiles\}。镐书很好地涵盖了这一点here,部分是通用分隔输入。 关于ruby-Ruby是否支持逐字字符串?,我们在StackOverflow上找到一个类似的问题: https:/
我正在编写一个Rubygem,在我的代码中使用{key:'value'}哈希语法。我的测试都在1.9.x中通过,但我(可以理解)在1.8.7中得到syntaxerror,unexpected':',expecting')'。是否有支持1.8.x的最佳实践?我是否需要使用我们的老friend=>重写代码,还是有更好的策略? 最佳答案 我认为你运气不好,如果你想支持1.8,那么你必须使用=>。像往常一样,我会提到在1.9的某些情况下您必须使用=>:如果键不是一个符号。请记住,任何对象(符号、字符串、类、float……)都可以是Ruby哈
在Rails中,什么是集成更新模型某些元素的UDP监听过程的最佳方式(特别是它将向其中一个表添加行)。简单的答案似乎是在同一个进程中使用UDP套接字对象启动一个线程,但我什至不清楚我应该在哪里做适合Rails方式的事情。有没有一种巧妙的方法来开始收听UDP?具体来说,我希望能够编写一个UDPController并在每个数据报消息上调用一个特定的方法。理想情况下,我希望避免在UDP上使用HTTP(因为它会浪费一些在这种情况下非常宝贵的空间),但我完全控制消息格式,因此我可以为Rails提供它需要的任何信息。 最佳答案 Rails是一个
我目前正在上一门数据库类(class),其中一个实验室问题让我困惑于如何实现上述内容,事实上,如果可能的话。我试过搜索docs但是定义的交易方式比较模糊。这是我第一次尝试在没有Rails的情况下进行任何数据库操作,所以我有点迷茫。我已经成功地创建了一个到我的postgresql数据库的连接并且可以执行语句,我需要做的最后一件事是根据一个简单的条件回滚一个事务。请允许我向您展示代码:require'pg'@conn=PG::Connection.open(:dbname=>'db_15_11_labs')@conn.prepare('insert','INSERTINTOhouse(ho