最近,我开始放精力在OurCoders上面,解决了很多技术问题。然后,一直有一个很挠头的技术问题,我一直懒得解决,就是OurCoders的页面生成时间,有时侯是几个毫秒,有时候是上千毫秒。这个问题很难解释,我以前以为有什么简单问题,但是找来照去在代码里面看不到问题。
于是我就想,我去把mysql的slow queries功能打开吧。打开以后,我发现,90%以上的slow queries其实是这句
SELECT * FROM mail_queue WHERE status = 0 ORDER BY rand() ASC
解释下mail_queue,这个表就是当系统需要发邮件的时候,先会把需要发的邮件放在这个表里面,然后每隔一分钟通过crontab启动一个发邮件的php页面,把100封邮件发出去。这句SQL就是在选择100条status=0的邮件,0代表未发送,1代表发送成功,2代表发送失败。
我就想这句怎么这么愚蠢啊,为啥by rand()啊,你一旦by rand()的话,这个就没办法被缓存命中了,也用不上索引啊。于是去掉了这句话,我还去掉了另外一个多发的slow query:
SELECT id,title,replys FROM threads where createbyid <> 28504 AND id in (SELECT threadid FROM thread_replys WHERE userid = 28504 and del = 0 GROUP BY threadid) AND del = 0
效果非常好,整天都没有slow queries了。
但是,到了下周一,有人报周总结邮件收到了7-8封,这个bug在很久很久出现过,我已经记不得怎么解决了,但是发送代码百分百没有错误,不可能会重复发送。一时想不出来主意,我就把这个问题撂下了。
过了几天,我正在走路的时候,突然想起当年为啥会有by rand()。
原来故事是这样的,因为我要设置1发送成功和2发送失败这两个标志,所以,发送流程是,一次选择100封,一封一封的发送邮件,等待一封完成,取得返回值,根据返回值设置1或者2。
所以,在很久以前,某一次邮件服务器发送很慢的时候,一封邮件还没发完,这时候1分钟过去了,系统就又选取了100封邮件,而第一次还没发送完的邮件,因为没有设1或者2,仍旧会被选中来发送,所以就会重复发送。
当年为了解决这个问题,我加了一个by rand(),从概率上,两次发送流程很难遇到重复的邮件,所以,就不会产生重复发送问题。
一切都想通后,我发现问题还在于之前图省事儿,非要等邮件发送完毕以后才设置flag。修正方法是,选取100封邮件后,先设置为1,然后挨个发送,一旦发送失败,根据返回着设置flag为2。
这样整体效率和不重复之间的矛盾就解决了,我之前好愚蠢。