读写偏斜现象的初学者指南

介绍

在关于ACID和数据库事务的文章中,我介绍了SQL标准描述的三种现象:

  • 脏读
  • 不可重复读
  • 幻读

尽管这些可以很好地区分四个隔离级别(未提交读,已提交读,可重复读和可序列化),但实际上,还有更多的现象需要考虑。 1995年的论文(《 ANSI SQL隔离级别批判》)介绍了标准规范中省略的其他现象。

在我的《高性能Java持久性》一书中,我决定坚持“事务”一章,因为它对数据访问的有效性和效率都非常重要。

领域模型

对于以下示例,我将使用以下两个实体:

读写偏斜现象的初学者指南_第1张图片

在我们的虚构应用程序中,更改“ 帖子”标题后,必须将作者记录在关联的“ PostDetails”记录中。 如果未防止读写时滞,则可以破坏此域模型约束,如以下测试案例所示。

读取歪斜

以下测试模拟了读取偏斜如何发生:

doInConnection(aliceConnection -> {
    prepareConnection(aliceConnection);
    String title = selectStringColumn(
        aliceConnection, 
        selectPostTitleSql()
    );
    executeSync(() -> {
        doInConnection(bobConnection -> {
            prepareConnection(bobConnection);
            try {
                update(
                    bobConnection, 
                    updatePostTitleParamSql(), 
                    new Object[]{"Bob"}
                );
                update(
                    bobConnection, 
                    updatePostDetailsAuthorParamSql(), 
                    new Object[]{"Bob"}
                );
            } catch (Exception e) {
                LOGGER.info("Exception thrown", e);
                preventedByLocking.set(true);
            }
        });
    });
    String createdBy = selectStringColumn(
        aliceConnection, 
        selectPostDetailsAuthorSql()
    );
});
  • 爱丽丝选择一个帖子标题
  • Bob潜入并更新了PostPostDetails实体
  • Alice线程将继续,并且她选择PostDetails记录

如果允许读取偏斜,则Alice会看到Bob的更新,并且可以假定先前的Post版本(在交易开始时读取)是Bob发行的(可能不准确)。

在四个最常见的关系数据库系统上运行此测试可获得以下结果:

数据库隔离级别 读取歪斜
Oracle读已提交
Oracle可序列化 没有
SQL Server读未提交
SQL Server读取已提交
SQL Server读取提交的快照隔离
SQL Server可重复读取 没有
SQL Server可序列化 没有
SQL Server快照隔离 没有
PostgreSQL读未提交
PostgreSQL读已提交
PostgreSQL可重复读 没有
PostgreSQL可序列化 没有
MySQL读取未提交
MySQL读取提交
MySQL可重复读 没有
MySQL可序列化 没有

写偏斜

要模拟写偏斜,您需要执行以下测试用例:

doInConnection(aliceConnection -> {
    prepareConnection(aliceConnection);
    String title = selectStringColumn(
        aliceConnection, 
        selectPostTitleSql()
    );
    String createdBy = selectStringColumn(
        aliceConnection, 
        selectPostDetailsAuthorSql()
    );
    executeSync(() -> {
        doInConnection(bobConnection -> {
            prepareConnection(bobConnection);
            try {
                String bobTitle = selectStringColumn(
                    bobConnection, 
                    selectPostTitleSql()
                );
                String bonCreatedBy = selectStringColumn(
                    bobConnection, 
                    selectPostDetailsAuthorSql()
                );
                update(
                    bobConnection, 
                    updatePostTitleParamSql(), 
                    new Object[]{"Bob"}
                );
            } catch (Exception e) {
                LOGGER.info("Exception thrown", e);
                preventedByLocking.set(true);
            }
        });
    });
    update(
        aliceConnection, 
        updatePostDetailsAuthorParamSql(), 
        new Object[]{"Alice"}
    );
});
  • 爱丽丝从PostDetails记录中选择Post标题和作者
  • Bob还选择了Post标题和相关的作者,但他决定仅更新标题
  • 爱丽丝想在不更改帖子标题的情况下更新PostDetails记录

如果允许写偏斜,则将执行Alice和Bob不相交的写操作,而不会受到控制这两个记录的约束的阻止。

在四个最常见的关系数据库系统上运行此测试可获得以下结果:

数据库隔离级别 写偏斜
Oracle读已提交
Oracle可序列化
SQL Server读未提交
SQL Server读取已提交
SQL Server读取提交的快照隔离
SQL Server可重复读取 没有
SQL Server可序列化 没有
SQL Server快照隔离
PostgreSQL读未提交
PostgreSQL读已提交
PostgreSQL可重复读
PostgreSQL可序列化 没有
MySQL读取未提交
MySQL读取提交
MySQL可重复读
MySQL可序列化 没有

  • 写偏斜在多版本并发控制机制中很普遍,即使声称使用“可序列化”,Oracle也无法阻止它,而实际上它只是一个快照隔离级别。
  • 当使用“可重复读”和“可序列化”时,SQL Server默认的基于锁定的隔离级别可以防止写偏斜。 快照隔离级别(基于MVCC)中的任何一个都不能阻止/检测到它。
  • PostgreSQL使用更高级的可序列化快照隔离级别来阻止它
  • MySQL在使用Serializable时会使用共享锁,因此即使InnoDB也是基于MVCC的,也可以防止写偏斜

如果您对此主题感兴趣,那么您也可以阅读我正在编写的《 高性能Java持久性》一书。

翻译自: https://www.javacodegeeks.com/2015/10/a-beginners-guide-to-read-and-write-skew-phenomena.html

你可能感兴趣的:(读写偏斜现象的初学者指南)