使用redis list类型解决卡券类奖品发放问题

2024-01-16 22:48

本文主要是介绍使用redis list类型解决卡券类奖品发放问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题描述:

卡券类奖品是指预先导入对应的卡券数据,然后将卡券一条条分配出去。

在并发高的时候,很容易出现多个人拿取同一张卡券的问题。

比如说A用户拿道了卡券A,此时还没提交,B用户去数据库里拿取未发放的卡券,也拿到了卡券A。

此时一张卡券发给了两个人,这种情况从业务上来讲,肯定是不能接受的。

解决方案:

一、用数据库锁(不推荐)

方法:使用mysql数据库的写锁。

优点:保证前一个人还没拿到的时候,下一个也拿不到。

缺点:卡券发放流程是拿取后才知道对应的主键是什么,也就是无法根据主键做行锁,而是表锁,并发极低,访问量大的时候可能会造成功能不可用

二、随机获取(极不推荐)

方法:随机从数据库里拿取一条卡券数据。

优点:不会锁表,性能比较好

缺点:无异于饮鸠止渴,一开始数量多的时候不会重复,但是到后面,重复的概率会很高,并不能解决问题。

三、使用redis list类型(推荐)

方法:redis 是单线程服务,命令是一条条顺序执行的,也就是不会出现并发问题。redis 的 list类型 的pop方法,可以保证不会出现 卡券发给多个人的情况。

优点:redis 的数据都在内存里,读取超快,可以支持很高的并发。

缺点:数据需要从数据库里初始化到redis里,初始化会需要时间,如果没有提前初始化的话,可能会出现有一两秒的时间内,无法领取奖品。(具体看有多少条数据

python3代码示例:

# coding:utf-8
# 初始化redis
import redis
from blind_box.setting import REDIS_IP, REDIS_PORT, REDIS_PWD, REDIS_DB__author__ = 'mingv'redis_config = {'host': REDIS_IP,'port': REDIS_PORT,'password': REDIS_PWD,'db': REDIS_DB
}r = redis.Redis(**redis_config)
pool = redis.ConnectionPool(**redis_config)
red = redis.Redis(connection_pool=pool)
cache = redclass LotteryCache(object):"""奖品信息缓存类,用于抽奖"""@staticmethoddef __get_prize_list_by_level(level, cache_key):"""根据奖品等级获取奖品:param level::return: 1表示已经初始化过 2初始化成功"""# 用redis 的incr 自增方法保证只会初始化一次,不会多次初始化init_cache_key = "_init" + str(level)# 获取初始化数据init = cache.get(init_cache_key)# 如果没有初始化if not init:# 自增,拿到返回数字num = cache.incr(init_cache_key)# 如果返回大于 1,说明不是第一次初始化if num > 1:return 1# 如果已经初始化了,返回1else:return 1# 获取所有奖品prize_list = db.session.query(Prize).filter(Prize.level == level, Prize.bl_get == False).all()data_list = []# 使用redis 事务处理(不用也行,但会慢一些),不设置过期时间with cache.pipeline() as pipeline:# 开启事务pipeline.multi()for prize in prize_list:# 解析json格式为字符串 ,redis 只能保存字符串str_data = json.dumps(prize.to_json())data_list.append(str_data)# 写入list 类型里pipeline.lpush(cache_key, str_data)# 执行命令pipeline.execute()return 2def get_prize_by_level(self, level):"""获取奖品:return:"""cache_key = CACHE_NAME + '_prize_level_' + str(level)# 不存在的话就初始化if not cache.exists(cache_key):num = self.__get_prize_list_by_level(level, cache_key)# 如果已经初始化过了,就返回1# 此时有两种情况,一种是初始化过并且奖品发放完毕 另外一种是正在初始化中# 目前没有想到好的方法来分辨这两种情况,所以只好提前初始化,这样就不会出现第二种情况了if num == 1:return num# 拿取一个prize_data = cache.lpop(cache_key)if prize_data:prize_data = json.loads(prize_data.decode())return prize_data@staticmethoddef add_prize(prize_data):"""用于当抽奖流程报错时,回退奖品:return:"""cache_key = CACHE_NAME + '_prize_list'cache.lpush(cache_key, prize_data)

 


lottery_cache =LotteryCache()def get_prize():"""拿取奖品示例"""# 拿取一个奖品prize_data = lottery_cache.get_prize_by_level(level)# 如果没有拿到奖品,或者返回为 1,都认为抽奖失败了if not prize_data or prize_data == 1:return False, prize_datatry:prize_id = prize_data.get("id")prize_data['openid'] = openidprize_data['create_time'] = datetime.datetime.now()# 更新数据库里的奖品数据Prize.query.filter_by(id=prize_id).update(prize_data)db.session.commit()# 异常捕获except Exception as e:# 事务回滚db.session.rollback()# 删除多余字段if "openid" in prize_data:del prize_data['openid']if "bl_get" in prize_data:del prize_data['bl_get']# 将奖品重新加入list列表中lottery_cache.add_prize(json.dumps(prize_data))# 打印日志,发送消息logger.info("prize_id %s ,openid %s 领取失败  " % (prize_data.get("id"), openid))return False, None# 领取成功,返回奖品信息return True, prize_data

 

经过多次压测,没有出现超发以及重复发的情况。

 

完。

 

 

 

 

 

 

这篇关于使用redis list类型解决卡券类奖品发放问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/614176

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

Vim使用基础篇

本文内容大部分来自 vimtutor,自带的教程的总结。在终端输入vimtutor 即可进入教程。 先总结一下,然后再分别介绍正常模式,插入模式,和可视模式三种模式下的命令。 目录 看完以后的汇总 1.正常模式(Normal模式) 1.移动光标 2.删除 3.【:】输入符 4.撤销 5.替换 6.重复命令【. ; ,】 7.复制粘贴 8.缩进 2.插入模式 INSERT

2024.6.24 IDEA中文乱码问题(服务器 控制台 TOMcat)实测已解决

1.问题产生原因: 1.文件编码不一致:如果文件的编码方式与IDEA设置的编码方式不一致,就会产生乱码。确保文件和IDEA使用相同的编码,通常是UTF-8。2.IDEA设置问题:检查IDEA的全局编码设置和项目编码设置是否正确。3.终端或控制台编码问题:如果你在终端或控制台看到乱码,可能是终端的编码设置问题。确保终端使用的是支持你的文件的编码方式。 2.解决方案: 1.File -> S

Lipowerline5.0 雷达电力应用软件下载使用

1.配网数据处理分析 针对配网线路点云数据,优化了分类算法,支持杆塔、导线、交跨线、建筑物、地面点和其他线路的自动分类;一键生成危险点报告和交跨报告;还能生成点云数据采集航线和自主巡检航线。 获取软件安装包联系邮箱:2895356150@qq.com,资源源于网络,本介绍用于学习使用,如有侵权请您联系删除! 2.新增快速版,简洁易上手 支持快速版和专业版切换使用,快速版界面简洁,保留主

如何免费的去使用connectedpapers?

免费使用connectedpapers 1. 打开谷歌浏览器2. 按住ctrl+shift+N,进入无痕模式3. 不需要登录(也就是访客模式)4. 两次用完,关闭无痕模式(继续重复步骤 2 - 4) 1. 打开谷歌浏览器 2. 按住ctrl+shift+N,进入无痕模式 输入网址:https://www.connectedpapers.com/ 3. 不需要登录(也就是

vcpkg安装opencv中的特殊问题记录(无法找到opencv_corexd.dll)

我是按照网上的vcpkg安装opencv方法进行的(比如这篇:从0开始在visual studio上安装opencv(超详细,针对小白)),但是中间出现了一些别人没有遇到的问题,虽然原因没有找到,但是本人给出一些暂时的解决办法: 问题1: 我在安装库命令行使用的是 .\vcpkg.exe install opencv 我的电脑是x64,vcpkg在这条命令后默认下载的也是opencv2:x6

问题-windows-VPN不正确关闭导致网页打不开

为什么会发生这类事情呢? 主要原因是关机之前vpn没有关掉导致的。 至于为什么没关掉vpn会导致网页打不开,我猜测是因为vpn建立的链接没被更改。 正确关掉vpn的时候,会把ip链接断掉,如果你不正确关掉,ip链接没有断掉,此时你vpn又是没启动的,没有域名解析,所以就打不开网站。 你可以在打不开网页的时候,把vpn打开,你会发现网络又可以登录了。 方法一 注意:方法一虽然方便,但是可能会有