时间:2021-05-02
目录
前言
公司有一个发券的接口有并发安全问题,下面列出这个问题和解决这个问题的方式。
业务描述
这个接口的作用是给会员发多张券码。涉及到4张主体,分别是:用户,券,券码,用户领取记录。
下面是改造前的伪代码。
主要是因为查出券码那行存在并发安全问题,多个线程拿到同几个券码。以下都是基于如何让取券码变成原子的去展开。
改造过程
因为券码是多张,想用lua+redis的list结构去做弹出。为什么用这种方案是因为for update直接被否了。
这是写的lua脚本。。
? 1 2 3 4 5 local result = {} for i=1,ARGV[1],1 do result[i] = redis.call("lpop", KEYS[1]) end return table.contact(result , "|")这是写的执行lua脚本的client。。其实主要的解决方法就是在redis的list里rpush(存),lpop(取)取数据
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 @Slf4j @Component public class CouponCodeRedisQueueClient implements InitializingBean { /** * redis lua脚本文件路径 */ public static final String POP_COUPON_CODE_LUA_PATH = "lua/pop-coupon-code.lua"; public static final String SEPARATOR = "|"; private static final String COUPON_CODE_KEY_PATTERN = "PROMOTION:COUPON_CODE_{0}"; private String LUA_COUPON_CODE_SCRIPT; private String LUA_COUPON_CODE_SCRIPT_SHA; @Autowired private JedisTemplate jedisTemplate; @Override public void afterPropertiesSet() throws Exception { LUA_COUPON_CODE_SCRIPT = Resources.toString(Resources.getResource(POP_COUPON_CODE_LUA_PATH), Charsets.UTF_8); if (StringUtils.isNotBlank(LUA_COUPON_CODE_SCRIPT)) { LUA_COUPON_CODE_SCRIPT_SHA = jedisTemplate.execute(jedis -> { return jedis.scriptLoad(LUA_COUPON_CODE_SCRIPT); }); log.info("redis lock script sha:{}", LUA_COUPON_CODE_SCRIPT_SHA); } } /** * 获取Code * * @param activityId * @param num * @return */ public List<String> popCouponCode(Long activityId, String num , int retryNum) { if(retryNum == 0){ log.error("reload lua script error , try limit times ,activityId:{}", activityId); return Collections.emptyList(); } List<String> keys = Lists.newArrayList(); String key = buildKey(String.valueOf(activityId)); keys.add(key); List<String> args = Lists.newArrayList(); args.add(num); try { Object result = jedisTemplate.execute(jedis -> { if (StringUtils.isNotBlank(LUA_COUPON_CODE_SCRIPT_SHA)) { return jedis.evalsha(LUA_COUPON_CODE_SCRIPT_SHA, keys, args); } else { return jedis.eval(LUA_COUPON_CODE_SCRIPT, keys, args); } }); log.info("pop coupon code by lua script.result:{}", result); if (Objects.isNull(result)) { return Collections.emptyList(); } return Splitter.on(SEPARATOR).splitToList(result.toString()); } catch (JedisNoScriptException jnse) { log.error("no lua lock script found.try to reload it", jnse); reloadLuaScript(); //加载后重新执行 popCouponCode(activityId, num, --retryNum); } catch (Exception e) { log.error("failed to get a redis lock.key:{}", key, e); } return Collections.emptyList(); } /** * 重新加载LUA脚本 * * @throws Exception */ public void reloadLuaScript() { synchronized (CouponCodeRedisQueueClient.class) { try { afterPropertiesSet(); } catch (Exception e) { log.error("failed to reload redis lock lua script.retry load it."); reloadLuaScript(); } } } /** * 构建Key * * @param activityId * @return */ public String buildKey(String activityId) { return MessageFormat.format(COUPON_CODE_KEY_PATTERN, activityId); } }当然这种操作需要去提前把所有券的券码丢到redis里去,这里我们也碰到了一些问题(券码量比较大的情况下)。比如开始直接粗暴的用@PostConstruct去放入redis,导致项目启动需要很久很久。。这里就不展开了,说一下我们尝试的几种方法
到此这篇关于使用lua+redis解决发多张券的并发问题的文章就介绍到这了,更多相关redis多张券的并发内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!
原文链接:https://www.cnblogs.com/gistmap/p/14256794.html
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
上一篇文章聊了一下使用Redis事务来解决高并发商品超卖问题,今天我们来聊一下使用Redis链表来解决高并发商品超卖问题。实现原理使用redis链表来做,因为p
本文介绍如何用Nginx+Lua+Redis来构建高并发Web应用,Curl请求Nginx,Nginx通过Lua查询Redis,返回json数据。一、安装1、安
lua脚本Redis中使用lua脚本,我们需要注意的是,从Redis2.6.0后才支持lua脚本的执行。使用lua脚本的好处:原子操作:lua脚本是作为一个整体
一、Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作
redis技术的使用:redis真的是一个很好的技术,它可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动。。。redis之所以能解决高并发的