暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

最佳实践7| SequoiaDB巨杉数据库跨引擎事务

巨杉数据库 2021-01-20
1038

01 前言

在最佳实践系列第六篇中,我们利用多副本,实现了OLTP、OLAP的负载隔离。那么,单就OLTP系统而言,能否实现跨引擎事务功能呢?比如我们有2套应用系统,一套基于MySQL开发,一套基于PosgreSQL开发,两个应用系统能否灵活地实现数据互相访问,并且实现事务隔离?

在以往,这是不可能完成的。如果非要这样做,则需要将其中一套数据库,进行异构数据迁移,应用程序要做改造(SQL语句、存储过程等)。为了改造后达到兼容,还伴有大量的功能测试、性能测试,这样庞大的工作量,往往令人望而却步,转而走向业务的重新开发。

如果现在有这样一种技术,能让你在一个数据库里实现2种SQL引擎的访问,更为不可思议的是,不同的SQL引擎还可以实现事务,并支持4种事务隔离级别,你会相信么?

是的,您没有听错,巨杉数据库SequoiaDB的跨引擎事务特性,将这个需求的实现变成了可能。本文将进行讲解,便于您充分了解该技术特性,进而把它运用到适合的业务场景中,如微服务改造等。 

02 背景

目前,市面上主流的数据库,基本上都支持事务功能,网上也有很多技术资料,介绍数据库的事务功能及原理。

跨引擎事务则是一种新兴的技术概念。在这种技术下,同一个数据库,可以被不同的SQL引擎(如MySQL、PostgreSQL)访问,并且提供多种隔离级别、并发控制机制,实现事务的ACID。

目前,随着微服务架构的火热,各个应用系统都运行在相对独立的微服务中。这些服务是围绕业务功能构建的,因此,在软件开发阶段,软件厂商更倾向于采用自己惯用的SQL引擎。长此以往,就出现了多种数据库形态,如MySQL、PostgreSQL等,这导致不同的业务系统之间,不能够灵活的互相访问和数据共享。

巨杉数据库SequoiaDB,是一款多引擎数据库,提供一个统一的存储引擎作为底座,上层提供多种SQL引擎,如MySQL、PostgreSQL等,对数据进行访问。同时,为了支持更复杂的业务场景,提供了多种事务隔离级别。

结合巨杉数据库的高并发、在线弹性伸缩、海量数据存储等优势,可以很好地解决微服务架构下的数据碎片化、性能瓶颈和弹性扩张等问题。

03 SequoiaDB巨杉数据库跨引擎事务

3.1 并发事务的数据读取

在对数据库系统进行并发访问时,可能会出现几种情况:

1、 脏读

某事务,读取到了脏数据(正在被其它事务操作,但未提交的数据)。换句话说,数据查询结果,受到其他事务对数据的操作所影响,比如增、删、改等,尽管这些操作尚未提交。试想,一旦这些操作回滚,意味着读取到了无效数据。

例:情人节,小王的给他爱人转账520元(未提交),以表心意。小王老婆打开了账上银行APP,发现收到了520元的转账,喜笑颜开。但是,不料,小王提交转账订单时,网络中断,交易异常终止,转账操作被后台回滚,导致小王老婆空欢喜一场。在真实的业务场景中,在转账事务确认完成前,其他事务不应当感知到未提交的数据操作带来的影响,否则就会读取到无效数据,像本例中小王爱人收到的转账,就是无效数据。

为了避免这种情况,就需要读已提交隔离级别(Read-Committed)。

2、 不可重复读

一个事务中,对于相同的数据集,多次读取但得到的结果不一致(事务内,同一行数据多次查询不一致,因为被更新)。例:情人节,小王打算给老婆转520元,以表心意。他打开掌上银行APP,查询余额,发现账上还有1000元,因此发起转账,但操作时,APP却提醒他,余额不足,卡上只有400元。小王纳闷了,明明刚才还显示1000元,怎么余额不足了呢?打开账单明细发现,原来,不巧就在刚刚查询后到发起转账的短短的几秒钟,自动扣除了600元的自动还款,因时余额已经不足够完成此次的转账。

这就是一个不可重复读的例子。为了避免这种情况,就需要可重复读隔离级别(Read-Repeatable)。

3、 幻读

同一个事务中,多次查询返回的结果集数目不一致。例如:事务A新增了一条记录,事务B在事务A提交前、后读到的结果可能不一致,提交后多查到一条,就好像“幻象”一样,多出来一条记录。幻读是由其他事务插入(或删除)导致的。因为新增数据无法加行锁,所以无法通过MVCC或者锁表来解决幻读。

需要强调一下,不可重复度和幻读,描述有些相似,都是指在同一个事务中,对多次查询可能得不到一致的结果。区别是:不可重复读,主要是针对数据更新,幻读主要是针对数据结果集的变化,如增加、删除等。

 

3.2 事务隔离级别

在实际的业务系统中,不同的业务,对于事务并发访问的要求不同。为了达到其业务目的,避免上述的一种或多种问题,通常的做法,就是设置事务隔离级别。

巨杉分布式数据库SequoiaDB,提供了四种事务隔离级别。每种隔离级别,与其对应的并发问题处理能力如下:

隔离级别

脏读

不可重复度

幻读

读未提交 RU

可能

可能

可能

读已提交 RC

不可能

可能

可能

读稳定性 RS

不可能

不可能

可能

可重复读 RR

不可能

不可能

不可能

1. 读未提交(Read Uncommitted,简称RU):一个事务,能够访问到其他事务未提交的数据,这是最低隔离级别。在这种隔离级别下,上述所有的问题都会发生,如脏读、不可重复读、幻读。
2. 读已提交(Read Committed,简称RC):事务只会读取每条数据最新被提交的状态,从而避免了脏读。但是,无法保证事务中多次查询数据得到一致的结果,也就是说,仍无法避免不可重复读、幻读的发生。
3. 读稳定性(Read Stability,简称RS):事务中对同一份数据的多次读取,其结果与事务中首次读取到的数据一致,在该事务结束前,该查询结果保持不变。因此,在RC的基础上,进一步避免了不可重复读。
4. 可重复读(Repeatable Read,简称RR):事务中对同一份数据的多次读取,其结果与事务中首次读取到的数据一致,在该事务结束前,该查询结果保持不变。且符合查询条件的结果集记录数,也不会因为其他事务对数据的操作而改变。在该隔离级别下,脏读、不可重复读、幻读的问题都得到了避免。

Note: 

还有一种隔离级别经常被提到,那就是串行化读(Serialization)。这是一种十分严苛的隔离级别,解决了所有并发问题,如脏读、不可重复读、幻读等。但在实际业务系统中,这种隔离级别采用得很少,因为它要求事务必须串行化执行,不允许事务并发,这样等同于完全牺牲了系统的并发能力。

从以上可以看出,巨杉提供的四种隔离级别中,可重复读(RR)隔离级别最高:

  • 本身已包含读已提交(RC),避免了脏读;

  • 事务对数据进行首次读取时,其查询结果已经被确定,在该事务结束前查询到的结果,不会因其他事务对数据的修改而改变,避免了不可重复读;

  • 会因为其他事务对数据的新增和插入,而改变结果集的记录个数,避免了幻读。

 

3.3 SequoiaDB 跨引擎事务

巨杉数据库SequoiaDB,作为分布式数据库,将数据分布在多台服务器中的数据节点中。

  • 由SequoiaDB分布式存储引擎和SQL实例构成,底层的存储引擎提供统一的数据存储,上层的SQL实例只完成SQL语法的解析;

  • 由底层的SequoiaDB分布式存储引擎实现事务的并发控制,如事务隔离、锁机制等;

  • 通过二阶段提交协议实现分布式事务,支持跨表跨节点的事务原子操作;

  • 只需要适配对应SQL引擎的语法,即可达到跨引擎事务的效果。

 

SequoiaDB 巨杉数据库的可重复读级别(Repeatable Read,以下简称RR),通过多版本并发控制(MVCC,Multi-Version Convurrency Control)来实现。MVCC 是一种数据库常用的数据库并发控制机制,通过保存数据在某个事务时间点的快照来进行事务隔离控制。

RR级别使用STP(时间序列协议,Serial Time Protocol),为分布式事务分配全局时间,并使用全局事务一致性的仲裁机制,对分布式事务实现了因果排序,再结合 MVCC 的可见性算法,从而实现了分布式事务的全局一致性(可参考 时间序列服务)。
下文中,我们通过实验,来验证SequoiaDB的事务隔离级别,在并发场景中的表现。

04 分布式数据库HTAP最佳实践

4.1 环境描述

巨杉分布式数据库SequoiaDB,能够提供RU、RC、RS、RR四种隔离级别。RR是SequoiaDB提供的4种隔离级别中,最严格的一种,而且这也是MySQL的默认级别。

传统的关系型数据库,一般都只能实现RC级别。

因此,下文以RR、RC隔离级别来测试事务的隔离性。其他隔离级别的测试流程类似。

内容

隔离级别

RR、RC

SQL引擎

MySQL、PostgreSQL

测试项

RC:脏读

RR:脏读、不可重复读、幻读

 

本文中,采用了2个SQL实例,一个MySQL实例,一个PostgreSQL实例,底层使用一个统一的巨杉分布式存储引擎。

 

4.2. 参数设置

在SequoiaDB中,为了实现RR级别,需要通过数据库参数,开启全局事务和MVCC。此外,还需要时间序列服务(STP)的支持。

1. SequoiaDB数据库参数设置:

    //开启全局事务
    db.updateConf( { globtranson:true } );

    //开启MVCC(需重启数据节点)
    db.updateConf( { mvccon:1 } );

    2. 查看参数设置

      db.snapshot(SDB_SNAP_CONFIGS, { Role: "data" }, { NodeName: "", transisolation: "" ,transactionon: "",globtranson:"",mvccon: "" });

      参数解释:

      Transisolation表示在开启事务的情况下,使用的事务隔离级别。

      0:表示 RU, 

      1:表示 RC, 

      2:表示 RS, 

      3:表示 RR。

      3. 开启并检查STP服务

        //开启全局事务
        stpstart
        sdblist -t all -l |grep stp

        4. 为了便于演示,本实验环境关闭了SQL客户端的自动提交

          //MySQL
          set AUTOCOMMIT=off
          //PostgreSQL
          \set AUTOCOMMIT off

          4.3. 测试数据准备

          1. 登陆MySQL实例,创建bills数据库,创建一张orders表。

            $ 登陆mysql实例
            mysql -uroot -proot -h 127.0.0.1 -P 3306

            //创建database
            create database bills;
            use bills;

            //创建orders表
            create table bills.orders (
            order_id int,
            p_date date,
            location varchar(100) ,
            primary key (order_id)
            );

             2. 向bills.orders表中插入4条记录,并查询

              insert into bills.orders values(10001,'2017-06-01','Beijing');
              insert into bills.orders values(10002,'2018-06-01','Shanghai' );
              insert into bills.orders values(10003,'2019-06-01','Guangzhou' );
              insert into bills.orders values(10004,'2020-06-01','Shenzhen' );
              commit;
              select * from orders;

               3. 登陆PostgreSQL实例、创建bills数据库

                $ psql -p 5432
                create database bills;
                \c bills

                4. 创建SequoiaDB的连接器sdb_server和外部表orders,其中连接器默认开启事务

                  # 加载 SequoiaDB 连接驱动
                  create extension sdb_fdw;

                  # 创建sdb_server连接器
                  create server sdb_server foreign data wrapper sdb_fdw options(address 'localhost', service '11810', transaction 'on');

                  # 创建外部表order外表
                  create foreign table orders (
                  order_id int,
                  p_date date,
                  location varchar(100)
                  )
                  server sdb_server options ( collectionspace 'bills', collection 'orders', decimal 'on' ) ;

                   5. 数据查询验证

                  在PostgreSQL中查询orders表的数据。

                    select * from orders;

                    结果显示,PostgreSQL中的数据,与MySQL中插入的数据一致,说明数据正确,PostgreSQL和MySQL使用相同的数据源。

                     

                    4.4. 测试过程

                    4.4.1. RR模式测试

                    1. 设置SequoiaDB存储引擎事务隔离级别为RR

                      db.updateConf( { transisolation:3 } )

                      2. 修改MySQL的事务隔离级别(下一个事务开始生效)。

                      注:虽然在事务隔离级别SequoiaDB存储引擎中已经设置,但MySQL实例中仍需设置。PostgreSQL中因为使用了外部表,因此无需设置。

                        //MySQL中设置事务隔离级别为RR
                        set transaction_isolation='REPEATABLE-READ';

                        //查询
                        select @@tx_isolation;
                        3. 在MySQL会话中开启事务,将order_id=10001的订单数据的location字段,由Beijing修改为’Nanjing’,暂不提交。
                          begin;
                          update orders set location='Nanjing' where order_id=10001;
                          4. 在PostgreSQL会话中开启事务,查询数据。
                            begin;
                            select * from orders;
                            可以看到,在MySQL中未提交的数据,在PostgreSQL中查询不到。说明未发生脏读。
                            5. 在MySQL会话中,将会话提交。
                              commit;
                              6. 在PostgreSQL中,再次查询数据。
                                select * from orders;
                                可以看到,即便MySQL中已将update操作提交,但在PostgreSQL中,查到的数据,仍然是事务中首次查询时的状态。说明未发生不可重复读。
                                7. 在MySQL会话中执行insert操作,插入一条order_id为10005的订单数据,并提交。
                                  insert into bills.orders values(10005,'2021-01-01','Hangzhou' );
                                  commit;
                                  select * from orders;
                                  8. 在PostgreSQL会话中,再次查询
                                    select * from orders;
                                    可以看到,即便MySQL中插入了一条数据,且已提交,但在PostgreSQL中,查到的数据,仍然是事务中首次读取的状态,结果集的数量也没有发生改变。说明未发生幻读。
                                     
                                    通过上述步骤可以验证,由两个不同SQL引擎发起的事务,能够达到RR(可重复读)隔离级别,避免了脏读、不可重复读、幻读的情况。

                                    4.4.2. RC模式测试
                                    1. 在MySQL中,将测试数据初始化
                                      delete from orders;
                                      insert into bills.orders values(10001,'2017-06-01','Beijing');
                                      insert into bills.orders values(10002,'2018-06-01','Shanghai' );
                                      insert into bills.orders values(10003,'2019-06-01','Guangzhou' );
                                      insert into bills.orders values(10004,'2020-06-01','Shenzhen' );
                                      commit;
                                      select * from orders;

                                      2. 设置SequoiaDB存储引擎的事务隔离级别为RC。

                                        db.updateConf( { transisolation:1 } )
                                        3. 设置MySQL的事务隔离级别为RC(下一个事务开始)。
                                          set transaction_isolation='READ-COMMITTED';
                                          select @@tx_isolation;
                                          4.在MySQL会话中开启事务,将order_id=10001的记录的location字段由’Beijing’修改为’Nanjing’,暂不提交;
                                            begin;
                                            update orders set location='Nanjing' where order_id=10001;
                                            select * from orders;
                                            5. 在PostgreSQL会话中结束刚才的事务,并开启新的事务,查询数据。
                                              rollback;
                                              begin;
                                              select * from orders;

                                              可以看到,在MySQL中未提交的数据,在PostgreSQL中查询不到。说明未发生脏读。

                                              6. 在MySQL会话中,将会话提交。

                                                commit;

                                                 7. 在PostgreSQL中,再次查询数据。

                                                  select * from orders;
                                                  可以看到,MySQL中将update操作提交后,在PostgreSQL中,可以查询到提交后的数据。说明实现了RC(读已提交)隔离级别。

                                                  05 总结

                                                  在本文中,我们为大家介绍了SequoiaDB巨杉数据库的事务隔离级别,以及每种事务隔离级别,对应的并发问题处理能力。SequoiaDB支持RU、RC、RS、RR四种事务隔离级别,本文重点对的RR、RC级别,进行了验证。
                                                  通过验证,SequoiaDB的RR级别,能够避免脏读、不可重复读、幻读等数据可见性问题;RC级别,能够避免脏读问题。其他的隔离级别,读者朋友们有兴趣的话可以自行验证一下~
                                                  另外,我们将在近期推出《SequoiaDB数据库HDD/SSD混合部署最佳实践》,请期待!
                                                  往期技术干货
                                                  最佳实践6|分布式数据库HTAP混合负载最佳实践
                                                  最佳实践5|SequoiaDB巨杉数据库在线扩容(下)

                                                  最佳实践5|SequoiaDB巨杉数据库在线扩容(上)

                                                  最佳实践4|SequoiaDB巨杉数据库两地三中心部署
                                                  最佳实践3|SequoiaDB同城双中心灾难恢复工具
                                                  最佳实践2 | SequoiaDB同城双中心部署规划
                                                  最佳实践 | SequoiaDB单中心三副本部署规划

                                                  点击阅读原文,了解巨杉数据库最新资讯
                                                  文章转载自巨杉数据库,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                  评论