新客領(lǐng)取優(yōu)惠券邏輯
判斷是否新客
若為新客 查詢數(shù)據(jù)庫是否已領(lǐng)取過
未領(lǐng)取過 給用戶發(fā)券
同一用戶并發(fā)領(lǐng)券的情況下 會有超領(lǐng)的情況 因為并發(fā)進(jìn)來時查詢數(shù)據(jù)庫均沒有領(lǐng)取記錄
而又不能針對用戶和優(yōu)惠券做唯一索引 因為有些券允許用戶領(lǐng)取多張。
于是決定采用redis計數(shù)來防同一用戶并發(fā)超領(lǐng) 新客領(lǐng)券邏輯改成如下
判斷是否新客
若為新客 基于用戶ID計數(shù) incr {user_id}_receive_count
如果計數(shù)大于1 表示同一用戶并發(fā)領(lǐng)券 直接返回
如果計數(shù)==1 表示第一次領(lǐng)取
查詢數(shù)據(jù)庫是否已領(lǐng)取過
未領(lǐng)取過 給用戶發(fā)券
上述邏輯沒有問題 但糾結(jié)該如何設(shè)置計數(shù)key的過期時間
設(shè)置過期時間為1分鐘
基本上大部分并發(fā)操作都是在同一秒 過期時間1分鐘能滿足要求了
設(shè)置過期時間為1小時
萬一數(shù)據(jù)庫負(fù)載高呢 并不能在一分鐘內(nèi)完成一次領(lǐng)券事務(wù)操作 如雙十一這種場景 還是設(shè)的長一點(diǎn) 更保險一點(diǎn) 不管怎樣 1小時足夠完成一次領(lǐng)券事務(wù)操作了吧 但時間設(shè)的長了 會不會因為其他原因(如網(wǎng)絡(luò)超時)導(dǎo)致領(lǐng)券失敗回滾 用戶要等一小時后 才能重新領(lǐng)券
有沒其他防并發(fā)領(lǐng)取方案呢? 可以不用糾結(jié)這種過期時間的設(shè)置呢。
Il est recommandé de séparer le jugement de la prévention d'une concurrence excessive et celui de juger si un nouveau client est un nouveau client
Le super collier anti-concurrence peut être utilisé comme suit (verrouillé) N'oubliez pas de supprimer la clé (libérer le verrou) une fois chaque opération terminée
// 操作的原子性,如該key在有效時間30秒被設(shè)置過返回0,一般請求超時為30秒
$redis->set($key, 1, array("NX", "EX"=>'30'));
Pour déterminer s'il s'agit d'un nouveau client (si l'utilisateur l'a déjà re?u, l'heure de validité est fixée à la fin de l'événement), vous pouvez définir une nouvelle CLé comme statut de réception de l'utilisateur, et elle sera directement restitué à l'utilisateur qui a bénéficié de la remise (poursuite de Pour des performances concurrentes, la requête de base de données n'est pas connectée)
Obtenez le coupon (mettez le coupon dans la file d'attente à l'avance et récupérez-le dans la file d'attente, pas dans la base de données. L'avantage ici est que les données de la file d'attente dispara?tront une fois récupérées, et il y aura pas de situation de réception excessive), écrivez Entrez la file d'attente redis
File d'attente de consommation du processus en arrière-plan, j'utilise le type de blocage de liste cha?née pour la consommation (stocker les informations de collecte dans la base de données, puis interroger et déterminer si l'utilisateur a re?u le coupon avant d'entrer dans la base de données)
Le plan précis reste à mettre en ?uvre en fonction des métiers
Si l'entreprise n'exige pas 100%, chaque personne ne peut recevoir qu'un seul coupon, et votre cluster Redis est hautement disponible. Il suffit alors de le bloquer avec Redis
, le délai d'expiration spécifique de la clé, etc. . Le téléchargement de l'activité sera globalement invalide, car il s'agit d'une solution qui ne rentre pas dans la bibliothèque, donc si vous devez déterminer s'il faut répéter la demande, vous devez vous fier aux données de Redis (et si Redis se bloque). up ? Bien que Redis ait une fonction de persistance imparfaite, la perte La quantité de données est relativement faible, si l'entreprise le permet, elle est toujours acceptable), mais de nombreux scénarios commerciaux nécessitent une journalisation
Les exigences commerciales exigent qu'une personne ait un coupon (la valeur nominale du coupon peut être très importante), j'utiliserai donc toujours la solution Redis
du sujet. Après avoir jugé que c'est la première fois, insérez/mettez à jour directement la base de données (il y aura des solutions asynchrones ici, mais des solutions asynchrones appara?tront également. Les solutions asynchrones auront également des problèmes similaires à 1 : perte de données. Selon cette solution, on peut garantir que chaque personne ne peut que recevoir). un coupon (déterminé par la base de données) assure la cohérence des données). Comme l'a dit l'intervenant, en fait, vous n'avez pas besoin d'invalider les éléments dans Redis, car si un utilisateur demande à plusieurs reprises et de manière malveillante de tirer des coupons, Redis peut le bloquer directement sans accéder à la base de données pour interroger (il y a un autre problème ici. La solution consiste à mettre à jour le cache d'abord. Mettez à jour à nouveau la base de données, vous devez donc considérer la situation anormale dans laquelle la mise à jour du cache réussit mais la mise à jour de la base de données échoue. Si la base de données est anormale, vous devez vider le cache correspondant à la clé. )
Si vous ne pouvez vraiment pas le gérer (le matériel ne peut pas suivre), vous pouvez utiliser des files d'attente pour résoudre le problème et utiliser des files d'attente pour réduire le pic
La solution de @大woo est également réalisable, mais vous devez considérer Redis
cette situation anormale à un certain moment
La fonction mentionnée par @大woo est en fait similaire aux ventes flash. J'ai déjà écrit un article. Vous pouvez y jeter un oeil :
Fonctions essentielles pour les produits e-commerce : ventes flash et enchères
Je suis ivre si vous osez utiliser une base de données nosql pour ce genre de chose.
Il est recommandé d'apprendre d'abord la théorie des bases de données, en se concentrant sur le concept de transactions, plusieurs niveaux d'isolation des transactions, les verrous de lignes et de tables, puis d'utiliser une base de données relationnelle traditionnelle pour ce faire.
Si les performances d'un serveur de base de données ne suffisent pas, alors concevez une architecture de tables horizontales, avec différents serveurs, chaque serveur étant responsable d'une partie des données utilisateur. La source théorique de la table de niveaux est le chapitre de hachage des structures de données de premier cycle. Vous pouvez y jeter un ?il.