3.3 事务控制与权限


3.3 事务控制与权限

本节摘要:事务控制让一组语句"要么全做、要么全不做",权限控制决定谁能做哪一类操作。本节讲 COMMIT、ROLLBACK、SAVEPOINT 的组合用法与 autocommit 的默认陷阱,再讲 GRANT 最小授权模型的落地清单。位置:DML 之上的安全层,其 ACID 特性的内部实现将在第 6 章展开。

从一笔转账说起

扣库存和写订单是两条 UPDATE。想象程序执行完第一条、还没来得及执行第二条时进程崩了:库存扣了,订单没了,客户付了钱却查不到单。事务就是防这个的——把两条语句绑成一个整体,要么两条都成功,要么数据库假装什么都没发生过。

先看默认行为的陷阱。MySQL 默认 autocommit 打开,每条语句自成事务、立即提交。这意味着你以为"多条语句一起失败会一起回滚",实际上每条早已落盘。正确姿势:

SET autocommit = 0; -- 或显式 START TRANSACTION START TRANSACTION; UPDATE inventory SET stock = stock - 2 WHERE sku_id = 8001 AND warehouse = 'SH01' AND stock >= 2; -- 结果:Query OK, 1 row affected INSERT INTO orders (customer_id, amount, status) VALUES (1, 199.00, 2); -- 结果:Query OK, 1 row affected COMMIT; -- 两条同时生效;任何一步失败则 ROLLBACK,两条同时消失

注意那条 UPDATE 的 WHERE 里带着 AND stock >= 2:超卖防护要靠条件竞争一步完成,事务本身不能替你判断业务规则——它只保证"全部生效或全部不生效",不保证"语义正确"。这个区别在评审会上值得反复强调。

SAVEPOINT:长事务里的局部反悔

大流程不需要从头回滚。下单流程若含"创建订单 → 扣库存 → 发优惠券",优惠券失败未必要废掉整单:

START TRANSACTION; INSERT INTO orders (customer_id, amount, status) VALUES (1, 199.00, 2); SAVEPOINT after_order; UPDATE inventory SET stock = stock - 1 WHERE sku_id = 8001 AND warehouse = 'SH01' AND stock >= 1; -- 结果:Query OK, 1 row affected INSERT INTO coupon_grant (customer_id, coupon_id) VALUES (1, 55); -- 结果:ERROR 1062 Duplicate entry '55'(今日名额已抢完) ROLLBACK TO SAVEPOINT after_order; UPDATE orders SET status = 1 WHERE order_id = LAST_INSERT_ID(); COMMIT; -- 订单照常成立,只是没领到券

解读:SAVEPOINT 在事务内部立标记,ROLLBACK TO 只撤销标记之后的语句。用它的前提是业务语义明确"哪步可弃、哪步必成"。反过来提醒:事务不是越大越好,长事务持有锁和 undo 资源的时间更长,第 6 章会给出"事务要短"的量化理由——这里先立规矩:事务里别放 RPC 调用、别放文件 IO、别等用户确认。

权限:最小授权的落地清单

DCL 家族的核心是 GRANT 与 REVOKE。评审会上权限问题的标准答案是四个字:最小授权。生产环境的用户按角色分:

-- 应用账号:只给业务库的增删改查 CREATE USER 'mall_app'@'10.0.%' IDENTIFIED BY '复杂密码'; GRANT SELECT, INSERT, UPDATE, DELETE ON mall.* TO 'mall_app'@'10.0.%'; -- 只读账号:给报表与数据分析 CREATE USER 'mall_ro'@'10.0.%' IDENTIFIED BY '另一串复杂密码'; GRANT SELECT ON mall.* TO 'mall_ro'@'10.0.%'; -- DBA 运维账号:结构变更与全局管理,不进应用配置 GRANT ALL PRIVILEGES ON *.* TO 'mall_dba'@'10.0.%' WITH GRANT OPTION; -- 回收示例 REVOKE DELETE ON mall.* FROM 'mall_app'@'10.0.%'; SHOW GRANTS FOR 'mall_app'@'10.0.%';

三个细节值得点名。其一,'用户'@'来源' 双重要素:@ 后面限定来源网段,10.0.% 表示只允许内网地址连接,杜绝公网裸奔。其二,应用账号不给 DROP、ALTER 这类结构权限——即使 SQL 注入发生,攻击者能偷数据也难以毁掉表结构。其三,8.0 引入角色(ROLE)机制,先把权限打包成角色再授予用户,权限审计和批量调整都省力。

演练:一次"列级权限"需求

背景:客服系统要看订单但不能看金额。操作:

CREATE USER 'cs_view'@'10.0.%' IDENTIFIED BY '密码'; GRANT SELECT (order_id, customer_id, status, created_at) ON mall.orders TO 'cs_view'@'10.0.%';

结果:该账号能查 orders 的指定列,SELECT amount 直接报权限错误。解读:MySQL 支持列级授权,配合视图(VIEW)能把"能看什么"表达得更清晰——权限设计本身也是一种数据设计。

易错点与评审清单

  • 事务里等外部系统:RPC 超时 30 秒,锁也持有 30 秒,业务雪崩。正确顺序是先完成外部调用再开事务,或者用事务消息等模式解耦;
  • ROLLBACK 后以为数据没了:回滚只影响未提交部分,已 COMMIT 的历史要靠备份与 binlog,两码事;
  • root 到处飞:root 只留给运维应急,应用配置里出现 root 是评审一票否决项;
  • 权限只授不收:离职人员账号、废弃服务账号定期清理,权限清单要进交接文档。

要点回顾:autocommit 是默认陷阱,多步写入显式开事务;SAVEPOINT 提供局部反悔;事务要短,外部调用不进事务;最小授权四件套——按角色建账号、限来源网段、不给结构权限、定期审计。多表关联的取数难题,下一节见。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U