背景
年前开发的问题,总结一下:
用户在商城页面领取新客券,领过的用户不能再次领取,整个新用户领券逻辑如下:
Step 1: 验证商家是否登录、是否是老用户(R)
Step 2: 验证该商家是否领取过新客券(查询该商家拥有的优惠券是否含有新客券)(R)
Step 3: 领取新客券(W)
Step 4: 修改优惠券统计信息(W)
Step 5: 返回该商家刚刚领取的新客券(查询接口同Step 2)(R)
坑一:线上出现同一个用户领取多套优惠券情况,使用分布式锁解决
解决办法
增加分布式锁,使用 redis 缓存(锁未考虑当前线程),大致代码如下:
// @Transaction Step 1: 验证商家是否登录、是否是老用户(R) try{ if(!lock.lock()) { return; } Step 2: 验证该商家是否领取过新客券(查询该商家拥有的优惠券是否含有新客券)(R) Step 3: 领取新客券(W) Step 4: 修改优惠券统计信息(W) Step 5: 返回该商家刚刚领取的新客券(查询接口同Step 2)(R) } finally { lock.unlock(); }
坑二:加锁后仍然出现一个用户领取多套优惠券情况
在坑一出现问题的原因是多个线程并行时,很可能出现A线程释放B线程的锁,这里解决办法有多种,使用lua脚本使锁与线程相关 or 获取锁方法放到外面+超时时间 or threadLocal+锁改造,下面使用第二种:
解决办法
// @Transaction Step 1: 验证商家是否登录、是否是老用户(R) if(!lock.lock()) { // 这里加上超时时间,否则若获取锁超时,很容易再次获取不到 return; } try{ Step 2: 验证该商家是否领取过新客券(查询该商家拥有的优惠券是否含有新客券)(R) Step 3: 领取新客券(W) Step 4: 修改优惠券统计信息(W) Step 5: 返回该商家刚刚领取的新客券(查询接口同Step 2)(R) } finally { lock.unlock(); }
需要遵守:
lock.lock(); try{ doSomething(); }finally{ lock.unLock(); }
坑三:主从库延时可能导致用户领取新客券出现问题
解决办法
强制该事务中的SQL(包括读和写操作)都走主库