For some e-commerce websites with a certain number of users, if they simply use relational databases (such as MySQL, Oracle) for rush purchases, the pressure on the database will be very great, and if the database lock mechanism is not used properly, , it will also lead to the problem of oversold products and coupons. My company also encountered the same problem. The problem occurred when coupons were over-purchased. After the problem occurred, we began to think of ways to solve the problem. Since I use redis a lot, I plan to use redis to solve this problem. Use the high performance and transaction features of redis to solve the problem of online coupons being snapped up by overstock. Below I give the first version of my pseudo-code to temporarily solve this problem, with some details removed:
/** * 抢优惠券(秒杀) * @param int $couponId 商品ID * @param int $uid 用户ID * @return bool */ function secKill($couponId, $uid) { //1.初始化Redis连接 $redis = new Redis(); if (!$redis->connect('127.0.0.1', 6379)) { trigger_error('Redis连接出错!!!', E_USER_ERROR); } else { echo '连接正常<br>'; } //秒杀商品的库存key $key = 'secKill:'.$couponId.':stock'; $redis->watch($key); //获取库存 $stock = $redis->get($key); //秒杀未开始,表示库存为null if (!$stock && !is_numeric($stock)) { echo '秒杀未开始'; return false; } //判断库存,如果库存大于0,则减库存,将该成功秒杀用户加入哈希表,如果小于等于0,秒杀结束 if ($stock <= 0) { echo '秒杀已结束'; return false; } //用户已经成功秒杀过一次了,不允许再次参与秒杀 if ($redis->sIsMember('secKill:'.$couponId.':uid', $uid)) { echo '秒杀失败'; return false; } //代码走到这里,说明该用户是第一次参与秒杀,将库存减一,然后把这个人放到已抢到的集合表 $redisMulti = $redis->multi(); $redisMulti->decr($key); $redisMulti->sAdd('secKill:'.$couponId.':uid', $uid); $result = $redisMulti->exec(); if (empty($result)) {//事务被取消 echo '秒杀失败'; return false; } //抢券成功,将优惠券ID和UID放入到队列中,由一个单独的进程队列来消费队列里的数据,向用户推送抢到的优惠券 $redis->lPush('couponOrder', $couponId.'+'.$uid); return true; } $couponId = 11211; $uid = mt_rand(1, 100); secKill($couponId, $uid);
First, I simulated setting the coupon inventory with coupon ID 11211 to 10.
Then, we use the ab tool to simulate 1,000 requests and 50 concurrency to test
ab -n 1000 -c 50 www.test.com/
Then we use the Redis Desktop Manager to view some Redis Result
There is information about 10 users in the couponOrder queue
And the remaining number of coupons is also 0, no longer a negative number
#At the same time, the UID information of 10 users is also saved in the user coupon collection.
The above string of codes solves two problems:
It solves the problem of a large number of instantaneous queries to the database causing a lot of damage to the database. The problem of high pressure, the traffic is intercepted in the redis cache layer
Solved the problem of coupons being snapped up by overstock
However, This code also has certain problems:
The redis connection pool is not used, and frequent creation of new redis has certain performance impacts
Due to Using transactions, only one user will succeed in grabbing tickets in each concurrent request. Other users in the concurrent request will fail and can only wait for the second concurrency
It is still a transaction The resulting inventory problem is that if there are 10 products and 1,000 requests each have 200 concurrent requests, 1,000 requests will be completed with 5 concurrent requests, but only 5 users will successfully grab them. If there are no subsequent requests, there will be As a result, there are still 5 copies in stock
Tip: In the consumption queue, if the coupon issuance fails, be sure to record it immediately and notify the operation manager via text message to see if it can be reissued or Pushed to users through manual targeting in the background.
The above is the detailed content of How to use PHP+Redis to solve the problem of overselling products under high concurrency. For more information, please follow other related articles on the PHP Chinese website!