Redis WATCH MULTI EXEC由一个客户端
Redis WATCH MULTI EXEC by one client
我在RedisOnGo + node_redis上使用NodeJS + Express + Redis作为客户端。我期望有很多并发,所以试着测试WATCH。这个例子不包含Express,只包含必要的东西。
var redis = require("redis")
var rc = redis.createClient(config.redis.port, config.redis.host)
rc.auth(config.redis.hash, function(err) {
if (err) {
throw err
}
})
rc.on('ready', function () {
rc.set("inc",0)
for(var i=1;i<=10;i++){
rc.watch("inc")
rc.get("inc",function(err,data){
var multi = rc.multi()
data++ // I do know I can use rc.incr(), this is just for example
multi.set("inc",data)
multi.exec(function(err,replies){
console.log(replies)
})
})
}
})
期望结果:在执行回调中得到N个错误,最终得到"inc"变量= 10-N.
意外结果:在exec回调中得到0错误,但最终得到"inc"变量= 1.
手表不适合我的代码。
我发现这个线程redis和watch + multi允许并发用户。他们说这是因为只有redis客户端。
然后我发现这个线程我应该为每个连接创建一个新的Redis客户端吗?他们表示,"绝对不建议"为每笔交易生成一个新客户。我迷路了。
还请注意,我必须认证到Redis服务器。提前感谢!
版1:
我能够通过在每次WATCH-MULTI-EXEC迭代之前创建一个新的客户端连接来使用本地Redis实例(所以我不使用client.auth)使其工作。虽然不确定它是否好,但现在的结果是100%准确的。
版2 如果我在每次WATCH-MULTI-EXEC迭代之前创建一个新的客户端连接,然后做客户端,它就可以工作了。验证并等待client.on.
问题仍然存在,我是否可以为每次迭代创建新的客户端连接?
你的结果是完全可以预测的。这是正确的。
记住- node.js是一个线程应用程序。Node.js使用异步输入-输出,但命令应该以redis严格顺序的"请求-响应"发送。所以你的代码和你的请求执行严格并行,而你只使用一个连接到redis服务器。
查看你的代码:
rc.on('ready', function () {
rc.set("inc",0)
for(var i = 1; i <= 10; i++){
rc.watch("inc")
//10 times row by row call get function. It`s realy means that your written
//in an asynchronous style code executed strict in series. You are using just
//one connection - so all command would be executed one by one.
rc.get("inc",function(err,data){
//Your data variable data = 0 for each if request.
var multi = rc.multi()
data++ //This operation is not atomic for redis so your always has data = 1
multi.set("inc",data) //and set it
multi.exec(function(err,replies){
console.log(replies)
})
})
}
})
要确认这一点,执行以下步骤:
- 连接redis并执行
monitor
命令 - 运行node.js应用程序
输出将是
SET inc 0
WATCH inc
GET inc
.... get command more 9 times
MULTI
SET inc 1
EXEC
.... command block more 9 times
这样你就得到了你上面写的结果:"在执行回调中得到0个错误,但最终得到"inc"变量= 1."。
是否可以为每次迭代创建新的客户端连接?
对于这个示例-是的,它解决了您的问题。一般来说,这取决于你想要运行多少"并发"查询。Redis仍然是一个线程,所以这个"并发"的意思只是并发命令批处理到Redis引擎。
例如,如果使用2个连接,monitor
可以给出如下内容:
1 SET inc 0 //from 1st connection
2 WATCH inc //from 1st connection
3 SET inc 0 //from 2nd connection
4 GET inc //from 1nd connection
5 WATCH int //from 2nd connection
6 GET inc //from 2nd connection
7 MULTI //from 1st connection
8 SET inc 1 //from 1st connection
9 MULTI //from 2nd connection
10 SET inc 1 //from 2nd connection
11 EXEC //from 1st failed becouse of 2nd connection SET inc 0 (line 3)
//was executed after WATCH (line 2)
12 EXEC //success becouse of MULTI from 1st connection was failed and SET inc 1 from first
//connection was not executed
-------------------------------------------------------------------------------> time
| | | | | | | | | | | |
connection 1 set watch | get | | multi set | | exec(fail) |
connection 2 set watch get multi set exec
理解redis如何执行你的命令是非常重要的。Redis是单线程的,来自所有连接的所有命令都在一行中一个接一个地执行。Redis不保证来自一个连接的命令将在一行中执行(如果这里有另一个连接存在),所以你应该MULTI,如果你想确保你的命令执行一个块(如果需要的话)。但是为什么需要WATCH呢?看看上面我的redis命令。您可以看到来自不同连接的命令是混合的。手表可以让你处理这个问题。
这在文档中有很好的解释。请读一读!
我终于得到了你的问题。
如果你想测试WATCH的并发性,我认为你需要修改你的代码。我们都知道。WATCH只监视值的变化,不监视值的操作。因此,在您当前的代码中,所有get
命令将成功执行并获得0
,然后将inc
设置为1
。所有的设定值都是相同的(1
),所以手表不会失败。
在这种情况下,我们需要确保不仅write
的操作受到保护,而且read
的操作也受到保护。在设置inc
之前,您需要先设置watch
并修改另一个键,该键作为悲观锁,然后我们可以获取并更改inc
。这样,它将确保你的期望。
rc.set("inc",0)
for(var i=1;i<=10;i++){
rc.watch("inc-lock")
rc.get("inc",function(err,data){
var multi = rc.multi()
data++
multi.incr("inc-lock")
multi.set("inc",data)
multi.exec(function(err,replies){
console.log(replies)
})
})
}
我在我的电脑上测试了一下。
[2013-11-26 18:51:09.389][信息]控制台-[1,'好的']
[2013-11-26 18:51:09.390][信息]控制台-(2,'好的']
[2013-11-26 18:51:09.390][信息]控制台-[3,'好的']
[2013-11-26 18:51:09.390][信息]控制台-(4 '好的']
[2013-11-26 18:51:09.391][信息]控制台-[5,'好的']
[2013-11-26 18:51:09.391][信息]控制台——(6 '好的']
[2013-11-26 18:51:09.392][信息]控制台-[7,'好的']
[2013-11-26 18:51:09.392][信息]控制台-(8 '好的']
[2013-11-26 18:51:09.393][信息]控制台-(9,"OK")
[2013-11-26 18:51:09.393][信息]控制台——(10,"OK")
如果你想使用事务/原子MULTI操作,但你想使用共享连接,据我所知,你唯一的选择是使用LUA。
我在redis中使用LUA脚本进行许多事情,而LUA的事情是整个脚本将自动执行,这非常方便。你必须意识到,这意味着如果你有一个缓慢的LUA脚本,你会使每个人使用你的服务器的redis变慢。
另外,当使用LUA时,即使你可以操作不同的键,也要注意,如果你在脚本中使用多个键,一旦它被释放,你将无法使用Redis集群。这是因为,当使用集群时,密钥将被分发到不同的Redis进程,所以你的LUA脚本可能无法访问单个服务器上的所有密钥。
在任何情况下,redis集群的问题在发出MULTI时都是一样的,因为MULTI不允许在集群上设置不同的密钥。
欢呼,j
我发现executeIsolated(callback)
通过
似乎是正确的解决方案。
- 是否可以使用JavaScript/AAJAX在客户端创建一个文件
- 有没有办法用客户端的javascript生成一个AmazonEC2实例
- SignalR返回到一个客户端,由控制器生成
- 在重定向到 ASP.NET C# 中的另一个页面之前显示客户端脚本警报
- Javascript:我无法获得一个文本框来显示客户端更新的值
- 我已经从d3图表中创建了一个base64图像,需要使用客户端javascript或angular将其发布到faceboo
- 使用require.js,您可以(a)定义一个简单的插件吗?(b)从客户端的角度展示如何使用该插件
- 从一个页面在客户端存储数据,以便在另一个站点上填写表单
- 如何生成一个唯一的链接,该链接将加载一个会话,其中包含客户端可用的某些文档
- 通过服务器将视频从一个客户端流式传输到另一个客户端
- ExtJS-Ext.MessageBox没有't在其中一个客户端IE9浏览器上弹出
- 我如何保存一个客户端只有网页与文本输入
- 使用jquery/javascript在aspx页面中填充一个客户端下拉菜单和一个服务器端下拉菜单
- 有任何方法来运行一个客户端浏览器脚本的电话
- 如何通过SignalR将javascript对象从一个客户端发送到另一个客户端
- 为什么我的多人javascript游戏慢时,另一个客户端(新标签)连接
- Redis WATCH MULTI EXEC由一个客户端
- 我可以流视频从一个客户端到另一个通过Web RTC
- 用php编写一个客户端重定向服务器端
- 寻找一个客户端生成pdf的解决方案(angular js)