数据库定时重置的另一种思路

点击量:276

需求:
网页游戏里面经常会有一些功能模块是需要每天重置的,比如每天有5次抽奖次数,每天可以兑换资源的次数等。那么正常的思路必然是在第二天零点的时候对数据库相关表字段进行update。这是很合理的思维方式,在游戏发展初期是不会有什么问题的。但是当服务器玩家数量增加,零点需要重置的表变多时,0点就会出现很多锁超时的错误:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction导致某些玩家数据库更新失败。那怎么解决呢?

思路:
1.适当地调整MySQL锁超时的时间,默认设置是50S。可以通过设置参数innodb_lock_wait_timeout来调整时间。但是值得注意的是,这个参数不能调得太大,那样的话如果数据库长时间不释放锁会导致程序线程卡死,影响玩家体验。
2.跟策划讨论更改重置时间的可能,把不同的模块尽量错峰执行,比如有些是在0点,有些是在1点,2点等,但这种方式会限制游戏的玩法,不是个好的选择。
3.尽量减少全表更新,比如像这种SQL语句

应该尽量避免,因为这种方式会把整个表锁住,当玩家主动更新该表或者其他线程更新该表时会长时间等待,导致锁超时。比较好一点的做法是选出符合条件的玩家,一条一条记录更新,这样可以避免锁表。
OK,上面的三点都做到了,但是还不能根本地解决问题,线上某些玩家很多的服还是会时不时出现锁超时的报错。仔细思考下,一个游戏服正常在线或者活跃玩家数在100-1000之间,但是数据库表里的记录一般都是50000+(因为会有很多小号或者流失玩家)。也就是说真正需要更新的只有那些活跃玩家,其余的僵尸号都不需要更新。那另一个思路随即而来:在程序里维护这些活跃玩家的信息,把他们存起来,零点的时候只更新这些玩家的数据。但是这种方式需要额外维护一份内存数据,对已有代码的改造很麻烦,不是特别好。那么更进一步,既然我们只关注活跃玩家,那么这些重置的操作能不能由这些玩家自己完成呢?也就是说只有玩家去玩这个模块的时候服务器才会去重置。

实现:
对player_lottery表新增一个datetime类型的字段:last_reset_time用来记录上一次重置的时间,当玩家第一次读取这张表时,在DAO层做判断:如果last_reset_time在今天零点之前,则重置times字段,然后返回。否则不重置,直接返回。

这样就根本地解决了数据更新集中和数据更新的量太大的问题,也就是说其实我们并不需要零点集中重置!

有时候一些理所当然的思路其实并不是最优的,工程师需要不断思考。

发表评论

电子邮件地址不会被公开。 必填项已用*标注