【问题标题】:Auto increment in MongoDB to store sequence of Unique User ID在 MongoDB 中自动递增以存储唯一用户 ID 的序列
【发布时间】:2012-01-13 02:49:16
【问题描述】:

我正在制作一个分析系统,API 调用会提供一个唯一的用户 ID,但它不是按顺序排列的而且太稀疏。

我需要为每个唯一用户 ID 提供一个自动增量 ID,以在 bitarray/bitset 中标记分析数据点。因此,第一个用户遇到的将对应于 bitarray 的第一位,第二个用户将是 bitarray 中的第二位,依此类推。

那么有没有一种可靠且快速的方法来在 MongoDB 中生成增量唯一用户 ID?

【问题讨论】:

标签: mongodb auto-increment uniqueidentifier bitset bitarray


【解决方案1】:
// await collection.insertOne({ autoIncrementId: 1 });
const { value: { autoIncrementId } } = await collection.findOneAndUpdate(
  { autoIncrementId: { $exists: true } },
  {
    $inc: { autoIncrementId: 1 },
  },
);
return collection.insertOne({ id: autoIncrementId, ...data });

【讨论】:

  • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

这可能是另一种方法

const mongoose = require("mongoose");

const contractSchema = mongoose.Schema(
  {
    account: {
      type: mongoose.Schema.Types.ObjectId,
      required: true,
    },
    idContract: {
      type: Number,
      default: 0,
    },
  },
  { timestamps: true }
);

contractSchema.pre("save", function (next) {
  var docs = this;
  mongoose
    .model("contract", contractSchema)
    .countDocuments({ account: docs.account }, function (error, counter) {
      if (error) return next(error);
      docs.idContract = counter + 1;
      next();
    });
});

module.exports = mongoose.model("contract", contractSchema);

【讨论】:

    【解决方案3】:

    我有一个类似的问题,即我对生成唯一编号感兴趣,它可以用作标识符,但不是必须的。我想出了以下解决方案。首先初始化集合:

    fun create(mongo: MongoTemplate) {
            mongo.db.getCollection("sequence")
                    .insertOne(Document(mapOf("_id" to "globalCounter", "sequenceValue" to 0L)))
        }
    

    然后是返回唯一(和升序)数字的服务:

    @Service
    class IdCounter(val mongoTemplate: MongoTemplate) {
    
        companion object {
            const val collection = "sequence"
        }
    
        private val idField = "_id"
        private val idValue = "globalCounter"
        private val sequence = "sequenceValue"
    
        fun nextValue(): Long {
            val filter = Document(mapOf(idField to idValue))
            val update = Document("\$inc", Document(mapOf(sequence to 1)))
            val updated: Document = mongoTemplate.db.getCollection(collection).findOneAndUpdate(filter, update)!!
            return updated[sequence] as Long
        }
    }
    

    我相信 id 没有其他一些解决方案可能遭受的与并发环境相关的弱点。

    【讨论】:

    • 在获取最后一个 Id 和创建新文档之间会有一段时间,这两个操作不是原子的。在并发操作中,您不保证在其他线程执行其他操作之前会执行非原子操作。因此 2 个线程 A 和 B 可能会发生以下情况:A 获取 id -> B 获取 Id -> B 创建文档 -> A 创建文档。这将导致数据库密钥不一致。
    • 该解决方案使用原子的 findOneAndUpdate 在 DB 序列上同步。因此,如果线程切换发生在获取 ID 之后,您会得到以下信息: 1) 获取 doc A 的 ID,idA=1; 2) 获取文档 B 的 ID,idB=2; 3) 保存 B {id:2}; 4) 保存 A {id:1}。不可能引入不一致。
    • 您将拥有稍后创建的文档,其 id 低于之前创建的文档。当然不是重复错误,但是当/如果您依赖于 ID 的顺序(这主要是人们使用增量 ID 的原因)时,它可能会引入问题。除此之外,我认为这是最好的解决方案之一,只是该问题没有本地支持,因此没有干净的完全可行的解决方案。
    • 完全同意。我只是不认为这是不一致的。
    【解决方案4】:

    我发现达到我的目的的最佳方法是从字段中的最大值递增,为此,我使用了以下语法:

    maxObj = db.CollectionName.aggregate([
      {
        $group : { _id: '$item', maxValue: { $max: '$fieldName' } } 
      }
    ];
    fieldNextValue = maxObj.maxValue + 1;
    

    $fieldName 是您的字段名称,但没有 $ 符号。

    CollectionName 是您收藏的名称。

    我不使用count() 的原因是产生的价值可以满足现有价值。

    强制唯一索引的创建可以使其更安全:

    db.CollectionName.createIndex( { "fieldName": 1 }, { unique: true } )
    

    【讨论】:

    • 这不是原子的。使用唯一索引仅意味着您可能需要重试该操作,对于高流量场景可能需要多次重试。
    【解决方案5】:

    你可以,但你不应该 https://web.archive.org/web/20151009224806/http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

    mongo 中的每个对象都已经有一个 id,并且它们可以按插入顺序排序。获取用户对象的集合、对其进行迭代并将其用作递增的 ID 有什么问题?呃完全去做那种map-reduce的工作

    【讨论】:

    • 问题是并发的。 iterate 会发出重复的增量 ID。
    • 您只需要此 ID 用于分析,不需要数据存储。 IIUC,您只需要顺序 ID 作为数组的索引,因此您可以构建 bitset。您可以在不将此增量 ID 存储在数据库中的情况下实现它,并且您可以构建您的位数组而无需将数据检索到客户端
    • 每次都通过迭代获取incr ID是非常不明智的,尤其是每个数据点都有数百万用户。东一个 MAU 需要大约 30 次迭代。
    • 一开始就拥有数百万用户时,使用增量序列是不明智的。然而,数以百万计的用户也不能很好地使用位数组,不是吗?我发现很难说出你到底想要达到什么目标。使用findAndModify 并发不会有问题。另请参阅 mongodb.org/display/DOCS/Object+IDs 和 HiLo 算法:stackoverflow.com/questions/282099/whats-the-hi-lo-algorithm
    • 我只是想在 Mongo blog.getspool.com/2011/11/29/… 中存储一些 Redis 位图数据以供以后查询。
    【解决方案6】:

    应添加第一条记录

    "_id" = 1    in your db
    
    $database = "demo";
    $collections ="democollaction";
    echo getnextid($database,$collections);
    
    function getnextid($database,$collections){
    
         $m = new MongoClient();
        $db = $m->selectDB($database);
        $cursor = $collection->find()->sort(array("_id" => -1))->limit(1);
        $array = iterator_to_array($cursor);
    
        foreach($array as $value){
    
    
    
            return $value["_id"] + 1;
    
        }
     }
    

    【讨论】:

    • 这对于空集合将失败。此外,由于获取所有集合并对其进行排序,这将占用大量内存来存储大型集合。由于_id 已编入索引,因此不会占用太多处理,但仍会占用大量内存。
    【解决方案7】:

    我知道这是一个老问题,但我会为后代发布我的答案......

    这取决于您正在构建的系统和特定的业务规则。

    我正在使用 MongoDb、C#(后端 API)和 Angular(前端 Web 应用程序)构建中型到大型 CRM,发现 ObjectId 在 Angular 路由中用于选择特定实体时非常糟糕。与 API 控制器路由相同。

    上面的建议非常适合我的项目。

    db.contacts.insert({
     "id":db.contacts.find().Count()+1,
     "name":"John Doe",
     "emails":[
        "john@doe.com",
        "john.doe@business.com"
     ],
     "phone":"555111322",
     "status":"Active"
    });
    

    它非常适合我的情况,但并非所有情况都是因为如上述评论所述,如果您从集合中删除 3 条记录,则会发生冲突。

    我的业务规则规定,由于我们的内部 SLA,我们不允许删除超过我正在编写的应用程序的潜在生命周期的通信数据或客户记录,因此,我只是用枚举标记记录“状态”是“活动”或“已删除”。您可以从 UI 中删除某些内容,它会显示“联系人已被删除”,但应用程序所做的只是将联系人的状态更改为“已删除”,并且当应用程序调用存储库以获取联系人列表时,我会过滤在将数据推送到客户端应用程序之前删除已删除的记录。

    因此,db.collection.find().count() + 1 对我来说是一个完美的解决方案……

    它不适用于所有人,但如果您不删除数据,它可以正常工作。

    【讨论】:

    • mongodb 声明你必须使用函数和计数器序列,而不是你的灵魂作为 db.xxx.find.count+1 有什么特殊原因吗?也许交易处理混乱的事情?您的解决方案在 Web 服务器环境中运行良好吗?谢谢你的回答
    • 这在并发设置中不好。如果它们同时进行计数,您可以轻松获取具有相同 _id 的文档。
    • 绝对!在我的例子中,我根本不需要处理并发或分片,所以我没有任何问题使用 find().Count()+1 正如我原来的答案一样,这对每个人都不起作用情况,但它肯定在我的特定情况下有效。应用程序已经投入生产近 12 个月,我的 id 递增没有任何问题。
    • 这个解决方案不好,因为你改变了历史!一次可能是一个 id 为 3 的文档,另一次可能会再次发生。但是具有此id的文档之间没有任何关系
    • 如果你得到最大的id而不是count会更好
    【解决方案8】:

    正如所选答案所说,您可以使用 findAndModify 生成顺序 ID。

    但我强烈不同意您不应该这样做的观点。这一切都取决于您的业务需求。拥有 12 字节的 ID 可能会非常消耗资源,并且在未来会导致严重的可扩展性问题。

    我有详细回答here

    【讨论】:

    • 如果你愿意,你可以,我也不同意,因为这是 .createIndex ( { "number" : 1 }, { unique : true } ) 的 mongo 内置功能,其中一个表示增量,否则为 -1
    • @TinoCosta'ElNino' 你所说的不会创建增量字段,它只会在 number 字段上创建一个索引,并且该索引是增量的并强制唯一性,它不会在任何方式自动增加字段,甚至需要它或默认拥有它。
    • 实际上,关于答案本身,我看不出每个文档 12 个字节如何导致数据库/集合的严重缩放问题。从 12 字节 _id 更改为 4 字节(BJSON 限制),这样的集合可能会在 12 字节后出现缩放问题,一段时间后可能会溢出。此外,您保存的那些字节相当于用户输入的 8 个字符(如果集合包含用户输入,几乎总是如此),根本不值得付出努力和失去的所有好处。
    猜你喜欢
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    相关资源
    最近更新 更多