数据库的乐观锁和悲观锁

乐观锁(Optimistic Locking)

乐观锁是一种假设数据库操作不会发生冲突的锁定机制。在执行数据更新操作时,它并不会立刻加锁,而是先允许所有事务继续执行,并在提交时检查数据是否发生了变化。如果数据在读取后被其他事务修改了,那么当前事务就会被阻止,并给出一个冲突的提示。

乐观锁的使用场景:

  • 在并发冲突较少的环境中使用,适合读多写少的场景。
  • 不需要过多地使用数据库锁,减少了资源消耗,提高了系统的效率。

工作原理:

  • 每一行数据都有一个版本号(Version)或者时间戳(Timestamp),它在每次更新时会被递增或更新。
  • 当一个事务准备提交时,它会检查该行数据的版本号是否发生了变化。如果版本号没有变化,则提交更新;如果版本号变化了,说明有其他事务修改了该数据,当前事务则会被回滚或抛出冲突异常。

乐观锁的实现:

  1. 通过版本号: 每条数据在数据库中有一个版本号字段,每次更新时都会检查版本号是否一致,若不一致则抛出异常。
SELECT * FROM account WHERE id = 1 AND version = 1;
UPDATE account SET balance = balance - 100, version = version + 1 WHERE id = 1 AND version = 1;
  1. 通过时间戳: 每条数据也可以通过时间戳来标识数据的修改时间,更新时验证修改时间是否一致。

优点:

  • 对性能影响小,资源占用少。
  • 可以并发处理,降低锁竞争。

缺点:

  • 在高并发情况下,冲突概率大时,回滚和重试的成本可能增加。

悲观锁(Pessimistic Locking)

悲观锁是一种假设数据库操作会发生冲突的锁定机制。在执行数据更新操作时,事务会先加锁,其他事务在锁释放之前不能访问被锁定的数据,从而保证数据一致性。悲观锁通常是在读取数据时就加锁,直到事务结束才释放锁。

悲观锁的使用场景:

  • 在并发冲突较多的环境中使用,适合写多读少的场景。
  • 数据一致性要求高的情况下,适合使用悲观锁来避免数据被并发修改。

工作原理:

  • 事务在访问数据时会给数据加锁,直到事务完成操作并提交,锁才会释放。其他事务在获取不到锁时会被阻塞,直到锁被释放。
  • 悲观锁在数据库中通常通过 SELECT FOR UPDATE 语句来实现。

悲观锁的实现:

  1. 行级锁(SELECT FOR UPDATE): 在查询数据时加上 FOR UPDATE 关键字,保证当前数据行在事务提交之前不能被其他事务修改。
SELECT * FROM account WHERE id = 1 FOR UPDATE;
UPDATE account SET balance = balance - 100 WHERE id = 1;
  1. 数据库管理系统(DBMS)自动加锁: 例如,MySQL的InnoDB存储引擎在事务进行时会自动加锁,直到事务提交或回滚。

优点:

  • 数据的冲突较少,避免了多次提交检查的开销。
  • 数据一致性得到了严格保证。

缺点:

  • 性能开销较大,可能导致死锁(特别是在多个事务同时进行时),并发性能差。
  • 需要合理的锁粒度和锁管理,否则可能造成资源浪费。

总结对比:

特点乐观锁悲观锁
锁的类型读时不加锁,更新时检查版本号或时间戳读取时就加锁,直到事务结束才释放锁
使用场景并发冲突少,读多写少的场景并发冲突多,写多读少的数据场景
实现方式通过版本号、时间戳等方式实现通过行级锁(SELECT FOR UPDATE)等实现
性能开销性能较高,因为不加锁,只有提交时检查性能较低,可能导致阻塞和死锁
优点并发性能高,减少锁的竞争确保数据一致性,避免了冲突
缺点高并发情况下,回滚和重试的成本较高性能差,可能导致死锁和资源占用

在实际开发中,选择乐观锁还是悲观锁取决于具体的场景:

  • 对于冲突不频繁的场景(例如:库存、余额等),乐观锁是一个不错的选择;
  • 对于高并发的写入操作,且需要确保数据一致性的场景,悲观锁可能更适合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值