Apache Cassandra and Apache Ignite: Strong Consistency and Transactions

NoSQL databases, such as Apache Cassandra, are typical examples of eventually consistent systems, where the mechanics are relatively simple: if an application triggers a data change on one host, then at some point the update will be propagated to all Replicas, in other words, are eventually consistent.

The system as a whole is left in an inconsistent state until the changes are fully synchronized. Who knows what will happen if the changed data is read from an unsynchronized replica, and more importantly, the data is updated at the same time?

NoSQL vendors and users have embraced this mechanism and behavior because eventual consistency brings high scalability and high performance to distributed systems. This is a fact . If strong consistency and transactions are required, then traditional RDBMSs have to be considered, but now Not so!

In the previous article , it was mentioned that SQL can be executed efficiently even in a distributed database. For example, Apache Ignite can not only perform simple SQL operations, but also easily correlate data stored on different hosts, which was impossible ten years ago, but has now become the standard of modern distributed databases.

Returning to the topic of consistency and transactions again, Ignite can mix and match the horizontal scaling and high performance of NoSQL with the capabilities of the RDBMS domain. The following will compare Apache Cassandra as a representative of NoSQL databases and Ignite as a representative of modern distributed databases.

Tunable consistency and lightweight transactions

It's no secret that Cassandra focuses on high data consistency and transactions because that's what users want. First, if the read-write consistency level is configured to ALL , this achieves the highest consistency. In this mode, Cassandra will complete the write operation after completing the writing of the commit log and the memory table of all replica nodes. Correspondingly, for reading, the value will be returned only when all replicas approve it. This feature is convenient, but it reduces performance under the premise of ensuring consistency, and can be enabled if needed.

Second, this read-write ALL pattern does not solve the problem of concurrent updates. If you are updating a user account, how do you make sure no one else is interfering? Transactions are often used to solve such problems, in which case users can use a feature of Cassandra called Lightweight Transactions (LWT).

Lightweight transactions are specially designed to avoid concurrent updates of a single record . For example, if two different applications are trying to update a user account, LWT will ensure that only one application succeeds and the others fail. Assuming the first application initiated a transaction earlier, the age can be modified safely and atomically as follows:

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

But what about more complex operations, such as transfers between different accounts?

Unfortunately, this is beyond the scope of Cassandra and its LWT, as the latter is limited to a single partition, whereas bank accounts can be stored on different cluster nodes.

Strong Consistency and ACID Transactions

While transfers are a big problem in Cassandra, they are a regular operation in Ignite. First of all, to achieve strong consistency in Ignite, you need to configure the FULL_SYNC synchronization mode and enable transaction mode for the cache (or table) , or even enable the write-ahead log in FSYNC mode to avoid power failure of the entire cluster.

Second, using Ignite's transaction API, it is possible to transfer funds between accounts that may be stored on different nodes:

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();
}

That's all, Ignite's ACID transaction is based on an advanced version of 2-phase commit (2PC) , which ensures data consistency even if it fails. Refer to this series of articles to learn more details about the implementation of Ignite's transaction subsystem.

Summarize

Ignite proves that distributed ACID transactions and strong consistency are feasible, and is widely adopted by modern databases that are horizontally scalable and highly available, and it is fully configurable as needed. Take a look at how many financial institutions trust Ignite to deploy it into their core applications, and you'll know that ASF's project is not in vain.

This article is translated from Denis Magda's blog .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324377616&siteId=291194637