怎么避免并发修改同一条数据
发布于 11 年前 作者 linminqin 11726 次浏览 最后一次编辑是 8 年前

有个功能大致如下,在一堆没用过的数据中取一条数据,并将其标示为已用,nodejs要怎么才能把一块代码或一个函数设为不能同步执行,类似于java的synchronized,谢谢。

22 回复

不建议使用全局变量。一律采用传参,回调的方式处理问题。node里面没有锁的概念。

mysql, oracle…? 用事务啊 redis? 大部分情况不需要你处理原子性 memcache? cas就可以

能说的详细点吗,我是担心两个请求同时取到同一条记录

是mysql,我是担心两个请求同时取到同一条记录,这个应该跟事务没关系吧

node.js的编程模型是单线程异步IO,从这个角度来说,应该不会出现你说的问题。

node 基于数据库的操作,或者第三方模块,都是用回调的方式调用异步的。node也是单线程异步io,所以在同一时间里是不可能有两个或多个io同时工作,所谓单线程并发其实也就是串行执行的,只不过减少了cpu等待,提高了cpu的系统吞吐量

  1. 不知道楼主的意思是不是由于 Node.js 的单线程模型所以不是问题。比如当某个函数中没有异步代码时,这时,这个函数一定会独占 CPU 直到执行完毕。

  2. 也可能楼主遇到的问题在 Node.js 的单线程中也可能发生。比如在函数中出现了异步调用。

我读取数据的方法就是在回调函数里面执行的,大致如下:

function(err, cb) {
   ...
   executeOneFunc(err, function(err){  //执行某个函数
      readData(function(err, data) {  //读取数据
         checkData();                 //检查数据
         updateData(data, function(err){ //修改数据
           doOther();//执行其他的操作
         });
      });
   });
   ...
}

这样子也不用担心同步问题吗,我刚接触nodejs不久,所以问的比较初级,请见谅。

谢谢,实际情况我在下面做了回复,那种情况是不是也不用担心。

谢谢,实际情况我在下面做了回复,那种情况是不是也不用担心。

谢谢,实际情况我在下面做了回复,那种情况是不是也不用担心。

你这样写妥妥的有问题…

你checkdata 里面是异步操作吧…不要少嵌套了…

建议用async…

额。。这时需要担心的。我暂时没有解决方案。

既然用的mysql,那很简单吧,增加一个字段表示处理过没有(或者已有字段能表示也行),updateData的时候,类似下面的伪代码逻辑: update a=1 b=2 flag=1 where flag==0 && id=xxx 原子性由sql语句自己保证。

@youxiachai 我之前看过async,没认真看,以为它的作用就是让代码不用那样一层一层嵌套,看起来简洁点,它可以解决这问题吗

@xuduo35 谢谢建议,已有一个字段标示处理状态,如果像你写的这样sql的话,是可以知道记录是不是同时被别人修改了,但是因为我得到自己获取并且修改的记录的某个信息,所以需要回滚事务并且重新执行一遍,假设重新执行的情况下又出现同步问题,那又要回滚重新执行,这样很复杂的吧,有没有什么办法可以在读取的时候就保证不会读取到同一条

@linminqin 你的意思是要先从数据库获取一条没处理过的记录,然后处理,然后update?就是第一步都可能重复? 简单的想法,可以先update任何一条没有标记过的row标记一下,成功的话mysql应该会返回一条row,直接处理就行了。这样就不会有重复的了吧?好像也没什么需要回滚的。 只不过需要update两回。

好像我看到update的时候,只会返回affectedRows,能得到update的那条记录的id吗。因为获取到的数据还要修改关联的其他数据,如果其他数据修改失败,那这个update也要回滚,整个是处于事务的

@linminqin 应该可以吧,我也不是很了解,你可以找下文档。如果不行,变通一点,搞个全局counter(获取当前值,然后加1),update的时候把这个counter当flag,之后query一次where flag=yourcounter,找到就说明这是你的row。 都是歪招。如果不改一下程序总体设计,不知道有没有其他更好的办法。

@xuduo35 恩,3Q,我暂时用了你之前提的update a=1 b=2 flag=1 where flag==0 && id=xxx来处理,因为正式部署时可能会集群部署为多个,在代码上做了同步,也只能保证单个系统不出错,不知道有没有什么办法处理多个系统间的同步问题

把这种功能抽出来,单独部署. 然后其他节点统一通过这个模块来操作

var proxy = new EventProxy(); var status = “ready”; var select = function (callback) { proxy.once(“selected”, callback); if (status === “ready”) { status = “pending”; db.select(“SQL”, function (results) { proxy.emit(“selected”, results); status = “ready”; }); } };

回到顶部