公司做抽奖或者红包活动,总有人恶意大访问量请求,查询mysql去做重复验证在大并发上限制不住,总会有重复插入,会造成多发奖品。
想用redis做mysql的缓存,但是现在遇到的问题是如何把redis的数据写回mysql,不可能每次校验的时候就写回mysql,那样的话根本没有解决问题。
现在的想法是能否利用php,或者其他什么技术,定时将redis中的数据写回mysql。程序只与redis交互。
希望能给出具体的逻辑或者解决方案,网上的回答都太笼统了,根本解决不了问题。
谢谢。
1月16日补充:
我问题描述的不太清晰。
我把遇到的问题详细的说一下吧,并不是发奖时候脏读的问题,而是判断这个人是否有机会抽奖。
有一个抽奖的表,如果这个人抽过奖了,就在表中插入一条记录。
目前的实现方式是:
<code>1.读取抽奖表判断这个人是否抽过奖 2.如果表中有记录,那就是抽过了,直接告诉没机会 3.如果表中没有记录,走抽奖逻辑,然后插入抽奖表</code>
正常情况下是没有问题的,
但是有人用恶意脚本进行刷奖,也就是同一个人发起大量请求,1秒可能一两百的请求甚至更多,而且不只一个人刷奖。
问题出在1这一步
举个例子,假设每人只能抽一次奖,因为请求太快,同一人的a,b两个请求几乎同时来,a走完抽奖逻辑了,并且在抽奖表中插入记录的过程时,因为mysql的性能的问题,b去走1这一步是读不到表中的记录的,因为a的插入根本没有完成。所以b请求会再走一次抽奖逻辑。造成同一人抽奖两次,然后再插入抽奖表。
我关心的是能否a插入抽奖表的瞬间,b就能判断出抽奖表有数据。
所以我觉得问题是mysql写入的不够快,读取的不够快,所以我要采用redis做一层快速缓存。
我们做的抽奖是单一奖品百分之百中奖,只限制奖品数量,所以必须保证每人只能抽一次,而且尽量在程序层面去解决。
1月20日补充
看了大家的回答很受启发,非常感谢。
至于有人提到并发没想象的那么大这件事,是这样的,我们的服务器是阿里云单台的ECS,配置4核8G 独享50m宽带,centos,只做对外活动。
这是12月24号上线的一个不到4小时活动的抽奖活动的浏览量。
这是当时TCP连接数的监控
抽奖不同于网站访问,这些参与者基本绝大多数是同一时间段来访问服务器。我个人觉得在服务器配置不升级的情况下,软件层面的优化完全能扛住这些访问。我目前觉得瓶颈在mysql。
回复内容:
公司做抽奖或者红包活动,总有人恶意大访问量请求,查询mysql去做重复验证在大并发上限制不住,总会有重复插入,会造成多发奖品。
想用redis做mysql的缓存,但是现在遇到的问题是如何把redis的数据写回mysql,不可能每次校验的时候就写回mysql,那样的话根本没有解决问题。
现在的想法是能否利用php,或者其他什么技术,定时将redis中的数据写回mysql。程序只与redis交互。
希望能给出具体的逻辑或者解决方案,网上的回答都太笼统了,根本解决不了问题。
谢谢。
1月16日补充:
我问题描述的不太清晰。
我把遇到的问题详细的说一下吧,并不是发奖时候脏读的问题,而是判断这个人是否有机会抽奖。
有一个抽奖的表,如果这个人抽过奖了,就在表中插入一条记录。
目前的实现方式是:
<code>1.读取抽奖表判断这个人是否抽过奖 2.如果表中有记录,那就是抽过了,直接告诉没机会 3.如果表中没有记录,走抽奖逻辑,然后插入抽奖表</code>
正常情况下是没有问题的,
但是有人用恶意脚本进行刷奖,也就是同一个人发起大量请求,1秒可能一两百的请求甚至更多,而且不只一个人刷奖。
问题出在1这一步
举个例子,假设每人只能抽一次奖,因为请求太快,同一人的a,b两个请求几乎同时来,a走完抽奖逻辑了,并且在抽奖表中插入记录的过程时,因为mysql的性能的问题,b去走1这一步是读不到表中的记录的,因为a的插入根本没有完成。所以b请求会再走一次抽奖逻辑。造成同一人抽奖两次,然后再插入抽奖表。
我关心的是能否a插入抽奖表的瞬间,b就能判断出抽奖表有数据。
所以我觉得问题是mysql写入的不够快,读取的不够快,所以我要采用redis做一层快速缓存。
我们做的抽奖是单一奖品百分之百中奖,只限制奖品数量,所以必须保证每人只能抽一次,而且尽量在程序层面去解决。
1月20日补充
看了大家的回答很受启发,非常感谢。
至于有人提到并发没想象的那么大这件事,是这样的,我们的服务器是阿里云单台的ECS,配置4核8G 独享50m宽带,centos,只做对外活动。
这是12月24号上线的一个不到4小时活动的抽奖活动的浏览量。
这是当时TCP连接数的监控
抽奖不同于网站访问,这些参与者基本绝大多数是同一时间段来访问服务器。我个人觉得在服务器配置不升级的情况下,软件层面的优化完全能扛住这些访问。我目前觉得瓶颈在mysql。
可以考虑crontab跑定时任务 执行php脚本去写入 或者跑守护进程去写
先简单的意淫一下题主的问题。
查询mysql去做重复验证在大并发上限制不住,总会有重复插入
我想题主这个原因主要是因为,需要把某些信息从数据库查出来,然后再比较,最后插入数据库吧?所以这里有可能出现脏读的问题。所以题主是否可以考虑一下实现方式,通过数据库的乐观锁来控制呢?
想用
redis
做mysql
的缓存
题主用redis
的原因,是因为有数据的重复插入,所以我想题主是想使用redis
的原子操作吧?我觉得这个出发点不是很正确,不能因为直接使用数据库有重复数据,就使用redis
。因为这个因果关系看上去有点蛋疼,我觉得首先得解决的是为什么会有重复数据插入。至于什么时候要用redis
,应该是业务量(QPS)太大导致数据库撑不住才考虑使用缓存。
把
redis
的数据写回mysql
题主想隔断时间把redis
的数据更新回mysql
,利用@BUBBAK 的方法确实是可行的,通过一个定时任务,定时的把数据刷回数据库。但是假设,仅仅是假设,如果redis
挂掉了,恰好数据没有刷回数据库,这就会造成数据丢失(这里并先不考虑redis
的持久化)。
还有一种方式就是,直接修改缓存的数据,然后起一个单独的线程去修改数据库的数据,这样的好处就是如果缓存挂掉,数据库的数据还是可靠的,但是这样的缺点是,如果并发量很高,就会起很多子线程把缓存刷回数据库,这样对系统是有开销的而且会造成CPU
的负载变高。
最后题主可以参考
如何设计高并发下的抽奖?下我的回答。
如果有什么其它的想法,大家可以一起再讨论讨论。
题主你之所以会出现这么多问题,是因为你思路混乱,根本没搞清楚这些问题到底是怎么回事,并且得病后还自己乱下药。
1.【总有人恶意大访问量请求】,这是系统安全问题,你需要做的是防攻击。
2.【查询mysql去做重复验证在大并发上限制不住】:这是功能缺失。既然有缺失,就立即做新功能研发。
3.【总会有重复插入,会造成多发奖品】:这是已有系统的Bug,需要通过调试来修正问题。
综上,这些问题与redis有什么关系?
redis中有个setnx(),这个操作是原子性的。首先可以通过在redis记录每个ip的请求时间,去判断该ip是否请求过快(这种形式又可能误伤,可能某个公司是同一个出口ip)。其次判断每个人是否抽过奖可以存在redis中啊,如果在redis中查不到该用户抽奖的标志就说明没有抽过奖,然后将该用户在redis中标记已经抽过奖了,然后在将该记录插入数据中即可。再说楼主这个只是在公司内部用,这种形式完全撑的住。
我感觉就是读脏数据的问题,楼主希望判断是否抽奖,抽奖逻辑,写抽奖表,这个过程中不能有第二相同用户来抽奖,实际上这个过程中,抽奖逻辑肯定要占大部分时间,至少要执行一次生成随机数的操作吧,在这个过程中,还没写表,所以其他请求读取数据库显示自己还没抽奖能够走抽奖逻辑。我的想法是第一步执行后加一个行锁,第三步执行后再释放这个锁,防止在这个过程中再有请求读取数据。本人愚见,若有错漏,望指点
抽奖记录表的用户名称即openid和抽奖版本字段做一个uniqu唯一索引不就行了
1.读取抽奖表判断这个人是否抽过奖
2.如果表中有记录,那就是抽过了,直接告诉没机会
3.如果表中没有记录,走抽奖逻辑,然后插入抽奖表
你这里逻辑错了,应该是先执行向抽奖表的插入操作,抽奖表通过唯一索引限制每用户唯一,创建成功在执行实际的发奖动作,失败表示已经抽过奖了,直接返回已抽奖完事。
加 redis 缓存的话可以在 redis 里面增加用户是否已抽奖的标记,实际抽奖流程是:
1.检查 redis 是否有用户已抽奖的标志
2.在 redis 设置用户已抽奖标志
3.插入抽奖记录(通过唯一索引限制每用户唯一),奖项为空
4.插入失败表示已抽过奖,插入成功表示可以抽奖
5.进行抽奖,写入中间结果,发奖
https://segmentfault.com/a/1190000004136250
利用php redis简单抗高并发。另外,这种情况建议参考我的另一篇文章,防单ip恶意提交攻击,这个可以做个单ip限制。
1.看一下数据,日均流量和峰值流量,不要有迫害妄想症,真是每次搞活动都被人搞的话,呵呵,恭喜了,你们是下一个阿里巴巴。
2.平时有没有反作弊、限流机制,策略是啥,对你解决这个问题有无帮助?
3.为啥用Redis不是memcache之类的?你要用Redis的队列?丰富的数据结构?还是备份?请忽略上面提到的crontab...
4.mysql写入、读取不够快...,你这个结论怎么得出的,预估的每秒读取、写入请求多少?
可以使用memcache的独占锁
我有过类似的问题。我的解决办法是:
用redis存储当前用户的session,包括ip,账号,电邮地址
在表单里面, 根据当前用户的session,生成一个form的验证码(有时间戳),以type='hidden'的形式存在。
-
在用户提交表单后:
<code>a) 先验证提交的隐藏验证码是否属于当前用户(不需要连接后台,根据已知的session来判定) b) 然后先搜索当前用户的session是否存在抽奖动作(不需要连接后台,根据已知的session来判定) c) 如果当前用户通过 a) 的验证后, 在 b)里面没有抽奖动作就证明该用户之前没有抽奖过,那么就添加一个抽奖动作为true. 如果当前用户通过 a) 的验证后,在 b) 里面有发现抽奖动作为true。就返回以抽奖页面。 也就是说c)跟b)是互相定义, 互相监督的。 </code>
在通过3的验证后,再开始抽奖程序。
在抽奖之后可以验证奖品存货量。(这一步可以在开始做,或者不做,看具体情况)
注意:
个人来说不赞成锁定数据库,这样会让程序变慢,对大多数用户来说不友好。毕竟会有情况真的是大量真实的抽奖请求出现,锁定数据库会让效能大大降低。
通过1,2,3的设置,可以屏蔽单IP短时间重复抽奖的问题。当然如果对方牛逼到能短时间内换IP+重复更换登陆用户+刷新页面生成不同的session,那你的程序绝对有重大逻辑错误,只能灭了重写了。
简单点就加锁,虽然会损失点性能
我这边是用redis锁,加一个记录,生存时间5秒,同一用户5秒请求1次. 进来就检测,如果没到时间直接返回一个提示消息就行了.
redis可以保证操作是原子性的,memcache不能.既然已经用了redis,那正好用下去就行了.
我觉得读写都可以在 Redis 中进行,使用 Redis 来维护这个『抽奖记录』。后台使用定时程序(最好不要用 crontab,自己使用定时器更好,有异常也可以有记录、报警),定期将 Redis 的数据备份、存储。此时,要不要入 MySQL,看具体业务上的需要了。
如果对数据可靠性要求很高,或者单表数量非常高,也可以考虑直接使用 MongoDB 来存储这个『抽奖记录』。使用 MongoDB Sharding 的话,10亿条记录以下基本问题不大。而且数据是存储于磁盘的,相比 Redis,健壮性更好。不过,如果使用 sharding 环境(其实千万条记录左右,我以为 Repl 就足够了),最好注意一下唯一性索引的使用。详情可以参考 MongoDB 的官方文档。
ls的应该都说的很清楚了。
1.防攻击:redis枷锁类似防重复提交。
2.加版本号和用户id建唯一索引。不过数据量大数据库应该压力大,频发抛异常
3.将控制用户只抽一次 交给redis,这样还可以通过redis有效期控制抽奖周期。
用乐观锁控制写入mysql,同时写入redis。
为了应对大量的 “新抽奖” ,用队列抗一下。
查询记录首先从redis查询,redis没有再回源到mysql去查。
mysql读写分离。
另外简单的防刷还是要做的,比如用户、ip限制,验证码等等。
恩就这样。
mysql行级锁 不要全表锁
就用redis就行,在redis中存
KEY(用户唯一标识) ----> VALUE(0/1)
0: 未抽奖,1:以抽奖
只要
判断KEY(用户唯一标识)的VALUE是0还是1
是0返回,未抽奖,并赋值VALUE为1
其他返回,以抽奖
以上3步是事务操作/原子操作,管你并发量破亿还是百亿,我们都吼得住。
假使有一个人同时发起了1000个请求1,2,3,...m...,999,1000。其中请求m略快一些被服务器先处理,m在执行上述3步时,其他999个只能眼巴巴的看着,因为是原子操作 等m被处理完了,其他999各只能看到1,即已抽过奖了
那么问题来了,在redis中如何实现上述3步的原子操作那?
答案是:redis的lua脚步,redis规定:redis的lua脚本的执行是原子性的
假使初始化时,所有用户的唯一标示对应的value都是0,下面给一个lua脚本实例
local uid = KEYS[1] local res = 1 local isDraw = redis.call('get', uid) if isDraw == 0 then res = 0 redis.call('set', uid, 1) end return res
这里只是一个不成熟的简单例子,可以参考 http://www.redis.cn/commands/eval.html 这里看看具体该如何写,思路就是这样
PS:一定记得做好redis的备份工作,要不断电了,就悲哀了呀!!
每秒几百个请求mysql就受不了了,我只能说要么机子配置实在是太差,要么代码写得太差
如果有用户ID可以这样做:
1、每人一个key:前缀:用户ID
2、抽奖时incr这个key,返回1则正常执行抽奖,否则中断
调用完redis后,用fastcgi_finish_request()这个函数,后面该怎么写mysql的处理就怎么写,执行完fastcgi_finish_request()后会返回响应的,进程不会结束,会把后面的程序处理完。
楼主应该充分利用redis的优势哦。
阅读完,我觉得楼主的第一步就错了,把是否抽奖放在mysql来读取,这个mysql是扛不住的。用redis的原子操作就轻松搞定了。因为第一步错了,所以后面不得不用很多其他措施来弥补。
要是我 我直接在 NGINX 成面上干掉大部分流量

PHPは、動的なWeb開発およびサーバー側のアプリケーションに使用されるサーバー側のスクリプト言語です。 1.PHPは、編集を必要とせず、迅速な発展に適した解釈言語です。 2。PHPコードはHTMLに組み込まれているため、Webページの開発が簡単になりました。 3。PHPプロセスサーバー側のロジック、HTML出力を生成し、ユーザーの相互作用とデータ処理をサポートします。 4。PHPは、データベースと対話し、プロセスフォームの送信、サーバー側のタスクを実行できます。

PHPは過去数十年にわたってネットワークを形成しており、Web開発において重要な役割を果たし続けます。 1)PHPは1994年に発信され、MySQLとのシームレスな統合により、開発者にとって最初の選択肢となっています。 2)コア関数には、動的なコンテンツの生成とデータベースとの統合が含まれ、ウェブサイトをリアルタイムで更新し、パーソナライズされた方法で表示できるようにします。 3)PHPの幅広いアプリケーションとエコシステムは、長期的な影響を促進していますが、バージョンの更新とセキュリティの課題にも直面しています。 4)PHP7のリリースなど、近年のパフォーマンスの改善により、現代の言語と競合できるようになりました。 5)将来的には、PHPはコンテナ化やマイクロサービスなどの新しい課題に対処する必要がありますが、その柔軟性とアクティブなコミュニティにより適応性があります。

PHPの中心的な利点には、学習の容易さ、強力なWeb開発サポート、豊富なライブラリとフレームワーク、高性能とスケーラビリティ、クロスプラットフォームの互換性、費用対効果が含まれます。 1)初心者に適した学習と使用が簡単。 2)Webサーバーとの適切な統合および複数のデータベースをサポートします。 3)Laravelなどの強力なフレームワークを持っています。 4)最適化を通じて高性能を達成できます。 5)複数のオペレーティングシステムをサポートします。 6)開発コストを削減するためのオープンソース。

PHPは死んでいません。 1)PHPコミュニティは、パフォーマンスとセキュリティの問題を積極的に解決し、PHP7.xはパフォーマンスを向上させます。 2)PHPは最新のWeb開発に適しており、大規模なWebサイトで広く使用されています。 3)PHPは学習しやすく、サーバーはうまく機能しますが、タイプシステムは静的言語ほど厳格ではありません。 4)PHPは、コンテンツ管理とeコマースの分野で依然として重要であり、エコシステムは進化し続けています。 5)OpcacheとAPCを介してパフォーマンスを最適化し、OOPと設計パターンを使用してコードの品質を向上させます。

PHPとPythonには独自の利点と短所があり、選択はプロジェクトの要件に依存します。 1)PHPは、Web開発に適しており、学習しやすく、豊富なコミュニティリソースですが、構文は十分に近代的ではなく、パフォーマンスとセキュリティに注意を払う必要があります。 2)Pythonは、簡潔な構文と学習が簡単なデータサイエンスと機械学習に適していますが、実行速度とメモリ管理にはボトルネックがあります。

PHPは動的なWebサイトを構築するために使用され、そのコア関数には次のものが含まれます。1。データベースに接続することにより、動的コンテンツを生成し、リアルタイムでWebページを生成します。 2。ユーザーのインタラクションを処理し、提出をフォームし、入力を確認し、操作に応答します。 3.セッションとユーザー認証を管理して、パーソナライズされたエクスペリエンスを提供します。 4.パフォーマンスを最適化し、ベストプラクティスに従って、ウェブサイトの効率とセキュリティを改善します。

PHPはMySQLIおよびPDO拡張機能を使用して、データベース操作とサーバー側のロジック処理で対話し、セッション管理などの関数を介してサーバー側のロジックを処理します。 1)MySQLIまたはPDOを使用してデータベースに接続し、SQLクエリを実行します。 2)セッション管理およびその他の機能を通じて、HTTPリクエストとユーザーステータスを処理します。 3)トランザクションを使用して、データベース操作の原子性を確保します。 4)SQLインジェクションを防ぎ、例外処理とデバッグの閉鎖接続を使用します。 5)インデックスとキャッシュを通じてパフォーマンスを最適化し、読みやすいコードを書き、エラー処理を実行します。

PHPで前処理ステートメントとPDOを使用すると、SQL注入攻撃を効果的に防ぐことができます。 1)PDOを使用してデータベースに接続し、エラーモードを設定します。 2)準備方法を使用して前処理ステートメントを作成し、プレースホルダーを使用してデータを渡し、メソッドを実行します。 3)結果のクエリを処理し、コードのセキュリティとパフォーマンスを確保します。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

DVWA
Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

メモ帳++7.3.1
使いやすく無料のコードエディター

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。
