Discord Bot存储BigInt类型用户ID后查询结果异常求助
这个问题我太熟悉了——本质是JavaScript的Number类型精度限制,加上MySQL驱动的默认处理逻辑,才导致查询回来的Discord ID末尾多了两个0。
为啥会出现这个情况?
Discord的用户ID是64位的Snowflake ID,这个数值早就超过了JavaScript里Number类型的最大安全整数(也就是9007199254740991)。当你的MySQL驱动从数据库读取BIGINT类型的ID时,默认会把它转成Number类型,而超出安全范围的数值会被自动近似处理,这种近似就可能表现为末尾多出两个0(不同数值的近似结果可能不一样,但都是精度丢失的表现)。
你在数据库里看到ID是对的,那是因为数据存在库里是完整的,但一旦被读到Node.js环境里,就已经被转成了不精确的Number,之后的JSON.stringify和JSON.parse只是把这个错误的数值固化下来,最后发给你的自然就是错的ID。
怎么解决?
给你几个靠谱的方案,按推荐程度排序:
1. 修改MySQL连接配置,强制返回字符串
在创建数据库连接的时候,加上supportBigNumbers: true和bigNumberStrings: true这两个配置,这样驱动就会把BIGINT类型的数据直接以字符串形式返回,完全避免精度丢失:
const con2 = mysql.createConnection({ host: '你的主机地址', user: '数据库用户名', password: '数据库密码', database: '你的库名', supportBigNumbers: true, bigNumberStrings: true });
这样处理后,你拿到的discord_id就是原原本本的字符串ID,序列化反序列化都不会出问题。
2. 把数据库字段改成VARCHAR类型
其实Discord ID根本不需要做数值计算,本质就是一串字符串。直接把discord_id字段改成VARCHAR(20)(Snowflake ID最长也就18位左右,20位完全够),从存储到读取全程用字符串处理,彻底绕开数值类型的坑。
3. 手动把查询结果里的BigInt转成字符串(临时方案)
如果不想改配置或数据库结构,可以在拿到查询结果后,先把ID转成字符串再序列化:
con2.query(`SELECT * FROM discord_user WHERE discord_id = '${id}'`, (err, result, fields) => { if (err) throw err; // 先把BigInt转成字符串 result[0].discord_id = result[0].discord_id.toString(); const string = JSON.stringify(result); const json = JSON.parse(string); message.author.send(json[0].discord_id); });
不过这个方法只适合单个字段的场景,要是有多个BigInt字段,处理起来就很麻烦,不如前两种彻底。
额外提个醒
你的代码里直接把id拼进SQL语句里,这可是SQL注入的高危操作!赶紧改成参数化查询:
con2.query('SELECT * FROM discord_user WHERE discord_id = ?', [id], (err, result, fields) => { // 后续逻辑不变 });
这样能有效防止恶意注入,代码安全多了。
内容的提问来源于stack exchange,提问作者Johannes Berger

