当用户请求某个链接生成数据,单次生成1k,两个用户同时请求时,数据库会产生重复数据,怎么避免这个问题?

用户访问某个链接,后台收到请求之后获取最后一个ID(比如 23) 并+1(得24),我拿这个数字通过接口获取相关链接 比如 ex.cn/24 之后再将 这个链接存入数据库,这个时候 这条数据 id是 24 链接编号也是24 链接是 ex.cn/24 。
接口限制每次只能生成一个而我需要大量的链接,这时我会获取最后一个ID并循环+1访问接口并把链接放入数组中。并一次性存入数据库。
问题是 这一套流程走下来 需要三分钟。这三分钟内 如果有人再次发起请求,生成的链接的编号就会重复。求大神指点 我该怎么处理?

阅读 3.1k
5 个回答

如果可以不需要立马返回消息,你可以把请求塞到一个队列里面,一个一个处理数据避免冲突,当然也可以使用自增id,或者用一个id生成器

  1. 数据库自增字段
  2. 使用统一id的分发服务

做一个生成链接的任务表

任务id完成状态创建时间完成时间
102021-04-15 20:00:002021-04-15 20:00:00
202021-04-15 20:03:002021-04-15 20:03:00

写一个脚本每分钟获取一次这个任务表中未完成的任务

再在任务中执行你现有的逻辑,取出数据中最后一个id+1啥的

最后更新一下当前任务的状态和完成时间

取最大值的方法改为从redis取,
每次取值加上分布式锁,
计算完本次请求要生成的ID再把计算完的ID放入redis,
释放redis锁,
redis为空再从数据库取出放入redis
步骤:
1.lock()
2.getMaxId()
3.setMaxId()
4.unlock()

我有个疑问,那个生成链接的接口和你的系统是什么关系。你那个因为第二个请求导致的生成重复链接问题,是说你又生成了1000链接,部分链接和第一次生成的链接重复了。还是说这个接口是第三方的,其他系统的用户的请求进去,导致你预生成的链接没有被你的系统占有到。

我按照第一种情况的话,你这个就是多线程访问变量,读到了脏数据。解决思路就是,不要+1,而是+1000。你可以用AtomicInteger类保存最终id,确保线程安全。如果当前id为11,你要一次性生成1000条,那可以用AtomicInteger.getAndAdd(1000); 方法返回的是11,但是最终id已经变为1011了。(也可能不是11和1011,但是不要紧,返回值就是你要生成id的起始数)。然后你就可以拿着这个起始id,一直+1操作了。其他线程再进来,获取到的是+1000后的id,也就不可能再产生重复问题了。这是一个乐观锁的思路,如果你最终id保存在数据库,也可以用这个思路。

而且我觉得你既然缓存了1000个结果,那么你第二个请求进来的时候,可能已经有很多缓存链接了,就不需要再去创建1000条缓存了。你这个时候应该判断是否有未被使用的缓存链接,如果有的话直接用,没有的话,再去创建1000条缓存。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题