node关于redis存储问题,redis的调用接口是异步的,如何来保证修改数据的顺序呢
发布于 9 年前 作者 daliniu 10058 次浏览 最后一次编辑是 8 年前 来自 问答

redis修改数据的时候是先get,之后再set,但是在node中一切都是异步的,也就是说get和set的过程都是异步,既然是异步也就有可能出现 2个请求分别对同一份数据进行修改,都是调用get后再set 第一种情况 get get set set 第二种情况 get set get set 请问如果出现第一种情况的话也就是说第一次修改的结果被第二次修改的结果覆盖,这种情况应该如何解决呢

11 回复
	/**
	 * Created by 10000489 on 2015/6/29.
	 */
	var redis = require('redis');
	var async = require('async');
	var client = redis.createClient(6379, "10.21.3.67");
	
	function foo1(next) {
		client.get("key1", function (err, data) {
			//do something
			next(null);
		});
	}
	
	function foo2(next) {
		client.set("key1", function (err, data) {
			//do something
			next(null);
		});
	}
	
	function foo3(next) {
		client.get("key2", function (err, data) {
			//do something
			next(null);
		});
	}
	
	function foo4(next) {
		client.set("key2", function (err, data) {
			//do something
			next(null);
		});
	}
	
	async.waterfall([
		function (cb) {
			foo1(cb);
		}, function (cb) {
			foo2(cb);
		}
	], function (err, res) {
		//do something
	});
	
	async.waterfall([
		function (cb) {
			foo3(cb);
		}, function (cb) {
			foo4(cb);
		}
	], function (err, res) {
		//do something
	});
	
	//如上,对于redis来说他会接受到foo1到foo4操作,所以这样的话1和3是随机的,如果必选强调1在3前的话必须修改为
	//下面:
	async.waterfall([
		function (cb) {
			foo1(cb);
		}, function (cb) {
			foo2(cb);
		}, function (cb) {
			foo3(cb);
		}, function (cb) {
			foo4(cb);
		}
	], function (err, res) {
		//do something
	});
	
	//NOTE:有redis操作的一定要严格控制流程,否则会出现各种奇怪的问题。
	//列举几个常见问题现象:
	//1、存在3库却出现在0库或在其他库中
	//2、数据被覆盖等。

@haozxuan

如果我只看key1 async.waterfall([ function (cb) { foo1(cb); }, function (cb) { foo2(cb); } ], function (err, res) { //do something });

这个过程在对redis get数据等callback的时候,又来一个请求也是
    async.waterfall([
    function (cb) {
        foo1(cb);
    }, function (cb) {
        foo2(cb);
    }
], function (err, res) {
    //do something
});
那么这个时候好像还是会出现我说的情况

get  get  set  set  这种情况

@daliniu 你仔细想想,你说的这种情况,多进程的语言,如PHP,也会遇到。这不是Node异步导致的问题。 如果你一定要保持 get set get set 的顺序,可以使用事务来进行操作。一个事务里面的所有操作能保证原子性。

@daliniu 加全局锁。。。在上个操作未完成前锁定

加watch锁 unwatch解锁

@haozxuan 你好,我想问下载node里面如何加全局锁

用 promise / q / Async / bluebird 等处理异步回调 不就是了吗

没有这么麻烦,另外 Redis 已经不需要用 WATCH 锁了。

如果不考虑其他 Event loop 介入,直接用下面的代码即可保证按照 get -> set -> get -> set 执行了:

var Redis = require('ioredis');
var redis = new Redis();

redis.get('key1', function (err, key1) {
  console.log('got key1: ' + key1);
});

redis.set('key1', 'new value for key1');

redis.get('key2', function (err, key2) {
  console.log('got key2: ' + key2);
});

redis.set('key2', 'new value for key2');

如果考虑到其他 Event loop 或者其他 Node 进程并行操作,则可以用 Pipelining 或者 Transaction:

var Redis = require('ioredis');
var redis = new Redis();

// Pipelining
redis.pipeline().get('key1').set('key1', 'v1').get('key2').set('key2', 'v2').exec(callback);
// 或者 Transaction
redis.multi().get('key1').set('key1', 'v1').get('key2').set('key2', 'v2').exec(callback);

@luinlee Pipelining 或者 Transaction是不是只有ioredis才有,大多数nodejs+redis开发是不是都是使用这种模式的,因为 Event loop 的情况还是存在的

@daliniu Pipelining 和 Transaction 都是 Redis 提供支持的。node_redis 不支持 Pipelining,但是支持 Transaction。如果需要保证顺序,Pipelining 或 Transaction 都可以的。

回到顶部