flask+redis实现抢购(秒杀)功能

本文介绍了一种使用Redis实现的高效抢购系统。通过结合Redis的Hash和List数据类型,确保了在高并发环境下,每个用户仅能抢购一件商品,并有效管理库存。文章详细解释了如何利用Redis的特性来防止超卖,以及如何通过Python Flask应用实现这一功能。

今天面试了 一家非常高大上的公司,问了我关于redis的实用性问题,但是答的不是很好,所以下午通过再次学习 redis,实现相关实用性功能的一种。

 

对于抢购功能,难点在于 抢购时 由于高并发请求,导致一个用户抢购多件商品,库存量小于订单量的情况。

如下通过redis的hash和list类型实现相关功能。

思路:

hash:主要用来存储用户抢购成功的信息,因其自身的特性,如果hash的key,val重复,会返回0,从而判断一个用户只能抢购一个商品。

list:主要用来存放商品,在每个请求进来时,从list中pop一个商品,这样做到针对redis(货物)做到单线程(无论并发多少个请求)。

整体思路:利用hash的不可重复特性和list, 在请求进来时从list中pop一个商品,然后添加到hash中,如果添加失败,就再次push一个商品到list中。

 

from flask import Flask, request
from flask.views import MethodView

app = Flask(__name__)
REDIS_CONF = {
    'host': '127.0.0.1',
    'port': 6379,
    'db': 1
}
app.config.update({'REDIS_CONF': REDIS_CONF})

from redis import StrictRedis
import random

REDIS = StrictRedis(**REDIS_CONF)


class GetGoods(MethodView):
    def post(self):
        uid = random.randint(1, 10)
        if REDIS.lpop('goods_list'):
            if REDIS.hset('user_list', uid, 1):
                print(f'Success,{uid}')
                return f'Success,{uid}'
            else:
                # 不可重复抢(每人限领一个)
                print(f'push ,{uid}')
                REDIS.lpush('goods_list', 1)
                return f'create a user {uid}'
        else:
            # 已抢完
            print('Finsh!')
            return 'Finsh!'

    def get(self):
        user_list = REDIS.hgetall('user_list')
        user_list_len = REDIS.hlen('user_list')
        goods_list = REDIS.llen('goods_list')
        result_dict = {"user_list": user_list, "user_list_len": user_list_len, 'goods_list': goods_list}
        print(result_dict)
        return 'success!'


class SendGoods(MethodView):
    def post(self):
        count = request.form.get('count')
        if REDIS.exists('goods_list'):
            print('delet exists goods')
            REDIS.delete('goods_list')

        for item in range(int(count)):
            REDIS.lpush('goods_list', 1)
        REDIS.delete('user_list')
        goods_list = REDIS.lrange('goods_list', 0, count)
        return f'send goods success! {goods_list}'


# 用户抢购接口
app.add_url_rule('/goods', view_func=GetGoods.as_view('goods'), methods=['POST'])
# 商家查看商品抢购结果
app.add_url_rule('/goods', view_func=GetGoods.as_view('get_goods'), methods=['GET'])
# 商家发布商品
app.add_url_rule('/send/goods', view_func=SendGoods.as_view('send_goods'), methods=['POST'])

app.run(host='127.0.0.1', port=8000, threaded=10, debug=True)

 

通过postman测试:

先执行  商家发布商品 接口,发送100个商品。

然后并发压力测试  商家查看商品抢购结果 接口。

然后执行 商家查看商品抢购结果 接口得到如下结果:

发布100个商品,只有10个人抢购1000此,结果做到了每人一个商品,剩下90个商品。

 

转载于:https://www.cnblogs.com/rgcLOVEyaya/p/RGC_LOVE_YAYA_836days.html

### FlaskRedis及MySQL集成的教程和文档 #### 集成概述 Flask 是一种轻量级的 Python Web 框架,而 Redis 和 MySQL 则分别提供了缓存服务和关系型数据库支持。通过将这些技术结合起来,可以构建高效、可扩展的应用程序。 #### 使用 Redis 的原因 Redis 可以用来存储会话数据、缓存查询结果以及实现消息队列等功能。这种设计能够显著提高应用性能[^1]。 #### 安装依赖项 为了使 Flask 应用能与 Redis 和 MySQL 进行交互,需先安装必要的库: ```bash $ pip install flask sqlalchemy pymysql redis flask-session ``` 上述命令中包含了 `sqlalchemy`(ORM 工具)、`pymysql`(MySQL 数据库驱动)、`redis`(客户端库)以及 `flask-session`(用于管理基于 Redis 的会话)。 #### 配置 MySQL 数据库连接 在 Flask 中可以通过 SQLAlchemy 来简化对 MySQL 数据库的操作。下面展示如何设置数据库 URI 并将其赋值给环境变量: ```python import os from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://username:password@localhost/db_name' db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) @app.route('/') def index(): users = User.query.all() return str(users) ``` 此代码片段定义了一个简单的模型类 `User` 并展示了如何从表中检索记录[^3]。 #### 整合 Redis 缓存机制 引入 Redis 后,我们可以利用其快速读写特性来减少频繁访问数据库的需求。这里提供一段示例说明如何配置 Redis 作为后端存储 session 数据的方法之一: ```python from flask_session import Session app.config["SESSION_TYPE"] = "redis" app.config["SESSION_PERMANENT"] = False app.config["SESSION_USE_SIGNER"] = True app.config["SESSION_REDIS"] = redis.from_url("redis://localhost:6379") Session(app) ``` 以上脚本设置了使用 Redis 存储 sessions,并启用了签名功能以增强安全性. #### 实际应用场景举例 假设有一个博客系统需要显示文章列表页面,在高并发情况下直接查询 MySQL 性能可能会下降。此时就可以考虑把热门文章的数据放入 Redis 缓存起来,只有当数据发生变化时才更新到 Redis 中去。 ```python cache = redis.StrictRedis(host='localhost', port=6379, decode_responses=True) @app.route('/posts') def posts(): cached_posts = cache.get('all_posts') if not cached_posts: all_posts = Post.query.order_by(Post.timestamp.desc()).all() # 假设Post为对应的model对象 serialized_posts = [post.serialize for post in all_posts] cache.setex('all_posts', timedelta(minutes=5), json.dumps(serialized_posts)) return jsonify(serialized_posts) else: return jsonify(json.loads(cached_posts)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值