
本文共 1549 字,大约阅读时间需要 5 分钟。
在探讨悲观锁和乐观锁之前,我们先要理解什么是锁。锁是一种在并发环境下,用于控制多个操作顺序执行以保证数据安全变动的机制。它是一种通用的数据保护手段,而不特指某一技术。悲观锁和乐观锁也是基于这一原理。
锁的选择取决于具体的业务场景。在数据库管理系统(DBMS)中,悲观锁确实会使用数据库提供的锁机制(如行锁、表锁等),但乐观锁并非完全依赖锁机制,而是结合特定机制(如版本号或时间戳)实现。
悲观锁概念
悲观锁顾名思义字符怀很悲观。它假设数据的修改几率较高,因此在获取数据时会立即加锁,排除其他事务的修改可能。这导致其他事务必须等待当前事务完成后才能继续处理。
传统关系型数据库中常用行锁、表锁等都是悲观锁的体现。在Java中,synchronized和ReentrantLock等独占锁也基于悲观锁思想实现。
悲观锁主要包含两种锁类型:共享锁和排他锁。
共享锁(Shared Lock)
又称读锁(Read Lock)或S锁。它允许多个事务同时共享对同一数据的读取权,但不允许修改。这种锁机制主要针对并发读取场景,避免读取过度竞争。
排他锁(Exclusive Lock)
又称写锁(Write Lock)或X锁。它确保在一个事务修改数据前,不允许其他事务进行任何访问(包括读和写)。这最大程度地防止数据不一致,但也导致严重的并发效率下降。
悲观锁的特点是"先取锁再访问",这一保守策略确保了数据的安全性,但同时也付出了较高的性能代价,可能导致死锁和并发次数下降。
乐观锁概念
乐观锁则"乐观"地认为数据的修改不会频繁发生。因此,它允许多个事务同时对数据进行读取和写入操作,只有在数据提交时才检测冲突情况。
乐观锁采用"改而不锁"的机制,通常通过版本号或时间戳实现。以下是版本号机制的常见流程:
这种机制假设数据冲突的概率较低,适用于读多写少的场景。
悲观锁和乐观锁的应用
下面我们以一个蛋糕店的例子,来说明悲观锁和乐观锁的应用场景。
悲观锁实现
假设一个蛋糕店存在下列商品数据表:
id | name | count |
---|---|---|
1 | 豆乳小蛋糕 | 1 |
如果两个买家A和B同时下单,悲观锁会采取以下方式:
此时,如果B想购买同样的蛋糕,可能发现数量为0而放弃购买。
乐观锁实现
乐观锁需要在数据表中增加一个版本号字段。假设更新后的数据结构如下:
id | name | count | version |
---|---|---|---|
1 | 豆乳小蛋糕 | 1 | 0 |
在实际购买时,A和B都能先读取version=0的数据。A完成购买后,将version增加到1。B尝试购买时,查看最新数据发现version=1,与原版本不一致,提示用户数据已被修改。
悲观锁和乐观锁的优缺点
悲观锁优点
- 利用数据库本身提供的锁机制,简单可靠。
- 适用于高并发读写并OLDER场景。
悲观锁缺点
- 持续锁定数据会导致并发吞吐量下降。
- 高 probability of deadlocks 当多个事务试图锁定相同数据时。
- 锁的颗粒度(如行锁)可能带来额外的锁管理开销。
乐观锁优点
- 不需要额外的加锁机制,减少系统开销。
- 适用于读多写少的场景。
- 支持更高的并发度。
乐观锁缺点
- 需要额外设计机制(如版本号)手动检测冲突,增加开发复杂度。
- 如果事务确实修改数据,而未正确检测冲突,可能导致数据不一致。
总结
在实际应用中,应该根据业务特点选择合适的并发控制机制。悲观锁适合对数据一致性要求极高但并发需求不高的场景,而乐观锁则适用于读多写少的情况。
发表评论
最新留言
关于作者
