This chapter walks you through a simple demonstration of how TiKV’s distributed transaction works.

Prerequisites

Before you start, ensure that you have set up a TiKV cluster and installed the tikv-client Python package according to TiKV in 5 Minutes.

TiKV Java client’s Transaction API has not been released yet, so the Python client is used in this example.

Test snapshot isolation

Transaction isolation is one of the foundations of database transaction processing. Isolation is one of the four key properties of a transaction (commonly referred as ACID).

TiKV implements Snapshot Isolation (SI) consistency, which means that:

  • all reads made in a transaction will see a consistent snapshot of the database (in practice TiKV Client reads the last committed values that exist when TiKV Client starts);
  • the transaction will successfully commit only if the updates that a transaction has made do not conflict with the concurrent updates made by other transactions since that snapshot.

The following example shows how to test TiKV’s snapshot isolation.

Save the following script to file test_snapshot_isolation.py.

  1. from tikv_client import TransactionClient
  2. client = TransactionClient.connect("127.0.0.1:2379")
  3. # clean
  4. txn1 = client.begin()
  5. txn1.delete(b"k1")
  6. txn1.delete(b"k2")
  7. txn1.commit()
  8. # put k1 & k2 without commit
  9. txn2 = client.begin()
  10. txn2.put(b"k1", b"Snapshot")
  11. txn2.put(b"k2", b"Isolation")
  12. # get k1 & k2 returns nothing
  13. # cannot read the data before transaction commit
  14. snapshot1 = client.snapshot(client.current_timestamp())
  15. print(snapshot1.batch_get([b"k1", b"k2"]))
  16. # commit txn2
  17. txn2.commit()
  18. # get k1 & k2 returns nothing
  19. # still cannot read the data after transaction commit
  20. # because snapshot1's timestamp < txn2's commit timestamp
  21. # snapshot1 can see a consistent snapshot of the database
  22. print(snapshot1.batch_get([b"k1", b"k2"]))
  23. # can read the data finally
  24. # because snapshot2's timestamp > txn2's commit timestamp
  25. snapshot2 = client.snapshot(client.current_timestamp())
  26. print(snapshot2.batch_get([b"k1", b"k2"]))

Run test script

  1. python3 test_snapshot_isolation.py
  2. []
  3. []
  4. [(b'k1', b'Snapshot'), (b'k2', b'Isolation')]

From the above example, you can find that snapshot1 cannot read the data before and after txn2 is commited. This indicates that snapshot1 can see a consistent snapshot of the database.

Try optimistic transaction model

TiKV supports distributed transactions using either pessimistic or optimistic transaction models.

TiKV uses the optimistic transaction model by default. With optimistic transactions, conflicting changes are detected as part of a transaction commit. This helps improve the performance when concurrent transactions infrequently modify the same rows, because the process of acquiring row locks can be skipped.

The following example shows how to test TiKV with optimistic transaction model.

Save the following script to file test_optimistic.py.

  1. from tikv_client import TransactionClient
  2. client = TransactionClient.connect("127.0.0.1:2379")
  3. # clean
  4. txn1 = client.begin(pessimistic=False)
  5. txn1.delete(b"k1")
  6. txn1.delete(b"k2")
  7. txn1.commit()
  8. # create txn2 and put k1 & k2
  9. txn2 = client.begin(pessimistic=False)
  10. txn2.put(b"k1", b"Optimistic")
  11. txn2.put(b"k2", b"Mode")
  12. # create txn3 and put k1
  13. txn3 = client.begin(pessimistic=False)
  14. txn3.put(b"k1", b"Optimistic")
  15. # txn2 commit successfully
  16. txn2.commit()
  17. # txn3 commit failed because of conflict
  18. # with optimistic transactions conflicting changes are detected when the transaction commits
  19. txn3.commit()

Run the test script

  1. python3 test_optimistic.py
  2. Exception: KeyError WriteConflict

From the above example, you can find that with optimistic transactions, conflicting changes are detected when the transaction commits.

Try pessimistic transaction model

In the optimistic transaction model, transactions might fail to be committed because of write–write conflict in heavy contention scenarios. In the case that concurrent transactions frequently modify the same rows (a conflict), pessimistic transactions might perform better than optimistic transactions.

The following example shows how to test TiKV with pessimistic transaction model.

Save the following script to file test_pessimistic.py.

  1. from tikv_client import TransactionClient
  2. client = TransactionClient.connect("127.0.0.1:2379")
  3. # clean
  4. txn1 = client.begin(pessimistic=True)
  5. txn1.delete(b"k1")
  6. txn1.delete(b"k2")
  7. txn1.commit()
  8. # create txn2
  9. txn2 = client.begin(pessimistic=True)
  10. # put k1 & k2
  11. txn2.put(b"k1", b"Pessimistic")
  12. txn2.put(b"k2", b"Mode")
  13. # create txn3
  14. txn3 = client.begin(pessimistic=True)
  15. # put k1
  16. # txn3 put data failed because of conflict
  17. # with pessimistic transactions conflicting changes are detected when writing data
  18. txn3.put(b"k1", b"Pessimistic")

Run the test script

  1. python3 test_pessimistic.py
  2. Exception: KeyError

From the above example, you can find that with pessimistic transactions, conflicting changes are detected at the moment of data writing.