Apache Cassandra和Apache Ignite:强一致和事务

NoSQL数据库,比如Apache Cassandra,是最终一致性系统的典型案例,这种系统的机制比较简单:如果应用在一台主机上触发了数据的变更,那么在某个时间点更新会被传播到所有的副本,换言之,最终一致。

在变更完全同步之前,系统作为一个整体会处于一个不一致的状态。如果从一个未同步的副本中读取变更的数据,更甚者,同时更新数据,谁知道会发生什么呢?

NoSQL的厂商和用户接受了这个机制和行为,因为最终一致给分布式系统带来了高可扩展和高性能,这是事实,如果需要强一致和事务,那么就得考虑传统RDBMS了,但是现在不是这样了!

在前面的文章中,提到即使在分布式数据库中,SQL也可以高效地执行。比如Apache Ignite,不仅仅可以执行简单的SQL操作,还可以容易地对存储于不同主机的数据进行关联,这在十年前是不可能的,但是目前已经成为现代分布式数据库的标配。

再次回到一致性和事务的话题,Ignite可以混合匹配NoSQL的水平扩展和高性能以及RDBMS领域的功能,下面会以Apache Cassandra作为NoSQL数据库的代表,与Ignite作为现代分布式数据库的代表进行比较。

可调一致性和轻量级事务

Cassandra关注于高数据一致性和事务,这不是秘密,因为这是用户的需求。 首先,如果将读写一致性级别配置为ALL,这就获得了最高的一致性。这个模式中,Cassandra会在完成提交日志和所有副本节点内存表的写入之后完成写入操作,相对应的对于读,会在所有副本认可的情况下,才会返回值。这个功能很方便,但是它在保证一致性的前提下,降低了性能,如果需要,可以启用。

其次,这个读写ALL模式没有解决并发更新的问题。如果要更新一个用户账户,如何确保没有其他人干扰呢?事务通常用于解决这样的问题,这时用户可以使用Cassandra的叫做轻量级事务(LWT)的功能。

轻量级事务是为避免单条记录的并发更新而特别设计的。比如,如果两个不同的应用都试图更新一个用户账户,那么LWT会确保只有一个应用成功,而其他的失败。假定第一个应用更早地发起了一个事务,那么就可以像下面这样安全而原子地对年龄进行修改:

UPDATE user_account
SET    user_age=35
IF     user_id=’Bob Smith’; 

但是对于更复杂的操作,比如不同账户间的转账,会怎么样呢?

不幸的是,这超出了Cassandra及其LWT的范围,因为后者限定在单一分区内,而银行账户是可以存储在不同的集群节点上的。

强一致和ACID事务

虽然转账在Cassandra中是个大问题,但是在Ignite中却是个常规操作。 首先,在Ignite中要做到强一致性,需要配置FULL_SYNC同步模式以及为缓存(或者表)开启事务模式,甚至开启FSYNC模式的预写日志来避免整个集群的电源故障。

其次,使用Ignite的事务API,可以对可能存储在不同节点上的账户间进行转账操作:

try (Transaction tx = Ignition.ignite().transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
    Account acctA = accounts.get(acctAid);
    
    Account acctB = accounts.get(acctBid);
    
    // Withdraw from accountB.
    acctB.update(amount);
    
    // Deposit into accountA.
    acctA.update(amount);

    // Store updated accounts in the cluster.
    accounts.put(acctAid, acctA);
    accounts.put(acctBid, acctB);

    tx.commit();
}

就是这些,Ignite的ACID事务基于2阶段提交(2PC)的高级版,即使故障,也能保证数据的一致性,参考这个系列文章,可以学习Ignite事务子系统实现的更多细节。

总结

Ignite证明了分布式的ACID事务和强一致是可行的,并且被可以水平扩展和高可用的现代数据库广泛采用,它完全可以根据需要进行配置。看一下有多少金融机构信任Ignite,将其部署进核心应用,你就知道ASF的项目不是徒有虚名。

本文译自Denis Magda的博客

猜你喜欢

转载自my.oschina.net/liyuj/blog/1787998