前边我们介绍了游戏中的邮件系统-----游戏服务器---邮件系统_zhangdell的专栏-CSDN博客
一:问题发现
最近客户端更换引擎,所有的功能都得重新对接,在对接邮件功能时客户端反馈了一些异常问题,当时第一感觉是客户端自己的问题,因为这些功能已经在生产环境跑了2年的时间了,而且邮件这套逻辑 已经在另外一个项目当中跑了8年了,如果有问题应该早就出了。具体问题如下:
CREATE TABLE `msg` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ruid` int(11) NOT NULL,
`msgtype` tinyint(3) UNSIGNED NOT NULL,
`title` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`content` tinytext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`got` tinyint(3) UNSIGNED NULL DEFAULT 0,
`sendtime` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `index_ruid`(`ruid`, `got`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 53176 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `msg_attachment` (
`msgid` int(10) UNSIGNED NOT NULL,
`valueitems` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`got` tinyint(3) UNSIGNED NULL DEFAULT 0,
`gottime` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`msgid`) USING BTREE,
INDEX `INDEX_gottime`(`gottime`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
服务器调用p_msg_query2存储过程,拉取玩家的未读 or 未领取附件的 邮件
CREATE DEFINER=`root`@`%` PROCEDURE `p_msg_query2`(puid int unsigned,
plastgotid int unsigned,
plimit int unsigned)
BEGIN
select id,
msgtype,
title,
content,
sendtime,
valueitems,
msg_attachment.got
from msg left join msg_attachment on (msg_attachment.msgid=msg.id)
where ruid=puid and (msg.got=0 or msg_attachment.got=0) and id>plastgotid limit plimit;
END
客户端 ruid=120114 每次拉取5封邮件,第一次发来的 plastgotid = 0,拉取了id为 53145 53152 49325 49329 49361 这5封邮件,第二次来拉的时候 plastgotid = 53152,服务器发现没有新邮件了,这样客户端只显示了5封邮件,这肯定是不正常的,因为数据库中ruid=120114这个用户 未读or未领取的邮件 有好多封,绝对不止这5封邮件。客户端重新捋了一遍逻辑说他那边没问题,这个时候我心里就犯了嘀咕,线上的老功能 咋会出现这么低级的错误尼,我开始查数据库里边的数据,果真颠覆了我的认知。。
执行存储过程得到的结果
从数据库的数据+服务器逻辑来看,客户端确实只会收到 53145 53152 49325 49329 49361这5封邮件,这就引起了我的注意了,为啥select 出来的结果 id 顺序是乱的,为啥线上没有出现这种问题。再次看了一下sql 发现sql中并没有明确的要求顺序是按照id 来排序的,大概知道问题的解决办法了,加上order by id应该就好使。此时我又写了一个简单的测试sql,select id,title,content from msg where ruid = 120114;
最简答的单表 select 得到的结果就是有问题的,重启mysql后 也没得到解决。修改后 select id,title,content from msg where ruid = 120114 order by id;得到的结果就是正确的。问题是解决了,但是不知道是如何引起的。
二:问题深入
后来查了一些资料,select * from 不加order by 顺序本来就得不到保证,53145,53152 这2条数据在插入时占用了以前删除的表空间,数据落盘了 重启mysql服务也没用。所以select 的时候 先出现了后插入的2条数据。