在阅读《高性能MySQL》这本书的过程中,我复习了一下关于数据库事务中“隔离性”的章节。回想起来,在大学数据库系统课程中应该学过这一部分内容,但现在确实记不清了。数据库中的事务是一种机制,允许连续执行一组SQL指令,如果在执行这一系列指令的过程中出现任何错误,整个事务将会回滚至最初的状态,以此保证事务要么整体成功,要么整体失败,不会出现部分成功、部分失败的情况。
举例来说,假设有两张银行账户表,分别记录了Alice和Bob各自的账户余额,初始状态下两人都有1000元。
现在Alice转账300元给Bob,正常的操作流程应该是执行两条SQL语句:
UPDATE balances SET balance = 700 WHERE account = "Alice";
UPDATE balances SET balance = 1300 WHERE account = "Bob";
在执行第一行操作时,如果系统出现问题导致未能执行第二行操作,这样会出现Alice的钱被扣除,而Bob的账户却没有增加相应金额的情况。这种错误发生后修复起来会非常麻烦。为此,数据库中的事务(Transaction)机制就是为了解决这类问题而设计的,它可以确保一个交易要么全部成功,要么全部失败,绝不会停留在中间状态。
在SQL中,可以声明事务的开始和结束,并在必要时回滚到原始状态,这样就能有效避免上述问题的发生。通过使用事务,当涉及到诸如银行转账(如从Alice账户扣款并转入Bob账户)这样的操作时,可以确保资金变动要么全部完成,要么都不发生,以此来维护数据库的一致性和完整性。
BEGIN TRANSACTION;
UPDATE balances SET balance = 700 WHERE account = "Alice";
UPDATE balances SET balance = 1300 WHERE account = "Bob";
COMMIT;
关系型数据库在处理事务时通常要求具备四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),即ACID原则。今天我们主要讨论的是“隔离性”。隔离性确保在系统中有多个事务同时执行时,每个事务之间互不影响。
以下是关于隔离性经常会遇到的几种现象,图表中的Y轴代表时间顺序。
如果一个事务还未提交,但其他事务却能读取到它已更新但尚未提交的数据,这种情况称为脏读。例如,事务A在执行过程中两次读取Alice的账户余额,第一次读取时数值为1000,但在事务A未结束前,另一个事务B改变了Alice的账户余额为700,但尚未提交。这时事务A再次读取Alice的余额,读取到了尚未提交的700元,这就是脏读。
在同一事务内,即使使用相同的查询语句多次读取数据,结果却发生了变化,这种情况称为不可重复读。例如,事务A第一次读取到Alice的账户余额为1000,在事务A还未结束时,事务B修改了Alice的余额为700并提交了事务。随后事务A再次执行相同的查询,这次读取到的余额变为700,这就构成了不可重复读。请注意,脏读也是不可重复读的一种特殊情况。
在同一个事务内连续两次执行同样的查询条件,但返回的记录数量却不一致,这种现象称为幻读。举例说明,事务A第一次查询账户余额在900到1000元范围内的账户,结果显示有Alice和Bob两人。而在事务A还未结束的同时,事务B将Alice的余额更新为700元。此时,如果事务A再次基于相同的条件查询,结果中的记录数会从原来的2条减至1条,这就发生了幻读。
针对上述提到的三种问题,SQL中通过不同的隔离等级来确定哪种等级的隔离性可以解决相应问题。隔离等级共有四种:
顺便提及,MySQL的默认隔离等级是可重复读,但可以通过以下命令调整为序列化:
SET SESSION transaction_isolation='SERIALIZABLE';
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。