身为开发人员,这些数据库合知识不掌握不合适( 二 )


数据库通常提供不同的隔离层,这样应用程序开发者可以选取性价比最佳的隔离层使用 。较弱的隔离速度较快,但可能会引发数据竞争 。强隔离会消除可能的数据竞争,但速度比较慢,而且可能会引入数据冲突,从而拖慢数据库速度,甚至会导致宕机 。

身为开发人员,这些数据库合知识不掌握不合适

文章插图
现有并发模型及其关系概览
SQL标准只定义了四层隔离,尽管无论从理论还是从实践来看,更多的隔离层也是可能的 。jepson.io(https://jepsen.io/consistency)提供了对于以后并发模型的另一种观点,你可以阅读一下 。例如,Google的Spanner能够保证带有时钟同步的外部序列化,而且这是一个标准中没有定义的、更强的隔离层 。
SQL标准中提到的隔离级别如下:
  • 可序列化(最严格,最昂贵):在同一个事务中,按顺序执行与并行执行能够产生相同的效果 。串行执行是指每个事务在下一个事务开始之前执行完成 。需要指出,序列化通常实现为“快照隔离”(如Oracle),而快照隔离并非SQL标准的内容 。
  • 可重复的读操作:当前事务中未提交的读操作仅对当前事务可见,而其他事务造成的读操作是不可见的 。
  • 读提交:未提交的读操作对于其他事务不可见 。只有提交的写操作才可见,但可能会出现影子读取的问题 。如果另一个事务插入并提交了新行,那么当前事务可能会看到新的数据 。
  • 未提交的读操作(不太严格,但比较廉价):允许脏读取,事务可以看到其他事务尚未提交的改变 。在实践中,该级别可以用来返回大致的聚合结果,如在表上执行COUNT(*)操作 。
可序列化的级别允许发生的数据竞争最少,但实现起来最昂贵,会给系统引入最多的数据竞争 。其他隔离级别比较廉价,但会增加数据竞争的可能性 。一些数据库允许自己设置隔离级别,另一些数据库则不支持某些隔离级别 。
即使自称支持某些隔离级别的数据库,也需要仔细检查其行为,理解其实际的操作:
身为开发人员,这些数据库合知识不掌握不合适

文章插图
不同数据库的不同隔离级别的实现 。
 
在无法保持锁的情况下可以采用乐观锁锁的实现可能非常昂贵,不仅因为它会引入数据竞争,还要求应用程序与数据库服务器之间存在稳定的连接 。排他锁可能会更严重地受到网络分区的影响,并导致难以识别和解决的死锁 。如果无法持有排他锁,则可以选择乐观锁 。
乐观锁指的是,在读取行时,记录版本号以及最近修改的时间戳或其校验和 。然后,您可以在更改记录之前检查版本是否没有原子更改 。
UPDATE productsSET name = 'Telegraph receiver', version = 2WHERE id = 1 AND version = 1如果另一个更新之前更改了该行,则对产品表的更新将影响0行 。如果没有更早的更新,它将影响1行,并且我们可以判断我们的更新已成功 。
5 除了脏读取和数据丢失之外,还有其他的异常情况在讨论数据一致性时,通常我们会将注意力放在可能会导致脏读取和数据丢失的数据竞争上 。然而数据的异常情况并不止这两种 。
这类异常的一个例子是写偏斜 。写偏斜很难识别,因为我们没有特意寻找它们 。写偏斜的原因不是发生脏读取或数据丢失,而是数据的逻辑约束被破坏 。
例如,假设某个监控应用程序要求随时必须有一个操作员在值守 。
身为开发人员,这些数据库合知识不掌握不合适

文章插图
在上述情况中,如果两个事务都成功提交,则会发生写偏斜 。尽管没有发生任何脏读取,也没有发生数据丢失,数据的一致性也会被破坏,因为两个操作员都被分配了值守任务 。
可序列化的隔离、结构设计或数据库约束可以帮助消灭写偏斜问题 。开发者应该能够在开发过程中识别出这种异常,并在生产环境中避免这种数据异常 。话虽如此,识别代码中可能出现的写偏斜非常困难 。即使在大型系统中,如果不同的团队负责同一个表上的不同功能,而缺乏互相交流,也会出现这种问题 。
6  数据库和我对于顺序的理解不一致数据库的核心功能之一就是保证顺序,但数据库理解的顺序可能与应用程序开发者看到的顺序不一致 。数据库看到的事务顺序是按照接收的时间排序的,而不是开发者认为的顺序 。在高并发系统中,事务的执行顺序很难预测 。
在开发期间,尤其是在使用非阻塞库时,较差的代码风格和可读性可能会导致以下问题:用户认为事务可以顺序执行,即使它们可以以任何顺序到达数据库 。下面的程序使T1和T2看起来将被顺序调用,但是如果这些函数是非阻塞的并且立即以承诺返回,则调用的顺序将取决于它们在数据库中收到的时间 。


推荐阅读