素材牛VIP会员
php redis做mysql的缓存,怎么异步redis同步到mysql数据库?
 Al***ay  分类:PHP代码  人气:3189  回帖:25  发布于6年前 收藏

公司做抽奖或者红包活动,总有人恶意大访问量请求,查询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。

讨论这个帖子(25)垃圾回帖将一律封号处理……

Lv1 新人
冷***知 职业无 6年前#1

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

Lv1 新人
海***人 Web前端工程师 6年前#2

可以使用memcache的独占锁

Lv5 码农
夏***t 移动开发工程师 6年前#3

先简单的意淫一下题主的问题。

查询mysql去做重复验证在大并发上限制不住,总会有重复插入

我想题主这个原因主要是因为,需要把某些信息从数据库查出来,然后再比较,最后插入数据库吧?所以这里有可能出现脏读的问题。所以题主是否可以考虑一下实现方式,通过数据库的乐观锁来控制呢?

想用redismysql的缓存

题主用redis的原因,是因为有数据的重复插入,所以我想题主是想使用redis的原子操作吧?我觉得这个出发点不是很正确,不能因为直接使用数据库有重复数据,就使用redis。因为这个因果关系看上去有点蛋疼,我觉得首先得解决的是为什么会有重复数据插入。至于什么时候要用redis,应该是业务量(QPS)太大导致数据库撑不住才考虑使用缓存。

redis的数据写回mysql

题主想隔断时间把redis的数据更新回mysql,利用@BUBBAK 的方法确实是可行的,通过一个定时任务,定时的把数据刷回数据库。但是假设,仅仅是假设,如果redis挂掉了,恰好数据没有刷回数据库,这就会造成数据丢失(这里并先不考虑redis的持久化)。
还有一种方式就是,直接修改缓存的数据,然后起一个单独的线程去修改数据库的数据,这样的好处就是如果缓存挂掉,数据库的数据还是可靠的,但是这样的缺点是,如果并发量很高,就会起很多子线程把缓存刷回数据库,这样对系统是有开销的而且会造成CPU的负载变高。

最后题主可以参考
如何设计高并发下的抽奖?下我的回答。
如果有什么其它的想法,大家可以一起再讨论讨论。

Lv1 新人
海***人 Web前端工程师 6年前#4

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

Lv6 码匠
zh***ni 职业无 6年前#5

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

你这里逻辑错了,应该是先执行向抽奖表的插入操作,抽奖表通过唯一索引限制每用户唯一,创建成功在执行实际的发奖动作,失败表示已经抽过奖了,直接返回已抽奖完事。

加 redis 缓存的话可以在 redis 里面增加用户是否已抽奖的标记,实际抽奖流程是:
1.检查 redis 是否有用户已抽奖的标志
2.在 redis 设置用户已抽奖标志
3.插入抽奖记录(通过唯一索引限制每用户唯一),奖项为空
4.插入失败表示已抽过奖,插入成功表示可以抽奖
5.进行抽奖,写入中间结果,发奖

Lv5 码农
流***雨 交互设计师 6年前#6

insert 的时候 带上 ON DUPLICATE KEY UPDATE
在创建个组合索引

Lv2 入门
he***ba JS工程师 6年前#7

redis中有个setnx(),这个操作是原子性的。首先可以通过在redis记录每个ip的请求时间,去判断该ip是否请求过快(这种形式又可能误伤,可能某个公司是同一个出口ip)。其次判断每个人是否抽过奖可以存在redis中啊,如果在redis中查不到该用户抽奖的标志就说明没有抽过奖,然后将该用户在redis中标记已经抽过奖了,然后在将该记录插入数据中即可。再说楼主这个只是在公司内部用,这种形式完全撑的住。

Lv4 码徒
想***儿 技术总监 6年前#8

抽奖记录表的用户名称即openid和抽奖版本字段做一个uniqu唯一索引不就行了

Lv1 新人
Sm***ty CEO 6年前#9

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

Lv6 码匠
素***2 学生 6年前#10

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

上一页123下一页
 文明上网,理性发言!   😉 阿里云幸运券,戳我领取