搜索

首页  >  问答  >  正文

高并发 - php redis做mysql的缓存,怎么异步redis同步到mysql数据库?

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

高洛峰高洛峰2793 天前2505

全部回复(24)我来回复

  • 迷茫

    迷茫2017-04-10 16:45:30

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

    回复
    0
  • 阿神

    阿神2017-04-10 16:45:30

    可以使用memcache的独占锁

    回复
    0
  • PHPz

    PHPz2017-04-10 16:45:30

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

    回复
    0
  • 阿神

    阿神2017-04-10 16:45:30

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

    回复
    0
  • 怪我咯

    怪我咯2017-04-10 16:45:30

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

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

    回复
    0
  • 天蓬老师

    天蓬老师2017-04-10 16:45:30

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

    回复
    0
  • PHP中文网

    PHP中文网2017-04-10 16:45:30

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

    回复
    0
  • 巴扎黑

    巴扎黑2017-04-10 16:45:30

    mysql行级锁 不要全表锁

    回复
    0
  • 高洛峰

    高洛峰2017-04-10 16:45:30

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

    回复
    0
  • PHPz

    PHPz2017-04-10 16:45:30

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

    回复
    0
  • 取消回复