MongoDB v4.0 事务,MongoError: 事务编号仅允许在副本集成员或mongos上使用。

78

我已经安装了MongoDB v4.0,因为它有一个最棒的特性,就是可以在Nodejs中使用mongodb 3.1驱动的事务(Transaction)

当我尝试使用事务会话时,遇到了以下错误:

MongoError: Transaction numbers are only allowed on a replica set member or mongos.

那是什么,我该如何去除它?

12个回答

68
MongoDB 4.0最令人兴奋的新功能无疑是事务。但是,遗憾的是,大多数安装和运行MongoDB的工具都启动了一个单独的服务器而不是副本集。如果您尝试在独立服务器上启动会话,您将收到此错误。 要使用事务,您需要一个MongoDB副本集,并且为开发启动副本集是一项复杂的过程。新的run-rs npm模块使启动副本集变得容易。只需运行run-rs即可启动副本集,run-rs甚至会为您安装正确版本的MongoDB。 除了Node.js和npm之外,run-rs没有任何外部依赖项。您不需要安装Docker、homebrew、APT、Python或者MongoDB。 使用npm的-g标志全局安装run-rs。您还可以在package.json文件的devDependencies中列出run-rs。
npm install run-rs -g

接下来,使用--version标志运行run-rs。run-rs将为您下载MongoDB v4.0.0。不要担心,它不会覆盖您现有的MongoDB安装。

run-rs -v 4.0.0 --shell

接着在你的连接字符串中使用replicaSet=rs

你可以在这里找到更多相关细节。


1
另外一个有用的工具是 bitnami/mongodb docker 镜像,它允许你使用 docker-compose 设置 replicaSet: https://github.com/bitnami/bitnami-docker-mongodb#setting-up-a-replication - mltsy
8
从文档来看,似乎很容易使用复制集运行现有(或新的)mongod实例: https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/ - Marcel Falliere
11
为什么使用事务需要 MongoDB 副本集?(在我的情况下,我有一个运行在 Docker 上用于测试的 MongoDb 实例,由于限制而无法运行事务) - Yohan
5
为什么需要复制集?我不太理解,似乎这是 MongoDB 团队的一个错误。 - java-addict301
2
@MarcelFalliere 看起来很简单,但设计得如此之复杂,以至于在Docker中实现它将会很痛苦。而且从另一个答案中看来,他们已经破坏了一个Docker解决方案。也许他们讨厌Docker,讨厌事务或者引诱我们使用副本?这毫无意义,违背了我们迄今为止制定的任何原则... - Gherman
显示剩余7条评论

33

我找到了解决方案,只需要在MongoDB配置文件中进行三行配置。

从MongoDB Atlas切换到安装在CentOS 7 VPS上的MongoDB v4.4.0后,我也遇到了这个问题。

run-rs 解决方案对我不起作用,但是我成功地在不使用任何第三方工具的情况下解决了这个问题,按照以下步骤操作:

1. 关闭 mongod

最有效的方法是使用命令 mongo 进入MongoDB shell,然后执行下列步骤:

db.shutdownServer()

您将无法使用MongoDB服务器。 对于我来说,关闭过程太长了,然后我用以下命令杀死了该进程:

systemctl stop -f mongod

如果您杀死了mongod进程,那么您可能需要运行mongod --dbpath /var/db --repair

var/db应该指向您的数据库目录。

2. 设置副本集配置。

有关副本集设置步骤,请查看/etc/mongod.conf文件, 查找replication值行,并将以下行添加如下:

replication:
   oplogSizeMB: <int>
   replSetName: <string>
   enableMajorityReadConcern: <boolean>
使用replSetName值进行下一步操作。 这些设置的示例:
   oplogSizeMB: 2000
   replSetName: rs0
   enableMajorityReadConcern: false

3. 添加连接字符串URL。

将replSetName的值添加到连接URL中&replicaSet=--YourReplicationSetName--

如果您使用了我们示例中的名称rs0,那么您应该在DB连接URL查询中添加replicaSet = rs0

4. 再次启动mongod

输入命令:systemctl start mongod

5. 访问您的副本集数据库

使用命令mongo进入MongoDB shell,输入命令rs.initiate()现在您应该在副本集数据库中。


2
我认为这是本地开发的最佳解决方案。这非常容易实现,且不需要任何第三方软件。 - Nick
我们需要在replSetName上给什么值?你能举个例子吗? - Kannan T
嗨@KannanT,我修改了我的答案并附上了一个例子。 - Oren Hahiashvili
1
如果您的Mongo服务在重新启动后无法启动,请参考https://askubuntu.com/questions/884541/cant-start-mongodb-service。 - Airy
如果使用MongoDB 5及以上版本,请在/etc/mongod.conf文件中排除enableMajorityReadConcern: true行。使用mongo --version检查您的Mongo安装版本。 - Looi
显示剩余2条评论

18
对于那些想要在Docker化的MongoDB实例上进行开发的人来说,这里有一个基于官方MongoDB Docker镜像的单文件docker-compose.yaml解决方案。
version: '3.8'

services:
  mongodb:
    image: mongo:5
    command: --replSet rs0
    ports:
      - "28017:27017"
    environment:
      MONGO_INITDB_DATABASE: attachment-api-local-dev
    healthcheck:
      # use 'mongo' instead of 'mongosh' if MongoDB version is lower than 5
      test: mongosh --eval 'db.runCommand("ping").ok' localhost:27017/test --quiet
      interval: 2s
      timeout: 3s
      retries: 5

  mongo-init:
    image: mongo:5
    restart: "no"
    depends_on:
      mongodb:
        condition: service_healthy
    command: >
      mongo --host mongodb:27017 --eval
      '
      rs.initiate( {
         _id : "rs0",
         members: [
            { _id: 0, host: "localhost:27017" }
         ]
      })
      '

一个更简单的解决方案是只需使用Bitnami MongoDB镜像
services:
  mongodb:
    image: bitnami/mongodb:5.0
    ports:
      - "27017:27017"
    environment:
      MONGODB_REPLICA_SET_MODE: primary
      ALLOW_EMPTY_PASSWORD: 'yes'

显然,MONGODB_REPLICA_SET_KEY 环境变量也是必需的。如果没有它,Mongo 进程将拒绝在容器中启动,并显示该变量是强制性的错误。我只需将其设置为类似 MONGODB_REPLICA_SET_KEY: 123456 的内容即可。 - Boaz
@Boaz在Mongo 5上从未遇到过这样的问题。你可能正在使用6+。 - Kirill
1
实际上,我使用的是4.2 :) - Boaz

13
可能的本地开发解决方案,使用docker 创建Dockerfile
FROM mongo:4.4.7
RUN echo "rs.initiate();" > /docker-entrypoint-initdb.d/replica-init.js
CMD [ "--replSet", "rs" ]

构建此Dockerfile。
docker build ./ -t mongodb:4.7-replset

运行此创建的镜像。
docker run --name mongodb-replset -p 27017:27017 -d mongodb:4.7-replset

使用此URI连接到数据库

mongodb://localhost:27017/myDB

1
建议的解决方案对于MongoDB 5.0.5无效,因为出现了“MongoServerError:command replSetInitiate requires authentication”错误(docker-entrypoint.sh没有使用用户名和密码调用mongo,也没有打印错误,只有在将mongo替换为mongosh时才能看到)。除此之外,您还需要CMD ["--replSet", "rs", "--keyFile", "/etc/mongodb.key"] - Dmitry Mugtasimov
即使有这个解决方法:RUN echo 'rs.initiate();' > /docker-entrypoint-initdb.d/replica-init.script RUN echo 'mongosh -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD" < /docker-entrypoint-initdb.d/replica-init.script' > /docker-entrypoint-initdb.d/replica-init.sh我们还是会出现“MongoServerError:This node was not started with the replSet option”错误。这只是因为docker-entrypoint.sh未设计用于运行副本。 实际上,它似乎是设计成不运行副本的。请参见源代码中的#remove“--auth”和“--replSet”以进行我们的初始启动 - Dmitry Mugtasimov
这里有相关的重大变更:https://github.com/docker-library/mongo/issues/211#issuecomment-344347819 - Dmitry Mugtasimov
如果有人感兴趣,可以在 https://github.com/thenewboston-developers/Node/pull/69 中查看 scripts/docker-entrypoint.sh.patch 和 Dockerfile 的解决方法。 - Dmitry Mugtasimov

8
我最近也遇到了相同的问题。在我的情况下,这是因为我连接到一个与本地开发环境不同版本的远程Mongo服务器。
为了快速解决此问题,我向我的连接字符串中添加了以下参数:
?retryWrites=false

5
这看起来相当可疑。你确定它不是只让所有错误变得无声而不是修复它们吗?为什么它应该工作? - Gherman

6
在Linux机器上运行MongoDB时,您可以通过编辑服务文件更新连接字符串来简单地使用复制。
/usr/lib/mongod.service   or   /lib/systemd/system/mongod.service

并使用以下内容进行更新。
ExecStart=/usr/bin/mongod --config "/etc/mongod.conf" --replSet rs0

--config "/etc/mongod.conf"指向您的MongoDB配置文件,--replSet rs0告诉它使用名称为rs0的复制。

然后重新启动。

sudo systemctl daemon-reload     //<--To reload service units
sudo systemctl restart mongod    //<--To Restart MongoDB Server

然后在终端通过mongod实例启动复制。
$ mongosh
$ rs.initiate()

1
这是唯一对我有效的方法。我有一个小问题,如果我像这样使用mongoose建立连接:mongodb://localhost:27017/database_name,我需要添加与副本集相关的内容吗?我的意思是,我已经开始进行一些测试,并且只有在URL中没有replicaSet的情况下才能正常工作,但我不知道这是否会在以后引起问题。 - Luis
今天发生了一些奇怪的事情。我再次遇到了复制集错误(这个问题我很久以前就解决了)。我不知道为什么,但那个配置被删除了。我不得不重新添加它。这与Mongo的更新有关吗? - Luis
@Luis 是的,这可能与你的Linux自动更新有很大关系。如果你没有配置更新设置,那么MongoDB很可能会被更新,以及它的相关文件。我也见过PHP和Nginx等软件出现类似情况。 - Airy
1
我明白了。谢谢,伙计。 - Luis
1
我明白了。谢谢,伙计。 - undefined
显示剩余3条评论

3
为了使用事务,您需要一个MongoDB副本集,并且在开发中启动本地副本集是一个复杂的过程。
您可以使用run-rs npm模块。零配置MongoDB运行程序。它启动一个没有非Node依赖项的副本集,甚至没有MongoDB。
或者您可以简单地在MongoDB Atlas中创建一个帐户,这将为您提供有限的资源MongoDB集群,因此您可以运行/测试应用程序。 MongoDB Atlas

1

适用于 mongo:5.0.5-focal 镜像。

Dockerfile:

FROM mongo:5.0.5-focal AS rs-mongo

# Make MongoDB a replica set to support transactions. Based on https://dev59.com/B1UK5IYBdhLWcg3wqRhn#68621185
RUN apt-get update && apt-get install patch

# How to create scripts/docker-entrypoint.sh.patch
# 1. Download the original file:
#    wget https://github.com/docker-library/mongo/raw/master/5.0/docker-entrypoint.sh
#    ( wget https://github.com/docker-library/mongo/raw/b5c0cd58cb5626fee4d963ce05ba4d9026deb265/5.0/docker-entrypoint.sh )
# 2. Make a copy of it:
#    cp docker-entrypoint.sh docker-entrypoint-patched.sh
# 3. Add required modifications to docker-entrypoint-patched.sh
# 4. Create patch:
#    diff -u docker-entrypoint.sh docker-entrypoint-patched.sh > scripts/docker-entrypoint.sh.patch
# 5. Clean up:
#    rm docker-entrypoint.sh docker-entrypoint-patched.sh
COPY scripts/docker-entrypoint.sh.patch .
RUN patch /usr/local/bin/docker-entrypoint.sh docker-entrypoint.sh.patch
RUN mkdir -p /etc/mongo-key && chown mongodb:mongodb /etc/mongo-key

CMD ["--replSet", "rs", "--keyFile", "/etc/mongo-key/mongodb.key"]

scripts/docker-entrypoint.sh.patch:

--- docker-entrypoint.sh    2022-01-04 15:35:19.594435819 +0300
+++ docker-entrypoint-patched.sh    2022-01-06 10:16:26.285394681 +0300
@@ -288,6 +288,10 @@
    fi

    if [ -n "$shouldPerformInitdb" ]; then
+
+     openssl rand -base64 756 > /etc/mongo-key/mongodb.key
+    chmod 400 /etc/mongo-key/mongodb.key
+
        mongodHackedArgs=( "$@" )
        if _parse_config "$@"; then
            _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}"
@@ -408,7 +412,14 @@
        set -- "$@" --bind_ip_all
    fi

-   unset "${!MONGO_INITDB_@}"
+  echo 'Initiating replica set'
+  "$@" --logpath "/proc/$$/fd/1" --fork
+  echo 'rs.initiate({"_id":"rs","members":[{"_id":0,"host":"127.0.0.1:27017"}]});' | mongosh -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD"
+  "$@" --logpath "/proc/$$/fd/1" --shutdown
+  echo 'Done initiating replica set'
+
+  unset "${!MONGO_INITDB_@}"
+
 fi

 rm -f "$jsonConfigFile" "$tempConfigFile"

docker-compose.yml:

version: '3.9'

services:
  mongo:
    image: rs-mongo:current
    restart: always
    env_file:
      - .env
    ports:
      - 127.0.0.1:27017:27017
    volumes:
      - mongo-db:/data/db
      - mongo-configdb:/data/configdb
      - mongo-key:/etc/mongo-key

volumes:
  mongo-db:
    driver: local
  mongo-configdb:
    driver: local
  mongo-key:
    driver: local

更新:2022年1月6日


0
我已经为这个问题奋斗了几周,并让您看到我的结论。为了能够在切片集群上使用事务,您需要在集群上运行至少 MongoDB 4.2 版本。如果该集群未经分片,则从 4.0 版本开始可用。
我一直在使用一个库,其中 mongodb NodeJS 驱动程序是其子依赖项。从版本 3.3.x 开始,该驱动程序无法兼容 MongoDB 4.0.4 的分片集群。
对我来说解决方案是将我的集群更新到 4.2 版本。
来源:https://www.bmc.com/blogs/mongodb-transactions/

0

出现错误是因为您正在使用MongoDB会话,但它未在您的系统上配置。

运行以下命令以安装run-rs:

npm install run-rs -g

运行:

run-rs -v 4.0.0 --shell

您应该看到以下输出。请耐心等待,因为MongoDB 4.0.0大约有70MB。

$ run-rs -v 4.0.0 --shell
Downloading MongoDB 4.0.0
Copied MongoDB 4.0.0 to '/home/node/lib/node_modules/run-rs/4.0.0'
Purging database...
Running '/home/node/lib/node_modules/run-rs/4.0.0/mongod'
Starting replica set...
Started replica set on "mongodb://localhost:27017,localhost:27018,localhost:27019"
Running mongo shell: /home/node/lib/node_modules/run-rs/4.0.0/mongo
rs:PRIMARY>

你现在本地运行着 MongoDB 4.0.0 副本集。运行 rs.status() 来验证副本集是否在运行。

注意:- 你的 nodejs 版本应该是 $gte v3.1.0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接