新客领取优惠券逻辑
判断是否新客
若为新客 查询数据库是否已领取过
未领取过 给用户发券
同一用户并发领券的情况下 会有超领的情况 因为并发进来时查询数据库均没有领取记录
而又不能针对用户和优惠券做唯一索引 因为有些券允许用户领取多张。
于是决定采用redis计数来防同一用户并发超领 新客领券逻辑改成如下
判断是否新客
若为新客 基于用户ID计数 incr {user_id}_receive_count
如果计数大于1 表示同一用户并发领券 直接返回
如果计数==1 表示第一次领取
查询数据库是否已领取过
未领取过 给用户发券
上述逻辑没有问题 但纠结该如何设置计数key的过期时间
设置过期时间为1分钟
基本上大部分并发操作都是在同一秒 过期时间1分钟能满足要求了
设置过期时间为1小时
万一数据库负载高呢 并不能在一分钟内完成一次领券事务操作 如双十一这种场景 还是设的长一点 更保险一点 不管怎样 1小时足够完成一次领券事务操作了吧 但时间设的长了 会不会因为其他原因(如网络超时)导致领券失败回滚 用户要等一小时后 才能重新领券
有没其他防并发领取方案呢? 可以不用纠结这种过期时间的设置呢。
PHP中文网2017-04-27 09:04:40
過剰同時防止の判断と新規顧客かどうかの判断は分けて行うことをお勧めします
同時実行防止スーパーカラーは以下を使用できます (ロック)。各操作の後にキーを削除する (ロックを解放する) ことを忘れないでください
ユーザーが新規顧客かどうか(ユーザーが以前に受信したかどうか、有効時間はイベントの終了までに設定されます)を判断するために、受信後にユーザーの受信ステータスとして新しいKEYを設定できます。割引を受けたユーザーに直接返されます(同時実行パフォーマンスを追求する場合、データベースクエリへの接続はありません)
クーポンを取得します (クーポンを事前にキューに入れ、データベースからではなくキューから取得します。ここでの利点は、キューのデータが取得されると削除され、過剰受信が発生しないことです。状況)、それを Redis キューに書き込みます
バックグラウンドプロセスの消費キュー、消費にはリンクリストブロックタイプを使用します(コレクション情報をデータベースに保存し、データベースに入る前にユーザーがクーポンを受け取ったかどうかをクエリして判断します)
黄舟2017-04-27 09:04:40
ビジネスに応じて具体的な計画を実行する必要があります
ビジネスが 100% を必要としない場合、各人が受け取ることができるクーポンは 1 つだけであり、Redis クラスターの可用性が高いため、Redis
を使用してブロックするだけで十分です。アクティビティがダウンロードされた後は、特定のキーの有効期限が無効になります。これはライブラリに含まれないソリューションであるため、リクエストを繰り返すかどうかを決定する必要がある場合は、Redis のデータに依存する必要があります (Redis の場合)。ハングします。ただし、Redis の価格比較は不完全です) 永続化機能ですが、失われるデータの量は比較的少ないため、ビジネスが許可している場合はまだ許容されます)、ただし、多くのビジネス シナリオではログアウトが必要です Redis
来挡就足够了,具体Key的失效时间,等活动下载再整体失效,因为这是一个不落库的方案,所以如果需要判断是否重复请求需要依赖Redis里面的数据(万一Redis挂了呢,虽然Redis有比价不完善的持久化功能,但是丢的数据量比较少,如果如果业务上允许,还是可以接受的),但是很多场景业务都需要要求落库
业务上要求,必须一人一券(可能券的面值很大),那么前面还是用题主的Redis
方案,在判断完是第一次以后,直接插/更新数据库(这里就会有异步的方案,但是异步的方案也会出现,异步的方案也会出现与1类似的问题:丢数据),按照这个方案,可以保证每个人只能领一张券(由数据库来保证数据的一致性)。如题主所说,其实你不需要把Redis里面的东西失效掉,因为如果一个用户重复恶意请求抽券,直接走Redis来挡,不用去数据库查询(这里还会一个问题,该方案是先更新缓存再更新数据库,所以需要考虑缓存更新成功,但是数据库更新失败的异常情况,如果数据库异常需要清理该Key对应的缓存)
如果实在扛不住(硬件跟不上),可以用队列来解决,用队列来削峰
@大呜 的方案也是可以行的,但是需要考虑某一时刻Redis
Redis
ソリューションが有効であると判断する前に引き続き使用します。初めてです。データベースを直接挿入/更新します (ここには非同期ソリューションもありますが、非同期ソリューションにも 1: データの損失と同様の問題が発生する可能性があります)。誰もが A クーポンのみを受け取ることができることが保証されます (データの一貫性はデータベースによって保証されます)。質問者が言ったように、実際には、ユーザーが悪意を持ってクーポンの描画を繰り返し要求した場合、Redis はデータベースに問い合わせることなく直接ブロックできるため、Redis 内の内容を無効にする必要はありません (ここには別の問題があります。)解決策は、まずキャッシュを更新することです。そのため、キャッシュの更新は成功したが、データベースの更新が失敗した場合は、該当するキャッシュをクリアする必要があります。鍵)#🎜🎜##🎜🎜#
#🎜🎜##🎜🎜#本当に対処できない場合(ハードウェアが追いつかない場合)、キューを使用して問題を解決し、キューを使用してピークをカットできます#🎜🎜##🎜🎜#
#🎜🎜##🎜🎜#@Daiwoo の解決策も実現可能ですが、ある瞬間の Redis
の異常な状況を考慮する必要があります #🎜🎜##🎜🎜#
#🎜🎜#淡淡烟草味2017-04-27 09:04:40
この種のことにあえて nosql データベースを使用するのは私も酔っています。
まず、トランザクションの概念、トランザクションのいくつかの分離レベル、行ロックとテーブル ロックに焦点を当ててデータベースの理論を学習し、それから従来のリレーショナル データベースを使用してこれを行うことをお勧めします。
データベース サーバーのパフォーマンスが十分でない場合は、異なるサーバーを備えた水平テーブル アーキテクチャを設計し、各サーバーがユーザー データの一部を担当します。レベルテーブルの理論的なソースは、学部レベルのデータ構造のハッシュの章です。