业务场景还是关于推送的,推送是针对我们自己的系统用户,所以需要一张表user_bind_push来绑定用户id和SDK里的唯一标识token。在用户登录的时候会访问一个push.bind的api接口,在该接口中会先删除和该手机设备里的token绑定的数据,然后绑定目前登录的用户id和推送SDK里面的token。
$sql = "DELETE FROM `user_bind_push` WHERE `push_id` = '{$token}'";
M()->execute($sql);
$sql = "INSERT INTO `user_bind_push` SET `uid`={$uid}, `push_type`={$push_type}, `push_id`={$push_id}, `device`='{$device}', `app_version`={$appVersion}, `build`='{$build}',`channel`='{$channel}'";
M()->execute($sql);
之前三个月一直运行的好好的,但是前几天由于做了一次广播推送,这样一时间同时登录的用户就多了。导致访问该接口却无法获取返回值。
打开数据库通过show processlist发现对该表的操作就出现堵塞了,已经有900多条。
是什么导致了这个问题呢?原来是push_id没有加入到索引(低级错误)。
这个时候为了防止出现数据库出现异常情况,就立即将该api里的两次SQL的执行给注释掉了,而是写入文件,等之前的请求都执行完了(也把索引给加上了),再把把代码恢复,并且将写入文件的SQL执行,既保证了服务的正常,也不会丢失数据。
$sql = "DELETE FROM `user_bind_push` WHERE `push_id` = '{$token}'";
//M()->execute($sql);
file_put_contents('todo.sql',$sql.";\n",FILE_APPEND);
$sql = "INSERT INTO `user_bind_push` SET `uid`={$uid}, `push_type`={$push_type}, `push_id`={$push_id}, `device`='{$device}', `app_version`={$appVersion}, `build`='{$build}',`channel`='{$channel}'";
//M()->execute($sql);
file_put_contents('todo.sql',$sql.";\n",FILE_APPEND);
其实我们应该在框架就应该把这种把SQL堵塞写入文件的紧急任务封装起来,发现问题了,方便立即切换。
我们的库一直还是一主多从,写入的压力都放在了主库上,但是发现在该表发生堵塞的时候,其他的有写操作的api对客户端提供的服务正常,比如留言等,在show processlist看到有堵塞只是user_bind_push这一张表的,并没有影响到其他表。