百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

Redis+Lua解决高并发场景抢购秒杀问题

wptr33 2024-12-18 17:32 35 浏览

转 载: itbsl

原文链接:https://www.cnblogs.com/itbsl/p/15021263.html

今天介绍一些如何使用PHP+Redis+Lua解决高并发下商品超卖问题。

为何要使用Lua脚本解决商品超卖的问题呢?

  • Redis在2.6版本后原生支持Lua脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。
  • 将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数,提升性能。
  • 原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现静态条件,无需使用事务。
  • 复用。客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。

首先,编写lua脚本,脚本名为secKill.lua:

-- 接收参数
local user_id  = KEYS[1]
local goods_id = KEYS[2]

-- 拼接字符串
local stock_key = "secKill:"..goods_id..":stock" -- 秒杀商品库存key
local users_key = "secKill:"..goods_id..":users" -- 成功秒杀商品的用户集合key

-- 判断用户是否已经成功秒杀过该商品,如果已经存在在集合中,说明已经成功秒杀该商品,直接返回标志2,防止重复抢购
local user_exists = redis.call('sismember', users_key, user_id)
if tonumber(user_exists, 10) == 1 then
    return 2
end

-- 获取当前商品库存,如果库存小于等于0,表名商品已经被抢购完了,否则库存-1,并将抢购成功的用户放入集合中
local left_goods_count = redis.call('get', stock_key)
if tonumber(left_goods_count, 10) <= 0 then
    return 0
else
    redis.call('decr', stock_key)
    redis.call('sadd', users_key, user_id)
end
return 1

上述代码中返回的数字0,1,2只是一种约定,自己可以根据自己的有业务约定不同状态返回的值。示例代码0:库存为0,1:秒杀成功,2:已秒杀成功的用户重复抢购。

lua脚本编写完成后,使用redis-cli命令生成该脚本的sha秘钥

redis-cli script load "$(cat /usr/local/redis/lua/secKill.lua)"
"63454a53284d9f6b30bdb6e5e12796a74f61f718"

最后,拿到lua脚本的sha秘钥,我们就可以在我们的代码中使用了。

$redis = new Redis();
$redis->connect("192.168.111.128", 6379);
$goodsId = 11211;
$userId = mt_rand(10000, 99999);
$res = $redis->evalSha('63454a53284d9f6b30bdb6e5e12796a74f61f718', [$userId, $goodsId], 2);

可以看到,我们将抢购逻辑写到lua脚本后,PHP代码就变得很少了,仅仅只有5行代码。

编写好代码,接着我们开始对上述代码进行测试。

首先,我们需要设置商品的库存量,正常逻辑是在后台商品管理页填写具体商品的库存量,此处假设我们的商品ID是11211(这个数字是不是很熟悉?是的,这是memcached的默认端口),商品数量为10个。

$redis-cli
> set secKill:11211:stock 10

我们使用ab压测工具模拟2000个用户并发量200来模拟抢购商品ID为11211的商品。

$ ab -n 2000 -c 200 http://www.master.com/index.php

如果没有ab工具需要使用 yum -y install httpd-tools安装

压测完成后,我们通过RedisDesktopManager(RDM)软件来查看抢购结果,可以看到即使是200的并发量,最终也只有10个用户抢购到商品,并且抢购成功的用户被写入到了secKill:11211:users的集合中,我们可以另外开一个守护进程专门用于从集合中获取用户ID处理后续事宜(将数据落盘写入数据库、给用户发短信等)

使用Redis+Lua来解决抢购秒杀类问题是当前比较流行的一种做法,希望对正在开发秒杀抢购功能的你能产生帮助。

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...