乐观锁和 MVCC 的区别

本文解析了数据库并发控制中的读-写冲突问题,介绍了多版本并发控制(MVCC)和乐观并发控制(OCC)两种无锁并发控制方法。MVCC通过为每个修改创建新版本来解决读写冲突;OCC则在提交前检查数据是否被更改。

作者:用心阁
链接:https://www.zhihu.com/question/27876575/answer/71836010
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在数据库中,并发控制是指在多个用户/进程/线程同时对数据库进行操作时,如何保证事务的一致性和隔离性的,同时最大程度地并发。当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形:读-读,不存在任何问题读-写,有隔离性问题,可能遇到脏读(会读到未提交的数据) ,幻影读等。写-写,可能丢失更新要解决冲突,一种办法是是锁,即基于锁的并发控制,比如2PL,这种方式开销比较高,而且无法避免死锁。多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读乐观并发控制(OCC)是一种用来解决写-写冲突的无锁并发控制,认为事务间争用没有那么多,所以先进行修改,在提交事务前,检查一下事务开始后,有没有新提交改变,如果没有就提交,如果有就放弃并重试。乐观并发控制类似自选锁。乐观并发控制适用于低数据争用,写冲突比较少的环境。多版本并发控制可以结合基于锁的并发控制来解决写-写冲突,即MVCC+2PL,也可以结合乐观并发控制来解决写-写冲突。

作者:吴镝
链接:https://www.zhihu.com/question/27876575/answer/73330077
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

MVCC解决的问题是读写互相不阻塞的问题,每次更新都产生一个新的版本,读的话可以读历史版本。试想,如果一个数据只有一个版本,那么多个事务对这个数据进行读写是不是需要读写锁来保护? 一个读写事务在运行的过程中在访问数据之前先加读/写锁这种实现叫做悲观锁,悲观体现在,先加锁,独占数据,防止别人加锁。乐观锁呢,读写事务,在真正的提交之前,不加读/写锁,而是先看一下数据的版本/时间戳,等到真正提交的时候再看一下版本/时间戳,如果两次相同,说明别人期间没有对数据进行过修改,那么就可以放心提交。乐观体现在,访问数据时不提前加锁。在资源冲突不激烈的场合,用乐观锁性能较好。如果资源冲突严重,乐观锁的实现会导致事务提交的时候经常看到别人在他之前已经修改了数据,然后要进行回滚或者重试,还不如一上来就加锁。

MVCC(Multi-Version Concurrency Control,多版本并发控制)数据库乐观锁(Optimistic Locking)是两种用于处理并发访问冲突的机制,它们在实现方式、适用场景底层原理上有所不同。下面是它们的详细区别: --- ### 1. **定义与作用** - **MVCC(多版本并发控制)** MVCC数据库系统中用于实现高并发访问的一种机制,它通过维护数据的多个版本来实现读写操作的非阻塞特性。读操作不会阻塞写操作,写操作也不会阻塞读操作,从而提高系统的并发性能。 - **乐观锁(Optimistic Locking)** 乐观锁是一种并发控制策略,它假设冲突较少,因此在进行数据修改时不会立即加,而是在提交时检查是否发生了冲突。如果冲突发生,则拒绝操作并提示重试。 --- ### 2. **实现方式** - **MVCC 实现方式:** - 每个事务在读取数据时看到的是一个一致性的快照(snapshot)。 - 数据库为每一行记录维护多个版本,并通过事务ID(如 `trx_id`)来标识哪个事务创建或删除了该行。 - 常见实现:InnoDB 引擎使用 undo log 版本号(如 `DB_TRX_ID`, `DB_ROLL_PTR`)来实现 MVCC。 - **乐观锁 实现方式:** - 通常通过版本号(version)或时间戳(timestamp)来实现。 - 在更新数据前检查版本号是否变化,如果变化则拒绝更新。 - 示例 SQL: ```sql UPDATE users SET name = 'Tom', version = version + 1 WHERE id = 1 AND version = 3; ``` --- ### 3. **适用场景** - **MVCC 更适合:** - 高并发读多写少的场景。 - 需要保证一致性读非阻塞读的场景。 - 例如:OLTP(在线事务处理)系统中的读操作。 - **乐观锁 更适合:** - 冲突较少的场景。 - 需要避免数据库长时间表的场景。 - 例如:Web 应用中对数据的更新操作。 --- ### 4. **优缺点对比** | 特性 | MVCC | 乐观锁 | |------|------|--------| | 并发性能 | 高(读写不阻塞) | 中(冲突时需重试) | | 实现复杂度 | 高(依赖数据库内部机制) | 低(应用层控制) | | 数据一致性 | 强一致性 | 最终一致性(可能失败) | | 适用范围 | 数据库引擎级别 | 应用层或业务逻辑 | --- ### 5. **Java 示例:乐观锁的实现** 以下是一个使用乐观锁更新数据的 Java 示例(使用 JPA 或 MyBatis 等框架时也类似): ```java public class UserService { public boolean updateUserName(Long id, String newName, Integer expectedVersion) { String sql = "UPDATE users SET name = ?, version = version + 1 WHERE id = ? AND version = ?"; try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password"); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, newName); ps.setLong(2, id); ps.setInt(3, expectedVersion); int rowsAffected = ps.executeUpdate(); return rowsAffected > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值