MySQL UPDATE 条件升级导致的事故

发布时间:2026/5/16 1:14:14

MySQL UPDATE 条件升级导致的事故 UPDATE 语句看起来简单但稍不注意就可能把整张表给毁了。这篇说说 UPDATE 的常见事故和正确写法。经典事故忘了加 WHERE-- 本意给 id1 的用户加 100 积分UPDATEuserSETpointspoints100WHEREid1;-- 事故忘了加 WHEREUPDATEuserSETpointspoints100;-- 全表所有用户都加 100 积分结果积分全部加 100如果用户量 100万后果不堪设想。怎么避免1. 先写 SELECT 验证-- 先查一下确认数据范围SELECT*FROMuserWHEREid1;-- 确认只有一条SELECTCOUNT(*)FROMuserWHEREpoints0;-- 确认范围-- 再执行 UPDATEUPDATEuserSETpointspoints100WHEREid1;2. 用事务包裹-- 开启事务先看结果确认没问题再提交STARTTRANSACTION;UPDATEuserSETpointspoints100WHEREid1;-- 检查一下SELECT*FROMuserWHEREid1;-- 确认无误提交COMMIT;-- 或者回滚-- ROLLBACK;3. 用 LIMIT 限制-- 加 LIMIT 限制即使忘加 WHERE 也只影响一条UPDATEuserSETpointspoints100WHEREid1LIMIT1;4. 加上 ORDER BY LIMIT-- 更新排序后的前几条UPDATEuserSETpointspoints100WHEREstatusvipORDERBYcreated_atDESCLIMIT100;常见错误写法1. 多表 UPDATE 忘了关联条件-- 本意给 Tom 的订单加 100UPDATEordero,useruSETo.amounto.amount100WHEREu.nameTomANDo.user_idu.id;-- 事故没有关联条件order 表全部更新UPDATEordero,useruSETo.amounto.amount100WHEREu.nameTom;-- 所有订单都加 100正确写法-- 方式1用 JOINUPDATEorderoINNERJOINuseruONo.user_idu.idSETo.amounto.amount100WHEREu.nameTom;-- 方式2用子查询UPDATEorderSETamountamount100WHEREuser_id(SELECTidFROMuserWHEREnameTom);2. 字符串更新漏了引号-- 正确字符串加引号UPDATEuserSETnameTomWHEREid1;-- 错误没加引号数据被截断UPDATEuserSETnameTomWHEREid1;-- 报错或数据错误3. 负数没处理-- 本意扣 100 积分最低扣到 0UPDATEuserSETpointspoints-100WHEREid1;-- 事故如果原来只有 50 积分结果变成 -50-- 正确IFNULL GREATESTUPDATEuserSETpointsGREATEST(points-100,0)WHEREid1;4. 浮点数精度问题-- 金额计算用 DECIMAL别用 FLOATUPDATEaccountSETbalancebalance-0.1WHEREid1;-- FLOAT 有精度问题UPDATEaccountSETbalancebalance-DECIMAL(0.1)WHEREid1;-- DECIMAL批量 UPDATE 的正确姿势1. 分批更新-- 更新 10000 条数据每批 500 条UPDATEuserSETpointspoints10WHEREstatusvipLIMIT500;-- 执行 20 次2. 用主键 IN-- 已知 ID 列表用 INUPDATEuserSETpointspoints10WHEREidIN(1,2,3,4,5);3. 临时表 JOIN-- 创建临时表存要更新的 IDCREATETEMPORARYTABLEtmp_ids(idBIGINT);INSERTINTOtmp_idsVALUES(1),(2),(3);-- JOIN 更新UPDATEuseruINNERJOINtmp_ids tONu.idt.idSETu.pointsu.points10;DROPTEMPORARYTABLEtmp_ids;怎么恢复误更新1. 有备份-- 从备份恢复mysql-u root-pdatabasebackup.sql2. 用 binlog 恢复-- 查看 binlog找误更新的时间点SHOWBINLOG EVENTSINmysql-bin.000001FROM123456;-- 用 mysqlbinlog 解析mysqlbinlog--start-datetime2024-01-15 10:00:00 /var/lib/mysql/mysql-bin.0000013. 反向补偿-- 如果误加了 100 积分扣回来UPDATEuserSETpointspoints-100WHEREid1;-- 补偿-- 如果误删了数据从其他表恢复或从备份恢复最佳实践做法说明先 SELECT 再 UPDATE验证数据范围用事务包裹可以回滚加 LIMIT防止全表事故字符串加引号防止数据错误金额用 DECIMAL避免精度问题批量用主键 IN精确控制范围UPDATE 操作的黄金法则先 SELECT后 UPDATE加 LIMIT不忘 WHERE。相关阅读[MySQL 事务隔离级别详解][MySQL 锁机制完全指南][MySQL 批量更新最佳实践]

相关新闻