高并发 - php redis做mysql的缓存,怎么异步redis同步到mysql数据库?
高洛峰
高洛峰 2017-04-10 16:43:30
0
24
2506

公司做抽奖或者红包活动,总有人恶意大访问量请求,查询mysql去做重复验证在大并发上限制不住,总会有重复插入,会造成多发奖品。
想用redis做mysql的缓存,但是现在遇到的问题是如何把redis的数据写回mysql,不可能每次校验的时候就写回mysql,那样的话根本没有解决问题。
现在的想法是能否利用php,或者其他什么技术,定时将redis中的数据写回mysql。程序只与redis交互。
希望能给出具体的逻辑或者解决方案,网上的回答都太笼统了,根本解决不了问题。

谢谢。

1月16日补充:
我问题描述的不太清晰。
我把遇到的问题详细的说一下吧,并不是发奖时候脏读的问题,而是判断这个人是否有机会抽奖。
有一个抽奖的表,如果这个人抽过奖了,就在表中插入一条记录。
目前的实现方式是:

1.读取抽奖表判断这个人是否抽过奖
2.如果表中有记录,那就是抽过了,直接告诉没机会
3.如果表中没有记录,走抽奖逻辑,然后插入抽奖表

正常情况下是没有问题的,
但是有人用恶意脚本进行刷奖,也就是同一个人发起大量请求,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。

高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

全部回复(24)
迷茫

1.看一下数据,日均流量和峰值流量,不要有迫害妄想症,真是每次搞活动都被人搞的话,呵呵,恭喜了,你们是下一个阿里巴巴。
2.平时有没有反作弊、限流机制,策略是啥,对你解决这个问题有无帮助?
3.为啥用Redis不是memcache之类的?你要用Redis的队列?丰富的数据结构?还是备份?请忽略上面提到的crontab...
4.mysql写入、读取不够快...,你这个结论怎么得出的,预估的每秒读取、写入请求多少?

阿神

可以使用memcache的独占锁

PHPzhong

简单点就加锁,虽然会损失点性能

阿神

我这边是用redis锁,加一个记录,生存时间5秒,同一用户5秒请求1次. 进来就检测,如果没到时间直接返回一个提示消息就行了.
redis可以保证操作是原子性的,memcache不能.既然已经用了redis,那正好用下去就行了.

刘奇

我觉得读写都可以在 Redis 中进行,使用 Redis 来维护这个『抽奖记录』。后台使用定时程序(最好不要用 crontab,自己使用定时器更好,有异常也可以有记录、报警),定期将 Redis 的数据备份、存储。此时,要不要入 MySQL,看具体业务上的需要了。

如果对数据可靠性要求很高,或者单表数量非常高,也可以考虑直接使用 MongoDB 来存储这个『抽奖记录』。使用 MongoDB Sharding 的话,10亿条记录以下基本问题不大。而且数据是存储于磁盘的,相比 Redis,健壮性更好。不过,如果使用 sharding 环境(其实千万条记录左右,我以为 Repl 就足够了),最好注意一下唯一性索引的使用。详情可以参考 MongoDB 的官方文档。

Peter_Zhu

ls的应该都说的很清楚了。
1.防攻击:redis枷锁类似防重复提交。
2.加版本号和用户id建唯一索引。不过数据量大数据库应该压力大,频发抛异常
3.将控制用户只抽一次 交给redis,这样还可以通过redis有效期控制抽奖周期。

Ty80

用乐观锁控制写入mysql,同时写入redis。
为了应对大量的 “新抽奖” ,用队列抗一下。
查询记录首先从redis查询,redis没有再回源到mysql去查。
mysql读写分离。
另外简单的防刷还是要做的,比如用户、ip限制,验证码等等。
恩就这样。

巴扎黑

mysql行级锁 不要全表锁

小葫芦

每秒几百个请求mysql就受不了了,我只能说要么机子配置实在是太差,要么代码写得太差

PHPzhong

如果有用户ID可以这样做:
1、每人一个key:前缀:用户ID
2、抽奖时incr这个key,返回1则正常执行抽奖,否则中断

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板