> [!info] Optimistic Concurrency Control (OCC) and Multi-Version Concurrency Control (MVCC) are two concurrency control mechanisms used to manage simultaneous operations in database systems, ensuring data consistency and isolation without significant performance degradation.
## Optimistic Concurrency Control (OCC)
### Overview
- **Assumption:** Transactions are unlikely to conflict.
- **Approach:** Transactions execute without acquiring locks. Before committing, each transaction checks whether other transactions have modified the data it has read.
- **Conflict Resolution:** If a conflict is detected during the validation phase, the transaction is rolled back and can be retried.
### Implementation in C++
Below is a simplified example demonstrating how OCC might be implemented using version numbers.
```cpp
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
#include <chrono>
class DataItem {
public:
int value;
std::atomic<int> version;
DataItem(int val) : value(val), version(0) {}
};
void optimisticTransaction(DataItem& item, int newValue, int transactionID) {
int readVersion = item.version.load();
int readValue = item.value;
std::cout << "Transaction " << transactionID << " read value: " << readValue << ", version: " << readVersion << "\n";
// Simulate some processing time
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Validation phase
if (item.version.load() == readVersion) {
// No other transaction has modified the data; safe to commit
item.value = newValue;
item.version++;
std::cout << "Transaction " << transactionID << " committed new value: " << newValue << "\n";
} else {
// Conflict detected
std::cout << "Transaction " << transactionID << " detected conflict; rolling back.\n";
// Retry logic can be implemented here
}
}
int main() {
DataItem item(100);
std::thread t1(optimisticTransaction, std::ref(item), 200, 1);
std::thread t2(optimisticTransaction, std::ref(item), 300, 2);
t1.join();
t2.join();
std::cout << "Final value of item: " << item.value << "\n";
return 0;
}
```
### Explanation
- **DataItem Class:** Represents a data item with a value and a version number.
- **optimisticTransaction Function:** Simulates a transaction that reads the data item's value and version, waits to simulate processing, and then checks if the version has changed before committing.
- **Version Checking:** If the version hasn't changed, the transaction commits by updating the value and incrementing the version. Otherwise, it detects a conflict and rolls back.
---
## Multi-Version Concurrency Control (MVCC)
### Overview
- **Assumption:** Multiple versions of data can coexist.
- **Approach:** When data is updated, a new version is created without overwriting the existing data. Transactions access the version of the data that was current at their start time.
- **Conflict Resolution:** Readers don't block writers and vice versa, reducing lock contention.
### Implementation in C++
Below is a simplified example demonstrating MVCC using versioned data items.
```cpp
#include <iostream>
#include <thread>
#include <map>
#include <shared_mutex>
class VersionedDataItem {
public:
std::map<int, int> versions; // Key: version number, Value: data value
std::shared_mutex mutex;
VersionedDataItem(int initialVersion, int initialValue) {
versions[initialVersion] = initialValue;
}
int read(int transactionVersion) {
std::shared_lock lock(mutex);
auto it = versions.upper_bound(transactionVersion);
if (it == versions.begin()) {
throw std::runtime_error("No valid version found.");
}
--it;
return it->second;
}
void write(int transactionVersion, int newValue) {
std::unique_lock lock(mutex);
versions[transactionVersion] = newValue;
}
};
void readerTransaction(VersionedDataItem& item, int transactionVersion) {
try {
int value = item.read(transactionVersion);
std::cout << "Reader Transaction " << transactionVersion << " read value: " << value << "\n";
} catch (const std::exception& e) {
std::cout << "Reader Transaction " << transactionVersion << " failed: " << e.what() << "\n";
}
}
void writerTransaction(VersionedDataItem& item, int transactionVersion, int newValue) {
item.write(transactionVersion, newValue);
std::cout << "Writer Transaction " << transactionVersion << " wrote new value: " << newValue << "\n";
}
int main() {
VersionedDataItem item(1, 100);
std::thread reader1(readerTransaction, std::ref(item), 1);
std::thread writer(writerTransaction, std::ref(item), 2, 200);
std::thread reader2(readerTransaction, std::ref(item), 1);
std::thread reader3(readerTransaction, std::ref(item), 2);
reader1.join();
writer.join();
reader2.join();
reader3.join();
return 0;
}
```
### Explanation
- **VersionedDataItem Class:** Maintains a map of versions to data values.
- **read Function:** Reads the most recent version not greater than the transaction's version number.
- **write Function:** Writes a new version of the data.
- **Transactions:**
- **Reader Transactions:** Attempt to read data based on their transaction version.
- **Writer Transaction:** Writes a new version without affecting readers accessing older versions.
---
## Differences Between OCC and MVCC
| Aspect | Optimistic Concurrency Control (OCC) | Multi-Version Concurrency Control (MVCC) |
|-----------------------|---------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| **Conflict Handling** | Detects conflicts at commit time and may roll back transactions | Avoids conflicts by providing each transaction with a consistent snapshot |
| **Versioning** | Uses version numbers primarily for validation | Maintains multiple versions of data items |
| **Locking** | Minimal locking; relies on version checks during validation | Uses shared/exclusive locks or lock-free mechanisms for version management |
| **Performance** | Efficient when conflicts are rare; may suffer from high rollback rates otherwise | Performs well under read-heavy workloads; may incur overhead due to version storage |
| **Use Cases** | Suitable for systems with low data contention | Ideal for databases with high concurrency and long-running read transactions |
---
## Summary
- **OCC** is optimistic, assuming conflicts are rare. It allows transactions to proceed without locking resources and checks for conflicts before committing.
- **MVCC** provides each transaction with a consistent view of the database by keeping multiple versions of data, allowing reads and writes to occur concurrently without interference.
- The choice between OCC and MVCC depends on the specific requirements of the system, such as the expected contention level and the read/write workload characteristics.