> [!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.