国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術(shù)|

服務(wù)器之家 - 數(shù)據(jù)庫 - Redis - 使用lua+redis解決發(fā)多張券的并發(fā)問題

使用lua+redis解決發(fā)多張券的并發(fā)問題

2021-02-26 18:08gistmap Redis

這篇文章主要介紹了使用lua+redis解決發(fā)多張券的并發(fā)問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

 

前言

公司有一個發(fā)券的接口有并發(fā)安全問題,下面列出這個問題和解決這個問題的方式。

 

業(yè)務(wù)描述

這個接口的作用是給會員發(fā)多張券碼。涉及到4張主體,分別是:用戶,券,券碼,用戶領(lǐng)取記錄。
下面是改造前的偽代碼。
主要是因為查出券碼那行存在并發(fā)安全問題,多個線程拿到同幾個券碼。以下都是基于如何讓取券碼變成原子的去展開。

?
1
2
3
4
5
6
7
8
9
10
11
12
public boolean sendCoupons(Long userId, Long couponId) {
 // 一堆校驗
 // ...
 // 查出券碼
 List<CouponCode> couponCodes = couponCodeService.findByCouponId(couponId, num);
 // batchUpdateStatus是一個被@Transactional(propagation = Propagation.REQUIRES_NEW)修飾的方法
 // 批量更新為已被領(lǐng)取狀態(tài)
 couponCodeService.batchUpdateStatus(couponCods);
 // 發(fā)券
 // 發(fā)權(quán)益
 // 新增用戶券碼領(lǐng)取記錄
}

 

改造過程

因為券碼是多張,想用lua+redis的list結(jié)構(gòu)去做彈出。為什么用這種方案是因為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 , "|")

這是寫的執(zhí)行l(wèi)ua腳本的client。。其實主要的解決方法就是在redis的list里rpush(存),lpop(取)取數(shù)據(jù)

?
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();
   //加載后重新執(zhí)行
   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();
   }
  }
 }
 
 /**
  * 構(gòu)建Key
  *
  * @param activityId
  * @return
  */
 public String buildKey(String activityId) {
  return MessageFormat.format(COUPON_CODE_KEY_PATTERN, activityId);
 }
 
}

當(dāng)然這種操作需要去提前把所有券的券碼丟到redis里去,這里我們也碰到了一些問題(券碼量比較大的情況下)。比如開始直接粗暴的用@PostConstruct去放入redis,導(dǎo)致項目啟動需要很久很久。。這里就不展開了,說一下我們嘗試的幾種方法

  • @PostConstruct注解
  • CommandLineRunner接口
  • redis的pipeline技術(shù)
  • 先保證每個卡券有一定量的券碼在redis,再用定時任務(wù)定時(根據(jù)業(yè)務(wù)量)去補

到此這篇關(guān)于使用lua+redis解決發(fā)多張券的并發(fā)問題的文章就介紹到這了,更多相關(guān)redis多張券的并發(fā)內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/gistmap/p/14256794.html

延伸 · 閱讀

精彩推薦
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關(guān)于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具...

    一線碼農(nóng)5812019-11-18
  • Redisredis實現(xiàn)排行榜功能

    redis實現(xiàn)排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現(xiàn)排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • Redis詳解Redis復(fù)制原理

    詳解Redis復(fù)制原理

    與大多數(shù)db一樣,Redis也提供了復(fù)制機制,以滿足故障恢復(fù)和負載均衡等需求。復(fù)制也是Redis高可用的基礎(chǔ),哨兵和集群都是建立在復(fù)制基礎(chǔ)上實現(xiàn)高可用的...

    李留廣10222021-08-09
  • RedisRedis全量復(fù)制與部分復(fù)制示例詳解

    Redis全量復(fù)制與部分復(fù)制示例詳解

    這篇文章主要給大家介紹了關(guān)于Redis全量復(fù)制與部分復(fù)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Redis爬蟲具有一定的參考學(xué)習(xí)...

    豆子先生5052019-11-27
  • RedisRedis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis的主從架構(gòu),能幫助我們實現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關(guān)于Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離的相關(guān)資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • RedisRedis的配置、啟動、操作和關(guān)閉方法

    Redis的配置、啟動、操作和關(guān)閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關(guān)閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
  • RedisRedis 事務(wù)知識點相關(guān)總結(jié)

    Redis 事務(wù)知識點相關(guān)總結(jié)

    這篇文章主要介紹了Redis 事務(wù)相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
主站蜘蛛池模板: 7799精品视频天天看 | 国产福利电影一区 | 久久中文精品 | 免费一级黄色 | 久久国产精品视频 | 日韩精品极品视频在线观看免费 | 狠狠干av| 国产一区av在线 | 国产美女精品人人做人人爽 | 日本v在线观看 | 在线a免费| 99久久影院 | 精品成人av一区二区三区 | 色吊丝在线永久观看最新版本 | 韩国一区二区视频 | 午夜影视 | 国产一区二区三区四区在线观看 | 中文字幕日韩视频 | 欧美综合在线观看 | 中文字幕亚洲欧美日韩在线不卡 | 久久久中文| 欧美黑人一级爽快片淫片高清 | 国产在线综合视频 | 一区二区三区欧美 | 国产高清视频一区二区 | 亚洲综合在线播放 | 一级免费毛片 | 欧美日韩国产一区二区三区 | 男女羞羞网站 | 成人三级视频网站 | av在线电影观看 | 国产视频1区2区 | 久久精品国产99国产精品 | 国产精品国产三级国产aⅴ原创 | www.天天操.com| 欧美午夜一区二区三区免费大片 | 午夜av电影 | 亚洲成人av一区二区三区 | 日韩二区 | 亚洲免费网站 | 亚洲第一黄色网 |