利用Redis解决重复数据时候的并发异常和分布式锁解决方案

7 篇文章 0 订阅

最近公司里有一个并发业务。多个线程消费一个kafka数据流,这个kafka数据流里数据的某个字段有重复。需要根据这个字段来做下去重。

一开始的方案是利用Redis来实现,先查Redis如果没有的话则setex。后来发现这个并不能保证并发安全性,后来的结果还是有重复的数据。

经过分析,发现是有的重复数据相隔极短。比如说A1和A2两条数据。如果A1 get不到,则说明没有,这个时候去写redis。但是还没写进去呢。A2也去get,如果A1还没写进去的话,A2就get不到。这个时候就出现了并发不安全了。

解决方案就是用redis另一个函数。类似于分布式锁的方式。

set(key, value, "NX", "EX", expireSeconds);  // SET IF NOT EXIST,而且还是原子的
// 操作成功,返回“OK”,否则返回null

如果此时返回ok,就说明这个数据不重复,如果返回null,就说明数据不重复。

看jedis的set函数

  /**
   * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1
   * GB).
   * @param key
   * @param value
   * @param nxxx NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the key
   *          if it already exist.
   * @param expx EX|PX, expire time units: EX = seconds; PX = milliseconds
   * @param time expire time in the units of <code>expx</code>
   * @return Status code reply
   */
  public String set(final String key, final String value, final String nxxx, final String expx,
      final long time) {
    checkIsInMultiOrPipeline();
    client.set(key, value, nxxx, expx, time);
    return client.getStatusCodeReply();
  }

对应的Redis命令如下

SET resource_name my_random_value NX PX 30000

该命令仅在密钥不存在(NX选项)且到期时间为30000毫秒(PX选项)时才设置密钥。密钥设置为“我的随机值”。该值在所有客户端和所有锁定请求中必须唯一。

基本上,使用随机值是为了以安全的方式释放锁,并且脚本会告诉Redis:仅当密钥存在且存储在密钥上的值恰好是我期望的值时,才删除该密钥。这是通过以下Lua脚本完成的:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

为了避免删除另一个客户端创建的锁,这一点很重要。例如,一个客户端可能获取了该锁,在某些操作中被阻塞的时间超过了该锁的有效时间(密钥将过期的时间),然后又删除了某个其他客户端已经获取的锁。仅使用DEL是不安全的,因为一个客户端可能会删除另一个客户端的锁。使用上述脚本时,每个锁都由一个随机字符串“签名”,因此仅当该锁仍是客户端尝试将其删除时设置的锁时,该锁才会被删除。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值