我有一个生成计数器的要求,该计数器将发送到一些api调用。我的应用程序在多个节点上运行,因此我想如何生成唯一计数器。我尝试了以下代码
public static long GetTransactionCountForUser(int telcoId)
{
long valreturn = 0;
string key = "TelcoId:" + telcoId + ":Sequence";
if (Muxer != null && Muxer.IsConnected && (Muxer.GetDatabase()) != null)
{
IDatabase db = Muxer.GetDatabase();
var val = db.StringGet(key);
int maxVal = 999;
if (Convert.ToInt32(val) < maxVal)
{
valreturn = db.StringIncrement(key);
}
else
{
bool isdone = db.StringSet(key, valreturn);
//db.SetAdd(key,new RedisValue) .StringIncrement(key, Convert.ToDouble(val))
}
}
return valreturn;
}
并通过Task Parallel libray运行测试。当我有边界值时,我看到的是设置了多次0条目
请让我知道我需要做的更正
更新:我的最终逻辑如下
public static long GetSequenceNumberForTelcoApiCallViaLuaScript(int telcoId)
{
long valreturn = 0;
int maxIncrement = 9999;//todo via configuration
if (true)//todo via configuration
{
IDatabase db;
string key = "TelcoId:" + telcoId + ":SequenceNumber";
if (Muxer != null && Muxer.IsConnected && (db = Muxer.GetDatabase()) != null)
{
valreturn = (int)db.ScriptEvaluate(@"
local result = redis.call('incr', KEYS[1])
if result > tonumber(ARGV[1]) then
result = 1
redis.call('set', KEYS[1], result)
end
return result", new RedisKey[] { key }, flags: CommandFlags.HighPriority, values: new RedisValue[] { maxIncrement });
}
}
return valreturn;
}
实际上,您的代码在翻转边界附近并不安全,因为您正在执行“获取”,(等待时间和思考),“设置”-无需检查“获取”中的条件是否仍然适用。如果服务器忙于项目1000,则有可能获得各种疯狂的输出,包括:
1
2
...
999
1000 // when "get" returns 998, so you do an incr
1001 // ditto
1002 // ditto
0 // when "get" returns 999 or above, so you do a set
0 // ditto
0 // ditto
1
选项:
ScriptEvaluate
现在,redis事务(按选项1)很困难。就我个人而言,我将使用“
2”-除了更易于编码和调试之外,这意味着您只有1次往返和操作,而不是“ get,watch,get,multi,incr / set,exec
/丢弃”和“从头开始重试”循环来解决中止情况。如果您愿意,我可以尝试将其写为Lua-应该大约4行。
这是Lua的实现:
string key = ...
for(int i = 0; i < 2000; i++) // just a test loop for me; you'd only do it once etc
{
int result = (int) db.ScriptEvaluate(@"
local result = redis.call('incr', KEYS[1])
if result > 999 then
result = 0
redis.call('set', KEYS[1], result)
end
return result", new RedisKey[] { key });
Console.WriteLine(result);
}
注意:如果需要参数化最大值,则可以使用:
if result > tonumber(ARGV[1]) then
和:
int result = (int)db.ScriptEvaluate(...,
new RedisKey[] { key }, new RedisValue[] { max });
(因此ARGV[1]
取的值max
)
有必要了解eval
/ evalsha
(这就是ScriptEvaluate
调用) 没有与其他服务器请求竞争
,因此在incr
和之间没有任何变化set
。这意味着我们不需要复杂的watch
逻辑。
通过事务/约束API,这是相同的(我认为!):
static int IncrementAndLoopToZero(IDatabase db, RedisKey key, int max)
{
int result;
bool success;
do
{
RedisValue current = db.StringGet(key);
var tran = db.CreateTransaction();
// assert hasn't changed - note this handles "not exists" correctly
tran.AddCondition(Condition.StringEqual(key, current));
if(((int)current) > max)
{
result = 0;
tran.StringSetAsync(key, result, flags: CommandFlags.FireAndForget);
}
else
{
result = ((int)current) + 1;
tran.StringIncrementAsync(key, flags: CommandFlags.FireAndForget);
}
success = tran.Execute(); // if assertion fails, returns false and aborts
} while (!success); // and if it aborts, we need to redo
return result;
}
复杂吗?在 简单的成功案例 在这里则是:
GET {key} # get the current value
WATCH {key} # assertion stating that {key} should be guarded
GET {key} # used by the assertion to check the value
MULTI # begin a block
INCR {key} # increment {key}
EXEC # execute the block *if WATCH is happy*
这是一项相当大的工作,涉及多路复用器上的流水线停顿。更复杂的情况(断言失败,监视失败,环绕)将具有稍微不同的输出,但应该可以工作。
主要内容:Redis分布式锁介绍,Redis分布式锁命令在分布式系统中,当不同进程或线程一起访问共享资源时,会造成资源争抢,如果不加以控制的话,就会引发程序错乱。此时使用分布式锁能够非常有效的解决这个问题,它采用了一种互斥机制来防止线程或进程间相互干扰,从而保证了数据的一致性。 提示:如果对分布式系统这一概念不清楚,可参考百度百科《分布式系统》,简而言之,它是一种架构、一种模式。 Redis分布式锁介绍 分布式锁并非是 Redis 独有,比如 MySQ
背景 在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等。大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对redis的连接并不存在竞争关系。其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制。 一、使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以
使用Redis实现分布式锁 redis命令:set users 10 nx ex 12 原子性命令 //使用uuid,解决锁释放的问题 @GetMapping public void testLock() throws InterruptedException { String uuid = UUID.randomUUID().toString(); Boolean b_loc
本文向大家介绍Redis 怎么实现分布式锁?相关面试题,主要包含被问及Redis 怎么实现分布式锁?时的应答技巧和注意事项,需要的朋友参考一下 Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。 占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。
Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。 Redis容易产生的几个问题: 锁未被释放 B锁被A锁释放了 数据库事务超时 锁过期了,业务还没执行完 Redis主从复制的问题
本文向大家介绍Redis 分布式锁有什么缺陷?相关面试题,主要包含被问及Redis 分布式锁有什么缺陷?时的应答技巧和注意事项,需要的朋友参考一下 Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。