Dark Dwarf Blog background

MongoDB 事务配置

MongoDB 事务配置

1. MongoDB 事务

MongoDB 的事务需要在副本集上完成。MongoDB 的事务操作依赖操作日志 oplog。这个日志包含了下面的信息:

  1. 原子性的记录:事务中的所有写操作会被打包成一个原子性的条目写入 oplog。这意味着,这些操作要么全部被记录,要么一个也不被记录。这保证了事务的原子性。
  2. 持久性和恢复的保证:oplog 是一个持久化的、有上限的集合。一旦事务被写入 oplog,就可以认为它已经“安全”了。即使数据库在将数据写入实际的数据文件之前崩溃,只要 oplog 中有记录,MongoDB 在恢复后就可以根据 oplog 来完成或重做这个事务,确保数据不会丢失或损坏。

而一个标准的单体 MongoDB 实例,默认情况下不会创建 oplog,因为 oplog 本身是为数据复制服务的。因此如果我们需要在 MongoDB 中使用事务操作、又不需要分片集群这种复杂架构的话,就需要自己给单体 MongoDB 创建对应的副本集,让 MongoDB 认为自己需要进行数据复制、从而创建 oplog。

2. 副本集创建

下面我们以在 docker-compose.yml 中配置 MongoDB 为例。

a.a. key-file 生成

在启用了副本集后,如果在 docker-compose 中配置了 username 和 password,需要提供 key-file 来完成认证。我们用 openssl 在容器内生成一个密钥文件然后将密钥文件交给 MongoDB 用户:

openssl rand -base64 756 > /data/mongodb-keyfile
chmod 400 /data/mongodb-keyfile
chown 999:999 /data/mongodb-keyfile
exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --keyFile /data/mongodb-keyfile

b.b. 副本集生成

rs.initiate({
  _id: 'rs0',
  members: [{ _id: 0, host: 'localhost:27017' }]
});

完整的 docker-compose.yml 脚本如下,可以直接拿去用:

services:
  mongo:
    image: mongo:7
    container_name: gdrive-mongo
    restart: unless-stopped
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME:-admin}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD:-password123}
    command: |
      bash -c '
        openssl rand -base64 756 > /data/mongodb-keyfile
        chmod 400 /data/mongodb-keyfile
        chown 999:999 /data/mongodb-keyfile
        exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --keyFile /data/mongodb-keyfile
      '
    volumes:
      - mongo-data:/data/db
    networks:
      - gdrive-network
    healthcheck:
      test: |
        mongosh --quiet -u $${MONGO_INITDB_ROOT_USERNAME:-admin} -p $${MONGO_INITDB_ROOT_PASSWORD:-password123} --authenticationDatabase admin --eval "
        try {
          rs.status();
        } catch (err) {
          rs.initiate({
            _id: 'rs0',
            members: [{ _id: 0, host: 'localhost:27017' }]
          });
        }
        "
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 15s