Redis + Lua solves the problem of rush buying and second killing in high concurrency scenarios

Time:2022-1-6

I wrote an article beforePHP + redis linked list to solve the problem of oversold of high and issued goodsToday, I’ll introduce how to use PHP + redis + Lua to solve the problem of high and oversold goods.

Why use Lua script to solve the problem of oversold?

  • After version 2.6, redis natively supports Lua script function, allowing developers to write scripts in Lua language and transfer them to redis for execution.
  • Write complex or multi-step redis operations into a script and submit them to redis for execution at one time, so as to reduce the number of repeated connections to redis and improve performance.
  • Atomic operation. Redis will execute the entire script as a whole and will not be inserted by other requests. Therefore, there is no need to worry about race conditions and transactions during script operation.
  • Reuse. The script sent by the client will be permanently stored in redis, so that other clients can reuse this script without using code to complete the same logic.

First, write a Lua script called seckill lua:

--Receive parameters
local user_id  = KEYS[1]
local goods_id = KEYS[2]

--Splice string
local stock_ key = "secKill:".. goods_ id.. ": stock" -- second kill commodity inventory key
local users_ key = "secKill:".. goods_ id.. ": users" -- the set of users who successfully killed the commodity key

--Judge whether the user has successfully killed the commodity. If it already exists in the collection, it indicates that the commodity has been successfully killed. Directly return flag 2 to prevent repeated rush buying
local user_exists = redis.call('sismember', users_key, user_id)
if tonumber(user_exists, 10) == 1 then
    return 2
end

--Get the current commodity inventory. If the inventory is less than or equal to 0, the table name commodities have been snapped up. Otherwise, the inventory is - 1, and the users who have successfully snapped up are put into the collection
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

The numbers 0, 1 and 2 returned in the above code are just conventions. You can return values in different states according to your own business conventions. Example code 0: the inventory is 0, 1: the second kill is successful, 2: repeated rush purchases by users who have succeeded in the second kill.

After the Lua script is written, use the redis cli command to generate the Sha secret key of the script

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

Finally, get the Sha key of lua script and we can use it in our code.

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

As you can see, after we write the rush purchase logic to the Lua script, the PHP code becomes very few, only 5 lines of code.

After writing the code, we begin to test the above code.

First, we need to set the inventory of goods. The normal logic is to fill in the inventory of specific goods on the background commodity management page. Here, it is assumed that our commodity ID is 11211 (is this number familiar? Yes, this is the default port of memcached), and the quantity of goods is 10.

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

We use AB pressure measurement tool to simulate 2000 users and 200 concurrent users to simulate the rush purchase of goods with commodity ID 11211.

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

If there are no AB tools, you need to use Yum – y install httpd tools to install

After the pressure test is completed, we can view the rush purchase results through redisdesktopmanager (RDM) software. We can see that even with a concurrent volume of 200, only 10 users rush to buy goods, and the successful users are written into the seckill: 11211: users collection, We can open another daemon to obtain the user ID from the collection and handle subsequent matters (write the data to the database, send text messages to users, etc.)

Using redis + Lua to solve the problem of rush buying is a popular method at present. I hope it can help you who are developing the rush buying function.