1

这可能是一个不同的坏主意,或者我们必须解决数据库并发问题的可能解决方案。

我们有一个方法被调用来更新 mongo 记录。我们看到了一些并发问题 - 进程 A 读取记录,进程 B 读取记录,进程 A 修改并保存记录,进程 B 修改并保存记录。因为 B 在 A 之后读取,在 A 写入之前,它不知道 A 所做的更改,因此我们丢失了来自 A 的数据。

我想知道我们是否不能使用数据库信号量,基本上是集合上的一个字段,它是一个布尔值。如果我们在方法开始时读取记录,并且该字段为真,则它正在被编辑。此时,使用 process.nexttick() 使用相同的数据重新调用该方法。否则,设置信号量,然后继续。

读取和保存之间仍有一些时间,但它应该/可能比我们现在正在做的更快。

变成这样。有什么想法,有人做过这样的事吗?它甚至会起作用吗?

function remove_source(service_id,session, next)
{
    var User = Mongoose.model("User");

    /* get the user, based on the session user id */
    User.findById(session.me,function(err,user_info)
    {
         if (user_info.semaphore === true)
         {
               process.nextTick(remove_source(service_id,session,next));
         }
         else
         {
               user_info.semaphore = true;
               user_info.save(function(err,user_new)
               {
                    if (err) next(err,user_new);
                    else continue_on(null,user_new);
               });
         }

         function continue_on(user_new)
         {
             etc.......
         }

编辑:新代码:

该函数现在如下所示。我正在对数组进行单独更新。这当然意味着,如果在第一个和第二个事务之间的事务失败,我现在有可能使数据不同步。我在想我可以简单地重新保存我在进入函数时检索到的用户对象,覆盖我的更改。如果我没有更改该对象,我不知道 Mongoose/Mongo 是否不会进行保存,将不得不尝试看看。还有什么想法吗?

 var User = Mongoose.model("User");

 /* get the user, based on the session user id */
 User.findById(session.me,function(err,user_info)
 {
      if (err)
      {
           next(err,user_info,null);
           return;
      }

      if (!user_info || user_info.length === 0)
      {
           next(_e("ACCOUNT_NOT_FOUND"),"user_id: " + session.me);
           return;
      }

      var source_service_info = _.where(user_info.credentials, {"source_service_id": service_id});
      var source_service = source_service_info.source_service;

      User.findByIdAndUpdate(session.me,{$pull: {"credentials": {"source_service_id": service_id}}},{},function(err,user_credential_removed)
      {
           if (err)
           {
                next(err,user_info,null);
                return;
           }

           User.findByIdAndUpdate(session.me,{$pull: {"criteria": {"source_service": source_service}}},{},function(err,user_criteria_removed)
           {
                if (err)
                {
                     next(err,user_info,null);
                     return;
                }

                else
                {
                     next(null,user_criteria_removed);
                }
           });
      });
 });

};

4

2 回答 2

1

您的方法的问题在于,它只是缩短了第二个进程可以读取数据的时间,并不能消除问题。

解决方案是将信号量设置为与读取相同的操作。我没有使用 Mongoose,但在 MongoDB 中,您可以使用 findAndModify 仅在信号量为 false 时返回用户记录,如果为 false,则在一个原子操作中将信号量设置为 true。

如果您不想使用 findAndModify,则可以先进行更新,仅在未设置信号量的情况下将信号量设置为 true(或设置为某个特定的 ID 值,以便您知道它是您的信号量)。然后,如果该过程成功,您可以进行查找(也许将您的信号量 ID 作为查找中的标准传递)。但是,如果在 Mongoose 中可以使用 findAndModify,它会一步完成。

此处描述了一种变体: http: //docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/在其中您执行一种乐观锁定形式,在将旧值更改为之前检查它们是否未更改新的价值观。

对此有一个变体,它使用单独的表来模拟两阶段提交:http ://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/

于 2013-09-21T01:48:03.463 回答
1

编辑:在下面交换时,这似乎是一个模式和更新问题。问题可能会变成这样:我在数组中有一些条目,这些条目的序号索引也与其他一些数组有关。如何在不匹配的情况下执行删除?

根据现实世界中的频率与 QA 测试场景的不同,会出现三种最常见的可能性。

  1. 考虑添加一个已删除的标志,但保持记录的顺序相同。如果有人切换,请重复使用相同的记录,但可以根据需要进行修复。
  2. 为每个元素(不是关系世界的特征)使用关联数组(JS 对象)。如果您需要顺序,请添加一个按顺序列出键的数组。两者都有语法更新,而不会触及任何其他已更改的内容,并且不会覆盖对不同字段的更改。
  3. 使用键是数字的关联数组。实际删除不会影响检索。

    stuff = {} stuff[1] = {some:'details'} stuff[2] = {some:'details2'}

是 1) 您是否对同一字段进行更改?把它变成一个数组,推送更改,然后弹出最新的以读取当前值。

2)您是否在更改不同的字段,但数据却被打败了?然后有更好的语法用于更新。您可以逐个字段更新。

$set: { 'fielda': 'valuea' } 

不会丢失对先前字段的编辑

3)改变你的架构

4) 改变进程的时间,使它们不重叠。或者他们在较小的子集中这样做,您可以设法防止重叠。

出于兴趣,我想知道需要哪些多个过程来更新同一记录?我不使用任何看起来像的东西。

于 2013-09-21T05:32:11.130 回答