新客领取优惠券逻辑
判断是否新客
若为新客 查询数据库是否已领取过
未领取过 给用户发券
同一用户并发领券的情况下 会有超领的情况 因为并发进来时查询数据库均没有领取记录
而又不能针对用户和优惠券做唯一索引 因为有些券允许用户领取多张。
于是决定采用redis计数来防同一用户并发超领 新客领券逻辑改成如下
判断是否新客
若为新客 基于用户ID计数 incr {user_id}_receive_count
如果计数大于1 表示同一用户并发领券 直接返回
如果计数==1 表示第一次领取
查询数据库是否已领取过
未领取过 给用户发券
上述逻辑没有问题 但纠结该如何设置计数key的过期时间
设置过期时间为1分钟
基本上大部分并发操作都是在同一秒 过期时间1分钟能满足要求了
设置过期时间为1小时
万一数据库负载高呢 并不能在一分钟内完成一次领券事务操作 如双十一这种场景 还是设的长一点 更保险一点 不管怎样 1小时足够完成一次领券事务操作了吧 但时间设的长了 会不会因为其他原因(如网络超时)导致领券失败回滚 用户要等一小时后 才能重新领券
有没其他防并发领取方案呢? 可以不用纠结这种过期时间的设置呢。
PHP中文网2017-04-27 09:04:40
It is recommended to separate the prevention of over-concurrency and the determination of whether a new customer is a new customer
Anti-concurrency super collar can use the following (lock), remember to delete the key (release the lock) after each operation
// 操作的原子性,如该key在有效时间30秒被设置过返回0,一般请求超时为30秒
$redis->set($key, 1, array("NX", "EX"=>'30'));
To determine whether the user is a new customer (whether the user has received it before, the valid time is set to the end of the event), you can set a new KEY as the user's receiving status. After receiving it, it will be directly returned to the user who has received the discount (when pursuing concurrency performance, No connection to database query)
Get the coupon (put the coupon into the queue in advance and get it from the queue, not from the database. The advantage here is that the queue data will be gone once it is retrieved, and there will be no over-receipt situation), and write it into the redis queue
Background process consumption queue, I use linked list blocking type for consumption (store the collection information into the database, and query and determine whether the user has received the coupon before entering the database)
黄舟2017-04-27 09:04:40
The specific plan still needs to be implemented according to the business
If the business does not require 100%, each person can only receive one coupon, and your Redis cluster is highly available. Then it is enough to block it with Redis
. The specific Key expiration time will be invalid after the activity is downloaded. Because this is a solution that does not fall into the database, if you need to determine whether to make repeated requests, you need to rely on the data in Redis (what if Redis crashes? Although Redis has an imperfect persistence function, the amount of data lost is relatively small. If the business allows it, it is still acceptable), but many business scenarios require logging out
Business requirements require that one person must have one coupon (the face value of the coupon may be very large), so the topic owner’s Redis
plan should be used in the front. After judging that it is the first time, directly insert/update the database (there will be asynchronous here scheme, but asynchronous schemes will also appear, and asynchronous schemes will also have problems similar to 1: losing data). According to this scheme, it can be guaranteed that each person can only receive one coupon (the database ensures the consistency of the data) . As the questioner said, in fact, you don’t need to invalidate the things in Redis, because if a user repeatedly maliciously requests to draw coupons, Redis can directly block it without going to the database to query (there is another problem here. The solution is to update the cache first. Update the database again, so you need to consider the abnormal situation where the cache update is successful but the database update fails. If the database is abnormal, you need to clear the cache corresponding to the key)
If you really can’t handle it (the hardware can’t keep up), you can use queues to solve the problem and use queues to cut peaks
@大woo's solution is also feasible, but you need to consider this abnormal situation at a certain momentRedis
滿天的星座2017-04-27 09:04:40
@大woo The function mentioned is actually similar to flash sales. I wrote an article before. You can take a look at:
Essential functions for e-commerce products: flash sales and bidding
淡淡烟草味2017-04-27 09:04:40
You dare to use nosql database for this kind of thing. I’m also drunk.
It is recommended to learn the theory of database first, focusing on the concept of transactions, several isolation levels of transactions, row locks and table locks, and then use a traditional relational database to do this.
If the performance of a database server is not enough, then design a horizontal table architecture, with different servers, each server responsible for a part of the user data. The theoretical source of the level table is the hash chapter of undergraduate-level data structures. You can take a look at it.