前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多用户抢红包,如何保证只有一个抢到

多用户抢红包,如何保证只有一个抢到

原创
作者头像
BLACK595
发布2024-09-24 14:17:09
480
发布2024-09-24 14:17:09

前言

在一个百人群中,群主发了个红包,设置的3个人瓜分。如何能够保证只有3个人能抢到。100个人去抢,相当于就是100个线程去争夺这3个资源,如果处理不好,可能就会发生“超卖”,产生脏数据,威胁系统的正常运行。

当100个人同时去抢,也就是线程1,线程2,线程3...,此时线程1和线程2已经抢到了,就还剩一个红包了,而此时线程3和线程4同时发出抢红包的命令,线程3查询数据库发现还剩1个,抢下成功,而线程3还未修改库存时,线程4也来读取,发现还剩一个,也抢成功。结果这就发生“超卖”,红包被抢了4个,数据库一看红包剩余为-1。

image.png
image.png

解决思路

为了保证资源的安全,不能让多个用户同时访问到资源,也就是需要互斥的访问共有资源,同一时刻只能让一个用户访问,也就是给共享资源加上一个悲观锁,只有拿到锁的线程才能正常访问资源,拿不到锁的线程也不能让他一直等着,直接返回用户让他稍后重试。

JVM本地锁

JVM本地锁由ReentrantLock或synchronized实现

代码语言:java
复制
//抢红包方法加锁
public synchronized void grabRedPaper(){
    ...业务处理
}

不过这种同步锁粒度太大,我们需要的是针对抢同一红包的用户互斥,而这种方式是所有调用grabRedPaper方法的线程都需要等待,即限制所有人抢红包操作,效率低且不符合业务需求。每个红包应该都有一个唯一性ID,在单个红包上加锁效率就会高很多,也是单进程常用的使用方式。

代码语言:java
复制
private Map<String, Object> lockMap = new HashMap<>();

//抢红包方法
public void grabRedPaper(String redPaperId) {
    Object lock = getLock(redPaperId);
    synchronized (lock) {
        // 在这里进行对业务的互斥访问操作
    }
}
//获取红包ID锁对象
private Object getLock(String redPaperId) {
    if (!lockMap.containsKey(redPaperId)) {
        lockMap.put(redPaperId, new Object());
    }
    return lockMap.get(redPaperId);
}
image.png
image.png

Redis分布式锁

但当我们使用分布式系统中,一个业务功能会打包部署到多台服务器上,也就是会有多个进程来尝试获取共享资源,本地JVM锁也就无法完成需求了,所以我们需要第三方统一控制资源的分配,也就是分布式锁。

image.png
image.png

分布式锁一般一般需要满足四个基本条件:

  1. 互斥:同一时刻,只能有一个线程获取到资源。
  2. 可重入:获取到锁资源后,后续还能继续获取到锁。
  3. 高可用:锁服务一个宕机后还能有另一个接着服务;再者即使发生了错误,一定时间内也能自动释放锁,避免死锁发生。
  4. 非阻塞:如果获取不到锁,不能无限等待。

Mysql行锁

再者我们还可以通过Mysql的行锁实现,SELECT...FOR UPDATE,这种方式会将查询时的行锁住,不允许其他事务修改,直到读取完毕。将行锁和修改红包剩余数量放在一个事务中,也能做到互斥。不过这种做法效率较差,不推荐使用。

总结

方案

实现举例

优点

缺点

JVM本地锁

synchronized

实现简单,性能较好

只能在单个 JVM 进程内使用,无法用于分布式环境

Mysql行锁

SELECT...FOR UPDATE

保证并发情况下的隔离性,避免出现脏数据

增加了数据库的开销,特别是在高并发场景下;对应用程序有一定的侵入性,需要在 SQL 语句中正确使用锁定机制。

分布式锁

Redis分布式锁

可用于分布式,性能较高

实现相对复杂,需要考虑锁的续租、释放等问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 解决思路
    • JVM本地锁
    • Redis分布式锁
    • Mysql行锁
    • 总结
    相关产品与服务
    云数据库 Redis
    腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档